<commit_before>/* Player
 *
 */

var Player = (function(window, document, hyperaudio, Popcorn) {

	function Player(options) {

		this.options = hyperaudio.extend({}, this.options, {

			entity: 'PLAYER', // Not really an option... More like a manifest

			target: '#transcript-video', // The selector of element where the video is generated

			// Caching can cause problem in Chrome due to the bug:
			//   Issue 31014: Byte range cache is locked when attempting to open the same video twice
			//   https://code.google.com/p/chromium/issues/detail?id=31014
			cache: false,

			media: {
				youtube: '', // The URL of the Youtube video.
				mp4: '', // The URL of the mp4 video.
				webm:'' // The URL of the webm video.
			},

			// Types valid in a video element
			mediaType: {
				mp4: 'video/mp4', // The mp4 mime type.
				webm:'video/webm', // The webm mime type.
				mpeg:'audio/mpeg' // The mp3 mime type.
			},

			guiNative: false, // TMP during dev. Either we have a gui or we are chomeless.

			gui: false, // True to add a gui, or Object to pass GUI options.
			cssClass: 'hyperaudio-player', // Class added to the target for the GUI CSS. (passed to GUI and Projector)
			solutionClass: 'solution', // Class added to the solution that is active.
			async: true // When true, some operations are delayed by a timeout.
		}, options);

		// Properties
		this.target = typeof this.options.target === 'string' ? document.querySelector(this.options.target) : this.options.target;
		this.videoElem = null;
		this.timeout = {};
		this.commandsIgnored = /ipad|iphone|ipod|android/i.test(window.navigator.userAgent);

		// List of the media types, used to check for changes in media.
		this.mediaTypes = "youtube mp4 webm";

		this.youtube = false; // A flag to indicate if the YT player being used.

		// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
		this.ytFix = [];

		if(this.options.DEBUG) {
			this._debug();
		}

		if(this.target) {
			this.create();
		}
	}

	Player.prototype = {
		create: function() {
			var self = this;
			var tryLoad = false;

			if(this.target) {

				this.wrapper = {
					html: document.createElement('div'),
					youtube: document.createElement('div')
				};
				hyperaudio.addClass(this.wrapper.html, this.options.cssClass + '-video-wrapper');
				hyperaudio.addClass(this.wrapper.youtube, this.options.cssClass + '-youtube-wrapper');

				this.solution = {
					html: document.createElement('video'),
					youtube: Popcorn.HTMLYouTubeVideoElement(this.wrapper.youtube)
				};

				// Default to a video element to start with
				this.videoElem = this.solution.html;
				this.youtube = false;
				this.updateSolution();

				this.solution.html.controls = this.options.guiNative; // TMP during dev. Either we have a gui or we are chomeless.

				// Add listeners to the video element
				this.solution.html.addEventListener('progress', function(e) {
					if(this.readyState > 0) {
						self.commandsIgnored = false;
					}
				}, false);

				// Clear the target element and add the video
				this.empty(this.target);
				this.wrapper.html.appendChild(this.solution.html);
				// this.wrapper.youtube.appendChild(this.solution.youtube);
				this.target.appendChild(this.wrapper.html);
				this.target.appendChild(this.wrapper.youtube);

				if(this.options.gui) {

					var guiOptions = {
						player: this,

						navigation: false,		// next/prev buttons
						fullscreen: false,		// fullscreen button

						cssClass: this.options.cssClass // Pass in the option, so only have to define it in this class
					};

					if(typeof this.options.gui === 'object') {
						hyperaudio.extend(guiOptions, this.options.gui);
					}

					this.GUI = new hyperaudio.PlayerGUI(guiOptions);

					var handler = function(event) {
						var video = self.videoElem;
						self.GUI.setStatus({
							paused: video.paused,
							currentTime: video.currentTime,
							duration: video.duration
						});
					};

					this.addEventListener('progress', handler); // Important for YT player GUI to update on set/change
					this.addEventListener('timeupdate', handler);
					this.addEventListener('play', handler);
					this.addEventListener('pause', handler);
					this.addEventListener('ended', handler);
				}

				// See if the media object contains anything.
				hyperaudio.each(this.options.media, function(format, url) {
					if(url) {
						tryLoad = true;
						return false; // exit each
					}
				});

				// if(this.options.media.youtube || this.options.media.mp4) { // Assumes we have the webm
				if(tryLoad) { // We have something - try to load it.
					this.load();
				}
			} else {
				this._error('Target not found : ' + this.options.target);
			}
		},

		mediaDiff: function(media) {
			var self = this,
				diff = false;
			if(media) {
				hyperaudio.each(this.mediaTypes.split(/\s+/g), function() {
					if(self.options.media[this] !== media[this]) {
						diff = true;
						return false; // exit each
					}
				});
			} else {
				diff = true;
			}
			return diff;
		},

		updateSolution: function() {
			var wrapper = this.wrapper,
				cssClass = this.options.solutionClass;

			if(this.youtube) {
				hyperaudio.removeClass(wrapper.html, cssClass);
				hyperaudio.addClass(wrapper.youtube, cssClass);
			} else {
				hyperaudio.removeClass(wrapper.youtube, cssClass);
				hyperaudio.addClass(wrapper.html, cssClass);
			}
		},

		show: function() {
			this.updateSolution();
		},
		hide: function() {
			var wrapper = this.wrapper,
				cssClass = this.options.solutionClass;

			hyperaudio.removeClass(wrapper.html, cssClass);
			hyperaudio.removeClass(wrapper.youtube, cssClass);
		},

		load: function(media) {
			var self = this,
				newMedia = this.mediaDiff(media);

			if(media) {
				this.options.media = media;
			}

			if(this.target) {

				if(newMedia) {

					this.pause(); // Pause the player, otherwise switching solution may leave 1 playing while hidden.

					this.killPopcorn();

					// console.log('media: %o', this.options.media);

					if(this.options.media.youtube) {

						// The YT element needs to be recreated while bugs in wrapper.
						// this.empty(this.wrapper.youtube);
						// this.solution.youtube = Popcorn.HTMLYouTubeVideoElement(this.wrapper.youtube);

						this.solution.youtube.src = this.options.media.youtube + '&html5=1';
						this.videoElem = this.solution.youtube;
						this.youtube = true;
						this.updateSolution();

						// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
						// this._ytFixListeners();
					} else {

						this.empty(this.solution.html);

						// Setup to work with mp4 and webm property names. See options.
						hyperaudio.each(this.options.media, function(format, url) {
							// Only create known formats, so we can add other info to the media object.
							if(self.options.mediaType[format] && url) {

								if(!self.options.cache) {
									url = url + ((/\?/).test(url) ? "&" : "?") + Math.floor(1e12 * Math.random());
								}

								var source = document.createElement('source');
								source.setAttribute('type', self.options.mediaType[format]);
								source.setAttribute('src', url); // Could use 'this' but less easy to read.
								self.solution.html.appendChild(source);
							}
						});

						this.solution.html.load();
						this.videoElem = this.solution.html;
						this.youtube = false;
						this.updateSolution();
					}

					this.initPopcorn();
				}
			} else {
				this._error('Video player not created : ' + this.options.target);
			}
		},
		initPopcorn: function() {
			this.killPopcorn();
			this.popcorn = Popcorn(this.videoElem);
		},
		killPopcorn: function() {
			if(this.popcorn) {
				this.popcorn.destroy();
				delete this.popcorn;
			}
		},
		empty: function(el) {
			// Empties the element... Possibly better than el.innerHTML = '';
			while(el && el.firstChild) {
				el.removeChild(el.firstChild);
			}
		},
		gui_play: function(time) {
			this._trigger(hyperaudio.event.userplay, {msg: 'User clicked play'});
			this.play(time);
		},
		gui_pause: function(time) {
			this._trigger(hyperaudio.event.userpause, {msg: 'User clicked pause'});
			this.pause(time);
		},
		gui_currentTime: function(time, play) {
			this._trigger(hyperaudio.event.usercurrenttime, {msg: 'User clicked the progress bar'});
			this.currentTime(time, play);
		},
		play: function(time) {
			if(this.youtube) {
				this.popcorn.play(time);
			} else {
				this.currentTime(time, true);
			}
		},
		pause: function(time) {
			if(this.youtube) {
				this.popcorn.pause(time);
			} else {
				this.videoElem.pause();
				this.currentTime(time);
			}
		},
		currentTime: function(time, play) {
			var self = this,
				media = this.videoElem;

			clearTimeout(this.timeout.currentTime);

			if(this.youtube) {
				this.popcorn.currentTime(time);
				return;
			}

			if(typeof time === 'number' && !isNaN(time)) {

				// Attempt to play it, since iOS has been ignoring commands
				if(play && this.commandsIgnored) {
					media.play();
				}

				try {
					// !media.seekable is for old HTML5 browsers, like Firefox 3.6.
					// Checking seekable.length is important for iOS6 to work with currentTime changes immediately after changing media
					if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) {
						media.currentTime = time;
						if(play) {
							media.play();
						}
					} else {
						throw 1;
					}
				} catch(err) {
					this.timeout.currentTime = setTimeout(function() {
						self.currentTime(time, play);
					}, 250);
				}
			} else {
				if(play) {
					media.play();
				}
			}
		},
		addEventListener: function(type, handler) {
			var self = this,
				handlers;

			if(this.solution && typeof type === 'string' && typeof handler === 'function') {
				handlers = {
					html: function(event) {
						if(!self.youtube) {
							handler.call(this, event);
						}
					},
					youtube: function(event) {
						if(self.youtube) {
							// Bugged YT wrapper context.
							// Reported https://bugzilla.mozilla.org/show_bug.cgi?id=946293
							// handler.call(this, event); // Bugged
							// this and event.target point at the document
							// event.detail.target points at the youtube target element
							handler.call(self.solution.youtube, event);
						}
					}
				};
				this.solution.html.addEventListener(type, handlers.html, false);
				this.solution.youtube.addEventListener(type, handlers.youtube, false);

				// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
				this.ytFix.push({
					type: type,
					handler: handlers.youtube
				});
			}

			return handlers;
		},
		removeEventListener: function(type, handlers) {
			if(this.solution && typeof type === 'string' && typeof handlers === 'object') {
				this.solution.html.removeEventListener(type, handlers.html, false);
				this.solution.youtube.removeEventListener(type, handlers.youtube, false);

				// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
				for(var i=0, l=this.ytFix.length; i<l; i++) {
					if(this.ytFix[i].type === type && this.ytFix[i].handler === handlers.youtube) {
						this.ytFix.splice(i, 1);
					}
				}
			}
		},
		// OBSOLETE Function due to Popcorn and YT Wrapper being fixed. ie., No more destroy and create... It persists!
		_ytFixListeners: function() {
			// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
			for(var i=0, l=this.ytFix.length; i<l; i++) {
				this.solution.youtube.addEventListener(this.ytFix[i].type, this.ytFix[i].handler, false);
			}
		}
	};

	return Player;
}(window, document, hyperaudio, Popcorn));
<commit_msg>Fixed Player diff test for changing mp3 media.<commit_after>/* Player
 *
 */

