<commit_before>/**
* trials search, selections to combine etc...
* @author Isaak Y Tecle <iyt2@cornell.edu>
*
*/

JSAN.use("Prototype");
JSAN.use('jquery.blockUI');

function getPopIds () {

    var searchedPopsList = jQuery("#searched_trials_table tr").length;

    var tableId;

    if (searchedPopsList) {
	tableId = 'searched_trials_table';
    } else {
	tableId = 'all_trials_table';
    }

    jQuery('#' +tableId + ' tr').filter(':has(:checkbox:checked)')
        .bind('click',  function() {

            jQuery("#done_selecting").val('Done selecting');
            var td =  jQuery(this).html();

            var selectedTrial = '
' + td + '
';

            jQuery("#selected_trials_table tr:last").after(selectedTrial);

            jQuery("#selected_trials_table tr").each( function() {
                jQuery(this).find("input[type=checkbox]")
                    .attr('onclick', 'removeSelectedTrial()')
                    .prop('checked', true);
            });
        });

    jQuery("#selected_trials").show();
    jQuery("#combine_trials_div").show();
    jQuery("#search_again_div").hide();

}

jQuery(document).ready(function() {
    jQuery('#done_selecting').on('click', function() {
	hideTrialsList();
    });

});

function hideTrialsList() {
    jQuery("#all_trials_div").empty();
    jQuery("#searched_trials_div").empty();
    jQuery("#done_selecting_div").hide();
    jQuery("#all_trials_search_message").hide();

}

function removeSelectedTrial() {

    jQuery("#selected_trials_table tr").on("change", function() {

        jQuery(this).remove();

        if (jQuery("#selected_trials_table td").length == 0) {
            jQuery("#selected_trials").hide();
            jQuery("#combine_trials_div").hide();
            jQuery("#search_again_div").hide();
            jQuery("#done_selecting").val('Select');

          //  searchAgain();
        }
    });

}

// jQuery(document).ready(function() {
//     jQuery('#search_again').on('click', function() {
// 	searchAgain();
//     });

// });

