<commit_before>var video;
var canvas;
var localMediaStream = null;
var isStopped = true;
var photoRegex = /^\S*\/photo\/\w{8}_.*$/;
var photoImg;
var photoInput;
var userProfileData;
var themeSelector;
var changeProfileForm;
var notificationInput;
var cacheMessagesInput;
var embeddedYoutubeInput;
var highlightCodeInput;
var logsInput;
var photoSrc = null;

function initChangeProfile() {
	photoImg = $('photoImg');
	video = $('changeProfileVideo');
	canvas = $('canvasBuffer');
	photoInput = $('id_photo');
	userProfileData = $('userProfileData');
	themeSelector = $('themeSelector');
	changeProfileForm = $('changeProfileForm');
	notificationInput = $('id_notifications');
	cacheMessagesInput = $('id_cache_messages');
	embeddedYoutubeInput = $('id_embedded_youtube');
	highlightCodeInput = $('id_highlight_code');
	logsInput = $('id_logs');
	var item = localStorage.getItem('theme');
	video.addEventListener('click', takeSnapshot, false);
	if (item != null) {
		themeSelector.value = item;
		/*TODO $* to var*/
	}
	photoImg.addEventListener('drop', dropPhoto, false);
	var events = ["dragenter", "dragstart", "dragend", "dragleave", "dragover", "drag", "drop"];
	for (var i = 0, iLen = events.length; i < iLen; i++) {
		photoImg.addEventListener(events[i], preventDefault, false);
	}
	photoInput.onchange = photoInputChanged;
	if (isDateMissing()) {
		logger.warn("Browser doesn't support html5 input type date, trying to load javascript datepicker")();
		doGet(PICKADAY_CSS_URL);
		doGet(MOMENT_JS_URL, function () {
			// load pikaday only after moment.js
			doGet(PICKADAY_JS_URL, function () {
				var picker = new Pikaday({
					field: $('id_birthday'),
					format: 'YYYY-MM-DD', // DATE_INPUT_FORMATS_JS
					firstDay: 1,
					maxDate: new Date(),
					yearRange: [1930, 2010]
				});
				logger.info("pikaday date picker has been loaded")();
			});
		})
	}
	if (!navigator.getUserMedia) {
		logger.warn('Browser doesnt support capturing video, skipping photo snapshot')();
	}
	CssUtils.hideElement(video);
}

function dropPhoto(e) {
	e.preventDefault();
	var ok = setPhotoFromReader(e.dataTransfer.files[0], 'drop');
	if (ok) {
		photoInput.value = '';
	}
}

function photoInputChanged(e) {
	var ok = setPhotoFromReader(e.target.files[0], 'input');
	if (!ok) {
		photoInput.value = '';
	}
}

function setPhotoFromReader(file, type) {
	if (file && file.type && file.type.match(/image.*/)) {
		var reader = new window.FileReader();
		reader.onload = function (e) {
			photoImg.src = e.target.result;
			growlSuccess("Photo has been rendered, click save to apply it");
		};
		reader.readAsDataURL(file);
		photoSrc = type;
		return true;
	} else {
		growlError("Invalid file type " + file.type);
		return false;
	}
}

function startSharingVideo() {
	var constraints = {video: true};

	function successCallback(localMediaStream) {
		window.stream = localMediaStream; // stream available to console
		video.src = window.URL.createObjectURL(localMediaStream);
		video.play();
	}

	function errorCallback(error) {
		logger.info("navigator.getUserMedia error: {}", error)();
	}

	navigator.getUserMedia(constraints, successCallback, errorCallback);
}

function takeSnapshot() {
	if (localMediaStream) {
		var ctx = canvas.getContext('2d');
		canvas.width = video.videoWidth;
		canvas.height = video.videoHeight;
		ctx.drawImage(video, 0, 0);
		// "image/webp" works in Chrome.
		// Other browsers will fall back to image/png.
		photoImg.src = canvas.toDataURL('image/webp');
		photoSrc = 'canvas';
		photoInput.value = '';
		growlInfo('Image has been set. Click on "Finish" to hide video');
	}
}

function startCapturingVideo(button) {
	if (isStopped) {
		// Not showing vendor prefixes or code that works cross-browser.
		navigator.getUserMedia({video: true}, function (stream) {
			video.src = window.URL.createObjectURL(stream);
			localMediaStream = stream;
			CssUtils.showElement(video);
			photoImg.addEventListener('click', takeSnapshot, false);
			photoImg.style.cursor = 'pointer';
			button.value = 'Finish';
			isStopped = false;
			growlInfo("Click on your video to take a photo")
		}, function (e) {
			logger.error('Error while trying to capture a picture "{}"', e.message || e.name)();
			growlError('Unable to use your webcam because "{}"'.format(e.message || e.name ));
		});
	} else if (!isStopped) {
		if (localMediaStream.stop) {
			localMediaStream.stop();
		} else {
			 localMediaStream.getVideoTracks()[0].stop();
		}
		photoImg.removeEventListener('click', takeSnapshot);
		photoImg.style.cursor = '';
		button.value = 'Renew the photo';
		growlInfo("To apply photo click on save");
		CssUtils.hideElement(video);

		CssUtils.showElement(userProfileData);
		isStopped = true;
	}
}

function preventDefault(e) {
	 e.preventDefault();
}

function setJsState() {
	var options = [];
	localStorage.setItem('theme', themeSelector.value);
	setTheme();
	notifications = notificationInput.checked; // global var
	cacheMessages = cacheMessagesInput.checked; // global var
	window.embeddedYoutube = embeddedYoutubeInput.checked; // global var
	window.highlightCode = highlightCodeInput.checked; // global var
	if (!cacheMessages) {
		storage.clearStorage();
	}
	if (notifications) {
		notifier.tryAgainRegisterServiceWorker();
	}
	if (window.highlightCode) {
		doGet(HIGHLIGHT_JS_URL, function() {
			Utils.highlightCode(document.body);
		});
	}
	window.loggerFactory.logsEnabled = logsInput.checked;
}

function saveProfile(event) {
	event.preventDefault();
	var image = null;
	var params = null;
	if (photoSrc == 'canvas' || photoSrc == 'drop') {
		if (photoSrc == 'canvas') {
			image = canvas.toDataURL("image/png");
		} else if (photoSrc == 'drop') {
			image = photoImg.src;
		}
		params = {base64_image: image};
	}
	ajaxShow();
	doPost('/profile', params, function (response) {
		if (response.match(photoRegex)) {
			photoImg.onload = ajaxHide;
			photoImg.onerror = ajaxHide;
			if (typeof photoImg.onload !== 'function' || typeof photoImg.onerror !== 'function' ) {
				ajaxHide();
			}
			photoImg.src = response;
			snapshot = false;
			response = RESPONSE_SUCCESS;
		} else {
			ajaxHide();
		}
		if (response === RESPONSE_SUCCESS) {
			growlSuccess("Your profile has been successfully updated. Press home icon to return on main page");
			setJsState();
		} else {
			growlError(response);
		}
	}, changeProfileForm);
}

/** Checks whether browser supports html5 input type date */
function isDateMissing() {
	var input = document.createElement('input');
	input.setAttribute('type', 'date');
	var notADateValue = 'not-a-date';
	input.setAttribute('value', notADateValue);
	return input.value === notADateValue;
}
<commit_msg>Removed notificationInput<commit_after>var video;
var canvas;
var localMediaStream = null;
var isStopped = true;
var photoRegex = /^\S*\/photo\/\w{8}_.*$/;
var photoImg;
var photoInput;
var userProfileData;
var themeSelector;
var changeProfileForm;
var cacheMessagesInput;
var embeddedYoutubeInput;
var highlightCodeInput;
var logsInput;
var photoSrc = null;

function initChangeProfile() {
	photoImg = $('photoImg');
	video = $('changeProfileVideo');
	canvas = $('canvasBuffer');
	photoInput = $('id_photo');
	userProfileData = $('userProfileData');
	themeSelector = $('themeSelector');
	changeProfileForm = $('changeProfileForm');
	cacheMessagesInput = $('id_cache_messages');
	embeddedYoutubeInput = $('id_embedded_youtube');
	highlightCodeInput = $('id_highlight_code');
	logsInput = $('id_logs');
	var item = localStorage.getItem('theme');
	video.addEventListener('click', takeSnapshot, false);
	if (item != null) {
		themeSelector.value = item;
		/*TODO $* to var*/
	}
	photoImg.addEventListener('drop', dropPhoto, false);
	var events = ["dragenter", "dragstart", "dragend", "dragleave", "dragover", "drag", "drop"];
	for (var i = 0, iLen = events.length; i < iLen; i++) {
		photoImg.addEventListener(events[i], preventDefault, false);
	}
	photoInput.onchange = photoInputChanged;
	if (isDateMissing()) {
		logger.warn("Browser doesn't support html5 input type date, trying to load javascript datepicker")();
		doGet(PICKADAY_CSS_URL);
		doGet(MOMENT_JS_URL, function () {
			// load pikaday only after moment.js
			doGet(PICKADAY_JS_URL, function () {
				var picker = new Pikaday({
					field: $('id_birthday'),
					format: 'YYYY-MM-DD', // DATE_INPUT_FORMATS_JS
					firstDay: 1,
					maxDate: new Date(),
					yearRange: [1930, 2010]
				});
				logger.info("pikaday date picker has been loaded")();
			});
		})
	}
	if (!navigator.getUserMedia) {
		logger.warn('Browser doesnt support capturing video, skipping photo snapshot')();
	}
	CssUtils.hideElement(video);
}

function dropPhoto(e) {
	e.preventDefault();
	var ok = setPhotoFromReader(e.dataTransfer.files[0], 'drop');
	if (ok) {
		photoInput.value = '';
	}
}

function photoInputChanged(e) {
	var ok = setPhotoFromReader(e.target.files[0], 'input');
	if (!ok) {
		photoInput.value = '';
	}
}

function setPhotoFromReader(file, type) {
	if (file && file.type && file.type.match(/image.*/)) {
		var reader = new window.FileReader();
		reader.onload = function (e) {
			photoImg.src = e.target.result;
			growlSuccess("Photo has been rendered, click save to apply it");
		};
		reader.readAsDataURL(file);
		photoSrc = type;
		return true;
	} else {
		growlError("Invalid file type " + file.type);
		return false;
	}
}

function startSharingVideo() {
	var constraints = {video: true};

	function successCallback(localMediaStream) {
		window.stream = localMediaStream; // stream available to console
		video.src = window.URL.createObjectURL(localMediaStream);
		video.play();
	}

	function errorCallback(error) {
		logger.info("navigator.getUserMedia error: {}", error)();
	}

	navigator.getUserMedia(constraints, successCallback, errorCallback);
}

