1/** 2 * Legacy flyout code extracted from global.js 3 * 4 * @copyright 2009-2018 Vanilla Forums Inc. 5 * @license GPL-2.0-only 6 */ 7 8/** 9 * IFFE for flyout code. 10 * 11 * @param {Window} window 12 * @param {jQuery} $ 13 */ 14(function(window, $) { 15 var USE_NEW_FLYOUTS = window.gdn.meta.themeFeatures.NewFlyouts || false; 16 var OPEN_CLASS = "Open"; 17 18 /** 19 * Content load handler, which is fired on first load, and when additional content is loaded in. 20 */ 21 $(document).on("contentLoad", function(e) { 22 kludgeFlyoutHTML(); 23 }); 24 25 /** 26 * Document ready handler. Runs only the first time the page is loaded. 27 */ 28 $(function() { 29 $(document).delegate(".Hijack, .js-hijack", "click", handleHijackClick); 30 $(document).delegate(".ButtonGroup > .Handle", "click", handleButtonHandleClick); 31 $(document).delegate(".ToggleFlyout", "click", handleToggleFlyoutClick); 32 $(document).delegate(".ToggleFlyout a", "mouseup", handleToggleFlyoutMouseUp); 33 $(document).delegate(document, "click", closeAllFlyouts); 34 $(document).delegate(".mobileFlyoutOverlay", "click", function (e) { 35 e.stopPropagation(); 36 closeAllFlyouts(); 37 }); 38 }); 39 40 /** 41 * Workarounds for limitations of flyout's HTML structure. 42 */ 43 function kludgeFlyoutHTML() { 44 var $handles = $(".ToggleFlyout:not([data-is-kludged]), .editor-dropdown:not([data-is-kludged]), .ButtonGroup:not([data-is-kludged])"); 45 46 $handles.each(function() { 47 $handles 48 .find(".FlyoutButton, .Handle, .editor-action:not(.editor-action-separator)") 49 .each(function() { 50 $(this) 51 .attr("tabindex", "0") 52 .attr("role", "button") 53 .attr("aria-haspopup", "true"); 54 55 $(this).accessibleFlyoutHandle(false); 56 }); 57 58 $handles.find(".Flyout, .Dropdown").each(function() { 59 $(this).accessibleFlyout(false); 60 61 $(this) 62 .find("a") 63 .each(function() { 64 $(this).attr("tabindex", "0"); 65 }); 66 }); 67 68 $(this).attr("data-is-kludged", "true"); 69 $(this).attr("role", "button"); 70 $(this).attr("tabIndex", "0"); 71 }); 72 73 if (USE_NEW_FLYOUTS) { 74 var $contents = $(".Flyout:not([data-is-kludged]), .ButtonGroup .Dropdown:not([data-is-kludged])"); 75 var wrap = document.createElement("span"); 76 wrap.classList.add("mobileFlyoutOverlay"); 77 78 $contents.each(function() { 79 var $item = $(this); 80 if (!this.parentElement.classList.contains("mobileFlyoutOverlay")) { 81 $item.wrap(wrap); 82 } 83 84 // Some flyouts had conflicting inline display: none directly in the view. 85 // We don't change that on open/close with the new style anymore so let's clean it up here. 86 $item.removeAttr("style"); 87 88 $(this).attr("data-is-kludged", "true"); 89 }); 90 } 91 } 92 93 var BODY_CLASS = "flyoutIsOpen"; 94 95 /** 96 * Close all flyouts and open the specified one. 97 * 98 * @param {JQuery} $toggleFlyout The flyout handle 99 * @param {JQuery} $flyout The flyout body. 100 */ 101 function openFlyout($toggleFlyout, $flyout) { 102 closeAllFlyouts(); 103 104 $toggleFlyout 105 .addClass(OPEN_CLASS) 106 .closest(".Item") 107 .addClass(OPEN_CLASS); 108 109 if (!USE_NEW_FLYOUTS) { 110 $flyout.show(); 111 } 112 $toggleFlyout.setFlyoutAttributes(); 113 document.body.classList.add(BODY_CLASS); 114 } 115 116 /** 117 * Close the specified flyout. 118 * 119 * @param {JQuery} $toggleFlyout The flyout handle 120 * @param {JQuery} $flyout The flyout body. 121 */ 122 function closeFlyout($toggleFlyout, $flyout) { 123 if (!USE_NEW_FLYOUTS) { 124 $flyout.hide(); 125 } 126 $toggleFlyout 127 .removeClass(OPEN_CLASS) 128 .closest(".Item") 129 .removeClass(OPEN_CLASS); 130 $toggleFlyout.setFlyoutAttributes(); 131 document.body.classList.remove(BODY_CLASS); 132 } 133 134 /** 135 * Close all flyouts, including ButtonGroups. 136 */ 137 function closeAllFlyouts(e) { 138 closeFlyout($(".ToggleFlyout"), $(".Flyout")); 139 // Clear the button groups that are open as well. 140 $(".ButtonGroup") 141 .removeClass(OPEN_CLASS)
142 .setFlyoutAttributes(); 143 144 // Kludge for legacy editor. 145 $(".editor-dropdown-open") 146 .removeClass("editor-dropdown-open") 147 .setFlyoutAttributes(); 148 document.body.classList.remove(BODY_CLASS); 149 } 150 151 window.closeAllFlyouts = closeAllFlyouts; 152 153 /** 154 * Take over the clicking of an element in order to make a post request. 155 * 156 * @param {MouseEvent} e The click event. 157 */ 158 function handleHijackClick(e) { 159 var $elem = $(this); 160 var $parent = $(this).closest(".Item"); 161 var $toggleFlyout = $elem.closest(".ToggleFlyout"); 162 var href = $elem.attr("href"); 163 var progressClass = $elem.hasClass("Bookmark") ? "Bookmarking" : "InProgress"; 164 var ariaPressed = $elem.attr("aria-pressed"); 165 166 // If empty, or starts with a fragment identifier, do not send 167 // an async request. 168 if (!href || href.trim().indexOf("#") === 0) return; 169 170 gdn.disable(this, progressClass); 171 e.stopPropagation(); 172 173 $.ajax({ 174 type: "POST", 175 url: href, 176 data: { DeliveryType: "VIEW", DeliveryMethod: "JSON", TransientKey: gdn.definition("TransientKey") }, 177 dataType: "json", 178 complete: function() { 179 gdn.enable($elem.get(0)); 180 if (typeof ariaPressed !== 'undefined') { 181 $elem.attr("aria-pressed", !ariaPressed); 182 } 183 $elem.removeClass(progressClass); 184 $elem.attr("href", href); 185 $flyout = $toggleFlyout.find(".Flyout"); 186 closeFlyout($toggleFlyout, $flyout); 187 }, 188 error: function(xhr) { 189 gdn.informError(xhr); 190 }, 191 success: function(json) { 192 if (json === null) json = {}; 193 194 var informed = gdn.inform(json); 195 gdn.processTargets(json.Targets, $elem, $parent); 196 197 // return focus to element 198 // this is necessary because the element got re-rendered 199 // select element by href because classes got manipulated 200 if ($parent.length > 0) { 201 $parent.find('[href="'+href+'"]').focus(); 202 } 203 204 // If there is a redirect url, go to it. 205 if (json.RedirectTo) { 206 setTimeout(function() { 207 window.location.replace(json.RedirectTo); 208 }, informed ? 3000 : 0); 209 } 210 }, 211 }); 212 213 return false; 214 } 215 216 /** 217 * Close existing flyouts and dropdowns and open the dropdown for a particular button handle. 218 */ 219 function handleButtonHandleClick() { 220 var $buttonGroup = $(this).closest(".ButtonGroup"); 221 var $isOpen = $buttonGroup.hasClass(OPEN_CLASS); 222 closeAllFlyouts(); 223 if (!$isOpen) { 224 // Open this one 225 $buttonGroup.addClass(OPEN_CLASS).setFlyoutAttributes(); 226 } 227 return false; 228 } 229 230 /** 231 * Handle clicks on the flyout. 232 * 233 * @param {MouseEvent} e The click event to handle. 234 */ 235 function handleToggleFlyoutClick(e) { 236 var $toggleFlyout = $(this); 237 var $flyout = $(".Flyout", this); 238 var isHandle = false; 239 240 if ($(e.target).closest(".Flyout").length === 0) { 241 isHandle = true; 242 e.stopPropagation(); 243 } else if ( 244 $(e.target).hasClass("Hijack") || 245 $(e.target) 246 .closest("a") 247 .hasClass("Hijack") 248 ) { 249 return; 250 } 251 e.stopPropagation(); 252 $toggleFlyout.fillFlyoutDynamically(); 253 254 // The old check. 255 var isFlyoutClosed = $flyout.css("display") == "none"; 256 if (USE_NEW_FLYOUTS) { 257 // The new check. 258 isFlyoutClosed = !$toggleFlyout.hasClass(OPEN_CLASS); 259 } 260 261 // Toggling. 262 if (isFlyoutClosed) { 263 openFlyout($toggleFlyout, $flyout); 264 } else { 265 closeFlyout($toggleFlyout, $flyout); 266 } 267 268 if (isHandle) return false;
269 } 270 271 /** 272 * Close all of the flyouts unless we are clicking on a button inside of a flyout. 273 */ 274 function handleToggleFlyoutMouseUp() { 275 if ($(this).hasClass("FlyoutButton")) return; 276 closeAllFlyouts(); 277 } 278 279 /** 280 * jQuery function extensions 281 */ 282 $.fn.extend({ 283 fillFlyoutDynamically: function() { 284 var rel = $(this).attr("rel"); 285 if (rel) { 286 $flyout = $(this).find(".Flyout"); 287 288 // Clear the rel and set a progress indicator. 289 $(this).attr("rel", ""); 290 $flyout.html('<div class="InProgress" style="height: 30px"></div>'); 291 292 // Fetch the contents dynamically and fill on contents of the flyout. 293 $.ajax({ 294 url: gdn.url(rel), 295 data: { DeliveryType: "VIEW" }, 296 success: function(data) { 297 $flyout.html(data); 298 }, 299 error: function(xhr) { 300 $flyout.html(""); 301 gdn.informError(xhr, true); 302 }, 303 }); 304 } 305 }, 306 accessibleFlyoutHandle: function(isOpen) { 307 $(this).attr("aria-expanded", isOpen.toString()); 308 }, 309 310 accessibleFlyout: function(isOpen) { 311 $(this).attr("aria-hidden", (!isOpen).toString()); 312 }, 313 314 setFlyoutAttributes: function() { 315 $toggleFlyouts = $(this); 316 $toggleFlyouts.each(function() { 317 $toggle = $(this); 318 var $handle = $(this).find( 319 ".FlyoutButton, .Button-Options, .Handle, .editor-action:not(.editor-action-separator)" 320 ); 321 var $flyout = $(this).find(".Flyout, .Dropdown"); 322 var isOpen = $toggle.hasClass(OPEN_CLASS); 323 324 $handle.accessibleFlyoutHandle(isOpen); 325 $flyout.accessibleFlyout(isOpen); 326 }); 327 }, 328 }); 329})(window, jQuery);
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.