<commit_before>/*
 * Ideal Image Slider v1.4.0
 *
 * By Gilbert Pellegrom
 * http://gilbert.pellegrom.me
 *
 * Copyright (C) 2014 Dev7studios
 * https://raw.githubusercontent.com/gilbitron/Ideal-Image-Slider/master/LICENSE
 */

var IdealImageSlider = (function() {
	"use strict";

	/*
	 * Helper functions
	 */
	var _deepExtend = function(out) {
		out = out || {};
		for(var i = 1; i < arguments.length; i++){
			var obj = arguments[i];
			if(!obj)
				continue;
			for(var key in obj){
				if(obj.hasOwnProperty(key)){
					if(typeof obj[key] === 'object')
						deepExtend(out[key], obj[key]);
					else
						out[key] = obj[key];
				}
			}
		}
		return out;
	};

	var _hasClass = function(el, className) {
		if(!className) return false;
		if(el.classList){
			return el.classList.contains(className);
		} else {
			return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
		}
	};

	var _addClass = function(el, className) {
		if(!className) return;
		if(el.classList){
			el.classList.add(className);
		} else {
			el.className += ' ' + className;
		}
	};

	var _removeClass = function(el, className) {
		if(!className) return;
		if(el.classList){
			el.classList.remove(className);
		} else {
			el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
		}
	};

	var _toArray = function(obj) {
		return Array.prototype.slice.call(obj);
	};

	var _arrayRemove = function(array, from, to) {
		var rest = array.slice((to || from) + 1 || array.length);
		array.length = from < 0 ? array.length + from : from;
		return array.push.apply(array, rest);
	};

	var _loadImg = function(slide, callback) {
		if(!slide.style.backgroundImage){
			var img = new Image();
			img.setAttribute('src', slide.getAttribute('data-src'));
			img.onload = function() {
				slide.style.backgroundImage = 'url('+ slide.getAttribute('data-src') +')';
				if(typeof(callback) === 'function') callback(this);
			};
		}
	};

	var _isHighDPI = function() {
	    var mediaQuery = "(-webkit-min-device-pixel-ratio: 1.5),(min--moz-device-pixel-ratio: 1.5),(-o-min-device-pixel-ratio: 3/2),(min-resolution: 1.5dppx)";
	    if (window.devicePixelRatio > 1)
	        return true;
	    if (window.matchMedia && window.matchMedia(mediaQuery).matches)
	        return true;
	    return false;
	};

	var _translate = function(slide, dist, speed) {
		slide.style.webkitTransitionDuration =
		slide.style.MozTransitionDuration =
		slide.style.msTransitionDuration =
		slide.style.OTransitionDuration =
		slide.style.transitionDuration = speed + 'ms';

		slide.style.webkitTransform =
		slide.style.MozTransform =
		slide.style.msTransform =
		slide.style.OTransform = 'translateX('+ dist +'px)';
	};

	var _unTranslate = function(slide) {
		slide.style.removeProperty('-webkit-transition-duration');
		slide.style.removeProperty('transition-duration');

		slide.style.removeProperty('-webkit-transform');
		slide.style.removeProperty('-ms-transform');
		slide.style.removeProperty('transform');
	};

	var _touch = {
		vars: {
			start: {},
			delta: {},
			isScrolling: undefined,
			direction: null
		},
		start: function(event) {
			if(_hasClass(this._attributes.container, this.settings.classes.animating)) return;

			var touches = event.touches[0];
			_touch.vars.start = {
				x: touches.pageX,
				y: touches.pageY,
				time: +new Date()
			};
			_touch.vars.delta = {};
			_touch.vars.isScrolling = undefined;
			_touch.vars.direction = null;

			this.stop(); // Stop slider

			this.settings.beforeChange.apply(this);
			_addClass(this._attributes.container, this.settings.classes.touching);
		},
		move: function(event) {
			if(_hasClass(this._attributes.container, this.settings.classes.animating)) return;
			// Ensure swiping with one touch and not pinching
			if(event.touches.length > 1 || event.scale && event.scale !== 1) return;

			var touches = event.touches[0];
			_touch.vars.delta = {
				x: touches.pageX - _touch.vars.start.x,
				y: touches.pageY - _touch.vars.start.y
			};

			if(typeof _touch.vars.isScrolling == 'undefined'){
				_touch.vars.isScrolling = !!(_touch.vars.isScrolling || Math.abs(_touch.vars.delta.x) < Math.abs(_touch.vars.delta.y));
			}

			// If user is not trying to scroll vertically
			if (!_touch.vars.isScrolling) {
				event.preventDefault(); // Prevent native scrolling

				_translate(this._attributes.previousSlide, _touch.vars.delta.x - this._attributes.previousSlide.offsetWidth, 0);
				_translate(this._attributes.currentSlide, _touch.vars.delta.x, 0);
				_translate(this._attributes.nextSlide, _touch.vars.delta.x + this._attributes.currentSlide.offsetWidth, 0);
			}
		},
		end: function(event) {
			if(_hasClass(this._attributes.container, this.settings.classes.animating)) return;

			var duration = +new Date() - _touch.vars.start.time;

			// Determine if slide attempt triggers next/prev slide
			var isChangeSlide = Number(duration) < 250 && Math.abs(_touch.vars.delta.x) > 20 || Math.abs(_touch.vars.delta.x) > this._attributes.currentSlide.offsetWidth/2;

			var direction = _touch.vars.delta.x < 0 ? 'next' : 'previous';
			var speed = this.settings.transitionDuration ? this.settings.transitionDuration/2 : 0;

			// If not scrolling vertically
			if(!_touch.vars.isScrolling){
				if(isChangeSlide){
					_touch.vars.direction = direction;

					if(_touch.vars.direction == 'next'){
						_translate(this._attributes.currentSlide, -this._attributes.currentSlide.offsetWidth, speed);
						_translate(this._attributes.nextSlide, 0, speed);
					} else {
						_translate(this._attributes.previousSlide, 0, speed);
						_translate(this._attributes.currentSlide, this._attributes.currentSlide.offsetWidth, speed);
					}

					setTimeout(_touch.transitionEnd.bind(this), speed);
				} else {
					// Slides return to original position
					if(direction == 'next'){
						_translate(this._attributes.currentSlide, 0, speed);
						_translate(this._attributes.nextSlide, this._attributes.currentSlide.offsetWidth, speed);
					} else {
						_translate(this._attributes.previousSlide, -this._attributes.previousSlide.offsetWidth, speed);
						_translate(this._attributes.currentSlide, 0, speed);
					}
				}

				if(speed){
					_addClass(this._attributes.container, this.settings.classes.animating);
					setTimeout(function(){
						_removeClass(this._attributes.container, this.settings.classes.animating);
					}.bind(this), speed);
				}
			}
		},
		transitionEnd: function(event) {
			if(_touch.vars.direction){
				_unTranslate(this._attributes.previousSlide);
				_unTranslate(this._attributes.currentSlide);
				_unTranslate(this._attributes.nextSlide);
				_removeClass(this._attributes.container, this.settings.classes.touching);

				_removeClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
				_removeClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
				_removeClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
				this._attributes.currentSlide.setAttribute('aria-hidden', 'true');

				var slides = this._attributes.slides,
					index = slides.indexOf(this._attributes.currentSlide);

				if(_touch.vars.direction == 'next'){
					this._attributes.previousSlide = this._attributes.currentSlide;
					this._attributes.currentSlide = slides[index+1];
					this._attributes.nextSlide = slides[index+2];
					if(typeof this._attributes.currentSlide === 'undefined' &&
					typeof this._attributes.nextSlide === 'undefined'){
						this._attributes.currentSlide = slides[0];
						this._attributes.nextSlide = slides[1];
					} else {
						if(typeof this._attributes.nextSlide === 'undefined'){
							this._attributes.nextSlide = slides[0];
						}
					}

					_loadImg(this._attributes.nextSlide);
				} else {
					this._attributes.nextSlide = this._attributes.currentSlide;
					this._attributes.previousSlide = slides[index-2];
					this._attributes.currentSlide = slides[index-1];
					if(typeof this._attributes.currentSlide === 'undefined' &&
					typeof this._attributes.previousSlide === 'undefined'){
						this._attributes.currentSlide = slides[slides.length-1];
						this._attributes.previousSlide = slides[slides.length-2];
					} else {
						if(typeof this._attributes.previousSlide === 'undefined'){
							this._attributes.previousSlide = slides[slides.length-1];
						}
					}

					_loadImg(this._attributes.previousSlide);
				}

				_addClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
				_addClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
				_addClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
				this._attributes.currentSlide.setAttribute('aria-hidden', 'false');

				this.settings.afterChange.apply(this);
			}
		}
	};

	/*
	 * Slider class
	 */
	var Slider = function(args) {
		// Defaults
		this.settings = {
			selector: '',
			height: 400, // Required but can be set by CSS
			interval: 4000,
			transitionDuration: 700,
			effect: 'slide',
			disableNav: false,
			keyboardNav: true,
			previousNavSelector: '',
			nextNavSelector: '',
			classes: {
				container: 'ideal-image-slider',
				slide: 'iis-slide',
				previousSlide: 'iis-previous-slide',
				currentSlide: 'iis-current-slide',
				nextSlide: 'iis-next-slide',
				previousNav: 'iis-previous-nav',
				nextNav: 'iis-next-nav',
				animating: 'iis-is-animating',
				touchEnabled: 'iis-touch-enabled',
				touching: 'iis-is-touching',
				directionPrevious: 'iis-direction-previous',
				directionNext: 'iis-direction-next'
			},
			onInit: function(){},
			onStart: function(){},
			onStop: function(){},
			onDestroy: function(){},
			beforeChange: function(){},
			afterChange: function(){}
		};

		// Parse args
		if(typeof args === 'string'){
			this.settings.selector = args;
		}
		else if(typeof args === 'object'){
			_deepExtend(this.settings, args);
		}

		// Slider (container) element
		var sliderEl = document.querySelector(this.settings.selector);
		if(!sliderEl) return null;

		// Slides
		var origChildren = _toArray(sliderEl.children),
			validSlides = [];
		sliderEl.innerHTML = '';
		Array.prototype.forEach.call(origChildren, function(slide, i){
			if(slide instanceof HTMLImageElement || slide instanceof HTMLAnchorElement){
				var slideEl = document.createElement('a'),
					href = '',
					target = '';

				if(slide instanceof HTMLAnchorElement){
					href = slide.getAttribute('href');
					target = slide.getAttribute('target');

					var img = slide.querySelector('img');
					if(img !== null){
						slide = img;
					} else {
						return;
					}
				}

				if(typeof slide.dataset !== 'undefined'){
					_deepExtend(slideEl.dataset, slide.dataset);
					if(slide.dataset.src){
						// Use data-src for on-demand loading
						slideEl.dataset.src = slide.dataset.src;
					} else {
						slideEl.dataset.src = slide.src;
					}

					// HiDPI support
					if(_isHighDPI() && slide.dataset['src-2x']){
						slideEl.dataset.src = slide.dataset['src-2x'];
					}
				} else {
					// IE
					if(slide.getAttribute('data-src')){
						slideEl.setAttribute('data-src', slide.getAttribute('data-src'));
					} else {
						slideEl.setAttribute('data-src', slide.getAttribute('src'));
					}
				}

				if(href) slideEl.setAttribute('href', href);
				if(target) slideEl.setAttribute('target', target);
				if(slide.getAttribute('className')) _addClass(slideEl, slide.getAttribute('className'));
				if(slide.getAttribute('id')) slideEl.setAttribute('id', slide.getAttribute('id'));
				if(slide.getAttribute('title')) slideEl.setAttribute('title', slide.getAttribute('title'));
				if(slide.getAttribute('alt')) slideEl.innerHTML = slide.getAttribute('alt');
				slideEl.setAttribute('role', 'tabpanel');
				slideEl.setAttribute('aria-hidden', 'true');

				slideEl.style.cssText += '-webkit-transition-duration:'+ this.settings.transitionDuration +'ms;-moz-transition-duration:'+ this.settings.transitionDuration +'ms;-o-transition-duration:'+ this.settings.transitionDuration +'ms;transition-duration:'+ this.settings.transitionDuration +'ms;';

				sliderEl.appendChild(slideEl);
				validSlides.push(slideEl);
			}
		}.bind(this));
		var slides = validSlides;
		if(slides.length <= 1){
			sliderEl.innerHTML = '';
			Array.prototype.forEach.call(origChildren, function(child, i){
				sliderEl.appendChild(child);
			});
			return null;
		}

		// Create navigation
		if(!this.settings.disableNav){
			var previousNav, nextNav;
			if(this.settings.previousNavSelector){
				previousNav = document.querySelector(this.settings.previousNavSelector);
			} else {
				previousNav = document.createElement('a');
				sliderEl.appendChild(previousNav);
			}
			if(this.settings.nextNavSelector){
				nextNav = document.querySelector(this.settings.nextNavSelector);
			} else {
				nextNav = document.createElement('a');
				sliderEl.appendChild(nextNav);
			}

			_addClass(previousNav, this.settings.classes.previousNav);
			_addClass(nextNav, this.settings.classes.nextNav);
			previousNav.addEventListener('click', function(){
				if(_hasClass(this._attributes.container, this.settings.classes.animating)) return false;
				this.stop();
				this.previousSlide();
			}.bind(this));
			nextNav.addEventListener('click', function(){
				if(_hasClass(this._attributes.container, this.settings.classes.animating)) return false;
				this.stop();
				this.nextSlide();
			}.bind(this));

			// Touch Navigation
			if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch){
				this.settings.effect = 'slide';
				previousNav.style.display = 'none';
				nextNav.style.display = 'none';
				_addClass(sliderEl, this.settings.classes.touchEnabled);

				sliderEl.addEventListener('touchstart', _touch.start.bind(this), false);
				sliderEl.addEventListener('touchmove', _touch.move.bind(this), false);
				sliderEl.addEventListener('touchend', _touch.end.bind(this), false);
			}

			// Keyboard Navigation
			if(this.settings.keyboardNav){
				document.addEventListener('keyup', function(e){
					e = e || window.event;
					var button = (typeof e.which == 'number') ? e.which : e.keyCode;
					if (button == 37) {
						if(_hasClass(this._attributes.container, this.settings.classes.animating)) return false;
						this.stop();
						this.previousSlide();
					} else if (button == 39) {
						if(_hasClass(this._attributes.container, this.settings.classes.animating)) return false;
						this.stop();
						this.nextSlide();
					}
				}.bind(this));
			}
		}

		// Create internal attributes
		this._attributes = {
			container: sliderEl,
			slides: slides,
			previousSlide: typeof slides[slides.length-1] !== 'undefined' ? slides[slides.length-1] : slides[0],
			currentSlide: slides[0],
			nextSlide: typeof slides[1] !== 'undefined' ? slides[1] : slides[0],
			timerId: 0,
			origChildren: origChildren // Used in destroy()
		};

		// Set height
		if(this.settings.height){
			this._attributes.container.style.height = this.settings.height +'px';
		}

		// Add classes
		_addClass(sliderEl, this.settings.classes.container);
		_addClass(sliderEl, 'iis-effect-'+ this.settings.effect);
		Array.prototype.forEach.call(this._attributes.slides, function(slide, i){
			_addClass(slide, this.settings.classes.slide);
		}.bind(this));
		_addClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
		_addClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
		_addClass(this._attributes.nextSlide, this.settings.classes.nextSlide);

		// ARIA
		this._attributes.currentSlide.setAttribute('aria-hidden', 'false');

		// Load first image
		_loadImg(this._attributes.currentSlide, (function(){
			this.settings.onInit.apply(this);
		}).bind(this));
		// Preload next images
		_loadImg(this._attributes.previousSlide);
		_loadImg(this._attributes.nextSlide);
	};

	Slider.prototype.get = function(attribute) {
		if(!this._attributes) return null;
		if(this._attributes.hasOwnProperty(attribute)){
			return this._attributes[attribute];
		}
	};

	Slider.prototype.set = function(attribute, value) {
		if(!this._attributes) return null;
		return (this._attributes[attribute] = value);
	};

	Slider.prototype.start = function() {
		if(!this._attributes) return;
		this._attributes.timerId = setInterval(this.nextSlide.bind(this), this.settings.interval);
		this.settings.onStart.apply(this);

		// Stop if window blur
		window.onblur = function(){
			this.stop();
		}.bind(this);
	};

	Slider.prototype.stop = function() {
		if(!this._attributes) return;
		clearInterval(this._attributes.timerId);
		this._attributes.timerId = 0;
		this.settings.onStop.apply(this);
	};

	Slider.prototype.previousSlide = function() {
		this.settings.beforeChange.apply(this);
		_removeClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
		_removeClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
		_removeClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
		this._attributes.currentSlide.setAttribute('aria-hidden', 'true');

		var slides = this._attributes.slides,
			index = slides.indexOf(this._attributes.currentSlide);
		this._attributes.nextSlide = this._attributes.currentSlide;
		this._attributes.previousSlide = slides[index-2];
		this._attributes.currentSlide = slides[index-1];
		if(typeof this._attributes.currentSlide === 'undefined' &&
		typeof this._attributes.previousSlide === 'undefined'){
			this._attributes.currentSlide = slides[slides.length-1];
			this._attributes.previousSlide = slides[slides.length-2];
		} else {
			if(typeof this._attributes.previousSlide === 'undefined'){
				this._attributes.previousSlide = slides[slides.length-1];
			}
		}

		// Preload next image
		_loadImg(this._attributes.previousSlide);

		_addClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
		_addClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
		_addClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
		this._attributes.currentSlide.setAttribute('aria-hidden', 'false');

		_addClass(this._attributes.container, this.settings.classes.directionPrevious);
		setTimeout(function(){
			_removeClass(this._attributes.container, this.settings.classes.directionPrevious);
		}.bind(this), this.settings.transitionDuration);

		if(this.settings.transitionDuration){
			_addClass(this._attributes.container, this.settings.classes.animating);
			setTimeout(function(){
				_removeClass(this._attributes.container, this.settings.classes.animating);
			}.bind(this), this.settings.transitionDuration);
		}
		this.settings.afterChange.apply(this);
	};

	Slider.prototype.nextSlide = function() {
		this.settings.beforeChange.apply(this);
		_removeClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
		_removeClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
		_removeClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
		this._attributes.currentSlide.setAttribute('aria-hidden', 'true');

		var slides = this._attributes.slides,
			index = slides.indexOf(this._attributes.currentSlide);
		this._attributes.previousSlide = this._attributes.currentSlide;
		this._attributes.currentSlide = slides[index+1];
		this._attributes.nextSlide = slides[index+2];
		if(typeof this._attributes.currentSlide === 'undefined' &&
		   typeof this._attributes.nextSlide === 'undefined'){
			this._attributes.currentSlide = slides[0];
			this._attributes.nextSlide = slides[1];
		} else {
			if(typeof this._attributes.nextSlide === 'undefined'){
				this._attributes.nextSlide = slides[0];
			}
		}

		// Preload next image
		_loadImg(this._attributes.nextSlide);

		_addClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
		_addClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
		_addClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
		this._attributes.currentSlide.setAttribute('aria-hidden', 'false');

		_addClass(this._attributes.container, this.settings.classes.directionNext);
		setTimeout(function(){
			_removeClass(this._attributes.container, this.settings.classes.directionNext);
		}.bind(this), this.settings.transitionDuration);

		if(this.settings.transitionDuration){
			_addClass(this._attributes.container, this.settings.classes.animating);
			setTimeout(function(){
				_removeClass(this._attributes.container, this.settings.classes.animating);
			}.bind(this), this.settings.transitionDuration);
		}
		this.settings.afterChange.apply(this);
	};

	Slider.prototype.gotoSlide = function(index) {
		this.settings.beforeChange.apply(this);
		this.stop();

		_removeClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
		_removeClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
		_removeClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
		this._attributes.currentSlide.setAttribute('aria-hidden', 'true');

		index--; // Index should be 1-indexed
		var slides = this._attributes.slides,
			oldIndex = slides.indexOf(this._attributes.currentSlide);
		this._attributes.previousSlide = slides[index-1];
		this._attributes.currentSlide = slides[index];
		this._attributes.nextSlide = slides[index+1];
		if(typeof this._attributes.previousSlide === 'undefined'){
			this._attributes.previousSlide = slides[slides.length-1];
		}
		if(typeof this._attributes.nextSlide === 'undefined'){
			this._attributes.nextSlide = slides[0];
		}

		// Load images
		_loadImg(this._attributes.previousSlide);
		_loadImg(this._attributes.currentSlide);
		_loadImg(this._attributes.nextSlide);

		_addClass(this._attributes.previousSlide, this.settings.classes.previousSlide);
		_addClass(this._attributes.currentSlide, this.settings.classes.currentSlide);
		_addClass(this._attributes.nextSlide, this.settings.classes.nextSlide);
		this._attributes.currentSlide.setAttribute('aria-hidden', 'false');

		if(index < oldIndex){
			_addClass(this._attributes.container, this.settings.classes.directionPrevious);
			setTimeout(function(){
				_removeClass(this._attributes.container, this.settings.classes.directionPrevious);
			}.bind(this), this.settings.transitionDuration);
		} else {
			_addClass(this._attributes.container, this.settings.classes.directionNext);
			setTimeout(function(){
				_removeClass(this._attributes.container, this.settings.classes.directionNext);
			}.bind(this), this.settings.transitionDuration);
		}

		if(this.settings.transitionDuration){
			_addClass(this._attributes.container, this.settings.classes.animating);
			setTimeout(function(){
				_removeClass(this._attributes.container, this.settings.classes.animating);
			}.bind(this), this.settings.transitionDuration);
		}
		this.settings.afterChange.apply(this);
	};

	Slider.prototype.destroy = function() {
		clearInterval(this._attributes.timerId);
		this._attributes.timerId = 0;

		this._attributes.container.innerHTML = '';
		Array.prototype.forEach.call(this._attributes.origChildren, function(child, i){
			this._attributes.container.appendChild(child);
		}.bind(this));

		_removeClass(this._attributes.container, this.settings.classes.container);
		_removeClass(this._attributes.container, 'iis-effect-'+ this.settings.effect);
		this._attributes.container.style.height = '';

		this.settings.onDestroy.apply(this);
	};

	return {
		_hasClass: _hasClass,
		_addClass: _addClass,
		_removeClass: _removeClass,
		Slider: Slider
	};

})();
<commit_msg>Delete ideal-image-slider.js<commit_after><|endoftext|>