function takeSnapshot() {
	if (localMediaStream) {
		var ctx = canvas.getContext('2d');
		canvas.width = video.videoWidth;
		canvas.height = video.videoHeight;
		ctx.drawImage(video, 0, 0);
		// "image/webp" works in Chrome.
		// Other browsers will fall back to image/png.
		photoImg.src = canvas.toDataURL('image/webp');
		photoSrc = 'canvas';
		photoInput.value = '';
		growlInfo('Image has been set. Click on "Finish" to hide video');
	}
}

function startCapturingVideo(button) {
	if (isStopped) {
		// Not showing vendor prefixes or code that works cross-browser.
		navigator.getUserMedia({video: true}, function (stream) {
			video.src = window.URL.createObjectURL(stream);
			localMediaStream = stream;
			CssUtils.showElement(video);
			photoImg.addEventListener('click', takeSnapshot, false);
			photoImg.style.cursor = 'pointer';
			button.value = 'Finish';
			isStopped = false;
			growlInfo("Click on your video to take a photo")
		}, function (e) {
			logger.error('Error while trying to capture a picture "{}"', e.message || e.name)();
			growlError('Unable to use your webcam because "{}"'.format(e.message || e.name ));
		});
	} else if (!isStopped) {
		if (localMediaStream.stop) {
			localMediaStream.stop();
		} else {
			 localMediaStream.getVideoTracks()[0].stop();
		}
		photoImg.removeEventListener('click', takeSnapshot);
		photoImg.style.cursor = '';
		button.value = 'Renew the photo';
		growlInfo("To apply photo click on save");
		CssUtils.hideElement(video);

		CssUtils.showElement(userProfileData);
		isStopped = true;
	}
}

function preventDefault(e) {
	 e.preventDefault();
}

function setJsState() {
	var options = [];
	localStorage.setItem('theme', themeSelector.value);
	setTheme();
	cacheMessages = cacheMessagesInput.checked; // global var
	window.embeddedYoutube = embeddedYoutubeInput.checked; // global var
	window.highlightCode = highlightCodeInput.checked; // global var
	if (!cacheMessages) {
		storage.clearStorage();
	}

	if (window.highlightCode) {
		doGet(HIGHLIGHT_JS_URL, function() {
			Utils.highlightCode(document.body);
		});
	}
	window.loggerFactory.logsEnabled = logsInput.checked;
}

function saveProfile(event) {
	event.preventDefault();
	var image = null;
	var params = null;
	if (photoSrc == 'canvas' || photoSrc == 'drop') {
		if (photoSrc == 'canvas') {
			image = canvas.toDataURL("image/png");
		} else if (photoSrc == 'drop') {
			image = photoImg.src;
		}
		params = {base64_image: image};
	}
	ajaxShow();
	doPost('/profile', params, function (response) {
		if (response.match(photoRegex)) {
			photoImg.onload = ajaxHide;
			photoImg.onerror = ajaxHide;
			if (typeof photoImg.onload !== 'function' || typeof photoImg.onerror !== 'function' ) {
				ajaxHide();
			}
			photoImg.src = response;
			snapshot = false;
			response = RESPONSE_SUCCESS;
		} else {
			ajaxHide();
		}
		if (response === RESPONSE_SUCCESS) {
			growlSuccess("Your profile has been successfully updated. Press home icon to return on main page");
			setJsState();
		} else {
			growlError(response);
		}
	}, changeProfileForm);
}

/** Checks whether browser supports html5 input type date */
function isDateMissing() {
	var input = document.createElement('input');
	input.setAttribute('type', 'date');
	var notADateValue = 'not-a-date';
	input.setAttribute('value', notADateValue);
	return input.value === notADateValue;
}
<|endoftext|>


<commit_before>"use strict";

var cp = require('child_process');

  //callback(err, exit, lastline);
module.exports = function(cmd, options, chain){
    if(Array.isArray(options))
      options = { args : options} ;
    options = options || {};

  var ps   = cp.spawn(cmd, options.args || [], options),
      _ret = "";

  ps.on('error', function(err){
    ps.removeAllListeners('close');
    ps.on('close', function(exit) {
      return chain(err, exit);
    });
  });
  ps.stdout.pipe(process.stdout);
  ps.stderr.pipe(process.stderr);
  ps.stdout.on("data", function(data){
    _ret = data.toString();
  });

  ps.on('close', function(exit) {
    return chain(null, exit, _ret);
  });
}

<commit_msg>Fix child_process/passthru implementation (force stdio inherit)<commit_after>"use strict";

var cp = require('child_process');

  //callback(err, exit, lastline);
module.exports = function(cmd, options, chain){
  if(Array.isArray(options))
    options = { args : options} ;

  options = options || {};
  options.stdio = ['inherit', 'inherit', 'inherit'];

  var ps   = cp.spawn(cmd, options.args || [], options);

  ps.on('error', function(err){
    ps.removeAllListeners('close');
    ps.on('close', function(exit) {
      return chain(err, exit);
    });
  });

  ps.on('close', function(exit) {
    return chain(null, exit);
  });
}

<|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/.
 */

