<commit_before>$(document).ready(function(){

	// bubble popup for brief panel documentation
	$.fn.qTip({
		'pageName': 'phenotype',
		'textAlign': 'left',
		'tip': 'topRight'
	});

	//function to fire off a refresh of a table and it's dropdown filters
	var selectedFilters = "";
	var dropdownsList = new Array();
	// use jquery DataTable for table searching/sorting/pagination
	var aDataTblCols = [0,1,2,3,4,5,6];
	var oDataTable = $.fn.initDataTable($('table#phenotypes'), {
		"aoColumns": [
		              { "sType": "html", "mRender":function( data, type, full ) {
		            	  return (type === "filter") ? $(data).text() : data;
		              }},
		              { "sType": "html", "mRender":function( data, type, full ) {
		            	  return (type === "filter") ? $(data).text() : data;
		              }},
		              { "sType": "string"},
		              { "sType": "string"},
		              { "sType": "alt-string", "bSearchable" : false },
		              /*  { "sType": "string"}, */
		              { "sType": "html"}
		              , { "sType": "string", "bSortable" : false }

		              ],
		              "bDestroy": true,
		              "bFilter":false
	});

	$('[rel=tooltip]').tooltip();

	//$.fn.dataTableshowAllShowLess(oDataTable, aDataTblCols, null);
	$('div#exportIconsDiv').append($.fn.loadFileExporterUI({
		label: 'Export table as:',
		formatSelector: {
			TSV: 'tsv_phenoAssoc',
			XLS: 'xls_phenoAssoc'
		},
		class: 'fileIcon'
	}));

	var mpId = window.location.href.split("/")[window.location.href.split("/").length-1];
	var windowLocation = window.location;
	initFileExporter({
		mpId: "\"" + mpId+ "\"",
		externalDbId: 3,
		fileName: 'gene_variants_with_phen_'+mpId.replace(/:/g,'_'),
		solrCoreName: 'genotype-phenotype',
		dumpMode: 'all',
		baseUrl: windowLocation,
		page:"phenotype",
		gridFields: 'marker_symbol,allele_symbol,zygosity,sex,procedure_name,resource_fullname,parameter_stable_id,mp_term_name,marker_accession_id, parameter_name,parameter_name',
		//TODO add here filter params too
//		params: "qf=auto_suggest&defType=edismax&wt=json&rows=100000&q=*:*&fq=(mp_term_id:\"" + mpId + "&" + dropdownsList[0].name+':(\"' + dropdownsList[0].array.join("\"OR\"") + '\")&' + dropdownsList[1].name+':(\"' + dropdownsList[1].array.join("\"OR\"") + '\")&' + dropdownsList[2].name+':(\"' + dropdownsList[2].array.join("\"OR\"") + '\")' +  "\")"
		params: "qf=auto_suggest&defType=edismax&wt=json&rows=100000&q=*:*&fq=(mp_term_id:\"" + mpId + "\"+OR+top_level_mp_term_id:\"" + mpId + "\")"
	});
	function initFileExporter(conf){
		$('button.fileIcon').click(function(){
			var fileType = $(this).text();
			var url = baseUrl + '/export';
			var sInputs = '';
			for ( var k in conf ){
				if (k == "params")
					sInputs += "<input type='text' name='" + k + "' value='" + conf[k] + selectedFilters + "'>";
				else
					sInputs += "<input type='text' name='" + k + "' value='" + conf[k] + "'>";
			}
			sInputs += "<input type='text' name='fileType' value='" + fileType.toLowerCase() + "'>";
			var form = $("<form action='"+ url + "' method=get>" + sInputs + "</form>");
			_doDataExport(url, form);
			console.log(sInputs);
		}).corner('6px');
	}

	 function _doDataExport(url, form){
	    	$.ajax({
				type: 'GET',
				url: url,
				cache: false,
				data: $(form).serialize(),
				success:function(data){
					$(form).appendTo('body').submit().remove();
				},
				error:function(){
					//alert("Oops, there is error during data export..");
				}
			});
	 }
	function refreshPhenoTable(newUrl){
		//alert(newUrl);
		$.ajax({
			url: newUrl,
			cache: false
		}).done(function( html ) {
			$("#phenotypes_wrapper").html(html);//phenotypes wrapper div has been created by the original datatable so we need to replace this div with the new table and content
			var aDataTblCols = [0,1,2,3,4,5];
			var oDataTable = $.fn.initDataTable($('table#phenotypes'), {
				"aoColumns": [
				              { "sType": "html", "mRender":function( data, type, full ) {
				            	  return (type === "filter") ? $(data).text() : data;
				              }},
				              { "sType": "html", "mRender":function( data, type, full ) {
				            	  return (type === "filter") ? $(data).text() : data;
				              }},
				              { "sType": "string"},
				              { "sType": "alt-string", "bSearchable" : false },
				              /*  { "sType": "string"}, */
				              { "sType": "html"}
				              , { "sType": "string", "bSortable" : false }

				              ],
				              "bDestroy": true,
				              "bFilter":false
			});
		});
	}
	//http://stackoverflow.com/questions/5990386/datatables-search-box-outside-datatable
	//to move the input text or reassign the div that does it and hide the other one??
	//put filtering in another text field than the default so we can position it with the other controls like dropdown ajax filters for project etc

	//stuff for dropdown tick boxes here
	var allDropdowns = new Array();
	allDropdowns[0] = $('#resource_fullname');
	allDropdowns[1] = $('#procedure_name');
	allDropdowns[2] = $('#marker_symbol');
	allDropdowns[3] = $('#mp_term_name');
	// the phenptype dropdown should only be shown on the top level terms pages

	createDropdown(allDropdowns[3].sort(), "Phenotype: All", allDropdowns);
	createDropdown(allDropdowns[0],"Source: All", allDropdowns);
	createDropdown(allDropdowns[1], "Procedure: All", allDropdowns);
	createDropdown(allDropdowns[2].sort(), "Gene: All", allDropdowns);

	function createDropdown(multipleSel, emptyText,  allDd){
		$(multipleSel).dropdownchecklist( { firstItemChecksAll: false, emptyText: emptyText, icon: {},
			minWidth: 150, onItemClick: function(checkbox, selector){
				var justChecked = checkbox.prop("checked");
				console.log("justChecked="+justChecked);
				console.log("checked="+ checkbox.val());
				var values = [];

				for(var  i=0; i < selector.options.length; i++ ) {
					if (selector.options[i].selected && (selector.options[i].value != "")) {
						values .push(selector.options[i].value);
					}
				}

				if(justChecked){
					values.push( checkbox.val());
				}else{//just unchecked value is in the array so we remove it as already ticked
					var index = $.inArray(checkbox.val(), values);
					values.splice(index, 1);
				}

				console.log("values="+values );
				// add current one and create dropdown object
				dd1 = new Object();
				dd1.name = multipleSel.attr('id');
				dd1.array = values; // selected values
				dropdownsList[0] = dd1;

				var ddI  = 1;
				for (var ii=0; ii<allDd.length; ii++) {
					if ($(allDd[ii]).attr('id') != multipleSel.attr('id')) {
						console.log ("here " + allDd[ii].val() + " " + allDd[ii].attr('id'));
						dd = new Object();
						dd.name = allDd[ii].attr('id');
						dd.array = allDd[ii].val() || [];
						dropdownsList[ddI++] = dd;
					}
				}
				console.log("call with " + dropdownsList.length);
				refreshGenesPhenoFrag(dropdownsList);
			}, textFormatFunction: function(options) {
				var selectedOptions = options.filter(":selected");
		        var countOfSelected = selectedOptions.size();
		        var size = options.size();
		        var text = "";
		        if (size > 1){
		        	options.each(function() {
	                    if ($(this).prop("selected")) {
	                        if ( text != "" ) { text += ", "; }
	                        /* NOTE use of .html versus .text, which can screw up ampersands for IE */
	                        var optCss = $(this).attr('style');
	                        var tempspan = $('<span/>');
	                        tempspan.html( $(this).html() );
	                        if ( optCss == null ) {
	                                text += tempspan.html();
	                        } else {
	                                tempspan.attr('style',optCss);
	                                text += $("<span/>").append(tempspan).html();
	                        }
	                    }
	                });
		        }
		        switch(countOfSelected) {
		           case 0: return emptyText;
		           case 1: return selectedOptions.text();
		           case options.size(): return emptyText;
		           default: return text;
		        }
			}
		} );
	}
	//if filter parameters are already set then we need to set them as selected in the dropdowns
	var previousParams=$("#filterParams").html();

	function refreshGenesPhenoFrag(dropdownsList) {
		var rootUrl=window.location.href;
		console.log("genesPhenFrag method (refreshGenesPhenoFrag) called with "+dropdownsList.length);
		var newUrl=rootUrl.replace("phenotypes", "geneVariantsWithPhenotypeTable");
		var output ='?';
		selectedFilters = "";
		for (var it = 0; it < dropdownsList.length; it++){
			console.log(dropdownsList[it].array);
			if(dropdownsList[it].array.length == 1){//if only one entry for this parameter then don't use brackets and or
				output += '&fq=' + dropdownsList[it].name + ':"' + dropdownsList[it].array+'"';
				selectedFilters += '+AND+' + dropdownsList[it].name + ':"' + dropdownsList[it].array+'"';
			}
			if(dropdownsList[it].array.length > 1)	{
				output += '&fq='+dropdownsList[it].name+':(\"' + dropdownsList[it].array.join("\"OR\"") + '\")';
				selectedFilters += '+AND+'+dropdownsList[it].name+':(\"' + dropdownsList[it].array.join("\"OR\"") + '\")';
			}
		}
		newUrl+=output;
		refreshPhenoTable(newUrl);
		return false;
	}
});

