Add Function
No expertise needed. Just JS Coding skills. We will review the code and make the changes in SVN. 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
@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!
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110111 112 113 114 115116 117 118 119 120121 122 123 124 125126 127 128 129 130131 132 133 134 135136 137 138 139 140141 142 143 144 145146 147 148 149 150151 152 153 154 155156 157 158 159 160161 162 163 164 165166 167 168 169 170171 172 173 174 175176 177 178 179 180181 182 183 184 185186 187 188 189 190191 192 193 194 195196 197 198 199 200201 202 203 204 205206 207 208 209 210211 | 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.
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 | 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.
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 | 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)
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 | <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.
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 | 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:
1 | 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:
1 2 | 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:
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 | 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.
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 | 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.
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 35 | 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:
1 | 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.
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110111 112 113 114 115116 117 118 119 120121 122 123 124 125126 127 128 129 130131 132 133 134 135136 137 138 139 140141 142 143 144 145146 147 148 149 150151 152 153 154 155156 157 158 159 160161 | 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...
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110 | 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
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110 | 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
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110 | 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...
1 2 3 4 56 7 8 9 1011 12 13 14 | /** * Replace version_compare() * * @category PHP * @package PHP_Compat * @license LGPL - http://www.gnu.org/licenses/lgpl.html * @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net> * @link http://php.net/function.version_compare * @author Philippe Jausions <Philippe.Jausions@11abacus.com> * @author Aidan Lister <aidan@php.net> * @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()
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 20 | 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)); } |
@ Brett Zamir: Not really PHP-like, but I don't see how it could conflict either. And I do see how this could be really helpful for JS developers. So yes, I'm cool with that.
That looks cool--but could we add one enhancement to allow an array of string options? If one is using the string syntax, it could be convenient to be add flags easily in this way (and see what it is supposed to do without defining constants or passing integers)...
@ Brett Zamir & Francis Lewis: Good job guys, keep it up!
Considering constant / bitwise arguments, I really like the function pathinfo to be an example for how to implement this cause it has full compatibility.
For example, it allows combining multiple arguments just like in PHP.
Checkout the notes of the function for further clarification:
http://phpjs.org/functions/pathinfo:486
Oh, as you can see, we've generally implemented constants as the equivalent capitalized strings. However, I think we should support them also as integers (and optionally define all of the constants in the package), especially if bitwise-or'ed to include multiple flags (for strings, we could just allow the user to pass the strings in an array to represent multiple flags for a given argument).
I don't have time to work on it now, but here's a skeleton based on your code for implementing all of the aspects of preg_match. Note that the return value should be an integer (for preg_match_all(), the integer can be higher than 0, but not for preg_match).
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 | function preg_match(pattern, subject, matches, flags, offset) { // http://kevin.vanzonneveld.net // + original by: Francis Lewis // * example 1: matches = []; // * example 1: preg_match(/(\w+)\W([\W\w]+)/, 'this is some text', matches); // * matches 1: matches[1] == 'this' var i, array = [], regexpFlags=''; if (typeof pattern === 'string') { /* // Potentially useful configuration if (/a-zA-Z/.test(pattern[0]) || pattern.length === 1) { // The user is probably not using letters for delimiters (not recommended, but could be convenient for non-flagged expressions) pattern = new RegExp(pattern); } else { */ var lastDelimPos = pattern.lastIndexOf(pattern[0]); if (lastDelimPos === 0) { throw 'Improperly formed regular expression passed to '+arguments.callee.name; } var patternPart = pattern.slice(1, lastDelimPos); var flagPart = pattern.slice(lastDelimPos+1); for (i=0; i < flagPart.length; i++) { switch(flagPart[i]) { case 'g': // We don't use this in preg_match, but it's presumably not an error case 'm': case 'i': regexpFlags += flagPart[i]; break; case 's': case 'x': case 'e': case 'A': case 'D': case 'S': case 'U': case 'X': case 'J': case 'u': throw 'The passed flag(s) are presently unsupported in '+arguments.callee.name; break; case 'y': throw 'Flag "y" is a non-cross-browser, non-PHP flag, not supported in '+arguments.callee.name; break; default: throw 'Unrecognized flag passed to '+arguments.callee.name; break; } } patternPart = patternPart.replace(/\(\?<(.*)\)/g, function (namedSubpattern, name) { return namedSubpattern; // Not implemented }); pattern = new RegExp(patternPart, regexpFlags); // } } // store the matches in the first indice of the array array[0] = pattern.exec(subject); if (!array[0]) { return 0; } // If the user passed in a RegExp object or literal, we will probably need to reflect on // its source, ignoreCase, global, and multiline properties to form a new expression, and // and use lastIndex if (offset) { // Not implemented } if (flags === 'PREG_OFFSET_CAPTURE') { // Not implemented return 1; // matches will need to be different, so we return early here } // loop through the first indice of the array and store the values in the $matches array for (i = 0; i < array[0].length; i++) { matches[i] = array[0][i]; } return 1;} |
It doesn't appear that my previous comment was added here, so trying it again.
Fix - Fixed an issue where array[0] could contain no result if no matches were found and throws an error
Change - Added return true/return false - since this is how the php function works.
Change - Formatted according to the phpjs coding guidelines
Change - Provided a more dynamic example.
Notes: the only items missing at this point are Flags and Offset
The only flag that can be passed in the php version is: PREG_OFFSET_CAPTURE
Please let me know if anybody has an idea how to pass the flag since it's a constant in php... I can only think of passing true/false in that parameter at the moment, but then it wouldn't be true to the php function. Not sure what kind of issues that could bring.
Offset would be a simple use of substr, so substr would be a dependant for this function when that is added
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 | function preg_match(regex, input, result) { // + original by: Francis Lewis (http://startrekguide.com) // * example 1: result = []; // * example 1: preg_match(/[\d-]{2}?(\d{3})-(\d{3})-(\d{4})[\W+]?([\W\w]+)?/, '1-800-123-4567 fax', result); // * results 1: result[1] == '800' // * results 1: result[2] == '123' // * results 1: result[3] == '4567' // * results 1: result[4] == 'fax' var i; var array = {}; // store the result in the first indice of the array array[0] = regex.exec(input); // make sure the array contains any value if (!array[0]) { return false; } // loop through the first indice of the array and store the values in the $result array for (i = 0; i < array[0].length; i++) { result[i] = array[0][i]; } // we will always have a result at this point, so return true return true; } |



Marco Marchiò
17 Dec '09