<commit_before>/**
 * T'SoundSystem for JavaScript (Web Audio API)
 */

/**
 * AudioPlayback prototype
 *
 * This prototype provides an audio output stream for real time sound
 * rendering.
 * @author Takashi Toyoshima <toyoshim@gmail.com>
 *
 */
function AudioLooper (bufferSize) {
    this.bufferSize = 4096;  // 92msec
    if (arguments.length > 0)
        this.bufferSize = bufferSize;
    // Initialize variables.
    this.channel = null;
    this.initialized = true;

    // Web Audio API on Chrome and Safari.
    if (window.webkitAudioContext != undefined) {
        Log.getLog().info("use Web Audio API");
        this.audioContext = new webkitAudioContext();
        if (this.audioContext == null) {
            Log.getLog().fatal("could not use webkitAudioContext");
            this.initialized = false;
            return;
        }

        // Allocate JavaScript synthesis node.
        this.jsNode = this.audioContext.createJavaScriptNode(this.bufferSize);

        // Connect to output audio device.
        this.jsNode.connect(this.audioContext.destination);

        // Register callback
        this.jsNode.owner = this;
        this.jsNode.onaudioprocess = function (event) {
            this.owner.onAudioProcess(event);
        };

        return;
    }

    // Audio Data API on Firefox.
    if (window.Audio != undefined) {
        Log.getLog().info("use Audio Data API");
        this.audio = new Audio();
        if ((this.audio == null) || (this.audio.mozSetup == undefined)) {
            Log.getLog().fatal("could not use Audio Data API");
            this.initialized = false;
            return;
        }

        // Set up playback configuration.
        this.audioChannel = 2;
        this.audioFrequency = 44100;
        this.audio.mozSetup(this.audioChannel, this.audioFrequency);

        // Set up output buffer.
        this.bufferId = 0;
        this.bufferPage = 4;
        this.bufferWritten = 0;
        this.buffer = new Array(this.bufferPage);
        var arraySize = this.bufferSize * this.audioChannel;
        for (var i = 0; i < this.bufferPage; i++) {
            this.buffer[i] = new Float32Array(arraySize);
            this.bufferWritten += this.audio.mozWriteAudio(this.buffer[i]);
        }

        // Register callback with 50msec interval.
        this.audio.owner = this;
        setInterval(function (object) { object.onAudioInterval() }, 50, this);

        return;
    }
    Log.getLog().error("Audio API unavailable");
    this.initialized = false;
}

/**
 * Register sound generator.
 * @param newChannel sound generator
 */
AudioLooper.prototype.setChannel = function (newChannel) {
    if (null != newChannel) {
        newChannel.setBufferLength(this.bufferSize * 2);
    }
    this.channel = newChannel;
};

/**
 * Audio processing event handler for Web Audio API.
 * @param event AudioProcessingEvent
 */
AudioLooper.prototype.onAudioProcess = function (event) {
    // Logged event contents at the first event.
    if (null == this.firstAudioEvent) {
        this.firstAudioEvent = true;
        Log.getLog().info(event);
    }

    // Get Float32Array output buffer.
    var lOut = event.outputBuffer.getChannelData(0);
    var rOut = event.outputBuffer.getChannelData(1);

    // Process no input channel.
    var i;
    if (null == this.channel) {
        for (i = 0; i < this.bufferSize; i++) {
            lOut[i] = 0.0;
            rOut[i] = 0.0;
        }
        return;
    }

    // Get Int32Array input buffer.
    this.channel.generate(this.bufferSize * 2);
    var lrIn = this.channel.getBuffer();

    // Process buffer conversion.
    for (i = 0; i < this.bufferSize; i++) {
        lOut[i] = lrIn[i * 2 + 0] / 32768.0;
        rOut[i] = lrIn[i * 2 + 1] / 32768.0;
    }
};

/**
 * Audio interval callback handler for Audio Data API.
 */
AudioLooper.prototype.onAudioInterval = function () {
    // Logged event contents at the first event.
    if (null == this.firstAudioEvent) {
        this.firstAudioEvent = true;
        Log.getLog().info("onAudioInterval");
        Log.getLog().info(this);
    }

    // Check buffer status.
    var audioRead = this.audio.mozCurrentSampleOffset();
    var pageSize = this.bufferSize * this.audioChannel;
    var pageOffset = audioRead % (pageSize * this.bufferPage);
    var playingPage = ~~(pageOffset / pageSize);
    if (this.bufferId == playingPage &&
            this.bufferWritten != audioRead) {
        // Buffers are busy.
        return;
    }

    // Update buffer tracking variables.
    var lrOut = this.buffer[this.bufferId];
    this.bufferId = (this.bufferId + 1) % this.bufferPage;

    // Process next buffer.
    var i;
    if (null == this.channel) {
        // Process no input channel.
        for (i = 0; i < this.bufferSize; i++) {
            lrOut[i * 2 + 0] = 0.0;
            lrOut[i * 2 + 1] = 0.0;
        }
    } else {
        // Process buffer conversion.
        this.channel.generate(this.bufferSize * this.audioChannel);
        var lrIn = this.channel.getBuffer();
        for (i = 0; i < this.bufferSize; i++) {
            lrOut[i * 2 + 0] = lrIn[i * 2 + 0] / 32768.0;
            lrOut[i * 2 + 1] = lrIn[i * 2 + 1] / 32768.0;
        }
    }

    // Play next buffer.
    this.bufferWritten += this.audio.mozWriteAudio(lrOut);
};
<commit_msg>Bug fix on FireFox callback interval for customized buffer size<commit_after>/**
 * T'SoundSystem for JavaScript (Web Audio API)
 */

/**
 * AudioPlayback prototype
 *
 * This prototype provides an audio output stream for real time sound
 * rendering.
 * @author Takashi Toyoshima <toyoshim@gmail.com>
 *
 */
function AudioLooper (bufferSize) {
    this.bufferSize = 4096;  // 92msec
    if (arguments.length > 0)
        this.bufferSize = bufferSize;
    // Initialize variables.
    this.channel = null;
    this.initialized = true;

    // Web Audio API on Chrome and Safari.
    if (window.webkitAudioContext != undefined) {
        Log.getLog().info("use Web Audio API");
        this.audioContext = new webkitAudioContext();
        if (this.audioContext == null) {
            Log.getLog().fatal("could not use webkitAudioContext");
            this.initialized = false;
            return;
        }

        // Allocate JavaScript synthesis node.
        this.jsNode = this.audioContext.createJavaScriptNode(this.bufferSize);

        // Connect to output audio device.
        this.jsNode.connect(this.audioContext.destination);

        // Register callback
        this.jsNode.owner = this;
        this.jsNode.onaudioprocess = function (event) {
            this.owner.onAudioProcess(event);
        };

        return;
    }

    // Audio Data API on Firefox.
    if (window.Audio != undefined) {
        Log.getLog().info("use Audio Data API");
        this.audio = new Audio();
        if ((this.audio == null) || (this.audio.mozSetup == undefined)) {
            Log.getLog().fatal("could not use Audio Data API");
            this.initialized = false;
            return;
        }

        // Set up playback configuration.
        this.audioChannel = 2;
        this.audioFrequency = 44100;
        this.audio.mozSetup(this.audioChannel, this.audioFrequency);

        // Set up output buffer.
        this.bufferId = 0;
        this.bufferPage = 4;
        this.bufferWritten = 0;
        this.buffer = new Array(this.bufferPage);
        var arraySize = this.bufferSize * this.audioChannel;
        for (var i = 0; i < this.bufferPage; i++) {
            this.buffer[i] = new Float32Array(arraySize);
            this.bufferWritten += this.audio.mozWriteAudio(this.buffer[i]);
        }

        // Register callback with 50msec interval.
        this.audio.owner = this;

        // Set half time of buffer playback time.
        var interval = this.bufferSize * 1000 / 44100 / 2;
        setInterval(function (object) { object.onAudioInterval() }, interval,
            this);

        return;
    }
    Log.getLog().error("Audio API unavailable");
    this.initialized = false;
}

/**
 * Register sound generator.
 * @param newChannel sound generator
 */
AudioLooper.prototype.setChannel = function (newChannel) {
    if (null != newChannel) {
        newChannel.setBufferLength(this.bufferSize * 2);
    }
    this.channel = newChannel;
};

/**
 * Audio processing event handler for Web Audio API.
 * @param event AudioProcessingEvent
 */
AudioLooper.prototype.onAudioProcess = function (event) {
    // Logged event contents at the first event.
    if (null == this.firstAudioEvent) {
        this.firstAudioEvent = true;
        Log.getLog().info(event);
    }

    // Get Float32Array output buffer.
    var lOut = event.outputBuffer.getChannelData(0);
    var rOut = event.outputBuffer.getChannelData(1);

    // Process no input channel.
    var i;
    if (null == this.channel) {
        for (i = 0; i < this.bufferSize; i++) {
            lOut[i] = 0.0;
            rOut[i] = 0.0;
        }
        return;
    }

    // Get Int32Array input buffer.
    this.channel.generate(this.bufferSize * 2);
    var lrIn = this.channel.getBuffer();

    // Process buffer conversion.
    for (i = 0; i < this.bufferSize; i++) {
        lOut[i] = lrIn[i * 2 + 0] / 32768.0;
        rOut[i] = lrIn[i * 2 + 1] / 32768.0;
    }
};

/**
 * Audio interval callback handler for Audio Data API.
 */
AudioLooper.prototype.onAudioInterval = function () {
    // Logged event contents at the first event.
    if (null == this.firstAudioEvent) {
        this.firstAudioEvent = true;
        Log.getLog().info("onAudioInterval");
        Log.getLog().info(this);
    }

    // Check buffer status.
    var audioRead = this.audio.mozCurrentSampleOffset();
    var pageSize = this.bufferSize * this.audioChannel;
    var pageOffset = audioRead % (pageSize * this.bufferPage);
    var playingPage = ~~(pageOffset / pageSize);
    if (this.bufferId == playingPage &&
            this.bufferWritten != audioRead) {
        // Buffers are busy.
        return;
    }

    // Update buffer tracking variables.
    var lrOut = this.buffer[this.bufferId];
    this.bufferId = (this.bufferId + 1) % this.bufferPage;

    // Process next buffer.
    var i;
    if (null == this.channel) {
        // Process no input channel.
        for (i = 0; i < this.bufferSize; i++) {
            lrOut[i * 2 + 0] = 0.0;
            lrOut[i * 2 + 1] = 0.0;
        }
    } else {
        // Process buffer conversion.
        this.channel.generate(this.bufferSize * this.audioChannel);
        var lrIn = this.channel.getBuffer();
        for (i = 0; i < this.bufferSize; i++) {
            lrOut[i * 2 + 0] = lrIn[i * 2 + 0] / 32768.0;
            lrOut[i * 2 + 1] = lrIn[i * 2 + 1] / 32768.0;
        }
    }

    // Play next buffer.
    this.bufferWritten += this.audio.mozWriteAudio(lrOut);
};
<|endoftext|>


<commit_before>/* exported update_browserstring, config_open, text_preview_init */
/* global queues:false, active:false, notice_visible:true */
/* global queue:true, slide:true, config:true */

