1if (typeof window.SidePanelController === "undefined") 2{ 3 (function () 4 { 5 6 /** 7 * @typedef SidePanelMode 8 * 9 * @type {"free"|"pinned"} 10 */ 11 12 /** 13 * @typedef SidePanelContext 14 * 15 * @type {Object} 16 * 17 * @property {HTMLElement} panel The `Panel` element. 18 * @property {HTMLElement} openTrigger The `OpenTrigger` element. 19 * @property {HTMLElement} closeTrigger The `CloseTrigger` element. 20 * @property {HTMLElement} sizer The `Sizer` element. 21 * @property {{free: HTMLElement, pinned: HTMLElement}} changeModeTriggers The elements which cause the Panel to change its mode (pinned/free-floating). 22 */ 23 24 /** 25 * @typedef ActivateOptions 26 * 27 * @type {Object} 28 * 29 * @property {string} panelSelector CSS selector of the `Panel` element. 30 * @property {string} openTriggerSelector CSS selector of the `OpenTrigger` element. 31 * @property {string} closeTriggerSelector CSS selector of the `CloseTrigger` element. 32 * @property {string} sizerSelector CSS selector of the `Sizer` element. 33 * @property {{free: string, pinned: string}} changeModeTriggerSelectors CSS selectors for the `ChangeModeTrigger_Pinned` element and the `ChangeModeTrigger_Free` element. 34 * @property {SidePanelMode} mode The initial mode of the side panel. 35 * @property {string} prefID ID to store side panel state as user preference. 36 * @property {SidePanelMode} [forcedMode] The forced mode of the side panel. If provided, user will not be able to switch between modes. 37 * 38 * @property {Function} onModeChange Callback when mode changes. 39 * @property {number} autoCollapseThreshold Automatically collapse side panels when viewport width is smaller than this threshold (px). 40 */ 41 42 /** 43 * Link: https://link.orangelogic.com/Tasks/22TYZT
44 * This object contains logic that help controlling the folding/expanding of a single DamView side panel. 45 * 46 * ## Prerequisites: 47 * For each side panel [Left/Right] in `DamView_VForm`, there must be a set of the following elements: 48 * - `Panel` - the main container of the side panel. Must be a foldable panel. 49 * - `OpenTrigger` - the element that will cause the Panel to expand. 50 * - `CloseTrigger` - the element that will cause the Panel to fold. 51 * - `Sizer` - the element that will be used to calculate the width of the Panel. Its original width must be equal to `Panel`. 52 * - The elements that will cause the Panel to change its mode (pinned/free-floating): 53 * + `ChangeModeTrigger_Pinned` - the element that will cause the Panel to change its mode to pinned. 54 * + `ChangeModeTrigger_Free` - the element that will cause the Panel to change its mode to free-floating. 55 * 56 * ## Behavior: 57 * The behavior of the side panel will be of the following: 58 * - Common behavior (regardless of whether the side panel is pinned or not): 59 * + Clicking on `CloseTrigger` will fold `Panel`. 60 * - When side panel is **not pinned** (i.e. free-floating mode): 61 * + `ChangeModeTrigger_Free` must be hidden 62 * + Hovering on the `OpenTrigger` will expand `Panel`. 63 * + Moving cursor out of `Panel` will fold `Panel`. 64 * + `Sizer`'s min width must be overridden with 1 (as min width 0 is invali
64d + 1px is enough for our case) so that `Panel` will not take up space from the content) 65 * + Clicking on the `ChangeModeTrigger_Pinned` will change the mode to pinned. 66 * - When side panel is **pinned** (i.e. pinned mode): 67 * + `ChangeModeTrigger_Pinned` must be hidden 68 * + Clicking on the `OpenTrigger` will expand `Panel`. 69 * + Remove 0 overrides from the `Sizer`'s min width so that `Panel` will take up space from the content. 70 * + Clicking on the `ChangeModeTrigger_Free` will change the mode to free-floating and automatically close `Panel`. 71 * 72 * @param {SidePanelContext} context 73 * @param {ActivateOptions} options 74 * 75 * @class 76 */ 77 function SidePanelController(context, options) 78 { 79 /** @type {string} */ 80 this.id = context.panel.GetId(); 81 82 /** @type {SidePanelContext} */ 83 this.context = context; 84 85 /** @type {SidePanelMode} */ 86 this.mode = options.forcedMode ? options.forcedMode : options.mode; 87 88 /** @type {string} */ 89 this.prefID = options.prefID; 90 91 /** @type {?SidePanelMode} */ 92 this.forcedMode = options.forcedMode; 93 94 /** @type {?Function} */ 95 this.onModeChange = options.onModeChange; 96 97 /** @type {number} */ 98 this.autoCollapseThreshold = options.autoCollapseThreshold; 99 100 /** @type {ActivateOptions} */ 101 this.options = options; 102 103 /** @type {Function[]} */ 104 this._cleanupFunctions = []; 105 } 106 107 /** 108 * Store instances of {@link SidePanelController} 109 * 110 * @type {Object.<string, SidePanelController>} 111 */ 112 SidePanelController.Instances = {}; 113 114 /** 115 * @typedef SidePanelControllerConst 116 * 117 * @property {"free"} SIDE_PANEL_MODE_FREE Side panel mode "free" 118 * @property {"pinned"} SIDE_PANEL_MODE_PINNED Side panel mode "pinned" 119 * @property {SidePanelMode[]} SIDE_PANEL_ALL_MODES All side panel modes 120 * 121 * @property {boolean} DEBUG_MODE Flag to activate debug logging 122 */ 123 124 /** 125 * Constant values 126 * 127 * @type {SidePanelControllerConst} 128 * 129 */ 130 SidePanelController.Const = 131 { 132 SIDE_PANEL_MODE_FREE: "free", 133 SIDE_PANEL_MODE_PINNED: "pinned", 134 SIDE_PANEL_ALL_MODES: ["free", "pinned"], 135 136 ATTR_DATA_SIDE_PANEL_MODE: "data-side-panel-mode", 137 138 DEBUG_MODE: false, 139 }; 140 141 /** 142 * Log an error message to console for debugging purpose 143 */ 144 SidePanelController._error = function () 145 { 146 if (!SidePanelController.Const.DEBUG_MODE) return; 147 148 console.error("%c[DEBUG MODE] SidePanelController%c%s", 149 "color:white; background-color:var(--cx-color-danger-800); padding:8px;", 150 "color:white; background-color:var(--cx-color-primary); padding:8px;", 151 new Date().toLocaleString(undefined, { 152 year: 'numeric', 153 month: 'numeric', 154 day: 'numeric', 155 hour: 'numeric', 156 minute: 'numeric', 157 second: 'numeric', 158 fractionalSecondDigits: 3 159 }), ...arguments); 160 }; 161 162 /** 163 * Log a message to console for debugging purpose 164 * 165 * @returns 166 */ 167 SidePanelController._debug = function () 168 { 169 if (!SidePanelController.Const.DEBUG_MODE) return; 170 171 console.log("%c[DEBUG MODE] SidePanelController%c%s", 172 "color:white; background-color:var(--cx-color-danger-800); padding:8px;", 173 "color:white; background-color:var(--cx-color-primary); padding:8px;", 174 new Date().toLocaleString(undefined, { 175 year: 'numeric', 176 month: 'numeric', 177 day: 'numeric', 178 hour: 'numeric', 179 minute: 'numeric', 180 second: 'numeric', 181 fractionalSecondDigits: 3 182 }), ...arguments); 183 }; 184 185 /** 186 * Ensure a condition is true. Log an error if it is not. 187 * 188 * @param {boolean} mustTrueCondition The condition that should be true. 189 * @param {any} messageIfFail Message to log if the condition fails. 190 * 191 * @returns {boolean} True if the condition is true. False if it fails. 192 */
193 SidePanelController._ensure = function (mustTrueCondition, messageIfFail) 194 { 195 if (!mustTrueCondition) 196 { 197 SidePanelController._error(messageIfFail); 198 return false; 199 } 200 return true; 201 }; 202 203 /** 204 * Validate Activate's options 205 * 206 * @param {ActivateOptions} options Options. 207 * 208 * @returns {boolean} True if the options are valid. 209 */ 210 SidePanelController._validateActivateOptions = function (options) 211 { 212 return SidePanelController._ensure(Utils.IsStringFilled(options.panelSelector), "Panel Selector must be filled") 213 && SidePanelController._ensure(Utils.IsStringFilled(options.openTriggerSelector), "OpenTrigger Selector must be filled") 214 && SidePanelController._ensure(Utils.IsStringFilled(options.closeTriggerSelector), "CloseTrigger Selector must be filled") 215 && SidePanelController._ensure(Utils.IsStringFilled(options.sizerSelector), "Sizer Selector must be filled") 216 && SidePanelController._ensure(options.changeModeTriggerSelectors != null, "ChangeModeTrigger Selectors must be provided") 217 && SidePanelController._ensure(Utils.IsStringFilled(options.changeModeTriggerSelectors.free), "ChangeModeTrigger_Free Selector must be filled") 218 && SidePanelController._ensure(Utils.IsStringFilled(options.changeModeTriggerSelectors.pinned), "ChangeModeTrigger_Pinned Selector must be filled") 219 && SidePanelController._ensure(SidePanelController.Const.SIDE_PANEL_ALL_MODES.includes(options.mode), 220 'Mode must be any of ["' + SidePanelController.Const.SIDE_PANEL_ALL_MODES.join('","') + '"]. Received: ' + options.mode) 221 && SidePanelController._ensure(Utils.IsStringFilled(options.prefID), "Preference ID must be filled") 222 && SidePanelController._ensure(!Utils.IsStringFilled(options.forcedMode) || SidePanelController.Const.SIDE_PANEL_ALL_MODES.includes(options.forcedMode), 223 'Forced mode must not be set or must be any of ["' + SidePanelController.Const.SIDE_PANEL_ALL_MODES.join('","') + '"]. Received: ' + options.forcedMode); 224 }; 225 226 /** 227 * Refresh controller instance 228 * @param {any} panelID 229 */ 230 SidePanelController.RefreshInstance = function (panelID) 231 { 232 var controller = SidePanelController.Instances[panelID]; 233 if (!controller) return; 234 235 var options = controller.options; 236 options.mode = controller.mode; 237 controller.Dispose(); 238 SidePanelController.Activate(options); 239 }; 240 241 /** 242 * Activate the controller on a single side panel. 243 * 244 * @param {ActivateOptions} options Options. 245 * 246 */ 247 SidePanelController.Activate = function (options) 248 { 249 SidePanelController._debug("Creating new controller with the following options:", options); 250 251 // Validate options 252 if (!SidePanelController._validateActivateOptions(options)) 253 { 254 SidePanelController._debug("Receive invalid options", options, "--> No controller created"); 255 return; 256 } 257 var panel = document.querySelector(options.panelSelector); 258 var openTrigger = document.querySelector(options.openTriggerSelector); 259 var closeTrigger = document.querySelector(options.closeTriggerSelector); 260 var sizer = document.querySelector(options.sizerSelector); 261 var changeModeTriggers = { 262 free: document.querySelector(options.changeModeTriggerSelectors.free), 263 pinned: document.querySelector(options.changeModeTriggerSelectors.pinned), 264 } 265 if (!panel || !openTrigger || !closeTrigger || !sizer || !changeModeTriggers.free || !changeModeTriggers.pinned) 266 { 267 SidePanelController._debug("Cannot find required elements", { 268 [options.panelSelector]: panel, 269 [options.openTriggerSelector]: openTrigger, 270 [options.closeTriggerSelector]: closeTrigger, 271 [options.sizerSelector]: sizer, 272 [options.changeModeTriggerSelectors.free]: changeModeTriggers.free, 273 [options.changeModeTriggerSelectors.pinned]: changeModeTriggers.pinned, 274 }, "--> No controller created"); 275 return; 276 } 277 278 // Create controller 279 var panelID = panel.GetId(); 280 var controller = new SidePanelController({ 281 panel: panel, 282 openTrigger: openTrigger, 283 closeTrigger: closeTrigger, 284 sizer: sizer, 285 changeModeTriggers, 286 }, options); 287 288 if (SidePanelController.Instances[panelID]) 289 { 290 SidePanelController.Instances[panelID].Dispose(); 291 } 292 SidePanelController.Instances[panelID] = controller; 293 294 SidePanelController._debug("Created new controller for Side Panel", panelID); 295 296 // Initialize controller 297 controller.Initialize(); 298 299 // Register for event to clean up when the panel is removed from the page 300 Matrix3.RegisterForCleanup(panelID, "SidePanelController_" + panelID, function () 301 { 302 try 303 { 304 controller.Dispose(); 305 SidePanelController._debug("Disposed the controller for Side Panel", panelID); 306 } catch (e) { } 307 }); 308 309 return controller; 310 }; 311 312 /** 313 * Log a message to console for debugging purpose 314 */ 315 SidePanelController.prototype._debug = function () 316 { 317 SidePanelController._debug(this.id, ...arguments); 318 }; 319 320 /** 321 * Log an error to console for debugging purpose 322 */ 323 SidePanelController.prototype._error = function () 324 { 325 SidePanelController._error(this.id, ...arguments); 326 }; 327 328 /** 329 * Ensure a condition is true. Log an error if it is not. 330 * 331 * @param {boolean} mustTrueCondition The condition that should be true. 332 * @param {any} messageIfFail Message to log if the condition fails. 333 * 334 * @returns {boolean} True if the condition is true. False if it fails. 335 */ 336 SidePanelController.prototype._ensure = function (mustTrue
336Condition, messageIfFail) 337 { 338 if (!mustTrueCondition) 339 { 340 this._error(messageIfFail); 341 return false; 342 } 343 return true; 344 }; 345 346 /** 347 * Initialize the controller 348 */ 349 SidePanelController.prototype.Initialize = function () 350 { 351 var controller = this; 352 353 controller._debug("Initializing controller"); 354 355 controller._debug("Setup event listeners"); 356 357 controller._setupEvents(); 358 359 controller._debug("Setup elements for initial mode", controller.mode); 360 361 controller._setupElements(controller.mode); 362 363 controller._debug("Initialized controller"); 364 }; 365 366 /** 367 * Dispose the controller 368 */ 369 SidePanelController.prototype.Dispose = function () 370 { 371 for (var i = 0; i < this._cleanupFunctions.length; i++) 372 { 373 this._cleanupFunctions[i].call(null); 374 } 375 this._cleanupFunctions = []; 376 377 this.context.sizer.SetProperty(ConstProperties.Const.MAX_RESIZE_WIDTH, this.context.sizer.__originalMaxResizeWidth); 378 379 delete SidePanelController.Instances[this.id]; 380 } 381 382 /** 383 * Setup keyboard navigation for Content panel 384 * @private 385 */ 386 SidePanelController.prototype._setupKeyboardNavigation = function () 387 { 388 var controller = this; 389 390 // Find Content panel and OptionsPnl 391 if (!controller.context || !controller.context.panel) { 392 return; 393 } 394 395 var contentPanel = controller.context.panel.querySelector('[id$=":Content"]'); 396 var optionsPnl = controller.context.panel.querySelector('[id$=":OptionsPnl"]'); 397 398 if (!contentPanel) 399 { 400 return; 401 } 402 403 // Ensure Content panel has tabindex for keyboard focus 404 if (!contentPanel.hasAttribute('tabindex')) 405 { 406 contentPanel.setAttribute('tabindex', '0'); 407 } 408 409 // Keyboard event handler for Content panel 410 var onKeyDownContentPanel = function (e) 411 { 412 // Only handle keyboard events when the Content panel itself is focused
413 // Not when focus is on child elements inside the Content panel 414 if (!document.activeElement || document.activeElement !== contentPanel) 415 { 416 return; 417 } 418 419 var key = e.key; 420 421 // Handle Tab key - move to OptionsPnl 422 if (key === "Tab" && !e.shiftKey && optionsPnl) 423 { 424 var firstFocusableInOptions = Utils.FindFirstFocusableElement(optionsPnl); 425 if (firstFocusableInOptions) 426 { 427 e.preventDefault(); 428 e.stopPropagation(); 429 firstFocusableInOptions.focus(); 430 } 431 } 432 // Handle Enter key - move focus inside Content panel 433 else if (key === "Enter") 434 { 435 var firstFocusableInContent = Utils.FindFirstFocusableElement(contentPanel); 436 if (firstFocusableInContent) 437 { 438 e.preventDefault(); 439 e.stopPropagation(); 440 firstFocusableInContent.focus(); 441 } 442 } 443 }; 444 445 contentPanel.addEventListener('keydown', onKeyDownContentPanel); 446 controller._cleanupFunctions.push(function () { 447 contentPanel.removeEventListener('keydown', onKeyDownContentPanel); 448 contentPanel = null; 449 }); 450 451 // Setup keyboard handler for OptionsPnl to handle Shift+Tab back to Content panel 452 if (optionsPnl) 453 { 454 var onKeyDownOptionsPnl = function (e) 455 { 456 var key = e.key; 457 458 // Handle Shift+Tab from first focusable element in OptionsPnl 459 if (key === "Tab" && e.shiftKey) 460 { 461 var activeElement = document.activeElement; 462 var allFocusable = Utils.FindAllFocusableElements(optionsPnl); 463 var focusableElements = []; 464 465 // Filter out the container itself 466 for (var i = 0; i < allFocusable.length; i++) 467 { 468 if (allFocusable[i] !== optionsPnl) 469 { 470 focusableElements.push(allFocusable[i]); 471 } 472 } 473 474 // Check if we're on the first focusable element 475 if (focusableElements.length > 0 && activeElement === focusableElements[0]) 476 { 477 e.preventDefault(); 478 e.stopPropagation(); 479 contentPanel.focus(); 480 } 481 } 482 }; 483 484 optionsPnl.addEventListener('keydown', onKeyDownOptionsPnl, true); 485 controller._cleanupFunctions.push(function () 486 { 487 optionsPnl.removeEventListener('keydown', onKeyDownOptionsPnl, true); 488 optionsPnl = null; 489 }); 490 } 491 }; 492 493 /** 494 * Setup event listeners 495 */ 496 SidePanelController.prototype._setupEvents = function () 497 { 498 var controller = this; 499 500 var panelControl = $Control(controller.context.panel.GetId()); 501 502 // Setup keyboard navigation for Content panel 503 controller._setupKeyboardNavigation(); 504 505 // #region Common behavior 506 507 // Collapse when clicking `CloseTrigger` 508 var onClickCloseTrigger = function () 509 { 510 panelControl.Action_CloseFold(); 511 }; 512 controller.context.closeTrigger.addEventListener('click', onClickCloseTrigger); 513 controller._cleanupFunctions.push(function () { controller.context.closeTrigger.removeEventListener('click', onClickCloseTrigger); }); 514 515 // Change mode to pinned when clicking `ChangeModeTrigger_Pinned` 516 var onClickPinnedTrigger = function () 517 { 518 controller.ChangeMode("pinned"); 519 }; 520 controller.context.changeModeTriggers.pinned.addEventListener('click', onClickPinnedTrigger); 521 controller._cleanupFunctions.push(function () { controller.context.changeModeTriggers.pinned.removeEventListener('click', onClickPinnedTrigger); }); 522 523 // Change mode to pinned when clicking `ChangeModeTrigger_Free` 524 var onClickFreeTrigger = function () 525 { 526 controller.ChangeMode("free"); 527 }; 528 controller.context.changeModeTriggers.free.addEventListener('click', onClickFreeTrigger); 529 controller._cleanupFunctions.push(function () { controller.context.changeModeTriggers.free.removeEventListener('click', onClickFreeTrigger); }); 530 531 // Auto-collapse under screen size threshold 532 var handleResize = function () 533 { 534 var viewportWidth = Utils.GetViewportWidth(); 535 if (viewportWidth < controller.autoCollapseThreshold) 536 { 537 panelControl.Action_CloseFold(); 538 } 539 }; 540 window.addEventListener('resize', handleResize); 541 controller._cleanupFunctions.push(function () { window.removeEventListener('resize', handleResize); }); 542 handleResize(); 543 544 // #endregion 545 546 // #region Free mode behavior 547 548 // Expand when hovering `OpenTrigger` 549 var onHoverOpenTrigger = function () 550 { 551 if (controller.mode === "pinned") 552 { 553 return; 554 } 555 556 panelControl.Action_OpenFold(); 557 }; 558 controller.context.openTrigger.addEventListener('mouseover', onHoverOpenTrigger); 559 controller._cleanupFunctions.push(function () { controller.context.openTrigger.removeEventListener('mouseover', onHoverOpenTrigger); }); 560 561 // Register mousedown inside 562 var mousedownInsidePanel = false;
563 var onMouseDownInsidePanel = function () 564 { 565 if (controller.mode === "pinned") 566 { 567 return; 568 } 569 570 mousedownInsidePanel = true; 571 document.addEventListener("mouseup", function () { mousedownInsidePanel = false; }, { once: true, capture: true }); 572 }; 573 controller.context.panel.addEventListener('mousedown', onMouseDownInsidePanel); 574 controller._cleanupFunctions.push(function () { controller.context.panel.removeEventListener('mousedown', onMouseDownInsidePanel); }); 575 576 // Collapse when moving cursor out of `Panel` 577 var onMouseOutPanel = function (e) 578 { 579 if (controller.mode === "pinned") 580 { 581 return; 582 } 583 584 controller._debug("Panel is free-floating. Checking if mouse outside panel") 585 // Test if the mouse is actually out of the panel 586 if (!controller.context.panel.IsUnderMouse(e, true)) 587 { 588 controller._debug("Mouse outside panel"); 589 if (mousedownInsidePanel) 590 { 591 controller._debug("User has mosuedown inside panel & drag outside. Do not fold unless the mousedown is released"); 592 return; 593 } 594 595 // Task: https://link.orangelogic.com/Tasks/4G806 596 // Check if the current mouse out has any text selection as user has drag the mouse outside of the control. 597 var sameControlTextSelection = false; // Are there any selected text and does the that text selection is inside the element that register this event? 598 599 // Not working with firefox as it does not support window.getSelection (https://link.orangelogic.com/Tasks/4G2P0) 600 if (!Utils.IsFirefox()) 601 { 602 var selection = window.getSelection(); 603 604 sameControlTextSelection = selection !== null && selection.toString() !== "" // text selection exists 605 if (sameControlTextSelection) 606 { 607 sameControlTextSelection = controller.context.panel.contains(selection.baseNode); // the text is select on a child of the registered control 608 } 609 } 610 611 if (!sameControlTextSelection) 612 { 613 controller._debug("No text selection. Collapse the panel"); 614 panelControl.Action_CloseFold(); 615 } 616 } 617 }; 618 document.addEventListener('mousemove', onMouseOutPanel); 619 controller._cleanupFunctions.push(function () { document.removeEventListener('mousemove', onMouseOutPanel); }); 620 621 // Collapse when loosing focus to elements outside panel 622 var onFocusOutPanel = function (e) 623 { 624 if (controller.mode === "pinned") 625 { 626 return; 627 } 628 629 controller._debug("Panel is free-floating. Checking if loosing focus to elements outside panel"); 630 if (!controller.context.panel.contains(e.target)) 631 { 632 controller._debug("Active element is outside panel. Collapse the panel"); 633 panelControl.Action_CloseFold(); 634 } 635 }; 636 document.addEventListener('focusin', onFocusOutPanel, true); 637 controller._cleanupFunctions.push(function () { document.removeEventListener('focusin', onFocusOutPanel, true); }); 638 639 640 // #endregion 641 642 // #region Pinned mode behavior 643 644 // Expand when clicking `OpenTrigger` 645 var onClickOpenTrigger = function () 646 { 647 if (controller.mode === "free") 648 { 649 return; 650 } 651 652 panelControl.Action_OpenFold(); 653 }; 654 controller.context.openTrigger.addEventListener('click', onClickOpenTrigger); 655 controller._cleanupFunctions.push(function () { controller.context.openTrigger.removeEventListener('click', onClickOpenTrigger); }); 656 657 // #endregion 658 }; 659 660 SidePanelController.prototype._hideElement = function (element) 661 { 662 element.classList.add(StyleMgr.Const.CLASS_INVISIBLE); 663 element.style.setProperty("pointer-events", "none"); 664 var a = element.querySelector('a[tabindex]'); 665 if (!a) 666 { 667 return; 668 } 669 a.setAttribute('data-original-tabindex', a.getAttribute('tabindex')); 670 a.setAttribute('tabindex', -1); 671 }; 672 673 SidePanelController.prototype._showElement = function (element) 674 { 675 element.classList.remove(StyleMgr.Const.CLASS_INVISIBLE); 676 element.style.setProperty("pointer-events", ""); 677 var a = element.querySelector('a[tabindex]'); 678 if (!a) 679 { 680 return; 681 } 682 a.setAttribute('tabindex', a.getAttribute('data-original-tabindex') || 0); 683 a.removeAttribute('data-original-tabindex'); 684 }; 685 686 /** 687 * Setup elements based on the mode 688 * @param {SidePanelMode} mode 689 */ 690 SidePanelController.prototype._setupElements = function (mode) 691 { 692 var controller = this; 693 694 controller.context.panel.setAttribute(SidePanelController.Const.ATTR_DATA_SIDE_PANEL_MODE, mode); 695 696 if (mode === "pinned") 697 { 698 controller._showElement(controller.context.closeTrigger); 699 700 controller._showElement(controller.context.changeModeTriggers.free); 701 controller._hideElement(controller.context.changeModeTriggers.pinned); 702 703 controller.context.sizer.SetProperty(ConstProperties.Const.MAX_RESIZE_WIDTH, controller.context.sizer.__originalMaxResizeWidth); 704 } 705 else if (mode === "free") 706 { 707 controller._hideElement(controller.context.closeTrigger); 708 709 controller._hideElement(controller.context.changeModeTriggers.free); 710 controller._showElement(controller.context.changeModeTriggers.pinned); 711 712 controller.context.sizer.__originalMaxResizeWidth = controller.context.sizer.GetProperty(ConstProperties.Const.MAX_RESIZE_WIDTH); 713 controller.context.sizer.SetProperty(ConstProperties.Const.MAX_RESIZE_WIDTH, 1); 714 715 // Automatically collapse `Panel` when switching to free mode 716 var panelControl = $Control(controller.context.panel.GetId()); 717 panelControl.Action_CloseFold(); 718 } 719 // If mode is forced, hide all triggers to change mode 720 if (controller.forcedMode) 721 { 722 controller._hideElement(controller.context.changeModeTriggers.free); 723 controller._hideElement(controller.context.changeModeTriggers.pinned); 724 } 725 controller.context.sizer.ForceHasChanged(); 726 PositionMgr.GetInstance().AddToDirtyXList(controller.context.sizer); 727 PositionMgr.GetInstance().CalculatePosition(); 728 } 729 730 /** 731 * Change panel mode 732 * 733 * @param {SidePanelMode} mode Target mode 734 * 735 * @returns 736 */ 737 SidePanelController.prototype.ChangeMode = function (mode) 738 { 739 var controller = this; 740 741 if (!controller._ensure(SidePanelController.Const.SIDE_PANEL_ALL_MODES.includes(mode), 742 'Mode must be any of ["' + SidePanelController.Const.SIDE_PANEL_ALL_MODES.join('","') + '"]. Received: ' + mode)) 743 { 744 return; 745 } 746 747 if (controller.mode === mode) 748 { 749 controller._debug("Mode is already ", controller.mode); 750 return; 751 } 752 753 if (controller.forcedMode && controller.forcedMode !== mode) 754 { 755 controller._debug("Mode is forced to ", controller.forcedMode, ". Unable to change to ", mode); 756 return; 757 } 758 759 controller.mode = mode; 760 controller._debug("Mode updated to", mode); 761 if (typeof controller.onModeChange === 'function') 762 { 763 controller.onModeChange(mode); 764 } 765 766 controller._setupElements(mode); 767 768 $.ajax({ type: "GET", url: "htm/AlterSession.aspx?SPMID=" + encodeURIComponent(controller.prefID) + "&SPM=" + controller.mode }) 769 .done(function () 770 { 771 controller._debug("Updated mode to", mode, "in user preference"); 772 }) 773 .fail(function () 774 { 775 controller._debug("Failed to update mode to", mode, "in user preference"); 776 }); 777 }; 778 779 SidePanelController._debug("SidePanelController loaded"); 780 781 window.SidePanelController = SidePanelController; 782 })(); 783}
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.