1
|
/*
|
2
|
JavaScript Scalebar for MapServer (scalebar.js)
|
3
|
|
4
|
Copyright (c) 2005 Tim Schaub of CommEn Space (http://www.commenspace.org)
|
5
|
|
6
|
This is free software; you can redistribute it and/or modify it under
|
7
|
the terms of the GNU Lesser General Public License as published by the
|
8
|
Free Software Foundation; either version 2.1 of the License, or (at
|
9
|
your option) any later version.
|
10
|
|
11
|
This software is distributed in the hope that it will be useful, but
|
12
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
13
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
14
|
License for more details.
|
15
|
|
16
|
You should have received a copy of the GNU Lesser General Public License
|
17
|
along with this software; if not, write to the Free Software Foundation,
|
18
|
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
|
20
|
v1.3 - scalebar is now centered on .sbWrapper div by default, more css control
|
21
|
- reduced likelihood of displaying very large numbers
|
22
|
- added condition to deal with @import styles (thanks dokai)
|
23
|
|
24
|
*/
|
25
|
/*
|
26
|
adapted from http://mapserver.commenspace.org/tools/scalebar/
|
27
|
|
28
|
$Id: MapScaleBar.js 3879 2008-02-27 14:20:29Z gjvoosten $
|
29
|
*/
|
30
|
|
31
|
// Ensure this object's dependancies are loaded.
|
32
|
mapbuilder.loadScript(baseDir+"/widget/WidgetBase.js");
|
33
|
|
34
|
/**
|
35
|
* Widget to display the scale of a map as a graphical bar. The model of this widget
|
36
|
* must have an extent object associated with it which is the case when the
|
37
|
* model has a MapContanier widget.
|
38
|
*
|
39
|
* @constructor
|
40
|
* @base WidgetBase
|
41
|
* @param widgetNode This widget's object node from the configuration document.
|
42
|
* @param model The model that this widget is a view of.
|
43
|
*/
|
44
|
|
45
|
function MapScaleBar(widgetNode, model) {
|
46
|
WidgetBase.apply(this,new Array(widgetNode, model));
|
47
|
|
48
|
// default properties
|
49
|
// may be modified after construction
|
50
|
// if modified after ScaleBar.place(), use ScaleBar.update()
|
51
|
this.scaleDenominator = 1; //this will get updated on the first paint
|
52
|
|
53
|
this.displaySystem = this.getProperty("mb:displaySystem", "metric"); // metric, english or nautical supported
|
54
|
this.minWidth = this.getProperty("mb:minWidth", 100); // pixels
|
55
|
this.maxWidth = this.getProperty("mb:maxWidth", 200); // pixels
|
56
|
this.divisions = this.getProperty("mb:divisions", 2);
|
57
|
this.subdivisions = this.getProperty("mb:subdivisions", 2);
|
58
|
this.showMinorMeasures = Mapbuilder.parseBoolean(this.getProperty("mb:showMinorMeasures", false));
|
59
|
this.abbreviateLabel = Mapbuilder.parseBoolean(this.getProperty("mb:abbreviateLabel", false));
|
60
|
this.singleLine = Mapbuilder.parseBoolean(this.getProperty("mb:singleLine", false));
|
61
|
this.align = this.getProperty("mb:align", "center"); // left, center, or right supported
|
62
|
this.resolution = 72; // dpi
|
63
|
|
64
|
// create scalebar elements
|
65
|
this.containerId = this.outputNodeId;
|
66
|
this.labelContainerId = this.containerId + "Label";
|
67
|
this.graphicsContainerId = this.containerId + "Graphics";
|
68
|
this.numbersContainerId = this.containerId + "Numbers";
|
69
|
|
70
|
/**
|
71
|
* adds a bbox listener on the model
|
72
|
*/
|
73
|
this.model.addListener("bbox", this.update, this);
|
74
|
this.model.addListener("refresh", this.update, this);
|
75
|
}
|
76
|
|
77
|
MapScaleBar.prototype.getContainerNode = function() {
|
78
|
var node = document.getElementById(this.containerId);
|
79
|
if (!node) {
|
80
|
var node = document.createElement('div');
|
81
|
node.className = 'sbWrapper';
|
82
|
node.style.position = 'relative';
|
83
|
node.setAttribute("id",this.containerId);
|
84
|
}
|
85
|
return node;
|
86
|
}
|
87
|
MapScaleBar.prototype.getGraphicsContainerNode = function() {
|
88
|
var node = document.getElementById(this.graphicsContainerId);
|
89
|
if (!node) {
|
90
|
var node = document.createElement('div');
|
91
|
node.style.position = 'absolute';
|
92
|
node.className = 'sbGraphicsContainer';
|
93
|
node.setAttribute("id",this.graphicsContainerId);
|
94
|
|
95
|
// put in some markers and bar pieces so style attributes can be grabbed
|
96
|
// this is a solution for Safari support
|
97
|
var markerMajor = document.createElement('div');
|
98
|
markerMajor.className = 'sbMarkerMajor';
|
99
|
node.appendChild(markerMajor);
|
100
|
var markerMinor = document.createElement('div');
|
101
|
markerMinor.className = 'sbMarkerMinor';
|
102
|
node.appendChild(markerMinor);
|
103
|
var barPiece = document.createElement('div');
|
104
|
barPiece.className = 'sbBar';
|
105
|
node.appendChild(barPiece);
|
106
|
var barPieceAlt = document.createElement('div');
|
107
|
barPieceAlt.className = 'sbBarAlt';
|
108
|
node.appendChild(barPieceAlt);
|
109
|
}
|
110
|
return node;
|
111
|
}
|
112
|
MapScaleBar.prototype.getLabelContainerNode = function() {
|
113
|
var node = document.getElementById(this.labelContainerId);
|
114
|
if (!node) {
|
115
|
var node = document.createElement('div');
|
116
|
node.className = 'sbUnitsContainer';
|
117
|
node.style.position = 'absolute';
|
118
|
node.setAttribute("id",this.labelContainerId);
|
119
|
}
|
120
|
return node;
|
121
|
}
|
122
|
MapScaleBar.prototype.getNumbersContainerNode = function() {
|
123
|
var node = document.getElementById(this.numbersContainerId);
|
124
|
if (!node) {
|
125
|
var node = document.createElement('div');
|
126
|
node.style.position = 'absolute';
|
127
|
node.className = 'sbNumbersContainer';
|
128
|
node.setAttribute("id",this.numbersContainerId);
|
129
|
}
|
130
|
return node;
|
131
|
}
|
132
|
|
133
|
/**
|
134
|
* Render the widget.
|
135
|
* @param objRef Pointer to widget object.
|
136
|
*/
|
137
|
MapScaleBar.prototype.update = function(objRef) {
|
138
|
|
139
|
//create the output node the first time this is called
|
140
|
var outputNode = document.getElementById( objRef.outputNodeId );
|
141
|
if (!outputNode) objRef.getNode().appendChild(objRef.getContainerNode());
|
142
|
|
143
|
var scaleDenominator = objRef.model.map.getScale();
|
144
|
if(scaleDenominator != null) {
|
145
|
objRef.scaleDenominator = scaleDenominator;
|
146
|
}
|
147
|
// local functions (and object constructors)
|
148
|
function HandsomeNumber(smallUglyNumber, bigUglyNumber, sigFigs) {
|
149
|
var sigFigs = (sigFigs == null) ? 10 : sigFigs;
|
150
|
var bestScore = Number.POSITIVE_INFINITY;
|
151
|
var bestTieBreaker = Number.POSITIVE_INFINITY;
|
152
|
// if all else fails, return a small ugly number
|
153
|
var handsomeValue = smallUglyNumber;
|
154
|
var handsomeNumDec = 3;
|
155
|
// try the first three comely multiplicands (in order of comliness)
|
156
|
for(var halvingExp = 0; halvingExp < 3; ++halvingExp) {
|
157
|
var comelyMultiplicand = Math.pow(2, (-1 * halvingExp));
|
158
|
var maxTensExp = Math.floor(Math.log(bigUglyNumber / comelyMultiplicand) / Math.LN10)
|
159
|
for(var tensExp = maxTensExp; tensExp > (maxTensExp - sigFigs + 1); --tensExp) {
|
160
|
var numDec = Math.max(halvingExp - tensExp, 0);
|
161
|
var testMultiplicand = comelyMultiplicand * Math.pow(10, tensExp);
|
162
|
// check if there is an integer multiple of testMultiplicand between smallUglyNumber and bigUglyNumber
|
163
|
if((testMultiplicand * Math.floor(bigUglyNumber / testMultiplicand)) >= smallUglyNumber) {
|
164
|
// check if smallUglyNumber is an integer multiple of testMultiplicand
|
165
|
if(smallUglyNumber % testMultiplicand == 0) {
|
166
|
var testMultiplier = smallUglyNumber / testMultiplicand;
|
167
|
}
|
168
|
// otherwise go for the smallest integer multiple between small and big
|
169
|
else {
|
170
|
var testMultiplier = Math.floor(smallUglyNumber / testMultiplicand) + 1;
|
171
|
}
|
172
|
// test against the best (lower == better)
|
173
|
var testScore = testMultiplier + (2 * halvingExp);
|
174
|
var testTieBreaker = (tensExp < 0) ? (Math.abs(tensExp) + 1) : tensExp;
|
175
|
if((testScore < bestScore) || ((testScore == bestScore) && (testTieBreaker < bestTieBreaker))) {
|
176
|
bestScore = testScore;
|
177
|
bestTieBreaker = testTieBreaker;
|
178
|
handsomeValue = (testMultiplicand * testMultiplier).toFixed(numDec);
|
179
|
handsomeNumDec = numDec;
|
180
|
}
|
181
|
}
|
182
|
}
|
183
|
}
|
184
|
this.value = handsomeValue;
|
185
|
this.score = bestScore;
|
186
|
this.tieBreaker = bestTieBreaker;
|
187
|
this.numDec = handsomeNumDec;
|
188
|
}
|
189
|
HandsomeNumber.prototype.toString = function() {
|
190
|
return this.value.toString();
|
191
|
}
|
192
|
HandsomeNumber.prototype.valueOf = function() {
|
193
|
return this.value;
|
194
|
}
|
195
|
function styleValue(aSelector, styleKey) {
|
196
|
// returns an integer value associated with a particular selector and key
|
197
|
// given a stylesheet with .someSelector {border: 2px solid red}
|
198
|
// styleValue('.someSelector', 'borderWidth') returns 2
|
199
|
var aValue = 0;
|
200
|
if(document.styleSheets) {
|
201
|
for(var sheetIndex = document.styleSheets.length - 1; sheetIndex >= 0; --sheetIndex) {
|
202
|
var aSheet = document.styleSheets[sheetIndex];
|
203
|
if(!aSheet.disabled) {
|
204
|
var allRules;
|
205
|
if(typeof(aSheet.rules) == 'undefined') {
|
206
|
if(typeof(aSheet.rules) == 'undefined') {
|
207
|
// can't get rules, assume zero
|
208
|
return 0;
|
209
|
}
|
210
|
else {
|
211
|
allRules = aSheet.rules;
|
212
|
}
|
213
|
}
|
214
|
else {
|
215
|
allRules = aSheet.rules;
|
216
|
}
|
217
|
for(var ruleIndex = 0; ruleIndex < allRules.length; ++ruleIndex) {
|
218
|
var aRule = allRules[ruleIndex];
|
219
|
if(aRule.selectorText && (aRule.selectorText.toLowerCase() == aSelector.toLowerCase())) {
|
220
|
if(aRule.style[styleKey] != '') {
|
221
|
aValue = parseInt(aRule.style[styleKey]);
|
222
|
}
|
223
|
}
|
224
|
}
|
225
|
}
|
226
|
}
|
227
|
}
|
228
|
// if the styleKey was not found, the equivalent value is zero
|
229
|
return aValue ? aValue : 0;
|
230
|
}
|
231
|
function formatNumber(aNumber, numDecimals) {
|
232
|
numDecimals = (numDecimals) ? numDecimals : 0;
|
233
|
var formattedInteger = '' + Math.round(aNumber);
|
234
|
var thousandsPattern = /(-?[0-9]+)([0-9]{3})/;
|
235
|
while(thousandsPattern.test(formattedInteger)) {
|
236
|
formattedInteger = formattedInteger.replace(thousandsPattern, '$1,$2');
|
237
|
}
|
238
|
if(numDecimals > 0) {
|
239
|
var formattedDecimal = Math.floor(Math.pow(10, numDecimals) * (aNumber - Math.round(aNumber)));
|
240
|
if(formattedDecimal == 0) {
|
241
|
return formattedInteger;
|
242
|
}
|
243
|
else {
|
244
|
return formattedInteger + '.' + formattedDecimal;
|
245
|
}
|
246
|
}
|
247
|
else {
|
248
|
return formattedInteger;
|
249
|
}
|
250
|
}
|
251
|
// update the container title (for displaying scale as a tooltip)
|
252
|
var container = objRef.getContainerNode();
|
253
|
var graphicsContainer = objRef.getGraphicsContainerNode();
|
254
|
var labelContainer = objRef.getLabelContainerNode();
|
255
|
var numbersContainer = objRef.getNumbersContainerNode();
|
256
|
container.title = mbGetMessage("scale", formatNumber(objRef.scaleDenominator));
|
257
|
// measurementProperties holds display units, abbreviations,
|
258
|
// and conversion to inches (since we're using dpi) - per measurement sytem
|
259
|
var measurementProperties = new Object();
|
260
|
measurementProperties.english = {
|
261
|
units: [mbGetMessage("unitMiles"), mbGetMessage("unitFeet"), mbGetMessage("unitInches")],
|
262
|
abbr: [mbGetMessage("unitMilesAbbr"), mbGetMessage("unitFeetAbbr"), mbGetMessage("unitInchesAbbr")],
|
263
|
inches: [63360, 12, 1]
|
264
|
}
|
265
|
measurementProperties.nautical = {
|
266
|
units: [mbGetMessage("unitNauticalMiles"), mbGetMessage("unitFeet"), mbGetMessage("unitInches")],
|
267
|
abbr: [mbGetMessage("unitNauticalMilesAbbr"), mbGetMessage("unitFeetAbbr"), mbGetMessage("unitInchesAbbr")],
|
268
|
inches: [72913.3860, 12, 1]
|
269
|
}
|
270
|
measurementProperties.metric = {
|
271
|
units: [mbGetMessage("unitKilometers"), mbGetMessage("unitMeters"), mbGetMessage("unitCentimeters")],
|
272
|
abbr: [mbGetMessage("unitKilometersAbbr"), mbGetMessage("unitMetersAbbr"), mbGetMessage("unitCentimetersAbbr")],
|
273
|
inches: [39370.07874, 39.370079, 0.393701]
|
274
|
}
|
275
|
// check each measurement unit in the display system
|
276
|
var comparisonArray = new Array();
|
277
|
for(var unitIndex = 0; unitIndex < measurementProperties[objRef.displaySystem].units.length; ++unitIndex) {
|
278
|
comparisonArray[unitIndex] = new Object();
|
279
|
var pixelsPerDisplayUnit = objRef.resolution * measurementProperties[objRef.displaySystem].inches[unitIndex] / objRef.scaleDenominator;
|
280
|
var minSDDisplayLength = (objRef.minWidth / pixelsPerDisplayUnit) / (objRef.divisions * objRef.subdivisions);
|
281
|
var maxSDDisplayLength = (objRef.maxWidth / pixelsPerDisplayUnit) / (objRef.divisions * objRef.subdivisions);
|
282
|
// add up scores for each marker (even if numbers aren't displayed)
|
283
|
for(var valueIndex = 0; valueIndex < (objRef.divisions * objRef.subdivisions); ++valueIndex) {
|
284
|
var minNumber = minSDDisplayLength * (valueIndex + 1);
|
285
|
var maxNumber = maxSDDisplayLength * (valueIndex + 1);
|
286
|
var niceNumber = new HandsomeNumber(minNumber, maxNumber);
|
287
|
comparisonArray[unitIndex][valueIndex] = {value: (niceNumber.value / (valueIndex + 1)), score: 0, tieBreaker: 0, numDec: 0, displayed: 0};
|
288
|
// now tally up scores for all values given this subdivision length
|
289
|
for(var valueIndex2 = 0; valueIndex2 < (objRef.divisions * objRef.subdivisions); ++valueIndex2) {
|
290
|
displayedValuePosition = niceNumber.value * (valueIndex2 + 1) / (valueIndex + 1);
|
291
|
niceNumber2 = new HandsomeNumber(displayedValuePosition, displayedValuePosition);
|
292
|
var isMajorMeasurement = ((valueIndex2 + 1) % objRef.subdivisions == 0);
|
293
|
var isLastMeasurement = ((valueIndex2 + 1) == (objRef.divisions * objRef.subdivisions));
|
294
|
if((objRef.singleLine && isLastMeasurement) || (!objRef.singleLine && (isMajorMeasurement || objRef.showMinorMeasures))) {
|
295
|
// count scores for displayed marker measurements
|
296
|
comparisonArray[unitIndex][valueIndex].score += niceNumber2.score;
|
297
|
comparisonArray[unitIndex][valueIndex].tieBreaker += niceNumber2.tieBreaker;
|
298
|
comparisonArray[unitIndex][valueIndex].numDec = Math.max(comparisonArray[unitIndex][valueIndex].numDec, niceNumber2.numDec);
|
299
|
comparisonArray[unitIndex][valueIndex].displayed += 1;
|
300
|
}
|
301
|
else {
|
302
|
// count scores for non-displayed marker measurements
|
303
|
comparisonArray[unitIndex][valueIndex].score += niceNumber2.score / objRef.subdivisions;
|
304
|
comparisonArray[unitIndex][valueIndex].tieBreaker += niceNumber2.tieBreaker / objRef.subdivisions;
|
305
|
}
|
306
|
}
|
307
|
// adjust scores so numbers closer to 1 are preferred for display
|
308
|
var scoreAdjustment = (unitIndex + 1) * comparisonArray[unitIndex][valueIndex].tieBreaker / comparisonArray[unitIndex][valueIndex].displayed;
|
309
|
comparisonArray[unitIndex][valueIndex].score *= scoreAdjustment;
|
310
|
}
|
311
|
}
|
312
|
// get the value (subdivision length) with the lowest cumulative score
|
313
|
var subdivisionDisplayLength = null;
|
314
|
var displayUnits = null;
|
315
|
var displayUnitsAbbr = null;
|
316
|
var subdivisionPixelLength = null;
|
317
|
var bestScore = Number.POSITIVE_INFINITY;
|
318
|
var bestTieBreaker = Number.POSITIVE_INFINITY;
|
319
|
var numDec = 0;
|
320
|
for(var unitIndex = 0; unitIndex < comparisonArray.length; ++unitIndex) {
|
321
|
for(valueIndex in comparisonArray[unitIndex]) {
|
322
|
if((comparisonArray[unitIndex][valueIndex].score < bestScore) || ((comparisonArray[unitIndex][valueIndex].score == bestScore) && (comparisonArray[unitIndex][valueIndex].tieBreaker < bestTieBreaker))) {
|
323
|
bestScore = comparisonArray[unitIndex][valueIndex].score;
|
324
|
bestTieBreaker = comparisonArray[unitIndex][valueIndex].tieBreaker;
|
325
|
subdivisionDisplayLength = comparisonArray[unitIndex][valueIndex].value;
|
326
|
numDec = comparisonArray[unitIndex][valueIndex].numDec;
|
327
|
displayUnits = measurementProperties[objRef.displaySystem].units[unitIndex];
|
328
|
displayUnitsAbbr = measurementProperties[objRef.displaySystem].abbr[unitIndex];
|
329
|
pixelsPerDisplayUnit = objRef.resolution * measurementProperties[objRef.displaySystem].inches[unitIndex] / objRef.scaleDenominator;
|
330
|
subdivisionPixelLength = pixelsPerDisplayUnit * subdivisionDisplayLength; // round before use in style
|
331
|
}
|
332
|
}
|
333
|
}
|
334
|
// determine offsets for graphic elements
|
335
|
var xOffsetMarkerMajor = (styleValue('.sbMarkerMajor', 'borderLeftWidth') + styleValue('.sbMarkerMajor', 'width') + styleValue('.sbMarkerMajor', 'borderRightWidth')) / 2;
|
336
|
var xOffsetMarkerMinor = (styleValue('.sbMarkerMinor', 'borderLeftWidth') + styleValue('.sbMarkerMinor', 'width') + styleValue('.sbMarkerMinor', 'borderRightWidth')) / 2;
|
337
|
var xOffsetBar = (styleValue('.sbBar', 'borderLeftWidth') + styleValue('.sbBar', 'border-right-width')) / 2;
|
338
|
var xOffsetBarAlt = (styleValue('.sbBarAlt', 'borderLeftWidth') + styleValue('.sbBarAlt', 'borderRightWidth')) / 2;
|
339
|
// support for browsers without the Document.styleSheets property (Opera)
|
340
|
if(!document.styleSheets) {
|
341
|
// this is a two part hack, one for the offsets here and one for the css below
|
342
|
xOffsetMarkerMajor = 0.5;
|
343
|
xOffsetMarkerMinor = 0.5;
|
344
|
}
|
345
|
// clean out any old content from containers
|
346
|
while(labelContainer.hasChildNodes()) {
|
347
|
labelContainer.removeChild(labelContainer.firstChild);
|
348
|
}
|
349
|
while(graphicsContainer.hasChildNodes()) {
|
350
|
graphicsContainer.removeChild(graphicsContainer.firstChild);
|
351
|
}
|
352
|
while(numbersContainer.hasChildNodes()) {
|
353
|
numbersContainer.removeChild(numbersContainer.firstChild);
|
354
|
}
|
355
|
// create all divisions
|
356
|
var aMarker, aBarPiece, numbersBox, xOffset;
|
357
|
var alignmentOffset = {
|
358
|
left: 0,
|
359
|
center: (-1 * objRef.divisions * objRef.subdivisions * subdivisionPixelLength / 2),
|
360
|
right: (-1 * objRef.divisions * objRef.subdivisions * subdivisionPixelLength)
|
361
|
}
|
362
|
var xPosition = 0 + alignmentOffset[objRef.align];
|
363
|
var markerMeasure = 0;
|
364
|
for(var divisionIndex = 0; divisionIndex < objRef.divisions; ++divisionIndex) {
|
365
|
// set xPosition and markerMeasure to start of division
|
366
|
xPosition = divisionIndex * objRef.subdivisions * subdivisionPixelLength;
|
367
|
xPosition += alignmentOffset[objRef.align];
|
368
|
markerMeasure = (divisionIndex == 0) ? 0 : ((divisionIndex * objRef.subdivisions) * subdivisionDisplayLength).toFixed(numDec);
|
369
|
// add major marker
|
370
|
aMarker = document.createElement('div');
|
371
|
aMarker.className = 'sbMarkerMajor';
|
372
|
aMarker.style.position = 'absolute';
|
373
|
aMarker.style.overflow = 'hidden';
|
374
|
aMarker.style.left = Math.round(xPosition - xOffsetMarkerMajor) + 'px';
|
375
|
aMarker.appendChild(document.createTextNode(' '));
|
376
|
graphicsContainer.appendChild(aMarker);
|
377
|
// add initial measure
|
378
|
if(!objRef.singleLine) {
|
379
|
numbersBox = document.createElement('div');
|
380
|
numbersBox.className = 'sbNumbersBox';
|
381
|
numbersBox.style.position = 'absolute';
|
382
|
numbersBox.style.overflow = 'hidden';
|
383
|
numbersBox.style.textAlign = 'center';
|
384
|
if(objRef.showMinorMeasures) {
|
385
|
numbersBox.style.width = Math.round(subdivisionPixelLength * 2) + 'px';
|
386
|
numbersBox.style.left = Math.round(xPosition - subdivisionPixelLength) + 'px';
|
387
|
}
|
388
|
else {
|
389
|
numbersBox.style.width = Math.round(objRef.subdivisions * subdivisionPixelLength * 2) + 'px';
|
390
|
numbersBox.style.left = Math.round(xPosition - (objRef.subdivisions * subdivisionPixelLength)) + 'px';
|
391
|
}
|
392
|
numbersBox.appendChild(document.createTextNode(markerMeasure));
|
393
|
numbersContainer.appendChild(numbersBox);
|
394
|
}
|
395
|
// create all subdivisions
|
396
|
for(var subdivisionIndex = 0; subdivisionIndex < objRef.subdivisions; ++subdivisionIndex) {
|
397
|
aBarPiece = document.createElement('div');
|
398
|
aBarPiece.style.position = 'absolute';
|
399
|
aBarPiece.style.overflow = 'hidden';
|
400
|
aBarPiece.style.width = Math.round(subdivisionPixelLength) + 'px';
|
401
|
if((subdivisionIndex % 2) == 0) {
|
402
|
aBarPiece.className = 'sbBar';
|
403
|
aBarPiece.style.left = Math.round(xPosition - xOffsetBar) + 'px';
|
404
|
}
|
405
|
else {
|
406
|
aBarPiece.className = 'sbBarAlt';
|
407
|
aBarPiece.style.left = Math.round(xPosition - xOffsetBarAlt) + 'px';
|
408
|
}
|
409
|
aBarPiece.appendChild(document.createTextNode(' '));
|
410
|
graphicsContainer.appendChild(aBarPiece);
|
411
|
// add minor marker if not the last subdivision
|
412
|
if(subdivisionIndex < (objRef.subdivisions - 1)) {
|
413
|
// set xPosition and markerMeasure to end of subdivision
|
414
|
xPosition = ((divisionIndex * objRef.subdivisions) + (subdivisionIndex + 1)) * subdivisionPixelLength;
|
415
|
xPosition += alignmentOffset[objRef.align];
|
416
|
markerMeasure = (divisionIndex * objRef.subdivisions + subdivisionIndex + 1) * subdivisionDisplayLength;
|
417
|
aMarker = document.createElement('div');
|
418
|
aMarker.className = 'sbMarkerMinor';
|
419
|
aMarker.style.position = 'absolute';
|
420
|
aMarker.style.overflow = 'hidden';
|
421
|
aMarker.style.left = Math.round(xPosition - xOffsetMarkerMinor) + 'px';
|
422
|
aMarker.appendChild(document.createTextNode(' '));
|
423
|
graphicsContainer.appendChild(aMarker);
|
424
|
if(objRef.showMinorMeasures && !objRef.singleLine) {
|
425
|
// add corresponding measure
|
426
|
numbersBox = document.createElement('div');
|
427
|
numbersBox.className = 'sbNumbersBox';
|
428
|
numbersBox.style.position = 'absolute';
|
429
|
numbersBox.style.overflow = 'hidden';
|
430
|
numbersBox.style.textAlign = 'center';
|
431
|
numbersBox.style.width = Math.round(subdivisionPixelLength * 2) + 'px';
|
432
|
numbersBox.style.left = Math.round(xPosition - subdivisionPixelLength) + 'px';
|
433
|
numbersBox.appendChild(document.createTextNode(markerMeasure));
|
434
|
numbersContainer.appendChild(numbersBox);
|
435
|
}
|
436
|
}
|
437
|
}
|
438
|
}
|
439
|
// set xPosition and markerMeasure to end of divisions
|
440
|
xPosition = (objRef.divisions * objRef.subdivisions) * subdivisionPixelLength;
|
441
|
xPosition += alignmentOffset[objRef.align];
|
442
|
markerMeasure = ((objRef.divisions * objRef.subdivisions) * subdivisionDisplayLength).toFixed(numDec);
|
443
|
// add the final major marker
|
444
|
aMarker = document.createElement('div');
|
445
|
aMarker.className = 'sbMarkerMajor';
|
446
|
aMarker.style.position = 'absolute';
|
447
|
aMarker.style.overflow = 'hidden';
|
448
|
aMarker.style.left = Math.round(xPosition - xOffsetMarkerMajor) + 'px';
|
449
|
aMarker.appendChild(document.createTextNode(' '));
|
450
|
graphicsContainer.appendChild(aMarker);
|
451
|
// add final measure
|
452
|
if(!objRef.singleLine) {
|
453
|
numbersBox = document.createElement('div');
|
454
|
numbersBox.className = 'sbNumbersBox';
|
455
|
numbersBox.style.position = 'absolute';
|
456
|
numbersBox.style.overflow = 'hidden';
|
457
|
numbersBox.style.textAlign = 'center';
|
458
|
if(objRef.showMinorMeasures) {
|
459
|
numbersBox.style.width = Math.round(subdivisionPixelLength * 2) + 'px';
|
460
|
numbersBox.style.left = Math.round(xPosition - subdivisionPixelLength) + 'px';
|
461
|
}
|
462
|
else {
|
463
|
numbersBox.style.width = Math.round(objRef.subdivisions * subdivisionPixelLength * 2) + 'px';
|
464
|
numbersBox.style.left = Math.round(xPosition - (objRef.subdivisions * subdivisionPixelLength)) + 'px';
|
465
|
}
|
466
|
numbersBox.appendChild(document.createTextNode(markerMeasure));
|
467
|
numbersContainer.appendChild(numbersBox);
|
468
|
}
|
469
|
// add content to the label container
|
470
|
var labelBox = document.createElement('div');
|
471
|
labelBox.style.position = 'absolute';
|
472
|
var labelText;
|
473
|
if(objRef.singleLine) {
|
474
|
labelText = markerMeasure;
|
475
|
labelBox.className = 'sbLabelBoxSingleLine';
|
476
|
labelBox.style.top = '-0.6em';
|
477
|
labelBox.style.left = (xPosition + 10) + 'px';
|
478
|
}
|
479
|
else {
|
480
|
labelText = '';
|
481
|
labelBox.className = 'sbLabelBox';
|
482
|
labelBox.style.textAlign = 'center';
|
483
|
labelBox.style.width = Math.round(objRef.divisions * objRef.subdivisions * subdivisionPixelLength) + 'px'
|
484
|
labelBox.style.left = Math.round(alignmentOffset[objRef.align]) + 'px';
|
485
|
labelBox.style.overflow = 'hidden';
|
486
|
}
|
487
|
if(objRef.abbreviateLabel) {
|
488
|
labelText += ' ' + displayUnitsAbbr;
|
489
|
}
|
490
|
else {
|
491
|
labelText += ' ' + displayUnits;
|
492
|
}
|
493
|
labelBox.appendChild(document.createTextNode(labelText));
|
494
|
labelContainer.appendChild(labelBox);
|
495
|
// support for browsers without the Document.styleSheets property (Opera)
|
496
|
if(!document.styleSheets) {
|
497
|
// override custom css with default
|
498
|
var defaultStyle = document.createElement('style');
|
499
|
defaultStyle.type = 'text/css';
|
500
|
var styleText = '.sbBar {top: 0px; background: #666666; height: 1px; border: 0;}';
|
501
|
styleText += '.sbBarAlt {top: 0px; background: #666666; height: 1px; border: 0;}';
|
502
|
styleText += '.sbMarkerMajor {height: 7px; width: 1px; background: #666666; border: 0;}';
|
503
|
styleText += '.sbMarkerMinor {height: 5px; width: 1px; background: #666666; border: 0;}';
|
504
|
styleText += '.sbLabelBox {top: -16px;}';
|
505
|
styleText += '.sbNumbersBox {top: 7px;}';
|
506
|
defaultStyle.appendChild(document.createTextNode(styleText));
|
507
|
document.getElementsByTagName('head').item(0).appendChild(defaultStyle);
|
508
|
}
|
509
|
// append the child containers to the parent container
|
510
|
container.appendChild(graphicsContainer);
|
511
|
container.appendChild(labelContainer);
|
512
|
container.appendChild(numbersContainer);
|
513
|
}
|