var queue = function(){
	'use strict';

	function all_queues_except(id){
		return queues.filter(function(x){ return x !== id; });
	}

	return {
		sorting: function(){
			/* test if queues exists on this page */
			if ( typeof(queues) == 'undefined' ){
				return;
			}

			/* queues is a global variable */
			for ( var i in queues ){
				var id = queues[i];
				var other = all_queues_except(id);
				$(id).bind('updated', function(){
					var current_id = '#' + $(this).attr('id');
					var list = $(this).sortable('toArray');
					var n = list.length;

					/* show/hide warning about empty queue */
					if ( current_id === active ){
						if ( notice_visible && n > 0 ){
							notice_visible = false;
							$("#empty_notice").fadeOut("slow");
						}
						if ( !notice_visible && n === 0 ){
							notice_visible = true;
							$("#empty_notice").fadeIn("slow");
						}
					}

					/* notify server about update */
					$.ajax({
						type: "POST",
						url: "/slides/ajax/move",
						data: "queue=" + $(this).attr('id') + "&slides=" + list,
						error: function(x, status, error){
							alert(status + '\n' + error);
						},
					});
				});
				$(id).sortable({
					connectWith: other,
					placeholder: 'slide_placeholder',
					tolerance: 'pointer',
					distance: 10,
					update: function(){
						$(this).trigger('updated');
					},
				});
				$(id).disableSelection();
			}
		},
	};
}();

var slide = function(){
	'use strict';

	var post = function(url, data, func){
		$.ajax({
			type: "POST",
			url: url,
			data: data,
			dataType: 'json',
			success: function(data){
				if ( data.success ){
					func(data);
				} else {
					alert(data.message);
				}
			},
			error: function(x, status, error){
				alert(status + '\n' + error);
			},
		});
	};

	return {
		/* show a confirmation dialog and delete if user confirms (exception
		 * being if quick is true in which case it is deleted immediately) */
		delete: function(id, quick){
			if ( quick ){
				slide.real_delete(id);
				return;
			}

			$("#delete_dialog img").attr('src', '/slides/show/' + id + '/800/600');
			$("#delete_dialog").dialog({
				modal: true,
				resizable: false,
				position: 'center',
				width: 834, /* 800 + 17 + 17 (padding) */
				height: 700,
			});
		},

		/* delete slide without confirmation */
		real_delete: function(id){
			$.ajax({
				type: "POST",
				url: "/slides/ajax/delete",
				data: {id: id},
				dataType: 'json',
				success: function(data){
					if ( data.success ){
						$('#slide_' + id).remove();
					} else {
						alert(data.message);
					}
					$('#delete_dialog').dialog('close');
				},
				error: function(x, status, error){
					alert(status + '\n' + error);
					$('#delete_dialog').dialog('close');
				},
			});
		},

		activate: function(id){
			return post("/slides/ajax/activate", {id: id}, function(data){
				$('#slide_' + id).attr('class', data.class);
			});
		},

		deactivate: function(id){
			return post("/slides/ajax/deactivate", {id: id}, function(data){
				$('#slide_' + id).attr('class', data.class);
			});
		},
	};
}();

var config = function(){
	'use strict';

	function preview_transition(){
		var selected = $('.conf .transition :selected').val();
		$('#transition_preview').html('');
	}

	return {
		preview_transition: preview_transition,
	};
}();

$(document).ready(function(){
	'use strict';

	/* enable sorting on main page */
	queue.sorting();

	/* setup delete dialog buttons */
	$('#delete_cancel').bind('click', function(){
		$('#delete_dialog').dialog('close');
	});
	$('#delete_confirm').bind('click', slide.real_delete);

	/* fold slide upload fieldsets */
	var $f = $('.foldable');
	$f.foldable({
		collapsed: function(){
			/* all start collapsed but text assembler */
			return $(this).attr('id') !== 'assembler_text';
		},
		expanded_html: '',
		collapsed_html: '',
		connected: $f,
	});

	/* preview transition during configuration */
	config.preview_transition();
	$('.conf .transition').change(function(){
		config.preview_transition();
	});
});

function update_browserstring(){
	'use strict';

	var provider = $("input[name='Database.Provider']").val();
	var username = $("input[name='Database.Username']").val();
	var hostname = $("input[name='Database.Hostname']").val();
	var name     = $("input[name='Database.Name']").val();

	/* build credential-part of the browserstring */
	var credential = '';
	if ( username !== '' ){
		credential = username + '@';
	}

	/* append a / to hostname if specified */
	if ( hostname !== "" ){
		hostname += "/";
	}

	$('.browserstring').html(provider + "://" + credential + hostname + name);
}

function config_open(section){
	'use strict';

	/* reset current selection */
	$('#sidebar a').attr('class', '');
	$('.conf fieldset').hide();

	$('#menu_'+section+' a').attr('class', 'selected');
	$('#conf_'+section).show();
}

/* text preview (see text assembler) */
(function($){
	'use strict';

	var delay = 800; /* ms */
	var fields = [];
	var timer;

	return $(init);

	function getParams(){
		var data = {};
		fields.each(function(){
			data[$(this).attr('name')] = $(this).val();
		});
		return $.param(data);
	}

	function update(){
		var url = '/slides/preview?' + getParams();
		$('img.preview-target').attr('src', url);
	}

	function init(){
		fields = $('.assembler.have-preview .trigger-preview').find('input, textarea');
		fields
			.blur(update)
			.keydown(function(){
				clearTimeout(timer);
				timer = setTimeout(update, delay);
			})
		;

		/* initial preview */
		update();
	}

})(jQuery);
<commit_msg>eslint: fix another no-loop-func<commit_after>/* exported update_browserstring, config_open, text_preview_init */
/* global queues:false, active:false, notice_visible:true */
/* global queue:true, slide:true, config:true */

var queue = function(){
	'use strict';

	function all_queues_except(id){
		return queues.filter(function(x){ return x !== id; });
	}

	return {
		sorting: function(){
			/* test if queues exists on this page */
			if ( typeof(queues) == 'undefined' ){
				return;
			}

			function updated(){
				var current_id = '#' + $(this).attr('id');
				var list = $(this).sortable('toArray');
				var n = list.length;

				/* show/hide warning about empty queue */
				if ( current_id === active ){
					if ( notice_visible && n > 0 ){
						notice_visible = false;
						$("#empty_notice").fadeOut("slow");
					}
					if ( !notice_visible && n === 0 ){
						notice_visible = true;
						$("#empty_notice").fadeIn("slow");
					}
				}

				/* notify server about update */
				$.ajax({
					type: "POST",
					url: "/slides/ajax/move",
					data: "queue=" + $(this).attr('id') + "&slides=" + list,
					error: function(x, status, error){
						alert(status + '\n' + error);
					},
				});
			}

			/* queues is a global variable */
			for ( var i in queues ){
				var id = queues[i];
				var other = all_queues_except(id);
				$(id).bind('updated', updated);
				$(id).sortable({
					connectWith: other,
					placeholder: 'slide_placeholder',
					tolerance: 'pointer',
					distance: 10,
					update: function(){
						$(this).trigger('updated');
					},
				});
				$(id).disableSelection();
			}
		},
	};
}();

var slide = function(){
	'use strict';

	var post = function(url, data, func){
		$.ajax({
			type: "POST",
			url: url,
			data: data,
			dataType: 'json',
			success: function(data){
				if ( data.success ){
					func(data);
				} else {
					alert(data.message);
				}
			},
			error: function(x, status, error){
				alert(status + '\n' + error);
			},
		});
	};

	return {
		/* show a confirmation dialog and delete if user confirms (exception
		 * being if quick is true in which case it is deleted immediately) */
		delete: function(id, quick){
			if ( quick ){
				slide.real_delete(id);
				return;
			}

			$("#delete_dialog img").attr('src', '/slides/show/' + id + '/800/600');
			$("#delete_dialog").dialog({
				modal: true,
				resizable: false,
				position: 'center',
				width: 834, /* 800 + 17 + 17 (padding) */
				height: 700,
			});
		},

		/* delete slide without confirmation */
		real_delete: function(id){
			$.ajax({
				type: "POST",
				url: "/slides/ajax/delete",
				data: {id: id},
				dataType: 'json',
				success: function(data){
					if ( data.success ){
						$('#slide_' + id).remove();
					} else {
						alert(data.message);
					}
					$('#delete_dialog').dialog('close');
				},
				error: function(x, status, error){
					alert(status + '\n' + error);
					$('#delete_dialog').dialog('close');
				},
			});
		},

		activate: function(id){
			return post("/slides/ajax/activate", {id: id}, function(data){
				$('#slide_' + id).attr('class', data.class);
			});
		},

		deactivate: function(id){
			return post("/slides/ajax/deactivate", {id: id}, function(data){
				$('#slide_' + id).attr('class', data.class);
			});
		},
	};
}();

var config = function(){
	'use strict';

	function preview_transition(){
		var selected = $('.conf .transition :selected').val();
		$('#transition_preview').html('');
	}

	return {
		preview_transition: preview_transition,
	};
}();

$(document).ready(function(){
	'use strict';

	/* enable sorting on main page */
	queue.sorting();

	/* setup delete dialog buttons */
	$('#delete_cancel').bind('click', function(){
		$('#delete_dialog').dialog('close');
	});
	$('#delete_confirm').bind('click', slide.real_delete);

	/* fold slide upload fieldsets */
	var $f = $('.foldable');
	$f.foldable({
		collapsed: function(){
			/* all start collapsed but text assembler */
			return $(this).attr('id') !== 'assembler_text';
		},
		expanded_html: '',
		collapsed_html: '',
		connected: $f,
	});

	/* preview transition during configuration */
	config.preview_transition();
	$('.conf .transition').change(function(){
		config.preview_transition();
	});
});

function update_browserstring(){
	'use strict';

	var provider = $("input[name='Database.Provider']").val();
	var username = $("input[name='Database.Username']").val();
	var hostname = $("input[name='Database.Hostname']").val();
	var name     = $("input[name='Database.Name']").val();

	/* build credential-part of the browserstring */
	var credential = '';
	if ( username !== '' ){
		credential = username + '@';
	}

	/* append a / to hostname if specified */
	if ( hostname !== "" ){
		hostname += "/";
	}

	$('.browserstring').html(provider + "://" + credential + hostname + name);
}

function config_open(section){
	'use strict';

	/* reset current selection */
	$('#sidebar a').attr('class', '');
	$('.conf fieldset').hide();

	$('#menu_'+section+' a').attr('class', 'selected');
	$('#conf_'+section).show();
}

/* text preview (see text assembler) */
(function($){
	'use strict';

	var delay = 800; /* ms */
	var fields = [];
	var timer;

	return $(init);

	function getParams(){
		var data = {};
		fields.each(function(){
			data[$(this).attr('name')] = $(this).val();
		});
		return $.param(data);
	}

	function update(){
		var url = '/slides/preview?' + getParams();
		$('img.preview-target').attr('src', url);
	}

	function init(){
		fields = $('.assembler.have-preview .trigger-preview').find('input, textarea');
		fields
			.blur(update)
			.keydown(function(){
				clearTimeout(timer);
				timer = setTimeout(update, delay);
			})
		;

		/* initial preview */
		update();
	}

})(jQuery);
<|endoftext|>


<commit_before>/**
 * @class
 * @author      Lee Driscoll
 * @docauthor   Lee Driscoll
 *
 * Date         14/03/13
 *
 * Enter description of class here using markdown
 */