var simplels = (function() {

    // Our global simplels container is defined via a closure to get
    // privately these shorthands for Components, not available in SeaMonkey,
    // and to prevent re-declarating them in other cases.
    const Cc = Components.classes;
    const Ci = Components.interfaces;
    const Cu = Components.utils;

    return {

    langsvc: null,
    windowLocale: null,

    prefs: null,
    strings: null,
    localeStrings: {languageNames: null, regionNames: null, formats: null},

    toolbarButtonAsWidget: null,
    toolbarButtonId: null,
    isToolbarButtonUpdatePending: false,

    handleEvent: function(event) {
        switch (event.type) {
            case "load":
                window.removeEventListener("load", simplels);
                this.onLoad();
                break;

            case "aftercustomization":
                this.checkIfUpdatingToolbarButton();
                break;

            case "unload":
                this.onUnload();
                break;
        }
    },

    onLoad: function() {
        Cu.import("chrome://simplels/content/modules/general.jsm", this);
        Cu.import("chrome://simplels/content/modules/language.jsm", this);

        this.langsvc = Cc["@nohamelin/sls/language-service;1"]
                       .getService().wrappedJSObject;
        this.windowLocale = this.langsvc.currentLocale;

        this.prefs = Services.prefs.getBranch("extensions.simplels.");
        this.strings = this.getStringBundle("simplels-strings");
        this.localeStrings.languageNames = this.getStringBundle(
                                                "simplels-language-names");
        this.localeStrings.regionNames = this.getStringBundle(
                                                "simplels-region-names");
        this.localeStrings.formats = this.getStringBundle(
                                                "simplels-name-formats");

        this.prefs.QueryInterface(Ci.nsIPrefBranch2);   // COMPAT: Gecko 12-
        this.prefs.addObserver("", this, false);

        Services.obs.addObserver(this, "sls:selected-changed", false);
        Services.obs.addObserver(this, "sls:availables-changed", false);

        // In Firefox 29 and later, the toolbar button will be built with the
        // new mechanisms provided by CustomizableUI, instead of plain XUL.
        this.toolbarButtonAsWidget = !!document.getElementById(
                                                    "simplels-widget-view");
        this.toolbarButtonId = this.toolbarButtonAsWidget ? "simplels-widget"
                                                          : "simplels-button";
        if (this.toolbarButtonAsWidget)
            this.createToolbarButtonAsWidget();

        // Initialize dinamic attributes of commands and toolbar button
        this.updateRestartCommand();
        this.updateMatchCommand();
        this.updateDescriptionsBroadcaster();
        this.updateCustomProviderBroadcaster();

        window.setTimeout(function() {
            simplels.tryToUpdateToolbarButton();
        }, 60);

        // Don't miss to update the toolbar button when it's added from the
        // toolbar palette.
        if ("CustomizableUI" in window) {
            CustomizableUI.addListener(this.customizableListener);
        } else {
            // The next is no longer enougth with CustomizableUI because
            // adding our widget to some area from the palette make it
            // immediately full interactive in any existent browser window
            // except the one displaying yet about:customizing
            window.addEventListener("aftercustomization", simplels);
        }
    },

    onUnload: function() {
        this.prefs.removeObserver("", this);

        Services.obs.removeObserver(this, "sls:selected-changed");
        Services.obs.removeObserver(this, "sls:availables-changed");

        if ("CustomizableUI" in window)
            CustomizableUI.removeListener(this.customizableListener);
    },

    updateRestartCommand: function() {
        let restartCmd = document.getElementById(
                                  "sls_toggleRestartAfterSwitch");
        let wantRestart = this.prefs.getBoolPref("button.restartAfterSwitch");

        restartCmd.setAttribute("checked", wantRestart);
    },

    updateMatchCommand: function() {
        let matchCmd = document.getElementById("sls_enableMatchingOS");
        let wantMatch = this.langsvc.matchingOS;
        let disableMatch = this.langsvc.isMatchingOSLocked;

        matchCmd.setAttribute("checked", wantMatch);
        if (disableMatch)
            matchCmd.setAttribute("disabled", "true");
        else
            matchCmd.removeAttribute("disabled");
    },

    updateDescriptionsBroadcaster: function() {
        let descBcstr = document.getElementById("sls_showLocaleDescriptions");
        let wantDescriptions = this.prefs.getBoolPref(
                                         "button.showDescriptions");

        descBcstr.setAttribute("descriptions", wantDescriptions);
    },

    updateCustomProviderBroadcaster: function() {
        let customBcstr = document.getElementById(
                                   "sls_customLanguagesProvider");
        let resetCmd = document.getElementById("sls_resetLanguagesProvider");
        let customProvider = !this.langsvc.isSelectedProviderDefault;

        customBcstr.setAttribute("hidden", !customProvider);

        if (customProvider)
            resetCmd.removeAttribute("disabled");
        else
            resetCmd.setAttribute("disabled", "true");
    },

    createToolbarButtonAsWidget: function() {
        let widget = CustomizableUI.getWidget("simplels-widget");

        if (!widget || widget.provider === CustomizableUI.PROVIDER_XUL) {
            if (widget)
                CustomizableUI.destroyWidget("simplels-widget");

            CustomizableUI.createWidget({
                id: "simplels-widget",
                type: "view",
                viewId: "simplels-widget-view",
                label: simplels.strings.getString("widget.label"),
                tooltiptext: "Language",   // HACK, see below

                onCreated: function(node) {
                    // HACK: The tooltiptext property was specified only for
                    // to prevent a dummy "Could not localize property..."
                    // warning from the application while building the widget;
                    // this callback is run after that.
                    node.removeAttribute("tooltiptext");
                    node.tooltip = "simplels-button-tooltip";
                }
            });

            // Ensure that, after updating the extension (from 0.6 or older),
            // the widget is placed in the same original position of the XUL
            // button.
            let buttonPlacement = CustomizableUI.getPlacementOfWidget(
                                                 "simplels-button");
            if (buttonPlacement) {
                let migrated = false;
                try {
                    migrated = this.prefs.getBoolPref("button.migrated");
                } catch (e) {}

                if (!migrated) {
                    CustomizableUI.addWidgetToArea("simplels-widget",
                                                   buttonPlacement.area,
                                                   buttonPlacement.position);
                    this.prefs.setBoolPref("button.migrated", true);
                }
            }
        }
    },

    checkIfUpdatingToolbarButton: function() {
        if (this.isToolbarButtonUpdatePending) {
            this.tryToUpdateToolbarButton();
        }
    },

    tryToUpdateToolbarButton: function() {
        let foundButton = this.toolbarButtonAsWidget
                    ? CustomizableUI.getPlacementOfWidget("simplels-widget")
                    : document.getElementById("simplels-button");

        if (foundButton) {
            this.isToolbarButtonUpdatePending = false;

            this.updateToolbarButtonTooltip();

            // The general items of the toolbar button's popup work with
            // broadcasters, so they are always correctly set, and we can
            // to ignore them here.

            let popup = this.toolbarButtonAsWidget
                        ? document.getElementById("simplels-view-body")
                        : document.getElementById("simplels-button-popup");

            this.populatePopupLocales(popup, this.toolbarButtonAsWidget);
        } else {
            this.isToolbarButtonUpdatePending = true;
        }
    },

    updateToolbarButtonTooltip: function() {
        let $ = function(id) {
            return document.getElementById("simplels-button-tooltip-" + id);
        };

        // The tooltip displays information about the next pieces:
        //
        // 1. The locale selected, expected by the user.
        let selectedLocale = this.langsvc.selectedLocale;
        let isSelectedMatchingOS = this.langsvc.matchingOS;
        let isSelectedAvailable = this.langUtils
                                      .isLocaleAvailable(selectedLocale);
        let informAboutSelected = isSelectedMatchingOS || !isSelectedAvailable;

        $("selected").value = this.getLocaleName(selectedLocale);
        $("selected-about").hidden = !informAboutSelected;

        let aboutMsg = "";
        if (informAboutSelected) {
            let matchingMsg = this.strings.getString("tooltip.matchingOS");
            let noAvailableMsg = this.strings.getString("tooltip.noAvailable");

            if (isSelectedMatchingOS && !isSelectedAvailable) {
                aboutMsg = this.strings.getFormattedString("tooltip.both",
                                                           [matchingMsg,
                                                            noAvailableMsg]);
            } else if (isSelectedMatchingOS) {
                aboutMsg = matchingMsg;
            } else {    // !isSelectedAvailable
                aboutMsg = noAvailableMsg;
            }
        }
        $("selected-about").value = aboutMsg;

        // 2. The locale applied to the current window.
        //    It's hidden if its value matches the currently selected locale.
        let areLocalesInConflict = (this.windowLocale !== selectedLocale);

        $("current-row").hidden = !areLocalesInConflict;
        $("current").value = this.getLocaleName(this.windowLocale);

        document.getElementById("simplels-button-tooltip")
                .setAttribute("conflicting", areLocalesInConflict);

        // 3. The locale provider selected.
        //    It's hidden (by a broadcaster) if the default provider is used.
        let selectedProvider = this.langsvc.selectedProvider;
        let selectedProviderName = this.localeStrings
                                       .formats
                                       .getFormattedString("unnamed",
                                                           [selectedProvider]);
        $("provider").value = selectedProviderName;
    },

    populatePopupLocales: function(popup, forWidget) {
        Cu.import("chrome://simplels/content/modules/dom.jsm", this);

        let localeItemType = forWidget ? "toolbarbutton" : "menuitem";
        let localeItemClass = forWidget ? "simplels-locale subviewbutton"
                                        : "simplels-locale";
        let localeItemCallback = function(locale) {
            return function() { simplels.switchTo(locale); };
        };

        let checkedLocale = this.langsvc.userLocale;
        let isCheckedAvailable = this.langUtils
                                     .isLocaleAvailable(checkedLocale);
        let isCheckedIgnored = this.langsvc.matchingOS;
        let isCheckedLocked = this.langsvc.isUserLocaleLocked;
        let isCurrentAvailable = this.langUtils
                                     .isLocaleAvailable(this.windowLocale);

        let locales = this.getWindowRelevantLocales();
        this.langUtils.sortLocales(locales);

        this.domUtils.removeChildrenByClassName(popup, "simplels-locale");

        let popupFragment = document.createDocumentFragment();
        locales.forEach(function(locale) {
            let item = document.createElement(localeItemType);

            item.className = localeItemClass;
            item.setAttribute("type", "radio");
            item.setAttribute("autocheck", "false");
            item.setAttribute("locale", locale);
            item.setAttribute("label", this.getLocaleName(locale));
            item.setAttribute("description", locale);
            item.addEventListener("command", localeItemCallback(locale));

            if (locale === this.windowLocale) {
                item.setAttribute("current", "true");
                if (!isCurrentAvailable)
                    item.setAttribute("disabled", "true");
            }
            if (locale === checkedLocale) {
                if (!isCheckedIgnored)
                    item.setAttribute("checked", "true");
                if (!isCheckedAvailable) {
                    item.setAttribute("disabled", "true");

                    if (isCheckedIgnored && locale !== this.windowLocale)
                        item.setAttribute("hidden", "true");  // Not relevant
                }
            }
            if (isCheckedLocked) {
                item.setAttribute("disabled", "true");
            }

            popupFragment.appendChild(item);
        }, this);

        let matchNode = popup.getElementsByClassName("simplels-matching")[0];
        popup.insertBefore(popupFragment, matchNode.nextSibling);
    },

    updatePopupLocaleTooltip: function() {
        let node = document.tooltipNode;

        // The locale attribute is used here instead of the simplels-locale
        // class for to include the case of an empty locale.
        if (!node.getAttribute("locale"))
            return false;

        document.getElementById("simplels-locale-tooltip-name")
                .value = node.label;
        document.getElementById("simplels-locale-tooltip-tag")
                .value = node.getAttribute("locale");

        return true;    // Show the tooltip
    },

    observe: function(subject, topic, data) {
        switch (topic) {
            case "sls:selected-changed":
                this.updateMatchCommand();
                this.tryToUpdateToolbarButton();
                break;

            case "sls:availables-changed":
                this.updateCustomProviderBroadcaster();
                this.tryToUpdateToolbarButton();
                break;

            case "nsPref:changed":
                switch (data) {
                    case "button.restartAfterSwitch":
                        this.updateRestartCommand();
                        break;

                    case "button.showDescriptions":
                        this.updateDescriptionsBroadcaster();
                        break;
                }
                break;
        }
    },

    customizableListener: {

        onWidgetAdded: function(id) {
            if (id === simplels.toolbarButtonId)
                this._handle();
        },

        onWidgetUndoMove: function(node) {
            if (node.id === simplels.toolbarButtonId)
                this._handle();
        },

        // At least one add-on (The Puzzle Piece 2, aka Puzzle Toolbars)
        // relies that we handle onAreaNodeRegistered to initialize our
        // button if it's placed in an extra toolbar provided by them.
        onAreaNodeRegistered: function(area, container) {
            this._handle();
        },

        _handle: function() {
            // Note that CustomizableUI events are dispatched synchronously
            // on the UI thread.
            window.setTimeout(function() {
                simplels.checkIfUpdatingToolbarButton();
            }, 60);
        }
    },

    openLanguagesManager: function() {
        switch (this.utils.application) {
            case this.THUNDERBIRD_ID:
                openAddonsMgr("addons://list/locale");
                break;

            case this.SEAMONKEY_ID:
                toEM("addons://list/locale");
                break;

            case this.FIREFOX_ID:
            default:
                BrowserOpenAddonsMgr("addons://list/locale");
                break;
        }
    },

    openGetMoreLanguagesPage: function() {
        Cu.import("chrome://simplels/content/modules/langpacks.jsm", this);
        let getURL = this.getMoreLanguagesURL();

        switch (this.utils.application) {
            case this.THUNDERBIRD_ID:
                this.tbUtils.openContentTab(getURL);
                break;

            case this.SEAMONKEY_ID:
                openUILink(getURL);
                break;

            case this.FIREFOX_ID:
            default:
                openUILinkIn(getURL, "tab");
                break;
        }
    },

    toggleRestartAfterSwitch: function() {
        let wantedRestart = this.prefs.getBoolPref("button.restartAfterSwitch");

        this.prefs.setBoolPref("button.restartAfterSwitch", !wantedRestart);
    },

    enableMatchingOS: function() {
        this.langsvc.matchingOS = true;

        this.restartAfterSwitch();
    },

    switchTo: function(locale) {
        this.langsvc.userLocale = locale;

        this.restartAfterSwitch();
    },

    restartAfterSwitch: function() {
        let wantRestart = this.prefs.getBoolPref("button.restartAfterSwitch");
        if (wantRestart)
            this.utils.restartApplication();    // It could be canceled
    },

    resetLanguagesProvider: function() {
        this.langsvc.resetSelectedProvider();
    },

    getWindowRelevantLocales: function() {
        let locales = this.langUtils.findRelevantLocales();

        if (locales.indexOf(this.windowLocale) === -1) { // If it isn't found:
            // TODO: The slicing could to be avoided in some cases
            locales = locales.slice();
            locales.push(this.windowLocale);
        }
        return locales;
    },

    getLocaleName: function(locale) {
        return this.langUtils.buildLocaleName(locale, this.localeStrings);
    },

    getStringBundle: function(id) {
        return this.langUtils.getStringBundle(id, document);
    }

    };
})();

window.addEventListener("load", simplels);
window.addEventListener("unload", simplels);
<commit_msg>Prevent a dummy warning from the AMO add-on validator<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/.
 */