var Player = (function(window, document, hyperaudio, Popcorn) {

	function Player(options) {

		this.options = hyperaudio.extend({}, this.options, {

			entity: 'PLAYER', // Not really an option... More like a manifest

			target: '#transcript-video', // The selector of element where the video is generated

			// Caching can cause problem in Chrome due to the bug:
			//   Issue 31014: Byte range cache is locked when attempting to open the same video twice
			//   https://code.google.com/p/chromium/issues/detail?id=31014
			cache: false,

			media: {
				youtube: '', // The URL of the Youtube video.
				mp4: '', // The URL of the mp4 video.
				webm: '', // The URL of the webm video.
				mpeg: '' // The URL of the mp3 audio.
			},

			// Types valid in a video element
			mediaType: {
				mp4: 'video/mp4', // The mp4 mime type.
				webm:'video/webm', // The webm mime type.
				mpeg:'audio/mpeg' // The mp3 mime type.
			},

			guiNative: false, // TMP during dev. Either we have a gui or we are chomeless.

			gui: false, // True to add a gui, or Object to pass GUI options.
			cssClass: 'hyperaudio-player', // Class added to the target for the GUI CSS. (passed to GUI and Projector)
			solutionClass: 'solution', // Class added to the solution that is active.
			async: true // When true, some operations are delayed by a timeout.
		}, options);

		// Properties
		this.target = typeof this.options.target === 'string' ? document.querySelector(this.options.target) : this.options.target;
		this.videoElem = null;
		this.timeout = {};
		this.commandsIgnored = /ipad|iphone|ipod|android/i.test(window.navigator.userAgent);

		// List of the media types, used to check for changes in media.
		this.mediaTypes = "youtube mp4 webm mpeg";

		this.youtube = false; // A flag to indicate if the YT player being used.

		// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
		this.ytFix = [];

		if(this.options.DEBUG) {
			this._debug();
		}

		if(this.target) {
			this.create();
		}
	}

	Player.prototype = {
		create: function() {
			var self = this;
			var tryLoad = false;

			if(this.target) {

				this.wrapper = {
					html: document.createElement('div'),
					youtube: document.createElement('div')
				};
				hyperaudio.addClass(this.wrapper.html, this.options.cssClass + '-video-wrapper');
				hyperaudio.addClass(this.wrapper.youtube, this.options.cssClass + '-youtube-wrapper');

				this.solution = {
					html: document.createElement('video'),
					youtube: Popcorn.HTMLYouTubeVideoElement(this.wrapper.youtube)
				};

				// Default to a video element to start with
				this.videoElem = this.solution.html;
				this.youtube = false;
				this.updateSolution();

				this.solution.html.controls = this.options.guiNative; // TMP during dev. Either we have a gui or we are chomeless.

				// Add listeners to the video element
				this.solution.html.addEventListener('progress', function(e) {
					if(this.readyState > 0) {
						self.commandsIgnored = false;
					}
				}, false);

				// Clear the target element and add the video
				this.empty(this.target);
				this.wrapper.html.appendChild(this.solution.html);
				// this.wrapper.youtube.appendChild(this.solution.youtube);
				this.target.appendChild(this.wrapper.html);
				this.target.appendChild(this.wrapper.youtube);

				if(this.options.gui) {

					var guiOptions = {
						player: this,

						navigation: false,		// next/prev buttons
						fullscreen: false,		// fullscreen button

						cssClass: this.options.cssClass // Pass in the option, so only have to define it in this class
					};

					if(typeof this.options.gui === 'object') {
						hyperaudio.extend(guiOptions, this.options.gui);
					}

					this.GUI = new hyperaudio.PlayerGUI(guiOptions);

					var handler = function(event) {
						var video = self.videoElem;
						self.GUI.setStatus({
							paused: video.paused,
							currentTime: video.currentTime,
							duration: video.duration
						});
					};

					this.addEventListener('progress', handler); // Important for YT player GUI to update on set/change
					this.addEventListener('timeupdate', handler);
					this.addEventListener('play', handler);
					this.addEventListener('pause', handler);
					this.addEventListener('ended', handler);
				}

				// See if the media object contains anything.
				hyperaudio.each(this.options.media, function(format, url) {
					if(url) {
						tryLoad = true;
						return false; // exit each
					}
				});

				// if(this.options.media.youtube || this.options.media.mp4) { // Assumes we have the webm
				if(tryLoad) { // We have something - try to load it.
					this.load();
				}
			} else {
				this._error('Target not found : ' + this.options.target);
			}
		},

		mediaDiff: function(media) {
			var self = this,
				diff = false;
			if(media) {
				hyperaudio.each(this.mediaTypes.split(/\s+/g), function() {
					if(self.options.media[this] !== media[this]) {
						diff = true;
						return false; // exit each
					}
				});
			} else {
				diff = true;
			}
			return diff;
		},

		updateSolution: function() {
			var wrapper = this.wrapper,
				cssClass = this.options.solutionClass;

			if(this.youtube) {
				hyperaudio.removeClass(wrapper.html, cssClass);
				hyperaudio.addClass(wrapper.youtube, cssClass);
			} else {
				hyperaudio.removeClass(wrapper.youtube, cssClass);
				hyperaudio.addClass(wrapper.html, cssClass);
			}
		},

		show: function() {
			this.updateSolution();
		},
		hide: function() {
			var wrapper = this.wrapper,
				cssClass = this.options.solutionClass;

			hyperaudio.removeClass(wrapper.html, cssClass);
			hyperaudio.removeClass(wrapper.youtube, cssClass);
		},

		load: function(media) {
			var self = this,
				newMedia = this.mediaDiff(media);

			if(media) {
				this.options.media = media;
			}

			if(this.target) {

				if(newMedia) {

					this.pause(); // Pause the player, otherwise switching solution may leave 1 playing while hidden.

					this.killPopcorn();

					// console.log('media: %o', this.options.media);

					if(this.options.media.youtube) {

						// The YT element needs to be recreated while bugs in wrapper.
						// this.empty(this.wrapper.youtube);
						// this.solution.youtube = Popcorn.HTMLYouTubeVideoElement(this.wrapper.youtube);

						this.solution.youtube.src = this.options.media.youtube + '&html5=1';
						this.videoElem = this.solution.youtube;
						this.youtube = true;
						this.updateSolution();

						// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
						// this._ytFixListeners();
					} else {

						this.empty(this.solution.html);

						// Setup to work with mp4 and webm property names. See options.
						hyperaudio.each(this.options.media, function(format, url) {
							// Only create known formats, so we can add other info to the media object.
							if(self.options.mediaType[format] && url) {

								if(!self.options.cache) {
									url = url + ((/\?/).test(url) ? "&" : "?") + Math.floor(1e12 * Math.random());
								}

								var source = document.createElement('source');
								source.setAttribute('type', self.options.mediaType[format]);
								source.setAttribute('src', url); // Could use 'this' but less easy to read.
								self.solution.html.appendChild(source);
							}
						});

						this.solution.html.load();
						this.videoElem = this.solution.html;
						this.youtube = false;
						this.updateSolution();
					}

					this.initPopcorn();
				}
			} else {
				this._error('Video player not created : ' + this.options.target);
			}
		},
		initPopcorn: function() {
			this.killPopcorn();
			this.popcorn = Popcorn(this.videoElem);
		},
		killPopcorn: function() {
			if(this.popcorn) {
				this.popcorn.destroy();
				delete this.popcorn;
			}
		},
		empty: function(el) {
			// Empties the element... Possibly better than el.innerHTML = '';
			while(el && el.firstChild) {
				el.removeChild(el.firstChild);
			}
		},
		gui_play: function(time) {
			this._trigger(hyperaudio.event.userplay, {msg: 'User clicked play'});
			this.play(time);
		},
		gui_pause: function(time) {
			this._trigger(hyperaudio.event.userpause, {msg: 'User clicked pause'});
			this.pause(time);
		},
		gui_currentTime: function(time, play) {
			this._trigger(hyperaudio.event.usercurrenttime, {msg: 'User clicked the progress bar'});
			this.currentTime(time, play);
		},
		play: function(time) {
			if(this.youtube) {
				this.popcorn.play(time);
			} else {
				this.currentTime(time, true);
			}
		},
		pause: function(time) {
			if(this.youtube) {
				this.popcorn.pause(time);
			} else {
				this.videoElem.pause();
				this.currentTime(time);
			}
		},
		currentTime: function(time, play) {
			var self = this,
				media = this.videoElem;

			clearTimeout(this.timeout.currentTime);

			if(this.youtube) {
				this.popcorn.currentTime(time);
				return;
			}

			if(typeof time === 'number' && !isNaN(time)) {

				// Attempt to play it, since iOS has been ignoring commands
				if(play && this.commandsIgnored) {
					media.play();
				}

				try {
					// !media.seekable is for old HTML5 browsers, like Firefox 3.6.
					// Checking seekable.length is important for iOS6 to work with currentTime changes immediately after changing media
					if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) {
						media.currentTime = time;
						if(play) {
							media.play();
						}
					} else {
						throw 1;
					}
				} catch(err) {
					this.timeout.currentTime = setTimeout(function() {
						self.currentTime(time, play);
					}, 250);
				}
			} else {
				if(play) {
					media.play();
				}
			}
		},
		addEventListener: function(type, handler) {
			var self = this,
				handlers;

			if(this.solution && typeof type === 'string' && typeof handler === 'function') {
				handlers = {
					html: function(event) {
						if(!self.youtube) {
							handler.call(this, event);
						}
					},
					youtube: function(event) {
						if(self.youtube) {
							// Bugged YT wrapper context.
							// Reported https://bugzilla.mozilla.org/show_bug.cgi?id=946293
							// handler.call(this, event); // Bugged
							// this and event.target point at the document
							// event.detail.target points at the youtube target element
							handler.call(self.solution.youtube, event);
						}
					}
				};
				this.solution.html.addEventListener(type, handlers.html, false);
				this.solution.youtube.addEventListener(type, handlers.youtube, false);

				// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
				this.ytFix.push({
					type: type,
					handler: handlers.youtube
				});
			}

			return handlers;
		},
		removeEventListener: function(type, handlers) {
			if(this.solution && typeof type === 'string' && typeof handlers === 'object') {
				this.solution.html.removeEventListener(type, handlers.html, false);
				this.solution.youtube.removeEventListener(type, handlers.youtube, false);

				// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
				for(var i=0, l=this.ytFix.length; i<l; i++) {
					if(this.ytFix[i].type === type && this.ytFix[i].handler === handlers.youtube) {
						this.ytFix.splice(i, 1);
					}
				}
			}
		},
		// OBSOLETE Function due to Popcorn and YT Wrapper being fixed. ie., No more destroy and create... It persists!
		_ytFixListeners: function() {
			// Until the YouTube wrapper is fixed, we need to recreate it and the listeners when the YT media changes.
			for(var i=0, l=this.ytFix.length; i<l; i++) {
				this.solution.youtube.addEventListener(this.ytFix[i].type, this.ytFix[i].handler, false);
			}
		}
	};

	return Player;
}(window, document, hyperaudio, Popcorn));
<|endoftext|>


