<commit_before>StradivariAutocompleter = function() {
  var autoCompleteField = $("input.autocomplete")
  var bloodhounds = prepareTheBloodhounds($("[data-autocomplete]"));
  var datasets = prepareTheDatasets();

  initializeTheBloodhounds(true);

  autoCompleteField.typeahead({highlight: true}, datasets);

  attachSelectEvent();

  function prepareTheBloodhounds(labels) {
    var bloodHoundsPack = [];

    $.each(labels, function(_, label) {
      var dataset_name = $(label).attr("for").replace("q_", "");
      var bloodhound = new Bloodhound({
          datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
          queryTokenizer: Bloodhound.tokenizers.whitespace,
          local: filterForm.getOptions(dataset_name)
        })
      bloodhound.dataset_name = dataset_name;
      bloodhound.label = $(label).find(".text").html();
      bloodHoundsPack.push(bloodhound);
    })
    return bloodHoundsPack;
  }

  function prepareTheDatasets() {
    var datasets = [];

    $.each(bloodhounds, function(_, bloodhound){
      datasets.push({
        name: bloodhound.dataset_name,
        displayKey: 'name',
        source: bloodhound.ttAdapter(),
        templates: {
          header: '
' + bloodhound.label + '
'
        }
      })
    })
    return datasets;

  }

  function initializeTheBloodhounds(clear_cache) {
    $.each(bloodhounds, function(_, bloodhound){
      if(clear_cache){
        bloodhound.clearPrefetchCache();
        }
      bloodhound.initialize();
    })
  }

  function attachSelectEvent(){
    autoCompleteField.on("typeahead:selected", function(e, selected, dataset){
    $("#q_" + dataset + "_" + selected.id.toLowerCase()).prop('checked', true);
    this.value = '';
    filterForm.form.submit();
    })
  }

}

$(function() {
  new StradivariAutocompleter();
})

<commit_msg>datasets cache set to true<commit_after>StradivariAutocompleter = function() {
  var autoCompleteField = $("input.autocomplete")
  var bloodhounds = prepareTheBloodhounds($("[data-autocomplete]"));
  var datasets = prepareTheDatasets();

  initializeTheBloodhounds(false);

  autoCompleteField.typeahead({highlight: true}, datasets);

  attachSelectEvent();

  function prepareTheBloodhounds(labels) {
    var bloodHoundsPack = [];

    $.each(labels, function(_, label) {
      var dataset_name = $(label).attr("for").replace("q_", "");
      var bloodhound = new Bloodhound({
          datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
          queryTokenizer: Bloodhound.tokenizers.whitespace,
          local: filterForm.getOptions(dataset_name)
        })
      bloodhound.dataset_name = dataset_name;
      bloodhound.label = $(label).find(".text").html();
      bloodHoundsPack.push(bloodhound);
    })
    return bloodHoundsPack;
  }

  function prepareTheDatasets() {
    var datasets = [];

    $.each(bloodhounds, function(_, bloodhound){
      datasets.push({
        name: bloodhound.dataset_name,
        displayKey: 'name',
        source: bloodhound.ttAdapter(),
        templates: {
          header: '
' + bloodhound.label + '
'
        }
      })
    })
    return datasets;

  }

  function initializeTheBloodhounds(clear_cache) {
    $.each(bloodhounds, function(_, bloodhound){
      if(clear_cache)
        bloodhound.clearPrefetchCache();
      bloodhound.initialize();
    })
  }

  function attachSelectEvent(){
    autoCompleteField.on("typeahead:selected", function(e, selected, dataset){
    $("#q_" + dataset + "_" + selected.id.toLowerCase()).prop('checked', true);
    this.value = '';
    filterForm.form.submit();
    })
  }

}

$(function() {
  new StradivariAutocompleter();
})

<|endoftext|>


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

    $("#quicksearch").searchField();

    $(".resource :input", document.myForm).bind("change", function() { Typus.setConfirmUnload(true); });

    $("a.fancybox").fancybox({
        'titlePosition': 'over',
        'type': 'image',
        'centerOnScroll': true,
        'scrolling': false,
    });

    $(".iframe").fancybox(Typus.fancyboxDefaults);

    $(".iframe_with_page_reload").fancybox(
        $.extend(Typus.fancyboxDefaults, { onClosed: Typus.reloadParent })
    );

    // This method is used by Typus::Controller::Bulk
    $(".action-toggle").click(function() {
        var status = this.checked;
        $('input.action-select').each(function() { this.checked = status; });
        $('.action-toggle').each(function() { this.checked = status; });
    });

});

var Typus = {};

Typus.fancyboxDefaults = {
    'width': 720,
    'height': '90%',
    'autoScale': false,
    'transitionIn': 'none',
    'transitionOut': 'none',
    'type': 'iframe',
    'centerOnScroll': true,
    'scrolling': false,
};

Typus.setConfirmUnload = function(on) {
    window.onbeforeunload = (on) ? Typus.unloadMessage : null;
}

Typus.reloadParent = function() {
    if (Typus.parent_location_reload) parent.location.reload(true);
}

Typus.unloadMessage = function () {
    return "You have entered new data on this page. If you navigate away from this page without first saving your data, the changes will be lost.";
}
<commit_msg>Gardening<commit_after>$(document).ready(function() {

    $("#quicksearch").searchField();

    $(".resource :input", document.myForm).bind("change", function() {
        Typus.setConfirmUnload(true);
    });

    $("a.fancybox").fancybox({
        'titlePosition': 'over',
        'type': 'image',
        'centerOnScroll': true,
        'scrolling': false,
    });

    $(".iframe").fancybox(Typus.fancyboxDefaults);

    $(".iframe_with_page_reload").fancybox(
        $.extend(Typus.fancyboxDefaults, { onClosed: Typus.reloadParent })
    );

    // This method is used by Typus::Controller::Bulk
    $(".action-toggle").click(function() {
        var status = this.checked;
        $('input.action-select').each(function() { this.checked = status; });
        $('.action-toggle').each(function() { this.checked = status; });
    });

});

var Typus = {};

Typus.fancyboxDefaults = {
    'width': 720,
    'height': '90%',
    'autoScale': false,
    'transitionIn': 'none',
    'transitionOut': 'none',
    'type': 'iframe',
    'centerOnScroll': true,
    'scrolling': false,
};

Typus.setConfirmUnload = function(on) {
    window.onbeforeunload = (on) ? Typus.unloadMessage : null;
}

Typus.reloadParent = function() {
    if (Typus.parent_location_reload) parent.location.reload(true);
}

Typus.unloadMessage = function () {
    return "You have entered new data on this page. If you navigate away from this page without first saving your data, the changes will be lost.";
}
<|endoftext|>


<commit_before>// this sets the background color of the master UIView (when there are no windows/tab groups on it)
Titanium.UI.setBackgroundColor('#000');

// create tab group
var tabGroup = Titanium.UI.createTabGroup({id:'tabGroup1'});

//
// create base UI tab and root window
//
var win1 = Titanium.UI.createWindow({className:'win1'});

var tab1 = Titanium.UI.createTab({
	id:'tab1',
    window:win1
});

//
// create controls tab and root window
//
var win2 = Titanium.UI.createWindow({
    url:'main_windows/controls.js',
    titleid:'controls_win_title'
});
var tab2 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_ui.png',
    titleid:'controls_win_title',
    window:win2
});

//
// create phone tab and root window
//
var win3 = Titanium.UI.createWindow({
    url:'main_windows/phone.js',
    titleid:'phone_win_title'
});
var tab3 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_phone.png',
    titleid:'phone_win_title',
    window:win3
});

//
// create platform tab and root window
//
var win4 = Titanium.UI.createWindow({
    url:'main_windows/platform.js',
    titleid:'platform_win_title'
});
var tab4 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_platform.png',
    titleid:'platform_win_title',
// Commented out as per 1773
//	active:true,
    window:win4
});

//
// create mashup tab and root window
//
var win5 = Titanium.UI.createWindow({
    url:'main_windows/mashups.js',
    titleid:'mashups_win_title'
});
var tab5 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_mashup.png',
    titleid:'mashups_win_title',
    window:win5
});

//
//  add tabs
//
tabGroup.addTab(tab1);
tabGroup.addTab(tab2);
tabGroup.addTab(tab3);
tabGroup.addTab(tab4);
tabGroup.addTab(tab5);

tabGroup.addEventListener('open',function()
{
	// set background color back to white after tab group transition
	Titanium.UI.setBackgroundColor('#fff');
});