(function (jsPDFAPI) {
    'use strict';

    /**
     * Create a table from an {#Ext.data.Store} or a set of data.
     * @param data {Ext.data.Model[]|Ext.data.Store}
     * @param [headers] {Array} Omit or null to auto-generate headers at a performance cost
     * @param [autoSize=true] {Boolean}
     */
    jsPDFAPI.table = function(dataSet, headers, config){

        var models;

        if(config){
            var autoSize        = config.autoSize || false,
                printHeaders    = config.printColumnHeaders || true,
                autoStretch     = config.autoStretch || true;
        }

        if(!dataSet){
            Ext.Error.raise('No data for PDF table');
        }

        if(dataSet.isStore){
            var isStore = true;
            models = dataSet.data.items;
        } else {
            models = dataSet;
        }

        // Set headers
        if(headers === undefined || (headers === null)){
            // No headers defined so we derive from data
            if(isStore){
                headers = Ext.Array.pluck(dataSet.model.getFields(), 'name');
            } else {
                headers = Ext.Object.getKeys(models[0]);
            }
            console.log(headers);
        }

        // Create Columns Matrix
        var columnMatrix = {}, columnWidths = {}, index, columnData;

        var TextMetrics = new Ext.util.TextMetrics();

        for (var i = 0, ln = headers.length; i < ln; i++) {
            index = headers[i];
            columnMatrix[index] = Ext.Array.map(models, function(rec){
                return rec.get(index);
            });

            var columnMinWidths = [], column;

            // get header width
//            columnMinWidths.push(this.getTextDimensions(index).w);
            columnMinWidths.push(TextMetrics.getWidth(index));

            column = columnMatrix[index];

            // Get cell widths
            for (var j = 0, ln = columnMatrix[index].length; j < ln; j++) {
                columnData = columnMatrix[index][j];
//                columnMinWidths.push(this.getTextDimensions(columnData).w);
                columnMinWidths.push(TextMetrics.getWidth(columnData));
            }

            // get final column width
            columnWidths[index] = Ext.Array.max(columnMinWidths);
        }

        console.log(columnWidths);

        // Construct the table

        // Construct the header row
        var header;
        for (var i = 0, ln = headers.length; i < ln; i++) {
            header = headers[i];
            this.setTableHeaderRow(10, 40, columnWidths[header], 25, String(header), 1);
        }

        // Construct the data rows
        var record;

        for (var i = 0, ln = models.length; i < ln; i++) {
            record = models[i];

            for (var j = 0, jln = headers.length; j < jln; j++) {
                index = headers[j];
                this.cell(10, 40, columnWidths[index], 25, String(record.get(index)), i+2);
            }
        }

        return this;
    };

    jsPDFAPI.setTableHeaderRow = function(config){
        this.tableHeaderRow = config;
    };

    jsPDFAPI.cell = function (x, y, w, h, txt, ln) {
        if ((((ln * h) + y + (h * 2)) / pages) >= this.internal.pageSize.height && pages === 1) {
            this.cellAddPage();
            this.setLastCellPosition(undefined, undefined, undefined, undefined, undefined);
            if (this.getMaxLn() === 0) {
                this.setMaxLn(ln);
            }
        } else if (pages > 1 && this.getMaxLn() === ln / pages) {
            this.cellAddPage();
            this.setLastCellPosition(undefined, undefined, undefined, undefined, undefined);
        }
        var curCell = this.getLastCellPosition();

        if (curCell.x !== undefined && curCell.ln === ln) {
            x = curCell.x + curCell.w;
            if (curCell.y !== undefined && curCell.y === y) {
                y = curCell.y;
            }
            if (curCell.h !== undefined && curCell.h === h) {
                h = curCell.h;
            }
            if (curCell.ln !== undefined && curCell.ln === ln) {
                ln = curCell.ln;
            }
        }
        this.rect(x, (y + (h * Math.abs(this.getMaxLn() * pages - ln - this.getMaxLn()))), w, h);
        this.text(txt, x + 3, ((y + (h * Math.abs(this.getMaxLn() * pages - ln - this.getMaxLn()))) + h - 3));
        this.setLastCellPosition(x, y, w, h, ln);
        return this;
    };
})(jsPDF.API);<commit_msg>Removed jspdf.plugin.table.js<commit_after><|endoftext|>


<commit_before>let http = require("http")
let fs = require("fs")
let timers = require("timers")

halfCarHeight = 50
halfCarWidth = halfCarHeight / 2

FORWARD = 0
BACKWARD = 1

function init() {
    traffic = {
        cars: [
            {name: "Lucas", img: "Car1", pos: {x:0, y:0}, rot: 0, vel: 3, steering: 0,
                ai: {road_queue: [
                    {road: 0, direction: FORWARD},
                    {road: 2, direction: FORWARD},
                ]}},
            {name: "Felix", img: "Car2", pos: {x:2, y:20}, rot: 0, vel: 2, steering: 0,
                ai: {road_queue: [
                ]}},
        ],
        roads: [
            {start: {x: 10, y: 5}, end: {x: 10, y: 9}, startRoadIdx: -1, endRoadIdx: -1}, // Up
            {start: {x: 11, y: 9}, end: {x: 11, y: 5}, startRoadIdx: -1, endRoadIdx: -1},
            {start: {x: 9, y: 10}, end: {x: 5, y: 10}, startRoadIdx: -1, endRoadIdx: -1}, // Right
            {start: {x: 5, y: 11}, end: {x: 9, y: 11}, startRoadIdx: -1, endRoadIdx: -1},
            {start: {x: 10, y: 12}, end: {x: 10, y: 16}, startRoadIdx: -1, endRoadIdx: -1}, // Down
            {start: {x: 11, y: 16}, end: {x: 11, y: 12}, startRoadIdx: -1, endRoadIdx: -1},
            {start: {x: 16, y: 10}, end: {x: 12, y: 10}, startRoadIdx: -1, endRoadIdx: -1}, // Left
            {start: {x: 12, y: 11}, end: {x: 16, y: 11}, startRoadIdx: -1, endRoadIdx: -1},
        ]
    }
}

init()

lastTime = Date.now()
totalTime = 0

toRadians = (theta => theta * Math.PI / 180)
toDegrees = (theta => theta * 180 / Math.PI)

function distance(a, b) {
    dx = a.x - b.x
    dy = a.y - b.y
    return Math.sqrt(dx * dx + dy * dy)
}

var physics = timers.setInterval(() => {
    delta = (Date.now() - lastTime) / 1000
    totalTime += delta
    lastTime = Date.now()

    traffic.cars = traffic.cars.map(car => {
        theta = toRadians(car.rot)
        rx = Math.cos(theta) * delta * car.vel
        ry = Math.sin(theta) * delta * car.vel
        new_pos = {x: car.pos.x + rx, y: car.pos.y + ry}
        car.pos = new_pos

        car.rot += car.steering * delta * 5

        // Calculate AI
        if (car.ai && car.ai.road_queue.length > 0) {
            current_path = car.ai.road_queue[0]

            road = traffic.roads[current_path.road]
            road_delta = {x: road.end.x - road.start.x, y: road.end.y - road.start.y}
            road_rot = toDegrees(Math.atan2(road_delta.y, road_delta.x))

            if (road_delta.x == 0) {
                k = 1 / 0
                m = 0
                cx = road.start.x
                cy = car.pos.y
            }
            else {
                k = road_delta.y / road_delta.x
                m = road.start.y - k * road.start.x

                cx = (car.pos.x + k * car.pos.y - k * m) / (1 + k * k)
                cy = k * cx + m
            }
            closest = {x:cx, y:cy}
            if (distance(closest, road.start) + distance(closest, road.end) > distance(road.start, road.end)) {
                if (distance(closest, road.start) < distance(closest, road.end))
                    closest = road.start
                else
                    closest = road.end
            }
            dist = distance(car.pos, closest)
            dist_exag = Math.exp(3 * dist)

            // Steer car towards closest point
            wanted_end_pos = current_path.direction == FORWARD ? road.end : road.start

            wanted_rot = (toDegrees(Math.atan2(car.pos.y - (closest.y * dist_exag + wanted_end_pos.y) / (dist_exag + 1), car.pos.x - (closest.x * dist_exag + wanted_end_pos.x) / (dist_exag + 1)))) % 360 + 180

            car.steering = wanted_rot - car.rot

            car.steering = (car.steering + 180) % 360 - 180

            dwx = wanted_end_pos.x - car.pos.x
            dwy = wanted_end_pos.y - car.pos.y
            dist_to_finish = Math.sqrt(dwx * dwx + dwy * dwy)

            if (dist_to_finish < 1) {
                car.ai.road_queue.shift()
            }
        }
        else {
            car.vel /= Math.pow(5, delta)
            car.steering /= Math.pow(5, delta)
        }
        return car
    })
    if (totalTime > 20) {
        totalTime = 0
        init()
    }
}, 20)

var server = http.createServer((req, res) => {
    method = req.method
    url = req.url
    if (method == "GET") {
        filePath = "Web" + url
        if (url === "/Cars") {
            res.setHeader("Content-Type", "text/json")
            res.end(JSON.stringify(traffic))
        } else if (fs.existsSync(filePath)) {
            console.log(method + " " + url)
            if (!fs.lstatSync(filePath).isFile()) {
                filePath = "Web/index.html"
            }
            content = fs.readFileSync(filePath)
            res.setHeader("Content-Type", "text/html")
            res.end(content)
        }
    }
    else
        res.end("Hello!")
})

server.listen(8000)
console.log("Started")<commit_msg>WIP on (no branch): 35843db WIP on master: 350ca73 Time accidentally went double-backwards on client side, fixed it. Also made cars slow down when connection is lost<commit_after>let http = require("http")
let fs = require("fs")
let timers = require("timers")

halfCarHeight = 50
halfCarWidth = halfCarHeight / 2

FORWARD = 0
BACKWARD = 1

function init() {
    traffic = {
        cars: [
            {name: "Lucas", img: "Car1", pos: {x:0, y:0}, rot: 0, vel: 4, steering: 0,
                ai: {road_queue: [
                    {road: 0, direction: FORWARD},
                    {road: 1, direction: FORWARD},
                    {road: 2, direction: FORWARD}
                ]}},
            {name: "Felix", img: "Car2", pos: {x:2, y:20}, rot: 0, vel: 4, steering: 0,
                ai: {road_queue: [
                    {road: 3, direction: FORWARD},
                    {road: 1, direction: BACKWARD},
                    {road: 4, direction: FORWARD}
                ]}},
        ],
        roads: [
            {start: {x: 5, y: 5}, end: {x: 10, y: 5}, startRoadIdx: -1, endRoadIdx: 1},
            {start: {x: 10, y: 5}, end: {x: 10, y: 10}, startRoadIdx: 0, endRoadIdx: 2},
            {start: {x: 10, y: 10}, end: {x: 20, y: 10}, startRoadIdx: 1, endRoadIdx: -1},
            {start: {x: 5, y: 15}, end: {x: 10, y: 10}, startRoadIdx: -1, endRoadIdx: 1},
            {start: {x: 10, y: 5}, end: {x: 15, y: 0}, startRoadIdx: 1, endRoadIdx: -1},
        ]
    }
}

init()

lastTime = Date.now()
totalTime = 0

toRadians = (theta => theta * Math.PI / 180)
toDegrees = (theta => theta * 180 / Math.PI)

function distance(a, b) {
    dx = a.x - b.x
    dy = a.y - b.y
    return Math.sqrt(dx * dx + dy * dy)
}

