_base.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.gfx._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.gfx._base"] = true;
  8. dojo.provide("dojox.gfx._base");
  9. (function(){
  10. var g = dojox.gfx, b = g._base;
  11. // candidates for dojox.style (work on VML and SVG nodes)
  12. g._hasClass = function(/*DomNode*/node, /*String*/classStr){
  13. // summary:
  14. // Returns whether or not the specified classes are a portion of the
  15. // class list currently applied to the node.
  16. // return (new RegExp('(^|\\s+)'+classStr+'(\\s+|$)')).test(node.className) // Boolean
  17. var cls = node.getAttribute("className");
  18. return cls && (" " + cls + " ").indexOf(" " + classStr + " ") >= 0; // Boolean
  19. };
  20. g._addClass = function(/*DomNode*/node, /*String*/classStr){
  21. // summary:
  22. // Adds the specified classes to the end of the class list on the
  23. // passed node.
  24. var cls = node.getAttribute("className") || "";
  25. if(!cls || (" " + cls + " ").indexOf(" " + classStr + " ") < 0){
  26. node.setAttribute("className", cls + (cls ? " " : "") + classStr);
  27. }
  28. };
  29. g._removeClass = function(/*DomNode*/node, /*String*/classStr){
  30. // summary: Removes classes from node.
  31. var cls = node.getAttribute("className");
  32. if(cls){
  33. node.setAttribute(
  34. "className",
  35. cls.replace(new RegExp('(^|\\s+)' + classStr + '(\\s+|$)'), "$1$2")
  36. );
  37. }
  38. };
  39. // candidate for dojox.html.metrics (dynamic font resize handler is not implemented here)
  40. // derived from Morris John's emResized measurer
  41. b._getFontMeasurements = function(){
  42. // summary:
  43. // Returns an object that has pixel equivilents of standard font
  44. // size values.
  45. var heights = {
  46. '1em': 0, '1ex': 0, '100%': 0, '12pt': 0, '16px': 0, 'xx-small': 0,
  47. 'x-small': 0, 'small': 0, 'medium': 0, 'large': 0, 'x-large': 0,
  48. 'xx-large': 0
  49. };
  50. if(dojo.isIE){
  51. // we do a font-size fix if and only if one isn't applied already.
  52. // NOTE: If someone set the fontSize on the HTML Element, this will kill it.
  53. dojo.doc.documentElement.style.fontSize="100%";
  54. }
  55. // set up the measuring node.
  56. var div = dojo.create("div", {style: {
  57. position: "absolute",
  58. left: "0",
  59. top: "-100px",
  60. width: "30px",
  61. height: "1000em",
  62. borderWidth: "0",
  63. margin: "0",
  64. padding: "0",
  65. outline: "none",
  66. lineHeight: "1",
  67. overflow: "hidden"
  68. }}, dojo.body());
  69. // do the measurements.
  70. for(var p in heights){
  71. div.style.fontSize = p;
  72. heights[p] = Math.round(div.offsetHeight * 12/16) * 16/12 / 1000;
  73. }
  74. dojo.body().removeChild(div);
  75. return heights; // object
  76. };
  77. var fontMeasurements = null;
  78. b._getCachedFontMeasurements = function(recalculate){
  79. if(recalculate || !fontMeasurements){
  80. fontMeasurements = b._getFontMeasurements();
  81. }
  82. return fontMeasurements;
  83. };
  84. // candidate for dojox.html.metrics
  85. var measuringNode = null, empty = {};
  86. b._getTextBox = function( /*String*/ text,
  87. /*Object*/ style,
  88. /*String?*/ className){
  89. var m, s, al = arguments.length;
  90. if(!measuringNode){
  91. measuringNode = dojo.create("div", {style: {
  92. position: "absolute",
  93. top: "-10000px",
  94. left: "0"
  95. }}, dojo.body());
  96. }
  97. m = measuringNode;
  98. // reset styles
  99. m.className = "";
  100. s = m.style;
  101. s.borderWidth = "0";
  102. s.margin = "0";
  103. s.padding = "0";
  104. s.outline = "0";
  105. // set new style
  106. if(al > 1 && style){
  107. for(var i in style){
  108. if(i in empty){ continue; }
  109. s[i] = style[i];
  110. }
  111. }
  112. // set classes
  113. if(al > 2 && className){
  114. m.className = className;
  115. }
  116. // take a measure
  117. m.innerHTML = text;
  118. if(m["getBoundingClientRect"]){
  119. var bcr = m.getBoundingClientRect();
  120. return {l: bcr.left, t: bcr.top, w: bcr.width || (bcr.right - bcr.left), h: bcr.height || (bcr.bottom - bcr.top)};
  121. }else{
  122. return dojo.marginBox(m);
  123. }
  124. };
  125. // candidate for dojo.dom
  126. var uniqueId = 0;
  127. b._getUniqueId = function(){
  128. // summary: returns a unique string for use with any DOM element
  129. var id;
  130. do{
  131. id = dojo._scopeName + "Unique" + (++uniqueId);
  132. }while(dojo.byId(id));
  133. return id;
  134. };
  135. })();
  136. dojo.mixin(dojox.gfx, {
  137. // summary:
  138. // defines constants, prototypes, and utility functions
  139. // default shapes, which are used to fill in missing parameters
  140. defaultPath: {
  141. type: "path", path: ""
  142. },
  143. defaultPolyline: {
  144. type: "polyline", points: []
  145. },
  146. defaultRect: {
  147. type: "rect", x: 0, y: 0, width: 100, height: 100, r: 0
  148. },
  149. defaultEllipse: {
  150. type: "ellipse", cx: 0, cy: 0, rx: 200, ry: 100
  151. },
  152. defaultCircle: {
  153. type: "circle", cx: 0, cy: 0, r: 100
  154. },
  155. defaultLine: {
  156. type: "line", x1: 0, y1: 0, x2: 100, y2: 100
  157. },
  158. defaultImage: {
  159. type: "image", x: 0, y: 0, width: 0, height: 0, src: ""
  160. },
  161. defaultText: {
  162. type: "text", x: 0, y: 0, text: "", align: "start",
  163. decoration: "none", rotated: false, kerning: true
  164. },
  165. defaultTextPath: {
  166. type: "textpath", text: "", align: "start",
  167. decoration: "none", rotated: false, kerning: true
  168. },
  169. // default geometric attributes
  170. defaultStroke: {
  171. type: "stroke", color: "black", style: "solid", width: 1,
  172. cap: "butt", join: 4
  173. },
  174. defaultLinearGradient: {
  175. type: "linear", x1: 0, y1: 0, x2: 100, y2: 100,
  176. colors: [
  177. { offset: 0, color: "black" }, { offset: 1, color: "white" }
  178. ]
  179. },
  180. defaultRadialGradient: {
  181. type: "radial", cx: 0, cy: 0, r: 100,
  182. colors: [
  183. { offset: 0, color: "black" }, { offset: 1, color: "white" }
  184. ]
  185. },
  186. defaultPattern: {
  187. type: "pattern", x: 0, y: 0, width: 0, height: 0, src: ""
  188. },
  189. defaultFont: {
  190. type: "font", style: "normal", variant: "normal",
  191. weight: "normal", size: "10pt", family: "serif"
  192. },
  193. getDefault: (function(){
  194. var typeCtorCache = {};
  195. // a memoized delegate()
  196. return function(/*String*/ type){
  197. var t = typeCtorCache[type];
  198. if(t){
  199. return new t();
  200. }
  201. t = typeCtorCache[type] = new Function;
  202. t.prototype = dojox.gfx[ "default" + type ];
  203. return new t();
  204. }
  205. })(),
  206. normalizeColor: function(/*Color*/ color){
  207. // summary:
  208. // converts any legal color representation to normalized
  209. // dojo.Color object
  210. return (color instanceof dojo.Color) ? color : new dojo.Color(color); // dojo.Color
  211. },
  212. normalizeParameters: function(existed, update){
  213. // summary:
  214. // updates an existing object with properties from an "update"
  215. // object
  216. // existed: Object
  217. // the "target" object to be updated
  218. // update: Object
  219. // the "update" object, whose properties will be used to update
  220. // the existed object
  221. if(update){
  222. var empty = {};
  223. for(var x in existed){
  224. if(x in update && !(x in empty)){
  225. existed[x] = update[x];
  226. }
  227. }
  228. }
  229. return existed; // Object
  230. },
  231. makeParameters: function(defaults, update){
  232. // summary:
  233. // copies the original object, and all copied properties from the
  234. // "update" object
  235. // defaults: Object
  236. // the object to be cloned before updating
  237. // update: Object
  238. // the object, which properties are to be cloned during updating
  239. if(!update){
  240. // return dojo.clone(defaults);
  241. return dojo.delegate(defaults);
  242. }
  243. var result = {};
  244. for(var i in defaults){
  245. if(!(i in result)){
  246. result[i] = dojo.clone((i in update) ? update[i] : defaults[i]);
  247. }
  248. }
  249. return result; // Object
  250. },
  251. formatNumber: function(x, addSpace){
  252. // summary: converts a number to a string using a fixed notation
  253. // x: Number: number to be converted
  254. // addSpace: Boolean?: if it is true, add a space before a positive number
  255. var val = x.toString();
  256. if(val.indexOf("e") >= 0){
  257. val = x.toFixed(4);
  258. }else{
  259. var point = val.indexOf(".");
  260. if(point >= 0 && val.length - point > 5){
  261. val = x.toFixed(4);
  262. }
  263. }
  264. if(x < 0){
  265. return val; // String
  266. }
  267. return addSpace ? " " + val : val; // String
  268. },
  269. // font operations
  270. makeFontString: function(font){
  271. // summary: converts a font object to a CSS font string
  272. // font: Object: font object (see dojox.gfx.defaultFont)
  273. return font.style + " " + font.variant + " " + font.weight + " " + font.size + " " + font.family; // Object
  274. },
  275. splitFontString: function(str){
  276. // summary:
  277. // converts a CSS font string to a font object
  278. // description:
  279. // Converts a CSS font string to a gfx font object. The CSS font
  280. // string components should follow the W3C specified order
  281. // (see http://www.w3.org/TR/CSS2/fonts.html#font-shorthand):
  282. // style, variant, weight, size, optional line height (will be
  283. // ignored), and family.
  284. // str: String
  285. // a CSS font string
  286. var font = dojox.gfx.getDefault("Font");
  287. var t = str.split(/\s+/);
  288. do{
  289. if(t.length < 5){ break; }
  290. font.style = t[0];
  291. font.variant = t[1];
  292. font.weight = t[2];
  293. var i = t[3].indexOf("/");
  294. font.size = i < 0 ? t[3] : t[3].substring(0, i);
  295. var j = 4;
  296. if(i < 0){
  297. if(t[4] == "/"){
  298. j = 6;
  299. }else if(t[4].charAt(0) == "/"){
  300. j = 5;
  301. }
  302. }
  303. if(j < t.length){
  304. font.family = t.slice(j).join(" ");
  305. }
  306. }while(false);
  307. return font; // Object
  308. },
  309. // length operations
  310. cm_in_pt: 72 / 2.54, // Number: points per centimeter
  311. mm_in_pt: 7.2 / 2.54, // Number: points per millimeter
  312. px_in_pt: function(){
  313. // summary: returns a number of pixels per point
  314. return dojox.gfx._base._getCachedFontMeasurements()["12pt"] / 12; // Number
  315. },
  316. pt2px: function(len){
  317. // summary: converts points to pixels
  318. // len: Number: a value in points
  319. return len * dojox.gfx.px_in_pt(); // Number
  320. },
  321. px2pt: function(len){
  322. // summary: converts pixels to points
  323. // len: Number: a value in pixels
  324. return len / dojox.gfx.px_in_pt(); // Number
  325. },
  326. normalizedLength: function(len) {
  327. // summary: converts any length value to pixels
  328. // len: String: a length, e.g., "12pc"
  329. if(len.length == 0) return 0;
  330. if(len.length > 2){
  331. var px_in_pt = dojox.gfx.px_in_pt();
  332. var val = parseFloat(len);
  333. switch(len.slice(-2)){
  334. case "px": return val;
  335. case "pt": return val * px_in_pt;
  336. case "in": return val * 72 * px_in_pt;
  337. case "pc": return val * 12 * px_in_pt;
  338. case "mm": return val * dojox.gfx.mm_in_pt * px_in_pt;
  339. case "cm": return val * dojox.gfx.cm_in_pt * px_in_pt;
  340. }
  341. }
  342. return parseFloat(len); // Number
  343. },
  344. // a constant used to split a SVG/VML path into primitive components
  345. pathVmlRegExp: /([A-Za-z]+)|(\d+(\.\d+)?)|(\.\d+)|(-\d+(\.\d+)?)|(-\.\d+)/g,
  346. pathSvgRegExp: /([A-Za-z])|(\d+(\.\d+)?)|(\.\d+)|(-\d+(\.\d+)?)|(-\.\d+)/g,
  347. equalSources: function(a, b){
  348. // summary: compares event sources, returns true if they are equal
  349. return a && b && a == b;
  350. },
  351. switchTo: function(renderer){
  352. var ns = dojox.gfx[renderer];
  353. if(ns){
  354. dojo.forEach(["Group", "Rect", "Ellipse", "Circle", "Line",
  355. "Polyline", "Image", "Text", "Path", "TextPath",
  356. "Surface", "createSurface"], function(name){
  357. dojox.gfx[name] = ns[name];
  358. });
  359. }
  360. }
  361. });
  362. }