var simplels = (function() {

    // Our global simplels container is defined via a closure to get
    // privately these shorthands for Components, not available in SeaMonkey,
    // and to prevent re-declarating them in other cases.
    const Cc = Components.classes;
    const Ci = Components.interfaces;
    const Cu = Components.utils;

    return {

    langsvc: null,
    windowLocale: null,

    prefs: null,
    strings: null,
    localeStrings: {languageNames: null, regionNames: null, formats: null},

    toolbarButtonAsWidget: null,
    toolbarButtonId: null,
    isToolbarButtonUpdatePending: false,

    handleEvent: function(event) {
        switch (event.type) {
            case "load":
                window.removeEventListener("load", simplels);
                this.onLoad();
                break;

            case "aftercustomization":
                this.checkIfUpdatingToolbarButton();
                break;

            case "unload":
                this.onUnload();
                break;
        }
    },

    onLoad: function() {
        Cu.import("chrome://simplels/content/modules/general.jsm", this);
        Cu.import("chrome://simplels/content/modules/language.jsm", this);

        this.langsvc = Cc["@nohamelin/sls/language-service;1"]
                       .getService().wrappedJSObject;
        this.windowLocale = this.langsvc.currentLocale;

        this.prefs = Services.prefs.getBranch("extensions.simplels.");
        this.strings = this.getStringBundle("simplels-strings");
        this.localeStrings.languageNames = this.getStringBundle(
                                                "simplels-language-names");
        this.localeStrings.regionNames = this.getStringBundle(
                                                "simplels-region-names");
        this.localeStrings.formats = this.getStringBundle(
                                                "simplels-name-formats");

        this.prefs.QueryInterface(Ci.nsIPrefBranch2);   // COMPAT: Gecko 12-
        this.prefs.addObserver("", this, false);

        Services.obs.addObserver(this, "sls:selected-changed", false);
        Services.obs.addObserver(this, "sls:availables-changed", false);

        // In Firefox 29 and later, the toolbar button will be built with the
        // new mechanisms provided by CustomizableUI, instead of plain XUL.
        this.toolbarButtonAsWidget = !!document.getElementById(
                                                    "simplels-widget-view");
        this.toolbarButtonId = this.toolbarButtonAsWidget ? "simplels-widget"
                                                          : "simplels-button";
        if (this.toolbarButtonAsWidget)
            this.createToolbarButtonAsWidget();

        // Initialize dinamic attributes of commands and toolbar button
        this.updateRestartCommand();
        this.updateMatchCommand();
        this.updateDescriptionsBroadcaster();
        this.updateCustomProviderBroadcaster();

        window.setTimeout(function() {
            simplels.tryToUpdateToolbarButton();
        }, 60);

        // Don't miss to update the toolbar button when it's added from the
        // toolbar palette.
        if ("CustomizableUI" in window) {
            CustomizableUI.addListener(this.customizableListener);
        } else {
            // The next is no longer enougth with CustomizableUI because
            // adding our widget to some area from the palette make it
            // immediately full interactive in any existent browser window
            // except the one still displaying the "Customize Firefox" tab.
            window.addEventListener("aftercustomization", simplels);
        }
    },

    onUnload: function() {
        this.prefs.removeObserver("", this);

        Services.obs.removeObserver(this, "sls:selected-changed");
        Services.obs.removeObserver(this, "sls:availables-changed");

        if ("CustomizableUI" in window)
            CustomizableUI.removeListener(this.customizableListener);
    },

    updateRestartCommand: function() {
        let restartCmd = document.getElementById(
                                  "sls_toggleRestartAfterSwitch");
        let wantRestart = this.prefs.getBoolPref("button.restartAfterSwitch");

        restartCmd.setAttribute("checked", wantRestart);
    },

    updateMatchCommand: function() {
        let matchCmd = document.getElementById("sls_enableMatchingOS");
        let wantMatch = this.langsvc.matchingOS;
        let disableMatch = this.langsvc.isMatchingOSLocked;

        matchCmd.setAttribute("checked", wantMatch);
        if (disableMatch)
            matchCmd.setAttribute("disabled", "true");
        else
            matchCmd.removeAttribute("disabled");
    },

    updateDescriptionsBroadcaster: function() {
        let descBcstr = document.getElementById("sls_showLocaleDescriptions");
        let wantDescriptions = this.prefs.getBoolPref(
                                         "button.showDescriptions");

        descBcstr.setAttribute("descriptions", wantDescriptions);
    },

    updateCustomProviderBroadcaster: function() {
        let customBcstr = document.getElementById(
                                   "sls_customLanguagesProvider");
        let resetCmd = document.getElementById("sls_resetLanguagesProvider");
        let customProvider = !this.langsvc.isSelectedProviderDefault;

        customBcstr.setAttribute("hidden", !customProvider);

        if (customProvider)
            resetCmd.removeAttribute("disabled");
        else
            resetCmd.setAttribute("disabled", "true");
    },

    createToolbarButtonAsWidget: function() {
        let widget = CustomizableUI.getWidget("simplels-widget");

        if (!widget || widget.provider === CustomizableUI.PROVIDER_XUL) {
            if (widget)
                CustomizableUI.destroyWidget("simplels-widget");

            CustomizableUI.createWidget({
                id: "simplels-widget",
                type: "view",
                viewId: "simplels-widget-view",
                label: simplels.strings.getString("widget.label"),
                tooltiptext: "Language",   // HACK, see below

                onCreated: function(node) {
                    // HACK: The tooltiptext property was specified only for
                    // to prevent a dummy "Could not localize property..."
                    // warning from the application while building the widget;
                    // this callback is run after that.
                    node.removeAttribute("tooltiptext");
                    node.tooltip = "simplels-button-tooltip";
                }
            });

            // Ensure that, after updating the extension (from 0.6 or older),
            // the widget is placed in the same original position of the XUL
            // button.
            let buttonPlacement = CustomizableUI.getPlacementOfWidget(
                                                 "simplels-button");
            if (buttonPlacement) {
                let migrated = false;
                try {
                    migrated = this.prefs.getBoolPref("button.migrated");
                } catch (e) {}

                if (!migrated) {
                    CustomizableUI.addWidgetToArea("simplels-widget",
                                                   buttonPlacement.area,
                                                   buttonPlacement.position);
                    this.prefs.setBoolPref("button.migrated", true);
                }
            }
        }
    },

    checkIfUpdatingToolbarButton: function() {
        if (this.isToolbarButtonUpdatePending) {
            this.tryToUpdateToolbarButton();
        }
    },

    tryToUpdateToolbarButton: function() {
        let foundButton = this.toolbarButtonAsWidget
                    ? CustomizableUI.getPlacementOfWidget("simplels-widget")
                    : document.getElementById("simplels-button");

        if (foundButton) {
            this.isToolbarButtonUpdatePending = false;

            this.updateToolbarButtonTooltip();

            // The general items of the toolbar button's popup work with
            // broadcasters, so they are always correctly set, and we can
            // to ignore them here.

            let popup = this.toolbarButtonAsWidget
                        ? document.getElementById("simplels-view-body")
                        : document.getElementById("simplels-button-popup");

            this.populatePopupLocales(popup, this.toolbarButtonAsWidget);
        } else {
            this.isToolbarButtonUpdatePending = true;
        }
    },

    updateToolbarButtonTooltip: function() {
        let $ = function(id) {
            return document.getElementById("simplels-button-tooltip-" + id);
        };

        // The tooltip displays information about the next pieces:
        //
        // 1. The locale selected, expected by the user.
        let selectedLocale = this.langsvc.selectedLocale;
        let isSelectedMatchingOS = this.langsvc.matchingOS;
        let isSelectedAvailable = this.langUtils
                                      .isLocaleAvailable(selectedLocale);
        let informAboutSelected = isSelectedMatchingOS || !isSelectedAvailable;

        $("selected").value = this.getLocaleName(selectedLocale);
        $("selected-about").hidden = !informAboutSelected;

        let aboutMsg = "";
        if (informAboutSelected) {
            let matchingMsg = this.strings.getString("tooltip.matchingOS");
            let noAvailableMsg = this.strings.getString("tooltip.noAvailable");

            if (isSelectedMatchingOS && !isSelectedAvailable) {
                aboutMsg = this.strings.getFormattedString("tooltip.both",
                                                           [matchingMsg,
                                                            noAvailableMsg]);
            } else if (isSelectedMatchingOS) {
                aboutMsg = matchingMsg;
            } else {    // !isSelectedAvailable
                aboutMsg = noAvailableMsg;
            }
        }
        $("selected-about").value = aboutMsg;

        // 2. The locale applied to the current window.
        //    It's hidden if its value matches the currently selected locale.
        let areLocalesInConflict = (this.windowLocale !== selectedLocale);

        $("current-row").hidden = !areLocalesInConflict;
        $("current").value = this.getLocaleName(this.windowLocale);

        document.getElementById("simplels-button-tooltip")
                .setAttribute("conflicting", areLocalesInConflict);

        // 3. The locale provider selected.
        //    It's hidden (by a broadcaster) if the default provider is used.
        let selectedProvider = this.langsvc.selectedProvider;
        let selectedProviderName = this.localeStrings
                                       .formats
                                       .getFormattedString("unnamed",
                                                           [selectedProvider]);
        $("provider").value = selectedProviderName;
    },

    populatePopupLocales: function(popup, forWidget) {
        Cu.import("chrome://simplels/content/modules/dom.jsm", this);

        let localeItemType = forWidget ? "toolbarbutton" : "menuitem";
        let localeItemClass = forWidget ? "simplels-locale subviewbutton"
                                        : "simplels-locale";
        let localeItemCallback = function(locale) {
            return function() { simplels.switchTo(locale); };
        };

        let checkedLocale = this.langsvc.userLocale;
        let isCheckedAvailable = this.langUtils
                                     .isLocaleAvailable(checkedLocale);
        let isCheckedIgnored = this.langsvc.matchingOS;
        let isCheckedLocked = this.langsvc.isUserLocaleLocked;
        let isCurrentAvailable = this.langUtils
                                     .isLocaleAvailable(this.windowLocale);

        let locales = this.getWindowRelevantLocales();
        this.langUtils.sortLocales(locales);

        this.domUtils.removeChildrenByClassName(popup, "simplels-locale");

        let popupFragment = document.createDocumentFragment();
        locales.forEach(function(locale) {
            let item = document.createElement(localeItemType);

            item.className = localeItemClass;
            item.setAttribute("type", "radio");
            item.setAttribute("autocheck", "false");
            item.setAttribute("locale", locale);
            item.setAttribute("label", this.getLocaleName(locale));
            item.setAttribute("description", locale);
            item.addEventListener("command", localeItemCallback(locale));

            if (locale === this.windowLocale) {
                item.setAttribute("current", "true");
                if (!isCurrentAvailable)
                    item.setAttribute("disabled", "true");
            }
            if (locale === checkedLocale) {
                if (!isCheckedIgnored)
                    item.setAttribute("checked", "true");
                if (!isCheckedAvailable) {
                    item.setAttribute("disabled", "true");

                    if (isCheckedIgnored && locale !== this.windowLocale)
                        item.setAttribute("hidden", "true");  // Not relevant
                }
            }
            if (isCheckedLocked) {
                item.setAttribute("disabled", "true");
            }

            popupFragment.appendChild(item);
        }, this);

        let matchNode = popup.getElementsByClassName("simplels-matching")[0];
        popup.insertBefore(popupFragment, matchNode.nextSibling);
    },

    updatePopupLocaleTooltip: function() {
        let node = document.tooltipNode;

        // The locale attribute is used here instead of the simplels-locale
        // class for to include the case of an empty locale.
        if (!node.getAttribute("locale"))
            return false;

        document.getElementById("simplels-locale-tooltip-name")
                .value = node.label;
        document.getElementById("simplels-locale-tooltip-tag")
                .value = node.getAttribute("locale");

        return true;    // Show the tooltip
    },

    observe: function(subject, topic, data) {
        switch (topic) {
            case "sls:selected-changed":
                this.updateMatchCommand();
                this.tryToUpdateToolbarButton();
                break;

            case "sls:availables-changed":
                this.updateCustomProviderBroadcaster();
                this.tryToUpdateToolbarButton();
                break;

            case "nsPref:changed":
                switch (data) {
                    case "button.restartAfterSwitch":
                        this.updateRestartCommand();
                        break;

                    case "button.showDescriptions":
                        this.updateDescriptionsBroadcaster();
                        break;
                }
                break;
        }
    },

    customizableListener: {

        onWidgetAdded: function(id) {
            if (id === simplels.toolbarButtonId)
                this._handle();
        },

        onWidgetUndoMove: function(node) {
            if (node.id === simplels.toolbarButtonId)
                this._handle();
        },

        // At least one add-on (The Puzzle Piece 2, aka Puzzle Toolbars)
        // relies that we handle onAreaNodeRegistered to initialize our
        // button if it's placed in an extra toolbar provided by them.
        onAreaNodeRegistered: function(area, container) {
            this._handle();
        },

        _handle: function() {
            // Note that CustomizableUI events are dispatched synchronously
            // on the UI thread.
            window.setTimeout(function() {
                simplels.checkIfUpdatingToolbarButton();
            }, 60);
        }
    },

    openLanguagesManager: function() {
        switch (this.utils.application) {
            case this.THUNDERBIRD_ID:
                openAddonsMgr("addons://list/locale");
                break;

            case this.SEAMONKEY_ID:
                toEM("addons://list/locale");
                break;

            case this.FIREFOX_ID:
            default:
                BrowserOpenAddonsMgr("addons://list/locale");
                break;
        }
    },

    openGetMoreLanguagesPage: function() {
        Cu.import("chrome://simplels/content/modules/langpacks.jsm", this);
        let getURL = this.getMoreLanguagesURL();

        switch (this.utils.application) {
            case this.THUNDERBIRD_ID:
                this.tbUtils.openContentTab(getURL);
                break;

            case this.SEAMONKEY_ID:
                openUILink(getURL);
                break;

            case this.FIREFOX_ID:
            default:
                openUILinkIn(getURL, "tab");
                break;
        }
    },

    toggleRestartAfterSwitch: function() {
        let wantedRestart = this.prefs.getBoolPref("button.restartAfterSwitch");

        this.prefs.setBoolPref("button.restartAfterSwitch", !wantedRestart);
    },

    enableMatchingOS: function() {
        this.langsvc.matchingOS = true;

        this.restartAfterSwitch();
    },

    switchTo: function(locale) {
        this.langsvc.userLocale = locale;

        this.restartAfterSwitch();
    },

    restartAfterSwitch: function() {
        let wantRestart = this.prefs.getBoolPref("button.restartAfterSwitch");
        if (wantRestart)
            this.utils.restartApplication();    // It could be canceled
    },

    resetLanguagesProvider: function() {
        this.langsvc.resetSelectedProvider();
    },

    getWindowRelevantLocales: function() {
        let locales = this.langUtils.findRelevantLocales();

        if (locales.indexOf(this.windowLocale) === -1) { // If it isn't found:
            // TODO: The slicing could to be avoided in some cases
            locales = locales.slice();
            locales.push(this.windowLocale);
        }
        return locales;
    },

    getLocaleName: function(locale) {
        return this.langUtils.buildLocaleName(locale, this.localeStrings);
    },

    getStringBundle: function(id) {
        return this.langUtils.getStringBundle(id, document);
    }

    };
})();