var physics = timers.setInterval(() => {
    delta = (Date.now() - lastTime) / 1000
    totalTime += delta
    lastTime = Date.now()

    traffic.cars = traffic.cars.map(car => {
        theta = toRadians(car.rot)
        rx = Math.cos(theta) * delta * car.vel
        ry = Math.sin(theta) * delta * car.vel
        new_pos = {x: car.pos.x + rx, y: car.pos.y + ry}
        car.pos = new_pos

        car.rot += car.steering * delta * 5

        // Calculate AI
        if (car.ai && car.ai.road_queue.length > 0) {
            current_path = car.ai.road_queue[0]

            road = traffic.roads[current_path.road]
            road_delta = {x: road.end.x - road.start.x, y: road.end.y - road.start.y}
            road_rot = toDegrees(Math.atan2(road_delta.y, road_delta.x))

            if (road_delta.x == 0) {
                k = 1 / 0
                m = 0
                cx = road.start.x
                cy = car.pos.y
            }
            else {
                k = road_delta.y / road_delta.x
                m = road.start.y - k * road.start.x

                cx = (car.pos.x + k * car.pos.y - k * m) / (1 + k * k)
                cy = k * cx + m
            }
            closest = {x:cx, y:cy}
            if (distance(closest, road.start) + distance(closest, road.end) > distance(road.start, road.end)) {
                if (distance(closest, road.start) < distance(closest, road.end))
                    closest = road.start
                else
                    closest = road.end
            }
            dist = distance(car.pos, closest)
            dist_exag = Math.exp(3 * dist)

            // Steer car towards closest point
            wanted_end_pos = current_path.direction == FORWARD ? road.end : road.start

            wanted_rot = (toDegrees(Math.atan2(car.pos.y - (closest.y * dist_exag + wanted_end_pos.y) / (dist_exag + 1), car.pos.x - (closest.x * dist_exag + wanted_end_pos.x) / (dist_exag + 1)))) % 360 + 180

            car.steering = wanted_rot - car.rot

            car.steering = (car.steering + 180) % 360 - 180

            dwx = wanted_end_pos.x - car.pos.x
            dwy = wanted_end_pos.y - car.pos.y
            dist_to_finish = Math.sqrt(dwx * dwx + dwy * dwy)

            if (dist_to_finish < 1) {
                car.ai.road_queue.shift()
            }
        }
        else {
            car.vel /= Math.pow(5, delta)
            car.steering /= Math.pow(5, delta)
        }
        return car
    })
    if (totalTime > 20) {
        totalTime = 0
        init()
    }
}, 20)

var server = http.createServer((req, res) => {
    method = req.method
    url = req.url
    if (method == "GET") {
        filePath = "Web" + url
        if (url === "/Cars") {
            res.setHeader("Content-Type", "text/json")
            res.end(JSON.stringify(traffic))
        } else if (fs.existsSync(filePath)) {
            console.log(method + " " + url)
            if (!fs.lstatSync(filePath).isFile()) {
                filePath = "Web/index.html"
            }
            content = fs.readFileSync(filePath)
            res.setHeader("Content-Type", "text/html")
            res.end(content)
        }
    }
    else
        res.end("Hello!")
})

server.listen(8000)
console.log("Started")<|endoftext|>


<commit_before>try {
    var exec = require('cordova/exec');
} catch (e) {

}

var CDVNotification_updatePermission = function() {
    cordova.plugins.notification.local.hasPermission(function (granted) {
	Notification.permission = granted;
    });
};

var CDVNotification_encodeTag = function(tag) {
    id = "";
    for (var i = 0; i < tag.length; i++) {
	id = id + tag.charCodeAt(i);
	id = id + "0";
    }
    return id;
};

var CDVNotification_decodeTag = function(encodedTag) {
    var arr = encodedTag.split("0");
    var tag = "";
    for (var i = 0; i < arr.length-1; i++) {
	tag = tag + String.fromCharCode(Number(arr[i]));
    }
    return tag;
};

function Notification(title, options) {
    if (title === undefined) {
	throw new TypeError("Failed to construct 'Notification': 1 argument required, but only 0 present");
    }
    if (typeof options === 'undefined') {
	options = {};
    }
    this.title 	  = title;
    this.onclick  = null;
    this.onshow   = null;
    this.onerror  = null;
    this.onclose  = null;
    this.dir      = options.dir || "auto";
    this.lang     = options.lang || "";
    this.body     = options.body || "";
    this.tag      = options.tag || "";
    this.icon     = options.icon || "";
    this.sound    = options.sound || "";
    this.renotify = options.renotify || false;
    this.silent   = options.silent || false;
    this.noscreen = options.noscreen || false;
    this.sticky   = options.sticky || false;
    this.data     = options.data || {};
    this.id       = this.tag || Date.now();
    if (this.id === this.tag) {
	this.id = CDVNotification_encodeTag(this.tag);
    }
    this.regData = {
	    id: this.id,
	    title: this.title,
	    text: this.body,
	    //every: 0,
	    //at: new Date(),
	    //badge: 0,
	    sound: this.sound,
	    data: this.data,
	    icon: (this.addEventListener !== undefined ? this.icon : ""),
	    ongoing: this.sticky
    };
    var that = this;
    var eventCallback = function(eventType) {
	if (eventType === "click") {
	    try {
		return that.onclick;
	    } catch (e) {}
	}
	if (eventType === "close") {
	    try {
		return that.onclose;
	    } catch (e) {}
	}
	if (eventType === "show") {
	    try {
		return that.onshow;
	    } catch (e) {}
	}
	if (eventType === "error") {
	    try {
		return that.onerror;
	    } catch (e) {}
	}
    };
    var schedule = function() {
	cordova.plugins.notification.local.schedule(that.regData);
    };
    var update = function() {
	cordova.plugins.notification.local.update(that.regData, function() {
	    that.onclick = click;
	    that.onclose = close;
	});
    };
    var click, close;
    CDVNotification_getEventHandler(that.id, "click", function(fn) { click = fn; });
    CDVNotification_getEventHandler(that.id, "close", function(fn) { close = fn; });
    CDVNotification_registerTag(that, schedule, update, eventCallback);
    CDVNotification_updatePermission();
}

Notification.permission = false;

Notification.requestPermission = function() {
    cordova.plugins.notification.local.registerPermission(function(granted) {
	console.log("permission has been granted: " + granted);
	Notification.permission = granted;
    });
};

var ctor = function() {};
try
{
    ctor.prototype = EventTarget.prototype;
    Notification.prototype = new ctor();
    Notification.prototype.constructor = Notification;
}
catch(e)
{
    console.log("This webview does not support EventTarget");
}

Notification.prototype.close = function() {
    that = this;
    cordova.plugins.notification.local.cancel(this.id);
};

var CDVNotification_setupListeners = function () {
    CDVNotification_updatePermission();
    cordova.plugins.notification.local.on("cancel", function(registration) {
	if (typeof exec !== 'undefined') {
	    var callback = function(fn) {
		if (fn) {
		    fn.call();
		}
	    };
	    CDVNotification_getEventHandler(registration.id, "close", callback);
	    exec(null, null, "Notification", "cordovaUnregisterNotificationTag", [registration.id]);
	}
    });
    cordova.plugins.notification.local.on("click", function(registration) {
	if (typeof exec !== 'undefined') {
	    var callback = function(fn) {
		if (fn) {
		    fn.call();
		}
		CDVNotification_fireSWClickEvent(registration.id);
	    };
	    CDVNotification_getEventHandler(registration.id, "click", callback);
	}
    });
    cordova.plugins.notification.local.on("trigger", function(registration) {
	//TODO: Implement onShow
    });
};

document.addEventListener('deviceready', CDVNotification_setupListeners);

try {
    navigator.serviceWorker.ready.then(function(swreg) {
	exec(null, null, "Notification", "setup", []);
    });
} catch(e) {
    // This is in the service worker context
}

module.exports = Notification;
<commit_msg>Changed undefined check for consistency<commit_after>try {
    var exec = require('cordova/exec');
} catch (e) {

}

var CDVNotification_updatePermission = function() {
    cordova.plugins.notification.local.hasPermission(function (granted) {
	Notification.permission = granted;
    });
};

var CDVNotification_encodeTag = function(tag) {
    id = "";
    for (var i = 0; i < tag.length; i++) {
	id = id + tag.charCodeAt(i);
	id = id + "0";
    }
    return id;
};

var CDVNotification_decodeTag = function(encodedTag) {
    var arr = encodedTag.split("0");
    var tag = "";
    for (var i = 0; i < arr.length-1; i++) {
	tag = tag + String.fromCharCode(Number(arr[i]));
    }
    return tag;
};

function Notification(title, options) {
    if (typeof title === 'undefined') {
	throw new TypeError("Failed to construct 'Notification': 1 argument required, but only 0 present");
    }
    if (typeof options === 'undefined') {
	options = {};
    }
    this.title 	  = title;
    this.onclick  = null;
    this.onshow   = null;
    this.onerror  = null;
    this.onclose  = null;
    this.dir      = options.dir || "auto";
    this.lang     = options.lang || "";
    this.body     = options.body || "";
    this.tag      = options.tag || "";
    this.icon     = options.icon || "";
    this.sound    = options.sound || "";
    this.renotify = options.renotify || false;
    this.silent   = options.silent || false;
    this.noscreen = options.noscreen || false;
    this.sticky   = options.sticky || false;
    this.data     = options.data || {};
    this.id       = this.tag || Date.now();
    if (this.id === this.tag) {
	this.id = CDVNotification_encodeTag(this.tag);
    }
    this.regData = {
	    id: this.id,
	    title: this.title,
	    text: this.body,
	    //every: 0,
	    //at: new Date(),
	    //badge: 0,
	    sound: this.sound,
	    data: this.data,
	    icon: (this.addEventListener !== undefined ? this.icon : ""),
	    ongoing: this.sticky
    };
    var that = this;
    var eventCallback = function(eventType) {
	if (eventType === "click") {
	    try {
		return that.onclick;
	    } catch (e) {}
	}
	if (eventType === "close") {
	    try {
		return that.onclose;
	    } catch (e) {}
	}
	if (eventType === "show") {
	    try {
		return that.onshow;
	    } catch (e) {}
	}
	if (eventType === "error") {
	    try {
		return that.onerror;
	    } catch (e) {}
	}
    };
    var schedule = function() {
	cordova.plugins.notification.local.schedule(that.regData);
    };
    var update = function() {
	cordova.plugins.notification.local.update(that.regData, function() {
	    that.onclick = click;
	    that.onclose = close;
	});
    };
    var click, close;
    CDVNotification_getEventHandler(that.id, "click", function(fn) { click = fn; });
    CDVNotification_getEventHandler(that.id, "close", function(fn) { close = fn; });
    CDVNotification_registerTag(that, schedule, update, eventCallback);
    CDVNotification_updatePermission();
}

Notification.permission = false;

Notification.requestPermission = function() {
    cordova.plugins.notification.local.registerPermission(function(granted) {
	console.log("permission has been granted: " + granted);
	Notification.permission = granted;
    });
};

var ctor = function() {};
try
{
    ctor.prototype = EventTarget.prototype;
    Notification.prototype = new ctor();
    Notification.prototype.constructor = Notification;
}
catch(e)
{
    console.log("This webview does not support EventTarget");
}

Notification.prototype.close = function() {
    that = this;
    cordova.plugins.notification.local.cancel(this.id);
};

var CDVNotification_setupListeners = function () {
    CDVNotification_updatePermission();
    cordova.plugins.notification.local.on("cancel", function(registration) {
	if (typeof exec !== 'undefined') {
	    var callback = function(fn) {
		if (fn) {
		    fn.call();
		}
	    };
	    CDVNotification_getEventHandler(registration.id, "close", callback);
	    exec(null, null, "Notification", "cordovaUnregisterNotificationTag", [registration.id]);
	}
    });
    cordova.plugins.notification.local.on("click", function(registration) {
	if (typeof exec !== 'undefined') {
	    var callback = function(fn) {
		if (fn) {
		    fn.call();
		}
		CDVNotification_fireSWClickEvent(registration.id);
	    };
	    CDVNotification_getEventHandler(registration.id, "click", callback);
	}
    });
    cordova.plugins.notification.local.on("trigger", function(registration) {
	//TODO: Implement onShow
    });
};