<commit_msg>change the page name to match what's in the config<commit_after>$(document).ready(function(){

	// bubble popup for brief panel documentation
	$.fn.qTip({
		'pageName': 'phenotypes',
		'textAlign': 'left',
		'tip': 'topRight'
	});

	//function to fire off a refresh of a table and it's dropdown filters
	var selectedFilters = "";
	var dropdownsList = new Array();
	// use jquery DataTable for table searching/sorting/pagination
	var aDataTblCols = [0,1,2,3,4,5,6];
	var oDataTable = $.fn.initDataTable($('table#phenotypes'), {
		"aoColumns": [
		              { "sType": "html", "mRender":function( data, type, full ) {
		            	  return (type === "filter") ? $(data).text() : data;
		              }},
		              { "sType": "html", "mRender":function( data, type, full ) {
		            	  return (type === "filter") ? $(data).text() : data;
		              }},
		              { "sType": "string"},
		              { "sType": "string"},
		              { "sType": "alt-string", "bSearchable" : false },
		              /*  { "sType": "string"}, */
		              { "sType": "html"}
		              , { "sType": "string", "bSortable" : false }

		              ],
		              "bDestroy": true,
		              "bFilter":false
	});

	$('[rel=tooltip]').tooltip();

	//$.fn.dataTableshowAllShowLess(oDataTable, aDataTblCols, null);
	$('div#exportIconsDiv').append($.fn.loadFileExporterUI({
		label: 'Export table as:',
		formatSelector: {
			TSV: 'tsv_phenoAssoc',
			XLS: 'xls_phenoAssoc'
		},
		class: 'fileIcon'
	}));

	var mpId = window.location.href.split("/")[window.location.href.split("/").length-1];
	var windowLocation = window.location;
	initFileExporter({
		mpId: "\"" + mpId+ "\"",
		externalDbId: 3,
		fileName: 'gene_variants_with_phen_'+mpId.replace(/:/g,'_'),
		solrCoreName: 'genotype-phenotype',
		dumpMode: 'all',
		baseUrl: windowLocation,
		page:"phenotype",
		gridFields: 'marker_symbol,allele_symbol,zygosity,sex,procedure_name,resource_fullname,parameter_stable_id,mp_term_name,marker_accession_id, parameter_name,parameter_name',
		//TODO add here filter params too
//		params: "qf=auto_suggest&defType=edismax&wt=json&rows=100000&q=*:*&fq=(mp_term_id:\"" + mpId + "&" + dropdownsList[0].name+':(\"' + dropdownsList[0].array.join("\"OR\"") + '\")&' + dropdownsList[1].name+':(\"' + dropdownsList[1].array.join("\"OR\"") + '\")&' + dropdownsList[2].name+':(\"' + dropdownsList[2].array.join("\"OR\"") + '\")' +  "\")"
		params: "qf=auto_suggest&defType=edismax&wt=json&rows=100000&q=*:*&fq=(mp_term_id:\"" + mpId + "\"+OR+top_level_mp_term_id:\"" + mpId + "\")"
	});
	function initFileExporter(conf){
		$('button.fileIcon').click(function(){
			var fileType = $(this).text();
			var url = baseUrl + '/export';
			var sInputs = '';
			for ( var k in conf ){
				if (k == "params")
					sInputs += "<input type='text' name='" + k + "' value='" + conf[k] + selectedFilters + "'>";
				else
					sInputs += "<input type='text' name='" + k + "' value='" + conf[k] + "'>";
			}
			sInputs += "<input type='text' name='fileType' value='" + fileType.toLowerCase() + "'>";
			var form = $("<form action='"+ url + "' method=get>" + sInputs + "</form>");
			_doDataExport(url, form);
			console.log(sInputs);
		}).corner('6px');
	}

	 function _doDataExport(url, form){
	    	$.ajax({
				type: 'GET',
				url: url,
				cache: false,
				data: $(form).serialize(),
				success:function(data){
					$(form).appendTo('body').submit().remove();
				},
				error:function(){
					//alert("Oops, there is error during data export..");
				}
			});
	 }
	function refreshPhenoTable(newUrl){
		//alert(newUrl);
		$.ajax({
			url: newUrl,
			cache: false
		}).done(function( html ) {
			$("#phenotypes_wrapper").html(html);//phenotypes wrapper div has been created by the original datatable so we need to replace this div with the new table and content
			var aDataTblCols = [0,1,2,3,4,5];
			var oDataTable = $.fn.initDataTable($('table#phenotypes'), {
				"aoColumns": [
				              { "sType": "html", "mRender":function( data, type, full ) {
				            	  return (type === "filter") ? $(data).text() : data;
				              }},
				              { "sType": "html", "mRender":function( data, type, full ) {
				            	  return (type === "filter") ? $(data).text() : data;
				              }},
				              { "sType": "string"},
				              { "sType": "alt-string", "bSearchable" : false },
				              /*  { "sType": "string"}, */
				              { "sType": "html"}
				              , { "sType": "string", "bSortable" : false }

				              ],
				              "bDestroy": true,
				              "bFilter":false
			});
		});
	}
	//http://stackoverflow.com/questions/5990386/datatables-search-box-outside-datatable
	//to move the input text or reassign the div that does it and hide the other one??
	//put filtering in another text field than the default so we can position it with the other controls like dropdown ajax filters for project etc

	//stuff for dropdown tick boxes here
	var allDropdowns = new Array();
	allDropdowns[0] = $('#resource_fullname');
	allDropdowns[1] = $('#procedure_name');
	allDropdowns[2] = $('#marker_symbol');
	allDropdowns[3] = $('#mp_term_name');
	// the phenptype dropdown should only be shown on the top level terms pages

	createDropdown(allDropdowns[3].sort(), "Phenotype: All", allDropdowns);
	createDropdown(allDropdowns[0],"Source: All", allDropdowns);
	createDropdown(allDropdowns[1], "Procedure: All", allDropdowns);
	createDropdown(allDropdowns[2].sort(), "Gene: All", allDropdowns);

	function createDropdown(multipleSel, emptyText,  allDd){
		$(multipleSel).dropdownchecklist( { firstItemChecksAll: false, emptyText: emptyText, icon: {},
			minWidth: 150, onItemClick: function(checkbox, selector){
				var justChecked = checkbox.prop("checked");
				console.log("justChecked="+justChecked);
				console.log("checked="+ checkbox.val());
				var values = [];

				for(var  i=0; i < selector.options.length; i++ ) {
					if (selector.options[i].selected && (selector.options[i].value != "")) {
						values .push(selector.options[i].value);
					}
				}

				if(justChecked){
					values.push( checkbox.val());
				}else{//just unchecked value is in the array so we remove it as already ticked
					var index = $.inArray(checkbox.val(), values);
					values.splice(index, 1);
				}

				console.log("values="+values );
				// add current one and create dropdown object
				dd1 = new Object();
				dd1.name = multipleSel.attr('id');
				dd1.array = values; // selected values
				dropdownsList[0] = dd1;

				var ddI  = 1;
				for (var ii=0; ii<allDd.length; ii++) {
					if ($(allDd[ii]).attr('id') != multipleSel.attr('id')) {
						console.log ("here " + allDd[ii].val() + " " + allDd[ii].attr('id'));
						dd = new Object();
						dd.name = allDd[ii].attr('id');
						dd.array = allDd[ii].val() || [];
						dropdownsList[ddI++] = dd;
					}
				}
				console.log("call with " + dropdownsList.length);
				refreshGenesPhenoFrag(dropdownsList);
			}, textFormatFunction: function(options) {
				var selectedOptions = options.filter(":selected");
		        var countOfSelected = selectedOptions.size();
		        var size = options.size();
		        var text = "";
		        if (size > 1){
		        	options.each(function() {
	                    if ($(this).prop("selected")) {
	                        if ( text != "" ) { text += ", "; }
	                        /* NOTE use of .html versus .text, which can screw up ampersands for IE */
	                        var optCss = $(this).attr('style');
	                        var tempspan = $('<span/>');
	                        tempspan.html( $(this).html() );
	                        if ( optCss == null ) {
	                                text += tempspan.html();
	                        } else {
	                                tempspan.attr('style',optCss);
	                                text += $("<span/>").append(tempspan).html();
	                        }
	                    }
	                });
		        }
		        switch(countOfSelected) {
		           case 0: return emptyText;
		           case 1: return selectedOptions.text();
		           case options.size(): return emptyText;
		           default: return text;
		        }
			}
		} );
	}
	//if filter parameters are already set then we need to set them as selected in the dropdowns
	var previousParams=$("#filterParams").html();

	function refreshGenesPhenoFrag(dropdownsList) {
		var rootUrl=window.location.href;
		console.log("genesPhenFrag method (refreshGenesPhenoFrag) called with "+dropdownsList.length);
		var newUrl=rootUrl.replace("phenotypes", "geneVariantsWithPhenotypeTable");
		var output ='?';
		selectedFilters = "";
		for (var it = 0; it < dropdownsList.length; it++){
			console.log(dropdownsList[it].array);
			if(dropdownsList[it].array.length == 1){//if only one entry for this parameter then don't use brackets and or
				output += '&fq=' + dropdownsList[it].name + ':"' + dropdownsList[it].array+'"';
				selectedFilters += '+AND+' + dropdownsList[it].name + ':"' + dropdownsList[it].array+'"';
			}
			if(dropdownsList[it].array.length > 1)	{
				output += '&fq='+dropdownsList[it].name+':(\"' + dropdownsList[it].array.join("\"OR\"") + '\")';
				selectedFilters += '+AND+'+dropdownsList[it].name+':(\"' + dropdownsList[it].array.join("\"OR\"") + '\")';
			}
		}
		newUrl+=output;
		refreshPhenoTable(newUrl);
		return false;
	}
});

<|endoftext|>


<commit_before>!function($, wysi) {
	"use scrict"

	var templates = {
		"font-styles": "
" +
							"" +
								" Normal text " +
							"" +
						    "
" +
						      	"
Normal text
" +
					            "
Heading 1
" +
					            "
Heading 2
" +
						    "
" +
						"
",
		"emphasis":     "
" +
							"
" +
							    "Bold" +
							    "Italic" +
							"
" +
						"
",
		"lists": 		"
" +
							"
" +
						    	"" +
							    "" +
							"
" +
						"
"
	};

	var defaultOptions = {
		"font-styles": true,
		"emphasis": true,
		"lists": true
	};

	Wysihtml5 = function(el, options) {
		this.el = el;
		this.toolbar = this.createToolbar(el, options || defaultOptions);
		this.editor =  new wysi.Editor(this.el.attr('id'), {
    		toolbar: this.toolbar.attr('id')
  		});

		this.editor.on('aftercommand:composer', function() {
			console.log(arguments);
		});
	};

	Wysihtml5.prototype = {
		constructor: Wysihtml5,

		createToolbar: function(el, options) {
			var toolbar = $("

").button();

			for(var key in defaultOptions) {
				var value;

				if(options[key] != undefined) {
					if(options[key] == true) {
						value = true;
					}
				} else {
					value = defaultOptions[key];
				}

				if(value == true) {
					toolbar.append(templates[key]);
				}
			}

			var self = this;

			toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
				var el = $(e.srcElement);
				self.toolbar.find('.current-font').text(el.html())
			});

			this.el.before(toolbar);

			return toolbar;
		}
	};

	$.fn.wysihtml5 = function (options) {
		return this.each(function () {
			var $this = $(this);
	      	$this.data('wysihtml5', new Wysihtml5($this, options));
	    })
  	};

  	$.fn.wysihtml5.Constructor = Wysihtml5;

}(window.jQuery, window.wysihtml5);<commit_msg>removing logging<commit_after>!function($, wysi) {
	"use scrict"

	var templates = {
		"font-styles": "
" +
							"" +
								" Normal text " +
							"" +
						    "
" +
						      	"
Normal text
" +
					            "
Heading 1
" +
					            "
Heading 2
" +
						    "
" +
						"
",
		"emphasis":     "
" +
							"
" +
							    "Bold" +
							    "Italic" +
							"
" +
						"
",
		"lists": 		"
" +
							"
" +
						    	"" +
							    "" +
							"
" +
						"
"
	};

	var defaultOptions = {
		"font-styles": true,
		"emphasis": true,
		"lists": true
	};

	Wysihtml5 = function(el, options) {
		this.el = el;
		this.toolbar = this.createToolbar(el, options || defaultOptions);
		this.editor =  new wysi.Editor(this.el.attr('id'), {
    		toolbar: this.toolbar.attr('id')
  		});
	};

	Wysihtml5.prototype = {
		constructor: Wysihtml5,

		createToolbar: function(el, options) {
			var toolbar = $("

").button();

			for(var key in defaultOptions) {
				var value;

				if(options[key] != undefined) {
					if(options[key] == true) {
						value = true;
					}
				} else {
					value = defaultOptions[key];
				}

				if(value == true) {
					toolbar.append(templates[key]);
				}
			}

			var self = this;

			toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
				var el = $(e.srcElement);
				self.toolbar.find('.current-font').text(el.html())
			});

			this.el.before(toolbar);

			return toolbar;
		}
	};

	$.fn.wysihtml5 = function (options) {
		return this.each(function () {
			var $this = $(this);
	      	$this.data('wysihtml5', new Wysihtml5($this, options));
	    })
  	};

  	$.fn.wysihtml5.Constructor = Wysihtml5;

}(window.jQuery, window.wysihtml5);<|endoftext|>


