<commit_before>class VexFlow {

	constructor() {
		// code...
	}

	/**
	 * Support for converting VexFlow voice into MidiWriterJS track
	 * @return MidiWritier.Track object
	 */
	trackFromVoice(voice) {
		var track = new Track();
		var wait;
		var pitches = [];

		voice.tickables.forEach(function(tickable) {
			pitches = [];

			if (tickable.noteType === 'n') {
				tickable.keys.forEach(function(key) {
					// build array of pitches
					pitches.push(this.convertPitch(key));
				});

			} else if (tickable.noteType === 'r') {
				// move on to the next tickable and use this rest as a `wait` property for the next event
				wait = this.convertDuration(tickable);
				return;
			}

			track.addEvent(new NoteEvent({pitch: pitches, duration: this.convertDuration(tickable), wait: wait}));

			// reset wait
			wait = 0;
		});

		return track;
	}

	/**
	 * Converts VexFlow pitch syntax to MidiWriterJS syntax
	 * @param pitch string
	 */
	convertPitch(pitch) {
		return pitch.replace('/', '');
	}

	/**
	 * Converts VexFlow duration syntax to MidiWriterJS syntax
	 * @param note struct from VexFlow
	 */
	convertDuration(note) {
		switch (note.duration) {
			case 'w':
				return '1';
			case 'h':
				return note.isDotted() ? 'd2' : '2';
			case 'q':
				return note.isDotted() ? 'd4' : '4';
			case '8':
				return note.isDotted() ? 'd8' : '8';
		}

		return note.duration;
	};
}

export {VexFlow};
<commit_msg>fix: use arrow function maintain proper this<commit_after>class VexFlow {

	constructor() {
		// code...
	}

	/**
	 * Support for converting VexFlow voice into MidiWriterJS track
	 * @return MidiWritier.Track object
	 */
	trackFromVoice(voice) {
		var track = new Track();
		var wait;
		var pitches = [];

		voice.tickables.forEach(tickable => {
			pitches = [];

			if (tickable.noteType === 'n') {
				tickable.keys.forEach(key => {
					// build array of pitches
					pitches.push(this.convertPitch(key));
				});

			} else if (tickable.noteType === 'r') {
				// move on to the next tickable and use this rest as a `wait` property for the next event
				wait = this.convertDuration(tickable);
				return;
			}

			track.addEvent(new NoteEvent({pitch: pitches, duration: this.convertDuration(tickable), wait: wait}));

			// reset wait
			wait = 0;
		});

		return track;
	}

	/**
	 * Converts VexFlow pitch syntax to MidiWriterJS syntax
	 * @param pitch string
	 */
	convertPitch(pitch) {
		return pitch.replace('/', '');
	}

	/**
	 * Converts VexFlow duration syntax to MidiWriterJS syntax
	 * @param note struct from VexFlow
	 */
	convertDuration(note) {
		switch (note.duration) {
			case 'w':
				return '1';
			case 'h':
				return note.isDotted() ? 'd2' : '2';
			case 'q':
				return note.isDotted() ? 'd4' : '4';
			case '8':
				return note.isDotted() ? 'd8' : '8';
		}

		return note.duration;
	};
}

export {VexFlow};
<|endoftext|>


<commit_before>var Util   = require("util");
var Bot    = require("./lib/irc");
var moment = require('moment')
var boo    = require('boo')
var spice  = require('spice')
var profile = require('./nanoprofile')

function ensure_not_active(bot, cx) {
  if (!bot.current_ww.active)
    return cx.channel.send_reply(cx.sender, "No WordWar active.")
  else
    return true
}

var WordWar = boo.Base.derive({
  init:
  function _init() {
    this.open   = false
    this.active = false
  }

, activate:
  function _activate(sender, minutes) {
    this.participants = [sender]
    this.starter      = sender
    this.open         = true
    this.active       = true
    this.time         = minutes
  }

, stop:
  function _stop() {
    this.open   = false
    this.active = false
  }

, join:
  function _add(name) {
    this.participants.push(name)
  }

, part:
  function _part(name) {
    this.participants = this.participants.filter(function(a){ return a !== name })
  }

, is_participating:
  function _is_participating(name) {
    return this.participants.some(function(a){ return a === name })
  }

, notify:
  function _notify() {
    return spice('{:sender} is asking for a {:minutes} minutes WordWar! '
                +'Type "!join" to participate. Type "!start" to begin.'
                , { sender:  this.starter
                  , minutes: this.time })
  }

, notify_start:
  function _notify_start() {
    this.open       = false
    this.start_time = moment(new Date)
    this.end_time   = moment(new Date).add('minutes', this.time)
    return spice('WordWar from {:start} to {:end} starting. Go {:participants}!'
                , { participants: this.participants.join(', ')
                  , start:        this.start_time.format('HH:mm')
                  , end:          this.end_time.format('HH:mm')
                  })
  }

, notify_end:
  function _notify_end() {
    return spice('WordWar ended, {:participants}.'
                , { participants: this.participants.join(', ') })
  }

, notify_status:
  function _notify_status() {
    return spice('WordWar will end on {:end} ({:minutes} minutes left). {:participants} nanowriters are in.'
                , { participants: this.participants.length
                  , end:          this.end_time.format('HH:mm')
                  , minutes:      moment(this.end_time.toDate())
                                    .subtract(new Date)
                                    .format('HH:mm')
                  })
  }
})

Util.inherits(NanoBot, Bot)
function NanoBot(profile) {
	Bot.call(this, profile)
	this.set_log_level(this.LOG_ALL)
	this.set_trigger("!")
};

NanoBot.prototype.init = function() {
	Bot.prototype.init.call(this)

  this.current_ww = WordWar.make()

	this.register_command("ww", this.ww)
  this.register_command("stop", this.stop_ww)
  this.register_command("join", this.join_ww)
  this.register_command("start", this.start_ww)
  this.register_command("part", this.part_ww)
  this.register_command("status", this.status_ww)
	this.on('command_not_found', this.unrecognized)
};

NanoBot.prototype.ww = function(cx, text) {
  var minutes = Number(text)
  if (this.current_ww.active)
    return cx.channel.send_reply(cx.sender, "There's a WordWar going on already!")
  if (isNaN(minutes) || minutes < 0)
    return cx.channel.send_reply(cx.sender, 'Use "!ww [minutes]" (e.g.: "!ww 30"). The default are 20 minutes.')

  this.current_ww.activate(cx.sender, minutes || 20)
  cx.channel.send(this.current_ww.notify())
};

NanoBot.prototype.start_ww = function(cx, text) {
  if (!ensure_not_active(this, cx)) return
  if (!this.current_ww.open)
    return cx.channel.send_reply("There's a WordWar going on already. " + this.current_ww.notify_status())

  this.current_ww.open = false
  this.current_ww.timer = setTimeout(function() {
      cx.channel.send(this.current_ww.notify_end())
      this.current_ww.stop()
  }.bind(this), this.current_ww.time * 60 * 1000)
  cx.channel.send(this.current_ww.notify_start())
}

NanoBot.prototype.stop_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return

  clearTimeout(this.current_ww.timer)
  this.current_ww.stop()
  cx.channel.send_reply(cx.sender, "WordWar stopped.")
}

NanoBot.prototype.join_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender))
    return cx.channel.send_reply(cx.sender, "You're already in.")

  this.current_ww.join(cx.sender)
  cx.channel.send(cx.sender + " joined the WordWar")
}

NanoBot.prototype.part_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender)) {
    this.current_ww.part(cx.sender)
    cx.channel.send(cx.sender + " left the WordWar")
  }
}

NanoBot.prototype.status_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.open)
    cx.channel.send_reply(cx.sender, 'The word war hasn\'t started yet. You can type "!join" to participate.')
  else
    cx.channel.send_reply(cx.sender, this.current_ww.notify_status())
}

NanoBot.prototype.unrecognized = function(cx, text) {
	cx.channel.send_reply(cx.sender, "There is no command: "+text)
}

;(new NanoBot(profile)).init()<commit_msg>Fixes send_reply call.<commit_after>var Util   = require("util");
var Bot    = require("./lib/irc");
var moment = require('moment')
var boo    = require('boo')
var spice  = require('spice')
var profile = require('./nanoprofile')

function ensure_not_active(bot, cx) {
  if (!bot.current_ww.active)
    return cx.channel.send_reply(cx.sender, "No WordWar active.")
  else
    return true
}

var WordWar = boo.Base.derive({
  init:
  function _init() {
    this.open   = false
    this.active = false
  }

, activate:
  function _activate(sender, minutes) {
    this.participants = [sender]
    this.starter      = sender
    this.open         = true
    this.active       = true
    this.time         = minutes
  }

, stop:
  function _stop() {
    this.open   = false
    this.active = false
  }

, join:
  function _add(name) {
    this.participants.push(name)
  }

, part:
  function _part(name) {
    this.participants = this.participants.filter(function(a){ return a !== name })
  }

, is_participating:
  function _is_participating(name) {
    return this.participants.some(function(a){ return a === name })
  }

, notify:
  function _notify() {
    return spice('{:sender} is asking for a {:minutes} minutes WordWar! '
                +'Type "!join" to participate. Type "!start" to begin.'
                , { sender:  this.starter
                  , minutes: this.time })
  }

, notify_start:
  function _notify_start() {
    this.open       = false
    this.start_time = moment(new Date)
    this.end_time   = moment(new Date).add('minutes', this.time)
    return spice('WordWar from {:start} to {:end} starting. Go {:participants}!'
                , { participants: this.participants.join(', ')
                  , start:        this.start_time.format('HH:mm')
                  , end:          this.end_time.format('HH:mm')
                  })
  }

, notify_end:
  function _notify_end() {
    return spice('WordWar ended, {:participants}.'
                , { participants: this.participants.join(', ') })
  }

, notify_status:
  function _notify_status() {
    return spice('WordWar will end on {:end} ({:minutes} minutes left). {:participants} nanowriters are in.'
                , { participants: this.participants.length
                  , end:          this.end_time.format('HH:mm')
                  , minutes:      moment(this.end_time.toDate())
                                    .subtract(new Date)
                                    .format('HH:mm')
                  })
  }
})

Util.inherits(NanoBot, Bot)
function NanoBot(profile) {
	Bot.call(this, profile)
	this.set_log_level(this.LOG_ALL)
	this.set_trigger("!")
};

NanoBot.prototype.init = function() {
	Bot.prototype.init.call(this)

  this.current_ww = WordWar.make()

	this.register_command("ww", this.ww)
  this.register_command("stop", this.stop_ww)
  this.register_command("join", this.join_ww)
  this.register_command("start", this.start_ww)
  this.register_command("part", this.part_ww)
  this.register_command("status", this.status_ww)
	this.on('command_not_found', this.unrecognized)
};

