<commit_before>class HtmlUtils {
    static QS(key) {
        // セレクタ
        const element = document.querySelector(key);
        if (element == null) return null;
        // 問い合わせフォームの確認画面ではhiddenになるため。
        if (element.getAttribute('type') == 'hidden') return null;
        return element;
    }
    static createSelectBox(options) {
        const element = document.createElement('select');
        // セレクトボックスを作成
        element.length = options.length;
        for (var i = 0; i < options.length; i++){
            const item = options[i];
            element.options[i].text = item.text || item.value;
            element.options[i].value = item.value || item.text;
        }
        return element;
    }
    static setValue(key, value) {
        // 値を設定
        // key:Selector
        // value:value
        const element = HtmlUtils.QS(key);
        if (element == null) return;
        element.value = value;
    }
}

class Widgets {
    createCountries(key) {
        // 国家名のセレクトボックスを作成
        // key:Selector セレクトボックスを追加するための位置ヒントとして利用
        // セレクトボックスを選択時に、input項目に反映
        const element = HtmlUtils.QS(key);
        if (element == null) return;
        const select = HtmlUtils.createSelectBox(COUNTRIES);
        // セレクトボックス => input項目への反映
        select.onchange = (event) => {
           const target = event.target;
           element.value = target.options[target.selectedIndex].value;
        }
        element.parentNode.insertBefore(select, element.nextSibling);
    }
    setdefaultvalues() {
	   const now = new Date();
	   //---------------------------------------------------------------
	   // 【不具合のご報告】 フォーム
	   //---------------------------------------------------------------
	   HtmlUtils.setValue('select[name="trouble_date1"]', now.getFullYear());
	   HtmlUtils.setValue('select[name="trouble_date2"]', now.getMonth() + 1);
	   HtmlUtils.setValue('select[name="trouble_date3"]', now.getDate());
	   HtmlUtils.setValue('select[name="trouble_date4"]', now.getHours());
	   HtmlUtils.setValue('select[name="webbrowser_name"]', 'Google Chrome');
	   //---------------------------------------------------------------
	   // 【迷惑行為のご報告】 フォーム
	   //---------------------------------------------------------------
	   HtmlUtils.setValue('select[name="lova_report_datetime1"]', now.getFullYear());
	   HtmlUtils.setValue('select[name="lova_report_datetime2"]', now.getMonth() + 1);
	   HtmlUtils.setValue('select[name="lova_report_datetime3"]', now.getDate());
	   HtmlUtils.setValue('select[name="lova_report_datetime4"]', now.getHours());
	   this.createCountries('input[name="fez_reporter_region"]'); //通報者の国家名
	   this.createCountries('input[name="fez_report_region"]'); //違反者の国家名
	   //---------------------------------------------------------------
	   // 【不具合のご報告】、 【迷惑行為のご報告】フォームの両方にある項目
	   //---------------------------------------------------------------
	   HtmlUtils.setValue('select[name="fez_world"]', 'ワールド統合後');
    }
}
const COUNTRIES = [
    { text:'', value:''},
    { text:'ネツァワル'},
    { text:'エルソード'},
    { text:'ホルデイン'},
    { text:'ゲブランド'},
    { text:'カセドリア'},
];
// call function
var widgets = new Widgets();
widgets.setdefaultvalues();
<commit_msg>add Widgets constructor<commit_after>class HtmlUtils {
    static QS(key) {
        // セレクタ
        const element = document.querySelector(key);
        if (element == null) return null;
        // 問い合わせフォームの確認画面ではhiddenになるため。
        if (element.getAttribute('type') == 'hidden') return null;
        return element;
    }
    static createSelectBox(options) {
        const element = document.createElement('select');
        // セレクトボックスを作成
        element.length = options.length;
        for (var i = 0; i < options.length; i++){
            const item = options[i];
            element.options[i].text = item.text || item.value;
            element.options[i].value = item.value || item.text;
        }
        return element;
    }
    static setValue(key, value) {
        // 値を設定
        // key:Selector
        // value:value
        const element = HtmlUtils.QS(key);
        if (element == null) return;
        element.value = value;
    }
}

class Widgets {
    constructor() {
        this.createCountries('input[name="fez_reporter_region"]'); //通報者の国家名
        this.createCountries('input[name="fez_report_region"]'); //違反者の国家名
    }
    createCountries(key) {
        // 国家名のセレクトボックスを作成
        // key:Selector セレクトボックスを追加するための位置ヒントとして利用
        // セレクトボックスを選択時に、input項目に反映
        const element = HtmlUtils.QS(key);
        if (element == null) return;
        const select = HtmlUtils.createSelectBox(COUNTRIES);
        // セレクトボックス => input項目への反映
        select.onchange = (event) => {
           const target = event.target;
           element.value = target.options[target.selectedIndex].value;
        }
        element.parentNode.insertBefore(select, element.nextSibling);
    }
    setdefaultvalues() {
	   const now = new Date();
	   //---------------------------------------------------------------
	   // 【不具合のご報告】 フォーム
	   //---------------------------------------------------------------
	   HtmlUtils.setValue('select[name="trouble_date1"]', now.getFullYear());
	   HtmlUtils.setValue('select[name="trouble_date2"]', now.getMonth() + 1);
	   HtmlUtils.setValue('select[name="trouble_date3"]', now.getDate());
	   HtmlUtils.setValue('select[name="trouble_date4"]', now.getHours());
	   HtmlUtils.setValue('select[name="webbrowser_name"]', 'Google Chrome');
	   //---------------------------------------------------------------
	   // 【迷惑行為のご報告】 フォーム
	   //---------------------------------------------------------------
	   HtmlUtils.setValue('select[name="lova_report_datetime1"]', now.getFullYear());
	   HtmlUtils.setValue('select[name="lova_report_datetime2"]', now.getMonth() + 1);
	   HtmlUtils.setValue('select[name="lova_report_datetime3"]', now.getDate());
	   HtmlUtils.setValue('select[name="lova_report_datetime4"]', now.getHours());
	   //---------------------------------------------------------------
	   // 【不具合のご報告】、 【迷惑行為のご報告】フォームの両方にある項目
	   //---------------------------------------------------------------
	   HtmlUtils.setValue('select[name="fez_world"]', 'ワールド統合後');
    }
}
const COUNTRIES = [
    { text:'', value:''},
    { text:'ネツァワル'},
    { text:'エルソード'},
    { text:'ホルデイン'},
    { text:'ゲブランド'},
    { text:'カセドリア'},
];
// call function
var widgets = new Widgets();
widgets.setdefaultvalues();
<|endoftext|>


<commit_before>// name space for app

app.properties = {
    viewModel: new propertiesViewModel()
};
var authenticated = authenticated || false;

$(document).ready(function () {

    ko.applyBindings(app.properties.viewModel, document.getElementById('property-html'));
	// might be terribly inefficient to bind app to the tabs but we need many viewModels to update their state
	// beyond basic tabs (eg. when scenarios require xyz and we want to alert the user via the tabs...)
    ko.applyBindings(app, document.getElementById('global-tabs'));

    $(window).resize(app.onResize);
    app.onResize();

    map.zoomToExtent(OpenLayers.Bounds.fromArray([-13954802.50397, 5681411.4375898, -13527672.389972, 5939462.8450446]));
	app.globalErrorHandling();

	app.standUploadFormInit();

	app.chartInit();

	app.deferredClickHandler();

	app.router();

	app.moreHelp();

	window.onhashchange = app.hashChange;

	// temp hack. django switch in template is always adding this.
	//
	if (authenticated) {
		$('body').removeClass('no-auth');
	}

});

app.router = function () {
	var viewList = ['#properties', '#stands', '#scenarios'],
		loc = window.location.hash.split("/");

	//set in map_ext
	if (authenticated) {
		//TODO should probably account for about and help
		if ( $.inArray(loc[0], viewList) < 0) {
			window.location.hash = "#properties";
			//app.log('Rerouting to #properties');
		} else {
			app.hashChange();
		}

	}

};
app.deferredClickHandler = function () {
	//let's bubble.
	$('body').on('click', function (event, a) {
		var $t = $(event.target);

		if ($t.is('.more-toggle')) {
			$t.next('.more').toggle();
			event.preventDefault();
			return false;
		}

	});
};

//needed by global tabs. Need to bind on page load but they don't exist yet. Cheap defer.
app.selectedPropertyName = ko.observable("");
app.selectedPropertyUID = ko.observable("");

app.moreHelp = function () {

	$('.info-help').find('strong')
		.next('span').hide()
		.after(' Read More...');

	$('body').on('click', function (e) {
		var $target = $(e.target);
		if ($target.is('.read-more')) {
			$target.prev('span').toggle();
			$target.closest('.info-help').toggleClass('opened');
			if ($target.prev('span').is(':visible')) {
				$target.text(' Read Less');
			} else {
				$target.text(' Read More...');
			}
			e.stopImmediatePropagation();
		}
	});
};

app.onResize = function () {
    var height = $(window).height(),
		width = $(window).width(),
		divWidth;

    //$("#map").height(height - 137);
    $("#map").height(height - 163);
    map.render('map');

    divWidth = width * (7 / 12); // span7
    if (divWidth > height) {
        // "widescreen" so go side-by-side
        $(".timemap-container").width('48%');
        $(".timemap").height(height - 314);
    } else {
        // "narrow" so go top-to-bottom
        $(".timemap-container").width('99%');
        $(".timemap").height((height - 324) / 2.0);
    }
    if (scenarioPlot) {
        refreshCharts();
    }
};

app.chartInit = function () {
	// initialize chart and timemap metrics dropdowns
	// more in chart.js
    var chart_sel = $('#chart-metrics-select'),
		timemap_sel = $('#timemap-metrics-select'),
		metric;

    for (var prop in chartMetrics) {
        metric = chartMetrics[prop];
        chart_sel.append($('<option value="' + metric.variableName + '">' + metric.title + '</option>'));
        timemap_sel.append($('<option value="' + metric.variableName + '">' + metric.title + '</option>'));
    }
    chart_sel.change(refreshCharts);
    timemap_sel.change( function () { refreshTimeMap(true, true); });

    $('.selectpicker').selectpicker();

    $('#scenario-charts-tab').on('shown', refreshCharts);
};