tabGroup.setActiveTab(1);
// open tab group with a transition animation
tabGroup.open({
	transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
});

// setTimeout(function()
// {
// 	tabGroup.close({
// 		transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
// 	});
//
// },2000);

//
//  TAB GROUP EVENTS
//
var messageWin = Titanium.UI.createWindow({
	height:30,
	width:250,
	bottom:70,
	borderRadius:10,
	touchEnabled:false,

	orientationModes : [
	Titanium.UI.PORTRAIT,
	Titanium.UI.UPSIDE_PORTRAIT,
	Titanium.UI.LANDSCAPE_LEFT,
	Titanium.UI.LANDSCAPE_RIGHT,
	]
});
var messageView = Titanium.UI.createView({
	id:'messageview',
	height:30,
	width:250,
	borderRadius:10,
	backgroundColor:'#000',
	opacity:0.7,
	touchEnabled:false
});

var messageLabel = Titanium.UI.createLabel({
	id:'messagelabel',
	text:'',
	color:'#fff',
	width:250,
	height:'auto',
	font:{
		fontFamily:'Helvetica Neue',
		fontSize:13
	},
	textAlign:'center'
});
messageWin.add(messageView);
messageWin.add(messageLabel);

//
// TAB EVENTS
//

// tab group close event
tabGroup.addEventListener('close', function(e)
{
	messageLabel.text = 'tab group close event';
	messageWin.open();
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);
});

// tab group open event
tabGroup.addEventListener('open', function(e)
{
	messageLabel.text = 'tab group open event';
	messageWin.open();
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);

});

// focus event listener for tracking tab changes
tabGroup.addEventListener('focus', function(e)
{
	messageLabel.text = 'tab changed to ' + e.index + ' old index ' + e.previousIndex;
	messageWin.open();
	setTimeout(function()
	{
		Ti.API.info('tab ' + e.tab.title  + ' prevTab = ' + (e.previousTab ? e.previousTab.title : null));
		messageLabel.text = 'active title ' + e.tab.title + ' old title ' + (e.previousTab ? e.previousTab.title : null);
	},1000);

	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},2000);

});

// blur event listener for tracking tab changes
tabGroup.addEventListener('blur', function(e)
{
	Titanium.API.info('tab blur - new index ' + e.index + ' old index ' + e.previousIndex);
});

//
//   CUSTOM EVENTS
//
Titanium.App.addEventListener('event_one', function(e)
{
	messageLabel.text = 'app.js: event one, array length = ' + e.data.length;
	messageWin.open();
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);
});

Titanium.App.addEventListener('event_two', function(e)
{
	messageLabel.text = 'app.js: event two, name = ' + e.name;
	messageWin.open();
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);

});

//
//  CREATE CUSTOM LOADING INDICATOR
//
var indWin = null;
var actInd = null;
function showIndicator()
{
	if (Ti.Platform.osname != 'android')
	{
		// window container
		indWin = Titanium.UI.createWindow({
			height:150,
			width:150
		});

		// black view
		var indView = Titanium.UI.createView({
			height:150,
			width:150,
			backgroundColor:'#000',
			borderRadius:10,
			opacity:0.8
		});
		indWin.add(indView);
	}

	// loading indicator
	actInd = Titanium.UI.createActivityIndicator({
		style:Titanium.UI.iPhone.ActivityIndicatorStyle.BIG,
		height:30,
		width:30,
	});

	if (Ti.Platform.osname != 'android')
	{
		indWin.add(actInd);

		// message
		var message = Titanium.UI.createLabel({
			text:'Loading',
			color:'#fff',
			width:'auto',
			height:'auto',
			font:{fontSize:20,fontWeight:'bold'},
			bottom:20
		});
		indWin.add(message);
		indWin.open();
	} else {
		actInd.message = "Loading";
	}
	actInd.show();

};

function hideIndicator()
{
	actInd.hide();
	if (Ti.Platform.osname != 'android') {
		indWin.close({opacity:0,duration:500});
	}
};

//
// Add global event handlers to hide/show custom indicator
//
Titanium.App.addEventListener('show_indicator', function(e)
{
	Ti.API.info("IN SHOW INDICATOR");
	showIndicator();
});
Titanium.App.addEventListener('hide_indicator', function(e)
{
	Ti.API.info("IN HIDE INDICATOR");
	hideIndicator();
});

// trap app shutdown event
Titanium.App.addEventListener('close',function(e)
{
	Ti.API.info("The application is being shutdown");
});

// test for loading in a root-level include
Ti.include("welcome.js");

// test out logging to developer console, formatting and localization
Ti.API.info(String.format("%s%s",L("welcome_message","default_not_set"),Titanium.version));
Ti.API.debug(String.format("%s %s",L("user_agent_message","default_not_set"),Titanium.userAgent));

Ti.API.debug(String.format("locale specific date is %s",String.formatDate(new Date))); // default is short
Ti.API.debug(String.format("locale specific date (medium) is %s",String.formatDate(new Date,"medium")));
Ti.API.debug(String.format("locale specific date (long) is %s",String.formatDate(new Date,"long")));
Ti.API.debug(String.format("locale specific time is %s",String.formatTime(new Date)));
Ti.API.debug(String.format("locale specific currency is %s",String.formatCurrency(12.99)));
Ti.API.debug(String.format("locale specific decimal is %s",String.formatDecimal(12.99)));

Ti.API.info("should be en, was = "+Ti.Locale.currentLanguage);
Ti.API.info("welcome_message = "+Ti.Locale.getString("welcome_message"));
Ti.API.info("should be def, was = "+Ti.Locale.getString("welcome_message2","def"));
Ti.API.info("welcome_message = "+L("welcome_message"));
Ti.API.info("should be def, was = "+L("welcome_message2","def"));
Ti.API.info("should be 1, was = "+String.format('%d',1));
Ti.API.info("should be 1.0, was = "+String.format('%1.1f',1));
Ti.API.info("should be hello, was = "+String.format('%s','hello'));

Ti.include("examples/version.js");

if (isiOS4Plus())
{
	// register a background service. this JS will run when the app is backgrounded
	var service = Ti.App.iOS.registerBackgroundService({url:'bg.js'});

	Ti.API.info("registered background service = "+service);

	// listen for a local notification event
	Ti.App.iOS.addEventListener('notification',function(e)
	{
		Ti.API.info("local notification received: "+JSON.stringify(e));
	});

	// fired when an app resumes for suspension
	Ti.App.addEventListener('resume',function(e){
		Ti.API.info("app is resuming from the background");
	});
	Ti.App.addEventListener('resumed',function(e){
		Ti.API.info("app has resumed from the background");
	});

	Ti.App.addEventListener('pause',function(e){
		Ti.API.info("app was paused from the foreground");
	});
}
<commit_msg>Compensating for the test meaning two different things on the platforms.<commit_after>// this sets the background color of the master UIView (when there are no windows/tab groups on it)
Titanium.UI.setBackgroundColor('#000');

// create tab group
var tabGroup = Titanium.UI.createTabGroup({id:'tabGroup1'});

//
// create base UI tab and root window
//
var win1 = Titanium.UI.createWindow({className:'win1'});

var tab1 = Titanium.UI.createTab({
	id:'tab1',
    window:win1
});

//
// create controls tab and root window
//
var win2 = Titanium.UI.createWindow({
    url:'main_windows/controls.js',
    titleid:'controls_win_title'
});
var tab2 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_ui.png',
    titleid:'controls_win_title',
    window:win2
});

//
// create phone tab and root window
//
var win3 = Titanium.UI.createWindow({
    url:'main_windows/phone.js',
    titleid:'phone_win_title'
});
var tab3 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_phone.png',
    titleid:'phone_win_title',
    window:win3
});

//
// create platform tab and root window
//
var win4 = Titanium.UI.createWindow({
    url:'main_windows/platform.js',
    titleid:'platform_win_title'
});
var tab4 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_platform.png',
    titleid:'platform_win_title',
// Commented out as per 1773
//	active:true,
    window:win4
});

//
// create mashup tab and root window
//
var win5 = Titanium.UI.createWindow({
    url:'main_windows/mashups.js',
    titleid:'mashups_win_title'
});
var tab5 = Titanium.UI.createTab({
    icon:'images/tabs/KS_nav_mashup.png',
    titleid:'mashups_win_title',
    window:win5
});

