1(function() { 2 3 "use strict"; 4 5 BX.namespace("BX.UI.Notification"); 6 7 /** 8 * 9 * @enum {number} 10 */ 11 var State = { 12 INIT: 0, 13 OPENING: 1, 14 OPEN: 2, 15 CLOSING: 3, 16 CLOSED: 4, 17 PAUSED: 5, 18 QUEUED: 6 19 }; 20 21 BX.UI.Notification.State = State; 22 23 /** 24 * @typedef {object} BX.UI.Notification.BalloonOptions 25 * @property {BX.UI.Notification.Stack} stack 26 * @property {string|Element} [content] 27 * @property {boolean} [autoHide=true] 28 * @property {boolean} [showOnTopWindow=false] 29 * @property {number} [autoHideDelay=8000] 30 * @property {boolean} [closeButton=true] 31 * @property {string} [category] 32 * @property {string} [id] 33 * @property {BX.UI.Notification.Action[]} [actions] 34 * @property {function} [render] 35 * @property {number} [width=400] 36 * @property {object} [data] 37 * @property {?object.<string, function>} [events] 38 * @property {boolean} [useAirDesign=false] 39 */ 40 41 /** 42 * 43 * @param {BX.UI.Notification.BalloonOptions} options 44 * @constructor 45 */ 46 BX.UI.Notification.Balloon = function(options) 47 { 48 options = BX.type.isPlainObject(options) ? options : Object.create(null); 49 50 if (!(options.stack instanceof BX.UI.Notification.Stack)) 51 { 52 throw new Error("BX.UI.Notification.Balloon: 'stack' parameter is required."); 53 } 54 55 this.id = BX.type.isNotEmptyString(options.id) ? options.id : BX.util.getRandomString(8).toLowerCase(); 56 this.stack = options.stack; 57 this.state = State.INIT; 58 59 this.showOnTopWindow = options.showOnTopWindow === true; 60 this.container = null; 61 this.content = null; 62 this.actions = []; 63 this.animationClassName = "ui-notification-balloon-animate"; 64 this.customRender = null; 65 this.category = null; 66 67 this.autoHide = true; 68 this.autoHideDelay = 8000; 69 this.autoHideTimeout = null; 70 71 this.useAirDesign = options.useAirDesign === true; 72 73 this.data = {}; 74 this.width = 400; 75 if (this.useAirDesign === true) 76 { 77 this.width = 339; 78 } 79 80 this.closeButton = null; 81 this.closeButtonVisibility = true; 82 83 if (BX.type.isPlainObject(options.events)) 84 { 85 for (var eventName in options.events) 86 { 87 this.addEvent(eventName, options.events[eventName]); 88 } 89 } 90 91 this.setOptions(options); 92 }; 93 94 BX.UI.Notification.Balloon.prototype = 95 { 96 /** 97 * @public 98 */ 99 show: function() 100 { 101 if (this.getState() === State.OPENING) 102 { 103 return; 104 } 105 106 if (this.getState() === State.OPEN) 107 { 108 this.activateAutoHide(); 109 return; 110 } 111 112 var firstLaunch = false; 113 if (!this.getContainer().parentNode) 114 { 115 firstLaunch = true; 116 if (this.showOnTopWindow) 117 { 118 window.top.document.body.appendChild(this.getContainer()); 119 } 120 else 121 { 122 document.body.appendChild(this.getContainer()); 123 } 124 BX.ZIndexManager.register(this.getContainer(), { alwaysOnTop: true }); 125 126 this.getStack().add(this); 127 if (this.getState() === State.QUEUED) 128 { 129 return; 130 } 131 } 132 133 var paused = this.getState() === State.PAUSED; 134 this.setState(State.OPENING); 135 this.adjustPosition(); 136 BX.ZIndexManager.bringToFront(this.getContainer()); 137 138 this.animateIn(function() { 139 140 if (this.getState() !== State.OPENING) 141 { 142 return; 143 } 144 145 this.setState(State.OPEN); 146 147 if (firstLaunch) 148 { 149 this.fireEvent("onOpen"); 150 } 151 152 if (!paused) 153 { 154 this.activateAutoHide(); 155 } 156 157 }.bind(this)); 158 159 }, 160 161 /** 162 * 163 * @param {BX.UI.Notification.BalloonOptions} options 164 */ 165 setOptions: function(options) 166 { 167 if (!BX.type.isPlainObject(options)) 168 { 169 return; 170 } 171 172 this.setContent(options.content); 173 this.setWidth(options.width); 174 this.setData(options.data); 175 this.setCloseButtonVisibility(options.closeButton); 176 this.setActions(options.actions); 177 this.setCategory(options.category); 178 this.setAutoHide(options.autoHide); 179 this.setCustomRender(options.render); 180 this.setAutoHideDelay(options.autoHideDelay); 181 }, 182 183 /** 184 * 185 * @param {BX.UI.Notification.BalloonOptions|null} options 186 */ 187 update: function(options) 188 { 189 this.setOptions(options); 190 191 BX.cleanNode(this.getContainer()); 192 193 this.getContainer().appendChild(this.render()); 194 195 this.deactivateAutoHide(); 196 this.activateAutoHide(); 197 }, 198 199 /** 200 * @public 201 */ 202 close: function() 203 {
204 const startState = this.getState(); 205 if (startState === State.CLOSING || startState === State.CLOSED) 206 { 207 return; 208 } 209 210 this.setState(State.CLOSING); 211 this.deactivateAutoHide(); 212 213 const finalize = () => { 214 if (this.getState() !== State.CLOSING) 215 { 216 return; 217 } 218 219 this.setState(State.CLOSED); 220 221 BX.ZIndexManager.unregister(this.getContainer()); 222 BX.remove(this.getContainer()); 223 this.container = null; 224 225 this.fireEvent("onClose"); 226 }; 227 228 if (startState === State.OPENING) 229 { 230 finalize(); 231 } 232 else 233 { 234 this.animateOut(() => { 235 finalize(); 236 }); 237 } 238 }, 239 240 /** 241 * @private 242 */ 243 blink: function() 244 { 245 var self = this; 246 this.animateOut(function() { 247 setTimeout(function() { 248 self.update(null); 249 self.animateIn(function() {}); 250 }, 200); 251 }); 252 }, 253 254 /** 255 * @public 256 */ 257 adjustPosition: function() 258 { 259 if (this.getStack().isNewestOnTop()) 260 { 261 this.getStack().adjustPosition(); 262 } 263 else 264 { 265 this.getStack().adjustPosition(this); 266 } 267 }, 268 269 /** 270 * @public 271 * @return {string} 272 */ 273 getId: function() 274 { 275 return this.id; 276 }, 277 278 /** 279 * @package 280 * @return {Element} 281 */ 282 getCloseButton: function() 283 { 284 if (this.closeButton !== null) 285 { 286 return this.closeButton; 287 } 288 289 this.closeButton = BX.create("div", { 290 props: { 291 className: "ui-notification-balloon-close-btn" 292 }, 293 events: { 294 click: this.handleCloseBtnClick.bind(this) 295 } 296 }); 297 298 return this.closeButton; 299 }, 300 301 /** 302 * 303 * @param {boolean} visibility 304 */ 305 setCloseButtonVisibility: function(visibility) 306 { 307 this.closeButtonVisibility = visibility !== false; 308 }, 309 310 /** 311 * @public 312 * @return {boolean} 313 */ 314 isCloseButtonVisible: function() 315 { 316 return this.closeButtonVisibility; 317 }, 318 319 /** 320 * @public 321 * @return {Element|string} 322 */ 323 getContent: function() 324 { 325 return this.content; 326 }, 327 328 /** 329 * @public 330 * @param {Element|string} content 331 */ 332 setContent: function(content) 333 { 334 if (BX.type.isString(content) || BX.type.isDomNode(content)) 335 { 336 this.content = content; 337 } 338 }, 339 340 /** 341 * @public 342 * @return {number|"auto"} 343 */ 344 getWidth: function() 345 { 346 return this.width; 347 }, 348 349 /** 350 * @public 351 * @param {number|"auto"} width 352 */ 353 setWidth: function(width) 354 { 355 if (BX.type.isNumber(width) || width === "auto") 356 { 357 this.width = width; 358 } 359 }, 360 361 /** 362 * @return {number} 363 */ 364 getZIndex: function() 365 { 366 var component = BX.ZIndexManager.getComponent(this.getContainer()); 367 368 return component ? component.getZIndex() : 0; 369 }, 370 371 /** 372 * @deprecated 373 * @param {number} zIndex 374 */ 375 setZIndex: function(zIndex) 376 { 377 378 }, 379 380 /** 381 * @package 382 * @return {number} 383 */ 384 getHeight: function() 385 { 386 return this.getContainer().offsetHeight; 387 }, 388 389 /** 390 * @public 391 * @return {string|null} 392 */ 393 getCategory: function() 394 { 395 return this.category; 396 }, 397 398 /** 399 * @public 400 * @param {string|null} category 401 */ 402 setCategory: function(category) 403 { 404 if (BX.type.isNotEmptyString(category) || category === null) 405 { 406 this.category = category; 407 } 408 }, 409 410 /** 411 * @public 412 * @param {BX.UI.Notification.Action[]|null} actions 413 */ 414 setActions: function(actions) 415 { 416 if (BX.type.isArray(actions)) 417 { 418 this.actions = []; 419 actions.forEach(function(action) { 420 this.actions.push(new BX.UI.Notification.Action(this, action)); 421 }, this); 422 } 423 else if (actions === null) 424 { 425 this.actions = []; 426 } 427 }, 428 429 /** 430 * @public 431 * @return {BX.UI.Notification.Action[]} 432 */ 433 getActions: function() 434 { 435 return this.actions; 436 }, 437 438 /** 439 * @public 440 * @param {string} id 441 * @return {BX.UI.Notification.Action} 442 */ 443 getAction: function(id) 444 { 445 for (var i = 0; i < this.actions.length; i++) 446 { 447 var action = this.actions[i]; 448 if (action.getId() === id) 449 { 450 return action; 451 } 452 } 453 454 return null; 455 }, 456 457 /** 458 * @public 459 * @return {Element} 460 */ 461 getContainer: function() 462 { 463 if (this.container !== null) 464 { 465 return this.container; 466 } 467 468 this.container = BX.create("div", { 469 props: { 470 className: "ui-notification-balloon" 471 }, 472 children: [ 473 this.render() 474 ], 475 events: { 476 mouseenter: this.handleMouseEnter.bind(this), 477 mouseleave: this.handleMouseLeave.bind(this) 478 } 479 }); 480 481 return this.container; 482 }, 483 484 /** 485 * @protected 486 * @return {Element} 487 */ 488 render: function() 489 { 490 if (this.getCustomRender() !== null) 491 { 492 return this.getCustomRender().apply(this, [this]); 493 } 494 495 var actions = this.getActions().map(function(action) { 496 return action.getContainer(); 497 }); 498 499 var content = this.getContent(); 500 var width = this.getWidth(); 501 502 return BX.create("div", { 503 props: { 504 className: "ui-notification-balloon-content" 505 }, 506 style: { 507 width: BX.type.isNumber(width) ? (width + "px") : width 508 }, 509 children: [ 510 BX.create("div", { 511 props: { 512 className: "ui-notification-balloon-message" 513 }, 514 html: BX.type.isDomNode(content) ? null : content, 515 children: BX.type.isDomNode(content) ? [content] : [] 516 }), 517 BX.create("div", { 518 props: { 519 className: "ui-notification-balloon-actions" 520 }, 521 children: actions 522 }), 523 this.isCloseButtonVisible() ? this.getCloseButton(): null 524 ] 525 }); 526 }, 527 528 /** 529 * @public 530 * @param {function} render 531 */ 532 setCustomRender: function(render) 533 { 534 if (BX.type.isFunction(render)) 535 { 536 this.customRender = render; 537 } 538 }, 539 540 /** 541 * @public 542 * @return {Function} 543 */ 544 getCustomRender: function() 545 { 546 return this.customRender; 547 }, 548 549 /** 550 * @public 551 * @return {BX.UI.Notification.Stack} 552 */ 553 getStack: function() 554 { 555 return this.stack; 556 }, 557 558 /** 559 * @package 560 * @param {State} state 561 */ 562 setState: function(state) 563 {
564 var code = this.getStateCode(state); 565 if (code !== null) 566 { 567 this.state = state; 568 } 569 }, 570 571 /** 572 * @public 573 * @return {State} 574 */ 575 getState: function() 576 { 577 return this.state; 578 }, 579 580 /** 581 * @public 582 * @param mode 583 * @return {?string} 584 */ 585 getStateCode: function(mode) 586 { 587 for (var code in State) 588 { 589 if (State[code] === mode) 590 { 591 return code; 592 } 593 } 594 595 return null; 596 }, 597 598 /** 599 * @public 600 */ 601 activateAutoHide: function() 602 { 603 if (!this.getAutoHide()) 604 { 605 return; 606 } 607 608 this.deactivateAutoHide(); 609 610 this.autoHideTimeout = setTimeout(function() { 611 this.close(); 612 }.bind(this), this.getAutoHideDelay()); 613 }, 614 615 /** 616 * @public 617 */ 618 deactivateAutoHide: function() 619 { 620 clearTimeout(this.autoHideTimeout); 621 this.autoHideTimeout = null; 622 }, 623 624 /** 625 * @public 626 * @param {boolean} autoHide 627 */ 628 setAutoHide: function(autoHide) 629 { 630 this.autoHide = autoHide !== false; 631 }, 632 633 /** 634 * @public 635 * @return {boolean} 636 */ 637 getAutoHide: function() 638 { 639 return this.autoHide; 640 }, 641 642 /** 643 * @public 644 * @param {number} delay 645 */ 646 setAutoHideDelay: function(delay) 647 { 648 if (BX.type.isNumber(delay) && delay > 0) 649 { 650 this.autoHideDelay = delay; 651 } 652 }, 653 654 /** 655 * @public 656 * @return {number} 657 */ 658 getAutoHideDelay: function() 659 { 660 return this.autoHideDelay; 661 }, 662 663 /** 664 * @private 665 * @param {function} callback 666 */ 667 animateIn: function(callback) 668 { 669 if (!this.getContainer().classList.contains(this.getAnimationClassName())) 670 { 671 this.getContainer().addEventListener("transitionend", function handleTransitionEnd() { 672 this.removeEventListener("transitionend", handleTransitionEnd); 673 callback(); 674 }); 675 676 this.getContainer().classList.add(this.getAnimationClassName()); 677 678 if (this.useAirDesign === true) 679 { 680 BX.Dom.addClass(this.getContainer(), ['--air', 'âui-context-content-dark']); 681 } 682 } 683 else 684 { 685 callback(); 686 } 687 }, 688 689 /** 690 * @private 691 * @param {function} callback 692 */ 693 animateOut: function(callback) 694 { 695 if (this.getContainer().classList.contains(this.getAnimationClassName())) 696 { 697 this.getContainer().addEventListener("transitionend", function handleTransitionEnd() { 698 this.removeEventListener("transitionend", handleTransitionEnd); 699 callback(); 700 }); 701 702 this.getContainer().classList.remove(this.getAnimationClassName()); 703 } 704 else 705 { 706 callback(); 707 } 708 }, 709 710 /** 711 * @private 712 * @return {string} 713 */ 714 getAnimationClassName: function() 715 { 716 return this.animationClassName; 717 }, 718 719 /** 720 * @private 721 */ 722 handleCloseBtnClick: function() 723 { 724 this.close(); 725 }, 726 727 /** 728 * @private 729 */ 730 handleMouseEnter: function() 731 { 732 this.fireEvent("onMouseEnter"); 733 this.deactivateAutoHide(); 734 this.setState(State.PAUSED); 735 this.show(); 736 }, 737 738 /** 739 * @private 740 */ 741 handleMouseLeave: function() 742 { 743 this.fireEvent("onMouseLeave"); 744 this.activateAutoHide(); 745 }, 746 747 /** 748 * @package 749 * @param {string} eventName 750 * @returns {BX.UI.Notification.Event} 751 */ 752 fireEvent: function(eventName) 753 { 754 var event = this.getEvent(eventName); 755 BX.onCustomEvent(this, event.getFullName(), [event]); 756 757 return event; 758 }, 759 760 /** 761 * @public 762 * @param {string} eventName 763 * @param {function} fn 764 */ 765 addEvent: function(eventName, fn) 766 { 767 if (BX.type.isFunction(fn)) 768 { 769 BX.addCustomEvent(this, BX.UI.Notification.Event.getFullName(eventName), fn); 770 } 771 }, 772 773 /** 774 * @public 775 * @param {string} eventName 776 * @param {function} fn 777 */ 778 removeEvent: function(eventName, fn) 779 { 780 if (BX.type.isFunction(fn)) 781 { 782 BX.removeCustomEvent(this, BX.UI.Notification.Event.getFullName(eventName), fn); 783 } 784 }, 785 786 /** 787 * @private 788 * @param {string} eventName 789 * @returns {BX.UI.Notification.Event} 790 */ 791 getEvent: function(eventName) 792 { 793 var event = new BX.UI.Notification.Event(); 794 event.setBalloon(this); 795 event.setName(eventName); 796 797 return event; 798 }, 799 800 /** 801 * @public 802 * @return {object} 803 */ 804 getData: function() 805 { 806 return this.data; 807 }, 808 809 /** 810 * @public 811 * @param {object} data 812 */ 813 setData: function(data) 814 { 815 if (BX.type.isPlainObject(data)) 816 { 817 this.data = data; 818 } 819 } 820 }; 821 822 BX.UI.Notification.Event = function() 823 { 824 this.balloon = null; 825 this.name = null; 826 }; 827 828 /** 829 * 830 * @param {string} eventName 831 * @return {string} 832 */ 833 BX.UI.Notification.Event.getFullName = function(eventName) 834 { 835 return "UI.Notification.Balloon:" + eventName; 836 }; 837 838 BX.UI.Notification.Event.prototype = 839 { 840 /** 841 * @public 842 * @return {BX.UI.Notification.Balloon} 843 */ 844 getBalloon: function() 845 { 846 return this.balloon; 847 }, 848 849 /** 850 * @public 851 * @param {BX.UI.Notification.Balloon} balloon 852 */ 853 setBalloon: function(balloon) 854 { 855 if (balloon instanceof BX.UI.Notification.Balloon) 856 { 857 this.balloon = balloon; 858 } 859 }, 860 861 /** 862 * @public 863 * @returns {string} 864 */ 865 getName: function() 866 { 867 return this.name; 868 }, 869 870 /** 871 * @public 872 * @param {string} name 873 */ 874 setName: function(name) 875 { 876 if (BX.type.isNotEmptyString(name)) 877 { 878 this.name = name; 879 } 880 }, 881 882 /** 883 * @public 884 * @returns {string} 885 */ 886 getFullName: function() 887 { 888 return BX.UI.Notification.Event.getFullName(this.getName()); 889 } 890 }; 891 892 /** 893 * 894 * @param {BX.UI.Notification.Balloon} balloon 895 * @param {object} [options] 896 * @param {string} [options.id] 897 * @param {string} [options.href] 898 * @param {string} [options.title] 899 * @param {?object.<string, function>} [options.events] 900 * @constructor 901 */ 902 BX.UI.Notification.Action = function(balloon, options) 903 { 904 options = BX.type.isPlainObject(options) ? options : Object.create(null); 905 906 this.balloon = balloon; 907 this.id = BX.type.isNotEmptyString(options.id) ? options.id : BX.util.getRandomString(8).toLowerCase(); 908 this.container = null; 909 this.href = BX.type.isNotEmptyString(options.href) ? options.href : null; 910 this.title = BX.type.isNotEmptyString(options.title) ? options.title : null; 911 this.window = window; 912 913 this.events = {}; 914 if (BX.type.isPlainObject(options.events)) 915 { 916 for (var eventName in options.events) 917 { 918 var fn = options.events[eventName]; 919 if (!BX.type.isFunction(fn)) 920 { 921 continue; 922 } 923 924 this.events[eventName] = (function(fn, action) { 925 return function(event) { 926 fn.call(event.target, event, action.getBalloon(), action); 927 }; 928 })(fn, this); 929 } 930 } 931 }; 932 933 BX.UI.Notification.Action.prototype = 934 { 935 /** 936 * @public 937 * @return {BX.UI.Notification.Balloon} 938 */ 939 getBalloon: function() 940 { 941 return this.balloon; 942 }, 943 944 /** 945 * @public 946 * @return {string} 947 */ 948 getId: function() 949 { 950 return this.id; 951 }, 952 953 /** 954 * @public 955 * @return {string} 956 */ 957 getTitle: function() 958 { 959 return this.title; 960 }, 961 962 /** 963 * @public 964 * @return {string} 965 */ 966 getHref: function() 967 { 968 return this.href; 969 }, 970 971 /** 972 * @public 973 * @return {Element} 974 */ 975 getContainer: function() 976 { 977 if (this.container === null) 978 { 979 this.container = BX.create(this.getHref() ? "a" : "span", { 980 props: { 981 href: this.getHref(), 982 className: "ui-notification-balloon-action" 983 }, 984 events: this.events, 985 text: this.getTitle() 986 }) 987 } 988 989 return this.container; 990 }, 991 992 getWindow: function() 993 { 994 return this.window; 995 } 996 }; 997 998})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.