<commit_before>const proxyquire = require('proxyquire')
const mockfs = require('./mockfs')
const { MarkdownRenderer } = proxyquire('../src/renderer', { 'fs': mockfs })

describe ('Renderer', () => {
  var renderer
  var callback

  beforeEach(() => {
    callback = jasmine.createSpy('callback')
    renderer = new MarkdownRenderer(callback)
  })

  it('converts a markdown file', (done) => {
    const path = 'header.md'
    mockfs.writeFileSync(path, '# Hello')

    renderer.loadFile(path).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hello

'
      )
      done()
    }, done.fail)
  })

  it('converts a markdown string', (done) => {
    var markdown = '## Hi'

    renderer.load(markdown).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hi

'
      )
      done()
    }, done.fail)
  })

  it('preserves new lines in code', (done) => {
    const path = 'code.md'
    mockfs.writeFileSync(path, '```\nvar x = 3\nvar y = null\n```')

    renderer.loadFile(path).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

var x = 3\nvar y = null\n

'
      )
      done()
    }, done.fail)
  })

  it('watches loaded file', (done) => {
    const path = 'file.md'
    mockfs.writeFileSync(path, 'original')

    renderer.loadFile(path)
      .then(() => {
        mockfs.writeFileSync('file.md', 'changed')
      })
      .then(() => {
        expect(callback.calls.mostRecent().args[0]).toEqual(
          '

changed

'
        )
        done()
      }, done.fail)
  })
})
<commit_msg>Add test for ignoring old file<commit_after>const proxyquire = require('proxyquire')
const mockfs = require('./mockfs')
const { MarkdownRenderer } = proxyquire('../src/renderer', { 'fs': mockfs })

describe ('Renderer', () => {
  var renderer
  var callback

  beforeEach(() => {
    callback = jasmine.createSpy('callback')
    renderer = new MarkdownRenderer(callback)
  })

  it('converts a markdown file', (done) => {
    const path = 'header.md'
    mockfs.writeFileSync(path, '# Hello')

    renderer.loadFile(path).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hello

'
      )
      done()
    }, done.fail)
  })

  it('converts a markdown string', (done) => {
    var markdown = '## Hi'

    renderer.load(markdown).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hi

'
      )
      done()
    }, done.fail)
  })

  it('preserves new lines in code', (done) => {
    const path = 'code.md'
    mockfs.writeFileSync(path, '```\nvar x = 3\nvar y = null\n```')

    renderer.loadFile(path).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

var x = 3\nvar y = null\n

'
      )
      done()
    }, done.fail)
  })

  it('watches loaded file', (done) => {
    const path = 'file.md'
    mockfs.writeFileSync(path, 'original')

    renderer.loadFile(path)
      .then(() => {
        mockfs.writeFileSync('file.md', 'changed')
      })
      .then(() => {
        expect(callback.calls.mostRecent().args[0]).toEqual(
          '

changed

'
        )
        done()
      }, done.fail)
  })

  it('ignores old file when new is loaded', (done) => {
    const path_old = 'file_old.md'
    const path_new = 'file_new.md'
    mockfs.writeFileSync(path_old, 'original')
    mockfs.writeFileSync(path_new, 'anything')

    renderer.loadFile(path_old)
      .then(renderer.loadFile(path_new))
      .then(() => {
        mockfs.writeFileSync(path_old, 'changed')
      })
      .then(() => {
        expect(callback).not.toHaveBeenCalledWith(
          '

changed

'
        )
        done()
      }, done.fail)
  })
})
<|endoftext|>


<commit_before>/* global jasmine, expect, spyOn */

var lib = require('../app/scripts/template');
var cytoscape;
var cytograph;

describe('template', function() {
  var codemirror, codemirrorDoc, graphArea;

  beforeEach(function() {
    codemirror = jasmine.createSpyObj('codemirror', ['getDoc']);
    codemirrorDoc = jasmine.createSpyObj('codemirrorDoc', ['setValue', 'getValue']);
    codemirror.getDoc.and.returnValue(codemirrorDoc);
    graphArea = jasmine.createSpyObj('graphArea', ['css']);
    cytoscape = jasmine.createSpy('cytoscape');
    cytograph = jasmine.createSpyObj('graph', ['boxSelectionEnabled']);
    cytoscape.and.returnValue(cytograph);
  });

  describe('template.setData', function() {
    var test, template;

    beforeEach(function() {
      test = jasmine.createSpyObj('test', ['success', 'failure']);
      template = lib.template({
        'editor': codemirror,
        'graphContainer': graphArea,
        'cytolib': cytoscape
      });
    });

    it('will call the success function on success', function() {
      template.setData('{}', test.success, test.failure);
      expect(test.success).toHaveBeenCalled();
      expect(cytoscape).toHaveBeenCalled();
    });
    it('will call the failure function on failure', function() {
      template.setData('{', test.success, test.failure);
      expect(test.failure).toHaveBeenCalled();
    });
    it('will call the failure function if there is no data', function() {
      template.setData(null, test.success, test.failure);
      expect(test.failure).toHaveBeenCalled();
    });
  });

  describe('template.text', function() {
    var codemirror, codemirrorDoc, template;
    beforeEach(function() {
      codemirror = jasmine.createSpyObj('codemirror', ['getDoc']);
      codemirrorDoc = jasmine.createSpyObj('codemirrorDoc', ['getValue']);
      codemirror.getDoc.and.returnValue(codemirrorDoc);
      template = lib.template({
        'editor': codemirror,
        'graphContainer': graphArea,
        'cytolib': cytoscape
      });
      spyOn(template, 'json');
    });
    it('will retrieve directly from codemirror without indent', function() {
      template.text();
      expect(codemirrorDoc.getValue).toHaveBeenCalled();
    });
    it('will call json() if there is an indent', function() {
      codemirrorDoc.getValue.and.returnValue('{}');
      spyOn(JSON, 'stringify');
      spyOn(JSON, 'parse');
      template.text(2);
      expect(JSON.parse).toHaveBeenCalled();
      expect(JSON.stringify).toHaveBeenCalled();
    });
  });

  describe('template.fromURL', function() {
    var input, template, jsonproxy, callbacks, fromURL, url;

    beforeEach(function() {
      url = 'http://www.example.com';
      jsonproxy = jasmine.createSpy('jsonp');
      template = lib.template({
        'editor': codemirror,
        'graphContainer': graphArea,
        'cytolib': cytoscape,
        'jsonproxy': jsonproxy
      });
      callbacks = jasmine.createSpyObj('callbacks', ['s', 'f']);
      fromURL = function(){
        return template.fromURL(url, callbacks.s, callbacks.f);
      };
    });

    it('calls success function on success', function() {
      jsonproxy.and.callFake(function(options) {
        options.success('{}');
      });
      fromURL();
      expect(callbacks.s).toHaveBeenCalled();
    });

    it('calls failure function on failure', function() {
      jsonproxy.and.callFake(function(options) {
        options.error({status: 404, statusText: 'TEST - Not Found'},'error');
      });
      fromURL();
      expect(callbacks.f).toHaveBeenCalledWith(url, 'Unable to load: status 404 TEST - Not Found', jasmine.any(Object), jasmine.any(String));
    });

    it('calls failure function on parsererror', function() {
      jsonproxy.and.callFake(function(options) {
        options.error({},'parsererror');
      });
      fromURL();
      expect(callbacks.f).toHaveBeenCalledWith(url, 'Unable to parse the result as valid JSON', jasmine.any(Object), jasmine.any(String));
      expect(codemirrorDoc.setValue).toHaveBeenCalled();
    });
  });
});<commit_msg>Test fromFile with valid and invalid data<commit_after>/* global jasmine, expect, spyOn */

var lib = require('../app/scripts/template');
var cytoscape;
var cytograph;