function searchAgain () {

    var url = window.location.pathname;

    if (url.match(/solgs\/search\/trials\/trait\//) != null) {
	var traitId = jQuery("input[name='trait_id']").val();
	url = '/solgs/search/result/populations/' + traitId;
    } else {
	url = '/solgs/search/trials/';
    }

    jQuery('#all_trials_div').empty();
    jQuery("#searched_trials_div").empty();
    searchAllTrials(url);
    jQuery("#all_trials_search_message").show();
    jQuery("#done_selecting_div").show();
    jQuery("#done_selecting").val('Select');

}

jQuery(document).ready(function() {
    jQuery('#combine_trait_trials').on('click', function() {
	//combineTraitTrials();
	getCombinedPopsId();
    });

});

function combineTraitTrials () {
    var trId = getTraitId();

    var trialIds = getSelectedTrials();

    var action = "/solgs/combine/populations/trait/" + trId;
    var selectedPops = trId + "=" + trialIds + '&' + 'combine=combine';

    jQuery.blockUI.defaults.applyPlatformOpacityRules = false;
    jQuery.blockUI({message: 'Please wait..'});

    jQuery.ajax({
        type: 'POST',
        dataType: "json",
        url: action,
        data: selectedPops,
        success: function(res) {

            if (res.status) {

                var comboPopsId = res.combo_pops_id;
                var newUrl = '/solgs/model/combined/populations/' + comboPopsId + '/trait/' + trId;

		if (comboPopsId) {
		    window.location.href = newUrl;
		    jQuery.unblockUI();
                } else if (res.pop_id) {
		    goToSingleTrialTrainingPopPage(res.pop_id, trId);
		    jQuery.unblockUI();
                }

            } else {

                if (res.not_matching_pops){
                    alert('populations ' + res.not_matching_pops +
                          ' were genotyped using different marker sets. ' +
                          'Please make new selections to combine.' );
                    window.location.href =  '/solgs/search/result/populations/' + trId;
                }

                if (res.redirect_url) {
                    window.location.href = res.redirect_url;
                }
            }
	}
    });

}

jQuery(document).ready(function() {
    jQuery('#combine_trials').on('click', function() {
	getCombinedPopsId();
    });

});

function getCombinedPopsId(comboPopsList) {

    if (!comboPopsList) {
	comboPopsList = getSelectedTrials();
    }

    var traitId       = getTraitId();
    var referer       = window.location.href;
    var page;

    if (comboPopsList.length > 1) {
	jQuery.ajax({
            type: 'POST',
            dataType: "json",
            url: "/solgs/get/combined/populations/id",
            data: {'trials': comboPopsList},
            success: function(res) {
		if (res.status) {
    		    var comboPopsId = res.combo_pops_id;

		    if (window.Prototype) {
			delete Array.prototype.toJSON;
		    }

		     var args = {
			'combo_pops_id'   : [ comboPopsId ],
			'combo_pops_list' : comboPopsList,
			'trait_id'        : traitId,
		     };

		    goToCombinedTrialsTrainingPopPage(args);
		}

            },
            error: function(res) {
    		alert('Error occured getting combined trials unique id');
            }
	});

    } else {
	var popId = comboPopsList;
	goToSingleTrialTrainingPopPage(popId, traitId)
    }
}

function goToCombinedTrialsTrainingPopPage (args) {

    console.log('going to combined trials page')
    args['analysis_type'] = 'combine populations';
    args['data_set_type'] = 'multiple populations';

    var comboPopsId = args.combo_pops_id;
    comboPopsId = comboPopsId[0];

    var referer = window.location.href;
    var page;

    if (referer.match(/search\/trials\/trait\//)) {
	var traitId = args.trait_id;
	traitId = traitId[0];

	page = '/solgs/model/combined/trials/' + comboPopsId + '/trait/' + traitId;

    } else {
	page = '/solgs/populations/combined/' + comboPopsId;
    }
    console.log('calling solgs wait page...')
    solGS.waitPage(page, args);

}

function retrievePopsData(trialsIds) {

    if (!trialsIds) {
	trialsIds = getSelectedTrials();
    }

    var action = "/solgs/retrieve/populations/data";

    jQuery.blockUI.defaults.applyPlatformOpacityRules = false;
    jQuery.blockUI({message: 'Please wait..'});

    jQuery.ajax({
        type: 'POST',
        dataType: "json",
        url: action,
        data: {'trials': trialsIds},
        success: function(res) {
            if (res.not_matching_pops == null) {

                var combinedPopsId = res.combined_pops_id;

                if (combinedPopsId) {
                    goToCombinedTrialsPage(combinedPopsId);
                    jQuery.unblockUI();
                } else if (res.pop_id) {
                    goToSingleTrialTrainingPopPage(res.pop_id);
                    jQuery.unblockUI();
                }

            } else if(res.not_matching_pops )  {

                jQuery.unblockUI();
                alert('populations ' + res.not_matching_pops +
                      ' were genotyped using different marker sets. ' +
                      'Please make new selections to combine.' );

            }
        },
        error: function(res) {
            jQuery.unblockUI();
            alert('An error occured retrieving phenotype' +
                  'and genotype data for trials..');
        }
    });
}

function getSelectedTrials () {

    var trialIds = [];

    if (jQuery("#selected_trials_table").length) {
        jQuery("#selected_trials_table tr").each(function () {
            var trialId = jQuery(this).find("input[type=checkbox]").val();

            if (trialId) {
                trialIds.push(trialId);
            }
        });
    }

    trialIds = trialIds.sort();

    return trialIds;

}

function goToCombinedTrialsPage(combinedPopsId) {

    var action = '/solgs/populations/combined/' + combinedPopsId;

    if (combinedPopsId) {
        window.location.href = action;
    }
}

// function goToSingleTrialPage(url) {

//     if (url) {
// 	window.location.href = url;
//    }

// }

function goToSingleTrialTrainingPopPage(popId, traitId) {

    var referer       = window.location.href;
    var page;
    if (referer.match(/search\/trials\/trait\//)) {
	page = '/solgs/trait/' + traitId + '/population/' + popId;

    } else {
	var hostName = window.location.protocol + '//' + window.location.host;
	page = hostName +  '/solgs/population/' + popId;
    }

    var args = {'population_id'   : [ popId ],
		'analysis_type'   : 'population download',
		'data_set_type'   : 'single population',
		'trait_id'        : traitId,
	       };

    solGS.waitPage(page, args);
}

function  getTraitId() {

    var id = jQuery("input[name='trait_id']").val();
    return id;
}

Array.prototype.unique =
    function() {
    var a = [];
    var l = this.length;
    for(var i=0; i<l; i++) {
      for(var j=i+1; j<l; j++) {
        // If this[i] is fo3und later in the array
        if (this[i] === this[j])
          j = ++i;
      }
      a.push(this[i]);
    }
    return a;
  };

<commit_msg>create combinedTrials object in solGS<commit_after>/**
* trials search, selections to combine etc...
* @author Isaak Y Tecle <iyt2@cornell.edu>
*
*/

JSAN.use("Prototype");
JSAN.use('jquery.blockUI');

var solGS = solGS || function solGS () {};

solGS.combinedTrials = {

     getPopIds: function () {

	 var searchedPopsList = jQuery("#searched_trials_table tr").length;

	 var tableId;

	 if (searchedPopsList) {
	     tableId = 'searched_trials_table';
	 } else {
	     tableId = 'all_trials_table';
	 }

	 jQuery('#' +tableId + ' tr')
	     .filter(':has(:checkbox:checked)')
             .bind('click',  function() {

		 jQuery("#done_selecting").val('Done selecting');
		 var td =  jQuery(this).html();

		 var selectedTrial = '
' + td + '
';

		 jQuery("#selected_trials_table tr:last").after(selectedTrial);

		 jQuery("#selected_trials_table tr").each( function() {
                     jQuery(this).find("input[type=checkbox]")
			 .attr('onclick', 'solGS.combinedTrials.removeSelectedTrial()')
			 .prop('checked', true);
		 });
             });

	 jQuery("#selected_trials").show();
	 jQuery("#combine_trials_div").show();
	 jQuery("#search_again_div").hide();

     },

    hideTrialsList: function() {
	jQuery("#all_trials_div").empty();
	jQuery("#searched_trials_div").empty();
	jQuery("#done_selecting_div").hide();
	jQuery("#all_trials_search_message").hide();

    },

    removeSelectedTrial: function() {

	jQuery("#selected_trials_table tr").on("change", function() {

            jQuery(this).remove();

            if (jQuery("#selected_trials_table td").length == 0) {
		jQuery("#selected_trials").hide();
		jQuery("#combine_trials_div").hide();
		jQuery("#search_again_div").hide();
		jQuery("#done_selecting").val('Select');

		//  searchAgain();
            }
	});

    },

    searchAgain: function () {

	var url = window.location.pathname;

	if (url.match(/solgs\/search\/trials\/trait\//) != null) {
	    var traitId = jQuery("input[name='trait_id']").val();
	    url = '/solgs/search/result/populations/' + traitId;
	} else {
	    url = '/solgs/search/trials/';
	}

	jQuery('#all_trials_div').empty();
	jQuery("#searched_trials_div").empty();
	searchAllTrials(url);
	jQuery("#all_trials_search_message").show();
	jQuery("#done_selecting_div").show();
	jQuery("#done_selecting").val('Select');

    },

    combineTraitTrials: function () {
	var trId = this.getTraitId();

	var trialIds = this.getSelectedTrials();

	var action = "/solgs/combine/populations/trait/" + trId;
	var selectedPops = trId + "=" + trialIds + '&' + 'combine=combine';

	jQuery.blockUI.defaults.applyPlatformOpacityRules = false;
	jQuery.blockUI({message: 'Please wait..'});

	jQuery.ajax({
            type: 'POST',
            dataType: "json",
            url: action,
            data: selectedPops,
            success: function(res) {

		if (res.status) {

                    var comboPopsId = res.combo_pops_id;
                    var newUrl = '/solgs/model/combined/populations/' + comboPopsId + '/trait/' + trId;

		    if (comboPopsId) {
			window.location.href = newUrl;
			jQuery.unblockUI();
                    } else if (res.pop_id) {
			this.goToSingleTrialTrainingPopPage(res.pop_id, trId);
			jQuery.unblockUI();
                    }

		} else {

                    if (res.not_matching_pops){
			alert('populations ' + res.not_matching_pops +
                              ' were genotyped using different marker sets. ' +
                              'Please make new selections to combine.' );
			window.location.href =  '/solgs/search/result/populations/' + trId;
                    }

                    if (res.redirect_url) {
			window.location.href = res.redirect_url;
                    }
		}
	    }
	});

    },

    getCombinedPopsId: function (comboPopsList) {

	if (!comboPopsList) {
	    comboPopsList = this.getSelectedTrials();
	}

	console.log('getCombinedPopsId: combopopsid ' + comboPopsList)

	var traitId = this.getTraitId();
	var referer = window.location.href;

	var page;

	if (comboPopsList.length > 1) {
	    jQuery.ajax({
		type: 'POST',
		dataType: "json",
		url: "/solgs/get/combined/populations/id",
		data: {'trials': comboPopsList},
		success: function(res) {
		    if (res.status) {
    			var comboPopsId = res.combo_pops_id;
			console.log('getCombinedPopsId: combopopsid ' + comboPopsId)

			if (window.Prototype) {
			    delete Array.prototype.toJSON;
			}

			var args = {
			    'combo_pops_id'   : [ comboPopsId ],
			    'combo_pops_list' : comboPopsList,
			    'trait_id'        : traitId,
			};

			console.log('goToCombinedTrialsTrainingPop')
			solGS.combinedTrials.downloadCombinedTrialsTrainingPopData(args);
			console.log('DONE goToCombinedTrialsTrainingPop')

		    }

		},
		error: function(res) {
    		    alert('Error occured getting combined trials unique id');
		}

	    });

	} else {
	    var popId = comboPopsList;
	    this.downloadSingleTrialTrainingPopData(popId, traitId);
	}

    },

    downloadCombinedTrialsTrainingPopData: function (args) {

    	console.log('going to combined trials page');

    	if (window.Prototype) {
    	    delete Array.prototype.toJSON;
    	}

    	args['analysis_type'] = 'combine populations';
    	args['data_set_type'] = 'multiple populations';

    	var comboPopsId = args.combo_pops_id;
    	comboPopsId = comboPopsId[0];

    	var referer = window.location.href;
    	var page;

    	if (referer.match(/search\/trials\/trait\//)) {
    	    var traitId = args.trait_id;
    	    traitId = traitId[0];

    	    page = '/solgs/model/combined/trials/'
    		+ comboPopsId + '/trait/'
    		+ traitId;

    	} else {
    	    page = '/solgs/populations/combined/' + comboPopsId;
    	}

    	console.log('calling solgs wait page...')

    	solGS.waitPage(page, args);

    },

    displayCombinedTrialsTrainingPopPage: function(trialsIds) {

	console.log('retrievepopsdata: trialsids ' + trialsIds)

	if (!trialsIds) {
	    trialsIds = this.getSelectedTrials();
	}

	var action = "/solgs/retrieve/populations/data";

	jQuery.blockUI.defaults.applyPlatformOpacityRules = false;
	jQuery.blockUI({message: 'Please wait..'});

	jQuery.ajax({
            type: 'POST',
            dataType: "json",
            url: action,
            data: {'trials': trialsIds},
            success: function(res) {
		if (res.not_matching_pops == null) {

                    var combinedPopsId = res.combined_pops_id;

                    if (combinedPopsId) {
			solGS.combinedTrials.goToCombinedTrialsTrainingPopPage(combinedPopsId);
			jQuery.unblockUI();
                    } else if (res.pop_id) {
			solGS.combinedTrials.goToSingleTrialTrainingPopPage(res.pop_id);
			jQuery.unblockUI();
                    }

		} else if(res.not_matching_pops )  {

                    jQuery.unblockUI();
                    alert('populations ' + res.not_matching_pops +
			  ' were genotyped using different marker sets. ' +
			  'Please make new selections to combine.' );

		}
            },
            error: function(res) {
		jQuery.unblockUI();
		alert('An error occured retrieving phenotype' +
                      'and genotype data for trials..');
            }
	});

    },

    getSelectedTrials: function () {

	var trialIds = [];

	if (jQuery("#selected_trials_table").length) {
            jQuery("#selected_trials_table tr")
		.each(function () {

		    var trialId = jQuery(this)
			.find("input[type=checkbox]")
			.val();

		if (trialId) {
                    trialIds.push(trialId);
		}
            });
	}

	return trialIds.sort();

    },

    goToCombinedTrialsTrainingPopPage: function (comboPopsId) {

    	var page = '/solgs/populations/combined/' + comboPopsId;

    	if (comboPopsId) {
            window.location = page;
    	} else {
	    alert('combined Trials id missing.')
	}
    },

    goToSingleTrialTrainingPopPage: function (popId, traitId) {

	var referer = window.location.href;
	var page;

	if (referer.match(/search\/trials\/trait\//)) {
	    page = '/solgs/trait/' + traitId + '/population/' + popId;

	} else {
	    var hostName = window.location.protocol + '//' + window.location.host;
	    page = hostName +  '/solgs/population/' + popId;
	}

	window.location = page;

    },

    downloadSingleTrialTrainingPopData: function (popId, traitId) {

	var referer = window.location.href;
	var page;

	if (referer.match(/search\/trials\/trait\//)) {
	    page = '/solgs/trait/' + traitId + '/population/' + popId;

	} else {
	    var hostName = window.location.protocol + '//' + window.location.host;
	    page = hostName +  '/solgs/population/' + popId;
	}

	var args = {'population_id'   : [ popId ],
		    'analysis_type'   : 'population download',
		    'data_set_type'   : 'single population',
		    'trait_id'        : traitId,
		   };

	solGS.waitPage(page, args);

    },

    getTraitId: function() {

	var id = jQuery("input[name='trait_id']").val();
	return id;
    }

/////
}
/////

Array.prototype.unique =
    function() {
    var a = [];
    var l = this.length;
    for(var i=0; i<l; i++) {
      for(var j=i+1; j<l; j++) {
        // If this[i] is fo3und later in the array
        if (this[i] === this[j])
          j = ++i;
      }
      a.push(this[i]);
    }
    return a;
    };

jQuery(document).ready(function() {
    jQuery('#done_selecting').on('click', function() {
	solGS.combinedTrials.hideTrialsList();
    });

});

jQuery(document).ready(function() {
    jQuery('#combine_trait_trials').on('click', function() {
	//combineTraitTrials();
	solGS.combinedTrials.getCombinedPopsId();
    });

});

jQuery(document).ready(function() {
    jQuery('#combine_trials').on('click', function() {
	console.log('clicked combine_trials')
	solGS.combinedTrials.getCombinedPopsId();
    });

});

// jQuery(document).ready(function() {
//     jQuery('#search_again').on('click', function() {
// 	searchAgain();
//     });

// });
<|endoftext|>


<commit_before>/**
 * plugin.js
 *
 * Copyright 2014, Red Sweater Software
 * Released under LGPL License.
 *
 * This plugin includes nuances for behavior of the TinyMCE framework which are not clearly
 * bug fixes that should be submitted as pull requests, but which for Red Sweater's purposes
 * are desirable behaviors across the board.
 */

tinymce.PluginManager.add('redsweater', function(editor) {

	var each = tinymce.util.Tools.each;

	// Cribbed from the table plugin - they historically had behavior that would
	// do the honor of removing the bogus empty paragraph markup from the end of a document
	// as part of serialization. In TinyMCE4 this is strictly relegated when the paragraph
	// is directly preceded by a table, and obviously only effective if the table plugin is
	// actually installed. We want to have this "cleanup" take place in all instances.
	editor.on('PreProcess', function(o) {
		var last = o.node.lastChild;

		// If it's a standalone BR node, or if it's a paragraph with a break node or literal newline in it.
		if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 &&
			(last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))))
		{
			editor.dom.remove(last);
		}
	});

	// The way TinyMCE handles Bold, Italic, Underline, Underscore, etc. is fundamentally wrong for the way
	// the Mac behaves in that it aggressively tries to stretch out and affect e.g. the whole word if you are
	// in the middle of a word like "te|st" where | is the collapsed caret. It would appear the easiest
	// way for us to overcome this behavior without mucking around literally in the TinyMCE sources as we
	// did previously with "sendToBrowser" intercepts in TinyMCE3, is to register ourselves as the
	// implementors of these commands, and pass them through to the browser on our own.

	var overrideCommands = ["Bold", "Italic", "Underline", "Underscore", "Superscript", "Subscript"];
	each(overrideCommands, function (commandName) {
		editor.addCommand(commandName, function(ui, val) {
			var browserSuccess = editor.getDoc().execCommand(commandName, ui, val);

			// The TinyMCE logic is backwards and expects to return false if it should return
			// true on our behalf...
			return (browserSuccess === false);
		});
	});

	// In TinyMCE3 we suffered the terrible behavior of PRE blocks that return would add a new PRE block so that
	// there was a long string of them. They've fixed that in TinyMCE4 however the behavior is now such that
	// carriage returns map to
 by default, making it impossible to get out intuititively. Until we support
	// a more obvious way to get out of a pre block (e.g. by clicking some subtle UI that appears while editing one),
	// we want to maintain the old behavior at least that if you press return twice at the very end of a PRE block
	// it will get you out.
	function nodeInserted(changeEvent) {
		// Only interested if we inserted a BR into a PRE
		if ((changeEvent.relatedNode.nodeName === 'PRE') && (changeEvent.target.nodeName === 'BR'))
		{
			// Did we just insert a second BR in a row at the end of the PRE block?
			// Note that it's weird because sometimes the new br will be inserted BEFORE the existing
			// br at the end, and sometimes after. So two in a row in either case means times to remove
			// them both and add a new paragraph.
			var parent = changeEvent.relatedNode;
			var brNode = changeEvent.target;
			var brPrevious = changeEvent.target.previousElementSibling;
			var brNext = changeEvent.target.nextElementSibling;

			// TinyMCE does some shenanigans where they actually put an extra BR in at the end of the block,
			// for some reason (see "extraBr" in EnterKey.js), so we accept as two BR's "at the end" an
			// inserted BR with a BR previous sibling and a BR next sibling whose next sibling is null.
			if (brPrevious && (brPrevious.nodeName === 'BR'))
			{
				if (brNext && (brNext.nodeName === 'BR') && (brNext.nextElementSibling == null))
				{
					// Zap the two BR nodes that led to this condition, but do so after a delay so
					// the TinyMCE code that is currently inserting these breaks doesn't hit an exception
					// trying to work with the added BR
					window.setTimeout(function (par, first, second) {
						par.removeChild(first);
						par.removeChild(second);

						// Now we actually just employ TinyMCE's default shift-return behavior by faking a key event
						ev = new CustomEvent("keydown", {"keyCode":13, "target":editor});
						ev.shiftKey = true;
						ev.keyCode = 13;
						ev.isImmediatePropagationStopped = function () { return false; };
						ev.isDefaultPrevented = function () { return false; };
						editor.fire('keydown', ev);
					}, 100, parent, brPrevious, brNode);
				}
			}
		}
	}

	// After the editor is done loading, pay attention to node insertions
	editor.on("init", function (e) {
		editor.getBody().addEventListener("DOMNodeInserted", nodeInserted, false);
	});

//	editor.on("change", function(nodeChangeEvent) {
//		var theElement = nodeChangeEvent.element;
//		console.log("Got node change on " + theElement.nodeName);
//	});
});<commit_msg>No need to delay at least in WebKit with setTimeout intended only to essentially postpone action until after the current JavaScript stack has returned.<commit_after>/**
 * plugin.js
 *
 * Copyright 2014, Red Sweater Software
 * Released under LGPL License.
 *
 * This plugin includes nuances for behavior of the TinyMCE framework which are not clearly
 * bug fixes that should be submitted as pull requests, but which for Red Sweater's purposes
 * are desirable behaviors across the board.
 */

tinymce.PluginManager.add('redsweater', function(editor) {

	var each = tinymce.util.Tools.each;

	// Cribbed from the table plugin - they historically had behavior that would
	// do the honor of removing the bogus empty paragraph markup from the end of a document
	// as part of serialization. In TinyMCE4 this is strictly relegated when the paragraph
	// is directly preceded by a table, and obviously only effective if the table plugin is
	// actually installed. We want to have this "cleanup" take place in all instances.
	editor.on('PreProcess', function(o) {
		var last = o.node.lastChild;

		// If it's a standalone BR node, or if it's a paragraph with a break node or literal newline in it.
		if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 &&
			(last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))))
		{
			editor.dom.remove(last);
		}
	});

	// The way TinyMCE handles Bold, Italic, Underline, Underscore, etc. is fundamentally wrong for the way
	// the Mac behaves in that it aggressively tries to stretch out and affect e.g. the whole word if you are
	// in the middle of a word like "te|st" where | is the collapsed caret. It would appear the easiest
	// way for us to overcome this behavior without mucking around literally in the TinyMCE sources as we
	// did previously with "sendToBrowser" intercepts in TinyMCE3, is to register ourselves as the
	// implementors of these commands, and pass them through to the browser on our own.

	var overrideCommands = ["Bold", "Italic", "Underline", "Underscore", "Superscript", "Subscript"];
	each(overrideCommands, function (commandName) {
		editor.addCommand(commandName, function(ui, val) {
			var browserSuccess = editor.getDoc().execCommand(commandName, ui, val);

			// The TinyMCE logic is backwards and expects to return false if it should return
			// true on our behalf...
			return (browserSuccess === false);
		});
	});

	// In TinyMCE3 we suffered the terrible behavior of PRE blocks that return would add a new PRE block so that
	// there was a long string of them. They've fixed that in TinyMCE4 however the behavior is now such that
	// carriage returns map to
 by default, making it impossible to get out intuititively. Until we support
	// a more obvious way to get out of a pre block (e.g. by clicking some subtle UI that appears while editing one),
	// we want to maintain the old behavior at least that if you press return twice at the very end of a PRE block
	// it will get you out.
	function nodeInserted(changeEvent) {
		// Only interested if we inserted a BR into a PRE
		if ((changeEvent.relatedNode.nodeName === 'PRE') && (changeEvent.target.nodeName === 'BR'))
		{
			// Did we just insert a second BR in a row at the end of the PRE block?
			// Note that it's weird because sometimes the new br will be inserted BEFORE the existing
			// br at the end, and sometimes after. So two in a row in either case means times to remove
			// them both and add a new paragraph.
			var parent = changeEvent.relatedNode;
			var brNode = changeEvent.target;
			var brPrevious = changeEvent.target.previousElementSibling;
			var brNext = changeEvent.target.nextElementSibling;

			// TinyMCE does some shenanigans where they actually put an extra BR in at the end of the block,
			// for some reason (see "extraBr" in EnterKey.js), so we accept as two BR's "at the end" an
			// inserted BR with a BR previous sibling and a BR next sibling whose next sibling is null.
			if (brPrevious && (brPrevious.nodeName === 'BR'))
			{
				if (brNext && (brNext.nodeName === 'BR') && (brNext.nextElementSibling == null))
				{
					// Zap the two BR nodes that led to this condition, but do so after a delay so
					// the TinyMCE code that is currently inserting these breaks doesn't hit an exception
					// trying to work with the added BR
					window.setTimeout(function (par, first, second) {
						par.removeChild(first);
						par.removeChild(second);

						// Now we actually just employ TinyMCE's default shift-return behavior by faking a key event
						ev = new CustomEvent("keydown", {"keyCode":13, "target":editor});
						ev.shiftKey = true;
						ev.keyCode = 13;
						ev.isImmediatePropagationStopped = function () { return false; };
						ev.isDefaultPrevented = function () { return false; };
						editor.fire('keydown', ev);
					}, 0, parent, brPrevious, brNode);
				}
			}
		}
	}

	// After the editor is done loading, pay attention to node insertions
	editor.on("init", function (e) {
		editor.getBody().addEventListener("DOMNodeInserted", nodeInserted, false);
	});

//	editor.on("change", function(nodeChangeEvent) {
//		var theElement = nodeChangeEvent.element;
//		console.log("Got node change on " + theElement.nodeName);
//	});
});<|endoftext|>


<commit_before>
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// Load jquery and jquery-ui
var $ = require('jquery');
window.$ = window.jQuery = $;
require('jquery-ui');

// Load styling
require('jquery-ui/themes/smoothness/jquery-ui.min.css');
require('font-awesome/css/font-awesome.css');
require('../css/widgets.min.css');

// Magic global widget rendering function:
var widgets = require('./index');
var manager = new widgets.EmbedManager();

function loadInlineWidgets(event) {
    // If requirejs is not on the page on page load, load it from cdn.
    if (!window.requirejs) {
        var scriptjs = require('scriptjs');
        scriptjs('https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js', function() {
            renderInlineWidgets(event);
        });
    } else {
        renderInlineWidgets(event);
    }
}

function renderInlineWidgets(event) {
  // Define jupyter-js-widget requirejs module
  // This is needed for custom widget model to be able to require
  // jupyter-js-widgets.
  window.define('jupyter-js-widgets', function() {
      return widgets;
  });

  var element = event.target || document;
  var tags = element.querySelectorAll('script[type="application/vnd.jupyter-embedded-widgets"]');
  for (var i=0; i!=tags.length; ++i) {
    var tag = tags[i];
    var widgetStateObject = JSON.parse(tag.innerHTML);
    var widgetContainer = document.createElement('div');
    widgetContainer.className = 'widget-area';
    manager.display_widget_state(widgetStateObject, widgetContainer).then(function() {
        if (tag.previousElementSibling &&
            tag.previousElementSibling.matches('img.jupyter-widget')) {
            tag.parentElement.removeChild(tag.previousElementSibling);
        }
        tag.parentElement.insertBefore(widgetContainer, tag);
    });
  }
}

window.addEventListener('load', loadInlineWidgets);

// Module exports
module.exports = {
    manager: manager,
    renderInlineWidgets: renderInlineWidgets
}
<commit_msg>Use npmcdn<commit_after>
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// Load jquery and jquery-ui
var $ = require('jquery');
window.$ = window.jQuery = $;
require('jquery-ui');

// Load styling
require('jquery-ui/themes/smoothness/jquery-ui.min.css');
require('font-awesome/css/font-awesome.css');
require('../css/widgets.min.css');

// Magic global widget rendering function:
var widgets = require('./index');
var manager = new widgets.EmbedManager();

function loadInlineWidgets(event) {
    // If requirejs is not on the page on page load, load it from cdn.
    if (!window.requirejs) {
        var scriptjs = require('scriptjs');
        scriptjs('https://npmcdn.com/requirejs/require.js', function() {
            renderInlineWidgets(event);
        });
    } else {
        renderInlineWidgets(event);
    }
}

function renderInlineWidgets(event) {
  // Define jupyter-js-widget requirejs module
  // This is needed for custom widget model to be able to require
  // jupyter-js-widgets.
  window.define('jupyter-js-widgets', function() {
      return widgets;
  });

  var element = event.target || document;
  var tags = element.querySelectorAll('script[type="application/vnd.jupyter-embedded-widgets"]');
  for (var i=0; i!=tags.length; ++i) {
    var tag = tags[i];
    var widgetStateObject = JSON.parse(tag.innerHTML);
    var widgetContainer = document.createElement('div');
    widgetContainer.className = 'widget-area';
    manager.display_widget_state(widgetStateObject, widgetContainer).then(function() {
        if (tag.previousElementSibling &&
            tag.previousElementSibling.matches('img.jupyter-widget')) {
            tag.parentElement.removeChild(tag.previousElementSibling);
        }
        tag.parentElement.insertBefore(widgetContainer, tag);
    });
  }
}

window.addEventListener('load', loadInlineWidgets);

// Module exports
module.exports = {
    manager: manager,
    renderInlineWidgets: renderInlineWidgets
}
<|endoftext|>


<commit_before>// TODO - optimize code

var Lang = A.Lang,
	isArray = Lang.isArray,
	isObject = Lang.isObject,
	isValue = Lang.isValue,

	Aeach = A.Array.each,
	getObjectKeys = A.Object.keys,
	getObjectValues = A.Object.values,
	selectorTest = A.Selector.test,
	YgetClassName = A.ClassNameManager.getClassName,

	_toInitialCap = A.cached(function(str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }),

	_getEvtType = A.cached(function(tagName, eventType) {
		return tagName + _toInitialCap(eventType.toLowerCase());
	}),

	BOUNDING_BOX = 'boundingBox',
	CELL = 'cell',
	CELL_SELECTOR = 'cellSelector',
	CLICK = 'click',
	COLUMN = 'column',
	EVENTS = 'events',
	HEADER = 'header',
	HOST = 'host',
	IN_HEAD = 'inHead',
	KEYDOWN = 'keydown',
	KEYUP = 'keyup',
	LINER = 'liner',
	MOUSEDOWN = 'mousedown',
	MOUSEENTER = 'mouseenter',
	MOUSELEAVE = 'mouseleave',
	MOUSEUP = 'mouseup',
	RECORDSET = 'recordset',
	ROW = 'row',
	TABLE = 'table',
	TAGS = 'tags',
	TAG_NAME = 'tagName',
	TBODY = 'tbody',
	THEAD = 'thead',
	TR = 'tr',
    DATATABLE = 'datatable',

	_COMMA = ',',
	_DOT = '.',

	CSS_DATATABLE_LINER = YgetClassName(DATATABLE, LINER);

var DataTableEvents = A.Base.create("dataTableEvents", A.Plugin.Base, [], {
	_bubbling: false,
	_handler: null,
	_tagsFilter: null,

     initializer: function(config) {
		var instance = this;
		var tags = instance.get(TAGS);

		instance._tagsFilter = getObjectKeys(tags).join(_COMMA);

		instance._initEvents();
    },

	destructor: function() {
		var instance = this;
		var handler = instance._handler;

		if (handler) {
			handler.detach();
		}
	},

	_filterBubble: function(target) {
		var instance = this;
		var host = instance.get(HOST);
		var container = host._tableNode.getDOM();

		var nodes = [];

		while (target) {
			var isContainer = (target === container);

			if (selectorTest(target, instance._tagsFilter, (isContainer ? null : container))) {
				nodes.push(target);
			}

			if (isContainer) {
				break;
			}

			target = target.parentNode;
		}

		// Adding respective col node to the nodes array in such a way
		// that the bubble order is: (td|th), tr, col, (tbody|thead) and table.
		if (nodes.length) {
			var column = host.getColNode(A.one(nodes[0]));

			if (column) {
				nodes.splice(2, 0, column.getDOM());
			}
		}

		return nodes;
	},

	_handleEvents: function(event) {
		var i, length;
		var instance = this;
		var host = instance.get(HOST);
		var thead  = host._theadNode;
		var tags = instance.get(TAGS);
		var currentTarget = event.currentTarget;
		var eventType = event.type;

		var inHead = currentTarget.getData(IN_HEAD);
		var liner = currentTarget.getData(LINER);
		var row = currentTarget.getData(ROW);

		if (!isValue(inHead)) {
			inHead = thead.contains(currentTarget);
			currentTarget.setData(IN_HEAD, inHead);
		}

		if (!isValue(liner)) {
			liner = currentTarget.one(_DOT+CSS_DATATABLE_LINER);
			currentTarget.setData(LINER, liner);
		}

		if (!isValue(row)) {
			row = currentTarget.ancestor(TR);
			currentTarget.setData(ROW, row);
		}

		var payload = {
			cell: currentTarget,
			column: host.getColumnByCell(currentTarget),
			inHead: inHead,
			liner: liner,
			originalEvent: event,
			row: row,
			record: host.get(RECORDSET).getRecordByRow(row)
		};

		var nodes = instance._filterBubble(event.currentTarget.getDOM());

		instance._bubbling = true;

		for (i = 0, length = nodes.length; (i < length) && instance._bubbling; i++) {
			var node = A.one(nodes[i]);
			var propertyName = tags[node.get(TAG_NAME).toLowerCase()];

			payload.node = node;
			payload.property = propertyName;

			host.fire(
				_getEvtType(propertyName, eventType),
				payload
			);
		}
	},

	_initEvents: function() {
		var instance = this;
		var host = instance.get(HOST);
		var tags = instance.get(TAGS);
		var events = instance.get(EVENTS);

		var publish = {};

		Aeach(getObjectValues(tags), function(propertyName) {
			Aeach(events, function(eventType) {
				var newEventType = _getEvtType(propertyName, eventType);

				publish[newEventType] = {
					stoppedFn: A.bind(instance._stopBubble, instance)
				};
			});
		});

		host.publish(publish);

		instance._handler = host.get(BOUNDING_BOX).delegate(events, A.bind(instance._handleEvents, instance), instance.get(CELL_SELECTOR));
	},

	_stopBubble: function() {
		var instance = this;

		instance._bubbling = false;
	}
},
{
    NS: "events",

    NAME: "dataTableEvents",

    ATTRS: {
		cellSelector: {
			value: 'td,th',
			writeOnce: true
		},

		events: {
			validator: isArray,
            value: [ CLICK, KEYDOWN, KEYUP, MOUSEDOWN, MOUSEENTER, MOUSELEAVE, MOUSEUP ]
        },

		tags: {
			validator: isObject,
			value: {
				col: COLUMN,
				table: TABLE,
				thead: THEAD,
				tbody: TBODY,
				tr: ROW,
				th: HEADER,
				td: CELL
			},
			writeOnce: true
		}
    }
});

A.namespace("Plugin").DataTableEvents = DataTableEvents;<commit_msg>AUI-413 Add Dblclick DataTable event<commit_after>// TODO - optimize code

var Lang = A.Lang,
	isArray = Lang.isArray,
	isObject = Lang.isObject,
	isValue = Lang.isValue,

	Aeach = A.Array.each,
	getObjectKeys = A.Object.keys,
	getObjectValues = A.Object.values,
	selectorTest = A.Selector.test,
	YgetClassName = A.ClassNameManager.getClassName,

	_toInitialCap = A.cached(function(str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }),

	_getEvtType = A.cached(function(tagName, eventType) {
		return tagName + _toInitialCap(eventType.toLowerCase());
	}),

	BOUNDING_BOX = 'boundingBox',
	CELL = 'cell',
	CELL_SELECTOR = 'cellSelector',
	CLICK = 'click',
	COLUMN = 'column',
	DBLCLICK = 'dblclick',
	EVENTS = 'events',
	HEADER = 'header',
	HOST = 'host',
	IN_HEAD = 'inHead',
	KEYDOWN = 'keydown',
	KEYUP = 'keyup',
	LINER = 'liner',
	MOUSEDOWN = 'mousedown',
	MOUSEENTER = 'mouseenter',
	MOUSELEAVE = 'mouseleave',
	MOUSEUP = 'mouseup',
	RECORDSET = 'recordset',
	ROW = 'row',
	TABLE = 'table',
	TAGS = 'tags',
	TAG_NAME = 'tagName',
	TBODY = 'tbody',
	THEAD = 'thead',
	TR = 'tr',
    DATATABLE = 'datatable',

	_COMMA = ',',
	_DOT = '.',

	CSS_DATATABLE_LINER = YgetClassName(DATATABLE, LINER);

var DataTableEvents = A.Base.create("dataTableEvents", A.Plugin.Base, [], {
	_bubbling: false,
	_handler: null,
	_tagsFilter: null,

     initializer: function(config) {
		var instance = this;
		var tags = instance.get(TAGS);

		instance._tagsFilter = getObjectKeys(tags).join(_COMMA);

		instance._initEvents();
    },

	destructor: function() {
		var instance = this;
		var handler = instance._handler;

		if (handler) {
			handler.detach();
		}
	},

	_filterBubble: function(target) {
		var instance = this;
		var host = instance.get(HOST);
		var container = host._tableNode.getDOM();

		var nodes = [];

		while (target) {
			var isContainer = (target === container);

			if (selectorTest(target, instance._tagsFilter, (isContainer ? null : container))) {
				nodes.push(target);
			}

			if (isContainer) {
				break;
			}

			target = target.parentNode;
		}

		// Adding respective col node to the nodes array in such a way
		// that the bubble order is: (td|th), tr, col, (tbody|thead) and table.
		if (nodes.length) {
			var column = host.getColNode(A.one(nodes[0]));

			if (column) {
				nodes.splice(2, 0, column.getDOM());
			}
		}

		return nodes;
	},

	_handleEvents: function(event) {
		var i, length;
		var instance = this;
		var host = instance.get(HOST);
		var thead  = host._theadNode;
		var tags = instance.get(TAGS);
		var currentTarget = event.currentTarget;
		var eventType = event.type;

		var inHead = currentTarget.getData(IN_HEAD);
		var liner = currentTarget.getData(LINER);
		var row = currentTarget.getData(ROW);

		if (!isValue(inHead)) {
			inHead = thead.contains(currentTarget);
			currentTarget.setData(IN_HEAD, inHead);
		}

		if (!isValue(liner)) {
			liner = currentTarget.one(_DOT+CSS_DATATABLE_LINER);
			currentTarget.setData(LINER, liner);
		}

		if (!isValue(row)) {
			row = currentTarget.ancestor(TR);
			currentTarget.setData(ROW, row);
		}

		var payload = {
			cell: currentTarget,
			column: host.getColumnByCell(currentTarget),
			inHead: inHead,
			liner: liner,
			originalEvent: event,
			row: row,
			record: host.get(RECORDSET).getRecordByRow(row)
		};

		var nodes = instance._filterBubble(event.currentTarget.getDOM());

		instance._bubbling = true;

		for (i = 0, length = nodes.length; (i < length) && instance._bubbling; i++) {
			var node = A.one(nodes[i]);
			var propertyName = tags[node.get(TAG_NAME).toLowerCase()];

			payload.node = node;
			payload.property = propertyName;

			host.fire(
				_getEvtType(propertyName, eventType),
				payload
			);
		}
	},

	_initEvents: function() {
		var instance = this;
		var host = instance.get(HOST);
		var tags = instance.get(TAGS);
		var events = instance.get(EVENTS);

		var publish = {};

		Aeach(getObjectValues(tags), function(propertyName) {
			Aeach(events, function(eventType) {
				var newEventType = _getEvtType(propertyName, eventType);

				publish[newEventType] = {
					stoppedFn: A.bind(instance._stopBubble, instance)
				};
			});
		});

		host.publish(publish);

		instance._handler = host.get(BOUNDING_BOX).delegate(events, A.bind(instance._handleEvents, instance), instance.get(CELL_SELECTOR));
	},

	_stopBubble: function() {
		var instance = this;

		instance._bubbling = false;
	}
},
{
    NS: "events",

    NAME: "dataTableEvents",

    ATTRS: {
		cellSelector: {
			value: 'td,th',
			writeOnce: true
		},

		events: {
			validator: isArray,
            value: [ CLICK, DBLCLICK, KEYDOWN, KEYUP, MOUSEDOWN, MOUSEENTER, MOUSELEAVE, MOUSEUP ]
        },

		tags: {
			validator: isObject,
			value: {
				col: COLUMN,
				table: TABLE,
				thead: THEAD,
				tbody: TBODY,
				tr: ROW,
				th: HEADER,
				td: CELL
			},
			writeOnce: true
		}
    }
});

A.namespace("Plugin").DataTableEvents = DataTableEvents;<|endoftext|>


<commit_before>/* eslint-disable */
import React from 'react'
import assert from 'assert'
import { mount } from 'enzyme'
import { transform } from 'babel-standalone'

// SET TO TRUE WHEN QA IS COMPLETE:
export const QA = true;

// ---------------------------- define challenge title ----------------------------
export const challengeTitle = `<span class = 'default'>Challenge: </span>Connect Redux to React`

// ---------------------------- challenge text ----------------------------
export const challengeText = `Intro: Now that you've written both the mapStateToProps() and the mapDispatchToProps() functions, you can use them to map state and dispatch to the props of one of your React components. The connect method from React Redux can handle this task. This method takes two optional arguments, mapStateToProps() and mapDispatchToProps(). They are optional because you may have a component that only needs access to state but doesn't need to dispatch any actions, or vice versa.

To use this method, pass in the functions as arguments, and immediately call the result with your component. This syntax is a little unusual and looks like:

connect(mapStateToProps, mapDispatchToProps)(MyComponent)

Note
If you want to omit one of the arguments to the connect method, you pass null in its place.`

// ---------------------------- challenge instructions ----------------------------
export const challengeInstructions = `Instructions: The code editor has the mapStateToProps() and mapDispatchToProps() functions and a new React component called Presentational. Connect this component to Redux with the connect method from the ReactRedux global object, and call it immediately on the Presentational component. Assign the result to a new const called ConnectedComponent that represents the connected component. That's it, now you're connected to Redux! Try changing either of connect's arguments to null and observe the test results.`

// ---------------------------- define challenge seed code ----------------------------
export const seedCode =
`const addMessage = (message) => {
	return {
    type: 'ADD',
    message: message
  }
};

const mapStateToProps = (state) => {
  return {
  	messages: state
  }
};

const mapDispatchToProps = (dispatch) => {
  return {
  	submitNewMessage: (message) => {
  		dispatch(addMessage(message));
  	}
  }
};

class Presentational extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		return

	}
};

const connect = ReactRedux.connect;
// change code below this line
`

// ---------------------------- define challenge solution code ----------------------------
export const solutionCode =
`const addMessage = (message) => {
	return {
    type: 'ADD',
    message: message
  }
};

const mapStateToProps = (state) => {
  return {
  	messages: state
  }
};

const mapDispatchToProps = (dispatch) => {
  return {
  	submitNewMessage: (message) => {
  		dispatch(addMessage(message));
  	}
  }
};

class Presentational extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		return
This is a Presentational Component

	}
};

const connect = ReactRedux.connect;
// change code below this line

const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational)`

// ---------------------------- define challenge tests ----------------------------

export const executeTests = (code) => {

	const error_0 = 'Your JSX code should transpile successfully.';
	const error_1 = 'The Presentational component should render.';
	const error_2 = 'The Presentational component should receive a prop \'messages\' via connect.';
	const error_3 = 'The Presentational component should receive a prop \'submitNewMessage\' via connect.';

	let testResults = [
		{
			test: 0,
			status: false,
			condition: error_0
		},
		{
			test: 1,
			status: false,
			condition: error_1
		},
		{
			test: 2,
			status: false,
			condition: error_2
		},
		{
			test: 3,
			status: false,
			condition: error_3
		}
	];

	let es5, mockedComponent, passed = true;

	// this applies an export to the user's code so
	// we can access their component here for tests

	const exportScript = `\n
		const store = Redux.createStore(
			(state = '__INITIAL__STATE__', action) => state
		);

		class AppWrapper extends React.Component {
			render() {
				return (
					<ReactRedux.Provider store = {store}>
						<ConnectedComponent/>
					</ReactRedux.Provider>
				);
			}
		}; export default AppWrapper;`
	const modifiedCode = code.concat(exportScript);

	// test 0: try to transpile JSX, ES6 code to ES5 in browser
	try {
		es5 = transform(modifiedCode, { presets: [ 'es2015', 'stage-2', 'react' ] }).code;
		testResults[0].status = true;
	} catch (err) {
		passed = false;
		testResults[0].status = false;
	}

	// now we will try to shallow render the component with Enzyme's shallow method
	// you can also use mount to perform a full render to the DOM environment
	// to do this you must import mount above; i.e. import { shallow, mount } from enzyme
	try {
		mockedComponent = mount(React.createElement(eval(es5)));
	} catch (err) {
		passed = false;
	}

	// run specific tests to verify the functionality
	// that the challenge is trying to assess:

	// test 1:
	try {
		assert.strictEqual(mockedComponent.find('Presentational').length, 1, error_1);
		testResults[1].status = true;
	} catch (err) {
		passed = false;
		testResults[1].status = false;
	}

	let props;

	// test 2:
	try {
		props = mockedComponent.find('Presentational').node.props;
		assert.strictEqual(props.messages, '__INITIAL__STATE__', error_2);
		testResults[2].status = true;
	} catch (err) {
		passed = false;
		testResults[2].status = false;
	}

	// test 3:
	try {
		assert.strictEqual(typeof props.submitNewMessage, 'function', error_3);
		testResults[3].status = true;
	} catch (err) {
		passed = false;
		testResults[3].status = false;
	}

	return {
		passed,
		testResults
	}

}

// ---------------------------- define live render function ----------------------------

export const liveRender = (code) => {

	try {
	const exportScript = `\n
		const store = Redux.createStore(
			(state = '__INITIAL__STATE__', action) => state
		);

		class AppWrapper extends React.Component {
			render() {
				return (
					<ReactRedux.Provider store = {store}>
						<ConnectedComponent/>
					</ReactRedux.Provider>
				);
			}
		}; export default AppWrapper;`
		const modifiedCode = code.concat(exportScript);
		const es5 = transform(modifiedCode, { presets: [ 'es2015', 'stage-2', 'react' ] }).code;
		const renderedComponent = React.createElement(eval(es5));
		return renderedComponent;
	} catch (err) {
		console.log(err);
	}

}
<commit_msg>edited react-redux 07<commit_after>/* eslint-disable */
import React from 'react'
import assert from 'assert'
import { mount } from 'enzyme'
import { transform } from 'babel-standalone'

// SET TO TRUE WHEN QA IS COMPLETE:
export const QA = true;

// ---------------------------- define challenge title ----------------------------
export const challengeTitle = `<span class = 'default'>Challenge: </span>Connect Redux to React`

// ---------------------------- challenge text ----------------------------
export const challengeText = `Intro: Now that you've written both the mapStateToProps() and the mapDispatchToProps() functions, you can use them to map state and dispatch to the props of one of your React components. The connect method from React Redux can handle this task. This method takes two optional arguments, mapStateToProps() and mapDispatchToProps(). They are optional because you may have a component that only needs access to state but doesn't need to dispatch any actions, or vice versa.

To use this method, pass in the functions as arguments, and immediately call the result with your component. This syntax is a little unusual and looks like:

connect(mapStateToProps, mapDispatchToProps)(MyComponent)

Note
If you want to omit one of the arguments to the connect method, you pass null in its place.`

// ---------------------------- challenge instructions ----------------------------
export const challengeInstructions = `Instructions: The code editor has the mapStateToProps() and mapDispatchToProps() functions and a new React component called Presentational. Connect this component to Redux with the connect method from the ReactRedux global object, and call it immediately on the Presentational component. Assign the result to a new const called ConnectedComponent that represents the connected component. That's it, now you're connected to Redux! Try changing either of connect's arguments to null and observe the test results.`

// ---------------------------- define challenge seed code ----------------------------
export const seedCode =
`const addMessage = (message) => {
	return {
    type: 'ADD',
    message: message
  }
};

const mapStateToProps = (state) => {
  return {
  	messages: state
  }
};

const mapDispatchToProps = (dispatch) => {
  return {
  	submitNewMessage: (message) => {
  		dispatch(addMessage(message));
  	}
  }
};

class Presentational extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		return
This is a Presentational Component

	}
};

const connect = ReactRedux.connect;
// change code below this line
`

// ---------------------------- define challenge solution code ----------------------------
export const solutionCode =
`const addMessage = (message) => {
	return {
    type: 'ADD',
    message: message
  }
};

const mapStateToProps = (state) => {
  return {
  	messages: state
  }
};

const mapDispatchToProps = (dispatch) => {
  return {
  	submitNewMessage: (message) => {
  		dispatch(addMessage(message));
  	}
  }
};

class Presentational extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		return
This is a Presentational Component

	}
};

const connect = ReactRedux.connect;
// change code below this line

const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational)`

// ---------------------------- define challenge tests ----------------------------

export const executeTests = (code) => {

	const error_0 = 'Your JSX code should transpile successfully.';
	const error_1 = 'The Presentational component should render.';
	const error_2 = 'The Presentational component should receive a prop \'messages\' via connect.';
	const error_3 = 'The Presentational component should receive a prop \'submitNewMessage\' via connect.';

	let testResults = [
		{
			test: 0,
			status: false,
			condition: error_0
		},
		{
			test: 1,
			status: false,
			condition: error_1
		},
		{
			test: 2,
			status: false,
			condition: error_2
		},
		{
			test: 3,
			status: false,
			condition: error_3
		}
	];

	let es5, mockedComponent, passed = true;

	// this applies an export to the user's code so
	// we can access their component here for tests

	const exportScript = `\n
		const store = Redux.createStore(
			(state = '__INITIAL__STATE__', action) => state
		);

		class AppWrapper extends React.Component {
			render() {
				return (
					<ReactRedux.Provider store = {store}>
						<ConnectedComponent/>
					</ReactRedux.Provider>
				);
			}
		}; export default AppWrapper;`
	const modifiedCode = code.concat(exportScript);

	// test 0: try to transpile JSX, ES6 code to ES5 in browser
	try {
		es5 = transform(modifiedCode, { presets: [ 'es2015', 'stage-2', 'react' ] }).code;
		testResults[0].status = true;
	} catch (err) {
		passed = false;
		testResults[0].status = false;
	}

	// now we will try to shallow render the component with Enzyme's shallow method
	// you can also use mount to perform a full render to the DOM environment
	// to do this you must import mount above; i.e. import { shallow, mount } from enzyme
	try {
		mockedComponent = mount(React.createElement(eval(es5)));
	} catch (err) {
		passed = false;
	}

	// run specific tests to verify the functionality
	// that the challenge is trying to assess:

	// test 1:
	try {
		assert.strictEqual(mockedComponent.find('Presentational').length, 1, error_1);
		testResults[1].status = true;
	} catch (err) {
		passed = false;
		testResults[1].status = false;
	}

	let props;

	// test 2:
	try {
		props = mockedComponent.find('Presentational').node.props;
		assert.strictEqual(props.messages, '__INITIAL__STATE__', error_2);
		testResults[2].status = true;
	} catch (err) {
		passed = false;
		testResults[2].status = false;
	}

	// test 3:
	try {
		assert.strictEqual(typeof props.submitNewMessage, 'function', error_3);
		testResults[3].status = true;
	} catch (err) {
		passed = false;
		testResults[3].status = false;
	}

	return {
		passed,
		testResults
	}

}

// ---------------------------- define live render function ----------------------------

export const liveRender = (code) => {

	try {
	const exportScript = `\n
		const store = Redux.createStore(
			(state = '__INITIAL__STATE__', action) => state
		);

		class AppWrapper extends React.Component {
			render() {
				return (
					<ReactRedux.Provider store = {store}>
						<ConnectedComponent/>
					</ReactRedux.Provider>
				);
			}
		}; export default AppWrapper;`
		const modifiedCode = code.concat(exportScript);
		const es5 = transform(modifiedCode, { presets: [ 'es2015', 'stage-2', 'react' ] }).code;
		const renderedComponent = React.createElement(eval(es5));
		return renderedComponent;
	} catch (err) {
		console.log(err);
	}

}
<|endoftext|>


<commit_before>import {SecurityServiceServer} from "../../server/service/SecurityServiceServer"
import {ServerTaskService} from "../../server/service/ServerTaskService"
import {TimeSlotService} from "../../both/service/TimeSlotService"
import { Accounts } from 'meteor/accounts-base'

/** @class ServerUserService */
export class ServerUserService {

    static updateUserName(userId,newUsername){
        Accounts.setUsername(userId, newUsername);
    }

    static updateUserEmail(userId,newEmail) {

        var email = Meteor.users.findOne(userId).emails[0];
        if (email) {
            var oldEmail = email.address;
            Accounts.removeEmail(userId, oldEmail);
        }
        Accounts.addEmail(userId, newEmail);
    }

    static sendVerificationEmail(userId){
        Accounts.sendVerificationEmail(userId);
    }

    /**
     * @summary Update a user to add specific fields not handled by Meteor Accounts
     * @param userId (will be always null)
     * @param doc
     * @locus server
     */
    static updateUser(userId, doc) {
        //user is not log in yet, userId is null, we bypass security with .direct and propagate role with direct call to method
        var settings = Settings.findOne();
        if(settings)
            var defaultGroupRolesId = settings.defaultGroupRoles;
        if(!defaultGroupRolesId){
            console.error("No default group role has been found, user will not be able to access anything");
            return;
        }

        if(!defaultGroupRolesId){
            console.error(`Are you injecting data pragmatically ? If so, ignore this message. GroupRoles with id defaultGroupRolesId doesn't exists, user '${doc.name}' will not be created as a custom user as it won't have any roles. `)
            return;
        }

        var _id = Meteor.users.update(doc._id, {
            $set: {
                groupRoles: [defaultGroupRolesId],
            }
        });

        console.info("A new user has been update :"+doc.username+" whith _id :"+_id+" and '"+defaultGroupRolesId+"' group roles");
    }

    /**
     * @summary GroupRoles.after.update hook
     * @description
     * If group roles' roles are updated user roles are update as well.
     * @locus server
     * @param userId
     * @param doc
     * @param fieldNames
     * @param modifier
     * @param options
     */
    static propagateGroupRoles(userId, doc, fieldNames, modifier, options) {
        if (_.contains(fieldNames, "roles")) {

            //users having the updated groupRole
            var users = Meteor.users.find({groupRoles: doc._id}).fetch();
            users.forEach(user => {
                //update user roles, Account roles will be update by hooks
                var modifier = {
                    $set: {groupRoles: user.groupRoles}
                };
                //will fire the Meteor.users after hook and call propagateRoles
                Meteor.users.direct.update(user._id, modifier);
            });
        }
    }

    /**
     * @summary Meteor.users.after.update hook.
     * @description
     * About roles, we only add roles to the custom Meteor.users collection, **not** with the Roles library. This hooks is responsible to propagate roles to the
     * Meteor.users linked account.
     * @locus server
     * @param userId
     * @param doc
     * @param fieldNames
     * @param modifier
     * @param options
     */
    static propagateRoles(userId, doc, fieldNames, modifier) {
        var allGroupRolesMerged;
        if (fieldNames) { //update
            if (_.contains(fieldNames, "groupRoles")) {
                if (modifier.$set) {
                    //we have to merge roles and roles from groups
                    allGroupRolesMerged = ServerUserService.getRolesFromGroupRoles(modifier.$set.groupRoles);
                } else if(modifier.$push){
                    allGroupRolesMerged = ServerUserService.getRolesFromGroupRoles(modifier.$push.groupRoles.$each);
                } else {
                    //we have to remove all roles
                    allGroupRolesMerged = [];
                }
                console.info("Roles.setUserRoles "+ doc._id,allGroupRolesMerged);
                Roles.setUserRoles(doc._id, allGroupRolesMerged); //add to Account package
            }
        } else {//insert
            if(doc.groupRoles){
                var allGroupRolesMerged = ServerUserService.getRolesFromGroupRoles(doc.groupRoles);
                console.info("Roles.setUserRoles "+ doc._id,allGroupRolesMerged);
                Roles.setUserRoles(doc._id, allGroupRolesMerged); //add to Account package
            }
        }

    }

    static getRolesFromGroupRoles(groupRoles) {
        var newGroups = GroupRoles.find({_id: {$in: groupRoles}}).fetch(); //get all related roles
        var result = []; //reduce to get only the roles array
        newGroups.forEach(group => {
            result.push(group.roles);
        });
        var allGroupRolesMerged = _.uniq(_.flatten(result)); //obtain a single array
        return allGroupRolesMerged;
    }

    /**
     * @summary  Meteor.users.before.insert
     * @description
     * - Collection Hooks :  Meteor.users.before.insert
     * - Needed role : USERWRITE
     */
    static allowInsert(userId, doc) {
        if(!userId){
            console.info("allow insert user because userId is undefined, meaning a new user should be created with username "+doc.userName);
            return true;
        }
        SecurityServiceServer.grantAccessToItem(userId, RolesEnum.USERWRITE, doc, 'user');
    }

    /**
     * @summary  Meteor.users.before.update
     * @description
     * - Collection Hooks :  Meteor.users.before.update
     * - Needed role : USERWRITE
     *    - ROLE
     *    - ASSIGNMENTTASKUSER
     *
     * if userId is the doc being updated, no need of USERWRITE (a user can update itself)
     */
    static allowUpdate(userId, doc, fieldNames, modifier, options) {
        var user = Meteor.users.findOne(doc._id);

        //we are skipping some security test

        //Meteor account doing its bizness
        if(_.contains(fieldNames, "services") && ( fieldNames.length === 1 || fieldNames.length === 2 && _.contains(fieldNames, "emails")) ){
            console.info("Users.allowUpdate : authorizing updating user services because default its Meteor.account bizness")
            return true;
        }

        if(user.username === SUPERADMIN){
            if (_.contains(fieldNames, "groupRoles"))
                if (modifier.$set.groupRoles)
                    if(user.groupRoles.length !== 0)
                        throw new Meteor.Error("403","User superadmin can not be updated");

            //when superadmin is created, there is just superadmin group roles
            if (_.contains(fieldNames, "roles")) {
                var superadminGroupRoles = GroupRoles.findOne({name:"superadmin"});
                if (modifier.$set && modifier.$set.roles
                    && modifier.$set.roles.length === superadminGroupRoles.roles.length
                    && _.difference(modifier.$set.roles, superadminGroupRoles.roles).length === 0) {
                    console.info("Users.allowUpdate : authorizing setting superadmin roles because superadmin group role has been used")
                    return true;
                }
            }
        }

        //when inserting new user, its default group role have to be propagated even if default group role doesn't provide any roles
        var defaultGroupRoles = GroupRoles.findOne(Settings.findOne().defaultGroupRoles);

        //we authorize to $SET DEFAULT group role without any security check
        if (_.contains(fieldNames, "groupRoles") && fieldNames.length === 1)
            if (modifier.$set && modifier.$set.groupRoles
                && modifier.$set.groupRoles.length === 1) { //todo check ce qu'il y a dedans le groupRoles
                console.info("Users.allowUpdate : authorizing setting user group roles because default group role has been used")
                return true;
            }

        //we authorize Meteor.account to update user roles to DEFAULT without any security check
        if (_.contains(fieldNames, "roles"))
            if (modifier.$set && modifier.$set.roles
                && modifier.$set.roles.length === defaultGroupRoles.roles.length
                && _.difference(modifier.$set.roles, defaultGroupRoles.roles).length === 0) {
                console.info("Users.allowUpdate : authorizing setting user roles because default group has been used")
                return true;
            }

        //back to classic security check
        if (userId !== doc._id)
                SecurityServiceServer.grantAccessToItem(userId, RolesEnum.USERWRITE, doc, 'user');

        if (_.contains(fieldNames, "groupRoles") ){
            SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ROLE, doc, 'user');
        }

        if (_.contains(fieldNames, "isReadyForAssignment"))
            if (modifier.$set.isReadyForAssignment)
                SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');

        if (_.contains(fieldNames, "teams")) {
            if (modifier.$pull)
                if (modifier.$pull.teams) {
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
                }
            if (modifier.$set) //TODO a virer
                if (modifier.$set.teams)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
            if (modifier.$push)
                if (modifier.$push.teams) {
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
                }
        }

        if (_.contains(fieldNames, "assignments")) {
            if (modifier.$pull)
                if (modifier.$pull.assignments)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
            if (modifier.$set) //TODO a virer
                if (modifier.$set.assignments)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
            if (modifier.$push)
                if (modifier.$push.assignments)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
        }

    }

    /**
     * @summary  Meteor.users.before.remove
     * @description
     * - Collection Hooks :  Meteor.users.before.remove
     * - Needed role : USERDELETE
     */
    static allowDelete(userId, doc) {
        SecurityServiceServer.grantAccessToItem(userId, RolesEnum.USERDELETE, doc, 'task');

        if(Meteor.users.findOne(doc._id).name === SUPERADMIN){
            throw new Meteor.Error("403","User superadmin can not be deleted");
        }

        if(doc.assingments && doc.assignments.length !== 0 ){
            throw new Meteor.Error("403", "Can't delete user with assignments");
        }
    }

}<commit_msg>[RPI] feat (auth) : read data from Google<commit_after>import {SecurityServiceServer} from "../../server/service/SecurityServiceServer"
import {ServerTaskService} from "../../server/service/ServerTaskService"
import {TimeSlotService} from "../../both/service/TimeSlotService"
import { Accounts } from 'meteor/accounts-base'

