<commit_before>const electron = require("electron");
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const path = require("path");
const url = require("url");

const windowStateKeeper = require("electron-window-state");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

function createWindow() {
  const mainWindowState = windowStateKeeper({
    defaultWidth: 350,
    defaultHeight: 420
  });

  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: mainWindowState.width,
    height: mainWindowState.height,
    x: mainWindowState.x,
    y: mainWindowState.y,
    titleBarStyle: "hiddenInset",
    fullscreen: false,
    alwaysOnTop: true
  });

  mainWindowState.manage(mainWindow);

  // and load the index.html of the app.
  mainWindow.loadURL(
    url.format({
      pathname: path.join(__dirname, "index.html"),
      protocol: "file:",
      slashes: true
    })
  );

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on("closed", function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);

// Quit when all windows are closed.
app.on("window-all-closed", function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== "darwin") {
    app.quit();
  }
});

app.on("activate", function() {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow();
  }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<commit_msg>Visible on all workspaces<commit_after>const electron = require("electron");
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const path = require("path");
const url = require("url");

const windowStateKeeper = require("electron-window-state");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

function createWindow() {
  const mainWindowState = windowStateKeeper({
    defaultWidth: 350,
    defaultHeight: 420
  });

  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: mainWindowState.width,
    height: mainWindowState.height,
    x: mainWindowState.x,
    y: mainWindowState.y,
    titleBarStyle: "hiddenInset",
    fullscreen: false,
    alwaysOnTop: true
  });

  mainWindowState.manage(mainWindow);

  mainWindow.setVisibleOnAllWorkspaces(true);

  // and load the index.html of the app.
  mainWindow.loadURL(
    url.format({
      pathname: path.join(__dirname, "index.html"),
      protocol: "file:",
      slashes: true
    })
  );

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on("closed", function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);

// Quit when all windows are closed.
app.on("window-all-closed", function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== "darwin") {
    app.quit();
  }
});

