twb 0.0.33 → 0.0.34

Sign up to get free protection for your applications and to get access to all the features.
data/lib/twb.rb CHANGED
@@ -8,10 +8,11 @@ require_relative 'twb/localfield'
8
8
  require_relative 'twb/metadatafield'
9
9
  require_relative 'twb/docdashboard'
10
10
  require_relative 'twb/util/htmllistcollapsible'
11
+ require_relative 'twb/util/xraydashboards'
11
12
 
12
13
  # Represents Tableau Workbooks and their contents.
13
14
  #
14
15
  module Twb
15
- VERSION = '0.0.33'
16
+ VERSION = '0.0.34'
16
17
  end
17
18
 
@@ -25,37 +25,6 @@ module Twb
25
25
 
26
26
  attr_reader :title, :dashnode, :winnode, :type #, :panels, :autosize, :size
27
27
 
28
- @@oneColWebPageDash = Nokogiri::XML::Document.parse <<-ONECOlWEBDASH
29
- <dashdoc>
30
- <dashboard name='Single Column Web Page Automatic'>
31
- <style></style>
32
- <zones>
33
- <zone h='100000' id='3' param='vert' type='layout-flow' w='100000' x='0' y='0'>
34
- <zone h='6221' id='1' type='title' w='100000' x='0' y='0'></zone>
35
- <zone h='93157' id='4' param='horz' type='layout-flow' w='100000' x='0' y='6221'>
36
- <zone forceUpdate='' h='93157' id='6' param='URL' type='web' w='99655' x='0' y='6221'></zone>
37
- </zone>
38
- </zone>
39
- </zones>
40
- </dashboard>
41
- <window auto-hidden='0' class='dashboard' maximized='1' name='Single Column Web Page Automatic'>
42
- <zones>
43
- <zone h='6221' id='1' name='' type='title' w='100000' x='0' y='0' />
44
- <zone forceUpdate='' h='93157' id='5' name='' param='Web Page' type='web' w='50000' x='50000' y='6221' />
45
- </zones>
46
- </window>
47
- </dashdoc>
48
- ONECOlWEBDASH
49
- # notes:
50
- # - adding a size element to the <dashboard element will change it from automatic, e.g.
51
- #
52
- # <dashboard name='One Column Web Page Laptop (800w 600h)'>
53
- # <style>
54
- # </style>
55
- # <size maxheight='600' maxwidth='800' minheight='600' minwidth='800' />
56
- #
57
- # - the 'name' sttributes for the window and dashboard must match
58
-
59
28
  def title=(title)
60
29
  @title = title
61
30
  @dashnode['name'] = title
@@ -78,7 +47,7 @@ module Twb
78
47
  end
79
48
 
80
49
  def to_s
81
- return title + ' :: ' + type
50
+ return @title + ' :: ' + @type
82
51
  end
83
52
 
84
53
  end
@@ -88,9 +57,39 @@ module Twb
88
57
  attr_reader :url
89
58
 
90
59
  def initialize
60
+ @oneColWebPageDash = Nokogiri::XML::Document.parse <<-ONECOlWEBDASH
61
+ <dashdoc>
62
+ <dashboard name='Single Column Web Page Automatic'>
63
+ <style></style>
64
+ <zones>
65
+ <zone h='100000' id='3' param='vert' type='layout-flow' w='100000' x='0' y='0'>
66
+ <zone h='6221' id='1' type='title' w='100000' x='0' y='0'></zone>
67
+ <zone h='93157' id='4' param='horz' type='layout-flow' w='100000' x='0' y='6221'>
68
+ <zone forceUpdate='' h='93157' id='6' param='URL' type='web' w='99655' x='0' y='6221'></zone>
69
+ </zone>
70
+ </zone>
71
+ </zones>
72
+ </dashboard>
73
+ <window auto-hidden='0' class='dashboard' maximized='1' name='Single Column Web Page Automatic'>
74
+ <zones>
75
+ <zone h='6221' id='1' name='' type='title' w='100000' x='0' y='0' />
76
+ <zone forceUpdate='' h='93157' id='5' name='' param='Web Page' type='web' w='50000' x='50000' y='6221' />
77
+ </zones>
78
+ </window>
79
+ </dashdoc>
80
+ ONECOlWEBDASH
81
+ # notes:
82
+ # - adding a size element to the <dashboard element will change it from automatic, e.g.
83
+ #
84
+ # <dashboard name='One Column Web Page Laptop (800w 600h)'>
85
+ # <style>
86
+ # </style>
87
+ # <size maxheight='600' maxwidth='800' minheight='600' minwidth='800' />
88
+ #
89
+ # - the 'name' sttributes for the window and dashboard must match
91
90
  @type = 'columnar Web Page'
