зеркало из
				https://github.com/jlind0/multiplex.studio.web.git
				synced 2025-10-30 21:46:10 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			587 строки
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			587 строки
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| (function($) {
 | |
| 
 | |
| 	/**
 | |
| 	 * Generate an indented list of links from a nav. Meant for use with panel().
 | |
| 	 * @return {jQuery} jQuery object.
 | |
| 	 */
 | |
| 	$.fn.navList = function() {
 | |
| 
 | |
| 		var	$this = $(this);
 | |
| 			$a = $this.find('a'),
 | |
| 			b = [];
 | |
| 
 | |
| 		$a.each(function() {
 | |
| 
 | |
| 			var	$this = $(this),
 | |
| 				indent = Math.max(0, $this.parents('li').length - 1),
 | |
| 				href = $this.attr('href'),
 | |
| 				target = $this.attr('target');
 | |
| 
 | |
| 			b.push(
 | |
| 				'<a ' +
 | |
| 					'class="link depth-' + indent + '"' +
 | |
| 					( (typeof target !== 'undefined' && target != '') ? ' target="' + target + '"' : '') +
 | |
| 					( (typeof href !== 'undefined' && href != '') ? ' href="' + href + '"' : '') +
 | |
| 				'>' +
 | |
| 					'<span class="indent-' + indent + '"></span>' +
 | |
| 					$this.text() +
 | |
| 				'</a>'
 | |
| 			);
 | |
| 
 | |
| 		});
 | |
| 
 | |
| 		return b.join('');
 | |
| 
 | |
| 	};
 | |
| 
 | |
| 	/**
 | |
| 	 * Panel-ify an element.
 | |
| 	 * @param {object} userConfig User config.
 | |
| 	 * @return {jQuery} jQuery object.
 | |
| 	 */
 | |
| 	$.fn.panel = function(userConfig) {
 | |
| 
 | |
| 		// No elements?
 | |
| 			if (this.length == 0)
 | |
| 				return $this;
 | |
| 
 | |
| 		// Multiple elements?
 | |
| 			if (this.length > 1) {
 | |
| 
 | |
| 				for (var i=0; i < this.length; i++)
 | |
| 					$(this[i]).panel(userConfig);
 | |
| 
 | |
| 				return $this;
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 		// Vars.
 | |
| 			var	$this = $(this),
 | |
| 				$body = $('body'),
 | |
| 				$window = $(window),
 | |
| 				id = $this.attr('id'),
 | |
| 				config;
 | |
| 
 | |
| 		// Config.
 | |
| 			config = $.extend({
 | |
| 
 | |
| 				// Delay.
 | |
| 					delay: 0,
 | |
| 
 | |
| 				// Hide panel on link click.
 | |
| 					hideOnClick: false,
 | |
| 
 | |
| 				// Hide panel on escape keypress.
 | |
| 					hideOnEscape: false,
 | |
| 
 | |
| 				// Hide panel on swipe.
 | |
| 					hideOnSwipe: false,
 | |
| 
 | |
| 				// Reset scroll position on hide.
 | |
| 					resetScroll: false,
 | |
| 
 | |
| 				// Reset forms on hide.
 | |
| 					resetForms: false,
 | |
| 
 | |
| 				// Side of viewport the panel will appear.
 | |
| 					side: null,
 | |
| 
 | |
| 				// Target element for "class".
 | |
| 					target: $this,
 | |
| 
 | |
| 				// Class to toggle.
 | |
| 					visibleClass: 'visible'
 | |
| 
 | |
| 			}, userConfig);
 | |
| 
 | |
| 			// Expand "target" if it's not a jQuery object already.
 | |
| 				if (typeof config.target != 'jQuery')
 | |
| 					config.target = $(config.target);
 | |
| 
 | |
| 		// Panel.
 | |
| 
 | |
| 			// Methods.
 | |
| 				$this._hide = function(event) {
 | |
| 
 | |
| 					// Already hidden? Bail.
 | |
| 						if (!config.target.hasClass(config.visibleClass))
 | |
| 							return;
 | |
| 
 | |
| 					// If an event was provided, cancel it.
 | |
| 						if (event) {
 | |
| 
 | |
| 							event.preventDefault();
 | |
| 							event.stopPropagation();
 | |
| 
 | |
| 						}
 | |
| 
 | |
| 					// Hide.
 | |
| 						config.target.removeClass(config.visibleClass);
 | |
| 
 | |
| 					// Post-hide stuff.
 | |
| 						window.setTimeout(function() {
 | |
| 
 | |
| 							// Reset scroll position.
 | |
| 								if (config.resetScroll)
 | |
| 									$this.scrollTop(0);
 | |
| 
 | |
| 							// Reset forms.
 | |
| 								if (config.resetForms)
 | |
| 									$this.find('form').each(function() {
 | |
| 										this.reset();
 | |
| 									});
 | |
| 
 | |
| 						}, config.delay);
 | |
| 
 | |
| 				};
 | |
| 
 | |
| 			// Vendor fixes.
 | |
| 				$this
 | |
| 					.css('-ms-overflow-style', '-ms-autohiding-scrollbar')
 | |
| 					.css('-webkit-overflow-scrolling', 'touch');
 | |
| 
 | |
| 			// Hide on click.
 | |
| 				if (config.hideOnClick) {
 | |
| 
 | |
| 					$this.find('a')
 | |
| 						.css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
 | |
| 
 | |
| 					$this
 | |
| 						.on('click', 'a', function(event) {
 | |
| 
 | |
| 							var $a = $(this),
 | |
| 								href = $a.attr('href'),
 | |
| 								target = $a.attr('target');
 | |
| 
 | |
| 							if (!href || href == '#' || href == '' || href == '#' + id)
 | |
| 								return;
 | |
| 
 | |
| 							// Cancel original event.
 | |
| 								event.preventDefault();
 | |
| 								event.stopPropagation();
 | |
| 
 | |
| 							// Hide panel.
 | |
| 								$this._hide();
 | |
| 
 | |
| 							// Redirect to href.
 | |
| 								window.setTimeout(function() {
 | |
| 
 | |
| 									if (target == '_blank')
 | |
| 										window.open(href);
 | |
| 									else
 | |
| 										window.location.href = href;
 | |
| 
 | |
| 								}, config.delay + 10);
 | |
| 
 | |
| 						});
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 			// Event: Touch stuff.
 | |
| 				$this.on('touchstart', function(event) {
 | |
| 
 | |
| 					$this.touchPosX = event.originalEvent.touches[0].pageX;
 | |
| 					$this.touchPosY = event.originalEvent.touches[0].pageY;
 | |
| 
 | |
| 				})
 | |
| 
 | |
| 				$this.on('touchmove', function(event) {
 | |
| 
 | |
| 					if ($this.touchPosX === null
 | |
| 					||	$this.touchPosY === null)
 | |
| 						return;
 | |
| 
 | |
| 					var	diffX = $this.touchPosX - event.originalEvent.touches[0].pageX,
 | |
| 						diffY = $this.touchPosY - event.originalEvent.touches[0].pageY,
 | |
| 						th = $this.outerHeight(),
 | |
| 						ts = ($this.get(0).scrollHeight - $this.scrollTop());
 | |
| 
 | |
| 					// Hide on swipe?
 | |
| 						if (config.hideOnSwipe) {
 | |
| 
 | |
| 							var result = false,
 | |
| 								boundary = 20,
 | |
| 								delta = 50;
 | |
| 
 | |
| 							switch (config.side) {
 | |
| 
 | |
| 								case 'left':
 | |
| 									result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta);
 | |
| 									break;
 | |
| 
 | |
| 								case 'right':
 | |
| 									result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
 | |
| 									break;
 | |
| 
 | |
| 								case 'top':
 | |
| 									result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta);
 | |
| 									break;
 | |
| 
 | |
| 								case 'bottom':
 | |
| 									result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta));
 | |
| 									break;
 | |
| 
 | |
| 								default:
 | |
| 									break;
 | |
| 
 | |
| 							}
 | |
| 
 | |
| 							if (result) {
 | |
| 
 | |
| 								$this.touchPosX = null;
 | |
| 								$this.touchPosY = null;
 | |
| 								$this._hide();
 | |
| 
 | |
| 								return false;
 | |
| 
 | |
| 							}
 | |
| 
 | |
| 						}
 | |
| 
 | |
| 					// Prevent vertical scrolling past the top or bottom.
 | |
| 						if (($this.scrollTop() < 0 && diffY < 0)
 | |
| 						|| (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
 | |
| 
 | |
| 							event.preventDefault();
 | |
| 							event.stopPropagation();
 | |
| 
 | |
| 						}
 | |
| 
 | |
| 				});
 | |
| 
 | |
| 			// Event: Prevent certain events inside the panel from bubbling.
 | |
| 				$this.on('click touchend touchstart touchmove', function(event) {
 | |
| 					event.stopPropagation();
 | |
| 				});
 | |
| 
 | |
| 			// Event: Hide panel if a child anchor tag pointing to its ID is clicked.
 | |
| 				$this.on('click', 'a[href="#' + id + '"]', function(event) {
 | |
| 
 | |
| 					event.preventDefault();
 | |
| 					event.stopPropagation();
 | |
| 
 | |
| 					config.target.removeClass(config.visibleClass);
 | |
| 
 | |
| 				});
 | |
| 
 | |
| 		// Body.
 | |
| 
 | |
| 			// Event: Hide panel on body click/tap.
 | |
| 				$body.on('click touchend', function(event) {
 | |
| 					$this._hide(event);
 | |
| 				});
 | |
| 
 | |
| 			// Event: Toggle.
 | |
| 				$body.on('click', 'a[href="#' + id + '"]', function(event) {
 | |
| 
 | |
| 					event.preventDefault();
 | |
| 					event.stopPropagation();
 | |
| 
 | |
| 					config.target.toggleClass(config.visibleClass);
 | |
| 
 | |
| 				});
 | |
| 
 | |
| 		// Window.
 | |
| 
 | |
| 			// Event: Hide on ESC.
 | |
| 				if (config.hideOnEscape)
 | |
| 					$window.on('keydown', function(event) {
 | |
| 
 | |
| 						if (event.keyCode == 27)
 | |
| 							$this._hide(event);
 | |
| 
 | |
| 					});
 | |
| 
 | |
| 		return $this;
 | |
| 
 | |
| 	};
 | |
| 
 | |
| 	/**
 | |
| 	 * Apply "placeholder" attribute polyfill to one or more forms.
 | |
| 	 * @return {jQuery} jQuery object.
 | |
| 	 */
 | |
| 	$.fn.placeholder = function() {
 | |
| 
 | |
| 		// Browser natively supports placeholders? Bail.
 | |
| 			if (typeof (document.createElement('input')).placeholder != 'undefined')
 | |
| 				return $(this);
 | |
| 
 | |
| 		// No elements?
 | |
| 			if (this.length == 0)
 | |
| 				return $this;
 | |
| 
 | |
| 		// Multiple elements?
 | |
| 			if (this.length > 1) {
 | |
| 
 | |
| 				for (var i=0; i < this.length; i++)
 | |
| 					$(this[i]).placeholder();
 | |
| 
 | |
| 				return $this;
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 		// Vars.
 | |
| 			var $this = $(this);
 | |
| 
 | |
| 		// Text, TextArea.
 | |
| 			$this.find('input[type=text],textarea')
 | |
| 				.each(function() {
 | |
| 
 | |
| 					var i = $(this);
 | |
| 
 | |
| 					if (i.val() == ''
 | |
| 					||  i.val() == i.attr('placeholder'))
 | |
| 						i
 | |
| 							.addClass('polyfill-placeholder')
 | |
| 							.val(i.attr('placeholder'));
 | |
| 
 | |
| 				})
 | |
| 				.on('blur', function() {
 | |
| 
 | |
| 					var i = $(this);
 | |
| 
 | |
| 					if (i.attr('name').match(/-polyfill-field$/))
 | |
| 						return;
 | |
| 
 | |
| 					if (i.val() == '')
 | |
| 						i
 | |
| 							.addClass('polyfill-placeholder')
 | |
| 							.val(i.attr('placeholder'));
 | |
| 
 | |
| 				})
 | |
| 				.on('focus', function() {
 | |
| 
 | |
| 					var i = $(this);
 | |
| 
 | |
| 					if (i.attr('name').match(/-polyfill-field$/))
 | |
| 						return;
 | |
| 
 | |
| 					if (i.val() == i.attr('placeholder'))
 | |
| 						i
 | |
| 							.removeClass('polyfill-placeholder')
 | |
| 							.val('');
 | |
| 
 | |
| 				});
 | |
| 
 | |
| 		// Password.
 | |
| 			$this.find('input[type=password]')
 | |
| 				.each(function() {
 | |
| 
 | |
| 					var i = $(this);
 | |
| 					var x = $(
 | |
| 								$('<div>')
 | |
| 									.append(i.clone())
 | |
| 									.remove()
 | |
| 									.html()
 | |
| 									.replace(/type="password"/i, 'type="text"')
 | |
| 									.replace(/type=password/i, 'type=text')
 | |
| 					);
 | |
| 
 | |
| 					if (i.attr('id') != '')
 | |
| 						x.attr('id', i.attr('id') + '-polyfill-field');
 | |
| 
 | |
| 					if (i.attr('name') != '')
 | |
| 						x.attr('name', i.attr('name') + '-polyfill-field');
 | |
| 
 | |
| 					x.addClass('polyfill-placeholder')
 | |
| 						.val(x.attr('placeholder')).insertAfter(i);
 | |
| 
 | |
| 					if (i.val() == '')
 | |
| 						i.hide();
 | |
| 					else
 | |
| 						x.hide();
 | |
| 
 | |
| 					i
 | |
| 						.on('blur', function(event) {
 | |
| 
 | |
| 							event.preventDefault();
 | |
| 
 | |
| 							var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
 | |
| 
 | |
| 							if (i.val() == '') {
 | |
| 
 | |
| 								i.hide();
 | |
| 								x.show();
 | |
| 
 | |
| 							}
 | |
| 
 | |
| 						});
 | |
| 
 | |
| 					x
 | |
| 						.on('focus', function(event) {
 | |
| 
 | |
| 							event.preventDefault();
 | |
| 
 | |
| 							var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']');
 | |
| 
 | |
| 							x.hide();
 | |
| 
 | |
| 							i
 | |
| 								.show()
 | |
| 								.focus();
 | |
| 
 | |
| 						})
 | |
| 						.on('keypress', function(event) {
 | |
| 
 | |
| 							event.preventDefault();
 | |
| 							x.val('');
 | |
| 
 | |
| 						});
 | |
| 
 | |
| 				});
 | |
| 
 | |
| 		// Events.
 | |
| 			$this
 | |
| 				.on('submit', function() {
 | |
| 
 | |
| 					$this.find('input[type=text],input[type=password],textarea')
 | |
| 						.each(function(event) {
 | |
| 
 | |
| 							var i = $(this);
 | |
| 
 | |
| 							if (i.attr('name').match(/-polyfill-field$/))
 | |
| 								i.attr('name', '');
 | |
| 
 | |
| 							if (i.val() == i.attr('placeholder')) {
 | |
| 
 | |
| 								i.removeClass('polyfill-placeholder');
 | |
| 								i.val('');
 | |
| 
 | |
| 							}
 | |
| 
 | |
| 						});
 | |
| 
 | |
| 				})
 | |
| 				.on('reset', function(event) {
 | |
| 
 | |
| 					event.preventDefault();
 | |
| 
 | |
| 					$this.find('select')
 | |
| 						.val($('option:first').val());
 | |
| 
 | |
| 					$this.find('input,textarea')
 | |
| 						.each(function() {
 | |
| 
 | |
| 							var i = $(this),
 | |
| 								x;
 | |
| 
 | |
| 							i.removeClass('polyfill-placeholder');
 | |
| 
 | |
| 							switch (this.type) {
 | |
| 
 | |
| 								case 'submit':
 | |
| 								case 'reset':
 | |
| 									break;
 | |
| 
 | |
| 								case 'password':
 | |
| 									i.val(i.attr('defaultValue'));
 | |
| 
 | |
| 									x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
 | |
| 
 | |
| 									if (i.val() == '') {
 | |
| 										i.hide();
 | |
| 										x.show();
 | |
| 									}
 | |
| 									else {
 | |
| 										i.show();
 | |
| 										x.hide();
 | |
| 									}
 | |
| 
 | |
| 									break;
 | |
| 
 | |
| 								case 'checkbox':
 | |
| 								case 'radio':
 | |
| 									i.attr('checked', i.attr('defaultValue'));
 | |
| 									break;
 | |
| 
 | |
| 								case 'text':
 | |
| 								case 'textarea':
 | |
| 									i.val(i.attr('defaultValue'));
 | |
| 
 | |
| 									if (i.val() == '') {
 | |
| 										i.addClass('polyfill-placeholder');
 | |
| 										i.val(i.attr('placeholder'));
 | |
| 									}
 | |
| 
 | |
| 									break;
 | |
| 
 | |
| 								default:
 | |
| 									i.val(i.attr('defaultValue'));
 | |
| 									break;
 | |
| 
 | |
| 							}
 | |
| 						});
 | |
| 
 | |
| 				});
 | |
| 
 | |
| 		return $this;
 | |
| 
 | |
| 	};
 | |
| 
 | |
| 	/**
 | |
| 	 * Moves elements to/from the first positions of their respective parents.
 | |
| 	 * @param {jQuery} $elements Elements (or selector) to move.
 | |
| 	 * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations.
 | |
| 	 */
 | |
| 	$.prioritize = function($elements, condition) {
 | |
| 
 | |
| 		var key = '__prioritize';
 | |
| 
 | |
| 		// Expand $elements if it's not already a jQuery object.
 | |
| 			if (typeof $elements != 'jQuery')
 | |
| 				$elements = $($elements);
 | |
| 
 | |
| 		// Step through elements.
 | |
| 			$elements.each(function() {
 | |
| 
 | |
| 				var	$e = $(this), $p,
 | |
| 					$parent = $e.parent();
 | |
| 
 | |
| 				// No parent? Bail.
 | |
| 					if ($parent.length == 0)
 | |
| 						return;
 | |
| 
 | |
| 				// Not moved? Move it.
 | |
| 					if (!$e.data(key)) {
 | |
| 
 | |
| 						// Condition is false? Bail.
 | |
| 							if (!condition)
 | |
| 								return;
 | |
| 
 | |
| 						// Get placeholder (which will serve as our point of reference for when this element needs to move back).
 | |
| 							$p = $e.prev();
 | |
| 
 | |
| 							// Couldn't find anything? Means this element's already at the top, so bail.
 | |
| 								if ($p.length == 0)
 | |
| 									return;
 | |
| 
 | |
| 						// Move element to top of parent.
 | |
| 							$e.prependTo($parent);
 | |
| 
 | |
| 						// Mark element as moved.
 | |
| 							$e.data(key, $p);
 | |
| 
 | |
| 					}
 | |
| 
 | |
| 				// Moved already?
 | |
| 					else {
 | |
| 
 | |
| 						// Condition is true? Bail.
 | |
| 							if (condition)
 | |
| 								return;
 | |
| 
 | |
| 						$p = $e.data(key);
 | |
| 
 | |
| 						// Move element back to its original location (using our placeholder).
 | |
| 							$e.insertAfter($p);
 | |
| 
 | |
| 						// Unmark element as moved.
 | |
| 							$e.removeData(key);
 | |
| 
 | |
| 					}
 | |
| 
 | |
| 			});
 | |
| 
 | |
| 	};
 | |
| 
 | |
| })(jQuery); | 