<commit_before>import {closest, css, getEventPos, includes, isRtl, isTouch, off, on, pointerCancel, pointerDown, pointerMove, pointerUp, selInput, trigger} from 'uikit-util';

export default {

    props: {
        draggable: Boolean
    },

    data: {
        draggable: true,
        threshold: 10
    },

    created() {

        ['start', 'move', 'end'].forEach(key => {

            const fn = this[key];
            this[key] = e => {

                const pos = getEventPos(e).x * (isRtl ? -1 : 1);

                this.prevPos = pos !== this.pos ? this.pos : this.prevPos;
                this.pos = pos;

                fn(e);
            };

        });

    },

    events: [

        {

            name: pointerDown,

            delegate() {
                return this.selSlides;
            },

            handler(e) {

                if (!this.draggable
                    || !isTouch(e) && hasTextNodesOnly(e.target)
                    || closest(e.target, selInput)
                    || e.button > 0
                    || this.length < 2
                ) {
                    return;
                }

                this.start(e);
            }

        },

        {
            name: 'dragstart',

            handler(e) {
                e.preventDefault();
            }
        }

    ],

    methods: {

        start() {

            this.drag = this.pos;

            if (this._transitioner) {

                this.percent = this._transitioner.percent();
                this.drag += this._transitioner.getDistance() * this.percent * this.dir;

                this._transitioner.cancel();
                this._transitioner.translate(this.percent);

                this.dragging = true;

                this.stack = [];

            } else {
                this.prevIndex = this.index;
            }

            // Workaround for iOS's inert scrolling preventing pointerdown event
            // https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action
            on(this.list, 'touchmove', this.move, {passive: false});

            on(document, pointerMove, this.move, {passive: false});
            on(document, `${pointerUp} ${pointerCancel}`, this.end, true);

            css(this.list, 'userSelect', 'none');

        },

        move(e) {

            const distance = this.pos - this.drag;

            if (distance === 0 || this.prevPos === this.pos || !this.dragging && Math.abs(distance) < this.threshold) {
                return;
            }

            // prevent click event
            css(this.list, 'pointerEvents', 'none');

            e.cancelable && e.preventDefault();

            this.dragging = true;
            this.dir = (distance < 0 ? 1 : -1);

            const {slides} = this;
            let {prevIndex} = this;
            let dis = Math.abs(distance);
            let nextIndex = this.getIndex(prevIndex + this.dir, prevIndex);
            let width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth;

            while (nextIndex !== prevIndex && dis > width) {

                this.drag -= width * this.dir;

                prevIndex = nextIndex;
                dis -= width;
                nextIndex = this.getIndex(prevIndex + this.dir, prevIndex);
                width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth;

            }

            this.percent = dis / width;

            const prev = slides[prevIndex];
            const next = slides[nextIndex];
            const changed = this.index !== nextIndex;
            const edge = prevIndex === nextIndex;

            let itemShown;

            [this.index, this.prevIndex].filter(i => !includes([nextIndex, prevIndex], i)).forEach(i => {
                trigger(slides[i], 'itemhidden', [this]);

                if (edge) {
                    itemShown = true;
                    this.prevIndex = prevIndex;
                }

            });

            if (this.index === prevIndex && this.prevIndex !== prevIndex || itemShown) {
                trigger(slides[this.index], 'itemshown', [this]);
            }

            if (changed) {
                this.prevIndex = prevIndex;
                this.index = nextIndex;

                !edge && trigger(prev, 'beforeitemhide', [this]);
                trigger(next, 'beforeitemshow', [this]);
            }

            this._transitioner = this._translate(Math.abs(this.percent), prev, !edge && next);

            if (changed) {
                !edge && trigger(prev, 'itemhide', [this]);
                trigger(next, 'itemshow', [this]);
            }

        },

        end() {

            off(this.list, 'touchmove', this.move, {passive: false});
            off(document, pointerMove, this.move, {passive: false});
            off(document, `${pointerUp} ${pointerCancel}`, this.end, true);

            if (this.dragging) {

                this.dragging = null;

                if (this.index === this.prevIndex) {
                    this.percent = 1 - this.percent;
                    this.dir *= -1;
                    this._show(false, this.index, true);
                    this._transitioner = null;
                } else {

                    const dirChange = (isRtl ? this.dir * (isRtl ? 1 : -1) : this.dir) < 0 === this.prevPos > this.pos;
                    this.index = dirChange ? this.index : this.prevIndex;

                    if (dirChange) {
                        this.percent = 1 - this.percent;
                    }

                    this.show(this.dir > 0 && !dirChange || this.dir < 0 && dirChange ? 'next' : 'previous', true);
                }

            }

            css(this.list, {userSelect: '', pointerEvents: ''});

            this.drag
                = this.percent
                = null;

        }

    }

};

function hasTextNodesOnly(el) {
    return !el.children.length && el.childNodes.length;
}
<commit_msg>refactor: remove iOS workaround as it does not seem to work<commit_after>import {closest, css, getEventPos, includes, isRtl, isTouch, off, on, pointerCancel, pointerDown, pointerMove, pointerUp, selInput, trigger} from 'uikit-util';

export default {

    props: {
        draggable: Boolean
    },

    data: {
        draggable: true,
        threshold: 10
    },

    created() {

        ['start', 'move', 'end'].forEach(key => {

            const fn = this[key];
            this[key] = e => {

                const pos = getEventPos(e).x * (isRtl ? -1 : 1);

                this.prevPos = pos !== this.pos ? this.pos : this.prevPos;
                this.pos = pos;

                fn(e);
            };

        });

    },

    events: [

        {

            name: pointerDown,

            delegate() {
                return this.selSlides;
            },

            handler(e) {

                if (!this.draggable
                    || !isTouch(e) && hasTextNodesOnly(e.target)
                    || closest(e.target, selInput)
                    || e.button > 0
                    || this.length < 2
                ) {
                    return;
                }

                this.start(e);
            }

        },

        {
            name: 'dragstart',

            handler(e) {
                e.preventDefault();
            }
        }

    ],

    methods: {

        start() {

            this.drag = this.pos;

            if (this._transitioner) {

                this.percent = this._transitioner.percent();
                this.drag += this._transitioner.getDistance() * this.percent * this.dir;

                this._transitioner.cancel();
                this._transitioner.translate(this.percent);

                this.dragging = true;

                this.stack = [];

            } else {
                this.prevIndex = this.index;
            }

            on(document, pointerMove, this.move, {passive: false, capture: true});
            on(document, `${pointerUp} ${pointerCancel}`, this.end, true);

            css(this.list, 'userSelect', 'none');

        },

        move(e) {

            const distance = this.pos - this.drag;

            if (distance === 0 || this.prevPos === this.pos || !this.dragging && Math.abs(distance) < this.threshold) {
                return;
            }

            // prevent click event
            css(this.list, 'pointerEvents', 'none');

            e.cancelable && e.preventDefault();

            this.dragging = true;
            this.dir = (distance < 0 ? 1 : -1);

            const {slides} = this;
            let {prevIndex} = this;
            let dis = Math.abs(distance);
            let nextIndex = this.getIndex(prevIndex + this.dir, prevIndex);
            let width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth;

            while (nextIndex !== prevIndex && dis > width) {

                this.drag -= width * this.dir;

                prevIndex = nextIndex;
                dis -= width;
                nextIndex = this.getIndex(prevIndex + this.dir, prevIndex);
                width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth;

            }

            this.percent = dis / width;

            const prev = slides[prevIndex];
            const next = slides[nextIndex];
            const changed = this.index !== nextIndex;
            const edge = prevIndex === nextIndex;

            let itemShown;

            [this.index, this.prevIndex].filter(i => !includes([nextIndex, prevIndex], i)).forEach(i => {
                trigger(slides[i], 'itemhidden', [this]);

                if (edge) {
                    itemShown = true;
                    this.prevIndex = prevIndex;
                }

            });

            if (this.index === prevIndex && this.prevIndex !== prevIndex || itemShown) {
                trigger(slides[this.index], 'itemshown', [this]);
            }

            if (changed) {
                this.prevIndex = prevIndex;
                this.index = nextIndex;

                !edge && trigger(prev, 'beforeitemhide', [this]);
                trigger(next, 'beforeitemshow', [this]);
            }

            this._transitioner = this._translate(Math.abs(this.percent), prev, !edge && next);

            if (changed) {
                !edge && trigger(prev, 'itemhide', [this]);
                trigger(next, 'itemshow', [this]);
            }

        },

        end() {

            off(document, pointerMove, this.move, {passive: false});
            off(document, `${pointerUp} ${pointerCancel}`, this.end, true);

            if (this.dragging) {

                this.dragging = null;

                if (this.index === this.prevIndex) {
                    this.percent = 1 - this.percent;
                    this.dir *= -1;
                    this._show(false, this.index, true);
                    this._transitioner = null;
                } else {

                    const dirChange = (isRtl ? this.dir * (isRtl ? 1 : -1) : this.dir) < 0 === this.prevPos > this.pos;
                    this.index = dirChange ? this.index : this.prevIndex;

                    if (dirChange) {
                        this.percent = 1 - this.percent;
                    }

                    this.show(this.dir > 0 && !dirChange || this.dir < 0 && dirChange ? 'next' : 'previous', true);
                }

            }

            css(this.list, {userSelect: '', pointerEvents: ''});

            this.drag
                = this.percent
                = null;

        }

    }

};

function hasTextNodesOnly(el) {
    return !el.children.length && el.childNodes.length;
}
<|endoftext|>


<commit_before>/*
Language: Objective C
Author: Valerii Hiora <valerii.hiora@gmail.com>
Contributors: Angel G. Olloqui <angelgarcia.mail@gmail.com>, Matt Diephouse <matt@diephouse.com>, Andrew Farmer <ahfarmer@gmail.com>
Category: common
*/