/** @class ServerUserService */
export class ServerUserService {

    static updateUserName(userId,newUsername){
        Accounts.setUsername(userId, newUsername);
    }

    static updateUserEmail(userId,newEmail) {

        var email = Meteor.users.findOne(userId).emails[0];
        if (email) {
            var oldEmail = email.address;
            Accounts.removeEmail(userId, oldEmail);
        }
        Accounts.addEmail(userId, newEmail);
    }

    static sendVerificationEmail(userId){
        Accounts.sendVerificationEmail(userId);
    }

    /**
     * @summary Update a user to add specific fields not handled by Meteor Accounts
     * @param userId (will be always null)
     * @param doc
     * @locus server
     */
    static updateUser(userId, doc) {
        //user is not log in yet, userId is null, we bypass security with .direct and propagate role with direct call to method
        var settings = Settings.findOne();
        if(settings)
            var defaultGroupRolesId = settings.defaultGroupRoles;
        if(!defaultGroupRolesId){
            console.error("No default group role has been found, user will not be able to access anything");
            return;
        }

        if(!defaultGroupRolesId){
            console.error(`Are you injecting data pragmatically ? If so, ignore this message. GroupRoles with id defaultGroupRolesId doesn't exists, user '${doc.name}' will not be created as a custom user as it won't have any roles. `)
            return;
        }

        var _id = Meteor.users.update(doc._id, {
            $set: {
                groupRoles: [defaultGroupRolesId],
            }
        });

        if(doc.services && doc.services.google){
            var google = doc.services.google;
            console.info("Read user data from Google");
            Meteor.users.update(doc._id, {
                $set: {
                    'profile.familyName': google.family_name,
                    'profile.firstName': google.given_name,
                    'username': google.name
                }
            });
            Accounts.addEmail(doc._id, google.email, google.verified_email);
            console.info("Data adder from Google",google.family_name,google.given_name,google.email);
        }

            console.info("A new user has been update :"+doc.username+" whith _id :"+_id+" and '"+defaultGroupRolesId+"' group roles");
    }