app.standUploadFormInit = function () {

	var options = {
		beforeSubmit: function (formData, jqForm, options) {
			var name, file;
			$(formData).each(function () {
				if (this.name === 'new_property_name') {
					name = this.value;
				} else if (this.name === 'ogrfile') {
					file = this.value;
				}
			});
			if (!name) {
				$("#upload-propertyname-required").fadeIn();
				return false;
			}
			if (!file) {
				$("#upload-file-required").fadeIn();
				return false;
			}

			$("#uploadProgress").show();
		},
		uploadProgress: function (event, position, total, percentComplete) {
			var percentVal = percentComplete + '%';
			$("#uploadProgress .bar").css('width', percentVal);
			if (percentComplete > 99) {
				$("#uploadProgress").hide();
				$("#uploadResponse").html('
Upload complete. Processing stand data...
');
			}
		},
		complete: function (xhr) {
			$("#uploadProgress").hide();
			$("#uploadProgress .bar").css('width', '0%');
		},
		error: function (data, status, xhr) {
			$("#uploadResponse").html(data.responseText);
		},
		success: function (data, status, xhr) {
			if (xhr.status === 201) {
				$("#uploadResponse").fadeOut();
				$("#uploadResponse").html('
Success
');
				$("#uploadResponse").fadeIn();
				$('#uploadForm').clearForm();
				var interval = setTimeout( function() {
					$("#uploadResponse").html('');
					app.properties.viewModel.afterUploadSuccess(data);
				}, 2000);
			}
		}

	};
    $('#uploadForm').submit(function () {
        $(this).ajaxSubmit(options);
        return false;
    });
};

app.globalErrorHandling = function () {
	$('body').prepend('

×');
	app.$flash = $('#flash');

	$( document ).ajaxError(function (event, request, settings, exception) {
		app.flash(request.responseText);
        // TODO parse out human-readable text from html error responses?
	});

	app.$flash.find('.close').bind('click', app.flash.dismiss);
};

//@TODO should pass an object. it's 2013. -wm
app.flash = function (message, header, flashType) {
	// bs: alert-error, alert-warning,
	var type = flashType || 'alert-error',
		hdr = header || 'Oops',
		msg = message || '';

	app.$flash.addClass( type ).slideDown(200)
		.find('div').text( msg ).end()
		.find('h4').text( hdr );

	// $(window).scrollTop(0);
	$("html, body").animate({ scrollTop: 0 }, 300);

};
app.flash.dismiss = function () {
	app.$flash.hide().removeClass('alert-error alert-warning alert-success');
	app.$flash.find('div, h4').text('');
};

// for when logging goes wild. pass a string and a hex or named css color
app.log = function (string, color) {
	var logged = string.toUpperCase();
	color = color || "#007";
	console.log("%c     " + logged + "     ",  "color:white; background-color:" + color + "");
};
<commit_msg>stand loading notification<commit_after>// name space for app

app.properties = {
    viewModel: new propertiesViewModel()
};
var authenticated = authenticated || false;

$(document).ready(function () {

    ko.applyBindings(app.properties.viewModel, document.getElementById('property-html'));
	// might be terribly inefficient to bind app to the tabs but we need many viewModels to update their state
	// beyond basic tabs (eg. when scenarios require xyz and we want to alert the user via the tabs...)
    ko.applyBindings(app, document.getElementById('global-tabs'));

    $(window).resize(app.onResize);
    app.onResize();

    map.zoomToExtent(OpenLayers.Bounds.fromArray([-13954802.50397, 5681411.4375898, -13527672.389972, 5939462.8450446]));
	app.globalErrorHandling();

	app.standUploadFormInit();

	app.chartInit();

	app.deferredClickHandler();

	app.router();

	app.moreHelp();

	window.onhashchange = app.hashChange;

	// temp hack. django switch in template is always adding this.
	//
	if (authenticated) {
		$('body').removeClass('no-auth');
	}

});

app.router = function () {
	var viewList = ['#properties', '#stands', '#scenarios'],
		loc = window.location.hash.split("/");

	//set in map_ext
	if (authenticated) {
		//TODO should probably account for about and help
		if ( $.inArray(loc[0], viewList) < 0) {
			window.location.hash = "#properties";
			//app.log('Rerouting to #properties');
		} else {
			app.hashChange();
		}

	}

};
app.deferredClickHandler = function () {
	//let's bubble.
	$('body').on('click', function (event, a) {
		var $t = $(event.target);

		if ($t.is('.more-toggle')) {
			$t.next('.more').toggle();
			event.preventDefault();
			return false;
		}

	});
};

//needed by global tabs. Need to bind on page load but they don't exist yet. Cheap defer.
app.selectedPropertyName = ko.observable("");
app.selectedPropertyUID = ko.observable("");

app.moreHelp = function () {

	$('.info-help').find('strong')
		.next('span').hide()
		.after(' Read More...');

	$('body').on('click', function (e) {
		var $target = $(e.target);
		if ($target.is('.read-more')) {
			$target.prev('span').toggle();
			$target.closest('.info-help').toggleClass('opened');
			if ($target.prev('span').is(':visible')) {
				$target.text(' Read Less');
			} else {
				$target.text(' Read More...');
			}
			e.stopImmediatePropagation();
		}
	});
};

app.onResize = function () {
    var height = $(window).height(),
		width = $(window).width(),
		divWidth;

    //$("#map").height(height - 137);
    $("#map").height(height - 163);
    map.render('map');

    divWidth = width * (7 / 12); // span7
    if (divWidth > height) {
        // "widescreen" so go side-by-side
        $(".timemap-container").width('48%');
        $(".timemap").height(height - 314);
    } else {
        // "narrow" so go top-to-bottom
        $(".timemap-container").width('99%');
        $(".timemap").height((height - 324) / 2.0);
    }
    if (scenarioPlot) {
        refreshCharts();
    }
};

app.chartInit = function () {
	// initialize chart and timemap metrics dropdowns
	// more in chart.js
    var chart_sel = $('#chart-metrics-select'),
		timemap_sel = $('#timemap-metrics-select'),
		metric;

    for (var prop in chartMetrics) {
        metric = chartMetrics[prop];
        chart_sel.append($('<option value="' + metric.variableName + '">' + metric.title + '</option>'));
        timemap_sel.append($('<option value="' + metric.variableName + '">' + metric.title + '</option>'));
    }
    chart_sel.change(refreshCharts);
    timemap_sel.change( function () { refreshTimeMap(true, true); });

    $('.selectpicker').selectpicker();

    $('#scenario-charts-tab').on('shown', refreshCharts);
};

app.standUploadFormInit = function () {

	var options = {
		beforeSubmit: function (formData, jqForm, options) {
			var name, file;
			$(formData).each(function () {
				if (this.name === 'new_property_name') {
					name = this.value;
				} else if (this.name === 'ogrfile') {
					file = this.value;
				}
			});
			if (!name) {
				$("#upload-propertyname-required").fadeIn();
				return false;
			}
			if (!file) {
				$("#upload-file-required").fadeIn();
				return false;
			}

			$("#uploadProgress").show();
		},
		uploadProgress: function (event, position, total, percentComplete) {
			var percentVal = percentComplete + '%';
			$("#uploadProgress .bar").css('width', percentVal);
			if (percentComplete > 99) {
				$("#uploadProgress").hide();
				$("#uploadResponse").html('
Transfer complete. Processing stand data; please wait... ' +
                    '
');
			}
		},
		complete: function (xhr) {
			$("#uploadProgress").hide();
			$("#uploadProgress .bar").css('width', '0%');
		},
		error: function (data, status, xhr) {
			$("#uploadResponse").html(data.responseText);
		},
		success: function (data, status, xhr) {
			if (xhr.status === 201) {
				$("#uploadResponse").fadeOut();
				$("#uploadResponse").html('
Success
');
				$("#uploadResponse").fadeIn();
				$('#uploadForm').clearForm();
				var interval = setTimeout( function() {
					$("#uploadResponse").html('');
					app.properties.viewModel.afterUploadSuccess(data);
				}, 2000);
			}
		}

	};
    $('#uploadForm').submit(function () {
        $(this).ajaxSubmit(options);
        return false;
    });
};

app.globalErrorHandling = function () {
	$('body').prepend('

×');
	app.$flash = $('#flash');

	$( document ).ajaxError(function (event, request, settings, exception) {
		app.flash(request.responseText);
        // TODO parse out human-readable text from html error responses?
	});

	app.$flash.find('.close').bind('click', app.flash.dismiss);
};

//@TODO should pass an object. it's 2013. -wm
app.flash = function (message, header, flashType) {
	// bs: alert-error, alert-warning,
	var type = flashType || 'alert-error',
		hdr = header || 'Oops',
		msg = message || '';

	app.$flash.addClass( type ).slideDown(200)
		.find('div').text( msg ).end()
		.find('h4').text( hdr );

	// $(window).scrollTop(0);
	$("html, body").animate({ scrollTop: 0 }, 300);

};
app.flash.dismiss = function () {
	app.$flash.hide().removeClass('alert-error alert-warning alert-success');
	app.$flash.find('div, h4').text('');
};

// for when logging goes wild. pass a string and a hex or named css color
app.log = function (string, color) {
	var logged = string.toUpperCase();
	color = color || "#007";
	console.log("%c     " + logged + "     ",  "color:white; background-color:" + color + "");
};
<|endoftext|>


<commit_before>(function() {
    var $this;
    $('.devhub-form').on('click', '.toggles a', _pd(function() {
        $this = $(this);
        var $choices = $this.closest('td').find('.checkbox-choices input[type=checkbox]:not(:disabled)');
        if ($this.hasClass('all')) {
            $choices.attr('checked', true);
        } else {
            $choices.removeAttr('checked');
        }
    })).on('editLoaded.disableCheckboxes', function(e) {
        // Disable individual checkbox fields when we see them.
        // (Customizing Django's CheckboxSelectMultiple widget is stupid.)
        $('.checkbox-choices').each(function() {
            $this = $(this);
            var choices = JSON.parse($this.attr('data-disabled'));
            var selectors = _.map(choices, function(val) {
                return format('input[value="{0}"]', val);
            });
            $this.find(selectors.join(', ')).attr('disabled', true).removeAttr('checked');
        });
    });
})();
<commit_msg>do checkbox disabling only when appropriate (bug 782740)<commit_after>(function() {
    var $this;
    $('.devhub-form').on('click', '.toggles a', _pd(function() {
        $this = $(this);
        var $choices = $this.closest('td').find('.checkbox-choices input[type=checkbox]:not(:disabled)');
        if ($this.hasClass('all')) {
            $choices.attr('checked', true);
        } else {
            $choices.removeAttr('checked');
        }
    })).on('editLoaded.disableCheckboxes', function(e) {
        // Disable individual checkbox fields when we see them.
        // (Customizing Django's CheckboxSelectMultiple widget is stupid.)
        $('.checkbox-choices[data-disabled]').each(function() {
            $this = $(this);
            var choices = JSON.parse($this.attr('data-disabled'));
            var selectors = _.map(choices, function(val) {
                return format('input[value="{0}"]', val);
            });
            $this.find(selectors.join(', ')).attr('disabled', true).removeAttr('checked');
        });
    });
})();
<|endoftext|>


<commit_before>const { h, when, send, resolve, Value, Array: MutantArray, computed } = require('mutant')
const nest = require('depnest')
const ssbMentions = require('ssb-mentions')
const extend = require('xtend')
const addSuggest = require('suggest-box')
const blobFiles = require('ssb-blob-files')
const get = require('lodash/get')
const datSharedFiles = require('dat-shared-files')

exports.gives = nest('message.html.compose')

exports.needs = nest({
  'about.async.suggest': 'first',
  'channel.async.suggest': 'first',
  'emoji.async.suggest': 'first',
  'meme.async.suggest': 'first',
  'message.html.confirm': 'first',
  'drafts.sync.get': 'first',
  'drafts.sync.set': 'first',
  'drafts.sync.remove': 'first',
  'settings.obs.get': 'first',
  'sbot.obs.connection': 'first'
})

exports.create = function (api) {
  return nest({ 'message.html.compose': compose })

  function compose (options, cb) {
    const {
      meta,
      location,
      feedIdsInThread = [],
      prepublish,
      placeholder = 'Write a message',
      shrink = true
    } = options

    if (typeof resolve(meta) !== 'object') throw new Error('Compose needs meta data about what sort of message composer you are making')
    if (!location) throw new Error('Compose expects a unique location so it can save drafts of messages')

    var files = []
    var filesById = {}
    var channelInputFocused = Value(false)
    var textAreaFocused = Value(false)
    var focused = computed([channelInputFocused, textAreaFocused], (a, b) => a || b)
    var hasContent = Value(false)

    var blurTimeout = null

    var expanded = computed([shrink, focused, hasContent], (shrink, focused, hasContent) => {
      if (!shrink || hasContent) return true

      return focused
    })

    var channelInput = h('input.channel', {
      'ev-input': () => hasContent.set(!!channelInput.value),
      'ev-keyup': ev => {
        ev.target.value = ev.target.value.replace(/^#*([\w@%&])/, '#$1')
      },
      'ev-blur': () => {
        clearTimeout(blurTimeout)
        blurTimeout = setTimeout(() => channelInputFocused.set(false), 200)
      },
      'ev-focus': send(channelInputFocused.set, true),
      placeholder: '#channel (optional)',
      value: computed(meta.channel, ch => ch ? '#' + ch : null),
      disabled: meta.channel ? true : undefined,
      title: meta.channel ? 'Reply is in same channel as original message' : undefined
    })

    var draftPerstTimeout = null
    var draftLocation = location
    var textArea = h('textarea', {
      'ev-input': () => {
        hasContent.set(!!textArea.value)
        clearTimeout(draftPerstTimeout)
        draftPerstTimeout = setTimeout(() => {
          api.drafts.sync.set(draftLocation, textArea.value)
        }, 200)
      },
      'ev-blur': () => {
        clearTimeout(blurTimeout)
        blurTimeout = setTimeout(() => textAreaFocused.set(false), 200)
      },
      'ev-focus': send(textAreaFocused.set, true),
      'ev-paste': ev => {
        const files = get(ev, 'clipboardData.files')
        if (!files || !files.length) return
        const opts = {
          stripExif: api.settings.obs.get('patchbay.removeExif', true),
          isPrivate
        }

        blobFiles(files, api.sbot.obs.connection, opts, afterBlobed)
      },
      placeholder
    })

    textArea.publish = publish // TODO: fix - clunky api for the keyboard shortcut to target

    // load draft
    let draft = api.drafts.sync.get(draftLocation)
    if (typeof draft === 'string') {
      textArea.value = draft
      hasContent.set(true)
    }

    var isPrivate = location.page === 'private' ||
      (location.key && !location.value) ||
      (location.value && location.value.private)

    var warningMessages = MutantArray([])
    var warning = computed(warningMessages, msgs => {
      if (!msgs.length) return

      return h('section.warnings', msgs.map((m, i) => {
        return h('div.warning', [
          h('i.fa.fa-exclamation-triangle'),
          h('div.message', m),
          h('i.fa.fa-times', { 'ev-click': () => warningMessages.deleteAt(i) })
        ])
      }))
    })

    var ssbBlobInput = h('input -ssb', {
      type: 'file',
      // accept,
      attributes: { multiple: true, title: 'Add files as blobs' },
      'ev-click': () => hasContent.set(true),
      'ev-change': (ev) => {
        warningMessages.set([])

        const files = ev.target.files
        const opts = {
          stripExif: api.settings.obs.get('patchbay.removeExif', true),
          isPrivate
        }
        blobFiles(files, api.sbot.obs.connection, opts, afterBlobed)
      }
    })
    function afterBlobed (err, result) {
      if (err) {
        console.error(err)
        warningMessages.push(err.message)
        return
      }

      files.push(result)
      filesById[result.link] = result

      const pos = textArea.selectionStart
      const embed = result.type.match(/^image/) ? '!' : ''
      const spacer = embed ? '\n' : ' '
      const insertLink = spacer + embed + '[' + result.name + ']' + '(' + result.link + ')' + spacer

      textArea.value = textArea.value.slice(0, pos) + insertLink + textArea.value.slice(pos)

      console.log('added:', result)
    }

    var datBlobInput = h('input -dat', {
      type: 'file',
      attributes: { multiple: true, title: 'Add files as dat link' },
      'ev-click': () => hasContent.set(true),
      'ev-change': (ev) => {
        const filenames = Array.from(ev.target.files).map(f => f.path)
        datSharedFiles.shareFiles(filenames, (err, datLink) => {
          if (err) {
            console.error(err)
            return
          }

          const pos = textArea.selectionStart
          let insertLink = datLink
          if (filenames.length == 1)
            insertLink = '[' + ev.target.files[0].name + ']' + '(' + datLink + '/' + ev.target.files[0].name + ')'

          textArea.value = textArea.value.slice(0, pos) + insertLink + textArea.value.slice(pos)
        })
      }
    })

    var isPublishing = Value(false)
    var publishBtn = h('button', { 'ev-click': publish, disabled: isPublishing }, isPrivate ? 'Reply' : 'Publish')

    var actions = h('section.actions', [
      h('div.attach', [
        h('i.fa.fa-paperclip'),
        h('div.attachers', [
          h('div.attacher', { 'ev-click': () => ssbBlobInput.click() }, [
            h('i.fa.fa-file-o'),
            h('div.label', 'small file(s)'),
            h('div.subtext', '< 5MB')
          ]),
          h('div.attacher', { 'ev-click': () => datBlobInput.click() }, [
            h('i.fa.fa-file-archive-o'),
            h('div.label', 'large file(s)'),
            h('div.subtext', 'DAT archive, (BETA)')
          ]),
          ssbBlobInput,
          datBlobInput
        ])
      ]),
      publishBtn
    ])

    var composer = h('Compose', {
      classList: when(expanded, '-expanded', '-contracted')
    }, [
      channelInput,
      textArea,
      warning,
      actions
    ])

    composer.addQuote = function (data) {
      try {
        if (typeof data.content.text === 'string') {
          var text = data.content.text
          textArea.value += '> ' + text.replace(/\r\n|\r|\n/g, '\n> ') + '\r\n\n'
          hasContent.set(!!textArea.value)
        }
      } catch (err) {
        // object not have text or content
      }
    }

    if (location.action === 'quote') { composer.addQuote(location.value) }

    addSuggest(channelInput, (inputText, cb) => {
      if (inputText[0] === '#') {
        api.channel.async.suggest(inputText.slice(1), cb)
      }
    }, { cls: 'PatchSuggest' })
    channelInput.addEventListener('suggestselect', ev => {
      channelInput.value = ev.detail.id // HACK : this over-rides the markdown value
    })

    addSuggest(textArea, (inputText, cb) => {
      const char = inputText[0]
      const wordFragment = inputText.slice(1)

      if (char === '@') api.about.async.suggest(wordFragment, feedIdsInThread, cb)
      if (char === '#') api.channel.async.suggest(wordFragment, cb)
      if (char === ':') api.emoji.async.suggest(wordFragment, cb)
      if (char === '&') api.meme.async.suggest(wordFragment, cb)
    }, { cls: 'PatchSuggest' })

    return composer

    // scoped

    function publish () {
      if (resolve(isPublishing)) return
      isPublishing.set(true)

      const channel = channelInput.value.startsWith('#')
        ? channelInput.value.substr(1).trim()
        : channelInput.value.trim()
      const mentions = ssbMentions(textArea.value).map(mention => {
        // merge markdown-detected mention with file info
        var file = filesById[mention.link]
        if (file) {
          if (file.type) mention.type = file.type
          if (file.size) mention.size = file.size
        }
        return mention
      })

      var content = extend(resolve(meta), {
        text: textArea.value,
        channel,
        mentions
      })

      if (!channel) delete content.channel
      if (!mentions.length) delete content.mentions
      if (content.recps && content.recps.length === 0) delete content.recps

      try {
        if (typeof prepublish === 'function') {
          content = prepublish(content)
        }
      } catch (err) {
        isPublishing.set(false)
        if (cb) cb(err)
        else throw err
      }

      return api.message.html.confirm(content, done)

      function done (err, msg) {
        isPublishing.set(false)
        if (err) throw err
        else if (msg) {
          textArea.value = ''
          api.drafts.sync.remove(draftLocation)
        }
        if (cb) cb(err, msg)
      }
    }
  }
}
<commit_msg>file(s) => files<commit_after>const { h, when, send, resolve, Value, Array: MutantArray, computed } = require('mutant')
const nest = require('depnest')
const ssbMentions = require('ssb-mentions')
const extend = require('xtend')
const addSuggest = require('suggest-box')
const blobFiles = require('ssb-blob-files')
const get = require('lodash/get')
const datSharedFiles = require('dat-shared-files')

exports.gives = nest('message.html.compose')

exports.needs = nest({
  'about.async.suggest': 'first',
  'channel.async.suggest': 'first',
  'emoji.async.suggest': 'first',
  'meme.async.suggest': 'first',
  'message.html.confirm': 'first',
  'drafts.sync.get': 'first',
  'drafts.sync.set': 'first',
  'drafts.sync.remove': 'first',
  'settings.obs.get': 'first',
  'sbot.obs.connection': 'first'
})

exports.create = function (api) {
  return nest({ 'message.html.compose': compose })

  function compose (options, cb) {
    const {
      meta,
      location,
      feedIdsInThread = [],
      prepublish,
      placeholder = 'Write a message',
      shrink = true
    } = options

    if (typeof resolve(meta) !== 'object') throw new Error('Compose needs meta data about what sort of message composer you are making')
    if (!location) throw new Error('Compose expects a unique location so it can save drafts of messages')

    var files = []
    var filesById = {}
    var channelInputFocused = Value(false)
    var textAreaFocused = Value(false)
    var focused = computed([channelInputFocused, textAreaFocused], (a, b) => a || b)
    var hasContent = Value(false)

    var blurTimeout = null

    var expanded = computed([shrink, focused, hasContent], (shrink, focused, hasContent) => {
      if (!shrink || hasContent) return true

      return focused
    })

    var channelInput = h('input.channel', {
      'ev-input': () => hasContent.set(!!channelInput.value),
      'ev-keyup': ev => {
        ev.target.value = ev.target.value.replace(/^#*([\w@%&])/, '#$1')
      },
      'ev-blur': () => {
        clearTimeout(blurTimeout)
        blurTimeout = setTimeout(() => channelInputFocused.set(false), 200)
      },
      'ev-focus': send(channelInputFocused.set, true),
      placeholder: '#channel (optional)',
      value: computed(meta.channel, ch => ch ? '#' + ch : null),
      disabled: meta.channel ? true : undefined,
      title: meta.channel ? 'Reply is in same channel as original message' : undefined
    })

    var draftPerstTimeout = null
    var draftLocation = location
    var textArea = h('textarea', {
      'ev-input': () => {
        hasContent.set(!!textArea.value)
        clearTimeout(draftPerstTimeout)
        draftPerstTimeout = setTimeout(() => {
          api.drafts.sync.set(draftLocation, textArea.value)
        }, 200)
      },
      'ev-blur': () => {
        clearTimeout(blurTimeout)
        blurTimeout = setTimeout(() => textAreaFocused.set(false), 200)
      },
      'ev-focus': send(textAreaFocused.set, true),
      'ev-paste': ev => {
        const files = get(ev, 'clipboardData.files')
        if (!files || !files.length) return
        const opts = {
          stripExif: api.settings.obs.get('patchbay.removeExif', true),
          isPrivate
        }

        blobFiles(files, api.sbot.obs.connection, opts, afterBlobed)
      },
      placeholder
    })

    textArea.publish = publish // TODO: fix - clunky api for the keyboard shortcut to target

    // load draft
    let draft = api.drafts.sync.get(draftLocation)
    if (typeof draft === 'string') {
      textArea.value = draft
      hasContent.set(true)
    }

    var isPrivate = location.page === 'private' ||
      (location.key && !location.value) ||
      (location.value && location.value.private)

    var warningMessages = MutantArray([])
    var warning = computed(warningMessages, msgs => {
      if (!msgs.length) return

      return h('section.warnings', msgs.map((m, i) => {
        return h('div.warning', [
          h('i.fa.fa-exclamation-triangle'),
          h('div.message', m),
          h('i.fa.fa-times', { 'ev-click': () => warningMessages.deleteAt(i) })
        ])
      }))
    })

    var ssbBlobInput = h('input -ssb', {
      type: 'file',
      // accept,
      attributes: { multiple: true, title: 'Add files as blobs' },
      'ev-click': () => hasContent.set(true),
      'ev-change': (ev) => {
        warningMessages.set([])

        const files = ev.target.files
        const opts = {
          stripExif: api.settings.obs.get('patchbay.removeExif', true),
          isPrivate
        }
        blobFiles(files, api.sbot.obs.connection, opts, afterBlobed)
      }
    })
    function afterBlobed (err, result) {
      if (err) {
        console.error(err)
        warningMessages.push(err.message)
        return
      }

      files.push(result)
      filesById[result.link] = result

      const pos = textArea.selectionStart
      const embed = result.type.match(/^image/) ? '!' : ''
      const spacer = embed ? '\n' : ' '
      const insertLink = spacer + embed + '[' + result.name + ']' + '(' + result.link + ')' + spacer

      textArea.value = textArea.value.slice(0, pos) + insertLink + textArea.value.slice(pos)

      console.log('added:', result)
    }

    var datBlobInput = h('input -dat', {
      type: 'file',
      attributes: { multiple: true, title: 'Add files as dat link' },
      'ev-click': () => hasContent.set(true),
      'ev-change': (ev) => {
        const filenames = Array.from(ev.target.files).map(f => f.path)
        datSharedFiles.shareFiles(filenames, (err, datLink) => {
          if (err) {
            console.error(err)
            return
          }

          const pos = textArea.selectionStart
          let insertLink = datLink
          if (filenames.length == 1)
            insertLink = '[' + ev.target.files[0].name + ']' + '(' + datLink + '/' + ev.target.files[0].name + ')'

          textArea.value = textArea.value.slice(0, pos) + insertLink + textArea.value.slice(pos)
        })
      }
    })

    var isPublishing = Value(false)
    var publishBtn = h('button', { 'ev-click': publish, disabled: isPublishing }, isPrivate ? 'Reply' : 'Publish')

    var actions = h('section.actions', [
      h('div.attach', [
        h('i.fa.fa-paperclip'),
        h('div.attachers', [
          h('div.attacher', { 'ev-click': () => ssbBlobInput.click() }, [
            h('i.fa.fa-file-o'),
            h('div.label', 'small files'),
            h('div.subtext', '< 5MB')
          ]),
          h('div.attacher', { 'ev-click': () => datBlobInput.click() }, [
            h('i.fa.fa-file-archive-o'),
            h('div.label', 'large files'),
            h('div.subtext', 'DAT archive, (BETA)')
          ]),
          ssbBlobInput,
          datBlobInput
        ])
      ]),
      publishBtn
    ])

    var composer = h('Compose', {
      classList: when(expanded, '-expanded', '-contracted')
    }, [
      channelInput,
      textArea,
      warning,
      actions
    ])

    composer.addQuote = function (data) {
      try {
        if (typeof data.content.text === 'string') {
          var text = data.content.text
          textArea.value += '> ' + text.replace(/\r\n|\r|\n/g, '\n> ') + '\r\n\n'
          hasContent.set(!!textArea.value)
        }
      } catch (err) {
        // object not have text or content
      }
    }

    if (location.action === 'quote') { composer.addQuote(location.value) }

    addSuggest(channelInput, (inputText, cb) => {
      if (inputText[0] === '#') {
        api.channel.async.suggest(inputText.slice(1), cb)
      }
    }, { cls: 'PatchSuggest' })
    channelInput.addEventListener('suggestselect', ev => {
      channelInput.value = ev.detail.id // HACK : this over-rides the markdown value
    })

    addSuggest(textArea, (inputText, cb) => {
      const char = inputText[0]
      const wordFragment = inputText.slice(1)

      if (char === '@') api.about.async.suggest(wordFragment, feedIdsInThread, cb)
      if (char === '#') api.channel.async.suggest(wordFragment, cb)
      if (char === ':') api.emoji.async.suggest(wordFragment, cb)
      if (char === '&') api.meme.async.suggest(wordFragment, cb)
    }, { cls: 'PatchSuggest' })

    return composer

    // scoped

    function publish () {
      if (resolve(isPublishing)) return
      isPublishing.set(true)

      const channel = channelInput.value.startsWith('#')
        ? channelInput.value.substr(1).trim()
        : channelInput.value.trim()
      const mentions = ssbMentions(textArea.value).map(mention => {
        // merge markdown-detected mention with file info
        var file = filesById[mention.link]
        if (file) {
          if (file.type) mention.type = file.type
          if (file.size) mention.size = file.size
        }
        return mention
      })

      var content = extend(resolve(meta), {
        text: textArea.value,
        channel,
        mentions
      })

      if (!channel) delete content.channel
      if (!mentions.length) delete content.mentions
      if (content.recps && content.recps.length === 0) delete content.recps

      try {
        if (typeof prepublish === 'function') {
          content = prepublish(content)
        }
      } catch (err) {
        isPublishing.set(false)
        if (cb) cb(err)
        else throw err
      }

      return api.message.html.confirm(content, done)

      function done (err, msg) {
        isPublishing.set(false)
        if (err) throw err
        else if (msg) {
          textArea.value = ''
          api.drafts.sync.remove(draftLocation)
        }
        if (cb) cb(err, msg)
      }
    }
  }
}
<|endoftext|>


<commit_before>export { Bounds };

import { ScaleTranslate } from "./ScaleTranslate.js";
import { BoundsArray } from "./BoundsArray.js";

/**
 * Describe the [ lower, upper ] bounds of a floating-point value
 *
 * @member {number} lower
 *   The lower bound of the range.
 *
 * @member {number} upper
 *   The upper bound of the range.
 *
 * @member {number} difference
 *   The distance between lower and upper.
 */
class Bounds {

  constructor( lower, upper ) {
    this.lower = Math.min( lower, upper ); // Confirm ( lower <= upper ).
    this.upper = Math.max( lower, upper );
  }

  get difference() {
    let difference = this.upper - this.lower;
    return difference;
  }

  /**
   * @return {Bounds}
   *   Return newly created object which is a copy of this Bounds.
   */
  clone() {
    return new Bounds( this.lower, this.upper );
  }

  /**
   * @param {number} N
   *   Set ( this.lower, this.upper ) by N.
   *
   * @return {Bounds} Return this (modified) object which is [ n, N ].
   */
  set_byN( N ) {
    this.lower = this.upper = N;
    return this;
  }

  /**
   * @param {number} aLower
   *   Set this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Set this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ aLower, aUpper ].
   */
  set_byLowerUpper( aLower, aUpper ) {
    this.lower = Math.min( aLower, aUpper ); // Confirm ( lower <= upper ).
    this.upper = Math.max( aLower, aUpper );
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Set this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object whose values are copied from aBounds.
   */
  set_byBounds( aBounds ) {
    return this.set_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} Ns    Set ( this.lower, this.upper ) by ( Ns[ aIndex ], Ns[ aIndex ] ).
   * @param {number} aIndex  The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  set_byNs( Ns, aIndex ) {
    return this.set_byN( Ns[ aIndex ] );
  }

  /**
   * @param {number[]} aLowers  Set ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number[]} aUppers  Set ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  set_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.set_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Set ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  set_byBoundsArray( aBoundsArray, aIndex ) {
    return this.set_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {number} aLower   Clamp this.lower by aLower.
   * @param {number} aUpper   Clamp this.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ max( this.lower, aLower ), min( this.upper, aUpper ) ].
   */
  clamp_byLowerUpper( aLower, aUpper ) {

//!!! ...unfinished... (2022/01/11) What if two bounds does not intersect?

    // Confirm ( lower <= upper ) and ( aLower <= aUpper ).
    let lower_clamped = Math.max( Math.min( this.lower, this.upper ), Math.min( aLower, aUpper ) );
    let upper_clamped = Math.min( Math.max( this.lower, this.upper ), Math.max( aLower, aUpper ) );
    this.lower = lower_clamped;
    this.upper = upper_clamped;
    return this;
  }

  /**
   * @param {Bounds} aBounds   Clamp [ this.lower, this.upper ] by [ aBounds.lower, aBounds.upper ].
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ max( this.lower, aBounds.lower ), min( this.upper, aBounds.upper ) ].
   */
  clamp_byBounds( aBounds ) {
    return this.clamp_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} aLowers  Clamp this.lower by aLowers[ aIndex ].
   * @param {number[]} aUppers  Clamp this.upper by aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  clamp_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.clamp_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Clamp ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  clamp_byBoundsArray( aBoundsArray, aIndex ) {
    return this.clamp_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {number} N
   *   Add this Bounds.lower by N, and also add this Bounds.upper by N.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.add_LowerUpper( N, N ).
   */
  add_byN( N ) {
    // Confirm the lower and upper. And then, add corresponds.
    let thisLower = Math.min( this.lower, this.upper );
    let thisUpper = Math.max( this.lower, this.upper );
    this.lower = thisLower + N;
    this.upper = thisUpper + N;
    return this;
  }

  /**
   * @param {number} aLower
   *   Add this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Add this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by Bounds [ aLower, aUpper ].
   */
  add_byLowerUpper( aLower, aUpper ) {
    // Confirm the lower and upper. And then, add corresponds.
    let thisLower = Math.min( this.lower, this.upper );
    let thisUpper = Math.max( this.lower, this.upper );
    this.lower = thisLower + Math.min( aLower, aUpper );
    this.upper = thisUpper + Math.max( aLower, aUpper );
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Add this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by aBounds.
   */
  add_byBounds( aBounds ) {
    return this.add_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} Ns    Add ( this.lower, this.upper ) by ( Ns[ aIndex ], Ns[ aIndex ] ).
   * @param {number} aIndex  The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  add_byNs( Ns, aIndex ) {
    return this.add_byN( Ns[ aIndex ] );
  }

  /**
   * @param {number[]} aLowers  Add ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number[]} aUppers  Add ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  add_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.add_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Add ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  add_byBoundsArray( aBoundsArray, aIndex ) {
    return this.add_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {number} N
   *   The multiplier of this.lower and this.upper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.multiply_LowerUpper( N, N ) or repeating N times this.add_Bounds( this ).
   */
  multiply_byN( N ) {
    // Because the different sign of lower and upper, it needs compute all combinations to determine the bounds of result.
    let lower_N = this.lower * N;
    let upper_N = this.upper * N;
    this.lower = Math.min( lower_N, upper_N );
    this.upper = Math.max( lower_N, upper_N );
    return this;
  }

  /**
   * @param {number} aLower
   *   Multiply this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Multiply this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by Bounds [ aLower, aUpper ].
   */
  multiply_byLowerUpper( aLower, aUpper ) {
    // Because the different sign of lower and upper, it needs compute all combination to determine the bounds of result.
    let lower_lower = this.lower * aLower;
    let lower_upper = this.lower * aUpper;
    let upper_lower = this.upper * aLower;
    let upper_upper = this.upper * aUpper;
    this.lower = Math.min( lower_lower, lower_upper, upper_lower, upper_upper );
    this.upper = Math.max( lower_lower, lower_upper, upper_lower, upper_upper );
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Multiply this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by aBounds.
   */
  multiply_byBounds( aBounds ) {
    return this.multiply_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} Ns    Multiply ( this.lower, this.upper ) by ( Ns[ aIndex ], Ns[ aIndex ] ).
   * @param {number} aIndex  The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  multiply_byNs( Ns, aIndex ) {
    return this.multiply_byN( Ns[ aIndex ] );
  }

  /**
   * @param {number[]} aLowers  Multiply ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number[]} aUppers  Multiply ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  multiply_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.multiply_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Multiply ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  multiply_byBoundsArray( aBoundsArray, aIndex ) {
    return this.multiply_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {Bounds} aBounds
   *   The first multiplier (a Bounds).
   *
   * @param {number} N
   *   The second multiplier (a number).
   *
   * @return {Bounds}
   *   Return this (modified) object which is the sumed Bounds of repeating N times of multiplying this by aBounds.
   */
  multiply_byBounds_multiply_byN( aBounds, N ) {
    return this.multiply_byBounds( aBounds ).multiply_byN( N );
  }

  /**
   * @param {ScaleTranslate} aScaleTranslate
   *   Multiply this Bounds by aScaleTranslate.scale, and then add this Bounds by aScaleTranslate.translate.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.multiply_N( aScaleTranslate.scale ).add_N( aScaleTranslate.translate ).
   */
  scaleTranslate_byScaleTranslate( aScaleTranslate ) {
    return this.multiply_byN( aScaleTranslate.scale ).add_byN( aScaleTranslate.translate );
  }

  /**
   * @param {number} value
   *   The value to be clamped.
   *
   * @return {number}
   *   Return value clamped between this Bounds [ this.lower, this.upper ]. If value is NaN, it will become zero first and then be clamped
   * between this Bounds [ this.lower, this.upper ].
   */
  clamp_or_zeroIfNaN( value ) {
    if ( Number.isNaN( value ) )
      value = 0; // If NaN, view it as 0.
    return Math.max( this.lower, Math.min( value, this.upper ) );
  }

  /**
   * Confirm:
   *   - Every element is not NaN. (If it is, become 0.)
   *   - Every element is between [ lower, upper ].
   *
   * @param {Float32Array} source
   *   The source Float32Array.
   *
   * @return {Float32Array}
   *   Return a copy of source. Every element (float32):
   *     - If ( Number.isNaN( element ) == true ), let it become 0.
   *     - Otherwise, Math.max( lower, Math.min( element, upper ) ).
   */
  Float32Array_RestrictedClone( sourceArray ) {
    let resultArray = new Float32Array( sourceArray.length );
    for ( let i = 0; i < sourceArray.length; ++i ) {
      let element = sourceArray[ i ];
      if ( !Number.isNaN( element ) ) {
        resultArray[ i ] = Math.max( this.lower, Math.min( element, this.upper ) );
      } // If NaN, let it become 0. (Just do nothing, because Float32Array is initialized to zero by default.)
    }
    return resultArray;
  }

}

<commit_msg>Update Bounds.js<commit_after>export { Bounds };

import { ScaleTranslate } from "./ScaleTranslate.js";
import { BoundsArray } from "./BoundsArray.js";

/**
 * Describe the [ lower, upper ] bounds of a floating-point value
 *
 * @member {number} lower
 *   The lower bound of the range.
 *
 * @member {number} upper
 *   The upper bound of the range.
 *
 * @member {number} difference
 *   The distance between lower and upper.
 */
class Bounds {

  constructor( lower, upper ) {
    this.lower = Math.min( lower, upper ); // Confirm ( lower <= upper ).
    this.upper = Math.max( lower, upper );
  }

  get difference() {
    let difference = this.upper - this.lower;
    return difference;
  }

  /**
   * @return {Bounds}
   *   Return newly created object which is a copy of this Bounds.
   */
  clone() {
    return new Bounds( this.lower, this.upper );
  }

  /**
   * @param {number} N
   *   Set ( this.lower, this.upper ) by N.
   *
   * @return {Bounds} Return this (modified) object which is [ n, N ].
   */
  set_byN( N ) {
    this.lower = this.upper = N;
    return this;
  }

  /**
   * @param {number} aLower
   *   Set this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Set this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ aLower, aUpper ].
   */
  set_byLowerUpper( aLower, aUpper ) {
    this.lower = Math.min( aLower, aUpper ); // Confirm ( lower <= upper ).
    this.upper = Math.max( aLower, aUpper );
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Set this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object whose values are copied from aBounds.
   */
  set_byBounds( aBounds ) {
    return this.set_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} Ns    Set ( this.lower, this.upper ) by ( Ns[ aIndex ], Ns[ aIndex ] ).
   * @param {number} aIndex  The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  set_byNs( Ns, aIndex ) {
    return this.set_byN( Ns[ aIndex ] );
  }

  /**
   * @param {number[]} aLowers  Set ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number[]} aUppers  Set ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  set_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.set_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Set ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  set_byBoundsArray( aBoundsArray, aIndex ) {
    return this.set_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {number} aLower   Clamp this.lower by aLower.
   * @param {number} aUpper   Clamp this.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ max( this.lower, aLower ), min( this.upper, aUpper ) ].
   */
  clamp_byLowerUpper( aLower, aUpper ) {
    let anotherLower = Math.min( aLower, aUpper ); // Confirm ( anotherLower <= anotherUpper )
    let anotherUpper = Math.max( aLower, aUpper );

    // Because two bounds may be totally non-intersected, both thisLower and thisUpper needs be clamped by [ aLower, aUpper ].
    let lower_clamped = Math.min( Math.max( anotherLower, this.lower ), anotherUpper );
    let upper_clamped = Math.min( Math.max( anotherLower, this.upper ), anotherUpper );
    this.lower = Math.min( lower_clamped, upper_clamped ); // Confirm ( lower <= upper )
    this.upper = Math.max( lower_clamped, upper_clamped );
    return this;
  }

  /**
   * @param {Bounds} aBounds   Clamp [ this.lower, this.upper ] by [ aBounds.lower, aBounds.upper ].
   *
   * @return {Bounds}
   *   Return this (modified) object which is [ max( this.lower, aBounds.lower ), min( this.upper, aBounds.upper ) ].
   */
  clamp_byBounds( aBounds ) {
    return this.clamp_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} aLowers  Clamp this.lower by aLowers[ aIndex ].
   * @param {number[]} aUppers  Clamp this.upper by aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  clamp_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.clamp_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Clamp ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  clamp_byBoundsArray( aBoundsArray, aIndex ) {
    return this.clamp_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {number} N
   *   Add this Bounds.lower by N, and also add this Bounds.upper by N.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.add_LowerUpper( N, N ).
   */
  add_byN( N ) {
    // Confirm the lower and upper. And then, add corresponds.
    let thisLower = Math.min( this.lower, this.upper );
    let thisUpper = Math.max( this.lower, this.upper );
    this.lower = thisLower + N;
    this.upper = thisUpper + N;
    return this;
  }

  /**
   * @param {number} aLower
   *   Add this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Add this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by Bounds [ aLower, aUpper ].
   */
  add_byLowerUpper( aLower, aUpper ) {
    // Confirm the lower and upper. And then, add corresponds.
    let thisLower = Math.min( this.lower, this.upper );
    let thisUpper = Math.max( this.lower, this.upper );
    this.lower = thisLower + Math.min( aLower, aUpper );
    this.upper = thisUpper + Math.max( aLower, aUpper );
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Add this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is added by aBounds.
   */
  add_byBounds( aBounds ) {
    return this.add_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} Ns    Add ( this.lower, this.upper ) by ( Ns[ aIndex ], Ns[ aIndex ] ).
   * @param {number} aIndex  The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  add_byNs( Ns, aIndex ) {
    return this.add_byN( Ns[ aIndex ] );
  }

  /**
   * @param {number[]} aLowers  Add ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number[]} aUppers  Add ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  add_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.add_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Add ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  add_byBoundsArray( aBoundsArray, aIndex ) {
    return this.add_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {number} N
   *   The multiplier of this.lower and this.upper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.multiply_LowerUpper( N, N ) or repeating N times this.add_Bounds( this ).
   */
  multiply_byN( N ) {
    // Because the different sign of lower and upper, it needs compute all combinations to determine the bounds of result.
    let lower_N = this.lower * N;
    let upper_N = this.upper * N;
    this.lower = Math.min( lower_N, upper_N );
    this.upper = Math.max( lower_N, upper_N );
    return this;
  }

  /**
   * @param {number} aLower
   *   Multiply this Bounds.lower by aLower.
   *
   * @param {number} aUpper
   *   Multiply this Bounds.upper by aUpper.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by Bounds [ aLower, aUpper ].
   */
  multiply_byLowerUpper( aLower, aUpper ) {
    // Because the different sign of lower and upper, it needs compute all combination to determine the bounds of result.
    let lower_lower = this.lower * aLower;
    let lower_upper = this.lower * aUpper;
    let upper_lower = this.upper * aLower;
    let upper_upper = this.upper * aUpper;
    this.lower = Math.min( lower_lower, lower_upper, upper_lower, upper_upper );
    this.upper = Math.max( lower_lower, lower_upper, upper_lower, upper_upper );
    return this;
  }

  /**
   * @param {Bounds} aBounds
   *   Multiply this Bounds by aBounds.
   *
   * @return {Bounds}
   *   Return this (modified) object which is multiplied by aBounds.
   */
  multiply_byBounds( aBounds ) {
    return this.multiply_byLowerUpper( aBounds.lower, aBounds.upper );
  }

  /**
   * @param {number[]} Ns    Multiply ( this.lower, this.upper ) by ( Ns[ aIndex ], Ns[ aIndex ] ).
   * @param {number} aIndex  The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  multiply_byNs( Ns, aIndex ) {
    return this.multiply_byN( Ns[ aIndex ] );
  }

  /**
   * @param {number[]} aLowers  Multiply ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number[]} aUppers  Multiply ( this.lower, this.upper ) by ( aLowers[ aIndex ], aUppers[ aIndex ] ).
   * @param {number} aIndex     The array index of aLowers[] and aUppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  multiply_byLowersUppers( aLowers, aUppers, aIndex ) {
    return this.multiply_byLowerUpper( aLowers[ aIndex ], aUppers[ aIndex ] );
  }

  /**
   * @param {BoundsArray} aBoundsArray  Multiply ( this.lower, this.upper ) by ( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] ).
   * @param {number} aIndex             The array index of aBoundsArray.lowers[] and aBoundsArray.uppers[].
   *
   * @return {Bounds} Return this (modified) object.
   */
  multiply_byBoundsArray( aBoundsArray, aIndex ) {
    return this.multiply_byLowerUpper( aBoundsArray.lowers[ aIndex ], aBoundsArray.uppers[ aIndex ] );
  }

  /**
   * @param {Bounds} aBounds
   *   The first multiplier (a Bounds).
   *
   * @param {number} N
   *   The second multiplier (a number).
   *
   * @return {Bounds}
   *   Return this (modified) object which is the sumed Bounds of repeating N times of multiplying this by aBounds.
   */
  multiply_byBounds_multiply_byN( aBounds, N ) {
    return this.multiply_byBounds( aBounds ).multiply_byN( N );
  }

  /**
   * @param {ScaleTranslate} aScaleTranslate
   *   Multiply this Bounds by aScaleTranslate.scale, and then add this Bounds by aScaleTranslate.translate.
   *
   * @return {Bounds}
   *   Return this (modified) object which is the same as this.multiply_N( aScaleTranslate.scale ).add_N( aScaleTranslate.translate ).
   */
  scaleTranslate_byScaleTranslate( aScaleTranslate ) {
    return this.multiply_byN( aScaleTranslate.scale ).add_byN( aScaleTranslate.translate );
  }

  /**
   * @param {number} value
   *   The value to be clamped.
   *
   * @return {number}
   *   Return value clamped between this Bounds [ this.lower, this.upper ]. If value is NaN, it will become zero first and then be clamped
   * between this Bounds [ this.lower, this.upper ].
   */
  clamp_or_zeroIfNaN( value ) {
    if ( Number.isNaN( value ) )
      value = 0; // If NaN, view it as 0.
    return Math.max( this.lower, Math.min( value, this.upper ) );
  }

  /**
   * Confirm:
   *   - Every element is not NaN. (If it is, become 0.)
   *   - Every element is between [ lower, upper ].
   *
   * @param {Float32Array} source
   *   The source Float32Array.
   *
   * @return {Float32Array}
   *   Return a copy of source. Every element (float32):
   *     - If ( Number.isNaN( element ) == true ), let it become 0.
   *     - Otherwise, Math.max( lower, Math.min( element, upper ) ).
   */
  Float32Array_RestrictedClone( sourceArray ) {
    let resultArray = new Float32Array( sourceArray.length );
    for ( let i = 0; i < sourceArray.length; ++i ) {
      let element = sourceArray[ i ];
      if ( !Number.isNaN( element ) ) {
        resultArray[ i ] = Math.max( this.lower, Math.min( element, this.upper ) );
      } // If NaN, let it become 0. (Just do nothing, because Float32Array is initialized to zero by default.)
    }
    return resultArray;
  }

}

<|endoftext|>


<commit_before>/*
 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/.
 Copyright (c) 2014 Mozilla Corporation

 Contributors:
 Jeff Bryner jbryner@mozilla.com
 Anthony Verez averez@mozilla.com
 Sanchit Kapoor sanchitlucknow@gmail.com
 Yash Mehrotra yashmehrotra95@gmail.com
 Avijit Gupta 526avijit@gmail.com
 */

if (Meteor.isClient) {

  var MESHPROPERTIES = {
    count: 4,
    position: [
      {x: 0, y: 0, z: 0},
      {x: -800, y: -20, z: 0},
      {x: -800, y: -20, z: -800},
      {x: 0 , y: -20, z: -800}
    ],
    rotationY: [
      0,
      0,
      Math.PI/2,
      Math.PI/2
    ]
  };

  // TODO: @YASH : BECAUSE YOU REALLY DONT KNOW ANYTHING ABOUT JS
  var RANKCOORDINATES = [
    {x: -286, z: -115},
    {x: -15, z: 11},
    {x: -234, z: 232},
    {x: -69, z: -142},
    {x: 124, z: 293},
    {x: 153, z: -869},
    {x: 124, z: -1122},
    {x: -261, z: -1031},
    {x: 87, z: -583},
    {x: -288, z: -625},
    {x: 265, z: -501},
    {x: -850, z: -134},
    {x: -1094, z: -74},
    {x: -576, z: -190},
    {x: -605, z: 98},
    {x: -964, z: 293},
    {x: -1084, z: -554},
    {x: -899, z: -628},
    {x: -589, z: -716},
    {x: -992, z: -962},
    {x: -754, z: -1087}
  ];
  var ATTACKANIMATIONS = {
    'broxss': Examples.smoke,
    'brosqli': Examples.smoke,
    'brotunnel': Examples.smoke
  };
  var WIDTH  = window.innerWidth;
  var HEIGHT = window.innerHeight;
  var SPEED = 0.01;
  var OPENNAV = 'cbp-spmenu-open';

  var geometry = null;
  var scene = null;
  var camera = null;
  var renderer = null;
  var material = null;
  var json = null;
  var geometry = null;
  var controls = null;
  var clock = null;
  var mesh = null;
  var spotlight = null;
  var loader = null;
  var projector = null;
  var sceneObjects = [];
  var attackedIds = 0;
  var cssRenderer = null;
  var intersectedObject = null;
  var engine = null;
  var enginesList = [];
  var world = {};
  var jsonData = {};

  function init() {
    Meteor.call('getVrJson', function(err, response) {
      jsonData = JSON.parse(response);
      initMeshes();
      setCamera();
      setLights();
      setRenderers();
      setStats();

      document.getElementById("container").appendChild(renderer.domElement);
      document.getElementById("container").appendChild(cssRenderer.domElement);
    });
  }

  function initVariables() {
    clock = new THREE.Clock();
    scene = new THREE.Scene();
    loader = new THREE.JSONLoader();
    camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10000);
    sceneControls = new THREE.TrackballControls(camera);
    spotLight = new THREE.SpotLight(0xffffff, 1);
    renderer = new THREE.WebGLRenderer({ antialias: true });
    projector = new THREE.Projector();
    cssRenderer = new THREE.CSS3DRenderer();
    controls = new THREE.OrbitControls(camera);
    stats = new Stats();
    Session.set('filterType', 'all');
  }

  function restartEngine(parameters, x, z) {
    parameters.positionBase.x=x;
    parameters.positionBase.z=z;

    engine = new ParticleEngine();
    engine.setValues(parameters);
    engine.initialize(scene);

    enginesList.push(engine);
  }

  function setStats() {
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.bottom = '0px';
    stats.domElement.style.zIndex = 100;
  }

  function setCamera() {
    camera.position.set(-39.52908903855581, -4.352138336979161, 40.70626794923796);
    var lookAt = { x: -30.52908903855581, y: -4.352138336979161, z: 37.70626794923796 }
    camera.lookAt(lookAt);
  }

  function setRenderers() {
    renderer.setSize(WIDTH, HEIGHT);
    renderer.shadowMapEnabled = true;  // enable shadows

    cssRenderer.setSize(WIDTH, HEIGHT);
    cssRenderer.domElement.style.position = 'absolute';
    cssRenderer.domElement.style.top = 0;
  }

  function setLights() {
    spotLight.position.set( 100, 10000, 100 );
    scene.add(spotLight);
    spotLight.castShadow = true;
  }

  function initMeshes() {
    json = loader.parse(jsonData);
    geometry = json.geometry;
    material = new THREE.MeshPhongMaterial(json.materials);
    for (var i = 0; i < MESHPROPERTIES.count; i++) {
      mesh = new THREE.Mesh(geometry, material);
      mesh.scale.x = mesh.scale.y = mesh.scale.z = 50.75;
      mesh.translation = THREE.GeometryUtils.center(geometry);
      mesh.castShadow = true;
      mesh.position.copy(MESHPROPERTIES.position[i]);
      mesh.rotation.y = MESHPROPERTIES.rotationY[i];
      scene.add(mesh);
    }
  }

  function update() {
    controls.update();
    stats.update();
    enginesList.forEach(function(engine) {
      engine.update(0.01*0.5);
    });
  }

  function render() {
    requestAnimationFrame(render);
    update();
    renderer.render(scene, camera);
    cssRenderer.render(scene, camera);
  }

  function parsedb() {
    Meteor.subscribe("attackers-summary-yash", onReady = function() {

      // var filterType = Session.get('filterType');
      attackers.find().forEach(function(element) {
        // TODO: Take care of timestamp
        element.events.forEach(function(evt) {
          var evtHost = evt.documentsource.details.host;
          var doc = evt.documentsource;
          doc.id = evt.documentid;
          doc.read = evt.read;
          if (evtHost == undefined) {
            return;
          }

          // if (filterType === 'read') {
          //   if (!doc.read || doc.read == undefined) {
          //     return;
          //   }
          // }
          // if (filterType === 'unread') {
          //   if (doc.read) {
          //     return;
          //   }
          // }
          if (world[evtHost]) {
            world[evtHost].push(doc);
          } else {
            world[evtHost] = [doc];
            // TODO: Is the below line required
            world[evtHost].rank = attackedIds++;
            // world[evtHost].host = evtHost || 'hard-coded';
          }
        });
      });

      console.log(world);

      var attacks = Object.keys(world).sort(function(prev, current) {
        return world[current].length - world[prev].length;
      });
      console.log('attacks', attacks);
      attacks.forEach(function(host, index) {
        var attackRank = world[host].rank;
        // Create enclosing transparent sphere
        var sphereGeometry = new THREE.SphereGeometry(70);
        var sphereMaterial = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 });
        // var sphereMaterial = new THREE.MeshBasicMaterial();
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.x = RANKCOORDINATES[attackRank].x;
        sphere.position.z = RANKCOORDINATES[attackRank].z;
        sphere.name = "EnclosingSphere" + attackRank;
        sphere.rank = attackRank;
        sphere.host = host;
        sphere.attacks = [];

        world[host].forEach(function(attack) {
          if (typeof attack === "object") {
            attackType = attack.category;
            console.log("attacks - ", attackType);
            sphere.attacks.push(attack);
            if (Object.keys(ATTACKANIMATIONS).indexOf(attackType) > -1) {
              mappedAttack = ATTACKANIMATIONS[attackType];
              restartEngine(mappedAttack, RANKCOORDINATES[index].x, RANKCOORDINATES[index].z);
              console.log(RANKCOORDINATES[index].x, RANKCOORDINATES[index].z);
            }
          }
        });

        sceneObjects.push(sphere);
        scene.add(sphere);
      });

    });
  }

  Template.vr.created = function () {
    initVariables();
  };

  Template.vr.rendered = function () {
    init();
    render();
    parsedb();
  };//end template.attackers.rendered

  Template.vr.helpers({
    hostAttacks: function() {
      var hostAttacks = Session.get('hostAttacks');
      var isReadOrUnread = Session.get('filterType');
      var filteredAttacks = { attacks: [] };
      if (hostAttacks) {
        filteredAttacks.rank = hostAttacks.rank;
        filteredAttacks.host = hostAttacks.host;
        hostAttacks.attacks.forEach(function(attack, index) {
          attack.index = index;
          if (isReadOrUnread === 'read' && attack.read) {
            filteredAttacks.attacks.push(attack);
          }
          else if (isReadOrUnread === 'unread' && !attack.read) {
            filteredAttacks.attacks.push(attack);
          }
          else if (isReadOrUnread === 'all') {
            filteredAttacks.attacks.push(attack);
          }
        });
        return filteredAttacks;
      }
    },
    attackDetails: function() {
      return Session.get('attackDetails');
    },
    displayIndex: function(index) {
      return index + 1
    }
  });

  Template.vr.events({

    "click #container": function(e) {
      var mouse = {
        x: (e.clientX / WIDTH)*2 - 1,
        y: (e.clientY / HEIGHT)*2 - 1
      };
      var mouseVector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
      projector.unprojectVector(mouseVector, camera);
      var raycaster = new THREE.Raycaster(camera.position, mouseVector.sub( camera.position).normalize() );
      var intersects = raycaster.intersectObjects(sceneObjects, true);
      var sideNav = $('#attack-sidenav');
      console.log(intersects);

      if (intersects.length) {
        intersects.forEach(function(intersect) {
          // console.log(intersect);
          var attackRank = intersect.object.rank;
          var attackRegion = intersect.object.host;
          var attacks = intersect.object.attacks;
          if (typeof attackRank !== "undefined") {
            var sessionAttackObj = {
              host: attackRegion,
              rank: attackRank,
              attacks: attacks
            };
            Session.set('hostAttacks', sessionAttackObj);
            // Open the nav if not already opened
            if (!sideNav.hasClass(OPENNAV)) {
              sideNav.addClass(OPENNAV);
            }
          }
        });
      }
      else if(sideNav.hasClass(OPENNAV)) {
        sideNav.removeClass(OPENNAV);
      }
    },

  "mousemove": function(e) {
      var mouse = {
        x: (e.clientX / WIDTH)*2 - 1,
        y: (e.clientY / HEIGHT)*2 - 1
      };
      var mouseVector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
      projector.unprojectVector(mouseVector, camera);
      var raycaster = new THREE.Raycaster(camera.position, mouseVector.sub( camera.position).normalize() );
      var intersects = raycaster.intersectObjects(sceneObjects, true);

      $('body').removeClass('mousepointer');

      if (intersects.length) {
        intersects.forEach(function(intersect) {
          var attackRank = intersect.object.rank;
          if (typeof attackRank !== "undefined") {
            $('body').addClass('mousepointer');
          }
        });
      }
    },

    "change .cbox": function(event) {
      var isChecked = $('.cbox').is(':checked');
      var attackId = Session.get('attackDetails').id;
      var setModifier = { $set: {}};
      var element = attackers.findOne({"events.documentid": attackId});

      element.events.forEach(function(evt, index) {
        if (evt.documentid === attackId) {
          setModifier.$set['events.' + index + '.read'] = isChecked;
          attackers.update({'_id': element._id}, setModifier);
        }
      });
    },

    "change .filter-type": function (event) {
      Session.set('filterType', $('.filter-radios input[name=filter-type]:checked').val());
      // filterAttacksType();
    },

    "click .attacks-list-item": function(event) {
      var attackIndex = event.target.getAttribute('data-index');
      var hostName = document.getElementById('attacks-host').innerText;
      var attackDetails = world[hostName][attackIndex];
      Session.set('attackDetails', attackDetails);
      $('#specific-attack-details').slideToggle();
      $('#attacks-list').slideToggle();
    },

    "click .back-button": function() {
      $('#specific-attack-details').slideToggle();
      $('#attacks-list').slideToggle();
    },

    "click .blockip": function(e,t){
      Session.set('blockIPipaddress',($(e.target).attr('data-ipaddress')));
      //disable and hook to re-enable the scene controls so they don't grab the mouse and use it
      sceneControls.enabled = false;
      controls.enabled = false;
      $('#modalBlockIPWindow').on('hidden', function () {
          sceneControls.enabled=true;
          controls.enabled = true;
      });
      $('#modalBlockIPWindow').modal();
    },

  });

  Template.vr.destroyed = function () {
    container.removeChild(renderer.domElement);
    scene = null;
    camera = null;
    renderer = null;
    WIDTH  = window.innerWidth;
    HEIGHT = window.innerHeight;
    SPEED = 0.01;
  };//end template.attackers.destroyed

}
<commit_msg>Perhaps its not a crime to use _ in JS<commit_after>/*
 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/.
 Copyright (c) 2014 Mozilla Corporation

 Contributors:
 Jeff Bryner jbryner@mozilla.com
 Anthony Verez averez@mozilla.com
 Sanchit Kapoor sanchitlucknow@gmail.com
 Yash Mehrotra yashmehrotra95@gmail.com
 Avijit Gupta 526avijit@gmail.com
 */

if (Meteor.isClient) {

  var MESH_PROPERTIES = {
    count: 4,
    position: [
      {x: 0, y: 0, z: 0},
      {x: -800, y: -20, z: 0},
      {x: -800, y: -20, z: -800},
      {x: 0 , y: -20, z: -800}
    ],
    rotationY: [
      0,
      0,
      Math.PI/2,
      Math.PI/2
    ]
  };

  var RANK_COORDINATES = [
    {x: -286, z: -115},
    {x: -15, z: 11},
    {x: -234, z: 232},
    {x: -69, z: -142},
    {x: 124, z: 293},
    {x: 153, z: -869},
    {x: 124, z: -1122},
    {x: -261, z: -1031},
    {x: 87, z: -583},
    {x: -288, z: -625},
    {x: 265, z: -501},
    {x: -850, z: -134},
    {x: -1094, z: -74},
    {x: -576, z: -190},
    {x: -605, z: 98},
    {x: -964, z: 293},
    {x: -1084, z: -554},
    {x: -899, z: -628},
    {x: -589, z: -716},
    {x: -992, z: -962},
    {x: -754, z: -1087}
  ];
  var ATTACK_ANIMATIONS = {
    'broxss': Examples.smoke,
    'brosqli': Examples.smoke,
    'brotunnel': Examples.smoke
  };

  var WIDTH  = window.innerWidth;
  var HEIGHT = window.innerHeight;
  var SPEED = 0.01;
  var OPENNAV = 'cbp-spmenu-open';

  var geometry = null;
  var scene = null;
  var camera = null;
  var renderer = null;
  var material = null;
  var json = null;
  var controls = null;
  var clock = null;
  var mesh = null;
  var spotlight = null;
  var loader = null;
  var projector = null;
  var sceneObjects = [];
  var attackedIds = 0;
  var cssRenderer = null;
  var intersectedObject = null;
  var engine = null;
  var enginesList = [];
  var world = {};
  var jsonData = {};

  function init() {
    Meteor.call('getVrJson', function(err, response) {
      jsonData = JSON.parse(response);
      initMeshes();
      setCamera();
      setLights();
      setRenderers();
      setStats();

      document.getElementById("container").appendChild(renderer.domElement);
      document.getElementById("container").appendChild(cssRenderer.domElement);
    });
  }

  function initVariables() {
    clock = new THREE.Clock();
    scene = new THREE.Scene();
    loader = new THREE.JSONLoader();
    camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10000);
    sceneControls = new THREE.TrackballControls(camera);
    spotLight = new THREE.SpotLight(0xffffff, 1);
    renderer = new THREE.WebGLRenderer({ antialias: true });
    projector = new THREE.Projector();
    cssRenderer = new THREE.CSS3DRenderer();
    controls = new THREE.OrbitControls(camera);
    stats = new Stats();
    Session.set('filterType', 'all');
  }

  function restartEngine(parameters, x, z) {
    parameters.positionBase.x=x;
    parameters.positionBase.z=z;

    engine = new ParticleEngine();
    engine.setValues(parameters);
    engine.initialize(scene);

    enginesList.push(engine);
  }

  function setStats() {
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.bottom = '0px';
    stats.domElement.style.zIndex = 100;
  }

  function setCamera() {
    camera.position.set(-39.52908903855581, -4.352138336979161, 40.70626794923796);
    var lookAt = { x: -30.52908903855581, y: -4.352138336979161, z: 37.70626794923796 };
    camera.lookAt(lookAt);
  }

  function setRenderers() {
    renderer.setSize(WIDTH, HEIGHT);
    renderer.shadowMapEnabled = true;  // enable shadows

    cssRenderer.setSize(WIDTH, HEIGHT);
    cssRenderer.domElement.style.position = 'absolute';
    cssRenderer.domElement.style.top = 0;
  }

  function setLights() {
    spotLight.position.set( 100, 10000, 100 );
    scene.add(spotLight);
    spotLight.castShadow = true;
  }

  function initMeshes() {
    json = loader.parse(jsonData);
    geometry = json.geometry;
    material = new THREE.MeshPhongMaterial(json.materials);
    for (var i = 0; i < MESH_PROPERTIES.count; i++) {
      mesh = new THREE.Mesh(geometry, material);
      mesh.scale.x = mesh.scale.y = mesh.scale.z = 50.75;
      mesh.translation = THREE.GeometryUtils.center(geometry);
      mesh.castShadow = true;
      mesh.position.copy(MESH_PROPERTIES.position[i]);
      mesh.rotation.y = MESH_PROPERTIES.rotationY[i];
      scene.add(mesh);
    }
  }

  function update() {
    controls.update();
    stats.update();
    enginesList.forEach(function(engine) {
      engine.update(0.01*0.5);
    });
  }

  function render() {
    requestAnimationFrame(render);
    update();
    renderer.render(scene, camera);
    cssRenderer.render(scene, camera);
  }

  function parsedb() {
    Meteor.subscribe("attackers-summary-yash", onReady = function() {

      // var filterType = Session.get('filterType');
      attackers.find().forEach(function(element) {
        // TODO: Take care of timestamp
        element.events.forEach(function(evt) {
          var evtHost = evt.documentsource.details.host;
          var doc = evt.documentsource;
          doc.id = evt.documentid;
          doc.read = evt.read;
          if (evtHost == undefined) {
            return;
          }

          // if (filterType === 'read') {
          //   if (!doc.read || doc.read == undefined) {
          //     return;
          //   }
          // }
          // if (filterType === 'unread') {
          //   if (doc.read) {
          //     return;
          //   }
          // }
          if (world[evtHost]) {
            world[evtHost].push(doc);
          } else {
            world[evtHost] = [doc];
            // TODO: Is the below line required
            world[evtHost].rank = attackedIds++;
            // world[evtHost].host = evtHost || 'hard-coded';
          }
        });
      });

      console.log(world);

      var attacks = Object.keys(world).sort(function(prev, current) {
        return world[current].length - world[prev].length;
      });
      console.log('attacks', attacks);
      attacks.forEach(function(host, index) {
        var attackRank = world[host].rank;
        // Create enclosing transparent sphere
        var sphereGeometry = new THREE.SphereGeometry(70);
        var sphereMaterial = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 });
        // var sphereMaterial = new THREE.MeshBasicMaterial();
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.x = RANK_COORDINATES[attackRank].x;
        sphere.position.z = RANK_COORDINATES[attackRank].z;
        sphere.name = "EnclosingSphere" + attackRank;
        sphere.rank = attackRank;
        sphere.host = host;
        sphere.attacks = [];

        world[host].forEach(function(attack) {
          if (typeof attack === "object") {
            attackType = attack.category;
            console.log("attacks - ", attackType);
            sphere.attacks.push(attack);
            if (Object.keys(ATTACK_ANIMATIONS).indexOf(attackType) > -1) {
              mappedAttack = ATTACK_ANIMATIONS[attackType];
              restartEngine(mappedAttack, RANK_COORDINATES[index].x, RANK_COORDINATES[index].z);
              console.log(RANK_COORDINATES[index].x, RANK_COORDINATES[index].z);
            }
          }
        });

        sceneObjects.push(sphere);
        scene.add(sphere);
      });

    });
  }

  Template.vr.created = function () {
    initVariables();
  };

  Template.vr.rendered = function () {
    init();
    render();
    parsedb();
  };//end template.attackers.rendered

  Template.vr.helpers({
    hostAttacks: function() {
      var hostAttacks = Session.get('hostAttacks');
      var isReadOrUnread = Session.get('filterType');
      var filteredAttacks = { attacks: [] };
      if (hostAttacks) {
        filteredAttacks.rank = hostAttacks.rank;
        filteredAttacks.host = hostAttacks.host;
        hostAttacks.attacks.forEach(function(attack, index) {
          attack.index = index;
          if (isReadOrUnread === 'read' && attack.read) {
            filteredAttacks.attacks.push(attack);
          }
          else if (isReadOrUnread === 'unread' && !attack.read) {
            filteredAttacks.attacks.push(attack);
          }
          else if (isReadOrUnread === 'all') {
            filteredAttacks.attacks.push(attack);
          }
        });
        return filteredAttacks;
      }
    },
    attackDetails: function() {
      return Session.get('attackDetails');
    },
    displayIndex: function(index) {
      return index + 1
    }
  });

  Template.vr.events({

    "click #container": function(e) {
      var mouse = {
        x: (e.clientX / WIDTH)*2 - 1,
        y: (e.clientY / HEIGHT)*2 - 1
      };
      var mouseVector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
      projector.unprojectVector(mouseVector, camera);
      var raycaster = new THREE.Raycaster(camera.position, mouseVector.sub( camera.position).normalize() );
      var intersects = raycaster.intersectObjects(sceneObjects, true);
      var sideNav = $('#attack-sidenav');
      console.log(intersects);

      if (intersects.length) {
        intersects.forEach(function(intersect) {
          // console.log(intersect);
          var attackRank = intersect.object.rank;
          var attackRegion = intersect.object.host;
          var attacks = intersect.object.attacks;
          if (typeof attackRank !== "undefined") {
            var sessionAttackObj = {
              host: attackRegion,
              rank: attackRank,
              attacks: attacks
            };
            Session.set('hostAttacks', sessionAttackObj);
            // Open the nav if not already opened
            if (!sideNav.hasClass(OPENNAV)) {
              sideNav.addClass(OPENNAV);
            }
          }
        });
      }
      else if(sideNav.hasClass(OPENNAV)) {
        sideNav.removeClass(OPENNAV);
      }
    },

  "mousemove": function(e) {
      var mouse = {
        x: (e.clientX / WIDTH)*2 - 1,
        y: (e.clientY / HEIGHT)*2 - 1
      };
      var mouseVector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
      projector.unprojectVector(mouseVector, camera);
      var raycaster = new THREE.Raycaster(camera.position, mouseVector.sub( camera.position).normalize());
      var intersects = raycaster.intersectObjects(sceneObjects, true);

      $('body').removeClass('mousepointer');

      if (intersects.length) {
        intersects.forEach(function(intersect) {
          var attackRank = intersect.object.rank;
          if (typeof attackRank !== "undefined") {
            $('body').addClass('mousepointer');
          }
        });
      }
    },

    "change .cbox": function(event) {
      var isChecked = $('.cbox').is(':checked');
      var attackId = Session.get('attackDetails').id;
      var setModifier = { $set: {}};
      var element = attackers.findOne({"events.documentid": attackId});

      element.events.forEach(function(evt, index) {
        if (evt.documentid === attackId) {
          setModifier.$set['events.' + index + '.read'] = isChecked;
          attackers.update({'_id': element._id}, setModifier);
        }
      });
    },

    "change .filter-type": function (event) {
      Session.set('filterType', $('.filter-radios input[name=filter-type]:checked').val());
      // filterAttacksType();
    },

    "click .attacks-list-item": function(event) {
      var attackIndex = event.target.getAttribute('data-index');
      var hostName = document.getElementById('attacks-host').innerText;
      var attackDetails = world[hostName][attackIndex];
      Session.set('attackDetails', attackDetails);
      $('#specific-attack-details').slideToggle();
      $('#attacks-list').slideToggle();
    },

    "click .back-button": function() {
      $('#specific-attack-details').slideToggle();
      $('#attacks-list').slideToggle();
    },

    "click .blockip": function(e,t) {
      Session.set('blockIPipaddress',($(e.target).attr('data-ipaddress')));
      //disable and hook to re-enable the scene controls so they don't grab the mouse and use it
      sceneControls.enabled = false;
      controls.enabled = false;
      $('#modalBlockIPWindow').on('hidden', function () {
          sceneControls.enabled=true;
          controls.enabled = true;
      });
      $('#modalBlockIPWindow').modal();
    },

  });

  Template.vr.destroyed = function () {
    container.removeChild(renderer.domElement);
    scene = null;
    camera = null;
    renderer = null;
    WIDTH  = window.innerWidth;
    HEIGHT = window.innerHeight;
    SPEED = 0.01;
  };//end template.attackers.destroyed

}
<|endoftext|>


<commit_before>const utils = require('../utils');
const fetch = require('node-fetch');

function middleware(log, endpoint, preferApi, apiDataPath, rootPath) {
  return function endpointMiddleware(req, res, next) {
    if (endpoint !== null) {
      const params = utils.getQueryString(req);
      const allowedMethods = utils.reallyAllowedMethods(
        req,
        params,
        apiDataPath,
        rootPath
      );
      if (allowedMethods.length && !preferApi) {
        res.set('Saray-Stubbed', true);
        log.info(`Stubbing API call ${req.method} ${req.path} ${params}`);
        next();
      } else {
        res.set('Saray-Stubbed', false);
        log.info(`Not stubbing API call ${req.method} ${req.path} ${params}`);

        const headers = Object.assign({}, req.headers);
        delete headers.host;
        const opts = {
          method: req.method,
          headers: headers
        };

        if (req.method === 'POST' || req.method === 'PATCH') {
          opts.body = JSON.stringify(req.body);
        }

        const strippedPath = utils.stripRootPath(module.exports.rootPath, req.path);
        log.info(`Fetching API call ${req.method} ${strippedPath} from ${endpoint}`);
        fetch(endpoint + strippedPath, opts).then(function(response) {
          const contentType = response.headers.get('content-type');
          if (contentType) {
            res.set('Content-type', response.headers.get('content-type'));
          }
          log.info(`Fetched API call ${req.method} ${strippedPath} from ${endpoint} with status ${response.status}`);
          res.status(response.status);
          return response.text();
        }).then(function(text) {
          res.send(text);
        }).catch(function() {
          log.info(`Error with API call ${req.method} ${req.path} from ${endpoint}`);
          res.sendStatus(404);
        });
      }
    } else {
      res.set('Saray-Stubbed', true);
      log.info(`Stubbing API call ${req.method} ${req.path} with no endpoint specified`);
      next();
    }
  };
}

module.exports = middleware;<commit_msg>Fix missing module.exports in endpoint middleware<commit_after>const utils = require('../utils');
const fetch = require('node-fetch');

function middleware(log, endpoint, preferApi, apiDataPath, rootPath) {
  return function endpointMiddleware(req, res, next) {
    if (endpoint !== null) {
      const params = utils.getQueryString(req);
      const allowedMethods = utils.reallyAllowedMethods(
        req,
        params,
        apiDataPath,
        rootPath
      );
      if (allowedMethods.length && !preferApi) {
        res.set('Saray-Stubbed', true);
        log.info(`Stubbing API call ${req.method} ${req.path} ${params}`);
        next();
      } else {
        res.set('Saray-Stubbed', false);
        log.info(`Not stubbing API call ${req.method} ${req.path} ${params}`);

        const headers = Object.assign({}, req.headers);
        delete headers.host;
        const opts = {
          method: req.method,
          headers: headers
        };

        if (req.method === 'POST' || req.method === 'PATCH') {
          opts.body = JSON.stringify(req.body);
        }

        const strippedPath = utils.stripRootPath(rootPath, req.path);
        log.info(`Fetching API call ${req.method} ${strippedPath} from ${endpoint}`);
        fetch(endpoint + strippedPath, opts).then(function(response) {
          const contentType = response.headers.get('content-type');
          if (contentType) {
            res.set('Content-type', response.headers.get('content-type'));
          }
          log.info(`Fetched API call ${req.method} ${strippedPath} from ${endpoint} with status ${response.status}`);
          res.status(response.status);
          return response.text();
        }).then(function(text) {
          res.send(text);
        }).catch(function() {
          log.info(`Error with API call ${req.method} ${req.path} from ${endpoint}`);
          res.sendStatus(404);
        });
      }
    } else {
      res.set('Saray-Stubbed', true);
      log.info(`Stubbing API call ${req.method} ${req.path} with no endpoint specified`);
      next();
    }
  };
}

module.exports = middleware;<|endoftext|>


<commit_before>export const LOADPEOPLE_START = 'react/people/LOADPEOPLE_START';
export const LOADPEOPLE_COMPLETE = 'react/people/LOADPEOPLE_COMPLETE';
export const LOADPEOPLE_ERROR = 'react/people/LOADPEOPLE_ERROR';

export default function reducer(state = {}, action = {}) {
  switch (action.type) {
    case LOADPEOPLE_START:
      return {
        ...state,
        loading: true,
        error: null
      };
    case LOADPEOPLE_COMPLETE:
      return {
        ...state,
        loading: false,
        people: action.result
      };
    case LOADPEOPLE_ERROR:
      return {
        ...state,
        loading: false,
        error: 'An error occured loading people. This error is thrown randomly for demo purposes.'
      };
    default:
      if(!state) return {}; // we want to always have at least an empty object
      return state;
  }
}

export function loadPeople() {
  return {
    types: [LOADPEOPLE_START, LOADPEOPLE_COMPLETE, LOADPEOPLE_ERROR],
    promise: (client) => client.post('/api/people/load')
  };
}
<commit_msg>Space after if statement fixing Gulp build error<commit_after>export const LOADPEOPLE_START = 'react/people/LOADPEOPLE_START';
export const LOADPEOPLE_COMPLETE = 'react/people/LOADPEOPLE_COMPLETE';
export const LOADPEOPLE_ERROR = 'react/people/LOADPEOPLE_ERROR';

export default function reducer(state = {}, action = {}) {
  switch (action.type) {
    case LOADPEOPLE_START:
      return {
        ...state,
        loading: true,
        error: null
      };
    case LOADPEOPLE_COMPLETE:
      return {
        ...state,
        loading: false,
        people: action.result
      };
    case LOADPEOPLE_ERROR:
      return {
        ...state,
        loading: false,
        error: 'An error occured loading people. This error is thrown randomly for demo purposes.'
      };
    default:
      if (!state) return {}; // we want to always have at least an empty object
      return state;
  }
}

export function loadPeople() {
  return {
    types: [LOADPEOPLE_START, LOADPEOPLE_COMPLETE, LOADPEOPLE_ERROR],
    promise: (client) => client.post('/api/people/load')
  };
}
<|endoftext|>