/* sprintf, v1.1 November 13, 2004 (c) 2003 Nate Cook Get (sparse) documentation and version updates at: http://www.natecook.com/downloads/ This is an Actionscript version of the sprintf function, as based mostly on documentation in the printf(3) man page on OS X - another version is found at: http://developer.apple.com/documentation/Darwin/Reference/ManPages/html/printf.3.html Support for all elements is not entirely in place, mostly because the translation from C to a scripting language like Actionscript loses some pieces by default, like pointer references and all the AltiVec stuff mentioned on the above page. If you think there's something that could be added that isn't supported, let me know. This file may be distributed and used in any projects (commercial or not) as long as it is unaltered and this notice is intact. I'd love to hear from you with feedback, about bugs, ideas for further features, etc. Thanks! USAGE: sprintf(format, [args...]) implementation of c-style string formatting tracef(format, [args...]) shortcut to trace a formatted string ERRORS: By default all errors are silently ignored (since that's what makes sense to me). To see error messages in trace output, use this line: sprintf_TRACE = true; To see error messages in the result output, use this line: sprintf_DEBUG = true; VERSION HISTORY v1.1 Changed file name, other changes for AS2.0 compatibility v1.0 Initial release. */ // -=-=-=-=-=-=-=-=-=-=-=-=-=-=- // globally accessible routines // -=-=-=-=-=-=-=-=-=-=-=-=-=-=- _global.sprintf = function(format) { if (format == null) return ''; var destString = ''; var argIndex = 0; // our place in arguments[] var formatIndex = 0; // our place in the format string var percentIndex; // the location of the next '%' delimiter var ch; // -=-=-=-=-=-=-=-=-=-=-=-=-=- vars for dealing with each field var value, length, precision; var properties; // options: left justified, zero padding, etc... var fieldCount; // tracks number of sections in field var fieldOutcome; // when set to true, field parsed successfully // when set to a string, error resulted while (formatIndex < format.length) { percentIndex = format.indexOf('%',formatIndex); if (percentIndex == -1) { destString += format.substr(formatIndex); formatIndex = format.length; } else { destString += format.substring(formatIndex,percentIndex); fieldOutcome = '** sprintf: invalid format at ' + argIndex + ' **'; length = properties = fieldCount = 0; precision = -1; formatIndex = percentIndex + 1; value = arguments[++argIndex]; while (!fieldOutcome && (formatIndex < format.length)) { ch = format.charAt(formatIndex++); switch (ch) { // -=-=-=-=-=-=-=-=-=-=-=-=-=- // pre-processing items // -=-=-=-=-=-=-=-=-=-=-=-=-=- case '#': if (fieldCount == 0) { properties |= sprintf.kALT_FORM; } else { fieldOutcome = '** sprintf: "#" came too late **'; } break; case '-': if (fieldCount == 0) { properties |= sprintf.kLEFT_ALIGN; } else { fieldOutcome = '** sprintf: "-" came too late **'; } break; case '+': if (fieldCount == 0) { properties |= sprintf.kSHOW_SIGN; } else { fieldOutcome = '** sprintf: "+" came too late **'; } break; case ' ': if (fieldCount == 0) { properties |= sprintf.kPAD_POS; } else { fieldOutcome = '** sprintf: " " came too late **'; } break; case '.': if (fieldCount < 2) { fieldCount = 2; precision = 0; } else { fieldOutcome = '** sprintf: "." came too late **'; } break; /* case 'h': if (fieldCount < 3) { fieldCount = 3; } else { fieldOutcome = '** sprintf: must have only one of h,l,L **'; } break; case 'l': case 'L': if (fieldCount < 3) { fieldCount = 3; properties |= sprintf.kLONG_VALUE; } else { fieldOutcome = '** sprintf: must have only one of h,l,L **'; } break; */ case '0': if (fieldCount == 0) { properties |= sprintf.kPAD_ZEROES; break; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (fieldCount == 3) { fieldOutcome = '** sprintf: shouldn\'t have a digit after h,l,L **'; } else if (fieldCount < 2) { fieldCount = 1; length = (length * 10) + Number(ch); } else { precision = (precision * 10) + Number(ch); } break; // -=-=-=-=-=-=-=-=-=-=-=-=-=- // conversion specifiers // -=-=-=-=-=-=-=-=-=-=-=-=-=- case 'd': case 'i': fieldOutcome = true; destString += sprintf.formatD(value,properties,length,precision); break; case 'o': fieldOutcome = true; destString += sprintf.formatO(value,properties,length,precision); break; case 'x': case 'X': fieldOutcome = true; destString += sprintf.formatX(value,properties,length,precision,(ch == 'X')); break; case 'e': case 'E': fieldOutcome = true; destString += sprintf.formatE(value,properties,length,precision,(ch == 'E')); break; case 'f': fieldOutcome = true; destString += sprintf.formatF(value,properties,length,precision); break; case 'g': case 'G': fieldOutcome = true; destString += sprintf.formatG(value,properties,length,precision,(ch == 'G')); break; case 'c': case 'C': precision = 1; case 's': case 'S': fieldOutcome = true; destString += sprintf.formatS(value,properties,length,precision); break; case '%': fieldOutcome = true; destString += '%'; // we don't need a value for this, so back up argIndex--; break; default: fieldOutcome = '** sprintf: ' + ch + ' not supported **'; break; } } if (fieldOutcome != true) { if (sprintf_DEBUG) destString += fieldOutcome; if (sprintf_TRACE) ((ctrace != undefined) ? ctrace(fieldOutcome) : trace(fieldOutcome)); } } } return destString; } _global.tracef = function() { trace(sprintf.apply(this,arguments)); } // -=-=-=-=-=-=-=-=-=-=-=-=-=- // "constants" // -=-=-=-=-=-=-=-=-=-=-=-=-=- sprintf.kPAD_ZEROES = 0x01; sprintf.kLEFT_ALIGN = 0x02; sprintf.kSHOW_SIGN = 0x04; sprintf.kPAD_POS = 0x08; sprintf.kALT_FORM = 0x10; sprintf.kLONG_VALUE = 0x20; sprintf.kUSE_SEPARATOR = 0x40; sprintf.sprintf_DEBUG = false; sprintf.sprintf_TRACE = false; // -=-=-=-=-=-=-=-=-=-=-=-=-=- // formatting functions // -=-=-=-=-=-=-=-=-=-=-=-=-=- sprintf.finish = function(output,value,properties,length,precision,prefix) { if (prefix == null) prefix = ''; // add sign to prefix if (value < 0) { prefix = '-' + prefix; } else if (properties & sprintf.kSHOW_SIGN) { prefix = '+' + prefix; } else if (properties & sprintf.kPAD_POS) { prefix = ' ' + prefix; } if ((length == 0) && (precision > -1)) { length = precision; properties |= sprintf.kPAD_ZEROES; } // add padding while (output.length + prefix.length < length) { if (properties & sprintf.kLEFT_ALIGN) { output = output + ' '; } else if (properties & sprintf.kPAD_ZEROES) { output = '0' + output; } else { prefix = ' ' + prefix; } } return prefix + output; } // integer sprintf.formatD = function(value,properties,length,precision) { var output = ''; value = Number(value); if ((precision != 0) || (value != 0)) { output = String(Math.floor(Math.abs(value))); } while (output.length < precision) { output = '0' + output; } return sprintf.finish(output,value,properties,length,precision); } // octal sprintf.formatO = function(value,properties,length,precision) { var output = ''; var prefix = ''; value = Number(value); if ((precision != 0) && (value != 0)) { output = value.toString(8); } if (properties & sprintf.kALT_FORM) { prefix = '0'; } while (output.length < precision) { output = '0' + output; } return sprintf.finish(output,value,properties,length,precision,prefix); } // hexidecimal sprintf.formatX = function(value,properties,length,precision,upper) { var output = ''; var prefix = ''; value = Number(value); if ((precision != 0) && (value != 0)) { output = value.toString(16); } if (properties & sprintf.kALT_FORM) { prefix = '0x'; } while (output.length < precision) { output = '0' + output; } if (upper) { prefix = prefix.toUpperCase(); output = output.toUpperCase(); } else { output = output.toLowerCase(); // Flash documentation isn't clear about what case the Number.toString() method uses } return sprintf.finish(output,value,properties,length,precision,prefix); } // scientific notation sprintf.formatE = function(value,properties,length,precision,upper) { var output = ''; var expCount = 0; value = Number(value); if (Math.abs(value) > 1) { while (Math.abs(value) > 10) { value /= 10; expCount++; } } else { while (Math.abs(value) < 1) { value *= 10; expCount--; } } expCount = sprintf('%c%+.2d',(upper ? 'E' : 'e'),expCount); if (properties & sprintf.kLEFT_ALIGN) { // give small length output = sprintf.formatF(value,properties,1,precision) + expCount; while (output.length < length) { output += ' '; } } else { output = sprintf.formatF(value,properties,Math.max(length - expCount.length,0),precision) + expCount; } return output; } // float (or real) sprintf.formatF = function(value,properties,length,precision) { var output = ''; var intPortion = ''; var decPortion = ''; var valString = ''; // unspecified precision defaults to 6 if (precision == -1) { precision = 6; } valString = new String(value); if (valString.indexOf('.') == -1) { intPortion = (Math.abs(Number(value))).toString(); decPortion = 0; } else { intPortion = (Math.abs(Number(valString.substring(0,valString.indexOf('.'))))).toString(); decPortion = valString.substr(valString.indexOf('.') + 1); } // create decimal portion if (Number(decPortion) == 0) { decPortion = new String(); while (decPortion.length < precision) decPortion += '0'; } else { if (decPortion.length > precision) { var dec = Math.round(Math.pow(10,precision) * Number('0.' + decPortion)); if (((String(dec)).length > precision) && (dec != 0)) { decPortion = '0'; intPortion = ((Math.abs(Number(intPortion)) + 1) * (Number(intPortion) >= 0 ? 1 : -1)).toString(); } else { decPortion = new String(dec); } } if (decPortion.length < precision) { decPortion = new String(decPortion); while (decPortion.length < precision) decPortion += '0'; } } // combine pieces if (precision == 0) { output = intPortion; if (properties & sprintf.kALT_FORM) { output += '.'; } } else { output = intPortion + '.' + decPortion; } return sprintf.finish(output,value,properties,length,precision,''); } // shorter of float or scientific sprintf.formatG = function(value,properties,length,precision,upper) { // use 1 as the length for the test because the // padded value will be the same -> not useful var out1 = sprintf.formatE(value,properties,1,precision,upper); var out2 = sprintf.formatF(value,properties,1,precision); if (out1.length < out2.length) { return sprintf.formatE(value,properties,length,precision,upper); } else { return sprintf.formatF(value,properties,length,precision); } } // string sprintf.formatS = function(value,properties,length,precision) { var output = new String(value); if ((precision > 0) && (precision < output.length)) { output = output.substring(0,precision); } // ignore unneeded flags properties &= ~(sprintf.kPAD_ZEROES | sprintf.kSHOW_SIGN | sprintf.kPAD_POS | sprintf.kALT_FORM); return sprintf.finish(output,value,properties,length,precision,''); }