<commit_before>function bytesToSize(bytes) {
   var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
   if (bytes == 0) return '0 B';
   var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
   return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}

var Torrent = function(data) {
    this.Name = data.Name;
    this.State = data.State;
    this.Infohash = data.Infohash;
    this.Peers = function() { return data.Peers ? data.Peers.length : 0; };
    this.Speed = function() {
        var tx = 0, rx = 0;
        if (data.Peers)
            data.Peers.forEach(function(p){tx += p.TX; rx += p.RX;});
        return "↑ " + bytesToSize(tx) +"/s ↓ " + bytesToSize(rx) + "/s";
    };
    this.RX = function() {
      var rx = 0;
      if(data.Peers) data.peers.forEach(function(p) { rx += p.RX; });
      return rx;
    }
    this.TX = function() {
      var tx = 0;
      if(data.Peers) data.peers.forEach(function(p) { tx += p.TX; });
      return tx;
    }
    this.TotalSize = function() {
        var total_size = 0;
        data.Files.forEach(function(f){ total_size += f.FileInfo.Length });
        return bytesToSize(total_size);
    };
    this.Progress = data.Progress * 100;
    this.remove = function() {
        viewModel._apicall({method: "XD.ChangeTorrent", action: "delete", infohash: this.Infohash, swarm: "0"}, function(data){
            console.log(data);
        });
    }.bind(this);
}

