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 +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
|