/** * function tries to check if the color contains just hex-characters * if so, it returns the color-definition prefixed with a '#' * otherwise it assumes the color is a named one */ function renderColorAsString(c) { if (c && c.length == 6) { var nonhex = new RegExp("[^0-9,a-f]"); nonhex.ignoreCase = true; var found = c.match(nonhex); if (!found) { // color-string contains just hex-characters, so we prefix it with '#' return("#" + c); } } return(c); } /** * renders a color as hex or named string. * you may provide parameters modifying the * appearance of the color: * 1.) param may contain one or more of * these strings: * shiftHue, shiftSaturation, shiftLuminance. * these strings must specify float values (see * function modifyColHsl for sensible value ranges). * 2.) param may also contain one or more of these * strings: * setHue, setSaturation, setLuminance. * these strings must also specify float values, * which will override the respective parameters * with the absolute values specified. * 3.) param may also contain one or both of * these parameters: * invertSaturation, invertLuminance. * the value of these parameters does not matter; * when it is given, the respective variable * is inverted. * you may use any combination of 1.), 2.) and 3.). * note that inverting hue is a matter of * setting shiftSaturation="180". */ function renderColor(c, param) { if (param) { c = modifyCol(c, param); } res.write(renderColorAsString(c)); } /** * does the same as modifyColHsl, but takes the * parameters in the same form as renderColor. * it's used as a bridge between these two * functions. */ function modifyCol(c, param) { var modifiedCol = c; if (param.shiftHue || param.shiftSaturation || param.shiftLuminance || param.setHue || param.setSaturation || param.setLuminance || param.invertLuminance || param.invertSaturation) { // initialize h, s and l deltas (differences): if (param.shiftHue) { dH = parseFloat(param.shiftHue); } else { dH = 0.0; } if (param.shiftSaturation) { dS = parseFloat(param.shiftSaturation); } else { dS = 0.0; } if (param.shiftLuminance) { dL = parseFloat(param.shiftLuminance); } else { dL = 0.0; } // modify colour using deltas modifiedCol = modifyColHsl(c, dH, dS, dL, param); } return modifiedCol; } /** * modifyColHsl takes a string denoting a colour in hex * and changes its hue, saturation and luminance * (brightness) according to the respective parameters, * which can indicate changes of +/- 0-360 degrees (hue) * or +/- 0.0-1.0 (saturation, luminance). overflows * are truncated. the modified colour is returned again * as a string denoting the colour in hex. * | example: modifyCol("400000", 0, 0, 0.25) returns * | "C00000". easy! * if one or more parameter strings (setHue, setSaturation, * setLuminance) are given, these settings override the * specified colour and set the respective variable to the * specified value. you can use this to shift to absolute * values instead relatively. * invertSaturation and invertLuminance work similar and * act on relative values as well as on absolute ones. */ function modifyColHsl(colour, dH, dS, dL, param) { // extract normalised RGB values var r = parseInt(colour.substring(0, 2), 16) / 255.0; var g = parseInt(colour.substring(2, 4), 16) / 255.0; var b = parseInt(colour.substring(4, 6), 16) / 255.0; // modify the colour in HSL space and truncate overflow var colourHsl = new rgbToHsl(r, g, b); var modifiedHue = (colourHsl.hue + dH) % 360; var modifiedSaturation = Math.max( Math.min((colourHsl.saturation + dS), 1.0), 0.0); var modifiedLuminance = Math.max( Math.min((colourHsl.luminance + dL), 1.0), 0.0); // override values as necessary if (param) { if (param.setHue) { modifiedHue = parseFloat(param.setHue) % 360; } if (param.setSaturation) { modifiedSaturation = Math.max(Math.min( parseFloat(param.setSaturation), 1.0), 0.0); } if (param.setLuminance) { modifiedLuminance = Math.max(Math.min( parseFloat(param.setLuminance), 1.0), 0.0); } if (param.invertSaturation) { modifiedSaturation = 1.0 - modifiedSaturation; } if (param.invertLuminance) { modifiedLuminance = 1.0 - modifiedLuminance; } } var modifiedRgb = new hslToRgb(modifiedHue, modifiedSaturation, modifiedLuminance); // convert result to return value r = Math.round(modifiedRgb.red * 255.0); g = Math.round(modifiedRgb.green * 255.0); b = Math.round(modifiedRgb.blue * 255.0); return wordToHexStr((r << 16) + (g << 8) + b); } /** * function converting float RGB values in the range 0-1 * to float HSL values in the ranges 0-360, 0-1, 0-1 */ function rgbToHsl(r, g, b) { var h, s, l; var minVal = Math.min(Math.min(r, g), b); var maxVal = Math.max(Math.max(r, g), b); var range = maxVal - minVal; if (minVal == maxVal) { // r == g == b; grey tone s = 0; h = 0; l = r; } else { l = (maxVal + minVal) / 2; if (l < 0.5) { s = (range) / (maxVal + minVal); } else { s = (range) / (2.0 - maxVal - minVal); } if (r == maxVal) { h = (g - b) / range; } else if (g == maxVal) { h = 2.0 + (b - r) / range; } else { // b == maxVal h = 4.0 + (r - g) / range; } } // h is not normalised to the standard range yet this.hue = h * 60; this.saturation = s; this.luminance = l; } /** * function converting float HSL values in the ranges 0-360, * 0-1, 0-1 to float RGB values in the range 0-1 */ function hslToRgb(h, s, l) { var r, g, b; if (s == 0) { r = g = b = l; } else { // in order to reverse the algorithm of RgbToHsl, we need // five helper variables with silly names... var h1, h2, h3r, h3g, h3b; if (l < 0.5) { h1 = l * (1.0 + s); } else { h1 = l + s - (l * s); } h2 = (2.0 * l) - h1; // normalise hue to the range 0-1 h = (h % 360) / 360; h3r = h + (1.0 / 3.0); h3g = h; h3b = h - (1.0 / 3.0); // derive r // instead of the following to IFs, it should be // possible to write "h3r %= 1;", but that didn't // work (???). feel free to correct. if (h3r < 0) { h3r += 1; } if (h3r > 1) { h3r -= 1; } if (h3r < (1.0 / 6.0)) { r = h2 + ((h1 - h2) * 6.0 * h3r); } else if (h3r < 0.5) { r = h1; } else if (h3r < (2.0 / 3.0)) { r = h2 + ((h1 - h2) * ((2.0 / 3.0) - h3r) * 6.0); } else { r = h2; } // derive g. this is a copy&paste of the code above // with r replaced with g. this is as elegant as // the queen's wardrobe. feel free to correct. if (h3g < 0) { h3g += 1; } if (h3g > 1) { h3g -= 1; } if (h3g < (1.0 / 6.0)) { g = h2 + ((h1 - h2) * 6.0 * h3g); } else if (h3g < 0.5) { g = h1; } else if (h3g < (2.0 / 3.0)) { g = h2 + ((h1 - h2) * ((2.0 / 3.0) - h3g) * 6.0); } else { g = h2; } // derive b. same as above. if (h3b < 0) { h3b += 1; } if (h3b > 1) { h3b -= 1; } if (h3b < (1.0 / 6.0)) { b = h2 + ((h1 - h2) * 6.0 * h3b); } else if (h3b < 0.5) { b = h1; } else if (h3b < (2.0 / 3.0)) { b = h2 + ((h1 - h2) * ((2.0 / 3.0) - h3b) * 6.0); } else { b = h2; } } this.red = r; this.green = g; this.blue = b; } /** * this function converts a 32-bit number to a string. * i copied it from somewhere, it's used in the function * shiftCol. if ECMA-Script has something like that built in * already, feel free to correct. */ function wordToHexStr(word) { var h="0123456789ABCDEF"; var retVal = ""; var hexStr = ""; var remainder, i; while (word != 0) { remainder = word % 16; hexStr += h.charAt(remainder); word >>= 4; } // hexStr is backwards, turn it around for(i = 5; i >= 0; i--) { retVal += hexStr.charAt(i); } while (retVal.length < 6) { // pad missing 0s retVal = "0" + retVal; } return retVal; } /** * open an html link element * @param String URL to use in a-tag */ function openLink(url) { var attr = new Object(); attr.href = url; openMarkupElement("a", attr); } /** * close an html link element */ function closeLink() { closeMarkupElement("a"); } /** * Opens an arbitrary x/html element ("begin tag") * @param name String containing the element's name * @param attr Object containing the element's attributes as properties */ function openMarkupElement(name, attr) { renderMarkupPart(name, attr); res.write(">"); } /** * Closes an arbitray x/html element ("end tag") * @param name String containing the element's name */ function closeMarkupElement(name) { res.write(""); } /** * Outputs an arbitrary empty x/html element ("contentless tag") * @param name String containing the element's name * @param attr Object containing the element's attributes as properties */ function renderMarkupElement(name, attr) { renderMarkupPart(name, attr); res.write(" />"); } /** * Outputs the first part of an arbitrary x/html element * except for the closing ">" or "/>" which is done by * openMarkupElement() or renderMarkupElement(), resp. * @param name String containing the element's name * @param attr Object containing the element's attributes as properties */ function renderMarkupPart(name, attr) { res.write("<" + name); if (attr) { // temporary mapping of class attribute // if attr.style contains class definition // (due to backwards-compatibility) if (attr.style && attr.style.indexOf(":") < 0) { attr["class"] = attr.style; delete attr.style; } delete attr.as; var attributes = ""; // creating the attribute string for (var i in attr) { if (!attr[i]) continue; res.write(" " + i + "=\"" + attr[i] + "\""); } } } /** * renders image element * @param img Object contains the images's properties * @param param Object contains user-defined properties */ function renderImage(img, param) { if (!param.title) param.title = img.alttext; param.src = getProperty("imgUrl"); param.src += img.site ? img.site.alias + "/" : ""; param.src += img.filename + "." + img.fileext; if (!param.width) param.width = img.width; if (!param.height) param.height = img.height; if (!param.border) param.border = "0"; param.alt = param.description ? param.description : img.alttext; renderMarkupElement("img", param); } /** * renders a textarea * @param param Object contains the element's attributes */ function renderInputTextarea(param) { if (param.width) param.cols = param.width; if (param.height) param.rows = param.height; if (!param.cols) param.cols = 40; if (!param.rows) param.rows = 5; if (!param.wrap) param.wrap = "virtual"; var value = param.value ? encodeForm(param.value) : ""; delete param.value; delete param.width; delete param.height; delete param.as; openMarkupElement("textarea", param); res.write(value); closeMarkupElement("textarea"); } /** * renders a submit-button * @param param Object contains the element's attributes */ function renderInputButton(param) { if (!param) return; param.type = "submit"; if (param.content) { param.value = param.content; delete param.content; } if (!param.name) param.name = param.type; param.value = param.value ? encodeForm(param.value) : param.type; renderMarkupElement("input", param); } /** * renders an input type text * @param param Object contains the element's attributes */ function renderInputText(param) { if (!param) return; param.type = "text"; // this one is left for backwards-compatibility if (param.width) param.size = param.width; if (!param.size) param.size = 20; delete param.width; renderMarkupElement("input", param); } /** * renders an input type password * @param param Object contains the element's attributes */ function renderInputPassword(param) { if (!param) return; param.type = "password"; param.size = param.width ? param.width : "20"; delete param.width; renderMarkupElement("input", param); } /** * function renders an input type file * @param param Object contains the element's attributes */ function renderInputFile(param) { if (!param) return; param.type = "file"; renderMarkupElement("input", param); } /** * renders an input type checkbox * @param param Object contains the element's attributes */ function renderInputCheckbox(param) { if (!param || !param.name) return; param.type = "checkbox"; param.checked = param.check; delete param.check; if (parseInt(param.value, 10) == 1 || param.value == true) param.checked = "checked"; param.value = "1"; renderMarkupElement("input", param); } /** * Renders a drop down box from an Array and an optional * current selection index. This is a simpler alternative * for the drop-down framework in hopobject. Its main * advantage is that Arrays are much simpler to set up in * JavaScript than (Hop)Objects: */ function renderDropDownBox(name, options, selectedIndex, firstoption) { var param = new Object(); param.name = name; param.size = "1"; openMarkupElement("select", param); if (firstoption) { param = new Object(); param.value = ""; openMarkupElement("option", param); res.write(firstoption); closeMarkupElement("option"); } for (var i in options) { param = new Object(); param.name = encode(options[i]); param.value = i; if (param.value == selectedIndex) param.selected = "true"; openMarkupElement("option", param); res.write(param.name); closeMarkupElement("option"); } closeMarkupElement("select"); } /** * function retuns only a part of the text passed as argument * length of the string to show is defined by argument "limit" */ function renderTextPreviewAsString(text, limit) { var limit = Math.min(limit, text.length); var text = stripTags(text); var idx = 0; while (idx < limit) { var nIdx = text.indexOf(" ", idx); if (nIdx < 0) break; idx = ++nIdx; } var prev = text.substring(0,(idx > 1 ? idx : limit)); // and now we "enrich" the text with -tags var str = ""; for (var i=0; i"; return(str); } /** * function renders only a part of the text passed as argument * length of the string to show is defined by argument "limit" */ function renderTextPreview(text, limit) { res.write(renderTextPreviewAsString(text, limit)); } /** * Do Wiki style substitution, transforming * stuff contained between asterisks into links. */ function doWikiStuff (src) { // robert, disabled: didn't get the reason for this: // var src= " "+src; if (src.indexOf ("<*") < 0) return src; // do the Wiki link thing, <*asterisk style*> var regex = new RegExp ("<[*]([^*]+)[*]>"); regex.ignoreCase=true; var text = ""; var start = 0; while (true) { var found = regex.exec (src.substring(start)); var to = found == null ? src.length : start + found.index; text += src.substring(start, to); if (found == null) break; var name = ""+(new java.lang.String (found[1])).trim(); var item = path.site.topics.get (name); if (item == null && name.lastIndexOf("s") == name.length-1) item = path.site.topics.get (name.substring(0, name.length-1)); if (item == null || !item.size()) text += format(name)+" [define "+format(name)+"]"; else text += ""+name+""; start += found.index + found[1].length+4; } return text; } /** * DEPRECATED! * use openMarkupElement(), closeMarkupElement() and * renderMarkupElement() instead * * Returns an arbitrary x/html element as string * @param name String containing the element's name (tag) * @param content String containing the element's content * @param attr Object containing the element's attributes as properties */ function renderMarkupElementAsString(name, content, attr) { if (!content) content = ""; // temporary mapping of class attribute // (due to backwards-compatibility) if (!attr["class"]) { attr["class"] = attr.style; delete attr.style; } var attributes = ""; // creating the attribute string for (var i in attr) { if (!attr[i]) continue; attributes += " " + i + "=\"" + attr[i] + "\""; } return("<" + name + attributes + ">" + content + ""); } /** * function renders a dropdown-box containing all available * locales * @param Obj Locale-Object to preselect */ function renderLocaleChooser(loc) { var locs = java.util.Locale.getAvailableLocales(); var options = new Array(); // get the defined locale of this site for comparison for (var i in locs) { options[i] = locs[i].getDisplayName(); if (loc && locs[i].equals(loc)) var selectedIndex = i; } renderDropDownBox("locale",options,selectedIndex); }