describe('template', function() {
  var codemirror, codemirrorDoc, graphArea;

  beforeEach(function() {
    codemirror = jasmine.createSpyObj('codemirror', ['getDoc']);
    codemirrorDoc = jasmine.createSpyObj('codemirrorDoc', ['setValue', 'getValue']);
    codemirror.getDoc.and.returnValue(codemirrorDoc);
    graphArea = jasmine.createSpyObj('graphArea', ['css']);
    cytoscape = jasmine.createSpy('cytoscape');
    cytograph = jasmine.createSpyObj('graph', ['boxSelectionEnabled']);
    cytoscape.and.returnValue(cytograph);
  });

  describe('template.setData', function() {
    var test, template;

    beforeEach(function() {
      test = jasmine.createSpyObj('test', ['success', 'failure']);
      template = lib.template({
        'editor': codemirror,
        'graphContainer': graphArea,
        'cytolib': cytoscape
      });
    });

    it('will call the success function on success', function() {
      template.setData('{}', test.success, test.failure);
      expect(test.success).toHaveBeenCalled();
      expect(cytoscape).toHaveBeenCalled();
    });
    it('will call the failure function on failure', function() {
      template.setData('{', test.success, test.failure);
      expect(test.failure).toHaveBeenCalled();
    });
    it('will call the failure function if there is no data', function() {
      template.setData(null, test.success, test.failure);
      expect(test.failure).toHaveBeenCalled();
    });
  });

  describe('template.text', function() {
    var codemirror, codemirrorDoc, template;
    beforeEach(function() {
      codemirror = jasmine.createSpyObj('codemirror', ['getDoc']);
      codemirrorDoc = jasmine.createSpyObj('codemirrorDoc', ['getValue']);
      codemirror.getDoc.and.returnValue(codemirrorDoc);
      template = lib.template({
        'editor': codemirror,
        'graphContainer': graphArea,
        'cytolib': cytoscape
      });
      spyOn(template, 'json');
    });
    it('will retrieve directly from codemirror without indent', function() {
      template.text();
      expect(codemirrorDoc.getValue).toHaveBeenCalled();
    });
    it('will call json() if there is an indent', function() {
      codemirrorDoc.getValue.and.returnValue('{}');
      spyOn(JSON, 'stringify');
      spyOn(JSON, 'parse');
      template.text(2);
      expect(JSON.parse).toHaveBeenCalled();
      expect(JSON.stringify).toHaveBeenCalled();
    });
  });

  describe('template.fromFile', function() {
    var template, callbacks;
    var mockReader = { readAsText: function(){} };
    FileReader = function() {
      return mockReader;
    };

    beforeEach(function() {
      template = lib.template({
        'editor': codemirror,
        'graphContainer': graphArea,
        'cytolib': cytoscape,
        'jsonproxy': jasmine.createSpy('jsonp')
      });
      callbacks = jasmine.createSpyObj('callbacks', ['s', 'f']);
    });

    it('calls success function on success', function() {

      spyOn(mockReader,'readAsText').and.callFake(function(){
      mockReader.result = {};
      mockReader.onload();
      });
      template.fromFile({name: 'test.txt'}, callbacks.s, callbacks.f);
      expect(callbacks.s).toHaveBeenCalled();
    });

    it('calls failure function on failure', function() {

      spyOn(mockReader,'readAsText').and.callFake(function(){
      mockReader.result = 'O_o';
      mockReader.onload();
      });
      template.fromFile({name: 'test.txt'}, callbacks.s, callbacks.f);
      expect(callbacks.f).toHaveBeenCalled();
    });
  });

  describe('template.fromURL', function() {
    var input, template, jsonproxy, callbacks, fromURL, url;

    beforeEach(function() {
      url = 'http://www.example.com';
      jsonproxy = jasmine.createSpy('jsonp');
      template = lib.template({
        'editor': codemirror,
        'graphContainer': graphArea,
        'cytolib': cytoscape,
        'jsonproxy': jsonproxy
      });
      callbacks = jasmine.createSpyObj('callbacks', ['s', 'f']);
      fromURL = function(){
        return template.fromURL(url, callbacks.s, callbacks.f);
      };
    });

    it('calls success function on success', function() {
      jsonproxy.and.callFake(function(options) {
        options.success('{}');
      });
      fromURL();
      expect(callbacks.s).toHaveBeenCalled();
    });

    it('calls failure function on failure', function() {
      jsonproxy.and.callFake(function(options) {
        options.error({status: 404, statusText: 'TEST - Not Found'},'error');
      });
      fromURL();
      expect(callbacks.f).toHaveBeenCalledWith(url, 'Unable to load: status 404 TEST - Not Found', jasmine.any(Object), jasmine.any(String));
    });

    it('calls failure function on parsererror', function() {
      jsonproxy.and.callFake(function(options) {
        options.error({},'parsererror');
      });
      fromURL();
      expect(callbacks.f).toHaveBeenCalledWith(url, 'Unable to parse the result as valid JSON', jasmine.any(Object), jasmine.any(String));
      expect(codemirrorDoc.setValue).toHaveBeenCalled();
    });
  });
});<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import {
  View,
  Text as NativeText,
  StyleSheet,
  TouchableHighlight,
  Platform,
} from 'react-native';
import colors from '../config/colors';
import Text from '../text/Text';
import normalize from '../helpers/normalizeText';

const ButtonGroup = props => {
  const {
    component,
    buttons,
    onPress,
    selectedIndex,
    containerStyle,
    innerBorderStyle,
    lastBorderStyle,
    buttonStyle,
    textStyle,
    selectedTextStyle,
    selectedBackgroundColor,
    underlayColor,
    activeOpacity,
    onHideUnderlay,
    onShowUnderlay,
    setOpacityTo,
    containerBorderRadius,
    ...attributes
  } = props;

  const Component = component || TouchableHighlight;
  return (
    <View
      style={[styles.container, containerStyle && containerStyle]}
      {...attributes}
    >
      {buttons.map((button, i) => {
        return (
          <Component
            activeOpacity={activeOpacity}
            setOpacityTo={setOpacityTo}
            onHideUnderlay={onHideUnderlay}
            onShowUnderlay={onShowUnderlay}
            underlayColor={underlayColor || '#ffffff'}
            onPress={onPress ? () => onPress(i) : () => {}}
            key={i}
            style={[
              styles.button,
              i < buttons.length - 1 && {
                borderRightWidth: (innerBorderStyle &&
                  innerBorderStyle.width) ||
                  1,
                borderRightColor: (innerBorderStyle &&
                  innerBorderStyle.color) ||
                  colors.grey4,
              },
              i === buttons.length - 1 && {
                ...lastBorderStyle,
                borderTopRightRadius: containerBorderRadius || 0,
                borderBottomRightRadius: containerBorderRadius || 0,
              },
              i === 0 && {
                borderTopLeftRadius: containerBorderRadius || 0,
                borderBottomLeftRadius: containerBorderRadius || 0,
              },
              selectedIndex === i && {
                backgroundColor: selectedBackgroundColor || 'white',
              },
            ]}
          >
            <View style={[styles.textContainer, buttonStyle && buttonStyle]}>
              {button.element
                ? <button.element />
                : <Text
                    style={[
                      styles.buttonText,
                      textStyle && textStyle,
                      selectedIndex === i && { color: colors.grey1 },
                      selectedIndex === i && selectedTextStyle,
                    ]}
                  >
                    {button}
                  </Text>}
            </View>
          </Component>
        );
      })}
    </View>
  );
};

const styles = StyleSheet.create({
  button: {
    flex: 1,
  },
  textContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  container: {
    marginLeft: 10,
    marginRight: 10,
    marginBottom: 5,
    marginTop: 5,
    borderColor: '#e3e3e3',
    borderWidth: 1,
    flexDirection: 'row',
    borderRadius: 3,
    overflow: 'hidden',
    backgroundColor: '#f5f5f5',
    height: 40,
  },
  buttonText: {
    fontSize: normalize(13),
    color: colors.grey2,
    ...Platform.select({
      ios: {
        fontWeight: '500',
      },
    }),
  },
});

ButtonGroup.propTypes = {
  button: PropTypes.object,
  component: PropTypes.any,
  onPress: PropTypes.func,
  buttons: PropTypes.array,
  containerStyle: View.propTypes.style,
  textStyle: NativeText.propTypes.style,
  selectedTextStyle: NativeText.propTypes.style,
  underlayColor: PropTypes.string,
  selectedIndex: PropTypes.number,
  activeOpacity: PropTypes.number,
  onHideUnderlay: PropTypes.func,
  onShowUnderlay: PropTypes.func,
  setOpacityTo: PropTypes.any,
  innerBorderStyle: PropTypes.shape({
    color: PropTypes.string,
    width: PropTypes.number,
  }),
  lastBorderStyle: PropTypes.oneOfType([
    View.propTypes.style,
    NativeText.propTypes.style,
  ]),
  buttonStyle: View.propTypes.style,
  selectedBackgroundColor: PropTypes.string,
  containerBorderRadius: PropTypes.number,
};

export default ButtonGroup;
<commit_msg>Workaround the border bug on ButtonGroup<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import {
  View,
  Text as NativeText,
  StyleSheet,
  TouchableHighlight,
  Platform,
} from 'react-native';
import colors from '../config/colors';
import Text from '../text/Text';
import normalize from '../helpers/normalizeText';

const ButtonGroup = props => {
  const {
    component,
    buttons,
    onPress,
    selectedIndex,
    containerStyle,
    innerBorderStyle,
    lastBorderStyle,
    buttonStyle,
    textStyle,
    selectedTextStyle,
    selectedBackgroundColor,
    underlayColor,
    activeOpacity,
    onHideUnderlay,
    onShowUnderlay,
    setOpacityTo,
    containerBorderRadius,
    ...attributes
  } = props;

  const Component = component || TouchableHighlight;
  return (
    <View
      style={[styles.container, containerStyle && containerStyle]}
      {...attributes}
    >
      {buttons.map((button, i) => {
        return (
          <Component
            activeOpacity={activeOpacity}
            setOpacityTo={setOpacityTo}
            onHideUnderlay={onHideUnderlay}
            onShowUnderlay={onShowUnderlay}
            underlayColor={underlayColor || '#ffffff'}
            onPress={onPress ? () => onPress(i) : () => {}}
            key={i}
            style={[
              styles.button,
              // FIXME: This is a workaround to the borderColor and borderRadius bug
              // react-native ref: https://github.com/facebook/react-native/issues/8236
              i < buttons.length - 1 && {
                borderRightWidth: i === 0
                  ? 0
                  : (innerBorderStyle && innerBorderStyle.width) || 1,
                borderRightColor:
                  (innerBorderStyle && innerBorderStyle.color) || colors.grey4,
              },
              i === 1 && {
                borderLeftWidth:
                  (innerBorderStyle && innerBorderStyle.width) || 1,
                borderLeftColor:
                  (innerBorderStyle && innerBorderStyle.color) || colors.grey4,
              },
              i === buttons.length - 1 && {
                ...lastBorderStyle,
                borderTopRightRadius: containerBorderRadius || 3,
                borderBottomRightRadius: containerBorderRadius || 3,
              },
              i === 0 && {
                borderTopLeftRadius: containerBorderRadius || 3,
                borderBottomLeftRadius: containerBorderRadius || 3,
              },
              selectedIndex === i && {
                backgroundColor: selectedBackgroundColor || 'white',
              },
            ]}
          >
            <View style={[styles.textContainer, buttonStyle && buttonStyle]}>
              {button.element
                ? <button.element />
                : <Text
                    style={[
                      styles.buttonText,
                      textStyle && textStyle,
                      selectedIndex === i && { color: colors.grey1 },
                      selectedIndex === i && selectedTextStyle,
                    ]}
                  >
                    {button}
                  </Text>}
            </View>
          </Component>
        );
      })}
    </View>
  );
};