//
//  add tabs
//
tabGroup.addTab(tab1);
tabGroup.addTab(tab2);
tabGroup.addTab(tab3);
tabGroup.addTab(tab4);
tabGroup.addTab(tab5);

tabGroup.addEventListener('open',function()
{
	// set background color back to white after tab group transition
	Titanium.UI.setBackgroundColor('#fff');
});

tabGroup.setActiveTab(1);
// open tab group with a transition animation
tabGroup.open({
	transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
});

// setTimeout(function()
// {
// 	tabGroup.close({
// 		transition:Titanium.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT
// 	});
//
// },2000);

//
//  TAB GROUP EVENTS
//
var messageWin = Titanium.UI.createWindow({
	height:30,
	width:250,
	bottom:70,
	borderRadius:10,
	touchEnabled:false,

	orientationModes : [
	Titanium.UI.PORTRAIT,
	Titanium.UI.UPSIDE_PORTRAIT,
	Titanium.UI.LANDSCAPE_LEFT,
	Titanium.UI.LANDSCAPE_RIGHT,
	]
});
var messageView = Titanium.UI.createView({
	id:'messageview',
	height:30,
	width:250,
	borderRadius:10,
	backgroundColor:'#000',
	opacity:0.7,
	touchEnabled:false
});

var messageLabel = Titanium.UI.createLabel({
	id:'messagelabel',
	text:'',
	color:'#fff',
	width:250,
	height:'auto',
	font:{
		fontFamily:'Helvetica Neue',
		fontSize:13
	},
	textAlign:'center'
});
messageWin.add(messageView);
messageWin.add(messageLabel);

//
// TAB EVENTS
//

// tab group close event
tabGroup.addEventListener('close', function(e)
{
	messageLabel.text = 'tab group close event';
	messageWin.open();
	if (Ti.Platform.osname == "iphone") {
//On iOS, when we're closing the tab group, this is a result
//of the tab group example of 'Close/Animate Tab Group' and
//we want to reopen the tab group so the user can continue with
//using Kitchen Sink. HOWEVER, on Android, this is also triggered
//when the app is being closed via back button, where reopening
//the tab group is not desired. This is purely a quirk of the tests.
		tabGroup.open();
	}
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);
});

// tab group open event
tabGroup.addEventListener('open', function(e)
{
	messageLabel.text = 'tab group open event';
	messageWin.open();
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);

});

// focus event listener for tracking tab changes
tabGroup.addEventListener('focus', function(e)
{
	messageLabel.text = 'tab changed to ' + e.index + ' old index ' + e.previousIndex;
	messageWin.open();
	setTimeout(function()
	{
		Ti.API.info('tab ' + e.tab.title  + ' prevTab = ' + (e.previousTab ? e.previousTab.title : null));
		messageLabel.text = 'active title ' + e.tab.title + ' old title ' + (e.previousTab ? e.previousTab.title : null);
	},1000);

	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},2000);

});

// blur event listener for tracking tab changes
tabGroup.addEventListener('blur', function(e)
{
	Titanium.API.info('tab blur - new index ' + e.index + ' old index ' + e.previousIndex);
});

//
//   CUSTOM EVENTS
//
Titanium.App.addEventListener('event_one', function(e)
{
	messageLabel.text = 'app.js: event one, array length = ' + e.data.length;
	messageWin.open();
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);
});

Titanium.App.addEventListener('event_two', function(e)
{
	messageLabel.text = 'app.js: event two, name = ' + e.name;
	messageWin.open();
	setTimeout(function()
	{
		messageWin.close({opacity:0,duration:500});
	},1000);

});

//
//  CREATE CUSTOM LOADING INDICATOR
//
var indWin = null;
var actInd = null;
function showIndicator()
{
	if (Ti.Platform.osname != 'android')
	{
		// window container
		indWin = Titanium.UI.createWindow({
			height:150,
			width:150
		});

		// black view
		var indView = Titanium.UI.createView({
			height:150,
			width:150,
			backgroundColor:'#000',
			borderRadius:10,
			opacity:0.8
		});
		indWin.add(indView);
	}

	// loading indicator
	actInd = Titanium.UI.createActivityIndicator({
		style:Titanium.UI.iPhone.ActivityIndicatorStyle.BIG,
		height:30,
		width:30,
	});

	if (Ti.Platform.osname != 'android')
	{
		indWin.add(actInd);

		// message
		var message = Titanium.UI.createLabel({
			text:'Loading',
			color:'#fff',
			width:'auto',
			height:'auto',
			font:{fontSize:20,fontWeight:'bold'},
			bottom:20
		});
		indWin.add(message);
		indWin.open();
	} else {
		actInd.message = "Loading";
	}
	actInd.show();

};

function hideIndicator()
{
	actInd.hide();
	if (Ti.Platform.osname != 'android') {
		indWin.close({opacity:0,duration:500});
	}
};

//
// Add global event handlers to hide/show custom indicator
//
Titanium.App.addEventListener('show_indicator', function(e)
{
	Ti.API.info("IN SHOW INDICATOR");
	showIndicator();
});
Titanium.App.addEventListener('hide_indicator', function(e)
{
	Ti.API.info("IN HIDE INDICATOR");
	hideIndicator();
});

// trap app shutdown event
Titanium.App.addEventListener('close',function(e)
{
	Ti.API.info("The application is being shutdown");
});

// test for loading in a root-level include
Ti.include("welcome.js");

// test out logging to developer console, formatting and localization
Ti.API.info(String.format("%s%s",L("welcome_message","default_not_set"),Titanium.version));
Ti.API.debug(String.format("%s %s",L("user_agent_message","default_not_set"),Titanium.userAgent));

Ti.API.debug(String.format("locale specific date is %s",String.formatDate(new Date))); // default is short
Ti.API.debug(String.format("locale specific date (medium) is %s",String.formatDate(new Date,"medium")));
Ti.API.debug(String.format("locale specific date (long) is %s",String.formatDate(new Date,"long")));
Ti.API.debug(String.format("locale specific time is %s",String.formatTime(new Date)));
Ti.API.debug(String.format("locale specific currency is %s",String.formatCurrency(12.99)));
Ti.API.debug(String.format("locale specific decimal is %s",String.formatDecimal(12.99)));

Ti.API.info("should be en, was = "+Ti.Locale.currentLanguage);
Ti.API.info("welcome_message = "+Ti.Locale.getString("welcome_message"));
Ti.API.info("should be def, was = "+Ti.Locale.getString("welcome_message2","def"));
Ti.API.info("welcome_message = "+L("welcome_message"));
Ti.API.info("should be def, was = "+L("welcome_message2","def"));
Ti.API.info("should be 1, was = "+String.format('%d',1));
Ti.API.info("should be 1.0, was = "+String.format('%1.1f',1));
Ti.API.info("should be hello, was = "+String.format('%s','hello'));

Ti.include("examples/version.js");

if (isiOS4Plus())
{
	// register a background service. this JS will run when the app is backgrounded
	var service = Ti.App.iOS.registerBackgroundService({url:'bg.js'});

	Ti.API.info("registered background service = "+service);

	// listen for a local notification event
	Ti.App.iOS.addEventListener('notification',function(e)
	{
		Ti.API.info("local notification received: "+JSON.stringify(e));
	});

	// fired when an app resumes for suspension
	Ti.App.addEventListener('resume',function(e){
		Ti.API.info("app is resuming from the background");
	});
	Ti.App.addEventListener('resumed',function(e){
		Ti.API.info("app has resumed from the background");
	});

	Ti.App.addEventListener('pause',function(e){
		Ti.API.info("app was paused from the foreground");
	});
}
<|endoftext|>


<commit_before>/* eslint-disable no-console */

import express from 'express';
import compression from 'compression';
import { resolve as pathResolve } from 'path';
import appRootDir from 'app-root-dir';
import reactApplication from './react-middleware';
import security from 'universally/src/server/middleware/security';
import clientBundle from 'universally/src/server/middleware/clientBundle';
import serviceWorker from 'universally/src/server/middleware/serviceWorker';
import offlinePage from 'universally/src/server/middleware/offlinePage';
import errorHandlers from 'universally/src/server/middleware/errorHandlers';
import config from 'universally/config';
import session from 'express-session';

// Create our express based server.
const app = express();

// Don't expose any software information to potential hackers.
app.disable('x-powered-by');

// Security middlewares.
app.use(...security);

// Gzip compress the responses.
app.use(compression());