window.addEventListener("load", simplels);
window.addEventListener("unload", simplels);
<|endoftext|>


<commit_before>// Slingshot.fileRestrictions("cropUploader", {
//   allowedFileTypes: ["image/png", "image/jpeg", "image/gif"],
//   maxSize: 10 * 1024 * 1024 // 10 MB (use null for unlimited)
// });

CropUploader = {
	images: new Meteor.Collection('images'),
	name: 'cropUploader',
	urlBase: null,
	replaceDerivative:function(imageId, name, derivative) {
		var self = this;
		// console.log( derivative );
		check(imageId, String);
		check(name, String);
		// derivative needs to be File or Blob
		if (! (derivative instanceof window.File) && ! (derivative instanceof window.Blob))
			throw new Meteor.Error("The derivative to replace must be File or Blob");
		// check(derivative, Blob);
		var image = self.images.findOne(imageId);
		if(!image) throw new Meteor.Error(403, "the provided image does not exist");
		//
		// setup slingshot with uuid from parent
		//
		var uploader = new Slingshot.Upload(self.name, {uuid: image.uuid });
		var oldfile = name in image['derivatives'] ? image['derivatives'][name] : null;
		// set name of blob so it will put in the right place
		derivative.name = 'derivative/'+name+'/';
		uploader.send(derivative, function (error, thumbnailUrl) {
		  if (error) {
			console.error('Error uploading', uploader.xhr.response);
			alert (error);
		  } else {
			//
			// update the collection object
			//
			var set = {}
			set['derivatives.'+name] = thumbnailUrl;
			self.images.update(image._id,{$set: set}, function(err,res){
				if(err) console.error(err);
				else console.log('uploaded and updated', res);
			});
			//
			// this should actually happen in the allow.update
			//
			if(oldfile)
			{
				// console.log('delete', oldfile);
				Meteor.call('cropUploaderS3Delete', oldfile, function (error, result) {
					if(error) console.err(error);
				});
			}
		  }
		});
	},
	crop: {
		template: null,
		save: function(type) {
			var self = this;
			var imageid = self.template.imageid;
			type = type || 'thumbnail';
			self.template.canvas = self.template.cropimage.cropper('getCroppedCanvas',{
			  width: self.template.data.thumbnailWidth,
			  height: self.template.data.thumbnailHeight
			});

			if(self.template.canvas && self.template.canvas.toBlob)
			{
			  self.template.canvas.toBlob(function(blob){
				CropUploader.replaceDerivative( imageid, type, blob );
			  },'image/png');
			} else console.log('no canvas', self.template.canvas);
		},
		rotate: function() {
			var self = this;
			self.template.cropimage.cropper('rotate',90);
		}
	},
	init: function(name, directory, consolidate) {
		var self = this;
		directory = directory || '';
		consolidate = consolidate || false;
		check(name, String);
		check(directory, String);
		check(consolidate, Boolean);
		if(directory.length>0 && directory[directory.length-1]!="/") directory+='/';
		self.directory = directory;
		self.name = name;
		if(Meteor.isServer)
		{
			var Knox = Npm.require("knox");
			var directive = Slingshot.getDirective(self.name);
			// console.log(directive._directive.region);
			self.knox = Knox.createClient({
				key: directive._directive.AWSAccessKeyId,
				secret: directive._directive.AWSSecretAccessKey,
				region: directive._directive.region,
				bucket: directive._directive.bucket,
			});
			directive._directive.key = function (file, meta) {
				// file
				// {
				//  name: '11075296_10152631810352261_1337077077459889796_n.jpg',
				//  size: 17224,
				//  type: 'image/jpeg'
				// }
				// console.log(file, meta);
				var ext = file.type.split('/')[1];
				if(file.name.match('derivative/'))
				{
					var d = new Date().getTime();
					return CropUploader.directory + file.name + meta.uuid +'.'+d+'.'+ext;
				}
				else
				{
					return CropUploader.directory+meta.uuid+'.'+ext;
				}
			}
			if(consolidate) Meteor.call('cropUploaderConsolidate');
		}
	}
};<commit_msg>check permission in replaceDerivative<commit_after>// Slingshot.fileRestrictions("cropUploader", {
//   allowedFileTypes: ["image/png", "image/jpeg", "image/gif"],
//   maxSize: 10 * 1024 * 1024 // 10 MB (use null for unlimited)
// });

CropUploader = {
	images: new Meteor.Collection('images'),
	name: 'cropUploader',
	urlBase: null,
	replaceDerivative:function(imageId, name, derivative) {
		var self = this;
		// console.log( derivative );
		check(imageId, String);
		check(name, String);
		// derivative needs to be File or Blob
		if (! (derivative instanceof window.File) && ! (derivative instanceof window.Blob))
			throw new Meteor.Error("The derivative to replace must be File or Blob");
		// check(derivative, Blob);
		var image = self.images.findOne(imageId);
		if(!image) throw new Meteor.Error(401, "the provided image does not exist");
		if(image.userId != Meteor.userId()) throw new Meteor.Error(403, "you don't have permission to replace derivative for "+imageid);
		//
		// setup slingshot with uuid from parent
		//
		var uploader = new Slingshot.Upload(self.name, {uuid: image.uuid });
		var oldfile = name in image['derivatives'] ? image['derivatives'][name] : null;
		// set name of blob so it will put in the right place
		derivative.name = 'derivative/'+name+'/';
		uploader.send(derivative, function (error, thumbnailUrl) {
		  if (error) {
			console.error('Error uploading', uploader.xhr.response);
			alert (error);
		  } else {
			//
			// update the collection object
			//
			var set = {}
			set['derivatives.'+name] = thumbnailUrl;
			self.images.update(image._id,{$set: set}, function(err,res){
				if(err) console.error(err);
				else console.log('uploaded and updated', res);
			});
			//
			// this should actually happen in the allow.update
			//
			if(oldfile)
			{
				// console.log('delete', oldfile);
				Meteor.call('cropUploaderS3Delete', oldfile, function (error, result) {
					if(error) console.err(error);
				});
			}
		  }
		});
	},
	crop: {
		template: null,
		save: function(type) {
			var self = this;
			var imageid = self.template.imageid;
			type = type || 'thumbnail';
			self.template.canvas = self.template.cropimage.cropper('getCroppedCanvas',{
			  width: self.template.data.thumbnailWidth,
			  height: self.template.data.thumbnailHeight
			});

			if(self.template.canvas && self.template.canvas.toBlob)
			{
			  self.template.canvas.toBlob(function(blob){
				CropUploader.replaceDerivative( imageid, type, blob );
			  },'image/png');
			} else console.log('no canvas', self.template.canvas);
		},
		rotate: function() {
			var self = this;
			self.template.cropimage.cropper('rotate',90);
		}
	},
	init: function(name, directory, consolidate) {
		var self = this;
		directory = directory || '';
		consolidate = consolidate || false;
		check(name, String);
		check(directory, String);
		check(consolidate, Boolean);
		if(directory.length>0 && directory[directory.length-1]!="/") directory+='/';
		self.directory = directory;
		self.name = name;
		if(Meteor.isServer)
		{
			var Knox = Npm.require("knox");
			var directive = Slingshot.getDirective(self.name);
			// console.log(directive._directive.region);
			self.knox = Knox.createClient({
				key: directive._directive.AWSAccessKeyId,
				secret: directive._directive.AWSSecretAccessKey,
				region: directive._directive.region,
				bucket: directive._directive.bucket,
			});
			directive._directive.key = function (file, meta) {
				// file
				// {
				//  name: '11075296_10152631810352261_1337077077459889796_n.jpg',
				//  size: 17224,
				//  type: 'image/jpeg'
				// }
				// console.log(file, meta);
				var ext = file.type.split('/')[1];
				if(file.name.match('derivative/'))
				{
					var d = new Date().getTime();
					return CropUploader.directory + file.name + meta.uuid +'.'+d+'.'+ext;
				}
				else
				{
					return CropUploader.directory+meta.uuid+'.'+ext;
				}
			}
			if(consolidate) Meteor.call('cropUploaderConsolidate');
		}
	}
};<|endoftext|>