const styles = StyleSheet.create({
  button: {
    flex: 1,
  },
  textContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  container: {
    marginLeft: 10,
    marginRight: 10,
    marginBottom: 5,
    marginTop: 5,
    borderColor: '#e3e3e3',
    borderWidth: 1,
    flexDirection: 'row',
    borderRadius: 3,
    overflow: 'hidden',
    backgroundColor: '#f5f5f5',
    height: 40,
  },
  buttonText: {
    fontSize: normalize(13),
    color: colors.grey2,
    ...Platform.select({
      ios: {
        fontWeight: '500',
      },
    }),
  },
});

ButtonGroup.propTypes = {
  button: PropTypes.object,
  component: PropTypes.any,
  onPress: PropTypes.func,
  buttons: PropTypes.array,
  containerStyle: View.propTypes.style,
  textStyle: NativeText.propTypes.style,
  selectedTextStyle: NativeText.propTypes.style,
  underlayColor: PropTypes.string,
  selectedIndex: PropTypes.number,
  activeOpacity: PropTypes.number,
  onHideUnderlay: PropTypes.func,
  onShowUnderlay: PropTypes.func,
  setOpacityTo: PropTypes.any,
  innerBorderStyle: PropTypes.shape({
    color: PropTypes.string,
    width: PropTypes.number,
  }),
  lastBorderStyle: PropTypes.oneOfType([
    View.propTypes.style,
    NativeText.propTypes.style,
  ]),
  buttonStyle: View.propTypes.style,
  selectedBackgroundColor: PropTypes.string,
  containerBorderRadius: PropTypes.number,
};

export default ButtonGroup;
<|endoftext|>


<commit_before>/* global Tooltip */

import $ from 'jquery';
import Ember from 'ember';
import { computed } from '@ember/object';
import { assign } from '@ember/polyfills';
import { run } from '@ember/runloop';
import { warn } from '@ember/debug';
import Component from '@ember/component';
import RSVP from 'rsvp';
import config from 'ember-get-config';
import layout from '../templates/components/ember-tooltip-base';

const { environment } = config;

const ANIMATION_CLASS = 'ember-tooltip-show';
const POPPER_DEFAULT_MODIFIERS = {
  flip: {
    enabled: true,
  },
  preventOverflow: {
    escapeWithReference: true
  }
};

function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.substring(1);
}

function getOppositeSide(placement) {
  if (!placement) {
    return null;
  }

  const [side] = placement.split('-');
  let oppositeSide;

  switch (side) {
    case 'top':
      oppositeSide = 'bottom';
      break;
    case 'right':
      oppositeSide = 'left';
      break;
    case 'bottom':
      oppositeSide = 'top';
      break;
    case 'left':
      oppositeSide = 'right';
      break;
  }

  return oppositeSide;
}

function cleanNumber(stringOrNumber) {
  let cleanNumber;

  if (stringOrNumber && typeof stringOrNumber === 'string') {
    cleanNumber = parseInt(stringOrNumber, 10);

    /* Remove invalid parseInt results */

    if (isNaN(cleanNumber) || !isFinite(cleanNumber)) {
      cleanNumber = 0;
    }
  } else {
    cleanNumber = stringOrNumber;
  }

  return cleanNumber;
}

export default Component.extend({
  classNames: ['ember-tooltip-base'],
  delay: 0,
  delayOnChange: true,
  duration: 0,
  effect: 'slide', // Options: fade, slide, none // TODO - make slide work
  event: 'hover', // Options: hover, click, focus, none
  tooltipClassName: 'ember-tooltip', /* Custom classes */
  isShown: false,
  text: null,
  side: 'top',
  spacing: 10,
  targetId: null,
  layout,
  updateFor: null,
  popperOptions: null,

  /* Actions */

  onDestroy: null,
  onHide: null,
  onRender: null,
  onShow: null,

  hideOn: computed('event', function() {
    const event  = this.get('event');

    let hideOn;

    switch (event) {
      case 'hover':
        hideOn = 'mouseleave';
        break;
      case 'focus':
        hideOn = 'blur';
        break;
      case 'ready':
        hideOn = null;
        break;
      default:
        hideOn = event;
        break;
    }

    return hideOn;
  }),

  showOn: computed('event', function() {
    const event  = this.get('event');

    let showOn;

    switch (event) {
      case 'hover':
        showOn = 'mouseenter';
        break;
      default:
        showOn = event;
        break;
    }

    return showOn;
  }),

  target: computed('targetId', function() {
    const targetId = this.get('targetId');

    let target;

    if (targetId) {
      target = document.getElementById(targetId);

      if (!target) {
        warn('No target found for targetId ', targetId, {
          id: 'ember-tooltips.no-element-with-targetId'
        });
      }
    } else {
      target = this.element.parentNode;
    }

    return target;
  }),

  /* An ID used to identify this tooltip from other tooltips */

  wormholeId: computed('elementId', function() {
    return `${this.get('elementId')}-wormhole`;
  }),

  _awaitingTooltipElementRendered: true,
  _tooltipEvents: null,
  _tooltip: null,
  _spacingRequestId: null,

  _animationDuration: computed(function() {
    const inTestingMode = environment === 'test' || Ember.testing;

    return inTestingMode ? 0 : 200;
  }),

  init() {
    this._super(...arguments);
    this.set('_tooltipEvents', []);
  },

  didInsertElement() {
    this._super(...arguments);

    this.createTooltip().then(() => {

    });
  },

  didUpdateAttrs() {
    this._super(...arguments);

    if (this.get('isShown')) {
      this.show();

      /* If updateFor exists, update the tooltip incase the changed Attr affected the tooltip content's height or width */

      if (this.get('updateFor') && this.get('_tooltip').popperInstance) {
        const popper = this.get('_tooltip').popperInstance;

        if (popper) {
          run.scheduleOnce('afterRender', popper, popper.update);
        }
      }
    } else {
      this.hide();
    }
  },

  willDestroyElement() {
    this._super(...arguments);

    this.hide();

    const _tooltipEvents = this.get('_tooltipEvents');

    /* Remove event listeners used to show and hide the tooltip */

    _tooltipEvents.forEach((tooltipEvent) => {
      const {
        callback,
        target,
        eventName,
      } = tooltipEvent;

      target.removeEventListener(eventName, callback);
    });

    this.get('_tooltip').dispose();

    this._dispatchAction('onDestroy', this);
  },

  addTargetEventListeners() {
    this.addTooltipTargetEventListeners();
  },

  addTooltipBaseEventListeners() {

  },

  addTooltipTargetEventListeners() {

    /* Setup event handling to hide and show the tooltip */

    const event = this.get('event');

    /* Setup event handling to hide and show the tooltip */

    if (event === 'none') {
      return;
    }

    const hideOn = this.get('hideOn');
    const showOn = this.get('showOn');

    /* If show and hide are the same (e.g. click) toggle
    the visibility */

    if (showOn === hideOn) {
      this._addEventListener(showOn, () => {
        this.toggle();
      });
    } else {

      /* Else, add the show and hide events individually */

      if (showOn !== 'none') {
        this._addEventListener(showOn, () => {
          this.show();
        });
      }

      if (hideOn !== 'none') {
        this._addEventListener(hideOn, () => {
          this.hide();
        });
      }
    }

    /* Hide and show the tooltip on focus and escape
    for accessibility */

    if (event !== 'focus') {

      /* If the event is click, we don't want the
      click to also trigger focusin */

      if (event !== 'click') {
        this._addEventListener('focusin', () => {
          this.show();
        });
      }

      this._addEventListener('focusout', () => {
        this.hide();
      });
    }

    this._addEventListener('keydown', (keyEvent) => {

      keyEvent.stopImmediatePropagation(); /* So this callback only fires once per keydown */

      if (keyEvent.which === 27) {
        this.hide();

        keyEvent.preventDefault();

        return false;
      }
    }, document);
  },

  createTooltip() {
    return new RSVP.Promise((resolve, reject) => {

      try {
        run(() => {
          const rootElement = document.querySelector(config.APP.rootElement);
          const target = this.get('target');
          const tooltipClassName = this.get('tooltipClassName');

          const targetTitle = target.title;

          target.removeAttribute('title');

          const tooltip = new Tooltip(target, {
            container: rootElement || false,
            html: true,
            placement: this.get('side'),
            title: '',
            trigger: 'manual',
            template: `

`,

            popperOptions: {
              modifiers: mergeModifiers(
                POPPER_DEFAULT_MODIFIERS,
                this.get('popperOptions.modifiers')
              ),

              onCreate: (tooltipData) => {
                run(() => {

                  this._dispatchAction('onRender', this);

                  this.set('_awaitingTooltipElementRendered', false);

                  /* The tooltip element must exist in order to add event listeners to it */

                  this.addTooltipBaseEventListeners();

                  /* Once the wormhole has done it's work, we need the tooltip to be positioned again */

                  run.scheduleOnce('afterRender', () => {
                    const popperInstance = tooltipData.instance;

                    popperInstance.update();
                  });

                  target.setAttribute('title', targetTitle);

                  resolve(tooltipData);
                });
              },

              onUpdate: () => {
                this.setSpacing();
              },
            },
          });

          /* Add a class to the tooltip target */

          target.classList.add('ember-tooltip-target');

          this.addTargetEventListeners();
          this.set('_tooltip', tooltip);

          /* If user passes isShown=true, show the tooltip as soon as it's created */

          if (this.get('isShown')) {
            this.show();
          }
        });
      } catch(error) {
        reject(error);
      }
    });
  },

  setSpacing() {
    if (this._spacingRequestId || !this.get('isShown') || this.get('isDestroying')) {
      return;
    }

    this._spacingRequestId = requestAnimationFrame(() => {
      this._spacingRequestId = null;

      if (!this.get('isShown') || this.get('isDestroying')) {
        return;
      }

      const { popperInstance } = this.get('_tooltip');
      const { popper } = popperInstance;
      const side = popper.getAttribute('x-placement');
      const marginSide = getOppositeSide(side);
      const { style } = popper;

      style.marginTop = 0;
      style.marginRight = 0;
      style.marginBottom = 0;
      style.marginLeft = 0;

      popper.style[`margin${capitalize(marginSide)}`] = `${this.get('spacing')}px`;
    });
  },

  hide() {

    if (this.get('isDestroying')) {
      return;
    }

    /* If the tooltip is about to be showed by
    a delay, stop is being shown. */

    run.cancel(this.get('_showTimer'));

    this._hideTooltip();
  },

  show() {

    if (this.get('isDestroying')) {
      return;
    }

    const delay = this.get('delay');
    const duration = this.get('duration');

    run.cancel(this.get('_showTimer'));

    if (duration) {
      this.setHideTimer(duration);
    }

    if (delay) {
      this.setShowTimer(delay);
    } else {
      this._showTooltip();
    }
  },

  setHideTimer(duration) {
    duration = cleanNumber(duration);

    run.cancel(this.get('_hideTimer'));

    if (duration) {

      /* Hide tooltip after specified duration */

      const hideTimer = run.later(this, this.hide, duration);

      /* Save timer ID for canceling should an event
      hide the tooltip before the duration */

      this.set('_hideTimer', hideTimer);
    }
  },

  setShowTimer(delay) {
    delay = cleanNumber(delay);

    if (!this.get('delayOnChange')) {

      /* If the `delayOnChange` property is set to false, we
      don't want to delay opening this tooltip/popover if there is
      already a tooltip/popover shown in the DOM. Check that here
      and adjust the delay as needed. */

      let shownTooltipsOrPopovers = $(`.${ANIMATION_CLASS}`);

      if (shownTooltipsOrPopovers.length) {
        delay = 0;
      }
    }

    const _showTimer = run.later(this, () => {
      this._showTooltip();
    }, delay);

    this.set('_showTimer', _showTimer);
  },

  _hideTooltip() {
    const _tooltip = this.get('_tooltip');

    if (!_tooltip || this.get('isDestroying')) {
      return;
    }

    if (_tooltip.popperInstance) {
      _tooltip.popperInstance.popper.classList.remove(ANIMATION_CLASS);
    }

    run.later(() => {

      if (this.get('isDestroying')) {
        return;
      }

      cancelAnimationFrame(this._spacingRequestId);
      _tooltip.hide();

      this.set('_isHiding', false);
      this.set('isShown', false);
      this._dispatchAction('onHide', this);
    }, this.get('_animationDuration'));
  },

  _showTooltip() {

    if (this.get('isDestroying')) {
      return;
    }

    const _tooltip = this.get('_tooltip');

    _tooltip.show();

    this.set('isShown', true);

    run(() => {
      if (this.get('isDestroying')) {
        return;
      }

      _tooltip.popperInstance.popper.classList.add(ANIMATION_CLASS);

      this._dispatchAction('onShow', this);
    });
  },

  toggle() {

    /* We don't use toggleProperty because we centralize
    logic for showing and hiding in the show() and hide()
    methods. */

    if (this.get('isShown')) {
      this.hide();
    } else {
      this.show();
    }
  },

  _addEventListener(eventName, callback, element) {
    const target = element || this.get('target');

    /* Remember event listeners so they can removed on teardown */

    const boundCallback = run.bind(this, callback);

    this.get('_tooltipEvents').push({
      callback: boundCallback,
      target,
      eventName,
    });

    /* Add the event listeners */

    target.addEventListener(eventName, boundCallback);
  },

  _dispatchAction(actionName, ...args) {
    const action = this.get(actionName);

    if (!this.isDestroying && !this.isDestroyed && action) {
      action(...args);
    }
  }
});