// When in production mode, we will serve our service worker which was generated
// by the offline-plugin webpack plugin. See the webpack plugins section for
// more information.
// Note: the service worker needs to be served from the http root of your
// application for it to work correctly.
if (process.env.NODE_ENV === 'production' && config.serviceWorker.enabled) {
  app.get(`/${config.serviceWorker.fileName}`, serviceWorker);
  app.get(
    `${config.bundles.client.webPath}${config.serviceWorker.offlinePageFileName}`,
    offlinePage,
  );
}

// Configure serving of our client bundle.
app.use(config.bundles.client.webPath, clientBundle);

// Configure static serving of our "public" root http path static files.
// Note: these will be served off the root (i.e. '/') of our application.
app.use(express.static(pathResolve(appRootDir.get(), config.publicAssetsPath)));

app.useSession = (url, getArgs) => {
  app.set('trust proxy', 2);
  if (!getArgs) {
    getArgs = url;
    url = null;
  }
  const args = getArgs(session);
  if (url) app.use(url, session(args));
  else app.use(session(args));
};
app.useStatic = url => express.static(url);

try {
  const server = require('@root/server');
  if (server.default) {
    server.default(app);
  } else {
    server(app);
  }
} catch (err) { console.log('No server.js or server/index.js file found, using default settings'); }
// The React application middleware.
app.get('*', reactApplication);

// Error Handler middlewares.
app.use(...errorHandlers);

// Create an http listener for our express app.
const listener = app.listen(config.port, () =>
  console.log(`Server listening on port ${process.env.PORT || config.port}`),
);

// We export the listener as it will be handy for our development hot reloader,
// or for exposing a general extension layer for application customisations.
export default listener;
<commit_msg>fix graphiql<commit_after>/* eslint-disable no-console */

import express from 'express';
import compression from 'compression';
import { resolve as pathResolve } from 'path';
import appRootDir from 'app-root-dir';
import reactApplication from './react-middleware';
import security from 'universally/src/server/middleware/security';
import clientBundle from 'universally/src/server/middleware/clientBundle';
import serviceWorker from 'universally/src/server/middleware/serviceWorker';
import offlinePage from 'universally/src/server/middleware/offlinePage';
import errorHandlers from 'universally/src/server/middleware/errorHandlers';
import config from 'universally/config';
import session from 'express-session';

const launchAPI = () => {
  app.useSession = (url, getArgs) => {
    app.set('trust proxy', 2);
    if (!getArgs) {
      getArgs = url;
      url = null;
    }
    const args = getArgs(session);
    if (url) app.use(url, session(args));
    else app.use(session(args));
  };
  try {
    const server = require('@root/server');
    if (server.default) {
      server.default(app);
    } else {
      server(app);
    }
  } catch (err) { console.log('No server.js or server/index.js file found, using default settings'); }
}

// Create our express based server.
const app = express();

// Don't expose any software information to potential hackers.
app.disable('x-powered-by');

if (process.env.NODE_ENV !== 'production') launchAPI();
// Security middlewares.
app.use(...security);

// Gzip compress the responses.
app.use(compression());

// When in production mode, we will serve our service worker which was generated
// by the offline-plugin webpack plugin. See the webpack plugins section for
// more information.
// Note: the service worker needs to be served from the http root of your
// application for it to work correctly.
if (process.env.NODE_ENV === 'production' && config.serviceWorker.enabled) {
  app.get(`/${config.serviceWorker.fileName}`, serviceWorker);
  app.get(
    `${config.bundles.client.webPath}${config.serviceWorker.offlinePageFileName}`,
    offlinePage,
  );
}

// Configure serving of our client bundle.
app.use(config.bundles.client.webPath, clientBundle);

// Configure static serving of our "public" root http path static files.
// Note: these will be served off the root (i.e. '/') of our application.
app.use(express.static(pathResolve(appRootDir.get(), config.publicAssetsPath)));

app.useStatic = url => express.static(url);

if (process.env.NODE_ENV === 'production') launchAPI();
// The React application middleware.
app.get('*', reactApplication);

// Error Handler middlewares.
app.use(...errorHandlers);

// Create an http listener for our express app.
const listener = app.listen(config.port, () =>
  console.log(`Server listening on port ${process.env.PORT || config.port}`),
);

// We export the listener as it will be handy for our development hot reloader,
// or for exposing a general extension layer for application customisations.
export default listener;
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'

import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'

import { bid } from './actions'

const mapStateToProps = ({}) => ({
})

class MatchingButton extends Component {
  constructor(props) {
    super(props)
    this.state = {
      value: ''
    }
  }

  handleChange(event) {
    const value = event.target.value
    this.setState({
      value
    })
  }

  handleClick() {
    const { dispatch } = this.props
    const { value } = this.state
    dispatch(bid(parseInt(value, 10)))
  }

  render() {
    const { value } = this.state
    return (

        <TextField
          floatingLabelText='提案金額'
          value={value}
          onChange={this.handleChange.bind(this)}
        />

        <RaisedButton
          onClick={this.handleClick.bind(this)}
        >送信</RaisedButton>

    )
  }
}

export default connect(mapStateToProps)(MatchingButton)
<commit_msg>Fire submit on enter in bidding form<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'

import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'

import { bid } from './actions'

const mapStateToProps = ({}) => ({
})

class MatchingButton extends Component {
  constructor(props) {
    super(props)
    this.state = {
      value: ''
    }
  }

  handleChange(event) {
    const value = event.target.value
    this.setState({
      value
    })
  }

  handleClick() {
    const { dispatch } = this.props
    const { value } = this.state
    dispatch(bid(parseInt(value, 10)))
  }

  handleKeyDown(event) {
    if (event.key === "Enter" || event.keyCode === 13) { // Enter
      this.handleClick()
    }
  }

  render() {
    const { value } = this.state
    return (

        <TextField
          floatingLabelText='提案金額'
          value={value}
          onChange={this.handleChange.bind(this)}
          onKeyDown={this.handleKeyDown.bind(this)}
        />

        <RaisedButton
          onClick={this.handleClick.bind(this)}
        >送信</RaisedButton>

    )
  }
}

export default connect(mapStateToProps)(MatchingButton)
<|endoftext|>


<commit_before>(function() {
  /*
   * @name   MenusCtrl
   * @desc   Controller for the menus tab
   *         Start off the google search by looking up businesses within our current location
   * @test1  test to see if @locations has data
   * @test2  test to see if @locate is equal to our current longitude and latitude
   */
  var MenusCtrl = function($scope, Menu, $timeout, $document, ngGPlacesAPI, BackgroundGeo) {

    var vm = this;

    BackgroundGeo
      .current()
      .then(function(data){
        vm.locate = data;
      })

    vm.searchEventTimeout = void 0;

    vm.searchInputElement = angular.element($document.find('#searchQuery'));

    vm.searchQuery = {
      vicinity: 'San Francisco',
      // reference: "Cliffs house",
      latitude: vm.locate.latitude,
      longitude: vm.locate.longitude
    };

    googleSearch(vm.searchQuery)
      .then(function(data) {
        console.log(data);
        vm.locations = data;
      });

    /////////////////////

    function googleSearch(query){
      return ngGPlacesAPI.nearbySearch(query);
    }

  };

  MenusCtrl
    .$inject = ['$scope', 'Menu', '$timeout', '$document', 'ngGPlacesAPI', 'BackgroundGeo'];
  angular
    .module('app.modules.tabs.menus.controllers', [])
    .controller('MenusCtrl', MenusCtrl);
})();
<commit_msg>Update refactor for backgroundGeo<commit_after>(function() {
  /*
   * @name   MenusCtrl
   * @desc   Controller for the menus tab
   *         Start off the google search by looking up businesses within our current location
   * @test1  test to see if @locations has data
   * @test2  test to see if @locate is equal to our current longitude and latitude
   */
  var MenusCtrl = function($scope, Menu, $timeout, $document, ngGPlacesAPI, BackgroundGeo) {

    var vm = this;

    BackgroundGeo
      .current()
      .then(function(data){

        vm.locate = data;

        vm.searchQuery = {
          vicinity: 'San Francisco',
          // reference: "Cliffs house",
          latitude: vm.locate.latitude,
          longitude: vm.locate.longitude
        };

        googleSearch(vm.searchQuery)
          .then(function(data) {
            console.log(data);
            vm.locations = data;
          });
      })

    vm.searchEventTimeout = void 0;

    vm.searchInputElement = angular.element($document.find('#searchQuery'));

    /////////////////////

    function googleSearch(query){
      return ngGPlacesAPI.nearbySearch(query);
    }

  };

  MenusCtrl
    .$inject = ['$scope', 'Menu', '$timeout', '$document', 'ngGPlacesAPI', 'BackgroundGeo'];
  angular
    .module('app.modules.tabs.menus.controllers', [])
    .controller('MenusCtrl', MenusCtrl);
})();
<|endoftext|>


