<commit_before>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( jQuery, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !jQuery.support.display || !jQuery.support.position || !jQuery.support.overflow || !jQuery.support.floatclear ) {
		return;
	}

	//these properties should be made easy to override externally
	jQuery.mobile = {};

	jQuery.extend(jQuery.mobile, {
		subPageUrlKey: 'ui-page' //define the key used in urls for sub-pages. Defaults to &ui-page=
	});
	jQuery.mobile.subPageUrlKey = 'ui-page';

	var $window = jQuery(window),
		$html = jQuery('html'),
		$head = jQuery('head'),
		$body,
		$loader = jQuery('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		pageTransition,
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [ {
			url: location.hash.replace( /^#/, "" ),
			transition: "slide"
		} ],
		nextPageRole = null,
		noCache = '.ui-dialog';

	// hide address bar
	function hideBrowserChrome() {
		// prevent scrollstart and scrollstop events
		jQuery.event.special.scrollstart.enabled = false;
		window.scrollTo( 0, 0 );
		setTimeout(function() {
			jQuery.event.special.scrollstart.enabled = true;
		}, 150 );
	}

	function manageGlobalNav(activePage){
		if(activePage.is('.ui-globalnav-expanded')){
			jQuery('[data-role="globalnav"]').addClass('ui-globalnav-disable');
		}
		else{
			jQuery('[data-role="globalnav"]').removeClass('ui-globalnav-disable');
		}
	}

	// send a link through hash tracking
	jQuery.fn.ajaxClick = function() {
		var href = jQuery( this ).attr( "href" );
		pageTransition = jQuery( this ).attr( "data-transition" ) || "slide";
		nextPageRole = jQuery( this ).attr( "data-rel" );

		// let the hashchange event handler take care of everything else
		location.hash = href;

		// note: if it's a non-local-anchor and Ajax is not supported, go to page
		if ( href.match( /^[^#]/ ) && !jQuery.support.ajax ) {
			window.location = href;
		}

		return this;
	};

	// ajaxify all navigable links
	jQuery( "a:not([href=#]):not([target=_blank]):not([rel=external])" ).live( "click", function() {
		jQuery( this ).ajaxClick();
		return false;
	});

	// turn on/off page loading message.. also hides the ui-content div
	function pageLoading( done ) {
		if ( done ) {
			$html.removeClass( "ui-loading" );
			//fade in page content, remove loading msg
			jQuery('.ui-page-active .ui-content').addClass('dissolve in');
		} else {
			$html.addClass( "ui-loading" );
			$loader.appendTo( $body ).addClass( "dissolve in" );
		}
	};

	// transition between pages - based on transitions from jQtouch
	function changePage( from, to, transition, back ) {
		hideBrowserChrome();

		// kill keyboard (thx jQtouch :) )
		jQuery( document.activeElement ).blur();

		// animate in / out
		from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
		to.appendTo($body).addClass( activePageClass + " " + transition +
			" in " + ( back ? "reverse" : "" ) );
		//make sure globalnav is on top
		jQuery('[data-role="globalnav"]').appendTo($body);
		manageGlobalNav(to);

		// callback - remove classes, etc
		to.animationComplete(function() {
			from.add( to ).removeClass(" out in reverse " + transitions );
			from.removeClass( activePageClass );
			pageLoading( true );
			jQuery.fixedToolbars.show();
		});
	};

	jQuery(function() {
		$body = jQuery( "body" );
		pageLoading();

		// needs to be bound at domready (for IE6)
		// find or load content, make it active
		$window.bind( "hashchange", function(e) {
			var url = location.hash.replace( /^#/, "" ),
				stackLength = urlStack.length,
				// pageTransition only exists if the user clicked a link
				back = !pageTransition && stackLength > 1 &&
					urlStack[ stackLength - 2 ].url === url,
				transition = pageTransition || "slide",
				fileUrl = url;
			pageTransition = undefined;

			// if the new href is the same as the previous one
			if ( back ) {
				transition = urlStack.pop().transition;
			} else {
				urlStack.push({ url: url, transition: transition });
			}

			//remove any pages that shouldn't cache
			jQuery(noCache).remove();

			//function for setting role of next page
			function setPageRole( newPage ) {
				if ( nextPageRole ) {
					newPage.attr( "data-role", nextPageRole );
					nextPageRole = undefined;
				}
			}

			if ( url ) {
				// see if content is present already
				var localDiv = jQuery( "[id='" + url + "']" );
				if ( localDiv.length ) {
					if ( localDiv.is( "[data-role]" ) ) {
						setPageRole( localDiv );
					}
					mobilize( localDiv );
					changePage( jQuery( ".ui-page-active" ), localDiv, transition, back );
				} else { //ajax it in
					pageLoading();
					if(url.match( '&' + jQuery.mobile.subPageUrlKey )){
						fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0];
					}
					var newPage = jQuery( "
" )
						.appendTo( $body )
						.load( fileUrl + ' [data-role="page"]', function() {
							// TODO: test this (avoids querying the dom for new element):
//							var newPage = jQuery( this ).find( ".ui-page" ).eq( 0 )
//								.attr( "id", url );
//							jQuery( this ).replaceWith( newPage );
//							setPageRole( newPage );
//							mobilize( newPage );
//							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
							jQuery( this ).replaceWith(
								jQuery( this ).find( '[data-role="page"]' ).eq( 0 ).attr( "id", fileUrl ) );
							var newPage = jQuery( "[id='" + fileUrl + "']" );
							setPageRole( newPage );
							mobilize( newPage );
							newPage = jQuery( "[id='" + url + "']" );
							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
						});
				}
			} else {
				// either we've backed up to the root page url
				// or it's the first page load with no hash present
				var currentPage = jQuery( ".ui-page-active" );
				if ( currentPage.length && !startPage.is( ".ui-page-active" ) ) {
					changePage( currentPage, startPage, transition, back );
				} else {
					startPage.addClass( activePageClass );
					manageGlobalNav(startPage);
					jQuery.fixedToolbars.show();
					pageLoading( true );
				}
			}
		});

		hideBrowserChrome();
	});

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta - these will need to be configurable somehow.
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />'+
		'<link rel="apple-touch-icon" href="images/homeicon.png" />'+
		'<link rel="apple-touch-startup-image" href="images/startscreen.png" />'+
		'<meta name="apple-mobile-web-app-capable" content="yes" />' +
        '<meta name="apple-mobile-web-app-status-bar-style" content="default" />');

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	jQuery.fn.animationComplete = function(callback){
		if(jQuery.support.WebKitAnimationEvent){
			return jQuery(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.not('[data-mobilized]').each(function(){

			var $el = $(this);

			//hide no-js content
			$el.find('[data-role="nojs"]').addClass('ui-nojs');

			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			$el.find('button, input[type=submit], input[type=reset], input[type=image]').customButton();
			$el.find('input[type=text],input[type=number],input[type=password],textarea').customTextInput();
			$el.find("input, select").filter('[data-role="slider"]').slider();
			$el.find('select').not('[data-role="slider"]').customSelect();

			/*temporary datepicker workaround */
			$el.find('input[type=date]').hide().after( $('
').datepicker() );
			$('.ui-datepicker-header').addClass('ui-body-c ui-corner-top').removeClass('ui-corner-all');
			$('.ui-datepicker-prev').buttonMarkup({iconPos: 'notext', icon: 'arrow-l', shadow: true, corners: true});
			$('.ui-datepicker-next').buttonMarkup({iconPos: 'notext', icon: 'arrow-r', shadow: true, corners: true});
			$('.ui-datepicker-calendar thead').remove();
			$('.ui-datepicker-calendar .ui-btn-text span').attr('class','');
			$('.ui-datepicker-calendar td').addClass('ui-btn-up-c');
			$('.ui-datepicker-calendar a').buttonMarkup({corners: false, shadow: false});
			/* //temporary datepicker workaround */

			//pre-find data els
			var $dataEls = $el.find('[data-role]').andSelf().each(function() {
				var $this = $(this),
					role = $this.attr("data-role");
				switch(role) {
				case 'header':
				case 'footer':
					$this.addClass('ui-bar-' + ($(this).data('theme') ? $(this).data('theme') : 'a'));
				case 'page':
				case 'content':
					$this.addClass('ui-' + role);
					break;
				case 'collapsible':
				case 'fieldcontain':
				case 'globalnav':
				case 'listview':
				case 'dialog':
				case 'ajaxform':
					$this[role]();
					break;
				case 'controlgroup':
					// FIXME for some reason this has to come before the form control stuff (see above)
					$this.controlgroup({
						direction: $this.attr("data-type")
					});
					break;
				}
			});

			//fix toolbars
			$el.fixHeaderFooter();

			//add back buttons to headers that don't have them
			// FIXME make that optional?
			// TODO don't do that on devices that have a native back button?
			var backBtn = $el.find('.ui-header a.ui-back');
			if(!backBtn.length){
				backBtn = jQuery('').appendTo($el.find('.ui-header'));
			}

			//buttons from links in headers,footers,bars, or with data-role
			$dataEls.filter('[data-role="button"]').add('.ui-header a, .ui-footer a, .ui-bar a').not('.ui-btn').buttonMarkup();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//make all back buttons mimic the back button (pre-js, these links are usually "home" links)
			backBtn
				.click(function(){
					// FIXME if you navigated from some page directly to subpage (ala .../#_form-controls.html), back button will actually go to the previous page, instead of the parent page
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);

			$el.attr('data-mobilized', true);
		});
	};

	jQuery.extend({
		mobilize: mobilize,
		pageLoading: pageLoading,
		changePage: changePage,
		hideBrowserChrome: hideBrowserChrome
	});

	//dom-ready
	jQuery(function(){

		//set up active page - mobilize it!
		startPage = jQuery('[data-role="page"]:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//mobilize all pages present
		mobilize(jQuery('[data-role="page"]'));

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( jQuery.event.special.orientationchange.orientation( $window ) );

		//some debug stuff for the events pages
		jQuery('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			jQuery('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});
})( jQuery, this );
<commit_msg>removed references to globalnavs, as they'll need to be managed in a different way.<commit_after>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( jQuery, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !jQuery.support.display || !jQuery.support.position || !jQuery.support.overflow || !jQuery.support.floatclear ) {
		return;
	}

	//these properties should be made easy to override externally
	jQuery.mobile = {};

	jQuery.extend(jQuery.mobile, {
		subPageUrlKey: 'ui-page' //define the key used in urls for sub-pages. Defaults to &ui-page=
	});
	jQuery.mobile.subPageUrlKey = 'ui-page';

	var $window = jQuery(window),
		$html = jQuery('html'),
		$head = jQuery('head'),
		$body,
		$loader = jQuery('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		pageTransition,
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [ {
			url: location.hash.replace( /^#/, "" ),
			transition: "slide"
		} ],
		nextPageRole = null,
		noCache = '.ui-dialog';

	// hide address bar
	function hideBrowserChrome() {
		// prevent scrollstart and scrollstop events
		jQuery.event.special.scrollstart.enabled = false;
		window.scrollTo( 0, 0 );
		setTimeout(function() {
			jQuery.event.special.scrollstart.enabled = true;
		}, 150 );
	}

	// send a link through hash tracking
	jQuery.fn.ajaxClick = function() {
		var href = jQuery( this ).attr( "href" );
		pageTransition = jQuery( this ).attr( "data-transition" ) || "slide";
		nextPageRole = jQuery( this ).attr( "data-rel" );

		// let the hashchange event handler take care of everything else
		location.hash = href;

		// note: if it's a non-local-anchor and Ajax is not supported, go to page
		if ( href.match( /^[^#]/ ) && !jQuery.support.ajax ) {
			window.location = href;
		}

		return this;
	};

	// ajaxify all navigable links
	jQuery( "a:not([href=#]):not([target=_blank]):not([rel=external])" ).live( "click", function() {
		jQuery( this ).ajaxClick();
		return false;
	});

	// turn on/off page loading message.. also hides the ui-content div
	function pageLoading( done ) {
		if ( done ) {
			$html.removeClass( "ui-loading" );
			//fade in page content, remove loading msg
			jQuery('.ui-page-active .ui-content').addClass('dissolve in');
		} else {
			$html.addClass( "ui-loading" );
			$loader.appendTo( $body ).addClass( "dissolve in" );
		}
	};

	// transition between pages - based on transitions from jQtouch
	function changePage( from, to, transition, back ) {
		hideBrowserChrome();

		// kill keyboard (thx jQtouch :) )
		jQuery( document.activeElement ).blur();

		// animate in / out
		from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
		to.appendTo($body).addClass( activePageClass + " " + transition +
			" in " + ( back ? "reverse" : "" ) );

		// callback - remove classes, etc
		to.animationComplete(function() {
			from.add( to ).removeClass(" out in reverse " + transitions );
			from.removeClass( activePageClass );
			pageLoading( true );
			jQuery.fixedToolbars.show();
		});
	};

	jQuery(function() {
		$body = jQuery( "body" );
		pageLoading();

		// needs to be bound at domready (for IE6)
		// find or load content, make it active
		$window.bind( "hashchange", function(e) {
			var url = location.hash.replace( /^#/, "" ),
				stackLength = urlStack.length,
				// pageTransition only exists if the user clicked a link
				back = !pageTransition && stackLength > 1 &&
					urlStack[ stackLength - 2 ].url === url,
				transition = pageTransition || "slide",
				fileUrl = url;
			pageTransition = undefined;

			// if the new href is the same as the previous one
			if ( back ) {
				transition = urlStack.pop().transition;
			} else {
				urlStack.push({ url: url, transition: transition });
			}

			//remove any pages that shouldn't cache
			jQuery(noCache).remove();

			//function for setting role of next page
			function setPageRole( newPage ) {
				if ( nextPageRole ) {
					newPage.attr( "data-role", nextPageRole );
					nextPageRole = undefined;
				}
			}

			if ( url ) {
				// see if content is present already
				var localDiv = jQuery( "[id='" + url + "']" );
				if ( localDiv.length ) {
					if ( localDiv.is( "[data-role]" ) ) {
						setPageRole( localDiv );
					}
					mobilize( localDiv );
					changePage( jQuery( ".ui-page-active" ), localDiv, transition, back );
				} else { //ajax it in
					pageLoading();
					if(url.match( '&' + jQuery.mobile.subPageUrlKey )){
						fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0];
					}
					var newPage = jQuery( "
" )
						.appendTo( $body )
						.load( fileUrl + ' [data-role="page"]', function() {
							// TODO: test this (avoids querying the dom for new element):
//							var newPage = jQuery( this ).find( ".ui-page" ).eq( 0 )
//								.attr( "id", url );
//							jQuery( this ).replaceWith( newPage );
//							setPageRole( newPage );
//							mobilize( newPage );
//							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
							jQuery( this ).replaceWith(
								jQuery( this ).find( '[data-role="page"]' ).eq( 0 ).attr( "id", fileUrl ) );
							var newPage = jQuery( "[id='" + fileUrl + "']" );
							setPageRole( newPage );
							mobilize( newPage );
							newPage = jQuery( "[id='" + url + "']" );
							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
						});
				}
			} else {
				// either we've backed up to the root page url
				// or it's the first page load with no hash present
				var currentPage = jQuery( ".ui-page-active" );
				if ( currentPage.length && !startPage.is( ".ui-page-active" ) ) {
					changePage( currentPage, startPage, transition, back );
				} else {
					startPage.addClass( activePageClass );
					jQuery.fixedToolbars.show();
					pageLoading( true );
				}
			}
		});

		hideBrowserChrome();
	});

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta - these will need to be configurable somehow.
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />'+
		'<link rel="apple-touch-icon" href="images/homeicon.png" />'+
		'<link rel="apple-touch-startup-image" href="images/startscreen.png" />'+
		'<meta name="apple-mobile-web-app-capable" content="yes" />' +
        '<meta name="apple-mobile-web-app-status-bar-style" content="default" />');

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	jQuery.fn.animationComplete = function(callback){
		if(jQuery.support.WebKitAnimationEvent){
			return jQuery(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.not('[data-mobilized]').each(function(){

			var $el = $(this);

			//hide no-js content
			$el.find('[data-role="nojs"]').addClass('ui-nojs');

			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			$el.find('button, input[type=submit], input[type=reset], input[type=image]').customButton();
			$el.find('input[type=text],input[type=number],input[type=password],textarea').customTextInput();
			$el.find("input, select").filter('[data-role="slider"]').slider();
			$el.find('select').not('[data-role="slider"]').customSelect();

			/*temporary datepicker workaround */
			$el.find('input[type=date]').hide().after( $('
').datepicker() );
			$('.ui-datepicker-header').addClass('ui-body-c ui-corner-top').removeClass('ui-corner-all');
			$('.ui-datepicker-prev').buttonMarkup({iconPos: 'notext', icon: 'arrow-l', shadow: true, corners: true});
			$('.ui-datepicker-next').buttonMarkup({iconPos: 'notext', icon: 'arrow-r', shadow: true, corners: true});
			$('.ui-datepicker-calendar thead').remove();
			$('.ui-datepicker-calendar .ui-btn-text span').attr('class','');
			$('.ui-datepicker-calendar td').addClass('ui-btn-up-c');
			$('.ui-datepicker-calendar a').buttonMarkup({corners: false, shadow: false});
			/* //temporary datepicker workaround */

			//pre-find data els
			var $dataEls = $el.find('[data-role]').andSelf().each(function() {
				var $this = $(this),
					role = $this.attr("data-role");
				switch(role) {
				case 'header':
				case 'footer':
					$this.addClass('ui-bar-' + ($(this).data('theme') ? $(this).data('theme') : 'a'));
				case 'page':
				case 'content':
					$this.addClass('ui-' + role);
					break;
				case 'collapsible':
				case 'fieldcontain':
				case 'globalnav':
				case 'listview':
				case 'dialog':
				case 'ajaxform':
					$this[role]();
					break;
				case 'controlgroup':
					// FIXME for some reason this has to come before the form control stuff (see above)
					$this.controlgroup({
						direction: $this.attr("data-type")
					});
					break;
				}
			});

			//fix toolbars
			$el.fixHeaderFooter();

			//add back buttons to headers that don't have them
			// FIXME make that optional?
			// TODO don't do that on devices that have a native back button?
			var backBtn = $el.find('.ui-header a.ui-back');
			if(!backBtn.length){
				backBtn = jQuery('').appendTo($el.find('.ui-header'));
			}

			//buttons from links in headers,footers,bars, or with data-role
			$dataEls.filter('[data-role="button"]').add('.ui-header a, .ui-footer a, .ui-bar a').not('.ui-btn').buttonMarkup();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//make all back buttons mimic the back button (pre-js, these links are usually "home" links)
			backBtn
				.click(function(){
					// FIXME if you navigated from some page directly to subpage (ala .../#_form-controls.html), back button will actually go to the previous page, instead of the parent page
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);

			$el.attr('data-mobilized', true);
		});
	};

	jQuery.extend({
		mobilize: mobilize,
		pageLoading: pageLoading,
		changePage: changePage,
		hideBrowserChrome: hideBrowserChrome
	});

	//dom-ready
	jQuery(function(){

		//set up active page - mobilize it!
		startPage = jQuery('[data-role="page"]:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//mobilize all pages present
		mobilize(jQuery('[data-role="page"]'));

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( jQuery.event.special.orientationchange.orientation( $window ) );

		//some debug stuff for the events pages
		jQuery('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			jQuery('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});
})( jQuery, this );
<|endoftext|>


<commit_before>var TradeOfferManager = require('./index.js');

var EResult = require('../resources/EResult.js');

TradeOfferManager.prototype._apiCall = function(httpMethod, method, version, input, callback) {
	var iface = 'IEconService';
	if(typeof method === 'object') {
		iface = method.iface;
		method = method.method;
	}

	var options = {
		"uri": "https://api.steampowered.com/" + iface + "/" + method + "/v" + version + "/",
		"json": true,
		"method": httpMethod
	};

	input = input || {};
	input.key = this.apiKey;
	options[httpMethod == 'GET' ? 'qs' : 'form'] = input;

	this._request(options, function(err, response, body) {
		if(err || response.statusCode != 200) {
			callback(err || new Error("HTTP error " + response.statusCode));
			return;
		}

		var eresult = response.headers['x-eresult'];
		if(typeof eresult !== 'undefined' && eresult != 1) {
			callback(new Error(EResult[eresult]));
			return;
		}

		if(!body || typeof body != 'object') {
			callback(new Error("Invalid API response"));
			return;
		}

		callback(null, body);
	});
};
<commit_msg>Include eresult property in WebAPI errors<commit_after>var TradeOfferManager = require('./index.js');

var EResult = require('../resources/EResult.js');

TradeOfferManager.prototype._apiCall = function(httpMethod, method, version, input, callback) {
	var iface = 'IEconService';
	if(typeof method === 'object') {
		iface = method.iface;
		method = method.method;
	}

	var options = {
		"uri": "https://api.steampowered.com/" + iface + "/" + method + "/v" + version + "/",
		"json": true,
		"method": httpMethod
	};

	input = input || {};
	input.key = this.apiKey;
	options[httpMethod == 'GET' ? 'qs' : 'form'] = input;

	this._request(options, function(err, response, body) {
		if(err || response.statusCode != 200) {
			callback(err || new Error("HTTP error " + response.statusCode));
			return;
		}

		var eresult = response.headers['x-eresult'];
		if(typeof eresult !== 'undefined' && eresult != 1) {
			var error = new Error(EResult.getName(eresult));
			error.eresult = eresult;
			callback(error);
			return;
		}

		if(!body || typeof body != 'object') {
			callback(new Error("Invalid API response"));
			return;
		}

		callback(null, body);
	});
};
<|endoftext|>


<commit_before>function init(){
  var sections = ['home','about','services','references','contact'];
  var sectionPositions = [];
  var autoScroll = false;

  var parseHash = function() {
    if(window.location.hash)
      return window.location.hash.slice(window.location.hash.indexOf('!')+1);
    else
      return null;
  };

  var scroll = function(divId) {
    if(divId && $('#'+divId).length){
      autoScroll = true;
      $("html, body").animate({ scrollTop: $('#'+divId).offset().top }, 1000, function(){
        autoScroll = false;
      });
    }
    else
      console.error('Section ' + divId + ' not found!');
  };

  var activateNavLink = function(href) {
    var navLinks = $('.navbar-dbf .nav-menu li');
    navLinks.each(function(key,navLink){
      navLink = $(navLink);
      navLink.removeClass('active');
      if(navLink.find('a').attr('href') == href)
        navLink.addClass('active');
    });
  };

  var findCurrentScrollSection = function(i) {
    var pos = window.scrollY;
    if(i < sectionPositions.length) {
      if(pos >= sectionPositions[i].pos) {
        if(!sectionPositions[i+1] || pos < sectionPositions[i+1].pos)
          return sectionPositions[i].id;
        else
          return findCurrentScrollSection(i+1);
      }
    }
  };

  window.onhashchange = function() {
    scroll(parseHash());
    activateNavLink(window.location.hash);
  };

  window.onscroll = function(){
    if(!autoScroll)
      activateNavLink('#!'+findCurrentScrollSection(0));
  };

  sections.forEach(function(val){
    sectionPositions.push({id: val, pos: $('#'+val).offset().top});
  });

  // first nav.
  scroll(parseHash());
}

init();
<commit_msg>findCurrentSection null check added<commit_after>function init(){
  var sections = ['home','about','services','references','contact'];
  var sectionPositions = [];
  var autoScroll = false;

  var parseHash = function() {
    if(window.location.hash)
      return window.location.hash.slice(window.location.hash.indexOf('!')+1);
    else
      return null;
  };

  var scroll = function(divId) {
    if(divId && $('#'+divId).length){
      autoScroll = true;
      $("html, body").animate({ scrollTop: $('#'+divId).offset().top }, 1000, function(){
        autoScroll = false;
      });
    }
    else
      console.error('Section ' + divId + ' not found!');
  };

  var activateNavLink = function(href) {
    var navLinks = $('.navbar-dbf .nav-menu li');
    navLinks.each(function(key,navLink){
      navLink = $(navLink);
      navLink.removeClass('active');
      if(navLink.find('a').attr('href') == href)
        navLink.addClass('active');
    });
  };

  var findCurrentScrollSection = function(i) {
    var pos = window.scrollY;
    if(i < sectionPositions.length) {
      if(pos >= sectionPositions[i].pos) {
        if(!sectionPositions[i+1] || pos < sectionPositions[i+1].pos)
          return sectionPositions[i].id;
        else
          return findCurrentScrollSection(i+1);
      }
    }
  };

  window.onhashchange = function() {
    scroll(parseHash());
    activateNavLink(window.location.hash);
  };

  window.onscroll = function(){
    if(!autoScroll){
      section = findCurrentScrollSection(0);
      if(section)
        activateNavLink('#!'+section);
    }
  };

  sections.forEach(function(val){
    sectionPositions.push({id: val, pos: $('#'+val).offset().top});
  });

  // first nav.
  scroll(parseHash());
}

init();
<|endoftext|>


<commit_before>
Paloquiz.states.Highscores = function(game) {
    this.currentPage = 0;
    this.friendsScores = [];
    this.userPos = undefined;
    this.profileImagesData = [];
    this.numFriendsInPage = 0;
    this.maxPage = 0;

    this.scores = [];
    this.nameTextStyle;
    this.scoreTextStyle;
    this.nameUserTextStyle;
    this.scoreUserTextStyle;
    this.arrowRight;
    this.arrowLeft;
    this.backButton;
};

Paloquiz.states.Highscores.prototype = {

    PAGE_SIZE: 5,
    HIGHSCORE_REGION: {
        x: 0,
        y: 0,
        width: 0,
        height: 0
    },
    HIGHSCORE_ENTRY_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_NAME_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_IMAGE_SIZE: 0,

    init: function() {
        this.HIGHSCORE_REGION.x = this.game.width * .1;
        this.HIGHSCORE_REGION.y = this.game.height * .1;
        this.HIGHSCORE_REGION.width = this.game.width * .8;
        this.HIGHSCORE_REGION.height = this.game.height * .8;

        this.HIGHSCORE_ENTRY_SIZE.width = this.HIGHSCORE_REGION.width;
        this.HIGHSCORE_ENTRY_SIZE.height =
            this.HIGHSCORE_REGION.height / this.PAGE_SIZE;

        this.HIGHSCORE_NAME_SIZE.width = this.HIGHSCORE_REGION.width * .5;
        this.HIGHSCORE_NAME_SIZE.height = this.HIGHSCORE_ENTRY_SIZE.height;

        this.HIGHSCORE_IMAGE_SIZE = .8 * Math.min(
            this.HIGHSCORE_ENTRY_SIZE.width * .25,
            this.HIGHSCORE_ENTRY_SIZE.height);

        // Font styles
        this.createFontStyles();
    },

    preload: function() {
        // Enable cross-origin image loading
        this.load.crossOrigin = 'anonymous';

        this.load.image('noface', 'assets/noface.png');
        this.load.image('arrow', 'assets/arrow.png');
        this.load.image('back', 'assets/back.png');
    },

    create: function() {
        // Set up background
        var background = this.add.image(this.game.world.centerX, this.game.world.centerY, 'background');
        background.anchor.setTo(0.5, 0.5);
        background.width = this.game.width;
        background.height = this.game.height;

        // Create game objects
        this.createHighscoreEntries();
        this.createUI();

        // Handler for loaded profile pictures
        this.load.onLoadComplete.add(this.loadComplete, this);

        // Exit if not logged in
        if (!fbIsLoggedIn()) {
            this.state.start('Main');
            return;
        }
        fbGetFriendsScores(function(friendsScores, userPos) {
            this.friendsScores = friendsScores;
            this.userPos = userPos;
            this.maxPage = Math.floor(this.friendsScores.length / this.PAGE_SIZE);
            this.loadScoresPage();
        }, this);
    },

    shutdown: function() {
        // Remove handlers
        this.load.onLoadComplete.remove(this.loadComplete, this);
    },

    loadComplete: function() {
        for (var i = 0; i < this.numFriendsInPage; i++) {
            if (!this.profileImagesData[i].isSilhouette) {
                this.scores[i].img.loadTexture(i);
            }
            this.scores[i].img.width = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.height = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.anchor.setTo(.5, .5);
            this.scores[i].img.visible = true;
        }
    },

    loadScoresPage: function() {
        // Hide all entries
        this.scores.forEach(function(s) {
            s.img.loadTexture('noface');
            s.img.visible = false;
            s.name.setStyle(this.nameTextStyle);
            s.name.visible = false;
            s.score.setStyle(this.scoreTextStyle);
            s.score.visible = false;
        }, this);

        // Make sure page is in range
        if (this.currentPage < 0) {
            this.currentPage = 0;
        }
        if (this.currentPage > this.maxPage) {
            this.currentPage = this.maxPage;
        }

        // Hide arrows as needed
        this.arrowRight.visible = this.currentPage < this.maxPage;
        this.arrowLeft.visible = this.currentPage > 0;

        // Count friends in page
        var iFriendStart = this.PAGE_SIZE * this.currentPage;
        var iFriendEnd = Math.min(
            this.PAGE_SIZE * (this.currentPage + 1),
            this.friendsScores.length);
        this.numFriendsInPage = iFriendEnd - iFriendStart;
        this.profileImagesData = new Array(this.numFriendsInPage);

        // Function that starts the loader after every friend information is read
        var readFriends = 0;
        var friendInformationRead = function() {
            readFriends++;
            if (readFriends >= this.numFriendsInPage) {
                this.profileImagesData.forEach(function(img, i) {
                    this.load.image(i, img.url);
                }, this);
                this.load.start();
            }
        }
        friendInformationRead = friendInformationRead.bind(this);

        // Iterate through friends
        for (var iFriend = iFriendStart, iScore = 0; iFriend < iFriendEnd; iFriend++, iScore++) {
            // Copy index to use in closure
            var iFriendCpy = iFriend;
            var iScoreCpy = iScore;
            fbGetProfileDetails(this.friendsScores[iFriend].user.id, function(friend) {
                this.scores[iScoreCpy].name.setText(friend.first_name || friend.name);
                this.scores[iScoreCpy].score.setText(this.friendsScores[iFriendCpy].score);
                // Use special style for the user
                if (iFriendCpy == this.userPos) {
                    this.scores[iScoreCpy].name.setStyle(this.nameUserTextStyle);
                    this.scores[iScoreCpy].score.setStyle(this.scoreUserTextStyle);
                }
                this.scores[iScoreCpy].name.visible = true;
                this.scores[iScoreCpy].score.visible = true;
                this.profileImagesData[iScoreCpy] = friend.image;
                friendInformationRead();
            }, this);
        }
    },

    createFontStyles: function () {
        var textSize = Math.floor(this.HIGHSCORE_ENTRY_SIZE.height / 3) + 'px';
        var nameTextSize = textSize;
        var scoreTextSize = textSize;
        this.nameTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'white',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'white',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
        this.nameUserTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'red',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreUserTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'red',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
    },

    createHighscoreEntries: function () {
        for (var i = 0; i < this.PAGE_SIZE; i++) {
            // Y baseline
            var y = this.HIGHSCORE_REGION.y + i * this.HIGHSCORE_ENTRY_SIZE.height;

            // Image occupies the first 25% in X dimension
            var img = this.add.image(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .125,
                y + this.HIGHSCORE_REGION.height / 2,
                'noface');
            img.visible = false;

            // Name goes after image
            var name = this.add.text(0, 0, '', this.nameTextStyle);
            // Set bounds
            name.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .25,
                y,
                this.HIGHSCORE_REGION.width * .5,
                this.HIGHSCORE_ENTRY_SIZE.height);
            name.visible = false;

            // Score goes at the end
            var score = this.add.text(0, 0, '', this.scoreTextStyle);
            score.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .75,
                y,
                this.HIGHSCORE_REGION.width * .25,
                this.HIGHSCORE_ENTRY_SIZE.height);
            score.visible = false;
            this.scores.push({
                img: img,
                name: name,
                score: score
            });
        }
    },

    createUI: function () {
        // Back button to go back to main state
        var backButton = this.add.button(
            this.game.width * .1, this.game.height * .1, 'back',
            function () {
                this.state.start('Main');
            }, this, 0, 0, 0);
        backButton.anchor.setTo(0, 0);

        // Arrows to navigate through pages
        this.arrowRight = this.add.button(
            this.game.width * .9, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage < this.maxPage) {
                    this.currentPage++;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowRight.anchor.setTo(1, 1);
        this.arrowLeft = this.add.button(
            this.game.width * .1, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage > 0) {
                    this.currentPage--;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowLeft.scale.setTo(-1, 1);
        this.arrowLeft.anchor.setTo(1, 1);
    }

};
<commit_msg>ughhhh<commit_after>
Paloquiz.states.Highscores = function(game) {
    this.currentPage = 0;
    this.friendsScores = [];
    this.userPos = undefined;
    this.profileImagesData = [];
    this.numFriendsInPage = 0;
    this.maxPage = 0;

    this.scores = [];
    this.nameTextStyle;
    this.scoreTextStyle;
    this.nameUserTextStyle;
    this.scoreUserTextStyle;
    this.arrowRight;
    this.arrowLeft;
    this.backButton;
};

Paloquiz.states.Highscores.prototype = {

    PAGE_SIZE: 5,
    HIGHSCORE_REGION: {
        x: 0,
        y: 0,
        width: 0,
        height: 0
    },
    HIGHSCORE_ENTRY_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_NAME_SIZE: {
        width: 0,
        height: 0
    },
    HIGHSCORE_IMAGE_SIZE: 0,

    init: function() {
        this.HIGHSCORE_REGION.x = this.game.width * .1;
        this.HIGHSCORE_REGION.y = this.game.height * .1;
        this.HIGHSCORE_REGION.width = this.game.width * .8;
        this.HIGHSCORE_REGION.height = this.game.height * .8;

        this.HIGHSCORE_ENTRY_SIZE.width = this.HIGHSCORE_REGION.width;
        this.HIGHSCORE_ENTRY_SIZE.height =
            this.HIGHSCORE_REGION.height / this.PAGE_SIZE;

        this.HIGHSCORE_NAME_SIZE.width = this.HIGHSCORE_REGION.width * .5;
        this.HIGHSCORE_NAME_SIZE.height = this.HIGHSCORE_ENTRY_SIZE.height;

        this.HIGHSCORE_IMAGE_SIZE = .8 * Math.min(
            this.HIGHSCORE_ENTRY_SIZE.width * .25,
            this.HIGHSCORE_ENTRY_SIZE.height);

        // Font styles
        this.createFontStyles();
    },

    preload: function() {
        // Enable cross-origin image loading
        this.load.crossOrigin = 'anonymous';

        this.load.image('noface', 'assets/noface.png');
        this.load.image('arrow', 'assets/arrow.png');
        this.load.image('back', 'assets/back.png');
    },

    create: function() {
        // Set up background
        var background = this.add.image(this.game.world.centerX, this.game.world.centerY, 'background');
        background.anchor.setTo(0.5, 0.5);
        background.width = this.game.width;
        background.height = this.game.height;

        // Create game objects
        this.createHighscoreEntries();
        this.createUI();

        // Handler for loaded profile pictures
        this.load.onLoadComplete.add(this.loadComplete, this);

        // Exit if not logged in
        if (!fbIsLoggedIn()) {
            this.state.start('Main');
            return;
        }
        fbGetFriendsScores(function(friendsScores, userPos) {
            this.friendsScores = friendsScores;
            this.userPos = userPos;
            this.maxPage = Math.floor(this.friendsScores.length / this.PAGE_SIZE);
            this.loadScoresPage();
        }, this);
    },

    shutdown: function() {
        // Remove handlers
        this.load.onLoadComplete.remove(this.loadComplete, this);
    },

    loadComplete: function() {
        for (var i = 0; i < this.numFriendsInPage; i++) {
            if (!this.profileImagesData[i].isSilhouette) {
                this.scores[i].img.loadTexture(i);
            }
            this.scores[i].img.width = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.height = this.HIGHSCORE_IMAGE_SIZE;
            this.scores[i].img.anchor.setTo(.5, .5);
            this.scores[i].img.visible = true;
        }
    },

    loadScoresPage: function() {
        // Hide all entries
        this.scores.forEach(function(s) {
            s.img.loadTexture('noface');
            s.img.visible = false;
            s.name.setStyle(this.nameTextStyle);
            s.name.visible = false;
            s.score.setStyle(this.scoreTextStyle);
            s.score.visible = false;
        }, this);

        // Make sure page is in range
        if (this.currentPage < 0) {
            this.currentPage = 0;
        }
        if (this.currentPage > this.maxPage) {
            this.currentPage = this.maxPage;
        }

        // Hide arrows as needed
        this.arrowRight.visible = this.currentPage < this.maxPage;
        this.arrowLeft.visible = this.currentPage > 0;

        // Count friends in page
        var iFriendStart = this.PAGE_SIZE * this.currentPage;
        var iFriendEnd = Math.min(
            this.PAGE_SIZE * (this.currentPage + 1),
            this.friendsScores.length);
        this.numFriendsInPage = iFriendEnd - iFriendStart;
        this.profileImagesData = new Array(this.numFriendsInPage);

        // Function that starts the loader after every friend information is read
        var readFriends = 0;
        var friendInformationRead = function() {
            readFriends++;
            if (readFriends >= this.numFriendsInPage) {
                this.profileImagesData.forEach(function(img, i) {
                    this.load.image(i, img.url);
                }, this);
                this.load.start();
            }
        }
        friendInformationRead = friendInformationRead.bind(this);

        // Iterate through friends
        for (var iFriend = iFriendStart, iScore = 0; iFriend < iFriendEnd; iFriend++, iScore++) {
            // Copy index to use in closure
            var iFriendCpy = iFriend;
            var iScoreCpy = iScore;
            fbGetProfileDetails(this.friendsScores[iFriend].user.id, function(friend) {
                this.scores[iScoreCpy].name.setText(friend.first_name || friend.name);
                this.scores[iScoreCpy].score.setText(this.friendsScores[iFriendCpy].score);
                // Use special style for the user
                if (iFriendCpy == this.userPos) {
                    this.scores[iScoreCpy].name.setStyle(this.nameUserTextStyle);
                    this.scores[iScoreCpy].score.setStyle(this.scoreUserTextStyle);
                }
                this.scores[iScoreCpy].name.visible = true;
                this.scores[iScoreCpy].score.visible = true;
                this.profileImagesData[iScoreCpy] = friend.image;
                friendInformationRead();
            }, this);
        }
    },

    createFontStyles: function () {
        var textSize = Math.floor(this.HIGHSCORE_ENTRY_SIZE.height / 3) + 'px';
        var nameTextSize = textSize;
        var scoreTextSize = textSize;
        this.nameTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'white',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'white',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
        this.nameUserTextStyle = {
            font: 'Pixel Art',
            fontSize: nameTextSize,
            align: 'left',
            fill: 'red',
            align: 'left',
            boundsAlignH: 'left',
            boundsAlignV: 'middle',
            wordWrap: true,
            wordWrapWidth: this.HIGHSCORE_NAME_SIZE.width
        };
        this.scoreUserTextStyle = {
            font: 'Pixel Art',
            fontSize: scoreTextSize,
            align: 'center',
            fill: 'red',
            align: 'center',
            boundsAlignH: 'center',
            boundsAlignV: 'middle'
        };
    },

    createHighscoreEntries: function () {
        for (var i = 0; i < this.PAGE_SIZE; i++) {
            // Y baseline
            var y = this.HIGHSCORE_REGION.y + i * this.HIGHSCORE_ENTRY_SIZE.height;

            // Image occupies the first 25% in X dimension
            var img = this.add.image(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .125,
                y + this.HIGHSCORE_ENTRY_SIZE.height / 2,
                'noface');
            img.visible = false;

            // Name goes after image
            var name = this.add.text(0, 0, '', this.nameTextStyle);
            // Set bounds
            name.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .25,
                y,
                this.HIGHSCORE_REGION.width * .5,
                this.HIGHSCORE_ENTRY_SIZE.height);
            name.visible = false;

            // Score goes at the end
            var score = this.add.text(0, 0, '', this.scoreTextStyle);
            score.setTextBounds(
                this.HIGHSCORE_REGION.x + this.HIGHSCORE_REGION.width * .75,
                y,
                this.HIGHSCORE_REGION.width * .25,
                this.HIGHSCORE_ENTRY_SIZE.height);
            score.visible = false;
            this.scores.push({
                img: img,
                name: name,
                score: score
            });
        }
    },

    createUI: function () {
        // Back button to go back to main state
        var backButton = this.add.button(
            this.game.width * .1, this.game.height * .1, 'back',
            function () {
                this.state.start('Main');
            }, this, 0, 0, 0);
        backButton.anchor.setTo(0, 0);

        // Arrows to navigate through pages
        this.arrowRight = this.add.button(
            this.game.width * .9, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage < this.maxPage) {
                    this.currentPage++;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowRight.anchor.setTo(1, 1);
        this.arrowLeft = this.add.button(
            this.game.width * .1, this.game.height * .9, 'arrow',
            function () {
                if (this.currentPage > 0) {
                    this.currentPage--;
                    this.loadScoresPage();
                }
            }, this, 0, 0, 0);
        this.arrowLeft.scale.setTo(-1, 1);
        this.arrowLeft.anchor.setTo(1, 1);
    }

};
<|endoftext|>


<commit_before>/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require("path");
const { exec } = require("child_process");
const { file } = require("tmp-promise");
const { writeFile } = require("fs-extra");
const toneJson = require("../../docs/tone.json");
const eachLimit = require("async/eachLimit");
const os = require("os");
const cpuCount = os.cpus().length;

/**
 * Get all of the examples
 */
function findExamples(obj) {
	let examples = [];
	for (const prop in obj) {
		if (Array.isArray(obj[prop])) {
			obj[prop].forEach(child => {
				examples = [...examples, ...findExamples(child)];
			});
		} else if (prop === "comment" && obj[prop].tags) {
			examples = [
				...examples,
				...obj[prop].tags.filter(tag => tag.tag === "example").map(tag => tag.text)
			];
		} else if (typeof obj[prop] === "object") {
			examples = [...examples, ...findExamples(obj[prop])];
		} else {
			// console.log(prop);
		}
	}
	// filter any repeats
	return [...new Set(examples)];
}

/**
 * A promise version of exec
 */
function execPromise(cmd) {
	return new Promise((done, error) => {
		exec(cmd, (e, output) => {
			if (e) {
				error(output);
			} else {
				done();
			}
		});
	});
}

/**
 * Run the string through the typescript compiler
 */
async function testExampleString(str) {
	// str = str.replace("from \"tone\"", `from "${resolve(__dirname, "../../")}"`);
	str = `
		import * as Tone from "${resolve(__dirname, "../../")}"
		${str}
	`;
	const { path, cleanup } = await file({ postfix: ".ts" });
	// work with file here in fd
	await writeFile(path, str);
	try {
		await execPromise(`tsc  --noEmit --target es5 --lib dom,ES2015 ${path}`);
	} finally {
		cleanup();
	}
}

async function main() {
	const examples = findExamples(toneJson);
	let passed = 0;
	await eachLimit(examples, cpuCount, async example => {
		try {
			await testExampleString(example);
			passed++;
			// print a dot for each passed example
			process.stdout.write(".");
			// add a new line occasionally
			if (passed % 100 === 0) {
				process.stdout.write("\n");
			}
		} catch (e) {
			console.log(example + "\n" + e);
			throw e;
		}
	});
	console.log(`valid examples ${passed}/${examples.length}`);
}
main();

<commit_msg>moving writeFile into try/catch<commit_after>/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require("path");
const { exec } = require("child_process");
const { file } = require("tmp-promise");
const { writeFile } = require("fs-extra");
const toneJson = require("../../docs/tone.json");
const eachLimit = require("async/eachLimit");
const os = require("os");
const cpuCount = os.cpus().length;

/**
 * Get all of the examples
 */
function findExamples(obj) {
	let examples = [];
	for (const prop in obj) {
		if (Array.isArray(obj[prop])) {
			obj[prop].forEach(child => {
				examples = [...examples, ...findExamples(child)];
			});
		} else if (prop === "comment" && obj[prop].tags) {
			examples = [
				...examples,
				...obj[prop].tags.filter(tag => tag.tag === "example").map(tag => tag.text)
			];
		} else if (typeof obj[prop] === "object") {
			examples = [...examples, ...findExamples(obj[prop])];
		} else {
			// console.log(prop);
		}
	}
	// filter any repeats
	return [...new Set(examples)];
}

/**
 * A promise version of exec
 */
function execPromise(cmd) {
	return new Promise((done, error) => {
		exec(cmd, (e, output) => {
			if (e) {
				error(output);
			} else {
				done();
			}
		});
	});
}

/**
 * Run the string through the typescript compiler
 */
async function testExampleString(str) {
	// str = str.replace("from \"tone\"", `from "${resolve(__dirname, "../../")}"`);
	str = `
		import * as Tone from "${resolve(__dirname, "../../")}"
		${str}
	`;
	const { path, cleanup } = await file({ postfix: ".ts" });
	try {
		// work with file here in fd
		await writeFile(path, str);
		await execPromise(`tsc  --noEmit --target es5 --lib dom,ES2015 ${path}`);
	} finally {
		cleanup();
	}
}

async function main() {
	const examples = findExamples(toneJson);
	let passed = 0;
	await eachLimit(examples, cpuCount, async example => {
		try {
			await testExampleString(example);
			passed++;
			// print a dot for each passed example
			process.stdout.write(".");
			// add a new line occasionally
			if (passed % 100 === 0) {
				process.stdout.write("\n");
			}
		} catch (e) {
			console.log(example + "\n" + e);
			throw e;
		}
	});
	console.log(`valid examples ${passed}/${examples.length}`);
}
main();

<|endoftext|>


<commit_before>
/**
 * 用于控制歌词框的模块
 */

LyricsBox = (function () {

	function LyricsBox(options) {

		var lyricsBox = this;

		options = options || {};

		lyricsBox.lyricsWrap = options.lyricsWrap;
		lyricsBox.lyricsSlide = options.lyricsSlide;
		lyricsBox.lyricList = options.lyricList;
		lyricsBox.notice = options.notice;
		lyricsBox.selectArea = options.selectArea;
		lyricsBox.onselect = options.onselect;

		lyricsBox.lyricsInfo = {
			startTime: Date.now(),
			offset: 0,
			lyrics: []
		};
		lyricsBox.lyricsInfoCache = {
			startTime: Date.now(),
			offset: 0,
			lyrics: []
		};

		lyricsBox.lyricList.addEventListener('mousemove', function (event) {

			lyricsBox.startSelect();

			lyricsBox.resizeSelectArea(event.layerY, event.clientY);
		});

		window.addEventListener('mouseup', function (event) {

			if (event.button === 0) {

				lyricsBox.stopHover();

				lyricsBox.stopSelect();
			}
		});

		setInterval(function () {

			lyricsBox.refresh();

		}, 50);
	}

	LyricsBox.prototype = {

		/**
		 * 重置构造函数
		 */
		constructor: LyricsBox,

		/**
		 * 歌词外框元素
		 */
		lyricsWrap: null,

		/**
		 * 歌词滑动框元素
		 */
		lyricsSlide: null,

		/**
		 * 歌词列表元素
		 */
		lyricList: null,

		/**
		 * 歌词框中通知元素
		 */
		notice: null,

		/**
		 * 选择区域元素
		 */
		selectArea: null,

		/**
		 * 歌词信息
		 */
		lyricsInfo: null,

		/**
		 * 歌词信息缓存
		 */
		lyricsInfoCache: null,

		/**
		 * 当前歌词位置
		 */
		currentPosition: 0,

		/**
		 * 选择状态
		 *  'none' - 未选择
		 *  'hover' - 准备选择
		 *  'selecting' - 正在选择
		 *  'selected' - 已选择
		 */
		selectState: 'none',

		/**
		 * 选择开始元素序号
		 */
		selectStartIndex: null,

		/**
		 * 选择结束元素序号
		 */
		selectEndIndex: null,

		/**
		 * 当歌词被选中时触发该句柄
		 */
		onselect: null,

		/**
		 * 刷新歌词框中歌词位置
		 */
		refresh: function () {

			var transitionTime = 0;
			var transitionOffset = 300;
			var transitionEasing = function (t) {

				return 0.5 - Math.cos(t * Math.PI) / 2;
			};

			var lyricsBox = this;

			var time = Date.now() -
				(lyricsBox.lyricsInfo.startTime + lyricsBox.lyricsInfoCache.offset + transitionOffset);

			if (!lyricsBox.lyricsInfo.lyrics.some(function (item, position) {

				var timeOffset = time - item.time;
				var positionOffset = 0;

				if (!lyricsBox.lyricsInfo.lyrics[position + 1] && time >= item.time) {

					lyricsBox.scrollTo(position);

					return true;
				}

				if (time >= item.time && time < lyricsBox.lyricsInfo.lyrics[position + 1].time) {

					var duration = lyricsBox.lyricsInfo.lyrics[position + 1].time - item.time;

					if (duration > transitionTime) {

						var easingOffset = timeOffset - (duration - transitionTime);

						if (easingOffset >= 0 && transitionTime > 0) {

							positionOffset = transitionEasing(easingOffset / transitionTime);
						}

					} else {

						positionOffset = transitionEasing(timeOffset / duration);
					}

					lyricsBox.scrollTo(position + positionOffset);

					return true;
				}

				return false;

			})) {

				lyricsBox.scrollTo(0);
			}
		},

		/**
		 * 向歌词缓存中添加一行歌词
		 */
		append: function (item) {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache.lyrics.push(item);
		},

		/**
		 * 获取歌词的开始时间
		 */
		getStartTime: function () {

			var lyricsBox = this;

			return lyricsBox.lyricsInfoCache.startTime;
		},

		/**
		 * 设置歌词的开始时间
		 */
		setStartTime: function (startTime) {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache.startTime = startTime || Date.now();
		},

		/**
		 * 获取歌词的全局时间偏移量
		 */
		getOffset: function () {

			var lyricsBox = this;

			return lyricsBox.lyricsInfoCache.offset;
		},

		/**
		 * 设置歌词的全局时间偏移量
		 */
		setOffset: function (offset) {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache.offset = offset || 0;
		},

		/**
		 * 从歌词缓存中加载歌词
		 */
		update: function () {

			var lyricsBox = this;

			lyricsBox.lyricsInfo.startTime = 0;

			if (lyricsBox.selectState === 'none') {

				lyricsBox.lyricList.innerHTML = '';

				lyricsBox.lyricsInfo = {
					startTime: lyricsBox.lyricsInfoCache.startTime,
					offset: lyricsBox.lyricsInfoCache.offset,
					lyrics: lyricsBox.lyricsInfoCache.lyrics.map(function (item) {

						return item;
					})
				};

				if (lyricsBox.lyricsInfo.lyrics.length > 0) {

					lyricsBox.lyricsInfo.lyrics.forEach(function (item) {

						var element = document.createElement('li');

						element.innerHTML = item.lyric;

						if (item.lyric === '') element.classList.add('blank');

						element.addEventListener('mousedown', function (event) {

							event.preventDefault();

							if (event.button === 0) {

								lyricsBox.startHover(event.target, event.layerY);
							}
						});

						element.addEventListener('mouseleave', function (event) {

							lyricsBox.stopHover();
						});

						lyricsBox.lyricList.appendChild(element);
					});

					for (var index = 0; index < lyricsBox.lyricList.children.length; index++) {

						var element = lyricsBox.lyricList.children[index];

						element.offsetBottom = lyricsBox.lyricList.clientHeight -
							element.offsetTop - element.clientHeight;
						element.offsetMidline = element.offsetTop +
							element.clientHeight / 2;
					}

					lyricsBox.scrollTo();

					lyricsBox.notice.initializing.classList.add('hidden');
					lyricsBox.notice.lyricsNotFound.classList.add('hidden');

				} else {

					lyricsBox.scrollTo(0);

					lyricsBox.notice.initializing.classList.add('hidden');
					lyricsBox.notice.lyricsNotFound.classList.remove('hidden');
				}
			}
		},

		/**
		 * 清空歌词缓存
		 */
		clear: function () {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache = {
				startTime: Date.now(),
				offset: 0,
				lyrics: []
			};
		},

		/**
		 * 滚动到指定行的歌词
		 */
		scrollTo: function (position) {

			var lyricsBox = this;

			if (typeof(position) === 'undefined') position = lyricsBox.currentPosition;

			lyricsBox.currentPosition = position;

			for (var index = 0; index < lyricsBox.lyricList.children.length; index++) {

				var element = lyricsBox.lyricList.children[index];

				element.classList.remove('highlight');
			}

			var element = lyricsBox.lyricList.children[Math.floor(position)];

			if (element) {

				element.classList.add('highlight');
			}

			position = Math.min(position, lyricsBox.lyricList.children.length - 1);
			position = Math.max(position, 0);

			element = lyricsBox.lyricList.children[Math.floor(position)];

			if (lyricsBox.selectState === 'none') {

				var offset = 0;

				if (element) {

					offset = element.offsetMidline + ((element.nextElementSibling ||
						element).offsetMidline - element.offsetMidline) *
						(position - Math.floor(position)) + lyricsBox.lyricList.offsetTop;
				}

				lyricsBox.lyricsSlide.style.top = (320 - offset) + 'px';
			}
		},

		/**
		 * 滚动到下一行歌词
		 */
		scroll: function () {

			var lyricsBox = this;

			lyricsBox.scrollTo(Math.floor(lyricsBox.currentPosition + 1));
		},

		/**
		 * 设置选择区域范围
		 */
		setSelectArea: function (startIndex, endIndex) {

			var lyricsBox = this;

			var topElement = lyricsBox.lyricList.children[Math.min(startIndex, endIndex)];
			var bottomElement = lyricsBox.lyricList.children[Math.max(startIndex, endIndex)];

			lyricsBox.selectArea.style.top = (lyricsBox.lyricList.offsetTop + topElement.offsetTop) + 'px';
			lyricsBox.selectArea.style.bottom = bottomElement.offsetBottom + 'px';
		},

		/**
		 * 准备开始选择
		 */
		startHover: function (element, cursorOffset) {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'none') {

				lyricsBox.selectState = 'hover';

				for (var index = 0; index < lyricsBox.lyricList.children.length; index++) {

					if (lyricsBox.lyricList.children[index] === element) {

						lyricsBox.selectStartIndex = index;
						lyricsBox.selectEndIndex = index;;

						break;
					}
				}

				lyricsBox.selectArea.style.top = (lyricsBox.lyricList.offsetTop + cursorOffset) + 'px';
				lyricsBox.selectArea.style.bottom = lyricsBox.lyricList.clientHeight - cursorOffset + 'px';

				setTimeout(function () {

					lyricsBox.selectArea.classList.add('scroll');

				}, 1);

				setTimeout(function () {

					lyricsBox.startSelect();

				}, 850);
			}
		},

		/**
		 * 取消开始选择准备
		 */
		stopHover: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'hover') {

				lyricsBox.selectState = 'none';

				lyricsBox.update();
			}
		},

		/**
		 * 开始选择
		 */
		startSelect: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'hover') {

				lyricsBox.selectState = 'selecting';

				lyricsBox.selectArea.classList.remove('hidden');
				lyricsBox.lyricsWrap.classList.add('selecting');

				lyricsBox.setSelectArea(
					lyricsBox.selectStartIndex,
					lyricsBox.selectStartIndex
				);
			}
		},

		/**
		 * 改变选择区域范围大小
		 */
		resizeSelectArea: function (cursorOffset, cursorOffsetToWindow) {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'selecting') {

				if (cursorOffsetToWindow < 80
				 && lyricsBox.selectEndIndex > 0) {

					lyricsBox.lyricsSlide.style.top =
						(parseInt(lyricsBox.lyricsSlide.style.top) + 4) + 'px';
				}

				if (window.innerHeight - cursorOffsetToWindow < 80
				 && lyricsBox.selectEndIndex < lyricsBox.lyricList.children.length - 1) {

					lyricsBox.lyricsSlide.style.top =
						(parseInt(lyricsBox.lyricsSlide.style.top) - 4) + 'px';
				}

				if (cursorOffset < lyricsBox.lyricList.children[lyricsBox.selectStartIndex].offsetMidline) {

					for (var index = 0;
						index <= lyricsBox.selectStartIndex; index++) {

						lyricsBox.selectEndIndex = index;

						if (lyricsBox.lyricList.children[index].offsetMidline > cursorOffset) break;
					}

				} else {

					for (var index = lyricsBox.lyricList.children.length - 1;
						index >= lyricsBox.selectStartIndex; index--) {

						lyricsBox.selectEndIndex = index;

						if (lyricsBox.lyricList.children[index].offsetMidline < cursorOffset) break;
					}
				}

				lyricsBox.setSelectArea(
					lyricsBox.selectEndIndex,
					lyricsBox.selectStartIndex
				);
			}
		},

		/**
		 * 结束选择
		 */
		stopSelect: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'selecting') {

				lyricsBox.selectState = 'selected';

				lyricsBox.lyricsSlide.style.top = (407 - lyricsBox.selectArea.offsetTop -
					lyricsBox.selectArea.clientHeight * 0.6) + 'px';

				lyricsBox.lyricsWrap.classList.remove('selecting');

				var selectContent = [];

				var topIndex = Math.min(lyricsBox.selectStartIndex, lyricsBox.selectEndIndex);
				var bottomIndex = Math.max(lyricsBox.selectStartIndex, lyricsBox.selectEndIndex);

				for (var index = topIndex; index <= bottomIndex; index++) {

					selectContent.push(lyricsBox.lyricList.children[index].innerHTML);
				}

				if (lyricsBox.onselect) lyricsBox.onselect({
					target: lyricsBox,
					content: selectContent.join(' ')
				});
			}
		},

		/**
		 * 取消选择
		 */
		cancelSelect: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'selected') {

				lyricsBox.selectState = 'none';

				lyricsBox.selectArea.classList.remove('scroll');
				lyricsBox.selectArea.classList.add('hidden');

				lyricsBox.update();
			}
		}

	};

	return LyricsBox;

})();<commit_msg>解决了歌词框中歌词元素highlight类闪烁的问题<commit_after>
/**
 * 用于控制歌词框的模块
 */

LyricsBox = (function () {

	function LyricsBox(options) {

		var lyricsBox = this;

		options = options || {};

		lyricsBox.lyricsWrap = options.lyricsWrap;
		lyricsBox.lyricsSlide = options.lyricsSlide;
		lyricsBox.lyricList = options.lyricList;
		lyricsBox.notice = options.notice;
		lyricsBox.selectArea = options.selectArea;
		lyricsBox.onselect = options.onselect;

		lyricsBox.lyricsInfo = {
			startTime: Date.now(),
			offset: 0,
			lyrics: []
		};
		lyricsBox.lyricsInfoCache = {
			startTime: Date.now(),
			offset: 0,
			lyrics: []
		};

		lyricsBox.lyricList.addEventListener('mousemove', function (event) {

			lyricsBox.startSelect();

			lyricsBox.resizeSelectArea(event.layerY, event.clientY);
		});

		window.addEventListener('mouseup', function (event) {

			if (event.button === 0) {

				lyricsBox.stopHover();

				lyricsBox.stopSelect();
			}
		});

		setInterval(function () {

			lyricsBox.refresh();

		}, 50);
	}

	LyricsBox.prototype = {

		/**
		 * 重置构造函数
		 */
		constructor: LyricsBox,

		/**
		 * 歌词外框元素
		 */
		lyricsWrap: null,

		/**
		 * 歌词滑动框元素
		 */
		lyricsSlide: null,

		/**
		 * 歌词列表元素
		 */
		lyricList: null,

		/**
		 * 歌词框中通知元素
		 */
		notice: null,

		/**
		 * 选择区域元素
		 */
		selectArea: null,

		/**
		 * 歌词信息
		 */
		lyricsInfo: null,

		/**
		 * 歌词信息缓存
		 */
		lyricsInfoCache: null,

		/**
		 * 当前歌词位置
		 */
		currentPosition: 0,

		/**
		 * 选择状态
		 *  'none' - 未选择
		 *  'hover' - 准备选择
		 *  'selecting' - 正在选择
		 *  'selected' - 已选择
		 */
		selectState: 'none',

		/**
		 * 选择开始元素序号
		 */
		selectStartIndex: null,

		/**
		 * 选择结束元素序号
		 */
		selectEndIndex: null,

		/**
		 * 当歌词被选中时触发该句柄
		 */
		onselect: null,

		/**
		 * 刷新歌词框中歌词位置
		 */
		refresh: function () {

			var transitionTime = 0;
			var transitionOffset = 300;
			var transitionEasing = function (t) {

				return 0.5 - Math.cos(t * Math.PI) / 2;
			};

			var lyricsBox = this;

			var time = Date.now() -
				(lyricsBox.lyricsInfo.startTime + lyricsBox.lyricsInfoCache.offset + transitionOffset);

			if (!lyricsBox.lyricsInfo.lyrics.some(function (item, position) {

				var timeOffset = time - item.time;
				var positionOffset = 0;

				if (!lyricsBox.lyricsInfo.lyrics[position + 1] && time >= item.time) {

					lyricsBox.scrollTo(position);

					return true;
				}

				if (time >= item.time && time < lyricsBox.lyricsInfo.lyrics[position + 1].time) {

					var duration = lyricsBox.lyricsInfo.lyrics[position + 1].time - item.time;

					if (duration > transitionTime) {

						var easingOffset = timeOffset - (duration - transitionTime);

						if (easingOffset >= 0 && transitionTime > 0) {

							positionOffset = transitionEasing(easingOffset / transitionTime);
						}

					} else {

						positionOffset = transitionEasing(timeOffset / duration);
					}

					lyricsBox.scrollTo(position + positionOffset);

					return true;
				}

				return false;

			})) {

				lyricsBox.scrollTo(0);
			}
		},

		/**
		 * 向歌词缓存中添加一行歌词
		 */
		append: function (item) {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache.lyrics.push(item);
		},

		/**
		 * 获取歌词的开始时间
		 */
		getStartTime: function () {

			var lyricsBox = this;

			return lyricsBox.lyricsInfoCache.startTime;
		},

		/**
		 * 设置歌词的开始时间
		 */
		setStartTime: function (startTime) {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache.startTime = startTime || Date.now();
		},

		/**
		 * 获取歌词的全局时间偏移量
		 */
		getOffset: function () {

			var lyricsBox = this;

			return lyricsBox.lyricsInfoCache.offset;
		},

		/**
		 * 设置歌词的全局时间偏移量
		 */
		setOffset: function (offset) {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache.offset = offset || 0;
		},

		/**
		 * 从歌词缓存中加载歌词
		 */
		update: function () {

			var lyricsBox = this;

			lyricsBox.lyricsInfo.startTime = 0;

			if (lyricsBox.selectState === 'none') {

				lyricsBox.lyricList.innerHTML = '';

				lyricsBox.lyricsInfo = {
					startTime: lyricsBox.lyricsInfoCache.startTime,
					offset: lyricsBox.lyricsInfoCache.offset,
					lyrics: lyricsBox.lyricsInfoCache.lyrics.map(function (item) {

						return item;
					})
				};

				if (lyricsBox.lyricsInfo.lyrics.length > 0) {

					lyricsBox.lyricsInfo.lyrics.forEach(function (item) {

						var element = document.createElement('li');

						element.innerHTML = item.lyric;

						if (item.lyric === '') element.classList.add('blank');

						element.addEventListener('mousedown', function (event) {

							event.preventDefault();

							if (event.button === 0) {

								lyricsBox.startHover(event.target, event.layerY);
							}
						});

						element.addEventListener('mouseleave', function (event) {

							lyricsBox.stopHover();
						});

						lyricsBox.lyricList.appendChild(element);
					});

					for (var index = 0; index < lyricsBox.lyricList.children.length; index++) {

						var element = lyricsBox.lyricList.children[index];

						element.offsetBottom = lyricsBox.lyricList.clientHeight -
							element.offsetTop - element.clientHeight;
						element.offsetMidline = element.offsetTop +
							element.clientHeight / 2;
					}

					lyricsBox.scrollTo();

					lyricsBox.notice.initializing.classList.add('hidden');
					lyricsBox.notice.lyricsNotFound.classList.add('hidden');

				} else {

					lyricsBox.scrollTo(0);

					lyricsBox.notice.initializing.classList.add('hidden');
					lyricsBox.notice.lyricsNotFound.classList.remove('hidden');
				}
			}
		},

		/**
		 * 清空歌词缓存
		 */
		clear: function () {

			var lyricsBox = this;

			lyricsBox.lyricsInfoCache = {
				startTime: Date.now(),
				offset: 0,
				lyrics: []
			};
		},

		/**
		 * 滚动到指定行的歌词
		 */
		scrollTo: function (position) {

			var lyricsBox = this;

			if (typeof(position) === 'undefined') position = lyricsBox.currentPosition;

			lyricsBox.currentPosition = position;

			for (var index = 0; index < lyricsBox.lyricList.children.length; index++) {

				var element = lyricsBox.lyricList.children[index];

				if (index === Math.floor(position)) {

					element.classList.add('highlight');

				} else {

					element.classList.remove('highlight');
				}
			}

			var element = lyricsBox.lyricList.children[Math.floor(position)];

			position = Math.min(position, lyricsBox.lyricList.children.length - 1);
			position = Math.max(position, 0);

			element = lyricsBox.lyricList.children[Math.floor(position)];

			if (lyricsBox.selectState === 'none') {

				var offset = 0;

				if (element) {

					offset = element.offsetMidline + ((element.nextElementSibling ||
						element).offsetMidline - element.offsetMidline) *
						(position - Math.floor(position)) + lyricsBox.lyricList.offsetTop;
				}

				lyricsBox.lyricsSlide.style.top = (320 - offset) + 'px';
			}
		},

		/**
		 * 滚动到下一行歌词
		 */
		scroll: function () {

			var lyricsBox = this;

			lyricsBox.scrollTo(Math.floor(lyricsBox.currentPosition + 1));
		},

		/**
		 * 设置选择区域范围
		 */
		setSelectArea: function (startIndex, endIndex) {

			var lyricsBox = this;

			var topElement = lyricsBox.lyricList.children[Math.min(startIndex, endIndex)];
			var bottomElement = lyricsBox.lyricList.children[Math.max(startIndex, endIndex)];

			lyricsBox.selectArea.style.top = (lyricsBox.lyricList.offsetTop + topElement.offsetTop) + 'px';
			lyricsBox.selectArea.style.bottom = bottomElement.offsetBottom + 'px';
		},

		/**
		 * 准备开始选择
		 */
		startHover: function (element, cursorOffset) {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'none') {

				lyricsBox.selectState = 'hover';

				for (var index = 0; index < lyricsBox.lyricList.children.length; index++) {

					if (lyricsBox.lyricList.children[index] === element) {

						lyricsBox.selectStartIndex = index;
						lyricsBox.selectEndIndex = index;;

						break;
					}
				}

				lyricsBox.selectArea.style.top = (lyricsBox.lyricList.offsetTop + cursorOffset) + 'px';
				lyricsBox.selectArea.style.bottom = lyricsBox.lyricList.clientHeight - cursorOffset + 'px';

				setTimeout(function () {

					lyricsBox.selectArea.classList.add('scroll');

				}, 1);

				setTimeout(function () {

					lyricsBox.startSelect();

				}, 850);
			}
		},

		/**
		 * 取消开始选择准备
		 */
		stopHover: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'hover') {

				lyricsBox.selectState = 'none';

				lyricsBox.update();
			}
		},

		/**
		 * 开始选择
		 */
		startSelect: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'hover') {

				lyricsBox.selectState = 'selecting';

				lyricsBox.selectArea.classList.remove('hidden');
				lyricsBox.lyricsWrap.classList.add('selecting');

				lyricsBox.setSelectArea(
					lyricsBox.selectStartIndex,
					lyricsBox.selectStartIndex
				);
			}
		},

		/**
		 * 改变选择区域范围大小
		 */
		resizeSelectArea: function (cursorOffset, cursorOffsetToWindow) {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'selecting') {

				if (cursorOffsetToWindow < 80
				 && lyricsBox.selectEndIndex > 0) {

					lyricsBox.lyricsSlide.style.top =
						(parseInt(lyricsBox.lyricsSlide.style.top) + 4) + 'px';
				}

				if (window.innerHeight - cursorOffsetToWindow < 80
				 && lyricsBox.selectEndIndex < lyricsBox.lyricList.children.length - 1) {

					lyricsBox.lyricsSlide.style.top =
						(parseInt(lyricsBox.lyricsSlide.style.top) - 4) + 'px';
				}

				if (cursorOffset < lyricsBox.lyricList.children[lyricsBox.selectStartIndex].offsetMidline) {

					for (var index = 0;
						index <= lyricsBox.selectStartIndex; index++) {

						lyricsBox.selectEndIndex = index;

						if (lyricsBox.lyricList.children[index].offsetMidline > cursorOffset) break;
					}

				} else {

					for (var index = lyricsBox.lyricList.children.length - 1;
						index >= lyricsBox.selectStartIndex; index--) {

						lyricsBox.selectEndIndex = index;

						if (lyricsBox.lyricList.children[index].offsetMidline < cursorOffset) break;
					}
				}

				lyricsBox.setSelectArea(
					lyricsBox.selectEndIndex,
					lyricsBox.selectStartIndex
				);
			}
		},

		/**
		 * 结束选择
		 */
		stopSelect: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'selecting') {

				lyricsBox.selectState = 'selected';

				lyricsBox.lyricsSlide.style.top = (407 - lyricsBox.selectArea.offsetTop -
					lyricsBox.selectArea.clientHeight * 0.6) + 'px';

				lyricsBox.lyricsWrap.classList.remove('selecting');

				var selectContent = [];

				var topIndex = Math.min(lyricsBox.selectStartIndex, lyricsBox.selectEndIndex);
				var bottomIndex = Math.max(lyricsBox.selectStartIndex, lyricsBox.selectEndIndex);

				for (var index = topIndex; index <= bottomIndex; index++) {

					selectContent.push(lyricsBox.lyricList.children[index].innerHTML);
				}

				if (lyricsBox.onselect) lyricsBox.onselect({
					target: lyricsBox,
					content: selectContent.join(' ')
				});
			}
		},

		/**
		 * 取消选择
		 */
		cancelSelect: function () {

			var lyricsBox = this;

			if (lyricsBox.selectState === 'selected') {

				lyricsBox.selectState = 'none';

				lyricsBox.selectArea.classList.remove('scroll');
				lyricsBox.selectArea.classList.add('hidden');

				lyricsBox.update();
			}
		}

	};

	return LyricsBox;

})();<|endoftext|>