function mergeModifiers(defaults, overrides = {}) {
  const defaultKeys = Object.keys(defaults);
  const overriddenKeys = Object.keys(overrides);
  const keys = [].concat(defaultKeys, overriddenKeys).reduce((acc, key) => {
    if (!acc.includes(key)) acc.push(key);
    return acc;
  }, []);
  const modifiers = assign({}, defaults);

  for (const key of keys) {
    if (defaultKeys.includes(key) && overriddenKeys.includes(key)) {
      modifiers[key] = assign(
        {},
        defaults[key],
        overrides[key]
      );
    } else if (overriddenKeys.includes(key)) {
      modifiers[key] = overrides[key];
    }
  }

  return modifiers;
}
<commit_msg>Avoid scheduling additional runloop timers in willDestroyElement<commit_after>/* global Tooltip */

import $ from 'jquery';
import Ember from 'ember';
import { computed } from '@ember/object';
import { assign } from '@ember/polyfills';
import { run } from '@ember/runloop';
import { warn } from '@ember/debug';
import Component from '@ember/component';
import RSVP from 'rsvp';
import config from 'ember-get-config';
import layout from '../templates/components/ember-tooltip-base';

const { environment } = config;

const ANIMATION_CLASS = 'ember-tooltip-show';
const POPPER_DEFAULT_MODIFIERS = {
  flip: {
    enabled: true,
  },
  preventOverflow: {
    escapeWithReference: true
  }
};

function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.substring(1);
}

function getOppositeSide(placement) {
  if (!placement) {
    return null;
  }

  const [side] = placement.split('-');
  let oppositeSide;

  switch (side) {
    case 'top':
      oppositeSide = 'bottom';
      break;
    case 'right':
      oppositeSide = 'left';
      break;
    case 'bottom':
      oppositeSide = 'top';
      break;
    case 'left':
      oppositeSide = 'right';
      break;
  }

  return oppositeSide;
}

function cleanNumber(stringOrNumber) {
  let cleanNumber;

  if (stringOrNumber && typeof stringOrNumber === 'string') {
    cleanNumber = parseInt(stringOrNumber, 10);

    /* Remove invalid parseInt results */

    if (isNaN(cleanNumber) || !isFinite(cleanNumber)) {
      cleanNumber = 0;
    }
  } else {
    cleanNumber = stringOrNumber;
  }

  return cleanNumber;
}

export default Component.extend({
  classNames: ['ember-tooltip-base'],
  delay: 0,
  delayOnChange: true,
  duration: 0,
  effect: 'slide', // Options: fade, slide, none // TODO - make slide work
  event: 'hover', // Options: hover, click, focus, none
  tooltipClassName: 'ember-tooltip', /* Custom classes */
  isShown: false,
  text: null,
  side: 'top',
  spacing: 10,
  targetId: null,
  layout,
  updateFor: null,
  popperOptions: null,

  /* Actions */

  onDestroy: null,
  onHide: null,
  onRender: null,
  onShow: null,

  hideOn: computed('event', function() {
    const event  = this.get('event');

    let hideOn;

    switch (event) {
      case 'hover':
        hideOn = 'mouseleave';
        break;
      case 'focus':
        hideOn = 'blur';
        break;
      case 'ready':
        hideOn = null;
        break;
      default:
        hideOn = event;
        break;
    }

    return hideOn;
  }),

  showOn: computed('event', function() {
    const event  = this.get('event');

    let showOn;

    switch (event) {
      case 'hover':
        showOn = 'mouseenter';
        break;
      default:
        showOn = event;
        break;
    }

    return showOn;
  }),

  target: computed('targetId', function() {
    const targetId = this.get('targetId');

    let target;

    if (targetId) {
      target = document.getElementById(targetId);

      if (!target) {
        warn('No target found for targetId ', targetId, {
          id: 'ember-tooltips.no-element-with-targetId'
        });
      }
    } else {
      target = this.element.parentNode;
    }

    return target;
  }),

  /* An ID used to identify this tooltip from other tooltips */

  wormholeId: computed('elementId', function() {
    return `${this.get('elementId')}-wormhole`;
  }),

  _awaitingTooltipElementRendered: true,
  _tooltipEvents: null,
  _tooltip: null,
  _spacingRequestId: null,

  _animationDuration: computed(function() {
    const inTestingMode = environment === 'test' || Ember.testing;

    return inTestingMode ? 0 : 200;
  }),

  init() {
    this._super(...arguments);
    this.set('_tooltipEvents', []);
  },

  didInsertElement() {
    this._super(...arguments);

    this.createTooltip().then(() => {

    });
  },

  didUpdateAttrs() {
    this._super(...arguments);

    if (this.get('isShown')) {
      this.show();

      /* If updateFor exists, update the tooltip incase the changed Attr affected the tooltip content's height or width */

      if (this.get('updateFor') && this.get('_tooltip').popperInstance) {
        const popper = this.get('_tooltip').popperInstance;

        if (popper) {
          run.scheduleOnce('afterRender', popper, popper.update);
        }
      }
    } else {
      this.hide();
    }
  },

  willDestroyElement() {
    this._super(...arguments);

    const _tooltipEvents = this.get('_tooltipEvents');

    /* Remove event listeners used to show and hide the tooltip */

    _tooltipEvents.forEach((tooltipEvent) => {
      const {
        callback,
        target,
        eventName,
      } = tooltipEvent;

      target.removeEventListener(eventName, callback);
    });

    this._cleanupTimers();

    this.get('_tooltip').dispose();

    this._dispatchAction('onDestroy', this);
  },

  addTargetEventListeners() {
    this.addTooltipTargetEventListeners();
  },

  addTooltipBaseEventListeners() {

  },

  addTooltipTargetEventListeners() {

    /* Setup event handling to hide and show the tooltip */

    const event = this.get('event');

    /* Setup event handling to hide and show the tooltip */

    if (event === 'none') {
      return;
    }

    const hideOn = this.get('hideOn');
    const showOn = this.get('showOn');

    /* If show and hide are the same (e.g. click) toggle
    the visibility */

    if (showOn === hideOn) {
      this._addEventListener(showOn, () => {
        this.toggle();
      });
    } else {

      /* Else, add the show and hide events individually */

      if (showOn !== 'none') {
        this._addEventListener(showOn, () => {
          this.show();
        });
      }

      if (hideOn !== 'none') {
        this._addEventListener(hideOn, () => {
          this.hide();
        });
      }
    }

    /* Hide and show the tooltip on focus and escape
    for accessibility */

    if (event !== 'focus') {

      /* If the event is click, we don't want the
      click to also trigger focusin */

      if (event !== 'click') {
        this._addEventListener('focusin', () => {
          this.show();
        });
      }

      this._addEventListener('focusout', () => {
        this.hide();
      });
    }

    this._addEventListener('keydown', (keyEvent) => {

      keyEvent.stopImmediatePropagation(); /* So this callback only fires once per keydown */

      if (keyEvent.which === 27) {
        this.hide();

        keyEvent.preventDefault();

        return false;
      }
    }, document);
  },

  createTooltip() {
    return new RSVP.Promise((resolve, reject) => {

      try {
        run(() => {
          const rootElement = document.querySelector(config.APP.rootElement);
          const target = this.get('target');
          const tooltipClassName = this.get('tooltipClassName');

          const targetTitle = target.title;

          target.removeAttribute('title');

          const tooltip = new Tooltip(target, {
            container: rootElement || false,
            html: true,
            placement: this.get('side'),
            title: '',
            trigger: 'manual',
            template: `

`,

            popperOptions: {
              modifiers: mergeModifiers(
                POPPER_DEFAULT_MODIFIERS,
                this.get('popperOptions.modifiers')
              ),

              onCreate: (tooltipData) => {
                run(() => {

                  this._dispatchAction('onRender', this);

                  this.set('_awaitingTooltipElementRendered', false);

                  /* The tooltip element must exist in order to add event listeners to it */

                  this.addTooltipBaseEventListeners();

                  /* Once the wormhole has done it's work, we need the tooltip to be positioned again */

                  run.scheduleOnce('afterRender', () => {
                    const popperInstance = tooltipData.instance;

                    popperInstance.update();
                  });

                  target.setAttribute('title', targetTitle);

                  resolve(tooltipData);
                });
              },

              onUpdate: () => {
                this.setSpacing();
              },
            },
          });

          /* Add a class to the tooltip target */

          target.classList.add('ember-tooltip-target');

          this.addTargetEventListeners();
          this.set('_tooltip', tooltip);

          /* If user passes isShown=true, show the tooltip as soon as it's created */

          if (this.get('isShown')) {
            this.show();
          }
        });
      } catch(error) {
        reject(error);
      }
    });
  },

  setSpacing() {
    if (this._spacingRequestId || !this.get('isShown') || this.get('isDestroying')) {
      return;
    }

    this._spacingRequestId = requestAnimationFrame(() => {
      this._spacingRequestId = null;

      if (!this.get('isShown') || this.get('isDestroying')) {
        return;
      }

      const { popperInstance } = this.get('_tooltip');
      const { popper } = popperInstance;
      const side = popper.getAttribute('x-placement');
      const marginSide = getOppositeSide(side);
      const { style } = popper;

      style.marginTop = 0;
      style.marginRight = 0;
      style.marginBottom = 0;
      style.marginLeft = 0;

      popper.style[`margin${capitalize(marginSide)}`] = `${this.get('spacing')}px`;
    });
  },

  hide() {

    if (this.get('isDestroying')) {
      return;
    }

    /* If the tooltip is about to be showed by
    a delay, stop is being shown. */

    run.cancel(this.get('_showTimer'));

    this._hideTooltip();
  },

  show() {

    if (this.get('isDestroying')) {
      return;
    }

    const delay = this.get('delay');
    const duration = this.get('duration');

    run.cancel(this.get('_showTimer'));

    if (duration) {
      this.setHideTimer(duration);
    }

    if (delay) {
      this.setShowTimer(delay);
    } else {
      this._showTooltip();
    }
  },

  setHideTimer(duration) {
    duration = cleanNumber(duration);

    run.cancel(this.get('_hideTimer'));

    if (duration) {

      /* Hide tooltip after specified duration */

      const hideTimer = run.later(this, this.hide, duration);

      /* Save timer ID for canceling should an event
      hide the tooltip before the duration */

      this.set('_hideTimer', hideTimer);
    }
  },

  setShowTimer(delay) {
    delay = cleanNumber(delay);

    if (!this.get('delayOnChange')) {

      /* If the `delayOnChange` property is set to false, we
      don't want to delay opening this tooltip/popover if there is
      already a tooltip/popover shown in the DOM. Check that here
      and adjust the delay as needed. */

      let shownTooltipsOrPopovers = $(`.${ANIMATION_CLASS}`);

      if (shownTooltipsOrPopovers.length) {
        delay = 0;
      }
    }

    const _showTimer = run.later(this, () => {
      this._showTooltip();
    }, delay);

    this.set('_showTimer', _showTimer);
  },

  _hideTooltip() {
    const _tooltip = this.get('_tooltip');

    if (!_tooltip || this.get('isDestroying')) {
      return;
    }

    if (_tooltip.popperInstance) {
      _tooltip.popperInstance.popper.classList.remove(ANIMATION_CLASS);
    }

    run.later(() => {

      if (this.get('isDestroying')) {
        return;
      }

      cancelAnimationFrame(this._spacingRequestId);
      _tooltip.hide();

      this.set('_isHiding', false);
      this.set('isShown', false);
      this._dispatchAction('onHide', this);
    }, this.get('_animationDuration'));
  },

  _showTooltip() {

    if (this.get('isDestroying')) {
      return;
    }

    const _tooltip = this.get('_tooltip');

    _tooltip.show();

    this.set('isShown', true);

    run(() => {
      if (this.get('isDestroying')) {
        return;
      }

      _tooltip.popperInstance.popper.classList.add(ANIMATION_CLASS);

      this._dispatchAction('onShow', this);
    });
  },

  toggle() {

    /* We don't use toggleProperty because we centralize
    logic for showing and hiding in the show() and hide()
    methods. */

    if (this.get('isShown')) {
      this.hide();
    } else {
      this.show();
    }
  },

  _addEventListener(eventName, callback, element) {
    const target = element || this.get('target');

    /* Remember event listeners so they can removed on teardown */

    const boundCallback = run.bind(this, callback);

    this.get('_tooltipEvents').push({
      callback: boundCallback,
      target,
      eventName,
    });

    /* Add the event listeners */

    target.addEventListener(eventName, boundCallback);
  },

  _dispatchAction(actionName, ...args) {
    const action = this.get(actionName);

    if (!this.isDestroying && !this.isDestroyed && action) {
      action(...args);
    }
  },

  _cleanupTimers() {
    run.cancel(this.get('_showTimer'));
    cancelAnimationFrame(this._spacingRequestId);
  }
});

