1(function($) { 2 3 /** 4 * Generate an indented list of links from a nav. Meant for use with panel(). 5 * @return {jQuery} jQuery object. 6 */ 7 $.fn.navList = function() { 8 9 var $this = $(this); 10 $a = $this.find('a'), 11 b = []; 12 13 $a.each(function() { 14 15 var $this = $(this), 16 indent = Math.max(0, $this.parents('li').length - 1), 17 href = $this.attr('href'), 18 target = $this.attr('target'); 19 20 b.push( 21 '<a ' + 22 'class="link depth-' + indent + '"' + 23 ( (typeof target !== 'undefined' && target != '') ? ' target="' + target + '"' : '') + 24 ( (typeof href !== 'undefined' && href != '') ? ' href="' + href + '"' : '') + 25 '>' + 26 '<span class="indent-' + indent + '"></span>' + 27 $this.text() + 28 '</a>' 29 ); 30 31 }); 32 33 return b.join(''); 34 35 }; 36 37 /** 38 * Panel-ify an element. 39 * @param {object} userConfig User config. 40 * @return {jQuery} jQuery object. 41 */ 42 $.fn.panel = function(userConfig) { 43 44 // No elements? 45 if (this.length == 0) 46 return $this; 47 48 // Multiple elements? 49 if (this.length > 1) { 50 51 for (var i=0; i < this.length; i++) 52 $(this[i]).panel(userConfig); 53 54 return $this; 55 56 } 57 58 // Vars. 59 var $this = $(this), 60 $body = $('body'), 61 $window = $(window), 62 id = $this.attr('id'), 63 config; 64 65 // Config. 66 config = $.extend({ 67 68 // Delay. 69 delay: 0, 70 71 // Hide panel on link click. 72 hideOnClick: false, 73 74 // Hide panel on escape keypress. 75 hideOnEscape: false, 76 77 // Hide panel on swipe. 78 hideOnSwipe: false, 79 80 // Reset scroll position on hide. 81 resetScroll: false, 82 83 // Reset forms on hide. 84 resetForms: false, 85 86 // Side of viewport the panel will appear. 87 side: null, 88 89 // Target element for "class". 90 target: $this, 91 92 // Class to toggle. 93 visibleClass: 'visible' 94 95 }, userConfig); 96 97 // Expand "target" if it's not a jQuery object already. 98 if (typeof config.target != 'jQuery') 99 config.target = $(config.target); 100 101 // Panel. 102 103 // Methods. 104 $this._hide = function(event) { 105 106 // Already hidden? Bail. 107 if (!config.target.hasClass(config.visibleClass)) 108 return; 109 110 // If an event was provided, cancel it. 111 if (event) { 112 113 event.preventDefault(); 114 event.stopPropagation(); 115 116 } 117 118 // Hide. 119 config.target.removeClass(config.visibleClass); 120 121 // Post-hide stuff. 122 window.setTimeout(function() { 123 124 // Reset scroll position. 125 if (config.resetScroll) 126 $this.scrollTop(0); 127 128 // Reset forms. 129 if (config.resetForms) 130 $this.find('form').each(function() { 131 this.reset(); 132 }); 133 134 }, config.delay); 135 136 }; 137 138 // Vendor fixes. 139 $this 140 .css('-ms-overflow-style', '-ms-autohiding-scrollbar') 141 .css('-webkit-overflow-scrolling', 'touch'); 142 143 // Hide on click. 144 if (config.hideOnClick) { 145 146 $this.find('a') 147 .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)'); 148 149 $this 150 .on('click', 'a', function(event) { 151 152 if(window.innerWidth < 961) { 153 return; 154 } 155 var $a = $(this), 156 href = $a.attr('href'), 157 target = $a.attr('target'); 158 159 if (!href || href == '#' || href == '' || href == '#' + id) 160 return; 161 162 // Cancel original event. 163 event.preventDefault(); 164 event.stopPropagation(); 165 166 // Hide panel. 167 $this._hide(); 168 169 // Redirect to href. 170 window.setTimeout(function() { 171 172 if (target == '_blank') 173 window.open(href); 174 else 175 window.location.href = href; 176 177 }, config.delay + 10); 178 179 }); 180 181 } 182 183 // Event: Touch stuff. 184 $this.on('touchstart', function(event) { 185 186 $this.touchPosX = event.originalEvent.touches[0].pageX; 187 $this.touchPosY = event.originalEvent.touches[0].pageY; 188 189 }) 190 191 $this.on('touchmove', function(event) { 192 193 if ($this.touchPosX === null 194 || $this.touchPosY === null) 195 return; 196 197 var diffX = $this.touchPosX - event.originalEvent.touches[0].pageX, 198 diffY = $this.touchPosY - event.originalEvent.touches[0].pageY, 199 th = $this.outerHeight(), 200 ts = ($this.get(0).scrollHeight - $this.scrollTop()); 201 202 // Hide on swipe? 203 if (config.hideOnSwipe) { 204 205 var result = false, 206 boundary = 20, 207 delta = 50; 208 209 switch (config.side) { 210 211 case 'left': 212 result = (diffY < boundary && diffY >
212 (-1 * boundary)) && (diffX > delta); 213 break; 214 215 case 'right': 216 result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta)); 217 break; 218 219 case 'top': 220 result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta); 221 break; 222 223 case 'bottom': 224 result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta)); 225 break; 226 227 default: 228 break; 229 230 } 231 232 if (result) { 233 234 $this.touchPosX = null; 235 $this.touchPosY = null; 236 $this._hide(); 237 238 return false; 239 240 } 241 242 } 243 244 // Prevent vertical scrolling past the top or bottom. 245 if (($this.scrollTop() < 0 && diffY < 0) 246 || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) { 247 248 event.preventDefault(); 249 event.stopPropagation(); 250 251 } 252 253 }); 254 255 // Event: Prevent certain events inside the panel from bubbling. 256 $this.on('click touchend touchstart touchmove', function(event) { 257 event.stopPropagation(); 258 }); 259 260 // Event: Hide panel if a child anchor tag pointing to its ID is clicked. 261 $this.on('click', 'a[href="#' + id + '"]', function(event) { 262 263 event.preventDefault(); 264 event.stopPropagation(); 265 266 config.target.removeClass(config.visibleClass); 267 268 }); 269 270 // Body. 271 272 // Event: Hide panel on body click/tap. 273 $body.on('click touchend', function(event) { 274 $this._hide(event); 275 }); 276 277 // Event: Toggle. 278 $body.on('click', 'a[href="#' + id + '"]', function(event) { 279 280 event.preventDefault(); 281 event.stopPropagation(); 282 283 config.target.toggleClass(config.visibleClass); 284 285 }); 286 287 // Window. 288 289 // Event: Hide on ESC. 290 if (config.hideOnEscape) 291 $window.on('keydown', function(event) { 292 293 if (event.keyCode == 27) 294 $this._hide(event); 295 296 }); 297 298 return $this; 299 300 }; 301 302 /** 303 * Apply "placeholder" attribute polyfill to one or more forms. 304 * @return {jQuery} jQuery object. 305 */ 306 $.fn.placeholder = function() { 307 308 // Browser natively supports placeholders? Bail. 309 if (typeof (document.createElement('input')).placeholder != 'undefined') 310 return $(this); 311 312 // No elements? 313 if (this.length == 0) 314 return $this; 315 316 // Multiple elements? 317 if (this.length > 1) { 318 319 for (var i=0; i < this.length; i++) 320 $(this[i]).placeholder(); 321 322 return $this; 323 324 } 325 326 // Vars. 327 var $this = $(this); 328 329 // Text, TextArea. 330 $this.find('input[type=text],textarea') 331 .each(function() { 332 333 var i = $(this); 334 335 if (i.val() == '' 336 || i.val() == i.attr('placeholder')) 337 i 338 .addClass('polyfill-placeholder') 339 .val(i.attr('placeholder')); 340 341 }) 342 .on('blur', function() { 343 344 var i = $(this); 345 346 if (i.attr('name').match(/-polyfill-field$/)) 347 return; 348 349 if (i.val() == '') 350 i 351 .addClass('polyfill-placeholder') 352 .val(i.attr('placeholder')); 353 354 }) 355 .on('focus', function() { 356 357 var i = $(this); 358 359 if (i.attr('name').match(/-polyfill-field$/)) 360 return; 361 362 if (i.val() == i.attr('placeholder')) 363 i 364 .removeClass('polyfill-placeholder') 365 .val(''); 366 367 }); 368 369 // Password. 370 $this.find('input[type=password]') 371 .each(function() { 372 373 var i = $(this); 374 var x = $( 375 $('<div>') 376 .append(i.clone()) 377 .remove() 378 .html() 379 .replace(/type="password"/i, 'type="text"') 380 .replace(/type=password/i, 'type=text') 381 ); 382 383 if (i.attr('id') != '') 384 x.attr('id', i.attr('id') + '-polyfill-field'); 385 386 if (i.attr('name') != '') 387 x.attr('name', i.attr('name') + '-polyfill-field'); 388 389 x.addClass('polyfill-placeholder') 390 .val(x.attr('placeholder')).insertAfter(i); 391 392 if (i.val() == '') 393 i.hide(); 394 else 395 x.hide(); 396 397 i 398 .on('blur', function(event) { 399 400 event.preventDefault(); 401 402 var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); 403 404 if (i.val() == '') { 405 406 i.hide(); 407 x.show(); 408 409 } 410 411 });
412 413 x 414 .on('focus', function(event) { 415 416 event.preventDefault(); 417 418 var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']'); 419 420 x.hide(); 421 422 i 423 .show() 424 .focus(); 425 426 }) 427 .on('keypress', function(event) { 428 429 event.preventDefault(); 430 x.val(''); 431 432 }); 433 434 }); 435 436 // Events. 437 $this 438 .on('submit', function() { 439 440 $this.find('input[type=text],input[type=password],textarea') 441 .each(function(event) { 442 443 var i = $(this); 444 445 if (i.attr('name').match(/-polyfill-field$/)) 446 i.attr('name', ''); 447 448 if (i.val() == i.attr('placeholder')) { 449 450 i.removeClass('polyfill-placeholder'); 451 i.val(''); 452 453 } 454 455 }); 456 457 }) 458 .on('reset', function(event) { 459 460 event.preventDefault(); 461 462 $this.find('select') 463 .val($('option:first').val()); 464 465 $this.find('input,textarea') 466 .each(function() { 467 468 var i = $(this), 469 x; 470 471 i.removeClass('polyfill-placeholder'); 472 473 switch (this.type) { 474 475 case 'submit': 476 case 'reset': 477 break; 478 479 case 'password': 480 i.val(i.attr('defaultValue')); 481 482 x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); 483 484 if (i.val() == '') { 485 i.hide(); 486 x.show(); 487 } 488 else { 489 i.show(); 490 x.hide(); 491 } 492 493 break; 494 495 case 'checkbox': 496 case 'radio': 497 i.attr('checked', i.attr('defaultValue')); 498 break; 499 500 case 'text': 501 case 'textarea': 502 i.val(i.attr('defaultValue')); 503 504 if (i.val() == '') { 505 i.addClass('polyfill-placeholder'); 506 i.val(i.attr('placeholder')); 507 } 508 509 break; 510 511 default: 512 i.val(i.attr('defaultValue')); 513 break; 514 515 } 516 }); 517 518 }); 519 520 return $this; 521 522 }; 523 524 /** 525 * Moves elements to/from the first positions of their respective parents. 526 * @param {jQuery} $elements Elements (or selector) to move. 527 * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations. 528 */ 529 $.prioritize = function($elements, condition) { 530 531 var key = '__prioritize'; 532 533 // Expand $elements if it's not already a jQuery object. 534 if (typeof $elements != 'jQuery') 535 $elements = $($elements); 536 537 // Step through elements. 538 $elements.each(function() { 539 540 var $e = $(this), $p, 541 $parent = $e.parent(); 542 543 // No parent? Bail. 544 if ($parent.length == 0) 545 return; 546 547 // Not moved? Move it. 548 if (!$e.data(key)) { 549 550 // Condition is false? Bail. 551 if (!condition) 552 return; 553 554 // Get placeholder (which will serve as our point of reference for when this element needs to move back). 555 $p = $e.prev(); 556 557 // Couldn't find anything? Means this element's already at the top, so bail. 558 if ($p.length == 0) 559 return; 560 561 // Move element to top of parent. 562 $e.prependTo($parent); 563 564 // Mark element as moved. 565 $e.data(key, $p); 566 567 } 568 569 // Moved already? 570 else { 571 572 // Condition is true? Bail. 573 if (condition) 574 return; 575 576 $p = $e.data(key); 577 578 // Move element back to its original location (using our placeholder). 579 $e.insertAfter($p); 580 581 // Unmark element as moved. 582 $e.removeData(key); 583 584 } 585 586 }); 587 588 }; 589 590})(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.