document.addEventListener('deviceready', CDVNotification_setupListeners);

try {
    navigator.serviceWorker.ready.then(function(swreg) {
	exec(null, null, "Notification", "setup", []);
    });
} catch(e) {
    // This is in the service worker context
}

module.exports = Notification;
<|endoftext|>


<commit_before><commit_msg>warn that IE isn't supported<commit_after><|endoftext|>


<commit_before>'use strict';

var React = require('react-native');
var {
  PropTypes,
  StyleSheet,
  PanResponder,
  View,
  Text,
  TouchableHighlight
} = React;

var defaultProps = {
  values: [50],
  step: 1,
  min:0,
  max:100,
  selectedStyle: {
    backgroundColor: 'blue'
  },
  unselectedStyle: {
    backgroundColor: 'grey'
  },
  containerStyle: {
    height:100,
  },
  fullTrackStyle: {
    height:10,
  },
  trackStyle: {
    borderRadius: 4,
  },
  markerStyle: {
    height:30,
    width: 30,
    borderRadius: 15,
    backgroundColor:"lightgrey",
    left: -15,
    borderWidth: 0.5,
    borderColor: 'grey',
  },
  sliderWidth: 280
};

var Slider = React.createClass({
  propTypes: sliderProps,

  getDefaultProps: function() {
    return defaultProps;
  },

  getInitialState: function() {
    this.optionsArray = this.props.optionsArray || converter.createArray(this.props.min,this.props.max,this.props.step);
    var initialValues = this.props.values.map(value => converter.valueToPosition(value,this.optionsArray,this.props.sliderWidth));
    return {
      pastOne: initialValues[0],
      pastTwo: initialValues[1],
      positionOne: initialValues[0],
      positionTwo: initialValues[1]
    };
  },

  componentWillMount: function () {

    this._panResponderOne = PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
      onPanResponderGrant: (evt, gestureState) => console.log("poop",gestureState),
      onPanResponderMove: (evt, gestureState) => this.moveOne(gestureState),
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => this.endOne(gestureState),
      onPanResponderTerminate: (evt, gestureState) => console.log('end'),
      onShouldBlockNativeResponder: (evt, gestureState) => true
    });
    this._panResponderTwo = PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
      onPanResponderGrant: (evt, gestureState) => console.log(arguments, "argument", gestureState),
      onPanResponderMove: (evt, gestureState) => this.moveTwo(gestureState),
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => this.endTwo(gestureState),
      onPanResponderTerminate: (evt, gestureState) => console.log('end'),
      onShouldBlockNativeResponder: (evt, gestureState) => true
    });

  },

  moveOne(gestureState) {
    var unconfined  = gestureState.dx + this.state.pastOne;
    var bottom      = 1;
    var top         = this.state.positionTwo || this.props.sliderWidth;;
    var confined    = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);
    this.setState({
      positionOne: confined
    });
  },

  endOne(gestureState) {
    this.setState({
      pastOne: this.state.positionOne,
    });
  },

  moveTwo(gestureState) {
    var unconfined  = gestureState.dx + this.state.pastTwo;
    var bottom      = this.state.positionOne;
    var top         = this.props.sliderWidth;
    var confined    = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);

    this.setState({
      positionTwo: confined
    });
  },

  endTwo(gestureState) {
    this.setState({
      pastTwo: this.state.positionTwo,
    });
  },

  render: function() {

    var {positionOne, positionTwo} = this.state;
    var {selectedStyle, unselectedStyle, sliderWidth} = this.props;
    var twoMarkers = positionTwo;

    var trackOneLength = positionOne;
    var trackOneStyle = twoMarkers ? unselectedStyle : selectedStyle;
    var trackThreeLength = twoMarkers ? sliderWidth - (positionTwo) : 0;
    var trackThreeStyle = unselectedStyle;
    var trackTwoLength = sliderWidth - trackOneLength - trackThreeLength;
    var trackTwoStyle = twoMarkers ? selectedStyle : unselectedStyle;

    var convertedValues = [positionOne,positionTwo].map(position => converter.positionToValue(position,this.optionsArray,sliderWidth))

    return (
      <View style={[styles.container, this.props.containerStyle]}>
        <Text>{convertedValues}</Text>
        <View style={[styles.fullTrack, this.props.fullTrackStyle, {width:sliderWidth}]}>
          <View style={[this.props.trackStyle, trackOneStyle, {width: trackOneLength}]} />
          <View style={[this.props.trackStyle, styles.track, trackTwoStyle, {width: trackTwoLength}]}>
            <View
              ref={component => this._markerOne = component}
              style={[this.props.markerStyle]}
              {...this._panResponderOne.panHandlers}
            />
          </View>
          {twoMarkers && (
            <View style={[this.props.trackStyle, styles.track, trackThreeStyle, {width: trackThreeLength}]}>
              <View
                ref={component => this._markerTwo = component}
                style={[this.props.markerStyle]}
                {...this._panResponderTwo.panHandlers}
              />
            </View>
          )}
        </View>
      </View>
    );
  }
});

module.exports = Slider;

var converter = {
  valueToPosition: function (value, valuesArray, sliderLength) {
    var arrLength;
    var index = valuesArray.indexOf(value);

    if (index === -1) {
      console.log('Invalid value, array does not contain: ', value)
      return null;
    } else {
      arrLength = valuesArray.length - 1;
      return sliderLength * index / arrLength;
    }
  },
  positionToValue: function (position, valuesArray, sliderLength) {
    var arrLength;
    var index;

    if ( position < 0 || sliderLength < position ) {
      console.log('invalid position: ', position);
      return null;
    } else {
      arrLength = valuesArray.length - 1;
      index = arrLength * position / sliderLength;
      return valuesArray[Math.round(index)];
    }
  },
  createArray: function (start, end, step) {
    var i;
    var length;
    var direction = start - end > 0 ? -1 : 1;
    var result = [];
    if (!step) {
        console.log('invalid step: ', step);
        return result;
    } else {
        length = Math.abs((start - end)/step) + 1;
        for (i=0 ; i<length ; i++){
          result.push(start + i * Math.abs(step)*direction);
        }
        return result;
    }
  }
}

var styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
  },
  fullTrack: {
    flexDirection: 'row',
  },
  marker: {
  },
  track: {
    justifyContent: 'center'
  }
});

var sliderProps = {
  values: PropTypes.arrayOf(PropTypes.number),

  min: PropTypes.number,
  max: PropTypes.number,
  step: PropTypes.number,

  optionsArray: PropTypes.array,

  containerStyle: PropTypes.object,
  trackStyle: PropTypes.object,
  selectedStyle: PropTypes.object,
  unselectedStyle: PropTypes.object,
  markerStyle: PropTypes.object,
};
<commit_msg>fixes bug where sliding both sliders to the right would cause them to get stuck<commit_after>'use strict';

var React = require('react-native');
var {
  PropTypes,
  StyleSheet,
  PanResponder,
  View,
  Text,
  TouchableHighlight
} = React;

var defaultProps = {
  values: [50],
  step: 1,
  min:0,
  max:100,
  selectedStyle: {
    backgroundColor: 'blue'
  },
  unselectedStyle: {
    backgroundColor: 'grey'
  },
  containerStyle: {
    height:100,
  },
  fullTrackStyle: {
    height:10,
  },
  trackStyle: {
    borderRadius: 4,
  },
  markerStyle: {
    height:30,
    width: 30,
    borderRadius: 15,
    backgroundColor:"lightgrey",
    left: -15,
    borderWidth: 0.5,
    borderColor: 'grey',
  },
  sliderWidth: 280
};

var Slider = React.createClass({
  propTypes: sliderProps,

  getDefaultProps: function() {
    return defaultProps;
  },

  getInitialState: function() {
    this.optionsArray = this.props.optionsArray || converter.createArray(this.props.min,this.props.max,this.props.step);
    var initialValues = this.props.values.map(value => converter.valueToPosition(value,this.optionsArray,this.props.sliderWidth));
    return {
      pastOne: initialValues[0],
      pastTwo: initialValues[1],
      positionOne: initialValues[0],
      positionTwo: initialValues[1]
    };
  },

  componentWillMount: function () {

    this._panResponderOne = PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
      onPanResponderGrant: (evt, gestureState) => console.log("poop",gestureState),
      onPanResponderMove: (evt, gestureState) => this.moveOne(gestureState),
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => this.endOne(gestureState),
      onPanResponderTerminate: (evt, gestureState) => console.log('end'),
      onShouldBlockNativeResponder: (evt, gestureState) => true
    });
    this._panResponderTwo = PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
      onPanResponderGrant: (evt, gestureState) => console.log(arguments, "argument", gestureState),
      onPanResponderMove: (evt, gestureState) => this.moveTwo(gestureState),
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => this.endTwo(gestureState),
      onPanResponderTerminate: (evt, gestureState) => console.log('end'),
      onShouldBlockNativeResponder: (evt, gestureState) => true
    });

  },

  moveOne(gestureState) {
    var unconfined  = gestureState.dx + this.state.pastOne;
    var bottom      = 1;
    var top         = this.state.positionTwo || this.props.sliderWidth;;
    var confined    = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);
    this.setState({
      positionOne: confined
    });
  },

  endOne(gestureState) {
    this.setState({
      pastOne: this.state.positionOne,
    });
  },

  moveTwo(gestureState) {
    var unconfined  = gestureState.dx + this.state.pastTwo;
    var bottom      = this.state.positionOne;
    var top         = this.props.sliderWidth;
    var confined    = unconfined < bottom ? bottom : (unconfined > top ? top : unconfined);

    this.setState({
      positionTwo: confined
    });
  },

  endTwo(gestureState) {
    this.setState({
      pastTwo: this.state.positionTwo,
    });
  },

  render: function() {

    var {positionOne, positionTwo} = this.state;
    var {selectedStyle, unselectedStyle, sliderWidth} = this.props;
    var twoMarkers = positionTwo;

    var trackOneLength = positionOne;
    var trackOneStyle = twoMarkers ? unselectedStyle : selectedStyle;
    var trackThreeLength = twoMarkers ? sliderWidth - (positionTwo) : 0;
    var trackThreeStyle = unselectedStyle;
    var trackTwoLength = sliderWidth - trackOneLength - trackThreeLength;
    var trackTwoStyle = twoMarkers ? selectedStyle : unselectedStyle;

    var convertedValues = [positionOne,positionTwo].map(position => converter.positionToValue(position,this.optionsArray,sliderWidth))

    return (
      <View style={[styles.container, this.props.containerStyle]}>
        <Text>{convertedValues}</Text>
        <View style={[styles.fullTrack, this.props.fullTrackStyle, {width:sliderWidth}]}>
          <View style={[this.props.trackStyle, trackOneStyle, {width: trackOneLength}]} />
          <View style={[this.props.trackStyle, styles.track, trackTwoStyle, {width: trackTwoLength}]}>
            <View
              ref={component => this._markerOne = component}
              style={[this.props.markerStyle]}
              {...this._panResponderOne.panHandlers}
            />
          </View>
          {twoMarkers && (positionOne !== this.props.sliderWidth) && (
            <View style={[this.props.trackStyle, styles.track, trackThreeStyle, {width: trackThreeLength}]}>
              <View
                ref={component => this._markerTwo = component}
                style={[this.props.markerStyle]}
                {...this._panResponderTwo.panHandlers}
              />
            </View>
          )}
        </View>
      </View>
    );
  }
});

module.exports = Slider;