function(hljs) {
  var API_CLASS = {
    className: 'built_in',
    begin: '(AV|CA|CF|CG|CI|MK|MP|NS|UI)\\w+',
  };
  var OBJC_KEYWORDS = {
    keyword:
      'int float while char export sizeof typedef const struct for union ' +
      'unsigned long volatile static bool mutable if do return goto void ' +
      'enum else break extern asm case short default double register explicit ' +
      'signed typename this switch continue wchar_t inline readonly assign ' +
      'readwrite self @synchronized id typeof ' +
      'nonatomic super unichar IBOutlet IBAction strong weak copy ' +
      'in out inout bycopy byref oneway __strong __weak __block __autoreleasing ' +
      '@private @protected @public @try @property @end @throw @catch @finally ' +
      '@autoreleasepool @synthesize @dynamic @selector @optional @required',
    literal:
      'false true FALSE TRUE nil YES NO NULL',
    built_in:
      'BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once'
  };
  var LEXEMES = /[a-zA-Z@][a-zA-Z0-9_]*/;
  var CLASS_KEYWORDS = '@interface @class @protocol @implementation';
  return {
    aliases: ['mm', 'objc', 'obj-c'],
    keywords: OBJC_KEYWORDS,
    lexemes: LEXEMES,
    illegal: '</',
    contains: [
      API_CLASS,
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      hljs.C_NUMBER_MODE,
      hljs.QUOTE_STRING_MODE,
      {
        className: 'string',
        variants: [
          {
            begin: '@"', end: '"',
            illegal: '\\n',
            contains: [hljs.BACKSLASH_ESCAPE]
          },
          {
            begin: '\'', end: '[^\\\\]\'',
            illegal: '[^\\\\][^\']'
          }
        ]
      },
      {
        className: 'meta',
        begin: '#',
        end: '$',
        contains: [
          {
            className: 'meta-string',
            variants: [
              { begin: '\"', end: '\"' },
              { begin: '<', end: '>' }
            ]
          }
        ]
      },
      {
        className: 'class',
        begin: '(' + CLASS_KEYWORDS.split(' ').join('|') + ')\\b', end: '({|$)', excludeEnd: true,
        keywords: CLASS_KEYWORDS, lexemes: LEXEMES,
        contains: [
          hljs.UNDERSCORE_TITLE_MODE
        ]
      },
      {
        begin: '\\.'+hljs.UNDERSCORE_IDENT_RE,
        relevance: 0
      }
    ]
  };
}
<commit_msg>Add XCTest framework highlighting support<commit_after>/*
Language: Objective C
Author: Valerii Hiora <valerii.hiora@gmail.com>
Contributors: Angel G. Olloqui <angelgarcia.mail@gmail.com>, Matt Diephouse <matt@diephouse.com>, Andrew Farmer <ahfarmer@gmail.com>
Category: common
*/

function(hljs) {
  var API_CLASS = {
    className: 'built_in',
    begin: '(AV|CA|CF|CG|CI|MK|MP|NS|UI|XC)\\w+',
  };
  var OBJC_KEYWORDS = {
    keyword:
      'int float while char export sizeof typedef const struct for union ' +
      'unsigned long volatile static bool mutable if do return goto void ' +
      'enum else break extern asm case short default double register explicit ' +
      'signed typename this switch continue wchar_t inline readonly assign ' +
      'readwrite self @synchronized id typeof ' +
      'nonatomic super unichar IBOutlet IBAction strong weak copy ' +
      'in out inout bycopy byref oneway __strong __weak __block __autoreleasing ' +
      '@private @protected @public @try @property @end @throw @catch @finally ' +
      '@autoreleasepool @synthesize @dynamic @selector @optional @required',
    literal:
      'false true FALSE TRUE nil YES NO NULL',
    built_in:
      'BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once'
  };
  var LEXEMES = /[a-zA-Z@][a-zA-Z0-9_]*/;
  var CLASS_KEYWORDS = '@interface @class @protocol @implementation';
  return {
    aliases: ['mm', 'objc', 'obj-c'],
    keywords: OBJC_KEYWORDS,
    lexemes: LEXEMES,
    illegal: '</',
    contains: [
      API_CLASS,
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      hljs.C_NUMBER_MODE,
      hljs.QUOTE_STRING_MODE,
      {
        className: 'string',
        variants: [
          {
            begin: '@"', end: '"',
            illegal: '\\n',
            contains: [hljs.BACKSLASH_ESCAPE]
          },
          {
            begin: '\'', end: '[^\\\\]\'',
            illegal: '[^\\\\][^\']'
          }
        ]
      },
      {
        className: 'meta',
        begin: '#',
        end: '$',
        contains: [
          {
            className: 'meta-string',
            variants: [
              { begin: '\"', end: '\"' },
              { begin: '<', end: '>' }
            ]
          }
        ]
      },
      {
        className: 'class',
        begin: '(' + CLASS_KEYWORDS.split(' ').join('|') + ')\\b', end: '({|$)', excludeEnd: true,
        keywords: CLASS_KEYWORDS, lexemes: LEXEMES,
        contains: [
          hljs.UNDERSCORE_TITLE_MODE
        ]
      },
      {
        begin: '\\.'+hljs.UNDERSCORE_IDENT_RE,
        relevance: 0
      }
    ]
  };
}
<|endoftext|>