var viewModel = {
    _url: window.location.protocol + "//" + window.location.host + "/ecksdee/api",
    _apicall: function(call, cb)
    {
        $.ajax({
            type: "POST",
            url: this._url,
            contentType: "text/json; charset=UTF-8",
            data: JSON.stringify(call),
            success: function(j, text, xhr) {
                // console.log(call, j);
                cb(JSON.parse(j));
            }
        });
    },
    torrents: ko.observableArray(),
    torrentURL: ko.observable(),
    torrentFilter: ko.observable('all').bind(this),
    setFilter: function(state) {
        this.torrentFilter(state); main(); },
    addTorrent: function()
    {
        var _this = this;
        this._apicall({method: "XD.AddTorrent", swarm: "0", url: this.torrentURL()}, function(data){
            if (!data.error) _this.torrentURL("");
        });
    },
    torrentStates: ['all', 'downloading', 'seeding'],
    globalSpeed: function()
    {
      var rx = 0;
      var tx = 0;
      if(this.torrents)
      {
        for (var idx in this.torrents)
        {
          var t = this.torrents[idx];
          if (t && t.RX && t.TX)
          {
            rx += t.RX();
            tx += t.TX();
          }
        }
      }
      return  "Global: ↑ " + bytesToSize(tx) +"/s ↓ " + bytesToSize(rx) + "/s";
    }
};

function main()
{
    viewModel._apicall({method: "XD.SwarmStatus"}, function(data){
        viewModel.torrents.removeAll();
        for (var prop in data) {
            if (viewModel.torrentFilter() != 'all' & viewModel.torrentFilter() != data[prop].State)
                continue;
            viewModel.torrents.push(new Torrent(data[prop]));
        }
    });
}

window.onload = function()
{
    ko.applyBindings(viewModel);
    main(); setInterval(main, 5000);
}
<commit_msg>fix previous commit<commit_after>function bytesToSize(bytes) {
   var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
   if (bytes == 0) return '0 B';
   var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
   return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}

var Torrent = function(data) {
    this.Name = data.Name;
    this.State = data.State;
    this.Infohash = data.Infohash;
    this.Peers = function() { return data.Peers ? data.Peers.length : 0; };
    this.Speed = function() {
        var tx = 0, rx = 0;
        if (data.Peers)
            data.Peers.forEach(function(p){tx += p.TX; rx += p.RX;});
        return "↑ " + bytesToSize(tx) +"/s ↓ " + bytesToSize(rx) + "/s";
    };
    this.RX = function() {
      var rx = 0;
      if(data.Peers) data.Peers.forEach(function(p) { rx += p.RX; });
      return rx;
    }
    this.TX = function() {
      var tx = 0;
      if(data.Peers) data.Peers.forEach(function(p) { tx += p.TX; });
      return tx;
    }
    this.TotalSize = function() {
        var total_size = 0;
        data.Files.forEach(function(f){ total_size += f.FileInfo.Length });
        return bytesToSize(total_size);
    };
    this.Progress = data.Progress * 100;
    this.remove = function() {
        viewModel._apicall({method: "XD.ChangeTorrent", action: "delete", infohash: this.Infohash, swarm: "0"}, function(data){
            console.log(data);
        });
    }.bind(this);
}

var viewModel = {
    _url: window.location.protocol + "//" + window.location.host + "/ecksdee/api",
    _apicall: function(call, cb)
    {
        $.ajax({
            type: "POST",
            url: this._url,
            contentType: "text/json; charset=UTF-8",
            data: JSON.stringify(call),
            success: function(j, text, xhr) {
                // console.log(call, j);
                cb(JSON.parse(j));
            }
        });
    },
    torrents: ko.observableArray(),
    torrentURL: ko.observable(),
    torrentFilter: ko.observable('all').bind(this),
    setFilter: function(state) {
        this.torrentFilter(state); main(); },
    addTorrent: function()
    {
        var _this = this;
        this._apicall({method: "XD.AddTorrent", swarm: "0", url: this.torrentURL()}, function(data){
            if (!data.error) _this.torrentURL("");
        });
    },
    torrentStates: ['all', 'downloading', 'seeding'],
    globalSpeed: function()
    {
      var rx = 0;
      var tx = 0;
      this.torrents().forEach(function(t) {
        rx += t.RX();
        tx += t.TX();
      });
      return  "Global: ↑ " + bytesToSize(tx) +"/s ↓ " + bytesToSize(rx) + "/s";
    }
};

function main()
{
    viewModel._apicall({method: "XD.SwarmStatus"}, function(data){
        viewModel.torrents.removeAll();
        for (var prop in data) {
            if (viewModel.torrentFilter() != 'all' & viewModel.torrentFilter() != data[prop].State)
                continue;
            viewModel.torrents.push(new Torrent(data[prop]));
        }
    });
}

window.onload = function()
{
    ko.applyBindings(viewModel);
    main(); setInterval(main, 5000);
}
<|endoftext|>


<commit_before>//loop branch
// window.Popcorn.version = 1.5.6
// http://cdn.popcornjs.org/code/dist/popcorn.min.js
// window.SC._version = 2.0.0
// http://connect.soundcloud.com/sdk-2.0.0.js