var converter = {
  valueToPosition: function (value, valuesArray, sliderLength) {
    var arrLength;
    var index = valuesArray.indexOf(value);

    if (index === -1) {
      console.log('Invalid value, array does not contain: ', value)
      return null;
    } else {
      arrLength = valuesArray.length - 1;
      return sliderLength * index / arrLength;
    }
  },
  positionToValue: function (position, valuesArray, sliderLength) {
    var arrLength;
    var index;

    if ( position < 0 || sliderLength < position ) {
      console.log('invalid position: ', position);
      return null;
    } else {
      arrLength = valuesArray.length - 1;
      index = arrLength * position / sliderLength;
      return valuesArray[Math.round(index)];
    }
  },
  createArray: function (start, end, step) {
    var i;
    var length;
    var direction = start - end > 0 ? -1 : 1;
    var result = [];
    if (!step) {
        console.log('invalid step: ', step);
        return result;
    } else {
        length = Math.abs((start - end)/step) + 1;
        for (i=0 ; i<length ; i++){
          result.push(start + i * Math.abs(step)*direction);
        }
        return result;
    }
  }
}

var styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
  },
  fullTrack: {
    flexDirection: 'row',
  },
  marker: {
  },
  track: {
    justifyContent: 'center'
  }
});

var sliderProps = {
  values: PropTypes.arrayOf(PropTypes.number),

  min: PropTypes.number,
  max: PropTypes.number,
  step: PropTypes.number,

  optionsArray: PropTypes.array,

  containerStyle: PropTypes.object,
  trackStyle: PropTypes.object,
  selectedStyle: PropTypes.object,
  unselectedStyle: PropTypes.object,
  markerStyle: PropTypes.object,
};
<|endoftext|>


<commit_before>/*
 * This file is part of TbSync.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

 "use strict";

tbSync.onInjectIntoAddressbook = function (window) {
    if (window.document.getElementById("abResultsTree")) {
        window.document.getElementById("abResultsTree").addEventListener("select", tbSync.onAbResultsPaneSelectionChanged, false);
        window.document.getElementById("abResultsTree").addEventListener("focus", tbSync.onAbResultsPaneSelectionChanged, false);
        tbSync.onAbResultsPaneSelectionChanged();
    }

    //hook into getProperties of abDirTreeItem to inject our own icons for the address books
    window.abDirTreeItem.prototype._origBeforeTbSyncGetProperties = window.abDirTreeItem.prototype.getProperties;
    window.abDirTreeItem.prototype.getProperties = function () {
        //get original properties
        let properties = this._origBeforeTbSyncGetProperties().split(" ");

        let type = "";
        if (!this._directory.isMailList && !this._directory.isRemote) {
            try {
                type = this._directory.getStringValue("tbSyncIcon", "");
            } catch (e) {}
        }

        if (type) properties.push(type);
        return properties.join(" ");
    }
}

tbSync.onRemoveFromAddressbook = function (window) {
    if (window.document.getElementById("abResultsTree")) {
        window.document.getElementById("abResultsTree").removeEventListener("select", tbSync.onAbResultsPaneSelectionChanged, false);
        window.document.getElementById("abResultsTree").removeEventListener("focus", tbSync.onAbResultsPaneSelectionChanged, false);
    }
    //remove our injection
    window.abDirTreeItem.prototype.getProperties = window.abDirTreeItem.prototype._origBeforeTbSyncGetProperties;
}

tbSync.onAbResultsPaneSelectionChanged = function () {
    //hide all extra fields of all providers
    for (let provider in tbSync.loadedProviders) {
        let container = window.document.getElementsByClassName(provider + "Container");
        for (let i=0; i < container.length; i++) {
            container[i].hidden = true;
        }
    }

    let cards = window.GetSelectedAbCards();
    if (cards.length == 1) {
        let aParentDirURI = window.GetSelectedDirectory();
        let abManager = Components.classes["@mozilla.org/abmanager;1"].getService(Components.interfaces.nsIAbManager);
        let selectedBook = abManager.getDirectory(aParentDirURI);
        if (selectedBook.isMailList) {
            aParentDirURI = aParentDirURI.substring(0, aParentDirURI.lastIndexOf("/"));
        }

        if (aParentDirURI) { //could be undefined
            let folders = tbSync.db.findFoldersWithSetting("target", aParentDirURI);
            if (folders.length == 1) {
                let provider = tbSync.db.getAccountSetting(folders[0].account, "provider");
                if (tbSync[provider].onAbResultsPaneSelectionChanged) {
                    tbSync[provider].onAbResultsPaneSelectionChanged(window, cards[0]);
                }
            }
        }
    }
}<commit_msg>allow providers to register default elements<commit_after>/*
 * This file is part of TbSync.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

 "use strict";

tbSync.onInjectIntoAddressbook = function (window) {
    if (window.document.getElementById("abResultsTree")) {
        window.document.getElementById("abResultsTree").addEventListener("select", tbSync.onAbResultsPaneSelectionChanged, false);
        window.document.getElementById("abResultsTree").addEventListener("focus", tbSync.onAbResultsPaneSelectionChanged, false);
        tbSync.onAbResultsPaneSelectionChanged();
    }

    //hook into getProperties of abDirTreeItem to inject our own icons for the address books
    window.abDirTreeItem.prototype._origBeforeTbSyncGetProperties = window.abDirTreeItem.prototype.getProperties;
    window.abDirTreeItem.prototype.getProperties = function () {
        //get original properties
        let properties = this._origBeforeTbSyncGetProperties().split(" ");

        let type = "";
        if (!this._directory.isMailList && !this._directory.isRemote) {
            try {
                type = this._directory.getStringValue("tbSyncIcon", "");
            } catch (e) {}
        }

        if (type) properties.push(type);
        return properties.join(" ");
    }
}

tbSync.onRemoveFromAddressbook = function (window) {
    if (window.document.getElementById("abResultsTree")) {
        window.document.getElementById("abResultsTree").removeEventListener("select", tbSync.onAbResultsPaneSelectionChanged, false);
        window.document.getElementById("abResultsTree").removeEventListener("focus", tbSync.onAbResultsPaneSelectionChanged, false);
    }
    //remove our injection
    window.abDirTreeItem.prototype.getProperties = window.abDirTreeItem.prototype._origBeforeTbSyncGetProperties;
}

tbSync.onAbResultsPaneSelectionChanged = function () {
    //hide all extra fields of all providers
    for (let provider in tbSync.loadedProviders) {
        let container = window.document.getElementsByClassName(provider + "Container");
        for (let i=0; i < container.length; i++) {
            container[i].hidden = true;
        }
    }

    //unhide all default elements, which have been hidden by some provider
    //provider must add class "defaultElement" to the elements it is hiding
    let defaultElements = window.document.getElementsByClassName("defaultElement");
    for (let i=0; i < defaultElements.length; i++) {
        defaultElements[i].hidden = false;
    }

    let cards = window.GetSelectedAbCards();
    if (cards.length == 1) {
        let aParentDirURI = window.GetSelectedDirectory();
        let abManager = Components.classes["@mozilla.org/abmanager;1"].getService(Components.interfaces.nsIAbManager);
        let selectedBook = abManager.getDirectory(aParentDirURI);
        if (selectedBook.isMailList) {
            aParentDirURI = aParentDirURI.substring(0, aParentDirURI.lastIndexOf("/"));
        }

        if (aParentDirURI) { //could be undefined
            let folders = tbSync.db.findFoldersWithSetting("target", aParentDirURI);
            if (folders.length == 1) {
                let provider = tbSync.db.getAccountSetting(folders[0].account, "provider");
                if (tbSync[provider].onAbResultsPaneSelectionChanged) {
                    tbSync[provider].onAbResultsPaneSelectionChanged(window, cards[0]);
                }
            }
        }
    }
}<|endoftext|>


<commit_before>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Externs file for OpenSocial shindig libraries.
 * Note this may not be comprehensive.
 *
 * @see http://incubator.apache.org/shindig/shindig-1.1.x/shindig-features/jsdoc
 * @externs
 */

// Root namespace for shindig related functionality.
var shindig = {};

/**
 * Implements a safer random() method that is seeded from
 * screen width/height and (presumably random/unguessable) mouse
 * movement, in an effort to create a better seed for random().
 * @return {number} The random number.
 */
shindig.random = function() {};

/**
 * Implements a SHA1 hasher.
 * @return {Object} SHA1 hasher object.
 */
shindig.sha1 = function() {};
<commit_msg><commit_after>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Externs file for OpenSocial shindig libraries.
 * Note this may not be comprehensive.
 *
 * @see http://incubator.apache.org/shindig/shindig-1.1.x/shindig-features/jsdoc
 * @externs
 */

// Root namespace for shindig related functionality.
var shindig = {};

/**
 * Implements a safer random() method that is seeded from
 * screen width/height and (presumably random/unguessable) mouse
 * movement, in an effort to create a better seed for random().
 * @return {number} The random number.
 */
shindig.random = function() {};

/**
 * Implements a SHA1 hasher.
 * @return {{reset: function(), update:
 * function(Array.<number>|string,number=), digest:
 * function():Array.<number>, digestString: function():string}} SHA1
 * hasher object. Methods of the returned object are: reset() which
 * resets the internal accumulator, update(bytes, opt_length) which
 * adds a byte array or string to the internal accumulator (or
 * optionally just a prefix of length opt_length), digest() which
 * returns a finalized hash as an array of 20 byte values, and
 * digestString() which returns a hex-encoded string containing a
 * finalized hash.
 */
shindig.sha1 = function() {};
<|endoftext|>


<commit_before>import _ from 'lodash';
import BigNumber from 'bignumber.js';
import {Widget, Inject, Intent} from 'interstellar-core';
import {Alert, AlertGroup} from 'interstellar-ui-messages';
import {Account, Asset, Keypair, Memo, Operation, Transaction, TransactionBuilder, xdr} from 'stellar-sdk';
import {FederationServer} from 'stellar-sdk';
import BasicClientError from '../errors';
import StellarLedger from 'stellar-ledger-api';

@Widget('send', 'SendWidgetController', 'interstellar-basic-client/send-widget')
@Inject("$scope", "$rootScope", '$sce', "interstellar-sessions.Sessions", "interstellar-network.Server", "interstellar-ui-messages.Alerts")
export default class SendWidgetController {
  constructor($scope, $rootScope, $sce, Sessions, Server, Alerts) {
    if (!Sessions.hasDefault()) {
      console.error('No session');
      return;
    }

    this.view = 'sendSetup';
    this.$scope = $scope;
    this.$rootScope = $rootScope;
    this.Server = Server;
    this.Sessions = Sessions;
    this.session = Sessions.default;
    this.rocketImage = require('../images/sending.gif');
    this.memo = false;
    this.memoType = null;
    this.memoValue = null;
    this.memoBlocked = false;
    this.stellarAddress = null;
    // Resolved destination (accountId/address)
    this.destination = null;
    // XDR of the last transaction submitted to horizon. Will be helpful resubmit is needed.
    this.lastTransactionXDR = null;

    this.$scope.$watch('widget.memoType', type => {
      switch (type) {
        case 'MEMO_ID':
          this.memoPlaceholder = 'Enter memo ID number';
          break;
        case 'MEMO_TEXT':
          this.memoPlaceholder = 'Up to 28 characters';
          break;
        case 'MEMO_HASH':
        case 'MEMO_RETURN':
          this.memoPlaceholder = 'Enter 64 character encoded string';
          break;
      }
    });

    this.addressAlertGroup = new AlertGroup();
    this.addressAlertGroup.registerUpdateListener(alerts => {
      this.addressAlerts = alerts;
    });
    Alerts.registerGroup(this.addressAlertGroup);

    this.amountAlertGroup = new AlertGroup();
    this.amountAlertGroup.registerUpdateListener(alerts => {
      // Some amount alert messages contain HTML
      for (let alert of alerts) {
        alert._text = $sce.trustAsHtml(alert._text)
      }
      this.amountAlerts = alerts;
    });
    Alerts.registerGroup(this.amountAlertGroup);

    this.memoAlertGroup = new AlertGroup();
    this.memoAlertGroup.registerUpdateListener(alerts => {
      this.memoAlerts = alerts;
    });
    Alerts.registerGroup(this.memoAlertGroup);

    this.useLedger = this.session.data && this.session.data['useLedger'];
    this.bip32Path = this.session.data && this.session.data['bip32Path'];
  }