<commit_before>// BUILDERS
module.exports.builders = function() {
	var builder_count = _.filter(Game.creeps, (creep) =>
												(creep.memory.role == 'builder'));
	if (builder_count.length < 3) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'builder', harvesting: 'true' });

		console.log('Spawning new Builder: ' + newName);
	}
}

// HARVESTERS
module.exports.harvesters = function() {
	var harvester_count = _.filter(Game.creeps, (creep) =>
													(creep.memory.role == 'harvester'));
	if (harvester_count.length < 3) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'harvester', harvesting: 'true' });

		console.log('Spawning new Harvester: ' + newName);
	}
}

// HAULERS
module.exports.haulers = function() {
	var hauler_count = _.filter(Game.creeps, (creep) =>
											 (creep.memory.role == 'hauler'));
	if (hauler_count.length < 2) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[CARRY, CARRY, CARRY, CARRY, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'hauler', harvesting: 'true' });

		console.log('Spawning new Hauler: ' + newName);
	}
}

// HEALERS
module.exports.healers = function() {
	var healer_count = _.filter(Game.creeps, (creep) =>
											 (creep.memory.role == 'healer'));
	if (healer_count.length < 1) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'healer', harvesting: 'true' });

		console.log('Spawning new Healer: ' + newName);
	}
}

// UPGRADERS
module.exports.upgraders = function() {
	var upgrader_count = _.filter(Game.creeps, (creep) =>
												 (creep.memory.role == 'upgrader'));
	if (upgrader_count.length < 2) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'upgrader', harvesting: 'true' });

		console.log('Spawning new Upgrader: ' + newName);
	}
}<commit_msg>Minor changes<commit_after>// SET UNIT NUMBERS HERE
var builders = 3;
var harvesters = 3;
var haulers = 2;
var healers = 2;
var upgraders = 4;

// BUILDERS
module.exports.builders = function() {
	var builder_count = _.filter(Game.creeps, (creep) =>
												(creep.memory.role == 'builder'));
	if (builder_count.length < builders) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'builder', harvesting: 'true' });

		console.log('Spawning new Builder: ' + newName);
	}
}

// HARVESTERS
module.exports.harvesters = function() {
	var harvester_count = _.filter(Game.creeps, (creep) =>
													(creep.memory.role == 'harvester'));
	if (harvester_count.length < harvesters) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'harvester', harvesting: 'true' });

		console.log('Spawning new Harvester: ' + newName);
	}
}

// HAULERS
module.exports.haulers = function() {
	var hauler_count = _.filter(Game.creeps, (creep) =>
											 (creep.memory.role == 'hauler'));
	if (hauler_count.length < haulers) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[CARRY, CARRY, CARRY, CARRY, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'hauler', harvesting: 'true' });

		console.log('Spawning new Hauler: ' + newName);
	}
}

// HEALERS
module.exports.healers = function() {
	var healer_count = _.filter(Game.creeps, (creep) =>
											 (creep.memory.role == 'healer'));
	if (healer_count.length < healers) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'healer', harvesting: 'true' });

		console.log('Spawning new Healer: ' + newName);
	}
}

// UPGRADERS
module.exports.upgraders = function() {
	var upgrader_count = _.filter(Game.creeps, (creep) =>
												 (creep.memory.role == 'upgrader'));
	if (upgrader_count.length < upgraders) {
		var newName = Game.spawns['Spawn1'].createCreep(
			[WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE],
			undefined,
			{ role: 'upgrader', harvesting: 'true' });

		console.log('Spawning new Upgrader: ' + newName);
	}
}<|endoftext|>


<commit_before>exports.commands = {
	declareaotd: function(target, room, user) {
		if (room.id != 'lobby') return this.sendReply("The command must be used in Lobby.");
		if (!user.can('broadcast', null, room)) return this.sendReply('You do not have enough authority to use this command.');
		if (!this.canTalk()) return false;
		this.add(
			'|raw|
AOTD has begun in Lux Music! ' +
			'<button name="joinRoom" value="luxmusic" target="_blank">Join now</button> to nominate your favorite artist for AOTD '
'
		);
		this.logModCommand(user.name + " used declareaotd.");
	}
};
<commit_msg>Update aotd.js<commit_after>exports.commands = {
	declareaotd: function(target, room, user) {
		if (room.id != 'lobby') return this.sendReply("The command must be used in Lobby.");
		if (!user.can('broadcast', null, room)) return this.sendReply('You do not have enough authority to use this command.');
		if (!this.canTalk()) return false;
		this.add(
			'|raw|
AOTD has begun in Lux Music! ' +
			'<button name="joinRoom" value="luxmusic" target="_blank">Join now</button> to nominate your favorite artist for AOTD
'
		);
		this.logModCommand(user.name + " used declareaotd.");
	}
};
<|endoftext|>


<commit_before>export { Base, Params };

/**
 * A base class for extracting and keeping weights. It composes of a Float32Array and a shape. It can
 * be used as CNN (depthwise, pointwise and bias) filter weights.
 *
 * @member {Float32Array} defaultInput
 *   The default input Float32Array. Its byteOffset will be checked against defaultByteOffsetBegin.
 * Its content will be interpret as weights if privilegeInput is null. Otherwise, its content
 * will be ignored if privilegeInput is not null.
 *
 * @member {number} defaultByteOffsetBegin
 *   The weights[] begins at defaultInput's defaultByteOffsetBegin (relative to defaultInput.buffer,
 * not to defaultInput.byteOffset). If this value less than defaultInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} defaultByteOffsetEnd
 *   The weights[] ends at defaultInput's defaultByteOffsetEnd (not inclusive) (relative to
 * defaultInput.buffer, not to defaultInput.byteOffset).
 *
 * @member {Float32Array} privilegeInput
 *   The privilege input Float32Array. If not null, its content will be interpret as weights and
 * the content of defaultInput will be ignored.
 *
 * @member {number} privilegeByteOffsetBegin
 *   The weights[] begins at privilegeInput's privilegeByteOffsetBegin (relative to privilegeInput.buffer,
 * not to privilegeInput.byteOffset). If this value less than privilegeInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} privilegeByteOffsetEnd
 *   The weights[] ends at privilegeInput's privilegeByteOffsetEnd (not inclusive) (relative
 * to privilegeInput.buffer, not to privilegeInput.byteOffset).
 *
 * @member {number[]} shape
 *   The weights shape (element count for every dimension). The shape.length is dimension. If ( shape.length == 0 ),
 * it is 0-dimension (i.e. scalar). The initialization will fail (i.e. ( isValid() == false ) ) if shape is too large
 * (or NaN) (exceeds the defaultInput (or, privilegeInput if not null) bounding).
 *
 * @member {Float32Array} weights
 *  The values. It is a reference (sub-range) to the underlying defaultInput (or privilegeInput).
 */
class Base {

  /**
   * Create Float32Array weights[] over the defaultInput (or privilegeInput) according to the specific
   * byteOffsetBegin, shape, and weightConverter.
   *
   * The defaultInput and privilegeInput can not both be null. If one of them is null, the non-null is used.
   * If both are non-null, the privilegeInput will be used.
   *
   * @return {boolean} Return false, if initialization failed.
   */
  init( defaultInput, defaultByteOffsetBegin, privilegeInput, privilegeByteOffsetBegin, shape ) {

    this.defaultInput =   defaultInput;
    this.privilegeInput = privilegeInput;
    this.shape =          shape;
    this.weights =        null;   // So that ( isValid() == false ) if re-initialization failed.

    //let weightCount =     ( shape ) ? shape.reduce( ( accumulator, currentValue ) => accumulator * currentValue ) : 0;
    let weightCount =     ( shape ) ? tf.sizeFromShape( shape ) : 0; // tf.sizeFromShape() can handle zero length (i.e. scalar).
    let weightByteCount = weightCount * Float32Array.BYTES_PER_ELEMENT;

    let input, byteOffsetBegin;
    let byteOffsetEnd; // Not inclusive. It will be used as the next filter's beginning.

    if ( privilegeInput ) {       // privilegeInput first.

      if ( privilegeByteOffsetBegin < privilegeInput.byteOffset )
        return false;  // Failed, the privilege beginning position is illegal (less than bounding).

      input = privilegeInput;
      byteOffsetBegin = this.privilegeByteOffsetBegin = privilegeByteOffsetBegin;
      byteOffsetEnd =   this.privilegeByteOffsetEnd =   privilegeByteOffsetBegin + weightByteCount;
      this.defaultByteOffsetBegin = this.defaultByteOffsetEnd = defaultByteOffsetBegin; // Stay at beginning for not used.

    } else if ( defaultInput ) {  // defaultInput second.

      if ( defaultByteOffsetBegin < defaultInput.byteOffset )
        return false;  // Failed, the default beginning position is illegal (less than bounding).

      input = defaultInput;
      byteOffsetBegin = this.defaultByteOffsetBegin = defaultByteOffsetBegin;
      byteOffsetEnd =   this.defaultByteOffsetEnd =   defaultByteOffsetBegin + weightByteCount;
      this.privilegeByteOffsetBegin = this.privilegeByteOffsetEnd = privilegeByteOffsetBegin; // Stay at beginning for not used.

    } else {
      return false;  // Failed, both privilege and default input are null.
    }

    // Bounded by the input.byteLength.
    let legalByteOffsetEnd = input.byteOffset + input.byteLength;
    if ( byteOffsetEnd > legalByteOffsetEnd )
      return false;  // Failed, if shape is too large (or NaN).

    this.weights = new Float32Array( input.buffer, byteOffsetBegin, weightCount );  // Share the underlying array buffer.
    return true;     // Success.
  }