<commit_before>(function (factory) {
if (typeof define === 'function' && define.amd) {
	//AMD
	define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
	// Node/CommonJS
	module.exports = factory(require('leaflet'));
} else {
	// Browser globals
	if (typeof window.L === 'undefined')
		throw 'Leaflet must be loaded first';
	factory(window.L);
}
})(function (L) {

L.Control.PanelLayers = L.Control.Layers.extend({

	includes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,

	options: {
		compact: false,
		compactOffset: 0,
		collapsed: false,
		autoZIndex: true,
		collapsibleGroups: false,
		buildItem: null,				//function that return row item html node(or html string)
		title: '',						//title of panel
		className: '',					//additional class name for panel
		position: 'topright'
	},

	initialize: function (baseLayers, overlays, options) {
		L.setOptions(this, options);
		this._layers = [];
		this._groups = {};
		this._items = {};
		this._layersActives = [];
		this._lastZIndex = 0;
		this._handlingClick = false;

		this.className = 'leaflet-panel-layers';

		var i, n, isCollapsed;

		for (i in baseLayers) {
			if (baseLayers[i].group && baseLayers[i].layers) {
				isCollapsed = baseLayers[i].collapsed || false;
				for (n in baseLayers[i].layers)
					this._addLayer(baseLayers[i].layers[n], false, baseLayers[i].group, isCollapsed);
			}
			else
				this._addLayer(baseLayers[i], false);
		}

		for (i in overlays) {
			if (overlays[i].group && overlays[i].layers) {
				isCollapsed = overlays[i].collapsed || false;
				for (n in overlays[i].layers)
					this._addLayer(overlays[i].layers[n], true, overlays[i].group, isCollapsed);
			}
			else
				this._addLayer(overlays[i], true);
		}
	},

	onAdd: function (map) {

		var self = this;

		for (var i in this._layersActives) {
			map.addLayer(this._layersActives[i]);
		}

		L.Control.Layers.prototype.onAdd.call(this, map);

		this._map.on('resize', function(e) {
			self._updateHeight(e.newSize.y);
		});

		return this._container;
	},

	//TODO addBaseLayerGroup
	//TODO addOverlayGroup

	addBaseLayer: function (layer, name, group) {
		layer.name = name || layer.name || '';
		this._addLayer(layer, false, group);
		this._update();
		return this;
	},

	addOverlay: function (layer, name, group) {
		layer.name = name || layer.name || '';
		this._addLayer(layer, true, group);
		this._update();
		return this;
	},

	removeLayer: function (layerDef) {
		var layer = layerDef.hasOwnProperty('layer') ? this._layerFromDef(layerDef) : layerDef;

		this._map.removeLayer(layer);

		L.Control.Layers.prototype.removeLayer.call(this, layer);
		return this;
	},

	clearLayers: function () {
		for (var i = 0; i < this._layers.length; i++) {
			this.removeLayer(this._layers[i]);
		}
	},

	_layerFromDef: function (layerDef) {
		for (var i = 0; i < this._layers.length; i++) {
			var id = L.stamp(this._layers[i].layer);
			//TODO add more conditions to comparing definitions
			if (this._getLayer(id).name === layerDef.name)
				return this._getLayer(id).layer;
		}
	},

	_update: function () {
		this._groups = {};
		this._items = {};
		L.Control.Layers.prototype._update.call(this);
	},

	_getLayer: function (id) {
		for (var i = 0; i < this._layers.length; i++) {
			if (this._layers[i] && this._layers[i].id == id) {
				return this._layers[i];
			}
		}
	},

	_addLayer: function (layerDef, overlay, group, isCollapsed) {

		if(!layerDef.layer)
			throw new Error('layer not defined in item: '+(layerDef.name||''));

		if (!(layerDef.layer instanceof L.Class) &&
			(layerDef.layer.type && layerDef.layer.args)) {
			layerDef.layer = this._getPath(L, layerDef.layer.type).apply(L, layerDef.layer.args);
		}

		if(!layerDef.hasOwnProperty('id'))
			layerDef.id = L.stamp(layerDef.layer);

		if(layerDef.active)
			this._layersActives.push(layerDef.layer);

		this._layers.push(L.Util.extend(layerDef, {
			collapsed: isCollapsed,
			overlay: overlay,
			group: group
		}));

		if (this.options.autoZIndex && layerDef.layer && layerDef.layer.setZIndex) {
			this._lastZIndex++;
			layerDef.layer.setZIndex(this._lastZIndex);
		}

	},

	_createItem: function (obj) {

		var self = this;

		var item, input, checked;

		item = L.DomUtil.create('div', this.className + '-item' + (obj.active ? ' active' : ''));

		checked = this._map.hasLayer(obj.layer);

		if (obj.overlay) {
			input = L.DomUtil.create('input', this.className + '-selector');
			input.type = 'checkbox';
			input.defaultChecked = checked;
			//TODO name
		} else
			input = this._createRadioElement('leaflet-base-layers', checked, obj);

		input.value = obj.id;
		input.layerId = obj.id;
		input.id = obj.id;
		input._layer = obj;

		L.DomEvent.on(input, 'click', function (e) {

			self._onInputClick();

			if (e.target.checked) {
				self.fire('panel:selected', e.target._layer);
			} else {
				self.fire('panel:unselected', e.target._layer);
			}

		}, this);

		var label = L.DomUtil.create('label', this.className + '-title');
		//TODO label.htmlFor = input.id;
		var title = L.DomUtil.create('span');
		title.innerHTML = obj.name || '';

		if (obj.icon) {
			var icon = L.DomUtil.create('i', this.className + '-icon');

			if (typeof obj.icon === 'string')
				icon.innerHTML = obj.icon || '';
			else
				icon.appendChild(obj.icon);

			label.appendChild(icon);
		}

		label.appendChild(input);
		label.appendChild(title);
		item.appendChild(label);

		if (this.options.buildItem) {
			var node = this.options.buildItem.call(this, obj); //custom node node or html string
			if (typeof node === 'string') {
				var tmp = L.DomUtil.create('div');
				tmp.innerHTML = node;
				item.appendChild(tmp.firstChild);
			}
			else
				item.appendChild(node);
		}

		this._items[input.value] = item;

		return item;
	},

	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
	_createRadioElement: function (name, checked, obj) {

		var radioHtml = '<input type="radio" class="' + this.className + '-selector" name="' + name + '" id="' + obj.id + '"';
		if (checked) {
			radioHtml += ' checked="checked"';
		}
		radioHtml += ' />';

		var radioFragment = document.createElement('div');
		radioFragment.innerHTML = radioHtml;

		return radioFragment.firstChild;
	},

	_addItem: function (obj) {
		var self = this,
			label, input, icon, checked;

		var list = obj.overlay ? this._overlaysList : this._baseLayersList;

		if (obj.group) {
			if (!obj.group.hasOwnProperty('name'))
				obj.group = {name: obj.group};

			if (!this._groups[obj.group.name]) {
				var collapsed = false;
				if (obj.collapsed === true)
					collapsed = true;
				this._groups[obj.group.name] = this._createGroup(obj.group, collapsed);
			}

			list.appendChild(this._groups[obj.group.name]);
			list = this._groups[obj.group.name];
		}

		label = this._createItem(obj);

		list.appendChild(label);

		return label;
	},

	_createGroup: function (groupdata, isCollapsed) {

		var self = this,
			groupdiv = L.DomUtil.create('div', this.className + '-group'),
			grouplabel, grouptit, groupexp;

		if (this.options.collapsibleGroups) {

			L.DomUtil.addClass(groupdiv, 'collapsible');

			groupexp = L.DomUtil.create('i', this.className + '-icon', groupdiv);
			if (isCollapsed === true)
				groupexp.innerHTML = ' + ';
			else
				groupexp.innerHTML = ' - ';

			L.DomEvent.on(groupexp, 'click', function () {
				if (L.DomUtil.hasClass(groupdiv, 'expanded')) {
					L.DomUtil.removeClass(groupdiv, 'expanded');
					groupexp.innerHTML = ' + ';
				} else {
					L.DomUtil.addClass(groupdiv, 'expanded');
					groupexp.innerHTML = ' - ';
				}
				self._updateHeight();
			});

			if (isCollapsed === false)
				L.DomUtil.addClass(groupdiv, 'expanded');
		}

		grouplabel = L.DomUtil.create('label', this.className + '-grouplabel', groupdiv);
		grouptit = L.DomUtil.create('span', this.className + '-title', grouplabel);
		grouptit.innerHTML = groupdata.name;

		return groupdiv;
	},

	_onInputClick: function () {
		var i, input, obj,
			inputs = this._form.getElementsByClassName(this.className + '-selector'),
			inputsLen = inputs.length;

		this._handlingClick = true;

		for (i = 0; i < inputsLen; i++) {

			input = inputs[i];

			obj = this._getLayer(input.value);

			if (input.checked && !this._map.hasLayer(obj.layer)) {
				L.DomUtil.addClass(input.parentNode.parentNode, 'active');
				this._map.addLayer(obj.layer);

			} else if (!input.checked && this._map.hasLayer(obj.layer)) {
				L.DomUtil.removeClass(input.parentNode.parentNode, 'active');
				this._map.removeLayer(obj.layer);
			}
		}

		this._handlingClick = false;

		this._refocusOnMap();
	},

	_initLayout: function () {
		var container = this._container = L.DomUtil.create('div', this.className);

		if(this.options.compact)
			L.DomUtil.addClass(container, 'compact');

		//Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
		container.setAttribute('aria-haspopup', true);

		L.DomEvent
			.disableClickPropagation(container)
			.disableScrollPropagation(container);

		if (this.options.className)
			L.DomUtil.addClass(container, this.options.className);

		this._form = L.DomUtil.create('form', this.className + '-list');

		this._updateHeight();

		if (this.options.collapsed) {

			if (L.Browser.android)
				L.DomEvent
					.on(container, 'click', this._expand, this);
			else {
				L.DomEvent
					.on(container, 'mouseenter', this._expand, this)
					.on(container, 'mouseleave', this._collapse, this);
			}

			this._map.on('click', this._collapse, this);

		} else {
			this._expand();
		}

		this._baseLayersList = L.DomUtil.create('div', this.className + '-base', this._form);
		this._separator = L.DomUtil.create('div', this.className + '-separator', this._form);
		this._overlaysList = L.DomUtil.create('div', this.className + '-overlays', this._form);

		/* maybe useless
		if (!this.options.compact)
			L.DomUtil.create('div', this.className + '-margin', this._form);*/

		if (this.options.title) {
			var titlabel = L.DomUtil.create('label', this.className + '-title');
			titlabel.innerHTML = '<span>' + this.options.title + '</span>';
			container.appendChild(titlabel);
		}

		container.appendChild(this._form);
	},

	_updateHeight: function (h) {
		h = h || this._map.getSize().y;

		if (this.options.compact)
			this._form.style.maxHeight = (h - this.options.compactOffset) + 'px';
		else
			this._form.style.height = h + 'px';
	},

	_expand: function () {
		L.DomUtil.addClass(this._container, 'expanded');
	},

	_collapse: function () {
		this._container.className = this._container.className.replace('expanded', '');
	},

	_getPath: function (obj, prop) {
		var parts = prop.split('.'),
			last = parts.pop(),
			len = parts.length,
			cur = parts[0],
			i = 1;

		if (len > 0)
			while ((obj = obj[cur]) && i < len)
				cur = parts[i++];

		if (obj)
			return obj[last];
	}
});

L.control.panelLayers = function (baseLayers, overlays, options) {
	return new L.Control.PanelLayers(baseLayers, overlays, options);
};

return L.Control.PanelLayers;

});
<commit_msg>Leaflet 1.4 missing this._section exception fix.<commit_after>(function (factory) {
if (typeof define === 'function' && define.amd) {
	//AMD
	define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
	// Node/CommonJS
	module.exports = factory(require('leaflet'));
} else {
	// Browser globals
	if (typeof window.L === 'undefined')
		throw 'Leaflet must be loaded first';
	factory(window.L);
}
})(function (L) {

L.Control.PanelLayers = L.Control.Layers.extend({

	includes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,

	options: {
		compact: false,
		compactOffset: 0,
		collapsed: false,
		autoZIndex: true,
		collapsibleGroups: false,
		buildItem: null,				//function that return row item html node(or html string)
		title: '',						//title of panel
		className: '',					//additional class name for panel
		position: 'topright'
	},

	initialize: function (baseLayers, overlays, options) {
		L.setOptions(this, options);
		this._layers = [];
		this._groups = {};
		this._items = {};
		this._layersActives = [];
		this._lastZIndex = 0;
		this._handlingClick = false;

		this.className = 'leaflet-panel-layers';

		var i, n, isCollapsed;

		for (i in baseLayers) {
			if (baseLayers[i].group && baseLayers[i].layers) {
				isCollapsed = baseLayers[i].collapsed || false;
				for (n in baseLayers[i].layers)
					this._addLayer(baseLayers[i].layers[n], false, baseLayers[i].group, isCollapsed);
			}
			else
				this._addLayer(baseLayers[i], false);
		}

		for (i in overlays) {
			if (overlays[i].group && overlays[i].layers) {
				isCollapsed = overlays[i].collapsed || false;
				for (n in overlays[i].layers)
					this._addLayer(overlays[i].layers[n], true, overlays[i].group, isCollapsed);
			}
			else
				this._addLayer(overlays[i], true);
		}
	},

	onAdd: function (map) {

		var self = this;

		for (var i in this._layersActives) {
			map.addLayer(this._layersActives[i]);
		}

		L.Control.Layers.prototype.onAdd.call(this, map);

		this._map.on('resize', function(e) {
			self._updateHeight(e.newSize.y);
		});

		return this._container;
	},

	//TODO addBaseLayerGroup
	//TODO addOverlayGroup

	addBaseLayer: function (layer, name, group) {
		layer.name = name || layer.name || '';
		this._addLayer(layer, false, group);
		this._update();
		return this;
	},

	addOverlay: function (layer, name, group) {
		layer.name = name || layer.name || '';
		this._addLayer(layer, true, group);
		this._update();
		return this;
	},

	removeLayer: function (layerDef) {
		var layer = layerDef.hasOwnProperty('layer') ? this._layerFromDef(layerDef) : layerDef;

		this._map.removeLayer(layer);

		L.Control.Layers.prototype.removeLayer.call(this, layer);
		return this;
	},

	clearLayers: function () {
		for (var i = 0; i < this._layers.length; i++) {
			this.removeLayer(this._layers[i]);
		}
	},

	_layerFromDef: function (layerDef) {
		for (var i = 0; i < this._layers.length; i++) {
			var id = L.stamp(this._layers[i].layer);
			//TODO add more conditions to comparing definitions
			if (this._getLayer(id).name === layerDef.name)
				return this._getLayer(id).layer;
		}
	},

	_update: function () {
		this._groups = {};
		this._items = {};
		L.Control.Layers.prototype._update.call(this);
	},

	_getLayer: function (id) {
		for (var i = 0; i < this._layers.length; i++) {
			if (this._layers[i] && this._layers[i].id == id) {
				return this._layers[i];
			}
		}
	},

	_addLayer: function (layerDef, overlay, group, isCollapsed) {

		if(!layerDef.layer)
			throw new Error('layer not defined in item: '+(layerDef.name||''));

		if (!(layerDef.layer instanceof L.Class) &&
			(layerDef.layer.type && layerDef.layer.args)) {
			layerDef.layer = this._getPath(L, layerDef.layer.type).apply(L, layerDef.layer.args);
		}

		if(!layerDef.hasOwnProperty('id'))
			layerDef.id = L.stamp(layerDef.layer);

		if(layerDef.active)
			this._layersActives.push(layerDef.layer);

		this._layers.push(L.Util.extend(layerDef, {
			collapsed: isCollapsed,
			overlay: overlay,
			group: group
		}));

		if (this.options.autoZIndex && layerDef.layer && layerDef.layer.setZIndex) {
			this._lastZIndex++;
			layerDef.layer.setZIndex(this._lastZIndex);
		}

	},

	_createItem: function (obj) {

		var self = this;

		var item, input, checked;

		item = L.DomUtil.create('div', this.className + '-item' + (obj.active ? ' active' : ''));

		checked = this._map.hasLayer(obj.layer);

		if (obj.overlay) {
			input = L.DomUtil.create('input', this.className + '-selector');
			input.type = 'checkbox';
			input.defaultChecked = checked;
			//TODO name
		} else
			input = this._createRadioElement('leaflet-base-layers', checked, obj);

		input.value = obj.id;
		input.layerId = obj.id;
		input.id = obj.id;
		input._layer = obj;

		L.DomEvent.on(input, 'click', function (e) {

			self._onInputClick();

			if (e.target.checked) {
				self.fire('panel:selected', e.target._layer);
			} else {
				self.fire('panel:unselected', e.target._layer);
			}

		}, this);

		var label = L.DomUtil.create('label', this.className + '-title');
		//TODO label.htmlFor = input.id;
		var title = L.DomUtil.create('span');
		title.innerHTML = obj.name || '';

		if (obj.icon) {
			var icon = L.DomUtil.create('i', this.className + '-icon');

			if (typeof obj.icon === 'string')
				icon.innerHTML = obj.icon || '';
			else
				icon.appendChild(obj.icon);

			label.appendChild(icon);
		}

		label.appendChild(input);
		label.appendChild(title);
		item.appendChild(label);

		if (this.options.buildItem) {
			var node = this.options.buildItem.call(this, obj); //custom node node or html string
			if (typeof node === 'string') {
				var tmp = L.DomUtil.create('div');
				tmp.innerHTML = node;
				item.appendChild(tmp.firstChild);
			}
			else
				item.appendChild(node);
		}

		this._items[input.value] = item;

		return item;
	},

	// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
	_createRadioElement: function (name, checked, obj) {

		var radioHtml = '<input type="radio" class="' + this.className + '-selector" name="' + name + '" id="' + obj.id + '"';
		if (checked) {
			radioHtml += ' checked="checked"';
		}
		radioHtml += ' />';

		var radioFragment = document.createElement('div');
		radioFragment.innerHTML = radioHtml;

		return radioFragment.firstChild;
	},

	_addItem: function (obj) {
		var self = this,
			label, input, icon, checked;

		var list = obj.overlay ? this._overlaysList : this._baseLayersList;

		if (obj.group) {
			if (!obj.group.hasOwnProperty('name'))
				obj.group = {name: obj.group};

			if (!this._groups[obj.group.name]) {
				var collapsed = false;
				if (obj.collapsed === true)
					collapsed = true;
				this._groups[obj.group.name] = this._createGroup(obj.group, collapsed);
			}

			list.appendChild(this._groups[obj.group.name]);
			list = this._groups[obj.group.name];
		}

		label = this._createItem(obj);

		list.appendChild(label);

		return label;
	},

	_createGroup: function (groupdata, isCollapsed) {

		var self = this,
			groupdiv = L.DomUtil.create('div', this.className + '-group'),
			grouplabel, grouptit, groupexp;

		if (this.options.collapsibleGroups) {

			L.DomUtil.addClass(groupdiv, 'collapsible');

			groupexp = L.DomUtil.create('i', this.className + '-icon', groupdiv);
			if (isCollapsed === true)
				groupexp.innerHTML = ' + ';
			else
				groupexp.innerHTML = ' - ';

			L.DomEvent.on(groupexp, 'click', function () {
				if (L.DomUtil.hasClass(groupdiv, 'expanded')) {
					L.DomUtil.removeClass(groupdiv, 'expanded');
					groupexp.innerHTML = ' + ';
				} else {
					L.DomUtil.addClass(groupdiv, 'expanded');
					groupexp.innerHTML = ' - ';
				}
				self._updateHeight();
			});

			if (isCollapsed === false)
				L.DomUtil.addClass(groupdiv, 'expanded');
		}

		grouplabel = L.DomUtil.create('label', this.className + '-grouplabel', groupdiv);
		grouptit = L.DomUtil.create('span', this.className + '-title', grouplabel);
		grouptit.innerHTML = groupdata.name;

		return groupdiv;
	},

	_onInputClick: function () {
		var i, input, obj,
			inputs = this._form.getElementsByClassName(this.className + '-selector'),
			inputsLen = inputs.length;

		this._handlingClick = true;

		for (i = 0; i < inputsLen; i++) {

			input = inputs[i];

			obj = this._getLayer(input.value);

			if (input.checked && !this._map.hasLayer(obj.layer)) {
				L.DomUtil.addClass(input.parentNode.parentNode, 'active');
				this._map.addLayer(obj.layer);

			} else if (!input.checked && this._map.hasLayer(obj.layer)) {
				L.DomUtil.removeClass(input.parentNode.parentNode, 'active');
				this._map.removeLayer(obj.layer);
			}
		}

		this._handlingClick = false;

		this._refocusOnMap();
	},

	_initLayout: function () {
		var container = this._container = L.DomUtil.create('div', this.className);

		if(this.options.compact)
			L.DomUtil.addClass(container, 'compact');

		//Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
		container.setAttribute('aria-haspopup', true);

		L.DomEvent
			.disableClickPropagation(container)
			.disableScrollPropagation(container);

		if (this.options.className)
			L.DomUtil.addClass(container, this.options.className);

		this._section = this._form = L.DomUtil.create('form', this.className + '-list');

		this._updateHeight();

		if (this.options.collapsed) {

			if (L.Browser.android)
				L.DomEvent
					.on(container, 'click', this._expand, this);
			else {
				L.DomEvent
					.on(container, 'mouseenter', this._expand, this)
					.on(container, 'mouseleave', this._collapse, this);
			}

			this._map.on('click', this._collapse, this);

		} else {
			this._expand();
		}

		this._baseLayersList = L.DomUtil.create('div', this.className + '-base', this._form);
		this._separator = L.DomUtil.create('div', this.className + '-separator', this._form);
		this._overlaysList = L.DomUtil.create('div', this.className + '-overlays', this._form);

		/* maybe useless
		if (!this.options.compact)
			L.DomUtil.create('div', this.className + '-margin', this._form);*/

		if (this.options.title) {
			var titlabel = L.DomUtil.create('label', this.className + '-title');
			titlabel.innerHTML = '<span>' + this.options.title + '</span>';
			container.appendChild(titlabel);
		}

		container.appendChild(this._form);
	},

	_updateHeight: function (h) {
		h = h || this._map.getSize().y;

		if (this.options.compact)
			this._form.style.maxHeight = (h - this.options.compactOffset) + 'px';
		else
			this._form.style.height = h + 'px';
	},

	_expand: function () {
		L.DomUtil.addClass(this._container, 'expanded');
	},

	_collapse: function () {
		this._container.className = this._container.className.replace('expanded', '');
	},

	_getPath: function (obj, prop) {
		var parts = prop.split('.'),
			last = parts.pop(),
			len = parts.length,
			cur = parts[0],
			i = 1;

		if (len > 0)
			while ((obj = obj[cur]) && i < len)
				cur = parts[i++];

		if (obj)
			return obj[last];
	}
});

L.control.panelLayers = function (baseLayers, overlays, options) {
	return new L.Control.PanelLayers(baseLayers, overlays, options);
};

return L.Control.PanelLayers;

});
<|endoftext|>


