Add Function
No expertise needed. Just JS Coding skills. We will review the code and make the changes in Git. When your code makes it into php.js you will be credited accordingly.
Add a comment with your code at the bottom of this page.
If you want you can read the Developer Guidelines that would save us some work.
Function proposals
@Italo, @navegador: what are you guys trying to post? Did you have actual data when you submitted it or...?
@Brett/Kevin: Harmony is a very interesting project and it's a good starting point. I think that i will start from converting it to js to parse the php code. Anyway now it's christmas time (also in Italy:D) and i don't like to work when there is the snow so i'm going to start in the 2010:D. Merry christmas and happy new year to everybody.
@Kevin/Marco: As far as the Harmony Framework, while that can be pretty cool, that is making use of tokenizer on the server-side, while our intention is to implement that part in JavaScript also. Nevertheless, it may still be of interest.
@Kevin: I have to agree, this looks like very good stuff (Just to check though, did you mean to redefine the variable 'i' within a loop which already was using 'i'?).
I think Tim's way with undefined is the best way though, as he is testing with "typeof". One advantage to this is that it is immune to "undefined" itself being redefined as JavaScript oddly allows. Also, if you test against undefined itself AND use a typeof, you will never get a true result because typeof always returns a string. Fixed in Git.
@ Tim de Koning: Brilliant stuff Tim, I must say. I've added your function to our repo: http://github.com/kvz/phpjs/commit/ad6df4d48a7551b2ddc53bf03c464bdf1d49b8e4 and hence will be online shortly.
I already fixed some common jslint issues: http://github.com/kvz/phpjs/commit/5cb72f3bd9c6b624827193a2bfde24de14c0ab32 but there are still some more to tackle.
You asked for feedback; one thing I would to see - if possible - is more orthodox for-loops. You seem to perform a lot of calculations in the header of a forloop while it may just as well be possible to do them in the body.
This would allow for less jslint errors, shorter lines, and hence improved readability & maintainability.
I also noticed you check for undefined with == 'undefined'. The correct way is === undefined: this will save you some headaches to come.
There's an interesting webcast by Douglass Crockford to be found in the Developer Guidelines here: http://wiki.github.com/kvz/phpjs/developerguidelines
It's related to a lot of my points above.
Anyway, please don't feel discouraged here, I'm just trying to help you write even better JavaScript. As for your contribution: pack is a wonderful new member to php.js' arsenal. Thanks so much!
function pack(format) {
// Pack given arguments into binary string according to format.
// For possible formats see http://www.php.net/pack
//
// Please note: 'machine dependant byte order and size' aren't applicable for javascript
// pack works as on a 32bit, little endian machine
//
// version: 0.1
// discuss at: http://www.kingsquare.nl/blog/12-12-2009/13507444/New-phpjs-function-pack-
// dependancies: none
// + original by: Tim de Koning (http://www.kingsquare.nl)
// + original float encoding by: Jonas Raoni Soares Silva (http://jsfromhell.com/classes/binary-parser)
//
// * example 1: pack("nvc*", 0x1234, 0x5678, 65, 66);
// * returns 1: '4xVAB';
//
// feedback is nice :-) phpjs-pack@kingsquare.nl
var formatPointer, argumentPointer, result, instruction, quantifier, word, nibble;
formatPointer = 0;
argumentPointer = 1;
result = '';
while (formatPointer < format.length) {
instruction = format[formatPointer];
quantifier = '';
formatPointer++;
while((formatPointer < format.length) && (format[formatPointer].match(/[0-9\*]/) != null)) {
quantifier += format[formatPointer];
formatPointer++;
}
if (quantifier == '') quantifier = 1;
//now pack variables: 'quantifier' times 'instruction'
switch(instruction) {
case 'A': //NUL-padded string
case 'a': //SPACE-padded string
if (typeof arguments[argumentPointer] == 'undefined') throw new Error('Warning: pack() Type '+instruction+': not enough arguments');
else argument = arguments[argumentPointer];
if (quantifier=='*') quantifier = argument.length;
for (var i=0; i<quantifier; i++) {
if (typeof argument[i] == 'undefined') {
if (instruction == 'a') result += "\0";
else result += " ";
}
else result += argument[i];
}
argumentPointer++;
break;
case 'h': //Hex string, low nibble first
case 'H': //Hex string, high nibble first
if (typeof arguments[argumentPointer] == 'undefined') throw new Error( 'Warning: pack() Type '+instruction+': not enough arguments');
else argument = arguments[argumentPointer];
if (quantifier=='*') quantifier = argument.length;
if (quantifier > argument.length) throw new Error('Warning: pack() Type '+instruction+': not enough characters in string');
for (var i=0; i<quantifier; i+=2) {
//always get per 2 bytes...
word = argument[i];
if (((i+1) >= quantifier) || typeof(argument[i+1])=='undefined') { word += "0"; }
else word += argument[i+1];
//the fastest way to reverse?
if (instruction == 'h') word = word[1]+word[0];
result += String.fromCharCode(parseInt(word, 16));
}
argumentPointer++;
break;
case 'c': //signed char
case 'C': //unsigned char
//c and C is the same in pack
if (quantifier=='*') quantifier = arguments.length - argumentPointer;
if (quantifier > (arguments.length - argumentPointer)) throw new Error('Warning: pack() Type '+instruction+': too few arguments');
for (var i=0; i<quantifier; i++) {
result += String.fromCharCode(arguments[argumentPointer]);
argumentPointer++
}
break;
case 's': // signed short (always 16 bit, machine byte order)
case 'S': // signed short (always 16 bit, machine byte order)
case 'v':
//s and S is the same in pack
//but can machine byte order be retrieved in javascript?
//this is default byte order anywayz...
if (quantifier=='*') quantifier = arguments.length - argumentPointer;
if (quantifier > (arguments.length - argumentPointer)) throw new Error('Warning: pack() Type '+instruction+': too few arguments');
for (var i=0; i<quantifier; i++) {
result += String.fromCharCode(arguments[argumentPointer]&0xFF);
result += String.fromCharCode(arguments[argumentPointer]>>8&0xFF);
argumentPointer++;
}
break;
case 'n': // unsigned short (always 16 bit, big endian byte order)
if (quantifier=='*') quantifier = arguments.length - argumentPointer;
if (quantifier > (arguments.length - argumentPointer)) throw new Error('Warning: pack() Type '+instruction+': too few arguments');
for (var i=0; i<quantifier; i++) {
result += String.fromCharCode(arguments[argumentPointer]>>8&0xFF);
result += String.fromCharCode(arguments[argumentPointer]&0xFF);
argumentPointer++;
}
break;
case 'i': // signed integer (machine dependent size and byte order)
case 'I': // unsigned integer (machine dependent size and byte order)
case 'l': // signed long (always 32 bit, machine byte order)
case 'L': // unsigned long (always 32 bit, machine byte order)
case 'V': // unsigned long (always 32 bit, little endian byte order)
if (quantifier=='*') quantifier = arguments.length - argumentPointer;
if (quantifier > (arguments.length - argumentPointer)) throw new Error('Warning: pack() Type '+instruction+': too few arguments');
for (var i=0; i<quantifier; i++) {
result += String.fromCharCode(arguments[argumentPointer]&0xFF);
result += String.fromCharCode(arguments[argumentPointer]>>8&0xFF);
result += String.fromCharCode(arguments[argumentPointer]>>16&0xFF);
result += String.fromCharCode(arguments[argumentPointer]>>24&0xFF);
argumentPointer++;
}
break;
case 'N': // unsigned long (always 32 bit, big endian byte order)
if (quantifier=='*') quantifier = arguments.length - argumentPointer;
if (quantifier > (arguments.length - argumentPointer)) throw new Error('Warning: pack() Type '+instruction+': too few arguments');
for (var i=0; i<quantifier; i++) {
result += String.fromCharCode(arguments[argumentPointer]>>24&0xFF);
result += String.fromCharCode(arguments[argumentPointer]>>16&0xFF);
result += String.fromCharCode(arguments[argumentPointer]>>8&0xFF);
result += String.fromCharCode(arguments[argumentPointer]&0xFF);
argumentPointer++;
}
break;
case 'f': // float (machine dependent size and representation)
case 'd': // double (machine dependent size and representation)
//version based on IEEE754y
var precisionBits = 23, exponentBits = 8;
if (instruction=='d') {
precisionBits = 52 ;
exponentBits = 11;
}
if (quantifier=='*') quantifier = arguments.length - argumentPointer;
if (quantifier > (arguments.length - argumentPointer)) throw new Error('Warning: pack() Type '+instruction+': too few arguments');
for (var i=0; i<quantifier; i++) {
var number = arguments[argumentPointer], bias = Math.pow(2, exponentBits - 1) - 1, minExp = -bias + 1, maxExp = bias, minUnnormExp = minExp - precisionBits, status = isNaN(n = parseFloat(number)) || n == -Infinity || n == +Infinity ? n : 0, exp = 0, len = 2 * bias + 1 + precisionBits + 3, bin = new Array(len), signal = (n = status !== 0 ? 0 : n) < 0, n = Math.abs(n), intPart = Math.floor(n), floatPart = n - intPart, i, lastBit, rounded, j, tmpResult;
for(i = len; i; bin[--i] = 0);
for(i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2));
for(i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0) && --floatPart);
for(i = -1; ++i < len && !bin[i];);
if(bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - (exp = minExp - 1))) + 1]){
if(!(rounded = bin[lastBit])) {
for(j = lastBit + 2; !rounded && j < len; rounded = bin[j++]);
}
for(j = lastBit + 1; rounded && --j >= 0; (bin[j] = !bin[j] - 0) && (rounded = 0));
}
for(i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];);
(exp = bias + 1 - i) >= minExp && exp <= maxExp ? ++i : exp < minExp && (exp != bias + 1 - len && exp < minUnnormExp, i = bias + 1 - (exp = minExp - 1));
(intPart || status !== 0) && (1, exp = maxExp + 1, i = bias + 2, status == -Infinity ? signal = 1 : isNaN(status) && (bin[i] = 1));
for(n = Math.abs(exp + bias), j = exponentBits + 1, tmpResult = ""; --j; tmpResult = (n % 2) + tmpResult, n = n >>= 1);
for(n = 0, j = 0, i = (tmpResult = (signal ? "1" : "0") + tmpResult + bin.slice(i, i + precisionBits).join("")).length, r = []; i; n += (1 << j) * tmpResult.charAt(--i), j == 7 && (r[r.length] = String.fromCharCode(n), n = 0), j = (j + 1) % 8);
r[r.length] = n ? String.fromCharCode(n) : "";
result += r.join('');
argumentPointer++;
}
break;
case 'x': //NUL byte
if (quantifier=='*') throw new Error('Warning: pack(): Type x: \'*\' ignored');
for (var i=0; i<quantifier; i++) {
result += "\0";
}
break;
case 'X': //Back up one byte
if (quantifier=='*') throw new Error('Warning: pack(): Type X: \'*\' ignored');
for (var i=0; i<quantifier; i++) {
if (result.length==0) {
throw new Error('Warning: pack(): Type X: outside of string');
} else {
result = result.substring(0, result.length - 1);
}
}
break;
case '@': //NUL-fill to absolute position
if (quantifier=='*') throw new Error('Warning: pack(): Type X: \'*\' ignored');
if (quantifier>result.length) {
var extraNullCount = quantifier - result.length;
for (var i=0; i<extraNullCount; i++) {
result += "\0";
}
}
if (quantifier<result.length) {
result = result.substring(0, quantifier);
}
break;
default:
throw new Error('Warning: pack() Type '+instruction+': unknown format code')
}
}
if (argumentPointer < arguments.length) throw new Error('Warning: pack(): '+( arguments.length - argumentPointer)+' arguments unused');
return result;
}
@Brett: interesting links:) right now I've a lot of work to do but when I have time i will try to do that.
@Marco, I've been trying to work on one myself, so I think a PHP-in-JavaScript parser is a great idea. :) (We could build it as the function token_get_all() as in the real PHP tokenizer extension.)
Among other things, it'd be great to have it to convert the PHP unit tests into php.js unit tests.
I figured it may be even cooler to parse a standard syntax like EBNF (see Wikipedia) so we could create parsers/lexers for any language which was defined in EBNF.
I found an EBNF grammar at http://www.icosaedro.it/articoli/php-syntax-ebnf.txt
I started playing around with it, then discovered Antlr, which is already meant to do this, and has a JavaScript implementation.
However, it is not documented with a lot of examples (see http://www.antlr.org/wiki/display/ANTLR3/ANTLR3JavaScriptTarget ), though there is a whole lot to read about it (http://www.antlr.org/wiki/display/ANTLR3/ANTLR+v3+printable+documentation or the tutorials at http://www.antlr.org/wiki/display/ANTLR3/Tutorials
I tried out the Expression Evaluator tool they have (see http://www.antlr.org/works/help/tutorial/calculator.html ) for download against the PHP EBNF, but after converting to a format the software recognized, I ran into some errors about parts of the EBNF being defined recursively, and I haven't had a chance to figure out how it could be resolved.
In other words, this could take a lot of time, but it would be soooo worth it if someone is interested to help out or try it out themselves.
Everything from syntax highlighters, to translators, etc. would be possible. And of course, it'd be cool to have one for JavaScript itself, though JSLint may already be a good one to use for that...
@Brett: what do you think about creating a php parser written in javascript, so that you can use the PHP sintax (especially classes) to generate the relative js code?
By the way, if anyone has any outstanding issues (besides the recent items for date() which I've already added to our tracker, and besides the error_log() and asort() issues which I have on my todo list), please let us know so we can try to make sure we are 100% tracking all outstanding issues...
@Kankrelune : Doesn't look like I told you that I added your reimplementation for version_compare... Thanks!
@Paul: That sounds like a good idea. I'd like to take a closer look when I have some time. As far as file writing, I've started a "file_put_contents()" implementation for use in Mozilla extensions, but we can adapt it to alternatively work for making Ajax POST or PUT requests (as well as make it work with CommonJS SSJS potentially too). We can use the stream context argument to do any configuration (e.g., to work with Mozilla file writing instead of Ajax POST or whatever).
Btw, one JavaScript gotcha to look out for (at least for us PHP devs) is that you can't test for "console" as you did; if it doesn't exist, it will cause an error that causes the script to stop. You either have to make the check as a try-catch block, or test it as a property (i.e., "if (window.console) ...").
Most browsers now support basic console.log() so it would be helpful to output error_log() to this.
I don't see a way to support email or file logging, but basic console.log() would be a good start.
function error_log(message,message_type,destination,extra_headers)
{
if(typeof(console) === 'undefined' || console == null)
return;
if(message_type==null)
message_type=0;
switch(message_type)
{
case 0: // console
case 4: // SAPI logging
console.log(message);
break;
case 1: // email
console.log(message);
break;
case 2: // nothing
break;
case 3: // file logging
if(console.group!=null)
console.group(destination);
console.log(message);
if(console.groupEnd!=null)
console.groupEnd();
break;
}
}
Also please note I just made another commit now which removed a trailing comma (needed for IE) in the initial OPTS assignment.
Hi Marco,
Ok, now looks great, thanks! I added a couple other test cases and they passed too. I also added some code toward the beginning to allow flags to be passed in as bitwise-OR'd numbers, a single-item string, or an array of strings (yours allowed multiple flags to be space-separated in a single string, but to avoid potential conflicts, I think we should stick with arrays and bitwise-OR'd numbers for this). The commit is at http://github.com/kvz/phpjs/commit/9e36e6a87aae3cbfbeb98890a2775d479297d735
However, it's up to Kevin as to whether this should be moved to the main /functions directory (and category) at this point since our preg functions do not support all of the flags and syntax of PHP regular expressions such as Unicode character classes as in a good many cases these could in fact be added, such as in the very good XRegExp package.
We could just release the preg functions for now as long as they implement all of the function arguments (at least preg_split() already does) and just hope that they will later be improved to support more of the PCRE syntax and flags.
@Brett: i've finally solved the problem and correct another bug. Now every test works.
function preg_split (pattern, subject, limit, flags) {
// http://kevin.vanzonneveld.net
// + original by: Marco Marchi?
// * example 1: preg_split(/[\s,]+/, "hypertext language, programming");
// * returns 1: [hypertext, language, programming]
// * example 2: preg_split('//', 'string', -1, 'PREG_SPLIT_NO_EMPTY');
// * returns 2:
// * example 3: var str = 'hypertext language programming';
// * example 3: preg_split('/ /', str, -1, 'PREG_SPLIT_OFFSET_CAPTURE');
// * returns 3:
// First example still causing infinite loop!
limit = limit || 0; flags = flags || ''; // Limit and flags are optional
var result, ret=[], index=0, i = 0,
noEmpty = flags.indexOf("PREG_SPLIT_NO_EMPTY") !== -1,
delim = flags.indexOf("PREG_SPLIT_DELIM_CAPTURE") !== -1,
offset = flags.indexOf("PREG_SPLIT_OFFSET_CAPTURE") !== -1,
regexpBody=/^\/(.*)\/\w*$/.exec(pattern.toString())[1],
regexpFlags=/^\/.*\/(\w*)$/.exec(pattern.toString())[1];
// Non-global regexp causes an infinite loop when executing the while,
// so if it's not global, copy the regexp and add the "g" modifier.
pattern=pattern.global && typeof pattern!="string" ? pattern :
new RegExp(regexpBody, regexpFlags+(regexpFlags.indexOf("g")!==-1 ? "":"g"));
var _filter = function(str,strindex) {
// If the match is empty and the PREG_SPLIT_NO_EMPTY flag is set don't add it
if(noEmpty && !str.length) {return;}
// If the PREG_SPLIT_OFFSET_CAPTURE flag is set
// transform the match into an array and add the index at position 1
if(offset) {str = [str,strindex];}
ret.push(str);
};
//Special case for empty regexp
if(!regexpBody){
result=subject.split("");
for(i=0;i<result.length;i++) {
_filter(result[i],i);
}
return ret;
}
// Exec the pattern and get the result
while(result = pattern.exec(subject)) {
// Stop if the limit is 1
if (limit === 1) {break;}
// Take the correct portion of the string and filter the match
_filter(subject.slice(index, result.index), index);
index = result.index+result[0].length;
// If the PREG_SPLIT_DELIM_CAPTURE flag is set every capture match must be included in the results array
if(delim) {
// Convert the regexp result into a normal array
var resarr = Array.prototype.slice.call(result);
for(i = 1; i < resarr.length; i++) {
if(result[i] !== undefined) {
_filter(result[i], result.index+result[0].indexOf(result[i]));
}
}
}
limit--;
}
// Filter last match
_filter(subject.slice(index, subject.length), index);
return ret;
}
@Marco: Here ya go... (Btw, I'd debug it myself, but busy and have a cold)
<script>
alert(preg_split(/[\s,]+/, "hypertext language, programming"));
function preg_split (pattern, subject, limit, flags) {
// http://kevin.vanzonneveld.net
// + original by: Marco Marchi?
// * example 1: preg_split(/[\s,]+/, "hypertext language, programming");
// * returns 1: 'hypertext,language,programming'
limit = limit || 0; flags = flags || ''; //Limit and flags are optional
var result, ret=[], index=0, i = 0,
noEmpty = flags.indexOf("PREG_SPLIT_NO_EMPTY") !== -1,
delim = flags.indexOf("PREG_SPLIT_DELIM_CAPTURE") !== -1,
offset = flags.indexOf("PREG_SPLIT_OFFSET_CAPTURE") !== -1,
regexpBody=/^\/(.*)\/\w*$/.exec(pattern.toString())[1],
regexpFlags=/^\/.*\/(\w*)$/.exec(pattern.toString())[1];
//non global regexp cause a infinite loop when executing the while,
//so if it's not global copy the regexp and add the "g" modifier.
pattern=pattern.global || typeof pattern!="string" ? pattern :
new RegExp(regexpBody,regexpFlags+(regexpFlags.indexOf("g")!==-1 ? "":regexpFlags+"g"));
var _filter = function(str,strindex) {
// If the match is empty and the PREG_SPLIT_NO_EMPTY flag is set don't add it
if(noEmpty && !str.length) {return;} // If the PREG_SPLIT_OFFSET_CAPTURE flag is set
// transform the match into an array and add the index at position 1
if(offset) {str = [str,strindex];}
ret.push(str);
};
//Special case for empty regexp
if(!regexpBody){
var result=subject.split("");
for(i=0;i<result.length;i++) _filter(result[i],i);
return ret; }
// Exec the pattern and get the result
var ctr=0;
while(result = pattern.exec(subject)) {
if (++ctr > 100) {break;}
// Stop if the limit is 1
if (limit === 1) {break;} // Take the correct portion of the string and filter the match
_filter(subject.slice(index, result.index), index);
index = result.index+result[0].length;
// If the PREG_SPLIT_DELIM_CAPTURE flag is set every capture match must be included in the results array
if(delim) { // Convert the regexp result into a normal array
var resarr = Array.prototype.slice.call(result);
for(i = 1; i < resarr.length; i++) {
if(result[i] !== undefined) {
_filter(result[i], result.index+result[0].indexOf(result[i])); }
}
}
limit--;
}
// Filter last match
_filter(subject.slice(index, subject.length), index);
return ret;
}
</script>
@Brett: I've done every test again in Firefox, IE7, IE6, Opera, Chrome and Safari and i haven't found any infinite loop. This time i have taken the copy of the function from github so it's correct. I'm sorry but i can't get the error you're talking about. Can you write the code that you use when you get the infinite loop?
@Marco: I am using the exact code you have below in Firefox, and it will run into an infinite loop with the first example you have listed there... Is it possible something was stripped out of your code by our comments system--if you test in Firefox against that code as it is there, you should see it also creates an infinite loop (though I'd put a limit in the while loop so your browser doesn't lock up)...
@Brett: i've done all tests with the last function that i've posted and they are all correct, so please try again. Remember that in js when you pass a string as a regex you must use the double escape char ( \s become \\s ), maybe you forgot it when you have done your tests because i don't get any error and i don't understand why you still getting an infinite loop.
@Marco: I'm still getting infinite loops on the first example. Please test all tests and make sure all three are working. If you are using Firefox, I would suggest https://addons.mozilla.org/en-US/firefox/addon/7434 and its XUL Editor or JS Env for doing JavaScript tests . Also, I'm not sure why, but the line returns are getting messed up--is that something you are doing or is the comment code at this site? Also, please use http://github.com/kvz/phpjs/blob/master/_workbench/pcre/preg_split.js . Thanks!
@Brett Zamir: I've fixed the infinite loop bug and done other tests. I think it works perfect now.
function preg_split (pattern, subject, limit, flags) {
// http://kevin.vanzonneveld.net
// + original by: Marco Marchi?
// * example 1: preg_split(/[\s,]+/, "hypertext language, programming");
// * returns 1: 'hypertext,language,programming'
limit = limit || 0; flags = flags || ''; //Limit and flags are optional
var result, ret=[], index=0, i = 0,
noEmpty = flags.indexOf("PREG_SPLIT_NO_EMPTY") !== -1,
delim = flags.indexOf("PREG_SPLIT_DELIM_CAPTURE") !== -1,
offset = flags.indexOf("PREG_SPLIT_OFFSET_CAPTURE") !== -1,
regexpBody=/^\/(.*)\/\w*$/.exec(pattern.toString())[1],
regexpFlags=/^\/.*\/(\w*)$/.exec(pattern.toString())[1];
//non global regexp cause a infinite loop when executing the while,
//so if it's not global copy the regexp and add the "g" modifier.
pattern=pattern.global || typeof pattern!="string" ? pattern :
new RegExp(regexpBody,regexpFlags+(regexpFlags.indexOf("g")!==-1 ? "":regexpFlags+"g"));
var _filter = function(str,strindex) {
// If the match is empty and the PREG_SPLIT_NO_EMPTY flag is set don't add it
if(noEmpty && !str.length) {return;}
// If the PREG_SPLIT_OFFSET_CAPTURE flag is set
// transform the match into an array and add the index at position 1
if(offset) {str = [str,strindex];}
ret.push(str);
};
//Special case for empty regexp
if(!regexpBody){
var result=subject.split("");
for(i=0;i<result.length;i++) _filter(result[i],i);
return ret;
}
// Exec the pattern and get the result
while(result = pattern.exec(subject)) {
// Stop if the limit is 1
if (limit === 1) {break;}
// Take the correct portion of the string and filter the match
_filter(subject.slice(index, result.index), index);
index = result.index+result[0].length;
// If the PREG_SPLIT_DELIM_CAPTURE flag is set every capture match must be included in the results array
if(delim) {
// Convert the regexp result into a normal array
var resarr = Array.prototype.slice.call(result);
for(i = 1; i < resarr.length; i++) {
if(result[i] !== undefined) {
_filter(result[i], result.index+result[0].indexOf(result[i]));
}
}
}
limit--;
}
// Filter last match
_filter(subject.slice(index, subject.length), index);
return ret;
}
@Marco: Not yet. Getting another infinite loop trying the second example:
preg_split('//', 'string', -1, 'PREG_SPLIT_NO_EMPTY');
Maybe it's because it's empty, haven't looked carefully at it.
Here's the third one to try too:
var str = 'hypertext language programming';
var chars = preg_split('/ /', str, -1, 'PREG_SPLIT_OFFSET_CAPTURE');
and make sure it follows that in the docs at http://www.php.net/manual/en/function.preg-split.php
Please, if you would, use this version to work off of in the future since I cleaned it up a little superficially:
http://github.com/kvz/phpjs/blob/master/_workbench/pcre/preg_split.js
Thanks!
@Ben Shelock: That was a nice attempt for someone who describes themselves as an amateur, so thanks for offering! However, in order to emulate the function PHP fully, which allows special treatment of C-escape sequences, which sometimes makes octal escapes, and allows ranges of characters, there is a good bit more which needed to be done. I had already begun an implementation on this earlier, and it is now finished at http://github.com/kvz/phpjs/blob/master/functions/strings/addcslashes.js thanks to your prompting... :)
Fixed the infinite loop bug:
function preg_split(pattern,subject,limit,flags)
{
var limit=limit || 0, flags=flags || "", //Limit and flags are optional
result, ret=new Array(), index=0,
//non global regexp cause a infinite loop when executing the while,
//so if it's not global copy the regexp and add the "g" modifier
pattern=pattern.global? pattern : new RegExp(/^\/(.*)\/\w*$/.exec(pattern.toString())[1],/^\/.*\/(\w*)$/.exec(pattern.toString())[1]+"g"),
noEmpty=flags.indexOf("PREG_SPLIT_NO_EMPTY")!=-1, delim=flags.indexOf("PREG_SPLIT_DELIM_CAPTURE")!=-1,
offset=flags.indexOf("PREG_SPLIT_OFFSET_CAPTURE")!=-1,
filter=function(str,strindex){
//If the match is empty and the PREG_SPLIT_NO_EMPTY flag is set dont'add it
if(noEmpty && !str.length) return;
//If the PREG_SPLIT_OFFSET_CAPTURE flag is set transform the match into an array and add the index at position 1
if(offset) str=new Array(str,strindex);
ret.push(str);
};
//Exec the pattern and get the result
while(result=pattern.exec(subject)) {
//Stop if the limit is 1
if(limit==1) break;
//Take the correct portion of the string and filter the match
filter(subject.slice(index,result.index),index);
index=result.index+result[0].length;
//If the PREG_SPLIT_DELIM_CAPTURE flag is set every capture match must be included in the results array
if(delim)
{
//Convert the regexp result into a normal array
var resarr=Array.prototype.slice.call(result);
for(i=1;i<resarr.length;i++)
if(result[i]!==undefined) filter(result[i],result.index+result[0].indexOf(result[i]));
}
limit--;
}
//Filter last match
filter(subject.slice(index,subject.length),index);
return ret;
}
@Brett Zamir: I can't see the test but when i have done my tests i remember that i found that bug too, but it's not an error in the code. It is produced by regexp that are not global (no G modifier), with those regexp the while loop become infinite. I try to solve the problem now.
function addcslashes(str, charlist){
// Escapes characters defined in charlist
//
// version: 1
// discuss at: http://phpjs.org/functions/addcslashes
// + original by: Ben Shelock
// % note 1: Developed by a javascript amature, forgive me.
// * example 1: addcslashes('Something', 'oei');
// * returns 1: S\om\eth\ing
var charlist = charlist.split(""), string = '';
for(var i = 0; i < str.length; i++){
if(in_array(str[i], charlist)){
string = string+'\\'+str[i];
}
else{
string = string+str[i];
}
}
return string;
}
@Marco: Thanks for the function (it would be a great one to have), and I added it to our workbench area, but it looks like there is still at least one bug needing fixing. If you use the example I added at http://github.com/kvz/phpjs/blob/8f2086ef2577bc81486a3f96d6a850e2b2d82b03/_workbench/pcre/preg_split.js it runs in an infinite loop.
function preg_split(pattern,subject,limit,flags)
{
var limit=limit || 0, flags=flags || "", //Limit and flags are optional
result, ret=new Array(), index=0,
noEmpty=flags.indexOf("PREG_SPLIT_NO_EMPTY")!=-1,
delim=flags.indexOf("PREG_SPLIT_DELIM_CAPTURE")!=-1,
offset=flags.indexOf("PREG_SPLIT_OFFSET_CAPTURE")!=-1,
filter=function(str,strindex){
//If the match is empty and the PREG_SPLIT_NO_EMPTY flag is set dont'add it
if(noEmpty && !str.length) return;
//If the PREG_SPLIT_OFFSET_CAPTURE flag is set transform the match into an array and add the index at position 1
if(offset) str=new Array(str,strindex);
ret.push(str);
};
//Exec the pattern and get the result
while(result=pattern.exec(subject)) {
//Stop if the limit is 1
if(limit==1) break;
//Take the correct portion of the string and filter the match
filter(subject.slice(index,result.index),index);
index=result.index+result[0].length;
//If the PREG_SPLIT_DELIM_CAPTURE flag is set every capture match must be included in the results array
if(delim)
{
//Convert the regexp result into a normal array
var resarr=Array.prototype.slice.call(result);
for(i=1;i<resarr.length;i++)
if(result[i]!==undefined) filter(result[i],result.index+result[0].indexOf(result[i]));
}
limit--;
}
//Filter last match
filter(subject.slice(index,subject.length),index);
return ret;
}
Honestly, I wrote this function several months ago for a project and discovered it would be easier if I implemented it in PL/SQL. I recently found it in some old files, tested it using the same test that Apache uses for their metaphone function and fixed any errors that I found (passes 238 tests out of 244, although PHP's implementation isn't without errors either)
I have it up here if you want to see it: http://gregoryfrazier.com/metaphone2.html
When I wrote it, I spent very little time on it, it's quite a hack job, thats why I'm saying I'm sure there are bugs. I don't know when I'd have time next to debug it; so I thought I'd just post it up here for someone else to look at and play around with, or else it would just rot on my hard drive.
As for the String() question, I vaguely remember something about Internet Explorer 6 giving me errors unless it was done that way. I have no idea, it doesn't make sense to me, but who knows?
As for the second argument, yeah that wouldn't be hard at all. Just change return to:
return metaword.substr(0,phones);
With some error checking to make sure it's an integer or parseInt it or whatever.
@Greg Frazier, Wow, very nice work!
I did a little bit of jslint.com - assisted clean-up, added examples, and fixed one bug (metaword spelled as metword in one place), so please use this version in the future: http://trac.phpjs.org/projects/phpjs/browser/trunk/_workbench/strings/metaphone.js (you can download as text via a link at the bottom of the page)
I haven't looked at the code closely, but are all those uses of the String() object wrapper necessary?
Also, would there be any way to implement the second argument "phones" (for maximum phonemes according to the PHP source)?
When you say there are bugs, can you give test cases/examples? Otherwise, I'd love for us to be able to release this out of the workbench area...
Thanks again, really great to have this one!
There are bugs, but in my testing, it's very accurate.
function metaphone(word){
var wordlength = word.length;
var x = 0, tempchar = "";
var metaword = "";
var removedbl = function(word){
var wordlength = word.length;
var tempword = word.toLowerCase();
var rebuilt;
var tempchar1, tempchar2;
var x;
tempchar1 = String(tempword).charAt(0);
rebuilt = tempchar1;
for(x=1;x<wordlength;x++){
tempchar2 = String(tempword).charAt(x)
if(tempchar2 != tempchar1 || tempchar2 == 'c' || tempchar2 == 'g'){
rebuilt += tempchar2;
}
tempchar1 = tempchar2;
}
return rebuilt;
}
var isVowel=function(a){
switch(a.toLowerCase())
{
case 'a': return true; break;
case 'e': return true; break;
case 'i': return true; break;
case 'o': return true; break;
case 'u': return true; break;
default: return false; break;
}
};
var tempword = removedbl(word.toLowerCase());
//Special wh- case
if(String(tempword).charAt(0) == 'w' && String(tempword).charAt(1) == 'h'){
// Remove "h" and rebuild the string
tempword = "w" + String(tempword).substr(2);
}
for(x=0; x<wordlength; x++){
tempchar = String(tempword).charAt(x)
if(x == 0 && x+1 <= wordlength){
switch(tempchar){
case 'a': if(String(tempword).charAt(x+1) == 'e'){ metaword += 'e'; }else{metaword += 'a';} break;
case 'e': metaword += 'e'; break;
case 'i': metaword += 'i'; break;
case 'o': metaword += 'o'; break;
case 'u': metaword += 'u'; break;
case 'g': if(String(tempword).charAt(x+1) == 'n'){ x += 1; tempchar = String(tempword).charAt(x);} break;
case 'k': if(String(tempword).charAt(x+1) == 'n'){ x += 1; tempchar = String(tempword).charAt(x);} break;
case 'p': if(String(tempword).charAt(x+1) == 'n'){ x += 1; tempchar = String(tempword).charAt(x);} break;
case 'w': if(String(tempword).charAt(x+1) == 'r'){ x += 1; tempchar = String(tempword).charAt(x); break;} break;
}
}
if(isVowel(tempchar) == false){
switch(tempchar){
case 'b': if(String(tempword).charAt(x-1) == 'm'){ break;}else{metaword += 'b';} break;
case 'c': if(x+1 <= wordlength){
if(String(tempword).charAt(x+1) == 'h' && String(tempword).charAt(x-1) != 's'){ metaword += 'x'; }else if(String(tempword).charAt(x+1) == 'i' && String(tempword).charAt(x+2) == 'a'){ metaword += 'x';}
else if(String(tempword).charAt(x+1) == 'i' || String(tempword).charAt(x+1) == 'e' || String(tempword).charAt(x+1) == 'y'){
if(x > 0){
if(String(tempword).charAt(x-1) == 's'){
break;
}else{
metaword += 's';
}
}else{
metaword += 's';
}
}else{
metaword += 'k';
}
}else{
metaword += 'k';
}
break;
case 'd': if(x+2 <= wordlength){
if(String(tempword).charAt(x+1) == 'g'){
if(String(tempword).charAt(x+2) == 'e' || String(tempword).charAt(x+2) == 'y' || String(tempword).charAt(x+2) == 'i'){
metaword += 'j';
x += 2;
}else{
metaword += 't';
}
}else{
metaword += 't';
}
}else{
metaword += 't';
}
break;
case 'f': metaword += 'f'; break;
case 'g': if(x < wordlength){
if((String(tempword).charAt(x+1) == 'n' && x+1 == wordlength - 1) || (String(tempword).charAt(x+1) == 'n' && String(tempword).charAt(x+2) == 's' && x+2 == wordlength - 1)){break;}
if(String(tempword).charAt(x+1) == 'n' && String(tempword).charAt(x+2) == 'e' && String(tempword).charAt(x+3) == 'd' && x+3 == wordlength - 1){break;}
if(String(tempword).charAt(x-1) == 'n' && String(tempword).charAt(x-2) == 'i' && x == wordlength - 1){break;}
if(String(tempword).charAt(x+1) == 'h' && x+1 <= wordlength-1 && String(tempword).charAt(x-1) == 'u' && String(tempword).charAt(x-2) == 'o'){metaword += 'f';break;}
if(String(tempword).charAt(x+1) == 'h' && x+2 <= wordlength){if(isVowel(String(tempword).charAt(x+2)) == false){break; /*silent*/ }else{metaword += 'k';}}
else if(x+1 == wordlength){if(String(tempword).charAt(x+1) == 'n'){break;}else{metaword += 'k';}}
else if(x+3 == wordlength){if(String(tempword).charAt(x+1) == 'n' && String(tempword).charAt(x+2) == 'e' && String(tempword).charAt(x+3) == 'd'){;}else{metaword += 'k';}}
else if(x+1 <= wordlength){if(String(tempword).charAt(x+1) == 'i' || String(tempword).charAt(x+1) == 'e' || String(tempword).charAt(x+1) == 'y'){
if(String(tempword).charAt(x-1) != 'g'){ metaword += 'j';}
}else if(x > 0){ if(String(tempword).charAt(x-1) == 'd'){
switch(String(tempword).charAt(x+1)){
case 'e':
case 'y':
case 'i': break;
default: metaword += 'k';
}
}else{metaword += 'k';}
}else{metaword += 'k';}
}else{
metaword += 'k';
}
}else{
metaword += 'k';
}
break;
case 'm': metaword += 'm'; break;
case 'j': metaword += 'j'; break;
case 'n': metaword += 'n'; break;
case 'q': metaword += 'k'; break;
case 'r': metaword += 'r'; break;
case 'l': metaword += 'l'; break;
case 'v': metaword += 'f'; break;
case 'z': metaword += 's'; break;
case 'x': if(x == 0){metaword += 's';}else{metaword += 'ks';} break;
case 'm': metaword += 'm'; break;
case 'k': if(x > 0){ if(String(tempword).charAt(x-1) != 'c'){metaword += 'k';}}else{metaword += 'k';} break;
case 'p': if(x+1 <= wordlength){if(String(tempword).charAt(x+1) == 'h'){metaword += 'f';}else{metaword +='p';}}else{metaword += 'p';} break;
case 'y': if(x+1 <= wordlength){if(isVowel(String(tempword).charAt(x+1)) == true){metaword += 'y';}}else{metword += 'y';} break;
case 'h': if(x > 0){ if(isVowel(String(tempword).charAt(x-1)) == true){
if(isVowel(String(tempword).charAt(x+1)) == true){metaword += 'h'; break;}
break;
}
switch(String(tempword).charAt(x-1)){
case 'c':
case 's':
case 'p':
case 't':
case 'g': break;
default: metaword += 'h';
}
}else{
metaword += 'h';
}
break;
case 's': if(x+1 <= wordlength){if(String(tempword).charAt(x+1) == 'h'){ metaword += 'x'; }
else if(x+2 <= wordlength){if(String(tempword).charAt(x+1) == 'i'){if(String(tempword).charAt(x+2) == 'o' || String(tempword).charAt(x+2) == 'a'){metaword += 'x';}else{metaword += 's';}
}else{metaword += 's';}}else{metaword += 's';}}else{metaword += 's';} break;
case 't': if(x+1 <= wordlength){if(String(tempword).charAt(x+1) == 'h'){ metaword += '0'; }
else if(x+2 <= wordlength){if(String(tempword).charAt(x+1) == 'i'){if(String(tempword).charAt(x+2) == 'o' || String(tempword).charAt(x+2) == 'a'){metaword += 'x';}else{metaword += 't';}
}else{metaword += 't';}}else{metaword += 't';}}else{metaword += 't';} break;
case 'w': if(x+1 <= wordlength){if(isVowel(String(tempword).charAt(x+1)) == true){metaword += 'w'}} break;
}
}
}
return metaword;
}
@jhkkhkj: Thanks for the input, but we'd really want it to work like in PHP. Although it's not documented in PHP yet, and it might not be implemented perfectly, I think, based on a quick look at the source, that it should be working now basically as it does in PHP: http://phpjs.org/functions/class_alias
Why the code is not complete... .. ?
a last test...
function version_compare(version1, version2, operator)
{
if (!version1)
return;
if (!version2)
return;
var v1, v2, compare = 0, i = 0, x = 0;
var standardise = function(v) {
v = v.replace(/(^\s*)|(\s*$)/g, "").replace(/[-|_|+]/g,'.').replace(/([^0-9\.]+)/g,'.$1.');
return v.replace(/\.\.*/g,'.').split('.');
};
var versions = {
'dev' : 0,
'alpha' : 1,
'a' : 1,
'beta' : 2,
'b' : 2,
'rc' : 3,
'#' : 4,
'p' : 5,
'pl' : 5
};
v1 = standardise(version1);
v2 = standardise(version2);
while (!v1[0]) { v1.shift(); }
while (!v2[0]) { v2.shift(); }
while (!v1[v1.length-1]) { v1.pop(); }
while (!v2[v2.length-1]) { v2.pop(); }
x = (v1.length > v2.length) ? v2.length : v1.length;
for (i = 0; i < x; i++) {
if (v1[i] == v2[i])
continue;
i1 = v1[i];
i2 = v2[i];
if (!isNaN(i1) && !isNaN(i2)) {
compare = (parseInt(i1) < parseInt(i2)) ? -1 : 1;
break;
}
if (i1 == '#')
i1 = '';
else if (!isNaN(i1))
i1 = '#';
if (i2 == '#')
i2 = '';
else if (!isNaN(i2))
i2 = '#';
i1 = i1.toLowerCase();
i2 = i2.toLowerCase();
if (versions[i1] && versions[i2])
compare = (versions[i1] < versions[i2]) ? -1 : 1;
else if (versions[i1])
compare = 1;
else if (versions[i2])
compare = -1;
else
compare = 0;
break;
}
if (compare == 0) {
if (v2.length > v1.length) {
if (versions[v2[i].toLowerCase()])
compare = (versions[v2[i].toLowerCase()] < 4) ? 1 : -1;
else
compare = -1;
}
else if (v2.length < v1.length) {
if (versions[v1[i].toLowerCase()])
compare = (versions[v1[i].toLowerCase()] < 4) ? -1 : 1;
else
compare = 1;
}
}
if (operator) {
switch (operator) {
case '>':
case 'gt':
return (compare > 0);
case '>=':
case 'ge':
return (compare >= 0);
case '<=':
case 'le':
return (compare <= 0);
case '==':
case '=':
case 'eq':
return (compare == 0);
case '<>':
case '!=':
case 'ne':
return (compare != 0);
case '':
case '<':
case 'lt':
default:
return (compare < 0);
}
}
return compare;
}
if the code is not complete mail me for a complete version... sorry for the flood... .. .
@ tchaOo°
Ô_o
function version_compare(version1, version2, operator)
{
if (!version1)
return;
if (!version2)
return;
var v1, v2, compare = 0, i = 0, x = 0;
var standardise = function(v) {
v = v.replace(/(^\s*)|(\s*$)/g, "").replace(/[-|_|+]/g,'.').replace(/([^0-9\.]+)/g,'.$1.');
return v.replace(/\.\.*/g,'.').split('.');
};
var versions = {
'dev' : 0,
'alpha' : 1,
'a' : 1,
'beta' : 2,
'b' : 2,
'rc' : 3,
'#' : 4,
'p' : 5,
'pl' : 5
};
v1 = standardise(version1);
v2 = standardise(version2);
while (!v1[0]) { v1.shift(); }
while (!v2[0]) { v2.shift(); }
while (!v1[v1.length-1]) { v1.pop(); }
while (!v2[v2.length-1]) { v2.pop(); }
x = (v1.length > v2.length) ? v2.length : v1.length;
for (i = 0; i < x; i++) {
if (v1[i] == v2[i])
continue;
i1 = v1[i];
i2 = v2[i];
if (!isNaN(i1) && !isNaN(i2)) {
compare = (parseInt(i1) < parseInt(i2)) ? -1 : 1;
break;
}
if (i1 == '#')
i1 = '';
else if (!isNaN(i1))
i1 = '#';
if (i2 == '#')
i2 = '';
else if (!isNaN(i2))
i2 = '#';
i1 = i1.toLowerCase();
i2 = i2.toLowerCase();
if (versions[i1] && versions[i2])
compare = (versions[i1] < versions[i2]) ? -1 : 1;
else if (versions[i1])
compare = 1;
else if (versions[i2])
compare = -1;
else
compare = 0;
break;
}
if (compare == 0) {
if (v2.length > v1.length) {
if (versions[v2[i].toLowerCase()])
compare = (versions[v2[i].toLowerCase()] < 4) ? 1 : -1;
else
compare = -1;
}
else if (v2.length < v1.length) {
if (versions[v1[i].toLowerCase()])
compare = (versions[v1[i].toLowerCase()] < 4) ? -1 : 1;
else
compare = 1;
}
}
if (operator) {
switch (operator) {
case '>':
case 'gt':
return (compare > 0);
case '>=':
case 'ge':
return (compare >= 0);
case '<=':
case 'le':
return (compare <= 0);
case '==':
case '=':
case 'eq':
return (compare == 0);
case '<>':
case '!=':
case 'ne':
return (compare != 0);
case '':
case '<':
case 'lt':
default:
return (compare < 0);
}
}
return compare;
}
bye
version_compare() conversion... it's a javascript adaptation of the version_compare() php function found on the Pear php_compat package...
> http://pear.php.net/package/PHP_Compat
function version_compare(version1, version2, operator)
{
if (!version1)
return;
if (!version2)
return;
var v1, v2, compare = 0, i = 0, x = 0;
var standardise = function(v) {
v = v.replace(/(^\s*)|(\s*$)/g, "").replace(/[-|_|+]/g,'.').replace(/([^0-9\.]+)/g,'.$1.');
return v.replace(/\.\.*/g,'.').split('.');
};
var versions = {
'dev' : 0,
'alpha' : 1,
'a' : 1,
'beta' : 2,
'b' : 2,
'rc' : 3,
'#' : 4,
'p' : 5,
'pl' : 5
};
v1 = standardise(version1);
v2 = standardise(version2);
while (!v1[0]) { v1.shift(); }
while (!v2[0]) { v2.shift(); }
while (!v1[v1.length-1]) { v1.pop(); }
while (!v2[v2.length-1]) { v2.pop(); }
x = (v1.length > v2.length) ? v2.length : v1.length;
for (i = 0; i < x; i++) {
if (v1[i] == v2[i])
continue;
i1 = v1[i];
i2 = v2[i];
if (!isNaN(i1) && !isNaN(i2)) {
compare = (parseInt(i1) < parseInt(i2)) ? -1 : 1;
break;
}
if (i1 == '#')
i1 = '';
else if (!isNaN(i1))
i1 = '#';
if (i2 == '#')
i2 = '';
else if (!isNaN(i2))
i2 = '#';
i1 = i1.toLowerCase();
i2 = i2.toLowerCase();
if (versions[i1] && versions[i2])
compare = (versions[i1] < versions[i2]) ? -1 : 1;
else if (versions[i1])
compare = 1;
else if (versions[i2])
compare = -1;
else
compare = 0;
break;
}
if (compare == 0) {
if (v2.length > v1.length) {
if (versions[v2[i].toLowerCase()])
compare = (versions[v2[i].toLowerCase()] < 4) ? 1 : -1;
else
compare = -1;
}
else if (v2.length < v1.length) {
if (versions[v1[i].toLowerCase()])
compare = (versions[v1[i].toLowerCase()] < 4) ? -1 : 1;
else
compare = 1;
}
}
if (operator) {
switch (operator) {
case '>':
case 'gt':
return (compare > 0);
case '>=':
case 'ge':
return (compare >= 0);
case '<=':
case 'le':
return (compare <= 0);
case '==':
case '=':
case 'eq':
return (compare == 0);
case '<>':
case '!=':
case 'ne':
return (compare != 0);
case '':
case '<':
case 'lt':
default:
return (compare < 0);
}
}
return compare;
}
she's work fine...
> http://img20.imageshack.us/my.php?image=testvhw.jpg
Php version credits...
/**
* Replace version_compare()
*
* @category PHP
* @package PHP_Compat
* @license LGPL - http://www.gnu.org/licenses/lgpl.html
* @copyright 2004-2007 Aidan Lister, Arpad Ray
* @link http://php.net/function.version_compare
* @author Philippe Jausions
* @author Aidan Lister
* @version Revision: 1.16
* @since PHP 4.1.0
* @require PHP 4.0.5 (user_error)
*/
cheers
@ tchaOo°
@Theriault, That's awesome to hear. I wasn't looking forward to trying to implement that one. :) I haven't tested your function yet, but wanted to ask if you wouldn't mind testing with http://pastebin.com/m410d37b0 to ensure that we have a somewhat cleaner copy (there was also a debugging alert I commented out, and I changed one use of 'j' to be a new variable 'k' since it seemed like 'k', if on an object, could be a string as we're trying to keep variables to the same type for the sake of declarations/readability (I also wasn't sure about another use of 'j'--line 217 my copy)?, but most of the other changes were for readability, maintainability, and/or early variable "type" declaration--such terse styles may work or save a little space, but can lead to bugs and be more difficult to read/maintain). We're also trying to adhere to http://jslint.com so I'd be most grateful if you could assess whether the remaining gripes of JSLint can be overcome. Also, have you tested with objects? Thank you once again!
I have better smaller and better solutions for ltrim(), rtrim() and trim()
function ltrim(str)
{
while(str.charAt(0)==" ")
str=str.replace(" ","");
return str;
}
function rtrim(str)
{
last=str.length-1;
while(str.charAt(last)==" ")
{
str=str.substring(0,last);
last--;
}
return str;
}
function trim(str)
{
return ltrim(rtrim(str));
}



Boris Fomin
Feb 2nd
function trim(str, charlist) { // Usage: string trim ( string str [, string charlist ] ) // Strip whitespace (or other characters) from the beginning and end of a string // str - the string that will be trimmed // charlist - optionally, the string of stripped characters can also be specified // using the charlist parameter. Simply list all characters that // you want to be stripped. // * example 1: trim(' Hello World '); // * returns 1: 'Hello World' // * example 2: trim('Hello World', 'Hdle'); // * returns 2: 'o Wor' // * example 3: trim(16, 1); // * returns 3: Error('String expected as argument of trim function instead of: ' + str) // * example 4: trim('16', 1); // * returns 4: '16' // * example 5: trim('16', '1'); // * returns 5: '6' 'use strict'; var s = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'; if (typeof (charlist) === 'string') { s = charlist; } if (typeof (str) === 'string') { while (((s.indexOf(str.charAt(0))) > -1) && (str !== '')) { str = str.substring(1); } while (((s.indexOf(str.charAt(str.length - 1))) > -1) && (str !== '')) { str = str.substring(0, str.length - 1); } return str; } else { throw new Error('String expected as argument of trim function instead of: ' + str); } };