  /** @return {number} Return the absolute value of the trucated value. */
  static toPositiveInteger( v ) {
    return Math.abs( Math.trunc( v ) );
  }

  /** @return Return true, if initialization is success (i.e. ( this.weights != null )). */
  isValid()                      { return ( this.weights ) ? true : false; }

  get weightByteCount()          { return this.weights.byteLength; }
  get weightCount()              { return this.weights.length; }
}

/**
 * The parameters for the weights of a neural network layer.
 *
 * @member {Map} parameterMap
 *   All parameters provided by this object. Its keys are the same as init()'s parameterMap. Its values are
 * combined from init()'s parameterMap and inputFloat32Array (or fixedWeights).
 *
 * @member {number} parameterCountExtracted
 *   How many parameters are extracted from inputFloat32Array or fixedWeights in fact. Only existed if init()
 * successfully. The same as this.weightCount (i.e. length of this.weights[] and this.weightsModified[]).
 *
 * @member {number} parameterCount
 *   Always ( parameterMap.size ). This is the total parameter count provided by this object
 * if init() successfully.
 *
 * @member {number} inChannels
 *   The input channel count of this neural network layer.
 *
 * @member {number} channelMultiplier
 *   Every input channel will be expanded into how many channels.
 *
 * @member {number} outChannels
 *   The output channel count of this neural network layer.
 *
 * @member {Float32Array} weightsModified
 *  The copied extracted values. They are copied from inputFloat32Array or fixedWeights, and then converted
 * to positive integer. Its length will be the same as parameterCountExtracted.
 */
class Params extends Base {

  /**
   *
   * The parameterMap should have at least:
   *
   * - Params.Keys.inChannels
   *   {number} There will be how many input channels. (Input channel count) (By specifying)
   *
   *
   * The parameterMap could also have (and any other custom key-value pairs):
   *
   * - Params.Keys.channelMultiplier
   *   {number} Every input channel will be expanded into how many channels.
   *     - If not null, it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   * - Params.Keys.outChannels
   *   {number} There will be how many output channels. (Output channel count)
   *     - If Number.isFinite(), it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If Infinity (Number.POSITIVE_INFINITY), it will be ( inChannels * channelMultiplier ). (By channelMultiplier)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights. It should have ( parameterCountExtractedAtLeast ) or
   * ( parameterCountExtractedAtLeast + 1 ) or ( parameterCountExtractedAtLeast + 2 ) elements according to the
   * value of channelMultiplier and outChannels.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {Map} parameterMap
   *   Describe what parameters to be used or extracted. The keys of parameterMap will be viewed as parameter names.
   * The values of parameterMap will be viewed as parameter values. If the value of a [ key, value ] entry is null,
   * the parameter's value will be extracted from inputFloat32Array or fixedWeights (i.e. by evolution). Otherwise,
   * the parameter's value will be the value of the [ key, value ] entry (i.e. by specifying).
   *
   * @param {Float32Array|Array} fixedWeights
   *   If null, extract parameters from inputFloat32Array. If not null, extract parameters from it instead of
   * inputFloat32Array. When not null, it should have parameterCountExtracted elements (i.e. the count of non-null values
   * of parameterMap).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, parameterMap, fixedWeights = null ) {

    this.weightsModified = this.parameterMap = null; // So that distinguishable if re-initialization failed.

    if ( !parameterMap )
      return false;  // Do not know what parameters to be used or extracted.

    let inChannels = parameterMap.get( Params.Keys.inChannels );
    if ( !inChannels )
      return false;  // At least, there should be a (required) parameter (i.e. input channel count).

    this.parameterMap = new Map( parameterMap );  // Copy so that the original map will not be modified.

    // Collect what parameters should be extracted from input array (rather than use values in the parameterMap).
    // At the same time, its array index will also be recorded for extracting its value from array.
    let arrayIndexMap = new Map();
    {
      let i = 0;
      for ( let [ key, value ] of parameterMap ) {
        // A null value means it should be extracted from inputFloat32Array or fixedWeights (i.e. by evolution).
        if ( !value ) {
          arrayIndexMap.set( key, i ); // Record the index to this.weightsModified[].
          ++i;
        }
      }
    }

    let parameterCountExtracted = arrayIndexMap.size; // Determine how many parameters should be extracted from array.

    // If has fixedWeights, use it as priviledge input.
    let privilegeInput;
    if ( fixedWeights ) {
      if ( fixedWeights instanceof Float32Array )
        privilegeInput = fixedWeights;
      else
        privilegeInput = new Float32Array( fixedWeights );  // Convert to Float32Array.
    }

    // Extract a block of input array.
    let bInitOk = super.init( inputFloat32Array, byteOffsetBegin, privilegeInput, 0, [ parameterCountExtracted ] );

    if ( !bInitOk )
      return false;

    // Copy and convert to integer.
    //
    // Do not modify the original array data. When backtracking (to try another neural network layer
    // configuration), the original data is necessary.
    this.weightsModified = new Float32Array( this.weights );
    this.weightsModified.forEach( ( element, i, array ) => array[ i ] = Base.toPositiveInteger( element ) );

    // Extract (by evolution) values from array into map.
    for ( let [ key, arrayIndex ] of arrayIndexMap ) {
      this.parameterMap.set( key, this.weightsModified[ arrayIndex ] );
    }

    // If original parameterMap has output channel count and its value is infinity, its value will depend
    // on channelMultiplier (i.e. by channelMultiplier).
    //
    // Usually, the embedding layer uses this behavior.
    let outChannelsOriginal = parameterMap.get( Params.Keys.outChannels );
    if ( outChannelsOriginal ) {
      if ( !Number.isFinite( outChannelsOriginal ) ) {
        let channelMultiplier = this.channelMultiplier;   // May be specified or extracted.
        let outChannels = inChannels * channelMultiplier;
        this.parameterMap.set( Params.Keys.outChannels, outChannels );
      } else {
        // Use the original value in parameterMap as output channel count (i.e. by specifying).
      }
    } else {
      // Either use the extracted value as output channel count (i.e. by evolution),
      // or there is no output channel count in fact.
    }

    return bInitOk;
  }

  get parameterCountExtracted() { return this.weightCount; }
  get parameterCount()          { return this.parameterMap.size; }

  // Most kinds of layers have these parameters.

  get inChannels()        { return this.parameterMap.get( Params.Keys.inChannels ); }
  get channelMultiplier() { return this.parameterMap.get( Params.Keys.channelMultiplier ); }
  get outChannels()       { return this.parameterMap.get( Params.Keys.outChannels ); }
}

/**
 * Define parameter keys.
 *
 * They are (static) symbol objects used as keys of Params.init()'s parameterMap. They can be seen inside Map when
 * debugging, and are faster than string (or String object) when Map's key comparing.
 */
Params.Keys = class {
//   // Although they are functions, they should not be called. They are (static function) objects used as keys of
//   // Params.init()'s parameterMap. Using function object (rather than general object) so that the (function) name
//   // can be seen inside Map when debugging. Using function object (rather than string or String object) so that
//   // Map's key comparing is faster.
//   static inChannels()        {}
//   static channelMultiplier() {}
//   static outChannels()       {}
}

Params.Keys.inChannels =        Symbol("inChannels");
Params.Keys.channelMultiplier = Symbol("channelMultiplier");
Params.Keys.outChannels =       Symbol("outChannels");

Params.Keys.dilationHeight =    Symbol("dilationHeight");
Params.Keys.dilationWidth =     Symbol("dilationWidth");
Params.Keys.filterHeight =      Symbol("filterHeight");
Params.Keys.filterWidth =       Symbol("filterWidth");
<commit_msg>Update Weights.js<commit_after>export { Base, Params };

/**
 * A base class for extracting and keeping weights. It composes of a Float32Array and a shape. It can
 * be used as CNN (depthwise, pointwise and bias) filter weights.
 *
 * @member {Float32Array} defaultInput
 *   The default input Float32Array. Its byteOffset will be checked against defaultByteOffsetBegin.
 * Its content will be interpret as weights if privilegeInput is null. Otherwise, its content
 * will be ignored if privilegeInput is not null.
 *
 * @member {number} defaultByteOffsetBegin
 *   The weights[] begins at defaultInput's defaultByteOffsetBegin (relative to defaultInput.buffer,
 * not to defaultInput.byteOffset). If this value less than defaultInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} defaultByteOffsetEnd
 *   The weights[] ends at defaultInput's defaultByteOffsetEnd (not inclusive) (relative to
 * defaultInput.buffer, not to defaultInput.byteOffset).
 *
 * @member {Float32Array} privilegeInput
 *   The privilege input Float32Array. If not null, its content will be interpret as weights and
 * the content of defaultInput will be ignored.
 *
 * @member {number} privilegeByteOffsetBegin
 *   The weights[] begins at privilegeInput's privilegeByteOffsetBegin (relative to privilegeInput.buffer,
 * not to privilegeInput.byteOffset). If this value less than privilegeInput.byteOffset, the
 * initialization will fail (i.e. ( isValid() == false ) ).
 *
 * @member {number} privilegeByteOffsetEnd
 *   The weights[] ends at privilegeInput's privilegeByteOffsetEnd (not inclusive) (relative
 * to privilegeInput.buffer, not to privilegeInput.byteOffset).
 *
 * @member {number[]} shape
 *   The weights shape (element count for every dimension). The shape.length is dimension. If ( shape.length == 0 ),
 * it is 0-dimension (i.e. scalar). The initialization will fail (i.e. ( isValid() == false ) ) if shape is too large
 * (or NaN) (exceeds the defaultInput (or, privilegeInput if not null) bounding).
 *
 * @member {Float32Array} weights
 *  The values. It is a reference (sub-range) to the underlying defaultInput (or privilegeInput).
 */
class Base {