<commit_before>const globals = require('../globals');
var _ = require('lodash');

function postHealthMetricsToInfluxdb(host, body, influxTags) {
    // Calculate server uptime

    var dateTime = Date.now();
    var timestamp = Math.floor(dateTime);

    var str = body.started;
    var year = str.substring(0, 4);
    var month = str.substring(4, 6);
    var day = str.substring(6, 8);
    var hour = str.substring(9, 11);
    var minute = str.substring(11, 13);
    var second = str.substring(13, 15);
    var dateTimeStarted = new Date(year, month - 1, day, hour, minute, second);
    var timestampStarted = Math.floor(dateTimeStarted);

    var diff = timestamp - timestampStarted;

    // Create a new JavaScript Date object based on the timestamp
    // multiplied by 1000 so that the argument is in milliseconds, not seconds.
    var date = new Date(diff);

    var days = Math.trunc(diff / (1000 * 60 * 60 * 24));

    // Hours part from the timestamp
    var hours = date.getHours();

    // Minutes part from the timestamp
    var minutes = '0' + date.getMinutes();

    // Seconds part from the timestamp
    var seconds = '0' + date.getSeconds();

    // Will display time in 10:30:23 format
    var formattedTime =
        days + ' days, ' + hours + 'h ' + minutes.substr(-2) + 'm ' + seconds.substr(-2) + 's';

    // Build tags structure that will be passed to InfluxDB
    globals.logger.debug(
        `HEALTH METRICS TO INFLUXDB: Health data: Tags sent to InfluxDB: ${JSON.stringify(
            influxTags,
        )}`,
    );

    // Get app names for active/loaded/inmemory docs
    var appNamesActive = [],
        appNamesLoaded = [],
        appNamesInMemory = [];

    globals.logger.debug(`HEALTH METRICS TO INFLUXDB: Number of apps loaded: ${body.apps.loaded_docs.length}`);
    globals.logger.debug(`HEALTH METRICS TO INFLUXDB: Number of apps in memory: ${body.apps.in_memory_docs.length}`);
    globals.logger.debug(`HEALTH METRICS TO INFLUXDB: Number of apps active: ${body.apps.active_docs.length}`);

    body.apps.active_docs.forEach(function(docID) {
        let app = globals.appNames.find(element => element.id == docID);

        if (app) {
            globals.logger.debug(`HEALTH METRICS TO INFLUXDB: App is active: ${app.name}`);
            appNamesActive.push(app.name);
        } else {
            appNamesActive.push(docID);
        }
    });

    appNamesActive.sort();

    body.apps.loaded_docs.forEach(function(docID) {
        let app = globals.appNames.find(element => element.id == docID);

        if (globals.appNames.find(element => element.id == docID)) {
            globals.logger.debug(`HEALTH METRICS TO INFLUXDB: App is loaded: ${app.name}`);
            appNamesLoaded.push(globals.appNames.find(element => element.id == docID).name);
        } else {
            appNamesLoaded.push(docID);
        }
    });

    appNamesLoaded.sort();

    body.apps.in_memory_docs.forEach(function(docID) {
        let app = globals.appNames.find(element => element.id == docID);

        if (globals.appNames.find(element => element.id == docID)) {
            globals.logger.debug(`HEALTH METRICS TO INFLUXDB: App is in memory: ${app.name}`);
            appNamesInMemory.push(globals.appNames.find(element => element.id == docID).name);
        } else {
            appNamesInMemory.push(docID);
        }
    });

    appNamesInMemory.sort();

    // Write the whole reading to Influxdb
    globals.influx
        .writePoints([
            {
                measurement: 'sense_server',
                tags: influxTags,
                fields: {
                    version: body.version,
                    started: body.started,
                    uptime: formattedTime,
                },
            },
            {
                measurement: 'mem',
                tags: influxTags,
                fields: {
                    comitted: body.mem.comitted,
                    allocated: body.mem.allocated,
                    free: body.mem.free,
                },
            },
            {
                measurement: 'apps',
                tags: influxTags,
                fields: {
                    active_docs_count: body.apps.active_docs.length,
                    loaded_docs_count: body.apps.loaded_docs.length,
                    in_memory_docs_count: body.apps.in_memory_docs.length,
                    active_docs: globals.config.get(
                        'Butler-SOS.influxdbConfig.includeFields.activeDocs',
                    )
                        ? body.apps.active_docs
                        : '',
                    active_docs_names: globals.config.get(
                        'Butler-SOS.appNames.enableAppNameExtract',
                    )
                        ? appNamesActive.toString()
                        : '',
                    loaded_docs: globals.config.get(
                        'Butler-SOS.influxdbConfig.includeFields.loadedDocs',
                    )
                        ? body.apps.loaded_docs
                        : '',
                    loaded_docs_names: globals.config.get(
                        'Butler-SOS.appNames.enableAppNameExtract',
                    )
                        ? appNamesLoaded.toString()
                        : '',
                    in_memory_docs: globals.config.get(
                        'Butler-SOS.influxdbConfig.includeFields.inMemoryDocs',
                    )
                        ? body.apps.in_memory_docs
                        : '',
                    in_memory_docs_names: globals.config.get(
                        'Butler-SOS.appNames.enableAppNameExtract',
                    )
                        ? appNamesInMemory.toString()
                        : '',
                    calls: body.apps.calls,
                    selections: body.apps.selections,
                },
            },
            {
                measurement: 'cpu',
                tags: influxTags,
                fields: {
                    total: body.cpu.total,
                },
            },
            {
                measurement: 'session',
                tags: influxTags,
                fields: {
                    active: body.session.active,
                    total: body.session.total,
                },
            },
            {
                measurement: 'users',
                tags: influxTags,
                fields: {
                    active: body.users.active,
                    total: body.users.total,
                },
            },
            {
                measurement: 'cache',
                tags: influxTags,
                fields: {
                    hits: body.cache.hits,
                    lookups: body.cache.lookups,
                    added: body.cache.added,
                    replaced: body.cache.replaced,
                    bytes_added: body.cache.bytes_added,
                },
            },
            {
                measurement: 'saturated',
                tags: influxTags,
                fields: {
                    saturated: body.saturated,
                },
            },
        ])

        .then(() => {
            globals.logger.verbose(
                `HEALTH METRICS: Sent health data to Influxdb for server ${influxTags.server_name}`,
            );
        })

        .catch(err => {
            globals.logger.error(
                `HEALTH METRICS: Error saving health data to InfluxDB! ${err.stack}`,
            );
        });
}