(function(window, document, callback) { // http://stackoverflow.com/questions/2170439/how-to-embed-javascript-widget-that-depends-on-jquery-into-an-unknown-environmen
    var loaded_p = true;    // default is to not load popcornjs
    var loaded_s = true;    // default is to not load soundcloud

    // document.head not standard before HTML5
    var insertionPoint = document.head || document.getElementsByTagName('head').item(0) || document.documentElement.childNodes[0];

    function cmp_vers(v1, v2) {
        var a1 = v1.split('.');
        var a2 = v2.split('.');
        for(var i = 0; i < Math.min(a1.length, a2.length); i++) {
            var n1 = parseInt(a1[i]);
            var n2 = parseInt(a2[i]);
            var d = n1 - n2;
            if(d) {
                return d;
            }
        }
        return(a1.length - a2.length);
    }

    function load_popcorn(version, cb) {
        var js = window.Popcorn, d, new_js;

        if(loaded_p) {
            cb(js);
        } else if(!js || cmp_vers(version, js.version) > 0 || cb(js)) {
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = "https://cdn.knightlab.com/libs/soundcite/latest/vendor/popcorn.min.js"; // no SSL version of this, will self-host
            script.onload = script.onreadystatechange = function() {
                if(!loaded_p && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
                    new_js = window.Popcorn;
                    if(js) {
                        window.Popcorn = js;
                    }
                    cb(new_js, loaded_p = true);
                    insertionPoint.removeChild(script);
                }
            };
            insertionPoint.appendChild(script);
        }
    }

    // Loading player api initializes incomplete version of window.SC
    function load_soundcloud(version, cb) {
        var js = window.SC, d;

        if(loaded_s) {
            cb(js);
        } else if(!js || !js.Dialog || cmp_vers(version, js._version) > 0 || cb(js)) {
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = "//connect.soundcloud.com/sdk-2.0.0.js";
            script.onload = script.onreadystatechange = function() {
                if(!loaded_s && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
                    cb(window.SC, loaded_s = true);
                    insertionPoint.removeChild(script);
                }
            };
            insertionPoint.appendChild(script);
        }
    }

    // http://dustindiaz.com/smallest-domready-ever
    function r(f) {
        if(/in/.test(document.readyState)) {
            setTimeout(function() { r(f); }, 9);
        } else {
            f();
        }
    }

    r(function() {
        var elements = document.getElementsByClassName('soundcite');

        for(var i = 0; i < elements.length; i++) {
            if(elements[i].getAttribute('data-url')) {
                loaded_p = false;   // needs popcorn
            } else {
                loaded_s = false;   // needs soundcloud
            }
        }

        load_popcorn("1.5.6", function(p) {
            load_soundcloud("2.0.0", function(s) {
               callback(elements, p, s);
            });
        });
    });

})(window, document, function(soundcite_elements, $Popcorn, $SoundCloud) {

    // borrowing underscore.js bind function
    var bind = function(func, context) {
        var slice = Array.prototype.slice;
        var args = slice.call(arguments, 2);
        return function() {
          return func.apply(context, args.concat(slice.call(arguments)));
        };
    };

    function removeClass(el, name) {
        var cn = el.className;
        for(var i = 0, arr = name.match(/\S+/g); i < arr.length; i++) {
            cn = cn.replace(new RegExp('(?:^|\\s)'+arr[i]+'(?!\\S)'), '');
        }
        el.className = cn;
    }

    function addClass(el, name) {
        var cn = el.className;
        for(var i = 0, arr = name.match(/\S+/g); i < arr.length; i++) {
            if(!cn.match(new RegExp('(?:^|\\s)'+arr[i]+'(?!\\S)'))) {
                cn += " "+arr[i];
            }
        }
        el.className = cn;
    }

    var SOUNDCITE_CONFIG = {
        update_playing_element: function(el, percentage) {
             el.style.cssText =
                'background: -webkit-linear-gradient(left, rgba(0,0,0,.15)' + percentage + '%, rgba(0,0,0,.05)' + (percentage + 1) + '%);'
              + 'background: linear-gradient(to right, rgba(0,0,0,.15)' + percentage + '%, rgba(0,0,0,.05)' + (percentage + 1) + '%);'
        }
    }
    for(var key in window.SOUNDCITE_CONFIG) {
        SOUNDCITE_CONFIG[key] = window.SOUNDCITE_CONFIG[key];
    }

    // global vars
    window.soundcite = {};

    // check for mobile
    if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
        soundcite.mobile = true;
    } else {
        soundcite.mobile = false;
    }

    var clips = [];

    // setup audio container
    var audio_container = document.createElement('div');
    addClass(audio_container, 'soundcite-audio');
    document.getElementsByTagName('body')[0].appendChild(audio_container);

    // initialize SoundCloud SDK
    if($SoundCloud) {
        $SoundCloud.initialize({
            client_id: "5f016c08c2201881c4217afd5f52e065"
        });
    }

    // pause all playing clips. Optionally pass a clip as an argument which
    // should not be paused, for the case that this is triggered from a
    // click handler.
    function pause_all_clips(except_clip){
        for(var i = 0; i < clips.length; i++) {
            if(clips[i].playing) {
                if (!except_clip || except_clip.el !== clips[i].el) {
                    clips[i].pause();
                }
            }
        }
    }

// Clip
    function Clip(el) {
        this.el = el;
        this.start = el.hasAttribute('data-start') ? el.getAttribute('data-start') : 0; // ms
        this.end = el.hasAttribute('data-end') ? el.getAttribute('data-end') : null;    // ms

        this.plays = el.hasAttribute('data-plays') ? parseInt(el.getAttribute('data-plays')) : 1;
        this.plays_left = this.plays;

        this.playing = false;
        this.sound = null;                          // implement in subclass
        clips.push(this);   // keep track of this
    }

    Clip.prototype.sound_loaded = function() {
        this.el.addEventListener('click', bind(this.click_handler, this));
        addClass(this.el, 'soundcite-loaded soundcite-play');
    }

    Clip.prototype.pause = function() {
        removeClass(this.el, 'soundcite-pause');
        addClass(this.el, 'soundcite-play');
        this.pause_sound();                         // implement in subclass
        this.playing = false;
    }

    Clip.prototype.stop = function() {
        removeClass(this.el, 'soundcite-pause');
        addClass(this.el, 'soundcite-play');
        this.stop_sound();                          // implement in subclass
        this.playing = false;
        this.plays_left = this.plays;    // reset plays_left!
    }

    Clip.prototype.track_progress = function() {
        var totalTime = this.end - this.start;
        var position = this.sound_position();       // implement in subclass
        var relative_position = position - this.start;
        var percentage = (relative_position * 100) / totalTime;
        SOUNDCITE_CONFIG.update_playing_element(this.el, percentage);
    }

    Clip.prototype.click_handler = function(event) {
        event.preventDefault(); // if used on a tag, don't follow href
        pause_all_clips(this);
        if(this.playing) {
            this.pause();
        } else {
            this.play();                            // implement in subclass
        }
    }

// SoundCloudClip
    function SoundCloudClip(el) {
        Clip.apply(this, Array.prototype.slice.call(arguments));

        this.id = el.getAttribute('data-id');

        $SoundCloud.stream(this.id, bind(function(sound) {
            this.sound = sound;

            this.sound._player.on("positionChange", bind(function(pos) {
                this.track_progress();

                if(pos >= this.end) {
                    if(this.plays) {
                        this.plays_left--;  // update plays_left
                        if(this.plays_left > 0) {
                            this.play();
                            this.track_position();
                        } else {
                            this.stop();
                        }
                    } else {                // infinite loop
                        this.play();
                        this.track_position();
                    }
                }
            }, this));

            if(this.end === null) {
                this.end = this.sound.getDuration();
            }

           this.sound_loaded();
        }, this));
    }
    SoundCloudClip.prototype = Object.create(Clip.prototype);

    SoundCloudClip.prototype.sound_position = function() {
        return this.sound.getCurrentPosition();
    }

    SoundCloudClip.prototype.pause_sound = function() {
        this.sound.pause();
    }

    SoundCloudClip.prototype.stop_sound = function() {
        this.sound.stop();
    }

    SoundCloudClip.prototype.play = function() {
        var pos = this.sound_position();

        if(pos < this.start || pos >= this.end) {
            this.sound.seek(this.start);
        }

        removeClass(this.el, 'soundcite-play');
        addClass(this.el, 'soundcite-pause');

        this.sound.play();
        this.playing = true;
    }

// Popcorn Clip
    function PopcornClip(el) {
        Clip.apply(this, Array.prototype.slice.call(arguments));

        this.id = 'soundcite-audio-'+clips.length;
        this.url = el.getAttribute('data-url');

        // convert to ms to secs
        this.start = Math.floor(this.start / 1000);
        if(this.end !== null) {
            this.end = Math.floor(this.end / 1000);
        }

        var audio = document.createElement("audio");
        audio.id = this.id;
        audio.setAttribute("src", this.url);
        audio.setAttribute("preload", "true");

        audio_container.appendChild(audio);

        this.sound = $Popcorn('#'+this.id, {'frameAnimation': true});

        // Safari iOS Audio streams cannot be loaded unless triggered by a
        // user event, so load in play_sound via click for mobile
        this.sound.on('loadeddata', bind(function() {
            if(this.end === null) {
                this.end = this.sound.duration();
            }

            this.sound.cue(this.end, bind(function() {
                if(this.plays) {
                    this.plays_left--;      // update plays_left
                    if(this.plays_left > 0) {
                        this.sound.currentTime(this.start);
                        this.play();
                    } else {
                        this.stop();
                        this.sound.currentTime(this.start);
                    }
                } else {                    // infinite loop
                    this.sound.currentTime(this.start);
                    this.play();
                }
            }, this));

            if(!soundcite.mobile) {
                this.sound_loaded();
            }
        }, this));

        if(soundcite.mobile) {
            this.sound_loaded();
        } else if(this.sound.readyState() > 1) {
            this.sound_loaded();
        }
    }
    PopcornClip.prototype = Object.create(Clip.prototype);

    PopcornClip.prototype.sound_position = function() {
        return this.sound.currentTime();
    }

    PopcornClip.prototype.pause_sound = function() {
        this.sound.pause();
        this.sound.off('timeupdate');
    }

    PopcornClip.prototype.stop_sound = function() {
        this.sound.pause();
        this.sound.off('timeupdate');
    }

    PopcornClip.prototype._play_sound = function() {
        removeClass(this.el, 'soundcite-loading soundcite-play');
        addClass(this.el, 'soundcite-pause');

        this.sound.play();
        this.playing = true;

        this.sound.on('timeupdate', bind(this.track_progress, this));
    }

    PopcornClip.prototype.play_sound = function() {
        var pos = this.sound.roundTime();

        if(pos < this.start || pos >= this.end) {
            this.sound.on('seeked', bind(function() {
                this.sound.off('seeked');
                this._play_sound();
            }, this));

            this.sound.currentTime(this.start);
        } else {
            this._play_sound();
        }
    }

    PopcornClip.prototype.play = function() {
        if(soundcite.mobile) {
            removeClass(this.el, 'soundcite-play');
            addClass(this.el, 'soundcite-loading');

            if(this.sound.readyState() > 1) {
                this.play_sound();
            } else {
                this.sound.on('canplaythrough', bind(function() {
                    this.play_sound();
                }, this));
                document.getElementById(this.id).load();
            }
        } else {
            this.play_sound();
        }
    }

// set up clips array from element array
    for(var i = 0; i < soundcite_elements.length; i++) {
        var el = soundcite_elements[i];
        if(el.getAttribute('data-url')) {
            new PopcornClip(el);
        } else if (el.getAttribute('data-id')) {
            new SoundCloudClip(el);
        } else {
            console.log('Unable to form Soundcite element because of missing attributes. The offending Soundcite was "' + el.textContent + '."');
        }
    }

    soundcite.Clip = Clip;
    soundcite.SoundCloudClip = SoundCloudClip;
    soundcite.PopcornClip = PopcornClip;
    soundcite.clips = clips;    // keep track of clips
    soundcite.pause_all_clips = pause_all_clips;
});
<commit_msg>support overriding SoundCloud SDK client_id in case of play count max<commit_after>//loop branch
// window.Popcorn.version = 1.5.6
// http://cdn.popcornjs.org/code/dist/popcorn.min.js
// window.SC._version = 2.0.0
// http://connect.soundcloud.com/sdk-2.0.0.js

