twb 0.0.33 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/twb.rb +2 -1
- data/lib/twb/docdashboard.rb +34 -35
- data/lib/twb/util/hashtohtml.rb +1 -1
- data/lib/twb/util/htmllistcollapsible.rb +0 -12
- data/lib/twb/util/xraydashboards.rb +553 -0
- data/lib/twb/workbook.rb +9 -2
- data/test/testDashboardXRay.rb +49 -0
- data/test/testDocDashboard.rb +0 -1
- data/test/testHTMLList.rb +3 -1
- data/{testTwbWrite.rb → test/testTwbWrite.rb} +4 -2
- data/twb-0.0.33.gem +0 -0
- metadata +4 -9
- data/test/No Content.injected.twb +0 -82
- data/test/No Dashboards.injected.twb +0 -618
- data/test/Special Documentation.html +0 -198
- data/test/TableauDocInlineCSS.html +0 -165
- data/test/Web Page Dashboards.injected.twb +0 -1361
- data/test/collapsibleList.html +0 -198
- data/testTwbGem.rb +0 -67
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.
|
16
|
+
VERSION = '0.0.34'
|
16
17
|
end
|
17
18
|
|
data/lib/twb/docdashboard.rb
CHANGED
@@ -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 =
|
93
|
-
@winnode =
|
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
|
data/lib/twb/util/hashtohtml.rb
CHANGED
@@ -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 = '–'
|
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 –100,000– 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 += "✓ 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
|