<commit_before>'use strict';

const https = require('https');
const fs = require('fs');
const async = require('./async');
const cp = require('child_process');
const report = require('./report').report;

const hardwareCode = process.argv[2];

const clientFolder = hardwareCode === 'aaeon' ? '/var/lib/lamassu-machine' : '/opt/lamassu-machine/data'
const lamassuCertFolder = hardwareCode === 'aaeon' ? '/var/lib/lamassu-machine' : '/opt/certs'

const key = fs.readFileSync(`${clientFolder}/client.key`);
const cert = fs.readFileSync(`${clientFolder}/client.pem`);
const ca = fs.readFileSync(`${lamassuCertFolder}/lamassu.pem`);

const TIMEOUT = 600000;

const deviceConfig = `${clientFolder}/device_config.json`
const udevRules = hardwareCode === 'aaeon' ? '/etc/udev/rules.d/99-douro.rules' : ('gaia' ? '/etc/udev/rules.d/99-gaia.rules' : '/etc/udev/rules.d/udevssu.rules')

function command(cmd, cb) {
  cp.exec(cmd, {timeout: TIMEOUT}, function(err) {
    cb(err);
  });
}

function tailFile(file, cb) {
  fs.exists(file, function(exists) {
    if (!exists) {
      report(file + ' does not exist', null, cb);
      return;
    }
    fs.stat(file, function(err, stats) {
      if (err) return report(err, null, cb);
      var opts = {
        start: stats.size - 1024000
      };

      var httpsOptions = {
        host: 'updates.lamassu.is',
        port: 8000,
        path: '/log',
        method: 'POST',
        key: key,
        cert: cert,
        ca: ca,
        ciphers: 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH',
        secureProtocol: 'TLSv1_method',
        rejectUnauthorized: true,
        headers: {
          'Content-Type': 'application/gzip'
        }
      };
      httpsOptions.agent = new https.Agent(httpsOptions);

      var req = https.request(httpsOptions, function(res) {
        res.resume();
        res.on('end', cb);
      });
      fs.createReadStream(file, opts).pipe(req);
    });
  });
}

async.series([
  async.apply(command, `tar -czf /tmp/config.tar.gz ${deviceConfig} ${udevRules}`),
  async.apply(tailFile, '/tmp/config.tar.gz'),
  async.apply(report, null, 'finished.')
], function(err) {
  if (err) throw err;
});
<commit_msg>fix: correct deviceConfig path<commit_after>'use strict';

const https = require('https');
const fs = require('fs');
const async = require('./async');
const cp = require('child_process');
const report = require('./report').report;

const hardwareCode = process.argv[2];

const clientFolder = hardwareCode === 'aaeon' ? '/var/lib/lamassu-machine' : '/opt/lamassu-machine/data'
const lmFolder = hardwareCode === 'aaeon' ? '/opt/apps/machine/lamassu-machine' : '/opt/lamassu-machine'
const lamassuCertFolder = hardwareCode === 'aaeon' ? '/var/lib/lamassu-machine' : '/opt/certs'

const key = fs.readFileSync(`${clientFolder}/client.key`);
const cert = fs.readFileSync(`${clientFolder}/client.pem`);
const ca = fs.readFileSync(`${lamassuCertFolder}/lamassu.pem`);

const TIMEOUT = 600000;

const deviceConfig = `${lmFolder}/device_config.json`
const udevRules = hardwareCode === 'aaeon' ? '/etc/udev/rules.d/99-douro.rules' : ('gaia' ? '/etc/udev/rules.d/99-gaia.rules' : '/etc/udev/rules.d/udevssu.rules')

function command(cmd, cb) {
  cp.exec(cmd, {timeout: TIMEOUT}, function(err) {
    cb(err);
  });
}

function tailFile(file, cb) {
  fs.exists(file, function(exists) {
    if (!exists) {
      report(file + ' does not exist', null, cb);
      return;
    }
    fs.stat(file, function(err, stats) {
      if (err) return report(err, null, cb);
      var opts = {
        start: stats.size - 1024000
      };

      var httpsOptions = {
        host: 'updates.lamassu.is',
        port: 8000,
        path: '/log',
        method: 'POST',
        key: key,
        cert: cert,
        ca: ca,
        ciphers: 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH',
        secureProtocol: 'TLSv1_method',
        rejectUnauthorized: true,
        headers: {
          'Content-Type': 'application/gzip'
        }
      };
      httpsOptions.agent = new https.Agent(httpsOptions);

      var req = https.request(httpsOptions, function(res) {
        res.resume();
        res.on('end', cb);
      });
      fs.createReadStream(file, opts).pipe(req);
    });
  });
}

async.series([
  async.apply(command, `tar -czf /tmp/config.tar.gz ${deviceConfig} ${udevRules}`),
  async.apply(tailFile, '/tmp/config.tar.gz'),
  async.apply(report, null, 'finished.')
], function(err) {
  if (err) throw err;
});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import $ from 'jquery'
import _ from 'underscore'
import React from 'react'
import ApolloClient from "apollo-boost";
import gql from "graphql-tag";
import {ApolloProvider, Query} from "react-apollo";
import StudentContextTray from '../context_cards/StudentContextTray'

const client = new ApolloClient({
  uri: '/api/graphql',
  request: operation => {
    operation.setContext({
      headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'GraphQL-Metrics': true,
        'X-CSRF-Token': $.cookie('_csrf_token')
      }
    })
  }
})

const SCC_QUERY = gql`
  query StudentContextCard($courseId: ID!, $studentId: ID!) {
    course: legacyNode(type: Course, _id: $courseId) {
      ... on Course {
        _id
        name
        permissions {
          become_user: becomeUser
          manage_grades: manageGrades
          send_messages: sendMessages
          view_all_grades: viewAllGrades
          view_analytics: viewAnalytics
        }
        usersConnection(filter: {userIds: [$studentId], enrollmentStates: [invited, creation_pending, active, completed, inactive]}) {
          edges {
            node {
              _id
              short_name: shortName
              avatar_url: avatarUrl
              enrollments(courseId: $courseId) {
                last_activity_at: lastActivityAt
                section {
                  name
                }
                grades {
                  current_grade: currentGrade
                  current_score: currentScore
                }
              }
              analytics: summaryAnalytics(courseId: $courseId) {
                page_views: pageViews {
                  total
                  max
                  level
                }
                participations {
                  total
                  max
                  level
                }
                tardiness_breakdown: tardinessBreakdown {
                  late
                  missing
                  on_time: onTime
                }
              }
            }
          }
        }
        submissionsConnection(
          first: 10
          orderBy: [{field: gradedAt, direction: descending}]
          studentIds: [$studentId]
        ) {
          edges {
            submission: node {
              id
              score
              grade
              excused
              user {
                _id
              }
              assignment {
                name
                html_url: htmlUrl
                points_possible: pointsPossible
              }
            }
          }
        }
      }
    }
  }
`

export default props => {
  return (
    <ApolloProvider client={client}>
      <Query query={SCC_QUERY} variables={{courseId: props.courseId, studentId: props.studentId}}>
        {({data, loading}) => {
          const {course} = data
          const user = course && course.usersConnection.edges[0].node
          return <StudentContextTray data={{loading, course, user}} {...props} />
        }}
      </Query>
    </ApolloProvider>
  )
}
<commit_msg>context cards: load user from node<commit_after>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import $ from 'jquery'
import _ from 'underscore'
import React from 'react'
import ApolloClient from "apollo-boost";
import gql from "graphql-tag";
import {ApolloProvider, Query} from "react-apollo";
import StudentContextTray from '../context_cards/StudentContextTray'

const client = new ApolloClient({
  uri: '/api/graphql',
  request: operation => {
    operation.setContext({
      headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'GraphQL-Metrics': true,
        'X-CSRF-Token': $.cookie('_csrf_token')
      }
    })
  }
})