(function(window, document, callback) { // http://stackoverflow.com/questions/2170439/how-to-embed-javascript-widget-that-depends-on-jquery-into-an-unknown-environmen
    var loaded_p = true;    // default is to not load popcornjs
    var loaded_s = true;    // default is to not load soundcloud

    // document.head not standard before HTML5
    var insertionPoint = document.head || document.getElementsByTagName('head').item(0) || document.documentElement.childNodes[0];

    function cmp_vers(v1, v2) {
        var a1 = v1.split('.');
        var a2 = v2.split('.');
        for(var i = 0; i < Math.min(a1.length, a2.length); i++) {
            var n1 = parseInt(a1[i]);
            var n2 = parseInt(a2[i]);
            var d = n1 - n2;
            if(d) {
                return d;
            }
        }
        return(a1.length - a2.length);
    }

    function load_popcorn(version, cb) {
        var js = window.Popcorn, d, new_js;

        if(loaded_p) {
            cb(js);
        } else if(!js || cmp_vers(version, js.version) > 0 || cb(js)) {
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = "https://cdn.knightlab.com/libs/soundcite/latest/vendor/popcorn.min.js"; // no SSL version of this, will self-host
            script.onload = script.onreadystatechange = function() {
                if(!loaded_p && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
                    new_js = window.Popcorn;
                    if(js) {
                        window.Popcorn = js;
                    }
                    cb(new_js, loaded_p = true);
                    insertionPoint.removeChild(script);
                }
            };
            insertionPoint.appendChild(script);
        }
    }

    // Loading player api initializes incomplete version of window.SC
    function load_soundcloud(version, cb) {
        var js = window.SC, d;

        if(loaded_s) {
            cb(js);
        } else if(!js || !js.Dialog || cmp_vers(version, js._version) > 0 || cb(js)) {
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = "//connect.soundcloud.com/sdk-2.0.0.js";
            script.onload = script.onreadystatechange = function() {
                if(!loaded_s && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
                    cb(window.SC, loaded_s = true);
                    insertionPoint.removeChild(script);
                }
            };
            insertionPoint.appendChild(script);
        }
    }

    // http://dustindiaz.com/smallest-domready-ever
    function r(f) {
        if(/in/.test(document.readyState)) {
            setTimeout(function() { r(f); }, 9);
        } else {
            f();
        }
    }

    r(function() {
        var elements = document.getElementsByClassName('soundcite');

        for(var i = 0; i < elements.length; i++) {
            if(elements[i].getAttribute('data-url')) {
                loaded_p = false;   // needs popcorn
            } else {
                loaded_s = false;   // needs soundcloud
            }
        }

        load_popcorn("1.5.6", function(p) {
            load_soundcloud("2.0.0", function(s) {
               callback(elements, p, s);
            });
        });
    });

})(window, document, function(soundcite_elements, $Popcorn, $SoundCloud) {

    // borrowing underscore.js bind function
    var bind = function(func, context) {
        var slice = Array.prototype.slice;
        var args = slice.call(arguments, 2);
        return function() {
          return func.apply(context, args.concat(slice.call(arguments)));
        };
    };

    function removeClass(el, name) {
        var cn = el.className;
        for(var i = 0, arr = name.match(/\S+/g); i < arr.length; i++) {
            cn = cn.replace(new RegExp('(?:^|\\s)'+arr[i]+'(?!\\S)'), '');
        }
        el.className = cn;
    }

    function addClass(el, name) {
        var cn = el.className;
        for(var i = 0, arr = name.match(/\S+/g); i < arr.length; i++) {
            if(!cn.match(new RegExp('(?:^|\\s)'+arr[i]+'(?!\\S)'))) {
                cn += " "+arr[i];
            }
        }
        el.className = cn;
    }

    var SOUNDCITE_CONFIG = {
        update_playing_element: function(el, percentage) {
             el.style.cssText =
                'background: -webkit-linear-gradient(left, rgba(0,0,0,.15)' + percentage + '%, rgba(0,0,0,.05)' + (percentage + 1) + '%);'
              + 'background: linear-gradient(to right, rgba(0,0,0,.15)' + percentage + '%, rgba(0,0,0,.05)' + (percentage + 1) + '%);'
        },
        soundcloud_client_id: "5f016c08c2201881c4217afd5f52e065"
    }
    for(var key in window.SOUNDCITE_CONFIG) {
        SOUNDCITE_CONFIG[key] = window.SOUNDCITE_CONFIG[key];
    }

    // global vars
    window.soundcite = {};

    // check for mobile
    if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
        soundcite.mobile = true;
    } else {
        soundcite.mobile = false;
    }

    var clips = [];

    // setup audio container
    var audio_container = document.createElement('div');
    addClass(audio_container, 'soundcite-audio');
    document.getElementsByTagName('body')[0].appendChild(audio_container);

    // initialize SoundCloud SDK
    if($SoundCloud) {
        $SoundCloud.initialize({
            client_id: SOUNDCITE_CONFIG['soundcloud_client_id']
        });
    }

    // pause all playing clips. Optionally pass a clip as an argument which
    // should not be paused, for the case that this is triggered from a
    // click handler.
    function pause_all_clips(except_clip){
        for(var i = 0; i < clips.length; i++) {
            if(clips[i].playing) {
                if (!except_clip || except_clip.el !== clips[i].el) {
                    clips[i].pause();
                }
            }
        }
    }

// Clip
    function Clip(el) {
        this.el = el;
        this.start = el.hasAttribute('data-start') ? el.getAttribute('data-start') : 0; // ms
        this.end = el.hasAttribute('data-end') ? el.getAttribute('data-end') : null;    // ms

        this.plays = el.hasAttribute('data-plays') ? parseInt(el.getAttribute('data-plays')) : 1;
        this.plays_left = this.plays;

        this.playing = false;
        this.sound = null;                          // implement in subclass
        clips.push(this);   // keep track of this
    }

    Clip.prototype.sound_loaded = function() {
        this.el.addEventListener('click', bind(this.click_handler, this));
        addClass(this.el, 'soundcite-loaded soundcite-play');
    }

    Clip.prototype.pause = function() {
        removeClass(this.el, 'soundcite-pause');
        addClass(this.el, 'soundcite-play');
        this.pause_sound();                         // implement in subclass
        this.playing = false;
    }

    Clip.prototype.stop = function() {
        removeClass(this.el, 'soundcite-pause');
        addClass(this.el, 'soundcite-play');
        this.stop_sound();                          // implement in subclass
        this.playing = false;
        this.plays_left = this.plays;    // reset plays_left!
    }

    Clip.prototype.track_progress = function() {
        var totalTime = this.end - this.start;
        var position = this.sound_position();       // implement in subclass
        var relative_position = position - this.start;
        var percentage = (relative_position * 100) / totalTime;
        SOUNDCITE_CONFIG.update_playing_element(this.el, percentage);
    }

    Clip.prototype.click_handler = function(event) {
        event.preventDefault(); // if used on a tag, don't follow href
        pause_all_clips(this);
        if(this.playing) {
            this.pause();
        } else {
            this.play();                            // implement in subclass
        }
    }

// SoundCloudClip
    function SoundCloudClip(el) {
        Clip.apply(this, Array.prototype.slice.call(arguments));

        this.id = el.getAttribute('data-id');

        $SoundCloud.stream(this.id, bind(function(sound) {
            this.sound = sound;

            this.sound._player.on("positionChange", bind(function(pos) {
                this.track_progress();

                if(pos >= this.end) {
                    if(this.plays) {
                        this.plays_left--;  // update plays_left
                        if(this.plays_left > 0) {
                            this.play();
                            this.track_position();
                        } else {
                            this.stop();
                        }
                    } else {                // infinite loop
                        this.play();
                        this.track_position();
                    }
                }
            }, this));

            if(this.end === null) {
                this.end = this.sound.getDuration();
            }

           this.sound_loaded();
        }, this));
    }
    SoundCloudClip.prototype = Object.create(Clip.prototype);

    SoundCloudClip.prototype.sound_position = function() {
        return this.sound.getCurrentPosition();
    }

    SoundCloudClip.prototype.pause_sound = function() {
        this.sound.pause();
    }

    SoundCloudClip.prototype.stop_sound = function() {
        this.sound.stop();
    }

    SoundCloudClip.prototype.play = function() {
        var pos = this.sound_position();

        if(pos < this.start || pos >= this.end) {
            this.sound.seek(this.start);
        }

        removeClass(this.el, 'soundcite-play');
        addClass(this.el, 'soundcite-pause');

        this.sound.play();
        this.playing = true;
    }

// Popcorn Clip
    function PopcornClip(el) {
        Clip.apply(this, Array.prototype.slice.call(arguments));

        this.id = 'soundcite-audio-'+clips.length;
        this.url = el.getAttribute('data-url');

        // convert to ms to secs
        this.start = Math.floor(this.start / 1000);
        if(this.end !== null) {
            this.end = Math.floor(this.end / 1000);
        }

        var audio = document.createElement("audio");
        audio.id = this.id;
        audio.setAttribute("src", this.url);
        audio.setAttribute("preload", "true");

        audio_container.appendChild(audio);

        this.sound = $Popcorn('#'+this.id, {'frameAnimation': true});

        // Safari iOS Audio streams cannot be loaded unless triggered by a
        // user event, so load in play_sound via click for mobile
        this.sound.on('loadeddata', bind(function() {
            if(this.end === null) {
                this.end = this.sound.duration();
            }

            this.sound.cue(this.end, bind(function() {
                if(this.plays) {
                    this.plays_left--;      // update plays_left
                    if(this.plays_left > 0) {
                        this.sound.currentTime(this.start);
                        this.play();
                    } else {
                        this.stop();
                        this.sound.currentTime(this.start);
                    }
                } else {                    // infinite loop
                    this.sound.currentTime(this.start);
                    this.play();
                }
            }, this));

            if(!soundcite.mobile) {
                this.sound_loaded();
            }
        }, this));

        if(soundcite.mobile) {
            this.sound_loaded();
        } else if(this.sound.readyState() > 1) {
            this.sound_loaded();
        }
    }
    PopcornClip.prototype = Object.create(Clip.prototype);

    PopcornClip.prototype.sound_position = function() {
        return this.sound.currentTime();
    }

    PopcornClip.prototype.pause_sound = function() {
        this.sound.pause();
        this.sound.off('timeupdate');
    }

    PopcornClip.prototype.stop_sound = function() {
        this.sound.pause();
        this.sound.off('timeupdate');
    }

    PopcornClip.prototype._play_sound = function() {
        removeClass(this.el, 'soundcite-loading soundcite-play');
        addClass(this.el, 'soundcite-pause');

        this.sound.play();
        this.playing = true;

        this.sound.on('timeupdate', bind(this.track_progress, this));
    }

    PopcornClip.prototype.play_sound = function() {
        var pos = this.sound.roundTime();

        if(pos < this.start || pos >= this.end) {
            this.sound.on('seeked', bind(function() {
                this.sound.off('seeked');
                this._play_sound();
            }, this));

            this.sound.currentTime(this.start);
        } else {
            this._play_sound();
        }
    }

    PopcornClip.prototype.play = function() {
        if(soundcite.mobile) {
            removeClass(this.el, 'soundcite-play');
            addClass(this.el, 'soundcite-loading');

            if(this.sound.readyState() > 1) {
                this.play_sound();
            } else {
                this.sound.on('canplaythrough', bind(function() {
                    this.play_sound();
                }, this));
                document.getElementById(this.id).load();
            }
        } else {
            this.play_sound();
        }
    }

// set up clips array from element array
    for(var i = 0; i < soundcite_elements.length; i++) {
        var el = soundcite_elements[i];
        if(el.getAttribute('data-url')) {
            new PopcornClip(el);
        } else if (el.getAttribute('data-id')) {
            new SoundCloudClip(el);
        } else {
            console.log('Unable to form Soundcite element because of missing attributes. The offending Soundcite was "' + el.textContent + '."');
        }
    }

    soundcite.Clip = Clip;
    soundcite.SoundCloudClip = SoundCloudClip;
    soundcite.PopcornClip = PopcornClip;
    soundcite.clips = clips;    // keep track of clips
    soundcite.pause_all_clips = pause_all_clips;
});
<|endoftext|>