  loadDestination($event) {
    this.loadingDestination = true;
    this.addressAlertGroup.clear();

    let resetState = () => {
      this.destination = null;
      this.stellarAddress = null;
      this.loadingDestination = false;
      this.memoBlocked = false;

    };

    if (!this.destinationAddress) {
      resetState();
      return;
    }

    FederationServer.resolve(this.destinationAddress)
      .then(value => {
        this.destination = value.account_id;

        if (this.destinationAddress == this.destination) {
          this.stellarAddress = null;
        } else {
          this.stellarAddress = this.destinationAddress;
        }

        switch (value.memo_type) {
          case 'id':
            value.memo_type = 'MEMO_ID';
            break;
          case 'text':
            value.memo_type = 'MEMO_TEXT';
            break;
          case 'hash':
            value.memo_type = 'MEMO_HASH';
            break;
          default:
            delete value.memo;
            delete value.memo_type;
        }

        if (value.memo_type && value.memo) {
          this.memo = true;
          this.memoType = value.memo_type;
          this.memoValue = value.memo;
          this.memoBlocked = true;
        } else {
          this.memoBlocked = false;

        }
        this.loadingDestination = false;
        this.$scope.$apply();
      })
      .catch(error => {
        let alert;
        if (this.destinationAddress.indexOf('*') < 0) {
          alert = new Alert({
            title: 'Invalid public key.',
            text: 'Public keys are uppercase and begin with the letter "G."',
            type: Alert.TYPES.ERROR
          });
        } else {
          alert = new Alert({
            title: 'Stellar address cannot be found or is invalid.',
            text: '',
            type: Alert.TYPES.ERROR
          });
        }
        this.addressAlertGroup.show(alert);
        resetState();
        this.$scope.$apply();
      });
  }

  showMemo($event) {
    $event.preventDefault();
    this.memo = true;
    this.memoType = 'MEMO_TEXT';
    this.memoValue = null;
  }

  hideMemo($event) {
    if ($event) {
      $event.preventDefault();
    }
    this.memoAlertGroup.clear();
    this.memo = false;
    this.memoType = null;
    this.memoValue = null;
  }

  showView($event, v) {
    if ($event) {
      $event.preventDefault();
    }
    this.view = v;
  }

  send() {
    if (this.loadingDestination) {
      return false;
    }

    this.sending = true;
    this.ledgerError = null;

    this.addressAlertGroup.clear();
    this.amountAlertGroup.clear();
    this.memoAlertGroup.clear();

    if (!Account.isValidAccountId(this.destination)) {
      let alert = new Alert({
        title: 'Stellar address or public key is invalid.',
        text: 'Public keys are uppercase and begin with the letter "G."',
        type: Alert.TYPES.ERROR
      });
      this.addressAlertGroup.show(alert);
    }

    // Check if amount is valid
    if (!Operation.isValidAmount(this.amount)) {
      let alert = new Alert({
        title: '',
        text: 'This amount is invalid.',
        type: Alert.TYPES.ERROR
      });
      this.amountAlertGroup.show(alert);
    }

    if (this.memo) {
      let memo, memoError;
      try {
        switch (this.memoType) {
          case 'MEMO_TEXT':
            memoError = 'MEMO_TEXT must contain a maximum of 28 characters';
            memo = Memo.text(this.memoValue);
            break;
          case 'MEMO_ID':
            memoError = 'MEMO_ID must be a valid 64 bit unsigned integer';
            memo = Memo.id(this.memoValue);
            break;
          case 'MEMO_HASH':
            memoError = 'MEMO_HASH must be a 32 byte hash represented in hexadecimal (A-Z0-9)';
            memo = Memo.hash(this.memoValue);
            break;
          case 'MEMO_RETURN':
            memoError = 'MEMO_RETURN must be a 32 byte hash represented in hexadecimal (A-Z0-9)';
            memo = Memo.returnHash(this.memoValue);
            break;
        }
      } catch (error) {
        let alert = new Alert({
          title: '',
          text: memoError,
          type: Alert.TYPES.ERROR
        });
        this.memoAlertGroup.show(alert);
      }
    }

    if (this.addressAlerts.length || this.amountAlerts.length || this.memoAlerts.length) {
      this.sending = false;
      return;
    }

    return this.Server.accounts()
      .accountId(this.session.address)
      .call()
      .then(account => {
        // Check if sending this transaction would make balance go below minimum balance
        let minimumBalance = 20 + (account.subentry_count) * 10;
        let nativeBalance = _(account.balances).find(balance => balance.asset_type === 'native').balance;
        let maxSend = new BigNumber(nativeBalance).minus(minimumBalance);
        if (maxSend.lt(this.amount)) {
          throw new BasicClientError('InsufficientBalanceError', {maxSend});
        }
      })
      .then(() => {
        // Check if destination account exists. If no, at least 20 XLM must be sent.
        if (new BigNumber(this.amount).gte(20)) {
          return;
        }

        return this.Server.accounts()
          .accountId(this.destination)
          .call()
          .catch(err => {
            if (err.name === 'NotFoundError') {
              throw new BasicClientError('DestinationAccountNotExistError');
            }
          });
      })
      .then(() => {
        this.displayedAmount = new BigNumber(this.amount).toFormat();
        this.showView(null, 'sendConfirm');
      })
      .catch(err => {
        let alert;
        switch (err.name) {
          case 'NotFoundError':
            alert = new Alert({
              title: '',
              text: 'Your account isn\'t funded.',
              type: Alert.TYPES.ERROR
            });
            break;
          case 'InsufficientBalanceError':
            alert = new Alert({
              title: 'Insufficient balance.',
              text:
                `To maintain your <a href="https://www.stellar.org/developers/learn/concepts/fees.html#minimum-balance" target="_blank">minimum balance</a>, the most you can currently send is ${err.data.maxSend}.`,
              type: Alert.TYPES.ERROR
            });
            break;
          case 'DestinationAccountNotExistError':
            alert = new Alert({
              title: 'Destination account doesn\'t exist.',
              text: 'You account must send at least 20 lumens to create an account.',
              type: Alert.TYPES.ERROR
            });
            break;
          default:
            alert = new Alert({
              title: 'Unknown error: '+err.name,
              text: '',
              type: Alert.TYPES.ERROR
            });
            break;
        }
        this.amountAlertGroup.show(alert);
      })
      .finally(() => {
        this.sending = false;
        this.$scope.$apply();
      });
  }

  confirm() {
    this.showView(null, 'sendWaiting');
    return this.Server.accounts()
      .accountId(this.destination)
      .call()
      .then(() => {
        // Account exist. Send payment operation.
        let operation = Operation.payment({
          destination: this.destination,
          asset: Asset.native(),
          amount: this.amount
        });
        return this._submitTransaction(operation);
      })
      .catch(err => {
        if (err.name === 'NotFoundError') {
          // Account does not exist. Send create_account operation.
          let operation = Operation.createAccount({
            destination: this.destination,
            startingBalance: this.amount
          });
          return this._submitTransaction(operation);
        } else {
          throw err;
        }
      });
  }

  resubmitTransaction() {
    if (this.lastTransactionXDR === null) {
      return;
    }

    this.showView(null, 'sendWaiting');

    var transaction = new Transaction(this.lastTransactionXDR);

    return this.Server.submitTransaction(transaction)
      .then(this._submitOnSuccess.bind(this))
      .catch(this._submitOnFailure.bind(this))
      .finally(this._submitFinally.bind(this));
  }

  _submitTransaction(operation) {
    return this.Sessions.loadDefaultAccount()
      .then(() => {
        let memo = Memo.none();

        if (this.memo) {
          switch (this.memoType) {
            case 'MEMO_TEXT':
              memo = Memo.text(this.memoValue);
              break;
            case 'MEMO_ID':
              memo = Memo.id(this.memoValue);
              break;
            case 'MEMO_HASH':
              memo = Memo.hash(this.memoValue);
              break;
            case 'MEMO_RETURN':
              memo = Memo.returnHash(this.memoValue);
              break;
          }
        }

        let transaction = new TransactionBuilder(this.session.getAccount())
          .addOperation(operation)
          .addMemo(memo)
          .build();

        if (this.useLedger) {
          let ledgerApi = new StellarLedger.Api(new StellarLedger.comm(120));
          return ledgerApi.signTx_async(this.bip32Path, transaction).then(result => {
            let signature = result['signature'];
            let keyPair = Keypair.fromAccountId(this.session.address);
            let hint = keyPair.signatureHint();
            let decorated = new xdr.DecoratedSignature({hint, signature});
            transaction.signatures.push(decorated);
          }).catch(e => {
            this.ledgerError = e;
            throw e;
          });
        } else {
          transaction.sign(Keypair.fromSeed(this.session.getSecret()));
        }

        this.lastTransactionXDR = transaction.toEnvelope().toXDR().toString("base64");
        return this.Server.submitTransaction(transaction);
      })
      .then(this._submitOnSuccess.bind(this))
      .catch(this._submitOnFailure.bind(this))
      .finally(this._submitFinally.bind(this));
  }

  _submitOnSuccess() {
    this.success = true;
    this.destinationAddress = null;
    this.destination = null;
    this.amount = null;
    this.memo = false;
    this.lastTransactionXDR = null;
    this.$rootScope.$broadcast('account-viewer.transaction-success');
  }

  _submitOnFailure(e) {
    this.success = false;
    this.needsResubmit = false;

    if (this.ledgerError !== null) {
      this.outcomeMessage = this.ledgerError;
    } else {
      if (e.title != "Transaction Failed" && e.title != "Transaction Malformed") {
        this.needsResubmit = true;
      }
      this.outcomeMessage = JSON.stringify(e, null, '  ');
    }
  }

  _submitFinally() {
    this.showView(null, 'sendOutcome');
    this.$scope.$apply()
  }
}
<commit_msg>Fix ledger support<commit_after>import _ from 'lodash';
import BigNumber from 'bignumber.js';
import {Widget, Inject, Intent} from 'interstellar-core';
import {Alert, AlertGroup} from 'interstellar-ui-messages';
import {Account, Asset, Keypair, Memo, Operation, Transaction, TransactionBuilder, xdr} from 'stellar-sdk';
import {FederationServer} from 'stellar-sdk';
import BasicClientError from '../errors';
import StellarLedger from 'stellar-ledger-api';