    /**
     * @summary GroupRoles.after.update hook
     * @description
     * If group roles' roles are updated user roles are update as well.
     * @locus server
     * @param userId
     * @param doc
     * @param fieldNames
     * @param modifier
     * @param options
     */
    static propagateGroupRoles(userId, doc, fieldNames, modifier, options) {
        if (_.contains(fieldNames, "roles")) {

            //users having the updated groupRole
            var users = Meteor.users.find({groupRoles: doc._id}).fetch();
            users.forEach(user => {
                //update user roles, Account roles will be update by hooks
                var modifier = {
                    $set: {groupRoles: user.groupRoles}
                };
                //will fire the Meteor.users after hook and call propagateRoles
                Meteor.users.direct.update(user._id, modifier);
            });
        }
    }

    /**
     * @summary Meteor.users.after.update hook.
     * @description
     * About roles, we only add roles to the custom Meteor.users collection, **not** with the Roles library. This hooks is responsible to propagate roles to the
     * Meteor.users linked account.
     * @locus server
     * @param userId
     * @param doc
     * @param fieldNames
     * @param modifier
     * @param options
     */
    static propagateRoles(userId, doc, fieldNames, modifier) {
        var allGroupRolesMerged;
        if (fieldNames) { //update
            if (_.contains(fieldNames, "groupRoles")) {
                if (modifier.$set) {
                    //we have to merge roles and roles from groups
                    allGroupRolesMerged = ServerUserService.getRolesFromGroupRoles(modifier.$set.groupRoles);
                } else if(modifier.$push){
                    allGroupRolesMerged = ServerUserService.getRolesFromGroupRoles(modifier.$push.groupRoles.$each);
                } else {
                    //we have to remove all roles
                    allGroupRolesMerged = [];
                }
                console.info("Roles.setUserRoles "+ doc._id,allGroupRolesMerged);
                Roles.setUserRoles(doc._id, allGroupRolesMerged); //add to Account package
            }
        } else {//insert
            if(doc.groupRoles){
                var allGroupRolesMerged = ServerUserService.getRolesFromGroupRoles(doc.groupRoles);
                console.info("Roles.setUserRoles "+ doc._id,allGroupRolesMerged);
                Roles.setUserRoles(doc._id, allGroupRolesMerged); //add to Account package
            }
        }

    }