<commit_before>(function (enyo, scope) {

	/**
	* Fires when a row is being initialized.
	*
	* @event onyx.FlyweightPicker#onSetupItem
	* @type {Object}
	* @property {Number} index - The row index.
	* @property {enyo.Control} flyweight - The row control, for decoration.
	* @see  enyo.FlyweightRepeater#onSetupItem
	* @public
	*/

	/**
	* Fires when an item is selected.
	*
	* @event onyx.FlyweightPicker#onSelect
	* @type {Object}
	* @property {String} content - Content of the selected item.
	* @property {Number} selected - Row index of the selected item.
	* @public
	*/

	/**
	* {@link onyx.FlyweightPicker}, a subkind of {@link onyx.Picker}, is a picker
	* that employs the flyweight pattern. It is used to display a large list of
	* selectable items.	As with {@link enyo.FlyweightRepeater}, the
	* [onSetupItem]{@link onyx.FlyweightPicker#onSetupItem} event allows for
	* customization of item rendering.
	*
	* To initialize the FlyweightPicker to a particular value, call `setSelected()`
	* with the index of the item you wish to select, and call `setContent()` with
	* the item that should be shown in the activator button.
	*
	* When an item is selected, FlyweightPicker sends an
	* [onSelect]{@link onyx.FlyweightPicker#onSelect} event with the selected
	* item's information. This may be handled by a client application to determine
	* which item was selected.
	*
	* ```
	* enyo.kind({
	* 	handlers: {
	* 		onSelect: 'itemSelected'
	* 	},
	* 	components: [
	* 		{kind: 'onyx.PickerDecorator', components: [
	* 			{},
	* 			{name: 'yearPicker', kind: 'onyx.FlyweightPicker', count: 200,
	* 				onSetupItem: 'setupYear', components: [
	* 					{name: 'year'}
	* 				]
	* 			}
	* 		]}
	* 	],
	* 	create: function () {
	* 		var d = new Date();
	* 		var y = d.getYear();
	* 		this.$.yearPicker.setSelected(y);
	* 		this.$.year.setContent(1900+y);
	* 	},
	* 	setupYear: function (sender, event) {
	* 		this.$.year.setContent(1900+event.index);
	* 	},
	* 	itemSelected: function (sender, event) {
	* 		enyo.log('Picker Item Selected: ' + event.selected.content);
	* 	}
	* })
	* ```
	*
	* @class   onyx.FlyweightPicker
	* @extends onyx.Picker
	* @ui
	* @public
	*/
	enyo.kind(
		/** @lends onyx.FlyweightPicker.prototype */ {

		/**
		* @private
		*/
		name: 'onyx.FlyweightPicker',

		/**
		* @private
		*/
		kind: 'onyx.Picker',

		/**
		* @private
		*/
		classes: 'onyx-flyweight-picker',

		/**
		* @lends  onyx.FlyweightPicker.prototype
		* @private
		*/
		published: {
			/**
			* The number of rows to render.
			*
			* @type {Number}
			* @default  0
			* @public
			*/
			count: 0
		},

		/**
		* @private
		*/
		events: {
			onSetupItem: '',
			onSelect: ''
		},

		/**
		* @private
		*/
		handlers: {
			onSelect: 'itemSelect'
		},

		/**
		* @private
		*/
		components: [
			{name: 'scroller', kind: 'enyo.Scroller', strategyKind: 'TouchScrollStrategy', components: [
				{name: 'flyweight', kind: 'FlyweightRepeater', noSelect: true, ontap: 'itemTap'}
			]}
		],

		/**
		* @private
		*/
		scrollerName: 'scroller',

		/**
		* Force the flyweight's client control ([MenuItem]{@link onyx.MenuItem} by default)
		* to activate. This will result in a call to `processActivatedItem()`, which preps
		* our picker selection logic. This is a workaround for changes caused by ENYO-1609
		* which resulted in ENYO-1611.
		*
		* @private
		*/
		initComponents: function () {
			this.controlParentName = 'flyweight';
	        this.inherited(arguments);
			this.$.flyweight.$.client.children[0].setActive(true);
	    },

		/**
		* @private
		*/
		create: function () {
			this.inherited(arguments);
			this.countChanged();
		},

		/**
		* @private
		*/
		rendered: function () {
			this.inherited(arguments);
			this.selectedChanged();
		},

		/**
		* Scrolls the [selected]{@link onyx.FlyweightPicker#selected} control into view.
		*
		* @public
		*/
		scrollToSelected: function () {
			var n = this.$.flyweight.fetchRowNode(this.selected);
			this.getScroller().scrollToNode(n, !this.menuUp);
		},

		/**
		* @private
		*/
		countChanged: function () {
			this.$.flyweight.count = this.count;
		},

		/**
		* @private
		*/
		processActivatedItem: function (item) {
			this.item = item;
		},

		/**
		* @fires onyx.Picker#onChange
		* @private
		*/
		selectedChanged: function (old) {
			if (!this.item) {
				return;
			}
			if (old != null) {
				this.item.removeClass('selected');
				this.$.flyweight.renderRow(old);
			}
			var n;
			if (this.selected != null) {
				this.item.addClass('selected');
				this.$.flyweight.renderRow(this.selected);
				// need to remove the class from control to make sure it won't apply to other rows
				this.item.removeClass('selected');
				n = this.$.flyweight.fetchRowNode(this.selected);
			}
			this.doChange({selected: this.selected, content: n && n.textContent || this.item.content});
		},

		/**
		* @fires onyx.FlyweightPicker#onSelect
		* @private
		*/
		itemTap: function (sender, event) {
			this.setSelected(event.rowIndex);
			//Send the select event that we want the client to receive.
			this.doSelect({selected: this.item, content: this.item.content});
		},

		/**
		* Blocks all `select` events that aren't coming from this control. This is to
		* prevent `select` events from MenuItems since they won't have the correct value
		* in a Flyweight context.
		*
		* @private
		*/
		itemSelect: function (sender, event) {
			if (event.originator != this) {
				return true;
			}
		}
	});

})(enyo, this);<commit_msg>JSDoc cleanup. Enyo-DCO-1.1-Signed-Off-By: Jim Tang (jim.tang@lge.com)<commit_after>(function (enyo, scope) {

	/**
	* Fires when a row is being initialized.
	*
	* @event onyx.FlyweightPicker#onSetupItem
	* @type {Object}
	* @property {Number} index - The row index.
	* @property {enyo.Control} flyweight - The row control, for decoration.
	* @see enyo.FlyweightRepeater.onSetupItem
	* @public
	*/

	/**
	* Fires when an item is selected.
	*
	* @event onyx.FlyweightPicker#onSelect
	* @type {Object}
	* @property {String} content - Content of the selected item.
	* @property {Number} selected - Row index of the selected item.
	* @public
	*/

	/**
	* {@link onyx.FlyweightPicker}, a subkind of {@link onyx.Picker}, is a picker
	* that employs the flyweight pattern. It is used to display a large list of
	* selectable items.	As with {@link enyo.FlyweightRepeater}, the
	* [onSetupItem]{@link onyx.FlyweightPicker#onSetupItem} event allows for
	* customization of item rendering.
	*
	* To initialize the FlyweightPicker to a particular value, call `setSelected()`
	* with the index of the item you wish to select, and call `setContent()` with
	* the item that should be shown in the activator button.
	*
	* When an item is selected, FlyweightPicker sends an
	* [onSelect]{@link onyx.FlyweightPicker#onSelect} event with the selected
	* item's information. This may be handled by a client application to determine
	* which item was selected.
	*
	* ```
	* enyo.kind({
	* 	handlers: {
	* 		onSelect: 'itemSelected'
	* 	},
	* 	components: [
	* 		{kind: 'onyx.PickerDecorator', components: [
	* 			{},
	* 			{name: 'yearPicker', kind: 'onyx.FlyweightPicker', count: 200,
	* 				onSetupItem: 'setupYear', components: [
	* 					{name: 'year'}
	* 				]
	* 			}
	* 		]}
	* 	],
	* 	create: function () {
	* 		var d = new Date();
	* 		var y = d.getYear();
	* 		this.$.yearPicker.setSelected(y);
	* 		this.$.year.setContent(1900+y);
	* 	},
	* 	setupYear: function (sender, event) {
	* 		this.$.year.setContent(1900+event.index);
	* 	},
	* 	itemSelected: function (sender, event) {
	* 		enyo.log('Picker Item Selected: ' + event.selected.content);
	* 	}
	* })
	* ```
	*
	* @class   onyx.FlyweightPicker
	* @extends onyx.Picker
	* @ui
	* @public
	*/
	enyo.kind(
		/** @lends onyx.FlyweightPicker.prototype */ {

		/**
		* @private
		*/
		name: 'onyx.FlyweightPicker',

		/**
		* @private
		*/
		kind: 'onyx.Picker',

		/**
		* @private
		*/
		classes: 'onyx-flyweight-picker',

		/**
		* @lends  onyx.FlyweightPicker.prototype
		* @private
		*/
		published: {
			/**
			* The number of rows to render.
			*
			* @type {Number}
			* @default  0
			* @public
			*/
			count: 0
		},

		/**
		* @private
		*/
		events: {
			onSetupItem: '',
			onSelect: ''
		},

		/**
		* @private
		*/
		handlers: {
			onSelect: 'itemSelect'
		},

		/**
		* @private
		*/
		components: [
			{name: 'scroller', kind: 'enyo.Scroller', strategyKind: 'TouchScrollStrategy', components: [
				{name: 'flyweight', kind: 'FlyweightRepeater', noSelect: true, ontap: 'itemTap'}
			]}
		],

		/**
		* @private
		*/
		scrollerName: 'scroller',

		/**
		* Force the flyweight's client control ([MenuItem]{@link onyx.MenuItem} by default)
		* to activate. This will result in a call to `processActivatedItem()`, which preps
		* our picker selection logic. This is a workaround for changes caused by ENYO-1609
		* which resulted in ENYO-1611.
		*
		* @private
		*/
		initComponents: function () {
			this.controlParentName = 'flyweight';
	        this.inherited(arguments);
			this.$.flyweight.$.client.children[0].setActive(true);
	    },

		/**
		* @private
		*/
		create: function () {
			this.inherited(arguments);
			this.countChanged();
		},

		/**
		* @private
		*/
		rendered: function () {
			this.inherited(arguments);
			this.selectedChanged();
		},

		/**
		* Scrolls the [selected]{@link onyx.FlyweightPicker#selected} control into view.
		*
		* @public
		*/
		scrollToSelected: function () {
			var n = this.$.flyweight.fetchRowNode(this.selected);
			this.getScroller().scrollToNode(n, !this.menuUp);
		},

		/**
		* @private
		*/
		countChanged: function () {
			this.$.flyweight.count = this.count;
		},

		/**
		* @private
		*/
		processActivatedItem: function (item) {
			this.item = item;
		},

		/**
		* @fires onyx.Picker#onChange
		* @private
		*/
		selectedChanged: function (old) {
			if (!this.item) {
				return;
			}
			if (old != null) {
				this.item.removeClass('selected');
				this.$.flyweight.renderRow(old);
			}
			var n;
			if (this.selected != null) {
				this.item.addClass('selected');
				this.$.flyweight.renderRow(this.selected);
				// need to remove the class from control to make sure it won't apply to other rows
				this.item.removeClass('selected');
				n = this.$.flyweight.fetchRowNode(this.selected);
			}
			this.doChange({selected: this.selected, content: n && n.textContent || this.item.content});
		},

		/**
		* @fires onyx.FlyweightPicker#onSelect
		* @private
		*/
		itemTap: function (sender, event) {
			this.setSelected(event.rowIndex);
			//Send the select event that we want the client to receive.
			this.doSelect({selected: this.item, content: this.item.content});
		},

		/**
		* Blocks all `select` events that aren't coming from this control. This is to
		* prevent `select` events from MenuItems since they won't have the correct value
		* in a Flyweight context.
		*
		* @private
		*/
		itemSelect: function (sender, event) {
			if (event.originator != this) {
				return true;
			}
		}
	});

})(enyo, this);<|endoftext|>


