//#############################################################################
//
// A JavaScript library for string manipulation
//
// ©2007 Peter A. Kemmer
//
//#############################################################################
//-----------------------------------------------------------------------------
// Verify Dependencies
//-----------------------------------------------------------------------------
/*
if (!isDefined(REQUIRED_LIBRARY_LOADED)) {
alert("Error: The library Required.js needs to be loaded before This.js");
}
*/
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
var CORE_LIBRARY_LOADED = true;
//#############################################################################
// General Utilities
//#############################################################################
/**
* isDefined
*
* Is an object defined?
*
* @param object - The object to test
*/
function isDefined(object) {
return object === undefined ? false : true;
}
/**
* isArray
*
* Is an object an array? This might be fooled by objects that define length
*
* DON'T TEST 'INSTANCEOF ARRAY' BECAUSE 'arguments' IS NOT REALLY AN ARRAY!
* That's right! When using the automatic 'arguments' var, instanceof borks!
*
* @param object - The object to test
*/
function isArray(object) {
return typeof object == "object" && typeof object.length == "number";
}
/**
* write
*
* A shortcut to document.write()?
*
* @param content - The content to write
*/
function write(content) {
document.write(content);
}
/**
* trimArguments
*
* Convenience routine to trim off the start of an arguments 'array'
* since it can't be sliced for some crazy reason I don't understand
*
* @param newArguments - The arguments to clip
* @param startIndex - The index of the first argument to keep
*/
function trimArguments(allArguments, startIndex) {
var newArguments = new Array();
for (var ii = 0; ii < allArguments.length - startIndex; ii++) {
newArguments[ii] = allArguments[ii + startIndex];
}
return newArguments
}
//#############################################################################
// Modifications To String Object
//#############################################################################
/**
* String.contains
*
* Test to see if this string contains another string
*/
String.prototype.contains = function(string) {
return this.indexOf(string) >= 0;
}
/**
* String.startsWith
*
* Test to see if this string starts with another string
*/
String.prototype.startsWith = function(string, ignoreCase) {
if (ignoreCase) {
return string.toLowerCase() == this.substring(0, string.length).toLowerCase();
} else {
return string == this.substring(0, string.length);
}
}
/**
* String.endsWith
*
* Test to see if this string ends with another string
*/
String.prototype.endsWith = function(string, ignoreCase) {
if (ignoreCase) {
return string.toLowerCase() == this.substring(this.length - string.length).toLowerCase();
} else {
return string == this.substring(this.length - string.length);
}
}
/**
* String.trim
*
* Remove all the whitespace from the ends of the string,
*/
String.prototype.trim = function() {
return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
/**
* prependLines
*
* Prepend each actual line of content with a custom value. If the
* value is a Number, it will prepend a string with that many tabs
*
* @param prependOpt - The value to prepend, defaults to one tab
* @param prependEmptylLinesOpt - Prepend lines even when empty
*/
String.prototype.prependLines = function(prependOpt, prependEmptylLinesOpt) {
var prepend = isDefined(prependOpt) ? prependOpt : 1;
// If prepend is a number, create a
// string containing that many tabs
if (!isNaN(prepend)) {
var buffer = new StringBuffer();
// Build tabs string to prepend
for (var ii = 0; ii < prepend; ii++) {
buffer.append("\t");
}
prepend = buffer.toString();
}
// Split on newline and add prepend
var lines = this.split("\n");
var buffer = new StringBuffer();
for (var ii = 0; ii < lines.length; ii++) {
// Only prepend to actual lines!!!
if (prependEmptylLinesOpt || lines[ii] != "") {
buffer.append(prepend);
buffer.append(lines[ii]);
}
// Replace the split newlines!
if (ii < lines.length - 1) {
buffer.append("\n");
}
}
return buffer.toString();
}
/**
* String._secretAppend
*
* The secret method that does the work for String.append*
*
* @param content - The content, or arrays of contnet
* @param separator - The separator
* @param buffer - The buffer used to build the whole string before joining
*/
String.prototype._secretAppend = function(content, separator, buffer) {
var isRoot = false;
if (!isDefined(buffer)) {
isRoot = true;
buffer = [ this ];
}
// Loop through the content, adding it
// to the buffer for assembly later!!!
for (var ii = 0; ii < content.length; ii++) {
var item = content[ii];
if (isArray(item)) {
// if the argument is an array, unroll it and add items
for (var jj = 0; jj < item.length; jj++) {
this._secretAppend(item[jj], separator, buffer);
}
} else if (item != null) {
// if it's not an array, just shove it into the buffer!
buffer[buffer.length] = item;
}
}
if (isRoot) {
return buffer.join(separator);
}
}
/**
* String.append
*
* Efficiently append a bunch of strings
* to this one, then return the value!!!
*/
String.prototype.append = function() {
return this._secretAppend(arguments, "");
}
/**
* String.appendSep
*
* Efficiently append a bunch of strings to this
* one using a separator, then return the value!
*
* @param separator - The separator
*/
String.prototype.appendSep = function(separator) {
return this._secretAppend(trimArguments(arguments, 1), separator);
}
/**
* String.escapeHtml
*
* Escapes all the HTML tags and escape codes in the string
*/
String.prototype.escapeHtml = function() {
var toEscape = { '&':'&', '<':'<', '>':'>', '"':'"' };
var proxy = this;
for (var ii in toEscape) {
proxy = proxy.replace(new RegExp(ii, 'g'), toEscape[ii]);
}
return proxy
}
/**
* String.unescapeHtml
*
* Unescapes all the HTML tags and escape codes in the string
*/
String.prototype.unescapeHtml = function() {
var toUnescape = { '&':'&', '<':'<', '>':'>', '"':'"' };
var proxy = this;
for (var ii in toUnescape) {
proxy = proxy.replace(new RegExp(ii, 'g'), toUnescape[ii]);
}
return proxy
}
/**
* String.urlEncode
*
* URL encode the string:
*/
String.prototype.urlEncode = function() {
return encodeURIComponent(this);
}
/**
* String.isEmail
*
* Test to see if the string is a valid email address
*/
String.prototype.isEmail = function () {
var regex = new RegExp("\\w+([-+.\']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*");
var matches = regex.exec(this);
return matches != null && this == matches[0];
}
/**
* String.isURL
*
* Test to see if the string is a valid URL
*/
String.prototype.isUrl = function () {
var regex = new RegExp("http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-\\+ ./?%:&=#\\[\\]]*)?");
var matches = regex.exec(this);
return matches != null && this == matches[0];
}
//#############################################################################
// StringBuffer - A Helper Class To Speed String Manipulation
//#############################################################################
/**
* StringBuffer 'Constructor'
*
* A convenience function to 'construct' a StringBuffer object that
* drastically speeds up string concatenation.
*
* If a prepend value is defined the buffer's toString will perform
* a extra step, prepending the value using the normal prepend code
* This is useful for block indentation and other
*
* If a default separator is defined, it will be added before every
* append that doesn't otherwise define it's own separator. Useful!
*
* The separator can be set to prefix instead of follow items, too
*
* @param defaultSeparatorOpt - Optional separator used to divide appends
* @param prependOpt - Optional value to prepend onto each line
* @param startWithSeparatorOpt - Optional start buffer with default separator
*/
function StringBuffer(defaultSeparatorOpt, prependOpt, startWithSeparatorOpt) {
// class variables --------------------------------------------------------
this.defaultSeparator = defaultSeparatorOpt ? defaultSeparatorOpt : null;
this.prepend = isDefined(prependOpt) ? prependOpt : 0;
this.startWithSeparator = startWithSeparatorOpt ? startWithSeparatorOpt : false;
this.buffer = new Array();
}
/**
* StringBuffer.setPrepend
*
* Set the value to prepend onto all lines in the buffer on toString()
*
* @param prependLines - The value to prepend
*/
StringBuffer.prototype.setPrepend = function(prepend) {
this.prepend = prepend;
};
/**
* StringBuffer.getPrepend
*
* Set the value to prepend onto all lines in the buffer on toString()
*/
StringBuffer.prototype.getPrepend = function() {
return this.prepend;
};
/**
* StringBuffer.setStartWithSeparator
*
* Set the boolean that tells the buffer to prefix the entire results
* with the default separator if one has been provided to the buffer!
*
* @param prefixing - Prefix the results, or not
*/
StringBuffer.prototype.setStartWithSeparator = function(startWithSeparator) {
this.startWithSeparator = startWithSeparator;
};
/**
* StringBuffer.getStartWithSeparator
*
* Set the boolean that tells the buffer to prefix the entire results
* with the default separator if one has been provided to the buffer!
*/
StringBuffer.prototype.getStartWithSeparator = function() {
return this.startWithSeparator;
};
/**
* StringBuffer.empty()
*
* Empty the buffer
*/
StringBuffer.prototype.empty = function() {
this.buffer = new Array();
};
/**
* StringBuffer.isEmpty()
*
* Test to see if buffer is empty
*/
StringBuffer.prototype.isEmpty = function() {
return this.buffer.length == 0;
};
/**
* StringBuffer.getActualData()
*
* Get the actual data
*/
StringBuffer.prototype.getActualData = function() {
return this.buffer;
};
/**
* StringBuffer.append
*
* Append a string to the buffer
*
* Use this instead of lots of string concatenation with a +=
*
* @param ... - The content to append
*/
StringBuffer.prototype.append = function() {
this.appendSep(null, arguments);
};
/**
* StringBuffer.appendSep
*
* Append a string to the buffer, with an optional separator!
*
* Use this instead of lots of string concatenation with a +=
*
* @param separator - The separator
* @param ... - The content to append
*/
StringBuffer.prototype.appendSep = function(separator) {
var currSeparator = separator != null ? separator : this.defaultSeparator;
for (var ii = 1; ii < arguments.length; ii++) {
var item = arguments[ii];
if (isArray(item)) {
// if the argument is an array, unroll it and add items
for (var jj = 0; jj < item.length; jj++) {
this.appendSep(separator, item[jj]);
}
} else if (item != null) {
// if it's not an array, convert to a string and buffer
item = item.toString();
if (item != "") {
if (currSeparator && (this.startWithSeparator || this.buffer.length > 0)) {
this.buffer[this.buffer.length] = currSeparator;
}
this.buffer[this.buffer.length] = item;
}
}
}
};
/**
* StringBuffer.newline
*
* Append newline to the buffer
*/
StringBuffer.prototype.newline = function() {
this.buffer[this.buffer.length] = "\n";
};
/**
* StringBuffer.toString
*
* Act like a String! Yeah! Do it!
*/
StringBuffer.prototype.toString = function() {
var string;
if (this.buffer.length == 1) {
// Return a single item as a string. This becomes handy
// if someone calls toString over and over. The initial
// call constructs the string and stores it as a single
// item in the buffer. The next time through it'll skip
// the concatenation step by hitting this block! Quick!
string = this.buffer[0];
} else {
string = this.buffer.join("");
// Before we return the concatenated string, wipe clear
// the old buffer and add the new string to it. This'll
// make toStringing the buffer next time so much faster
this.buffer = new Array();
this.buffer[this.buffer.length] = string;
}
if (this.prepend != null && this.prepend != "") {
return string.prependLines(this.prepend);
} else {
return string;
}
};
//#############################################################################
// AJAX
//#############################################################################
if (!isDefined(window.XMLHttpRequest)) {
window.XMLHttpRequest = function() {
var types = [
"Microsoft.XMLHTTP",
"MSXML2.XMLHTTP.5.0",
"MSXML2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0",
"MSXML2.XMLHTTP"
];
for (var ii = 0; ii < types.length; ii++) {
try {
return new ActiveXObject(types[ii]);
} catch (e) {
// Do nothing!
}
}
return undefined;
}
}