hurley / oRO-Pricealert

// ==UserScript==
// @name         oRO-Pricealert
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description  try to take over the world!
// @author       hurley
// @match        https://cp.originsro.org/market/vending/?item_id=*&name=*&type=*
// @grant        none
// @require      http://code.jquery.com/jquery-latest.js
// @licence      MIT
// ==/UserScript==

(function() {
	'use strict';

	var $ = jQuery;

	// current alert
	var currentUrl      = window.location.href;
	var currentAlert    = {
		maxPrice: 0,
		refreshTimeMin: 3, // in minutes
		refreshTimeMax: 6, // in minutes
		isActive: false,
	};
	var refreshInterval = null;

	// audio element
	var audioElement       = document.createElement('audio');
	var audioAlarmSound    = 'https://freesound.org/data/previews/25/25032_30374-lq.mp3';

	// oro selectors
	var priceTable      = $( '.table-responsive tbody' ); // the control panel table.
	var tableRows       = $( 'tr', priceTable );

	// local storage
	var localStorageKey = 'oroAlerts';

	// control constants
	var controlWidth    = '200px';
	var inputTimeout    = null

	var init = function() {
		initAlertObject();
		createAlarmAudio();
		createControlPanel();
	}

	// Get the existing alerts from local storage.
	var initAlertObject = function() {
		var lsAlerts = getLocalStorageAlerts();

		if ( 'object' === typeof lsAlerts && Object.keys( lsAlerts ).length > 0 && 'undefined' !== typeof lsAlerts[ currentUrl ] ) {
			currentAlert = lsAlerts[ currentUrl ];
		}
	}

	// Create the alarm element.
	var createAlarmAudio = function() {
		audioElement.setAttribute( 'src', audioAlarmSound );

		audioElement.addEventListener( 'ended', function() {
			this.play();
		}, false);
	}

	var createControlPanel = function() {
		// Init helper vars
		var activeChecked = '';
		var cpLeft = 'translateX( -' + controlWidth + ' )';

		if ( currentAlert.isActive ) {
			activeChecked = ' checked ';
			cpLeft = 'translateX(0)';
			startRefreshInterval();
		}

		// Wrapper.
		var controls = '\
			<div id="oro-alert-ctrl">\
				<div id="oro-alert-ctrl-icon"></div>\
				<p>\
					<strong>oRO-Price-Alert</strong>\
				</p>';

		// maxPrice input.
		controls += '\
				<p>\
					<label for="oro-price-alert-maxprice">Max-Price:</label>\
					<input id="oro-price-alert-maxprice" type="number" value="' + currentAlert.maxPrice + '">\
				</p>';
		// time inputs
		controls += '\
				<p>\
					<label for="oro-price-alert-refresh-time">Refresh time (min):</label>\
					<span id="oro-price-alert-refresh-time">Between\
						<input id="oro-price-alert-refresh-time-min" type="number" value="' + currentAlert.refreshTimeMin + '" min="3">\
						and\
						<input id="oro-price-alert-refresh-time-max" type="number" value="' + currentAlert.refreshTimeMax + '" min="3">\
					</span>\
				</p>';
		// start watcher checkbox.
		controls += '\
				<p>\
					<label for="oro-price-alert-start">\
						<input type="checkbox" id="oro-price-alert-start" value="1"' + activeChecked + '> Start Pricecheck\
					</label>\
				</p>';

		// closeWrapper
		controls += '</div>';

		// Append.
		$( 'body' ).append( controls );

		// Styles.
		$( '#oro-alert-ctrl' ).css({
			'position': 'fixed',
			'top': $( 'body > nav.navbar ' ).height(),
			'left': '0',
			'display': 'block',
			'width': controlWidth,
			'padding': '8px 16px',
			'background': '#fff',
			'border': '1px solid #000',
			'border-left': 'none',
			'border-bottom-right-radius': '5px',
			'z-index': 999999,
			'transform': cpLeft,
			'transition': 'transform 0.2s ease',
		});
		$( '#oro-alert-ctrl-icon' ).css({
			'position': 'absolute',
			'right': '-32px',
			'top': '-1px',
			'display': 'block',
			'height': '32px',
			'width': '32px',
			'border-top': '1px solid #000',
			'border-right': '1px solid #000',
			'border-bottom': '1px solid #000',
			'background': "#fff url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAPFBMVEX/AP+Zcm9UNkD////cy8ODXl+vhn/u39evp8HDm4+Vi6fKt69tSU+Ze3v/+8ipj4v/8+u4o5s7IzGJZ2sXP3SDAAAAAXRSTlMAQObYZgAAAAFiS0dEAxEMTPIAAAAHdElNRQfjAwoVOix5hYo5AAAAiklEQVQoz4XRQRLDIAgF0PSLpa2Q+PX+d63NZFNDJix5AwIuy008gAtJkmN6Jn3lC5FZjvbvpJ9/KIYDfIJdXFKZnnc1M9d1m2EXXd30NPDIVVprPAlQConu5xoH2It4sH5Dr0KLDmNOZ3gy0+4S1rAOyS2QJmMMWiijUyi/JRFBJ5DDL+uM83fxBQeOBVYPXx0RAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTAzLTEwVDIwOjU4OjQ0KzAxOjAwu7U1cgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wMy0xMFQyMDo1ODo0NCswMTowMMrojc4AAAAASUVORK5CYII=') no-repeat center center",
			'cursor': 'pointer',
			'border-top-right-radius': '5px',
			'border-bottom-right-radius': '5px',
		});

		/* Events. */
		// shows/hides the control.
		$( '#oro-alert-ctrl-icon' ).click( function( e ){
			if ( $( this ).parent().hasClass( 'open' ) ) {
				$( this ).parent().css( { 'transform': 'translateX( -' + controlWidth + ')' } );
				$( this ).parent().removeClass( 'open' );
			} else {
				$( this ).parent().css( { 'transform': 'translateX(0)' } );
				$( this ).parent().addClass( 'open' );
			}
		});

		// change on maxprice
		$( '#oro-price-alert-maxprice' ).on( 'input', function() {
			inputChange( 'maxPrice', $( this ).val() );
		} );
		// change on refreshtimeMin
		$( '#oro-price-alert-refresh-time-min' ).on( 'input', function() {
			inputChange( 'refreshTimeMin', $( this ).val() );
		} );
		// change on refreshTimeMax
		$( '#oro-price-alert-refresh-time-max' ).on( 'input', function() {
			inputChange( 'refreshTimeMax', $( this ).val() );
		} );
		// toggle active
		$( '#oro-price-alert-start' ).change( function() {
			if( $( this ).is( ':checked' ) ) {
				inputChange( 'isActive', true );
				startRefreshInterval();
			} else {
				inputChange( 'isActive', false );
				stopRefreshInterval();
			}
		} );
	}

	var startRefreshInterval = function() {
		if ( tableRows.length > 1 ) {
			for ( var i = 1; i < tableRows.length; i++ ) {
				var rowCells     = $( 'td', $( tableRows[i] ) );
				var priceHTML    = $( rowCells[4] ).html();
				var priceRegex   = /(\d+)/mg;
				var priceNumbers = priceHTML.match( priceRegex );
				var price        = '';
				var maxPrice     = currentAlert.maxPrice;

				$.each( priceNumbers, function( index, number ) {
					price += number;
				} );

				if ( parseInt( price ) <= parseInt( maxPrice ) ) {
					audioElement.play();

					$( tableRows[ i ] ).css({'background-color': 'red'});

					return false;
				}
			}
		}

		var refreshTime = Math.floor( Math.random() * currentAlert.refreshTimeMax * 60 * 1000 ) + currentAlert.refreshTimeMin * 60 * 1000;

		console.log( 'Next Refresh in: ', refreshTime / 1000 / 60 );

		refreshInterval = setInterval( function() {
			location.reload();
		}, refreshTime );
	}

	var stopRefreshInterval = function() {
		clearInterval( refreshInterval );
		audioElement.pause();
	}

	// Helper functions
	var inputChange = function( key, val ) {
		clearTimeout( inputTimeout );
		currentAlert[ key ] = val;
		inputTimeout = setTimeout( saveAlert, 500 );
	}

	var saveAlert = function() {
		var alerts = getLocalStorageAlerts();

		if ( 'object' === alerts ) {
			alerts[ currentUrl ] = currentAlert;
		} else {
			alerts = {};
			alerts[ currentUrl ] = currentAlert;
		}

		localStorage.setItem( localStorageKey, JSON.stringify( alerts ) );

		if ( currentAlert.isActive && null !== refreshInterval ) {
			stopRefreshInterval();
			startRefreshInterval();
		}
	}

	var getLocalStorageAlerts = function() {
		var lsAlerts = localStorage.getItem( localStorageKey );

		return null === lsAlerts ? {} : JSON.parse( lsAlerts );
	}

	init();
})();