function mergeModifiers(defaults, overrides = {}) {
  const defaultKeys = Object.keys(defaults);
  const overriddenKeys = Object.keys(overrides);
  const keys = [].concat(defaultKeys, overriddenKeys).reduce((acc, key) => {
    if (!acc.includes(key)) acc.push(key);
    return acc;
  }, []);
  const modifiers = assign({}, defaults);

  for (const key of keys) {
    if (defaultKeys.includes(key) && overriddenKeys.includes(key)) {
      modifiers[key] = assign(
        {},
        defaults[key],
        overrides[key]
      );
    } else if (overriddenKeys.includes(key)) {
      modifiers[key] = overrides[key];
    }
  }

  return modifiers;
}
<|endoftext|>


<commit_before>import Ember from 'ember'
const {
  Component,
  computed
} = Ember
const {
  readOnly
} = computed
import PropTypeMixin, {PropTypes} from 'ember-prop-types'
import layout from '../templates/components/frost-radio-button'

export default Component.extend(PropTypeMixin, {
  // == Properties  ============================================================

  attributeBindings: [
    'tabindex'
  ],
  classNames: [
    'frost-radio-button'
  ],
  classNameBindings: [
    'checked',
    'disabled',
    'required'
  ],
  layout,

  propTypes: {
    disabled: PropTypes.bool,
    hook: PropTypes.string,
    required: PropTypes.bool
  },

  getDefaultProps () {
    return {
      disabled: false,
      required: false
    }
  },

  // == Computed properties  ===================================================

  groupId: readOnly('parentView.id'),
  groupValue: readOnly('parentView.value'),
  onChange: readOnly('parentView.onChange'),

  checked: computed('groupValue', 'value', function () {
    return this.get('groupValue') === this.get('value')
  }),

  tabindex: Ember.computed('disabled', function () {
    return this.get('disabled') ? -1 : 0
  }),

  // == Functions ===============================================================

  _createEvent (_event, _target) {
    let event = Ember.$.Event(null, _event)
    let target = Ember.$.clone(_target)
    target.id = this.get('groupId')
    event.target = target
    return event
  },

  // == Events ===============================================================

  init () {
    this._super(...arguments)
    this._setupAssertions()
  },

  _setupAssertions () {
    Ember.assert(
      `${this.toString()} must be initialized in the yield block of 'frost-radio-group'`,
      /frost-radio-group/.test(this.parentView.toString()))
    Ember.assert(
      `${this.toString()} must be initialized with a 'value' property`,
      this.get('value')
    )
  },

  keyPress (e) {
    if (e.keyCode === 13 || e.keyCode === 32) {
      if (this.get('disabled') || this.get('groupValue') === this.get('value')) {
        return
      }
      let change = this.get('onChange')
      if (change && typeof change === 'function') {
        change(this._createEvent(e, Ember.$(e.target).find('input')[0]))
      }
    }
  },

  change (event) {
    const onChange = this.get('onChange')
    if (onChange && typeof onChange === 'function') {
      onChange(this._createEvent(event, event.target))
    }
  }
})
<commit_msg>cleanup on frost-radio-button<commit_after>import computed from 'ember-computed-decorators'
import Ember from 'ember'
const {
  $,
  assert,
  Component,
  computed: {
    readOnly
  },
  get,
  typeOf
} = Ember
import PropTypeMixin, {PropTypes} from 'ember-prop-types'
import layout from '../templates/components/frost-radio-button'

export default Component.extend(PropTypeMixin, {
  // == Properties  ============================================================

  attributeBindings: [
    'tabindex'
  ],
  classNames: [
    'frost-radio-button'
  ],
  classNameBindings: [
    'checked',
    'disabled',
    'required'
  ],
  layout,

  propTypes: {
    disabled: PropTypes.bool,
    hook: PropTypes.string,
    required: PropTypes.bool
  },

  getDefaultProps () {
    return {
      disabled: false,
      required: false
    }
  },

  // == Computed properties  ===================================================

  groupId: readOnly('parentView.id'),
  groupValue: readOnly('parentView.value'),
  onChange: readOnly('parentView.onChange'),

  @computed('groupValue', 'value')
  /**
   * Determine state of checked
   * @param {String} groupValue - which radio button in the group is set
   * @param {String} value - is this radio button selected
   * @returns {Boolean} whether this radio button is checked
   */
  checked (groupValue, value) {
    return groupValue === value
  },

  @computed('disabled')
  /**
   * Determine tabindex setting
   * @param {String} disabled - component disabled state
   * @returns {String} whether this radio button is checked
   */
  tabindex (disabled) {
    return disabled ? '-1' : '0'
  },

  // == Functions ===============================================================

  _createEvent (_event, _target) {
    let event = $.Event(null, _event)
    let target = $.clone(_target)
    target.id = get(this, 'groupId')
    event.target = target
    return event
  },

  // == Events ===============================================================

  init () {
    this._super(...arguments)
    this._setupAssertions()
  },

  _setupAssertions () {
    assert(
      `${this.toString()} must be initialized in the yield block of 'frost-radio-group'`,
      /frost-radio-group/.test(this.parentView.toString()))
    assert(
      `${this.toString()} must be initialized with a 'value' property`,
      get(this, 'value')
    )
  },

  keyPress (e) {
    if (e.keyCode === 13 || e.keyCode === 32) {
      if (get(this, 'disabled') || get(this, 'groupValue') === get(this, 'value')) {
        return
      }
      let change = get(this, 'onChange')
      if (change && typeOf(change) === 'function') {
        change(this._createEvent(e, $(e.target).find('input')[0]))
      }
    }
  },

  change (event) {
    const onChange = get(this, 'onChange')
    if (onChange && typeOf(onChange) === 'function') {
      onChange(this._createEvent(event, event.target))
    }
  }
})
<|endoftext|>


<commit_before>import Component from '@ember/component';
import { computed } from "@ember/object"
import layout from './template';