    static getRolesFromGroupRoles(groupRoles) {
        var newGroups = GroupRoles.find({_id: {$in: groupRoles}}).fetch(); //get all related roles
        var result = []; //reduce to get only the roles array
        newGroups.forEach(group => {
            result.push(group.roles);
        });
        var allGroupRolesMerged = _.uniq(_.flatten(result)); //obtain a single array
        return allGroupRolesMerged;
    }

    /**
     * @summary  Meteor.users.before.insert
     * @description
     * - Collection Hooks :  Meteor.users.before.insert
     * - Needed role : USERWRITE
     */
    static allowInsert(userId, doc) {
        if(!userId){
            console.info("allow insert user because userId is undefined, meaning a new user should be created with username "+doc.userName);
            return true;
        }
        SecurityServiceServer.grantAccessToItem(userId, RolesEnum.USERWRITE, doc, 'user');
    }

    /**
     * @summary  Meteor.users.before.update
     * @description
     * - Collection Hooks :  Meteor.users.before.update
     * - Needed role : USERWRITE
     *    - ROLE
     *    - ASSIGNMENTTASKUSER
     *
     * if userId is the doc being updated, no need of USERWRITE (a user can update itself)
     */
    static allowUpdate(userId, doc, fieldNames, modifier, options) {
        var user = Meteor.users.findOne(doc._id);

        //we are skipping some security test

        //Meteor account doing its bizness
        if(_.contains(fieldNames, "services") && ( fieldNames.length === 1 || fieldNames.length === 2 && _.contains(fieldNames, "emails")) ){
            console.info("Users.allowUpdate : authorizing updating user services because default its Meteor.account bizness : services, add email")
            return true;
        }
        if(_.contains(fieldNames, "profile") &&  _.contains(fieldNames, "username") && fieldNames.length === 2 ){
            if(modifier.$set && modifier.$set.username && modifier.$set['profile.firstName']&& modifier.$set['profile.familyName'])
                if(doc.profile.username == null && doc.profile.firstName == null && doc.profile.familyName == null) {
                    console.info("Users.allowUpdate : authorizing updating user services because default its Meteor.account bizness : updating firstName, familyName and username that where null")
                    return true;
                }
        }
        if(_.contains(fieldNames, "emails") &&  fieldNames.length === 1 ){
            //if(modifier.$addToSet && modifier.$addToSet['emails.address'] ) {
                console.info("Users.allowUpdate : authorizing updating user services because default its Meteor.account bizness : adding email because ")
                return true;
           // }
        }

        if(user.username === SUPERADMIN){
            if (_.contains(fieldNames, "groupRoles"))
                if (modifier.$set.groupRoles)
                    if(user.groupRoles.length !== 0)
                        throw new Meteor.Error("403","User superadmin can not be updated");

            //when superadmin is created, there is just superadmin group roles
            if (_.contains(fieldNames, "roles")) {
                var superadminGroupRoles = GroupRoles.findOne({name:"superadmin"});
                if (modifier.$set && modifier.$set.roles
                    && modifier.$set.roles.length === superadminGroupRoles.roles.length
                    && _.difference(modifier.$set.roles, superadminGroupRoles.roles).length === 0) {
                    console.info("Users.allowUpdate : authorizing setting superadmin roles because superadmin group role has been used")
                    return true;
                }
            }
        }

        //when inserting new user, its default group role have to be propagated even if default group role doesn't provide any roles
        var defaultGroupRoles = GroupRoles.findOne(Settings.findOne().defaultGroupRoles);

        //we authorize to $SET DEFAULT group role without any security check
        if (_.contains(fieldNames, "groupRoles") && fieldNames.length === 1)
            if (modifier.$set && modifier.$set.groupRoles
                && modifier.$set.groupRoles.length === 1) { //todo check ce qu'il y a dedans le groupRoles
                console.info("Users.allowUpdate : authorizing setting user group roles because default group role has been used")
                return true;
            }

        //we authorize Meteor.account to update user roles to DEFAULT without any security check
        if (_.contains(fieldNames, "roles"))
            if (modifier.$set && modifier.$set.roles
                && modifier.$set.roles.length === defaultGroupRoles.roles.length
                && _.difference(modifier.$set.roles, defaultGroupRoles.roles).length === 0) {
                console.info("Users.allowUpdate : authorizing setting user roles because default group has been used")
                return true;
            }

        //back to classic security check
        if (userId !== doc._id)
                SecurityServiceServer.grantAccessToItem(userId, RolesEnum.USERWRITE, doc, 'user');

        if (_.contains(fieldNames, "groupRoles") ){
            SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ROLE, doc, 'user');
        }

