1/** 2* DD_roundies, this adds rounded-corner CSS in standard browsers and VML sublayers in IE that accomplish a similar appearance when comparing said browsers. 3* Author: Drew Diller 4* Email: [email protected] 5* URL: http://www.dillerdesign.com/experiment/DD_roundies/ 6* Version: 0.0.2a 7* Licensed under the MIT License: http://dillerdesign.com/experiment/DD_roundies/#license 8* 9* Usage: 10* DD_roundies.addRule('#doc .container', '10px 5px'); // selector and multiple radii 11* DD_roundies.addRule('.box', 5, true); // selector, radius, and optional addition of border-radius code for standard browsers. 12* 13* Just want the PNG fixing effect for IE6, and don't want to also use the DD_belatedPNG library? Don't give any additional arguments after the CSS selector. 14* DD_roundies.addRule('.your .example img'); 15**/ 16 17var DD_roundies = { 18 19 ns: 'DD_roundies', 20 21 IE6: false, 22 IE7: false, 23 IE8: false, 24 IEversion: function() { 25 if (document.documentMode != 8 && document.namespaces && !document.namespaces[this.ns]) { 26 this.IE6 = true; 27 this.IE7 = true; 28 } 29 else if (document.documentMode == 8) { 30 this.IE8 = true; 31 } 32 }, 33 querySelector: document.querySelectorAll, 34 selectorsToProcess: [], 35 imgSize: {}, 36 37 createVmlNameSpace: function() { /* enable VML */ 38 if (this.IE6 || this.IE7) { 39 document.namespaces.add(this.ns, 'urn:schemas-microsoft-com:vml'); 40 } 41 if (this.IE8) { 42 document.writeln('<?import namespace="' + this.ns + '" implementation="#default#VML" ?>'); 43 } 44 }, 45 46 createVmlStyleSheet: function() { /* style VML, enable behaviors */ 47 /* 48 Just in case lots of other developers have added 49 lots of other stylesheets using document.createStyleSheet 50 and hit the 31-limit mark, let's not use that method! 51 further reading: http://msdn.microsoft.com/en-us/library/ms531194(VS.85).aspx 52 */ 53 var style = document.createElement('style'); 54 document.documentElement.firstChild.insertBefore(style, document.documentElement.firstChild.firstChild); 55 if (style.styleSheet) { /* IE */ 56 try { 57 var styleSheet = style.styleSheet; 58 styleSheet.addRule(this.ns + '\\:*', '{behavior:url(#default#VML)}'); 59 this.styleSheet = styleSheet; 60 } catch(err) {} 61 } 62 else { 63 this.styleSheet = style; 64 } 65 }, 66 67 /** 68 * Method to use from afar - refer to it whenever. 69 * Example for IE only: DD_roundies.addRule('div.boxy_box', '10px 5px'); 70 * Example for IE, Firefox, and WebKit: DD_roundies.addRule('div.boxy_box', '10px 5px', true); 71 * @param {String}
71 selector - REQUIRED - a CSS selector, such as '#doc .container' 72 * @param {Integer} radius - REQUIRED - the desired radius for the box corners 73 * @param {Boolean} standards - OPTIONAL - true if you also wish to output -moz-border-radius/-webkit-border-radius/border-radius declarations 74 **/ 75 addRule: function(selector, rad, standards) { 76 if (typeof rad == 'undefined' || rad === null) { 77 rad = 0; 78 } 79 if (rad.constructor.toString().search('Array') == -1) { 80 rad = rad.toString().replace(/[^0-9 ]/g, '').split(' '); 81 } 82 for (var i=0; i<4; i++) { 83 rad[i] = (!rad[i] && rad[i] !== 0) ? rad[Math.max((i-2), 0)] : rad[i]; 84 } 85 if (this.styleSheet) { 86 if (this.styleSheet.addRule) { /* IE */ 87 var selectors = selector.split(','); /* multiple selectors supported, no need for multiple calls to this anymore */ 88 for (var i=0; i<selectors.length; i++) { 89 this.styleSheet.addRule(selectors[i], 'behavior:expression(DD_roundies.roundify.call(this, [' + rad.join(',') + ']))'); /* seems to execute the function without adding it to the stylesheet - interesting... */ 90 } 91 } 92 else if (standards) { 93 var moz_implementation = rad.join('px ') + 'px'; 94 this.styleSheet.appendChild(document.createTextNode(selector + ' {border-radius:' + moz_implementation + '; -moz-border-radius:' + moz_implementation + ';}')); 95 this.styleSheet.appendChild(document.createTextNode(selector + ' {-webkit-border-top-left-radius:' + rad[0] + 'px ' + rad[0] + 'px; -webkit-border-top-right-radius:' + rad[1] + 'px ' + rad[1] + 'px; -webkit-border-bottom-right-radius:' + rad[2] + 'px ' + rad[2] + 'px; -webkit-border-bottom-left-radius:' + rad[3] + 'px ' + rad[3] + 'px;}')); 96 } 97 } 98 else if (this.IE8) { 99 this.selectorsToProcess.push({'selector':selector, 'radii':rad}); 100 } 101 }, 102 103 readPropertyChanges: function(el) { 104 switch (event.propertyName) { 105 case 'style.border': 106 case 'style.borderWidth': 107 case 'style.padding': 108 this.applyVML(el); 109 break; 110 case 'style.borderColor': 111 this.vmlStrokeColor(el); 112 break; 113 case 'style.backgroundColor': 114 case 'style.backgroundPosition': 115 case 'style.backgroundRepeat': 116 this.applyVML(el); 117 break; 118 case 'style.display': 119 el.vmlBox.style.display = (el.style.display == 'none') ? 'none' : 'block'; 120 break; 121 case 'style.filter': 122 this.vmlOpacity(el); 123 break; 124 case 'style.zIndex': 125 el.vmlBox.style.zIndex = el.style.zIndex; 126 break; 127 } 128 }, 129 130 applyVML: function(el) { 131 el.runtimeStyle.cssText = ''; 132 this.vmlFill(el); 133 this.vmlStrokeColor(el); 134 this.vmlStrokeWeight(el); 135 this.vmlOffsets(el); 136 this.vmlPath(el); 137 this.nixBorder(el); 138 this.vmlOpacity(el); 139 }, 140 141 vmlOpacity: function(el) { 142 if (el.currentStyle.filter.search('lpha') != -1) { 143 var trans = el.currentStyle.filter; 144 trans = parseInt(trans.substring(trans.lastIndexOf('=')+1, trans.lastIndexOf(')')), 10)/100; 145 for (var v in el.vml) { 146 el.vml[v].filler.opacity = trans; 147 } 148 } 149 }, 150 151 vmlFill: function(el) { 152 if (!el.currentStyle) { 153 return; 154 } else { 155 var elStyle = el.currentStyle; 156 } 157 el.runtimeStyle.backgroundColor = ''; 158 el.runtimeStyle.backgroundImage = ''; 159 var noColor = (elStyle.backgroundColor == 'transparent'); 160 var noImg = true; 161 if (elStyle.backgroundImage != 'none' || el.isImg) { 162 if (!el.isImg) { 163 el.vmlBg = elStyle.backgroundImage; 164 el.vmlBg = el.vmlBg.substr(5, el.vmlBg.lastIndexOf('")')-5); 165 } 166 else { 167 el.vmlBg = el.src; 168 } 169 var lib = this; 170 if (!lib.imgSize[el.vmlBg]) { /* determine size of loaded image */ 171 var img = document.createElement('img'); 172 img.attachEvent('onload', function() { 173 this.width = this.offsetWidth; /* weird cache-busting requirement! */ 174 this.height = this.offsetHeight; 175 lib.vmlOffsets(el); 176 }); 177 img.className = lib.ns + '_sizeFinder'; 178 img.runtimeStyle.cssText = 'behavior:none; position:absolute; top:-10000px; left:-10000px; border:none;'; /* make sure to set behavior to none to prevent accidental matching of the helper elements! */ 179 img.src = el.vmlBg; 180 img.removeAttribute('width'); 181 img.removeAttribute('height'); 182 document.body.insertBefore(img, document.body.firstChild); 183 lib.imgSize[el.vmlBg] = img; 184 } 185 el.vml.image.filler.src = el.vmlBg; 186 noImg = false; 187 } 188 el.vml.image.filled = !noImg; 189 el.vml.image.fillcolor = 'none'; 190 el.vml.color.filled = !noColor; 191 el.vml.color.fillcolor = elStyle.backgroundColor; 192 el.runtimeStyle.backgroundImage = 'none'; 193 el.runtimeStyle.backgroundColor = 'transparent'; 194 }, 195 196 vmlStrokeColor: function(el) { 197 el.vml.stroke.fillcolor = el.currentStyle.borderColor; 198 }, 199 200 vmlStrokeWeight: function(el) { 201 var borders = ['Top', 'Right', 'Bottom', 'Left']; 202 el.bW = {}; 203 for (var b=0; b<4; b++) { 204 el.bW[borders[b]] = parseInt(el.currentStyle['border' + borders[b] + 'Width'], 10) || 0; 205 } 206 }, 207 208 vmlOffsets: function(el) { 209 var dims = ['Left', 'Top', 'Width', 'Height']; 210 for (var d=0; d<4; d++) { 211 el.dim[dims[d]] = el['offset'+dims[d]]; 212 } 213 var assign = function(obj, topLeft) { 214 obj.style.left = (topLeft ? 0 : el.dim.Left) + 'px'; 215 obj.style.top = (topLeft ? 0 : el.dim.Top) + 'px';
216 obj.style.width = el.dim.Width + 'px'; 217 obj.style.height = el.dim.Height + 'px'; 218 }; 219 for (var v in el.vml) { 220 var mult = (v == 'image') ? 1 : 2; 221 el.vml[v].coordsize = (el.dim.Width*mult) + ', ' + (el.dim.Height*mult); 222 assign(el.vml[v], true); 223 } 224 assign(el.vmlBox, false); 225 226 if (DD_roundies.IE8) { 227 el.vml.stroke.style.margin = '-1px'; 228 if (typeof el.bW == 'undefined') { 229 this.vmlStrokeWeight(el); 230 } 231 el.vml.color.style.margin = (el.bW.Top-1) + 'px ' + (el.bW.Left-1) + 'px'; 232 } 233 }, 234 235 vmlPath: function(el) { 236 var coords = function(direction, w, h, r, aL, aT, mult) { 237 var cmd = direction ? ['m', 'qy', 'l', 'qx', 'l', 'qy', 'l', 'qx', 'l'] : ['qx', 'l', 'qy', 'l', 'qx', 'l', 'qy', 'l', 'm']; /* whoa */ 238 aL *= mult; 239 aT *= mult; 240 w *= mult; 241 h *= mult; 242 var R = r.slice(); /* do not affect original array */ 243 for (var i=0; i<4; i++) { 244 R[i] *= mult; 245 R[i] = Math.min(w/2, h/2, R[i]); /* make sure you do not get funky shapes - pick the smallest: half of the width, half of the height, or current value */ 246 } 247 var coords = [ 248 cmd[0] + Math.floor(0+aL) +','+ Math.floor(R[0]+aT), 249 cmd[1] + Math.floor(R[0]+aL) +','+ Math.floor(0+aT), 250 cmd[2] + Math.ceil(w-R[1]+aL) +','+ Math.floor(0+aT),
251 cmd[3] + Math.ceil(w+aL) +','+ Math.floor(R[1]+aT), 252 cmd[4] + Math.ceil(w+aL) +','+ Math.ceil(h-R[2]+aT), 253 cmd[5] + Math.ceil(w-R[2]+aL) +','+ Math.ceil(h+aT), 254 cmd[6] + Math.floor(R[3]+aL) +','+ Math.ceil(h+aT), 255 cmd[7] + Math.floor(0+aL) +','+ Math.ceil(h-R[3]+aT), 256 cmd[8] + Math.floor(0+aL) +','+ Math.floor(R[0]+aT) 257 ]; 258 if (!direction) { 259 coords.reverse(); 260 } 261 var path = coords.join(''); 262 return path; 263 }; 264 265 if (typeof el.bW == 'undefined') { 266 this.vmlStrokeWeight(el); 267 } 268 var bW = el.bW; 269 var rad = el.DD_radii.slice(); 270 271 /* determine outer curves */ 272 var outer = coords(true, el.dim.Width, el.dim.Height, rad, 0, 0, 2); 273 274 /* determine inner curves */ 275 rad[0] -= Math.max(bW.Left, bW.Top); 276 rad[1] -= Math.max(bW.Top, bW.Right); 277 rad[2] -= Math.max(bW.Right, bW.Bottom); 278 rad[3] -= Math.max(bW.Bottom, bW.Left); 279 for (var i=0; i<4; i++) { 280 rad[i] = Math.max(rad[i], 0); 281 } 282 var inner = coords(false, el.dim.Width-bW.Left-bW.Right, el.dim.Height-bW.Top-bW.Bottom, rad, bW.Left, bW.Top, 2);
283 var image = coords(true, el.dim.Width-bW.Left-bW.Right+1, el.dim.Height-bW.Top-bW.Bottom+1, rad, bW.Left, bW.Top, 1); 284 285 /* apply huge path string */ 286 el.vml.color.path = inner; 287 el.vml.image.path = image; 288 el.vml.stroke.path = outer + inner; 289 290 this.clipImage(el); 291 }, 292 293 nixBorder: function(el) { 294 var s = el.currentStyle; 295 var sides = ['Top', 'Left', 'Right', 'Bottom']; 296 for (var i=0; i<4; i++) { 297 el.runtimeStyle['padding' + sides[i]] = (parseInt(s['padding' + sides[i]], 10) || 0) + (parseInt(s['border' + sides[i] + 'Width'], 10) || 0) + 'px'; 298 } 299 el.runtimeStyle.border = 'none'; 300 }, 301 302 clipImage: function(el) { 303 var lib = DD_roundies; 304 if (!el.vmlBg || !lib.imgSize[el.vmlBg]) { 305 return; 306 } 307 var thisStyle = el.currentStyle; 308 var bg = {'X':0, 'Y':0}; 309 var figurePercentage = function(axis, position) { 310 var fraction = true; 311 switch(position) { 312 case 'left': 313 case 'top': 314 bg[axis] = 0; 315 break; 316 case 'center': 317 bg[axis] = 0.5; 318 break; 319 case 'right': 320 case 'bottom': 321 bg[axis] = 1; 322 break; 323 default: 324 if (position.search('%') != -1) { 325 bg[axis] = parseInt(position, 10) * 0.01; 326 } 327 else { 328 fraction = false; 329 } 330 } 331 var horz = (axis == 'X'); 332 bg[axis] = Math.ceil(fraction ? (( el.dim[horz ? 'Width' : 'Height'] - (el.bW[horz ? 'Left' : 'Top'] + el.bW[horz ? 'Right' : 'Bottom']) ) * bg[axis]) - (lib.imgSize[el.vmlBg][horz ? 'width' : 'height'] * bg[axis]) : parseInt(position, 10)); 333 bg[axis] += 1; 334 }; 335 for (var b in bg) { 336 figurePercentage(b, thisStyle['backgroundPosition'+b]); 337 } 338 el.vml.image.filler.position = (bg.X/(el.dim.Width-el.bW.Left-el.bW.Right+1)) + ',' + (bg.Y/(el.dim.Height-el.bW.Top-el.bW.Bottom+1)); 339 var bgR = thisStyle.backgroundRepeat; 340 var c = {'T':1, 'R':el.dim.Width+1, 'B':el.dim.Height+1, 'L':1}; /* these are defaults for repeat of any kind */ 341 var altC = { 'X': {'b1': 'L', 'b2': 'R', 'd': 'Width'}, 'Y': {'b1': 'T', 'b2': 'B', 'd': 'Height'} }; 342 if (bgR != 'repeat') { 343 c = {'T':(bg.Y), 'R':(bg.X+lib.imgSize[el.vmlBg].width), 'B':(bg.Y+lib.imgSize[el.vmlBg].height), 'L':(bg.X)}; /* these are defaults for no-repeat - clips down to the image location */ 344 if (bgR.search('repeat-') != -1) { /* now let's revert to dC for repeat-x or repeat-y */ 345 var v = bgR.split('repeat-')[1].toUpperCase(); 346 c[altC[v].b1] = 1; 347 c[altC[v].b2] = el.dim[altC[v].d]+1; 348 } 349 if (c.B > el.dim.Height) { 350 c.B = el.dim.Height+1; 351 } 352 } 353 el.vml.image.style.clip = 'rect('+c.T+'px '+c.R+'px '+c.B+'px '+c.L+'px)'; 354 }, 355 356 pseudoClass: function(el) { 357 var self = this; 358 setTimeout(function() { /* would not work as intended without setTimeout */ 359 self.applyVML(el); 360 }, 1); 361 }, 362 363 reposition: function(el) { 364 this.vmlOffsets(el); 365 this.vmlPath(el); 366 }, 367 368 roundify: function(rad) { 369 this.style.behavior = 'none'; 370 if (!this.currentStyle) { 371 return; 372 } 373 else { 374 var thisStyle = this.currentStyle; 375 }
376 var allowed = {BODY: false, TABLE: false, TR: false, TD: false, SELECT: false, OPTION: false, TEXTAREA: false}; 377 if (allowed[this.nodeName] === false) { /* elements not supported yet */ 378 return; 379 } 380 var self = this; /* who knows when you might need a setTimeout */ 381 var lib = DD_roundies; 382 this.DD_radii = rad; 383 this.dim = {}; 384 385 /* attach handlers */ 386 var handlers = {resize: 'reposition', move: 'reposition'}; 387 if (this.nodeName == 'A') { 388 var moreForAs = {mouseleave: 'pseudoClass', mouseenter: 'pseudoClass', focus: 'pseudoClass', blur: 'pseudoClass'}; 389 for (var a in moreForAs) { 390 handlers[a] = moreForAs[a]; 391 } 392 } 393 for (var h in handlers) { 394 this.attachEvent('on' + h, function() { 395 lib[handlers[h]](self); 396 }); 397 } 398 this.attachEvent('onpropertychange', function() { 399 lib.readPropertyChanges(self); 400 }); 401 402 /* ensure that this elent and its parent is given hasLayout (needed for accurate positioning) */ 403 var giveLayout = function(el) { 404 el.style.zoom = 1; 405 if (el.currentStyle.position == 'static') { 406 el.style.position = 'relative'; 407 } 408 }; 409 giveLayout(this.offsetParent); 410 giveLayout(this); 411 412 /* create vml elements */ 413 this.vmlBox = document.createElement('ignore'); /* IE8 really wants to be encased in a wrapper element for the VML to work, and I don't want to disturb getElementsByTagName('div') - open to suggestion on how to do this differently */ 414 this.vmlBox.runtimeStyle.cssText = 'behavior:none; position:absolute; margin:0; padding:0; border:0; background:none;'; /* super important - if something accidentally matches this (you yourseld did this once, Drew), you'll get infinitely-created elements and a frozen browser! */ 415 this.vmlBox.style.zIndex = thisStyle.zIndex; 416 this.vml = {'color':true, 'image':true, 'stroke':true}; 417 for (var v in this.vml) { 418 this.vml[v] = document.createElement(lib.ns + ':shape'); 419 this.vml[v].filler = document.createElement(lib.ns + ':fill'); 420 this.vml[v].appendChild(this.vml[v].filler); 421 this.vml[v].stroked = false; 422 this.vml[v].style.position = 'absolute'; 423 this.vml[v].style.zIndex = thisStyle.zIndex; 424 this.vml[v].coordorigin = '1,1'; 425 this.vmlBox.appendChild(this.vml[v]); 426 } 427 this.vml.image.fillcolor = 'none'; 428 this.vml.image.filler.type = 'tile'; 429 this.parentNode.insertBefore(this.vmlBox, this); 430 431 this.isImg = false; 432 if (this.nodeName == 'IMG') { 433 this.isImg = true; 434 this.style.visibility = 'hidden'; 435 } 436 437 setTimeout(function() { 438 lib.applyVML(self); 439 }, 1); 440 } 441 442}; 443 444try { 445 document.execCommand("BackgroundImageCache", false, true); 446} catch(err) {} 447DD_roundies.IEversion(); 448DD_roundies.createVmlNameSpace(); 449DD_roundies.createVmlStyleSheet(); 450 451if (DD_roundies.IE8 && document.attachEvent && DD_roundies.querySelector) { 452 document.attachEvent('onreadystatechange', function() { 453 if (document.readyState == 'complete') { 454 var selectors = DD_roundies.selectorsToProcess; 455 var length = selectors.length; 456 var delayedCall = function(node, radii, index) { 457 setTimeout(function() { 458 DD_roundies.roundify.call(node, radii); 459 }, index*100); 460 }; 461 for (var i=0; i<length; i++) { 462 var results = document.querySelectorAll(selectors[i].selector); 463 var rLength = results.length; 464 for (var r=0; r<rLength; r++) { 465 if (results[r].nodeName != 'INPUT') { /* IE8 doesn't like to do this to inputs yet */ 466 delayedCall(results[r], selectors[i].radii, r); 467 } 468 } 469 } 470 } 471 }); 472}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.