NanoBot.prototype.ww = function(cx, text) {
  var minutes = Number(text)
  if (this.current_ww.active)
    return cx.channel.send_reply(cx.sender, "There's a WordWar going on already!")
  if (isNaN(minutes) || minutes < 0)
    return cx.channel.send_reply(cx.sender, 'Use "!ww [minutes]" (e.g.: "!ww 30"). The default are 20 minutes.')

  this.current_ww.activate(cx.sender, minutes || 20)
  cx.channel.send(this.current_ww.notify())
};

NanoBot.prototype.start_ww = function(cx, text) {
  if (!ensure_not_active(this, cx)) return
  if (!this.current_ww.open)
    return cx.channel.send_reply(cx.sender, "There's a WordWar going on already. " + this.current_ww.notify_status())

  this.current_ww.open = false
  this.current_ww.timer = setTimeout(function() {
      cx.channel.send(this.current_ww.notify_end())
      this.current_ww.stop()
  }.bind(this), this.current_ww.time * 60 * 1000)
  cx.channel.send(this.current_ww.notify_start())
}

NanoBot.prototype.stop_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return

  clearTimeout(this.current_ww.timer)
  this.current_ww.stop()
  cx.channel.send_reply(cx.sender, "WordWar stopped.")
}

NanoBot.prototype.join_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender))
    return cx.channel.send_reply(cx.sender, "You're already in.")

  this.current_ww.join(cx.sender)
  cx.channel.send(cx.sender + " joined the WordWar")
}

NanoBot.prototype.part_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender)) {
    this.current_ww.part(cx.sender)
    cx.channel.send(cx.sender + " left the WordWar")
  }
}

NanoBot.prototype.status_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.open)
    cx.channel.send_reply(cx.sender, 'The word war hasn\'t started yet. You can type "!join" to participate.')
  else
    cx.channel.send_reply(cx.sender, this.current_ww.notify_status())
}

NanoBot.prototype.unrecognized = function(cx, text) {
	cx.channel.send_reply(cx.sender, "There is no command: "+text)
}

;(new NanoBot(profile)).init()<|endoftext|>


<commit_before>var url         = require('url'),
    fs          = require('fs'),
    node_static = require('node-static'),
    Negotiator  = require('negotiator'),
    winston     = require('winston'),
    SettingsGenerator = require('./settingsgenerator.js'),
    Stats       = require('./stats.js');

var HttpHandler = function (config) {
    var public_http = config.public_http || 'client/';
    this.file_server = new node_static.Server(public_http);
};

module.exports.HttpHandler = HttpHandler;

HttpHandler.prototype.serve = function (request, response) {
    // The incoming requests base path (ie. /kiwiclient)
    var base_path, base_check,
        whitelisted_folders = ['/assets', '/src'],
        is_whitelisted_folder = false;

    // Trim off any trailing slashes from the base_path
    base_path = global.config.http_base_path || '';
    if (base_path.substr(base_path.length - 1) === '/') {
        base_path = base_path.substr(0, base_path.length - 1);
    }

    // Normalise the URL + remove query strings to compare against the base_path
    base_check = request.url.split('?')[0];
    if (base_check.substr(base_check.length - 1) !== '/') {
        base_check += '/';
    }

    // Normalise the URL we use by removing the base path
    if (base_check.indexOf(base_path + '/') === 0) {
        request.url = request.url.replace(base_path, '');

    } else if (base_check !== '/') {
        // We don't handle requests outside of the base path and not /, so just 404
        response.writeHead(404);
        response.write('Not Found');
        response.end();
        return;
    }

    // Map any whitelisted folders to the local directories
    whitelisted_folders.forEach(function(folder) {
        if (request.url.indexOf(folder) === 0) {
            is_whitelisted_folder = true;
        }
    });

    // Any requests not for whitelisted assets returns the index page
    if (!is_whitelisted_folder) {
        request.url = '/index.html';
    }

    if (request.url === '/index.html') {
        Stats.incr('http.homepage');
    }

    // If the 'magic' translation is requested, figure out the best language to use from
    // the Accept-Language HTTP header. If nothing is suitible, fallback to our en-gb default translation
    if (request.url.substr(0, 16) === '/assets/locales/') {
        if (request.url === '/assets/locales/magic.json') {
            return serveMagicLocale.call(this, request, response);
        } else {
            response.setHeader('Content-Language', request.url.substr(16, request.url.indexOf('.') - 16));
        }
    } else if (request.url.substr(0, 21) === '/assets/settings.json') {
        return serveSettings.call(this, request, response);
    }

    this.file_server.serve(request, response, function (err) {
        if (err) {
            response.writeHead(err.status, err.headers);
            response.end();
        }
    });
};

// Cached list of available translations
var cached_available_locales = [];

// Get a list of the available translations we have
fs.readdir('../client/assets/locales', function (err, files) {
    if (err) {
        if (err.code === 'ENOENT') {
            winston.error('No locale files could be found at ' + err.path);
        } else {
            winston.error('Error reading locales.', err);
        }
    }

    (files || []).forEach(function (file) {
        if (file.substr(-5) === '.json') {
            cached_available_locales.push(file.slice(0, -5));
        }
    });
});

/**
 * Handle the /assets/locales/magic.json request
 * Find the closest translation we have for the language
 * set in the browser.
 **/
function serveMagicLocale(request, response) {
    var default_locale_id = 'en-gb',
        found_locale, negotiator;

    if (!request.headers['accept-language']) {
        // No accept-language specified in the request so send the default
        found_locale = default_locale_id;

    } else {
        negotiator = new Negotiator(request);
        found_locale = negotiator.language(cached_available_locales);

        // If a locale couldn't be negotiated, use the default
        found_locale = found_locale || default_locale_id;
    }

    // Send a locale to the browser
    this.file_server.serveFile('/assets/locales/' + found_locale + '.json', 200, {
        Vary: 'Accept-Language',
        'Content-Language': found_locale
    }, request, response);
}

/**
 * Handle the settings.json request
 */
function serveSettings(request, response) {
    var referrer_url,
        debug = false;

    // Check the referrer for a debug option
    if (request.headers.referer) {
        referrer_url = url.parse(request.headers.referer, true);
        if (referrer_url.query && referrer_url.query.debug) {
            debug = true;
        }
    }

    SettingsGenerator.get(debug, function(err, settings) {
        if (err) {
            winston.error('Error generating settings', err);
            response.writeHead(500, 'Internal Server Error');
            return response.end();
        }

        if (request.headers['if-none-match'] && request.headers['if-none-match'] === settings.hash) {
            response.writeHead(304, 'Not Modified');
            return response.end();
        }

        response.writeHead(200, {
            'ETag': settings.hash,
            'Content-Type': 'application/json'
        });
        response.end(settings.settings);
    });
}
<commit_msg>Using the public_http config option when reading available locales<commit_after>var url         = require('url'),
    fs          = require('fs'),
    node_static = require('node-static'),
    Negotiator  = require('negotiator'),
    winston     = require('winston'),
    SettingsGenerator = require('./settingsgenerator.js'),
    Stats       = require('./stats.js');

// Cached list of available translations
var cached_available_locales = null;

var HttpHandler = function (config) {
    var public_http = config.public_http || 'client/';
    this.file_server = new node_static.Server(public_http);

    if (!cached_available_locales) {
        updateLocalesCache();
    }
};

module.exports.HttpHandler = HttpHandler;

HttpHandler.prototype.serve = function (request, response) {
    // The incoming requests base path (ie. /kiwiclient)
    var base_path, base_check,
        whitelisted_folders = ['/assets', '/src'],
        is_whitelisted_folder = false;

    // Trim off any trailing slashes from the base_path
    base_path = global.config.http_base_path || '';
    if (base_path.substr(base_path.length - 1) === '/') {
        base_path = base_path.substr(0, base_path.length - 1);
    }

    // Normalise the URL + remove query strings to compare against the base_path
    base_check = request.url.split('?')[0];
    if (base_check.substr(base_check.length - 1) !== '/') {
        base_check += '/';
    }

    // Normalise the URL we use by removing the base path
    if (base_check.indexOf(base_path + '/') === 0) {
        request.url = request.url.replace(base_path, '');

    } else if (base_check !== '/') {
        // We don't handle requests outside of the base path and not /, so just 404
        response.writeHead(404);
        response.write('Not Found');
        response.end();
        return;
    }

    // Map any whitelisted folders to the local directories
    whitelisted_folders.forEach(function(folder) {
        if (request.url.indexOf(folder) === 0) {
            is_whitelisted_folder = true;
        }
    });

    // Any requests not for whitelisted assets returns the index page
    if (!is_whitelisted_folder) {
        request.url = '/index.html';
    }

    if (request.url === '/index.html') {
        Stats.incr('http.homepage');
    }

    // If the 'magic' translation is requested, figure out the best language to use from
    // the Accept-Language HTTP header. If nothing is suitible, fallback to our en-gb default translation
    if (request.url.substr(0, 16) === '/assets/locales/') {
        if (request.url === '/assets/locales/magic.json') {
            return serveMagicLocale.call(this, request, response);
        } else {
            response.setHeader('Content-Language', request.url.substr(16, request.url.indexOf('.') - 16));
        }
    } else if (request.url.substr(0, 21) === '/assets/settings.json') {
        return serveSettings.call(this, request, response);
    }

    this.file_server.serve(request, response, function (err) {
        if (err) {
            response.writeHead(err.status, err.headers);
            response.end();
        }
    });
};

/**
 * Cache the available locales we have so we don't read the same directory for each request
 **/
function updateLocalesCache() {
    cached_available_locales = [];

    fs.readdir(global.config.public_http + '/assets/locales', function (err, files) {
        if (err) {
            if (err.code === 'ENOENT') {
                winston.error('No locale files could be found at ' + err.path);
            } else {
                winston.error('Error reading locales.', err);
            }
        }

        (files || []).forEach(function (file) {
            if (file.substr(-5) === '.json') {
                cached_available_locales.push(file.slice(0, -5));
            }
        });
    });
}

/**
 * Handle the /assets/locales/magic.json request
 * Find the closest translation we have for the language
 * set in the browser.
 **/
function serveMagicLocale(request, response) {
    var default_locale_id = 'en-gb',
        found_locale, negotiator;

    if (!request.headers['accept-language']) {
        // No accept-language specified in the request so send the default
        found_locale = default_locale_id;

    } else {
        negotiator = new Negotiator(request);
        found_locale = negotiator.language(cached_available_locales);

        // If a locale couldn't be negotiated, use the default
        found_locale = found_locale || default_locale_id;
    }

    // Send a locale to the browser
    this.file_server.serveFile('/assets/locales/' + found_locale + '.json', 200, {
        Vary: 'Accept-Language',
        'Content-Language': found_locale
    }, request, response);
}

/**
 * Handle the settings.json request
 */