function postUserSessionsToInfluxdb(host, virtualProxy, body, influxTags) {
    globals.logger.debug(
        `USER SESSIONS: User sessions body received (VP=${virtualProxy}): ${JSON.stringify(body)}`,
    );
    globals.logger.debug(
        `USER SESSIONS: User session: Shared tags sent to InfluxDB (VP=${virtualProxy}): ${JSON.stringify(
            influxTags,
        )}`,
    );

    // Build tags structure that will be passed to InfluxDB
    // Get local copy of tags, then add user session specific tags
    let tmpTags = _.cloneDeep(influxTags);
    tmpTags.user_session_virtual_proxy = virtualProxy;
    tmpTags.user_session_host = host;

    // Build comma separated list of all user IDs connected via the current virtual proxy
    let userArray = Array.prototype.map.call(body, s => s.UserDirectory + '\\' + s.UserId);
    let uniqueUserList = Array.from(new Set(userArray)).toString();

    let datapoint = [
        {
            measurement: 'user_session_summary',
            tags: tmpTags,
            fields: {
                session_count: body.length,
            },
        },
        {
            measurement: 'user_session_list',
            tags: tmpTags,
            fields: {
                session_user_id_list: uniqueUserList,
            },
        },
    ];

    // Add details for each session
    body.forEach(bodyItem => {
        globals.logger.debug(`USER SESSIONS: User session: Body item: ${JSON.stringify(bodyItem)}`);

        // Start over with fresh copy of shared tags
        tmpTags = _.cloneDeep(influxTags);
        tmpTags.user_session_virtual_proxy = virtualProxy;
        tmpTags.user_session_host = host;

        // Add extra tags for this body item
        tmpTags.user_session_id = bodyItem.SessionId;
        tmpTags.user_session_user_directory = bodyItem.UserDirectory;
        tmpTags.user_session_user_id = bodyItem.UserId;

        let sessionDatapoint = {
            measurement: 'user_session_details',
            tags: tmpTags,
            fields: {
                // attributes: bodyItem.Attributes,
                session_id: bodyItem.SessionId,
                user_directory: bodyItem.UserDirectory,
                user_id: bodyItem.UserId,
            },
        };

        datapoint.push(sessionDatapoint);
    });

    globals.influx
        .writePoints(datapoint)
        .then(() => {
            globals.logger.silly(
                `USER SESSIONS: Influxdb datapoint for server "${host}", virtual proxy "${virtualProxy}"": ${JSON.stringify(
                    datapoint,
                    null,
                    2,
                )}`,
            );

            globals.logger.debug(
                `USER SESSIONS: Session count for server "${host}", virtual proxy "${virtualProxy}"": ${body.length}`,
            );
            globals.logger.debug(
                `USER SESSIONS: User list for server "${host}", virtual proxy "${virtualProxy}"": ${uniqueUserList}`,
            );

            globals.logger.verbose(
                `USER SESSIONS: Sent user session data to InfluxDB for server "${host}", virtual proxy "${virtualProxy}"`,
            );
        })
        .catch(err => {
            globals.logger.error(
                `USER SESSIONS: Error saving user session data to InfluxDB! ${err.stack}`,
            );
        });
}

module.exports = {
    postHealthMetricsToInfluxdb,
    postUserSessionsToInfluxdb,
};
<commit_msg>Refactor code to remove code smell<commit_after>const globals = require('../globals');
var _ = require('lodash');

function getFormattedTime(serverStarted) {

    var dateTime = Date.now();
    var timestamp = Math.floor(dateTime);

    var str = serverStarted;
    var year = str.substring(0, 4);
    var month = str.substring(4, 6);
    var day = str.substring(6, 8);
    var hour = str.substring(9, 11);
    var minute = str.substring(11, 13);
    var second = str.substring(13, 15);
    var dateTimeStarted = new Date(year, month - 1, day, hour, minute, second);
    var timestampStarted = Math.floor(dateTimeStarted);

    var diff = timestamp - timestampStarted;

    // Create a new JavaScript Date object based on the timestamp
    // multiplied by 1000 so that the argument is in milliseconds, not seconds.
    var date = new Date(diff);

    var days = Math.trunc(diff / (1000 * 60 * 60 * 24));

    // Hours part from the timestamp
    var hours = date.getHours();

    // Minutes part from the timestamp
    var minutes = '0' + date.getMinutes();

    // Seconds part from the timestamp
    var seconds = '0' + date.getSeconds();

    // Will display time in 10:30:23 format
    return days + ' days, ' + hours + 'h ' + minutes.substr(-2) + 'm ' + seconds.substr(-2) + 's';
}

function postHealthMetricsToInfluxdb(host, body, influxTags) {
    // Calculate server uptime
    var formattedTime=getFormattedTime(body.started);

    // Build tags structure that will be passed to InfluxDB
    globals.logger.debug(
        `HEALTH METRICS TO INFLUXDB: Health data: Tags sent to InfluxDB: ${JSON.stringify(
            influxTags,
        )}`,
    );

    // Get app names for active/loaded/inmemory docs
    var appNamesActive = [],
        appNamesLoaded = [],
        appNamesInMemory = [];

    globals.logger.debug(`HEALTH METRICS TO INFLUXDB: Number of apps loaded: ${body.apps.loaded_docs.length}`);
    globals.logger.debug(`HEALTH METRICS TO INFLUXDB: Number of apps in memory: ${body.apps.in_memory_docs.length}`);
    globals.logger.debug(`HEALTH METRICS TO INFLUXDB: Number of apps active: ${body.apps.active_docs.length}`);

    body.apps.active_docs.forEach(function(docID) {
        let app = globals.appNames.find(element => element.id == docID);

        if (app) {
            globals.logger.debug(`HEALTH METRICS TO INFLUXDB: App is active: ${app.name}`);
            appNamesActive.push(app.name);
        } else {
            appNamesActive.push(docID);
        }
    });

    appNamesActive.sort();

    body.apps.loaded_docs.forEach(function(docID) {
        let app = globals.appNames.find(element => element.id == docID);

        if (globals.appNames.find(element => element.id == docID)) {
            globals.logger.debug(`HEALTH METRICS TO INFLUXDB: App is loaded: ${app.name}`);
            appNamesLoaded.push(globals.appNames.find(element => element.id == docID).name);
        } else {
            appNamesLoaded.push(docID);
        }
    });

    appNamesLoaded.sort();

    body.apps.in_memory_docs.forEach(function(docID) {
        let app = globals.appNames.find(element => element.id == docID);

        if (globals.appNames.find(element => element.id == docID)) {
            globals.logger.debug(`HEALTH METRICS TO INFLUXDB: App is in memory: ${app.name}`);
            appNamesInMemory.push(globals.appNames.find(element => element.id == docID).name);
        } else {
            appNamesInMemory.push(docID);
        }
    });

    appNamesInMemory.sort();

    // Write the whole reading to Influxdb
    globals.influx
        .writePoints([
            {
                measurement: 'sense_server',
                tags: influxTags,
                fields: {
                    version: body.version,
                    started: body.started,
                    uptime: formattedTime,
                },
            },
            {
                measurement: 'mem',
                tags: influxTags,
                fields: {
                    comitted: body.mem.comitted,
                    allocated: body.mem.allocated,
                    free: body.mem.free,
                },
            },
            {
                measurement: 'apps',
                tags: influxTags,
                fields: {
                    active_docs_count: body.apps.active_docs.length,
                    loaded_docs_count: body.apps.loaded_docs.length,
                    in_memory_docs_count: body.apps.in_memory_docs.length,
                    active_docs: globals.config.get(
                        'Butler-SOS.influxdbConfig.includeFields.activeDocs',
                    )
                        ? body.apps.active_docs
                        : '',
                    active_docs_names: globals.config.get(
                        'Butler-SOS.appNames.enableAppNameExtract',
                    )
                        ? appNamesActive.toString()
                        : '',
                    loaded_docs: globals.config.get(
                        'Butler-SOS.influxdbConfig.includeFields.loadedDocs',
                    )
                        ? body.apps.loaded_docs
                        : '',
                    loaded_docs_names: globals.config.get(
                        'Butler-SOS.appNames.enableAppNameExtract',
                    )
                        ? appNamesLoaded.toString()
                        : '',
                    in_memory_docs: globals.config.get(
                        'Butler-SOS.influxdbConfig.includeFields.inMemoryDocs',
                    )
                        ? body.apps.in_memory_docs
                        : '',
                    in_memory_docs_names: globals.config.get(
                        'Butler-SOS.appNames.enableAppNameExtract',
                    )
                        ? appNamesInMemory.toString()
                        : '',
                    calls: body.apps.calls,
                    selections: body.apps.selections,
                },
            },
            {
                measurement: 'cpu',
                tags: influxTags,
                fields: {
                    total: body.cpu.total,
                },
            },
            {
                measurement: 'session',
                tags: influxTags,
                fields: {
                    active: body.session.active,
                    total: body.session.total,
                },
            },
            {
                measurement: 'users',
                tags: influxTags,
                fields: {
                    active: body.users.active,
                    total: body.users.total,
                },
            },
            {
                measurement: 'cache',
                tags: influxTags,
                fields: {
                    hits: body.cache.hits,
                    lookups: body.cache.lookups,
                    added: body.cache.added,
                    replaced: body.cache.replaced,
                    bytes_added: body.cache.bytes_added,
                },
            },
            {
                measurement: 'saturated',
                tags: influxTags,
                fields: {
                    saturated: body.saturated,
                },
            },
        ])

        .then(() => {
            globals.logger.verbose(
                `HEALTH METRICS: Sent health data to Influxdb for server ${influxTags.server_name}`,
            );
        })

        .catch(err => {
            globals.logger.error(
                `HEALTH METRICS: Error saving health data to InfluxDB! ${err.stack}`,
            );
        });
}