app.on("activate", function() {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow();
  }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<|endoftext|>


<commit_before>/**
 * Main file which distributes tasks.
 */

'use strict'

const fs = require('fs-extra')
const yandex = require('yandex-translate')(process.env.YANDEX_TRANSLATE_KEY)
const path = require('path')

const parse = require('./data/parse')
const inBlackList = require('./data/blackList').inBlackList
const dataIO = require('./data/dataIO')
const transform = require('./data/transform')
const src = require('./src')

const _log_ = require('./utils')._log_
const utils = require('./utils')

_log_('Start', 'onlyCron')
fs.ensureDirSync('./logs/')

for (let adr = 0; adr < src.address.length; adr++) {
  const srcFrom = src.address[adr][0]
  const srcName = `${srcFrom}_${src.address[adr][1]}`
  const srcType = src.types[srcFrom]
  const srcLink = src.address[adr][2]

  _log_(`Start ${srcName}`, 'onlyCron')

  // Paths to auxiliary files
  const newJSON = path.join(__dirname, 'json', `${srcName}_new.json`)
  const oldJSON = path.join(__dirname, 'json', `${srcName}_old.json`)

  const getNewData = dataIO.get(srcName, srcType, srcLink, newJSON, oldJSON)

  if (!getNewData) continue

  // Read data
  const newSrc = dataIO.read(srcFrom, newJSON)
  const oldSrc = dataIO.read(srcFrom, oldJSON)

  // Find new events
  let eventsPosition = dataIO.eventsPosition(srcFrom, newSrc, oldSrc)

  // RSS to API
  while (eventsPosition.length) {
    let title = dataIO.title(srcFrom, newSrc, eventsPosition)
    const link = dataIO.link(srcFrom, newSrc, eventsPosition)
    const data = dataIO.data(srcFrom, newSrc, eventsPosition)

    _log_(`${srcName}: ${link} start\n`)

    // Parse event description
    title = parse.title(srcFrom, title)
    let agenda = parse.agenda(srcFrom, data)
    let whenStart = parse.date(srcFrom, data)
    let whenEnd = parse.date(srcFrom, data)

    if (inBlackList(title, agenda, whenStart, `${link}\n${title}`)) {
      eventsPosition.shift()
      continue
    }

    let social = parse.social(srcFrom, data, link, title, agenda)
    let place = parse.place(srcFrom, data)
    const regUrl = parse.regUrl(srcFrom, data)
    const imgUrl = parse.imgUrl(srcFrom, data)
    let onlyDate = parse.time(srcFrom, data)

    if (onlyDate !== true) {
      whenStart += onlyDate
      whenEnd += parse.time(srcFrom, data)
      onlyDate = false
    }

    // Delete superfluous words
    title = transform.title(title)
    agenda = transform.agenda(agenda)
    social = transform.social(social)
    place = transform.place(place)

    // Yandex Translate can translate a little more 7000 symbols per request.
    const translateMax = 7000

    if (agenda.length > translateMax) {
      agenda = '
Too many. Do we really need this?
'
    }
    // Translate
    let ya = new Promise((resolve, reject) => {
      if (utils.lang === 'ru') {
        yandex.translate(agenda, {'from': 'ru', 'to': 'uk'}, (err, res) => {
          if (err) throw err
          agenda = res.text
          yandex.translate(title, {'from': 'ru', 'to': 'uk'}, (err, res) => {
            if (err) throw err
            title = res.text
            yandex.translate(place, {'from': 'ru', 'to': 'uk'}, (err, res) => {
              if (err) throw err
              place = res.text

              return resolve()
            })
          })
        })
      } else {
        return resolve()
      }
    })

    // Send event to API
    ya.then(() => {
      dataIO.sendtoAPI(title, agenda, social, place, regUrl, imgUrl, whenStart, whenEnd, onlyDate, srcName)

      return Promise.resolve()
    })
    eventsPosition.shift()
  }
}
_log_('End', 'onlyCron')
<commit_msg>Fix method names<commit_after>/**
 * Main file which distributes tasks.
 */

'use strict'

const fs = require('fs-extra')
const yandex = require('yandex-translate')(process.env.YANDEX_TRANSLATE_KEY)
const path = require('path')

const parse = require('./data/parse')
const inBlackList = require('./data/blackList').inBlackList
const dataIO = require('./data/dataIO')
const transform = require('./data/transform')
const src = require('./src')

const _log_ = require('./utils')._log_
const utils = require('./utils')

_log_('Start', 'onlyCron')
fs.ensureDirSync('./logs/')

for (let adr = 0; adr < src.address.length; adr++) {
  const srcFrom = src.address[adr][0]
  const srcName = `${srcFrom}_${src.address[adr][1]}`
  const srcType = src.types[srcFrom]
  const srcLink = src.address[adr][2]

  _log_(`Start ${srcName}`, 'onlyCron')

  // Paths to auxiliary files
  const newJSON = path.join(__dirname, 'json', `${srcName}_new.json`)
  const oldJSON = path.join(__dirname, 'json', `${srcName}_old.json`)

  const getNewData = dataIO.get(srcName, srcType, srcLink, newJSON, oldJSON)

  if (!getNewData) continue

  // Read data
  const newSrc = dataIO.read(srcFrom, newJSON)
  const oldSrc = dataIO.read(srcFrom, oldJSON)

  // Find new events
  let eventsPosition = dataIO.eventsPosition(srcFrom, newSrc, oldSrc)

  // RSS to API
  while (eventsPosition.length) {
    let title = dataIO.title(srcFrom, newSrc, eventsPosition)
    const link = dataIO.link(srcFrom, newSrc, eventsPosition)
    const data = dataIO.data(srcFrom, newSrc, eventsPosition)

    _log_(`${srcName}: ${link} start\n`)

    // Parse event description
    title = parse.title(srcFrom, title)
    let agenda = parse.agenda(srcFrom, data)
    let whenStart = parse.startDate(srcFrom, data)
    let whenEnd = parse.endDate(srcFrom, data)

    if (inBlackList(title, agenda, whenStart, `${link}\n${title}`)) {
      eventsPosition.shift()
      continue
    }

    let social = parse.social(srcFrom, data, link, title, agenda)
    let place = parse.place(srcFrom, data)
    const regUrl = parse.regUrl(srcFrom, data)
    const imgUrl = parse.imgUrl(srcFrom, data)
    let onlyDate = parse.startTime(srcFrom, data)

    if (onlyDate !== true) {
      whenStart += onlyDate
      whenEnd += parse.endTime(srcFrom, data)
      onlyDate = false
    }

    // Delete superfluous words
    title = transform.title(title)
    agenda = transform.agenda(agenda)
    social = transform.social(social)
    place = transform.place(place)

    // Yandex Translate can translate a little more 7000 symbols per request.
    const translateMax = 7000

    if (agenda.length > translateMax) {
      agenda = '
Too many. Do we really need this?
'
    }
    // Translate
    let ya = new Promise((resolve, reject) => {
      if (utils.lang === 'ru') {
        yandex.translate(agenda, {'from': 'ru', 'to': 'uk'}, (err, res) => {
          if (err) throw err
          agenda = res.text
          yandex.translate(title, {'from': 'ru', 'to': 'uk'}, (err, res) => {
            if (err) throw err
            title = res.text
            yandex.translate(place, {'from': 'ru', 'to': 'uk'}, (err, res) => {
              if (err) throw err
              place = res.text

              return resolve()
            })
          })
        })
      } else {
        return resolve()
      }
    })

    // Send event to API
    ya.then(() => {
      dataIO.sendtoAPI(title, agenda, social, place, regUrl, imgUrl, whenStart, whenEnd, onlyDate, srcName)

      return Promise.resolve()
    })
    eventsPosition.shift()
  }
}
_log_('End', 'onlyCron')
<|endoftext|>


<commit_before>const electron = require('electron')

const app = electron.app
const BrowserWindow = electron.BrowserWindow
const Tray = electron.Tray

const ipcMain = require('electron').ipcMain;

const path = require('path')
const url = require('url')

/*

https://medium.com/@ccnokes/how-to-securely-store-sensitive-information-in-electron-with-node-keytar-51af99f1cfc4
https://github.com/atom/node-keytar
https://github.com/whosonfirst/electron-whosonfirst-api-explorer/issues/12

Sigh... (20170418/thisisaaronland)

Uncaught Error: The module
'electron-whosonfirst-api-explorer/node_modules/keytar/build/Release/keytar.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 51. This version of Node.js requires
NODE_MODULE_VERSION 53. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or`npm install`).
*/

// const keytar = require('keytar')
// api_key = keytar.getPassword("whosonfirst", "api_explorer")

let mainWindow

function createWindow () {

	const icon_path = path.join(__dirname, 'images/64x64.png')

	mainWindow = new BrowserWindow({width: 1024, height: 600, icon: icon_path})

	mainWindow.loadURL(url.format({
		pathname: path.join(__dirname, 'index.html'),
		protocol: 'file:',
		slashes: true
	}))

	// mainWindow.webContents.openDevTools()

	mainWindow.on('closed', function () {
		mainWindow = null
	})
}

app.on('ready', createWindow)

app.on('window-all-closed', function () {
	if (process.platform !== 'darwin') {
		app.quit();
	}
})

app.on('activate', function () {
	if (mainWindow === null) {
		createWindow();
	}
})

ipcMain.on('renderer', (event, arg) => {

	/*
	if (arg == "question"){
		const d = require('electron').dialog;
		d.showMessageBox(mainWindow, {'type': 'question', 'message': 'what', 'buttons': ['submit', 'cancel']})
	}
	*/

	if (arg == "print"){

		// https://github.com/whosonfirst/electron-whosonfirst-api-explorer/issues/10
		// https://electron.atom.io/docs/api/web-contents/#contentsprinttopdfoptions-callback

		let contents = mainWindow.webContents;

		contents.print();
		return;

		// basically all this to default to a PDF file but still only
		// "prints" a single page/viewport rather than all the content
		// (20170419/thisisaaronland)

		var opts = {
			marginsType: 0,
			printBackground: false,
			printSelectionOnly: false,
			landscape: false
		};

		var cb = function(err, buf){

			if (err){
				throw error;
			}

			const remote = require("electron");
			const fs = require("fs");

			remote.dialog.showSaveDialog(BrowserWindow.getCurrentWindow, (filename) => {

				fs.writeFile(filename, buf, (error) => {

					if (error){
						throw error;
					}
				});
			});
		};

		contents.printToPDF(opts, cb);
	}
})
<commit_msg>add menu stuff to ensure copy/paste works (in native apps)<commit_after>const electron = require('electron')

const app = electron.app
const BrowserWindow = electron.BrowserWindow
const Menu = electron.Menu

const ipcMain = require('electron').ipcMain;

const path = require('path')
const url = require('url')

/*

https://medium.com/@ccnokes/how-to-securely-store-sensitive-information-in-electron-with-node-keytar-51af99f1cfc4
https://github.com/atom/node-keytar
https://github.com/whosonfirst/electron-whosonfirst-api-explorer/issues/12

Sigh... (20170418/thisisaaronland)

Uncaught Error: The module
'electron-whosonfirst-api-explorer/node_modules/keytar/build/Release/keytar.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 51. This version of Node.js requires
NODE_MODULE_VERSION 53. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or`npm install`).
*/

// const keytar = require('keytar')
// api_key = keytar.getPassword("whosonfirst", "api_explorer")

let mainWindow

function createWindow () {

	const icon_path = path.join(__dirname, 'images/64x64.png')

	mainWindow = new BrowserWindow({width: 1024, height: 600, icon: icon_path})

	mainWindow.loadURL(url.format({
		pathname: path.join(__dirname, 'index.html'),
		protocol: 'file:',
		slashes: true
	}))

	// mainWindow.webContents.openDevTools()

	mainWindow.on('closed', function () {
		mainWindow = null
	})
}

app.on('ready', function(){

	createWindow();

	// https://pracucci.com/atom-electron-enable-copy-and-paste.html

	var template = [{
		label: "Application",
		submenu: [
			{ label: "About Application", selector: "orderFrontStandardAboutPanel:" },
			{ type: "separator" },
			{ label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
		]}, {
			label: "Edit",
			submenu: [
				{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
				{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
				{ type: "separator" },
				{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
				{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
				{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
				{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
			]}
		       ];

	Menu.setApplicationMenu(Menu.buildFromTemplate(template));

});

app.on('window-all-closed', function () {
	if (process.platform !== 'darwin') {
		app.quit();
	}
})

app.on('activate', function () {
	if (mainWindow === null) {
		createWindow();
	}
})

ipcMain.on('renderer', (event, arg) => {

	/*
	if (arg == "question"){
		const d = require('electron').dialog;
		d.showMessageBox(mainWindow, {'type': 'question', 'message': 'what', 'buttons': ['submit', 'cancel']})
	}
	*/

	if (arg == "print"){

		// https://github.com/whosonfirst/electron-whosonfirst-api-explorer/issues/10
		// https://electron.atom.io/docs/api/web-contents/#contentsprinttopdfoptions-callback

		let contents = mainWindow.webContents;

		contents.print();
		return;

		// basically all this to default to a PDF file but still only
		// "prints" a single page/viewport rather than all the content
		// (20170419/thisisaaronland)

		var opts = {
			marginsType: 0,
			printBackground: false,
			printSelectionOnly: false,
			landscape: false
		};

		var cb = function(err, buf){

			if (err){
				throw error;
			}

			const remote = require("electron");
			const fs = require("fs");

			remote.dialog.showSaveDialog(BrowserWindow.getCurrentWindow, (filename) => {

				fs.writeFile(filename, buf, (error) => {

					if (error){
						throw error;
					}
				});
			});
		};

		contents.printToPDF(opts, cb);
	}
})
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var app = require('app');
var path = require('path');
var BrowserWindow = require('browser-window');

// ####################################################
// ####################################################

// Report crashes to our server.
require('crash-reporter').start();

var mainWindow = null;
var options = {
	"debug": true,
	"version": "1.0.0",
	"views_dir": "views",
	"root_view": "index.html"
};

options = _.extend({
	// ADDITIONAL CUSTOM SETTINGS
}, options);

// ############################################################################################
// ############################################################################################

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  if(process.platform !== 'darwin') { app.quit(); }
});

app.on('ready', function() {
  mainWindow = new BrowserWindow({width: 800, height: 600});
  mainWindow.loadUrl(path.join('file://', __dirname, options.views_dir, options.root_view));
  if(options.debug) { mainWindow.openDevTools(); }
  mainWindow.on('closed', function() { mainWindow = null; });
});

// ############################################################################################
// ############################################################################################
<commit_msg>Update function names<commit_after>'use strict';

var _ = require('lodash');
const {
	app,
	BrowserWindow,
	crashReporter
} = require('electron')
var path = require('path');

// ####################################################
// ####################################################

// Report crashes to our server.
crashReporter.start({
  productName: 'YourName',
  companyName: 'YourCompany',
  submitURL: 'https://your-domain.com/url-to-submit',
  autoSubmit: true
})

var mainWindow = null;
var options = {
	"debug": true,
	"version": "1.0.0",
	"views_dir": "views",
	"root_view": "index.html"
};

options = _.extend({
	// ADDITIONAL CUSTOM SETTINGS
}, options);

// ############################################################################################
// ############################################################################################

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  if(process.platform !== 'darwin') { app.quit(); }
});

app.on('ready', function() {
  mainWindow = new BrowserWindow({width: 800, height: 600});
  mainWindow.loadURL(path.join('file://', __dirname, options.views_dir, options.root_view));
  if(options.debug) { mainWindow.openDevTools(); }
  mainWindow.on('closed', function() { mainWindow = null; });
});

// ############################################################################################
// ############################################################################################
<|endoftext|>


<commit_before>/**
 * This is the main file of Pokémon Showdown Bot
 *
 * Some parts of this code are taken from the Pokémon Showdown server code, so
 * credits also go to Guangcong Luo and other Pokémon Showdown contributors.
 * https://github.com/Zarel/Pokemon-Showdown
 *
 * @license MIT license
 */

const MESSAGE_THROTTLE = 650;

function runNpm(command) {
	console.log('Running `npm ' + command + '`...');

	var child_process = require('child_process');
	var npm = child_process.spawn('npm', [command]);

	npm.stdout.on('data', function (data) {
		process.stdout.write(data);
	});

	npm.stderr.on('data', function (data) {
		process.stderr.write(data);
	});

	npm.on('close', function (code) {
		if (!code) {
			child_process.fork('main.js').disconnect();
		}
	});
}

// First dependencies and welcome message
try {
	require('babel/register')({loose: 'all'});
	require('sugar');
	global.colors = require('colors');
} catch (e) {
	console.log('Dependencies are not installed!');
	return runNpm('install');
}

global.info = function (text) {
	if (Config.debuglevel > 3) return;
	console.log('info'.cyan + '  ' + text);
};

global.debug = function (text) {
	if (Config.debuglevel > 2) return;
	console.log('debug'.blue + ' ' + text);
};

global.recv = function (text) {
	if (Config.debuglevel > 0) return;
	console.log('recv'.grey + '  ' + text);
};

global.cmdr = function (text) { // receiving commands
	if (Config.debuglevel !== 1) return;
	console.log('cmdr'.grey + '  ' + text);
};

global.dsend = function (text) {
	if (Config.debuglevel > 1) return;
	console.log('send'.grey + '  ' + text);
};

global.error = function (text) {
	console.log('error'.red + ' ' + text);
};

global.ok = function (text) {
	if (Config.debuglevel > 4) return;
	console.log('ok'.green + '    ' + text);
};

console.log('------------------------------------'.yellow);
console.log('| Welcome to Pokemon Showdown Bot! |'.yellow);
console.log('------------------------------------'.yellow);
console.log('');

global.toId = function (text) {
	return text.toLowerCase().replace(/[^a-z0-9]/g, '');
};

global.stripCommands = function (text) {
	text = text.trim();
	if (text.charAt(0) === '/') return '/' + text;
	if (text.charAt(0) === '!' || /^>>>? /.test(text)) return ' ' + text;
	return text;
};

// Config and config.js watching...
try {
	global.Config = require('./config.js');
} catch (e) {
	error('config.js doesn\'t exist; are you sure you copied config-example.js to config.js?');
	process.exit(-1);
}

var checkCommandCharacter = function () {
	if (!/[^a-z0-9 ]/i.test(Config.commandcharacter)) {
		error('invalid command character; should at least contain one non-alphanumeric character');
		process.exit(-1);
	}
};

checkCommandCharacter();

var fs = require('fs');
if (Config.watchconfig) {
	fs.watchFile('./config.js', function (curr, prev) {
		if (curr.mtime <= prev.mtime) return;
		try {
			delete require.cache[require.resolve('./config.js')];
			Config = require('./config.js');
			info('reloaded config.js');
			checkCommandCharacter();
		} catch (e) {}
	});
}

// And now comes the real stuff...
info('starting server');

var WebSocketClient = require('websocket').client;
global.Commands = require('./commands.js').commands;
global.Users = require('./users.js');
global.Rooms = require('./rooms.js');
global.Parse = require('./parser.js').parse;
global.Connection = null;

var queue = [];
var dequeueTimeout = null;
var lastSentAt = 0;

global.send = function (data) {
	if (!Connection.connected) return false;

	var now = Date.now();
	if (now < lastSentAt + MESSAGE_THROTTLE - 5) {
		queue.push(data);
		if (!dequeueTimeout) {
			dequeueTimeout = setTimeout(dequeue, now - lastSentAt + MESSAGE_THROTTLE);
		}
		return false;
	}

	if (!Array.isArray(data)) data = [data.toString()];
	data = JSON.stringify(data);
	dsend(data);
	Connection.send(data);

	lastSentAt = now;
	if (dequeueTimeout) {
		if (queue.length) {
			dequeueTimeout = setTimeout(dequeue, MESSAGE_THROTTLE);
		} else {
			dequeueTimeout = null;
		}
	}
};

function dequeue() {
	send(queue.shift());
}

var connect = function (retry) {
	if (retry) {
		info('retrying...');
	}

	var ws = new WebSocketClient();

	ws.on('connectFailed', function (err) {
		error('Could not connect to server ' + Config.server + ': ' + err.stack);
		info('retrying in one minute');

		setTimeout(function () {
			connect(true);
		}, 60000);
	});

	ws.on('connect', function (con) {
		Connection = con;
		ok('connected to server ' + Config.server);

		con.on('error', function (err) {
			error('connection error: ' + err.stack);
		});

		con.on('close', function (code, reason) {
			// Is this always error or can this be intended...?
			error('connection closed: ' + reason + ' (' + code + ')');
			info('retrying in one minute');

			for (var i in Users.users) {
				delete Users.users[i];
			}
			Rooms.rooms.clear();
			setTimeout(function () {
				connect(true);
			}, 60000);
		});

		con.on('message', function (response) {
			if (response.type !== 'utf8') return false;
			var message = response.utf8Data;
			recv(message);

			// SockJS messages sent from the server begin with 'a'
			// this filters out other SockJS response types (heartbeats in particular)
			if (message.charAt(0) !== 'a') return false;
			Parse.data(message);
		});
	});

	// The connection itself
	var id = ~~(Math.random() * 1000);
	var chars = 'abcdefghijklmnopqrstuvwxyz0123456789_';
	var str = '';
	for (var i = 0, l = chars.length; i < 8; i++) {
		str += chars.charAt(~~(Math.random() * l));
	}

	var conStr = 'ws://' + Config.server + ':' + Config.port + '/showdown/' + id + '/' + str + '/websocket';
	info('connecting to ' + conStr + ' - secondary protocols: ' + (Config.secprotocols.join(', ') || 'none'));
	ws.connect(conStr, Config.secprotocols);
};

connect();
<commit_msg>Fix crash in message queue<commit_after>/**
 * This is the main file of Pokémon Showdown Bot
 *
 * Some parts of this code are taken from the Pokémon Showdown server code, so
 * credits also go to Guangcong Luo and other Pokémon Showdown contributors.
 * https://github.com/Zarel/Pokemon-Showdown
 *
 * @license MIT license
 */

const MESSAGE_THROTTLE = 650;

function runNpm(command) {
	console.log('Running `npm ' + command + '`...');

	var child_process = require('child_process');
	var npm = child_process.spawn('npm', [command]);

	npm.stdout.on('data', function (data) {
		process.stdout.write(data);
	});

	npm.stderr.on('data', function (data) {
		process.stderr.write(data);
	});

	npm.on('close', function (code) {
		if (!code) {
			child_process.fork('main.js').disconnect();
		}
	});
}

// First dependencies and welcome message
try {
	require('babel/register')({loose: 'all'});
	require('sugar');
	global.colors = require('colors');
} catch (e) {
	console.log('Dependencies are not installed!');
	return runNpm('install');
}

global.info = function (text) {
	if (Config.debuglevel > 3) return;
	console.log('info'.cyan + '  ' + text);
};

global.debug = function (text) {
	if (Config.debuglevel > 2) return;
	console.log('debug'.blue + ' ' + text);
};

global.recv = function (text) {
	if (Config.debuglevel > 0) return;
	console.log('recv'.grey + '  ' + text);
};

global.cmdr = function (text) { // receiving commands
	if (Config.debuglevel !== 1) return;
	console.log('cmdr'.grey + '  ' + text);
};

global.dsend = function (text) {
	if (Config.debuglevel > 1) return;
	console.log('send'.grey + '  ' + text);
};

global.error = function (text) {
	console.log('error'.red + ' ' + text);
};

global.ok = function (text) {
	if (Config.debuglevel > 4) return;
	console.log('ok'.green + '    ' + text);
};

console.log('------------------------------------'.yellow);
console.log('| Welcome to Pokemon Showdown Bot! |'.yellow);
console.log('------------------------------------'.yellow);
console.log('');

global.toId = function (text) {
	return text.toLowerCase().replace(/[^a-z0-9]/g, '');
};

global.stripCommands = function (text) {
	text = text.trim();
	if (text.charAt(0) === '/') return '/' + text;
	if (text.charAt(0) === '!' || /^>>>? /.test(text)) return ' ' + text;
	return text;
};

// Config and config.js watching...
try {
	global.Config = require('./config.js');
} catch (e) {
	error('config.js doesn\'t exist; are you sure you copied config-example.js to config.js?');
	process.exit(-1);
}

var checkCommandCharacter = function () {
	if (!/[^a-z0-9 ]/i.test(Config.commandcharacter)) {
		error('invalid command character; should at least contain one non-alphanumeric character');
		process.exit(-1);
	}
};

checkCommandCharacter();

var fs = require('fs');
if (Config.watchconfig) {
	fs.watchFile('./config.js', function (curr, prev) {
		if (curr.mtime <= prev.mtime) return;
		try {
			delete require.cache[require.resolve('./config.js')];
			Config = require('./config.js');
			info('reloaded config.js');
			checkCommandCharacter();
		} catch (e) {}
	});
}

// And now comes the real stuff...
info('starting server');

var WebSocketClient = require('websocket').client;
global.Commands = require('./commands.js').commands;
global.Users = require('./users.js');
global.Rooms = require('./rooms.js');
global.Parse = require('./parser.js').parse;
global.Connection = null;

var queue = [];
var dequeueTimeout = null;
var lastSentAt = 0;

global.send = function (data) {
	if (!data || !Connection.connected) return false;

	var now = Date.now();
	if (now < lastSentAt + MESSAGE_THROTTLE - 5) {
		queue.push(data);
		if (!dequeueTimeout) {
			dequeueTimeout = setTimeout(dequeue, now - lastSentAt + MESSAGE_THROTTLE);
		}
		return false;
	}

	if (!Array.isArray(data)) data = [data.toString()];
	data = JSON.stringify(data);
	dsend(data);
	Connection.send(data);

	lastSentAt = now;
	if (dequeueTimeout) {
		if (queue.length) {
			dequeueTimeout = setTimeout(dequeue, MESSAGE_THROTTLE);
		} else {
			dequeueTimeout = null;
		}
	}
};

function dequeue() {
	send(queue.shift());
}

var connect = function (retry) {
	if (retry) {
		info('retrying...');
	}

	var ws = new WebSocketClient();

	ws.on('connectFailed', function (err) {
		error('Could not connect to server ' + Config.server + ': ' + err.stack);
		info('retrying in one minute');

		setTimeout(function () {
			connect(true);
		}, 60000);
	});

	ws.on('connect', function (con) {
		Connection = con;
		ok('connected to server ' + Config.server);

		con.on('error', function (err) {
			error('connection error: ' + err.stack);
		});

		con.on('close', function (code, reason) {
			// Is this always error or can this be intended...?
			error('connection closed: ' + reason + ' (' + code + ')');
			info('retrying in one minute');

			for (var i in Users.users) {
				delete Users.users[i];
			}
			Rooms.rooms.clear();
			setTimeout(function () {
				connect(true);
			}, 60000);
		});

		con.on('message', function (response) {
			if (response.type !== 'utf8') return false;
			var message = response.utf8Data;
			recv(message);

			// SockJS messages sent from the server begin with 'a'
			// this filters out other SockJS response types (heartbeats in particular)
			if (message.charAt(0) !== 'a') return false;
			Parse.data(message);
		});
	});

	// The connection itself
	var id = ~~(Math.random() * 1000);
	var chars = 'abcdefghijklmnopqrstuvwxyz0123456789_';
	var str = '';
	for (var i = 0, l = chars.length; i < 8; i++) {
		str += chars.charAt(~~(Math.random() * l));
	}

	var conStr = 'ws://' + Config.server + ':' + Config.port + '/showdown/' + id + '/' + str + '/websocket';
	info('connecting to ' + conStr + ' - secondary protocols: ' + (Config.secprotocols.join(', ') || 'none'));
	ws.connect(conStr, Config.secprotocols);
};

connect();
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 MKLab. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, regexp: true */
/*global define, $, _, window, app, type, appshell, document */

define(function (require, exports, module) {
    "use strict";

});
<commit_msg>add menus<commit_after>/*
 * Copyright (c) 2014 MKLab. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, regexp: true */
/*global define, $, _, window, app, type, appshell, document */

define(function (require, exports, module) {
    "use strict";

    var Repository       = app.getModule("core/Repository"),
        Engine           = app.getModule("engine/Engine"),
        SelectionManager = app.getModule("engine/SelectionManager"),
        Factory          = app.getModule("engine/Factory"),
        Commands         = app.getModule("command/Commands"),
        CommandManager   = app.getModule("command/CommandManager"),
        MenuManager      = app.getModule("menu/MenuManager"),
        Dialogs          = app.getModule("dialogs/Dialogs"),
        DiagramManager   = app.getModule("diagrams/DiagramManager");

    /**
     * Commands IDs
     */
    var CMD_CONVERT_DIAGRAM = 'convertDiagram',
        CMD_SEQ_TO_COMM     = 'convertDiagram.seq2comm',
        CMD_COMM_TO_SEQ     = 'convertDiagram.comm2seq';

    /**
     * Return a connector connecting between the two given lifelines
     * If no connector, then create one and return.
     *
     * @param {UMLLifeline} lifeline1
     * @param {UMLLifeline} lifeline2
     * @return {UMLConnector}
     */
    function getConnector(lifeline1, lifeline2) {
        var role1 = lifeline1.represent,
            role2 = lifeline2.represent,
            conns = Repository.getRelationshipsOf(role1, function (r) {
                return (r instanceof type.UMLConnector) &&
                    ((r.end1.reference === role1 && r.end2.reference === role2) || (r.end1.reference === role2 && r.end2.reference === role1));
            });

        if (conns.length === 0) {
            var newConn = Factory.createModel("UMLConnector", role1, "ownedElements", {
                modelInitializer: function (m) {
                    m.end1.reference = role1;
                    m.end2.reference = role2;
                }
            });
            return newConn;
        } else {
            return conns[0];
        }
    }

    /**
     * Convert Sequence diagram to Communication diagram
     */
    function convertSeqToComm(seq) {
        // create a communication diagram
        var interaction = seq._parent,
            comm = Factory.createDiagram("UMLCommunicationDiagram", interaction, {
            diagramInitializer: function (d) {
                d.name = seq.name;
            }
        });

        // create connectors if absent
        interaction.messages.forEach(function (m) {
            if (m instanceof type.UMLMessage) {
                if (!m.connector) {
                    var conn = getConnector(m.source, m.target);
                }
            }
        });

        // create lifelines
        interaction.participants.forEach(function (p) {
            if (p instanceof type.UMLLifeline) {
                Factory.createViewOf(p, comm, { editor: DiagramManager.getEditor() });
            }
        });

        // create messages
        interaction.messages.forEach(function (m) {
            if (m instanceof type.UMLMessage) {
                if (!m.connector) {
                    // create connector
                }
            }
        });

        // layout diagram
        // ...
    }

    /**
     * Convert Communication diagram to Sequence diagram
     */
    function convertCommToSeq(comm) {

        var interaction = comm._parent;

        interaction.messages.forEach(function (m) {
            var cons = getConnector(m.source, m.target);
            console.log(m, cons);
        });

    }

    /**
     * Handle for Seq2Comm Command
     */
    function _handleSeqToComm() {
        // Select active or selected sequence diagram
        var dgm = DiagramManager.getCurrentDiagram();
        if (!(dgm instanceof type.UMLSequenceDiagram)) {
            dgm = SelectionManager.getSelected();
        }
        if (dgm instanceof type.UMLSequenceDiagram) {
            Dialogs.showConfirmDialog(
                "Create a Communication Diagram converted from '" + dgm.name + "'?"
            ).done(function (buttonId) {
                if (buttonId === Dialogs.DIALOG_BTN_OK) {
                    convertSeqToComm(dgm);
                }
            });
        }
    }

    /**
     * Handle for Comm2Seq Command
     */
    function _handleCommToSeq() {
        // Select active or selected sequence diagram
        var dgm = DiagramManager.getCurrentDiagram();
        if (!(dgm instanceof type.UMLCommunicationDiagram)) {
            dgm = SelectionManager.getSelected();
        }
        if (dgm instanceof type.UMLCommunicationDiagram) {
            Dialogs.showConfirmDialog(
                "Create a Sequence Diagram converted from '" + dgm.name + "'?"
            ).done(function (buttonId) {
                if (buttonId === Dialogs.DIALOG_BTN_OK) {
                    convertCommToSeq(dgm);
                }
            });
        }
    }

    // Register Commands
    CommandManager.register("Convert Diagram",           CMD_CONVERT_DIAGRAM, CommandManager.doNothing);
    CommandManager.register("Sequence to Communication", CMD_SEQ_TO_COMM,     _handleSeqToComm);
    CommandManager.register("Communication to Sequence", CMD_COMM_TO_SEQ,     _handleCommToSeq);

    var menu, menuItem;
    menu = MenuManager.getMenu(Commands.TOOLS);
    menuItem = menu.addMenuItem(CMD_CONVERT_DIAGRAM);
    menuItem.addMenuItem(CMD_SEQ_TO_COMM);
    menuItem.addMenuItem(CMD_COMM_TO_SEQ);

});
<|endoftext|>


<commit_before>Date.prototype.toYMD = Date_toYMD;
function Date_toYMD() {
	var year = String(this.getFullYear()),
	month = ('00' + (this.getUTCMonth()+1)).slice(-2),
	day = ('00' + this.getUTCDate()).slice(-2),
	hours = ('00' + this.getUTCHours()).slice(-2),
	minutes = ('00' + this.getUTCMinutes()).slice(-2),
	seconds = ('00' + this.getUTCSeconds()).slice(-2);
	return year + "-" + month + "-" + day + 'T' + hours + ':' + minutes + ':' + seconds;
}

function passToWif(login, pass) {
	if(pass.charAt(0) == '5' && pass.length == 51) {
		return pass;
	} else {
		return steem.auth.toWif(login, pass, 'active');
	}
}

function gup( name ) {
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
	return "";
  else
	return results[1];
}

function Transaction(id, onLoadCallback) {
	if(!id.match( /^[a-z0-9\.\-]+-[\d]+$/ )) {
		alert('Не верный ID ' + id);
		return false;
	}

	var parts = id.split('-'),
		obj = this;
	obj.from = parts[0];
	obj.escrow_id = parts[1];

	steem.api.getEscrow(
		obj.from,
		obj.escrow_id,
		function(err, response) {
			if(!err && !response) {
				$('#result div.alert-danger').slideDown();
			} else if (typeof response == 'object') {
				console.log(response);
				$.each(response, function(index, value) {
					obj[index] = value;
				});
				if(obj.steem_balance == '0.000 GOLOS') {
					obj.money = obj.sbd_balance;
				} else {
					obj.money = obj.steem_balance;
				}

				if(!obj.agent_approved || !obj.to_approved) {
					obj.status = 'waitingForApproval';
				} else if (obj.disputed) {
					obj.status = 'disputeInitiated';
				} else {
					obj.status = 'approvalRecieved';
				}

				onLoadCallback(obj);
			}
			console.log(err, obj);
		}
	);

	obj.submitApprove = function(login, pass, approve, callback) {
		steem.broadcast.escrowApprove(
			passToWif(login, pass),
			obj.from,
			obj.to,
			obj.agent,
			login,
			obj.escrow_id,
			approve,
			function(err, response) {
				console.log(err, response);
				callback();
			}
		);
	}

	obj.submitRelease = function(login, pass, callback) {
			steem.broadcast.escrowRelease(
				passToWif(login, pass),
				obj.from,
				obj.to,
				obj.agent,
				login,
				login == obj.from ? obj.to : obj.from,
				obj.escrow_id,
				obj.sbd_balance,
				obj.steem_balance,
				function(err, response) {
					console.log(err, response);
					callback();
				}
			);
	}

	obj.submitDispute = function(login, pass, callback) {
		steem.broadcast.escrowDispute(
			passToWif(login, pass),
			obj.from,
			obj.to,
			obj.agent,
			login,
			obj.escrow_id,
			function(err, response) {
				console.log(err, response);
				callback();
			}
		);
	}

	obj.submitEscrow = function(login, pass, reciever, callback) {
		steem.broadcast.escrowRelease(
			passToWif(login, pass),
			obj.from,
			obj.to,
			obj.agent,
			login,
			obj[reciever],
			obj.escrow_id,
			obj.sbd_balance,
			obj.steem_balance,
			function(err, response) {
				console.log(err, response);
				callback();
			}
		);
	}

}

$(function() {
	$('a.inputToggle').click(function() {
		var toggles = $(this).data('toggles').split('-');
		if($(this).text() == toggles[0]) {
			$('.inputToggle').text(toggles[1]);
		} else {
			$('.inputToggle').text(toggles[0]);
		}
		return false;
	});

	$('a#tabCP').click(function() {
		if(!id) {
			id = prompt('Введите ID транзакции. Например xtar-8724723:');
			if(!id) {
				return false;
			}
			transaction = new Transaction(id);
			if(transaction.from === undefined) {
				id = null;
				return false;
			}
		}
		window.history.pushState('',id, '?id=' + id);
		$(this).parent().addClass('active').siblings().removeClass('active');
		$('#step1, #step2').hide();
		$('#controlPanel').show();
		return false;
	});

	$('a#tabSend').click(function() {
		$(this).parent().addClass('active').siblings().removeClass('active');
		$('#step1').show();
		$('#controlPanel').hide();
		return false;
	});

	$('#sendSubmit').click(function() {
		var btn = $(this),
		from = $('#sendLogin').val(),
		to = $('#sendReceiver').val(),
		wif = passToWif(from, $('#sendPassword').val()),
		agent = $('#sendAgent').val(),
		escrow_id = parseInt(Math.random() * (99999999 - 10000000) + 10000000),
		fee = parseFloat($('#sendAgent option:selected').data('fee')).toFixed(3) + ' ' + $('#sendAmountUnit').text(),
		sbd_amount = '0.000 GBG',
		steem_amount = '0.000 GOLOS',
		meta = {
			meta: $('#sendMeta').val()
		};

		btn.prop('disabled', true);

		if($('#sendAmountUnit').text() == 'GOLOS') {
			steem_amount = parseFloat($('#sendAmount').val().replace(',','.')).toFixed(3) + ' GOLOS';
		} else if ($('#sendAmountUnit').text() == 'GBG') {
			sbd_amount = parseFloat($('#sendAmount').val().replace(',','.')).toFixed(3) + ' GBG';
		}

		steem.api.getDynamicGlobalProperties(function(err, response) {
			// Added 'Z' to get correct UTC time in all browsers
			var ratification_deadline = new Date(response.time+'Z');
			ratification_deadline.setMinutes(ratification_deadline.getMinutes() + 24 * 60 - 1);

			var escrow_expiration = new Date(response.time+'Z');
			escrow_expiration.setHours(escrow_expiration.getHours() + parseInt($('#sendEscrowExpiration').val()));

			steem.broadcast.escrowTransfer(
				wif, // active key
				from,
				to, // to
				agent, // escrow nick
				escrow_id,
				sbd_amount, // amount gbg
				steem_amount, // amount golos
				fee, // fee
				ratification_deadline,
				escrow_expiration,
				JSON.stringify(meta),
				function(err, response) {
					if(!err && response.ref_block_num) {
						if(from) {
							localStorage.setItem('escrow_login', from);
						}
						$('#sendError').slideUp();
						$('#step1').slideUp();
						$('#step2').slideDown();
						$('.sentLink').html('https://golosim.ru/escrow/?id=' + from + '-' + escrow_id + '');
						$('.sentId').html(from + '-' + escrow_id);
					} else {
						$('#sendError').html(
							'
Возникла ошибка:

' +
							err.payload.error.message.replace(/([^>])\n/g, '$1

')
						);
						btn.prop('disabled', false);
					}
					console.log(err, response);
				}
			);
		});
	});
	$('#escrowData').on('click', 'button', function() {
		$('#transactionType').val($(this).data('form'));
		$('form').hide();
		$('#' + $(this).data('form')).show();
		$('#sendTransaction').hide();
		$('#sendTransaction').slideDown();
		$('input[type=submit]').prop('disabled', false); // fix firefox page refresh
		$('#login').val(transaction[$(this).data('nick')]);
		$('#password').val('').focus();
		if($(this).data('answer') == '1') {
			$('#approveYes').prop('checked', true);
		} else if($(this).data('answer') == '0') {
			$('#approveNo').prop('checked', true);
		}
		if($(this).data('answer') == 'escrowFrom') {
			$('#escrowFrom').prop('checked', true);
		} else if($(this).data('answer') == 'escrowTo') {
			$('#escrowTo').prop('checked', true);
		}
	});

	$('#approveForm').submit(function(e) {
		$(this).find('input[type=submit]').prop('disabled', true);
		transaction.submitApprove(
			$('#login').val(),
			$('#password').val(),
			$('#approveYes').is(':checked') ? 'true' : 'false',
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	$('#releaseForm, #cancelForm').submit(function(e) {
		$(this).find('input[type=submit]').prop('disabled', true);
		e.preventDefault();
		transaction.submitRelease(
			$('#login').val(),
			$('#password').val(),
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	$('#startDisputForm').submit(function(e) {
		$(this).find('input[type=submit]').prop('disabled', true);
		e.preventDefault();
		transaction.submitDispute(
			$('#login').val(),
			$('#password').val(),
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	$('#escrowForm').submit(function() {
		$(this).find('input[type=submit]').prop('disabled', true);
		transaction.submitEscrow(
			$('#login').val(),
			$('#password').val(),
			($('#escrowFrom').is(':checked') ? 'from' : 'to'),
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	if(localStorage.getItem('escrow_login') !== undefined && localStorage.getItem('escrow_login')) {
		$('#sendLogin').val(localStorage.getItem('escrow_login'));
	}

	var transaction,
	id = gup('id');

	$('#sendAgent').change(function() {
		var agent = $('#sendAgent').val();
		$('#sendFeeAmoutLabel').html('Комиссия от @' + agent + '');
		$('#sendFeeAmout').val($('#sendAgent option:selected').data('fee'));
	});

	// добавляем гарантов из блокчейна, сортируем по репутации
	steem.api.getContentReplies('xtar', 'khochesh-stat-garantom-bud-im', function(err, result) {
		var agents = [];
		if(!err) {
			if(result.length) {
				$('#sendAgent').html('');
			}
			result.sort(function(a, b) {
				return a.author_reputation < b.author_reputation;
			});
			console.log(result);
			$.each(result, function(index, val) {
				if(val.body !== undefined) {
					var matches = val.body.match(/^([0-9.]+) (.*)/);
					if(matches && matches.length == 3) {
						$('#sendAgent').append('<option value="' + val.author + '" data-fee="' + parseFloat(matches[1]).toFixed(3) + '">' + val.author + ', репутация ' + (Math.max( Math.log10(Math.abs(parseInt(val.author_reputation))) - 9, 0) * (parseInt(val.author_reputation) > 0 ? 1 : -1) * 9 + 25).toFixed(1) + ', комиссия ' + parseFloat(matches[1]).toFixed(3) + ', ' + matches[2] + '</option>');
					}
				}
			});
			$('#sendAgent').prop('disabled', false).change();
		}
	});

	if(id) {
		transaction = new Transaction(gup('id'), function(transaction) {
			$('div.' + transaction.status).show();
			if(!transaction.to_approved) {
				$('div.waitingForApprovalTo').show();
			}
			if(!transaction.agent_approved) {
				$('div.waitingForApprovalAgent').show();
			}
			$('span.transactionId').html('' + transaction.from + '-' + transaction.escrow_id + '');
			$('span.transactionFrom').html('@' + transaction.from + '');
			$('span.transactionTo').html('@' + transaction.to + '');
			$('span.transactionAgent').html('@' + transaction.agent + '');
			$('span.transactionMoney').html(transaction.money);
			$('span.transactionFee').html(transaction.pending_fee);
			$('span.transactionDate').html(transaction.escrow_expiration);
		});
		if(transaction.from === undefined) {
			id = null;
			$('#tabSend').click();
		} else {
			$('#tabCP').click();
		}
	} else {

		$('#tabSend').click();

		if($('#sendLogin').val()) {
			$('#sendPassword').focus();
		} else {
			$('#sendLogin').focus();
		}
	}

});
<commit_msg>Small fixes<commit_after>function passToWif(login, pass) {
	if(pass.charAt(0) == '5' && pass.length == 51) {
		return pass;
	} else {
		return steem.auth.toWif(login, pass, 'active');
	}
}

function gup( name ) {
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
	return "";
  else
	return results[1];
}

function Transaction(id, onLoadCallback) {
	if(!id.match( /^[a-z0-9\.\-]+-[\d]+$/ )) {
		alert('Не верный ID ' + id);
		return false;
	}

	var parts = id.split('-'),
		obj = this;
	obj.from = parts[0];
	obj.escrow_id = parts[1];

	steem.api.getEscrow(
		obj.from,
		obj.escrow_id,
		function(err, response) {
			if(!err && !response) {
				$('#result div.alert-danger').slideDown();
			} else if (typeof response == 'object') {
				console.log(response);
				$.each(response, function(index, value) {
					obj[index] = value;
				});
				if(obj.steem_balance == '0.000 GOLOS') {
					obj.money = obj.sbd_balance;
				} else {
					obj.money = obj.steem_balance;
				}

				if(!obj.agent_approved || !obj.to_approved) {
					obj.status = 'waitingForApproval';
				} else if (obj.disputed) {
					obj.status = 'disputeInitiated';
				} else {
					obj.status = 'approvalRecieved';
				}

				onLoadCallback(obj);
			}
			console.log(err, obj);
		}
	);

	obj.submitApprove = function(login, pass, approve, callback) {
		steem.broadcast.escrowApprove(
			passToWif(login, pass),
			obj.from,
			obj.to,
			obj.agent,
			login,
			obj.escrow_id,
			approve,
			function(err, response) {
				console.log(err, response);
				callback();
			}
		);
	}

	obj.submitRelease = function(login, pass, callback) {
			steem.broadcast.escrowRelease(
				passToWif(login, pass),
				obj.from,
				obj.to,
				obj.agent,
				login,
				login == obj.from ? obj.to : obj.from,
				obj.escrow_id,
				obj.sbd_balance,
				obj.steem_balance,
				function(err, response) {
					console.log(err, response);
					callback();
				}
			);
	}

	obj.submitDispute = function(login, pass, callback) {
		steem.broadcast.escrowDispute(
			passToWif(login, pass),
			obj.from,
			obj.to,
			obj.agent,
			login,
			obj.escrow_id,
			function(err, response) {
				console.log(err, response);
				callback();
			}
		);
	}

	obj.submitEscrow = function(login, pass, reciever, callback) {
		steem.broadcast.escrowRelease(
			passToWif(login, pass),
			obj.from,
			obj.to,
			obj.agent,
			login,
			obj[reciever],
			obj.escrow_id,
			obj.sbd_balance,
			obj.steem_balance,
			function(err, response) {
				console.log(err, response);
				callback();
			}
		);
	}

}

$(function() {
	$('a.inputToggle').click(function() {
		var toggles = $(this).data('toggles').split('-');
		if($(this).text() == toggles[0]) {
			$('.inputToggle').text(toggles[1]);
		} else {
			$('.inputToggle').text(toggles[0]);
		}
		return false;
	});

	$('a#tabCP').click(function() {
		if(!id) {
			id = prompt('Введите ID транзакции. Например xtar-8724723:');
			if(!id) {
				return false;
			}
			transaction = new Transaction(id);
			if(transaction.from === undefined) {
				id = null;
				return false;
			}
		}
		window.history.pushState('',id, '?id=' + id);
		$(this).parent().addClass('active').siblings().removeClass('active');
		$('#step1, #step2').hide();
		$('#controlPanel').show();
		return false;
	});

	$('a#tabSend').click(function() {
		$(this).parent().addClass('active').siblings().removeClass('active');
		$('#step1').show();
		$('#controlPanel').hide();
		return false;
	});

	$('#sendSubmit').click(function() {
		var btn = $(this),
		from = $('#sendLogin').val(),
		to = $('#sendReceiver').val(),
		wif = passToWif(from, $('#sendPassword').val()),
		agent = $('#sendAgent').val(),
		escrow_id = parseInt(Math.random() * (99999999 - 10000000) + 10000000),
		fee = parseFloat($('#sendAgent option:selected').data('fee')).toFixed(3) + ' ' + $('#sendAmountUnit').text(),
		sbd_amount = '0.000 GBG',
		steem_amount = '0.000 GOLOS',
		meta = {
			meta: $('#sendMeta').val()
		};

		btn.prop('disabled', true);

		if($('#sendAmountUnit').text() == 'GOLOS') {
			steem_amount = parseFloat($('#sendAmount').val().replace(',','.')).toFixed(3) + ' GOLOS';
		} else if ($('#sendAmountUnit').text() == 'GBG') {
			sbd_amount = parseFloat($('#sendAmount').val().replace(',','.')).toFixed(3) + ' GBG';
		}

		steem.api.getDynamicGlobalProperties(function(err, response) {
			// Added 'Z' to get correct UTC time in all browsers
			var ratification_deadline = new Date(response.time+'Z');
			ratification_deadline.setMinutes(ratification_deadline.getMinutes() + 24 * 60 - 1);

			var escrow_expiration = new Date(response.time+'Z');
			escrow_expiration.setHours(escrow_expiration.getHours() + parseInt($('#sendEscrowExpiration').val()));

			console.log(response.time);
			console.log(ratification_deadline);
			console.log(escrow_expiration);
			//return false;
			steem.broadcast.escrowTransfer(
				wif, // active key
				from,
				to, // to
				agent, // escrow nick
				escrow_id,
				sbd_amount, // amount gbg
				steem_amount, // amount golos
				fee, // fee
				ratification_deadline,
				escrow_expiration,
				JSON.stringify(meta),
				function(err, response) {
					if(!err && response.ref_block_num) {
						if(from) {
							localStorage.setItem('escrow_login', from);
						}
						$('#sendError').slideUp();
						$('#step1').slideUp();
						$('#step2').slideDown();
						$('.sentLink').html('https://golosim.ru/escrow/?id=' + from + '-' + escrow_id + '');
						$('.sentId').html(from + '-' + escrow_id);
					} else {
						$('#sendError').html(
							'
Возникла ошибка:

' +
							err.payload.error.message.replace(/([^>])\n/g, '$1

')
						);
						btn.prop('disabled', false);
					}
					console.log(err, response);
				}
			);
		});
	});
	$('#escrowData').on('click', 'button', function() {
		$('#transactionType').val($(this).data('form'));
		$('form').hide();
		$('#' + $(this).data('form')).show();
		$('#sendTransaction').hide();
		$('#sendTransaction').slideDown();
		$('input[type=submit]').prop('disabled', false); // fix firefox page refresh
		$('#login').val(transaction[$(this).data('nick')]);
		$('#password').val('').focus();
		if($(this).data('answer') == '1') {
			$('#approveYes').prop('checked', true);
		} else if($(this).data('answer') == '0') {
			$('#approveNo').prop('checked', true);
		}
		if($(this).data('answer') == 'escrowFrom') {
			$('#escrowFrom').prop('checked', true);
		} else if($(this).data('answer') == 'escrowTo') {
			$('#escrowTo').prop('checked', true);
		}
	});

	$('#approveForm').submit(function(e) {
		$(this).find('input[type=submit]').prop('disabled', true);
		transaction.submitApprove(
			$('#login').val(),
			$('#password').val(),
			$('#approveYes').is(':checked') ? 'true' : 'false',
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	$('#releaseForm, #cancelForm').submit(function(e) {
		$(this).find('input[type=submit]').prop('disabled', true);
		e.preventDefault();
		transaction.submitRelease(
			$('#login').val(),
			$('#password').val(),
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	$('#startDisputForm').submit(function(e) {
		$(this).find('input[type=submit]').prop('disabled', true);
		e.preventDefault();
		transaction.submitDispute(
			$('#login').val(),
			$('#password').val(),
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	$('#escrowForm').submit(function() {
		$(this).find('input[type=submit]').prop('disabled', true);
		transaction.submitEscrow(
			$('#login').val(),
			$('#password').val(),
			($('#escrowFrom').is(':checked') ? 'from' : 'to'),
			function(err, response) {
				location.reload();
			}
		);
		return false;
	});

	if(localStorage.getItem('escrow_login') !== undefined && localStorage.getItem('escrow_login')) {
		$('#sendLogin').val(localStorage.getItem('escrow_login'));
	}

	var transaction,
	id = gup('id');

	$('#sendAgent').change(function() {
		var agent = $('#sendAgent').val();
		$('#sendFeeAmoutLabel').html('Комиссия от @' + agent + '');
		$('#sendFeeAmout').val($('#sendAgent option:selected').data('fee'));
	});

	// добавляем гарантов из блокчейна, сортируем по репутации
	steem.api.getContentReplies('xtar', 'khochesh-stat-garantom-bud-im', function(err, result) {
		if(!err) {
			if(result.length) {
				$('#sendAgent').html('');
			}
			result.sort(function(a, b) {
				return a.author_reputation < b.author_reputation;
			});
			console.log(result);
			$.each(result, function(index, val) {
				if(val.body !== undefined) {
					var matches = val.body.match(/^([0-9.]+) (.*)/);
					if(matches && matches.length == 3) {
						$('#sendAgent').append('<option value="' + val.author + '" data-fee="' + parseFloat(matches[1]).toFixed(3) + '">' + val.author + ', репутация ' + (Math.max( Math.log10(Math.abs(parseInt(val.author_reputation))) - 9, 0) * (parseInt(val.author_reputation) > 0 ? 1 : -1) * 9 + 25).toFixed(1) + ', комиссия ' + parseFloat(matches[1]).toFixed(3) + ', ' + matches[2] + '</option>');
					}
				}
			});
			$('#sendAgent').prop('disabled', false).change();
		}
	});

	if(id) {
		transaction = new Transaction(gup('id'), function(transaction) {
			$('div.' + transaction.status).show();
			if(!transaction.to_approved) {
				$('div.waitingForApprovalTo').show();
			}
			if(!transaction.agent_approved) {
				$('div.waitingForApprovalAgent').show();
			}
			$('span.transactionId').html('' + transaction.from + '-' + transaction.escrow_id + '');
			$('span.transactionFrom').html('@' + transaction.from + '');
			$('span.transactionTo').html('@' + transaction.to + '');
			$('span.transactionAgent').html('@' + transaction.agent + '');
			$('span.transactionMoney').html(transaction.money);
			$('span.transactionFee').html(transaction.pending_fee);
			$('span.transactionDate').html(transaction.escrow_expiration);
		});
		if(transaction.from === undefined) {
			id = null;
			$('#tabSend').click();
		} else {
			$('#tabCP').click();
		}
	} else {

		$('#tabSend').click();

		if($('#sendLogin').val()) {
			$('#sendPassword').focus();
		} else {
			$('#sendLogin').focus();
		}
	}

});
<|endoftext|>


<commit_before>// ==UserScript==
// @name        Steam Trade Offer Enhancer
// @namespace   localhost
// @description Browser script to enhance Steam trade offers.
// @include     /^https?:\/\/steamcommunity\.com\/(id|profiles)\/.*\/tradeoffers.*/
// @include     /^https?:\/\/steamcommunity\.com\/tradeoffer.*/
// @version     1.3.1
// @author      HusKy
// ==/UserScript==

function getUrlParam(paramName) {
    var params = window.location.search.split(/\?|\&/);
    for(i = 0; i < params.length; i++) {
        var currentParam = params[i].split("=");
        if(currentParam[0] === paramName) {
            return currentParam[1];
        }
    }
}

// array of dangerous sescriptions
var dangerous_descriptions = [
    {
        tag: "uncraftable",
        description: "Not Usable in Crafting"
    },
    {
        tag: "gifted",
        description: "Gift from:"
    }
];

// array of rare TF2 keys (defindexes)
var rare_TF2_keys = [
    "5049", "5067", "5072", "5073",
    "5079", "5081", "5628", "5631",
    "5632", "5713", "5716", "5717",
    "5762"
];

var tradeOfferPage = {
    evaluate_items: function(items) {
        var result = {};

        result._total = items.find("div.trade_item").length;

        items.find("div.trade_item").each(function() {
            var img = jQuery(this).find("img").attr("src");
            var quality = jQuery(this).css("border-top-color");

            if(result[img] === undefined)
                result[img] = {};

            if(result[img][quality] === undefined) {
                result[img][quality] = 1;
            } else {
                result[img][quality]++;
            }
        });

        return result;
    },

    dump_summary: function(tradeoffer, type, items) {
        if(items._total <= 0) return;

        var htmlstring = "Summary (" + items._total + " " + (items._total === 1 ? "item" : "items") + "):
";

        for(var prop in items) {
            if(prop === "_total") continue;

            var item_type = items[prop];
            for(var quality in item_type) {
                htmlstring += "" + item_type[quality] + "";
            }
        }

        htmlstring += "

Items:
";
        tradeoffer.find("div." + type + " > div.tradeoffer_items_header")
                  .after("
" + htmlstring + "
");
    }
};

var tradeOfferWindow = {
    evaluate_items: function(items) {
        var result = {};
        result._total = 0;
        result._warnings = [];

        var slot_inner = items.find("div.slot_inner");

        slot_inner.each(function() {
            if(jQuery(this).html() !== "" && jQuery(this).html() !== null) {
                result._total++;
                var item = jQuery(this).find("div.item");

                var img = item.find("img").attr("src");
                var quality = item.css("border-top-color");

                if(result[img] === undefined)
                    result[img] = {};

                if(result[img][quality] === undefined) {
                    result[img][quality] = 1;
                } else {
                    result[img][quality]++;
                }

                // let's check item's info
                var appid = item[0].id.split("_")[0].replace("item", "");
                var contextid = item[0].id.split("_")[1];
                var assetid = item[0].id.split("_")[2];

                var inventory_item;
                if(items[0].id === "your_slots")
                    inventory_item = unsafeWindow.g_rgAppContextData[appid].rgContexts[contextid]
                        .inventory.rgInventory[assetid];
                else
                    inventory_item = unsafeWindow.g_rgPartnerAppContextData[appid].rgContexts[contextid]
                        .inventory.rgInventory[assetid];

                var descriptions = inventory_item.descriptions;
                var appdata = inventory_item.app_data;
                var fraudwarnings = inventory_item.fraudwarnings;

                var warning_text;

                if(typeof descriptions === "object") {
                    descriptions.forEach(function(d1) {
                        dangerous_descriptions.forEach(function(d2) {
                            if(d1.value.indexOf(d2.description) > -1) {
                                var warning_text = "Offer contains " + d2.tag + " item(s).";
                                if(result._warnings.indexOf(warning_text) === -1)
                                    result._warnings.push(warning_text);
                            }
                        });
                    });
                }

                if(typeof appdata === "object" && typeof appdata.def_index === "string") {
                    if(rare_TF2_keys.indexOf(appdata.def_index) > -1) {
                        warning_text = "Offer contains rare TF2 key(s).";
                        if(result._warnings.indexOf(warning_text) === -1)
                            result._warnings.push(warning_text);
                    }
                }

                if(typeof fraudwarnings === "object") {
                    fraudwarnings.forEach(function(text) {
                        if(text.indexOf("restricted gift") > -1) {
                            warning_text = "Offer contains restricted gift(s).";
                            if(result._warnings.indexOf(warning_text) === -1)
                                result._warnings.push(warning_text);
                        }
                    });
                }

            }
        });

        return result;
    },

    dump_summary: function(target, type, items) {
        if(items._total <= 0) return;

        var htmlstring = type + " summary (" + items._total + " " + (items._total === 1 ? "item" : "items") + "):
";

        // item counts
        for(var prop in items) {
            if(prop.indexOf("_") === 0) continue;

            var item_type = items[prop];
            for(var quality in item_type) {
                htmlstring += "" + item_type[quality] + "";
            }
        }

        // warnings
        if(items._warnings.length > 0) {
            htmlstring += "
Warning:
";
            items._warnings.forEach(function(warning, index) {
                htmlstring += warning;

                if(index < items._warnings.length - 1) {
                    htmlstring += "
";
                }
            });
            htmlstring += "";
        }

        target.append(htmlstring);
    },

    summarise: function() {
        var target = jQuery("div.tradeoffer_items_summary");
        target.html("");

        var mine = jQuery("div#your_slots");
        var other = jQuery("div#their_slots");

        var my_items = this.evaluate_items(mine);
        var other_items = this.evaluate_items(other);

        this.dump_summary(target, "My", my_items);
        if(other_items._total > 0) target.append("

");
        this.dump_summary(target, "Their", other_items);
    },

    init: function() {
        var self = this;

        // something is loading
        var isReady = jQuery("img[src$='throbber.gif']:visible").length <= 0;

        // our partner's inventory is also loading at this point
        var itemParamExists = getUrlParam("for_item") !== undefined;
        var hasBeenLoaded = true;

        if(itemParamExists) {
            // format: for_item=<appId>_<contextId>_<itemId>
            var item = getUrlParam("for_item").split("_");
            hasBeenLoaded = jQuery("div#inventory_" + UserThem.strSteamId + "_" + item[0] + "_" + item[1]).length > 0;
        }

        if(isReady && (!itemParamExists || hasBeenLoaded)) {
            setTimeout(function() {
                self.summarise();
            }, 500);

            return;
        }

        if(itemParamExists && hasBeenLoaded) {
            setTimeout(self.deadItem.bind(self), 5000);
            return;
        }

        setTimeout(function() {
            self.init();
        }, 250);
    },

    deadItem: function() {
        var deadItemExists = jQuery("a[href$='_undefined']").length > 0;
        var item = getUrlParam("for_item").split("_");

        if(deadItemExists) {
            unsafeWindow.g_rgCurrentTradeStatus.them.assets = [];
            RefreshTradeStatus(g_rgCurrentTradeStatus, true);
            alert("Seems like the item you are looking to buy (ID: " + item[2] + ") is no longer available. You should check other user's backpack and see if it's still there.");
        } else {
            // Something was loading very slowly, restart init...
            this.init();
        }
    }
};

jQuery(function() {

var location = window.location.pathname;

// Append CSS style.
var style = "";
jQuery(style).appendTo("head");

// Trade offer page with multiple trade offers ...
if(location.indexOf("tradeoffers") > -1) {

    // Retrieve all trade offers.
    var trade_offers = jQuery("div.tradeoffer");

    if(trade_offers.length > 0) {
        trade_offers.each(function() {
            var others = jQuery(this).find("div.primary > div.tradeoffer_item_list");
            var mine = jQuery(this).find("div.secondary > div.tradeoffer_item_list");

            // Evaluate both sides.
            var other_items = tradeOfferPage.evaluate_items(others);
            var my_items = tradeOfferPage.evaluate_items(mine);

            // Dump the summaries somewhere ...
            tradeOfferPage.dump_summary(jQuery(this), "primary", other_items);
            tradeOfferPage.dump_summary(jQuery(this), "secondary", my_items);

            // Check if trade offer is "unavailable"
            // Do this only for /tradeoffers page and nothing else
            var is_ok = location.indexOf("tradeoffers", location.length - "tradeoffers".length) !== -1;
            is_ok = is_ok || location.indexOf("tradeoffers/", location.length - "tradeoffers/".length) !== -1;

            if(is_ok) {
                var is_unavailable = jQuery(this).find("div.tradeoffer_items_banner").text().indexOf("Items Now Unavailable For Trade") > -1;
                if(is_unavailable) {
                    var trade_offer_id = jQuery(this).attr("id").split("_")[1];
                    var footer = jQuery(this).find("div.tradeoffer_footer");

                    var text = "This trade offer is stuck and invalid, but you can still decline it.";
                    footer.prepend("
" + text + "
");
                }
            }
        });
    }

    // Single trade offer window ...
} else if(location.indexOf("tradeoffer") > -1) {

    // Append new divs ...
    jQuery("div.trade_left div.trade_box_contents").append("

");
    jQuery("div.item_adder").append("
Add multiple items:
");
    jQuery("div.item_adder").append("<input id=\"amount_control\" class=\"filter_search_box\" type=\"text\" placeholder=\"16\"> ");
    jQuery("div.item_adder").append("<button id=\"btn_additems\" type=\"button\" class=\"btn_custom\">Add</button>");

    jQuery("div.trade_left div.trade_box_contents").append("

");

    // Refresh summaries whenever ...
    jQuery("body").click(function() {
        setTimeout(function() {
            tradeOfferWindow.summarise();
        }, 500);
    });

    // Handle item auto adder
    jQuery("button#btn_additems").click(function() {
        // Do not add items if the offer cannot be modified
        if(jQuery("div.modify_trade_offer:visible").length > 0) return;

        // Collect all items
        var inventory = jQuery("div.inventory_ctn:visible");
        var items = inventory.find("div.itemHolder").filter(function() {
            return jQuery(this).css("display") !== "none";
        }).find("div.item").filter(function() {
            return jQuery(this).css("display") !== "none";
        });

        // Get amount value
        var amount = parseInt(jQuery("input#amount_control").val());
        if(isNaN(amount)) amount = 16;
        if(items.length < amount) amount = items.length;

        // Add all items
        for(i = 0; i < amount; i++) {
            setTimeout(MoveItemToTrade, i * 50, items[i]);
        }

        // Refresh summaries
        setTimeout(function() {
            tradeOfferWindow.summarise();
        }, amount * 50 + 500);
    });

    tradeOfferWindow.init();

    var itemParam = getUrlParam("for_item");
    if(itemParam !== undefined) {
        var item = itemParam.split("_");

        unsafeWindow.g_rgCurrentTradeStatus.them.assets.push({
            "appid":item[0],
            "contextid":item[1],
            "assetid":item[2],
            "amount":1
        });

        RefreshTradeStatus(g_rgCurrentTradeStatus, true);
    }
}

});
<commit_msg>Added option to clear already added items.<commit_after>// ==UserScript==
// @name        Steam Trade Offer Enhancer
// @namespace   http://steamcommunity.com/id/H_s_K/
// @description Browser script to enhance Steam trade offers.
// @include     /^https?:\/\/steamcommunity\.com\/(id|profiles)\/.*\/tradeoffers.*/
// @include     /^https?:\/\/steamcommunity\.com\/tradeoffer.*/
// @version     1.3.2
// @author      HusKy
// ==/UserScript==

function getUrlParam(paramName) {
    var params = window.location.search.split(/\?|\&/);
    for(i = 0; i < params.length; i++) {
        var currentParam = params[i].split("=");
        if(currentParam[0] === paramName) {
            return currentParam[1];
        }
    }
}

// array of dangerous descriptions
var dangerous_descriptions = [
    {
        tag: "uncraftable",
        description: "Not Usable in Crafting"
    },
    {
        tag: "gifted",
        description: "Gift from:"
    }
];

// array of rare TF2 keys (defindexes)
var rare_TF2_keys = [
    "5049", "5067", "5072", "5073",
    "5079", "5081", "5628", "5631",
    "5632", "5713", "5716", "5717",
    "5762"
];

var tradeOfferPage = {
    evaluate_items: function(items) {
        var result = {};

        result._total = items.find("div.trade_item").length;

        items.find("div.trade_item").each(function() {
            var img = jQuery(this).find("img").attr("src");
            var quality = jQuery(this).css("border-top-color");

            if(result[img] === undefined)
                result[img] = {};

            if(result[img][quality] === undefined) {
                result[img][quality] = 1;
            } else {
                result[img][quality]++;
            }
        });

        return result;
    },

    dump_summary: function(tradeoffer, type, items) {
        if(items._total <= 0) return;

        var htmlstring = "Summary (" + items._total + " " + (items._total === 1 ? "item" : "items") + "):
";

        for(var prop in items) {
            if(prop === "_total") continue;

            var item_type = items[prop];
            for(var quality in item_type) {
                htmlstring += "" + item_type[quality] + "";
            }
        }

        htmlstring += "

Items:
";
        tradeoffer.find("div." + type + " > div.tradeoffer_items_header")
                  .after("
" + htmlstring + "
");
    }
};

var tradeOfferWindow = {
    evaluate_items: function(items) {
        var result = {};
        result._total = 0;
        result._warnings = [];

        var slot_inner = items.find("div.slot_inner");

        slot_inner.each(function() {
            if(jQuery(this).html() !== "" && jQuery(this).html() !== null) {
                result._total++;
                var item = jQuery(this).find("div.item");

                var img = item.find("img").attr("src");
                var quality = item.css("border-top-color");

                if(result[img] === undefined)
                    result[img] = {};

                if(result[img][quality] === undefined) {
                    result[img][quality] = 1;
                } else {
                    result[img][quality]++;
                }

                // let's check item's info
                var appid = item[0].id.split("_")[0].replace("item", "");
                var contextid = item[0].id.split("_")[1];
                var assetid = item[0].id.split("_")[2];

                var inventory_item;
                if(items[0].id === "your_slots")
                    inventory_item = unsafeWindow.g_rgAppContextData[appid].rgContexts[contextid]
                        .inventory.rgInventory[assetid];
                else
                    inventory_item = unsafeWindow.g_rgPartnerAppContextData[appid].rgContexts[contextid]
                        .inventory.rgInventory[assetid];

                var descriptions = inventory_item.descriptions;
                var appdata = inventory_item.app_data;
                var fraudwarnings = inventory_item.fraudwarnings;

                var warning_text;

                if(typeof descriptions === "object") {
                    descriptions.forEach(function(d1) {
                        dangerous_descriptions.forEach(function(d2) {
                            if(d1.value.indexOf(d2.description) > -1) {
                                var warning_text = "Offer contains " + d2.tag + " item(s).";
                                if(result._warnings.indexOf(warning_text) === -1)
                                    result._warnings.push(warning_text);
                            }
                        });
                    });
                }

                if(typeof appdata === "object" && typeof appdata.def_index === "string") {
                    if(rare_TF2_keys.indexOf(appdata.def_index) > -1) {
                        warning_text = "Offer contains rare TF2 key(s).";
                        if(result._warnings.indexOf(warning_text) === -1)
                            result._warnings.push(warning_text);
                    }
                }

                if(typeof fraudwarnings === "object") {
                    fraudwarnings.forEach(function(text) {
                        if(text.indexOf("restricted gift") > -1) {
                            warning_text = "Offer contains restricted gift(s).";
                            if(result._warnings.indexOf(warning_text) === -1)
                                result._warnings.push(warning_text);
                        }
                    });
                }

            }
        });

        return result;
    },

    dump_summary: function(target, type, items) {
        if(items._total <= 0) return;

        var htmlstring = type + " summary (" + items._total + " " + (items._total === 1 ? "item" : "items") + "):
";

        // item counts
        for(var prop in items) {
            if(prop.indexOf("_") === 0) continue;

            var item_type = items[prop];
            for(var quality in item_type) {
                htmlstring += "" + item_type[quality] + "";
            }
        }

        // warnings
        if(items._warnings.length > 0) {
            htmlstring += "
Warning:
";
            items._warnings.forEach(function(warning, index) {
                htmlstring += warning;

                if(index < items._warnings.length - 1) {
                    htmlstring += "
";
                }
            });
            htmlstring += "";
        }

        target.append(htmlstring);
    },

    summarise: function() {
        var target = jQuery("div.tradeoffer_items_summary");
        target.html("");

        var mine = jQuery("div#your_slots");
        var other = jQuery("div#their_slots");

        var my_items = this.evaluate_items(mine);
        var other_items = this.evaluate_items(other);

        this.dump_summary(target, "My", my_items);
        if(other_items._total > 0) target.append("

");
        this.dump_summary(target, "Their", other_items);
    },

    init: function() {
        var self = this;

        // something is loading
        var isReady = jQuery("img[src$='throbber.gif']:visible").length <= 0;

        // our partner's inventory is also loading at this point
        var itemParamExists = getUrlParam("for_item") !== undefined;
        var hasBeenLoaded = true;

        if(itemParamExists) {
            // format: for_item=<appId>_<contextId>_<itemId>
            var item = getUrlParam("for_item").split("_");
            hasBeenLoaded = jQuery("div#inventory_" + UserThem.strSteamId + "_" + item[0] + "_" + item[1]).length > 0;
        }

        if(isReady && (!itemParamExists || hasBeenLoaded)) {
            setTimeout(function() {
                self.summarise();
            }, 500);

            return;
        }

        if(itemParamExists && hasBeenLoaded) {
            setTimeout(self.deadItem.bind(self), 5000);
            return;
        }

        setTimeout(function() {
            self.init();
        }, 250);
    },

    deadItem: function() {
        var deadItemExists = jQuery("a[href$='_undefined']").length > 0;
        var item = getUrlParam("for_item").split("_");

        if(deadItemExists) {
            unsafeWindow.g_rgCurrentTradeStatus.them.assets = [];
            RefreshTradeStatus(g_rgCurrentTradeStatus, true);
            alert("Seems like the item you are looking to buy (ID: " + item[2] + ") is no longer available. You should check other user's backpack and see if it's still there.");
        } else {
            // Something was loading very slowly, restart init...
            this.init();
        }
    },

    clear: function(slots) {
        var timeout = 100;

        var added_items = jQuery(slots);
        var items = added_items.find("div.itemHolder").find("div.item");

        for(i = 0; i < items.length; i++) {
            setTimeout(MoveItemToInventory, i * timeout, items[i]);
        }

        setTimeout(function() {
            tradeOfferWindow.summarise();
        }, items.length * timeout + 500);
    }
};

jQuery(function() {

var location = window.location.pathname;

// Append CSS style.
var style = "";
jQuery(style).appendTo("head");

// Trade offer page with multiple trade offers ...
if(location.indexOf("tradeoffers") > -1) {

    // Retrieve all trade offers.
    var trade_offers = jQuery("div.tradeoffer");

    if(trade_offers.length > 0) {
        trade_offers.each(function() {
            var others = jQuery(this).find("div.primary > div.tradeoffer_item_list");
            var mine = jQuery(this).find("div.secondary > div.tradeoffer_item_list");

            // Evaluate both sides.
            var other_items = tradeOfferPage.evaluate_items(others);
            var my_items = tradeOfferPage.evaluate_items(mine);

            // Dump the summaries somewhere ...
            tradeOfferPage.dump_summary(jQuery(this), "primary", other_items);
            tradeOfferPage.dump_summary(jQuery(this), "secondary", my_items);

            // Check if trade offer is "unavailable"
            // Do this only for /tradeoffers page and nothing else
            var is_ok = location.indexOf("tradeoffers", location.length - "tradeoffers".length) !== -1;
            is_ok = is_ok || location.indexOf("tradeoffers/", location.length - "tradeoffers/".length) !== -1;

            if(is_ok) {
                var is_unavailable = jQuery(this).find("div.tradeoffer_items_banner").text().indexOf("Items Now Unavailable For Trade") > -1;
                if(is_unavailable) {
                    var trade_offer_id = jQuery(this).attr("id").split("_")[1];
                    var footer = jQuery(this).find("div.tradeoffer_footer");

                    var text = "This trade offer is stuck and invalid, but you can still decline it.";
                    footer.prepend("
" + text + "
");
                }
            }
        });
    }

    // Single trade offer window ...
} else if(location.indexOf("tradeoffer") > -1) {

    // Append new divs ...
    jQuery("div.trade_left div.trade_box_contents").append("

");
    jQuery("div.item_adder").append("
Add multiple items:
");
    jQuery("div.item_adder").append("<input id=\"amount_control\" class=\"filter_search_box\" type=\"text\" placeholder=\"16\"> ");
    jQuery("div.item_adder").append("<button id=\"btn_additems\" type=\"button\" class=\"btn_custom\">Add</button>

");
    jQuery("div.item_adder").append("<button id=\"btn_clearmyitems\" type=\"button\" class=\"btn_custom\">Clear my items</button>");
    jQuery("div.item_adder").append(" <button id=\"btn_cleartheiritems\" type=\"button\" class=\"btn_custom\">Clear their items</button>");

    jQuery("div.trade_left div.trade_box_contents").append("

");

    // Refresh summaries whenever ...
    jQuery("body").click(function() {
        setTimeout(function() {
            tradeOfferWindow.summarise();
        }, 500);
    });

    // Handle item auto adder
    jQuery("button#btn_additems").click(function() {
        // Do not add items if the offer cannot be modified
        if(jQuery("div.modify_trade_offer:visible").length > 0) return;

        // Collect all items
        var inventory = jQuery("div.inventory_ctn:visible");
        var items = inventory.find("div.itemHolder").filter(function() {
            return jQuery(this).css("display") !== "none";
        }).find("div.item").filter(function() {
            return jQuery(this).css("display") !== "none";
        });

        // Get amount value
        var amount = parseInt(jQuery("input#amount_control").val());
        if(isNaN(amount)) amount = 16;
        if(items.length < amount) amount = items.length;

        // Add all items
        for(i = 0; i < amount; i++) {
            setTimeout(MoveItemToTrade, i * 50, items[i]);
        }

        // Refresh summaries
        setTimeout(function() {
            tradeOfferWindow.summarise();
        }, amount * 50 + 500);
    });

    jQuery("button#btn_clearmyitems").click(function() {
        tradeOfferWindow.clear("div#your_slots");
    });

    jQuery("button#btn_cleartheiritems").click(function() {
        tradeOfferWindow.clear("div#their_slots");
    });

    tradeOfferWindow.init();

    var itemParam = getUrlParam("for_item");
    if(itemParam !== undefined) {
        var item = itemParam.split("_");

        unsafeWindow.g_rgCurrentTradeStatus.them.assets.push({
            "appid":item[0],
            "contextid":item[1],
            "assetid":item[2],
            "amount":1
        });

        RefreshTradeStatus(g_rgCurrentTradeStatus, true);
    }
}

});
<|endoftext|>