  /**
   * Create Float32Array weights[] over the defaultInput (or privilegeInput) according to the specific
   * byteOffsetBegin, shape, and weightConverter.
   *
   * The defaultInput and privilegeInput can not both be null. If one of them is null, the non-null is used.
   * If both are non-null, the privilegeInput will be used.
   *
   * @return {boolean} Return false, if initialization failed.
   */
  init( defaultInput, defaultByteOffsetBegin, privilegeInput, privilegeByteOffsetBegin, shape ) {

    this.defaultInput =   defaultInput;
    this.privilegeInput = privilegeInput;
    this.shape =          shape;
    this.weights =        null;   // So that ( isValid() == false ) if re-initialization failed.

    //let weightCount =     ( shape ) ? shape.reduce( ( accumulator, currentValue ) => accumulator * currentValue ) : 0;
    let weightCount =     ( shape ) ? tf.sizeFromShape( shape ) : 0; // tf.sizeFromShape() can handle zero length (i.e. scalar).
    let weightByteCount = weightCount * Float32Array.BYTES_PER_ELEMENT;

    let input, byteOffsetBegin;
    let byteOffsetEnd; // Not inclusive. It will be used as the next filter's beginning.

    if ( privilegeInput ) {       // privilegeInput first.

      if ( privilegeByteOffsetBegin < privilegeInput.byteOffset )
        return false;  // Failed, the privilege beginning position is illegal (less than bounding).

      input = privilegeInput;
      byteOffsetBegin = this.privilegeByteOffsetBegin = privilegeByteOffsetBegin;
      byteOffsetEnd =   this.privilegeByteOffsetEnd =   privilegeByteOffsetBegin + weightByteCount;
      this.defaultByteOffsetBegin = this.defaultByteOffsetEnd = defaultByteOffsetBegin; // Stay at beginning for not used.

    } else if ( defaultInput ) {  // defaultInput second.

      if ( defaultByteOffsetBegin < defaultInput.byteOffset )
        return false;  // Failed, the default beginning position is illegal (less than bounding).

      input = defaultInput;
      byteOffsetBegin = this.defaultByteOffsetBegin = defaultByteOffsetBegin;
      byteOffsetEnd =   this.defaultByteOffsetEnd =   defaultByteOffsetBegin + weightByteCount;
      this.privilegeByteOffsetBegin = this.privilegeByteOffsetEnd = privilegeByteOffsetBegin; // Stay at beginning for not used.

    } else {
      return false;  // Failed, both privilege and default input are null.
    }

    // Bounded by the input.byteLength.
    let legalByteOffsetEnd = input.byteOffset + input.byteLength;
    if ( byteOffsetEnd > legalByteOffsetEnd )
      return false;  // Failed, if shape is too large (or NaN).

    this.weights = new Float32Array( input.buffer, byteOffsetBegin, weightCount );  // Share the underlying array buffer.
    return true;     // Success.
  }

  /** @return {number} Return the absolute value of the trucated value. */
  static toPositiveInteger( v ) {
    return Math.abs( Math.trunc( v ) );
  }

  /** @return Return true, if initialization is success (i.e. ( this.weights != null )). */
  isValid()                      { return ( this.weights ) ? true : false; }

  get weightByteCount()          { return this.weights.byteLength; }
  get weightCount()              { return this.weights.length; }
}

/**
 * The parameters for the weights of a neural network layer.
 *
 * @member {Map} parameterMap
 *   All parameters provided by this object. Its keys are the same as init()'s parameterMap. Its values are
 * combined from init()'s parameterMap and inputFloat32Array (or fixedWeights).
 *
 * @member {number} parameterCountExtracted
 *   How many parameters are extracted from inputFloat32Array or fixedWeights in fact. Only existed if init()
 * successfully. The same as this.weightCount (i.e. length of this.weights[] and this.weightsModified[]).
 *
 * @member {number} parameterCount
 *   Always ( parameterMap.size ). This is the total parameter count provided by this object
 * if init() successfully.
 *
 * @member {number} inChannels
 *   The input channel count of this neural network layer.
 *
 * @member {number} channelMultiplier
 *   Every input channel will be expanded into how many channels.
 *
 * @member {number} outChannels
 *   The output channel count of this neural network layer.
 *
 * @member {Float32Array} weightsModified
 *  The copied extracted values. They are copied from inputFloat32Array or fixedWeights, and then converted
 * to positive integer. Its length will be the same as parameterCountExtracted.
 */
class Params extends Base {

  /**
   *
   * The parameterMap should have at least:
   *
   * - Params.Keys.inChannels
   *   {number} There will be how many input channels. (Input channel count) (By specifying)
   *
   *
   * The parameterMap could also have (and any other custom key-value pairs):
   *
   * - Params.Keys.channelMultiplier
   *   {number} Every input channel will be expanded into how many channels.
   *     - If not null, it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   * - Params.Keys.outChannels
   *   {number} There will be how many output channels. (Output channel count)
   *     - If Number.isFinite(), it will be used instead of extracting from inputFloat32Array or fixedWeights. (By specifying)
   *     - If Infinity (Number.POSITIVE_INFINITY), it will be ( inChannels * channelMultiplier ). (By channelMultiplier)
   *     - If null, extracted from inputFloat32Array or fixedWeights. (By evolution)
   *
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights. It should have ( parameterCountExtractedAtLeast ) or
   * ( parameterCountExtractedAtLeast + 1 ) or ( parameterCountExtractedAtLeast + 2 ) elements according to the
   * value of channelMultiplier and outChannels.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {Map} parameterMap
   *   Describe what parameters to be used or extracted. The keys of parameterMap will be viewed as parameter names.
   * The values of parameterMap will be viewed as parameter values. If the value of a [ key, value ] entry is null,
   * the parameter's value will be extracted from inputFloat32Array or fixedWeights (i.e. by evolution). Otherwise,
   * the parameter's value will be the value of the [ key, value ] entry (i.e. by specifying).
   *
   * @param {Float32Array|Array} fixedWeights
   *   If null, extract parameters from inputFloat32Array. If not null, extract parameters from it instead of
   * inputFloat32Array. When not null, it should have parameterCountExtracted elements (i.e. the count of non-null values
   * of parameterMap).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, parameterMap, fixedWeights = null ) {

    this.weightsModified = this.parameterMap = null; // So that distinguishable if re-initialization failed.

    if ( !parameterMap )
      return false;  // Do not know what parameters to be used or extracted.

    let inChannels = parameterMap.get( Params.Keys.inChannels );
    if ( !inChannels )
      return false;  // At least, there should be a (required) parameter (i.e. input channel count).

    this.parameterMap = new Map( parameterMap );  // Copy so that the original map will not be modified.

    // Collect what parameters should be extracted from input array (rather than use values in the parameterMap).
    // At the same time, its array index will also be recorded for extracting its value from array.
    let arrayIndexMap = new Map();
    {
      let i = 0;
      for ( let [ key, value ] of parameterMap ) {
        // A null value means it should be extracted from inputFloat32Array or fixedWeights (i.e. by evolution).
        if ( !value ) {
          arrayIndexMap.set( key, i ); // Record the index to this.weightsModified[].
          ++i;
        }
      }
    }

    let parameterCountExtracted = arrayIndexMap.size; // Determine how many parameters should be extracted from array.

    // If has fixedWeights, use it as priviledge input.
    let privilegeInput;
    if ( fixedWeights ) {
      if ( fixedWeights instanceof Float32Array )
        privilegeInput = fixedWeights;
      else
        privilegeInput = new Float32Array( fixedWeights );  // Convert to Float32Array.
    }

    // Extract a block of input array.
    let bInitOk = super.init( inputFloat32Array, byteOffsetBegin, privilegeInput, 0, [ parameterCountExtracted ] );

    if ( !bInitOk )
      return false;

    // Copy and convert to integer.
    //
    // Do not modify the original array data. When backtracking (to try another neural network layer
    // configuration), the original data is necessary.
    this.weightsModified = new Float32Array( this.weights );
//!!! (2020/12/16 Remarked) Loop is faster than function call.
//     this.weightsModified.forEach( ( element, i, array ) => array[ i ] = Base.toPositiveInteger( element ) );
    for ( let i = 0; i < this.weightsModified.length; ++i ) {
      this.weightsModified[ i ] = Base.toPositiveInteger( this.weightsModified[ i ] );
    }

    // Extract (by evolution) values from array into map.
    for ( let [ key, arrayIndex ] of arrayIndexMap ) {
      this.parameterMap.set( key, this.weightsModified[ arrayIndex ] );
    }

    // If original parameterMap has output channel count and its value is infinity, its value will depend
    // on channelMultiplier (i.e. by channelMultiplier).
    //
    // Usually, the embedding layer uses this behavior.
    let outChannelsOriginal = parameterMap.get( Params.Keys.outChannels );
    if ( outChannelsOriginal ) {
      if ( !Number.isFinite( outChannelsOriginal ) ) {
        let channelMultiplier = this.channelMultiplier;   // May be specified or extracted.
        let outChannels = inChannels * channelMultiplier;
        this.parameterMap.set( Params.Keys.outChannels, outChannels );
      } else {
        // Use the original value in parameterMap as output channel count (i.e. by specifying).
      }
    } else {
      // Either use the extracted value as output channel count (i.e. by evolution),
      // or there is no output channel count in fact.
    }

    return bInitOk;
  }

  get parameterCountExtracted() { return this.weightCount; }
  get parameterCount()          { return this.parameterMap.size; }

  // Most kinds of layers have these parameters.

  get inChannels()        { return this.parameterMap.get( Params.Keys.inChannels ); }
  get channelMultiplier() { return this.parameterMap.get( Params.Keys.channelMultiplier ); }
  get outChannels()       { return this.parameterMap.get( Params.Keys.outChannels ); }
}

/**
 * Define parameter keys.
 *
 * They are (static) symbol objects used as keys of Params.init()'s parameterMap. They can be seen inside Map when
 * debugging, and are faster than string (or String object) when Map's key comparing.
 */
Params.Keys = class {
//   // Although they are functions, they should not be called. They are (static function) objects used as keys of
//   // Params.init()'s parameterMap. Using function object (rather than general object) so that the (function) name
//   // can be seen inside Map when debugging. Using function object (rather than string or String object) so that
//   // Map's key comparing is faster.
//   static inChannels()        {}
//   static channelMultiplier() {}
//   static outChannels()       {}
}

Params.Keys.inChannels =        Symbol("inChannels");
Params.Keys.channelMultiplier = Symbol("channelMultiplier");
Params.Keys.outChannels =       Symbol("outChannels");

Params.Keys.dilationHeight =    Symbol("dilationHeight");
Params.Keys.dilationWidth =     Symbol("dilationWidth");
Params.Keys.filterHeight =      Symbol("filterHeight");
Params.Keys.filterWidth =       Symbol("filterWidth");
<|endoftext|>