<commit_before>'use strict';
define(
    ['config', 'three'],
    function(config, THREE) {
        return class Hud {

            // ##############################################
            // # Constructor ################################
            // ##############################################

            constructor() {
                let canvas    = document.createElement('canvas');
                canvas.width  = config.canvasWidth;
                canvas.height = config.canvasHeight;

                let bitmap       = canvas.getContext('2d');
                bitmap.font      = 'Normal 40px Changa';
                bitmap.textAlign = 'center';
                bitmap.fillStyle = 'rgba(245, 245, 245, 0.75)';
                bitmap.fillText('Initializing...', config.canvasWidth / 2, config.canvasHeight / 2);

                this.camera = new THREE.OrthographicCamera(
                    -config.canvasWidth / 2, config.canvasWidth / 2,
                    config.canvasHeight / 2, -config.canvasHeight / 2,
                    0, 300
                );

                this.scene = new THREE.Scene();
                this.texture = new THREE.Texture(canvas)
                this.texture.needsUpdate = true;
                let material = new THREE.MeshBasicMaterial({map: this.texture });
                material.transparent = true;

                let planeGeometry = new THREE.PlaneGeometry(config.canvasWidth, config.canvasHeight);
                let plane         = new THREE.Mesh(planeGeometry, material);
                this.scene.add(plane);
            }

            // ##############################################
            // # Public functions ###########################
            // ##############################################

            update() {
            }
        }
    }
);<commit_msg>Most HUD variables are now public<commit_after>'use strict';
define(
    ['config', 'three'],
    function(config, THREE) {
        return class Hud {

            // ##############################################
            // # Constructor ################################
            // ##############################################

            constructor() {
                this.canvas    = document.createElement('canvas');
                this.canvas.width  = 1024;
                this.canvas.height = 1024;

                this.bitmap      = this.canvas.getContext('2d');
                this.bitmap.font      = 'Normal 40px Changa';
                this.bitmap.textAlign = 'center';
                this.bitmap.fillStyle = 'rgba(245, 245, 245, 0.7)';
                this.bitmap.fillText('Initializing...', config.canvasWidth / 2, config.canvasHeight / 2);

                this.camera = new THREE.OrthographicCamera(
                    -config.canvasWidth / 2, config.canvasWidth / 2,
                    config.canvasHeight / 2, -config.canvasHeight / 2,
                    0, 300
                );

                this.scene   = new THREE.Scene();
                this.texture = new THREE.Texture(this.canvas)
                this.texture.needsUpdate = true;
                let material = new THREE.MeshBasicMaterial({map: this.texture });
                material.transparent = true;

                let planeGeometry = new THREE.PlaneGeometry(config.canvasWidth, config.canvasHeight);
                let plane         = new THREE.Mesh(planeGeometry, material);
                this.scene.add(plane);
            }

            // ##############################################
            // # Public functions ###########################
            // ##############################################

            update() {
                this.bitmap.clearRect(0, 0, config.canvasWidth, config.canvasHeight);
                this.bitmap.fillText('RAD]', config.canvasWidth / 2, config.canvasHeight / 2);
                this.texture.needsUpdate = true;
            }
        }
    }
);<|endoftext|>


<commit_before>import {LibraryAddCommand} from '../cmd/library';
import ParticleApi from '../cmd/api';
import settings from '../../settings';

import chalk from 'chalk';
import log from '../app/log';
import {spin} from '../app/ui';

import libraryInstall from './library_install';
import libraryMigrate from './library_migrate';
import libraryInit from './library_init';

//const ui = require('../cli/ui');

const apiJS = new ParticleApi(settings.apiUrl, {
	accessToken: settings.access_token
}).api;

export class CLILibraryAddCommandSite {
	constructor(argv) {
		[this.name, this.version='latest'] = argv.params.name.split('@');
		this.dir = argv.params.dir || process.cwd();
	}

	run(cmd) {
		return cmd.run(this, {
			name: this.name,
			version: this.version
		});
	}

	projectDir() {
		return this.dir;
	}

	fetchingLibrary(promise, name) {
		return spin(promise, `Adding library ${chalk.green(name)}`);
	}

	addedLibrary(name, version) {
		log.info(`Added library ${chalk.green(name)} ${version} to project`);
		return Promise.resolve();
	}
}

