website

The files for mattfehrenbach.xyz, a fork of a template.
git clone git://git.mattfehrenbach.xyz/website.git
Log | Files | Refs | LICENSE

util.js (12433B)


      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 							var $a = $(this),
    153 								href = $a.attr('href'),
    154 								target = $a.attr('target');
    155 
    156 							if (!href || href == '#' || href == '' || href == '#' + id)
    157 								return;
    158 
    159 							// Cancel original event.
    160 								event.preventDefault();
    161 								event.stopPropagation();
    162 
    163 							// Hide panel.
    164 								$this._hide();
    165 
    166 							// Redirect to href.
    167 								window.setTimeout(function() {
    168 
    169 									if (target == '_blank')
    170 										window.open(href);
    171 									else
    172 										window.location.href = href;
    173 
    174 								}, config.delay + 10);
    175 
    176 						});
    177 
    178 				}
    179 
    180 			// Event: Touch stuff.
    181 				$this.on('touchstart', function(event) {
    182 
    183 					$this.touchPosX = event.originalEvent.touches[0].pageX;
    184 					$this.touchPosY = event.originalEvent.touches[0].pageY;
    185 
    186 				})
    187 
    188 				$this.on('touchmove', function(event) {
    189 
    190 					if ($this.touchPosX === null
    191 					||	$this.touchPosY === null)
    192 						return;
    193 
    194 					var	diffX = $this.touchPosX - event.originalEvent.touches[0].pageX,
    195 						diffY = $this.touchPosY - event.originalEvent.touches[0].pageY,
    196 						th = $this.outerHeight(),
    197 						ts = ($this.get(0).scrollHeight - $this.scrollTop());
    198 
    199 					// Hide on swipe?
    200 						if (config.hideOnSwipe) {
    201 
    202 							var result = false,
    203 								boundary = 20,
    204 								delta = 50;
    205 
    206 							switch (config.side) {
    207 
    208 								case 'left':
    209 									result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta);
    210 									break;
    211 
    212 								case 'right':
    213 									result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
    214 									break;
    215 
    216 								case 'top':
    217 									result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta);
    218 									break;
    219 
    220 								case 'bottom':
    221 									result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta));
    222 									break;
    223 
    224 								default:
    225 									break;
    226 
    227 							}
    228 
    229 							if (result) {
    230 
    231 								$this.touchPosX = null;
    232 								$this.touchPosY = null;
    233 								$this._hide();
    234 
    235 								return false;
    236 
    237 							}
    238 
    239 						}
    240 
    241 					// Prevent vertical scrolling past the top or bottom.
    242 						if (($this.scrollTop() < 0 && diffY < 0)
    243 						|| (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
    244 
    245 							event.preventDefault();
    246 							event.stopPropagation();
    247 
    248 						}
    249 
    250 				});
    251 
    252 			// Event: Prevent certain events inside the panel from bubbling.
    253 				$this.on('click touchend touchstart touchmove', function(event) {
    254 					event.stopPropagation();
    255 				});
    256 
    257 			// Event: Hide panel if a child anchor tag pointing to its ID is clicked.
    258 				$this.on('click', 'a[href="#' + id + '"]', function(event) {
    259 
    260 					event.preventDefault();
    261 					event.stopPropagation();
    262 
    263 					config.target.removeClass(config.visibleClass);
    264 
    265 				});
    266 
    267 		// Body.
    268 
    269 			// Event: Hide panel on body click/tap.
    270 				$body.on('click touchend', function(event) {
    271 					$this._hide(event);
    272 				});
    273 
    274 			// Event: Toggle.
    275 				$body.on('click', 'a[href="#' + id + '"]', function(event) {
    276 
    277 					event.preventDefault();
    278 					event.stopPropagation();
    279 
    280 					config.target.toggleClass(config.visibleClass);
    281 
    282 				});
    283 
    284 		// Window.
    285 
    286 			// Event: Hide on ESC.
    287 				if (config.hideOnEscape)
    288 					$window.on('keydown', function(event) {
    289 
    290 						if (event.keyCode == 27)
    291 							$this._hide(event);
    292 
    293 					});
    294 
    295 		return $this;
    296 
    297 	};
    298 
    299 	/**
    300 	 * Apply "placeholder" attribute polyfill to one or more forms.
    301 	 * @return {jQuery} jQuery object.
    302 	 */
    303 	$.fn.placeholder = function() {
    304 
    305 		// Browser natively supports placeholders? Bail.
    306 			if (typeof (document.createElement('input')).placeholder != 'undefined')
    307 				return $(this);
    308 
    309 		// No elements?
    310 			if (this.length == 0)
    311 				return $this;
    312 
    313 		// Multiple elements?
    314 			if (this.length > 1) {
    315 
    316 				for (var i=0; i < this.length; i++)
    317 					$(this[i]).placeholder();
    318 
    319 				return $this;
    320 
    321 			}
    322 
    323 		// Vars.
    324 			var $this = $(this);
    325 
    326 		// Text, TextArea.
    327 			$this.find('input[type=text],textarea')
    328 				.each(function() {
    329 
    330 					var i = $(this);
    331 
    332 					if (i.val() == ''
    333 					||  i.val() == i.attr('placeholder'))
    334 						i
    335 							.addClass('polyfill-placeholder')
    336 							.val(i.attr('placeholder'));
    337 
    338 				})
    339 				.on('blur', function() {
    340 
    341 					var i = $(this);
    342 
    343 					if (i.attr('name').match(/-polyfill-field$/))
    344 						return;
    345 
    346 					if (i.val() == '')
    347 						i
    348 							.addClass('polyfill-placeholder')
    349 							.val(i.attr('placeholder'));
    350 
    351 				})
    352 				.on('focus', function() {
    353 
    354 					var i = $(this);
    355 
    356 					if (i.attr('name').match(/-polyfill-field$/))
    357 						return;
    358 
    359 					if (i.val() == i.attr('placeholder'))
    360 						i
    361 							.removeClass('polyfill-placeholder')
    362 							.val('');
    363 
    364 				});
    365 
    366 		// Password.
    367 			$this.find('input[type=password]')
    368 				.each(function() {
    369 
    370 					var i = $(this);
    371 					var x = $(
    372 								$('<div>')
    373 									.append(i.clone())
    374 									.remove()
    375 									.html()
    376 									.replace(/type="password"/i, 'type="text"')
    377 									.replace(/type=password/i, 'type=text')
    378 					);
    379 
    380 					if (i.attr('id') != '')
    381 						x.attr('id', i.attr('id') + '-polyfill-field');
    382 
    383 					if (i.attr('name') != '')
    384 						x.attr('name', i.attr('name') + '-polyfill-field');
    385 
    386 					x.addClass('polyfill-placeholder')
    387 						.val(x.attr('placeholder')).insertAfter(i);
    388 
    389 					if (i.val() == '')
    390 						i.hide();
    391 					else
    392 						x.hide();
    393 
    394 					i
    395 						.on('blur', function(event) {
    396 
    397 							event.preventDefault();
    398 
    399 							var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
    400 
    401 							if (i.val() == '') {
    402 
    403 								i.hide();
    404 								x.show();
    405 
    406 							}
    407 
    408 						});
    409 
    410 					x
    411 						.on('focus', function(event) {
    412 
    413 							event.preventDefault();
    414 
    415 							var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']');
    416 
    417 							x.hide();
    418 
    419 							i
    420 								.show()
    421 								.focus();
    422 
    423 						})
    424 						.on('keypress', function(event) {
    425 
    426 							event.preventDefault();
    427 							x.val('');
    428 
    429 						});
    430 
    431 				});
    432 
    433 		// Events.
    434 			$this
    435 				.on('submit', function() {
    436 
    437 					$this.find('input[type=text],input[type=password],textarea')
    438 						.each(function(event) {
    439 
    440 							var i = $(this);
    441 
    442 							if (i.attr('name').match(/-polyfill-field$/))
    443 								i.attr('name', '');
    444 
    445 							if (i.val() == i.attr('placeholder')) {
    446 
    447 								i.removeClass('polyfill-placeholder');
    448 								i.val('');
    449 
    450 							}
    451 
    452 						});
    453 
    454 				})
    455 				.on('reset', function(event) {
    456 
    457 					event.preventDefault();
    458 
    459 					$this.find('select')
    460 						.val($('option:first').val());
    461 
    462 					$this.find('input,textarea')
    463 						.each(function() {
    464 
    465 							var i = $(this),
    466 								x;
    467 
    468 							i.removeClass('polyfill-placeholder');
    469 
    470 							switch (this.type) {
    471 
    472 								case 'submit':
    473 								case 'reset':
    474 									break;
    475 
    476 								case 'password':
    477 									i.val(i.attr('defaultValue'));
    478 
    479 									x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
    480 
    481 									if (i.val() == '') {
    482 										i.hide();
    483 										x.show();
    484 									}
    485 									else {
    486 										i.show();
    487 										x.hide();
    488 									}
    489 
    490 									break;
    491 
    492 								case 'checkbox':
    493 								case 'radio':
    494 									i.attr('checked', i.attr('defaultValue'));
    495 									break;
    496 
    497 								case 'text':
    498 								case 'textarea':
    499 									i.val(i.attr('defaultValue'));
    500 
    501 									if (i.val() == '') {
    502 										i.addClass('polyfill-placeholder');
    503 										i.val(i.attr('placeholder'));
    504 									}
    505 
    506 									break;
    507 
    508 								default:
    509 									i.val(i.attr('defaultValue'));
    510 									break;
    511 
    512 							}
    513 						});
    514 
    515 				});
    516 
    517 		return $this;
    518 
    519 	};
    520 
    521 	/**
    522 	 * Moves elements to/from the first positions of their respective parents.
    523 	 * @param {jQuery} $elements Elements (or selector) to move.
    524 	 * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations.
    525 	 */
    526 	$.prioritize = function($elements, condition) {
    527 
    528 		var key = '__prioritize';
    529 
    530 		// Expand $elements if it's not already a jQuery object.
    531 			if (typeof $elements != 'jQuery')
    532 				$elements = $($elements);
    533 
    534 		// Step through elements.
    535 			$elements.each(function() {
    536 
    537 				var	$e = $(this), $p,
    538 					$parent = $e.parent();
    539 
    540 				// No parent? Bail.
    541 					if ($parent.length == 0)
    542 						return;
    543 
    544 				// Not moved? Move it.
    545 					if (!$e.data(key)) {
    546 
    547 						// Condition is false? Bail.
    548 							if (!condition)
    549 								return;
    550 
    551 						// Get placeholder (which will serve as our point of reference for when this element needs to move back).
    552 							$p = $e.prev();
    553 
    554 							// Couldn't find anything? Means this element's already at the top, so bail.
    555 								if ($p.length == 0)
    556 									return;
    557 
    558 						// Move element to top of parent.
    559 							$e.prependTo($parent);
    560 
    561 						// Mark element as moved.
    562 							$e.data(key, $p);
    563 
    564 					}
    565 
    566 				// Moved already?
    567 					else {
    568 
    569 						// Condition is true? Bail.
    570 							if (condition)
    571 								return;
    572 
    573 						$p = $e.data(key);
    574 
    575 						// Move element back to its original location (using our placeholder).
    576 							$e.insertAfter($p);
    577 
    578 						// Unmark element as moved.
    579 							$e.removeData(key);
    580 
    581 					}
    582 
    583 			});
    584 
    585 	};
    586 
    587 })(jQuery);