const SCC_QUERY = gql`
  query StudentContextCard($courseId: ID!, $studentId: ID!) {
    course: legacyNode(type: Course, _id: $courseId) {
      ... on Course {
        _id
        name
        permissions {
          become_user: becomeUser
          manage_grades: manageGrades
          send_messages: sendMessages
          view_all_grades: viewAllGrades
          view_analytics: viewAnalytics
        }
        submissionsConnection(
          first: 10
          orderBy: [{field: gradedAt, direction: descending}]
          studentIds: [$studentId]
        ) {
          edges {
            submission: node {
              id
              score
              grade
              excused
              user {
                _id
              }
              assignment {
                name
                html_url: htmlUrl
                points_possible: pointsPossible
              }
            }
          }
        }
      }
    }
    user: legacyNode(type: User, _id: $studentId) {
      ... on User {
        _id
        short_name: shortName
        avatar_url: avatarUrl
        enrollments(courseId: $courseId) {
          last_activity_at: lastActivityAt
          section {
            name
          }
          grades {
            current_grade: currentGrade
            current_score: currentScore
          }
        }
        analytics: summaryAnalytics(courseId: $courseId) {
          page_views: pageViews {
            total
            max
            level
          }
          participations {
            total
            max
            level
          }
          tardiness_breakdown: tardinessBreakdown {
            late
            missing
            on_time: onTime
          }
        }
      }
    }
  }
`

export default props => {
  return (
    <ApolloProvider client={client}>
      <Query query={SCC_QUERY} variables={{courseId: props.courseId, studentId: props.studentId}}>
        {({data, loading}) => {
          const {course, user} = data
          return <StudentContextTray data={{loading, course, user}} {...props} />
        }}
      </Query>
    </ApolloProvider>
  )
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import I18n from 'i18n!permissions'
import React, {Component, Fragment} from 'react'
import {arrayOf, func} from 'prop-types'
import {connect} from 'react-redux'
import $ from 'jquery'
// For screenreaderFlashMessageExclusive  Maybe there's a better way
import 'compiled/jquery.rails_flash_notifications'

import {Button, CondensedButton, IconButton} from '@instructure/ui-buttons'
import {Checkbox} from '@instructure/ui-checkbox'
import {IconArrowOpenEndSolid, IconArrowOpenDownSolid} from '@instructure/ui-icons'

import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import {Text} from '@instructure/ui-elements'
import {Tooltip} from '@instructure/ui-overlays'
import {View} from '@instructure/ui-layout'

import actions from '../actions'
import {GROUP_PERMISSION_DESCRIPTIONS} from '../templates'
import {ConnectedPermissionButton} from './PermissionButton'
import propTypes, {ENABLED_FOR_NONE} from '../propTypes'

export default class PermissionsTable extends Component {
  static propTypes = {
    roles: arrayOf(propTypes.role).isRequired,
    modifyPermissions: func.isRequired,
    permissions: arrayOf(propTypes.permission).isRequired,
    setAndOpenRoleTray: func.isRequired,
    setAndOpenPermissionTray: func.isRequired
  }

  state = {
    expanded: {}
  }

  // just a heads up: these likely break in RTL. the best thing would be to
  // change the css so you don't manually have to scroll the table in JS but
  // if you do have to do this in JS, you need to use something like
  // 'normalize-scroll-left' from npm (grep for where we use it in the gradebook)
  // so that it works cross browser in RTL
  fixScroll = e => {
    if (!this.contentWrapper) return
    const sidebarWidth = 300
    const leftScroll = this.contentWrapper.scrollLeft
    const leftOffset = e.target.closest('td,th').offsetLeft
    if (leftOffset - sidebarWidth < leftScroll) {
      const newScroll = Math.max(0, leftScroll - sidebarWidth)
      this.contentWrapper.scrollLeft = newScroll
    }
  }

  openRoleTray(role) {
    // TODO ideally (according to Kendall) we should have this close the current
    //      tray (an animation) before loading the new tray. I was hoping that
    //      calling hideTrays() here would be enough to do that, but it is
    //      alas not the case. The `Admin > People` page has an example of
    //      how this should look, so I should check there for inspiration.
    this.props.setAndOpenRoleTray(role)
  }

  renderTopHeader() {
    return (

              <Text weight="bold" size="small">
                {I18n.t('Permissions')}
              </Text>

          {this.props.roles.map(role => (

                <div
                  className="ic-permissions__header-content ic-permissions__header-content-col"
                  id={`ic-permissions__role-header-for-role-${role.id}`}
                >
                  <Tooltip tip={role.label}>
                    <Button
                      id={`role_${role.id}`}
                      variant="link"
                      onClick={() => this.openRoleTray(role)}
                      onFocus={this.fixScroll}
                      size="small"
                      theme={{smallPadding: '0', smallHeight: 'normal'}}
                    >
                      {role.label}
                    </Button>
                  </Tooltip>

          ))}

    )
  }

  renderLeftHeader(perm) {
    const isExpanded = this.state.expanded[perm.permission_name]
    const ExpandIcon = isExpanded ? IconArrowOpenDownSolid : IconArrowOpenEndSolid
    const name = perm.permission_name
    const granulars = perm.granular_permissions
    const hasGranulars = granulars?.length > 0
    const isGranular = perm.granular_permission_group

    const toggleExpanded = () => {
      this.setState(prevState => {
        // Need to make a copy to avoid mutating existing state
        // eslint-disable-next-line prefer-object-spread
        const expanded = Object.assign({}, prevState.expanded)
        expanded[name] = !expanded[name]

        const count = granulars.length
        if (expanded[name]) {
          $.screenReaderFlashMessage(I18n.t('%{count} rows added', {count}))
        } else {
          $.screenReaderFlashMessage(I18n.t('%{count} rows removed', {count}))
        }

        return {expanded}
      })
    }

    function renderGroupDescription() {
      const description = GROUP_PERMISSION_DESCRIPTIONS[name]
      if (typeof description !== 'function') return null

      return [

,
        <Text key="group-description-text" weight="light" size="small">
          {description()}
        </Text>
      ]
    }

    return (

            {hasGranulars && (
              <IconButton
                data-testid={`expand_${name}`}
                margin="0 0 0 x-small"
                withBorder={false}
                size="small"
                withBackground={false}
                onClick={toggleExpanded}
                screenReaderLabel={
                  isExpanded
                    ? I18n.t('Expand %{permission}', {permission: perm.label})
                    : I18n.t('Shrink %{permission}', {permission: perm.label})
                }
                renderIcon={ExpandIcon}
              />
            )}
            {isGranular && }
            <View maxWidth="17rem" as="div" padding="small">
              <CondensedButton
                variant="link"
                onClick={() => this.props.setAndOpenPermissionTray(perm)}
                id={`permission_${name}`}
                theme={{mediumPadding: '0', mediumHeight: 'normal'}}
                fluidWidth
              >
                {perm.label}
              </CondensedButton>
              {hasGranulars && renderGroupDescription()}
            </View>

    )
  }

  renderGranularCheckbox(role, perm) {
    const perms = role.permissions
    const name = perm.permission_name

    function toggle() {
      const enabled = !perms[name].enabled
      const id = role.id

      this.props.modifyPermissions({enabled, explicit: true, id, name})
    }

    return (

        <Checkbox
          inline
          checked={perms[name].enabled !== ENABLED_FOR_NONE}
          disabled={perms[name].readonly}
          label={<ScreenReaderContent>{perm.label}</ScreenReaderContent>}
          onFocus={this.fixScroll}
          onChange={toggle.bind(this)}
          value={perm.label}
        />

    )
  }

  renderExpandedRows(perm) {
    return perm.granular_permissions.map(permission => (

        {this.renderLeftHeader(permission)}
        {this.props.roles.map(role => (

              {this.renderGranularCheckbox(role, permission)}

        ))}

    ))
  }

  renderTable() {
    return (

        {this.renderTopHeader()}

          {this.props.permissions.map(perm => (
            <Fragment key={perm.permission_name}>

                {this.renderLeftHeader(perm)}
                {this.props.roles.map(role => (
                  <td key={role.id} id={`${perm.permission_name}_role_${role.id}`}>

                      <ConnectedPermissionButton
                        permission={role.permissions[perm.permission_name]}
                        permissionName={perm.permission_name}
                        permissionLabel={perm.label}
                        roleId={role.id}
                        roleLabel={role.label}
                        inTray={false}
                        onFocus={this.fixScroll}
                      />

                ))}

              {this.state.expanded[perm.permission_name] && this.renderExpandedRows(perm)}
            </Fragment>
          ))}

    )
  }

  render() {
    return (

 (this.contentWrapper = c)}>
        {this.renderTable()}

    )
  }
}

