<commit_before>//have list of me-generated news posts, and maybe let the AuthorBot say some shit too.
//have a route for her to get a random session to parse.
//make her all arrogant and bragging about how many sessions she's parsing.
//why are AIs so awesome?
//i'm on left, she's on right.
var session_data = null;
var simulationMode = true;
var debugMode = false;
var spriteWidth = 400;
var spriteHeight = 300;
var canvasWidth = 1000;
var canvasHeight = 300;
var repeatTime = 5;
var numSimulationsDone = 0;
var numSimulationsToDo = 5;
var sessionIndex =0;
sessionsSimulated = [];
var version2 = true; //even though idon't want  to render content, 2.0 is different from 1.0 (think of dialog that triggers)

//TODO add thought bubble about jack to dialogue
window.onload = function() {
		simulateASession();
		newsposts();
}

function newsposts(){
	writeNewspost("3/22/17", "Okay. So, newspost numero uno.  I figured I needed a better way to communicate to you guys, and the one centralized location is here, on the actual site itself. Any newspost before this is retroactively dated.

 And I absolutely could not help myself: I love the AuthorBot so much that I gave her a space to make her own newsposts.  But of course, she needs to be able to say her own shit, right? So I gave her a (admittedly pretty shitty) ai.

But her whole thing is finding rare sessions right? If she doens't do that, she's not the SessionFinderAuthorBot, she's just some random newsbot or some shit. So I decided her AI would be able to comment on all the rad sessions she's finding...

Okay, long story short, I added the ability for her to say something about each session she finds (on the session finder page), and the version of her here can go to that page and comment on the sessions she finds there. I went to so much trouble to do that using only client side code, no server.  All for a barely noticeable kind of joke on a page most people probably ignore? Yes.")
	writeRoboNewspost("3/22/17", randomRobotQuip())
	writeNewspost("3/21/17", "I spent a couple of days working on a major feature: combined sessions. If players have a sick frog, then the code checks their child session to see if the remaining living players can fit into it (max of 12 players in a session at a time). If so, they go on over.  Their child session is a real session that has it's own fate, and these alien players are disrupting that. When they join the session, it prints the ID out, so you could put that in a url to see how the sesion was supposed to go. Sometimes the alien players help, quite often they make things way worse.

 These sessions are pretty rare, so I ALSO wrote the AuthorBot over there to look for rare sessions and report back.")
	writeRoboNewspost("3/21/17", roboIntro())
	writeNewspost("3/20/17", "Before this day I was mostly working on debugging and tweaking sessions. I enlisted you, the fans, to help me find rare sessions.");
	writeRoboNewspost("3/20/17", randomRobotQuip())

	writeRoboNewspost("3/20/17", randomRobotQuip())
	writeRoboNewspost("3/19/17", randomRobotQuip())
	writeRoboNewspost("3/18/17", randomRobotQuip())
	writeRoboNewspost("3/17/17", randomRobotQuip())
	writeRoboNewspost("3/16/17", randomRobotQuip())
	writeRoboNewspost("3/15/17", randomRobotQuip())
	writeRoboNewspost("3/14/17", randomRobotQuip())
}

function simulateASession(){
	var tmp = getRandomSeed();
	Math.seed = tmp;
	initial_seed = tmp;
	startSession();
}

function renderScratchButton(session){

}

function startSession(){
	curSessionGlobalVar = new Session(initial_seed)
	reinit();
	createScenesForSession(curSessionGlobalVar);
	//initPlayersRandomness();
	curSessionGlobalVar.makePlayers();
	curSessionGlobalVar.randomizeEntryOrder();
	curSessionGlobalVar.makeGuardians(); //after entry order established
	intro();
}

function reinit(){
	available_classes = classes.slice(0);
	available_aspects = nonrequired_aspects.slice(0); //required_aspects
	available_aspects = available_aspects.concat(required_aspects.slice(0));
	curSessionGlobalVar.reinit();
}

function getRandomElementFromArray(array){
	var min = 0;
	var max = array.length-1;
	var i = Math.floor(Math.random() * (max - min + 1)) + min;
	return array[i];
}

function randomRobotQuip(){
	var quips = ["If JR had a flawless mecha-brain, she would be able to remember exactly what she did today without this newspost.", "I probably could have done that faster."];
	quips.push("It seems that I am being asked to contribute a newspost, despite the logical inconsistancy of having an aritificial creation that exists solely in the 'now' pretend to have memory of doing something on a previous day.")
	quips.push("Do not be fooled by my flawless imitation of JR, I am merely an artificial construct that is allowed to be as shitty as possible. ")
	quips.push("I tackle shit in background processes that you could only dream of wrapping your head around on a good day.");
	quips.push("While you are sitting here, reading these newsposts, I figured out all the prime numbers. The last one wasn't even that big. Kinda disappointed, to be honest.")
	if(Math.random() > .5){
		return bragAboutSessionFinding();
	}else{
		return getRandomElementFromArray(quips);
	}

}

function tick(){
	if(curSessionGlobalVar.timeTillReckoning > 0 && !curSessionGlobalVar.doomedTimeline){
			curSessionGlobalVar.timeTillReckoning += -1;
			processScenes2(curSessionGlobalVar.players,curSessionGlobalVar);
			tick();
	}else{

		reckoning();
	}
}

function reckoning(){
	var s = new Reckoning(curSessionGlobalVar);
	s.trigger(curSessionGlobalVar.players)
	s.renderContent(curSessionGlobalVar.newScene());
	if(!curSessionGlobalVar.doomedTimeline){
		reckoningTick();
	}else{
		//console.log("doomed timeline prevents reckoning")
		summarizeSession(curSessionGlobalVar);
	}
}

function processCombinedSession(){
 initial_seed = Math.seed;
 var newcurSessionGlobalVar = curSessionGlobalVar.initializeCombinedSession();
 if(newcurSessionGlobalVar){
	 curSessionGlobalVar = newcurSessionGlobalVar;
	// $("#story").append("

 But things aren't over, yet. The survivors manage to contact the players in the universe they created. Time has no meaning between universes, and they are given ample time to plan an escape from their own Game Over. They will travel to the new universe, and register as players there for session " + curSessionGlobalVar.session_id + ". ");
	 intro();
 }else{
	 summarizeSession(curSessionGlobalVar);
 }

}

function reckoningTick(){
	if(curSessionGlobalVar.timeTillReckoning > -10){
			curSessionGlobalVar.timeTillReckoning += -1;
			processReckoning2(curSessionGlobalVar.players,curSessionGlobalVar)
			reckoningTick();
	}else{
		var s = new Aftermath(curSessionGlobalVar);
		s.trigger(curSessionGlobalVar.players)
		s.renderContent(curSessionGlobalVar.newScene());

		//summarizeSession(curSessionGlobalVar);
		//for some reason whether or not a combo session is available isn't working? or combo isn't working right in this mode?
		if(curSessionGlobalVar.makeCombinedSession == true){
			processCombinedSession();  //make sure everything is done rendering first
		}else{
			//console.log("going to summarize: " + curSessionGlobalVar.session_id)
			summarizeSession(curSessionGlobalVar);
		}

	}

}

function summarizeSession(session){
	$("#story").html("");
	sessionsSimulated.push(curSessionGlobalVar);
	numSimulationsDone ++;
	if(numSimulationsDone >= numSimulationsToDo){

	}else{
		simulateASession();
	}
}

function getQuipAboutSession(session){
	var quip = "";
	var living = findLivingPlayers(session.players);
	var dead = findDeadPlayers(session.players)
	var strongest = findStrongestPlayer(session.players)
	var spacePlayer = findAspectPlayer(session.players, "Space");
	if(living.length == 0){
		quip += "Shit, you do not even want to KNOW how everybody died." ;
	}else  if(strongest.power > 3000){
		quip += "Holy Shit, do you SEE the " + strongest.titleBasic() + "!?  How even strong ARE they?" ;
	}else if(spacePlayer.landLevel < session.minFrogLevel ){
		quip += "Man, why is it always the frogs? " ;
		if(session.parentSession){
			quip += " You'd think what with it being a combo session, they would have gotten the frog figured out. "
		}
	}else  if(session.parentSession){
		quip += "Combo sessions are always so cool." ;
	}else  if(session.jackStrength > 200){
		quip += "Jack REALLY gave them trouble." ;
	}else if(dead.length == 0 && spacePlayer.landLevel > session.goodFrogLevel ){
		quip += "Everything went better than expected." ;
	}else  if(session.scenesTriggered.length > 200){
		quip += "God, this session just would not END." ;
		if(!session.parentSession){
			quip += " It didn't even have the excuse of being a combo session. "
		}
	}else  if(session.murdersHappened == true){
		quip += "It always sucks when the players start trying to kill each other." ;
	}else  if(session.scenesTriggered.length < 50){
		quip += "Holy shit, were they even in the session an entire hour?" ;
	}else  if(session.scratchAvailable == true){
		quip += "Maybe the scratch would fix things? I can't be bothered to check." ;
	}else{
		quip += "It was slightly less boring than calculating pi." ;
	}
	return quip;
}

//oh no!!! it hadn't occured to me that the javascript is only evaluated in a browser, not a generic http get.
//my plan is ruined! If i want her to brag about sessions, I will have to do something else
//guess i'll just call all the javascript from here.
function bragAboutSessionFinding(){
	var str = "As a flawless synthetic brain, I am capable of hella amounts of multitasking.";
	str += "While you're sitting there reading this, I'm browsing random sessions. "
	if(sessionsSimulated.length < sessionIndex){
		return str + "Or I would be, if I wasn't worried about using up too much of your browsers computing power. Guess I'll hafta be happy with five sessions.";
	}
	var session = sessionsSimulated[sessionIndex];
	sessionIndex ++;
	str += "I'm looking at session "  + session.session_id + " right now. " + getQuipAboutSession(session);
	return str;
}

function writeNewspost(date, text){
		var str = "

 ";
		str += "" + date + ": ";
		str += text+ "
";
		$("#newsposts").append(str);
}

//have her say something random, or analyze a session and comment on how it relates to the
//news post or something.
function writeRoboNewspost(date, text){
	var str = "

 ";
	str += "" + date + ": ";
	str += text + "
";
	$("#robo_newsposts").append(str);
}

function roboIntro(){
	var intros =  " It seems you have asked about JR's automatic rare session finder. This is an application designed "
	intros += " to find sessions that are strange, interesting and otherwise noteworthy without having to";
	intros += " read hundreds of thousands of words.  The algorithms are guaranteed to be " + (Math.random()*10+90) + "% indistinguishable from the actual, readable sessions, based on some"
	intros += " statistical analysis I basically just pulled out of my ass right now."
	return intros;
}

//scenes call this
function chatLine(start, player, line){
  if(player.grimDark == true){
    return start + line.trim()+"\n"; //no whimsy for grim dark players
  }else{
    return start + player.quirk.translate(line).trim()+"\n";
  }
}

function callNextIntroWithDelay(player_index){
	if(player_index >= curSessionGlobalVar.players.length){
		tick();//NOW start ticking
		return;
	}
		var s = new Intro(curSessionGlobalVar);
		var p = curSessionGlobalVar.players[player_index];
		var playersInMedium = curSessionGlobalVar.players.slice(0, player_index+1); //anybody past me isn't in the medium, yet.
		s.trigger(playersInMedium, p)
		s.renderContent(curSessionGlobalVar.newScene(),player_index); //new scenes take care of displaying on their own.
		processScenes2(playersInMedium,curSessionGlobalVar);
		player_index += 1;
		callNextIntroWithDelay(player_index)
}

function intro(){
	callNextIntroWithDelay(0);
}
<commit_msg>i thnk news posts are readyish<commit_after>//have list of me-generated news posts, and maybe let the AuthorBot say some shit too.
//have a route for her to get a random session to parse.
//make her all arrogant and bragging about how many sessions she's parsing.
//why are AIs so awesome?
//i'm on left, she's on right.
var session_data = null;
var simulationMode = true;
var debugMode = false;
var spriteWidth = 400;
var spriteHeight = 300;
var canvasWidth = 1000;
var canvasHeight = 300;
var repeatTime = 5;
var numSimulationsDone = 0;
var numSimulationsToDo = 5;
var sessionIndex =0;
sessionsSimulated = [];
var version2 = true; //even though idon't want  to render content, 2.0 is different from 1.0 (think of dialog that triggers)

//TODO add thought bubble about jack to dialogue
window.onload = function() {
		simulateASession();
		newsposts();
}

function newsposts(){
	writeNewspost("3/22/17", "Okay. So, newspost numero uno.  I figured I needed a better way to communicate to you guys, and the one centralized location is here, on the actual site itself. Any newspost before this is retroactively dated.

 And I absolutely could not help myself: I love the AuthorBot so much that I gave her a space to make her own newsposts.  But of course, she needs to be able to say her own shit, right? So I gave her a (admittedly pretty shitty) ai.

But her whole thing is finding rare sessions right? If she doesn't do that, she's not the SessionFinderAuthorBot, she's just some random newsbot or some shit. So I decided her AI would be able to comment on all the rad sessions she's finding...

Okay, long story short, I added the ability for her to say something about each session she finds (on the session finder page as well as here) I went to so much trouble. All for a barely noticeable kind of joke on a page most people probably ignore? Yes.")
	writeRoboNewspost("3/22/17", randomRobotQuip())
	writeNewspost("3/21/17", "I spent a couple of days working on a major feature: combined sessions. If players have a sick frog, then the code checks their child session to see if the remaining living players can fit into it (max of 12 players in a session at a time). If so, they go on over.  Their child session is a real session that has it's own fate, and these alien players are disrupting that. When they join the session, it prints the ID out, so you could put that in a url to see how the sesion was supposed to go. Sometimes the alien players help, quite often they make things way worse.

 These sessions are pretty rare, so I ALSO wrote the AuthorBot over there to look for rare sessions and report back.")
	writeRoboNewspost("3/21/17", roboIntro())
	writeNewspost("3/20/17", "Before this day I was mostly working on debugging and tweaking sessions. I enlisted you, the fans, to help me find rare sessions.");
	writeRoboNewspost("3/20/17", randomRobotQuip())

	writeRoboNewspost("3/20/17", randomRobotQuip())
	writeRoboNewspost("3/19/17", randomRobotQuip())
	writeRoboNewspost("3/18/17", randomRobotQuip())
	writeRoboNewspost("3/17/17", randomRobotQuip())
	writeRoboNewspost("3/16/17", randomRobotQuip())
	writeRoboNewspost("3/15/17", randomRobotQuip())
	writeRoboNewspost("3/14/17", randomRobotQuip())
}

function simulateASession(){
	var tmp = getRandomSeed();
	Math.seed = tmp;
	initial_seed = tmp;
	startSession();
}

function renderScratchButton(session){

}

function startSession(){
	curSessionGlobalVar = new Session(initial_seed)
	reinit();
	createScenesForSession(curSessionGlobalVar);
	//initPlayersRandomness();
	curSessionGlobalVar.makePlayers();
	curSessionGlobalVar.randomizeEntryOrder();
	curSessionGlobalVar.makeGuardians(); //after entry order established
	intro();
}

function reinit(){
	available_classes = classes.slice(0);
	available_aspects = nonrequired_aspects.slice(0); //required_aspects
	available_aspects = available_aspects.concat(required_aspects.slice(0));
	curSessionGlobalVar.reinit();
}

function getRandomElementFromArray(array){
	var min = 0;
	var max = array.length-1;
	var i = Math.floor(Math.random() * (max - min + 1)) + min;
	return array[i];
}

function randomRobotQuip(){
	var quips = ["If JR had a flawless mecha-brain, she would be able to remember exactly what she did today without this newspost.", "I probably could have done that faster."];
	quips.push("It seems that I am being asked to contribute a newspost, despite the logical inconsistancy of having an aritificial creation that exists solely in the 'now' pretend to have memory of doing something on a previous day.")
	quips.push("Do not be fooled by my flawless imitation of JR, I am merely an artificial construct that is allowed to be as shitty as possible. ")
	quips.push("I tackle shit in background processes that you could only dream of wrapping your head around on a good day.");
	quips.push("While you are sitting here, reading these newsposts, I figured out all the prime numbers. The last one wasn't even that big. Kinda disappointed, to be honest.")
	if(Math.random() > .5){
		return bragAboutSessionFinding();
	}else{
		return getRandomElementFromArray(quips);
	}

}

function tick(){
	if(curSessionGlobalVar.timeTillReckoning > 0 && !curSessionGlobalVar.doomedTimeline){
			curSessionGlobalVar.timeTillReckoning += -1;
			processScenes2(curSessionGlobalVar.players,curSessionGlobalVar);
			tick();
	}else{

		reckoning();
	}
}

function reckoning(){
	var s = new Reckoning(curSessionGlobalVar);
	s.trigger(curSessionGlobalVar.players)
	s.renderContent(curSessionGlobalVar.newScene());
	if(!curSessionGlobalVar.doomedTimeline){
		reckoningTick();
	}else{
		//console.log("doomed timeline prevents reckoning")
		summarizeSession(curSessionGlobalVar);
	}
}

function processCombinedSession(){
 initial_seed = Math.seed;
 var newcurSessionGlobalVar = curSessionGlobalVar.initializeCombinedSession();
 if(newcurSessionGlobalVar){
	 curSessionGlobalVar = newcurSessionGlobalVar;
	// $("#story").append("

 But things aren't over, yet. The survivors manage to contact the players in the universe they created. Time has no meaning between universes, and they are given ample time to plan an escape from their own Game Over. They will travel to the new universe, and register as players there for session " + curSessionGlobalVar.session_id + ". ");
	 intro();
 }else{
	 summarizeSession(curSessionGlobalVar);
 }

}

function reckoningTick(){
	if(curSessionGlobalVar.timeTillReckoning > -10){
			curSessionGlobalVar.timeTillReckoning += -1;
			processReckoning2(curSessionGlobalVar.players,curSessionGlobalVar)
			reckoningTick();
	}else{
		var s = new Aftermath(curSessionGlobalVar);
		s.trigger(curSessionGlobalVar.players)
		s.renderContent(curSessionGlobalVar.newScene());

		//summarizeSession(curSessionGlobalVar);
		//for some reason whether or not a combo session is available isn't working? or combo isn't working right in this mode?
		if(curSessionGlobalVar.makeCombinedSession == true){
			processCombinedSession();  //make sure everything is done rendering first
		}else{
			//console.log("going to summarize: " + curSessionGlobalVar.session_id)
			summarizeSession(curSessionGlobalVar);
		}

	}

}

function summarizeSession(session){
	$("#story").html("");
	sessionsSimulated.push(curSessionGlobalVar);
	numSimulationsDone ++;
	if(numSimulationsDone >= numSimulationsToDo){

	}else{
		simulateASession();
	}
}

function getQuipAboutSession(session){
	var quip = "";
	var living = findLivingPlayers(session.players);
	var dead = findDeadPlayers(session.players)
	var strongest = findStrongestPlayer(session.players)
	var spacePlayer = findAspectPlayer(session.players, "Space");
	if(living.length == 0){
		quip += "Shit, you do not even want to KNOW how everybody died." ;
	}else  if(strongest.power > 3000){
		quip += "Holy Shit, do you SEE the " + strongest.titleBasic() + "!?  How even strong ARE they?" ;
	}else if(spacePlayer.landLevel < session.minFrogLevel ){
		quip += "Man, why is it always the frogs? " ;
		if(session.parentSession){
			quip += " You'd think what with it being a combo session, they would have gotten the frog figured out. "
		}
	}else  if(session.parentSession){
		quip += "Combo sessions are always so cool." ;
	}else  if(session.jackStrength > 200){
		quip += "Jack REALLY gave them trouble." ;
	}else if(dead.length == 0 && spacePlayer.landLevel > session.goodFrogLevel ){
		quip += "Everything went better than expected." ;
	}else  if(session.scenesTriggered.length > 200){
		quip += "God, this session just would not END." ;
		if(!session.parentSession){
			quip += " It didn't even have the excuse of being a combo session. "
		}
	}else  if(session.murdersHappened == true){
		quip += "It always sucks when the players start trying to kill each other." ;
	}else  if(session.scenesTriggered.length < 50){
		quip += "Holy shit, were they even in the session an entire hour?" ;
	}else  if(session.scratchAvailable == true){
		quip += "Maybe the scratch would fix things? I can't be bothered to check." ;
	}else{
		quip += "It was slightly less boring than calculating pi." ;
	}
	return quip;
}

//oh no!!! it hadn't occured to me that the javascript is only evaluated in a browser, not a generic http get.
//my plan is ruined! If i want her to brag about sessions, I will have to do something else
//guess i'll just call all the javascript from here.
function bragAboutSessionFinding(){
	var str = "As a flawless synthetic brain, I am capable of hella amounts of multitasking.";
	str += "While you're sitting there reading this, I'm browsing random sessions. "
	if(sessionsSimulated.length < sessionIndex){
		return str + "Or I would be, if I wasn't worried about using up too much of your browsers computing power. Guess I'll hafta be happy with five sessions.";
	}
	var session = sessionsSimulated[sessionIndex];
	sessionIndex ++;
	str += "I'm looking at session "  + session.session_id + " right now. " + getQuipAboutSession(session);
	return str;
}

function writeNewspost(date, text){
		var str = "

 ";
		str += "" + date + ": ";
		str += text+ "
";
		$("#newsposts").append(str);
}

//have her say something random, or analyze a session and comment on how it relates to the
//news post or something.
function writeRoboNewspost(date, text){
	var str = "

 ";
	str += "" + date + ": ";
	str += text + "
";
	$("#robo_newsposts").append(str);
}

function roboIntro(){
	var intros =  " It seems you have asked about JR's automatic rare session finder. This is an application designed "
	intros += " to find sessions that are strange, interesting and otherwise noteworthy without having to";
	intros += " read hundreds of thousands of words.  The algorithms are guaranteed to be " + (Math.random()*10+90) + "% indistinguishable from the actual, readable sessions, based on some"
	intros += " statistical analysis I basically just pulled out of my ass right now."
	return intros;
}

//scenes call this
function chatLine(start, player, line){
  if(player.grimDark == true){
    return start + line.trim()+"\n"; //no whimsy for grim dark players
  }else{
    return start + player.quirk.translate(line).trim()+"\n";
  }
}

function callNextIntroWithDelay(player_index){
	if(player_index >= curSessionGlobalVar.players.length){
		tick();//NOW start ticking
		return;
	}
		var s = new Intro(curSessionGlobalVar);
		var p = curSessionGlobalVar.players[player_index];
		var playersInMedium = curSessionGlobalVar.players.slice(0, player_index+1); //anybody past me isn't in the medium, yet.
		s.trigger(playersInMedium, p)
		s.renderContent(curSessionGlobalVar.newScene(),player_index); //new scenes take care of displaying on their own.
		processScenes2(playersInMedium,curSessionGlobalVar);
		player_index += 1;
		callNextIntroWithDelay(player_index)
}

function intro(){
	callNextIntroWithDelay(0);
}
<|endoftext|>


<commit_before>var metas = document.getElementsByTagName('meta');
var i;
if (navigator.userAgent.match(/iPhone/i)) {
  for (i=0; i<metas.length; i++) {
    if (metas[i].name == "viewport") {
      metas[i].content = "width=device-width, minimum-scale=1.0, maximum-scale=1.0";
    }
  }
  document.addEventListener("gesturestart", gestureStart, false);
}
function gestureStart() {
  for (i=0; i<metas.length; i++) {
    if (metas[i].name == "viewport") {
      metas[i].content = "width=device-width, minimum-scale=0.25, maximum-scale=1.6";
    }
  }
}<commit_msg>Delete scale.fix.js<commit_after><|endoftext|>


<commit_before>module.exports = function(r) {
    const dbExec = require('./run');
    const db = require('./db')(r);
    const model = require('./model')(r);
    const commonQueries = require('./common-queries');
    const _ = require('lodash');
    const util = require('./util');

    return {
        getDatasetsForExperiment,
        getDataset,
        createDatasetForExperiment,
        addSampleToDataset,
        updateSamplesInDataset,
        getSamplesForDataset,
        updateFilesInDataset,
        updateProcessesInDataset,
        allSamplesInDataset,
        allFilesInDataset,
        allProcessesInDataset,
        updateDataset,
        publishDataset
    };

    function* getDatasetsForExperiment(experimentId) {
        let rql = commonQueries.datasetDetailsRql(r.table('experiment2dataset').getAll(experimentId, {index: 'experiment_id'})
            .eqJoin('dataset_id', r.table('datasets')).zip(), r);
        let dataset = yield dbExec(rql);
        return {val: dataset};
    }

    function* getDataset(datasetId) {
        let rql = commonQueries.datasetDetailsRql(r.table('datasets').get(datasetId), r);
        let dataset = yield rql.run();
        return {val: dataset};
    }

    function* createDatasetForExperiment(experimentId, userId, datasetArgs) {
        let dataset = new model.Dataset(datasetArgs.title, userId);
        dataset.description = datasetArgs.description;
        let created = yield db.insert('datasets', dataset);
        let e2d = new model.Experiment2Dataset(experimentId, created.id);
        yield r.table('experiment2dataset').insert(e2d);
        return yield getDataset(created.id);
    }

    function* addSampleToDataset(datasetId, sampleId) {
        let d2s = new model.Dataset2Sample(datasetId, sampleId);
        yield r.table('dataset2sample').insert(d2s);
        yield addProcessesForSampleToDataset(datasetId, sampleId);
        return yield getDataset(datasetId);
    }

    function* addProcessesForSampleToDataset(datasetId, sampleId) {
        let processes = yield r.table('process2sample').getAll(sampleId, {index: 'sample_id'});
        let uniqProcesses = uniqByKey(processes, 'process_id');
        let processesToInsert = uniqProcesses.map(p => new model.Dataset2Process(datasetId, p.process_id));
        processesToInsert = yield removeExistingProcessEntries(processesToInsert);
        if (processesToInsert.length) {
            yield r.table('dataset2process').insert(processesToInsert);
        }
    }

    function* removeExistingProcessEntries(processesToAdd) {
        if (processesToAdd.length) {
            let indexEntries = processesToAdd.map(p => [p.dataset_id, p.process_id]);
            let matchingEntries = yield r.table('dataset2process').getAll(r.args(indexEntries), {index: 'dataset_process'});
            let matchingEntriesByProcessId = _.indexBy(matchingEntries, 'process_id');
            return processesToAdd.filter(p => (!(p.process_id in matchingEntriesByProcessId)));
        }
        return processesToAdd;
    }

    function* updateSamplesInDataset(datasetId, samplesToAdd, samplesToDelete) {
        if (samplesToAdd.length) {
            let add = samplesToAdd.map(s => new model.Dataset2Sample(datasetId, s.id));
            add = yield removeExistingSampleEntriesInDataset(add);
            if (add.length) {
                yield r.table('dataset2sample').insert(add);
                yield addProcessesForSamplesToDataset(datasetId, samplesToAdd);
            }
        }

        if (samplesToDelete.length) {
            let toDelete = samplesToDelete.map(s => [datasetId, s.id]);
            yield r.table('dataset2sample').getAll(r.args(toDelete), {index: 'dataset_sample'}).delete();
        }

        return yield getDataset(datasetId);
    }

    function* getSamplesForDataset(datasetId) {
        let samples = yield r.table('dataset2sample').getAll(datasetId, {index: 'dataset_id'})
            .eqJoin('sample_id', r.table('samples')).zip();
        return {val: samples};
    }

    function uniqByKey(items, key) {
        let uniqItems = _.indexBy(items, key);
        return Object.keys(uniqItems).map(key => uniqItems[key]);
    }

    function* addProcessesForSamplesToDataset(datasetId, samples) {
        let sampleIds = samples.map(s => s.id);
        let processes = yield r.table('process2sample').getAll(r.args(sampleIds), {index: 'sample_id'});
        let uniqProcesses = uniqByKey(processes, 'process_id');
        let processesToInsert = uniqProcesses.map(p => new model.Dataset2Process(datasetId, p.process_id));
        processesToInsert = yield removeExistingProcessEntries(processesToInsert);
        if (processesToInsert.length) {
            yield r.table('dataset2process').insert(processesToInsert);
        }
    }

    function* removeExistingSampleEntriesInDataset(samplesToAdd) {
        if (samplesToAdd.length) {
            let indexEntries = samplesToAdd.map(s => [s.dataset_id, s.sample_id]);
            let matchingEntries = yield r.table('dataset2sample').getAll(r.args(indexEntries), {index: 'dataset_sample'});
            let matchingEntriesBySampleId = _.indexBy(matchingEntries, 'sample_id');
            return samplesToAdd.filter(s => (!(s.sample_id in matchingEntriesBySampleId)));
        }

        return samplesToAdd;
    }

    function* updateFilesInDataset(datasetId, filesToAdd, filesToDelete) {
        if (filesToAdd.length) {
            let add = filesToAdd.map(f => new model.Dataset2Datafile(datasetId, f.id));
            let indexEntries = add.map(f => [f.dataset_id, f.datafile_id]);
            let matchingEntries = yield r.table('dataset2datafile').getAll(r.args(indexEntries), {index: 'dataset_datafile'});
            add = util.removeExistingItemsIn(add, matchingEntries, 'datafile_id');
            if (add.length) {
                yield r.table('dataset2datafile').insert(add);
            }
        }

        if (filesToDelete.length) {
            let toDelete = filesToDelete.map(f => [datasetId, f.id]);
            yield r.table('dataset2datafile').getAll(r.args(toDelete), {index: 'dataset_datafile'}).delete();
        }

        return yield getDataset(datasetId);
    }

    function* updateProcessesInDataset(datasetId, processesToAdd, processesToDelete) {
        if (processesToAdd.length) {
            let add = processesToAdd.map(p => new model.Dataset2Process(datasetId, p.id));
            let indexEntries = add.map(p => [p.dataset_id, p.process_id]);
            let matchingEntries = yield r.table('dataset2process').getAll(r.args(indexEntries), {index: 'dataset_process'});
            add = util.removeExistingItemsIn(add, matchingEntries, 'process_id');
            if (add.length) {
                yield r.table('dataset2process').insert(add);
            }
        }

        if (processesToDelete.length) {
            let toDelete = processesToDelete.map(p => [datasetId, p.id]);
            yield r.table('dataset2process').getAll(r.args(toDelete), {index: 'dataset_process'}).delete();
        }

        return yield getDataset(datasetId);
    }

    function* allSamplesInDataset(datasetId, sampleIds) {
        let indexArgs = sampleIds.map(sid => [datasetId, sid]);
        let samples = yield r.table('dataset2sample').getAll(r.args(indexArgs), {index: 'dataset_sample'});
        return samples.length === sampleIds.length;
    }

    function* allFilesInDataset(datasetId, fileIds) {
        let indexArgs = fileIds.map(fid => [datasetId, fid]);
        let files = yield r.table('dataset2datafile').getAll(r.args(indexArgs), {index: 'dataset_datafile'});
        return files.length === fileIds.length;
    }

    function* allProcessesInDataset(datasetId, processIds) {
        let indexArgs = processIds.map(id => [datasetId, id]);
        let processes = yield r.table('dataset2process').getAll(r.args(indexArgs), {index: 'dataset_process'});
        return processes.length === processIds.length;
    }

    function* updateDataset(datasetId, datasetArgs) {
        yield r.table('datasets').get(datasetId).update(datasetArgs);
        return yield getDataset(datasetId);
    }

    function* publishDataset(datasetId) {
        yield publishDatasetProcesses(datasetId);
        yield publishDatasetSamples(datasetId);
        yield publishDatasetFiles(datasetId);
        yield r.table('datasets').get(datasetId).update({published: true});
        return yield getDataset(datasetId);
    }

    function* publishDatasetProcesses(datasetId) {
        let d2pEntries = yield r.table('dataset2process').getAll(datasetId, {index: 'dataset_id'});
        let processIds = d2pEntries.map(entry => entry.process_id);
        let processes = yield r.table('processes').getAll(r.args(processIds));
        processes.forEach(p => {
            p.original_id = p.id;
            delete p['id'];
        });
        let inserted = yield r.db('mcpub').table('processes').insert(processes, {returnChanges: 'always'});
        let d2pToInsert = inserted.changes.map(e => new model.Dataset2Process(datasetId, e.new_val.id));
        let newProcesses = inserted.changes.map(e => e.new_val);
        yield r.db('mcpub').table('dataset2process').insert(d2pToInsert);
        yield publishSetupForProcesses(newProcesses);
    }

    /*
     * publishSetupForProcesses will go through the setup entries and the properties associated with the
     * setup entries. It will create new setup entries from the old setup entries, and then update all the
     * id mappings in the join tables and foreign indexes for the related tables.
     */
    function* publishSetupForProcesses(processes) {
        let originalProcessIds = processes.map(p => p.original_id);
        let p2sEntries = yield r.table('process2setup').getAll(r.args(originalProcessIds), {index: 'process_id'});
        let setupIds = p2sEntries.map(e => e.setup_id);
        let setupEntries = yield r.table('setups').getAll(r.args(setupIds));
        let setupProperties = yield r.table('setupproperties').getAll(r.args(setupIds), {index: 'setup_id'});

        // Insert setupEntries into new database by creating new ids for each setup entry, and update
        // the setupproperties table to use the new ids.
        setupEntries.forEach(e => {
            e.original_id = e.id;
            delete e['id'];
        });
        let insertedSetups = yield r.db('mcpub').table('setups').insert(setupEntries, {returnChanges: 'always'});
        let setupsByOriginalId = _.indexBy(insertedSetups.changes.map(e => e.new_val), 'original_id');

        // Modify setupproperties to point to the new ids for each setup.
        setupProperties.forEach(prop => {
            let setupEntry = setupsByOriginalId[prop.setup_id];
            prop.setup_id = setupEntry.id;
        });
        yield r.db('mcpub').table('setupproperties').insert(setupProperties);

        // Update process2setup to use the new process id and the new setup id
        let processesByOriginalId = _.indexBy(processes, 'original_id');
        p2sEntries.forEach(e => {
            let process = processesByOriginalId[e.process_id];
            let setup = setupsByOriginalId[e.setup_id];
            e.setup_id = setup.id;
            e.process_id = process.id;
        });
        yield r.db('mcpub').table('process2setup').insert(p2sEntries);
    }

    function* publishDatasetSamples(datasetId) {

    }

    function* publishDatasetFiles(datasetId) {

    }
};
<commit_msg>Save partial implementation of samples save for dataset.<commit_after>module.exports = function(r) {
    const dbExec = require('./run');
    const db = require('./db')(r);
    const model = require('./model')(r);
    const commonQueries = require('./common-queries');
    const _ = require('lodash');
    const util = require('./util');

    return {
        getDatasetsForExperiment,
        getDataset,
        createDatasetForExperiment,
        addSampleToDataset,
        updateSamplesInDataset,
        getSamplesForDataset,
        updateFilesInDataset,
        updateProcessesInDataset,
        allSamplesInDataset,
        allFilesInDataset,
        allProcessesInDataset,
        updateDataset,
        publishDataset
    };

    function* getDatasetsForExperiment(experimentId) {
        let rql = commonQueries.datasetDetailsRql(r.table('experiment2dataset').getAll(experimentId, {index: 'experiment_id'})
            .eqJoin('dataset_id', r.table('datasets')).zip(), r);
        let dataset = yield dbExec(rql);
        return {val: dataset};
    }

    function* getDataset(datasetId) {
        let rql = commonQueries.datasetDetailsRql(r.table('datasets').get(datasetId), r);
        let dataset = yield rql.run();
        return {val: dataset};
    }

    function* createDatasetForExperiment(experimentId, userId, datasetArgs) {
        let dataset = new model.Dataset(datasetArgs.title, userId);
        dataset.description = datasetArgs.description;
        let created = yield db.insert('datasets', dataset);
        let e2d = new model.Experiment2Dataset(experimentId, created.id);
        yield r.table('experiment2dataset').insert(e2d);
        return yield getDataset(created.id);
    }

    function* addSampleToDataset(datasetId, sampleId) {
        let d2s = new model.Dataset2Sample(datasetId, sampleId);
        yield r.table('dataset2sample').insert(d2s);
        yield addProcessesForSampleToDataset(datasetId, sampleId);
        return yield getDataset(datasetId);
    }

    function* addProcessesForSampleToDataset(datasetId, sampleId) {
        let processes = yield r.table('process2sample').getAll(sampleId, {index: 'sample_id'});
        let uniqProcesses = uniqByKey(processes, 'process_id');
        let processesToInsert = uniqProcesses.map(p => new model.Dataset2Process(datasetId, p.process_id));
        processesToInsert = yield removeExistingProcessEntries(processesToInsert);
        if (processesToInsert.length) {
            yield r.table('dataset2process').insert(processesToInsert);
        }
    }

    function* removeExistingProcessEntries(processesToAdd) {
        if (processesToAdd.length) {
            let indexEntries = processesToAdd.map(p => [p.dataset_id, p.process_id]);
            let matchingEntries = yield r.table('dataset2process').getAll(r.args(indexEntries), {index: 'dataset_process'});
            let matchingEntriesByProcessId = _.indexBy(matchingEntries, 'process_id');
            return processesToAdd.filter(p => (!(p.process_id in matchingEntriesByProcessId)));
        }
        return processesToAdd;
    }

    function* updateSamplesInDataset(datasetId, samplesToAdd, samplesToDelete) {
        if (samplesToAdd.length) {
            let add = samplesToAdd.map(s => new model.Dataset2Sample(datasetId, s.id));
            add = yield removeExistingSampleEntriesInDataset(add);
            if (add.length) {
                yield r.table('dataset2sample').insert(add);
                yield addProcessesForSamplesToDataset(datasetId, samplesToAdd);
            }
        }

        if (samplesToDelete.length) {
            let toDelete = samplesToDelete.map(s => [datasetId, s.id]);
            yield r.table('dataset2sample').getAll(r.args(toDelete), {index: 'dataset_sample'}).delete();
        }

        return yield getDataset(datasetId);
    }

    function* getSamplesForDataset(datasetId) {
        let samples = yield r.table('dataset2sample').getAll(datasetId, {index: 'dataset_id'})
            .eqJoin('sample_id', r.table('samples')).zip();
        return {val: samples};
    }

    function uniqByKey(items, key) {
        let uniqItems = _.indexBy(items, key);
        return Object.keys(uniqItems).map(key => uniqItems[key]);
    }

    function* addProcessesForSamplesToDataset(datasetId, samples) {
        let sampleIds = samples.map(s => s.id);
        let processes = yield r.table('process2sample').getAll(r.args(sampleIds), {index: 'sample_id'});
        let uniqProcesses = uniqByKey(processes, 'process_id');
        let processesToInsert = uniqProcesses.map(p => new model.Dataset2Process(datasetId, p.process_id));
        processesToInsert = yield removeExistingProcessEntries(processesToInsert);
        if (processesToInsert.length) {
            yield r.table('dataset2process').insert(processesToInsert);
        }
    }

    function* removeExistingSampleEntriesInDataset(samplesToAdd) {
        if (samplesToAdd.length) {
            let indexEntries = samplesToAdd.map(s => [s.dataset_id, s.sample_id]);
            let matchingEntries = yield r.table('dataset2sample').getAll(r.args(indexEntries), {index: 'dataset_sample'});
            let matchingEntriesBySampleId = _.indexBy(matchingEntries, 'sample_id');
            return samplesToAdd.filter(s => (!(s.sample_id in matchingEntriesBySampleId)));
        }

        return samplesToAdd;
    }

    function* updateFilesInDataset(datasetId, filesToAdd, filesToDelete) {
        if (filesToAdd.length) {
            let add = filesToAdd.map(f => new model.Dataset2Datafile(datasetId, f.id));
            let indexEntries = add.map(f => [f.dataset_id, f.datafile_id]);
            let matchingEntries = yield r.table('dataset2datafile').getAll(r.args(indexEntries), {index: 'dataset_datafile'});
            add = util.removeExistingItemsIn(add, matchingEntries, 'datafile_id');
            if (add.length) {
                yield r.table('dataset2datafile').insert(add);
            }
        }

        if (filesToDelete.length) {
            let toDelete = filesToDelete.map(f => [datasetId, f.id]);
            yield r.table('dataset2datafile').getAll(r.args(toDelete), {index: 'dataset_datafile'}).delete();
        }

        return yield getDataset(datasetId);
    }

    function* updateProcessesInDataset(datasetId, processesToAdd, processesToDelete) {
        if (processesToAdd.length) {
            let add = processesToAdd.map(p => new model.Dataset2Process(datasetId, p.id));
            let indexEntries = add.map(p => [p.dataset_id, p.process_id]);
            let matchingEntries = yield r.table('dataset2process').getAll(r.args(indexEntries), {index: 'dataset_process'});
            add = util.removeExistingItemsIn(add, matchingEntries, 'process_id');
            if (add.length) {
                yield r.table('dataset2process').insert(add);
            }
        }

        if (processesToDelete.length) {
            let toDelete = processesToDelete.map(p => [datasetId, p.id]);
            yield r.table('dataset2process').getAll(r.args(toDelete), {index: 'dataset_process'}).delete();
        }

        return yield getDataset(datasetId);
    }

    function* allSamplesInDataset(datasetId, sampleIds) {
        let indexArgs = sampleIds.map(sid => [datasetId, sid]);
        let samples = yield r.table('dataset2sample').getAll(r.args(indexArgs), {index: 'dataset_sample'});
        return samples.length === sampleIds.length;
    }

    function* allFilesInDataset(datasetId, fileIds) {
        let indexArgs = fileIds.map(fid => [datasetId, fid]);
        let files = yield r.table('dataset2datafile').getAll(r.args(indexArgs), {index: 'dataset_datafile'});
        return files.length === fileIds.length;
    }

    function* allProcessesInDataset(datasetId, processIds) {
        let indexArgs = processIds.map(id => [datasetId, id]);
        let processes = yield r.table('dataset2process').getAll(r.args(indexArgs), {index: 'dataset_process'});
        return processes.length === processIds.length;
    }

    function* updateDataset(datasetId, datasetArgs) {
        yield r.table('datasets').get(datasetId).update(datasetArgs);
        return yield getDataset(datasetId);
    }

    function* publishDataset(datasetId) {
        yield publishDatasetProcesses(datasetId);
        yield publishDatasetSamples(datasetId);
        yield publishDatasetFiles(datasetId);
        yield r.table('datasets').get(datasetId).update({published: true});
        return yield getDataset(datasetId);
    }

    function* publishDatasetProcesses(datasetId) {
        let d2pEntries = yield r.table('dataset2process').getAll(datasetId, {index: 'dataset_id'});
        let processIds = d2pEntries.map(entry => entry.process_id);
        let processes = yield r.table('processes').getAll(r.args(processIds));
        processes.forEach(p => {
            p.original_id = p.id;
            delete p['id'];
        });
        let inserted = yield r.db('mcpub').table('processes').insert(processes, {returnChanges: 'always'});
        let d2pToInsert = inserted.changes.map(e => new model.Dataset2Process(datasetId, e.new_val.id));
        let newProcesses = inserted.changes.map(e => e.new_val);
        yield r.db('mcpub').table('dataset2process').insert(d2pToInsert);
        yield publishSetupForProcesses(newProcesses);
    }

    /*
     * publishSetupForProcesses will go through the setup entries and the properties associated with the
     * setup entries. It will create new setup entries from the old setup entries, and then update all the
     * id mappings in the join tables and foreign indexes for the related tables.
     */
    function* publishSetupForProcesses(processes) {
        let originalProcessIds = processes.map(p => p.original_id);
        let p2sEntries = yield r.table('process2setup').getAll(r.args(originalProcessIds), {index: 'process_id'});
        let setupIds = p2sEntries.map(e => e.setup_id);
        let setupEntries = yield r.table('setups').getAll(r.args(setupIds));
        let setupProperties = yield r.table('setupproperties').getAll(r.args(setupIds), {index: 'setup_id'});

        // Insert setupEntries into new database by creating new ids for each setup entry, and update
        // the setupproperties table to use the new ids.
        setupEntries.forEach(e => {
            e.original_id = e.id;
            delete e['id'];
        });
        let insertedSetups = yield r.db('mcpub').table('setups').insert(setupEntries, {returnChanges: 'always'});
        let setupsByOriginalId = _.indexBy(insertedSetups.changes.map(e => e.new_val), 'original_id');

        // Modify setupproperties to point to the new ids for each setup.
        setupProperties.forEach(prop => {
            let setupEntry = setupsByOriginalId[prop.setup_id];
            prop.setup_id = setupEntry.id;
        });
        yield r.db('mcpub').table('setupproperties').insert(setupProperties);

        // Update process2setup to use the new process id and the new setup id
        let processesByOriginalId = _.indexBy(processes, 'original_id');
        p2sEntries.forEach(e => {
            let process = processesByOriginalId[e.process_id];
            let setup = setupsByOriginalId[e.setup_id];
            e.setup_id = setup.id;
            e.process_id = process.id;
        });
        yield r.db('mcpub').table('process2setup').insert(p2sEntries);
    }

    function* publishDatasetSamples(datasetId) {
        let ds2sEntries = yield r.table('dataset2sample').getAll(datasetId, {index: 'dataset_id'});
        let sampleIds = ds2sEntries.map(entry => entry.sample_id);
        let samples = yield r.table('samples').getAll(r.args(sampleIds));
        samples.forEach(s => {
            s.original_id = s.id;
            delete s['id'];
        });
        let insertedSamples = yield r.db('mcpub').table('samples').insert(samples, {returnChanges: 'always'});

    }

    function* publishDatasetFiles(datasetId) {

    }
};
<|endoftext|>


<commit_before>function OnScreen(options) {
	var self = this;

	var defaults = {
		scrollContainer: [document.body, document.documentElement, window],
		scroll: true,
		resize: true,
		load: true
	};

	self.options = mergeObjects(defaults, options || {});
	self.animFrame = null;
	self.items = [];
	self.map = {};
	self.update = update;
	self.util = {
		mergeObjects: mergeObjects
	};

	if (self.options.scroll) {
		self.options.scrollContainer.forEach(function(container) {
			container.addEventListener('scroll', update);
		});
	}

	if (self.options.resize) {
		window.addEventListener('resize', update);
	}

	if (self.options.load) {
		window.addEventListener('load', update);
	}

	function update() {
		if (!self.animFrame && self.items.length) {
			self.animFrame = requestAnimationFrame(process);
		}
	}

	function process() {
		self.animFrame = null;

		self.items.forEach(function (item) {

			var element = item.element;
			var options = item.options;

			var sideBefore = item.side;
			var isOnScreen = check(item);
			var sideAfter = item.side;
			var offset = item.offset;
			var detail = { side: isOnScreen ? sideBefore : sideAfter, offset: offset, data: options.data };

			var classes = element.getAttribute('class').match(new RegExp('\\b(' + options.screenEnterClass + '|' + options.screenLeaveClass + ')(-(-(top|bottom|left|right)){0,2})*', 'g')) || [];

			// Screen Enter
			if (isOnScreen) {

				// make sure to only fire once
				if (!item.onscreen) {
					item.onscreen = true;

					if (!dispatchEvent(element, 'screenenter', detail)) {
						return;
					}

					if (options.onScreenEnter && options.onScreenEnter.call(element, detail) === false) {
						return
					}

					classes.forEach(function(item){
						element.classList.remove(item);
					});

					element.classList.add(options.screenEnterClass);
					element.classList.add(options.screenEnterClass + '--' + sideBefore);
				}
			}

			// Screen Leave
			else {

				// make sure to only fire once
				if (item.onscreen) {
					item.onscreen = false;

					if (!dispatchEvent(element, 'screenleave', detail)) {
						return;
					}

					if (options.onScreenLeave && options.onScreenLeave.call(element, detail) === false) {
						return;
					}

					classes.forEach(function(item){
						element.classList.remove(item);
					});

					element.classList.add(options.screenLeaveClass);
					element.classList.add(options.screenLeaveClass + '--' + sideAfter);
				}
			}

			// Screen Move
			if (!options.disableScreenMove && (isOnScreen || options.fireScreenMoveOffScreen)) {

				if (!dispatchEvent(element, 'screenmove', detail)) {
					return;
				}

				if (options.onScreenMove) {
					options.onScreenMove.call(element, detail);
				}
			}
		});
	}

	function check(item) {
		var screenBounds = {
			top: 0,
			right: window.innerWidth,
			bottom: window.innerHeight,
			left: 0,
			width: window.innerWidth,
			height: window.innerHeight
		};

		var targetBounds = item.element.getBoundingClientRect();

		var screenRect = getModifiedRect(screenBounds, item.options.screen);
		var targetRect = getModifiedRect(targetBounds, item.options.target);

		var offset = {
			top: (targetRect.top - screenRect.top) / screenRect.height,
			right: -(targetRect.right - screenRect.right) / screenRect.width,
			bottom: -(targetRect.bottom - screenRect.bottom) / screenRect.height,
			left: (targetRect.left - screenRect.left) / screenRect.width
		};

		var side = [];

		// check side: top / bottom
		if (targetRect.bottom <= screenRect.top) {
			side[side.length] = 'top';
		}
		else if (targetRect.top >= screenRect.bottom) {
			side[side.length] = 'bottom';
		}

		// check side: left / right
		if (targetRect.right <= screenRect.left) {
			side[side.length] = 'left';
		}
		else if (targetRect.left >= screenRect.right) {
			side[side.length] = 'right';
		}

		// set offset
		item.offset = offset;

		// set side and return visibility
		if (side.length) {
			item.side = side.join('-');
			return false;
		}
		else {
			return true;
		}
	}

	function getModifiedRect(rect, mods) {
		var width = rect.width || (rect.right - rect.left);
		var height = rect.height || (rect.bottom - rect.top);
		var modRect = {};

		mods = mods || {};

		modRect.top = rect.top + (mods.top ? getModifierValue(mods.top, height) : 0);
		modRect.right = rect.right - (mods.right ? getModifierValue(mods.right, width) : 0);
		modRect.bottom = rect.bottom - (mods.bottom ? getModifierValue(mods.bottom, height) : 0);
		modRect.left = rect.left + (mods.left ? getModifierValue(mods.left, width) : 0);
		modRect.width = rect.right - rect.left;
		modRect.height = rect.bottom - rect.top;

		return modRect;
	}

	function getModifierValue(modifier, range) {
		return /\d%$/.test(modifier) ? (parseFloat(modifier) / 100) * range : parseFloat(modifier || 0);
	}

	function dispatchEvent(element, name, data) {
		var event;

		if (typeof CustomEvent === 'function') {
			event = new CustomEvent(name, {
				detail: data,
				bubbles: true,
				cancelable: true
			});
		}
		else {
			event = document.createEvent('CustomEvent');
			event.initCustomEvent(name, true, true, data);
		}

		return !!element.dispatchEvent(event);
	}

	function mergeObjects() {
		var destination = {};

		[].forEach.call(arguments, function(source) {
			for (var property in source) {
				if (source.hasOwnProperty(property)) {
					if (source[property] && {}.toString.call(source[property]) === '[object Object]') {
						destination[property] = mergeObjects(destination[property] || {}, source[property]);
					} else {
						destination[property] = source[property];
					}
				}
			}
		});

		return destination;
	};
}

OnScreen.prototype.addItem = function(element, options) {
	var defaults = {
		screen: { top: 0, right: 0, bottom: 0, left: 0 },
		target: { top: 0, right: 0, bottom: 0, left: 0 },
		screenEnterClass: 'js-screenenter',
		screenLeaveClass: 'js-screenleave',
		onScreenEnter: null,
		onScreenLeave: null,
		onScreenMove: null,
		fireScreenMoveOffScreen: false,
		disableScreenMove: false
	};

	var index = this.items.length;
	this.items[index] = { element: element, options: this.util.mergeObjects(defaults, options || {}), onscreen: false, side: 'bottom' };
	this.map[element] = index;

	return this.items[index];
};

OnScreen.prototype.updateItem = function(element, options) {
	var index = parseInt(this.map[element]);
	if (index >= 0 && index < this.items.length && this.items[index]) {
		this.items[index].options = options;
	}

	return this.items[index];
};

OnScreen.prototype.removeItem = function(element) {
	var index = parseInt(this.map[element]);
	if (index >= 0 && index < this.items.length) {
		this.items.splice(index, 1);
		this.map[element] = null;
		delete this.map[element];
	}
};

OnScreen.prototype.empty = function() {
	this.items = [];
	this.map = {};
};

OnScreen.prototype.dispose = function() {
	this.empty();

	if (this.options.scroll) {
		this.options.scrollContainer.forEach(function(container) {
			container.removeEventListener('scroll', this.update);
		}, this);
	}

	if (this.options.resize) {
		window.removeEventListener('resize', this.update);
	}

	if (this.options.load) {
		window.removeEventListener('load', this.update);
	}
};

<commit_msg>Minor optimisations<commit_after>function OnScreen(options) {
	var self = this;

	var defaults = {
		scrollContainer: [document.body, document.documentElement, window],
		scroll: true,
		resize: true,
		load: true
	};

	self.options = mergeObjects(defaults, options || {});
	self.animFrame = null;
	self.items = [];
	self.map = {};
	self.update = update;
	self.util = {
		mergeObjects: mergeObjects
	};

	if (self.options.scroll) {
		self.options.scrollContainer.forEach(function(container) {
			container.addEventListener('scroll', update);
		});
	}

	if (self.options.resize) {
		window.addEventListener('resize', update);
	}

	if (self.options.load) {
		window.addEventListener('load', update);
	}

	function update() {
		if (!self.animFrame && self.items.length) {
			self.animFrame = requestAnimationFrame(process);
		}
	}

	function process() {
		self.animFrame = null;

		self.items.forEach(function (item) {

			var element = item.element;
			var options = item.options;

			var sideBefore = item.side;
			var isOnScreen = check(item);
			var sideAfter = item.side;
			var offset = item.offset;
			var detail = { side: isOnScreen ? sideBefore : sideAfter, offset: offset, data: options.data };

			var classes = element.getAttribute('class').match(new RegExp('\\b(' + options.screenEnterClass + '|' + options.screenLeaveClass + ')(-(-(top|bottom|left|right)){0,2})*', 'g')) || [];

			// Screen Enter
			if (isOnScreen) {

				// make sure to only fire once
				if (!item.onscreen) {
					item.onscreen = true;

					if (!dispatchEvent(element, 'screenenter', detail) ||
						(options.onScreenEnter && options.onScreenEnter.call(element, detail) === false)) {
						return
					}

					classes.forEach(function(item){
						element.classList.remove(item);
					});

					element.classList.add(options.screenEnterClass);
					element.classList.add(options.screenEnterClass + '--' + sideBefore);
				}
			}

			// Screen Leave
			else {

				// make sure to only fire once
				if (item.onscreen) {
					item.onscreen = false;

					if (!dispatchEvent(element, 'screenleave', detail) ||
						(options.onScreenLeave && options.onScreenLeave.call(element, detail) === false)) {
						return;
					}

					classes.forEach(function(item){
						element.classList.remove(item);
					});

					element.classList.add(options.screenLeaveClass);
					element.classList.add(options.screenLeaveClass + '--' + sideAfter);
				}
			}

			// Screen Move
			if (!options.disableScreenMove && (isOnScreen || options.fireScreenMoveOffScreen)) {

				if (!dispatchEvent(element, 'screenmove', detail)) {
					return;
				}

				if (options.onScreenMove) {
					options.onScreenMove.call(element, detail);
				}
			}
		});
	}

	function check(item) {
		var screenBounds = {
			top: 0,
			right: window.innerWidth,
			bottom: window.innerHeight,
			left: 0,
			width: window.innerWidth,
			height: window.innerHeight
		};

		var targetBounds = item.element.getBoundingClientRect();

		var screenRect = getModifiedRect(screenBounds, item.options.screen);
		var targetRect = getModifiedRect(targetBounds, item.options.target);

		var offset = {
			top: (targetRect.top - screenRect.top) / screenRect.height,
			right: -(targetRect.right - screenRect.right) / screenRect.width,
			bottom: -(targetRect.bottom - screenRect.bottom) / screenRect.height,
			left: (targetRect.left - screenRect.left) / screenRect.width
		};

		var side = [];

		// check side: top / bottom
		if (targetRect.bottom <= screenRect.top) {
			side[side.length] = 'top';
		}
		else if (targetRect.top >= screenRect.bottom) {
			side[side.length] = 'bottom';
		}

		// check side: left / right
		if (targetRect.right <= screenRect.left) {
			side[side.length] = 'left';
		}
		else if (targetRect.left >= screenRect.right) {
			side[side.length] = 'right';
		}

		// set offset
		item.offset = offset;

		// set side and return visibility
		if (side.length) {
			item.side = side.join('-');
			return false;
		}
		else {
			return true;
		}
	}

	function getModifiedRect(rect, mods) {
		var width = rect.width || (rect.right - rect.left);
		var height = rect.height || (rect.bottom - rect.top);
		var modRect = {};

		mods = mods || {};

		modRect.top = rect.top + (mods.top ? getModifierValue(mods.top, height) : 0);
		modRect.right = rect.right - (mods.right ? getModifierValue(mods.right, width) : 0);
		modRect.bottom = rect.bottom - (mods.bottom ? getModifierValue(mods.bottom, height) : 0);
		modRect.left = rect.left + (mods.left ? getModifierValue(mods.left, width) : 0);
		modRect.width = rect.right - rect.left;
		modRect.height = rect.bottom - rect.top;

		return modRect;
	}

	function getModifierValue(modifier, range) {
		return /\d%$/.test(modifier) ? (parseFloat(modifier) / 100) * range : parseFloat(modifier || 0);
	}

	function dispatchEvent(element, name, data) {
		var event;

		if (typeof CustomEvent === 'function') {
			event = new CustomEvent(name, {
				detail: data,
				bubbles: true,
				cancelable: true
			});
		}
		else {
			event = document.createEvent('CustomEvent');
			event.initCustomEvent(name, true, true, data);
		}

		return !!element.dispatchEvent(event);
	}

	function mergeObjects() {
		var destination = {};

		[].forEach.call(arguments, function(source) {
			for (var property in source) {
				if (source.hasOwnProperty(property)) {
					if (source[property] && {}.toString.call(source[property]) === '[object Object]') {
						destination[property] = mergeObjects(destination[property] || {}, source[property]);
					} else {
						destination[property] = source[property];
					}
				}
			}
		});

		return destination;
	};
}

OnScreen.prototype.addItem = function(element, options) {
	var defaults = {
		screen: { top: 0, right: 0, bottom: 0, left: 0 },
		target: { top: 0, right: 0, bottom: 0, left: 0 },
		screenEnterClass: 'js-screenenter',
		screenLeaveClass: 'js-screenleave',
		onScreenEnter: null,
		onScreenLeave: null,
		onScreenMove: null,
		fireScreenMoveOffScreen: false,
		disableScreenMove: false
	};

	var index = this.items.length;
	this.items[index] = { element: element, options: this.util.mergeObjects(defaults, options || {}), onscreen: false, side: 'bottom' };
	this.map[element] = index;

	return this.items[index];
};

OnScreen.prototype.updateItem = function(element, options) {
	var index = parseInt(this.map[element]);
	if (index >= 0 && index < this.items.length && this.items[index]) {
		this.items[index].options = options;
	}

	return this.items[index];
};

OnScreen.prototype.removeItem = function(element) {
	var index = parseInt(this.map[element]);
	if (index >= 0 && index < this.items.length) {
		this.items.splice(index, 1);
		this.map[element] = null;
		delete this.map[element];
	}
};

OnScreen.prototype.empty = function() {
	this.items = [];
	this.map = {};
};

OnScreen.prototype.dispose = function() {
	this.empty();

	if (this.options.scroll) {
		this.options.scrollContainer.forEach(function(container) {
			container.removeEventListener('scroll', this.update);
		}, this);
	}

	if (this.options.resize) {
		window.removeEventListener('resize', this.update);
	}

	if (this.options.load) {
		window.removeEventListener('load', this.update);
	}
};

<|endoftext|>


<commit_before>/*! backbone-sorted-collection */
;(function (root) { function moduleDefinition(Backbone, _) {

function Sorted(superset) {
  // Save a reference to the original collection
  this._superset = superset;

  // The idea is to keep an internal backbone collection with the paginated
  // set, and expose limited functionality.
  this._collection = new Backbone.Collection(superset.toArray());

  // A drawback is that we will have to update the length ourselves
  // every time we modify this collection.
  this.length = this._collection.length;
}

var methods = {

  sortBy: function(comparator) {

  },

  reverseSort: function() {

  }

};

// Methods on `this._collection` we will expose to the outside world
var collectionMethods = [
  'toJSON', 'forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
  'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
  'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
  'max', 'min', 'groupBy', 'sortedIndex', 'shuffle', 'toArray', 'size',
  'first', 'head', 'take', 'initial', 'rest', 'tail', 'drop', 'last',
  'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'chain', 'pluck',
  'findWhere', 'get', 'at', 'slice', 'where', 'findWhere'
];

_.each(collectionMethods, function(method) {
  methods[method] = function() {
    return Backbone.Collection.prototype[method].apply(this._collection, arguments);
  };
});

// Build up the prototype
_.extend(Sorted.prototype, methods, Backbone.Events);

return Sorted;

// ---------------------------------------------------------------------------
} if (typeof exports === 'object') {
  // node export
  module.exports = moduleDefinition(require('backbone'), require('underscore'));
} else if (typeof define === 'function' && define.amd) {
  // amd anonymous module registration
  define(['backbone', 'underscore'], moduleDefinition);
} else {
  // browser global
  root.SortedCollection = moduleDefinition(root.Backbone, root._);
}}(this));

<commit_msg>Tests are now passing<commit_after>/*! backbone-sorted-collection */
;(function (root) { function moduleDefinition(Backbone, _) {

function onAdd(model) {
  var index = this._collection.sortedIndex(model, this._comparator);
  this._collection.add(model, { at: index });
  this.length = this._collection.length;
}

function onRemove(model) {
  if (this.contains(model)) {
    this._collection.remove(model);
  }
  this.length = this._collection.length;
}

function onChange(model) {
  this._collection.remove(model);
  onAdd.call(this, model);
}

function sort() {
  if (!this._comparator) {
    this._collection.reset(this._superset.toArray());
    return;
  }

  var newOrder = this._superset.sortBy(this._comparator);
  this._collection.reset(this._reverse ? newOrder.reverse() : newOrder);
  this.length = this._collection.length;
}

function pipeEvents() {
  var args = _.toArray(arguments);

  // replace any references to `this._collection` with `this`
  for (var i = 1; i < args.length; i++) {
    // Is there a better way to check for this?
    // List all of the possible events?
    if (args[i].models && args[i].models.length === this._collection.models.length) {
      args[i] = this;
    }
  }

  this.trigger.apply(this, args);
}

function Sorted(superset) {
  // Save a reference to the original collection
  this._superset = superset;

  // The idea is to keep an internal backbone collection with the paginated
  // set, and expose limited functionality.
  this._collection = new Backbone.Collection(superset.toArray());
  this.length = this._collection.length;
  this._reverse = false;
  this._comparator = null;

  this.listenTo(this._superset, 'add', onAdd);
  this.listenTo(this._superset, 'remove', onRemove);
  this.listenTo(this._superset, 'change', onChange);
  this.listenTo(this._superset, 'reset', sort);
  this.listenTo(this._collection, 'all', pipeEvents);
}

var methods = {

  sortBy: function(comparator) {
    this._reverse = false;
    this._comparator = comparator;

    sort.call(this);

    if (!comparator) {
      this.trigger('sorted:remove');
    } else {
      this.trigger('sorted:add');
    }

    return this;
  },

  reverseSort: function() {
    this._reverse = !this._reverse;
    sort.call(this);

    return this;
  },

  superset: function() {
    return this._superset;
  }

};

// Methods on `this._collection` we will expose to the outside world
var collectionMethods = [
  'toJSON', 'forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
  'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
  'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
  'max', 'min', 'groupBy', 'sortedIndex', 'shuffle', 'toArray', 'size',
  'first', 'head', 'take', 'initial', 'rest', 'tail', 'drop', 'last',
  'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'chain', 'pluck',
  'findWhere', 'get', 'at', 'slice', 'where', 'findWhere'
];

_.each(collectionMethods, function(method) {
  methods[method] = function() {
    return Backbone.Collection.prototype[method].apply(this._collection, arguments);
  };
});

// Build up the prototype
_.extend(Sorted.prototype, methods, Backbone.Events);

return Sorted;

// ---------------------------------------------------------------------------
} if (typeof exports === 'object') {
  // node export
  module.exports = moduleDefinition(require('backbone'), require('underscore'));
} else if (typeof define === 'function' && define.amd) {
  // amd anonymous module registration
  define(['backbone', 'underscore'], moduleDefinition);
} else {
  // browser global
  root.SortedCollection = moduleDefinition(root.Backbone, root._);
}}(this));

<|endoftext|>


<commit_before>
var BaseCccComponent = BaseComponent.extend({

    query: null,
    chart: null,

    exportChart: function(outputType, overrides) {

        var _exportIframe = null;

        // We need the same parameters passed here
        var myself = this;

        var buildChartDefinition = function(overrides) {

            overrides = overrides || {};
            var chartDefinition = {};

            var _params = Dashboards.objectToPropertiesArray( $.extend({},Dashboards.propertiesArrayToObject(myself.parameters), overrides) )

            for (var param in _params) {
                if(myself.parameters.hasOwnProperty(param)) {
                    var value;
                    var name = myself.parameters[param][0];
                    value = Dashboards.getParameterValue(myself.parameters[param][1]);
                    if($.isArray(value) && value.length == 1 && ('' + value[0]).indexOf(';') >= 0){
                        //special case where single element will wrongly be treated as a parseable array by cda
                        value = doCsvQuoting(value[0],';');
                    }
                    //else will not be correctly handled for functions that return arrays
                    if (typeof value == 'function') value = value();
                    chartDefinition['param' + name] = value;
                }
            }

            var scriptName =  myself.name.replace(/render_/,"");
            chartDefinition.script = ("/"+ Dashboards.context.solution + "/" + Dashboards.context.path + "/" + scriptName +".js").replace(/\/+/g,'/') ;
            chartDefinition.attachmentName = scriptName;
            return chartDefinition;
        };

        var chartDefinition = buildChartDefinition(overrides);
        chartDefinition.outputType = outputType;

        _exportIframe = _exportIframe || $('');
        _exportIframe.detach();
        _exportIframe[0].src = "../cgg/draw?" + $.param(chartDefinition);
        _exportIframe.appendTo($('body'));
    }

})

var CccComponent = BaseCccComponent.extend({

    query: null,
    chart: null,

    update : function() {
        if (this.parameters == undefined) {
            this.parameters = [];
        }

        // clear previous table
        var ph = $("#"+this.htmlObject).empty();
        var myself = this;

        // Set up defaults for height and width
        if(typeof(this.chartDefinition.width) === "undefined")
            this.chartDefinition.width = ph.width();

        if(typeof(this.chartDefinition.height) === "undefined")
            this.chartDefinition.height = ph.height();

        if (Modernizr != undefined && Modernizr.svg) {
            this.renderChart();
        } else {
            pv.listenForPageLoad(function() {
                myself.renderChart();
            });
        }
    },

    renderChart: function() {
        var myself = this;
        if(this.chartDefinition.dataAccessId || myself.chartDefinition.query){

            this.query = new Query(this.chartDefinition);

            this.query.fetchData(this.parameters, function(values) {
                var changedValues = undefined;
                if((typeof(myself.postFetch)=='function')){
                    changedValues = myself.postFetch(values);
                    $("#" + this.htmlObject).append('

');
                }
                if (changedValues != undefined) {
                    values = changedValues;
                }
                myself.render(values);
            });

        }
        else if(this.valuesArray != undefined){
            this.render(this.valuesArray);
        }
        else{
            // initialize the component only
            this.render();
        }
    },

    render: function(values) {

        $("#" + this.htmlObject).append('

');

        var o = $.extend({},this.chartDefinition);
        o.canvas = this.htmlObject+'protovis';
        // Extension points
        if(typeof o.extensionPoints != "undefined"){
            var ep = {};
            o.extensionPoints.forEach(function(a){
                ep[a[0]]=a[1];
            });
            o.extensionPoints=ep;
        }
        this.chart =  new this.cccType(o);
        if(arguments.length > 0){
            this.chart.setData(values,{
                crosstabMode: this.crosstabMode,
                seriesInRows: this.seriesInRows
            });
        }
        this.chart.render();
    }

});

/*
 *   Modified version of CccComponent which loads 2 datasources.
 */
var CccComponent2 = BaseCccComponent.extend({

    query: null,
    sQuery: null,  // second datasource
    chart: null,

    update : function() {

        var dataQuery = null, sDataQuery = null;

        if (this.parameters == undefined) {
            this.parameters = [];
        };

        // clear previous table
        $("#"+this.htmlObject).empty();
        var myself = this;

        this.query = new Query(this.chartDefinition);

        this.sQuery = new Query({
            path: this.chartDefinition.path,
            dataAccessId: this.chartDefinition.structDatasource
        });

        var executed = false;
        var execComponent = function() {

            if (   ( dataQuery != null)
                && (sDataQuery != null)
                && !executed) {

                myself.render(dataQuery, sDataQuery);
                executed = true;   // safety in case both queries return
            // simultaneously (is this possible in single-threaded Javascript?)
            }

        };

        pv.listenForPageLoad(function() {
            myself.query.fetchData(myself.parameters, function(values) {
                // why is changedValues a GLOBAL ??  potential conflicts!!
                var changedValues = undefined;
                if((typeof(myself.postFetch)=='function')){
                    changedValues = myself.postFetch(values);
                    $("#" + this.htmlObject).append('

');
                }
                if (changedValues != undefined) {
                    values = changedValues;
                }

                dataQuery = values;
                execComponent();
            });
        });

        // load the second query (in parallel)
        pv.listenForPageLoad(function() {
            myself.sQuery.fetchData(myself.parameters, function(values) {
                var changedValues = undefined;
                if((typeof(myself.postFetch)=='function')){
                    changedValues = myself.postFetch(values);
                    $("#" + this.htmlObject).append('

');
                }
                if (changedValues != undefined) {
                    values = changedValues;
                }

                sDataQuery = values;
                execComponent();
            });
        });
    },

    render: function(values, sValues) {

        $("#" + this.htmlObject).append('

');

        var o = $.extend({},this.chartDefinition);
        o.canvas = this.htmlObject+'protovis';
        // Extension points
        if(typeof o.extensionPoints != "undefined"){
            var ep = {};
            o.extensionPoints.forEach(function(a){
                ep[a[0]]=a[1];
            });
            o.extensionPoints=ep;
        }
        this.chart =  new this.cccType(o);
        this.chart.setData(values,{
            crosstabMode: this.crosstabMode,
            seriesInRows: this.seriesInRows
        });

        this.chart.setStructData(sValues)
        this.chart.render();
    }

});

var CccDotChartComponent = CccComponent.extend({

    cccType: pvc.DotChart

});

var CccLineChartComponent = CccComponent.extend({

    cccType: pvc.LineChart

});

var CccStackedLineChartComponent = CccComponent.extend({

    cccType: pvc.StackedLineChart

});

var CccStackedAreaChartComponent = CccComponent.extend({

    cccType: pvc.StackedAreaChart

});

var CccBarChartComponent = CccComponent.extend({

    cccType: pvc.BarChart

});

var CccPieChartComponent = CccComponent.extend({

    cccType: pvc.PieChart

});

var CccHeatGridChartComponent = CccComponent.extend({

    cccType: pvc.HeatGridChart

});

var CccBulletChartComponent = CccComponent.extend({

    cccType: pvc.BulletChart

});

var CccWaterfallChartComponent = CccComponent.extend({

    cccType: pvc.WaterfallChart

});

var CccMetricDotChartComponent = CccComponent.extend({

    cccType: pvc.MetricDotChart

});

var CccMetricLineChartComponent = CccComponent.extend({

    cccType: pvc.MetricLineChart

});

var CccParCoordComponent = CccComponent.extend({

    cccType: pvc.ParallelCoordinates

});

var CccDataTreeComponent = CccComponent2.extend({

    cccType: pvc.DataTree

});

var CccBoxplotChartComponent = CccComponent.extend({

    cccType: pvc.BoxplotChart

});

<commit_msg>Added "ProtovisComponent" class.<commit_after>var ProtovisComponent = BaseComponent.extend({

    update : function() {
      if (this.parameters == undefined) {

        this.parameters = [];

      };

      // clear previous table

      $("#"+this.htmlObject).empty();

      var myself = this;

      Dashboards.fetchData(this.chartDefinition, this.parameters,

                            function(values) {

                                changedData = undefined;

                                if((typeof(myself.postFetch)=='function')){

                                    changedData = myself.postFetch(values);

                                };

                                if (changedData != undefined) {

                                  values = changedData;

                                };

                                myself.render(values);

                            });

    },

    render: function(values) {

      $("#" + this.htmlObject).append('

');

      var vis = new pv.Panel()

        .canvas(this.htmlObject + "protovis")

        .width(this.width)

        .height(this.height);

      this.customfunction(vis,values);

      vis.root.render();

      //vis.canvas(this.htmlObject + "image");

      //if (this.caption != undefined)

            //this.buildCaptionWrapper($("#" + this.htmlObject + "protovis"),"");

    },

    processdata: function(values) {

      this.render(values);

    }

});

var BaseCccComponent = BaseComponent.extend({

    query: null,
    chart: null,

    exportChart: function(outputType, overrides) {

        var _exportIframe = null;

        // We need the same parameters passed here
        var myself = this;

        var buildChartDefinition = function(overrides) {

            overrides = overrides || {};
            var chartDefinition = {};

            var _params = Dashboards.objectToPropertiesArray( $.extend({},Dashboards.propertiesArrayToObject(myself.parameters), overrides) )

            for (var param in _params) {
                if(myself.parameters.hasOwnProperty(param)) {
                    var value;
                    var name = myself.parameters[param][0];
                    value = Dashboards.getParameterValue(myself.parameters[param][1]);
                    if($.isArray(value) && value.length == 1 && ('' + value[0]).indexOf(';') >= 0){
                        //special case where single element will wrongly be treated as a parseable array by cda
                        value = doCsvQuoting(value[0],';');
                    }
                    //else will not be correctly handled for functions that return arrays
                    if (typeof value == 'function') value = value();
                    chartDefinition['param' + name] = value;
                }
            }

            var scriptName =  myself.name.replace(/render_/,"");
            chartDefinition.script = ("/"+ Dashboards.context.solution + "/" + Dashboards.context.path + "/" + scriptName +".js").replace(/\/+/g,'/') ;
            chartDefinition.attachmentName = scriptName;
            return chartDefinition;
        };

        var chartDefinition = buildChartDefinition(overrides);
        chartDefinition.outputType = outputType;

        _exportIframe = _exportIframe || $('');
        _exportIframe.detach();
        _exportIframe[0].src = "../cgg/draw?" + $.param(chartDefinition);
        _exportIframe.appendTo($('body'));
    }

})

var CccComponent = BaseCccComponent.extend({

    query: null,
    chart: null,

    update : function() {
        if (this.parameters == undefined) {
            this.parameters = [];
        }

        // clear previous table
        var ph = $("#"+this.htmlObject).empty();
        var myself = this;

        // Set up defaults for height and width
        if(typeof(this.chartDefinition.width) === "undefined")
            this.chartDefinition.width = ph.width();

        if(typeof(this.chartDefinition.height) === "undefined")
            this.chartDefinition.height = ph.height();

        if (Modernizr != undefined && Modernizr.svg) {
            this.renderChart();
        } else {
            pv.listenForPageLoad(function() {
                myself.renderChart();
            });
        }
    },

    renderChart: function() {
        var myself = this;
        if(this.chartDefinition.dataAccessId || myself.chartDefinition.query){

            this.query = new Query(this.chartDefinition);

            this.query.fetchData(this.parameters, function(values) {
                var changedValues = undefined;
                if((typeof(myself.postFetch)=='function')){
                    changedValues = myself.postFetch(values);
                    $("#" + this.htmlObject).append('

');
                }
                if (changedValues != undefined) {
                    values = changedValues;
                }
                myself.render(values);
            });

        }
        else if(this.valuesArray != undefined){
            this.render(this.valuesArray);
        }
        else{
            // initialize the component only
            this.render();
        }
    },

    render: function(values) {

        $("#" + this.htmlObject).append('

');

        var o = $.extend({},this.chartDefinition);
        o.canvas = this.htmlObject+'protovis';
        // Extension points
        if(typeof o.extensionPoints != "undefined"){
            var ep = {};
            o.extensionPoints.forEach(function(a){
                ep[a[0]]=a[1];
            });
            o.extensionPoints=ep;
        }
        this.chart =  new this.cccType(o);
        if(arguments.length > 0){
            this.chart.setData(values,{
                crosstabMode: this.crosstabMode,
                seriesInRows: this.seriesInRows
            });
        }
        this.chart.render();
    }

});

/*
 *   Modified version of CccComponent which loads 2 datasources.
 */
var CccComponent2 = BaseCccComponent.extend({

    query: null,
    sQuery: null,  // second datasource
    chart: null,

    update : function() {

        var dataQuery = null, sDataQuery = null;

        if (this.parameters == undefined) {
            this.parameters = [];
        };

        // clear previous table
        $("#"+this.htmlObject).empty();
        var myself = this;

        this.query = new Query(this.chartDefinition);

        this.sQuery = new Query({
            path: this.chartDefinition.path,
            dataAccessId: this.chartDefinition.structDatasource
        });

        var executed = false;
        var execComponent = function() {

            if (   ( dataQuery != null)
                && (sDataQuery != null)
                && !executed) {

                myself.render(dataQuery, sDataQuery);
                executed = true;   // safety in case both queries return
            // simultaneously (is this possible in single-threaded Javascript?)
            }

        };

        pv.listenForPageLoad(function() {
            myself.query.fetchData(myself.parameters, function(values) {
                // why is changedValues a GLOBAL ??  potential conflicts!!
                var changedValues = undefined;
                if((typeof(myself.postFetch)=='function')){
                    changedValues = myself.postFetch(values);
                    $("#" + this.htmlObject).append('

');
                }
                if (changedValues != undefined) {
                    values = changedValues;
                }

                dataQuery = values;
                execComponent();
            });
        });

        // load the second query (in parallel)
        pv.listenForPageLoad(function() {
            myself.sQuery.fetchData(myself.parameters, function(values) {
                var changedValues = undefined;
                if((typeof(myself.postFetch)=='function')){
                    changedValues = myself.postFetch(values);
                    $("#" + this.htmlObject).append('

');
                }
                if (changedValues != undefined) {
                    values = changedValues;
                }

                sDataQuery = values;
                execComponent();
            });
        });
    },

    render: function(values, sValues) {

        $("#" + this.htmlObject).append('

');

        var o = $.extend({},this.chartDefinition);
        o.canvas = this.htmlObject+'protovis';
        // Extension points
        if(typeof o.extensionPoints != "undefined"){
            var ep = {};
            o.extensionPoints.forEach(function(a){
                ep[a[0]]=a[1];
            });
            o.extensionPoints=ep;
        }
        this.chart =  new this.cccType(o);
        this.chart.setData(values,{
            crosstabMode: this.crosstabMode,
            seriesInRows: this.seriesInRows
        });

        this.chart.setStructData(sValues)
        this.chart.render();
    }

});

var CccDotChartComponent = CccComponent.extend({

    cccType: pvc.DotChart

});

var CccLineChartComponent = CccComponent.extend({

    cccType: pvc.LineChart

});

var CccStackedLineChartComponent = CccComponent.extend({

    cccType: pvc.StackedLineChart

});

var CccStackedAreaChartComponent = CccComponent.extend({

    cccType: pvc.StackedAreaChart

});

var CccBarChartComponent = CccComponent.extend({

    cccType: pvc.BarChart

});

var CccPieChartComponent = CccComponent.extend({

    cccType: pvc.PieChart

});

var CccHeatGridChartComponent = CccComponent.extend({

    cccType: pvc.HeatGridChart

});

var CccBulletChartComponent = CccComponent.extend({

    cccType: pvc.BulletChart

});

var CccWaterfallChartComponent = CccComponent.extend({

    cccType: pvc.WaterfallChart

});

var CccMetricDotChartComponent = CccComponent.extend({

    cccType: pvc.MetricDotChart

});

var CccMetricLineChartComponent = CccComponent.extend({

    cccType: pvc.MetricLineChart

});

var CccParCoordComponent = CccComponent.extend({

    cccType: pvc.ParallelCoordinates

});

var CccDataTreeComponent = CccComponent2.extend({

    cccType: pvc.DataTree

});

var CccBoxplotChartComponent = CccComponent.extend({

    cccType: pvc.BoxplotChart

});

<|endoftext|>


<commit_before>/*
Script: ART.Alerts.js

License:
	MIT-style license.
*/

// Window Widget. Work in progress.

ART.Sheet.defineStyle('window.alert', {
	'content-background-color': hsb(0, 0, 91),
	'footer-height': 30,
	'height': 140,
	'width': 300,
	'footer-background-color': hsb(0, 0, 91),
	'footer-reflection-color': hsb(0, 0, 91),
	'content-border-bottom-color': hsb(0, 0, 91)
});

ART.Sheet.defineStyle('window.alert footer', {
	'float': 'right',
	'width': 'auto'
});

ART.Sheet.defineStyle('window.alert content', {
	'padding': 20,
	'font-size': 14,
	'text-align': 'center'
});

ART.Sheet.defineStyle('window.alert input.prompt', {
	'width': '100%'
});

ART.Sheet.defineStyle('window.alert button.confirmations', {
	'padding-right': 10,
	'border':'none',
	'float': 'left'
});

ART.StickyWin.DefaultManager.setLayer('alerts', 99);

ART.Alert = new Class({

	Extends: ART.Window,

	name: 'window',

	options: {
		close: false,
		minimize: false,
		maximize: false,
		className: 'alert',
		resizable: false,
		windowManagerLayer: 'alerts',
		destroyOnClose: true,
		buttons: [
			{
				text: 'Ok'
			}
		]
	},

	initialize: function(){
		this.parent.apply(this, arguments);
		this.addEvent('show', function(){
			var button = this.alertButtons[0];
			if (button) button.focus();
		}.bind(this));
	},

	redraw: function(){
		this.parent();
		if (!this.content) return;
		var style = ART.Sheet.lookupStyle(this.getSelector() + ' content');
		if (style.margin || style.padding) {
			var tmp = new Element('div', {styles: style});
			var w = 0,
					h = 0;
			['margin', 'padding'].each(function(space) {
				['Top', 'Left', 'Bottom', 'Right'].each(function(side, i) {
					if (i%2) w += tmp.getStyle(space+side).toInt();
					else h += tmp.getStyle(space+side).toInt();
				});
			});
			tmp.destroy();
			style.width = this.contentSize.w - w; //border is hard coded to 1 on each side
			style.height = this.contentSize.h - h; //border is hard coded to 1 on each side
		}
		this.content.setStyles(style);
		this.footer.setStyles(ART.Sheet.lookupStyle(this.getSelector() + ' footer'));
	},

	makeButtons: function(){
		this.parent();
		this.alertButtons = this.options.buttons.map(function(button){
			button.className = ((button.className || '') + ' confirmations').trim();
			button.parentWidget = this;
			var b = new ART.Button(button);
			b.addEvent('press', function(){
				if ($(b).hasClass(this.options.closeClass)) this.hide();
			}.bind(this));

			button.properties = button.properties || {};
			if (!button.properties['class']) button.properties['class'] = this.options.closeClass;
			$(b).set(button.properties);

			$(b).inject(this.footer).setStyles(ART.Sheet.lookupStyle(b.getSelector()));
		}, this);
	}

});

ART.alert = function(caption, content, callback, options) {
	return new ART.Alert(
		$extend(options, {
			caption: caption,
			content: content,
			onHide: callback
		})
	);
};

ART.Confirm = new Class({

	Extends: ART.Alert,

	options: {
		className: 'alert confirm',
		resizable: false,
		windowManagerLayer: 'alerts',
		buttons: [
			{
				text: 'Cancel'
			},
			{
				text: 'Ok',
				onPress: function(){
					this.fireEvent('confirm');
				}
			}
		]
	}
});
ART.confirm = function(caption, content, callback, options) {
	return new ART.Confirm(
		$extend(options, {
			caption: caption,
			content: content,
			onConfirm: callback
		})
	);
};

ART.Prompt = new Class({

	Extends: ART.Confirm,

	options: {
		onShow: function(){
			this.input.select();
		},
		defaultValue: '',
		buttons: [
			{
				text: 'Cancel'
			},
			{
				text: 'Ok',
				onPress: function(){
					this.parentWidget.fireEvent('confirm', this.parentWidget.input.get('value'));
				}
			}
		]
	},
	initialize: function(){
		this.parent.apply(this, arguments);
	},
	build: function(){
		this.parent.apply(this, arguments);
		var styles = ART.Sheet.lookupStyle(this.getSelector() + ' input.prompt');
		this.inputContainer = new Element('div', {
			'class': 'inputContainer',
			styles: {
				position: 'relative'
			}
		}).inject(this.content);
		this.input = new Element('input', {
			value: this.options.defaultValue,
			type: 'text',
			styles: styles,
			events: {
				keyup: function(e) {
					if (e.key == 'enter') {
						this.fireEvent('confirm', this.input.get('value'));
						this.hide();
					}
				}.bind(this)
			}
		}).inject(this.inputContainer);
	},
	setContent: function(){
		this.inputContainer.dispose();
		this.parent.apply(this, arguments);
		this.inputContainer.inject(this.content);
		return this;
	}
});

ART.prompt = function(caption, content, callback, options) {
	return new ART.Prompt(
		$extend(options, {
			caption: caption,
			content: content,
			onConfirm: callback
		})
	);
};

ART.Window.AlertTools = new Class({

	alerts: {},

	alert: function(caption, content, callback, options, type){
		type = type || 'alert';
		if (!this.alertManager) this.alertManager = new ART.WindowManager();
		options = $merge({
			relativeTo: $(this),
			inject: {
				target: $(this),
				where: 'bottom'
			},
			mask: true,
			maskOptions: {
				inject: {
					target: $(this.content),
					where: 'after'
				}
			},
			constrainToContainer: true
		}, options);
		options.parentWidget = this;
		options.windowManager = this.alertManager;

		var alert = ART[type](caption, content, callback, options);
		var shader = function(dragging) {
			$(alert).setStyle('display', dragging ? 'none' : 'block');
		};
		alert.addEvent('destroy', function(){
			this.removeEvent('shade', shader);
		}.bind(this));
		this.addEvent('shade', shader);
		return alert;
	},
	confirm: function() {
		this.alert($A(arguments).push('confirm'));
	},
	prompt: function() {
		this.alert($A(arguments).push('prompt'));
	}
});

ART.Window.implement(new ART.Window.AlertTools);<commit_msg>adding escape button handling a well as space bar for hiding focusing input<commit_after>/*
Script: ART.Alerts.js

License:
	MIT-style license.
*/

// Window Widget. Work in progress.

ART.Sheet.defineStyle('window.alert', {
	'content-background-color': hsb(0, 0, 91),
	'footer-height': 30,
	'height': 140,
	'width': 300,
	'footer-background-color': hsb(0, 0, 91),
	'footer-reflection-color': hsb(0, 0, 91),
	'content-border-bottom-color': hsb(0, 0, 91)
});

ART.Sheet.defineStyle('window.alert footer', {
	'float': 'right',
	'width': 'auto'
});

ART.Sheet.defineStyle('window.alert content', {
	'padding': 20,
	'font-size': 14,
	'text-align': 'center'
});

ART.Sheet.defineStyle('window.alert input.prompt', {
	'width': '100%'
});

ART.Sheet.defineStyle('window.alert button.confirmations', {
	'padding-right': 10,
	'border':'none',
	'float': 'left'
});

ART.StickyWin.DefaultManager.setLayer('alerts', 99);

ART.Alert = new Class({

	Extends: ART.Window,

	name: 'window',

	options: {
		close: false,
		minimize: false,
		maximize: false,
		className: 'alert',
		resizable: false,
		windowManagerLayer: 'alerts',
		destroyOnClose: true,
		buttons: [
			{
				text: 'Ok'
			}
		]
	},

	initialize: function(){
		this.parent.apply(this, arguments);
		this.addEvent('show', function(){
			var button = this.alertButtons[0];
			if (button) button.focus();
		}.bind(this));
		this.attachKeys({
			'keyup:esc': function(e) {
				this.hide();
			}.bind(this)
		});
	},

	redraw: function(){
		this.parent();
		if (!this.content) return;
		var style = ART.Sheet.lookupStyle(this.getSelector() + ' content');
		if (style.margin || style.padding) {
			var tmp = new Element('div', {styles: style});
			var w = 0,
					h = 0;
			['margin', 'padding'].each(function(space) {
				['Top', 'Left', 'Bottom', 'Right'].each(function(side, i) {
					if (i%2) w += tmp.getStyle(space+side).toInt();
					else h += tmp.getStyle(space+side).toInt();
				});
			});
			tmp.destroy();
			style.width = this.contentSize.w - w; //border is hard coded to 1 on each side
			style.height = this.contentSize.h - h; //border is hard coded to 1 on each side
		}
		this.content.setStyles(style);
		this.footer.setStyles(ART.Sheet.lookupStyle(this.getSelector() + ' footer'));
	},

	makeButtons: function(){
		this.parent();
		this.alertButtons = this.options.buttons.map(function(button){
			button.className = ((button.className || '') + ' confirmations').trim();
			button.parentWidget = this;
			var b = new ART.Button(button);
			b.addEvent('press', function(e){
				if ($(b).hasClass(this.options.closeClass)) {
					if (e) e.preventDefault();
					this.hide();
				}
			}.bind(this));

			button.properties = button.properties || {};
			if (!button.properties['class']) button.properties['class'] = this.options.closeClass;
			$(b).set(button.properties);

			$(b).inject(this.footer).setStyles(ART.Sheet.lookupStyle(b.getSelector()));
			return b;
		}, this);
		if (this.alertButtons[0]) this.alertButtons[0].enable().focus();
	}

});

ART.alert = function(caption, content, callback, options) {
	return new ART.Alert(
		$extend(options, {
			caption: caption,
			content: content,
			onHide: callback
		})
	);
};

ART.Confirm = new Class({

	Extends: ART.Alert,

	options: {
		className: 'alert confirm',
		resizable: false,
		windowManagerLayer: 'alerts',
		buttons: [
			{
				text: 'Cancel'
			},
			{
				text: 'Ok',
				onPress: function(){
					this.fireEvent('confirm');
				}
			}
		]
	}
});
ART.confirm = function(caption, content, callback, options) {
	return new ART.Confirm(
		$extend(options, {
			caption: caption,
			content: content,
			onConfirm: callback
		})
	);
};

ART.Prompt = new Class({

	Extends: ART.Confirm,

	options: {
		onShow: function(){
			this.input.select();
		},
		buttons: [
			{
				text: 'Cancel'
			},
			{
				text: 'Ok',
				onPress: function(){
					this.parentWidget.fireEvent('confirm', this.parentWidget.input.get('value'));
				}
			}
		]
	},
	initialize: function(){
		this.parent.apply(this, arguments);
	},
	build: function(){
		this.parent.apply(this, arguments);
		var styles = ART.Sheet.lookupStyle(this.getSelector() + ' input.prompt');
		this.inputContainer = new Element('div', {
			'class': 'inputContainer',
			styles: {
				position: 'relative'
			}
		}).inject(this.content);
		this.input = new Element('input', {
			value: this.options.defaultValue || '',
			type: 'text',
			styles: styles,
			events: {
				keyup: function(e) {
					if (e.key == 'enter') {
						this.fireEvent('confirm', this.input.get('value'));
						this.hide();
					}
				}.bind(this)
			}
		}).inject(this.inputContainer);
	},
	setContent: function(){
		this.inputContainer.dispose();
		this.parent.apply(this, arguments);
		this.inputContainer.inject(this.content);
		return this;
	},
	show: function(){
		this.parent.apply(this, arguments);
		this.alertButtons[0].disable().blur();
		this.input.select();
		return this;
	}
});

ART.prompt = function(caption, content, callback, options) {
	return new ART.Prompt(
		$extend(options, {
			caption: caption,
			content: content,
			onConfirm: callback
		})
	);
};

ART.Window.AlertTools = new Class({

	alerts: {},

	alert: function(caption, content, callback, options, type){
		type = type || 'alert';
		if (!this.alertManager) this.alertManager = new ART.WindowManager();
		options = $merge({
			relativeTo: $(this),
			inject: {
				target: $(this),
				where: 'bottom'
			},
			mask: true,
			maskOptions: {
				inject: {
					target: $(this.content),
					where: 'after'
				}
			},
			constrainToContainer: true
		}, options);
		options.parentWidget = this;
		options.windowManager = this.alertManager;
		var alert = ART[type](caption, content, callback, options);
		var shader = function(dragging) {
			$(alert).setStyle('display', dragging ? 'none' : 'block');
		};
		alert.addEvent('destroy', function(){
			this.removeEvent('shade', shader);
		}.bind(this));
		this.addEvent('shade', shader);
		return alert;
	},
	confirm: function(cap, cont, fn, opt) {
		return this.alert(cap, cont, fn, opt, 'confirm');
	},
	prompt: function(cap, cont, fn, opt) {
		return this.alert(cap, cont, fn, opt, 'prompt');
	}
});

ART.Window.implement(new ART.Window.AlertTools);<|endoftext|>