function serveSettings(request, response) {
    var referrer_url,
        debug = false;

    // Check the referrer for a debug option
    if (request.headers.referer) {
        referrer_url = url.parse(request.headers.referer, true);
        if (referrer_url.query && referrer_url.query.debug) {
            debug = true;
        }
    }

    SettingsGenerator.get(debug, function(err, settings) {
        if (err) {
            winston.error('Error generating settings', err);
            response.writeHead(500, 'Internal Server Error');
            return response.end();
        }

        if (request.headers['if-none-match'] && request.headers['if-none-match'] === settings.hash) {
            response.writeHead(304, 'Not Modified');
            return response.end();
        }

        response.writeHead(200, {
            'ETag': settings.hash,
            'Content-Type': 'application/json'
        });
        response.end(settings.settings);
    });
}
<|endoftext|>


<commit_before>var net = require('net');

var HOST = '127.0.0.1';
var PORT = 1234;

header = "";

function WriteToLog(text) {
    var logDiv = $('#log')
    logDiv.empty()
    var newItem = $('
' + text + '
');
    logDiv.append(newItem);
}

function ConnectHandler(err) {
    if (!err) {
        WriteToLog('CONNECTED TO: ' + HOST + ':' + PORT);
    }
}

function ErrorHandler(err) {
    WriteToLog(err.code)
}

function DataHander(data) {
    var plane = $('#plane');

    if (header === "") {
        header = data;
        WriteToLog('HEADER: ' + data);
        return
    }

    var dataSplit = data.toString().split(",");

    plane.css({
        WebkitTransform: 'rotateZ(' + dataSplit[3] + 'deg)'
    });
    plane.css({
        WebkitTransform: 'rotateX(' + dataSplit[4] + 'deg)'
    });
    plane.css({
        WebkitTransform: 'rotateY(' + dataSplit[5] + 'deg)'
    });

    WriteToLog('
' + 'DATA: ' + dataSplit[3] + dataSplit[4] + dataSplit[5] + '
');
}

var client = new net.Socket();

client.connect(PORT, HOST, ConnectHandler);
client.on('error', ErrorHandler);
client.on('data', DataHander);
client.on('close', CloseHandler);

function CloseHandler() {
    WriteToLog('Connection closed');
    WriteToLog('Retrying connection');
    client = new net.Socket();
    client.connect(PORT, HOST, ConnectHandler);
    client.on('error', ErrorHandler);
    client.on('data', DataHander);
    client.on('close', CloseHandler);
}<commit_msg>Map header values to data<commit_after>var Net = require('net');

var Host = '127.0.0.1';
var Port = 1234;

var Header = [];
var FlightData = {};

function WriteToLog(text) {
    var logDiv = $('#log')
    logDiv.empty()
    var newItem = $('
' + text + '
');
    logDiv.append(newItem);
}

function ConnectHandler(err) {
    if (!err) {
        WriteToLog('Connected: ' + Host + ':' + Port);
    }
}

function ErrorHandler(err) {
    WriteToLog(err.code)
}

function DataHander(data) {
    if (Header.length === 0) {
        Header = data.toString().split(",");
        WriteToLog('Header: ' + data);
        return
    }

    var dataSplit = data.toString().split(",");

    for (var i = 0; i < dataSplit.length; i++) {
        FlightData[Header[i]] = dataSplit[i];
    }

    WriteToLog('
' + 'Data: ' + FlightData["pitch"] + FlightData["roll"] + FlightData["yaw"] + '
');

    UpdateUI()
}

function UpdateUI() {
    var plane = $('#plane');

    plane.css({
        WebkitTransform: 'rotateZ(' + FlightData["pitch"] + 'deg)'
    });
    plane.css({
        WebkitTransform: 'rotateX(' + FlightData["roll"] + 'deg)'
    });
    plane.css({
        WebkitTransform: 'rotateY(' + FlightData["yaw"] + 'deg)'
    });
}

var client = new Net.Socket();

client.connect(Port, Host, ConnectHandler);
client.on('error', ErrorHandler);
client.on('data', DataHander);
client.on('close', CloseHandler);

function CloseHandler() {
    WriteToLog('Connection closed');
    WriteToLog('Retrying connection');
    client = new Net.Socket();
    client.connect(Port, Host, ConnectHandler);
    client.on('error', ErrorHandler);
    client.on('data', DataHander);
    client.on('close', CloseHandler);
}<|endoftext|>


<commit_before>var url         = require('url');
var util        = require('util');
var http        = require('http');
var request     = require('request');
var cheerio     = require('cheerio');
var utils       = require('./utils');
var EE          = require('events').EventEmitter;
var dStop       = require('debug')('schema-check:visitor:stopSearch');
var dReq        = require('debug')('schema-check:visitor:makeRequest');
var dVisitRecur = require('debug')('schema-check:visitor:visitorRecursively');
var dReqVerbose = require('debug')('schema-check:visitor:makeRequest:verbose');

util.inherits(Visitor, EE);

function Visitor() {
  var self = this;

  this.on('stop', function _stopSearchHandler() {
    dStop('_stopSearchHandler: Stopping any more network requests');
    self._stopped = true;
  });
}

Visitor.prototype.visitRecursively = function visitRecursively(inputUrl, state) {
  var self = this;
  var parsedInputUrl;

  dVisitRecur('starting with (raw) input url %s', inputUrl);
  // dVisitRecur('state is %s', state);

  // emit error if inputUrl is falsy and return
  if (!inputUrl) {
    dVisitRecur('emitting error of type InputError');
    this.emit('errored', {
      error: new Error('Empty input url'),
      type: 'InputError',
      input: inputUrl
    });
    return;
  }

  // if state is falsy, set it to empty object
  state = state || {};

  try {
    parsedInputUrl = url.parse(inputUrl);
  }
  catch (e) {
    dVisitRecur('emitting error of type InputError');
    this.emit('errored', {
      error: e,
      type: 'InputError',
      input: inputUrl
    });
    return;
  }

  // if there's no host then emit error and return
  if (!parsedInputUrl.host || !parsedInputUrl.protocol) {
    dVisitRecur('emitting error of type InputError');
    this.emit('errored', {
      error: new Error('Malformed input url. It has no host or protocol'),
      type: 'InputError',
      input: inputUrl
    });
    return;
  }

  // store the initial url that visitor was called with
  // links found on this page are checked if they
  state.origin        = state.origin || parsedInputUrl.host.replace('www.', '');
  state.originWithWWW = state.originWithWWW || 'www.' + state.origin;
  state.protocol      = state.protocol || parsedInputUrl.protocol;

  // trim the last '/' from inputUrl cuz no keys in state have trailing '/'
  inputUrl = '/' === inputUrl[inputUrl.length - 1] ? inputUrl.slice(0, -1) : inputUrl;

  // trim whitespace
  inputUrl = inputUrl.trim();

  // todo: I should probably strip 'www.' from input url to prevent network call for both, say,
  // http://www.x.com and http://x.com
  // but then again, some people might have setup weird redirects for www. and non www.
  // if i decided to enable this, the fix is below
  inputUrl = inputUrl.replace(/^(https?:\/\/)www\./g, '$1');

  dVisitRecur('processed input url is %s', inputUrl);

  // if inputUrl has been visited then return
  if (state[inputUrl]) {
    console.log('Already visited %s', inputUrl);
    return;
  }

  dVisitRecur('has the search been stopped? %s', !!this._stopped);
  // if search has been stopped then just return blindly and dont hit the network
  if (this._stopped) return;

  // mark inputUrl as visited
  state[inputUrl] = true;

  // dVisitRecur('updated state is %o', state);

  // BIG BOI MAKING REQUEST OMG OMG THIS IS THE CALL THAT MAKES IT ALL HAPPEN
  // (including stackoverflow, sigh -.-)
  dVisitRecur('making request for url');
  makeRequest(self, inputUrl, parsedInputUrl, state);

  dVisitRecur('done with url %s. returning..\n', inputUrl);
};

function makeRequest(self, inputUrl, parsedInputUrl, state) {
  request(inputUrl, function _requestHandler(err, res, body) {
    var $;
    var data;
    var $htmlSchema;
    var $jsonSchema;
    // var urlsWithoutHttpAndHttpsRegex = /^(-\.)?([^\s/?\.#-]+\.?)+(\/[^\s]*)?$/i;

    // dReq('res %o', res);
    // dReq('body %s', body);

    dReqVerbose('parsed input url is %o', parsedInputUrl);

    // if error, emit error and return
    if (err) {
      dReq('emitting error of type RequestError %o', err);
      self.emit('errored', {
        error: err,
        message: err.message,
        type: 'RequestError',
        code: 500,
        input: inputUrl
      });
      return;
    }

    // if status is anything other than a 200, emit httpError and return
    if (res.statusCode !== 200) {
      err = new Error(http.STATUS_CODES[res.statusCode]);
      dReq('response status code is %s', res.statusCode);
      dReq('emitting error of type HttpError %o', err);
      self.emit('errored', {
        error: err,
        message: err.message,
        type: 'HttpError',
        input: inputUrl,
        code: res.statusCode
      });
      return;
    }

    //all good, move on

    // load the body into cheerio
    $ = cheerio.load(body);

    // check if any schemas present
    $htmlSchema = $('[itemtype*="schema.org"]');

    // get the schema that is added as json (for example., flipkart.com has schema embedded as json in a script tag)
    $jsonSchema = $('script[type*="ld+json"]');

    // dReqVerbose('html schema %o', $htmlSchema);
    // dReqVerbose('json schema %o', $jsonSchema.text());

    // no schemas? Emit notFound
    if (!$htmlSchema.length && !$jsonSchema.length) {
      dReq('emitting notFound');
      self.emit('notFound', {
        message: 'No schema found.',
        url: inputUrl
      });
    }
    else {
      /*
        structure
        {
        <itemtype>: {.. <itemprop>: <value> ..}
        }
      */
      data = {};

      if ($htmlSchema.length) { // go over each found schema
        $htmlSchema.each(function $htmlSchemaEachLoopCB(i, v) {
          var obj        = {};
          var $itemprops = $(v).find('[itemprop]');
          var itemtype   = $(v).attr('itemtype') || 'unknownItemtype' + i;

          data[itemtype] = data[itemtype] || {};

          // go over each child of current schema
          $itemprops.each(function $itempropsEachLoopCB(j, w) {
            // its of the form { <itemprop>: <value> }
            // example { name: "JW Heating & Air", streetAddress: "1135 Venice Blvd." }
            obj[$(w).attr('itemprop') || 'itemprop' + j] = $(w).text() || $(w).attr('src') || $(w).val();
          });

          // build data that will be emitted after all of this
          data[itemtype] = obj;
        });
      }

      if ($jsonSchema.length) { // go over the json schema and add to data
        $jsonSchema.each(function $jsonSchemaEachLoopCB(i, v) {
          try {
            var json = JSON.parse($(v).text());
            var type = json['@type'] || json.type;

            dReqVerbose('parsed json from json schema is %o', json);
            data[type] = data[type] || {};

            Object.keys(json).forEach(function(key) {
              data[type][key] = utils.stringifyObject(json[key], ', ');
            });
          }
          catch (e) {
            dReqVerbose('error in jsonSchema each loop %o', e);
          }

        });
      }

      dReq('emitting data %o', {
        data: data,
        url: inputUrl
      });
      // emit data
      // if there were no itemprops for an itemtype then data[itemtype]
      // will be [] i.e., empty array
      self.emit('data', {
        data: data,
        url: inputUrl
      });
    }

    // get the links on this page and call Visitor.prototype.visitRecursively on em, recursively :3
    $('a').each(function $aEachCB(i, v) {
      try {
        var temp         = url.parse($(v).attr('href'));
        var nextInputUrl = '';

        if (state.origin === temp.host || state.originWithWWW === temp.host || (!temp.host && temp.href)) {

          dReqVerbose('untouched temp.pathname is %s', temp.pathname);

          if (temp.pathname && ('/' !== temp.pathname[0] && '.' !== temp.pathname[0])) temp.pathname = '/' + temp.pathname;

          dReqVerbose('resolving %s and %s', parsedInputUrl.pathname, temp.pathname);

          // resolve relative href (like ../contact or ./resume) against the current pathname we are at i.e., parsedInputUrl.pathname
          temp.pathname = url.resolve(parsedInputUrl.pathname, temp.pathname);

          dReqVerbose('resolved pathname is %s', temp.pathname);

          nextInputUrl = 'http' === temp.protocol || 'https' === temp.protocol ? temp.protocol : state.protocol;
          nextInputUrl += '//';
          nextInputUrl += temp.host || state.origin;
          nextInputUrl += '/' === temp.pathname[0] ? temp.pathname : '/' + temp.pathname;
          nextInputUrl = '/' === nextInputUrl[nextInputUrl.length - 1] ? nextInputUrl.slice(0, -1) : nextInputUrl;
          nextInputUrl = nextInputUrl.replace(/^(https?:\/\/)www\./g, '$1');
          nextInputUrl = nextInputUrl.trim();

          if (nextInputUrl && !state[nextInputUrl]) {
            self.emit('state', state);
            process.nextTick(self.visitRecursively.bind(self, nextInputUrl, state));
            dReqVerbose('done calling visitRecursively with next input url %s', nextInputUrl);
          }
        }
      }
      catch (e) {
        dReq('error during parsing $(\'a\') hrefs %o', err);
        dReqVerbose('Faulty input was %s', $(v).attr('href'));
      }
    });
  });
}

module.exports = Visitor;

// var x = new Visitor;

// x.visitRecursively('http://muditameta.com', {});

// x.on('errored', function errorHandler(e) {
//   console.log('Error: %s', e.type);
//   console.log(e.error);
//   console.log(e.input);
// });

// x.on('data', function dataHandler(d) {
//   console.log('Data for %s', d.url);
//   console.log(d.data);
// });

// x.on('notFound', console.log.bind(console));
<commit_msg>[1.0.0] Fixes to solve the stack overflow issue<commit_after>var url         = require('url');
var util        = require('util');
var http        = require('http');
var request     = require('request');
var cheerio     = require('cheerio');
var utils       = require('./utils');
var EE          = require('events').EventEmitter;
var dStop       = require('debug')('schema-check:visitor:stopSearch');
var dReq        = require('debug')('schema-check:visitor:makeRequest');
var dVisitRecur = require('debug')('schema-check:visitor:visitorRecursively');
var dReqVerbose = require('debug')('schema-check:visitor:makeRequest:verbose');

util.inherits(Visitor, EE);

function Visitor() {
  var self = this;

  this.on('stop', function _stopSearchHandler() {
    dStop('_stopSearchHandler: Stopping any more network requests');
    self._stopped = true;
  });
}

Visitor.prototype.visitRecursively = function visitRecursively(inputUrl, state) {
  var self = this;
  var parsedInputUrl;

  dVisitRecur('starting with (raw) input url %s', inputUrl);
  // dVisitRecur('state is %s', state);

  // emit error if inputUrl is falsy and return
  if (!inputUrl) {
    dVisitRecur('emitting error of type InputError');
    this.emit('errored', {
      error: new Error('Empty input url'),
      type: 'InputError',
      input: inputUrl
    });
    return;
  }

  // if state is falsy, set it to empty object
  state = state || {};

  try {
    parsedInputUrl = url.parse(inputUrl);
  }
  catch (e) {
    dVisitRecur('emitting error of type InputError');
    this.emit('errored', {
      error: e,
      type: 'InputError',
      input: inputUrl
    });
    return;
  }

  // if there's no host then emit error and return
  if (!parsedInputUrl.host || !parsedInputUrl.protocol) {
    dVisitRecur('emitting error of type InputError');
    this.emit('errored', {
      error: new Error('Malformed input url. It has no host or protocol'),
      type: 'InputError',
      input: inputUrl
    });
    return;
  }

  // store the initial url that visitor was called with
  // links found on this page are checked if they
  state.origin        = state.origin || parsedInputUrl.host.replace('www.', '');
  state.originWithWWW = state.originWithWWW || 'www.' + state.origin;
  state.protocol      = state.protocol || parsedInputUrl.protocol;

  // trim the last '/' from inputUrl cuz no keys in state have trailing '/'
  inputUrl = '/' === inputUrl[inputUrl.length - 1] ? inputUrl.slice(0, -1) : inputUrl;

  // trim whitespace
  inputUrl = inputUrl.trim();

  // todo: I should probably strip 'www.' from input url to prevent network call for both, say,
  // http://www.x.com and http://x.com
  // but then again, some people might have setup weird redirects for www. and non www.
  // if i decided to enable this, the fix is below
  inputUrl = inputUrl.replace(/^(https?:\/\/)www\./g, '$1');

  dVisitRecur('processed input url is %s', inputUrl);

  // if inputUrl has been visited then return
  if (state[inputUrl]) {
    console.log('Already visited %s', inputUrl);
    return;
  }

  dVisitRecur('has the search been stopped? %s', !!this._stopped);
  // if search has been stopped then just return blindly and dont hit the network
  if (this._stopped) return;

  // mark inputUrl as visited
  state[inputUrl] = true;

  // dVisitRecur('updated state is %o', state);

  // BIG BOI MAKING REQUEST OMG OMG THIS IS THE CALL THAT MAKES IT ALL HAPPEN
  // (including stackoverflow, sigh -.-)
  dVisitRecur('making request for url');
  makeRequest(self, inputUrl, parsedInputUrl, state);

  dVisitRecur('done with url %s. returning..\n', inputUrl);
};

function makeRequest(self, inputUrl, parsedInputUrl, state) {
  if (self._stopped) {
    dReqVerbose('Search stopped, returning without any further recursive crawling from url %s', inputUrl);
    return;
  }
  request(inputUrl, function _requestHandler(err, res, body) {
    var $;
    var data;
    var $htmlSchema;
    var $jsonSchema;

    // dReq('res %o', res);
    // dReq('body %s', body);
    if (!utils.isSupportedMimeType(res.headers['content-type'].trim())) {
      dReq('unsupported mime type found');
      dReq('emitting error of type MimeTypeMismatchError %o', err);
      err = new Error('Unsupported mime type');
      self.emit('errored', {
        error: err,
        message: err.message,
        type: 'MimeTypeMismatchError',
        code: 500,
        input: inputUrl
      });
      return;
    }

    dReqVerbose('parsed input url is %o', parsedInputUrl);

    // if error, emit error and return
    if (err) {
      dReq('emitting error of type RequestError %o', err);
      self.emit('errored', {
        error: err,
        message: err.message,
        type: 'RequestError',
        code: 500,
        input: inputUrl
      });
      return;
    }

    // if status is anything other than a 200, emit httpError and return
    if (res.statusCode !== 200) {
      err = new Error(http.STATUS_CODES[res.statusCode]);
      dReq('response status code is %s', res.statusCode);
      dReq('emitting error of type HttpError %o', err);
      self.emit('errored', {
        error: err,
        message: err.message,
        type: 'HttpError',
        input: inputUrl,
        code: res.statusCode
      });
      return;
    }

    //all good, move on

    // load the body into cheerio
    $ = cheerio.load(body);

    // check if any schemas present
    $htmlSchema = $('[itemtype*="schema.org"]');

    // get the schema that is added as json (for example., flipkart.com has schema embedded as json in a script tag)
    $jsonSchema = $('script[type*="ld+json"]');

    // dReqVerbose('html schema %o', $htmlSchema);
    // dReqVerbose('json schema %o', $jsonSchema.text());

    // no schemas? Emit notFound
    if (!$htmlSchema.length && !$jsonSchema.length) {
      dReq('emitting notFound');
      self.emit('notFound', {
        message: 'No schema found.',
        url: inputUrl
      });
    }
    else {
      /*
        structure
        {
        <itemtype>: {.. <itemprop>: <value> ..}
        }
      */
      data = {};

      if ($htmlSchema.length) { // go over each found schema
        $htmlSchema.each(function $htmlSchemaEachLoopCB(i, v) {
          var obj        = {};
          var $itemprops = $(v).find('[itemprop]');
          var itemtype   = $(v).attr('itemtype') || 'unknownItemtype' + i;

          data[itemtype] = data[itemtype] || {};

          // go over each child of current schema
          $itemprops.each(function $itempropsEachLoopCB(j, w) {
            // its of the form { <itemprop>: <value> }
            // example { name: "JW Heating & Air", streetAddress: "1135 Venice Blvd." }
            obj[$(w).attr('itemprop') || 'itemprop' + j] = $(w).text() || $(w).attr('src') || $(w).val();
          });

          // build data that will be emitted after all of this
          data[itemtype] = obj;
        });
      }

      if ($jsonSchema.length) { // go over the json schema and add to data
        $jsonSchema.each(function $jsonSchemaEachLoopCB(i, v) {
          try {
            var json = JSON.parse($(v).text());
            var type = json['@type'] || json.type;

            dReqVerbose('parsed json from json schema is %o', json);
            data[type] = data[type] || {};

            Object.keys(json).forEach(function(key) {
              data[type][key] = utils.stringifyObject(json[key], ', ');
            });
          }
          catch (e) {
            dReqVerbose('error in jsonSchema each loop %o', e);
          }

        });
      }

      dReq('emitting data %o', {
        data: data,
        url: inputUrl
      });
      // emit data
      // if there were no itemprops for an itemtype then data[itemtype]
      // will be [] i.e., empty array
      self.emit('data', {
        data: data,
        url: inputUrl
      });
    }

    dReq('No of links found for url %s is %d', $('a').length);
    // get the links on this page and call Visitor.prototype.visitRecursively on em, recursively :3
    $('a').each(function $aEachCB(i, v) {
      try {
        var temp         = url.parse($(v).attr('href'));
        var nextInputUrl = '';

        if (state.origin === temp.host || state.originWithWWW === temp.host || (!temp.host && temp.href)) {

          dReqVerbose('untouched temp.pathname is %s', temp.pathname);

          if (temp.pathname && '/' !== temp.pathname[0] && '.' !== temp.pathname[0]) temp.pathname = '/' + temp.pathname;

          dReqVerbose('resolving %s and %s', parsedInputUrl.pathname, temp.pathname);

          // resolve relative href (like ../contact or ./resume) against the current pathname we are at i.e., parsedInputUrl.pathname
          temp.pathname = url.resolve(parsedInputUrl.pathname, temp.pathname);

          dReqVerbose('resolved pathname is %s', temp.pathname);

          nextInputUrl = 'http' === temp.protocol || 'https' === temp.protocol ? temp.protocol : state.protocol;
          nextInputUrl += '//';
          nextInputUrl += temp.host || state.origin;
          nextInputUrl += '/' === temp.pathname[0] ? temp.pathname : '/' + temp.pathname;
          nextInputUrl = '/' === nextInputUrl[nextInputUrl.length - 1] ? nextInputUrl.slice(0, -1) : nextInputUrl;
          nextInputUrl = nextInputUrl.replace(/^(https?:\/\/)www\./g, '$1');
          nextInputUrl = nextInputUrl.trim();

          dReqVerbose('next input url is %s', nextInputUrl);
          if (nextInputUrl && !state[nextInputUrl]) {
            self.emit('state', state);
            process.nextTick(self.visitRecursively.bind(self, nextInputUrl, state));
            dReqVerbose('done calling visitRecursively with next input url %s', nextInputUrl);
          }
        }
      }
      catch (e) {
        dReq('error during parsing $(\'a\') hrefs %o', err);
        dReqVerbose('Faulty input was %s', $(v).attr('href'));
      }
    });
  });
}

module.exports = Visitor;

// var x = new Visitor;

// x.visitRecursively('http://muditameta.com', {});

// x.on('errored', function errorHandler(e) {
//   console.log('Error: %s', e.type);
//   console.log(e.error);
//   console.log(e.input);
// });

// x.on('data', function dataHandler(d) {
//   console.log('Data for %s', d.url);
//   console.log(d.data);
// });

// x.on('notFound', console.log.bind(console));
<|endoftext|>


<commit_before>import models, {sequelize} from '../models';
import currencies from '../constants/currencies'
import config from 'config';

/*
* Hacky way to do currency conversion
*/
const generateFXConversionSQL = (aggregate) => {
  let currencyColumn = "t.currency";
  let amountColumn = "t.\"netAmountInGroupCurrency\"";

  if (aggregate) {
    currencyColumn = 'MAX(t.currency)';
    amountColumn = 'SUM("t.\"netAmountInGroupCurrency\"")';
  }

  const fxConversion = [];
  for (const currency in currencies) {
    fxConversion.push([currency, currencies[currency].fxrate]);
  }

  let sql = 'CASE ';
  sql += fxConversion.map(currency => `WHEN ${currencyColumn} = '${currency[0]}' THEN ${amountColumn} / ${currency[1]}`).join('\n');
  sql += 'ELSE 0 END';

  return sql;
};

const getTotalAnnualBudget = () => {
  return sequelize.query(`
  SELECT
    (SELECT
      COALESCE(SUM(${generateFXConversionSQL()} * 12),0)
      FROM "Subscriptions" s
      LEFT JOIN "Transactions" t
      ON (s.id = t."SubscriptionId"
        AND t.id = (SELECT MAX(id) from "Transactions" t where t."SubscriptionId" = s.id))
      WHERE t.amount > 0 AND t."GroupId" != 1
        AND t."deletedAt" IS NULL
        AND s.interval = 'month'
        AND s."isActive" IS TRUE
        AND s."deletedAt" IS NULL)
    +
    (SELECT
      COALESCE(SUM(${generateFXConversionSQL()}),0) FROM "Transactions" t
      LEFT JOIN "Subscriptions" s ON t."SubscriptionId" = s.id
      WHERE t.amount > 0 AND t."GroupId" != 1
        AND t."deletedAt" IS NULL
        AND ((s.interval = 'year' AND s."isActive" IS TRUE AND s."deletedAt" IS NULL) OR s.interval IS NULL))
    +
    (SELECT
      COALESCE(SUM(${generateFXConversionSQL()}),0) FROM "Transactions" t
      LEFT JOIN "Subscriptions" s ON t."SubscriptionId" = s.id
      WHERE t.amount > 0 AND t."GroupId" != 1
        AND t."deletedAt" IS NULL
        AND s.interval = 'month' AND s."isActive" IS FALSE AND s."deletedAt" IS NULL)
    "yearlyIncome"
  `, {
    type: sequelize.QueryTypes.SELECT
  })
  .then(res => Math.round(parseInt(res[0].yearlyIncome, 10)));
};

const getTotalDonations = () => {
  return sequelize.query(`
    SELECT SUM(${generateFXConversionSQL()}) AS "totalDonationsInUSD"
    FROM "Transactions"
    WHERE amount > 0 AND "PaymentMethodId" IS NOT NULL
  `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
  {
    type: sequelize.QueryTypes.SELECT
  })
  .then(res => Math.round(res[0].totalDonationsInUSD));
};

/**
 * Returns the top backers in a given time range in given tags
 * E.g. top backers in open source collectives last June
 */
const getTopBackers = (since, until, tags, limit) => {

  const sinceClause = (since) ? `AND t."createdAt" >= '${since.toISOString()}'`: '';
  const untilClause = (until) ? `AND t."createdAt" < '${until.toISOString()}'` : '';
  const tagsClause = (tags) ? `AND g.tags && $tags` : ''; // && operator means "overlaps"

  return sequelize.query(`
    SELECT MAX(u.id) as id, MAX(u."firstName") as "firstName", MAX(u."lastName") as "lastName", MAX(u.username) as username, MAX(u.website) as "website", MAX(u."twitterHandle") as "twitterHandle", MAX(u.avatar) as "avatar", SUM("amount") as "totalDonations", MAX(t.currency) as "currency"
    FROM "Transactions" t
    LEFT JOIN "Users" u ON u.id = t."UserId"
    LEFT JOIN "Groups" g ON g.id = t."GroupId"
    WHERE
      t.amount > 0
      ${sinceClause}
      ${untilClause}
      ${tagsClause}
    GROUP BY "UserId"
    ORDER BY "totalDonations" DESC
    LIMIT ${limit}
    `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
    {
      bind: { tags: tags || [] },
      model: models.User
    });
  }

/**
 * Get top collectives based on total donations
 */
const getGroupsByTag = (tag, limit, excludeList, minTotalDonation, randomOrder, orderBy, orderDir, offset) => {
  let tagClause = '';
  let excludeClause = '';
  let minTotalDonationClause = '';
  let orderClause = 'BY t."totalDonations"';
  const orderDirection = (orderDir === 'asc') ? 'ASC' : 'DESC';
  if (orderBy) {
    orderClause = `BY ${ orderBy }`;
  } else if (randomOrder) {
    orderClause = 'BY random()';
  }
  if (excludeList && excludeList.length > 0) {
    excludeClause = `AND g.id not in (${excludeList})`;
  }
  if (minTotalDonation && minTotalDonation > 0) {
    minTotalDonationClause = `t."totalDonations" >= ${minTotalDonation} AND`
  } else {
    minTotalDonationClause = ''
  }

  if (tag) {
    tagClause = 'g.tags && $tag AND'; // && operator means "overlaps", e.g. ARRAY[1,4,3] && ARRAY[2,1] == true
  }

  return sequelize.query(`
    WITH "totalDonations" AS (
      SELECT "GroupId", SUM(amount*100) as "totalDonations", MAX(currency) as currency, COUNT(DISTINCT "GroupId") as collectives FROM "Transactions" WHERE amount > 0 AND currency='USD' AND "PaymentMethodId" IS NOT NULL GROUP BY "GroupId"
    )
    SELECT g.id, g.name, g.slug, g.mission, g.logo, g."backgroundImage", g.currency, g.settings, g.data, t."totalDonations", t.collectives
    FROM "Groups" g LEFT JOIN "totalDonations" t ON t."GroupId" = g.id
    WHERE ${minTotalDonationClause} ${tagClause} g."deletedAt" IS NULL ${excludeClause}
    ORDER ${orderClause} ${orderDirection} NULLS LAST LIMIT ${limit} OFFSET ${offset || 0}
  `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
  {
    bind: { tag: [tag] },
    model: models.Group
  });
};

/**
* Get list of all unique tags for groups.
*/
const getUniqueGroupTags = () => {
  return sequelize.query('SELECT DISTINCT UNNEST(tags) FROM "Groups" WHERE ARRAY_LENGTH(tags, 1) > 0')
  .then(results => results[0].map(x => x.unnest).sort())
}

/**
 * Returns top sponsors ordered by number of collectives they sponsor and total amount donated
 */
const getTopSponsors = () => {
  return sequelize.query(`
    WITH "totalDonations" AS (
      SELECT "UserId", SUM(amount*100) as "totalDonations", MAX(currency) as currency, COUNT(DISTINCT "GroupId") as collectives FROM "Transactions" WHERE amount > 0 AND currency='USD' AND "PaymentMethodId" IS NOT NULL GROUP BY "UserId"
    )
    SELECT u.id, u."firstName", u."lastName", u.username, u.mission, u.description, u.avatar as logo, t."totalDonations", t.currency, t.collectives
    FROM "totalDonations" t LEFT JOIN "Users" u ON t."UserId" = u.id
    WHERE t."totalDonations" > 100 AND u."isOrganization" IS TRUE
    ORDER BY t.collectives DESC, "totalDonations" DESC LIMIT :limit
    `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
    {
      replacements: { limit: 6 },
      type: sequelize.QueryTypes.SELECT
  })
  .then(sponsors => sponsors.map(sponsor => {
    sponsor.publicUrl = `${config.host.website}/${sponsor.username}`
    return sponsor;
  }));
};

/**
 * Returns all the users of a group with their `totalDonations` and `role` (HOST/MEMBER/BACKER)
 */
const getUsersFromGroupWithTotalDonations = (GroupIds) => {
  const groupids = (typeof GroupIds === 'number') ? [GroupIds] : GroupIds;
  return sequelize.query(`
    WITH total_donations AS (
      SELECT
        max("UserId") as "UserId",
        SUM(amount) as amount
      FROM "Donations" d
      WHERE d."GroupId" IN (:groupids) AND d.amount >= 0
      GROUP BY "UserId"
    ), last_donation AS (
      SELECT
        max("UserId") as "UserId",
        max("updatedAt") as "updatedAt"
      FROM "Transactions" t
      WHERE t."GroupId" IN (:groupids) AND t.amount >= 0
      GROUP BY "UserId"
    )
    SELECT
      max(u.id) as id,
      max(ug."createdAt") as "createdAt",
      max(concat_ws(' ', u."firstName", u."lastName")) as name,
      max(u."firstName") as "firstName",
      max(u."lastName") as "lastName",
      max(u.username) as username,
      max(ug.role) as role,
      max(u.avatar) as avatar,
      max(u.website) as website,
      max(u.email) as email,
      max(u."twitterHandle") as "twitterHandle",
      max(td.amount) as "totalDonations",
      max(ld."updatedAt") as "lastDonation"
    FROM "Users" u
    LEFT JOIN total_donations td ON u.id = td."UserId"
    LEFT JOIN "UserGroups" ug ON u.id = ug."UserId"
    LEFT JOIN last_donation ld on ld."UserId" = ug."UserId"
    WHERE ug."GroupId" IN (:groupids)
    AND ug."deletedAt" IS NULL
    GROUP BY u.id
    ORDER BY "totalDonations" DESC, "createdAt" ASC
  `.replace(/\s\s+/g,' '), // this is to remove the new lines and save log space.
  {
    replacements: { groupids },
    type: sequelize.QueryTypes.SELECT
  });
};

export default {
  getTotalDonations,
  getTotalAnnualBudget,
  getUsersFromGroupWithTotalDonations,
  getTopSponsors,
  getTopBackers,
  getGroupsByTag,
  getUniqueGroupTags
};

<commit_msg>Simplify get users from group with total donations + add until<commit_after>import models, {sequelize} from '../models';
import currencies from '../constants/currencies'
import config from 'config';

/*
* Hacky way to do currency conversion
*/
const generateFXConversionSQL = (aggregate) => {
  let currencyColumn = "t.currency";
  let amountColumn = "t.\"netAmountInGroupCurrency\"";

  if (aggregate) {
    currencyColumn = 'MAX(t.currency)';
    amountColumn = 'SUM("t.\"netAmountInGroupCurrency\"")';
  }

  const fxConversion = [];
  for (const currency in currencies) {
    fxConversion.push([currency, currencies[currency].fxrate]);
  }

  let sql = 'CASE ';
  sql += fxConversion.map(currency => `WHEN ${currencyColumn} = '${currency[0]}' THEN ${amountColumn} / ${currency[1]}`).join('\n');
  sql += 'ELSE 0 END';

  return sql;
};

const getTotalAnnualBudget = () => {
  return sequelize.query(`
  SELECT
    (SELECT
      COALESCE(SUM(${generateFXConversionSQL()} * 12),0)
      FROM "Subscriptions" s
      LEFT JOIN "Transactions" t
      ON (s.id = t."SubscriptionId"
        AND t.id = (SELECT MAX(id) from "Transactions" t where t."SubscriptionId" = s.id))
      WHERE t.amount > 0 AND t."GroupId" != 1
        AND t."deletedAt" IS NULL
        AND s.interval = 'month'
        AND s."isActive" IS TRUE
        AND s."deletedAt" IS NULL)
    +
    (SELECT
      COALESCE(SUM(${generateFXConversionSQL()}),0) FROM "Transactions" t
      LEFT JOIN "Subscriptions" s ON t."SubscriptionId" = s.id
      WHERE t.amount > 0 AND t."GroupId" != 1
        AND t."deletedAt" IS NULL
        AND ((s.interval = 'year' AND s."isActive" IS TRUE AND s."deletedAt" IS NULL) OR s.interval IS NULL))
    +
    (SELECT
      COALESCE(SUM(${generateFXConversionSQL()}),0) FROM "Transactions" t
      LEFT JOIN "Subscriptions" s ON t."SubscriptionId" = s.id
      WHERE t.amount > 0 AND t."GroupId" != 1
        AND t."deletedAt" IS NULL
        AND s.interval = 'month' AND s."isActive" IS FALSE AND s."deletedAt" IS NULL)
    "yearlyIncome"
  `, {
    type: sequelize.QueryTypes.SELECT
  })
  .then(res => Math.round(parseInt(res[0].yearlyIncome, 10)));
};

const getTotalDonations = () => {
  return sequelize.query(`
    SELECT SUM(${generateFXConversionSQL()}) AS "totalDonationsInUSD"
    FROM "Transactions"
    WHERE amount > 0 AND "PaymentMethodId" IS NOT NULL
  `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
  {
    type: sequelize.QueryTypes.SELECT
  })
  .then(res => Math.round(res[0].totalDonationsInUSD));
};

/**
 * Returns the top backers in a given time range in given tags
 * E.g. top backers in open source collectives last June
 */
const getTopBackers = (since, until, tags, limit) => {

  const sinceClause = (since) ? `AND t."createdAt" >= '${since.toISOString()}'`: '';
  const untilClause = (until) ? `AND t."createdAt" < '${until.toISOString()}'` : '';
  const tagsClause = (tags) ? `AND g.tags && $tags` : ''; // && operator means "overlaps"

  return sequelize.query(`
    SELECT MAX(u.id) as id, MAX(u."firstName") as "firstName", MAX(u."lastName") as "lastName", MAX(u.username) as username, MAX(u.website) as "website", MAX(u."twitterHandle") as "twitterHandle", MAX(u.avatar) as "avatar", SUM("amount") as "totalDonations", MAX(t.currency) as "currency"
    FROM "Transactions" t
    LEFT JOIN "Users" u ON u.id = t."UserId"
    LEFT JOIN "Groups" g ON g.id = t."GroupId"
    WHERE
      t.amount > 0
      ${sinceClause}
      ${untilClause}
      ${tagsClause}
    GROUP BY "UserId"
    ORDER BY "totalDonations" DESC
    LIMIT ${limit}
    `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
    {
      bind: { tags: tags || [] },
      model: models.User
    });
  }

/**
 * Get top collectives based on total donations
 */
const getGroupsByTag = (tag, limit, excludeList, minTotalDonation, randomOrder, orderBy, orderDir, offset) => {
  let tagClause = '';
  let excludeClause = '';
  let minTotalDonationClause = '';
  let orderClause = 'BY t."totalDonations"';
  const orderDirection = (orderDir === 'asc') ? 'ASC' : 'DESC';
  if (orderBy) {
    orderClause = `BY ${ orderBy }`;
  } else if (randomOrder) {
    orderClause = 'BY random()';
  }
  if (excludeList && excludeList.length > 0) {
    excludeClause = `AND g.id not in (${excludeList})`;
  }
  if (minTotalDonation && minTotalDonation > 0) {
    minTotalDonationClause = `t."totalDonations" >= ${minTotalDonation} AND`
  } else {
    minTotalDonationClause = ''
  }

  if (tag) {
    tagClause = 'g.tags && $tag AND'; // && operator means "overlaps", e.g. ARRAY[1,4,3] && ARRAY[2,1] == true
  }

  return sequelize.query(`
    WITH "totalDonations" AS (
      SELECT "GroupId", SUM(amount*100) as "totalDonations", MAX(currency) as currency, COUNT(DISTINCT "GroupId") as collectives FROM "Transactions" WHERE amount > 0 AND currency='USD' AND "PaymentMethodId" IS NOT NULL GROUP BY "GroupId"
    )
    SELECT g.id, g.name, g.slug, g.mission, g.logo, g."backgroundImage", g.currency, g.settings, g.data, t."totalDonations", t.collectives
    FROM "Groups" g LEFT JOIN "totalDonations" t ON t."GroupId" = g.id
    WHERE ${minTotalDonationClause} ${tagClause} g."deletedAt" IS NULL ${excludeClause}
    ORDER ${orderClause} ${orderDirection} NULLS LAST LIMIT ${limit} OFFSET ${offset || 0}
  `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
  {
    bind: { tag: [tag] },
    model: models.Group
  });
};

/**
* Get list of all unique tags for groups.
*/
const getUniqueGroupTags = () => {
  return sequelize.query('SELECT DISTINCT UNNEST(tags) FROM "Groups" WHERE ARRAY_LENGTH(tags, 1) > 0')
  .then(results => results[0].map(x => x.unnest).sort())
}

/**
 * Returns top sponsors ordered by number of collectives they sponsor and total amount donated
 */
const getTopSponsors = () => {
  return sequelize.query(`
    WITH "totalDonations" AS (
      SELECT "UserId", SUM(amount*100) as "totalDonations", MAX(currency) as currency, COUNT(DISTINCT "GroupId") as collectives FROM "Transactions" WHERE amount > 0 AND currency='USD' AND "PaymentMethodId" IS NOT NULL GROUP BY "UserId"
    )
    SELECT u.id, u."firstName", u."lastName", u.username, u.mission, u.description, u.avatar as logo, t."totalDonations", t.currency, t.collectives
    FROM "totalDonations" t LEFT JOIN "Users" u ON t."UserId" = u.id
    WHERE t."totalDonations" > 100 AND u."isOrganization" IS TRUE
    ORDER BY t.collectives DESC, "totalDonations" DESC LIMIT :limit
    `.replace(/\s\s+/g, ' '), // this is to remove the new lines and save log space.
    {
      replacements: { limit: 6 },
      type: sequelize.QueryTypes.SELECT
  })
  .then(sponsors => sponsors.map(sponsor => {
    sponsor.publicUrl = `${config.host.website}/${sponsor.username}`
    return sponsor;
  }));
};

/**
 * Returns all the users of a group with their `totalDonations` and `role` (HOST/MEMBER/BACKER)
 */
const getUsersFromGroupWithTotalDonations = (GroupIds, until) => {
  const untilCondition = (table) => until ? `AND ${table}."createdAt" < '${until.toISOString().toString().substr(0,10)}'` : '';

  const groupids = (typeof GroupIds === 'number') ? [GroupIds] : GroupIds;
  return sequelize.query(`
    WITH stats AS (
      SELECT
        max("UserId") as "UserId",
        SUM("amountInTxnCurrency") as "totalDonations",
        max("createdAt") as "lastDonation",
        min("createdAt") as "firstDonation"
      FROM "Transactions" t
      WHERE t."GroupId" IN (:groupids) AND t.amount >= 0 ${untilCondition('t')}
      GROUP BY t."UserId"
    )
    SELECT
      max(u.id) as id,
      max(ug."createdAt") as "createdAt",
      max(concat_ws(' ', u."firstName", u."lastName")) as name,
      max(u."firstName") as "firstName",
      max(u."lastName") as "lastName",
      max(u.username) as username,
      max(ug.role) as role,
      max(u.avatar) as avatar,
      max(u.website) as website,
      max(u.email) as email,
      max(u."twitterHandle") as "twitterHandle",
      max(s."totalDonations") as "totalDonations",
      max(s."firstDonation") as "firstDonation",
      max(s."lastDonation") as "lastDonation"
    FROM "Users" u
    LEFT JOIN stats s ON u.id = s."UserId"
    LEFT JOIN "UserGroups" ug ON u.id = ug."UserId"
    WHERE ug."GroupId" IN (:groupids)
    AND ug."deletedAt" IS NULL ${untilCondition('ug')}
    GROUP BY u.id
    ORDER BY "totalDonations" DESC, "createdAt" ASC
  `.replace(/\s\s+/g,' '), // this is to remove the new lines and save log space.
  {
    replacements: { groupids },
    type: sequelize.QueryTypes.SELECT
  });
};

export default {
  getTotalDonations,
  getTotalAnnualBudget,
  getUsersFromGroupWithTotalDonations,
  getTopSponsors,
  getTopBackers,
  getGroupsByTag,
  getUniqueGroupTags
};

<|endoftext|>


<commit_before>/**
 * A Web-Map3DCityDB class to visualize Layer3DCityDB with Cesium.
 *
 * @alias Web-Map3DCityDB
 * @constructor
 *
 * @param {CesiumViewer} cesiumViewer
 */
(function() {
	function WebMap3DCityDB(cesiumViewer){
		this._cesiumViewerInstance = cesiumViewer;
		this._layers = [];
		this._mouseMoveEvents = false;
		this._mouseClickEvents = false;
		this._eventHandler = new Cesium.ScreenSpaceEventHandler(cesiumViewer.scene.canvas);
		this._cameraEventAggregator = new Cesium.CameraEventAggregator(cesiumViewer.scene.canvas);
		this._activeLayer = undefined;
		Cesium.knockout.track(this, ['_activeLayer']);
	}

	Object.defineProperties(WebMap3DCityDB.prototype, {
	    /**
	     * Gets or sets the active layer
	     * @memberof WebMap3DCityDB.prototype
	     * @type {3DCityDBLayer}
	     */
		activeLayer : {
	        get : function(){
	        	return this._activeLayer;
	        },
			set : function(value) {
				if (Cesium.defined(this._activeLayer)) {
					if (this._activeLayer.id != value.id) {
						this._activeLayer = value;
					}
				}
				else {
					this._activeLayer = value;
				}
			}
	    }
	});

	/**
	 * pass the object and modifier to the layer
	 * @param {string} modifier
	 * @param {Object} object
	 */
	WebMap3DCityDB.prototype.passEventToLayer = function(modifier, object){
		if(object){
			var i = 0;
			if(Cesium.BatchedModel && object instanceof Cesium.BatchedModel){
				var url = object.primitive.url;
				for(i = 0; i < this._layers.length; i++){
					if(this._layers[i].url == url){
						this._layers[i].triggerEvent(modifier, object);
						return true;
					}
				}
			}else{
				if(object.id && object.id.layerId){
					var layerid = object.id.layerId;
					for(i = 0; i < this._layers.length; i++){
						if(this._layers[i].id == layerid){
							this._layers[i].triggerEvent(modifier, object);
							return true;
						}
					}
				}
			}
		}
		return false;
	};

	/**
	 * adds a 3DCityDBLayer to the cesiumViewer
	 * @param {3DCityDBLayer} layer
	 */
	WebMap3DCityDB.prototype.addLayer = function(layer){
		for(var i = 0; i < this._layers.length; i++){
			if(layer.id == this._layers[i].id){
				return;
			}
		}
		layer.addToCesium(this._cesiumViewerInstance);
		this._layers.push(layer);
		return;
	};

	/**
	 * get a 3DCityDBLayer with the specified id
	 * @param {String} layerId
	 * @returns {3DCityDBLayer} The 3DCityDBLayer with the provided id or null if the id did not exist.
	 */
	WebMap3DCityDB.prototype.getLayerbyId = function(layerId){
		for (var i = 0; i < this._layers.length; i++){
			if (layerId == this._layers[i].id){
				return this._layers[i];
			}
		}
		return null;
	};

	/**
	 * @returns {Array.<3DCityDBLayer>} An array with 3dcitydb layer
	 */
	WebMap3DCityDB.prototype.getLayers = function(){
		return this._layers;
	};

	/**
	 * removes a 3DCityDBLayer from the cesiumViewer
	 * @param {String} id
	 */
	WebMap3DCityDB.prototype.removeLayer = function(id){
		for(var i = 0; i < this._layers.length; i++){
			var layer = this._layers[i];
			if(id == layer.id){
				layer.removeFromCesium(this._cesiumViewerInstance);
				this._layers.splice(i, 1);
				return;
			}
		}
		return;
	};

	/**
	 * activates viewchanged Event
	 * This event will be fired many times when the camera position or direction is changing
	 * @param {Boolean} active
	 */
	WebMap3DCityDB.prototype.activateViewChangedEvent = function(active){
		var that = this;
		var cesiumWidget = this._cesiumViewerInstance.cesiumWidget;
	    var camera = cesiumWidget.scene.camera;
	    var posX = camera.position.x;
	    var posY = camera.position.y;
	    var posZ = camera.position.z;
	    var dirX = camera.direction.x;
	    var dirY = camera.direction.y;
	    var dirZ = camera.direction.z;

	    // tolerance
	    var posD = 3;
		var dirD = 0.001;

		var listenerFunc = function() {
	    	var currentCamera = cesiumWidget.scene.camera;
	        var _posX = currentCamera.position.x;
	        var _posY = currentCamera.position.y;
	        var _posZ = currentCamera.position.z;
	        var _dirX = currentCamera.direction.x;
	        var _dirY = currentCamera.direction.y;
	        var _dirZ = currentCamera.direction.z;

	        if (Math.abs(posX - _posX) > posD ||
		    		Math.abs(posY - _posY) > posD ||
		    		Math.abs(posZ - _posZ) > posD ||
		    		Math.abs(dirX - _dirX) > dirD ||
		    		Math.abs(dirY - _dirY) > dirD ||
		    		Math.abs(dirZ - _dirZ) > dirD) {
	            console.log('view changed');
	            posX = _posX;
	            posY = _posY;
	            posZ = _posZ;
	            dirX = _dirX;
	            dirY = _dirY;
	            dirZ = _dirZ;
	            for (var i = 0; i < that._layers.length; i++){
					that._layers[i].triggerEvent("VIEWCHANGED");
				}
	        }
	    };

		if (active){
		    cesiumWidget.clock.onTick.addEventListener(listenerFunc);
		}
	};

	/**
	 * activates mouseClick Events over objects
	 * @param {boolean} active
	 */
	WebMap3DCityDB.prototype.activateMouseClickEvents = function(active){
		if(active){
			this._eventHandler.setInputAction(goog.bind(function(event){
				var object = this._cesiumViewerInstance.scene.pick(event.position);
				this.passEventToLayer("CLICK", object);
			}, this), Cesium.ScreenSpaceEventType.LEFT_CLICK);
			this._eventHandler.setInputAction(goog.bind(function(event){
				var object = this._cesiumViewerInstance.scene.pick(event.position);
				this.passEventToLayer("CTRLCLICK", object);
			}, this), Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.CTRL);
		}else{
			this._eventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
			this._eventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.CTRL);
		}
		this._mouseClickEvents = active;
	};

	/**
	 * activates mouseMove Events over objects
	 * @param {Boolean} active
	 */
	WebMap3DCityDB.prototype.activateMouseMoveEvents = function(active){
		var pickingInProgress = false;
		var currentObject = null;
		if(active){
			this._eventHandler.setInputAction(goog.bind(function(event){
				// When camera is moved do not trigger any other events
				if (this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.LEFT_DRAG) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.MIDDLE_DRAG) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.PINCH ) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.RIGHT_DRAG ) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.WHEEL)){
					return;
				}
				if(pickingInProgress) return;
				pickingInProgress = true;
				var object = this._cesiumViewerInstance.scene.pick(event.endPosition);
				if(currentObject && currentObject != object){
					if(this.passEventToLayer("MOUSEOUT", currentObject)){
						currentObject = null;
					}
				}
				if(object && currentObject != object){
					if(!this.passEventToLayer("MOUSEIN", object)){
						currentObject = null;
					}else{
						currentObject = object;
					}
				}
				pickingInProgress =false;
			}, this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
		}else{
			if(currentObject !== null){
				this.passEventToLayer("MOUSEOUT", currentObject);
				currentObject = null;
			}
			this._eventHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
		}
		this._mouseMoveEvents = active;
	};
	window.WebMap3DCityDB = WebMap3DCityDB;
})();
<commit_msg>dont use goog library in 3dcitydb-web-map<commit_after>/**
 * A Web-Map3DCityDB class to visualize Layer3DCityDB with Cesium.
 *
 * @alias Web-Map3DCityDB
 * @constructor
 *
 * @param {CesiumViewer} cesiumViewer
 */
