/* * This file is part of HOAM, copyright (C) 2002-2009 Robert D Butler Jr. * * HOAM is free software; you can redistribute it and/or modify it under the * terms of the GNU Affero General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * * HOAM is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with HOAM; if not, see http://www.gnu.org/licenses or write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 * * Questions specific to HOAM should be directed to HOA Management. Please see * the HOAM web site at http://hoam.hoa-management.com/ * * Some portions of HOAM incorporate ideas and/or code from other sources, and * those portions are explicitly mentioned and attributed in the relevant * section of HOAM source code. Questions about that code should be directed to * the original authors. * */ /* global variables */ var AjaxGlobalHandlers = { onCreate: HOAM_serverCommunicationBegin, onException: HOAM_serverCommunicationError, onLoading: HOAM_serverCommunicationLoading, onComplete: HOAM_serverCommunicationEnd }; Ajax.Responders.register (AjaxGlobalHandlers); var ZebraTable = { // Stolen from http://www.thewatchmakerproject.com/zebra.html // A few modifications, though... class_alternate: 'alternate-row', class_hover: '', stripe: function (el) { if (!$(el)) return; var rows = $(el).getElementsByTagName('tr'); for (var i = 0, len = rows.length; i < len; i++) { // Added the .extend for f'ing IE Element.extend (rows[i]); if (i % 2 == 0) rows[i].addClassName (this.class_alternate); Event.observe (rows[i], 'mouseover', function() { ZebraTable.mouseover(this); }, false); Event.observe (rows[i], 'mouseout', function() { ZebraTable.mouseout(this); }, false); } }, mouseover: function (row) { row.addClassName (this.class_hover); }, mouseout: function (row) { row.removeClassName (this.class_hover); } } function HOAM_sentenceFormat (string) { // Should be made smarter, just needed something quick. string = string.toLowerCase(); string = string.substring(0,1).toUpperCase() + string.substring (1, string.length); return (string); } function HOAM_countryLookup (messagePath) { var languageString; var url = '/scripts/help/countryLookup.php'; // var pars = 'string=' + messagePath.replace (/_/g, '/'); pars = 'string=' + messagePath; // Note, this call is being done synchronously, which could potentially // cause problems. However, it's done this way because the function was // returning before the request completed. var myAjax = new Ajax.Request (url, { method: 'get', asynchronous: false, parameters: pars, onComplete: function (originalRequest) { languageString = originalRequest.responseText; } }); return (languageString); } function HOAM_verifyDate (event) { if ((Event.element(event).value.length == 0)) { HOAM_formErrorClear (Event.element(event).getAttribute ('name')); } else { if ((Event.element(event).value.length > 0) && (Event.element(event).value.length < Event.element(event).getAttribute ('minlength'))) { HOAM_formErrorMessage (Event.element(event).getAttribute ('name'), HOAM_languageLookup ('errors|generic|invalid-date', Event.element(event).getAttribute ('minlength'))); } else { // I don't know why, but the string read in through the config lookup isn't // matching correctly, even though it's the same regex. // if (Event.element(event).value.match (HOAM_countryLookup ('date|match'))) { if (Event.element(event).value.match (/^([01]\d)[\-\.\/]([0-3]\d)[\-\.\/](19\d{2}|20\d{2})$/)) { HOAM_formErrorClear (Event.element(event).getAttribute ('name')); Event.element(event).value = Event.element(event).value.replace (/^([01]\d)[\-\.\/]([0-3]\d)[\-\.\/](19\d{2}|20\d{2})$/, '$1/$2/$3'); } else { HOAM_formErrorMessage (Event.element(event).getAttribute ('name'), HOAM_languageLookup ('errors|generic|invalid-date')); } } } } function HOAM_verifyPostalcode (event) { if (Event.element(event).value.length < Event.element(event).getAttribute ('minlength')) { HOAM_formErrorMessage (Event.element(event).getAttribute('name'), HOAM_languageLookup ('errors|generic|postalcode-too-short', Event.element(event).getAttribute ('minlength'))); } else { // if (Event.element(event).value.match (HOAM_countryLookup ('postalcode|match-js'))) { if (Event.element(event).value.match (/^(\d{5})$|^(\d{5})\-?(\d{4})$/)) { HOAM_formErrorClear (Event.element(event).getAttribute ('name')); // Would like to do some auto formatting, this isn't working // currently // Event.element(event).value = Event.element(event).value.replace (/^(\d{5})$|^(\d{5})\-?(\d{4})$/, '$1-$2'); } else { HOAM_formErrorMessage (Event.element(event).getAttribute ('name'), HOAM_languageLookup ('errors|generic|invalid-postalcode')); } } } function HOAM_verifyTelephone (event) { if ((Event.element(event).value.length == 0)) { HOAM_formErrorClear (Event.element(event).getAttribute ('name')); } else { if ((Event.element(event).value.length > 0) && (Event.element(event).value.length < Event.element(event).getAttribute ('minlength'))) { HOAM_formErrorMessage (Event.element(event).getAttribute ('name'), HOAM_languageLookup ('errors|generic|telephone-too-short', Event.element(event).getAttribute ('minlength'))); } else { // I don't know why, but the string read in through the config lookup isn't // matching correctly, even though it's the same regex. // if (Event.element(event).value.match (HOAM_countryLookup ('telephone|match'))) { if (Event.element(event).value.match (/^\(?([2-9]\d{2})[\)\-]?\s?(\d{3})\-?(\d{4})$/)) { HOAM_formErrorClear (Event.element(event).getAttribute ('name')); Event.element(event).value = Event.element(event).value.replace (/^\(?([2-9]\d{2})[\)\-]?\s?(\d{3})\-?(\d{4})$/, '($1) $2-$3'); } else { HOAM_formErrorMessage (Event.element(event).getAttribute ('name'), HOAM_languageLookup ('errors|generic|invalid-telephone')); } } } } function HOAM_languageLookup (messagePath, param1, param2) { var languageString; var url = '/scripts/help/languageLookup.php'; // var pars = 'string=' + messagePath.replace (/_/g, '/'); pars = 'string=' + messagePath; pars = pars + '¶m1=' + param1 + '¶m2=' + param2; // Note, this call is being done synchronously, which could potentially // cause problems. However, it's done this way because the function was // returning before the request completed. var myAjax = new Ajax.Request (url, { method: 'get', asynchronous: false, parameters: pars, onComplete: function (originalRequest) { languageString = originalRequest.responseText; } }); return (languageString); } function HOAM_languageLookupAsync (messagePath, param1, param2) { var languageString; var url = '/scripts/help/languageLookup.php'; // var pars = 'string=' + messagePath.replace (/_/g, '/'); pars = 'string=' + messagePath; pars = pars + '¶m1=' + param1 + '¶m2=' + param2; var myAjax = new Ajax.Request (url, { method: 'get', asynchronous: true, parameters: pars, onComplete: function (originalRequest) { alert (originalRequest.responseText); languageString = originalRequest.responseText; } }); return (languageString); } function HOAM_returnNumberCurrencyFormat (amount, show_sign) { // From (with modifications) http://javascript.internet.com/forms/currency-format.html // If we didn't receive anything, then just exit; we want a blank response. amount = amount.toString().replace('/\$|\,/g', ''); if (isNaN (amount)) { amount = "0"; } sign = (amount == (amount = Math.abs(amount))); amount = Math.floor(amount * 100 + 0.50000000001); cents = amount % 100; amount = Math.floor(amount / 100).toString(); if (cents < 10) { cents = "0" + cents; } for (var i = 0; i < Math.floor((amount.length - (1 + i)) / 3); i++) { amount = amount.substring(0, amount.length - (4 * i + 3)) + ',' + amount.substring(amount.length - (4 * i + 3)); } if (show_sign) { return (((sign) ? '' : '-') + amount + '.' + cents); } else { return (amount + '.' + cents); } } function HOAM_returnCleanFloat (obj) { return parseFloat ((obj).replace (/,/, '')) } function HOAM_serverCommunicationBegin () { // Display notice to end-user that communciation is occuring with the server // ie, activity is taking place var working = document.createElement ('DIV'); if ($('logo')) { var header = $('logo'); } else { var header = $('page_logo'); } var first = header.firstChild; working.id = 'server-communication'; working.className = 'server-communication'; working.innerHTML = ' Connecting '; header.insertBefore (working, first); } function HOAM_serverCommunicationLoading () { // Update the notice to show communication is occuring var working = $('server-communication'); working.innerHTML = ' Loading '; } function HOAM_serverCommunicationError () { // Update the notice to show communication is occuring var working = $('server-communication'); working.innerHTML = ' ** Error **'; } function HOAM_serverCommunicationEnd () { // Remove notice to end-user tha communication is occuring with the server // ie, all activity has ceased var working = $('server-communication'); if ($('logo')) { var header = $('logo'); } else { var header = $('page_logo'); } header.removeChild (working); } function HOAM_tabLoading (container) { if (container) { var old_contents = container.descendants (); for (var i = 0; i < old_contents.length; i++) { Element.remove (old_contents[i]); } var div = document.createElement ('DIV'); div.className = 'center'; var img = document.createElement ('IMG'); img.setAttribute ('src', '/images/icons/throbber.gif'); img.setAttribute ('alt', 'Progress animation'); var message = document.createElement ('DIV'); message.className = 'center'; message.innerHTML = 'Loading …'; div.appendChild (img); div.appendChild (message); container.appendChild (div); } } function HOAM_textareaDecreaseRows (event, divisor) { // Decreases the total number of rows in a textarea by multiplier // Switch the button to increaseRows, this provides a toggle effect. Event.stopObserving (Event.element(event), 'click', HOAM_textareaDecreaseRows); Event.observe (Event.element(event), 'click', HOAM_textareaIncreaseRows); // Find the textarea. This will break if the textarea item is not // the next node after the expander var obj = Event.element(event).nextSibling; if (!divisor) divisor = 2; var rows = obj.getAttribute ("rows"); obj.setAttribute ("rows", rows / divisor); } function HOAM_textareaIncreaseRows (event, multiplier) { // Increases the total number of rows in a textarea by multiplier // Switch the button to decreaseRows, this provides a toggle effect. Event.stopObserving (Event.element(event), 'click', HOAM_textareaIncreaseRows); Event.observe (Event.element(event), 'click', HOAM_textareaDecreaseRows); // Find the textarea. This will break if the textarea item is not // the next node after the expander var obj = Event.element(event).nextSibling; if (!multiplier) multiplier = 2; var rows = obj.getAttribute ("rows"); if (!rows) rows = 3; obj.setAttribute ("rows", rows * multiplier); } function HOAM_textareaExpander () { // Locate any textareas on the page, and allow them to be expanded / contracted textarea = document.getElementsByTagName ("TEXTAREA"); for (var i = 0; i < textarea.length; i++) { var parent = textarea[i].parentNode; var toggleswitch = document.createElement ("SPAN"); toggleswitch.className = "textareaExpander"; toggleswitch.innerHTML = HOAM_languageLookup ('field_names|textarea-expander'); Event.observe (toggleswitch, 'click', HOAM_textareaIncreaseRows); toggleswitch.title = HOAM_languageLookup ('titles|textarea-expander'); parent.insertBefore (toggleswitch, textarea[i]); } } function HOAM_tableStripe () { // Locate any tables on the page and stripe alternate rows // NOTE! Only touches tbody's, not tables themselves. This may change in // the future if I have time. // NOTE! If you do _NOT_ want a table striped, add the class 'nostripe' to // the tbody. tables = document.getElementsByTagName ('TBODY'); for (var i = 0; i < tables.length; i++) { // Added the .extend for f'ing IE Element.extend (tables[i]); if (!tables[i].hasClassName ('nostripe')) { ZebraTable.stripe (tables[i]); } } } function HOAM_userLoginCheck (event) { if (($F('user_loginName') == '') || ($F('user_loginName') == 'user name')) { alert (HOAM_languageLookup ('errors|user|accountname|required')); $('user_loginName').focus(); Event.stop (event); } if (!$F('user_loginPassword')) { alert (HOAM_languageLookup ('errors|user|password|required')); $('user_loginPassword').focus(); Event.stop (event); } } function getElementTextNS (prefix, local, parentElem, index) { // Based on Apple's example at http://developer.apple.com/internet/webcontent/xmlhttpreq.html // retrieve text of an XML document element, including elements using namespaces var result = ""; if (prefix) { // IE/Windows way of handling namespaces result = parentElem.getElementsByTagName (prefix + ":" + local)[index]; } else { // the namespace versions of this method // (getElementsByTagNameNS()) operate // differently in Safari and Mozilla, but both // return value with just local name, provided // there aren't conflicts with non-namespace element // names result = parentElem.getElementsByTagName(local)[index]; } if (result) { // get text, accounting for possible // whitespace (carriage return) text nodes if (result.childNodes.length > 1) { return result.childNodes[1].nodeValue; } else { if (result.childNodes.length == 1) { return result.firstChild.nodeValue; } else { return result.nodeValue; } } } else { return "n/a"; } } function leadingZero (number) { // Shamelessly stolen from http://www.quirksmode.org/js/date.html if (number < 10) { number = "0" + number; } return number; } function binarySearch (array, find, caseInsensitive, getSubstring, arrayCheckThisIndex) { if (!array || typeof (array) != "object" || typeof (find) == "undefined" || !array.length) { return null; } find = (!caseInsensitive) ? find : find.toLowerCase(); var low = 0; var high = array.length - 1; var highOnTop = (array[0] > array[array.length - 1]) ? 1 : 0; while (low <= high) { var aTry = parseInt((low + high) / 2); var checkThis = (typeof (arrayCheckThisIndex) == "undefined") ? array[aTry] : array[aTry][arrayCheckThisIndex]; checkThis = (!caseInsensitive) ? checkThis : checkThis.toLowerCase(); checkThis = (!getSubstring) ? checkThis : checkThis.substring(0, find.length); if (!highOnTop) { if (checkThis < find) { low = aTry + 1; continue; } if (checkThis > find) { high = aTry - 1; continue; } } else { if (checkThis > find) { low = aTry + 1; continue; } if (checkThis < find) { high = aTry - 1; continue; } } return aTry; } return null; } function HOAM_moveOption (form, source, dest) { // Believe this was originally Matt Kruse's /* Moves selected OPTION elements from one SELECT control to another. * * form - A reference to the containing form, * or a string containing the name or id. * source - The name or id of the source SELECT element. * dest - The name or id of the destination SELECT element. */ if (typeof form == 'string') { form = $(form); } if (form && 'object' == typeof form && this['Option']) { dest = $(dest); source = $(source); if (dest && source && 'object' == typeof dest && 'object' == typeof source) { var c, i = 0, j = 0, dO = dest.options, sO = source.options, t; while (i < sO.length) { c = sO[i++]; t = new Option (c.text, c.value); if (c.selected) { dO[dO.length] = t; } else { sO[j++] = t; } } sO.length = j; } } } function appendToSelectList (select, value, content) { // add item to select element the less elegant, but compatible way. var option = document.createElement ("option"); option.value = value; option.appendChild (content); select.appendChild (option); } function clearSelectList (id) { // empty select list content var select = document.getElementById (id); while (select.length > 0) { select.remove (0); } } function getSelectIndex (id, value) { // find out the select option index of the value we have var select = document.getElementById (id); // Make sure we have something to do... if (select.length > 0) { var i = 0; while (i < select.length) { if (select.childNodes[i].value == value) { return (i); } i++ } // fail out and return the default value return -1; } } function include (filename) { // From http://forums.digitalpoint.com/showthread.php?t=146094 // // Be aware of the limitation that this function will place the included // file at the end of the hard-coded head. This means that any code // dependent on the incuded file must be listed after the file // just included (typically meaning it must be included also). var head = document.getElementsByTagName ('head')[0]; script = document.createElement ('script'); script.src = filename; script.type = 'text/javascript'; head.appendChild (script) } function moreFields (source_elem, dest_elem) { // Stolen from the excellent http://www.quirksmode.org/ site // Modified, of course if (!source_elem) source_elem = $('readroot'); if (!dest_elem) dest_elem = $('writeroot'); // We're using a non-standard attribute to store a temporary counter of how many 'new' fields we've added var counter = parseInt (source_elem.parentNode.getAttribute ('ref')) + 1; if (isNaN (counter)) { counter = 1; } var newFields = source_elem.cloneNode (true); newFields.id = ''; newFields.style.display = 'block'; var newField = newFields.childNodes; for (var i = 0; i < newField.length; i++) { var theName = newField[i].name if (theName) newField[i].name = theName + counter; } // Save the counter. source_elem.parentNode.setAttribute ('ref', counter); dest_elem.parentNode.insertBefore (newFields, dest_elem); } function removeAllChildren (id) { i = 0; var object = document.getElementById (id); while (i < object.childNodes.length) { object.removeChild (object.childNodes[0]); } } function removeLastChild (id) { // Remove the last child (if present) var object = document.getElementById (id).lastChild; if (object) { document.getElementById (id).removeChild (object); } } function replaceTextNode (id, text) { var element = document.getElementById (id); element.value = text; } function setSelectIndex (id, value) { // find out the select option index of the value we have var select = document.getElementById (id); // Make sure we have something to do... for (var i = 0; i < select.length; i++) { if (select[i].value == value) { select.selectedIndex = i; return i; } } } function sleep (time) { // wait (time) millisecs var then, now; then = new Date ().getTime (); now = then; while ((now - then) < time) { now = new Date ().getTime (); } } function setTextNode (id, text) { var node = document.createTextNode (text); document.getElementById (id).appendChild (node); } function ToggleDisplay (id) { // DEPRECATED. any usage needs to be moved to the prototype equivlent. var element = document.getElementById (id); if (element.style.display == 'none') { if (document.all) { element.style.display = 'block'; } else{ element.style.display = ''; } } else { element.style.display = 'none'; } } function setSelect (select, property) { var found = false; for (var i = 0; i < select.options.length; i++) { if (select.options[i][property].toUpperCase().indexOf(field.value.toUpperCase()) == 0) { found = true; break; } } if (found) { select.selectedIndex = i; } else { select.selectedIndex = -1; } } function setSelectAll (select) { var select = document.getElementById (select); for (var i = 0; i < select.options.length; i++) { select.options[i].selected = true; } } /**********************************************************************\ * The JavaScript code in this section was written by: * * Rahul Mittal * * Sophomore, Dept. of Astronomy, Villanova University * * Feel free to use the code below provided this header remains intact! * * Drop me e-mail at rasteroid@hotmail.com if you want, and * * that way I can check out your homepage... :) * * Corrected Y2K bug October 3, 2003 (JD 2452915) by Adric Riedel \**********************************************************************/ function calculateJD(calendarDate) { cdDate = new Date(calendarDate) year = cdDate.getYear() //added + 1900 10/03/2003 if (year < 1000 ) {year+=1900} // modified 10/03/2003 //year = 1900+cdDate.getYear() month = cdDate.getMonth() + 1 //getMonth() returns 0-11 day = cdDate.getDate() hour = cdDate.getHours() min = cdDate.getMinutes() sec = cdDate.getSeconds() univTime = hour+(min/60)+(sec/3600) if ((100*year+month-190002.5) >= 0) {sign = 1} else {sign = -1} with (Math) { part1 = 367 * year part2 = floor((7*(year+floor((month+9)/12)))/4) part3 = day+floor((275*month)/9) part4 = 1721013.5+(univTime/24) part5 = 0.5*sign jd = part1-part2+part3+part4-part5+0.5 } return jd } function calculateCD(julianDate) { with (Math) { X = parseFloat(julianDate)+0.5 Z = floor(X) F = X - Z Y = floor((Z-1867216.25)/36524.25) A = Z+1+Y-floor(Y/4) B = A+1524 C = floor((B-122.1)/365.25) D = floor(365.25*C) G = floor((B-D)/30.6001) month = (G<13.5) ? (G-1) : (G-13) year = (month<2.5) ? (C-4715) : (C-4716) month -= 1 // month in JavaScript is from 0 to 11 UT = B-D-floor(30.6001*G)+F day = floor(UT) UT -= floor(UT) UT *= 24; hour = floor(UT) UT -= floor(UT) UT *= 60 minute = floor(UT) UT -= floor(UT) UT *= 60 second = round(UT) } cdDate = new Date(Date.UTC(year, month, day, hour, minute, second)) // return cdDate.toGMTString() return cdDate.toLocaleDateString(); } /*************** End Julian Date Code by Rahul Mittal *****************/ /********************************************************************** * Word highlighting code. Based on http://www.kryogenix.org/code/browser/searchhi/ * with several modifications (primarily to make it easier to understand.) HOWEVER, it's /horribly/ inefficient (checks every single word), and needs to be rewritten from scratch. **********************************************************************/ function highlightWord (node, word) { // Iterate into this nodes childNodes if (node.hasChildNodes) { var hi_cn; for (hi_cn = 0; hi_cn < node.childNodes.length; hi_cn++) { highlightWord (node.childNodes[hi_cn], word); } } // And do this node itself if (node.nodeType == 3) { // text node tempNodeVal = node.nodeValue.toLowerCase(); tempWordVal = word.toLowerCase(); if (tempNodeVal.indexOf(tempWordVal) != -1) { pn = node.parentNode; if (pn.className != "highlight") { // word has not already been highlighted! nv = node.nodeValue; ni = tempNodeVal.indexOf(tempWordVal); // Create a load of replacement nodes before = document.createTextNode(nv.substr(0,ni)); docWordVal = nv.substr(ni,word.length); after = document.createTextNode(nv.substr(ni+word.length)); hiwordtext = document.createTextNode(docWordVal); hiword = document.createElement("span"); hiword.className = "highlight"; hiword.appendChild(hiwordtext); pn.insertBefore(before,node); pn.insertBefore(hiword,node); pn.insertBefore(after,node); pn.removeChild(node); } } } } function wordHighlight () { // Does the user's client support the createElement function? if (!document.createElement) return; url = document.URL; // Does the url contain any Get options? if (url.indexOf ('?') == -1) return; // Find the '?' query_string = url.substr (url.indexOf ('?') + 1); // Create an array of all the different options specified query_string_array = query_string.split ('&'); // Go through the array for (i = 0; i < query_string_array.length; i++) { // Separate the query and response qsip = query_string_array[i].split ('='); // Does this query match what we're looking for? if (qsip[0] == 'highlight') { // words = unescape (qsip[1].replace('/\+/g',' ')).split('/\s+/'); // The original form of this code doesn't seem to work correctly. // This needs to be enhanced to also handle common search strings, // such as phrases in quotes. words = unescape (qsip[1].replace('/\+/g',' ')).split('+'); for (j = 0; j < words.length; j++) { // highlightWord (document.getElementsByTagName ("body")[0],words[j]); highlightWord ($('article'), words[j]); } } } } /*************** End Word Highlight Code *****************/ /** * sprintf() for JavaScript v.0.4 * * Copyright (c) 2007 Alexandru Marasteanu * Thanks to David Baird (unit test and patch). * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ function str_repeat(i, m) { for (var o = []; m > 0; o[--m] = i); return(o.join('')); } function sprintf () { var i = 0, a, f = arguments[i++], o = [], m, p, c, x; while (f) { if (m = /^[^\x25]+/.exec(f)) o.push(m[0]); else if (m = /^\x25{2}/.exec(f)) o.push('%'); else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) { if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) throw("Too few arguments."); if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) throw("Expecting number but found " + typeof(a)); switch (m[7]) { case 'b': a = a.toString(2); break; case 'c': a = String.fromCharCode(a); break; case 'd': a = parseInt(a); break; case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break; case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break; case 'o': a = a.toString(8); break; case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break; case 'u': a = Math.abs(a); break; case 'x': a = a.toString(16); break; case 'X': a = a.toString(16).toUpperCase(); break; } a = (/[def]/.test(m[7]) && m[2] && a > 0 ? '+' + a : a); c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' '; x = m[5] - String(a).length; p = m[5] ? str_repeat(c, x) : ''; o.push(m[4] ? a + p : p + a); } else throw ("Huh ?!"); f = f.substring(m[0].length); } return o.join(''); }