function mapStateToProps(state, ownProps) {
  const stateProps = {
    roles: state.roles.filter(r => r.displayed),
    permissions: state.permissions.filter(p => p.displayed)
  }
  return {...ownProps, ...stateProps}
}

const mapDispatchToProps = {
  modifyPermissions: actions.modifyPermissions,
  setAndOpenRoleTray: actions.setAndOpenRoleTray,
  setAndOpenPermissionTray: actions.setAndOpenPermissionTray
}

export const ConnectedPermissionsTable = connect(
  mapStateToProps,
  mapDispatchToProps
)(PermissionsTable)
<commit_msg>Scroll expanded granular permissions so they are visible<commit_after>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import I18n from 'i18n!permissions'
import React, {Component, Fragment} from 'react'
import {arrayOf, func} from 'prop-types'
import {connect} from 'react-redux'
import $ from 'jquery'
import {maxBy} from 'lodash'
// For screenreaderFlashMessageExclusive  Maybe there's a better way
import 'compiled/jquery.rails_flash_notifications'

import {Button, CondensedButton, IconButton} from '@instructure/ui-buttons'
import {Checkbox} from '@instructure/ui-checkbox'
import {IconArrowOpenEndSolid, IconArrowOpenDownSolid} from '@instructure/ui-icons'

import {ScreenReaderContent} from '@instructure/ui-a11y-content'
import {Text} from '@instructure/ui-elements'
import {Tooltip} from '@instructure/ui-overlays'
import {View} from '@instructure/ui-layout'

import actions from '../actions'
import {GROUP_PERMISSION_DESCRIPTIONS} from '../templates'
import {ConnectedPermissionButton} from './PermissionButton'
import propTypes, {ENABLED_FOR_NONE} from '../propTypes'

const GRANULAR_PERMISSION_TAG = 'ic-permissions__grp-tag'

export default class PermissionsTable extends Component {
  static propTypes = {
    roles: arrayOf(propTypes.role).isRequired,
    modifyPermissions: func.isRequired,
    permissions: arrayOf(propTypes.permission).isRequired,
    setAndOpenRoleTray: func.isRequired,
    setAndOpenPermissionTray: func.isRequired
  }

  constructor(props) {
    super(props)
    this.state = {
      expanded: {}
    }
    this.justExpanded = null
    this.tableRef = React.createRef()
  }

  componentDidUpdate() {
    if (this.justExpanded) {
      this.fixVerticalScroll()
      this.justExpanded = null
    }
  }

  // just a heads up: these likely break in RTL. the best thing would be to
  // change the css so you don't manually have to scroll the table in JS but
  // if you do have to do this in JS, you need to use something like
  // 'normalize-scroll-left' from npm (grep for where we use it in the gradebook)
  // so that it works cross browser in RTL
  fixHorizontalScroll = e => {
    if (!this.contentWrapper) return
    const sidebarWidth = 300
    const leftScroll = this.contentWrapper.scrollLeft
    const leftOffset = e.target.closest('td,th').offsetLeft
    if (leftOffset - sidebarWidth < leftScroll) {
      const newScroll = Math.max(0, leftScroll - sidebarWidth)
      this.contentWrapper.scrollLeft = newScroll
    }
  }

  fixVerticalScroll = () => {
    // All rows corrresponding to granular permissions will have a special
    // class attached to them. Find the ones corresponding to the expand-o
    // operation that JUST happened.
    if (!this.tableRef.current) return
    const newGranulars = this.tableRef.current.querySelectorAll(
      `tr.${GRANULAR_PERMISSION_TAG}-${this.justExpanded}`
    )
    if (newGranulars.length === 0) return

    // We now have the rows that were added as a result of expanding the group.
    // Find the bottom-most one of those, and then if it is below the visible
    // region of the scrolling div, scroll it into view.
    // Note that we don't have to worry about scrolling in the other direction,
    // because rows can only get added BELOW the group, so they can never be
    // off the top of the scroll region when first created
    const scrollToMe = maxBy(Array.from(newGranulars), 'offsetTop')
    const scrollArea = scrollToMe.closest('div.ic-permissions__table-container')
    const myBottom = scrollToMe.offsetTop + scrollToMe.offsetHeight
    const scrollAreaBottom = scrollArea.scrollTop + scrollArea.clientHeight
    if (myBottom > scrollAreaBottom)
      scrollArea.scrollBy({
        top: myBottom - scrollAreaBottom,
        left: 0,
        behavior: 'smooth'
      })
  }

  openRoleTray(role) {
    // TODO ideally (according to Kendall) we should have this close the current
    //      tray (an animation) before loading the new tray. I was hoping that
    //      calling hideTrays() here would be enough to do that, but it is
    //      alas not the case. The `Admin > People` page has an example of
    //      how this should look, so I should check there for inspiration.
    this.props.setAndOpenRoleTray(role)
  }

  renderTopHeader() {
    return (

              <Text weight="bold" size="small">
                {I18n.t('Permissions')}
              </Text>

          {this.props.roles.map(role => (

                <div
                  className="ic-permissions__header-content ic-permissions__header-content-col"
                  id={`ic-permissions__role-header-for-role-${role.id}`}
                >
                  <Tooltip tip={role.label}>
                    <Button
                      id={`role_${role.id}`}
                      variant="link"
                      onClick={() => this.openRoleTray(role)}
                      onFocus={this.fixHorizontalScroll}
                      size="small"
                      theme={{smallPadding: '0', smallHeight: 'normal'}}
                    >
                      {role.label}
                    </Button>
                  </Tooltip>

          ))}

    )
  }

  renderLeftHeader(perm) {
    const isExpanded = this.state.expanded[perm.permission_name]
    const ExpandIcon = isExpanded ? IconArrowOpenDownSolid : IconArrowOpenEndSolid
    const name = perm.permission_name
    const granulars = perm.granular_permissions
    const hasGranulars = granulars?.length > 0
    const isGranular = perm.granular_permission_group

    const toggleExpanded = () => {
      this.setState(prevState => {
        // Need to make a copy to avoid mutating existing state
        // eslint-disable-next-line prefer-object-spread
        const expanded = Object.assign({}, prevState.expanded)
        expanded[name] = !expanded[name]

        const count = granulars.length
        if (expanded[name]) {
          $.screenReaderFlashMessage(I18n.t('%{count} rows added', {count}))
        } else {
          $.screenReaderFlashMessage(I18n.t('%{count} rows removed', {count}))
        }

        if (expanded[name]) this.justExpanded = name
        return {expanded}
      })
    }

    function renderGroupDescription() {
      const description = GROUP_PERMISSION_DESCRIPTIONS[name]
      if (typeof description !== 'function') return null

      return [

,
        <Text key="group-description-text" weight="light" size="small">
          {description()}
        </Text>
      ]
    }

    return (

            {hasGranulars && (
              <IconButton
                data-testid={`expand_${name}`}
                margin="0 0 0 x-small"
                withBorder={false}
                size="small"
                withBackground={false}
                onClick={toggleExpanded}
                screenReaderLabel={
                  isExpanded
                    ? I18n.t('Expand %{permission}', {permission: perm.label})
                    : I18n.t('Shrink %{permission}', {permission: perm.label})
                }
                renderIcon={ExpandIcon}
              />
            )}
            {isGranular && }
            <View maxWidth="17rem" as="div" padding="small">
              <CondensedButton
                variant="link"
                onClick={() => this.props.setAndOpenPermissionTray(perm)}
                id={`permission_${name}`}
                theme={{mediumPadding: '0', mediumHeight: 'normal'}}
                fluidWidth
              >
                {perm.label}
              </CondensedButton>
              {hasGranulars && renderGroupDescription()}
            </View>

    )
  }

  renderGranularCheckbox(role, perm) {
    const perms = role.permissions
    const name = perm.permission_name

    function toggle() {
      const enabled = !perms[name].enabled
      const id = role.id

      this.props.modifyPermissions({enabled, explicit: true, id, name})
    }

    return (

        <Checkbox
          inline
          checked={perms[name].enabled !== ENABLED_FOR_NONE}
          disabled={perms[name].readonly}
          label={<ScreenReaderContent>{perm.label}</ScreenReaderContent>}
          onFocus={this.fixHorizontalScroll}
          onChange={toggle.bind(this)}
          value={perm.label}
        />

    )
  }