export default Component.extend({
  layout,
  classNames: ['x-toggle-component'],

  disabled: false,
  name: 'default',
  onLabel: 'On',
  offLabel: 'Off',
  value: false,

  // private
  toggled: computed.readOnly('value'),

  forId: computed(function() {
    return this.get('elementId') + '-x-toggle';
  }),

  actions: {
    sendToggle(value) {
      let onToggle = this.get('onToggle');

      if (value !== this.get('value') && typeof onToggle === 'function') {
        onToggle(value);

        // The click on input/label will toggle the input unconditionally.
        // Input state has to be updated manually to prevent it going out of
        // sync in case the action didn't update value.
        const checkbox = this.element.querySelector('.x-toggle');
        const newValue = this.get('value');

        if (checkbox.checked !== newValue) {
          checkbox.checked = newValue;
        }
      }
    }
  }
});
<commit_msg>chore: cleanup toggle component code<commit_after>import Component from '@ember/component';
import { computed } from '@ember/object';
import layout from './template';

export default Component.extend({
  layout,
  classNames: ['x-toggle-component'],

  disabled: false,
  name: 'default',
  onLabel: 'On',
  offLabel: 'Off',
  value: false,

  // private
  toggled: computed.readOnly('value'),

  forId: computed(function() {
    return this.get('elementId') + '-x-toggle';
  }),

  actions: {
    sendToggle(value) {
      let onToggle = this.get('onToggle');

      if (value !== this.get('value') && typeof onToggle === 'function') {
        onToggle(value);

        // The click on input/label will toggle the input unconditionally.
        // Input state has to be updated manually to prevent it going out of
        // sync in case the action didn't update value.
        const checkbox = this.element.querySelector('.x-toggle');
        const newValue = this.get('value');

        if (checkbox.checked !== newValue) {
          checkbox.checked = newValue;
        }
      }
    }
  }
});
<|endoftext|>


<commit_before>import getOwner from 'ember-getowner-polyfill';

export function initialize(appInstance) {
  const segment = appInstance.lookup('service:segment');

  appInstance.lookup('router:main').on('didTransition', function(){
    segment.trackPageView();
  });
}

export default {
  name: 'segment',
  initialize: initialize
};
<commit_msg>fix(segment): remove unused polyfill<commit_after>export function initialize(appInstance) {
  const segment = appInstance.lookup('service:segment');

  appInstance.lookup('router:main').on('didTransition', function(){
    segment.trackPageView();
  });
}

export default {
  name: 'segment',
  initialize: initialize
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import OptionsSupport from './options-support';
import getControl from 'furnace-forms/utils/get-control';
export default Ember.Mixin.create({
	optionControl : null,

	showOptionControl : false,

	actions : {
		onSelect : function(index) {

		},
	},

	select: function(index) {
		this.set('value',this.get('_options')[index-1].value);

		this.get("_options").invoke('set','selected',false);
		var option=this.get("_options").findBy('index',index);
		if(option) {
			option.set('selected',true);
		}
		this._valueObserver();
		this.send('onSelect',index);
	},

	_controlForOption: null,

	_selectedIndexObserver:Ember.observer('selectedIndex', function() {
		var option = this.getOption();
		if(option && option.value!==this.get('value')) {
			this.set('value',option.value);
		}
		else {
			if(this.get('selectedIndex')===null) {
				this.set('value',null);
			}
		}
		var ret=null;

		option = this.get("_options").findBy('index',this.get('selectedIndex'));
		if(option && !option.get('selected')) {
			this.get("_options").invoke('set','selected',false);
			option.set('selected',true);
		}
		if(this._controlForOption!==option) {
			this._controlForOption=option;
			if(this._controlsLoaded) {
				var control=this.get("controls").findBy('index',this.get('selectedIndex'));
				if(control) {
					ret = control.get('optionControl');
				}
			} else if(option && option.control) {
				var options=option.control._meta.options;
				options['_model']=this.get('value');
				options._path=this._panel._path;
				if(this.get('optionControl')) {
					this.get('optionControl').destroy();
				}
				ret = getControl.call(this,'value',option.control._meta.options._controlType,options);
			}

			if(ret) {
				this.set('showOptionControl',true);
			}
			else {
				this.set('showOptionControl',false);
			}

			if(ret!==this.get('optionControl')) {
				this.set('optionControl',ret);
			}
		}
	}),

	init: function() {
		Ember.warn('No options support for single-select',OptionsSupport.detect(this));
		this._super();
		this._updateSelected();
		this._selectedIndexObserver();
	},

	getOption: function(index) {
		if(index===undefined) {
			index=this.get('selectedIndex')-1;
		}
		return this.get('_options')[index];
	},

	selectedOption : Ember.computed({
		get : function(key,value) {
			if(value) {
				this.set('value',value.get('value'));
			}
			return this.getOption();
		}
	}).readOnly(),

	selectedIndex : 0,

	_valueObserver: Ember.observer('value',function() {
		this._super();
		this._updateSelected();
	}),

	_updateSelected : Ember.observer('_options.@each.selected',function() {
		var option=this.getOption();
		if(!option || this.get('value')!==option.value) {
			if(this.get('_options')) {
				option = this.get('_options').findBy('value',this.get('value'));
				this.set('selectedIndex',this.get('_options').indexOf(option)+1);
			}

		} else if(option && !option.get('selected')){
			this._selectedIndexObserver();
		}
	}),

	_optionsObserver: Ember.observer('_options.@each',function() {
//		this._valueObserver();
		this._selectedIndexObserver();
	}),

	setValid: function(valid) {
		Ember.run.once(this,function() {
			if(this.optionControl && valid && this.get('optionControl.isValid')===false) {
				var isValid=this.get('isValid');
				this.setFlag('isValid',false);
				this.set('_valid',valid);
				if(isValid===true) {
					this.notifyChange();
				}
			}
			else if(valid!==this._valid || valid!==this.isValid) {
				this.setFlag('isValid',valid);
				this.set('_valid',valid);
				this.notifyChange();
			}
		});
	},

	_apply : function() {
		if(this.optionControl) {
			// Notify the optionControl the model has changed
			// The optionControl is tied to the option, don't reset
			//this.optionControl._reset(true);
		}
		this._super();
	},

	_reset : function(modelChanged) {
		if(this.optionControl) {
			this.optionControl._reset(modelChanged);
		}
		this._super(modelChanged);
	},

	_controlValidObserver : Ember.observer('optionControl,optionControl.isValid',function() {
		Ember.run.once(this,function() {
			this.setValid(this._valid);
		});
	}),

});<commit_msg>Fixed single-select option remains selected on null value<commit_after>import Ember from 'ember';
import OptionsSupport from './options-support';
import getControl from 'furnace-forms/utils/get-control';
export default Ember.Mixin.create({
	optionControl : null,

	showOptionControl : false,

	actions : {
		onSelect : function(index) {

		},
	},

	select: function(index) {
		this.set('value',this.get('_options')[index-1].value);

		this.get("_options").invoke('set','selected',false);
		var option=this.get("_options").findBy('index',index);
		if(option) {
			option.set('selected',true);
		}
		this._valueObserver();
		this.send('onSelect',index);
	},

	_controlForOption: null,

	_selectedIndexObserver:Ember.observer('selectedIndex', function() {
		var option = this.getOption();
		if(option && option.value!==this.get('value')) {
			this.set('value',option.value);
		}
		else {
			if(this.get('selectedIndex')===null) {
				this.set('value',null);
			}
		}
		var ret=null;

		option = this.get("_options").findBy('index',this.get('selectedIndex'));
		if(option && !option.get('selected')) {
			this.get("_options").invoke('set','selected',false);
			option.set('selected',true);
		} else if(!option) {
			this.get("_options").invoke('set','selected',false);
		}

		if(this._controlForOption!==option) {
			this._controlForOption=option;
			if(this._controlsLoaded) {
				var control=this.get("controls").findBy('index',this.get('selectedIndex'));
				if(control) {
					ret = control.get('optionControl');
				}
			} else if(option && option.control) {
				var options=option.control._meta.options;
				options['_model']=this.get('value');
				options._path=this._panel._path;
				if(this.get('optionControl')) {
					this.get('optionControl').destroy();
				}
				ret = getControl.call(this,'value',option.control._meta.options._controlType,options);
			}

			if(ret) {
				this.set('showOptionControl',true);
			}
			else {
				this.set('showOptionControl',false);
			}

			if(ret!==this.get('optionControl')) {
				this.set('optionControl',ret);
			}
		}
	}),

	init: function() {
		Ember.warn('No options support for single-select',OptionsSupport.detect(this));
		this._super();
		this._updateSelected();
		this._selectedIndexObserver();
	},

	getOption: function(index) {
		if(index===undefined) {
			index=this.get('selectedIndex')-1;
		}
		return this.get('_options')[index];
	},

	selectedOption : Ember.computed({
		get : function(key,value) {
			if(value) {
				this.set('value',value.get('value'));
			}
			return this.getOption();
		}
	}).readOnly(),

	selectedIndex : 0,

	_valueObserver: Ember.observer('value',function() {
		this._super();
		this._updateSelected();
	}),

	_updateSelected : Ember.observer('_options.@each.selected',function() {
		var option=this.getOption();
		if(!option || this.get('value')!==option.value) {
			if(this.get('_options')) {
				option = this.get('_options').findBy('value',this.get('value'));
				this.set('selectedIndex',this.get('_options').indexOf(option)+1);
			}

		} else if(option && !option.get('selected')){
			this._selectedIndexObserver();
		}
	}),

	_optionsObserver: Ember.observer('_options.@each',function() {
//		this._valueObserver();
		this._selectedIndexObserver();
	}),

	setValid: function(valid) {
		Ember.run.once(this,function() {
			if(this.optionControl && valid && this.get('optionControl.isValid')===false) {
				var isValid=this.get('isValid');
				this.setFlag('isValid',false);
				this.set('_valid',valid);
				if(isValid===true) {
					this.notifyChange();
				}
			}
			else if(valid!==this._valid || valid!==this.isValid) {
				this.setFlag('isValid',valid);
				this.set('_valid',valid);
				this.notifyChange();
			}
		});
	},

	_apply : function() {
		if(this.optionControl) {
			// Notify the optionControl the model has changed
			// The optionControl is tied to the option, don't reset
			//this.optionControl._reset(true);
		}
		this._super();
	},

	_reset : function(modelChanged) {
		if(this.optionControl) {
			this.optionControl._reset(modelChanged);
		}
		this._super(modelChanged);
	},

	_controlValidObserver : Ember.observer('optionControl,optionControl.isValid',function() {
		Ember.run.once(this,function() {
			this.setValid(this._valid);
		});
	}),

});<|endoftext|>