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