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