92
- @dashnode = @@oneColWebPageDash.at_xpath('//dashboard')
93
- @winnode = @@oneColWebPageDash.at_xpath('//window')
91
+ @dashnode = @oneColWebPageDash.at_xpath('//dashboard')
92
+ @winnode = @oneColWebPageDash.at_xpath('//window')
94
93
  end
95
94
 
96
95
  def title=(title)
@@ -109,7 +108,7 @@ module Twb
109
108
  def url
110
109
  dashurl = @dashnode.at_xpath('.//zone[@type="web"]').attribute('param').value
111
110
  winurl = @winnode.at_xpath( './/zone[@type="web"]').attribute('param').value
112
- url = if dashurl == winurl then dashurl end
111
+ @url = if dashurl == winurl then dashurl end
113
112
  end
114
113
 
115
114
  end
@@ -22,7 +22,7 @@ class HashToHTMLList
22
22
  def li(collection)
23
23
  @level += 1
24
24
  collection.each do |key,value|
25
- open_tag('li',key) { ul(value) if value.is_a?(Hash) || value.is_a?(Array) }
25
+ open_tag('li',key) { ul(value) if (value.is_a?(Hash) || value.is_a?(Array)) && !value.empty? }
26
26
  end
27
27
  @level -= 1
28
28
  end
@@ -174,19 +174,7 @@ COLLAPSIBLELIST
174
174
  end
175
175
 
176
176
  def installNavImageFile
177
- puts "looking for nav controls image"
178
- # puts " __FILE__ #{__FILE__}" #File.expand_path(File.dirname(_FILE_))
179
- puts " Dir.pwd #{Dir.pwd}"
180
- puts " File.dirname(__FILE__) #{File.dirname(__FILE__)}"
181
- puts " __FILE__ #{__FILE__}"
182
177
  navimage = File.dirname(__FILE__) + "/UpLeftArrowsNav.png"
183
- puts " filemask: #{navimage}"
184
- cnt = 0
185
- Dir.glob(navimage) do |twb|
186
- puts twb
187
- cnt += 1
188
- end
189
- puts " found #{cnt} files"
190
178
  FileUtils.cp(navimage, Dir.pwd)
191
179
  end
192
180
 