        if (_.contains(fieldNames, "isReadyForAssignment"))
            if (modifier.$set.isReadyForAssignment)
                SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');

        if (_.contains(fieldNames, "teams")) {
            if (modifier.$pull)
                if (modifier.$pull.teams) {
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
                }
            if (modifier.$set) //TODO a virer
                if (modifier.$set.teams)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
            if (modifier.$push)
                if (modifier.$push.teams) {
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
                }
        }

        if (_.contains(fieldNames, "assignments")) {
            if (modifier.$pull)
                if (modifier.$pull.assignments)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
            if (modifier.$set) //TODO a virer
                if (modifier.$set.assignments)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
            if (modifier.$push)
                if (modifier.$push.assignments)
                    SecurityServiceServer.grantAccessToItem(userId, RolesEnum.ASSIGNMENTTASKUSER, doc, 'user');
        }

    }

    /**
     * @summary  Meteor.users.before.remove
     * @description
     * - Collection Hooks :  Meteor.users.before.remove
     * - Needed role : USERDELETE
     */
    static allowDelete(userId, doc) {
        SecurityServiceServer.grantAccessToItem(userId, RolesEnum.USERDELETE, doc, 'task');

        if(Meteor.users.findOne(doc._id).name === SUPERADMIN){
            throw new Meteor.Error("403","User superadmin can not be deleted");
        }

        if(doc.assingments && doc.assignments.length !== 0 ){
            throw new Meteor.Error("403", "Can't delete user with assignments");
        }
    }

}<|endoftext|>