  renderExpandedRows(perm) {
    return perm.granular_permissions.map(permission => (
      <tr
        key={permission.label}
        className={`${GRANULAR_PERMISSION_TAG}-${permission.granular_permission_group}`}
      >
        {this.renderLeftHeader(permission)}
        {this.props.roles.map(role => (

              {this.renderGranularCheckbox(role, permission)}

        ))}

    ))
  }

  renderTable() {
    return (

        {this.renderTopHeader()}

          {this.props.permissions.map(perm => (
            <Fragment key={perm.permission_name}>

                {this.renderLeftHeader(perm)}
                {this.props.roles.map(role => (
                  <td key={role.id} id={`${perm.permission_name}_role_${role.id}`}>

                      <ConnectedPermissionButton
                        permission={role.permissions[perm.permission_name]}
                        permissionName={perm.permission_name}
                        permissionLabel={perm.label}
                        roleId={role.id}
                        roleLabel={role.label}
                        inTray={false}
                        onFocus={this.fixHorizontalScroll}
                      />

                ))}

              {this.state.expanded[perm.permission_name] && this.renderExpandedRows(perm)}
            </Fragment>
          ))}

    )
  }

  render() {
    return (

 (this.contentWrapper = c)}>
        {this.renderTable()}

    )
  }
}

function mapStateToProps(state, ownProps) {
  const stateProps = {
    roles: state.roles.filter(r => r.displayed),
    permissions: state.permissions.filter(p => p.displayed)
  }
  return {...ownProps, ...stateProps}
}

const mapDispatchToProps = {
  modifyPermissions: actions.modifyPermissions,
  setAndOpenRoleTray: actions.setAndOpenRoleTray,
  setAndOpenPermissionTray: actions.setAndOpenPermissionTray
}

export const ConnectedPermissionsTable = connect(
  mapStateToProps,
  mapDispatchToProps
)(PermissionsTable)
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2019 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import React from 'react'
import {render, fireEvent} from '@testing-library/react'
import fetchMock from 'fetch-mock'
import DirectShareCourseTray from '../direct_share/DirectShareCourseTray'
import useManagedCourseSearchApi from '../effects/useManagedCourseSearchApi'

jest.mock('jsx/shared/effects/useManagedCourseSearchApi')

const userManagedCoursesList = [
  {
    name: 'Course Math 101',
    id: '234',
    term: 'Default Term',
    enrollment_start: null,
    account_name: 'QA-LOCAL-QA',
    account_id: '1',
    start_at: 'Aug 6, 2019 at 6:47pm',
    end_at: null
  },
  {
    name: 'Course Advanced Math 200',
    id: '123',
    term: 'Default Term',
    enrollment_start: null,
    account_name: 'QA-LOCAL-QA',
    account_id: '1',
    start_at: 'Apr 27, 2019 at 2:19pm',
    end_at: 'Dec 31, 2019 at 3am'
  }
]

describe('DirectShareCopyToTray', () => {
  beforeEach(() => {
    jest.spyOn(console, 'error').mockImplementation(() => {})
  })
  afterEach(() => {
    console.error.mockRestore()
    fetchMock.restore()
  })

  describe('tray controls', () => {
    it('closes the tray when X is clicked', () => {
      const handleDismiss = jest.fn()
      const {getByText} = render(<DirectShareCourseTray open onDismiss={handleDismiss} />)
      fireEvent.click(getByText(/close/i))

      expect(handleDismiss).toHaveBeenCalled()
    })

    it('handles error when user managed course fetch fails', async () => {
      useManagedCourseSearchApi.mockImplementationOnce(({error}) =>
        error([{status: 400, body: 'Error fetching data'}])
      )
      const {getByText} = render(<DirectShareCourseTray open />)

      expect(getByText('Sorry, Something Broke')).toBeInTheDocument()
    })
  })

  describe('course dropdown', () => {
    it('populates the list of all managed courses', () => {
      useManagedCourseSearchApi.mockImplementationOnce(({success}) => {
        success(userManagedCoursesList)
      })
      const {getByText, getByLabelText} = render(<DirectShareCourseTray open />)
      const courseDropdown = getByLabelText(/select a course/i)
      fireEvent.click(courseDropdown)

      expect(getByText('Course Advanced Math 200')).toBeInTheDocument()
      expect(getByText('Course Math 101')).toBeInTheDocument()
    })
  })

  describe('destination learning object dropdown', () => {
    it('shows all modules in selected course when launched from modules menu', () => {})
    it('shows all assignment groups in selected course when launched from assignments menu', () => {})
    it('locks destination to be discussion when launched from discussion menu', () => {})
    it('shows all assignment groups in selected course when launched from quizzes menu', () => {})
  })

  describe('place dropdown', () => {
    it('user can select place to be at the top', () => {})
    it('user can select place to be at the bottom', () => {})
  })

  describe('copying', () => {
    it('clicking the copy button displays a loading state', async () => {})
  })
})
<commit_msg>spec: fill test gaps for direct share trays<commit_after>/*
 * Copyright (C) 2019 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import React from 'react'
import {render, fireEvent, act} from '@testing-library/react'
import fetchMock from 'fetch-mock'
import DirectShareCourseTray from '../direct_share/DirectShareCourseTray'
import useManagedCourseSearchApi from '../effects/useManagedCourseSearchApi'
import useModuleCourseSearchApi from '../effects/useModuleCourseSearchApi'

jest.mock('jsx/shared/effects/useManagedCourseSearchApi')
jest.mock('jsx/shared/effects/useModuleCourseSearchApi')

const userManagedCoursesList = [
  {
    name: 'Course Math 101',
    id: '234',
    term: 'Default Term',
    enrollment_start: null,
    account_name: 'QA-LOCAL-QA',
    account_id: '1',
    start_at: 'Aug 6, 2019 at 6:47pm',
    end_at: null
  },
  {
    name: 'Course Advanced Math 200',
    id: '123',
    term: 'Default Term',
    enrollment_start: null,
    account_name: 'QA-LOCAL-QA',
    account_id: '1',
    start_at: 'Apr 27, 2019 at 2:19pm',
    end_at: 'Dec 31, 2019 at 3am'
  }
]

describe('DirectShareCopyToTray', () => {
  beforeEach(() => {
    jest.spyOn(console, 'error').mockImplementation(() => {})
  })
  afterEach(() => {
    console.error.mockRestore()
    fetchMock.restore()
  })

  describe('tray controls', () => {
    it('closes the tray when X is clicked', () => {
      const handleDismiss = jest.fn()
      const {getByText} = render(<DirectShareCourseTray open onDismiss={handleDismiss} />)
      fireEvent.click(getByText(/close/i))

      expect(handleDismiss).toHaveBeenCalled()
    })

    it('handles error when user managed course fetch fails', async () => {
      useManagedCourseSearchApi.mockImplementationOnce(({error}) =>
        error([{status: 400, body: 'Error fetching data'}])
      )
      const {getByText} = render(<DirectShareCourseTray open />)

      expect(getByText('Sorry, Something Broke')).toBeInTheDocument()
    })

    it('handles error when course module fetch fails', async () => {
      useManagedCourseSearchApi.mockImplementationOnce(({success}) => {
        success(userManagedCoursesList)
      })
      useModuleCourseSearchApi.mockImplementationOnce(({error}) =>
        error([{status: 400, body: 'Error fetching data'}])
      )
      const {getByText} = render(<DirectShareCourseTray open />)
      fireEvent.click(getByText(/select a course/i))
      fireEvent.click(getByText('Course Math 101'))
      await act(() => fetchMock.flush(true))

      expect(getByText('Sorry, Something Broke')).toBeInTheDocument()
    })
  })

  describe('course dropdown', () => {
    it('populates the list of all managed courses', () => {
      useManagedCourseSearchApi.mockImplementationOnce(({success}) => {
        success(userManagedCoursesList)
      })
      const {getByText, getByLabelText} = render(<DirectShareCourseTray open />)
      const courseDropdown = getByLabelText(/select a course/i)
      fireEvent.click(courseDropdown)

      expect(getByText('Course Advanced Math 200')).toBeInTheDocument()
      expect(getByText('Course Math 101')).toBeInTheDocument()
    })
  })

  describe('place dropdown', () => {
    it('user can select place to be at the top', () => {})
    it('user can select place to be at the bottom', () => {})
  })

  describe('copying', () => {
    it('clicking the copy button displays a loading state', async () => {})
  })
})
<|endoftext|>