@Widget('send', 'SendWidgetController', 'interstellar-basic-client/send-widget')
@Inject("$scope", "$rootScope", '$sce', "interstellar-sessions.Sessions", "interstellar-network.Server", "interstellar-ui-messages.Alerts")
export default class SendWidgetController {
  constructor($scope, $rootScope, $sce, Sessions, Server, Alerts) {
    if (!Sessions.hasDefault()) {
      console.error('No session');
      return;
    }

    this.view = 'sendSetup';
    this.$scope = $scope;
    this.$rootScope = $rootScope;
    this.Server = Server;
    this.Sessions = Sessions;
    this.session = Sessions.default;
    this.rocketImage = require('../images/sending.gif');
    this.memo = false;
    this.memoType = null;
    this.memoValue = null;
    this.memoBlocked = false;
    this.stellarAddress = null;
    // Resolved destination (accountId/address)
    this.destination = null;
    // XDR of the last transaction submitted to horizon. Will be helpful resubmit is needed.
    this.lastTransactionXDR = null;

    this.$scope.$watch('widget.memoType', type => {
      switch (type) {
        case 'MEMO_ID':
          this.memoPlaceholder = 'Enter memo ID number';
          break;
        case 'MEMO_TEXT':
          this.memoPlaceholder = 'Up to 28 characters';
          break;
        case 'MEMO_HASH':
        case 'MEMO_RETURN':
          this.memoPlaceholder = 'Enter 64 character encoded string';
          break;
      }
    });

    this.addressAlertGroup = new AlertGroup();
    this.addressAlertGroup.registerUpdateListener(alerts => {
      this.addressAlerts = alerts;
    });
    Alerts.registerGroup(this.addressAlertGroup);

    this.amountAlertGroup = new AlertGroup();
    this.amountAlertGroup.registerUpdateListener(alerts => {
      // Some amount alert messages contain HTML
      for (let alert of alerts) {
        alert._text = $sce.trustAsHtml(alert._text)
      }
      this.amountAlerts = alerts;
    });
    Alerts.registerGroup(this.amountAlertGroup);

    this.memoAlertGroup = new AlertGroup();
    this.memoAlertGroup.registerUpdateListener(alerts => {
      this.memoAlerts = alerts;
    });
    Alerts.registerGroup(this.memoAlertGroup);

    this.useLedger = this.session.data && this.session.data['useLedger'];
    this.bip32Path = this.session.data && this.session.data['bip32Path'];
  }

  loadDestination($event) {
    this.loadingDestination = true;
    this.addressAlertGroup.clear();

    let resetState = () => {
      this.destination = null;
      this.stellarAddress = null;
      this.loadingDestination = false;
      this.memoBlocked = false;

    };

    if (!this.destinationAddress) {
      resetState();
      return;
    }

    FederationServer.resolve(this.destinationAddress)
      .then(value => {
        this.destination = value.account_id;

        if (this.destinationAddress == this.destination) {
          this.stellarAddress = null;
        } else {
          this.stellarAddress = this.destinationAddress;
        }

        switch (value.memo_type) {
          case 'id':
            value.memo_type = 'MEMO_ID';
            break;
          case 'text':
            value.memo_type = 'MEMO_TEXT';
            break;
          case 'hash':
            value.memo_type = 'MEMO_HASH';
            break;
          default:
            delete value.memo;
            delete value.memo_type;
        }

        if (value.memo_type && value.memo) {
          this.memo = true;
          this.memoType = value.memo_type;
          this.memoValue = value.memo;
          this.memoBlocked = true;
        } else {
          this.memoBlocked = false;

        }
        this.loadingDestination = false;
        this.$scope.$apply();
      })
      .catch(error => {
        let alert;
        if (this.destinationAddress.indexOf('*') < 0) {
          alert = new Alert({
            title: 'Invalid public key.',
            text: 'Public keys are uppercase and begin with the letter "G."',
            type: Alert.TYPES.ERROR
          });
        } else {
          alert = new Alert({
            title: 'Stellar address cannot be found or is invalid.',
            text: '',
            type: Alert.TYPES.ERROR
          });
        }
        this.addressAlertGroup.show(alert);
        resetState();
        this.$scope.$apply();
      });
  }

  showMemo($event) {
    $event.preventDefault();
    this.memo = true;
    this.memoType = 'MEMO_TEXT';
    this.memoValue = null;
  }

  hideMemo($event) {
    if ($event) {
      $event.preventDefault();
    }
    this.memoAlertGroup.clear();
    this.memo = false;
    this.memoType = null;
    this.memoValue = null;
  }

  showView($event, v) {
    if ($event) {
      $event.preventDefault();
    }
    this.view = v;
  }

  send() {
    if (this.loadingDestination) {
      return false;
    }

    this.sending = true;
    this.ledgerError = null;

    this.addressAlertGroup.clear();
    this.amountAlertGroup.clear();
    this.memoAlertGroup.clear();

    if (!Account.isValidAccountId(this.destination)) {
      let alert = new Alert({
        title: 'Stellar address or public key is invalid.',
        text: 'Public keys are uppercase and begin with the letter "G."',
        type: Alert.TYPES.ERROR
      });
      this.addressAlertGroup.show(alert);
    }

    // Check if amount is valid
    if (!Operation.isValidAmount(this.amount)) {
      let alert = new Alert({
        title: '',
        text: 'This amount is invalid.',
        type: Alert.TYPES.ERROR
      });
      this.amountAlertGroup.show(alert);
    }

    if (this.memo) {
      let memo, memoError;
      try {
        switch (this.memoType) {
          case 'MEMO_TEXT':
            memoError = 'MEMO_TEXT must contain a maximum of 28 characters';
            memo = Memo.text(this.memoValue);
            break;
          case 'MEMO_ID':
            memoError = 'MEMO_ID must be a valid 64 bit unsigned integer';
            memo = Memo.id(this.memoValue);
            break;
          case 'MEMO_HASH':
            memoError = 'MEMO_HASH must be a 32 byte hash represented in hexadecimal (A-Z0-9)';
            memo = Memo.hash(this.memoValue);
            break;
          case 'MEMO_RETURN':
            memoError = 'MEMO_RETURN must be a 32 byte hash represented in hexadecimal (A-Z0-9)';
            memo = Memo.returnHash(this.memoValue);
            break;
        }
      } catch (error) {
        let alert = new Alert({
          title: '',
          text: memoError,
          type: Alert.TYPES.ERROR
        });
        this.memoAlertGroup.show(alert);
      }
    }

    if (this.addressAlerts.length || this.amountAlerts.length || this.memoAlerts.length) {
      this.sending = false;
      return;
    }

    return this.Server.accounts()
      .accountId(this.session.address)
      .call()
      .then(account => {
        // Check if sending this transaction would make balance go below minimum balance
        let minimumBalance = 20 + (account.subentry_count) * 10;
        let nativeBalance = _(account.balances).find(balance => balance.asset_type === 'native').balance;
        let maxSend = new BigNumber(nativeBalance).minus(minimumBalance);
        if (maxSend.lt(this.amount)) {
          throw new BasicClientError('InsufficientBalanceError', {maxSend});
        }
      })
      .then(() => {
        // Check if destination account exists. If no, at least 20 XLM must be sent.
        if (new BigNumber(this.amount).gte(20)) {
          return;
        }

        return this.Server.accounts()
          .accountId(this.destination)
          .call()
          .catch(err => {
            if (err.name === 'NotFoundError') {
              throw new BasicClientError('DestinationAccountNotExistError');
            }
          });
      })
      .then(() => {
        this.displayedAmount = new BigNumber(this.amount).toFormat();
        this.showView(null, 'sendConfirm');
      })
      .catch(err => {
        let alert;
        switch (err.name) {
          case 'NotFoundError':
            alert = new Alert({
              title: '',
              text: 'Your account isn\'t funded.',
              type: Alert.TYPES.ERROR
            });
            break;
          case 'InsufficientBalanceError':
            alert = new Alert({
              title: 'Insufficient balance.',
              text:
                `To maintain your <a href="https://www.stellar.org/developers/learn/concepts/fees.html#minimum-balance" target="_blank">minimum balance</a>, the most you can currently send is ${err.data.maxSend}.`,
              type: Alert.TYPES.ERROR
            });
            break;
          case 'DestinationAccountNotExistError':
            alert = new Alert({
              title: 'Destination account doesn\'t exist.',
              text: 'You account must send at least 20 lumens to create an account.',
              type: Alert.TYPES.ERROR
            });
            break;
          default:
            alert = new Alert({
              title: 'Unknown error: '+err.name,
              text: '',
              type: Alert.TYPES.ERROR
            });
            break;
        }
        this.amountAlertGroup.show(alert);
      })
      .finally(() => {
        this.sending = false;
        this.$scope.$apply();
      });
  }

  confirm() {
    this.showView(null, 'sendWaiting');
    return this.Server.accounts()
      .accountId(this.destination)
      .call()
      .then(() => {
        // Account exist. Send payment operation.
        let operation = Operation.payment({
          destination: this.destination,
          asset: Asset.native(),
          amount: this.amount
        });
        return this._submitTransaction(operation);
      })
      .catch(err => {
        if (err.name === 'NotFoundError') {
          // Account does not exist. Send create_account operation.
          let operation = Operation.createAccount({
            destination: this.destination,
            startingBalance: this.amount
          });
          return this._submitTransaction(operation);
        } else {
          throw err;
        }
      });
  }

  resubmitTransaction() {
    if (this.lastTransactionXDR === null) {
      return;
    }

    this.showView(null, 'sendWaiting');

    var transaction = new Transaction(this.lastTransactionXDR);

    return this.Server.submitTransaction(transaction)
      .then(this._submitOnSuccess.bind(this))
      .catch(this._submitOnFailure.bind(this))
      .finally(this._submitFinally.bind(this));
  }

  _submitTransaction(operation) {
    return this.Sessions.loadDefaultAccount()
      .then(() => {
        let memo = Memo.none();

        if (this.memo) {
          switch (this.memoType) {
            case 'MEMO_TEXT':
              memo = Memo.text(this.memoValue);
              break;
            case 'MEMO_ID':
              memo = Memo.id(this.memoValue);
              break;
            case 'MEMO_HASH':
              memo = Memo.hash(this.memoValue);
              break;
            case 'MEMO_RETURN':
              memo = Memo.returnHash(this.memoValue);
              break;
          }
        }

        let transaction = new TransactionBuilder(this.session.getAccount())
          .addOperation(operation)
          .addMemo(memo)
          .build();

        if (this.useLedger) {
          let ledgerApi = new StellarLedger.Api(new StellarLedger.comm(120));
          return ledgerApi.signTx_async(this.bip32Path, transaction).then(result => {
            let signature = result['signature'];
            let keyPair = Keypair.fromAccountId(this.session.address);
            let hint = keyPair.signatureHint();
            let decorated = new xdr.DecoratedSignature({hint, signature});
            transaction.signatures.push(decorated);
            this.lastTransactionXDR = transaction.toEnvelope().toXDR().toString("base64");
            return this.Server.submitTransaction(transaction);
          }).catch(e => {
            this.ledgerError = e;
            throw e;
          });
        } else {
          transaction.sign(Keypair.fromSeed(this.session.getSecret()));
          this.lastTransactionXDR = transaction.toEnvelope().toXDR().toString("base64");
          return this.Server.submitTransaction(transaction);
        }
      })
      .then(this._submitOnSuccess.bind(this))
      .catch(this._submitOnFailure.bind(this))
      .finally(this._submitFinally.bind(this));
  }

  _submitOnSuccess() {
    this.success = true;
    this.destinationAddress = null;
    this.destination = null;
    this.amount = null;
    this.memo = false;
    this.lastTransactionXDR = null;
    this.$rootScope.$broadcast('account-viewer.transaction-success');
  }

  _submitOnFailure(e) {
    this.success = false;
    this.needsResubmit = false;

    if (this.ledgerError !== null) {
      this.outcomeMessage = this.ledgerError;
    } else {
      if (e.title != "Transaction Failed" && e.title != "Transaction Malformed") {
        this.needsResubmit = true;
      }
      this.outcomeMessage = JSON.stringify(e, null, '  ');
    }
  }

  _submitFinally() {
    this.showView(null, 'sendOutcome');
    this.$scope.$apply()
  }
}
<|endoftext|>