1 /* See license.txt for terms of usage */ 2 3 FBL.ns(function() { with (FBL) { 4 5 // ************************************************************************************************ 6 7 function LayoutPanel() {} 8 9 LayoutPanel.prototype = extend(Firebug.Panel, 10 { 11 template: domplate( 12 { 13 tag: 14 DIV({class: "outerLayoutBox"}, 15 DIV({class: "positionLayoutBox $outerTopMode $outerRightMode $outerBottomMode $outerLeftMode focusGroup"}, 16 DIV({class: "layoutEdgeTop layoutEdge"}), 17 DIV({class: "layoutEdgeRight layoutEdge"}), 18 DIV({class: "layoutEdgeBottom layoutEdge"}), 19 DIV({class: "layoutEdgeLeft layoutEdge"}), 20 21 DIV({class: "layoutLabelBottom layoutLabel layoutLabelPosition"}, 22 SPAN({class: "layoutPosition layoutCaption", 'aria-label' : $STR('a11y.layout.position')}, $STR('position')+": "+'$position'), 23 SPAN({class: "layoutZIndex v$zIndex", 'aria-label' : $STR('a11y.layout.z-index')}, "z: "+'$zIndex') 24 ), 25 26 DIV({class: "layoutLabelTop layoutLabel v$outerTop"}, 27 SPAN({class: "editable focusStart", 'aria-label' : $STR('a11y.layout.position top')}, '$outerTop') 28 ), 29 DIV({class: "layoutLabelRight layoutLabel v$outerRight"}, 30 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.position right')}, '$outerRight') 31 ), 32 DIV({class: "layoutLabelBottom layoutLabel v$outerBottom"}, 33 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.position bottom')}, '$outerBottom') 34 ), 35 DIV({class: "layoutLabelLeft layoutLabel v$outerLeft"}, 36 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.position left')}, '$outerLeft') 37 ), 38 39 DIV({class: "layoutCaption"}, '$outerLabel'), 40 41 42 DIV({class: "marginLayoutBox layoutBox editGroup focusGroup"}, 43 DIV({class: "layoutCaption"}, $STR("LayoutMargin")), 44 DIV({class: "layoutLabelTop layoutLabel v$marginTop"}, 45 SPAN({class: "editable focusStart", 'aria-label' : $STR('a11y.layout.margin top')}, '$marginTop') 46 ), 47 DIV({class: "layoutLabelRight layoutLabel v$marginRight"}, 48 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout..margin right')}, '$marginRight') 49 ), 50 DIV({class: "layoutLabelBottom layoutLabel v$marginBottom"}, 51 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.margin bottom')}, '$marginBottom') 52 ), 53 DIV({class: "layoutLabelLeft layoutLabel v$marginLeft"}, 54 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.margin left')}, '$marginLeft') 55 ), 56 57 DIV({class: "borderLayoutBox layoutBox editGroup focusGroup"}, 58 DIV({class: "layoutCaption"}, $STR("LayoutBorder")), 59 DIV({class: "layoutLabelTop layoutLabel v$borderTop"}, 60 SPAN({class: "editable focusStart", 'aria-label' : $STR('a11y.layout.border top')}, '$borderTop') 61 ), 62 DIV({class: "layoutLabelRight layoutLabel v$borderRight"}, 63 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.border right')}, '$borderRight') 64 ), 65 DIV({class: "layoutLabelBottom layoutLabel v$borderBottom"}, 66 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.border bottom')}, '$borderBottom') 67 ), 68 DIV({class: "layoutLabelLeft layoutLabel v$borderLeft"}, 69 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.border left')}, '$borderLeft') 70 ), 71 72 DIV({class: "paddingLayoutBox layoutBox editGroup focusGroup"}, 73 DIV({class: "layoutCaption"}, $STR("LayoutPadding")), 74 DIV({class: "layoutLabelTop layoutLabel v$paddingTop"}, 75 SPAN({class: "editable focusStart", 'aria-label' : $STR('a11y.layout.padding top')}, '$paddingTop') 76 ), 77 DIV({class: "layoutLabelRight layoutLabel v$paddingRight"}, 78 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.padding right')}, '$paddingRight') 79 ), 80 DIV({class: "layoutLabelBottom layoutLabel v$paddingBottom"}, 81 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.padding bottom')}, '$paddingBottom') 82 ), 83 DIV({class: "layoutLabelLeft layoutLabel v$paddingLeft"}, 84 SPAN({class: "editable", 'aria-label' : $STR('a11y.layout.padding left')}, '$paddingLeft') 85 ), 86 87 DIV({class: "contentLayoutBox layoutBox editGroup focusGroup"}, 88 DIV({class: "layoutLabelCenter layoutLabel"}, 89 SPAN({class: "layoutLabelWidth layoutLabel editable focusStart", 'aria-label' : $STR('a11y.layout.width')}, '$width'), 90 " x ", 91 SPAN({class: "layoutLabelHeight layoutLabel editable", 'aria-label' : $STR('a11y.layout.height')}, '$height') 92 ) 93 ) 94 ) 95 ) 96 ) 97 ) 98 ), 99 100 getVerticalText: function(n) 101 { 102 return getVerticalText(n); 103 } 104 }), 105 106 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 107 108 onMouseOver: function(event) 109 { 110 var layoutBox = getAncestorByClass(event.target, "layoutBox"); 111 var boxFrame = layoutBox ? getBoxFrame(layoutBox) : null; 112 113 if (this.highlightedBox) 114 removeClass(this.highlightedBox, "highlighted"); 115 116 this.highlightedBox = layoutBox; 117 118 if (layoutBox) 119 setClass(layoutBox, "highlighted"); 120 121 Firebug.Inspector.highlightObject(this.selection, this.context, "boxModel", boxFrame); 122 }, 123 124 onMouseOut: function(event) 125 { 126 var nextTarget = event.relatedTarget; 127 if (nextTarget && getAncestorByClass(nextTarget, "layoutBox")) 128 return; 129 130 if (this.highlightedBox) 131 removeClass(this.highlightedBox, "highlighted"); 132 133 this.highlightedBox = null; 134 135 Firebug.Inspector.highlightObject(null, null, "boxModel"); 136 }, 137 138 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 139 // extends Panel 140 141 name: "layout", 142 parentPanel: "html", 143 order: 2, 144 145 initialize: function() 146 { 147 this.onMouseOver = bind(this.onMouseOver, this); 148 this.onMouseOut = bind(this.onMouseOut, this); 149 150 Firebug.Panel.initialize.apply(this, arguments); 151 }, 152 153 initializeNode: function(oldPanelNode) 154 { 155 this.panelNode.addEventListener("mouseover", this.onMouseOver, false); 156 this.panelNode.addEventListener("mouseout", this.onMouseOut, false); 157 dispatch([Firebug.A11yModel], 'onInitializeNode', [this]); 158 }, 159 160 destroyNode: function() 161 { 162 this.panelNode.removeEventListener("mouseover", this.onMouseOver, false); 163 this.panelNode.removeEventListener("mouseout", this.onMouseOut, false); 164 dispatch([Firebug.A11yModel], 'onDestroyNode', [this]); 165 }, 166 167 supportsObject: function(object) 168 { 169 return object instanceof Element ? 1 : 0; 170 }, 171 172 refresh: function() 173 { 174 this.updateSelection(this.selection); 175 }, 176 177 updateSelection: function(element) 178 { 179 var view = element ? element.ownerDocument.defaultView : null; 180 if (!view) 181 return this.panelNode.innerHTML = ""; 182 183 var prev = getPreviousElement(element.previousSibling); 184 var next = getNextElement(element.nextSibling); 185 186 var style = view.getComputedStyle(element, ""); 187 var prevStyle = prev ? view.getComputedStyle(prev, "") : null; 188 var nextStyle = next ? view.getComputedStyle(next, "") : null; 189 190 var args = getBoxFromStyles(style, element); 191 192 args.outerLeft = args.outerRight = args.outerTop = args.outerBottom = ''; 193 args.outerLeftMode = args.outerRightMode = args.outerTopMode = args.outerBottomMode = ""; 194 args.zIndex = args.zIndex ? args.zIndex : "auto"; 195 196 var position = style.getPropertyCSSValue("position").cssText; 197 args.position = position; 198 args.outerLabel = ''; 199 200 if (isElementSVG(element) || isElementMathML(element) || isElementXUL(element)) 201 { 202 var rect = element.getBoundingClientRect(); 203 if (rect.wrappedJSObject) 204 rect = rect.wrappedJSObject; 205 206 args.width = Math.round(rect.width); 207 args.height = Math.round(rect.height); 208 } 209 210 // these Modes are classes on the domplate 211 args.outerLeftMode = args.outerRightMode = args.outerTopMode 212 = args.outerBottomMode = "blankEdge"; 213 214 if (position == "absolute" || position == "fixed" || position == "relative") 215 { 216 function getStyle(style, name) { var v = style.getPropertyCSSValue(name); return (v && v.cssText) ? parseInt(v.cssText) : ' '; } 217 218 args.outerLabel = $STR("LayoutPosition"); 219 220 args.outerLeft = getStyle(style,'left'); 221 args.outerTop = getStyle(style,'top'); 222 args.outerRight = getStyle(style,'right'); 223 args.outerBottom = getStyle(style,'bottom'); 224 225 args.outerLeftMode = args.outerRightMode = args.outerTopMode 226 = args.outerBottomMode = "absoluteEdge"; 227 } 228 229 var node = this.template.tag.replace(args, this.panelNode); 230 this.adjustCharWidth(this.getMaxCharWidth(args, node), this.panelNode); 231 232 dispatch([Firebug.A11yModel], 'onLayoutBoxCreated', [this, node, args]); 233 }, 234 235 /* 236 * The nested boxes of the Layout panel have digits which need to fit between the boxes. 237 * @param maxWidth: pixels the largest digit string 238 * @param node: panelNode to be adjusted (from tag:) 239 */ 240 adjustCharWidth: function(maxWidth, node) 241 { 242 maxWidth += 10; // margin 243 if (maxWidth < 20) 244 maxWidth = 20; 245 246 this.adjustBoxWidth(node, "marginLayoutBox", maxWidth); 247 this.adjustBoxWidth(node, "borderLayoutBox", maxWidth); 248 this.adjustBoxWidth(node, "paddingLayoutBox", maxWidth); 249 250 var box = node.getElementsByClassName("outerLayoutBox").item(0); 251 box.style.cssText = "width: "+(240 + 3*maxWidth)+"px;"; // defaults to 300px 252 253 this.adjustLabelWidth(node, "layoutLabelLeft", maxWidth); 254 this.adjustLabelWidth(node, "layoutLabelRight", maxWidth); 255 }, 256 257 /* 258 * By adjusting this width, the labels can be centered. 259 */ 260 adjustLabelWidth: function(node, labelName, maxWidth) 261 { 262 var labels = node.getElementsByClassName(labelName); 263 for (var i = 0; i < labels.length; i++) 264 labels[i].style.cssText = "width: "+maxWidth+"px;"; 265 }, 266 267 adjustBoxWidth: function(node, boxName, width) 268 { 269 var box = node.getElementsByClassName(boxName).item(0); 270 box.style.cssText = "right: "+width + 'px;'+" left: "+width + 'px;'; 271 }, 272 273 getMaxCharWidth: function(args, node) 274 { 275 Firebug.MeasureBox.startMeasuring(node); 276 var maxWidth = Math.max( 277 Firebug.MeasureBox.measureText(args.marginLeft+"").width, 278 Firebug.MeasureBox.measureText(args.marginRight+"").width, 279 Firebug.MeasureBox.measureText(args.borderLeft+"").width, 280 Firebug.MeasureBox.measureText(args.borderRight+"").width, 281 Firebug.MeasureBox.measureText(args.paddingLeft+"").width, 282 Firebug.MeasureBox.measureText(args.paddingRight+"").width 283 ); 284 Firebug.MeasureBox.stopMeasuring(); 285 return maxWidth; 286 }, 287 288 updateOption: function(name, value) 289 { 290 /* 291 if (name == "newOptionHere") 292 { 293 this.updateSelection(this.selection); 294 } 295 */ 296 }, 297 298 getOptionsMenuItems: function() 299 { 300 return [ 301 optionMenu("ShowRulers", "showRulers") 302 ]; 303 }, 304 305 getEditor: function(target, value) 306 { 307 if (!this.editor) 308 this.editor = new LayoutEditor(this.document); 309 310 return this.editor; 311 } 312 }); 313 314 // ************************************************************************************************ 315 // LayoutEditor 316 317 function LayoutEditor(doc) 318 { 319 this.initializeInline(doc); 320 321 this.noWrap = false; 322 this.numeric = true; 323 } 324 325 LayoutEditor.prototype = domplate(Firebug.InlineEditor.prototype, 326 { 327 saveEdit: function(target, value, previousValue) 328 { 329 if (!this.panel.selection.style) 330 return; 331 332 var labelBox = getAncestorByClass(target, "layoutLabel"); 333 var layoutBox = getLayoutBox(labelBox); 334 335 var boxFrame = getBoxFrame(layoutBox); 336 var boxEdge = getBoxEdge(labelBox); 337 338 var styleName; 339 if (boxFrame == "content" || boxFrame == "position") 340 styleName = boxEdge.toLowerCase(); 341 else if (boxFrame == "border") 342 styleName = boxFrame+boxEdge+"Width"; 343 else 344 styleName = boxFrame+boxEdge; 345 346 var intValue = value ? value : 0; 347 this.panel.selection.style[styleName] = intValue + "px"; 348 349 if (Firebug.Inspector.highlightedElement == this.panel.selection) 350 { 351 var boxFrame = this.highlightedBox ? getBoxFrame(this.highlightedBox) : null; 352 Firebug.Inspector.highlightObject(this.panel.selection, this.panel.context, "boxModel", boxFrame); 353 } 354 355 if (hasClass(target, "layoutVerticalText")) 356 target.innerHTML = getVerticalText(intValue); 357 else 358 target.innerHTML = intValue; 359 360 if (previousValue == "0" && !!value) 361 removeClass(target.parentNode, "v0"); 362 else if (!value) 363 setClass(target.parentNode, "v0"); 364 }, 365 366 endEditing: function(target, value, cancel) 367 { 368 // Don't remove groups 369 return false; 370 } 371 }); 372 373 // ************************************************************************************************ 374 // Local Helpers 375 376 function getLayoutBox(element) 377 { 378 var re = /([^\s]+)LayoutBox/; 379 for (var box = element; box; box = box.parentNode) 380 { 381 if (re.exec(box.className)) 382 return box; 383 } 384 } 385 386 function getBoxFrame(element) 387 { 388 var re = /([^\s]+)LayoutBox/; 389 var m = re.exec(element.className); 390 return m ? m[1] : ""; 391 } 392 393 function getBoxEdge(element) 394 { 395 var re = /layoutLabel([^\s]+)/; 396 var m = re.exec(element.className); 397 return m ? m[1] : ""; 398 } 399 400 function getVerticalText(n) 401 { 402 n = n+""; 403 var text = []; 404 for (var i = 0; i < n.length; ++i) 405 text.push(n[i]); 406 return text.join("<br>"); 407 } 408 409 // ************************************************************************************************ 410 411 Firebug.registerPanel(LayoutPanel); 412 413 // ************************************************************************************************ 414 415 }}); 416