(function() {
	function WebMap3DCityDB(cesiumViewer){
		this._cesiumViewerInstance = cesiumViewer;
		this._layers = [];
		this._mouseMoveEvents = false;
		this._mouseClickEvents = false;
		this._eventHandler = new Cesium.ScreenSpaceEventHandler(cesiumViewer.scene.canvas);
		this._cameraEventAggregator = new Cesium.CameraEventAggregator(cesiumViewer.scene.canvas);
		this._activeLayer = undefined;
		Cesium.knockout.track(this, ['_activeLayer']);
	}

	Object.defineProperties(WebMap3DCityDB.prototype, {
	    /**
	     * Gets or sets the active layer
	     * @memberof WebMap3DCityDB.prototype
	     * @type {3DCityDBLayer}
	     */
		activeLayer : {
	        get : function(){
	        	return this._activeLayer;
	        },
			set : function(value) {
				if (Cesium.defined(this._activeLayer)) {
					if (this._activeLayer.id != value.id) {
						this._activeLayer = value;
					}
				}
				else {
					this._activeLayer = value;
				}
			}
	    }
	});

	/**
	 * pass the object and modifier to the layer
	 * @param {string} modifier
	 * @param {Object} object
	 */
	WebMap3DCityDB.prototype.passEventToLayer = function(modifier, object){
		if(object){
			var i = 0;
			if(Cesium.BatchedModel && object instanceof Cesium.BatchedModel){
				var url = object.primitive.url;
				for(i = 0; i < this._layers.length; i++){
					if(this._layers[i].url == url){
						this._layers[i].triggerEvent(modifier, object);
						return true;
					}
				}
			}else{
				if(object.id && object.id.layerId){
					var layerid = object.id.layerId;
					for(i = 0; i < this._layers.length; i++){
						if(this._layers[i].id == layerid){
							this._layers[i].triggerEvent(modifier, object);
							return true;
						}
					}
				}
			}
		}
		return false;
	};

	/**
	 * adds a 3DCityDBLayer to the cesiumViewer
	 * @param {3DCityDBLayer} layer
	 */
	WebMap3DCityDB.prototype.addLayer = function(layer){
		for(var i = 0; i < this._layers.length; i++){
			if(layer.id == this._layers[i].id){
				return;
			}
		}
		layer.addToCesium(this._cesiumViewerInstance);
		this._layers.push(layer);
		return;
	};

	/**
	 * get a 3DCityDBLayer with the specified id
	 * @param {String} layerId
	 * @returns {3DCityDBLayer} The 3DCityDBLayer with the provided id or null if the id did not exist.
	 */
	WebMap3DCityDB.prototype.getLayerbyId = function(layerId){
		for (var i = 0; i < this._layers.length; i++){
			if (layerId == this._layers[i].id){
				return this._layers[i];
			}
		}
		return null;
	};

	/**
	 * @returns {Array.<3DCityDBLayer>} An array with 3dcitydb layer
	 */
	WebMap3DCityDB.prototype.getLayers = function(){
		return this._layers;
	};

	/**
	 * removes a 3DCityDBLayer from the cesiumViewer
	 * @param {String} id
	 */
	WebMap3DCityDB.prototype.removeLayer = function(id){
		for(var i = 0; i < this._layers.length; i++){
			var layer = this._layers[i];
			if(id == layer.id){
				layer.removeFromCesium(this._cesiumViewerInstance);
				this._layers.splice(i, 1);
				return;
			}
		}
		return;
	};

	/**
	 * activates viewchanged Event
	 * This event will be fired many times when the camera position or direction is changing
	 * @param {Boolean} active
	 */
	WebMap3DCityDB.prototype.activateViewChangedEvent = function(active){
		var that = this;
		var cesiumWidget = this._cesiumViewerInstance.cesiumWidget;
	    var camera = cesiumWidget.scene.camera;
	    var posX = camera.position.x;
	    var posY = camera.position.y;
	    var posZ = camera.position.z;
	    var dirX = camera.direction.x;
	    var dirY = camera.direction.y;
	    var dirZ = camera.direction.z;

	    // tolerance
	    var posD = 3;
		var dirD = 0.001;

		var listenerFunc = function() {
	    	var currentCamera = cesiumWidget.scene.camera;
	        var _posX = currentCamera.position.x;
	        var _posY = currentCamera.position.y;
	        var _posZ = currentCamera.position.z;
	        var _dirX = currentCamera.direction.x;
	        var _dirY = currentCamera.direction.y;
	        var _dirZ = currentCamera.direction.z;

	        if (Math.abs(posX - _posX) > posD ||
		    		Math.abs(posY - _posY) > posD ||
		    		Math.abs(posZ - _posZ) > posD ||
		    		Math.abs(dirX - _dirX) > dirD ||
		    		Math.abs(dirY - _dirY) > dirD ||
		    		Math.abs(dirZ - _dirZ) > dirD) {
	            console.log('view changed');
	            posX = _posX;
	            posY = _posY;
	            posZ = _posZ;
	            dirX = _dirX;
	            dirY = _dirY;
	            dirZ = _dirZ;
	            for (var i = 0; i < that._layers.length; i++){
					that._layers[i].triggerEvent("VIEWCHANGED");
				}
	        }
	    };

		if (active){
		    cesiumWidget.clock.onTick.addEventListener(listenerFunc);
		}
	};

	/**
	 * activates mouseClick Events over objects
	 * @param {boolean} active
	 */
	WebMap3DCityDB.prototype.activateMouseClickEvents = function(active){
		if(active){
			var that = this;
			this._eventHandler.setInputAction(function(event){
				var object = that._cesiumViewerInstance.scene.pick(event.position);
				that.passEventToLayer("CLICK", object);
			}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
			this._eventHandler.setInputAction(function(event){
				var object = that._cesiumViewerInstance.scene.pick(event.position);
				that.passEventToLayer("CTRLCLICK", object);
			}, Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.CTRL);
		}else{
			this._eventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
			this._eventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.CTRL);
		}
		this._mouseClickEvents = active;
	};

	/**
	 * activates mouseMove Events over objects
	 * @param {Boolean} active
	 */
	WebMap3DCityDB.prototype.activateMouseMoveEvents = function(active){
		var pickingInProgress = false;
		var currentObject = null;
		if(active){
			this._eventHandler.setInputAction(goog.bind(function(event){
				// When camera is moved do not trigger any other events
				if (this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.LEFT_DRAG) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.MIDDLE_DRAG) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.PINCH ) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.RIGHT_DRAG ) ||
						this._cameraEventAggregator.isButtonDown(Cesium.CameraEventType.WHEEL)){
					return;
				}
				if(pickingInProgress) return;
				pickingInProgress = true;
				var object = this._cesiumViewerInstance.scene.pick(event.endPosition);
				if(currentObject && currentObject != object){
					if(this.passEventToLayer("MOUSEOUT", currentObject)){
						currentObject = null;
					}
				}
				if(object && currentObject != object){
					if(!this.passEventToLayer("MOUSEIN", object)){
						currentObject = null;
					}else{
						currentObject = object;
					}
				}
				pickingInProgress =false;
			}, this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
		}else{
			if(currentObject !== null){
				this.passEventToLayer("MOUSEOUT", currentObject);
				currentObject = null;
			}
			this._eventHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
		}
		this._mouseMoveEvents = active;
	};
	window.WebMap3DCityDB = WebMap3DCityDB;
})();
<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Button for initiating game levels and for depicting the progress made on each level.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Imports
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ProgressIndicator = require( 'VEGAS/ProgressIndicator' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );

  /**
   * @param {Image} icon Scenery node that appears on the button, scaled to fit
   * @param {number} numStars Number of stars to show in the progress indicator at the bottom of the button
   * @param {function} fireFunction Called when the button fires
   * @param {Property<number>} scoreProperty
   * @param {number} perfectScore
   * @param {*} options
   * @constructor
   */
  function LevelStartButton( icon, numStars, fireFunction, scoreProperty, perfectScore, options ) {

    options = _.extend( {
      buttonWidth: 150,
      buttonHeight: 150,
      backgroundColor: 'rgb( 242, 255, 204 )',
      highlightedBackgroundColor: 'rgb( 250, 255, 230 )',
      shadowColor: 'black',
      cornerRadius: 10
    }, options );
    var shadowOffset = 0.026 * options.buttonWidth;

    Node.call( this ); // Call super constructor.

    // Add the drop shadow.
    this.addChild( new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
        fill: options.shadowColor,
        top: shadowOffset,
        left: shadowOffset
      }
    ) );

    // Add the button outline, which is also the parent node for everything else that is on the button.
    var buttonOutline = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
      stroke: 'black',
      lineWidth: 1,
      fill: options.backgroundColor,
      cursor: 'pointer'
    } );
    this.addChild( buttonOutline );

    // Icon, scaled to fit.
    var iconScaleFactor = Math.min( options.buttonHeight * 0.65 / icon.height, options.buttonWidth * 0.85 / icon.width );
    icon.scale( iconScaleFactor );
    icon.centerX = options.buttonWidth / 2;
    icon.centerY = options.buttonHeight * 0.4;
    icon.pickable = false;
    buttonOutline.addChild( icon );

    // Progress indicator (stars), scaled to fit
    var progressIndicatorBackground = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight * 0.2, options.cornerRadius, options.cornerRadius, {
      fill: 'white',
      stroke: 'black',
      lineWidth: 1,
      pickable: false,
      bottom: options.buttonHeight
    } );
    progressIndicatorBackground.addChild( new ProgressIndicator( numStars, options.buttonWidth / ( numStars + 1 ), scoreProperty, perfectScore, {
      centerX: progressIndicatorBackground.centerX,
      centerY: progressIndicatorBackground.height / 2,
      pickable: false
    } ) );
    buttonOutline.addChild( progressIndicatorBackground );

    //TODO This behavior was borrowed from sun.PushButton, because sun.RectanglePushButtonDeprecated doesn't support the pseudo-3D behavior of this button.
    // Add the listener to update the appearance and handle a click.
    var update = function( state ) {
      if ( state === 'up' ) {
        buttonOutline.fill = options.backgroundColor;
        buttonOutline.top = 0;
        buttonOutline.left = 0;
      }
      else if ( state === 'over' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
      }
      else if ( state === 'down' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
        buttonOutline.top = shadowOffset;
        buttonOutline.left = shadowOffset;
      }
    };
    buttonOutline.addInputListener( new ButtonListener( {
      up: function() { update( 'up' ); },
      over: function() { update( 'over' ); },
      down: function() { update( 'down' ); },
      out: function() { update( 'up' ); }, // 'out' state looks the same as 'up'
      fire: fireFunction
    } ) );

    // Update for initial state
    update( 'up' );

    // Pass options to parent class
    this.mutate( options );
  }

  return inherit( Node, LevelStartButton );
} );
<commit_msg>change comment<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Button for initiating game levels and for depicting the progress made on each level.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Imports
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ProgressIndicator = require( 'VEGAS/ProgressIndicator' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );

  /**
   * @param {Image} icon Scenery node that appears on the button, scaled to fit
   * @param {number} numStars Number of stars to show in the progress indicator at the bottom of the button
   * @param {function} fireFunction Called when the button fires
   * @param {Property<number>} scoreProperty
   * @param {number} perfectScore
   * @param {*} options
   * @constructor
   */
  function LevelStartButton( icon, numStars, fireFunction, scoreProperty, perfectScore, options ) {

    options = _.extend( {
      buttonWidth: 150,
      buttonHeight: 150,
      backgroundColor: 'rgb( 242, 255, 204 )',
      highlightedBackgroundColor: 'rgb( 250, 255, 230 )',
      shadowColor: 'black',
      cornerRadius: 10
    }, options );
    var shadowOffset = 0.026 * options.buttonWidth;

    Node.call( this ); // Call super constructor.

    // Add the drop shadow.
    this.addChild( new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
        fill: options.shadowColor,
        top: shadowOffset,
        left: shadowOffset
      }
    ) );

    // Add the button outline, which is also the parent node for everything else that is on the button.
    var buttonOutline = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
      stroke: 'black',
      lineWidth: 1,
      fill: options.backgroundColor,
      cursor: 'pointer'
    } );
    this.addChild( buttonOutline );

    // Icon, scaled to fit.
    var iconScaleFactor = Math.min( options.buttonHeight * 0.65 / icon.height, options.buttonWidth * 0.85 / icon.width );
    icon.scale( iconScaleFactor );
    icon.centerX = options.buttonWidth / 2;
    icon.centerY = options.buttonHeight * 0.4;
    icon.pickable = false;
    buttonOutline.addChild( icon );

    // Progress indicator (stars), scaled to fit
    var progressIndicatorBackground = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight * 0.2, options.cornerRadius, options.cornerRadius, {
      fill: 'white',
      stroke: 'black',
      lineWidth: 1,
      pickable: false,
      bottom: options.buttonHeight
    } );
    progressIndicatorBackground.addChild( new ProgressIndicator( numStars, options.buttonWidth / ( numStars + 1 ), scoreProperty, perfectScore, {
      centerX: progressIndicatorBackground.centerX,
      centerY: progressIndicatorBackground.height / 2,
      pickable: false
    } ) );
    buttonOutline.addChild( progressIndicatorBackground );

    //TODO This behavior was borrowed from sun.PushButton, because sun.RectanglePushButton doesn't support the look/behavior of this button.
    // Add the listener to update the appearance and handle a click.
    var update = function( state ) {
      if ( state === 'up' ) {
        buttonOutline.fill = options.backgroundColor;
        buttonOutline.top = 0;
        buttonOutline.left = 0;
      }
      else if ( state === 'over' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
      }
      else if ( state === 'down' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
        buttonOutline.top = shadowOffset;
        buttonOutline.left = shadowOffset;
      }
    };
    buttonOutline.addInputListener( new ButtonListener( {
      up: function() { update( 'up' ); },
      over: function() { update( 'over' ); },
      down: function() { update( 'down' ); },
      out: function() { update( 'up' ); }, // 'out' state looks the same as 'up'
      fire: fireFunction
    } ) );

    // Update for initial state
    update( 'up' );

    // Pass options to parent class
    this.mutate( options );
  }

  return inherit( Node, LevelStartButton );
} );
<|endoftext|>