export default (app, cli) => {
	const lib = cli.createCategory(app, 'library', 'Manages firmware libraries', { alias: 'libraries' });

	libraryInit(lib, cli);
	libraryInstall(lib, cli);
	libraryMigrate(lib, cli);

	cli.createCommand(lib, 'add', 'Add a library to the current project.', {
		options: {},
		params: '<name>',

		handler: function libraryAddHandler(argv) {
			const apiClient = apiJS.client({ auth: settings.access_token });
			const site = new CLILibraryAddCommandSite(argv);
			const cmd = new LibraryAddCommand({ apiClient });
			return site.run(cmd);
		}
	});
	return lib;
};
<commit_msg>Create the API client later in the command implementation after settings are loaded.<commit_after>import {LibraryAddCommand} from '../cmd/library';
import ParticleApi from '../cmd/api';
import settings from '../../settings';

import chalk from 'chalk';
import log from '../app/log';
import {spin} from '../app/ui';

import libraryInstall from './library_install';
import libraryMigrate from './library_migrate';
import libraryInit from './library_init';

//const ui = require('../cli/ui');

let apiJS;

export class CLILibraryAddCommandSite {
	constructor(argv) {
		[this.name, this.version='latest'] = argv.params.name.split('@');
		this.dir = argv.params.dir || process.cwd();
	}

	run(cmd) {
		return cmd.run(this, {
			name: this.name,
			version: this.version
		});
	}

	projectDir() {
		return this.dir;
	}

	fetchingLibrary(promise, name) {
		return spin(promise, `Adding library ${chalk.green(name)}`);
	}

	addedLibrary(name, version) {
		log.info(`Added library ${chalk.green(name)} ${version} to project`);
		return Promise.resolve();
	}
}

export default (app, cli) => {
	apiJS =  new ParticleApi(settings.apiUrl, {
		accessToken: settings.access_token
	}).api;

	const lib = cli.createCategory(app, 'library', 'Manages firmware libraries', { alias: 'libraries' });

	libraryInit(lib, cli);
	libraryInstall(lib, cli);
	libraryMigrate(lib, cli);

	cli.createCommand(lib, 'add', 'Add a library to the current project.', {
		options: {},
		params: '<name>',

		handler: function libraryAddHandler(argv) {
			const apiClient = apiJS.client({ auth: settings.access_token });
			const site = new CLILibraryAddCommandSite(argv);
			const cmd = new LibraryAddCommand({ apiClient });
			return site.run(cmd);
		}
	});
	return lib;
};
<|endoftext|>


<commit_before>/**
 * Show or hide tiles loader
 *
 * Usage:
 *
 * var tiles_loader = new cdb.admin.TilesLoader();
 * mapWrapper.$el.append(tiles_loader.render().$el);
 *
 */

cdb.admin.TilesLoader = cdb.core.View.extend({

  id: "tiles_loader",

  default_options: {
    animationSpeed: 500,
    spinner_ops: {
      lines: 8,
      length: 0,
      width: 4,
      radius: 5,
      corners: 1,
      rotate: 0,
      color: '#000',
      speed: 1,
      trail: 60,
      shadow: false,
      hwaccel: false,
      className: 'spinner',
      zIndex: 2e9,
      top: '0',
      left: '0'
    }
  },

  initialize: function() {
    _.defaults(this.options, this.default_options);
    this.isVisible = false;
    this.template = this.options.template ? this.options.template : cdb.templates.getTemplate('geo/tiles_loader');
  },

  render: function() {
    var $content = $(this.template(this.options))
      , self = this;

    // Apply tipsy
    $content.tipsy({
      title: function() {
        if (self.isVisible) {
          return "Loading tiles"
        } else {
          return "";
        }
      },
      fade: true,
      offset: 3,
      gravity: 'w'
    });

    this.$el.html($content);
    return this;
  },

  show: function(ev) {
    this.isVisible = true;
    if (!$.browser.msie || ($.browser.msie && $.browser.version.indexOf("9.") != 0)) {
      this.$el.fadeTo(this.options.animationSpeed, 1)
    } else {
      this.$el.show();
    }
  },

  hide: function(ev) {
    this.isVisible = false;
    if (!$.browser.msie || ($.browser.msie && $.browser.version.indexOf("9.") == 0)) {
      this.$el.stop(true).fadeTo(this.options.animationSpeed, 0)
    } else {
      this.$el.hide();
    }
  }

});
<commit_msg>removed tile_loader from cartodb20<commit_after><|endoftext|>


<commit_before>// TYPESCRIPT TODO: all props here are missing types
/* eslint-disable react/prop-types */
import { connect } from 'react-redux'
import { useMap } from 'react-map-gl'
import ParkAndRideOverlay from '@opentripplanner/park-and-ride-overlay'
import React, { useEffect } from 'react'
import VehicleRentalOverlay from '@opentripplanner/vehicle-rental-overlay'

import * as mapActions from '../../actions/map'

import EnhancedStopMarker from './enhanced-stop-marker'

/**
 * An overlay to view a collection of stops.
 */
const StopViewerOverlay = ({
  configCompanies,
  mapConfig,
  setLocation,
  stopData,
  stops
}) => {
  const { mainMap } = useMap()

  useEffect(() => {
    if (stopData?.lat && stopData?.lon) {
      mainMap?.flyTo({
        center: { lat: stopData.lat, lon: stopData.lon },
        duration: 500,
        zoom: 16
      })
    }
  }, [stopData, mainMap])

  if (!stopData) return null
  const { bikeRental, parkAndRideLocations, vehicleRental } = stopData

  return (
    <>
      {stops &&
        stops.length > 0 &&
        stops.map((stop) => (
          <EnhancedStopMarker
            key={stop.id}
            setLocation={setLocation}
            stop={stop}
          />
        ))}
      <ParkAndRideOverlay
        parkAndRideLocations={parkAndRideLocations}
        setLocation={setLocation}
        visible
      />
      {mapConfig.overlays &&
        mapConfig.overlays.map((overlayConfig, k) => {
          switch (overlayConfig.type) {
            case 'bike-rental':
            case 'otp2-bike-rental':
              return (
                <VehicleRentalOverlay
                  key={k}
                  {...overlayConfig}
                  configCompanies={configCompanies}
                  stations={bikeRental && bikeRental.stations}
                />
              )
            case 'micromobility-rental':
            case 'otp2-micromobility-rental':
              return (
                <VehicleRentalOverlay
                  key={k}
                  {...overlayConfig}
                  configCompanies={configCompanies}
                  stations={vehicleRental?.stations}
                />
              )
            default:
              return null
          }
        })}
    </>
  )
}

// connect to the redux store

const mapStateToProps = (state) => {
  const stopLookup = state.otp.transitIndex.stops
  const stopData = stopLookup[state.otp.ui.viewedStop?.stopId]
  const nearbyStops = stopData?.nearbyStops?.map((stopId) => stopLookup[stopId])
  const stops = []
  if (nearbyStops && nearbyStops.length > 0 && stopData) {
    stops.push(...nearbyStops)
    stops.push(stopData)
  }
  return {
    configCompanies: state.otp.config.companies,
    mapConfig: state.otp.config.map,
    stopData,
    stops
  }
}

const mapDispatchToProps = {
  setLocation: mapActions.setLocation
}

export default connect(mapStateToProps, mapDispatchToProps)(StopViewerOverlay)
<commit_msg>refactor: don't use named map<commit_after>// TYPESCRIPT TODO: all props here are missing types
/* eslint-disable react/prop-types */
import { connect } from 'react-redux'
import { useMap } from 'react-map-gl'
import ParkAndRideOverlay from '@opentripplanner/park-and-ride-overlay'
import React, { useEffect } from 'react'
import VehicleRentalOverlay from '@opentripplanner/vehicle-rental-overlay'

import * as mapActions from '../../actions/map'

import EnhancedStopMarker from './enhanced-stop-marker'

/**
 * An overlay to view a collection of stops.
 */
const StopViewerOverlay = ({
  configCompanies,
  mapConfig,
  setLocation,
  stopData,
  stops
}) => {
  const { current: mainMap } = useMap()

  useEffect(() => {
    if (stopData?.lat && stopData?.lon) {
      mainMap?.flyTo({
        center: { lat: stopData.lat, lon: stopData.lon },
        duration: 500,
        zoom: 16
      })
    }
  }, [stopData, mainMap])

  if (!stopData) return null
  const { bikeRental, parkAndRideLocations, vehicleRental } = stopData

  return (
    <>
      {stops &&
        stops.length > 0 &&
        stops.map((stop) => (
          <EnhancedStopMarker
            key={stop.id}
            setLocation={setLocation}
            stop={stop}
          />
        ))}
      <ParkAndRideOverlay
        parkAndRideLocations={parkAndRideLocations}
        setLocation={setLocation}
        visible
      />
      {mapConfig.overlays &&
        mapConfig.overlays.map((overlayConfig, k) => {
          switch (overlayConfig.type) {
            case 'bike-rental':
            case 'otp2-bike-rental':
              return (
                <VehicleRentalOverlay
                  key={k}
                  {...overlayConfig}
                  configCompanies={configCompanies}
                  stations={bikeRental && bikeRental.stations}
                />
              )
            case 'micromobility-rental':
            case 'otp2-micromobility-rental':
              return (
                <VehicleRentalOverlay
                  key={k}
                  {...overlayConfig}
                  configCompanies={configCompanies}
                  stations={vehicleRental?.stations}
                />
              )
            default:
              return null
          }
        })}
    </>
  )
}

// connect to the redux store

const mapStateToProps = (state) => {
  const stopLookup = state.otp.transitIndex.stops
  const stopData = stopLookup[state.otp.ui.viewedStop?.stopId]
  const nearbyStops = stopData?.nearbyStops?.map((stopId) => stopLookup[stopId])
  const stops = []
  if (nearbyStops && nearbyStops.length > 0 && stopData) {
    stops.push(...nearbyStops)
    stops.push(stopData)
  }
  return {
    configCompanies: state.otp.config.companies,
    mapConfig: state.otp.config.map,
    stopData,
    stops
  }
}

const mapDispatchToProps = {
  setLocation: mapActions.setLocation
}

export default connect(mapStateToProps, mapDispatchToProps)(StopViewerOverlay)
<|endoftext|>