<commit_before>/*
 * Functionality for grouping tabs.
 *
 * Groups are implemented as a special kind of tab (see binding in
 * group.xml) that isn't selectable.  There are a few advantages and
 * disadvantages to this:
 *
 *   - Groups can be regular children of tabbrowser.tabContainer
 *     (cf. https://bugzilla.mozilla.org/show_bug.cgi?id=475142).
 *
 *   - The nsISessionStore service takes care of restoring groups and
 *     their properties.
 *
 *   - But we have to make sure that groups don't behave like tabs at
 *     all.
 */

var EXPORTED_SYMBOLS = ["VTGroups"];
Components.utils.import("resource://verticaltabs/tabdatastore.js");

function VTGroups(tabs) {
    this.tabs = tabs;
    tabs.VTGroups = this;

     // Hashmap (id -> tab) for easy access to tabs via id (assigned by us).
     // Necessary until https://bugzilla.mozilla.org/show_bug.cgi?id=529477
     // is implemented.
    this.tabsById = {};

    tabs.addEventListener('TabOpen', this, true);
    tabs.addEventListener('TabClose', this, true);
    tabs.addEventListener('SSTabRestoring', this, true);
    for (let i=0; i < tabs.childNodes.length; i++) {
        this.initTab(tabs.childNodes[i]);
    }
}
VTGroups.prototype = {

    kId: 'verticaltabs-id',
    kGroup: 'verticaltabs-group',
    kInGroup: 'verticaltabs-ingroup',
    kChildren: 'verticaltabs-children',
    kLabel: 'verticaltabs-grouplabel',

    initTab: function(aTab) {
        if (aTab.hasAttribute(this.kId)) {
            return;
        }

        let id = VTTabDataStore.getTabValue(aTab, this.kId) || this.makeNewId();
        aTab.setAttribute(this.kId, id);
        if (!(id in this.tabsById)) {
            this.tabsById[id] = aTab;
        }
    },

    destroyTab: function(aTab) {
        var id = aTag.getAttribute(this.kId);
        if (id && (id in this.tabsById)) {
            delete this.tabsById[id];
        }
    },

    restoreTab: function(aTab) {
        // Restore tab attributes from session data (this isn't done
        // automatically).  kId is already restored in initTab()
        for each (let attr in [this.kGroup,
                               this.kInGroup,
                               this.kChildren,
                               this.kLabel]) {
            let value = VTTabDataStore.getTabValue(aTab, attr);
            if (value) {
                aTab.setAttribute(attr, value);
            }
        }

        // Restore the original ID
        let oldId = aTab.getAttribute(this.kId);
        let newId = VTTabDataStore.getTabValue(aTab, this.kId);
        Components.utils.reportError(oldId + ' ' + newId);
        if (oldId && newId) {
            delete this.tabsById[oldId];
            aTab.setAttribute(this.kId, newId);
            this.tabsById[newId] = aTab;
        }
    },

    makeNewId: function() {
        return 'tab-<' + Date.now() + '-'
               + parseInt(Math.random() * 65000) + '>';
    },

    /*** Public API ***/

    addGroup: function(aLabel) {
        var group = this.tabs.tabbrowser.addTab();
        VTTabDataStore.setTabValue(group, this.kGroup, 'true');
        VTTabDataStore.setTabValue(group, this.kChildren, '');

        //XXX this doesn't work since the binding isn't made available
        // synchronously :(
/*
        if (aLabel) {
            VTTabDataStore.setTabValue(group, this.kLabel, aLabel);
            group.groupLabel = aLabel;
        } else {
            group.editLabel();
        }
*/
        return group;
    },

    getChildren: function(aGroup) {
        var childIds = VTTabDataStore.getTabValue(aGroup, this.kChildren);
        if (!childIds) {
            return [];
        }
        return [this.tabsById[id] for each (id in childIds.split('|'))];
    },

    addChild: function(aGroup, aTab) {
        // Only groups can have children
        if (!this.isGroup(aGroup)) {
            return;
        }
        // We don't allow nested groups
        if (this.isGroup(aTab)) {
            return;
        }

        var groupId = aGroup.getAttribute(this.kId);
        VTTabDataStore.setTabValue(aTab, this.kInGroup, groupId);
        var groupChildren = VTTabDataStore.getTabValue(aGroup, this.kChildren);
        // TODO this doesn't preserve any order
        if (!groupChildren) {
            groupChildren = aTab.getAttribute(this.kId);
        } else {
            groupChildren += '|' + aTab.getAttribute(this.kId);
        }
        VTTabDataStore.setTabValue(aGroup, this.kChildren, groupChildren);
    },

    createGroupFromMultiSelect: function() {
        var group = this.addGroup();
        var children = this.tabs.VTMultiSelect.getMultiSelection();
        for each (let tab in children) {
            this.addChild(group, tab);
            this.tabs.tabbrowser.moveTabTo(tab, group._tPos+1);  //XXX
        }
    },

    isGroup: function(aTab) {
        return (VTTabDataStore.getTabValue(aTab, this.kGroup) == "true");
    },

    /*** Event handlers ***/

    handleEvent: function(aEvent) {
        switch (aEvent.type) {
        case 'TabOpen':
            this.initTab(aEvent.originalTarget);
            return;
        case 'TabClose':
            this.destroyTab(aEvent.originalTarget);
            return;
        case 'SSTabRestoring':
            this.restoreTab(aEvent.originalTarget);
            return;
        }
    }

};
<commit_msg>Don't forget to remember the ID for new tabs.  Remove debug output.<commit_after>/*
 * Functionality for grouping tabs.
 *
 * Groups are implemented as a special kind of tab (see binding in
 * group.xml) that isn't selectable.  There are a few advantages and
 * disadvantages to this:
 *
 *   - Groups can be regular children of tabbrowser.tabContainer
 *     (cf. https://bugzilla.mozilla.org/show_bug.cgi?id=475142).
 *
 *   - The nsISessionStore service takes care of restoring groups and
 *     their properties.
 *
 *   - But we have to make sure that groups don't behave like tabs at
 *     all.
 */

var EXPORTED_SYMBOLS = ["VTGroups"];
Components.utils.import("resource://verticaltabs/tabdatastore.js");

function VTGroups(tabs) {
    this.tabs = tabs;
    tabs.VTGroups = this;

     // Hashmap (id -> tab) for easy access to tabs via id (assigned by us).
     // Necessary until https://bugzilla.mozilla.org/show_bug.cgi?id=529477
     // is implemented.
    this.tabsById = {};

    tabs.addEventListener('TabOpen', this, true);
    tabs.addEventListener('TabClose', this, true);
    tabs.addEventListener('SSTabRestoring', this, true);
    for (let i=0; i < tabs.childNodes.length; i++) {
        this.initTab(tabs.childNodes[i]);
    }
}
VTGroups.prototype = {

    kId: 'verticaltabs-id',
    kGroup: 'verticaltabs-group',
    kInGroup: 'verticaltabs-ingroup',
    kChildren: 'verticaltabs-children',
    kLabel: 'verticaltabs-grouplabel',

    initTab: function(aTab) {
        if (aTab.hasAttribute(this.kId)) {
            return;
        }

        let id = VTTabDataStore.getTabValue(aTab, this.kId) || this.makeNewId();
        VTTabDataStore.setTabValue(aTab, this.kId, id);
        if (!(id in this.tabsById)) {
            this.tabsById[id] = aTab;
        }
    },

    destroyTab: function(aTab) {
        var id = aTag.getAttribute(this.kId);
        if (id && (id in this.tabsById)) {
            delete this.tabsById[id];
        }
    },

    restoreTab: function(aTab) {
        // Restore tab attributes from session data (this isn't done
        // automatically).  kId is already restored in initTab()
        for each (let attr in [this.kGroup,
                               this.kInGroup,
                               this.kChildren,
                               this.kLabel]) {
            let value = VTTabDataStore.getTabValue(aTab, attr);
            if (value) {
                aTab.setAttribute(attr, value);
            }
        }

        // Restore the original ID
        let oldId = aTab.getAttribute(this.kId);
        let newId = VTTabDataStore.getTabValue(aTab, this.kId);
        if (oldId && newId) {
            delete this.tabsById[oldId];
            aTab.setAttribute(this.kId, newId);
            this.tabsById[newId] = aTab;
        }
    },

    makeNewId: function() {
        return 'tab-<' + Date.now() + '-'
               + parseInt(Math.random() * 65000) + '>';
    },

    /*** Public API ***/

    addGroup: function(aLabel) {
        var group = this.tabs.tabbrowser.addTab();
        VTTabDataStore.setTabValue(group, this.kGroup, 'true');
        VTTabDataStore.setTabValue(group, this.kChildren, '');

        //XXX this doesn't work since the binding isn't made available
        // synchronously :(
/*
        if (aLabel) {
            VTTabDataStore.setTabValue(group, this.kLabel, aLabel);
            group.groupLabel = aLabel;
        } else {
            group.editLabel();
        }
*/
        return group;
    },

    getChildren: function(aGroup) {
        var childIds = VTTabDataStore.getTabValue(aGroup, this.kChildren);
        if (!childIds) {
            return [];
        }
        return [this.tabsById[id] for each (id in childIds.split('|'))];
    },

    addChild: function(aGroup, aTab) {
        // Only groups can have children
        if (!this.isGroup(aGroup)) {
            return;
        }
        // We don't allow nested groups
        if (this.isGroup(aTab)) {
            return;
        }

        var groupId = aGroup.getAttribute(this.kId);
        VTTabDataStore.setTabValue(aTab, this.kInGroup, groupId);
        var groupChildren = VTTabDataStore.getTabValue(aGroup, this.kChildren);
        // TODO this doesn't preserve any order
        if (!groupChildren) {
            groupChildren = aTab.getAttribute(this.kId);
        } else {
            groupChildren += '|' + aTab.getAttribute(this.kId);
        }
        VTTabDataStore.setTabValue(aGroup, this.kChildren, groupChildren);
    },

    createGroupFromMultiSelect: function() {
        var group = this.addGroup();
        var children = this.tabs.VTMultiSelect.getMultiSelection();
        for each (let tab in children) {
            this.addChild(group, tab);
            this.tabs.tabbrowser.moveTabTo(tab, group._tPos+1);  //XXX
        }
    },

    isGroup: function(aTab) {
        return (VTTabDataStore.getTabValue(aTab, this.kGroup) == "true");
    },

    /*** Event handlers ***/

    handleEvent: function(aEvent) {
        switch (aEvent.type) {
        case 'TabOpen':
            this.initTab(aEvent.originalTarget);
            return;
        case 'TabClose':
            this.destroyTab(aEvent.originalTarget);
            return;
        case 'SSTabRestoring':
            this.restoreTab(aEvent.originalTarget);
            return;
        }
    }

};
<|endoftext|>


<commit_before>/*jshint nomen:false */
/*globals require, console, complete, __dirname, process */

'use strict';

var exec = require('child_process').exec,

commands = {
    minify: './node_modules/.bin/uglifyjs ./src/vagueTime.js --compress --mangle --output ./src/vagueTime.min.js',
    test: './node_modules/.bin/mocha --ui tdd --reporter spec --colors ./test/vagueTime.js',
    lint: './node_modules/.bin/jshint ./src/vagueTime.js --config config/jshint.json',
    prepare: 'npm install'
};

desc('Minify the source code for deployment.');
task('minify', function () {
    runTask('minify', 'Minifying...');
}, {
    async: true
});

desc('Run the unit tests.');
task('test', function () {
    runTask('test', 'Testing...');
}, {
    async: true
});

desc('Lint the source code.');
task('lint', function () {
    runTask('lint', 'Linting...');
}, {
    async: true
});

desc('Install dependencies.');
task('prepare', function () {
    runTask('prepare', 'Preparing the build environment...');
}, {
    async: true
});

function runTask (command, message) {
    console.log(message);
    runCommand(command);
}

function runCommand (command) {
    exec(commands[command], { cwd: __dirname }, function (error, stdout, stderr) {
        console.log(stdout);
        console.log(stderr);
        if (typeof error === 'object' && error !== null) {
            console.log(error.message);
            process.exit(1);
        }
        complete();
    });
}

<commit_msg>Simplify build script.<commit_after>/*jshint nomen:false */
/*globals require, console, complete, __dirname, process */

'use strict';

var exec = require('child_process').exec,

commands = {
    minify: './node_modules/.bin/uglifyjs ./src/vagueTime.js --compress --mangle --output ./src/vagueTime.min.js',
    test: './node_modules/.bin/mocha --ui tdd --reporter spec --colors ./test/vagueTime.js',
    lint: './node_modules/.bin/jshint ./src/vagueTime.js --config config/jshint.json',
    prepare: 'npm install'
};

desc('Minify the source code for deployment.');
task('minify', function () {
    runCommand('minify', 'Minifying...');
}, {
    async: true
});

desc('Run the unit tests.');
task('test', function () {
    runCommand('test', 'Testing...');
}, {
    async: true
});

desc('Lint the source code.');
task('lint', function () {
    runCommand('lint', 'Linting...');
}, {
    async: true
});

desc('Install dependencies.');
task('prepare', function () {
    runCommand('prepare', 'Preparing the build environment...');
}, {
    async: true
});

function runCommand (command, message) {
    console.log(message);
    exec(commands[command], { cwd: __dirname }, function (error, stdout, stderr) {
        console.log(stdout);
        console.log(stderr);
        if (typeof error === 'object' && error !== null) {
            console.log(error.message);
            process.exit(1);
        }
        complete();
    });
}

<|endoftext|>


<commit_before>//

function AssignSexualPartner(Person, Time){
	// The purpose of this function is to assign the duration of relationship and the relevant connection in the relationship
	// It does so by trying to bring the population rates up to the levels of sexual partnership expected for the population as releationships end

	// Determine how many relationships the population is missing
	var TotalSex=[];
	TotalSex[0]=0;//None

	TotalSex[1]=0;//Regular
	TotalSex[2]=0;//Other
	TotalSex[3]=0;//RegularAndOther

	var TotalSexAny=0;//Any

	for (var Pn in Person){
		if (Person[Pn].Alive(Time)==true && Person[Pn].IDU.Use(Time)>2){// is a current alive injector
			var SexIndex=Person[Pn].SexualPartner.Value(Time);
			TotalSex[SexIndex]++;
			if (SexIndex>0){
				TotalSexAny++;
			}
		}
	}

	//Find proportion having sex
	var TotalPeople=TotalSex[0]+TotalSexAny;
	var ProportionAnySex=TotalSexAny/TotalPeople;

	if (ProportionAnySex<Param.IDU.Sex.AnyLastMonth){
		var NumberOfPeopleToStartAnyRelationship=Round(TotalPeople*(Param.IDU.Sex.AnyLastMonth-ProportionAnySex));

		// Work out what the category will be by maintaining the same ratio
		var NumberInPartnerCategory=[TotalSex[1], TotalSex[2], TotalSex[3]];
		var AimProp=[Param.IDU.RegularLastMonth, Param.IDU.OtherLastMonth, Param.IDU.RegularAndOtherLastMonth];
		var AimNumberInPartnerCategory=Times(TotalSexAny, AimProp);

		for (var AddCount=0; AddCount<NumberOfPeopleToStartAnyRelationship; AddCount++){
			// add people into relationship type as appropriate

			var CategorySelectionWeight=Minus(AimNumberInPartnerCategory, NumberInPartnerCategory);
			// if the prop weight is negative, ignore unless they are all negative, then set all to one
			for (var CSWR in CategorySelectionWeight){
				if (CategorySelectionWeight[CSWR]<0){// to avoid situations where a
					CategorySelectionWeight[CSWR]=0;
				}
			}
			var PartnershipType=RandSampleWeighted(CategorySelectionWeight)+1;
			NumberInPartnerCategory[PartnershipType-1]++;// add people to category such that the proportions balance

			// Chose people at random until you have 10 eligible
			var SamplesFound=0;
			var TestIndex;
			var TestIndexSelectionVector=[];
			var TestIndexSelectionWeight=[];
			while (SamplesFound<10){
				TestIndex=Floor(Rand.Value()*Person.length);
				// Check that the person is alive and eligible to start a new relationship
				if (Person[TestIndex].Alive(Time)){

					// Here is where you make the selection of the relationship type of the PWID
					// if 1, then can only be zero
					// 		reduce total by 1, add 1 to category
					// if 2, then also can only be zero
					// 		reduce total by 1, add 1 to category
					// if 3, then can only be 1 or 2
					// 		reduce total by 0, add 1 to category
					// remember to perform the same test on the other partner!

					if (Person[Pi].SexualPartner.Value(Time)!=1 && Person[Pi].SexualPartner.Value(Time)!=3 ){// if the person doesn't already have a sexual partner
						// note: this section should probably include something that determines the probability that the person will be sexually active
						TestIndexSelectionVector.push(TestIndex);
						TestIndexSelectionWeight.push(SexualRelationship.RateOfSexAtAge(Person[TestIndex].Age(Time)));
						SamplesFound++;
					}
				}
			}

			// Do the simple index selection of a single element based on assumed sexual activity
			var IndexOfTestIndexToSelect=RandSampleWeighted(TestIndexSelectionWeight);
			var SelectedPersonIndex=TestIndexSelectionVector[IndexOfTestIndexToSelect];

			var SelectedPerson=Person[SelectedPersonIndex];

			// Determine the age of the partner that we are aiming for.
			// if Person=MSM

			// if Person=Heterosexual
				// Determine the sex of the partner
				var PartnerSex=Abs(SelectedPerson.Sex-1);// a new algorithm needs to go here to determine the sexual partner sex
				var AgeAim;
				if (SelectedPerson.Sex==0){
					AgeAim=SexualRelationship.ChooseMaleAge();
				}
				else{
					AgeAim=SexualRelationship.ChooseFemaleAge();
				}

			// Determine probability that an IDU individual will form a relationship with someone who is not an injector
			// Note that there are more males than females.
			if (Rand.Value()<(IDU.Sex.RegularPartnerInjects/2)){// note that we divide by 2 to get the right proportion because this creates a new link between 2 people who are not in a regular relationship
				// Select partner from injecting population

				// Search injectors for matches
				var AgeDiff;
				var AgeDiffChosen=1e9;
				var Pi2;

				// The function keeps looking for someone until a level of difference in age is reached that cannot be expected to be beaten.
				// a random index is chosen, and a limited number of selections take place (e.g. only 1000 people are taken into account when selecting)
				// an eventuality needs to be taken into consideration where people don't make a partnership, i.e. there is no one in the group that matches their needs

				for (var Pi2 in Person){
					if (Person[Pi2].Sex==PartnerSex && Pi2!=SelectedPersonIndex){
						AgeDiff=Abs(AgeAim-Person[Pi2].Age(Time));
						if (AgeDiff<AgeDiffChosen){
							SelectedPersonIndex2=Pi2;
						}
					}
				}
				// Add the sexual relationship to the person
				Person[SelectedPersonIndex].SexualPartner.Set(1, Time);
				Person[SelectedPersonIndex].SexualPartnerID.Set(SelectedPersonIndex2, Time);
				Person[SelectedPersonIndex].SexualPartnerInjects.Set(1, Time);
				// Add the sexual partnership to this other person
				Person[SelectedPersonIndex2].SexualPartner.Set(1, Time);
				Person[SelectedPersonIndex2].SexualPartnerID.Set(SelectedPersonIndex, Time);
				Person[SelectedPersonIndex2].SexualPartnerInjects.Set(1, Time);

				// If regular, determine relationship length
				var ThisPartnershipDuration=DeterminePartnerDuration(Param.IDU.Sex.PPartnerChangeYear1,Param.IDU.Sex.PPartnerChangeYear1);

				this.SexualPartner.Set(0, Time+ThisPartnershipDuration);
				this.SexualPartnerID.Set(-1, Time+ThisPartnershipDuration);
				this.SexualPartnerInjects.Set(0, Time+ThisPartnershipDuration);

			}
			else {// If the partner is not an injector
				// Create an individual of the appropriate age

			}

			// Choose partner by age
			// Randomly choose a person
			// Check if they

			// choose a person based on a weighting by age (this is from a list of people compiled outside the loop
				// each person is given a score out of 100
				// the total score for the population is determined
				// a random number is chosen between 0 and the total
				// run down the array, adding until the total is beaten
				// remove the element from the array
				// reduce the total by the weighting of the individual removed

			// Determine if the regular partner also injects
			// If true, find someone in the simulation to match with based on age

			// Select partners at random to match

			// Add this to the partnership records

			// if it is a person outside the scope of the simulation, they get a negative number
		}
	}

	// Give individuals relationships
		// Using Dyadic Data for a Network Analysis of HIV Infection and Risk Behaviors Among Injecting Drug Users
		// Alan Neaigus, Samuel R. Friedman, Marjorie Goldstein, Gilbert Ildefonso, Richard Curtis, and Benny Jose
		// Table 4, <1 year 36% 1-5 years 29% >5 Years 35%

		// TABLE 4. Duration of drug injectors’ relationships.
		// Length of time they have: 1 Year > 1 Year,
		// 5 years > 5 Years
		// Injected with their drug injecting
		// network members
		// Known their drug-injecting
		// network members
		// 44% 33% 23%
		// 28% 30% 42%
		// Been having sex with their sex
		// partners
		// 36% 29% 35%
}<commit_msg>Removing error<commit_after>//

function AssignSexualPartner(Person, Time){
	// The purpose of this function is to assign the duration of relationship and the relevant connection in the relationship
	// It does so by trying to bring the population rates up to the levels of sexual partnership expected for the population as releationships end

	// Determine how many relationships the population is missing
	var TotalSex=[];
	TotalSex[0]=0;//None

	TotalSex[1]=0;//Regular
	TotalSex[2]=0;//Other
	TotalSex[3]=0;//RegularAndOther

	var TotalSexAny=0;//Any

	for (var Pn in Person){
		if (Person[Pn].Alive(Time)==true && Person[Pn].IDU.Use.Value(Time)>2){// is a current alive injector
			var SexIndex=Person[Pn].SexualPartner.Value(Time);
			TotalSex[SexIndex]++;
			if (SexIndex>0){
				TotalSexAny++;
			}
		}
	}

	//Find proportion having sex
	var TotalPeople=TotalSex[0]+TotalSexAny;
	var ProportionAnySex=TotalSexAny/TotalPeople;

	if (ProportionAnySex<Param.IDU.Sex.AnyLastMonth){
		var NumberOfPeopleToStartAnyRelationship=Round(TotalPeople*(Param.IDU.Sex.AnyLastMonth-ProportionAnySex));

		// Work out what the category will be by maintaining the same ratio
		var NumberInPartnerCategory=[TotalSex[1], TotalSex[2], TotalSex[3]];
		var AimProp=[Param.IDU.RegularLastMonth, Param.IDU.OtherLastMonth, Param.IDU.RegularAndOtherLastMonth];
		var AimNumberInPartnerCategory=Times(TotalSexAny, AimProp);

		for (var AddCount=0; AddCount<NumberOfPeopleToStartAnyRelationship; AddCount++){
			// add people into relationship type as appropriate

			var CategorySelectionWeight=Minus(AimNumberInPartnerCategory, NumberInPartnerCategory);
			// if the prop weight is negative, ignore unless they are all negative, then set all to one
			for (var CSWR in CategorySelectionWeight){
				if (CategorySelectionWeight[CSWR]<0){// to avoid situations where a
					CategorySelectionWeight[CSWR]=0;
				}
			}
			var PartnershipType=RandSampleWeighted(CategorySelectionWeight)+1;
			NumberInPartnerCategory[PartnershipType-1]++;// add people to category such that the proportions balance

			// Chose people at random until you have 10 eligible
			var SamplesFound=0;
			var TestIndex;
			var TestIndexSelectionVector=[];
			var TestIndexSelectionWeight=[];
			while (SamplesFound<10){
				TestIndex=Floor(Rand.Value()*Person.length);
				// Check that the person is alive and eligible to start a new relationship
				if (Person[TestIndex].Alive(Time)){

					// Here is where you make the selection of the relationship type of the PWID
					// if 1, then can only be zero
					// 		reduce total by 1, add 1 to category
					// if 2, then also can only be zero
					// 		reduce total by 1, add 1 to category
					// if 3, then can only be 1 or 2
					// 		reduce total by 0, add 1 to category
					// remember to perform the same test on the other partner!

					if (Person[Pi].SexualPartner.Value(Time)!=1 && Person[Pi].SexualPartner.Value(Time)!=3 ){// if the person doesn't already have a sexual partner
						// note: this section should probably include something that determines the probability that the person will be sexually active
						TestIndexSelectionVector.push(TestIndex);
						TestIndexSelectionWeight.push(SexualRelationship.RateOfSexAtAge(Person[TestIndex].Age(Time)));
						SamplesFound++;
					}
				}
			}

			// Do the simple index selection of a single element based on assumed sexual activity
			var IndexOfTestIndexToSelect=RandSampleWeighted(TestIndexSelectionWeight);
			var SelectedPersonIndex=TestIndexSelectionVector[IndexOfTestIndexToSelect];

			var SelectedPerson=Person[SelectedPersonIndex];

			// Determine the age of the partner that we are aiming for.
			// if Person=MSM

			// if Person=Heterosexual
				// Determine the sex of the partner
				var PartnerSex=Abs(SelectedPerson.Sex-1);// a new algorithm needs to go here to determine the sexual partner sex
				var AgeAim;
				if (SelectedPerson.Sex==0){
					AgeAim=SexualRelationship.ChooseMaleAge();
				}
				else{
					AgeAim=SexualRelationship.ChooseFemaleAge();
				}

			// Determine probability that an IDU individual will form a relationship with someone who is not an injector
			// Note that there are more males than females.
			if (Rand.Value()<(IDU.Sex.RegularPartnerInjects/2)){// note that we divide by 2 to get the right proportion because this creates a new link between 2 people who are not in a regular relationship
				// Select partner from injecting population

				// Search injectors for matches
				var AgeDiff;
				var AgeDiffChosen=1e9;
				var Pi2;

				// The function keeps looking for someone until a level of difference in age is reached that cannot be expected to be beaten.
				// a random index is chosen, and a limited number of selections take place (e.g. only 1000 people are taken into account when selecting)
				// an eventuality needs to be taken into consideration where people don't make a partnership, i.e. there is no one in the group that matches their needs

				for (var Pi2 in Person){
					if (Person[Pi2].Sex==PartnerSex && Pi2!=SelectedPersonIndex){
						AgeDiff=Abs(AgeAim-Person[Pi2].Age(Time));
						if (AgeDiff<AgeDiffChosen){
							SelectedPersonIndex2=Pi2;
						}
					}
				}
				// Add the sexual relationship to the person
				Person[SelectedPersonIndex].SexualPartner.Set(1, Time);
				Person[SelectedPersonIndex].SexualPartnerID.Set(SelectedPersonIndex2, Time);
				Person[SelectedPersonIndex].SexualPartnerInjects.Set(1, Time);
				// Add the sexual partnership to this other person
				Person[SelectedPersonIndex2].SexualPartner.Set(1, Time);
				Person[SelectedPersonIndex2].SexualPartnerID.Set(SelectedPersonIndex, Time);
				Person[SelectedPersonIndex2].SexualPartnerInjects.Set(1, Time);

				// If regular, determine relationship length
				var ThisPartnershipDuration=DeterminePartnerDuration(Param.IDU.Sex.PPartnerChangeYear1,Param.IDU.Sex.PPartnerChangeYear1);

				this.SexualPartner.Set(0, Time+ThisPartnershipDuration);
				this.SexualPartnerID.Set(-1, Time+ThisPartnershipDuration);
				this.SexualPartnerInjects.Set(0, Time+ThisPartnershipDuration);

			}
			else {// If the partner is not an injector
				// Create an individual of the appropriate age

			}

			// Choose partner by age
			// Randomly choose a person
			// Check if they

			// choose a person based on a weighting by age (this is from a list of people compiled outside the loop
				// each person is given a score out of 100
				// the total score for the population is determined
				// a random number is chosen between 0 and the total
				// run down the array, adding until the total is beaten
				// remove the element from the array
				// reduce the total by the weighting of the individual removed

			// Determine if the regular partner also injects
			// If true, find someone in the simulation to match with based on age

			// Select partners at random to match

			// Add this to the partnership records

			// if it is a person outside the scope of the simulation, they get a negative number
		}
	}

	// Give individuals relationships
		// Using Dyadic Data for a Network Analysis of HIV Infection and Risk Behaviors Among Injecting Drug Users
		// Alan Neaigus, Samuel R. Friedman, Marjorie Goldstein, Gilbert Ildefonso, Richard Curtis, and Benny Jose
		// Table 4, <1 year 36% 1-5 years 29% >5 Years 35%

		// TABLE 4. Duration of drug injectors’ relationships.
		// Length of time they have: 1 Year > 1 Year,
		// 5 years > 5 Years
		// Injected with their drug injecting
		// network members
		// Known their drug-injecting
		// network members
		// 44% 33% 23%
		// 28% 30% 42%
		// Been having sex with their sex
		// partners
		// 36% 29% 35%
}<|endoftext|>


<commit_before>/*
 * Functionality for grouping tabs.
 *
 * Groups are implemented as a special kind of tab (see binding in
 * group.xml).  There are a few advantages and disadvantages to this:
 *
 *   - Groups can be regular children of tabbrowser.tabContainer
 *     (cf. https://bugzilla.mozilla.org/show_bug.cgi?id=475142).
 *
 *   - The nsISessionStore service takes care of restoring groups and
 *     their properties.
 *
 *   - But we have to make sure that groups don't behave like tabs at
 *     all.
 */

const EXPORTED_SYMBOLS = ["VTGroups"];
Components.utils.import("resource://verticaltabs/tabdatastore.js");

const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";

function VTGroups(tabs) {
    this.tabs = tabs;
    tabs.VTGroups = this;

    // Restore group and in-group status
    tabs.addEventListener('SSTabRestoring', this, true);

    // Updating UI
    tabs.addEventListener('TabSelect', this, false);

    // For clicks on the twisty
    tabs.addEventListener('click', this, true);

    // For synchronizing group behaviour and tab positioning
    tabs.addEventListener('dragover', this, false);
    tabs.addEventListener('dragenter', this, false);
    tabs.addEventListener('dragleave', this, false);
    tabs.addEventListener('dragend', this, false);
    tabs.addEventListener('drop', this, false);
    tabs.addEventListener('TabMove', this, false);
    tabs.addEventListener('TabClose', this, false);
}
VTGroups.prototype = {

    kId: 'verticaltabs-id',
    kGroup: 'verticaltabs-group',
    kInGroup: 'verticaltabs-ingroup',
    kLabel: 'verticaltabs-grouplabel',
    kCollapsed: 'verticaltabs-collapsed',
    kDropTarget: 'verticaltabs-droptarget',
    kDropInGroup: 'verticaltabs-dropingroup',
    kDropToNewGroup: 'verticaltabs-droptonewgroup',
    kIgnoreMove: 'verticaltabs-ignoremove',

    /*** Public API ***/

    /*
     * Create a new group tab.  If given as an argument, the label is
     * applied to the group.  Otherwise the label will be made
     * editable.
     */
    addGroup: function(aLabel) {
        let group = this.tabs.tabbrowser.addTab();
        VTTabDataStore.setTabValue(group, this.kGroup, "true");

        let window = this.tabs.ownerDocument.defaultView;
        function makeLabelEditable() {
            // XBL bindings aren't applied synchronously.
            if (typeof group.editLabel !== "function") {
                window.setTimeout(makeLabelEditable, 10);
                return;
            }
            group.editLabel();
        }

        if (aLabel) {
            VTTabDataStore.setTabValue(group, this.kLabel, aLabel);
            group.groupLabel = aLabel;
        } else {
            makeLabelEditable();
        }

        return group;
    },

    /*
     * Return the child tabs of a given group.  The return value is a
     * JavaScript Array (not just a NodeList) and is "owned" by the
     * caller (e.g. it may be modified).
     */
    getChildren: function(aGroup) {
        let groupId = this.tabs.VTTabIDs.id(aGroup);
        let children = this.tabs.getElementsByAttribute(this.kInGroup, groupId);
        // Return a copy
        return Array.prototype.slice.call(children);
    },

    /*
     * Add a tab to a group.  This won't physically move the tab
     * anywhere, just create the logical connection.
     */
    addChild: function(aGroup, aTab) {
        // Only groups can have children
        if (!this.isGroup(aGroup)) {
            return;
        }
        // We don't allow nested groups
        if (this.isGroup(aTab)) {
            return;
        }

        // Assign a group to the tab.  If the tab was in another group
        // before, this will simply overwrite the old value.
        let groupId = this.tabs.VTTabIDs.id(aGroup);
        VTTabDataStore.setTabValue(aTab, this.kInGroup, groupId);

        // Apply the group's collapsed state to the tab
        let collapsed = (VTTabDataStore.getTabValue(aGroup, this.kCollapsed)
                         == "true");
        this._tabCollapseExpand(aTab, collapsed);
    },

    addChildren: function(aGroup, aTabs) {
        for each (let tab in aTabs) {
            this.addChild(aGroup, tab);
        }
    },

    /*
     * Remove a tab from its group.
     */
    removeChild: function(aTab) {
        let groupId = VTTabDataStore.getTabValue(aTab, this.kInGroup);
        if (!groupId) {
            return;
        }

        VTTabDataStore.deleteTabValue(aTab, this.kInGroup);
    },

    removeChildren: function(aTabs) {
        for each (let tab in aTabs) {
            this.removeChild(tab);
        }
    },

    /*
     * Creates a tab from the active selection.
     */
    createGroupFromMultiSelect: function() {
        let group = this.addGroup();
        let children = this.tabs.VTMultiSelect.getSelected();
        for each (let tab in children) {
            // Moving the tabs to the right position is enough, the
            // TabMove handler knows the right thing to do.
            this.tabs.tabbrowser.moveTabTo(tab, group._tPos+1);
        }
        this.tabs.VTMultiSelect.clear();
    },

    /*
     * Return true if a given tab is a group tab.
     */
    isGroup: function(aTab) {
        return (VTTabDataStore.getTabValue(aTab, this.kGroup) == "true");
    },

    /*
     * Toggle collapsed/expanded state of a group tab.
     */
    collapseExpand: function(aGroup) {
        if (!this.isGroup(aGroup)) {
            return;
        }
        let collapsed = (VTTabDataStore.getTabValue(aGroup, this.kCollapsed)
                         == "true");
        for each (let tab in this.getChildren(aGroup)) {
            this._tabCollapseExpand(tab, !collapsed);
            if (tab.selected) {
                this.tabs.tabbrowser.selectedTab = aGroup;
            }
        }
        VTTabDataStore.setTabValue(aGroup, this.kCollapsed, !collapsed);
    },

    /*** Event handlers ***/

    handleEvent: function(aEvent) {
        switch (aEvent.type) {
        case "SSTabRestoring":
            this.onTabRestoring(aEvent.originalTarget);
            return;
        case "TabSelect":
            this.onTabSelect(aEvent);
            return;
        case "TabMove":
            this.onTabMove(aEvent);
            return;
        case "TabClose":
            this.onTabClose(aEvent);
            return;
        case "click":
            this.onClick(aEvent);
            return;
        case "dragover":
            this.onDragOver(aEvent);
            return;
        case "dragenter":
            this.onDragEnter(aEvent);
            return;
        case "dragleave":
            this.onDragLeave(aEvent);
            return;
        case "dragend":
            this._clearDropTargets();
            return;
        case "drop":
            this.onDrop(aEvent);
            return;
        }
    },

    onTabRestoring: function(aTab) {
        // Restore tab attributes from session data (this isn't done
        // automatically).  kId is restored by VTTabIDs.
        for each (let attr in [this.kGroup,
                               this.kInGroup,
                               this.kLabel,
                               this.kCollapsed]) {
            let value = VTTabDataStore.getTabValue(aTab, attr);
            if (value) {
                aTab.setAttribute(attr, value);
            }
        }

        // Restore collapsed state if we belong to a group.
        let groupId = VTTabDataStore.getTabValue(aTab, this.kInGroup);
        if (!groupId) {
            return;
        }

        let self = this;
        let window = this.tabs.ownerDocument.defaultView;
        function restoreCollapsedState() {
            // The group tab we belong to may not have been restored yet.
            let group = self.tabs.VTTabIDs.get(groupId);
            if (group === undefined) {
                window.setTimeout(restoreCollapsedState, 10);
                return;
            }
            let collapsed = (VTTabDataStore.getTabValue(group, self.kCollapsed)
                             == "true");
            self._tabCollapseExpand(aTab, collapsed);
        }
        restoreCollapsedState();
    },

    _tabCollapseExpand: function(aTab, collapsed) {
        if (collapsed) {
            aTab.classList.add(this.kCollapsed);
        } else {
            aTab.classList.remove(this.kCollapsed);
        }
    },

    onTabSelect: function(aEvent) {
        let tab = aEvent.target;
        let document = tab.ownerDocument;
        let urlbar = document.getElementById("urlbar");

        let isGroup = this.isGroup(tab);
        if (isGroup) {
            //TODO l10n
            urlbar.placeholder = "Group: " + tab.groupLabel;
        } else {
            urlbar.placeholder = urlbar.getAttribute("bookmarkhistoryplaceholder");
        }
        urlbar.disabled = isGroup;

        //XXX this doesn't quite work:
        let buttons = ["reload-button", "home-button", "urlbar", "searchbar"];
        for (let i=0; i < buttons.length; i++) {
            let element = document.getElementById(buttons[i]);
            element.disabled = isGroup;
        }
    },

    onClick: function(aEvent) {
        let tab = aEvent.target;
        if (tab.localName != "tab") {
            return;
        }
        if (aEvent.originalTarget !== tab.mTwisty) {
            return;
        }
        this.collapseExpand(tab);
    },

    /*
     * Remove style from all potential drop targets (usually there
     * should only be one...).
     */
    _clearDropTargets: function() {
        let groups = this.tabs.getElementsByClassName(this.kDropTarget);
        // Make a copy of the array before modifying its contents.
        groups = Array.prototype.slice.call(groups);
        for (let i=0; i < groups.length; i++) {
            groups[i].classList.remove(this.kDropTarget);
        }
    },

    onDragOver: function(aEvent) {
        if (aEvent.target.localName != "tab") {
            return;
        }
        // Potentially remove drop target style
        //XXX is this inefficient?
        this._clearDropTargets();

        // Directly dropping on a group or the tab icon:
        // Disable drop indicator, mark tab as drop target.
        if (this.isGroup(aEvent.target)
           || (aEvent.originalTarget.classList
               && aEvent.originalTarget.classList.contains("tab-icon-image"))) {
            aEvent.target.classList.add(this.kDropTarget);
            this.tabs._tabDropIndicator.collapsed = true;
            return;
        }

        // Find out if the tab's new position would add it to a group.
        // If so, mark the group as drop target and indent drop indicator.
        let dropindex = this.tabs._getDropIndex(aEvent);
        let tab = this.tabs.childNodes[dropindex];
        let groupId = VTTabDataStore.getTabValue(tab, this.kInGroup);
        if (!groupId) {
            this.tabs._tabDropIndicator.classList.remove(this.kDropInGroup);
            return;
        }
        // Add drop style to the group and the indicator
        let group = this.tabs.VTTabIDs.get(groupId);
        group.classList.add(this.kDropTarget);
        this.tabs._tabDropIndicator.classList.add(this.kDropInGroup);
    },

    onDragEnter: function(aEvent) {
        if (aEvent.target.localName != "tab") {
            return;
        }
        // Dragging a tab over a tab's icon changes the icon to the
        // "create group" icon.
        if (aEvent.originalTarget.classList
            && aEvent.originalTarget.classList.contains("tab-icon-image")) {
            aEvent.originalTarget.classList.add(this.kDropToNewGroup);
        }
    },

    onDragLeave: function(aEvent) {
        if (aEvent.target.localName != "tab") {
            return;
        }
        // Change the tab's icon back from the "create group" to
        // whatever it was before.
        if (aEvent.originalTarget.classList
            && aEvent.originalTarget.classList.contains("tab-icon-image")) {
            aEvent.originalTarget.classList.remove(this.kDropToNewGroup);
        }
    },

    onDrop: function(aEvent) {
        this._clearDropTargets();
        let tab = aEvent.target;

        let dt = aEvent.dataTransfer;
        let draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
        if (!this.tabs._isAllowedForDataTransfer(draggedTab)) {
            return;
        }

        // Dropping a tab on another tab's icon will create a new
        // group with those two tabs in it.
        if (aEvent.originalTarget.classList
            && aEvent.originalTarget.classList.contains("tab-icon-image")) {
            let group = this.addGroup();
            this.tabs.tabbrowser.moveTabTo(tab, group._tPos+1);
            this.tabs.tabbrowser.moveTabTo(draggedTab, group._tPos+1);
            return;
        }

        // Dropping on a group will append to that group's children.
        if (this.isGroup(tab)) {
            if (this.isGroup(draggedTab)) {
                // If it's a group we're dropping, merge groups.
                this.addChildren(tab, this.getChildren(draggedTab));
                this.tabs.tabbrowser.removeTab(draggedTab);
            } else {
                this.addChild(tab, draggedTab);
            }
        }
    },

    onTabMove: function(aEvent) {
        let tab = aEvent.target;
        if (tab.getAttribute(this.kIgnoreMove) == "true") {
            tab.removeAttribute(this.kIgnoreMove);
            return;
        }

        if (this.isGroup(tab)) {
            let newGroup = this._findGroupFromContext(tab);

            // Move group's children.
            let children = this.getChildren(tab);
            let offset = 0;
            if (children.length && children[0]._tPos > tab._tPos) {
                offset = 1;
            }
            for (let i = 0; i < children.length; i++) {
                children[i].setAttribute(this.kIgnoreMove, "true");
                this.tabs.tabbrowser.moveTabTo(children[i],
                                               tab._tPos + i + offset);
            }

            // If we're being dragged into another group, merge groups.
            if (newGroup) {
                this.addChildren(newGroup, children);
                this.tabs.tabbrowser.removeTab(tab);
            }
            return;
        }

        let group = this._findGroupFromContext(tab);
        if (!group) {
            this.removeChild(tab);
        } else {
            this.addChild(group, tab);
        }
    },

    /*
     * Determine whether a tab move should result in the tab being
     * added to a group (or removed from one).
     */
    _findGroupFromContext: function(tab) {
        let group;
        let nextPos = tab._tPos + 1;
        if (nextPos < this.tabs.childNodes.length) {
            // If the next tab down the line is in a group, then the
            // tab is added to that group.
            let next = this.tabs.childNodes[nextPos];
            let groupId = VTTabDataStore.getTabValue(next, this.kInGroup);
            group = this.tabs.VTTabIDs.get(groupId);
        } else {
            // We're moved to the last position, so let's look at the
            // previous tab.  Is it a group or in a group?
            nextPos = tab._tPos - 1;
            let prev = this.tabs.childNodes[nextPos];
            if (this.isGroup(prev)) {
                group = prev;
            } else {
                let groupId = VTTabDataStore.getTabValue(prev, this.kInGroup);
                group = this.tabs.VTTabIDs.get(groupId);
            }
        }
        return group;
    },

    onTabClose: function(aEvent) {
        let group = aEvent.target;
        if (!this.isGroup(group)) {
            return;
        }

        // If a collapsed group is removed, close its children as
        // well.  Otherwise just remove their group pointer.
        let collapsed = (VTTabDataStore.getTabValue(group, this.kCollapsed)
                         == "true");
        let children = this.getChildren(group);

        if (!collapsed) {
            this.removeChildren(children);
            return;
        }

        let window = group.ownerDocument.defaultView;
        let tabbrowser = this.tabs.tabbrowser;
        // Remove children async to avoid confusing tabbrowser.removeTab()
        window.setTimeout(function() {
            for each (let tab in children) {
                tabbrowser.removeTab(tab);
            }
        }, 10);
    }

};
<commit_msg>Selecting a tab in a collapsed group will expand that group.<commit_after>/*
 * Functionality for grouping tabs.
 *
 * Groups are implemented as a special kind of tab (see binding in
 * group.xml).  There are a few advantages and disadvantages to this:
 *
 *   - Groups can be regular children of tabbrowser.tabContainer
 *     (cf. https://bugzilla.mozilla.org/show_bug.cgi?id=475142).
 *
 *   - The nsISessionStore service takes care of restoring groups and
 *     their properties.
 *
 *   - But we have to make sure that groups don't behave like tabs at
 *     all.
 */

const EXPORTED_SYMBOLS = ["VTGroups"];
Components.utils.import("resource://verticaltabs/tabdatastore.js");

const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";

function VTGroups(tabs) {
    this.tabs = tabs;
    tabs.VTGroups = this;

    // Restore group and in-group status
    tabs.addEventListener('SSTabRestoring', this, true);

    // Updating UI
    tabs.addEventListener('TabSelect', this, false);

    // For clicks on the twisty
    tabs.addEventListener('click', this, true);

    // For synchronizing group behaviour and tab positioning
    tabs.addEventListener('dragover', this, false);
    tabs.addEventListener('dragenter', this, false);
    tabs.addEventListener('dragleave', this, false);
    tabs.addEventListener('dragend', this, false);
    tabs.addEventListener('drop', this, false);
    tabs.addEventListener('TabMove', this, false);
    tabs.addEventListener('TabClose', this, false);
}
VTGroups.prototype = {

    kId: 'verticaltabs-id',
    kGroup: 'verticaltabs-group',
    kInGroup: 'verticaltabs-ingroup',
    kLabel: 'verticaltabs-grouplabel',
    kCollapsed: 'verticaltabs-collapsed',
    kDropTarget: 'verticaltabs-droptarget',
    kDropInGroup: 'verticaltabs-dropingroup',
    kDropToNewGroup: 'verticaltabs-droptonewgroup',
    kIgnoreMove: 'verticaltabs-ignoremove',

    /*** Public API ***/

    /*
     * Create a new group tab.  If given as an argument, the label is
     * applied to the group.  Otherwise the label will be made
     * editable.
     */
    addGroup: function(aLabel) {
        let group = this.tabs.tabbrowser.addTab();
        VTTabDataStore.setTabValue(group, this.kGroup, "true");

        let window = this.tabs.ownerDocument.defaultView;
        function makeLabelEditable() {
            // XBL bindings aren't applied synchronously.
            if (typeof group.editLabel !== "function") {
                window.setTimeout(makeLabelEditable, 10);
                return;
            }
            group.editLabel();
        }

        if (aLabel) {
            VTTabDataStore.setTabValue(group, this.kLabel, aLabel);
            group.groupLabel = aLabel;
        } else {
            makeLabelEditable();
        }

        return group;
    },

    /*
     * Return the child tabs of a given group.  The return value is a
     * JavaScript Array (not just a NodeList) and is "owned" by the
     * caller (e.g. it may be modified).
     */
    getChildren: function(aGroup) {
        let groupId = this.tabs.VTTabIDs.id(aGroup);
        let children = this.tabs.getElementsByAttribute(this.kInGroup, groupId);
        // Return a copy
        return Array.prototype.slice.call(children);
    },

    /*
     * Add a tab to a group.  This won't physically move the tab
     * anywhere, just create the logical connection.
     */
    addChild: function(aGroup, aTab) {
        // Only groups can have children
        if (!this.isGroup(aGroup)) {
            return;
        }
        // We don't allow nested groups
        if (this.isGroup(aTab)) {
            return;
        }

        // Assign a group to the tab.  If the tab was in another group
        // before, this will simply overwrite the old value.
        let groupId = this.tabs.VTTabIDs.id(aGroup);
        VTTabDataStore.setTabValue(aTab, this.kInGroup, groupId);

        // Apply the group's collapsed state to the tab
        let collapsed = (VTTabDataStore.getTabValue(aGroup, this.kCollapsed)
                         == "true");
        this._tabCollapseExpand(aTab, collapsed);
    },

    addChildren: function(aGroup, aTabs) {
        for each (let tab in aTabs) {
            this.addChild(aGroup, tab);
        }
    },

    /*
     * Remove a tab from its group.
     */
    removeChild: function(aTab) {
        let groupId = VTTabDataStore.getTabValue(aTab, this.kInGroup);
        if (!groupId) {
            return;
        }

        VTTabDataStore.deleteTabValue(aTab, this.kInGroup);
    },

    removeChildren: function(aTabs) {
        for each (let tab in aTabs) {
            this.removeChild(tab);
        }
    },

    /*
     * Creates a tab from the active selection.
     */
    createGroupFromMultiSelect: function() {
        let group = this.addGroup();
        let children = this.tabs.VTMultiSelect.getSelected();
        for each (let tab in children) {
            // Moving the tabs to the right position is enough, the
            // TabMove handler knows the right thing to do.
            this.tabs.tabbrowser.moveTabTo(tab, group._tPos+1);
        }
        this.tabs.VTMultiSelect.clear();
    },

    /*
     * Return true if a given tab is a group tab.
     */
    isGroup: function(aTab) {
        return (VTTabDataStore.getTabValue(aTab, this.kGroup) == "true");
    },

    /*
     * Toggle collapsed/expanded state of a group tab.
     */
    collapseExpand: function(aGroup) {
        if (!this.isGroup(aGroup)) {
            return;
        }
        let collapsed = (VTTabDataStore.getTabValue(aGroup, this.kCollapsed)
                         == "true");
        for each (let tab in this.getChildren(aGroup)) {
            this._tabCollapseExpand(tab, !collapsed);
            if (tab.selected) {
                this.tabs.tabbrowser.selectedTab = aGroup;
            }
        }
        VTTabDataStore.setTabValue(aGroup, this.kCollapsed, !collapsed);
    },

    /*** Event handlers ***/

    handleEvent: function(aEvent) {
        switch (aEvent.type) {
        case "SSTabRestoring":
            this.onTabRestoring(aEvent.originalTarget);
            return;
        case "TabSelect":
            this.onTabSelect(aEvent);
            return;
        case "TabMove":
            this.onTabMove(aEvent);
            return;
        case "TabClose":
            this.onTabClose(aEvent);
            return;
        case "click":
            this.onClick(aEvent);
            return;
        case "dragover":
            this.onDragOver(aEvent);
            return;
        case "dragenter":
            this.onDragEnter(aEvent);
            return;
        case "dragleave":
            this.onDragLeave(aEvent);
            return;
        case "dragend":
            this._clearDropTargets();
            return;
        case "drop":
            this.onDrop(aEvent);
            return;
        }
    },

    onTabRestoring: function(aTab) {
        // Restore tab attributes from session data (this isn't done
        // automatically).  kId is restored by VTTabIDs.
        for each (let attr in [this.kGroup,
                               this.kInGroup,
                               this.kLabel,
                               this.kCollapsed]) {
            let value = VTTabDataStore.getTabValue(aTab, attr);
            if (value) {
                aTab.setAttribute(attr, value);
            }
        }

        // Restore collapsed state if we belong to a group.
        let groupId = VTTabDataStore.getTabValue(aTab, this.kInGroup);
        if (!groupId) {
            return;
        }

        let self = this;
        let window = this.tabs.ownerDocument.defaultView;
        function restoreCollapsedState() {
            // The group tab we belong to may not have been restored yet.
            let group = self.tabs.VTTabIDs.get(groupId);
            if (group === undefined) {
                window.setTimeout(restoreCollapsedState, 10);
                return;
            }
            let collapsed = (VTTabDataStore.getTabValue(group, self.kCollapsed)
                             == "true");
            self._tabCollapseExpand(aTab, collapsed);
        }
        restoreCollapsedState();
    },

    _tabCollapseExpand: function(aTab, collapsed) {
        if (collapsed) {
            aTab.classList.add(this.kCollapsed);
        } else {
            aTab.classList.remove(this.kCollapsed);
        }
    },

    onTabSelect: function(aEvent) {
        let tab = aEvent.target;
        let document = tab.ownerDocument;
        let urlbar = document.getElementById("urlbar");

        let isGroup = this.isGroup(tab);
        if (isGroup) {
            //TODO l10n
            urlbar.placeholder = "Group: " + tab.groupLabel;
        } else {
            urlbar.placeholder = urlbar.getAttribute("bookmarkhistoryplaceholder");
            // Selecting a tab that's in a collapsed group will expand
            // the group.
            if (tab.classList.contains(this.kCollapsed)) {
                let groupId = VTTabDataStore.getTabValue(tab, this.kInGroup);
                if (groupId) {
                    let group = this.tabs.VTTabIDs.get(groupId);
                    this.collapseExpand(group);
                }
            }
        }
        urlbar.disabled = isGroup;

        //XXX this doesn't quite work:
        let buttons = ["reload-button", "home-button", "urlbar", "searchbar"];
        for (let i=0; i < buttons.length; i++) {
            let element = document.getElementById(buttons[i]);
            element.disabled = isGroup;
        }
    },

    onClick: function(aEvent) {
        let tab = aEvent.target;
        if (tab.localName != "tab") {
            return;
        }
        if (aEvent.originalTarget !== tab.mTwisty) {
            return;
        }
        this.collapseExpand(tab);
    },

    /*
     * Remove style from all potential drop targets (usually there
     * should only be one...).
     */
    _clearDropTargets: function() {
        let groups = this.tabs.getElementsByClassName(this.kDropTarget);
        // Make a copy of the array before modifying its contents.
        groups = Array.prototype.slice.call(groups);
        for (let i=0; i < groups.length; i++) {
            groups[i].classList.remove(this.kDropTarget);
        }
    },

    onDragOver: function(aEvent) {
        if (aEvent.target.localName != "tab") {
            return;
        }
        // Potentially remove drop target style
        //XXX is this inefficient?
        this._clearDropTargets();

        // Directly dropping on a group or the tab icon:
        // Disable drop indicator, mark tab as drop target.
        if (this.isGroup(aEvent.target)
           || (aEvent.originalTarget.classList
               && aEvent.originalTarget.classList.contains("tab-icon-image"))) {
            aEvent.target.classList.add(this.kDropTarget);
            this.tabs._tabDropIndicator.collapsed = true;
            return;
        }

        // Find out if the tab's new position would add it to a group.
        // If so, mark the group as drop target and indent drop indicator.
        let dropindex = this.tabs._getDropIndex(aEvent);
        let tab = this.tabs.childNodes[dropindex];
        let groupId = VTTabDataStore.getTabValue(tab, this.kInGroup);
        if (!groupId) {
            this.tabs._tabDropIndicator.classList.remove(this.kDropInGroup);
            return;
        }
        // Add drop style to the group and the indicator
        let group = this.tabs.VTTabIDs.get(groupId);
        group.classList.add(this.kDropTarget);
        this.tabs._tabDropIndicator.classList.add(this.kDropInGroup);
    },

    onDragEnter: function(aEvent) {
        if (aEvent.target.localName != "tab") {
            return;
        }
        // Dragging a tab over a tab's icon changes the icon to the
        // "create group" icon.
        if (aEvent.originalTarget.classList
            && aEvent.originalTarget.classList.contains("tab-icon-image")) {
            aEvent.originalTarget.classList.add(this.kDropToNewGroup);
        }
    },

    onDragLeave: function(aEvent) {
        if (aEvent.target.localName != "tab") {
            return;
        }
        // Change the tab's icon back from the "create group" to
        // whatever it was before.
        if (aEvent.originalTarget.classList
            && aEvent.originalTarget.classList.contains("tab-icon-image")) {
            aEvent.originalTarget.classList.remove(this.kDropToNewGroup);
        }
    },

    onDrop: function(aEvent) {
        this._clearDropTargets();
        let tab = aEvent.target;

        let dt = aEvent.dataTransfer;
        let draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
        if (!this.tabs._isAllowedForDataTransfer(draggedTab)) {
            return;
        }

        // Dropping a tab on another tab's icon will create a new
        // group with those two tabs in it.
        if (aEvent.originalTarget.classList
            && aEvent.originalTarget.classList.contains("tab-icon-image")) {
            let group = this.addGroup();
            this.tabs.tabbrowser.moveTabTo(tab, group._tPos+1);
            this.tabs.tabbrowser.moveTabTo(draggedTab, group._tPos+1);
            return;
        }

        // Dropping on a group will append to that group's children.
        if (this.isGroup(tab)) {
            if (this.isGroup(draggedTab)) {
                // If it's a group we're dropping, merge groups.
                this.addChildren(tab, this.getChildren(draggedTab));
                this.tabs.tabbrowser.removeTab(draggedTab);
            } else {
                this.addChild(tab, draggedTab);
            }
        }
    },

    onTabMove: function(aEvent) {
        let tab = aEvent.target;
        if (tab.getAttribute(this.kIgnoreMove) == "true") {
            tab.removeAttribute(this.kIgnoreMove);
            return;
        }

        if (this.isGroup(tab)) {
            let newGroup = this._findGroupFromContext(tab);

            // Move group's children.
            let children = this.getChildren(tab);
            let offset = 0;
            if (children.length && children[0]._tPos > tab._tPos) {
                offset = 1;
            }
            for (let i = 0; i < children.length; i++) {
                children[i].setAttribute(this.kIgnoreMove, "true");
                this.tabs.tabbrowser.moveTabTo(children[i],
                                               tab._tPos + i + offset);
            }

            // If we're being dragged into another group, merge groups.
            if (newGroup) {
                this.addChildren(newGroup, children);
                this.tabs.tabbrowser.removeTab(tab);
            }
            return;
        }

        let group = this._findGroupFromContext(tab);
        if (!group) {
            this.removeChild(tab);
        } else {
            this.addChild(group, tab);
        }
    },

    /*
     * Determine whether a tab move should result in the tab being
     * added to a group (or removed from one).
     */
    _findGroupFromContext: function(tab) {
        let group;
        let nextPos = tab._tPos + 1;
        if (nextPos < this.tabs.childNodes.length) {
            // If the next tab down the line is in a group, then the
            // tab is added to that group.
            let next = this.tabs.childNodes[nextPos];
            let groupId = VTTabDataStore.getTabValue(next, this.kInGroup);
            group = this.tabs.VTTabIDs.get(groupId);
        } else {
            // We're moved to the last position, so let's look at the
            // previous tab.  Is it a group or in a group?
            nextPos = tab._tPos - 1;
            let prev = this.tabs.childNodes[nextPos];
            if (this.isGroup(prev)) {
                group = prev;
            } else {
                let groupId = VTTabDataStore.getTabValue(prev, this.kInGroup);
                group = this.tabs.VTTabIDs.get(groupId);
            }
        }
        return group;
    },

    onTabClose: function(aEvent) {
        let group = aEvent.target;
        if (!this.isGroup(group)) {
            return;
        }

        // If a collapsed group is removed, close its children as
        // well.  Otherwise just remove their group pointer.
        let collapsed = (VTTabDataStore.getTabValue(group, this.kCollapsed)
                         == "true");
        let children = this.getChildren(group);

        if (!collapsed) {
            this.removeChildren(children);
            return;
        }

        let window = group.ownerDocument.defaultView;
        let tabbrowser = this.tabs.tabbrowser;
        // Remove children async to avoid confusing tabbrowser.removeTab()
        window.setTimeout(function() {
            for each (let tab in children) {
                tabbrowser.removeTab(tab);
            }
        }, 10);
    }

};
<|endoftext|>


<commit_before>const moment = require('moment');

moment.locale('de');

function Levels(bot) {
    this.bot = bot;

    this.users = {};
}

Levels.prototype.getLevelExp = function (level) {
    return 5 * (Math.pow(level, 2)) + 50 * level + 100;
};

Levels.prototype.getLevelFromXp = function (xp) {
    let level = 0;

    while (xp >= this.getLevelExp(level)) {
        xp -= this.getLevelExp(level);
        level++;
    }

    return level;
};

Levels.prototype.getRandomExp = function () {
    return this.bot.randomInt(15, 25);
};

Levels.prototype.giveExp = function (message) {
    const key = `${this.bot.server.id}.Levels`;
    const field = message.author.id;

    if(message.author.id in this.users) {
        const timeoutEnd = this.users[message.author.id];

        if(timeoutEnd.diff(moment()) > 0) {
            return;
        }
    }

    let getCurrentExp = () => {
        this.bot.redis.hget(key, field, (err, result) => {
            if(err) {
                return this.bot.log(`[Levels] Could not giveExp (hget) ${err}`);
            }

            let currentExp = 0;

            if(result != null) {
                currentExp = result;
            }

            updateExp(parseInt(currentExp));
        })
    };

    let updateExp = currentExp => {
        const newExp = currentExp + this.getRandomExp();

        this.bot.redis.hset(key, field, newExp, err => {
            if(err) {
                return this.bot.log(`[Levels] Could not giveExp (hset) ${err}`);
            }

            this.users[message.author.id] = moment().add(1, 'm');

            const currentLevel = this.getLevelFromXp(currentExp);
            const newLevel = this.getLevelFromXp(newExp);

            if(currentLevel != newLevel) {
                this.bot.respond(message, `du hast soeben Level ${newLevel} erreicht!`);
            }
        })
    };

    getCurrentExp();
};

Levels.prototype.setExp = function (member, exp, callback) {
    const key = `${this.bot.server.id}.Levels`;
    const field = member.id;

    this.bot.redis.hset(key, field, exp, err => {
        if(err) {
            return callback(err);
        }

        callback(false);
    })
};

if (!(typeof exports === 'undefined')) {
    exports.Levels = Levels;
}
<commit_msg>added new util commands for levels<commit_after>const moment = require('moment');

moment.locale('de');

function Levels(bot) {
    this.bot = bot;

    this.users = {};
}

Levels.prototype.getLevelExp = function (level) {
    return 5 * (Math.pow(level, 2)) + 50 * level + 100;
};

Levels.prototype.getLevelFromXp = function (xp) {
    let level = 0;

    while (xp >= this.getLevelExp(level)) {
        xp -= this.getLevelExp(level);
        level++;
    }

    return level;
};

Levels.prototype.getRandomExp = function () {
    return this.bot.randomInt(15, 25);
};

Levels.prototype.giveExp = function (message) {
    const key = `${this.bot.server.id}.Levels`;
    const field = message.author.id;

    if (message.author.id in this.users) {
        const timeoutEnd = this.users[message.author.id];

        if (timeoutEnd.diff(moment()) > 0) {
            return;
        }
    }

    let getCurrentExp = () => {
        this.bot.redis.hget(key, field, (err, result) => {
            if (err) {
                return this.bot.log(`[Levels] Could not giveExp (hget) ${err}`);
            }

            let currentExp = 0;

            if (result != null) {
                currentExp = result;
            }

            updateExp(parseInt(currentExp));
        })
    };

    let updateExp = currentExp => {
        const newExp = currentExp + this.getRandomExp();

        this.bot.redis.hset(key, field, newExp, err => {
            if (err) {
                return this.bot.log(`[Levels] Could not giveExp (hset) ${err}`);
            }

            this.users[message.author.id] = moment().add(1, 'm');

            const currentLevel = this.getLevelFromXp(currentExp);
            const newLevel = this.getLevelFromXp(newExp);

            if (currentLevel != newLevel) {
                this.bot.respond(message, `du hast soeben Level ${newLevel} erreicht!`);
            }
        })
    };

    getCurrentExp();
};

Levels.prototype.getExp = function (member, callback) {
    const key = `${this.bot.server.id}.Levels`;
    const field = 'id' in member ? member.id : member;

    this.bot.redis.hget(key, field, (err, result) => {
        if (err) {
            return callback(err, null);
        }

        callback(false, result);
    });
};

Levels.prototype.getAllExp = function (callback) {
    const key = `${this.bot.server.id}.Levels`;

    this.bot.redis.hgetall(key, (err, results) => {
        if (err) {
            return callback(err, null);
        }

        callback(false, results);
    });
};

Levels.prototype.getRanklist = function (callback) {
    this.getAllExp((err, results) => {
        if (err) {
            return callback(err, null);
        }

        let ranks = Object.keys(results).map(function (key) {
            return [key, results[key]];
        });

        ranks.sort(function (first, second) {
            return second[1] - first[1];
        });

        callback(false, ranks);
    });
};

Levels.prototype.setExp = function (member, exp, callback) {
    const key = `${this.bot.server.id}.Levels`;
    const field = 'id' in member ? member.id : member;

    this.bot.redis.hset(key, field, exp, err => {
        if (err) {
            return callback(err);
        }

        callback(false);
    });
};

if (!(typeof exports === 'undefined')) {
    exports.Levels = Levels;
}
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Mon Jul 11 2016 13:14:14 GMT-0300 (BRT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'js/*.js',
      'specs/*.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<commit_msg>Adding osx progress reporter<commit_after>// Karma configuration
// Generated on Mon Jul 11 2016 13:14:14 GMT-0300 (BRT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'js/*.js',
      'specs/*.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'osx'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {Form, InputGroup, FormControl, Button} from 'react-bootstrap';

class ChatFooter extends Component {
    constructor(props) {
        super(props);
        this.state = {
            chatMessage: ''
        };
    }

    handleMessageInput = (event) => {
        this.setState({chatMessage: event.target.value});
    }

    handleMessageSubmit = (event) => {
        event.preventDefault();
        this.props.handleNewMessage(this.state.chatMessage);
        this.setState({chatMessage: ''});
    }

    handleKeyPress = (event) => {
        if (event.key === 'Escape') {
            this.props.handleNext();
            this.setState({chatMessage: ''});
        }
    }

    render() {
        return (

                    <Form onSubmit={this.handleMessageSubmit} onKeyUp={this.handleKeyPress}>
                        <InputGroup>

                                <Button
                                    bsStyle="danger"
                                    bsSize="small"
                                    id="btn-next"
                                    disabled={this.props.isFooterDisabled}
                                    onClick={this.props.handleNext}
                                >
                                    Next (Esc)
                                </Button>

                            <FormControl
                                type="text"
                                name="chat-message"
                                id="chat-message-input"
                                bsSize="small"
                                placeholder="Say something..."
                                value={this.state.chatMessage}
                                autoFocus
                                autoComplete="off"
                                onChange={this.handleMessageInput}
                            />

                                <Button
                                    type="submit"
                                    bsStyle="success"
                                    bsSize="small"
                                    id="btn-send"
                                    disabled={this.props.isFooterDisabled}
                                >
                                    Send (Enter)
                                </Button>

                        </InputGroup>
                    </Form>

        );
    }
}

export default ChatFooter;
<commit_msg>Handle Esc keypress only if footer active<commit_after>import React, { Component } from 'react';
import {Form, InputGroup, FormControl, Button} from 'react-bootstrap';

class ChatFooter extends Component {
    constructor(props) {
        super(props);
        this.state = {
            chatMessage: ''
        };
    }

    handleMessageInput = (event) => {
        this.setState({chatMessage: event.target.value});
    }

    handleMessageSubmit = (event) => {
        event.preventDefault();
        this.props.handleNewMessage(this.state.chatMessage);
        this.setState({chatMessage: ''});
    }

    handleKeyPress = (event) => {
        if (!this.props.isFooterDisabled && event.key === 'Escape') {
            this.props.handleNext();
            this.setState({chatMessage: ''});
        }
    }

    render() {
        return (

                    <Form onSubmit={this.handleMessageSubmit} onKeyUp={this.handleKeyPress}>
                        <InputGroup>

                                <Button
                                    bsStyle="danger"
                                    bsSize="small"
                                    id="btn-next"
                                    disabled={this.props.isFooterDisabled}
                                    onClick={this.props.handleNext}
                                >
                                    Next (Esc)
                                </Button>

                            <FormControl
                                type="text"
                                name="chat-message"
                                id="chat-message-input"
                                bsSize="small"
                                placeholder="Say something..."
                                value={this.state.chatMessage}
                                autoFocus
                                autoComplete="off"
                                onChange={this.handleMessageInput}
                            />

                                <Button
                                    type="submit"
                                    bsStyle="success"
                                    bsSize="small"
                                    id="btn-send"
                                    disabled={this.props.isFooterDisabled}
                                >
                                    Send (Enter)
                                </Button>

                        </InputGroup>
                    </Form>

        );
    }
}

export default ChatFooter;
<|endoftext|>


<commit_before>exports.BattleFormatsData = {
	blaziken: {
		inherit: true,
		tier: 'OU'
	},
	diancie: {
		inherit: true,
		isUnreleased: false,
		tier: 'OU'
	},
	genesect: {
		inherit: true,
		tier: 'OU'
	},
	deoxysdefense: {
		inherit: true,
		tier: 'Uber'
	},
	deoxysspeed: {
		inherit: true,
		tier: 'Uber'
	},
	kyurem: {
		inherit: true,
		tier: 'Uber'
	},
	kyuremblack: {
		inherit: true,
		tier: 'Uber'
	},
	deoxysdefense: {
		inherit: true,
		tier: 'Uber'
	}
};
<commit_msg>Remove duplicate Deo-D entry<commit_after>exports.BattleFormatsData = {
	blaziken: {
		inherit: true,
		tier: 'OU'
	},
	diancie: {
		inherit: true,
		isUnreleased: false,
		tier: 'OU'
	},
	genesect: {
		inherit: true,
		tier: 'OU'
	},
	deoxysdefense: {
		inherit: true,
		tier: 'Uber'
	},
	deoxysspeed: {
		inherit: true,
		tier: 'Uber'
	},
	kyurem: {
		inherit: true,
		tier: 'Uber'
	},
	kyuremblack: {
		inherit: true,
		tier: 'Uber'
	},
};
<|endoftext|>


<commit_before>"use strict";

class gsuiEffects {
	constructor() {
		const root = gsuiEffects.template.cloneNode( true ),
			elFxsList = root.querySelector( ".gsuiEffects-list" ),
			elBtnSelect = root.querySelector( ".gsuiEffects-addBtn" ),
			elAddSelect = root.querySelector( ".gsuiEffects-addSelect" ),
			reorder = new gsuiReorder();

		this.rootElement = root;
		this.onchange = GSData.noop;
		this.gsdata = new GSDataEffects( {
			actionCallback: ( obj, msg ) => this.onchange( obj, msg ),
			dataCallbacks: {
				addFx: this._addFx.bind( this ),
				removeFx: this._removeFx.bind( this ),
				toggleFx: this._toggleFx.bind( this ),
				reorderFx: this._reorderFx.bind( this ),
				changeFxData: this._changeFxData.bind( this ),
			},
		} );
		this._fxsAssets = Object.freeze( {
			delay: { gsuiCmp: gsuiFxDelay, name: "Delay", height: 120 },
		} );
		this._fxNames = 0;
		this._fxsHtml = new Map();
		this._attached = false;
		this._elFxsList = elFxsList;
		this._elAddSelect = elAddSelect;
		this._fxResizeTimeoutId = null;
		Object.seal( this );

		reorder.setRootElement( elFxsList );
		reorder.setSelectors( {
			item: ".gsuiEffects-fx",
			handle: ".gsuiEffects-fx-grip",
			parent: ".gsuiEffects-list",
		} );
		reorder.onchange = () => {};
		elBtnSelect.onclick = () => this._elAddSelect.value = "";
		elAddSelect.onchange = this._onchangeAddSelect.bind( this );
		elAddSelect.onkeydown = () => false;
		elAddSelect.append(
			this._createOption( false, "", "-- Select an Fx" ),
			...Object.entries( this._fxsAssets )
				.map( fx => this._createOption( true, fx[ 0 ], fx[ 1 ].name ) )
		);
	}

	// .........................................................................
	change( obj ) {
		this.gsdata.change( obj );
		gsuiReorder.listReorder( this._elFxsList, obj );
	}
	attached() {
		this._attached = true;
		this._fxsHtml.forEach( html => html.uiFx.attached() );
	}
	resized() {
		this._fxsHtml.forEach( html => html.uiFx.resized() );
	}
	setDestFilter( dest ) {
		this.gsdata.setDestFilter( dest );
	}

	// dataCallbacks:
	// .........................................................................
	_addFx( id, fx ) {
		const root = gsuiEffects.templateFx.cloneNode( true ),
			name = root.querySelector( ".gsuiEffects-fx-name" ),
			expand = root.querySelector( ".gsuiEffects-fx-expand" ),
			toggle = root.querySelector( ".gsuiEffects-fx-toggle" ),
			remove = root.querySelector( ".gsuiEffects-fx-remove" ),
			content = root.querySelector( ".gsuiEffects-fx-content" ),
			fxAsset = this._fxsAssets[ fx.type ],
			uiFx = new fxAsset.gsuiCmp(),
			html = Object.seal( {
				uiFx,
				root,
				expand,
				content,
				expanded: false,
			} );

		expand.onclick = this._onclickShowFx.bind( this, id );
		toggle.onclick = this._onclickToggleFx.bind( this, id );
		remove.onclick = this._onclickRemoveFx.bind( this, id );
		uiFx.onchange = this.gsdata.callAction.bind( this.gsdata, "changeFxData", id );
		name.textContent = fxAsset.name;
		content.append( uiFx.rootElement );
		this._fxsHtml.set( id, html );
		this._elFxsList.append( root );
		if ( this._attached ) {
			uiFx.attached();
		}
	}
	_removeFx( id ) {
		this._fxsHtml.get( id ).root.remove();
		this._fxsHtml.delete( id );
	}
	_toggleFx( id, b ) {
		this._fxsHtml.get( id ).root.classList.toggle( "gsuiEffects-fx-enable", b );
	}
	_reorderFx( id, order ) {
		this._fxsHtml.get( id ).root.dataset.order = order;
	}
	_changeFxData( id, data ) {
		this._fxsHtml.get( id ).uiFx.gsdata.change( data );
	}

	// .........................................................................
	_expandFx( id, b ) {
		const html = this._fxsHtml.get( id ),
			type = this.gsdata.data[ id ].type;

		html.expanded = b;
		html.root.classList.toggle( "gsuiEffects-fx-expanded", b );
		html.expand.dataset.icon = b ? "caret-down" : "caret-right";
		html.content.style.height = `${ b ? this._fxsAssets[ type ].height : 0 }px`;
		clearTimeout( this._fxResizeTimeoutId );
		if ( b ) {
			this._fxResizeTimeoutId = setTimeout( () => html.uiFx.resized(), 200 );
		}
	}

	// events:
	// .........................................................................
	_onchangeAddSelect() {
		const type = this._elAddSelect.value;

		this._elAddSelect.blur();
		this._elAddSelect.value = ""
		this.gsdata.callAction( "addFx", type );
	}
	_onclickShowFx( id ) {
		this._expandFx( id, !this._fxsHtml.get( id ).expanded );
	}
	_onclickToggleFx( id ) {
		this.gsdata.callAction( "toggleFx", id );
	}
	_onclickRemoveFx( id ) {
		this.gsdata.callAction( "removeFx", id );
	}

	// .........................................................................
	_createOption( enable, fxId, fxName ) {
		const opt = document.createElement( "option" );

		opt.value = fxId;
		opt.disabled = !enable;
		opt.textContent = fxName;
		return opt;
	}
}

gsuiEffects.template = document.querySelector( "#gsuiEffects-template" );
gsuiEffects.template.remove();
gsuiEffects.template.removeAttribute( "id" );

gsuiEffects.templateFx = document.querySelector( "#gsuiEffects-fx-template" );
gsuiEffects.templateFx.remove();
gsuiEffects.templateFx.removeAttribute( "id" );
<commit_msg>gsuiEffects: missing semi<commit_after>"use strict";

class gsuiEffects {
	constructor() {
		const root = gsuiEffects.template.cloneNode( true ),
			elFxsList = root.querySelector( ".gsuiEffects-list" ),
			elBtnSelect = root.querySelector( ".gsuiEffects-addBtn" ),
			elAddSelect = root.querySelector( ".gsuiEffects-addSelect" ),
			reorder = new gsuiReorder();

		this.rootElement = root;
		this.onchange = GSData.noop;
		this.gsdata = new GSDataEffects( {
			actionCallback: ( obj, msg ) => this.onchange( obj, msg ),
			dataCallbacks: {
				addFx: this._addFx.bind( this ),
				removeFx: this._removeFx.bind( this ),
				toggleFx: this._toggleFx.bind( this ),
				reorderFx: this._reorderFx.bind( this ),
				changeFxData: this._changeFxData.bind( this ),
			},
		} );
		this._fxsAssets = Object.freeze( {
			delay: { gsuiCmp: gsuiFxDelay, name: "Delay", height: 120 },
		} );
		this._fxNames = 0;
		this._fxsHtml = new Map();
		this._attached = false;
		this._elFxsList = elFxsList;
		this._elAddSelect = elAddSelect;
		this._fxResizeTimeoutId = null;
		Object.seal( this );

		reorder.setRootElement( elFxsList );
		reorder.setSelectors( {
			item: ".gsuiEffects-fx",
			handle: ".gsuiEffects-fx-grip",
			parent: ".gsuiEffects-list",
		} );
		reorder.onchange = () => {};
		elBtnSelect.onclick = () => this._elAddSelect.value = "";
		elAddSelect.onchange = this._onchangeAddSelect.bind( this );
		elAddSelect.onkeydown = () => false;
		elAddSelect.append(
			this._createOption( false, "", "-- Select an Fx" ),
			...Object.entries( this._fxsAssets )
				.map( fx => this._createOption( true, fx[ 0 ], fx[ 1 ].name ) )
		);
	}

	// .........................................................................
	change( obj ) {
		this.gsdata.change( obj );
		gsuiReorder.listReorder( this._elFxsList, obj );
	}
	attached() {
		this._attached = true;
		this._fxsHtml.forEach( html => html.uiFx.attached() );
	}
	resized() {
		this._fxsHtml.forEach( html => html.uiFx.resized() );
	}
	setDestFilter( dest ) {
		this.gsdata.setDestFilter( dest );
	}

	// dataCallbacks:
	// .........................................................................
	_addFx( id, fx ) {
		const root = gsuiEffects.templateFx.cloneNode( true ),
			name = root.querySelector( ".gsuiEffects-fx-name" ),
			expand = root.querySelector( ".gsuiEffects-fx-expand" ),
			toggle = root.querySelector( ".gsuiEffects-fx-toggle" ),
			remove = root.querySelector( ".gsuiEffects-fx-remove" ),
			content = root.querySelector( ".gsuiEffects-fx-content" ),
			fxAsset = this._fxsAssets[ fx.type ],
			uiFx = new fxAsset.gsuiCmp(),
			html = Object.seal( {
				uiFx,
				root,
				expand,
				content,
				expanded: false,
			} );

		expand.onclick = this._onclickShowFx.bind( this, id );
		toggle.onclick = this._onclickToggleFx.bind( this, id );
		remove.onclick = this._onclickRemoveFx.bind( this, id );
		uiFx.onchange = this.gsdata.callAction.bind( this.gsdata, "changeFxData", id );
		name.textContent = fxAsset.name;
		content.append( uiFx.rootElement );
		this._fxsHtml.set( id, html );
		this._elFxsList.append( root );
		if ( this._attached ) {
			uiFx.attached();
		}
	}
	_removeFx( id ) {
		this._fxsHtml.get( id ).root.remove();
		this._fxsHtml.delete( id );
	}
	_toggleFx( id, b ) {
		this._fxsHtml.get( id ).root.classList.toggle( "gsuiEffects-fx-enable", b );
	}
	_reorderFx( id, order ) {
		this._fxsHtml.get( id ).root.dataset.order = order;
	}
	_changeFxData( id, data ) {
		this._fxsHtml.get( id ).uiFx.gsdata.change( data );
	}

	// .........................................................................
	_expandFx( id, b ) {
		const html = this._fxsHtml.get( id ),
			type = this.gsdata.data[ id ].type;

		html.expanded = b;
		html.root.classList.toggle( "gsuiEffects-fx-expanded", b );
		html.expand.dataset.icon = b ? "caret-down" : "caret-right";
		html.content.style.height = `${ b ? this._fxsAssets[ type ].height : 0 }px`;
		clearTimeout( this._fxResizeTimeoutId );
		if ( b ) {
			this._fxResizeTimeoutId = setTimeout( () => html.uiFx.resized(), 200 );
		}
	}

	// events:
	// .........................................................................
	_onchangeAddSelect() {
		const type = this._elAddSelect.value;

		this._elAddSelect.blur();
		this._elAddSelect.value = "";
		this.gsdata.callAction( "addFx", type );
	}
	_onclickShowFx( id ) {
		this._expandFx( id, !this._fxsHtml.get( id ).expanded );
	}
	_onclickToggleFx( id ) {
		this.gsdata.callAction( "toggleFx", id );
	}
	_onclickRemoveFx( id ) {
		this.gsdata.callAction( "removeFx", id );
	}

	// .........................................................................
	_createOption( enable, fxId, fxName ) {
		const opt = document.createElement( "option" );

		opt.value = fxId;
		opt.disabled = !enable;
		opt.textContent = fxName;
		return opt;
	}
}

gsuiEffects.template = document.querySelector( "#gsuiEffects-template" );
gsuiEffects.template.remove();
gsuiEffects.template.removeAttribute( "id" );

gsuiEffects.templateFx = document.querySelector( "#gsuiEffects-fx-template" );
gsuiEffects.templateFx.remove();
gsuiEffects.templateFx.removeAttribute( "id" );
<|endoftext|>