/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml */ dojo.provide("dojo.html.metrics"); dojo.require("dojo.html.layout"); /* dojo.html.metrics * Methods to help determine font metrics, including things like * how much of a string will fit inside a certain width, what size * something might be if you were to place it in a certain node, etc. * * Based partially on a submitted patch by Morris Johns, and work * done with 13th Parallel and f( m ) (the 13th columns and the * unreleased f( m ) layout manager. */ // derived from Morris John's scrollbar measurer. dojo.html.getScrollbar=function(){ // summary // returns the width of a scrollbar. // set up the test nodes. var scroll = document.createElement("div"); scroll.style.width="100px"; scroll.style.height="100px"; scroll.style.overflow="scroll"; scroll.style.position="absolute"; scroll.style.top="-300px"; scroll.style.left="0px" var test = document.createElement("div"); test.style.width="400px"; test.style.height="400px"; scroll.appendChild(test); dojo.body().appendChild(scroll); var width=scroll.offsetWidth - scroll.clientWidth; dojo.body().removeChild(scroll); scroll.removeChild(test); scroll=test=null; // we return an object because we may add additional info in the future. return { width: width }; // object }; // derived from Morris John's emResized measurer dojo.html.getFontMeasurements = function(){ // summary // Returns an object that has pixel equivilents of standard font size values. var heights = { '1em':0, '1ex':0, '100%':0, '12pt':0, '16px':0, 'xx-small':0, 'x-small':0, 'small':0, 'medium':0, 'large':0, 'x-large':0, 'xx-large':0 }; if(dojo.render.html.ie){ // we do a font-size fix if and only if one isn't applied already. // NOTE: If someone set the fontSize on the HTML Element, this will kill it. document.documentElement.style.fontSize="100%"; } // set up the measuring node. var div=document.createElement("div"); div.style.position="absolute"; div.style.left="-100px"; div.style.top="0"; div.style.width="30px"; div.style.height="1000em"; div.style.border="0"; div.style.margin="0"; div.style.padding="0"; div.style.outline="0"; div.style.lineHeight="1"; div.style.overflow="hidden"; dojo.body().appendChild(div); // do the measurements. for(var p in heights){ div.style.fontSize = p; heights[p] = Math.round(div.offsetHeight * 12/16) * 16/12 / 1000; } dojo.body().removeChild(div); div = null; return heights; // object }; dojo.html._fontMeasurements = null; dojo.html.getCachedFontMeasurements = function(recalculate){ if(recalculate || !dojo.html._fontMeasurements){ dojo.html._fontMeasurements = dojo.html.getFontMeasurements(); } return dojo.html._fontMeasurements; }; dojo.html.measureFragment = function(/* HTMLElement */node, /* string */html, /* string? */boxType){ // summary // get the dimensions of passed node if it were populated with passed html. var clone = node.cloneNode(true); clone.innerHTML = html; node.parentNode.appendChild(clone); var ret = dojo.html.getElementBox(clone, boxType); node.parentNode.removeChild(clone); clone=null; return ret; // object }; // the following are derived from the 13th Parallel Column script, as // reinterpreted by trt. http://www.13thparallel.org/archive/column-script // Original by Dan Pupius and Michael van Ouwerkerk. dojo.html.getFittedFragment = function(/* HTMLElement */node, /* string */html){ // summary // Given html, return the fragment that will fit on one line of passed node. function cl(node){ var element = document.createElement(node.tagName); element.id = node.id + "-clone"; element.className = node.className; for (var j = 0; j < node.attributes.length; j++) { if (node.attributes[j].specified) { if (node.attributes[j].nodeName.toLowerCase() != "style" && node.attributes[j].nodeName.toLowerCase() != "edited" && node.attributes[j].nodeName.toLowerCase() != "contenteditable" && node.attributes[j].nodeName.toLowerCase() != "id" && node.attributes[j].nodeName.toLowerCase() != "class" ){ element.setAttribute(node.attributes[j].nodeName.toLowerCase(), node.attributes[j].nodeValue); } } } return element; } var height = dojo.html.getFontMeasurements()["16px"]; var n=cl(node); n.style.width=dojo.html.getBorderBox(node).width+"px"; n.style.height=(height+4)+"px"; node.parentNode.appendChild(n); var rem = dojo.html.fitToElement(n, html); var ret = n.innerHTML; n.parentNode.removeChild(n); return ret; }; dojo.html.fitToElement = function(/* HTMLElement */node, /* string */html){ // summary // will fit as much html as possible into node, and return the unused // portion, with tag corrections. function cl(node){ var element = document.createElement(node.tagName); element.id = node.id + "-clone"; element.className = node.className; for (var j = 0; j < node.attributes.length; j++) { if (node.attributes[j].specified) { if (node.attributes[j].nodeName.toLowerCase() != "style" && node.attributes[j].nodeName.toLowerCase() != "edited" && node.attributes[j].nodeName.toLowerCase() != "contenteditable" && node.attributes[j].nodeName.toLowerCase() != "id" && node.attributes[j].nodeName.toLowerCase() != "class" ){ element.setAttribute(node.attributes[j].nodeName.toLowerCase(), node.attributes[j].nodeValue); } } } return element; } var clone = cl(node); node.parentNode.appendChild(clone); var t=dojo.html.getBorderBox(node); clone.style.width = t.width+"px"; var singletons = ["br","img", "hr", "input", "!--"]; var chop = ["
","
","
","
","

","

"]; var openTags = []; var str = html; var i = 0; var limit = str.length; var add = 0; var doLoop = true; clone.innerHTML = str; while (doLoop) { add = Math.round((limit - i) / 2); if (add <= 1) doLoop = false; i += add; clone.innerHTML = str.substr(0, i); if (clone.offsetHeight > t.height) { limit = i; i -= add; } } if (str.substr(0, i) != str) { var lastSpace = str.substr(0, i).lastIndexOf(" "); var lastNewLine = str.substr(0, i).lastIndexOf("\n"); var lastGreater = str.substr(0, i).lastIndexOf(">"); var lastLess = str.substr(0, i).lastIndexOf("<"); if (lastLess <= lastGreater && lastNewLine == i - 1) i = i; else if (lastSpace != -1 && lastSpace > lastGreater && lastGreater > lastLess) i = lastSpace + 1; else if (lastLess > lastGreater) i = lastLess; else if (lastGreater != -1) i = lastGreater + 1; } str = str.substr(0, i); var ret = html.substr(str.length); // get the rest of the passed text. var doPush = true; var tags = str.split("<"); tags.shift(); for (var j = 0; j < tags.length; j++) { tags[j] = tags[j].split(">")[0]; if (tags[j].charAt(tags[j].length - 1) == "/"){ continue; } if (tags[j].charAt(0) != "/") { for (var k = 0; k < singletons.length; k++) { if (tags[j].split(" ")[0].toLowerCase() == singletons[k]){ doPush = false; } } if (doPush){ openTags.push(tags[j]); } doPush = true; } else { openTags.pop(); } } // close any open tags and prepend them to ret as well. for(var j=0; j=0; j--){ if(str.lastIndexOf(openTags[j]) == (str.length-openTags[j].length-1)){ str = str.substring(0, str.lastIndexOf(openTags[j])); } else { str += ""; } if(ret.length > 0){ ret = "<"+openTags[j]+">"+ret; } } for(var j=0; j