@@ -0,0 +1,553 @@
1
+ # Copyright (C) 2014, 2015 Chris Gerrard
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'nokogiri'
17
+
18
+ module Twb
19
+
20
+
21
+ class DashboardXRayer
22
+
23
+ @@typeLabels = {
24
+ 'bitmap' => 'Image',
25
+ 'color' => 'Color Legend',
26
+ 'currpage' => 'Pages Control',
27
+ 'draw' => 'Draw',
28
+ 'empty' => 'Blank',
29
+ 'filter' => 'Filter',
30
+ 'layout-basic' => 'Layout - Basic',
31
+ 'layout-flow' => 'Layout - Flow',
32
+ 'map' => 'Map Legend',
33
+ 'mru-entry' => 'Mru Entry',
34
+ 'paramctrl' => 'Parameter',
35
+ 'shape' => 'Shape Legend',
36
+ 'size' => 'Size Legend',
37
+ 'text' => 'Text',
38
+ 'text-block' => 'Text Block',
39
+ 'title' => 'Title',
40
+ 'web' => 'Web Page'
41
+ }
42
+
43
+ @@zoneNamePadText = '&ndash;'
44
+
45
+ @@shapeRadius = 1000
46
+ @@shapeDiam = 2*@@shapeRadius
47
+
48
+ @@htmlTableLead = '<p style="font: normal 12px Verdana, Calibri, Geneva, sans-serif; color: #000000;">The Dashboard\'s panels, one per row.<br />Mouse over the table to highlight in the diagram.</p>'
49
+ @@htmlTableFNote = '<p><span style="font: normal 12px Verdana, Calibri, Geneva, sans-serif; color: #000000;">Note: the Dashboard\'s \'x\', \'y\', \'w\', and \'h\' values are shown as coded in the<br />Tableau Workbook. It appears that the maximum value &ndash;100,000&ndash; indicates<br />that the Dashboard size is set to Automatic.</span></p>'
50
+
51
+ @maxSVGDim = 600.0 # need the decimal for dashboard scaling with svgScale in fn termDashSVG
52
+ @maxDashWidth = 1.0
53
+ @strokeWidth = 300
54
+ @@fontSize = 2000
55
+
56
+ @htmlHead = ''
57
+
58
+ @@htmlTableHead = "<table>\n<tr><th align='left'>Zone Name</th><th align='left'>Type</th><th>x</th><th>y</th><th>w</th><th>h</th><th>ID</th></tr>"
59
+ @htmlTable = ''
60
+ @svgHead = ''
61
+ @svg = ''
62
+ @@svgTail = "</g>\n</svg>"
63
+
64
+
65
+ @@typeFillColors = {
66
+ 'bitmap' => 'white',
67
+ 'chart' => 'white',
68
+ 'Color Legend' => '#CD853F',
69
+ 'currpage' => 'white',
70
+ 'draw' => 'white',
71
+ 'empty' => 'white',
72
+ 'Blank' => 'white',
73
+ 'Filter' => '#228B22',
74
+ 'layout-basic' => '#B0C4DE',
75
+ 'Layout - Basic' => '#B0C4DE',
76
+ 'layout-flow' => '#B0C4DE',
77
+ 'Layout - Flow' => '#B0C4DE',
78
+ 'Layout - Vertical' => '#B0C4DE',
79
+ 'Layout - Horizontal' => '#B0C4DE',
80
+ 'Image' => 'lightgreen',
81
+ 'map' => '#CD853F',
82
+ 'map legend' => '#CD853F',
83
+ 'mru-entry' => 'white',
84
+ 'paramctrl' => '#228B22',
85
+ 'Parameter' => '#228B22',
86
+ 'shape' => '#CD853F',
87
+ 'Shape Legend' => '#CD853F',
88
+ 'size' => '#CD853F',
89
+ 'Size Legend' => '#CD853F',
90
+ 'Text' => '#5F9EA0',
91
+ 'text-block' => '#5F9EA0',
92
+ 'Title' => '#5F9EA0',
93
+ 'web' => 'lightgreen'
94
+ }
95
+ def init
96
+ # --
97
+ @jsVars = <<-JSVARS
98
+ var oldFill = ''
99
+ var oldOpac = ''
100
+ var oldStroW = ''
101
+ var origLColWidth = "%s"
102
+ var origSvgWidth = "%s"
103
+ var origSvgHeight = "%s"
104
+ var origSvgScale = "%s"
105
+ JSVARS
106
+
107
+ # --
108
+ @fnScaleDash = <<-FNSD
109
+ function scaleDash(){
110
+ var svg = document.getElementById("svg");
111
+ var lCol = document.getElementById("LeftColumn");
112
+
113
+ var sf = document.getElementById("scaleFactor").value;
114
+ var scale = 1;
115
+ var spct = sf;
116
+ if (isNaN(sf))
117
+ { scale = 1;
118
+ spct = 100
119
+ }
120
+ else if (sf < 10)
121
+ { scale = .1 ;
122
+ spct = 10
123
+ }
124
+ else if (sf > 100)
125
+ { scale = 1 ;
126
+ spct = 100
127
+ }
128
+ else
129
+ { scale = sf / 100 ;
130
+ spct = sf
131
+ }
132
+
133
+ var newLColWidth = (origLColWidth * scale) + 5
134
+ var newSvgWidth = origSvgWidth * scale
135
+ var newSvgHeight = origSvgHeight * scale
136
+ var newSvgScale = origSvgScale * scale
137
+ var newSvgTransf = "translate(10,10) scale(" + newSvgScale + ")"
138
+
139
+ document.getElementById("LeftColumn").setAttribute("width",newLColWidth);
140
+ svg.setAttribute("width", newSvgWidth)
141
+ svg.setAttribute("height",newSvgHeight)
142
+ svg.getElementById("svgTransform").setAttribute("transform",newSvgTransf)
143
+
144
+ var newDivWidthPx = newLColWidth + "px"
145
+ var newDivWidthStr = "display:block;width:" + newDivWidthPx
146
+ document.getElementById('LeftColumn').setAttribute("style",newDivWidthStr);
147
+ document.getElementById('LeftColumn').style.width=newDivWidthPx;
148
+
149
+ var r = document.getElementById("result");
150
+ r.innerHTML = "scale: " + spct + "%" // + document.getElementById("LeftColumn").getAttribute("width")
151
+ }
152
+ FNSD
153
+ # --
154
+
155
+ end
156
+
157
+
158
+ # Create instance of the Dashboard X-Ray machine.
159
+ # The parameter 'twb' can be either a Twb::Workbook
160
+ # or a String naming the TWB to be X-Rayed.
161
+ def initialize twb
162
+ @workbook = if twb.instance_of?(Twb::Workbook)
163
+ then twb
164
+ else Twb::Workbook.new(twb)
165
+ end
166
+ @dashboards = @workbook.dashboards
167
+ @maxSVGDim = 600.0
168
+ @svgPadding = 10
169
+ init
170
+ end
171
+
172
+ # Performs an analysis of the Workbook's Sashboards akin to an X-Ray imaging.
173
+ # Each of the Dashboards' component parts, "<zone"s in the TWB's xml, is captured.
174
+ # Generates an HTML file that contains a schematic of the zones along with a
175
+ # table that identifes the zones and, when moused over, highlights the zone in
176
+ # the schematic.
177
+ # The active elements in the HTML file are in SVG.
178
+ def xray
179
+ dashCnt = 0
180
+ @dashboardHTMLDocs = {}
181
+ @dashboards.each do |dash|
182
+ @htmldoc = ''
183
+ @dashNodeDepth = dash.node.ancestors.size # depth 3 as of Tableau v7
184
+ $maxWidth, $maxHeight = 0, 0
185
+ @svg = ''
186
+ initDashSVG(dash.name)
187
+ setScale(dash)
188
+ dZones = dash.node.xpath('.//zone')
189
+ dzcnt = 0
190
+ @htmlTable = ''
191
+ dZones.each do |zone|
192
+ recordZone(dash.name, zone, dzcnt+=1)
193
+ end
194
+ termDashSVG
195
+ initDashHTML(dash.name, dashCnt += 1, @dashboards.length)
196
+ @dashboardHTMLDocs[dash.name] = termDashHTML
197
+ end
198
+ return @dashboardHTMLDocs
199
+ end
200
+
201
+ def getFillColor type
202
+ fillColor = if type.nil?
203
+ then 'white'
204
+ else @@typeFillColors[type.to_s]
205
+ end
206
+ return fillColor
207
+ end
208
+
209
+ def getZoneName zone
210
+ name = zone.attribute('name')
211
+ if name.nil?
212
+ then name = ''
213
+ end
214
+ zoneDepth = zone.ancestors.size
215
+ zoneRelDepth = zoneDepth - @dashNodeDepth - 2
216
+ # example TWB structure
217
+ # <dashboards>
218
+ # <dashboard
219
+ # <zones>
220
+ # <zone
221
+ zoneHead = @@zoneNamePadText * zoneRelDepth
222
+ name = zoneHead + '|' + name
223
+ return name
224
+ end
225
+
226
+ def getZoneType(zone)
227
+ type = zone.attribute('type')
228
+ param = zone.attribute('param').to_s
229
+ layoutHorz = zone.attribute('layout-horz').to_s
230
+ if type.nil?
231
+ then typeLabel = 'chart'
232
+ else typeLabel = @@typeLabels[type.to_s]
233
+ end
234
+ if typeLabel == 'Layout - Flow' then
235
+ typeLabel = case param
236
+ when 'vert' then 'Layout - Vertical'
237
+ when 'horz' then 'Layout - Horizontal'
238
+ else case layoutHorz
239
+ when 'true' then 'Layout - Horizontal'
240
+ when 'false' then 'Layout - Vertical'
241
+ else 'Layout - Flow'
242
+ end
243
+ end
244
+ end
245
+ return typeLabel
246
+ end
247
+
248
+ def initDashSVG(dashName)
249
+ dashFName = dashName.gsub(/[<>:'"\/\|?*]/,'')
250
+ $Fsvg = File.open("#{@workbook.name}.#{dashFName}.svg",'w')
251
+ $Fsvg.puts('<?xml version="1.0" standalone="no"?>')
252
+ $Fsvg.puts('<!DOCTYPE svg PUBLIC "-//W3C//Dth SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/Dth/svg11.dth">')
253
+ $Fsvg.puts(' ')
254
+ end
255
+
256
+
257
+
258
+ def termDashSVG
259
+ svgScale = @maxSVGDim / @maxDashWidth
260
+ @svgHead = "<svg id='svg' width='#{@maxDashWidth*svgScale + 2*@svgPadding}'\n"
261
+ @svgHead << " height='#{@maxDashHeight*svgScale + 2*@svgPadding}'\n"
262
+ @svgHead << " version='1.1'\n"
263
+ @svgHead << " xmlns='http://www.w3.org/2000/svg'>\n"
264
+ @svgHead << " <g id=\"svgTransform\" transform=\"translate(10,10) scale(#{svgScale})\">"
265
+ $Fsvg.puts @svgHead
266
+ $Fsvg.puts @svg
267
+ $Fsvg.puts @@svgTail
268
+ $Fsvg.close unless $Fsvg.nil?
269
+ end
270
+
271
+ def initDashHTML(dashName, dashNum, dashCount)
272
+ @htmlHead = '<!DOCTYPE HTML PUBLIC "-//W3C//Dth HTML 3.2 Final//EN">'
273
+ @htmlHead << "\n<HTML>"
274
+ @htmlHead << "\n<HEAD>"
275
+
276
+ @htmlHead << "<TITLE>#{dashName}Dashboard Schematic</TITLE>"
277
+ @htmlHead << "\n<META NAME=\"Generator\" CONTENT=\"xraydashboards.rb\">"
278
+ @htmlHead << "\n<META NAME=\"Author\" CONTENT=\"Chris Gerrard\">"
279
+ @htmlHead << "\n<META NAME=\"Copyright\" CONTENT=\"2012, 2015 Chris Gerrard all rights reserved.\">"
280
+ @htmlHead << "\n<META NAME=\"Keywords\" CONTENT=\"xraydashboards.rb TWB Tools\">"
281
+ @htmlHead << "\n<META NAME=\"Description\" CONTENT=\"Creates an interactive diagram of Tableau dashboard components with associated table of values.\">"
282
+
283
+ @htmlHead << "\n<style>"
284
+
285
+ @htmlHead << "\n#LeftTitle {font: Verdana, Calibri, Geneva, sans-serif; font-size:1em; float:left; margin: 0 5px 0 0; vertical-align:top; text-align:left;}"
286
+ @htmlHead << "\n#RightTitle {font: Verdana, Calibri, Geneva, sans-serif; font-size:1em; float:left; margin: 0 0 0 10px; vertical-align:top;}"
287
+ @htmlHead << "\n#LeftColumn {font: Verdana, Calibri, Geneva, sans-serif; font-size:1em; float:left; margin: 0 5px 0 0; vertical-align:top; text-align:left;}"
288
+ @htmlHead << "\n#RightColumn {font: Verdana, Calibri, Geneva, sans-serif; font-size:1em; float:left; margin: 0 0 0 10px; vertical-align:top;}"
289
+
290
+ @htmlHead << "\ntable { font-family: Verdana, Calibri, Geneva, sans-serif; font-size:.9em; border-collapse:collapse;padding:5 15 5 10; text-align:right;}"
291
+ @htmlHead << "\ntable td, table th { font-family: Verdana, Calibri, Geneva, sans-serif; font-size:.9em; border:3px solid #B0C4DE; padding:0 0 0 0; }"
292
+ @htmlHead << "\ntable th { font-family: Verdana, Calibri, Geneva, sans-serif; font-size:.9em; font-size:.8em; background-color:lightgrey; color:black; }"
293
+ @htmlHead << "\ntable tr.alt td { font-family: Verdana, Calibri, Geneva, sans-serif; font-size:.9em; border:1px solid #B0C4DE; color:#000; background-color:#EAF2D3; }"
294
+
295
+ @htmlHead << "\n</style>"
296
+ @htmlHead << "\n<script>\n"
297
+ @htmlHead << @jsVars % [@maxSVGDim, @maxDashWidth, @maxDashHeight, @maxSVGDim/@maxDashWidth]
298
+ @htmlHead << "\nfunction highlightDashComponent(id)"
299
+ @htmlHead << "\n{"
300
+ @htmlHead << "\n element = document.getElementById(id)"
301
+ @htmlHead << "\n oldFill = element.getAttribute('fill')"
302
+ @htmlHead << "\n oldOpac = element.getAttribute('opacity')"
303
+ @htmlHead << "\n oldStroW = element.getAttribute('stroke-width')"
304
+ @htmlHead << "\n element.setAttribute('fill','darkgrey')"
305
+ @htmlHead << "\n element.setAttribute('opacity','1')"
306
+ @htmlHead << "\n element.setAttribute('stroke-width','800')"
307
+ @htmlHead << "\n // alert(id + ' HIGH fill: ' + oldFill)"
308
+ @htmlHead << "\n}"
309
+ @htmlHead << "\nfunction resetDashComponent(id)"
310
+ @htmlHead << "\n{"
311
+ @htmlHead << "\n element = document.getElementById(id)"
312
+ @htmlHead << "\n element.setAttribute('fill',oldFill)"
313
+ @htmlHead << "\n element.setAttribute('opacity',oldOpac)"
314
+ @htmlHead << "\n element.setAttribute('stroke-width',oldStroW)"
315
+ @htmlHead << "\n}\n"
316
+ @htmlHead << @fnScaleDash
317
+ @htmlHead << "\n</script>"
318
+
319
+ @htmlHead << "\n</HEAD>"
320
+
321
+ @htmlHead << "\n<BODY BGCOLOR=\"#FFFFFF\">\n"
322
+ @htmlHead << <<-PAGELEAD
323
+ <div id='LeftTitle' width='45%'>
324
+ <p><span style='font: normal 16px Verdana, Calibri, Geneva, sans-serif; color: #000000;padding: 0px 10px 0px 15px;'>#{@workbook.name}</span><br />
325
+ <span style='font: normal 12px Verdana, Calibri, Geneva, sans-serif; color: #000000;padding: 10px 10px 0px 15px;'>Dashboard: \"#{dashName}\" \##{dashNum} of #{dashCount} </span></p>
326
+ </div>
327
+ <div id='RightTitle' width='45%'>
328
+ <p><span style='font: normal 12px Verdana, Calibri, Geneva, sans-serif; color: #351c75;padding: 10px 10px 0px 15px;'>
329
+ Resize the Dashboard, from 10-100% :
330
+ </span>
331
+ <input id='scaleFactor' type='range' min='10' max='100' value='100' step='5' onChange='scaleDash();'> <span id='result' style='font: normal 12px Verdana, Calibri, Geneva, sans-serif; color: #351c75;padding: 10px 10px 0px 15px;'>scale: 100%</span>
332
+ </p>
333
+ </div>
334
+ <div style='clear:both;'></div>
335
+ PAGELEAD
336
+ end
337
+
338
+ def termDashHTML
339
+ dhtml = ''
340
+ dhtml << @htmlHead
341
+ dhtml << '<div id="LeftColumn" width="620">'
342
+ dhtml << @svgHead
343
+ dhtml << @svg
344
+ dhtml << @@svgTail
345
+ dhtml << '</div>'
346
+ dhtml << '<div id="RightColumn">'
347
+ dhtml << @@htmlTableLead
348
+ dhtml << @@htmlTableHead
349
+ dhtml << @htmlTable
350
+ dhtml << ('</table>')
351
+ dhtml << @@htmlTableFNote
352
+ dhtml << '</div>'
353
+ dhtml << ('</BODY>')
354
+ dhtml << ('</HTML>')
355
+ end
356
+
357
+ def setScale dashboard
358
+ dashSize = dashboard.node.xpath('./size')
359
+ @maxDashWidth = 0
360
+ @maxDashHeight = 0
361
+ if dashSize.nil?
362
+ then
363
+ $maxWidth = 0
364
+ $maxHeight = 0
365
+ else
366
+ sizeNode = dashSize.first
367
+ if sizeNode.nil?
368
+ then
369
+ $maxWidth = 0
370
+ $maxHeight = 0
371
+ else
372
+ $maxWidth = if sizeNode.attribute('maxwidth').nil? then 1000.0 else sizeNode.attribute('maxwidth').text.to_f end
373
+ $maxHeight = if sizeNode.attribute('maxheight').nil? then 1000.0 else sizeNode.attribute('maxheight').text.to_f end
374
+ end
375
+ end
376
+ end
377
+
378
+ def recordZone(dashName, zone, thcnt)
379
+ id = "#{zone.attribute('id')}-:-#{thcnt}"
380
+ zoneName = getZoneName zone
381
+ type = getZoneType(zone)
382
+ fillColor = getFillColor(type)
383
+ fill = "fill=\"#{fillColor}\";"
384
+ @svg += "\n<g>"
385
+ rxy = ' rx="3000" ry="3000"'
386
+ drawLine = ''
387
+ if type.start_with?('Layout') then
388
+ @strokeWidth = 500
389
+ strokeStyle = 'stroke:black;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1200, 1200;stroke-dashoffset:0;'
390
+ x = zone.attribute('x').text.to_i + @strokeWidth
391
+ y = zone.attribute('y').text.to_i + @strokeWidth
392
+ w = zone.attribute('w').text.to_i
393
+ h = zone.attribute('h').text.to_i
394
+ rxy = ''
395
+ drawLine = if /.*ertical.*/ =~ type then 'vert' else 'horz' end
396
+ # ecx = x+w/2
397
+ # erx = w/2
398
+ # ecy = y+h/2
399
+ # ery = h/2
400
+ # @svg += "\n<ellipse cx=\"#{ecx}\" cy=\"#{ecy}\" rx=\"#{erx}\" ry=\"#{ery}\" style=\"stroke:darkblue;fill:blue;\"/>"
401
+ else
402
+ drawLine = ''
403
+ rxy = ' rx="3000" ry="3000"'
404
+ # @strokeWidth = 200
405
+ strokeStyle = 'stroke:blue;'
406
+ x = zone.attribute('x').text.to_i + @strokeWidth
407
+ y = zone.attribute('y').text.to_i + @strokeWidth
408
+ w = zone.attribute('w').text.to_i
409
+ h = zone.attribute('h').text.to_i
410
+ end
411
+ cw = x+w
412
+ ch = y+h
413
+ @maxDashWidth = [@maxDashWidth, cw].max
414
+ @maxDashHeight = [@maxDashHeight, ch].max
415
+ @svg += "\n<rect"
416
+ @svg += " id=\"#{id}-:-#{thcnt}\""
417
+ @svg += " width=\"#{w}\""
418
+ @svg += " height=\"#{h}\""
419
+ @svg += " x=\"#{x}\""
420
+ @svg += " y=\"#{y}\""
421
+ @svg += rxy
422
+ @svg += " fill=\"#{fillColor}\""
423
+ @svg += " opacity=\"0.2\""
424
+ @svg += " stroke-width=\"#{@strokeWidth}\""
425
+ @svg += " style=\"color:#000000;#{strokeStyle}stroke-opacity:1;\" />"
426
+ @svg += "\n"
427
+ case drawLine
428
+ when 'vert' then @svg += "\n<line x1=\"#{x}\" y1=\"#{y}\" x2=\"#{x+w}\" y2=\"#{y+h}\" style=\"stroke:grey;stroke-width:100\"/>"
429
+ when 'horz' then @svg += "\n<line x1=\"#{x}\" y1=\"#{y+h}\" x2=\"#{x+w}\" y2=\"#{y}\" style=\"stroke:grey;stroke-width:100\"/>"
430
+ end
431
+ case type.downcase
432
+ when 'color legend' then addColorLegend(x,y,w,h)
433
+ when 'filter' then addFilterLegend(x,y,w,h)
434
+ when 'chart' then addChartLegend(x,y,w,h)
435
+ when 'image' then addImageLegend(x,y,w,h)
436
+ when 'empty',
437
+ 'blank' then addLabel(x,y,w,h,'[ ]')
438
+ when 'paramctrl',
439
+ 'parameter' then addLabel(x,y,w,h,'>...<')
440
+ when 'size',
441
+ 'size legend' then addSizeLegend(x,y,w,h)
442
+ when 'shape',
443
+ 'shape legend' then addShapeLegend(x,y,w,h)
444
+ when 'text' then addLabel(x,y,w,h,'loren ipsum')
445
+ # else addLabel(x,y,w,h, "#{type} id: #{id}")
446
+ end
447
+ @svg += "\n</g>\n"
448
+ @htmlTable += "\n<tr style=\"cursor: pointer;\" onMouseOver=\"highlightDashComponent('#{id}-:-#{thcnt}')\" onMouseOut=\"resetDashComponent('#{id}-:-#{thcnt}')\">"
449
+ @htmlTable += " <td style='text-align: left;'>#{zoneName}</td>"
450
+ @htmlTable += " <td style='text-align: left;'>#{type}</td>"
451
+ @htmlTable += " <td>#{x.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse}</td>"
452
+ @htmlTable += " <td>#{y.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse}</td>"
453
+ @htmlTable += " <td>#{w.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse}</td>"
454
+ @htmlTable += " <td>#{h.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse}</td>"
455
+ @htmlTable += " <td>#{id.gsub('-:-','.')}.#{thcnt}</td>"
456
+ @htmlTable += "</tr>"
457
+ return @htmlTable
458
+ end # def recordZone
459
+
460
+ def addLabel(x,y,w,h,type)
461
+ tw = type.length * @@fontSize
462
+ centerX = x + w/2
463
+ centerY = y + h/2
464
+ transform = if tw > w*1.2 then " transform=\"rotate(-90 #{centerX} #{centerY})\"" else '' end
465
+ twh = tw/4
466
+ tx = x + w/2 - twh
467
+ ty = y + (h/2) + 500
468
+ @svg += "<text x=\"#{tx}\" y=\"#{ty}\" font-size=\"#{@@fontSize}\" font-family=\"Verdana,Arial\"#{transform}>#{type}</text>"
469
+ end
470
+
471
+ def addFilterLegend(x,y,w,h)
472
+ lx = x + w/2 - @@fontSize
473
+ ly = y + h/2 + @@fontSize/2
474
+ @svg += "<text x=\"#{lx}\" y=\"#{ly}\""
475
+ @svg += ' id="FilterYes"'
476
+ @svg += " font-size=\"#{@@fontSize}\""
477
+ @svg += ' font-family="Verdana,Arial">'
478
+ @svg += "&#x2713; X</text>"
479
+ end
480
+
481
+ def addChartLegend(x,y,w,h)
482
+ barHeight = h/4
483
+ barWidth = [w/20, barHeight/5].min
484
+ bx = x + (w/2) - barWidth
485
+ by = y + (h/2) - (barHeight/2)
486
+ @svg += "\n<rect x=\"#{bx}\" y=\"#{by}\" width=\"#{barWidth}\" height=\"#{barHeight}\" fill=\"blue\" stroke-width=\"200\" stroke=\"black\"/>"
487
+ @svg += "\n<rect x=\"#{bx + 1.5*barWidth}\" y=\"#{by+barHeight/2}\" width=\"#{barWidth}\" height=\"#{barHeight/2}\" fill=\"orange\" stroke-width=\"100\" stroke=\"black\"/>"
488
+ @svg += "\n"
489
+ end
490
+
491
+ def addImageLegend(x,y,w,h)
492
+ cameraW = @@shapeRadius * 5
493
+ cameraH = @@shapeRadius * 3
494
+ cx = x + w/2 - cameraW/2
495
+ cy = y + h/2
496
+ lx = cx + @@shapeDiam
497
+ ly = cy + cameraH/2
498
+ vw = cameraW / 5
499
+ vh = cameraH / 5
500
+ vx = cx + cameraW - vw*1.5
501
+ vy = cy + vh
502
+ @svg += "\n<rect x=\"#{cx}\" y=\"#{cy}\" width=\"#{cameraW}\" height=\"#{cameraH}\" fill=\"lightgrey\" stroke-width=\"200\" stroke=\"black\" opacity=\"1\"/>"
503
+ @svg += "\n<rect x=\"#{vx}\" y=\"#{vy}\" width=\"#{vw}\" height=\"#{vh}\" fill=\"lightgrey\" stroke-width=\"200\" stroke=\"black\" opacity=\"1\"/>"
504
+ @svg += "\n<circle cx=\"#{lx}\" cy=\"#{ly}\" r=\"#{@@shapeRadius*0.8}\" fill=\"lightgrey\" stroke-width=\"200\" stroke=\"black\" opacity=\"1\"/>"
505
+ @svg += "\n"
506
+ end
507
+
508
+ def addColorLegend(x,y,w,h)
509
+ d = @@shapeDiam
510
+ cx = x + w/2
511
+ cy = y + h/2
512
+ sx = cx - (2 * d)
513
+ sy = cy - (d / 2)
514
+ @svg += "\n<rect x=\"#{sx}\" y=\"#{sy}\" width=\"#{d}\" height=\"#{d}\" fill=\"blue\" stroke-width=\"none\" stroke=\"none\" opacity=\"1\"/>"
515
+ @svg += "\n<rect x=\"#{sx + 1.5*d}\" y=\"#{sy}\" width=\"#{d}\" height=\"#{d}\" fill=\"red\" stroke-width=\"none\" stroke=\"none\" opacity=\"1\"/>"
516
+ @svg += "\n<rect x=\"#{sx + 3*d}\" y=\"#{sy}\" width=\"#{d}\" height=\"#{d}\" fill=\"green\" stroke-width=\"none\" stroke=\"none\" opacity=\"1\"/>"
517
+ @svg += "\n"
518
+ end
519
+
520
+ def addShapeLegend(x,y,w,h)
521
+ centerX = x + w/2
522
+ centerY = y + h/2
523
+ cx = centerX - 3*@@shapeRadius
524
+ cy = centerY + @@shapeRadius/2
525
+ rx = centerX - @@shapeRadius
526
+ ry = centerY - @@shapeRadius/2
527
+ tx = centerX + @@shapeDiam
528
+ ty = centerY - @@shapeRadius/2
529
+ @svg += "\n<circle cx=\"#{cx}\" cy=\"#{cy}\" r=\"#{@@shapeRadius}\" fill=\"#008080\" stroke-width=\"200\" stroke=\"black\" opacity=\"0.8\"/>"
530
+ @svg += "\n<rect x=\"#{rx}\" y=\"#{ry}\" width=\"#{@@shapeDiam}\" height=\"#{@@shapeDiam}\" fill=\"#008080\" stroke-width=\"200\" stroke=\"black\" opacity=\"0.8\"/>"
531
+ @svg += "\n<polyline points=\"#{tx} ,#{ty+@@shapeDiam}
532
+ #{tx+@@shapeDiam} ,#{ty+@@shapeDiam}
533
+ #{tx+@@shapeRadius},#{ty}
534
+ #{tx} ,#{ty+@@shapeDiam}\" fill=\"#008080\" stroke-width=\"200\" stroke=\"black\" opacity=\"0.8\"/>"
535
+ end
536
+
537
+ def addSizeLegend(x,y,w,h)
538
+ centerX = x + w/2
539
+ centerY = y + h/2
540
+ smallRadius = @@shapeRadius/2
541
+ medRadius = @@shapeRadius
542
+ largeRadius = smallRadius + medRadius
543
+ sx = centerX - medRadius*3
544
+ mx = centerX - medRadius
545
+ lx = centerX + medRadius*2
546
+ @svg += "\n<circle cx=\"#{sx}\" cy=\"#{centerY}\" r=\"#{smallRadius}\" fill=\"#008080\" stroke-width=\"200\" stroke=\"black\" opacity=\"0.8\"/>"
547
+ @svg += "\n<circle cx=\"#{mx}\" cy=\"#{centerY}\" r=\"#{medRadius}\" fill=\"#008080\" stroke-width=\"200\" stroke=\"black\" opacity=\"0.8\"/>"
548
+ @svg += "\n<circle cx=\"#{lx}\" cy=\"#{centerY}\" r=\"#{largeRadius}\" fill=\"#008080\" stroke-width=\"200\" stroke=\"black\" opacity=\"0.8\"/>"
549
+ end
550
+
551
+ end
552
+
553
+ end