1 /* 2 * LengthUnit.js 3 * 4 * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <info@sweethome3d.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 /** 22 * Unit used for lengths. 23 * @constructor 24 * @author Emmanuel Puybaret 25 */ 26 var LengthUnit = {}; 27 28 LengthUnit.UNITS_LOCALIZATION = { 29 "": {"meterUnit": "m", 30 "centimeterUnit": "cm", 31 "millimeterUnit": "mm", 32 "inchUnit": "inch", 33 "footUnit": "ft", 34 "squareMeterUnit": "m\u00b2", 35 "squareFootUnit": "sq ft"}, 36 "ar": {"meterUnit": "\u0645", 37 "centimeterUnit": "\u0633\u0645", 38 "millimeterUnit": "\u0645\u0645", 39 "inchUnit": "\u0627\u0646\u0634", 40 "squareMeterUnit": "\u0645\u062a\u0631 \u0645\u0631\u0628\u0639", 41 "squareFootUnit": "\u0642\u062f\u0645 \u0645\u0631\u0628\u0639"}, 42 "bg": {"inchUnit": "inch"}, 43 "cs": {"inchUnit": "palce", 44 "squareFootUnit": "ft\u00b2"}, 45 "de": {"inchUnit": "inch", 46 "footUnit": "ft", 47 "squareFootUnit": "ft\u00b2"}, 48 "el": {"inchUnit": "\u03af\u03bd\u03c4\u03c3\u03b1", 49 "squareMeterUnit": "m\u00b2", 50 "squareFootUnit": "\u03c4\u03b5\u03c4\u03c1\u03b1\u03b3\u03c9\u03bd\u03b9\u03ba\u03cc \u03c0\u03cc\u03b4\u03b9"}, 51 "es": {"inchUnit": "pulgada", 52 "squareFootUnit": "ft\u00b2"}, 53 "eu": {"inchUnit": "hazbete", 54 "squareFootUnit": "sq ft"}, 55 "fi": {"inchUnit": "\"", 56 "squareFootUnit": "ft\u00b2"}, 57 "fr": {"inchUnit": "pouce", 58 "footUnit": "pied", 59 "squareFootUnit": "ft\u00b2"}, 60 "hu": {"inchUnit": "inch", 61 "squareFootUnit": "ft\u00b2"}, 62 "in": {"inchUnit": "inci", 63 "squareFootUnit": "ft\u00b2"}, 64 "it": {"inchUnit": "pollice", 65 "squareFootUnit": "ft\u00b2"}, 66 "ja": {"inchUnit": "inch", 67 "squareFootUnit": "sq ft"}, 68 "nl": {"inchUnit": "inch", 69 "squareFootUnit": "vt\u00b2"}, 70 "pl": {"inchUnit": "cal", 71 "squareFootUnit": "ft\u00b2"}, 72 "pt_BR": {"inchUnit": "pol", 73 "squareFootUnit": "ft\u00b2"}, 74 "pt": {"inchUnit": "pol", 75 "squareFootUnit": "p\u00e9\u00b2"}, 76 "ru": {"meterUnit": "\u043c", 77 "centimeterUnit": "\u0441\u043c", 78 "millimeterUnit": "\u043c\u043c", 79 "inchUnit": "\u0434\u044e\u0439\u043c", 80 "squareMeterUnit": "\u043c\u00b2", 81 "squareFootUnit": "ft\u00b2"}, 82 "sl": {"inchUnit": "in\u010d", 83 "squareFootUnit": "kv \u010dl"}, 84 "sr": {"inchUnit": "inch", 85 "squareFootUnit": "sq ft"}, 86 "sv": {"inchUnit": "tum", 87 "squareFootUnit": "ft\u00b2"}, 88 "th": {"meterUnit": "\u0e21.", 89 "centimeterUnit": "\u0e0b\u0e21.", 90 "millimeterUnit": "\u0e21\u0e21.", 91 "inchUnit": "\u0e19\u0e34\u0e49\u0e27", 92 "squareMeterUnit": "\u0e21.\u00b2", 93 "squareFootUnit": "\u0e15\u0e32\u0e23\u0e32\u0e07\u0e1f\u0e38\u0e15"}, 94 "tr": {"inchUnit": "in\u00e7", 95 "squareFootUnit": "kare ft "}, 96 "uk": {"meterUnit": "\u043c", 97 "centimeterUnit": "\u0441\u043c", 98 "millimeterUnit": "\u043c\u043c", 99 "inchUnit": "\u0434\u044e\u0439\u043c", 100 "squareMeterUnit": "\u043c\u00b2", 101 "squareFootUnit": "\u043a\u0432. \u0444\u0442"}, 102 "vi": {"inchUnit": "inch", 103 "squareFootUnit": "sq ft"}, 104 "zh_CN": {"centimeterUnit": "\u5398\u7c73", 105 "inchUnit": "\u82f1\u5bf8", 106 "millimeterUnit": "\u6beb\u7c73", 107 "meterUnit": "\u7c73", 108 "squareMeterUnit": "m\u00b2", 109 "squareFootUnit": "\u5e73\u65b9\u82f1\u5c3a"}, 110 "zh_TW": {"centimeterUnit": "\u516c\u5206", 111 "inchUnit": "\u82f1\u540b", 112 "millimeterUnit": "\u516c\u91d0", 113 "meterUnit": "\u516c\u5c3a", 114 "squareMeterUnit": "m\u00b2", 115 "squareFootUnit": "ft\u00b2"} 116 }; 117 118 /** 119 * Returns the unit name in default locale. 120 * @param {string} unit 121 */ 122 LengthUnit.getLocalizedUnit = function(unit) { 123 var locale = Locale.getDefault(); 124 var localizedUnits = LengthUnit.UNITS_LOCALIZATION [locale]; 125 if (localizedUnits !== undefined) { 126 var localizedUnit = localizedUnits [unit]; 127 if (localizedUnit !== undefined) { 128 return localizedUnit; 129 } 130 } 131 if (locale.indexOf("_") > 0) { 132 locale = locale.substring(0, locale.indexOf("_")); 133 var localizedUnits = LengthUnit.UNITS_LOCALIZATION [locale]; 134 if (localizedUnits !== undefined) { 135 var localizedUnit = localizedUnits [unit]; 136 if (localizedUnit !== undefined) { 137 return localizedUnit; 138 } 139 } 140 } 141 return LengthUnit.UNITS_LOCALIZATION [""][unit]; 142 } 143 144 /** 145 * Returns the value close to the given length under magnetism for meter units. 146 * @param {number} length 147 * @param {number} maxDelta 148 * @private 149 */ 150 LengthUnit.getMagnetizedMeterLength = function(length, maxDelta) { 151 // Use a maximum precision of 1 mm depending on maxDelta 152 maxDelta *= 2; 153 var precision = 1 / 10.; 154 if (maxDelta > 100) { 155 precision = 100; 156 } else if (maxDelta > 10) { 157 precision = 10; 158 } else if (maxDelta > 5) { 159 precision = 5; 160 } else if (maxDelta > 1) { 161 precision = 1; 162 } else if (maxDelta > 0.5) { 163 precision = 0.5; 164 } 165 var magnetizedLength = Math.round(length / precision) * precision; 166 if (magnetizedLength === 0 && length > 0) { 167 return length; 168 } else { 169 return magnetizedLength; 170 } 171 } 172 173 /** 174 * Returns the value close to the given length under magnetism for inch units. 175 * @param {number} length 176 * @param {number} maxDelta 177 * @private 178 */ 179 LengthUnit.getMagnetizedInchLength = function(length, maxDelta) { 180 // Use a maximum precision of 1/8 inch depending on maxDelta 181 maxDelta = LengthUnit.centimeterToInch(maxDelta) * 2; 182 var precision = 1 / 8.; 183 if (maxDelta > 6) { 184 precision = 6; 185 } else if (maxDelta > 3) { 186 precision = 3; 187 } else if (maxDelta > 1) { 188 precision = 1; 189 } else if (maxDelta > 0.5) { 190 precision = 0.5; 191 } else if (maxDelta > 0.25) { 192 precision = 0.25; 193 } 194 var magnetizedLength = LengthUnit.inchToCentimeter(Math.round(LengthUnit.centimeterToInch(length) / precision) * precision); 195 if (magnetizedLength === 0 && length > 0) { 196 return length; 197 } else { 198 return magnetizedLength; 199 } 200 } 201 202 /** 203 * Increases the index of <code>fieldPosition</code> to skip white spaces. 204 * @param {string} text 205 * @param {ParsePosition} fieldPosition 206 * @private 207 */ 208 LengthUnit.skipWhiteSpaces = function(text, fieldPosition) { 209 while (fieldPosition.getIndex() < text.length 210 && /\s/.test(text.charAt(fieldPosition.getIndex()))) { 211 fieldPosition.setIndex(fieldPosition.getIndex() + 1); 212 } 213 } 214 215 /** 216 * @param {number} length 217 * Returns the <code>length</code> given in centimeters converted to inches. 218 */ 219 LengthUnit.centimeterToInch = function(length) { 220 return length / 2.54; 221 } 222 223 /** 224 * @param {number} length 225 * Returns the <code>length</code> given in centimeters converted to feet. 226 */ 227 LengthUnit.centimeterToFoot = function(length) { 228 return length / 2.54 / 12; 229 } 230 231 /** 232 * @param {number} length 233 * Returns the <code>length</code> given in inches converted to centimeters. 234 */ 235 LengthUnit.inchToCentimeter = function(length) { 236 return length * 2.54; 237 } 238 239 /** 240 * @param {number} length 241 * Returns the <code>length</code> given in feet converted to centimeters. 242 */ 243 LengthUnit.footToCentimeter = function(length) { 244 return length * 2.54 * 12; 245 } 246 247 /** 248 * Returns the enum name of the given unit. 249 * <b>WARNING</b> enum name (CENTIMETERS, MILLIMETERS, ..) is different from unit's name (cm, mm, ..) 250 * @param {LengthUnit} unit 251 */ 252 LengthUnit.nameOf = function(unit) { 253 if (unit != null) { 254 var unitEnumNames = Object.keys(LengthUnit); 255 for (var i = 0; i < unitEnumNames.length; i++) { 256 var unitEnumName = unitEnumNames[i]; 257 if (LengthUnit[unitEnumName] == unit) { 258 return unitEnumName; 259 } 260 } 261 } 262 return null; 263 } 264 265 /** 266 * Gets a LengthUnit by its enum name 267 * <br/> 268 * <b>WARNING</b> enum name (CENTIMETER, MILLIMETER, ..) is different from unit's name (cm, mm, ..) 269 * @param {string} unitEnumName 270 * @return {LengthUnit} 271 */ 272 LengthUnit.valueOf = function(unitEnumName) { 273 return LengthUnit[unitEnumName]; 274 } 275 276 /** 277 * Millimeter unit. 278 */ 279 LengthUnit.MILLIMETER = { 280 formatLocale : null 281 }; 282 283 LengthUnit.MILLIMETER.name = function() { 284 return LengthUnit.nameOf(this); 285 } 286 287 LengthUnit.MILLIMETER.getFormatWithUnit = function() { 288 this.checkLocaleChange(); 289 return this.lengthFormatWithUnit; 290 } 291 292 LengthUnit.MILLIMETER.getAreaFormatWithUnit = function() { 293 this.checkLocaleChange(); 294 return this.areaFormatWithUnit; 295 } 296 297 LengthUnit.MILLIMETER.getFormat = function() { 298 this.checkLocaleChange(); 299 return this.lengthFormat; 300 } 301 302 LengthUnit.MILLIMETER.getName = function() { 303 this.checkLocaleChange(); 304 return this.unitName; // Use unitName rather than name field to avoid clashes with name() method 305 } 306 307 LengthUnit.MILLIMETER.checkLocaleChange = function() { 308 // Instantiate formats if locale changed 309 if (Locale.getDefault() != this.formatLocale) { 310 this.formatLocale = Locale.getDefault(); 311 this.unitName = LengthUnit.getLocalizedUnit("millimeterUnit"); 312 this.lengthFormatWithUnit = new MeterFamilyFormat("0", 10, this.unitName); 313 this.lengthFormat = new MeterFamilyFormat("0", 10); 314 var squareMeterUnit = LengthUnit.getLocalizedUnit("squareMeterUnit"); 315 this.areaFormatWithUnit = new SquareMeterAreaFormatWithUnit(squareMeterUnit); 316 } 317 } 318 319 LengthUnit.MILLIMETER.getMagnetizedLength = function(length, maxDelta) { 320 return LengthUnit.getMagnetizedMeterLength(length, maxDelta); 321 } 322 323 LengthUnit.MILLIMETER.getMinimumLength = function() { 324 return 0.1; 325 } 326 327 LengthUnit.MILLIMETER.getMaximumLength = function() { 328 return 100000.; 329 } 330 331 LengthUnit.MILLIMETER.getMaximumElevation = function() { 332 return this.getMaximumLength() / 10; 333 } 334 335 /** 336 * @since 6.6 337 */ 338 LengthUnit.MILLIMETER.getStepSize = function() { 339 return 0.5; 340 } 341 342 LengthUnit.MILLIMETER.centimeterToUnit = function(length) { 343 return length * 10.; 344 } 345 346 LengthUnit.MILLIMETER.unitToCentimeter = function(length) { 347 return length / 10.; 348 } 349 350 /** 351 * @since 7.0 352 */ 353 LengthUnit.MILLIMETER.isMetric = function() { 354 return true; 355 } 356 357 358 /** 359 * Centimeter unit. 360 */ 361 LengthUnit.CENTIMETER = { 362 formatLocale : null 363 }; 364 365 LengthUnit.CENTIMETER.name = function() { 366 return LengthUnit.nameOf(this); 367 } 368 369 LengthUnit.CENTIMETER.getFormatWithUnit = function() { 370 this.checkLocaleChange(); 371 return this.lengthFormatWithUnit; 372 } 373 374 LengthUnit.CENTIMETER.getAreaFormatWithUnit = function() { 375 this.checkLocaleChange(); 376 return this.areaFormatWithUnit; 377 } 378 379 LengthUnit.CENTIMETER.getFormat = function() { 380 this.checkLocaleChange(); 381 return this.lengthFormat; 382 } 383 384 LengthUnit.CENTIMETER.getName = function() { 385 this.checkLocaleChange(); 386 return this.unitName; // Use unitName rather than name field to avoid clashes with name() method 387 } 388 389 LengthUnit.CENTIMETER.checkLocaleChange = function() { 390 // Instantiate formats if locale changed 391 if (Locale.getDefault() != this.formatLocale) { 392 this.formatLocale = Locale.getDefault(); 393 this.unitName = LengthUnit.getLocalizedUnit("centimeterUnit"); 394 this.lengthFormatWithUnit = new MeterFamilyFormat("0.#", 1, this.unitName); 395 this.lengthFormat = new MeterFamilyFormat("0.#", 1); 396 var squareMeterUnit = LengthUnit.getLocalizedUnit("squareMeterUnit"); 397 this.areaFormatWithUnit = new SquareMeterAreaFormatWithUnit(squareMeterUnit); 398 } 399 } 400 401 LengthUnit.CENTIMETER.getMagnetizedLength = function(length, maxDelta) { 402 return LengthUnit.getMagnetizedMeterLength(length, maxDelta); 403 } 404 405 LengthUnit.CENTIMETER.getMinimumLength = function() { 406 return 0.1; 407 } 408 409 LengthUnit.CENTIMETER.getMaximumLength = function() { 410 return 100000.; 411 } 412 413 LengthUnit.CENTIMETER.getMaximumElevation = function() { 414 return this.getMaximumLength() / 10; 415 } 416 417 /** 418 * Returns the preferred step size in centimeter used to increment / decrement values of this unit. 419 * @since 6.6 420 */ 421 LengthUnit.CENTIMETER.getStepSize = function() { 422 return 0.5; 423 } 424 425 LengthUnit.CENTIMETER.centimeterToUnit = function(length) { 426 return length; 427 } 428 429 LengthUnit.CENTIMETER.unitToCentimeter = function(length) { 430 return length; 431 } 432 433 /** 434 * @since 7.0 435 */ 436 LengthUnit.CENTIMETER.isMetric = function() { 437 return true; 438 } 439 440 441 /** 442 * Meter unit. 443 */ 444 LengthUnit.METER = { 445 formatLocale : null 446 }; 447 448 LengthUnit.METER.name = function() { 449 return LengthUnit.nameOf(this); 450 } 451 452 LengthUnit.METER.getFormatWithUnit = function() { 453 this.checkLocaleChange(); 454 return this.lengthFormatWithUnit; 455 } 456 457 LengthUnit.METER.getAreaFormatWithUnit = function() { 458 this.checkLocaleChange(); 459 return this.areaFormatWithUnit; 460 } 461 462 LengthUnit.METER.getFormat = function() { 463 this.checkLocaleChange(); 464 return this.lengthFormat; 465 } 466 467 LengthUnit.METER.getName = function() { 468 this.checkLocaleChange(); 469 return this.unitName; // Use unitName rather than name field to avoid clashes with name() method 470 } 471 472 LengthUnit.METER.checkLocaleChange = function() { 473 // Instantiate formats if locale changed 474 if (Locale.getDefault() != this.formatLocale) { 475 this.formatLocale = Locale.getDefault(); 476 this.unitName = LengthUnit.getLocalizedUnit("meterUnit"); 477 this.lengthFormatWithUnit = new MeterFamilyFormat("0.00#", 0.01, this.unitName); 478 this.lengthFormat = new MeterFamilyFormat("0.00#", 0.01); 479 var squareMeterUnit = LengthUnit.getLocalizedUnit("squareMeterUnit"); 480 this.areaFormatWithUnit = new SquareMeterAreaFormatWithUnit(squareMeterUnit); 481 } 482 } 483 484 LengthUnit.METER.getMagnetizedLength = function(length, maxDelta) { 485 return LengthUnit.getMagnetizedMeterLength(length, maxDelta); 486 } 487 488 LengthUnit.METER.getMinimumLength = function() { 489 return 0.1; 490 } 491 492 LengthUnit.METER.getMaximumLength = function() { 493 return 100000.; 494 } 495 496 LengthUnit.METER.getMaximumElevation = function() { 497 return this.getMaximumLength() / 10; 498 } 499 500 /** 501 * @since 6.6 502 */ 503 LengthUnit.METER.getStepSize = function() { 504 return 0.5; 505 } 506 507 LengthUnit.METER.centimeterToUnit = function(length) { 508 return length / 100; 509 } 510 511 LengthUnit.METER.unitToCentimeter = function(length) { 512 return length * 100; 513 } 514 515 /** 516 * @since 7.0 517 */ 518 LengthUnit.METER.isMetric = function() { 519 return true; 520 } 521 522 523 /** 524 * Foot/Inch unit followed by fraction. 525 */ 526 LengthUnit.INCH = { 527 formatLocale : null 528 }; 529 530 LengthUnit.INCH.name = function() { 531 return LengthUnit.nameOf(this); 532 } 533 534 LengthUnit.INCH.getFormatWithUnit = function() { 535 this.checkLocaleChange(); 536 return this.lengthFormat; 537 } 538 539 LengthUnit.INCH.getFormat = function() { 540 return this.getFormatWithUnit(); 541 } 542 543 LengthUnit.INCH.getAreaFormatWithUnit = function() { 544 this.checkLocaleChange(); 545 return this.areaFormatWithUnit; 546 } 547 548 LengthUnit.INCH.getName = function() { 549 this.checkLocaleChange(); 550 return this.unitName; // Use unitName rather than name field to avoid clashes with name() method 551 } 552 553 LengthUnit.INCH.checkLocaleChange = function() { 554 // Instantiate format if locale changed 555 if (Locale.getDefault() != this.formatLocale) { 556 this.formatLocale = Locale.getDefault(); 557 this.unitName = LengthUnit.getLocalizedUnit("inchUnit"); 558 var footInchSeparator = ""; 559 this.lengthFormat = new InchFractionFormat(true, footInchSeparator); 560 var squareFootUnit = LengthUnit.getLocalizedUnit("squareFootUnit"); 561 this.areaFormatWithUnit = new SquareFootAreaFormatWithUnit("0", squareFootUnit); 562 } 563 } 564 565 LengthUnit.INCH.getMagnetizedLength = function(length, maxDelta) { 566 return LengthUnit.getMagnetizedInchLength(length, maxDelta); 567 } 568 569 LengthUnit.INCH.getMinimumLength = function() { 570 return LengthUnit.inchToCentimeter(0.125); 571 } 572 573 LengthUnit.INCH.getMaximumLength = function() { 574 return LengthUnit.footToCentimeter(3280); 575 } 576 577 LengthUnit.INCH.getMaximumElevation = function() { 578 return this.getMaximumLength() / 10; 579 } 580 581 /** 582 * @since 6.6 583 */ 584 LengthUnit.INCH.getStepSize = function() { 585 return LengthUnit.inchToCentimeter(0.125); 586 } 587 588 LengthUnit.INCH.centimeterToUnit = function(length) { 589 return LengthUnit.centimeterToInch(length); 590 } 591 592 LengthUnit.INCH.unitToCentimeter = function(length) { 593 return LengthUnit.inchToCentimeter(length); 594 } 595 596 /** 597 * @since 7.0 598 */ 599 LengthUnit.INCH.isMetric = function() { 600 return false; 601 } 602 603 604 /** 605 * Inch unit followed by fraction. 606 * @since 7.0 607 */ 608 LengthUnit.INCH_FRACTION = { 609 formatLocale : null 610 }; 611 612 LengthUnit.INCH_FRACTION.name = function() { 613 return LengthUnit.nameOf(this); 614 } 615 616 LengthUnit.INCH_FRACTION.getFormatWithUnit = function() { 617 this.checkLocaleChange(); 618 return this.lengthFormat; 619 } 620 621 LengthUnit.INCH_FRACTION.getFormat = function() { 622 return this.getFormatWithUnit(); 623 } 624 625 LengthUnit.INCH_FRACTION.getAreaFormatWithUnit = function() { 626 this.checkLocaleChange(); 627 return this.areaFormatWithUnit; 628 } 629 630 LengthUnit.INCH_FRACTION.getName = function() { 631 this.checkLocaleChange(); 632 return this.unitName; // Use unitName rather than name field to avoid clashes with name() method 633 } 634 635 LengthUnit.INCH_FRACTION.checkLocaleChange = function() { 636 // Instantiate format if locale changed 637 if (Locale.getDefault() != this.formatLocale) { 638 this.formatLocale = Locale.getDefault(); 639 this.unitName = LengthUnit.getLocalizedUnit("inchUnit"); 640 var footInchSeparator = ""; 641 this.lengthFormat = new InchFractionFormat(false, footInchSeparator); 642 var squareFootUnit = LengthUnit.getLocalizedUnit("squareFootUnit"); 643 this.areaFormatWithUnit = new SquareFootAreaFormatWithUnit("0", squareFootUnit); 644 } 645 } 646 647 LengthUnit.INCH_FRACTION.getMagnetizedLength = function(length, maxDelta) { 648 return LengthUnit.getMagnetizedInchLength(length, maxDelta); 649 } 650 651 LengthUnit.INCH_FRACTION.getMinimumLength = function() { 652 return LengthUnit.inchToCentimeter(0.125); 653 } 654 655 LengthUnit.INCH_FRACTION.getMaximumLength = function() { 656 return LengthUnit.footToCentimeter(3280); 657 } 658 659 LengthUnit.INCH_FRACTION.getMaximumElevation = function() { 660 return this.getMaximumLength() / 10; 661 } 662 663 LengthUnit.INCH_FRACTION.getStepSize = function() { 664 return LengthUnit.inchToCentimeter(0.125); 665 } 666 667 LengthUnit.INCH_FRACTION.centimeterToUnit = function(length) { 668 return LengthUnit.centimeterToInch(length); 669 } 670 671 LengthUnit.INCH_FRACTION.unitToCentimeter = function(length) { 672 return LengthUnit.inchToCentimeter(length); 673 } 674 675 /** 676 * @since 7.0 677 */ 678 LengthUnit.INCH_FRACTION.isMetric = function() { 679 return false; 680 } 681 682 683 /** 684 * Inch unit with decimals. 685 */ 686 LengthUnit.INCH_DECIMALS = { 687 formatLocale : null 688 }; 689 690 LengthUnit.INCH_DECIMALS.name = function() { 691 return LengthUnit.nameOf(this); 692 } 693 694 LengthUnit.INCH_DECIMALS.getFormatWithUnit = function() { 695 this.checkLocaleChange(); 696 return this.lengthFormatWithUnit; 697 } 698 699 LengthUnit.INCH_DECIMALS.getFormat = function() { 700 this.checkLocaleChange(); 701 return this.lengthFormat; 702 } 703 704 LengthUnit.INCH_DECIMALS.getAreaFormatWithUnit = function() { 705 this.checkLocaleChange(); 706 return this.areaFormatWithUnit; 707 } 708 709 LengthUnit.INCH_DECIMALS.getName = function() { 710 this.checkLocaleChange(); 711 return this.unitName; // Use unitName rather than name field to avoid clashes with name() method 712 } 713 714 LengthUnit.INCH_DECIMALS.checkLocaleChange = function() { 715 // Instantiate format if locale changed 716 if (Locale.getDefault() != this.formatLocale) { 717 this.formatLocale = Locale.getDefault(); 718 this.unitName = LengthUnit.getLocalizedUnit("inchUnit"); 719 this.lengthFormat = new InchDecimalsFormat("0.###"); 720 this.lengthFormatWithUnit = new InchDecimalsFormat("0.###", "\""); 721 var squareFootUnit = LengthUnit.getLocalizedUnit("squareFootUnit"); 722 this.areaFormatWithUnit = new SquareFootAreaFormatWithUnit("0.##", squareFootUnit); 723 } 724 } 725 726 LengthUnit.INCH_DECIMALS.getMagnetizedLength = function(length, maxDelta) { 727 return LengthUnit.getMagnetizedInchLength(length, maxDelta); 728 } 729 730 LengthUnit.INCH_DECIMALS.getMinimumLength = function() { 731 return LengthUnit.inchToCentimeter(0.125); 732 } 733 734 LengthUnit.INCH_DECIMALS.getMaximumLength = function() { 735 return LengthUnit.footToCentimeter(3280); 736 } 737 738 LengthUnit.INCH_DECIMALS.getMaximumElevation = function() { 739 return this.getMaximumLength() / 10; 740 } 741 742 /** 743 * @since 6.6 744 */ 745 LengthUnit.INCH_DECIMALS.getStepSize = function() { 746 return LengthUnit.inchToCentimeter(0.125); 747 } 748 749 LengthUnit.INCH_DECIMALS.centimeterToUnit = function(length) { 750 return LengthUnit.centimeterToInch(length); 751 } 752 753 LengthUnit.INCH_DECIMALS.unitToCentimeter = function(length) { 754 return LengthUnit.inchToCentimeter(length); 755 } 756 757 /** 758 * @since 7.0 759 */ 760 LengthUnit.INCH_DECIMALS.isMetric = function() { 761 return false; 762 } 763 764 765 /** 766 * Inch unit with decimals. 767 * @since 7.0 768 */ 769 LengthUnit.FOOT_DECIMALS = { 770 formatLocale : null 771 }; 772 773 LengthUnit.FOOT_DECIMALS.name = function() { 774 return LengthUnit.nameOf(this); 775 } 776 777 LengthUnit.FOOT_DECIMALS.getFormatWithUnit = function() { 778 this.checkLocaleChange(); 779 return this.lengthFormatWithUnit; 780 } 781 782 LengthUnit.FOOT_DECIMALS.getFormat = function() { 783 this.checkLocaleChange(); 784 return this.lengthFormat; 785 } 786 787 LengthUnit.FOOT_DECIMALS.getAreaFormatWithUnit = function() { 788 this.checkLocaleChange(); 789 return this.areaFormatWithUnit; 790 } 791 792 LengthUnit.FOOT_DECIMALS.getName = function() { 793 this.checkLocaleChange(); 794 return this.unitName; // Use unitName rather than name field to avoid clashes with name() method 795 } 796 797 LengthUnit.FOOT_DECIMALS.checkLocaleChange = function() { 798 // Instantiate format if locale changed 799 if (Locale.getDefault() != this.formatLocale) { 800 this.formatLocale = Locale.getDefault(); 801 this.unitName = LengthUnit.getLocalizedUnit("footUnit"); 802 this.lengthFormat = new FootDecimalsFormat("0.###"); 803 this.lengthFormatWithUnit = new FootDecimalsFormat("0.###", "\'"); 804 var squareFootUnit = LengthUnit.getLocalizedUnit("squareFootUnit"); 805 this.areaFormatWithUnit = new SquareFootAreaFormatWithUnit("0.##", squareFootUnit); 806 } 807 } 808 809 LengthUnit.FOOT_DECIMALS.getMagnetizedLength = function(length, maxDelta) { 810 return LengthUnit.getMagnetizedInchLength(length, maxDelta); 811 } 812 813 LengthUnit.FOOT_DECIMALS.getMinimumLength = function() { 814 return LengthUnit.inchToCentimeter(0.125); 815 } 816 817 LengthUnit.FOOT_DECIMALS.getMaximumLength = function() { 818 return LengthUnit.footToCentimeter(3280); 819 } 820 821 LengthUnit.FOOT_DECIMALS.getMaximumElevation = function() { 822 return this.getMaximumLength() / 10; 823 } 824 825 LengthUnit.FOOT_DECIMALS.getStepSize = function() { 826 return LengthUnit.inchToCentimeter(0.125); 827 } 828 829 LengthUnit.FOOT_DECIMALS.centimeterToUnit = function(length) { 830 return LengthUnit.centimeterToFoot(length); 831 } 832 833 LengthUnit.FOOT_DECIMALS.unitToCentimeter = function(length) { 834 return LengthUnit.footToCentimeter(length); 835 } 836 837 LengthUnit.FOOT_DECIMALS.isMetric = function() { 838 return false; 839 } 840 841 842 // Specific format classes for lengths 843 844 /** 845 * @constructor 846 * @extends DecimalFormat 847 * @private 848 */ 849 function MeterFamilyFormat(pattern, unitMultiplier, unit) { 850 DecimalFormat.call(this, pattern); 851 this.setGroupingUsed(true); 852 this.unitMultiplier = unitMultiplier; 853 this.unit = unit; 854 } 855 MeterFamilyFormat.prototype = Object.create(DecimalFormat.prototype); 856 MeterFamilyFormat.prototype.constructor = MeterFamilyFormat; 857 858 MeterFamilyFormat.prototype.format = function(number) { 859 var formattedNumber = DecimalFormat.prototype.format.call(this, number * this.unitMultiplier); 860 return formattedNumber + (this.unit ? " " + this.unit : ""); 861 } 862 863 MeterFamilyFormat.prototype.parse = function(text, parsePosition) { 864 var number = DecimalFormat.prototype.parse.call(this, text, parsePosition); 865 if (number === null) { 866 return null; 867 } else { 868 return number / this.unitMultiplier; 869 } 870 } 871 872 /** 873 * @constructor 874 * @extends DecimalFormat 875 * @private 876 */ 877 function SquareMeterAreaFormatWithUnit(squareMeterUnit) { 878 DecimalFormat.call(this, "0.##"); 879 this.setGroupingUsed(true); 880 this.squareMeterUnit = squareMeterUnit; 881 } 882 SquareMeterAreaFormatWithUnit.prototype = Object.create(DecimalFormat.prototype); 883 SquareMeterAreaFormatWithUnit.prototype.constructor = SquareMeterAreaFormatWithUnit; 884 885 SquareMeterAreaFormatWithUnit.prototype.format = function(number) { 886 var formattedNumber = DecimalFormat.prototype.format.call(this, number / 10000); 887 return formattedNumber + (this.squareMeterUnit ? " " + this.squareMeterUnit : ""); 888 } 889 890 891 /** 892 * A decimal format for inch lengths with fraction. 893 * @param {boolean} footInch 894 * @param {string} footInchSeparator 895 * @constructor 896 * @extends DecimalFormat 897 * @private 898 */ 899 function InchFractionFormat(footInch, footInchSeparator) { 900 DecimalFormat.call(this, "0.###"); 901 this.setGroupingUsed(true); 902 this.footInch = footInch; 903 this.footInchSeparator = footInchSeparator; 904 } 905 InchFractionFormat.prototype = Object.create(DecimalFormat.prototype); 906 InchFractionFormat.prototype.constructor = InchFractionFormat; 907 908 InchFractionFormat.INCH_FRACTION_CHARACTERS = ['\u215b', // 1/8 909 '\u00bc', // 1/4 910 '\u215c', // 3/8 911 '\u00bd', // 1/2 912 '\u215d', // 5/8 913 '\u00be', // 3/4 914 '\u215e']; // 7/8 915 InchFractionFormat.INCH_FRACTION_STRINGS = ["1/8", 916 "1/4", 917 "3/8", 918 "1/2", 919 "5/8", 920 "3/4", 921 "7/8"]; 922 923 InchFractionFormat.prototype.format = function(number) { 924 var absoluteValue = Math.abs(number); 925 var feet = Math.floor(LengthUnit.centimeterToFoot(absoluteValue)); 926 var remainingInches = LengthUnit.centimeterToInch(absoluteValue - LengthUnit.footToCentimeter(feet)); 927 if (remainingInches >= 11.9375) { 928 feet++; 929 remainingInches -= 12; 930 } 931 var result = number >= 0 ? "" : "-"; 932 // Format remaining inches only if it's larger that 0.0005 933 var feetString = DecimalFormat.prototype.format.call(this, feet); 934 if (remainingInches >= 0.0005) { 935 // Try to format decimals with 1/8, 1/4, 1/2 fractions first 936 var integerPart = Math.floor(remainingInches); 937 var fractionPart = remainingInches - integerPart; 938 var eighth = Math.round(fractionPart * 8); 939 if (this.footInch) { 940 result += feetString; 941 if (eighth === 0 || eighth === 8) { 942 result += "'"; 943 result += Math.round(remainingInches * 8) / 8; 944 } else { 945 result += "'"; 946 result += integerPart; 947 result += InchFractionFormat.INCH_FRACTION_CHARACTERS[eighth - 1]; 948 } 949 } else { 950 if (eighth === 0 || eighth === 8) { 951 result += feet * 12 + Math.round(remainingInches * 8) / 8; 952 } else { 953 result += feet * 12 + integerPart; 954 result += InchFractionFormat.INCH_FRACTION_CHARACTERS[eighth - 1]; 955 } 956 } 957 result += "\""; 958 } else { 959 if (this.footInch) { 960 result += feetString; 961 result += "'"; 962 } else { 963 result += DecimalFormat.prototype.format.call(this, feet * 12); 964 result += "\""; 965 } 966 } 967 return result; 968 } 969 970 InchFractionFormat.prototype.parse = function(text, parsePosition) { 971 var value = 0; 972 var numberPosition = new ParsePosition(parsePosition.getIndex()); 973 LengthUnit.skipWhiteSpaces(text, numberPosition); 974 var footNumberFormat = NumberFormat.getIntegerInstance(); 975 // Parse feet 976 var quoteIndex = text.indexOf('\'', parsePosition.getIndex()); 977 var negative = numberPosition.getIndex() < text.length 978 && text.charAt(numberPosition.getIndex()) === '-'; 979 var footValue = false; 980 if (quoteIndex !== -1) { 981 var feet = footNumberFormat.parse(text, numberPosition); 982 if (feet === null) { 983 parsePosition.setErrorIndex(numberPosition.getErrorIndex()); 984 return null; 985 } 986 LengthUnit.skipWhiteSpaces(text, numberPosition); 987 if (numberPosition.getIndex() === quoteIndex) { 988 value = LengthUnit.footToCentimeter(feet); 989 footValue = true; 990 numberPosition = new ParsePosition(quoteIndex + 1); 991 LengthUnit.skipWhiteSpaces(text, numberPosition); 992 // Test optional foot inch separator 993 if (numberPosition.getIndex() < text.length 994 && this.footInchSeparator.indexOf(text.charAt(numberPosition.getIndex())) >= 0) { 995 numberPosition.setIndex(numberPosition.getIndex() + 1); 996 LengthUnit.skipWhiteSpaces(text, numberPosition); 997 } 998 if (numberPosition.getIndex() === text.length) { 999 parsePosition.setIndex(text.length); 1000 return value; 1001 } 1002 } else { 1003 if (this.decimalSeparator === text.charAt(numberPosition.getIndex())) { 1004 var decimalNumberPosition = new ParsePosition(parsePosition.getIndex()); 1005 if (DecimalFormat.prototype.parse.call(this, text, decimalNumberPosition) !== null 1006 && decimalNumberPosition.getIndex() === quoteIndex) { 1007 // Don't allow a decimal number in front of a quote 1008 parsePosition.setErrorIndex(numberPosition.getErrorIndex()); 1009 return null; 1010 } 1011 } 1012 // Try to parse beginning as inches 1013 numberPosition.setIndex(parsePosition.getIndex()); 1014 } 1015 } 1016 1017 // Parse inches 1018 var inches = DecimalFormat.prototype.parse.call(this, text, numberPosition); 1019 if (inches === null) { 1020 if (footValue) { 1021 parsePosition.setIndex(numberPosition.getIndex()); 1022 return value; 1023 } else { 1024 parsePosition.setErrorIndex(numberPosition.getErrorIndex()); 1025 return null; 1026 } 1027 } 1028 if (negative) { 1029 if (quoteIndex === -1) { 1030 value = LengthUnit.inchToCentimeter(inches); 1031 } else { 1032 value -= LengthUnit.inchToCentimeter(inches); 1033 } 1034 } else { 1035 value += LengthUnit.inchToCentimeter(inches); 1036 } 1037 // Parse fraction 1038 LengthUnit.skipWhiteSpaces(text, numberPosition); 1039 if (numberPosition.getIndex() === text.length) { 1040 parsePosition.setIndex(text.length); 1041 return value; 1042 } 1043 if (text.charAt(numberPosition.getIndex()) === '\"') { 1044 parsePosition.setIndex(numberPosition.getIndex() + 1); 1045 return value; 1046 } 1047 1048 var fractionChar = text.charAt(numberPosition.getIndex()); 1049 var fractionString = text.length - numberPosition.getIndex() >= 3 1050 ? text.substring(numberPosition.getIndex(), numberPosition.getIndex() + 3) 1051 : null; 1052 for (var i = 0; i < InchFractionFormat.INCH_FRACTION_CHARACTERS.length; i++) { 1053 if (InchFractionFormat.INCH_FRACTION_CHARACTERS [i] === fractionChar 1054 || InchFractionFormat.INCH_FRACTION_STRINGS [i] == fractionString) { 1055 // Check no decimal fraction was specified 1056 var lastDecimalSeparatorIndex = text.lastIndexOf(this.decimalSeparator, 1057 numberPosition.getIndex() - 1); 1058 if (lastDecimalSeparatorIndex > quoteIndex) { 1059 return null; 1060 } else { 1061 if (negative) { 1062 value -= LengthUnit.inchToCentimeter((i + 1) / 8); 1063 } else { 1064 value += LengthUnit.inchToCentimeter((i + 1) / 8); 1065 } 1066 parsePosition.setIndex(numberPosition.getIndex() 1067 + (InchFractionFormat.INCH_FRACTION_CHARACTERS [i] === fractionChar ? 1 : 3)); 1068 LengthUnit.skipWhiteSpaces(text, parsePosition); 1069 if (parsePosition.getIndex() < text.length 1070 && text.charAt(parsePosition.getIndex()) === '\"') { 1071 parsePosition.setIndex(parsePosition.getIndex() + 1); 1072 } 1073 return value; 1074 } 1075 } 1076 } 1077 1078 parsePosition.setIndex(numberPosition.getIndex()); 1079 return value; 1080 } 1081 1082 /** 1083 * @constructor 1084 * @extends DecimalFormat 1085 * @private 1086 */ 1087 function SquareFootAreaFormatWithUnit(pattern, unit) { 1088 DecimalFormat.call(this, pattern); 1089 this.setGroupingUsed(true); 1090 this.unit = unit; 1091 } 1092 SquareFootAreaFormatWithUnit.prototype = Object.create(DecimalFormat.prototype); 1093 SquareFootAreaFormatWithUnit.prototype.constructor = SquareFootAreaFormatWithUnit; 1094 1095 SquareFootAreaFormatWithUnit.prototype.format = function(number) { 1096 var formattedNumber = DecimalFormat.prototype.format.call(this, number / 929.0304); 1097 return formattedNumber + (this.unit ? " " + this.unit : ""); 1098 } 1099 1100 /** 1101 * @constructor 1102 * @extends DecimalFormat 1103 * @private 1104 */ 1105 function InchDecimalsFormat(pattern, unit) { 1106 DecimalFormat.call(this, pattern); 1107 this.setGroupingUsed(true); 1108 this.unit = unit; 1109 } 1110 InchDecimalsFormat.prototype = Object.create(DecimalFormat.prototype); 1111 InchDecimalsFormat.prototype.constructor = InchDecimalsFormat; 1112 1113 InchDecimalsFormat.prototype.format = function(number) { 1114 var formattedNumber = DecimalFormat.prototype.format.call(this, LengthUnit.centimeterToInch(number)); 1115 return formattedNumber + (this.unit ? this.unit : ""); 1116 } 1117 1118 InchDecimalsFormat.prototype.parse = function(text, parsePosition) { 1119 var numberPosition = new ParsePosition(parsePosition.getIndex()); 1120 LengthUnit.skipWhiteSpaces(text, numberPosition); 1121 // Parse inches 1122 var inches = DecimalFormat.prototype.parse.call(this, text, numberPosition); 1123 if (inches === null) { 1124 parsePosition.setErrorIndex(numberPosition.getErrorIndex()); 1125 return null; 1126 } 1127 var value = LengthUnit.inchToCentimeter(inches); 1128 // Parse " 1129 LengthUnit.skipWhiteSpaces(text, numberPosition); 1130 if (numberPosition.getIndex() < text.length 1131 && text.charAt(numberPosition.getIndex()) === '\"') { 1132 parsePosition.setIndex(numberPosition.getIndex() + 1); 1133 } else { 1134 parsePosition.setIndex(numberPosition.getIndex()); 1135 } 1136 return value; 1137 } 1138 1139 /** 1140 * @constructor 1141 * @extends DecimalFormat 1142 * @private 1143 */ 1144 function FootDecimalsFormat(pattern, unit) { 1145 DecimalFormat.call(this, pattern); 1146 this.setGroupingUsed(true); 1147 this.unit = unit; 1148 } 1149 FootDecimalsFormat.prototype = Object.create(DecimalFormat.prototype); 1150 FootDecimalsFormat.prototype.constructor = FootDecimalsFormat; 1151 1152 FootDecimalsFormat.prototype.format = function(number) { 1153 var formattedNumber = DecimalFormat.prototype.format.call(this, LengthUnit.centimeterToFoot(number)); 1154 return formattedNumber + (this.unit ? this.unit : ""); 1155 } 1156 1157 FootDecimalsFormat.prototype.parse = function(text, parsePosition) { 1158 var numberPosition = new ParsePosition(parsePosition.getIndex()); 1159 LengthUnit.skipWhiteSpaces(text, numberPosition); 1160 // Parse feet 1161 var feet = DecimalFormat.prototype.parse.call(this, text, numberPosition); 1162 if (feet === null) { 1163 parsePosition.setErrorIndex(numberPosition.getErrorIndex()); 1164 return null; 1165 } 1166 var value = LengthUnit.footToCentimeter(feet); 1167 // Parse ' 1168 LengthUnit.skipWhiteSpaces(text, numberPosition); 1169 if (numberPosition.getIndex() < text.length 1170 && text.charAt(numberPosition.getIndex()) === '\'') { 1171 parsePosition.setIndex(numberPosition.getIndex() + 1); 1172 } else { 1173 parsePosition.setIndex(numberPosition.getIndex()); 1174 } 1175 return value; 1176 } 1177