vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

871 lines
21 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. /**
  2. * utility functions
  3. */
  4. var util = {};
  5. /**
  6. * Test whether given object is a number
  7. * @param {*} object
  8. * @return {Boolean} isNumber
  9. */
  10. util.isNumber = function isNumber(object) {
  11. return (object instanceof Number || typeof object == 'number');
  12. };
  13. /**
  14. * Test whether given object is a string
  15. * @param {*} object
  16. * @return {Boolean} isString
  17. */
  18. util.isString = function isString(object) {
  19. return (object instanceof String || typeof object == 'string');
  20. };
  21. /**
  22. * Test whether given object is a Date, or a String containing a Date
  23. * @param {Date | String} object
  24. * @return {Boolean} isDate
  25. */
  26. util.isDate = function isDate(object) {
  27. if (object instanceof Date) {
  28. return true;
  29. }
  30. else if (util.isString(object)) {
  31. // test whether this string contains a date
  32. var match = ASPDateRegex.exec(object);
  33. if (match) {
  34. return true;
  35. }
  36. else if (!isNaN(Date.parse(object))) {
  37. return true;
  38. }
  39. }
  40. return false;
  41. };
  42. /**
  43. * Test whether given object is an instance of google.visualization.DataTable
  44. * @param {*} object
  45. * @return {Boolean} isDataTable
  46. */
  47. util.isDataTable = function isDataTable(object) {
  48. return (typeof (google) !== 'undefined') &&
  49. (google.visualization) &&
  50. (google.visualization.DataTable) &&
  51. (object instanceof google.visualization.DataTable);
  52. };
  53. /**
  54. * Create a semi UUID
  55. * source: http://stackoverflow.com/a/105074/1262753
  56. * @return {String} uuid
  57. */
  58. util.randomUUID = function randomUUID () {
  59. var S4 = function () {
  60. return Math.floor(
  61. Math.random() * 0x10000 /* 65536 */
  62. ).toString(16);
  63. };
  64. return (
  65. S4() + S4() + '-' +
  66. S4() + '-' +
  67. S4() + '-' +
  68. S4() + '-' +
  69. S4() + S4() + S4()
  70. );
  71. };
  72. /**
  73. * Extend object a with the properties of object b or a series of objects
  74. * Only properties with defined values are copied
  75. * @param {Object} a
  76. * @param {... Object} b
  77. * @return {Object} a
  78. */
  79. util.extend = function (a, b) {
  80. for (var i = 1, len = arguments.length; i < len; i++) {
  81. var other = arguments[i];
  82. for (var prop in other) {
  83. if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
  84. a[prop] = other[prop];
  85. }
  86. }
  87. }
  88. return a;
  89. };
  90. /**
  91. * Convert an object to another type
  92. * @param {Boolean | Number | String | Date | Moment | Null | undefined} object
  93. * @param {String | undefined} type Name of the type. Available types:
  94. * 'Boolean', 'Number', 'String',
  95. * 'Date', 'Moment', ISODate', 'ASPDate'.
  96. * @return {*} object
  97. * @throws Error
  98. */
  99. util.convert = function convert(object, type) {
  100. var match;
  101. if (object === undefined) {
  102. return undefined;
  103. }
  104. if (object === null) {
  105. return null;
  106. }
  107. if (!type) {
  108. return object;
  109. }
  110. if (!(typeof type === 'string') && !(type instanceof String)) {
  111. throw new Error('Type must be a string');
  112. }
  113. //noinspection FallthroughInSwitchStatementJS
  114. switch (type) {
  115. case 'boolean':
  116. case 'Boolean':
  117. return Boolean(object);
  118. case 'number':
  119. case 'Number':
  120. return Number(object.valueOf());
  121. case 'string':
  122. case 'String':
  123. return String(object);
  124. case 'Date':
  125. if (util.isNumber(object)) {
  126. return new Date(object);
  127. }
  128. if (object instanceof Date) {
  129. return new Date(object.valueOf());
  130. }
  131. else if (moment.isMoment(object)) {
  132. return new Date(object.valueOf());
  133. }
  134. if (util.isString(object)) {
  135. match = ASPDateRegex.exec(object);
  136. if (match) {
  137. // object is an ASP date
  138. return new Date(Number(match[1])); // parse number
  139. }
  140. else {
  141. return moment(object).toDate(); // parse string
  142. }
  143. }
  144. else {
  145. throw new Error(
  146. 'Cannot convert object of type ' + util.getType(object) +
  147. ' to type Date');
  148. }
  149. case 'Moment':
  150. if (util.isNumber(object)) {
  151. return moment(object);
  152. }
  153. if (object instanceof Date) {
  154. return moment(object.valueOf());
  155. }
  156. else if (moment.isMoment(object)) {
  157. return moment(object);
  158. }
  159. if (util.isString(object)) {
  160. match = ASPDateRegex.exec(object);
  161. if (match) {
  162. // object is an ASP date
  163. return moment(Number(match[1])); // parse number
  164. }
  165. else {
  166. return moment(object); // parse string
  167. }
  168. }
  169. else {
  170. throw new Error(
  171. 'Cannot convert object of type ' + util.getType(object) +
  172. ' to type Date');
  173. }
  174. case 'ISODate':
  175. if (util.isNumber(object)) {
  176. return new Date(object);
  177. }
  178. else if (object instanceof Date) {
  179. return object.toISOString();
  180. }
  181. else if (moment.isMoment(object)) {
  182. return object.toDate().toISOString();
  183. }
  184. else if (util.isString(object)) {
  185. match = ASPDateRegex.exec(object);
  186. if (match) {
  187. // object is an ASP date
  188. return new Date(Number(match[1])).toISOString(); // parse number
  189. }
  190. else {
  191. return new Date(object).toISOString(); // parse string
  192. }
  193. }
  194. else {
  195. throw new Error(
  196. 'Cannot convert object of type ' + util.getType(object) +
  197. ' to type ISODate');
  198. }
  199. case 'ASPDate':
  200. if (util.isNumber(object)) {
  201. return '/Date(' + object + ')/';
  202. }
  203. else if (object instanceof Date) {
  204. return '/Date(' + object.valueOf() + ')/';
  205. }
  206. else if (util.isString(object)) {
  207. match = ASPDateRegex.exec(object);
  208. var value;
  209. if (match) {
  210. // object is an ASP date
  211. value = new Date(Number(match[1])).valueOf(); // parse number
  212. }
  213. else {
  214. value = new Date(object).valueOf(); // parse string
  215. }
  216. return '/Date(' + value + ')/';
  217. }
  218. else {
  219. throw new Error(
  220. 'Cannot convert object of type ' + util.getType(object) +
  221. ' to type ASPDate');
  222. }
  223. default:
  224. throw new Error('Cannot convert object of type ' + util.getType(object) +
  225. ' to type "' + type + '"');
  226. }
  227. };
  228. // parse ASP.Net Date pattern,
  229. // for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
  230. // code from http://momentjs.com/
  231. var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
  232. /**
  233. * Get the type of an object, for example util.getType([]) returns 'Array'
  234. * @param {*} object
  235. * @return {String} type
  236. */
  237. util.getType = function getType(object) {
  238. var type = typeof object;
  239. if (type == 'object') {
  240. if (object == null) {
  241. return 'null';
  242. }
  243. if (object instanceof Boolean) {
  244. return 'Boolean';
  245. }
  246. if (object instanceof Number) {
  247. return 'Number';
  248. }
  249. if (object instanceof String) {
  250. return 'String';
  251. }
  252. if (object instanceof Array) {
  253. return 'Array';
  254. }
  255. if (object instanceof Date) {
  256. return 'Date';
  257. }
  258. return 'Object';
  259. }
  260. else if (type == 'number') {
  261. return 'Number';
  262. }
  263. else if (type == 'boolean') {
  264. return 'Boolean';
  265. }
  266. else if (type == 'string') {
  267. return 'String';
  268. }
  269. return type;
  270. };
  271. /**
  272. * Retrieve the absolute left value of a DOM element
  273. * @param {Element} elem A dom element, for example a div
  274. * @return {number} left The absolute left position of this element
  275. * in the browser page.
  276. */
  277. util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
  278. var doc = document.documentElement;
  279. var body = document.body;
  280. var left = elem.offsetLeft;
  281. var e = elem.offsetParent;
  282. while (e != null && e != body && e != doc) {
  283. left += e.offsetLeft;
  284. left -= e.scrollLeft;
  285. e = e.offsetParent;
  286. }
  287. return left;
  288. };
  289. /**
  290. * Retrieve the absolute top value of a DOM element
  291. * @param {Element} elem A dom element, for example a div
  292. * @return {number} top The absolute top position of this element
  293. * in the browser page.
  294. */
  295. util.getAbsoluteTop = function getAbsoluteTop (elem) {
  296. var doc = document.documentElement;
  297. var body = document.body;
  298. var top = elem.offsetTop;
  299. var e = elem.offsetParent;
  300. while (e != null && e != body && e != doc) {
  301. top += e.offsetTop;
  302. top -= e.scrollTop;
  303. e = e.offsetParent;
  304. }
  305. return top;
  306. };
  307. /**
  308. * Get the absolute, vertical mouse position from an event.
  309. * @param {Event} event
  310. * @return {Number} pageY
  311. */
  312. util.getPageY = function getPageY (event) {
  313. if ('pageY' in event) {
  314. return event.pageY;
  315. }
  316. else {
  317. var clientY;
  318. if (('targetTouches' in event) && event.targetTouches.length) {
  319. clientY = event.targetTouches[0].clientY;
  320. }
  321. else {
  322. clientY = event.clientY;
  323. }
  324. var doc = document.documentElement;
  325. var body = document.body;
  326. return clientY +
  327. ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
  328. ( doc && doc.clientTop || body && body.clientTop || 0 );
  329. }
  330. };
  331. /**
  332. * Get the absolute, horizontal mouse position from an event.
  333. * @param {Event} event
  334. * @return {Number} pageX
  335. */
  336. util.getPageX = function getPageX (event) {
  337. if ('pageY' in event) {
  338. return event.pageX;
  339. }
  340. else {
  341. var clientX;
  342. if (('targetTouches' in event) && event.targetTouches.length) {
  343. clientX = event.targetTouches[0].clientX;
  344. }
  345. else {
  346. clientX = event.clientX;
  347. }
  348. var doc = document.documentElement;
  349. var body = document.body;
  350. return clientX +
  351. ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
  352. ( doc && doc.clientLeft || body && body.clientLeft || 0 );
  353. }
  354. };
  355. /**
  356. * add a className to the given elements style
  357. * @param {Element} elem
  358. * @param {String} className
  359. */
  360. util.addClassName = function addClassName(elem, className) {
  361. var classes = elem.className.split(' ');
  362. if (classes.indexOf(className) == -1) {
  363. classes.push(className); // add the class to the array
  364. elem.className = classes.join(' ');
  365. }
  366. };
  367. /**
  368. * add a className to the given elements style
  369. * @param {Element} elem
  370. * @param {String} className
  371. */
  372. util.removeClassName = function removeClassname(elem, className) {
  373. var classes = elem.className.split(' ');
  374. var index = classes.indexOf(className);
  375. if (index != -1) {
  376. classes.splice(index, 1); // remove the class from the array
  377. elem.className = classes.join(' ');
  378. }
  379. };
  380. /**
  381. * For each method for both arrays and objects.
  382. * In case of an array, the built-in Array.forEach() is applied.
  383. * In case of an Object, the method loops over all properties of the object.
  384. * @param {Object | Array} object An Object or Array
  385. * @param {function} callback Callback method, called for each item in
  386. * the object or array with three parameters:
  387. * callback(value, index, object)
  388. */
  389. util.forEach = function forEach (object, callback) {
  390. var i,
  391. len;
  392. if (object instanceof Array) {
  393. // array
  394. for (i = 0, len = object.length; i < len; i++) {
  395. callback(object[i], i, object);
  396. }
  397. }
  398. else {
  399. // object
  400. for (i in object) {
  401. if (object.hasOwnProperty(i)) {
  402. callback(object[i], i, object);
  403. }
  404. }
  405. }
  406. };
  407. /**
  408. * Update a property in an object
  409. * @param {Object} object
  410. * @param {String} key
  411. * @param {*} value
  412. * @return {Boolean} changed
  413. */
  414. util.updateProperty = function updateProp (object, key, value) {
  415. if (object[key] !== value) {
  416. object[key] = value;
  417. return true;
  418. }
  419. else {
  420. return false;
  421. }
  422. };
  423. /**
  424. * Add and event listener. Works for all browsers
  425. * @param {Element} element An html element
  426. * @param {string} action The action, for example "click",
  427. * without the prefix "on"
  428. * @param {function} listener The callback function to be executed
  429. * @param {boolean} [useCapture]
  430. */
  431. util.addEventListener = function addEventListener(element, action, listener, useCapture) {
  432. if (element.addEventListener) {
  433. if (useCapture === undefined)
  434. useCapture = false;
  435. if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
  436. action = "DOMMouseScroll"; // For Firefox
  437. }
  438. element.addEventListener(action, listener, useCapture);
  439. } else {
  440. element.attachEvent("on" + action, listener); // IE browsers
  441. }
  442. };
  443. /**
  444. * Remove an event listener from an element
  445. * @param {Element} element An html dom element
  446. * @param {string} action The name of the event, for example "mousedown"
  447. * @param {function} listener The listener function
  448. * @param {boolean} [useCapture]
  449. */
  450. util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
  451. if (element.removeEventListener) {
  452. // non-IE browsers
  453. if (useCapture === undefined)
  454. useCapture = false;
  455. if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
  456. action = "DOMMouseScroll"; // For Firefox
  457. }
  458. element.removeEventListener(action, listener, useCapture);
  459. } else {
  460. // IE browsers
  461. element.detachEvent("on" + action, listener);
  462. }
  463. };
  464. /**
  465. * Get HTML element which is the target of the event
  466. * @param {Event} event
  467. * @return {Element} target element
  468. */
  469. util.getTarget = function getTarget(event) {
  470. // code from http://www.quirksmode.org/js/events_properties.html
  471. if (!event) {
  472. event = window.event;
  473. }
  474. var target;
  475. if (event.target) {
  476. target = event.target;
  477. }
  478. else if (event.srcElement) {
  479. target = event.srcElement;
  480. }
  481. if (target.nodeType != undefined && target.nodeType == 3) {
  482. // defeat Safari bug
  483. target = target.parentNode;
  484. }
  485. return target;
  486. };
  487. /**
  488. * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent
  489. * @param {Element} element
  490. * @param {Event} event
  491. */
  492. util.fakeGesture = function fakeGesture (element, event) {
  493. var eventType = null;
  494. // for hammer.js 1.0.5
  495. var gesture = Hammer.event.collectEventData(this, eventType, event);
  496. // for hammer.js 1.0.6
  497. //var touches = Hammer.event.getTouchList(event, eventType);
  498. // var gesture = Hammer.event.collectEventData(this, eventType, touches, event);
  499. // on IE in standards mode, no touches are recognized by hammer.js,
  500. // resulting in NaN values for center.pageX and center.pageY
  501. if (isNaN(gesture.center.pageX)) {
  502. gesture.center.pageX = event.pageX;
  503. }
  504. if (isNaN(gesture.center.pageY)) {
  505. gesture.center.pageY = event.pageY;
  506. }
  507. return gesture;
  508. };
  509. util.option = {};
  510. /**
  511. * Convert a value into a boolean
  512. * @param {Boolean | function | undefined} value
  513. * @param {Boolean} [defaultValue]
  514. * @returns {Boolean} bool
  515. */
  516. util.option.asBoolean = function (value, defaultValue) {
  517. if (typeof value == 'function') {
  518. value = value();
  519. }
  520. if (value != null) {
  521. return (value != false);
  522. }
  523. return defaultValue || null;
  524. };
  525. /**
  526. * Convert a value into a number
  527. * @param {Boolean | function | undefined} value
  528. * @param {Number} [defaultValue]
  529. * @returns {Number} number
  530. */
  531. util.option.asNumber = function (value, defaultValue) {
  532. if (typeof value == 'function') {
  533. value = value();
  534. }
  535. if (value != null) {
  536. return Number(value) || defaultValue || null;
  537. }
  538. return defaultValue || null;
  539. };
  540. /**
  541. * Convert a value into a string
  542. * @param {String | function | undefined} value
  543. * @param {String} [defaultValue]
  544. * @returns {String} str
  545. */
  546. util.option.asString = function (value, defaultValue) {
  547. if (typeof value == 'function') {
  548. value = value();
  549. }
  550. if (value != null) {
  551. return String(value);
  552. }
  553. return defaultValue || null;
  554. };
  555. /**
  556. * Convert a size or location into a string with pixels or a percentage
  557. * @param {String | Number | function | undefined} value
  558. * @param {String} [defaultValue]
  559. * @returns {String} size
  560. */
  561. util.option.asSize = function (value, defaultValue) {
  562. if (typeof value == 'function') {
  563. value = value();
  564. }
  565. if (util.isString(value)) {
  566. return value;
  567. }
  568. else if (util.isNumber(value)) {
  569. return value + 'px';
  570. }
  571. else {
  572. return defaultValue || null;
  573. }
  574. };
  575. /**
  576. * Convert a value into a DOM element
  577. * @param {HTMLElement | function | undefined} value
  578. * @param {HTMLElement} [defaultValue]
  579. * @returns {HTMLElement | null} dom
  580. */
  581. util.option.asElement = function (value, defaultValue) {
  582. if (typeof value == 'function') {
  583. value = value();
  584. }
  585. return value || defaultValue || null;
  586. };
  587. util.GiveDec = function GiveDec(Hex) {
  588. if (Hex == "A")
  589. Value = 10;
  590. else if (Hex == "B")
  591. Value = 11;
  592. else if (Hex == "C")
  593. Value = 12;
  594. else if (Hex == "D")
  595. Value = 13;
  596. else if (Hex == "E")
  597. Value = 14;
  598. else if (Hex == "F")
  599. Value = 15;
  600. else
  601. Value = eval(Hex)
  602. return Value;
  603. };
  604. util.GiveHex = function GiveHex(Dec) {
  605. if (Dec == 10)
  606. Value = "A";
  607. else if (Dec == 11)
  608. Value = "B";
  609. else if (Dec == 12)
  610. Value = "C";
  611. else if (Dec == 13)
  612. Value = "D";
  613. else if (Dec == 14)
  614. Value = "E";
  615. else if (Dec == 15)
  616. Value = "F";
  617. else
  618. Value = "" + Dec;
  619. return Value;
  620. };
  621. /**
  622. * Parse a color property into an object with border, background, and
  623. * highlight colors
  624. * @param {Object | String} color
  625. * @return {Object} colorObject
  626. */
  627. util.parseColor = function(color) {
  628. var c;
  629. if (util.isString(color)) {
  630. if (util.isValidHex(color)) {
  631. var hsv = util.hexToHSV(color);
  632. var lighterColorHSV = {h:hsv.h,s:hsv.s * 0.45,v:Math.min(1,hsv.v * 1.05)};
  633. var darkerColorHSV = {h:hsv.h,s:Math.min(1,hsv.v * 1.25),v:hsv.v*0.6};
  634. var darkerColorHex = util.HSVToHex(darkerColorHSV.h ,darkerColorHSV.h ,darkerColorHSV.v);
  635. var lighterColorHex = util.HSVToHex(lighterColorHSV.h,lighterColorHSV.s,lighterColorHSV.v);
  636. c = {
  637. background: color,
  638. border:darkerColorHex,
  639. highlight: {
  640. background:lighterColorHex,
  641. border:darkerColorHex
  642. }
  643. };
  644. }
  645. else {
  646. c = {
  647. background:color,
  648. border:color,
  649. highlight: {
  650. background:color,
  651. border:color
  652. }
  653. };
  654. }
  655. }
  656. else {
  657. c = {};
  658. c.background = color.background || 'white';
  659. c.border = color.border || c.background;
  660. if (util.isString(color.highlight)) {
  661. c.highlight = {
  662. border: color.highlight,
  663. background: color.highlight
  664. }
  665. }
  666. else {
  667. c.highlight = {};
  668. c.highlight.background = color.highlight && color.highlight.background || c.background;
  669. c.highlight.border = color.highlight && color.highlight.border || c.border;
  670. }
  671. }
  672. return c;
  673. };
  674. /**
  675. * http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php
  676. *
  677. * @param {String} hex
  678. * @returns {{r: *, g: *, b: *}}
  679. */
  680. util.hexToRGB = function hexToRGB(hex) {
  681. hex = hex.replace("#","").toUpperCase();
  682. var a = util.GiveDec(hex.substring(0, 1));
  683. var b = util.GiveDec(hex.substring(1, 2));
  684. var c = util.GiveDec(hex.substring(2, 3));
  685. var d = util.GiveDec(hex.substring(3, 4));
  686. var e = util.GiveDec(hex.substring(4, 5));
  687. var f = util.GiveDec(hex.substring(5, 6));
  688. var r = (a * 16) + b;
  689. var g = (c * 16) + d;
  690. var b = (e * 16) + f;
  691. return {r:r,g:g,b:b};
  692. };
  693. util.RGBToHex = function RGBToHex(red,green,blue) {
  694. var a = util.GiveHex(Math.floor(red / 16));
  695. var b = util.GiveHex(red % 16);
  696. var c = util.GiveHex(Math.floor(green / 16));
  697. var d = util.GiveHex(green % 16);
  698. var e = util.GiveHex(Math.floor(blue / 16));
  699. var f = util.GiveHex(blue % 16);
  700. var hex = a + b + c + d + e + f;
  701. return "#" + hex;
  702. };
  703. /**
  704. * http://www.javascripter.net/faq/rgb2hsv.htm
  705. *
  706. * @param red
  707. * @param green
  708. * @param blue
  709. * @returns {*}
  710. * @constructor
  711. */
  712. util.RGBToHSV = function RGBToHSV (red,green,blue) {
  713. red=red/255; green=green/255; blue=blue/255;
  714. var minRGB = Math.min(red,Math.min(green,blue));
  715. var maxRGB = Math.max(red,Math.max(green,blue));
  716. // Black-gray-white
  717. if (minRGB == maxRGB) {
  718. return {h:0,s:0,v:minRGB};
  719. }
  720. // Colors other than black-gray-white:
  721. var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red);
  722. var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5);
  723. var hue = 60*(h - d/(maxRGB - minRGB))/360;
  724. var saturation = (maxRGB - minRGB)/maxRGB;
  725. var value = maxRGB;
  726. return {h:hue,s:saturation,v:value};
  727. };
  728. /**
  729. * https://gist.github.com/mjijackson/5311256
  730. * @param hue
  731. * @param saturation
  732. * @param value
  733. * @returns {{r: number, g: number, b: number}}
  734. * @constructor
  735. */
  736. util.HSVToRGB = function HSVToRGB(h, s, v) {
  737. var r, g, b;
  738. var i = Math.floor(h * 6);
  739. var f = h * 6 - i;
  740. var p = v * (1 - s);
  741. var q = v * (1 - f * s);
  742. var t = v * (1 - (1 - f) * s);
  743. switch (i % 6) {
  744. case 0: r = v, g = t, b = p; break;
  745. case 1: r = q, g = v, b = p; break;
  746. case 2: r = p, g = v, b = t; break;
  747. case 3: r = p, g = q, b = v; break;
  748. case 4: r = t, g = p, b = v; break;
  749. case 5: r = v, g = p, b = q; break;
  750. }
  751. return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) };
  752. };
  753. util.HSVToHex = function HSVToHex(h, s, v) {
  754. var rgb = util.HSVToRGB(h, s, v);
  755. return util.RGBToHex(rgb.r, rgb.g, rgb.b);
  756. };
  757. util.hexToHSV = function hexToHSV(hex) {
  758. var rgb = util.hexToRGB(hex);
  759. return util.RGBToHSV(rgb.r, rgb.g, rgb.b);
  760. };
  761. util.isValidHex = function isValidHex(hex) {
  762. var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
  763. return isOk;
  764. };
  765. util.copyObject = function copyObject(objectFrom, objectTo) {
  766. for (var i in objectFrom) {
  767. if (objectFrom.hasOwnProperty(i)) {
  768. if (typeof objectFrom[i] == "object") {
  769. objectTo[i] = {};
  770. util.copyObject(objectFrom[i], objectTo[i]);
  771. }
  772. else {
  773. objectTo[i] = objectFrom[i];
  774. }
  775. }
  776. }
  777. };