浏览代码

HPCC-9468 Main Graph Depth

Restrict initial load to 2 levels deep.
Allow overriding default DOT attributes (good for turning off splines).
Added Zoom and Find Icons.

Fixes HPCC-9468

Signed-off-by: Gordon Smith <gordon.smith@lexisnexis.com>
Gordon Smith 12 年之前
父节点
当前提交
9b2caa64aa

+ 36 - 0
esp/files/css/hpcc.css

@@ -271,6 +271,42 @@ margin-left:-20px;
     height: 16px;
 }
 
+.iconZoomOrig {
+    background-image: url("../img/zoomOrig.png");
+    width: 16px;
+    height: 16px;
+}
+
+.iconZoomAll {
+    background-image: url("../img/zoomAll.png");
+    width: 16px;
+    height: 16px;
+}
+
+.iconZoomWidth {
+    background-image: url("../img/zoomWidth.png");
+    width: 16px;
+    height: 16px;
+}
+
+.iconFind {
+    background-image: url("../img/find.png");
+    width: 16px;
+    height: 16px;
+}
+
+.iconFindNext {
+    background-image: url("../img/findNext.png");
+    width: 16px;
+    height: 16px;
+}
+
+.iconFindPrevious {
+    background-image: url("../img/findPrev.png");
+    width: 16px;
+    height: 16px;
+}
+
 .iconLogicalFile {
     background-image: url("../img/folder_table.png"); 
     width: 16px;

二进制
esp/files/img/find.png


二进制
esp/files/img/findNext.png


二进制
esp/files/img/findPrev.png


二进制
esp/files/img/zoomAll.png


二进制
esp/files/img/zoomOrig.png


二进制
esp/files/img/zoomWidth.png


+ 1 - 1
esp/files/scripts/ESPWorkunit.js

@@ -286,7 +286,7 @@ define([
             var context = this;
             WsWorkunits.WUQuery({
                 request: {
-                    Wuid: this.Wuid,
+                    Wuid: this.Wuid
                 },
                 load: function (response) {
                     if (lang.exists("WUQueryResponse.Workunits.ECLWorkunit", response)) {

+ 97 - 55
esp/files/scripts/GraphPageWidget.js

@@ -73,6 +73,7 @@ define([
         graphName: "",
         wu: null,
         editorControl: null,
+        global: null,
         main: null,
         overview: null,
         local: null,
@@ -101,6 +102,7 @@ define([
             this.borderContainer = registry.byId(this.id + "BorderContainer");
             this.rightBorderContainer = registry.byId(this.id + "RightBorderContainer");
             this.findField = registry.byId(this.id + "FindField");
+            this.mainDepth = registry.byId(this.id + "MainDepth");
             this.overviewDepth = registry.byId(this.id + "OverviewDepth");
             this.localDepth = registry.byId(this.id + "LocalDepth");
             this.localDistance = registry.byId(this.id + "LocalDistance");
@@ -142,6 +144,8 @@ define([
         //  Implementation  ---
         _initGraphControls: function () {
             var context = this;
+            this.global = registry.byId(this.id + "GlobalGraphWidget");
+
             this.main = registry.byId(this.id + "MainGraphWidget");
             this.main.onSelectionChanged = function (items) {
                 context.syncSelectionFrom(context.main);
@@ -205,9 +209,21 @@ define([
             this.xgmmlDialog = registry.byId(this.id + "XGMMLDialog");
             this.xgmmlTextArea = registry.byId(this.id + "XGMMLTextArea");
             on(dom.byId(this.id + "XGMMLDialogApply"), "click", function (event) {
-                var xgmml = context.xgmmlTextArea.get("value");
                 context.xgmmlDialog.hide();
-                context.loadGraphFromSource(xgmml);
+                if (context.xgmmlDialog.get("hpccMode") === "XGMML") {
+                    var xgmml = context.xgmmlTextArea.get("value");
+                    context.loadGraphFromXGMML(xgmml);
+                } else if (context.xgmmlDialog.get("hpccMode") === "DOT") {
+                    var dot = context.xgmmlTextArea.get("value");
+                    context.loadGraphFromDOT(dot);
+                } else if (context.xgmmlDialog.get("hpccMode") === "DOTATTRS") {
+                    var dotAttrs = context.xgmmlTextArea.get("value");
+                    context.global.setDotMetaAttributes(dotAttrs);
+                    context.main.setDotMetaAttributes(dotAttrs);
+                    context.overview.setDotMetaAttributes(dotAttrs);
+                    context.local.setDotMetaAttributes(dotAttrs);
+                    context._onMainRefresh();
+                }
             });
             on(dom.byId(this.id + "XGMMLDialogCancel"), "click", function (event) {
                 context.xgmmlDialog.hide();
@@ -267,21 +283,21 @@ define([
             this._initItemGrid(this.edgesGrid);
         },
 
-        _onLayout: function () {
+        _onMainRefresh: function () {
             this.main.setMessage("Performing Layout...");
             this.main.startLayout("dot");
         },
 
-        _onLocalSync: function () {
-            this.syncSelectionFrom(this.main);
+        _onLocalRefresh: function () {
+            this.refreshLocal(this.local.getSelectionAsGlobalID());
         },
 
         _doFind: function (prev) {
             if (this.findText != this.findField.value) {
                 this.findText = this.findField.value;
-                this.found = this.main.find(this.findText);
-                this.main.setSelected(this.found);
-                this.syncSelectionFrom(this.main);
+                this.found = this.global.findAsGlobalID(this.findText);
+                this.global.setSelectedAsGlobalID(this.found);
+                this.syncSelectionFrom(this.global);
                 this.foundIndex = -1;
             }
             this.foundIndex += prev ? -1 : +1;
@@ -291,7 +307,8 @@ define([
                 this.foundIndex = 0;
             }
             if (this.found.length) {
-                this.main.centerOnItem(this.found[this.foundIndex], true);
+                this.main.centerOnGlobalID(this.found[this.foundIndex], true);
+                this.local.centerOnGlobalID(this.found[this.foundIndex], true);
             }
         },
 
@@ -330,20 +347,40 @@ define([
         },
 
         _onGetXGMML: function () {
+            this.xgmmlDialog.set("title", "XGMML");
+            this.xgmmlDialog.set("hpccMode", "XGMML");
             this.xgmmlTextArea.set("value", this.main.getXGMML());
             this.xgmmlDialog.show();
         },
 
+        _onEditDOT: function () {
+            this.xgmmlDialog.set("title", "DOT");
+            this.xgmmlDialog.set("hpccMode", "DOT");
+            this.xgmmlTextArea.set("value", this.main.getDOT());
+            this.xgmmlDialog.show();
+        },
+
+        _onGetGraphAttributes: function () {
+            this.xgmmlDialog.set("title", "DOT Attributes");
+            this.xgmmlDialog.set("hpccMode", "DOTATTRS");
+            this.xgmmlTextArea.set("value", this.global.getDotMetaAttributes());
+            this.xgmmlDialog.show();
+        },
+
+        _onMainDepthChange: function (value) {
+            this.refreshMain();
+        },
+
         _onOverviewDepthChange: function (value) {
             this.refreshOverview();
         },
 
         _onLocalDepthChange: function (value) {
-            this.refreshLocal(this.main.getSelection());
+            this._onLocalRefresh();
         },
 
         _onLocalDistanceChange: function (value) {
-            this.refreshLocal(this.main.getSelection());
+            this._onLocalRefresh();
         },
 
         init: function (params) {
@@ -351,25 +388,27 @@ define([
                 return;
             }
             this.initalized = true;
-            this.graphName = params.GraphName;
-            this.wu = ESPWorkunit.Get(params.Wuid);
-
-            var firstLoad = true;
-            var context = this;
-            this.wu.monitor(function () {
-                context.wu.getInfo({
-                    onGetApplicationValues: function (applicationValues) {
-                    },
-                    onGetGraphs: function (graphs) {
-                        if (firstLoad == true) {
-                            firstLoad = false;
-                            context.loadGraph(context.wu, context.graphName);
-                        } else {
-                            context.refreshGraph(context.wu, context.graphName);
+            if (params.Wuid) {
+                this.graphName = params.GraphName;
+                this.wu = ESPWorkunit.Get(params.Wuid);
+
+                var firstLoad = true;
+                var context = this;
+                this.wu.monitor(function () {
+                    context.wu.getInfo({
+                        onGetApplicationValues: function (applicationValues) {
+                        },
+                        onGetGraphs: function (graphs) {
+                            if (firstLoad == true) {
+                                firstLoad = false;
+                                context.loadGraph(context.wu, context.graphName);
+                            } else {
+                                context.refreshGraph(context.wu, context.graphName);
+                            }
                         }
-                    }
+                    });
                 });
-            });
+            }
 
             this.timingGrid.init(lang.mixin({
                 query: this.graphName
@@ -381,29 +420,28 @@ define([
 
         },
 
-        loadGraphFromSource: function(xgmml, svg) {
-            this.main.setMessage("Loading Data...");
-            this.main.loadXGMML(xgmml);
+        loadGraphFromXGMML: function (xgmml) {
+            this.global.loadXGMML(xgmml, false);
+            this.refreshMain();
+            this.refreshOverview();
+            this.loadSubgraphs();
+            this.loadVertices();
+            this.loadEdges();
+        },
+
+        loadGraphFromDOT: function (dot) {
+            this.global.loadDOT(dot);
+            this.refreshMain();
             this.refreshOverview();
             this.loadSubgraphs();
             this.loadVertices();
             this.loadEdges();
-            if (svg) {
-                this.main.setMessage("Loading Layout...");
-                if (this.main.mergeSVG(svg)) {
-                    this.main.centerOnItem(0, true);
-                    this.main.setMessage("");
-                    return;
-                }
-            }
-            this.main.setMessage("Performing Layout...");
-            this.main.startLayout("dot");
         },
 
         loadGraph: function (wu, graphName) {
             var context = this;
             wu.fetchGraphXgmmlByName(graphName, function (xgmml, svg) {
-                context.loadGraphFromSource(xgmml, svg);
+                context.loadGraphFromXGMML(xgmml, svg);
             });
         },
 
@@ -539,20 +577,14 @@ define([
                 this.overview.setSelectedAsGlobalID(selectedGlobalIDs);
             }
 
-            var mainItems = [];
-            for (var i = 0; i < selectedGlobalIDs.length; ++i) {
-                mainItems.push(this.main.getItem(selectedGlobalIDs[i]));
-            }
-
             if (sourceControl != this.local) {
-                this.refreshLocal(mainItems);
-                this.local.setSelectedAsGlobalID(selectedGlobalIDs);
+                this.refreshLocal(selectedGlobalIDs);
             }
 
             var propertiesDom = dom.byId(this.id + "Properties");
             propertiesDom.innerHTML = "";
-            for (var i = 0; i < mainItems.length; ++i) {
-                this.main.displayProperties(mainItems[i], propertiesDom);
+            for (var i = 0; i < selectedGlobalIDs.length; ++i) {
+                this.global.displayProperties(selectedGlobalIDs[i], propertiesDom);
             }
         },
 
@@ -560,14 +592,24 @@ define([
             this.main.clear();
         },
 
-        refreshOverview: function() {
-            var xgmml = this.main.getLocalisedXGMML([0], this.overviewDepth.get("value"));
+        refreshMain: function () {
+            var xgmml = this.global.getLocalisedXGMML([0], this.mainDepth.get("value"));
+            this.main.loadXGMML(xgmml);
+        },
+
+        refreshOverview: function () {
+            var xgmml = this.global.getLocalisedXGMML([0], this.overviewDepth.get("value"));
             this.overview.loadXGMML(xgmml);
         },
 
-        refreshLocal: function (selection) {
-            var xgmml = this.main.getLocalisedXGMML(selection, this.localDepth.get("value"), this.localDistance.get("value"));
+        refreshLocal: function (selectedGlobalIDs) {
+            var globalItems = [];
+            for (var i = 0; i < selectedGlobalIDs.length; ++i) {
+                globalItems.push(this.global.getItem(selectedGlobalIDs[i]));
+            }
+            var xgmml = this.global.getLocalisedXGMML(globalItems, this.localDepth.get("value"), this.localDistance.get("value"));
             this.local.loadXGMML(xgmml);
+            this.local.setSelectedAsGlobalID(selectedGlobalIDs);
         },
 
         displayGraphs: function (graphs) {

+ 65 - 1
esp/files/scripts/GraphWidget.js

@@ -46,6 +46,14 @@ require([
             dot: "",
             svg: "",
 
+            //  Known control properties  ---
+            DOT_META_ATTR: "DOT_META_ATTR",
+
+            _onClickZoomOrig: function (args) {
+                this.setScale(100);
+                this.centerOnItem(0);
+            },
+
             _onClickZoomAll: function (args) {
                 this.centerOnItem(0, true);
             },
@@ -178,8 +186,9 @@ require([
                 );
             },
 
-            displayProperties: function (item, place) {
+            displayProperties: function (globalID, place) {
                 if (this._plugin) {
+                    var item = this._plugin.getItem(globalID);
                     var props = this._plugin.getProperties(item);
                     if (props.id) {
                         var table = domConstruct.create("h3", {
@@ -354,6 +363,25 @@ require([
                 return [];
             },
 
+            findAsGlobalID: function (findText) {
+                if (this._plugin) {
+                    var items = this.find(findText);
+                    var globalIDs = [];
+                    for (var i = 0; i < items.length; ++i) {
+                        globalIDs.push(this._plugin.getGlobalID(items[i]));
+                    }
+                    return globalIDs;
+                }
+                return [];
+            },
+
+            setScale: function(percent) {
+                if (this._plugin) {
+                    return this._plugin.setScale(percent);
+                }
+                return 100;
+            },
+
             centerOnItem: function (item, scaleToFit, widthOnly) {
                 if (this._plugin) {
                     return this._plugin.centerOnItem(item, scaleToFit, widthOnly);
@@ -361,6 +389,16 @@ require([
                 return null;
             },
 
+            centerOnGlobalID: function (globalID, scaleToFit, widthOnly) {
+                if (this._plugin) {
+                    var item = this._plugin.getItem(globalID);
+                    if (item) {
+                        return this.centerOnItem(item, scaleToFit, widthOnly);
+                    }
+                }
+                return null;
+            },
+
             setSelected: function (items) {
                 if (this._plugin) {
                     return this._plugin.setSelected(items);
@@ -479,6 +517,32 @@ require([
                 }
             },
 
+            getDotMetaAttributes: function () {
+                if (this._plugin && this._plugin.getControlProperty) {
+                    return this._plugin.getControlProperty(this.DOT_META_ATTR);
+                }
+                return "";
+            },
+
+            setDotMetaAttributes: function (dotMetaAttr) {
+                if (this._plugin && this._plugin.setControlProperty) {
+                    this._plugin.setControlProperty(this.DOT_META_ATTR, dotMetaAttr);
+                }
+            },
+
+            getProperty: function (item, key) {
+                if (this._plugin && this._plugin.getProperty) {
+                    return this._plugin.getProperty(item, key);
+                }
+                return "";
+            },
+
+            setProperty: function (item, key, value) {
+                if (this._plugin && this._plugin.setProperty) {
+                    this._plugin.setProperty(item, key, value);
+                }
+            },
+
             getProperties: function (item) {
                 if (this._plugin) {
                     return this._plugin.getProperties(item);

+ 14 - 12
esp/files/scripts/TimingGridWidget.js

@@ -114,19 +114,21 @@ define([
                 if (params.query) {
                     this.defaultQuery = params.query;
                 }
-                this.wu = ESPWorkunit.Get(params.Wuid);
 
-                var monitorCount = 4;
-                var context = this;
-                this.wu.monitor(function () {
-                    if (context.wu.isComplete() || ++monitorCount % 5 == 0) {
-                        context.wu.getInfo({
-                            onGetTimers: function (timers) {
-                                context.loadTimings(timers);
-                            }
-                        });
-                    }
-                });
+                if (params.Wuid) {
+                    this.wu = ESPWorkunit.Get(params.Wuid);
+                    var monitorCount = 4;
+                    var context = this;
+                    this.wu.monitor(function () {
+                        if (context.wu.isComplete() || ++monitorCount % 5 == 0) {
+                            context.wu.getInfo({
+                                onGetTimers: function (timers) {
+                                    context.loadTimings(timers);
+                                }
+                            });
+                        }
+                    });
+                }
             },
 
             setQuery: function (graphName) {

+ 16 - 10
esp/files/templates/GraphPageWidget.html

@@ -2,18 +2,23 @@
     <div id="${id}BorderContainer" class="${baseClass}BorderContainer" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.BorderContainer">
         <div id="${id}MainGraphWidget" data-dojo-props="region: 'center'" data-dojo-type="GraphWidget">
             <div id="${id}FindField" style="width: 120px" data-dojo-props="placeHolder:'find'" data-dojo-type="dijit.form.TextBox">Find</div>
-            <div id="${id}Find" data-dojo-attach-event="onClick:_onFind" data-dojo-type="dijit.form.Button">F</div>
-            <div id="${id}Previous" data-dojo-attach-event="onClick:_onFindPrevious" data-dojo-type="dijit.form.Button">&lt;</div>
-            <div id="${id}Next" data-dojo-attach-event="onClick:_onFindNext" data-dojo-type="dijit.form.Button">&gt;</div>
+            <div id="${id}Find" data-dojo-attach-event="onClick:_onFind" data-dojo-props="iconClass:'iconFind', showLabel:false" data-dojo-type="dijit.form.Button">Find</div>
+            <div id="${id}Previous" data-dojo-attach-event="onClick:_onFindPrevious" data-dojo-props="iconClass:'iconFindPrevious', showLabel:false" data-dojo-type="dijit.form.Button">Find Previous</div>
+            <div id="${id}Next" data-dojo-attach-event="onClick:_onFindNext" data-dojo-props="iconClass:'iconFindNext', showLabel:false" data-dojo-type="dijit.form.Button">Find Next</div>
             <span data-dojo-type="dijit.ToolbarSeparator"></span>
-            <div id="${id}Layout" data-dojo-attach-event="onClick:_onLayout" data-dojo-type="dijit.form.Button">Layout</div>
+            <label for="${id}MainDepth">Depth:</label>
+            <input id="${id}MainDepth" style="width: 60px" value="2" data-dojo-attach-event="onChange:_onMainDepthChange" data-dojo-props="placeHolder:'Depth', constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
+            <div id="${id}MainRefresh" data-dojo-attach-event="onClick:_onMainRefresh" data-dojo-props="iconClass:'iconRefresh'" data-dojo-type="dijit.form.Button">Refresh</div>
             <span data-dojo-type="dijit.ToolbarSeparator"></span>
             <div id="${id}AdvancedMenu" data-dojo-type="dijit.form.DropDownButton">
                 <span>Advanced</span>
                 <div data-dojo-type="dijit/Menu" >
                     <div id="${id}GetSVG" data-dojo-attach-event="onClick:_onGetSVG" data-dojo-type="dijit.MenuItem">Show SVG</div>
                     <div id="${id}RenderSVG" data-dojo-attach-event="onClick:_onRenderSVG" data-dojo-type="dijit.MenuItem">Render SVG</div>
-                    <div id="${id}GetXGMML" data-dojo-attach-event="onClick:_onGetXGMML" data-dojo-type="dijit.MenuItem">Show XGMML</div>
+                    <div id="${id}GetXGMML" data-dojo-attach-event="onClick:_onGetXGMML" data-dojo-type="dijit.MenuItem">Edit XGMML</div>
+                    <div id="${id}EditDOT" data-dojo-attach-event="onClick:_onEditDOT" data-dojo-type="dijit.MenuItem">Edit DOT</div>
+                    <span data-dojo-type="dijit.MenuSeparator"></span>
+                    <div id="${id}GetGraphAttributes" data-dojo-attach-event="onClick:_onGetGraphAttributes" data-dojo-type="dijit.MenuItem">Edit Graph Attributes</div>
                     <span data-dojo-type="dijit.MenuSeparator"></span>
                     <div id="${id}About" data-dojo-attach-event="onClick:_onAbout" data-dojo-type="dijit.MenuItem">About Graph Control</div>
                 </div>
@@ -23,7 +28,7 @@
             <div id="${id}OverviewTabContainer" data-dojo-props="region: 'center', tabPosition: 'bottom'" data-dojo-type="dijit.layout.TabContainer">
                 <div id="${id}MiniGraphWidget" title="Overview" data-dojo-type="GraphWidget">
                     <label for="${id}OverviewDepth">Depth:</label>
-                    <input id="${id}OverviewDepth" style="width: 60px" value="1" data-dojo-attach-event="onChange:_onOverviewDepthChange" data-dojo-props="placeHolder:'Depth', intermediateChanges:true, constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
+                    <input id="${id}OverviewDepth" style="width: 60px" value="1" data-dojo-attach-event="onChange:_onOverviewDepthChange" data-dojo-props="placeHolder:'Depth', constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
                 </div>
                 <div id="${id}TimingsGrid" title="Timings" data-dojo-type="TimingGridWidget">
                 </div>
@@ -45,17 +50,18 @@
             <div id="${id}LocalTabContainer" style="height: 66%" data-dojo-props="region: 'bottom', splitter:true, minSize: 120, tabPosition: 'bottom'" data-dojo-type="dijit.layout.TabContainer">
                 <div id="${id}LocalGraphWidget" title="Local" data-dojo-type="GraphWidget">
                     <label for="${id}LocalDepth">Depth:</label>
-                    <input id="${id}LocalDepth" style="width: 60px" value="2" data-dojo-attach-event="onChange:_onLocalDepthChange" data-dojo-props="placeHolder:'Depth', intermediateChanges:true, constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
+                    <input id="${id}LocalDepth" style="width: 60px" value="3" data-dojo-attach-event="onChange:_onLocalDepthChange" data-dojo-props="placeHolder:'Depth', constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
                     <label for="${id}LocalDistance">Distance:</label>
-                    <input id="${id}LocalDistance" style="width: 60px" value="3" data-dojo-attach-event="onChange:_onLocalDistanceChange" data-dojo-props="placeHolder:'Depth', intermediateChanges:true, constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
-                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
-                    <div id="${id}LocalSync" data-dojo-attach-event="onClick:_onLocalSync" data-dojo-type="dijit.form.Button">Recalculate</div>
+                    <input id="${id}LocalDistance" style="width: 60px" value="3" data-dojo-attach-event="onChange:_onLocalDistanceChange" data-dojo-props="placeHolder:'Depth', constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
+                    <div id="${id}LocalRefresh" data-dojo-attach-event="onClick:_onLocalRefresh" data-dojo-props="iconClass:'iconRefresh'" data-dojo-type="dijit.form.Button">Refresh</div>
                 </div>
                 <div id="${id}Properties" title="Properties" data-dojo-type="dijit.layout.ContentPane">
                 </div>
             </div>
         </div>
     </div>
+    <div id="${id}GlobalGraphWidget" data-dojo-type="GraphWidget">
+    </div>
     <div id="${id}XGMMLDialog" title="XGMML" data-dojo-type="dijit.Dialog">
         <div class="dijitDialogPaneContentArea">
             <textarea id="${id}XGMMLTextArea" rows="25" cols="80" data-dojo-type="dijit.form.SimpleTextarea">

+ 3 - 3
esp/files/templates/GraphWidget.html

@@ -2,9 +2,9 @@
     <div id="${id}BorderContainer" class="${baseClass}BorderContainer" style="width: 100%; height: 100%; padding: 0px; overflow: hidden" data-dojo-props="splitter: false, gutters:false" data-dojo-type="dijit.layout.BorderContainer">
         <div id="${id}ToolbarContentPane" class="${baseClass}ToolbarContentPane" style="padding: 0px; overflow: hidden" data-dojo-props="region: 'top'" data-dojo-type="dijit.layout.ContentPane">
             <div class="topPanel dijit dijitToolbar" role="toolbar">
-                <b>Zoom:</b>
-                <div data-dojo-attach-event="onClick:_onClickZoomAll" data-dojo-type="dijit.form.Button">All</div>
-                <div data-dojo-attach-event="onClick:_onClickZoomWidth" data-dojo-type="dijit.form.Button">Width</div>
+                <div data-dojo-attach-event="onClick:_onClickZoomOrig" data-dojo-props="iconClass:'iconZoomOrig', showLabel:false" data-dojo-type="dijit.form.Button">Zoom 100%</div>
+                <div data-dojo-attach-event="onClick:_onClickZoomAll" data-dojo-props="iconClass:'iconZoomAll', showLabel:false" data-dojo-type="dijit.form.Button">Zoom All</div>
+                <div data-dojo-attach-event="onClick:_onClickZoomWidth" data-dojo-props="iconClass:'iconZoomWidth', showLabel:false" data-dojo-type="dijit.form.Button">Zoom Width</div>
                 <span data-dojo-type="dijit.ToolbarSeparator"></span>
                 <span data-dojo-attach-point="containerNode"></span>
             </div>