function postUserSessionsToInfluxdb(host, virtualProxy, body, influxTags) {
    globals.logger.debug(
        `USER SESSIONS: User sessions body received (VP=${virtualProxy}): ${JSON.stringify(body)}`,
    );
    globals.logger.debug(
        `USER SESSIONS: User session: Shared tags sent to InfluxDB (VP=${virtualProxy}): ${JSON.stringify(
            influxTags,
        )}`,
    );

    // Build tags structure that will be passed to InfluxDB
    // Get local copy of tags, then add user session specific tags
    let tmpTags = _.cloneDeep(influxTags);
    tmpTags.user_session_virtual_proxy = virtualProxy;
    tmpTags.user_session_host = host;

    // Build comma separated list of all user IDs connected via the current virtual proxy
    let userArray = Array.prototype.map.call(body, s => s.UserDirectory + '\\' + s.UserId);
    let uniqueUserList = Array.from(new Set(userArray)).toString();

    let datapoint = [
        {
            measurement: 'user_session_summary',
            tags: tmpTags,
            fields: {
                session_count: body.length,
            },
        },
        {
            measurement: 'user_session_list',
            tags: tmpTags,
            fields: {
                session_user_id_list: uniqueUserList,
            },
        },
    ];

    // Add details for each session
    body.forEach(bodyItem => {
        globals.logger.debug(`USER SESSIONS: User session: Body item: ${JSON.stringify(bodyItem)}`);

        // Start over with fresh copy of shared tags
        tmpTags = _.cloneDeep(influxTags);
        tmpTags.user_session_virtual_proxy = virtualProxy;
        tmpTags.user_session_host = host;

        // Add extra tags for this body item
        tmpTags.user_session_id = bodyItem.SessionId;
        tmpTags.user_session_user_directory = bodyItem.UserDirectory;
        tmpTags.user_session_user_id = bodyItem.UserId;

        let sessionDatapoint = {
            measurement: 'user_session_details',
            tags: tmpTags,
            fields: {
                // attributes: bodyItem.Attributes,
                session_id: bodyItem.SessionId,
                user_directory: bodyItem.UserDirectory,
                user_id: bodyItem.UserId,
            },
        };

        datapoint.push(sessionDatapoint);
    });

    globals.influx
        .writePoints(datapoint)
        .then(() => {
            globals.logger.silly(
                `USER SESSIONS: Influxdb datapoint for server "${host}", virtual proxy "${virtualProxy}"": ${JSON.stringify(
                    datapoint,
                    null,
                    2,
                )}`,
            );

            globals.logger.debug(
                `USER SESSIONS: Session count for server "${host}", virtual proxy "${virtualProxy}"": ${body.length}`,
            );
            globals.logger.debug(
                `USER SESSIONS: User list for server "${host}", virtual proxy "${virtualProxy}"": ${uniqueUserList}`,
            );

            globals.logger.verbose(
                `USER SESSIONS: Sent user session data to InfluxDB for server "${host}", virtual proxy "${virtualProxy}"`,
            );
        })
        .catch(err => {
            globals.logger.error(
                `USER SESSIONS: Error saving user session data to InfluxDB! ${err.stack}`,
            );
        });
}

module.exports = {
    postHealthMetricsToInfluxdb,
    postUserSessionsToInfluxdb,
};
<|endoftext|>


<commit_before>/**
 *    _____ __
 *   / ___// /_  _____
 *   \__ \/ / / / / _ \
 *  ___/ / / /_/ /  __/
 * /____/_/\__, /\___/
 *       /____/
 *       Copyright 2017 Slye Development Team. All Rights Reserved.
 *       Licence: MIT License
 */

const quotations    = require('./quotations');

/**
 * Remove all spaces from source code and put all semicolons
 * @param code
 */
function spaces(code){
	var isQuote     = quotations(code).inQuote;
	var re          = '';
	var isSpace     = letter => [' ', '\t', '\n', '\r'].indexOf(letter) > -1;
	var needSpace   = letter => [
		';','{','}','(',')','=', '[', ']',
		'+', '-', '*', '/', '^',
		'?', ':', ',',
		'!', '%', '&', '<', '>', '|'
	].indexOf(letter) == -1;
	var isAlphabets = letter => letter >= 'A' && letter <= 'z';
	var oc  = {
		'(': 0,
		')': 0,
		'[': 0,
		']': 0,
		'{': 0,
		'}': 0
	};
	var keys    = Object.keys(oc);
	var notIn       = () => {
		return (oc['('] == oc[')']) && (oc['['] == oc[']']) && (oc['{'] == oc['}']) ;
	};
	for(var offset = 0; offset < code.length;offset++){
		if(isQuote(offset)){
			re += code[offset];
		}else {
			if(keys.indexOf(code[offset]) > -1)
				oc[code[offset]]++;
			var next = offset + 1;
			while(next < code.length && (isQuote(next) || isSpace(code[next]))){
				next++;
			}
			var pre = offset - 1;
			while(pre > 0 && (isQuote(pre) || isSpace(code[pre]))){
				pre--;
			}
			if(code[offset] == ';'){
				if([';','}'].indexOf(code[pre]) == -1){
					re += ';';
				}
				continue;
			}
			if(re.length > 0 && code[offset] == '\n' && notIn() && re[re.length - 1] !== ';' && code[next] !== '{' && code[pre] !== '}' && code[pre] !== ';')
				re += ';';
			if(!(offset > 0 && re[re.length - 1] == ' ' && isSpace(code[offset]))){
				if(isSpace(code[offset])){
					if(re.length > 0 && needSpace(re[re.length - 1]))
						re += ' ';
				}else {
					if(!needSpace(code[offset]) && re[re.length - 1] == ' ')
						re = re.substr(0, re.length - 1);
					re += code[offset];
				}
			}

			var as = code[next];
			var second = next + 1;
			while(second < code.length - 1&& isQuote(second)){
				second++;
			}
			as += code[second];
			if(!isAlphabets(code[second + 1]))
				as += ' ';
			if(as == 'as ' && re[re.length - 1] !== ' ')
				re += ' ';
			if(re.length > 0 && code[offset] == ')' && (isAlphabets(code[next]) || code[next] == '}') && as !== 'as ')
				re += ';';
		}
	}
	return re.trim();
}
module.exports  = spaces;<commit_msg>add "." to need space<commit_after>/**
 *    _____ __
 *   / ___// /_  _____
 *   \__ \/ / / / / _ \
 *  ___/ / / /_/ /  __/
 * /____/_/\__, /\___/
 *       /____/
 *       Copyright 2017 Slye Development Team. All Rights Reserved.
 *       Licence: MIT License
 */

const quotations    = require('./quotations');

/**
 * Remove all spaces from source code and put all semicolons
 * @param code
 */
function spaces(code){
	var isQuote     = quotations(code).inQuote;
	var re          = '';
	var isSpace     = letter => [' ', '\t', '\n', '\r'].indexOf(letter) > -1;
	var needSpace   = letter => [
		';','{','}','(',')','=', '[', ']',
		'+', '-', '*', '/', '^',
		'?', ':', ',','.',
		'!', '%', '&', '<', '>', '|'
	].indexOf(letter) == -1;
	var isAlphabets = letter => letter >= 'A' && letter <= 'z';
	var oc  = {
		'(': 0,
		')': 0,
		'[': 0,
		']': 0,
		'{': 0,
		'}': 0
	};
	var keys    = Object.keys(oc);
	var notIn       = () => {
		return (oc['('] == oc[')']) && (oc['['] == oc[']']) && (oc['{'] == oc['}']) ;
	};
	for(var offset = 0; offset < code.length;offset++){
		if(isQuote(offset)){
			re += code[offset];
		}else {
			if(keys.indexOf(code[offset]) > -1)
				oc[code[offset]]++;
			var next = offset + 1;
			while(next < code.length && (isQuote(next) || isSpace(code[next]))){
				next++;
			}
			var pre = offset - 1;
			while(pre > 0 && (isQuote(pre) || isSpace(code[pre]))){
				pre--;
			}
			if(code[offset] == ';'){
				if([';','}'].indexOf(code[pre]) == -1){
					re += ';';
				}
				continue;
			}
			if(re.length > 0 && code[offset] == '\n' && notIn() && re[re.length - 1] !== ';' && code[next] !== '{' && code[pre] !== '}' && code[pre] !== ';')
				re += ';';
			if(!(offset > 0 && re[re.length - 1] == ' ' && isSpace(code[offset]))){
				if(isSpace(code[offset])){
					if(re.length > 0 && needSpace(re[re.length - 1]))
						re += ' ';
				}else {
					if(!needSpace(code[offset]) && re[re.length - 1] == ' ')
						re = re.substr(0, re.length - 1);
					re += code[offset];
				}
			}

			var as = code[next];
			var second = next + 1;
			while(second < code.length - 1&& isQuote(second)){
				second++;
			}
			as += code[second];
			if(!isAlphabets(code[second + 1]))
				as += ' ';
			if(as == 'as ' && re[re.length - 1] !== ' ')
				re += ' ';
			if(re.length > 0 && code[offset] == ')' && (isAlphabets(code[next]) || code[next] == '}') && as !== 'as ')
				re += ';';
		}
	}
	return re.trim();
}
module.exports  = spaces;<|endoftext|>