<commit_before>var http = require("http");
var fs = require("fs");
var redis = require("redis");
var Step = require("step");

var db = redis.createClient();
var server = http.Server( handler );

var io = require("socket.io").listen(server);

server.listen(8000);

io.sockets.on('connection', function (socket) {
  var result = [];
  Step(
    function loadCardList() {
      db.lrange( "cards", 0, -1, this );
    },
    function loadCards(err,card_ids) {
      if ( err ) throw err;
      var group = this.group();
      card_ids.forEach( function (id) {
        loadCard( id, group() );
      });
    },
    function printCards(err,cards) {
      cards.forEach( function(card) {
        result.push( card );
      });
      socket.emit( 'cards', result );
    }
  );

  socket.on('save', function(data) {
    var id = data['id'];

    console.log( "SAVE ID: " + id );

    db.get( "card." + id + ".id", function( err, value ) {
      if ( err ) {
        console.log( "Error getting id for " + id );
      } else {
        if ( id === value ) {
          db.set( "card." + id + ".x", data['x'] );
          db.set( "card." + id + ".y", data['y'] );
          db.set( "card." + id + ".text", data['text'] );
        }
      }
    });
  });

  socket.on("click", function(data) {
    var id = data['id'];

    console.log( "CLICK: " + id );

    db.lpush( "cards", id );

    db.set( "card." + id + ".id", id );
    db.set( "card." + id + ".x", data['x'] );
    db.set( "card." + id + ".y", data['y'] );

    console.log( "  ID: " + id );
    console.log( "  X: " + data['x'] );
    console.log( "  Y: " + data['y'] );
  });

  socket.on("trash", function(data) {
    var id = data['id'];

    console.log( "DELETE " + id );

    db.lrem( "cards", 0, id );

    db.del( "card." + id + ".id" );
    db.del( "card." + id + ".x" );
    db.del( "card." + id + ".y" );
    db.del( "card." + id + ".text" );
  });

  socket.on("clear", function(data) {
    console.log("CLEAR");
    db.del( "cards" );
  });
});

function handler(req,res) {
  console.log( req.url );

  if ( req.url == "/" ) {
    fs.readFile("./index.html", function(error,content) {
      if (error) {
        res.writeHead(500);
        res.end();
      } else {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(content, 'utf-8');
      }
    } );
  } else if ( req.url == "/sticky_client.js" ) {
    fs.readFile("./sticky_client.js", function(error,content) {
      if (error) {
        res.writeHead(500);
        res.end();
      } else {
        res.writeHead(200, { 'Content-Type': 'text/javascript' });
        res.end(content, 'utf-8');
      }
    } );
  } else if ( req.url == "/sticky.css" ) {
    fs.readFile("./sticky.css", function(error,content) {
      if (error) {
        res.writeHead(500);
        res.end();
      } else {
        res.writeHead(200, { 'Content-Type': 'text/css' });
        res.end(content, 'utf-8');
      }
    } );
  } else if ( req.url == "/hello" ) {
    res.writeHead(200);
    res.end( "hello" );
  } else {
    res.writeHead(404);
    res.end("File not found");
  }
}

function loadCard( id, cb ) {
  var card = {};
  card.id = id;
  Step(
    function loadCard() {
      db.get( "card." + id + ".x", this.parallel() );
      db.get( "card." + id + ".y", this.parallel() );
      db.get( "card." + id + ".text", this.parallel() );
    },
    function saveCard(err,x,y,text) {
      card.x = x;
      card.y = y;
      card.text = text;
      return card;
    },
    function printCard(err,card) {
      cb( 0, card );
    }
  )
}
<commit_msg>Clean up code a bit.<commit_after>var fs = require("fs");
var Step = require("step");

var db = require("redis").createClient();
var app = require("http").Server( handler ).listen(8000);
var io = require("socket.io").listen(app);

io.sockets.on('connection', function (socket) {
  var result = [];
  Step(
    function loadCardList() {
      db.lrange( "cards", 0, -1, this );
    },
    function loadCards(err,card_ids) {
      if ( err ) throw err;
      var group = this.group();
      card_ids.forEach( function (id) {
        loadCard( id, group() );
      });
    },
    function printCards(err,cards) {
      cards.forEach( function(card) {
        result.push( card );
      });
      socket.emit( 'cards', result );
    }
  );

  socket.on('save', function(data) {
    var id = data['id'];

    console.log( "SAVE " + id );

    db.get( "card." + id + ".id", function( err, value ) {
      if ( err ) {
        console.log( "Error getting id for " + id );
      } else {
        if ( id === value ) {
          db.set( "card." + id + ".x", data['x'] );
          db.set( "card." + id + ".y", data['y'] );
          db.set( "card." + id + ".text", data['text'] );
        }
      }
    });
  });

  socket.on("click", function(data) {
    var id = data['id'];

    console.log( "CLICK " + id );

    db.lpush( "cards", id );

    db.set( "card." + id + ".id", id );
    db.set( "card." + id + ".x", data['x'] );
    db.set( "card." + id + ".y", data['y'] );
  });

  socket.on("trash", function(data) {
    var id = data['id'];

    console.log( "TRASH " + id );

    db.lrem( "cards", 0, id );

    db.del( "card." + id + ".id" );
    db.del( "card." + id + ".x" );
    db.del( "card." + id + ".y" );
    db.del( "card." + id + ".text" );
  });

  socket.on("clear", function(data) {
    console.log("CLEAR");

    db.del( "cards" );
  });
});

function loadCard( id, cb ) {
  var card = {};
  card.id = id;
  Step(
    function loadCard() {
      db.get( "card." + id + ".x", this.parallel() );
      db.get( "card." + id + ".y", this.parallel() );
      db.get( "card." + id + ".text", this.parallel() );
    },
    function saveCard(err,x,y,text) {
      card.x = x;
      card.y = y;
      card.text = text;
      return card;
    },
    function printCard(err,card) {
      cb( 0, card );
    }
  )
}

function handler(req,res) {
  console.log( req.url );

  if ( req.url == "/" ) {
    fs.readFile("./index.html", function(error,content) {
      if (error) {
        res.writeHead(500);
        res.end();
      } else {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(content, 'utf-8');
      }
    } );
  } else if ( req.url == "/sticky_client.js" ) {
    fs.readFile("./sticky_client.js", function(error,content) {
      if (error) {
        res.writeHead(500);
        res.end();
      } else {
        res.writeHead(200, { 'Content-Type': 'text/javascript' });
        res.end(content, 'utf-8');
      }
    } );
  } else if ( req.url == "/sticky.css" ) {
    fs.readFile("./sticky.css", function(error,content) {
      if (error) {
        res.writeHead(500);
        res.end();
      } else {
        res.writeHead(200, { 'Content-Type': 'text/css' });
        res.end(content, 'utf-8');
      }
    } );
  } else if ( req.url == "/hello" ) {
    res.writeHead(200);
    res.end( "hello" );
  } else {
    res.writeHead(404);
    res.end("File not found");
  }
}
<|endoftext|>


<commit_before>/*!
 * Sticky Sidebar v1.2
 *
 * Sticky Sidebar for FoundationPress
 * http://urbino.nl/fpdemo-ss
 *
 * Copyright (c) 2015 Henk Barreveld
 * Released under the MIT license (see the LICENSE file)
 */
$(document).ready(function () {
    "use strict";
    var row = $('section[role="document"]>.row'),
        rowTop,
        content,
        contentHeight,
        sidebar,
        sidebarTop,
        sidebarLeft,
        sidebarHeight,
        sidebarWidth,
        sidebarSticky,
        layoutBroken;

    function sidebarPos() {                 // function called on window resize, window scroll and sidebar click
        if (!sidebarSticky) {               // case: no sidebar or row with class="sidebar-nostick"
            return;
        }
        sidebar.removeClass('fixed');
        sidebar.removeAttr('style');
        sidebarHeight = sidebar.outerHeight(true);
        if (layoutBroken || (contentHeight < sidebarHeight)) {
            return;                         // case: sidebar below/above content or sidebar longer than content
        }
        var topbarBottom = $('.top-bar-container')[0].getBoundingClientRect().top + $('.top-bar-container').outerHeight(true);
        topbarBottom = Math.round(Math.max(topbarBottom, 0));
        rowTop = Math.round(row.offset().top - $(window).scrollTop());
        if (rowTop >= topbarBottom) {
            return;                         // row top is on the page and below the topbar
        }
        sidebar.addClass('fixed');
        var footerDistance = $('#footer-container').offset().top - $(window).scrollTop() - topbarBottom - sidebarHeight;
        sidebarTop = topbarBottom + ((footerDistance < 0) ? footerDistance : 0);
               // avoid sidebar overlapping footer, if viewport height is maller than sidebar height
        sidebar.css({'position': 'fixed', 'top': sidebarTop, 'left': sidebarLeft, 'width': sidebarWidth});
    }

    function sidebarInf() {                // function called on window load and window resize
        if (!sidebarSticky) {              // case: no sidebar or row with class="sidebar-nostick"
            return;
        }
        contentHeight = content.outerHeight(true);
        sidebar.removeAttr('style');
        sidebarWidth = sidebar.css('width');
        sidebarTop = sidebar.offset().top;
        sidebarLeft = sidebar.offset().left;
        layoutBroken = (sidebarTop !== content.offset().top);       //  sidebar not next to content but underneath (or above)
        sidebarPos();
    }

    sidebarSticky = (row.children('aside').length > 0) &&           // exlude pages with no sidebar
        !(row.hasClass('sidebar-nostick'));                         // exclude pages with

    if (sidebarSticky) {
        content = row.children('div').first();
        sidebar = row.children('aside').first();
    }

    sidebarInf();

    // on window scroll, reposition the sidebar
    $(window).scroll(sidebarPos);

    // on window resize, reposition the sidebar
    $(window).resize(sidebarInf);

    // on sidebar click, possibly changing the sidebar height, reposition the sidebar
    // Repositioning is delayed, to deal with any animation in the sidebar height change
    sidebar.click(function () {
        setTimeout(function () {
            sidebarPos();
        }, '400');
    });

    // debugging: on ESC press, make one or more alerts
    $(window).keypress(function(event) {
        var key = event.keyCode;
        if (event.keyCode == 27) {
            alert("topbarBottom: " + $('.top-bar-container')[0].getBoundingClientRect().top + $('.top-bar-container').outerHeight(true));
            alert("rowTop: " + rowTop);
        }
    });
});
<commit_msg>initial release 1.2<commit_after>/*!
 * Sticky Sidebar v1.2
 *
 * Sticky Sidebar for FoundationPress
 * http://urbino.nl/fpdemo-ss
 *
 * Copyright (c) 2015 Henk Barreveld
 * Released under the MIT license (see the LICENSE file)
 */
$(document).ready(function () {
    "use strict";
    var row = $('section[role="document"]>.row'),
        rowTop,
        content,
        contentHeight,
        sidebar,
        sidebarTop,
        sidebarLeft,
        sidebarHeight,
        sidebarWidth,
        sidebarSticky,
        layoutBroken;

    function sidebarPos() {                 // function called on window resize, window scroll and sidebar click
        if (!sidebarSticky) {               // case: no sidebar or row with class="sidebar-nostick"
            return;
        }
        sidebar.removeClass('fixed');
        sidebar.removeAttr('style');
        sidebarHeight = sidebar.outerHeight(true);
        if (layoutBroken || (contentHeight < sidebarHeight)) {
            return;                         // case: sidebar below/above content or sidebar longer than content
        }
        var topbarBottom = $('.top-bar-container')[0].getBoundingClientRect().top + $('.top-bar-container').outerHeight(true);
        topbarBottom = Math.round(Math.max(topbarBottom, 0));
        rowTop = Math.round(row.offset().top - $(window).scrollTop());
        if (rowTop >= topbarBottom) {
            return;                         // row top is on the page and below the topbar
        }
        sidebar.addClass('fixed');
        var footerDistance = $('#footer-container').offset().top - $(window).scrollTop() - topbarBottom - sidebarHeight;
        sidebarTop = topbarBottom + ((footerDistance < 0) ? footerDistance : 0);
               // avoid sidebar overlapping footer, if viewport height is maller than sidebar height
        sidebar.css({'position': 'fixed', 'top': sidebarTop, 'left': sidebarLeft, 'width': sidebarWidth});
    }

    function sidebarInf() {                // function called on window load and window resize
        if (!sidebarSticky) {              // case: no sidebar or row with class="sidebar-nostick"
            return;
        }
        contentHeight = content.outerHeight(true);
        sidebar.removeAttr('style');
        sidebarWidth = sidebar.css('width');
        sidebarTop = sidebar.offset().top;
        sidebarLeft = sidebar.offset().left;
        layoutBroken = (sidebarTop !== content.offset().top);       //  sidebar not next to content but underneath (or above)
        sidebarPos();
    }

    sidebarSticky = (row.children('aside').length > 0) &&           // exlude pages with no sidebar
        !(row.hasClass('sidebar-nostick'));                         // exclude pages with

    if (sidebarSticky) {
        content = row.children('div').first();
        sidebar = row.children('aside').first();
    }

    sidebarInf();

    // on window scroll, reposition the sidebar
    $(window).scroll(sidebarPos);

    // on window resize, reposition the sidebar
    $(window).resize(sidebarInf);

    // on sidebar click, possibly changing the sidebar height, reposition the sidebar
    // Repositioning is delayed, to deal with any animation in the sidebar height change
    sidebar.click(function () {
        setTimeout(function () {
            sidebarPos();
        }, '400');
    });

    /* debugging: on ESC press, make one or more alerts
    $(window).keypress(function(event) {
        var key = event.keyCode;
        if (event.keyCode == 27) {
            alert("ESC-key pressed");
        }
    }); */
});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012-2013 Vanderbilt University, All rights reserved.
 *
 * Author: Miklos Maroti
 */

define([ "util/assert" ], function (ASSERT) {
	"use strict";

	var Lock = function () {
		var waiters = [];

		return {
			lock: function (func) {
				waiters.push(func);
				if (waiters.length === 1) {
					func();
				}
			},

			unlock: function () {
                waiters.shift();
				if (waiters.length >= 1) {
					var func = waiters[0];
					func();
				}
			}
		};
	};

	var Database = function (database, options) {
		ASSERT(typeof database === "object" && typeof options === "object");

		options.cache = options.cache || 2000;

		var projects = {};
		var dlock = new Lock();

		function openProject (name, callback) {
			ASSERT(typeof name === "string" && typeof callback === "function");

			dlock.lock(function () {
				if (typeof projects[name] !== "undefined") {
					projects[name].reopenProject(callback);
					dlock.unlock();
				} else {
					database.openProject(name, function (err, project) {
						if (err) {
							callback(err);
						} else {
							project = wrapProject(name, project);
							projects[name] = project;
							project.reopenProject(callback);
						}
						dlock.unlock();
					});
				}
			});
		}

		function closeDatabase (callback) {
			dlock.lock(function () {
				var n;
				for (n in projects) {
					projects[n].abortProject();
				}
				projects = {};
				database.closeDatabase(callback);
				dlock.unlock();
			});
		}

		function deleteProject (name, callback) {
			if (typeof projects[name] !== "undefined") {
				projects[name].deleteProject();
			}

			database.deleteProject(name, callback);
		}

		function wrapProject (name, project) {
			var ID_NAME = project.ID_NAME;

			var refcount = 0;
			var branches = {};
			var missing = {};
			var backup = {};
			var cache = {};
			var cacheSize = 0;

			function deepFreeze (obj) {
				ASSERT(typeof obj === "object");

				Object.freeze(obj);

				var key;
				for (key in obj) {
					if (obj[key] !== null && typeof obj[key] === "object") {
						deepFreeze(obj[key]);
					}
				}
			}

			function cacheInsert (key, obj) {
				ASSERT(typeof cache[key] === "undefined" && obj[ID_NAME] === key);

				deepFreeze(obj);
				cache[key] = obj;

				if (++cacheSize >= options.cache) {
					backup = cache;
					cache = {};
					cacheSize = 0;
				}
			}

			function loadObject (key, callback) {
				ASSERT(typeof key === "string" && typeof callback === "function");
				ASSERT(project !== null);

				var obj = cache[key];
				if (typeof obj === "undefined") {
					obj = backup[key];
					if (typeof obj === "undefined") {
						obj = missing[key];
						if (typeof obj === "undefined") {
							obj = [ callback ];
							missing[key] = obj;
							project.loadObject(key, function (err, obj2) {
								ASSERT(typeof obj2 === "object" || typeof obj2 === "undefined");

								if (obj.length !== 0) {
									ASSERT(missing[key] === obj);

									delete missing[key];
									if (!err && obj2) {
										cacheInsert(key, obj2);
									}

									var cb;
									while ((cb = obj.pop())) {
										cb(err, obj2);
									}
								}
							});
						} else {
							obj.push(callback);
						}
						return;
					} else {
						cacheInsert(key, obj);
					}
				}

				ASSERT(typeof obj === "object" && obj !== null && obj[ID_NAME] === key);
				callback(null, obj);
			}

			function insertObject (obj, callback) {
				ASSERT(typeof obj === "object" && obj !== null && typeof callback === "function");

				var key = obj[ID_NAME];
				ASSERT(typeof key === "string");

				if (typeof cache[key] !== "undefined") {
					callback(null);
					return;
				} else {
					var item = backup[key];
					cacheInsert(key, obj);

					if (typeof item !== "undefined") {
						callback(null);
						return;
					} else {
						item = missing[key];
						if (typeof item !== "undefined") {
							delete missing[key];

							var cb;
							while ((cb = item.pop())) {
								cb(null, obj);
							}
						}
					}
				}

				project.insertObject(obj, callback);
			}

			function abortProject (callback) {
				if (project !== null) {
					var p = project;
					project = null;
					delete projects[name];
					deleteProject();
					p.closeProject(callback);
				} else if (typeof callback === "function ") {
					callback(null);
				}
			}

			function closeProject (callback) {
				ASSERT(refcount >= 1);

				if (--refcount === 0) {
					abortProject(callback);
				} else if (typeof callback === "function") {
					callback(null);
				}
			}

			function deleteProject () {
				var key, callbacks, cb, err = new Error("cache closed");
				for (key in missing) {
					callbacks = missing[key];
					while ((cb = callbacks.pop())) {
						cb(err);
					}
				}

				for (key in branches) {
					callbacks = branches[key];
					while ((cb = callbacks.pop())) {
						cb(err);
					}
				}

				branches = {};
				missing = {};
				backup = {};
				cache = {};
				cacheSize = 0;
			}

			function getBranchHash (name, oldhash, callback) {
				ASSERT(typeof name === "string" && typeof callback === "function");
				ASSERT(typeof oldhash === "string" || oldhash === null);

				var tag = name + "@" + oldhash;
				var branch = branches[tag];
				if (typeof branch === "undefined") {
					branch = [ callback ];
					branches[tag] = branch;

					project.getBranchHash(name, oldhash, function (err, newhash, forkedhash) {
						if (branches[tag] === branch) {
							var cb;
							delete branches[tag];

							while ((cb = branch.pop())) {
								cb(err, newhash, forkedhash);
							}
						}
					});
				} else {
					branch.push(callback);
				}
			}

			function setBranchHash (name, oldhash, newhash, callback) {
				ASSERT(typeof name === "string" && typeof oldhash === "string");
				ASSERT(typeof newhash === "string" && typeof callback === "function");

				project.setBranchHash(name, oldhash, newhash, function (err) {
					if (!err) {
						var prefix = name + "@", tag;
						for (tag in branches) {
							if (tag.substr(0, prefix.length) === prefix) {
								var cb, branch = branches[tag];
								delete branches[tag];

								while ((cb = branch.pop())) {
									cb(err, newhash, null);
								}
							}
						}
					}

					callback(err);
				});
			}

			function reopenProject (callback) {
				ASSERT(project !== null && refcount >= 0 && typeof callback === "function");

				++refcount;
				callback(null, {
					fsyncDatabase: project.fsyncDatabase,
					getDatabaseStatus: project.getDatabaseStatus,
					closeProject: closeProject,
					loadObject: loadObject,
					insertObject: insertObject,
					findHash: project.findHash,
					dumpObjects: project.dumpObjects,
					getBranchNames: project.getBranchNames,
					getBranchHash: getBranchHash,
					setBranchHash: setBranchHash,
					getCommits: project.getCommits,
					makeCommit: project.makeCommit,
					ID_NAME: project.ID_NAME
				});
			}

			return {
				reopenProject: reopenProject,
				abortProject: abortProject,
				deleteProject: deleteProject
			};
		}

		return {
			openDatabase: database.openDatabase,
			closeDatabase: closeDatabase,
			fsyncDatabase: database.fsyncDatabase,
			getDatabaseStatus: database.getDatabaseStatus,
			getProjectNames: database.getProjectNames,
			openProject: openProject,
			deleteProject: deleteProject,
            authenticate: database.authenticate
		};
	};

	return Database;
});
<commit_msg>temporary workout for mysterious freeze error<commit_after>/*
 * Copyright (C) 2012-2013 Vanderbilt University, All rights reserved.
 *
 * Author: Miklos Maroti
 */

define([ "util/assert" ], function (ASSERT) {
	"use strict";

	var Lock = function () {
		var waiters = [];

		return {
			lock: function (func) {
				waiters.push(func);
				if (waiters.length === 1) {
					func();
				}
			},

			unlock: function () {
                waiters.shift();
				if (waiters.length >= 1) {
					var func = waiters[0];
					func();
				}
			}
		};
	};

	var Database = function (database, options) {
		ASSERT(typeof database === "object" && typeof options === "object");

		options.cache = options.cache || 2000;

		var projects = {};
		var dlock = new Lock();

		function openProject (name, callback) {
			ASSERT(typeof name === "string" && typeof callback === "function");

			dlock.lock(function () {
				if (typeof projects[name] !== "undefined") {
					projects[name].reopenProject(callback);
					dlock.unlock();
				} else {
					database.openProject(name, function (err, project) {
						if (err) {
							callback(err);
						} else {
							project = wrapProject(name, project);
							projects[name] = project;
							project.reopenProject(callback);
						}
						dlock.unlock();
					});
				}
			});
		}

		function closeDatabase (callback) {
			dlock.lock(function () {
				var n;
				for (n in projects) {
					projects[n].abortProject();
				}
				projects = {};
				database.closeDatabase(callback);
				dlock.unlock();
			});
		}

		function deleteProject (name, callback) {
			if (typeof projects[name] !== "undefined") {
				projects[name].deleteProject();
			}

			database.deleteProject(name, callback);
		}

		function wrapProject (name, project) {
			var ID_NAME = project.ID_NAME;

			var refcount = 0;
			var branches = {};
			var missing = {};
			var backup = {};
			var cache = {};
			var cacheSize = 0;

			function deepFreeze (obj) {
				ASSERT(typeof obj === "object");

                try{
				    Object.freeze(obj);
                }
                catch(e){
                    //TODO find the proper answer why this can occur
                    return;
                }

				var key;
				for (key in obj) {
					if (obj[key] !== null && typeof obj[key] === "object") {
						deepFreeze(obj[key]);
					}
				}
			}

			function cacheInsert (key, obj) {
				ASSERT(typeof cache[key] === "undefined" && obj[ID_NAME] === key);

				deepFreeze(obj);
				cache[key] = obj;

				if (++cacheSize >= options.cache) {
					backup = cache;
					cache = {};
					cacheSize = 0;
				}
			}

			function loadObject (key, callback) {
				ASSERT(typeof key === "string" && typeof callback === "function");
				ASSERT(project !== null);

				var obj = cache[key];
				if (typeof obj === "undefined") {
					obj = backup[key];
					if (typeof obj === "undefined") {
						obj = missing[key];
						if (typeof obj === "undefined") {
							obj = [ callback ];
							missing[key] = obj;
							project.loadObject(key, function (err, obj2) {
								ASSERT(typeof obj2 === "object" || typeof obj2 === "undefined");

								if (obj.length !== 0) {
									ASSERT(missing[key] === obj);

									delete missing[key];
									if (!err && obj2) {
										cacheInsert(key, obj2);
									}

									var cb;
									while ((cb = obj.pop())) {
										cb(err, obj2);
									}
								}
							});
						} else {
							obj.push(callback);
						}
						return;
					} else {
						cacheInsert(key, obj);
					}
				}

				ASSERT(typeof obj === "object" && obj !== null && obj[ID_NAME] === key);
				callback(null, obj);
			}

			function insertObject (obj, callback) {
				ASSERT(typeof obj === "object" && obj !== null && typeof callback === "function");

				var key = obj[ID_NAME];
				ASSERT(typeof key === "string");

				if (typeof cache[key] !== "undefined") {
					callback(null);
					return;
				} else {
					var item = backup[key];
					cacheInsert(key, obj);

					if (typeof item !== "undefined") {
						callback(null);
						return;
					} else {
						item = missing[key];
						if (typeof item !== "undefined") {
							delete missing[key];

							var cb;
							while ((cb = item.pop())) {
								cb(null, obj);
							}
						}
					}
				}

				project.insertObject(obj, callback);
			}

			function abortProject (callback) {
				if (project !== null) {
					var p = project;
					project = null;
					delete projects[name];
					deleteProject();
					p.closeProject(callback);
				} else if (typeof callback === "function ") {
					callback(null);
				}
			}

			function closeProject (callback) {
				ASSERT(refcount >= 1);

				if (--refcount === 0) {
					abortProject(callback);
				} else if (typeof callback === "function") {
					callback(null);
				}
			}

			function deleteProject () {
				var key, callbacks, cb, err = new Error("cache closed");
				for (key in missing) {
					callbacks = missing[key];
					while ((cb = callbacks.pop())) {
						cb(err);
					}
				}

				for (key in branches) {
					callbacks = branches[key];
					while ((cb = callbacks.pop())) {
						cb(err);
					}
				}

				branches = {};
				missing = {};
				backup = {};
				cache = {};
				cacheSize = 0;
			}

			function getBranchHash (name, oldhash, callback) {
				ASSERT(typeof name === "string" && typeof callback === "function");
				ASSERT(typeof oldhash === "string" || oldhash === null);

				var tag = name + "@" + oldhash;
				var branch = branches[tag];
				if (typeof branch === "undefined") {
					branch = [ callback ];
					branches[tag] = branch;

					project.getBranchHash(name, oldhash, function (err, newhash, forkedhash) {
						if (branches[tag] === branch) {
							var cb;
							delete branches[tag];

							while ((cb = branch.pop())) {
								cb(err, newhash, forkedhash);
							}
						}
					});
				} else {
					branch.push(callback);
				}
			}

			function setBranchHash (name, oldhash, newhash, callback) {
				ASSERT(typeof name === "string" && typeof oldhash === "string");
				ASSERT(typeof newhash === "string" && typeof callback === "function");

				project.setBranchHash(name, oldhash, newhash, function (err) {
					if (!err) {
						var prefix = name + "@", tag;
						for (tag in branches) {
							if (tag.substr(0, prefix.length) === prefix) {
								var cb, branch = branches[tag];
								delete branches[tag];

								while ((cb = branch.pop())) {
									cb(err, newhash, null);
								}
							}
						}
					}

					callback(err);
				});
			}

			function reopenProject (callback) {
				ASSERT(project !== null && refcount >= 0 && typeof callback === "function");

				++refcount;
				callback(null, {
					fsyncDatabase: project.fsyncDatabase,
					getDatabaseStatus: project.getDatabaseStatus,
					closeProject: closeProject,
					loadObject: loadObject,
					insertObject: insertObject,
					findHash: project.findHash,
					dumpObjects: project.dumpObjects,
					getBranchNames: project.getBranchNames,
					getBranchHash: getBranchHash,
					setBranchHash: setBranchHash,
					getCommits: project.getCommits,
					makeCommit: project.makeCommit,
					ID_NAME: project.ID_NAME
				});
			}

			return {
				reopenProject: reopenProject,
				abortProject: abortProject,
				deleteProject: deleteProject
			};
		}

		return {
			openDatabase: database.openDatabase,
			closeDatabase: closeDatabase,
			fsyncDatabase: database.fsyncDatabase,
			getDatabaseStatus: database.getDatabaseStatus,
			getProjectNames: database.getProjectNames,
			openProject: openProject,
			deleteProject: deleteProject,
            authenticate: database.authenticate
		};
	};

	return Database;
});
<|endoftext|>


<commit_before>// @flow
import jsdom from 'mocha-jsdom';
import React from 'react'
import { mount, render } from 'enzyme'

import { resetStyled, expectCSSMatches } from './utils'
import ThemeProvider from '../models/ThemeProvider'

let styled

describe('theming', () => {
  beforeEach(() => {
    styled = resetStyled()
  })

  it('should inject props.theme into a styled component', () => {
    const Comp = styled.div`
      color: ${props => props.theme.color};
    `
    const theme = { color: 'black' }
    render(
      <ThemeProvider theme={theme}>
        <Comp />
      </ThemeProvider>
    )
    expectCSSMatches(`.a { color: ${theme.color}; }`)
  })

  it('should inject props.theme into a styled component multiple levels deep', () => {
    const Comp = styled.div`
      color: ${props => props.theme.color};
    `
    const theme = { color: 'black' }
    render(
      <ThemeProvider theme={theme}>

            <Comp />

      </ThemeProvider>
    )
    expectCSSMatches(`.a { color: ${theme.color}; }`)
  })

  it('should properly allow a component to fallback to its default props when a theme is not provided', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.test.color};
    `

    Comp1.defaultProps = {
      theme: {
        test: {
          color: "purple"
        }
      }
    }
    render(

        <Comp1 />

    )
    expectCSSMatches(`.a { color: purple; }`)
  })

  it('should properly set the theme with an empty object when no teme is provided and no defaults are set', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `
    render(

        <Comp1 />

    )
    expectCSSMatches(`.a { color: ; }`)
  })

  it('should only inject props.theme into styled components within its child component tree', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `
    const Comp2 = styled.div`
      background: ${props => props.theme.color};
    `

    const theme = { color: 'black' }
    render(

        <ThemeProvider theme={theme}>

            <Comp1 />

        </ThemeProvider>
        <Comp2 />

    )
    expectCSSMatches(`.a { color: ${theme.color}; } .b { background: ; }`)
  })

  it('should inject props.theme into all styled components within the child component tree', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `
    const Comp2 = styled.div`
      background: ${props => props.theme.color};
    `
    const theme = { color: 'black' }
    render(
      <ThemeProvider theme={theme}>

            <Comp1 />

          <Comp2 />

      </ThemeProvider>
    )
    expectCSSMatches(`.a { color: ${theme.color}; } .b { background: ${theme.color}; }`)
  })

  it('should inject new CSS when the theme changes', () => {
    const Comp = styled.div`
      color: ${props => props.theme.color};
    `
    const originalTheme = { color: 'black' }
    const newTheme = { color: 'blue' }
    let theme = originalTheme
    // Force render the component
    const renderComp = () => {
      render(
        <ThemeProvider theme={theme}>
          <Comp />
        </ThemeProvider>
      )
    }
    renderComp()
    const initialCSS = expectCSSMatches(`.a { color: ${theme.color}; }`)
    // Change the theme
    theme = newTheme
    renderComp()
    expectCSSMatches(`${initialCSS}.b { color: ${newTheme.color}; }`)
  })
})

describe('theming (jsdom)', () => {
  jsdom()

  beforeEach(() => {
    styled = resetStyled()
  })

  it('should properly render with the same theme from default props on re-render', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `

    Comp1.defaultProps = {
      theme: {
        color: "purple"
      }
    }
    const wrapper = mount(
      <Comp1 />
    )
    expectCSSMatches(`.a { color: purple; }`)

    wrapper.update();
    expectCSSMatches(`.a { color: purple; }`)
  })

  it('should properly update style if theme is changed', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `

    Comp1.defaultProps = {
      theme: {
        color: "purple"
      }
    }
    const wrapper = mount(
      <Comp1 />
    )
    expectCSSMatches(`.a { color: purple; }`)

    wrapper.setProps({ theme: { color: 'pink' } })
    expectCSSMatches(`.a { color: purple; }.b { color: pink; }`)
  })

  it('should properly update style if props used in styles is changed', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
      z-index: ${props => props.zIndex}px;
    `

    Comp1.defaultProps = {
      theme: {
        color: "purple"
      },
      zIndex: 0
    }
    const wrapper = mount(
      <Comp1 />
    )
    let expectedStyles = `.a { color: purple; z-index: 0px; }`
    expectCSSMatches(expectedStyles)

    wrapper.setProps({ theme: { color: 'pink' } })
    expectedStyles = `${expectedStyles}.b { color: pink; z-index: 0px; }`
    expectCSSMatches(expectedStyles)

    wrapper.setProps({ zIndex: 1 });
    expectCSSMatches(`${expectedStyles}.c { color: pink; z-index: 1px; }`)
  });
})
<commit_msg>Add test<commit_after>// @flow
import expect from 'expect'
import jsdom from 'mocha-jsdom'
import React from 'react'
import { mount, render } from 'enzyme'

import { resetStyled, expectCSSMatches } from './utils'
import ThemeProvider from '../models/ThemeProvider'

let styled

describe('theming', () => {
  beforeEach(() => {
    styled = resetStyled()
  })

  it('should inject props.theme into a styled component', () => {
    const Comp = styled.div`
      color: ${props => props.theme.color};
    `
    const theme = { color: 'black' }
    render(
      <ThemeProvider theme={theme}>
        <Comp />
      </ThemeProvider>
    )
    expectCSSMatches(`.a { color: ${theme.color}; }`)
  })

  it('should inject props.theme into a styled component multiple levels deep', () => {
    const Comp = styled.div`
      color: ${props => props.theme.color};
    `
    const theme = { color: 'black' }
    render(
      <ThemeProvider theme={theme}>

            <Comp />

      </ThemeProvider>
    )
    expectCSSMatches(`.a { color: ${theme.color}; }`)
  })

  it('should properly allow a component to fallback to its default props when a theme is not provided', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.test.color};
    `

    Comp1.defaultProps = {
      theme: {
        test: {
          color: "purple"
        }
      }
    }
    render(

        <Comp1 />

    )
    expectCSSMatches(`.a { color: purple; }`)
  })

  it('should properly set the theme with an empty object when no teme is provided and no defaults are set', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `
    render(

        <Comp1 />

    )
    expectCSSMatches(`.a { color: ; }`)
  })

  it('should only inject props.theme into styled components within its child component tree', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `
    const Comp2 = styled.div`
      background: ${props => props.theme.color};
    `

    const theme = { color: 'black' }
    render(

        <ThemeProvider theme={theme}>

            <Comp1 />

        </ThemeProvider>
        <Comp2 />

    )
    expectCSSMatches(`.a { color: ${theme.color}; } .b { background: ; }`)
  })

  it('should inject props.theme into all styled components within the child component tree', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `
    const Comp2 = styled.div`
      background: ${props => props.theme.color};
    `
    const theme = { color: 'black' }
    render(
      <ThemeProvider theme={theme}>

            <Comp1 />

          <Comp2 />

      </ThemeProvider>
    )
    expectCSSMatches(`.a { color: ${theme.color}; } .b { background: ${theme.color}; }`)
  })

  it('should inject new CSS when the theme changes', () => {
    const Comp = styled.div`
      color: ${props => props.theme.color};
    `
    const originalTheme = { color: 'black' }
    const newTheme = { color: 'blue' }
    let theme = originalTheme
    // Force render the component
    const renderComp = () => {
      render(
        <ThemeProvider theme={theme}>
          <Comp />
        </ThemeProvider>
      )
    }
    renderComp()
    const initialCSS = expectCSSMatches(`.a { color: ${theme.color}; }`)
    // Change the theme
    theme = newTheme
    renderComp()
    expectCSSMatches(`${initialCSS}.b { color: ${newTheme.color}; }`)
  })
})

describe('theming (jsdom)', () => {
  jsdom()

  beforeEach(() => {
    styled = resetStyled()
  })

  it('should properly render with the same theme from default props on re-render', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `

    Comp1.defaultProps = {
      theme: {
        color: "purple"
      }
    }
    const wrapper = mount(
      <Comp1 />
    )
    expectCSSMatches(`.a { color: purple; }`)

    wrapper.update();
    expectCSSMatches(`.a { color: purple; }`)
  })

  it('should properly update style if theme is changed', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
    `

    Comp1.defaultProps = {
      theme: {
        color: "purple"
      }
    }
    const wrapper = mount(
      <Comp1 />
    )
    expectCSSMatches(`.a { color: purple; }`)

    wrapper.setProps({ theme: { color: 'pink' } })
    expectCSSMatches(`.a { color: purple; }.b { color: pink; }`)
  })

  it('should properly update style if props used in styles is changed', () => {
    const Comp1 = styled.div`
      color: ${props => props.theme.color};
      z-index: ${props => props.zIndex}px;
    `

    Comp1.defaultProps = {
      theme: {
        color: "purple"
      },
      zIndex: 0
    }
    const wrapper = mount(
      <Comp1 />
    )
    let expectedStyles = `.a { color: purple; z-index: 0px; }`
    expectCSSMatches(expectedStyles)

    wrapper.setProps({ theme: { color: 'pink' } })
    expectedStyles = `${expectedStyles}.b { color: pink; z-index: 0px; }`
    expectCSSMatches(expectedStyles)

    wrapper.setProps({ zIndex: 1 });
    expectCSSMatches(`${expectedStyles}.c { color: pink; z-index: 1px; }`)
  })

  it('should change the classnames when the theme changes', () => {
    const Comp = styled.div`
      color: ${props => props.theme.color};
    `

    const originalTheme = { color: 'black' }
    const newTheme = { color: 'blue' }

    const Theme = ({ theme }) => (
      <ThemeProvider theme={theme}>
        <Comp someProps={theme} />
      </ThemeProvider>
    )

    const wrapper = mount(
      <Theme theme={originalTheme} />
    )

    expectCSSMatches(`.a { color: ${originalTheme.color}; }`)
    expect(wrapper.find('div').prop('className')).toBe('a')

    // Change theme
    wrapper.setProps({ theme: newTheme })

    expectCSSMatches(`.a { color: ${originalTheme.color}; }.b { color: ${newTheme.color}; }`)
    expect(wrapper.find('div').prop('className')).toBe('b')
  })
})
<|endoftext|>


<commit_before>import {field, isDate} from 'vega-util';
import inrange from './inrange';

var UNION = 'union',
    UNIT = 'unit',
    OTHERS = 'others';

function testPoint(datum, entry) {
  var fields = entry.fields,
      values = entry.values,
      getter = entry.getter || (entry.getter = []),
      n = fields.length,
      i = 0;

  for (; i<n; ++i) {
    getter[i] = getter[i] || field(fields[i]);
    if (getter[i](datum) !== values[i]) return false;
  }

  return true;
}

function testInterval(datum, entry) {
  var ivals = entry.intervals,
      n = ivals.length,
      i = 0,
      getter;

  for (; i<n; ++i) {
    getter = ivals[i].getter || (ivals[i].getter = field(ivals[i].field));
    if (ivals[i].extent[0] === ivals[i].extent[1]) return true;
    if (!inrange(getter(datum), ivals[i].extent)) return false;
  }
  return true;
}

/**
 * Tests if a tuple is contained within an interactive selection.
 * @param {string} name - The name of the data set representing the selection.
 * @param {*} unit - A unique key value indicating the current unit chart.
 * @param {object} datum - The tuple to test for inclusion.
 * @param {string} op - The set operation for combining selections.
 *   One of 'intersect' (default) or 'union'.
 * @param {string} scope - The scope within which to resolve the selection.
 *   One of 'all' (default, resolve against active selections across all unit charts),
 *   'unit' (consider only selections in the current unit chart),
 *   'others' (resolve against all units *except* the current unit).
 * @param {function(object,object):boolean} test - A boolean-valued test
 *   predicate for determining selection status within a single unit chart.
 * @return {boolean} - True if the datum is in the selection, false otherwise.
 */
function vlSelection(name, unit, datum, op, scope, test) {
  var data = this.context.data[name],
      entries = data ? data.values.value : [],
      intersect = op !== UNION,
      n = entries.length,
      i = 0,
      entry, b;

  for (; i<n; ++i) {
    entry = entries[i];

    // is the selection entry from the current unit?
    b = unit === entry.unit;

    // perform test if source unit is a valid selection source
    if (!(scope === OTHERS && b || scope === UNIT && !b)) {
      b = test(datum, entry);

      // if we find a match and we don't require intersection return true
      // if we find a miss and we do require intersection return false
      if (intersect ^ b) return b;
    }
  }

  // if intersecting and we made it here, then we saw no misses
  // if not intersecting, then we saw no matches
  // if no active selections, return true
  return !n || intersect;
}

// Assumes point selection tuples are of the form:
// {unit: string, encodings: array<string>, fields: array<string>, values: array<*>, }
export function vlPoint(name, unit, datum, op, scope) {
  return vlSelection.call(this, name, unit, datum, op, scope, testPoint);
}

// Assumes interval selection typles are of the form:
// {unit: string, intervals: array<{encoding: string, field:string, extent:array<number>}>}
export function vlInterval(name, unit, datum, op, scope) {
  return vlSelection.call(this, name, unit, datum, op, scope, testInterval);
}

/**
 * Materializes a point selection as a scale domain. With point selections,
 * we assume that they are projected over a single field or encoding channel.
 * @param   {string} name - The name of the dataset representing the selection.
 * @param   {string} [encoding] - A particular encoding channel to materialize.
 * @param   {string} [field] - A particular field to materialize.
 * @param   {string} [op='intersect'] - The set operation for combining selections.
 * One of 'intersect' (default) or 'union'.
 * @returns {array} An array of values to serve as a scale domain.
 */
export function vlPointDomain(name, encoding, field, op) {
  var data = this.context.data[name],
      entries = data ? data.values.value : [],
      units = [], values = {}, domain = [],
      i = 0, n = entries.length,
      entry, value, unit;

  for (; i<n; ++i) {
    entry = entries[i];
    unit  = entry.unit;
    value = entry.values[0];

    if (units.indexOf(unit) < 0) units.push(unit);
    if ((encoding && entry.encodings[0] === encoding) ||
        (field && entry.fields[0] === field)) {
      values[value] = values[value] || {value: value, units: []};
      if (values[value].units.indexOf(unit) < 0) values[value].units.push(unit);
    }
  }

  for (var key in values) {
    value = values[key];
    if (op !== UNION && value.units.length !== units.length) continue;
    domain.push(value.value);
  }

  return domain.length ? domain : undefined;
}

function asc(a, b) { return a-b; }

function enclosesInterval(a, b) { return a[0] <= b[0] && a[1] >= b[1]; }

function unionInterval(a, b) {
  return [Math.min(a[0], b[0]), Math.max(a[1], b[1])];
}

function intersectInterval(a, b) {
  if (a[0] > b[0]) b[0] = a[0];
  if (a[1] < b[1]) b[1] = a[1];
  return b;
}

/**
 * Materializes an interval selection as a scale domain.
 * @param   {string} name - The name of the dataset representing the selection.
 * @param   {string} [encoding] - A particular encoding channel to materialize.
 * @param   {string} [field] - A particular field to materialize.
 * @param   {string} [op='intersect'] - The set operation for combining selections.
 * One of 'intersect' (default) or 'union'.
 * @returns {array} An array of values to serve as a scale domain.
 */
export function vlIntervalDomain(name, encoding, field, op) {
  var data = this.context.data[name],
      entries = data ? data.values.value : [],
      i = 0, n = entries.length,
      extents = [],
      entry, m, j, interval;

  for (; i<n; ++i) {
    entry = entries[i];

    for (j=0, m=entry.intervals.length; j<m; ++j) {
      interval = entry.intervals[j];
      if ((encoding && interval.encoding === encoding) ||
        (field && interval.field === field)) {
        extents.push(interval.extent.slice(0).sort(asc));
      }
    }
  }

  var domain = extents.reduce(function(domain, ext) {
    if (!domain.length) return ext;
    if (op === UNION) {
      return unionInterval(ext, domain);
    } else {
      return enclosesInterval(ext, domain) ? intersectInterval(domain, ext) :
        enclosesInterval(domain, ext) ? intersectInterval(ext, domain) : [];
    }
  }, []);

  return domain.length &&
    (isDate(domain[0]) ?
      domain[0].getTime() !== domain[1].getTime() :
      domain[0] !== domain[1]) ? domain : undefined;
}
<commit_msg>Clean up selection domain methods.<commit_after>import {field} from 'vega-util';
import inrange from './inrange';

var UNION = 'union',
    UNIT = 'unit',
    OTHERS = 'others';

function testPoint(datum, entry) {
  var fields = entry.fields,
      values = entry.values,
      getter = entry.getter || (entry.getter = []),
      n = fields.length,
      i = 0;

  for (; i<n; ++i) {
    getter[i] = getter[i] || field(fields[i]);
    if (getter[i](datum) !== values[i]) return false;
  }

  return true;
}

function testInterval(datum, entry) {
  var ivals = entry.intervals,
      n = ivals.length,
      i = 0,
      getter;

  for (; i<n; ++i) {
    getter = ivals[i].getter || (ivals[i].getter = field(ivals[i].field));
    if (ivals[i].extent[0] === ivals[i].extent[1]) return true;
    if (!inrange(getter(datum), ivals[i].extent)) return false;
  }
  return true;
}

/**
 * Tests if a tuple is contained within an interactive selection.
 * @param {string} name - The name of the data set representing the selection.
 * @param {*} unit - A unique key value indicating the current unit chart.
 * @param {object} datum - The tuple to test for inclusion.
 * @param {string} op - The set operation for combining selections.
 *   One of 'intersect' (default) or 'union'.
 * @param {string} scope - The scope within which to resolve the selection.
 *   One of 'all' (default, resolve against active selections across all unit charts),
 *   'unit' (consider only selections in the current unit chart),
 *   'others' (resolve against all units *except* the current unit).
 * @param {function(object,object):boolean} test - A boolean-valued test
 *   predicate for determining selection status within a single unit chart.
 * @return {boolean} - True if the datum is in the selection, false otherwise.
 */
function vlSelection(name, unit, datum, op, scope, test) {
  var data = this.context.data[name],
      entries = data ? data.values.value : [],
      intersect = op !== UNION,
      n = entries.length,
      i = 0,
      entry, b;

  for (; i<n; ++i) {
    entry = entries[i];

    // is the selection entry from the current unit?
    b = unit === entry.unit;

    // perform test if source unit is a valid selection source
    if (!(scope === OTHERS && b || scope === UNIT && !b)) {
      b = test(datum, entry);

      // if we find a match and we don't require intersection return true
      // if we find a miss and we do require intersection return false
      if (intersect ^ b) return b;
    }
  }

  // if intersecting and we made it here, then we saw no misses
  // if not intersecting, then we saw no matches
  // if no active selections, return true
  return !n || intersect;
}

// Assumes point selection tuples are of the form:
// {unit: string, encodings: array<string>, fields: array<string>, values: array<*>, }
export function vlPoint(name, unit, datum, op, scope) {
  return vlSelection.call(this, name, unit, datum, op, scope, testPoint);
}

// Assumes interval selection typles are of the form:
// {unit: string, intervals: array<{encoding: string, field:string, extent:array<number>}>}
export function vlInterval(name, unit, datum, op, scope) {
  return vlSelection.call(this, name, unit, datum, op, scope, testInterval);
}

/**
 * Materializes a point selection as a scale domain. With point selections,
 * we assume that they are projected over a single field or encoding channel.
 * @param {string} name - The name of the dataset representing the selection.
 * @param {string} [encoding] - A particular encoding channel to materialize.
 * @param {string} [field] - A particular field to materialize.
 * @param {string} [op='intersect'] - The set operation for combining selections.
 * One of 'intersect' (default) or 'union'.
 * @returns {array} An array of values to serve as a scale domain.
 */
export function vlPointDomain(name, encoding, field, op) {
  var data = this.context.data[name],
      entries = data ? data.values.value : [],
      units = {}, count = 0,
      values = {}, domain = [],
      i = 0, n = entries.length,
      entry, unit, v, key;

  for (; i<n; ++i) {
    entry = entries[i];
    unit  = entry.unit;
    key   = entry.values[0];

    if (!units[unit]) units[unit] = ++count;

    if ((encoding && entry.encodings[0] === encoding) ||
        (field && entry.fields[0] === field))
    {
      if (!(v = values[key])) {
        values[key] = v = {value: key, units: {}, count: 0};
      }
      if (!v.units[unit]) v.units[unit] = ++v.count;
    }
  }

  for (key in values) {
    if (op !== UNION && (v = values[key]).count !== count) continue;
    domain.push(v.value);
  }

  return domain.length ? domain : undefined;
}

/**
 * Materializes an interval selection as a scale domain.
 * @param {string} name - The name of the dataset representing the selection.
 * @param {string} [encoding] - A particular encoding channel to materialize.
 * @param {string} [field] - A particular field to materialize.
 * @param {string} [op='intersect'] - The set operation for combining selections.
 * One of 'intersect' (default) or 'union'.
 * @returns {array} An array of values to serve as a scale domain.
 */
export function vlIntervalDomain(name, encoding, field, op) {
  var merge = op === UNION ? unionInterval : intersectInterval,
      data = this.context.data[name],
      entries = data ? data.values.value : [],
      i = 0, n = entries.length,
      entry, m, j, interval, extent, domain, lo, hi;

  for (; i<n; ++i) {
    entry = entries[i].intervals;

    for (j=0, m=entry.length; j<m; ++j) {
      interval = entry[j];
      if ((encoding && interval.encoding === encoding) ||
          (field && interval.field === field))
      {
        extent = interval.extent, lo = extent[0], hi = extent[1];
        if (lo > hi) hi = extent[1], lo = extent[0];
        domain = domain ? merge(domain, lo, hi) : [lo, hi];
      }
    }
  }

  return domain.length && (+domain[0] !== +domain[1]) ? domain : undefined;
}

function unionInterval(domain, lo, hi) {
  if (domain[0] > lo) domain[0] = lo;
  if (domain[1] < hi) domain[1] = hi;
  return domain;
}

function intersectInterval(domain, lo, hi) {
  if (hi < domain[0] || domain[1] < lo) {
    return [];
  } else {
    if (domain[0] < lo) domain[0] = lo;
    if (domain[1] > hi) domain[1] = hi;
  }
  return domain;
}
<|endoftext|>


<commit_before><commit_msg>Updated for use with hapi-bookshelf-crud<commit_after><|endoftext|>


<commit_before><commit_msg>client: added information about messages being received<commit_after><|endoftext|>


<commit_before><commit_msg>Defines SafeScript.toString outside of goog.DEBUG. RELNOTES: Defines SafeScript.toString outside of goog.DEBUG.<commit_after><|endoftext|>


<commit_before>
function getLastGlucose(data) {

    var one = data[0];
    var two = data[1];
    var o = {
        delta: one.glucose - two.glucose
        , glucose: one.glucose
    };

    return o;

}

function setTempBasal(rate, duration) {

    maxSafeBasal = Math.min(profile_data.max_basal, 2 * profile_data.max_daily_basal, 4 * profile_data.current_basal);

    if (rate < 0) { rate = 0; }

    else if (rate > maxSafeBasal) { rate = maxSafeBasal; }

    requestedTemp.duration = duration;
    requestedTemp.rate = rate;

};

if (!module.parent) {
    var iob_input = process.argv.slice(2, 3).pop()
    var temps_input = process.argv.slice(3, 4).pop()
    var glucose_input = process.argv.slice(4, 5).pop()
    var profile_input = process.argv.slice(5, 6).pop()

    if (!iob_input || !temps_input || !glucose_input || !profile_input) {
        console.log('usage: ', process.argv.slice(0, 2), '<iob.json> <current-temps.json> <glucose.json> <profile.json>');
        process.exit(1);
    }

    var glucose_data = require('./' + glucose_input);
    var temps_data = require('./' + temps_input);
    var iob_data = require('./' + iob_input);
    var profile_data = require('./' + profile_input);

    var glucose_status = getLastGlucose(glucose_data);
    var eventualBG = glucose_status.glucose - (iob_data.iob * profile_data.sens);
    var requestedTemp = {
        'temp': 'absolute'
    };

    //if old reading from Dexcom do nothing

    var systemTime = new Date();
    var displayTime = new Date(glucose_data[0].display_time.replace('T', ' '));
    var minAgo = (systemTime - displayTime) / 60 / 1000

    if (minAgo < 10) {

        if (eventualBG < profile_data.min_bg - 30) {

            if (glucose_status.delta > 0) {

                if (temps_data.rate > profile_data.current_basal) {

                    setTempBasal(0, 0);

                }
            }

            else if (glucose_status.delta <= 0) {
                setTempBasal(0, 30);

            }

        } else {

            if ((glucose_status.delta > 0 && eventualBG < profile_data.min_bg) || (glucose_status.delta < 0 && eventualBG >= profile_data.max_bg)) {
                // cancel temp
                setTempBasal(0, 0);

            } else if (eventualBG < profile_data.min_bg) {

                var insulinReq = Math.max(0, (profile_data.target_bg - eventualBG) / profile_data.sens);
                var rate = temps_data.rate - (2 * insulinReq);
                setTempBasal(rate, 30);

            } else if (eventualBG > profile_data.max_bg) {
                var insulinReq = (profile_data.target_bg - eventualBG) / profile_data.sens;
                var rate = temps_data.rate - (2 * insulinReq);
		if (rate > temps_data.rate){
                setTempBasal(rate, 30);}

            }
        }
    }

    console.log(JSON.stringify(requestedTemp));
}

<commit_msg>shebang<commit_after>#!/usr/bin/env node

function getLastGlucose(data) {

    var one = data[0];
    var two = data[1];
    var o = {
        delta: one.glucose - two.glucose
        , glucose: one.glucose
    };

    return o;

}

function setTempBasal(rate, duration) {

    maxSafeBasal = Math.min(profile_data.max_basal, 2 * profile_data.max_daily_basal, 4 * profile_data.current_basal);

    if (rate < 0) { rate = 0; }

    else if (rate > maxSafeBasal) { rate = maxSafeBasal; }

    requestedTemp.duration = duration;
    requestedTemp.rate = rate;

};

if (!module.parent) {
    var iob_input = process.argv.slice(2, 3).pop()
    var temps_input = process.argv.slice(3, 4).pop()
    var glucose_input = process.argv.slice(4, 5).pop()
    var profile_input = process.argv.slice(5, 6).pop()

    if (!iob_input || !temps_input || !glucose_input || !profile_input) {
        console.log('usage: ', process.argv.slice(0, 2), '<iob.json> <current-temps.json> <glucose.json> <profile.json>');
        process.exit(1);
    }

    var glucose_data = require('./' + glucose_input);
    var temps_data = require('./' + temps_input);
    var iob_data = require('./' + iob_input);
    var profile_data = require('./' + profile_input);

    var glucose_status = getLastGlucose(glucose_data);
    var eventualBG = glucose_status.glucose - (iob_data.iob * profile_data.sens);
    var requestedTemp = {
        'temp': 'absolute'
    };

    //if old reading from Dexcom do nothing

    var systemTime = new Date();
    var displayTime = new Date(glucose_data[0].display_time.replace('T', ' '));
    var minAgo = (systemTime - displayTime) / 60 / 1000

    if (minAgo < 10) {

        if (eventualBG < profile_data.min_bg - 30) {

            if (glucose_status.delta > 0) {

                if (temps_data.rate > profile_data.current_basal) {

                    setTempBasal(0, 0);

                }
            }

            else if (glucose_status.delta <= 0) {
                setTempBasal(0, 30);

            }

        } else {

            if ((glucose_status.delta > 0 && eventualBG < profile_data.min_bg) || (glucose_status.delta < 0 && eventualBG >= profile_data.max_bg)) {
                // cancel temp
                setTempBasal(0, 0);

            } else if (eventualBG < profile_data.min_bg) {

                var insulinReq = Math.max(0, (profile_data.target_bg - eventualBG) / profile_data.sens);
                var rate = temps_data.rate - (2 * insulinReq);
                setTempBasal(rate, 30);

            } else if (eventualBG > profile_data.max_bg) {
                var insulinReq = (profile_data.target_bg - eventualBG) / profile_data.sens;
                var rate = temps_data.rate - (2 * insulinReq);
		if (rate > temps_data.rate){
                setTempBasal(rate, 30);}

            }
        }
    }

    console.log(JSON.stringify(requestedTemp));
}

<|endoftext|>


<commit_before>var webview = document.getElementById('gmail')
var ipcRenderer = require('electron').ipcRenderer;

webview.addEventListener('page-title-updated', function(e) {
  document.getElementsByTagName("title")[0].text = e.title
});

webview.addEventListener('ipc-message', function(event) {
  ipcRenderer.send(event.channel, event.args[0])
  console.log('renderer forwarded ipc from guest to main process', event);
});

ipcRenderer.on('decrypt-result', function(event, arg) {
  webview.send('decrypt-result', arg)
});

webview.addEventListener("dom-ready", function() {
  //webview.openDevTools();
  webview.send('init');
});
<commit_msg>remove a verbose log<commit_after>var webview = document.getElementById('gmail')
var ipcRenderer = require('electron').ipcRenderer;

webview.addEventListener('page-title-updated', function(e) {
  document.getElementsByTagName("title")[0].text = e.title
});

webview.addEventListener('ipc-message', function(event) {
  ipcRenderer.send(event.channel, event.args[0])
});

ipcRenderer.on('decrypt-result', function(event, arg) {
  webview.send('decrypt-result', arg)
});

webview.addEventListener("dom-ready", function() {
  //webview.openDevTools();
  webview.send('init');
});
<|endoftext|>


<commit_before>/**
 * User: riza
 * Date: 23-04-13
 * Time: 11:28
 */

/**
 * GLOBAL PARAMETERS FOR GENERIC COMPONENTS
 * @type {number}
 */
var GT_JOB_TYPE = 'aCGHgroupTest';

var groupTestView;

/**
 * Buttons for Input Panel
 * @type {Array}
 */
var gtInputBarBtnList = ['->',{  // '->' making it right aligned
	xtype: 'button',
	text: 'Run Analysis',
	scale: 'medium',
	iconCls: 'runbutton',
	handler: function () {
		groupTestView.submitGroupTestJob();
	}
}];

var GroupTestInputWidget = Ext.extend(GenericAnalysisInputBar, {

	regionPanel: null,
	groupPanel: null,
	statTestPanel: null,
	alterationPanel: null,

	statRadios: [
		{boxLabel: 'Chi-square', name: 'stat-test-opt', XValue:'Chi-square'},
		{boxLabel: 'Wilcoxon', name: 'stat-test-opt', XValue:'Wilcoxon'},
		{boxLabel: 'Kruskal-Wallis', name: 'stat-test-opt', XValue:'KW'}
	],

	alterationRadios: [
		{boxLabel: 'GAIN', name: 'cb-col', XValue:'1'},
		{boxLabel: 'LOSS', name: 'cb-col', XValue:'-1'},
		{boxLabel: 'BOTH', name: 'cb-col', XValue:'0'}
	],

	constructor: function(config) {
		GroupTestInputWidget.superclass.constructor.apply(this, arguments);
		this.init();
	},

	init: function() {

		// define child panel configs
		var childPanelConfig = [{
			title: 'Region',
			id:  'gt-input-region',
			isDroppable: true,
			notifyFunc: dropOntoCategorySelection,
			toolTipTitle: 'Tip: Region',
			toolTipTxt: 'Drag and drop aCGH region here.'
		},{
			title: 'Group',
			id:  'gt-input-group',
			isDroppable: true,
			notifyFunc: dropOntoCategorySelection,
			toolTipTitle: 'Tip: Group',
			toolTipTxt: 'Drag and drop clinical variables to associate copy number data with. Please keep in mind that only ' +
				'one variable can be compared, e.g. gender (female) with gender (male); not gender (female) with age ' +
				'(>60)!'
		},{
			title: 'Statistical Test',
			id: 'gt-input-stat-test',
			toolTipTitle: 'Tip: Statistical Test',
			toolTipTxt: '

Chi-square: test for association between aberration pattern and group label; can also do ' +
				'multiple comparisons

Wilcoxon: rank-sum test for two groups

Kruskal-Wallis: generalisation of ' +
				'Wilcoxon for more than two groups

'
		},{
			title: 'Alteration Type',
			id:  'gt-input-alteration',
			toolTipTitle: 'Tip: Alteration Type',
			toolTipTxt: 'Select type of chromosomal alteration to test the association.'
		}];

		// create child panels
		this.regionPanel = this.createChildPanel(childPanelConfig[0]);
		this.groupPanel = this.createChildPanel(childPanelConfig[1]);
		this.statTestPanel = this.createChildPanel(childPanelConfig[2]);
		this.alterationPanel = this.createChildPanel(childPanelConfig[3]);

		// create check boxes
		this.statTestPanel.add(this.createRadioBtnGroup(this.statRadios,'stat-test-chk-group'));
		this.alterationPanel.add(this.createRadioBtnGroup(this.alterationRadios,'alteration-types-chk-group'));

		// re-draw
		this.doLayout();
	}
});

/**
 * list of result grid columns
 * @type {Array}
 * @private
 */
var _grouptestgrid_columns = [{
    id: 'chromosome', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 })
    header: "chromosome",
    dataIndex: 'chromosome',
    width: 400,
    sortable: true
},{
    header: "cytoband",
    dataIndex: 'cytoband',
    width: 100,
    sortable: true
},{
    header: "start",
    dataIndex: 'start',
    width: 100,
    sortable: true
},{
    header: "end",
    dataIndex: 'end',
    width: 100,
    sortable: true
},{
    header: "pvalue",
    dataIndex: 'pvalue',
    width: 100,
    align: 'right',
    sortable: true
},{
    id: 'fdr',
    header: "fdr",
    dataIndex: 'fdr',
    align: 'right',
    width: 100,
    sortable: true
}];

/**
 * This object represents the intermediate result grid
 * @type {*|Object}
 */
var GroupTestResultGrid = Ext.extend(GenericAnalysisResultGrid, {

    jobName : '',

    constructor: function(config) {
        GroupTestResultGrid.superclass.constructor.apply(this, arguments);
        this.init();
    },

    init: function () {

    },

    /**
     * Get selected rows from the grid
     * @returns {*}
     */
    getSelectedRows: function () {
        return this.getSelectionModel().getSelections();
    },

    /**
     * Display selected rows in tab panel
     * @param selectedRegions
     */
    displaySurvivalPlot: function (selectedRegions) {

        var outer_scope = this;

        // creating tab panel
        if (this.plotCurvePanel == null) {
            this.plotCurvePanel =  new GenericTabPlotPanel({
                id: 'plotResultCurve',
                renderTo: 'plotResultWrapper'
            });
        }
    }
});

/**
 * This class represents the whole Group Test view
 * @type {*|Object}
 */
var GroupTestView = Ext.extend(GenericAnalysisView, {

	// input panel
	inputBar : null,

	// result panel
	resultPanel : null,

	// alteration
	alteration : '',

    // job info
    jobInfo : null,

	// constructor
	constructor: function() {
		this.init();
	},

	init: function() {

		// first of all, let's reset all major components
		this.resetAll();

		// draw input panel
		this.inputBar = new GroupTestInputWidget({
			id: 'gtInputPanel',
			title: 'Input Parameters',
			iconCls: 'newbutton',
			renderTo: 'gtContainer',
			bbar: this.createInputToolBar()
		});
	},

    redraw: function () {
        this.inputBar.doLayout();
    },

	resetAll: function () {
		this.tabIndex = 0;
		Ext.destroy(this.inputBar);
		Ext.destroy(this.resultPanel);
	},

	createInputToolBar: function() {
		var _this = this;
		return new Ext.Toolbar({
			height: 30,
			items: gtInputBarBtnList
		});
	},

	areAllMandatoryFieldsFilled: function() {
		var isValid = true;
		var invalidInputs = [];

		var statisticalVal;
		var alterationVal;

		// check if region panel is empty
		if (this.inputBar.regionPanel.isEmpty()) {
			invalidInputs.push(this.inputBar.regionPanel.title);
			isValid = false;
		}

		// check if group panel is empty
		if (this.inputBar.groupPanel.isEmpty()) {
			invalidInputs.push(this.inputBar.groupPanel.title);
			isValid = false;
		}

		//check if stat test values has been selected
		var statChkGroup = this.inputBar.statTestPanel.getComponent('stat-test-chk-group');
		statisticalVal =  statChkGroup.getXValues();
		if (statisticalVal.length < 1) {
			isValid = false;
			invalidInputs.push(this.inputBar.statTestPanel.title);
		}

		//check if alteration values has been selected
		var alterationChkGroup = this.inputBar.alterationPanel.getComponent('alteration-types-chk-group');
		alterationVal =  alterationChkGroup.getSelectedValue();

		if (alterationVal.length < 1) {
			isValid = false;
			invalidInputs.push(this.inputBar.alterationPanel.title);
		}

		if (!isValid) {
			var strErrMsg = 'Following needs to be defined: ';
			invalidInputs.each(function (item) {
				strErrMsg += '['+item + '] ';
			})

			// inform user on mandatory inputs need to be defined
			Ext.MessageBox.show({
				title: 'Missing mandatory inputs',
				msg: strErrMsg,
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.ERROR
			});

		}

		return isValid;
	},

	isGroupFieldValid: function() {
		if (this.inputBar.groupPanel.getConceptCodes().length < 2) {
			Ext.MessageBox.show({
				title: 'Number of groups',
				msg: 'There should be selected more than one group.',
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.ERROR
			});
			return false;
		}
		return true;
	},

	validateInputs: function() {
		return this.areAllMandatoryFieldsFilled() && this.isGroupFieldValid()
	},

	createResultPlotPanel: function (jobName, view) {

		var _this = view;

		// initialize image path
		var imagePath = '';

		// get image path
		Ext.Ajax.request({
			url: pageInfo.basePath+"/aCGHgroupTest/imagePath",
			method: 'POST',
			success: function(result, request){

				console.log('result', result.responseText);
				imagePath = result.responseText;

				_this.resultPanel = new GenericPlotPanel({
					id: 'plotResultCurve',
					renderTo: 'gtPlotWrapper',
					width:'100%',
					frame:true,
					height:600,
					defaults: {autoScroll:true}
				});

				// Getting the template as blue print for survival curve plot.
				// Template is defined in GroupTestaCGH.gsp
				var groupTestPlotTpl = Ext.Template.from('template-group-test-plot');

                var translatedAlteration = groupTestView.translateAlteration(
                    groupTestView.jobInfo.jobInputsJson.aberrationType
                );

                var groupVariable = groupTestView.jobInfo.jobInputsJson.groupVariable;
                var groupVariableHtml = groupVariable ? groupVariable.replace('|', '
') : '';
				// create data instance
				var region = {
					filename: imagePath,
                    jobName: groupTestView.jobInfo.name,
                    startDate: groupTestView.jobInfo.startDate,
                    runTime: groupTestView.jobInfo.runTime,
                    inputRegion: groupTestView.jobInfo.jobInputsJson.regionVariable,
                    inputGroupVariable: groupVariableHtml,
                    inputStatisticsType: groupTestView.jobInfo.jobInputsJson.statisticsType,
                    inputCohort1: groupTestView.jobInfo.jobInputsJson.result_instance_id1,
                    inputCohort2: groupTestView.jobInfo.jobInputsJson.result_instance_id2,
                    inputAlteration: translatedAlteration
				};

				// generate template
				groupTestPlotTpl.overwrite(Ext.get('gtPlotWrapper'), region);

				// generate download button
				var exportBtn = new Ext.Button ({
					text : 'Download Result',
					iconCls : 'downloadbutton',
					renderTo: 'downloadBtn',
					handler: function () {
						_this.downloadGroupTestResult(jobName);
					}
				});
			},
			params: {
				jobName: jobName,
				alteration: _this.alteration
			}
		});
	},

    /**
     * generates intermediate result in grid panel
     * @param data
     */
    generateResultGrid: function (jobName, view) {

        var _this = this;

        Ext.Ajax.request ({
            // retrieve information about the job (status, inputs, run-time, etc)
            url: pageInfo.basePath+"/asyncJob/getjobbyname",
            method: 'GET',
            success: function (result, request) {

                var resultJSON = JSON.parse(result.responseText);
                _this.jobInfo = resultJSON.jobs[0];

                // create store data
                var store = new Ext.data.JsonStore({
                    root: 'result',
                    totalProperty: 'totalCount',
                    idProperty: 'threadid',
                    remoteSort: true,   // can be enhanced with remote sort

                    baseParams: {jobName:jobName},

                    fields: [
                        'chromosome',
                        'cytoband',
                        {name: 'start', type: 'int'},
                        {name: 'end', type: 'int'},
                        {name: 'pvalue', type: 'float'},
                        {name: 'fdr', type: 'float'}
                    ],

                    // load using script tags for cross domain, if the data in on the same domain as
                    // this page, an HttpProxy would be better
                    proxy: new Ext.data.HttpProxy({
                        url: "../aCGHgroupTest/resultTable"
                    })

                });
                store.setDefaultSort('chromosome', 'asc');

                // create paging bar with related store
                var pagingbar = new Ext.PagingToolbar({
                    pageSize: GEN_RESULT_GRID_LIMIT,
                    store: store,
                    displayInfo: true,
                    displayMsg: 'Displaying topics {0} - {1} of {2}',
                    emptyMsg: "No topics to display"
                });

                // make sure no instance from previous job
                Ext.destroy(Ext.get('intermediateGridPanel'));

                // create new grid and render it
                view.intermediateResultGrid  = new IntermediateResultGrid({
                    id: 'intermediateGridPanel',
                    title: 'Intermediate Result - Job Name: ' + jobName ,
                    renderTo: 'intermediateResultWrapper',
                    trackMouseOver:false,
                    loadMask: true,
                    columns: _grouptestgrid_columns,
                    store: store,
                    bbar: pagingbar,
                    jobName: jobName
                });

                view.intermediateResultGrid.render();

                // finally load the data
                store.load({params:{start:0, limit:GEN_RESULT_GRID_LIMIT}});
            },
            failure: function (result, request) {
                console.log('failure ....')
            },
            params: {
                jobName: jobName
            }
        })

    },

	downloadGroupTestResult: function (jobName) {

		// clean up
		try {
			Ext.destroy(Ext.get('downloadIframe'));
		}
		catch(e) {}

		// get the file
		Ext.DomHelper.append(document.body, {
			tag: 'iframe',
			id:'downloadIframe',
			frameBorder: 0,
			width: 0,
			height: 0,
			css: 'display:none;visibility:hidden;height:0px;',
			src: pageInfo.basePath+"/aCGHgroupTest/zipFile?jobName=" + jobName
		});
	},

	onJobFinish: function(jobName, view) {
		GLOBAL.CurrentSubsetIDs[1] = null;
		GLOBAL.CurrentSubsetIDs[2] = null;

		this.renderResults(jobName, view);
	},

    renderResults: function(jobName, view) {
        this.generateResultGrid(jobName, view);
        this.createResultPlotPanel(jobName, view);
    },

	submitGroupTestJob: function () {
		if (this.validateInputs()) {

			var regionVal = this.inputBar.regionPanel.getConceptCode();
			var groupVals = this.inputBar.groupPanel.getConceptCodes();
			var statTestComponent = this.inputBar.statTestPanel.getComponent('stat-test-chk-group');
			var statTestVal =  statTestComponent.getSelectedValue();
			var alternationComponent = this.inputBar.alterationPanel.getComponent('alteration-types-chk-group');
			var alternationVal =  alternationComponent.getSelectedValue();

			this.alteration = this.translateAlteration(alternationVal);
			console.log('this.alteration->', this.alteration);

			// compose params
			var formParams = {
				regionVariable: regionVal,
				groupVariable: groupVals,
				statisticsType: statTestVal,
				aberrationType: alternationVal,
				jobType: GT_JOB_TYPE
			};

			var job = this.submitJob(formParams, this.onJobFinish, this);
		}

	}

});

/**
 * Invoked when user selects Group Test aCGH from Analysis combo box
 */
function loadGroupTestaCGHView() {
	// everything starts here ..
	groupTestView = new GroupTestView();
}
<commit_msg>Fix for FT-215 Validate if any input is given for aCGH analysis.<commit_after>/**
 * User: riza
 * Date: 23-04-13
 * Time: 11:28
 */

/**
 * GLOBAL PARAMETERS FOR GENERIC COMPONENTS
 * @type {number}
 */
var GT_JOB_TYPE = 'aCGHgroupTest';

var groupTestView;

/**
 * Buttons for Input Panel
 * @type {Array}
 */
var gtInputBarBtnList = ['->',{  // '->' making it right aligned
	xtype: 'button',
	text: 'Run Analysis',
	scale: 'medium',
	iconCls: 'runbutton',
	handler: function () {
		groupTestView.submitGroupTestJob();
	}
}];

var GroupTestInputWidget = Ext.extend(GenericAnalysisInputBar, {

	regionPanel: null,
	groupPanel: null,
	statTestPanel: null,
	alterationPanel: null,

	statRadios: [
		{boxLabel: 'Chi-square', name: 'stat-test-opt', XValue:'Chi-square'},
		{boxLabel: 'Wilcoxon', name: 'stat-test-opt', XValue:'Wilcoxon'},
		{boxLabel: 'Kruskal-Wallis', name: 'stat-test-opt', XValue:'KW'}
	],

	alterationRadios: [
		{boxLabel: 'GAIN', name: 'cb-col', XValue:'1'},
		{boxLabel: 'LOSS', name: 'cb-col', XValue:'-1'},
		{boxLabel: 'BOTH', name: 'cb-col', XValue:'0'}
	],

	constructor: function(config) {
		GroupTestInputWidget.superclass.constructor.apply(this, arguments);
		this.init();
	},

	init: function() {

		// define child panel configs
		var childPanelConfig = [{
			title: 'Region',
			id:  'gt-input-region',
			isDroppable: true,
			notifyFunc: dropOntoCategorySelection,
			toolTipTitle: 'Tip: Region',
			toolTipTxt: 'Drag and drop aCGH region here.'
		},{
			title: 'Group',
			id:  'gt-input-group',
			isDroppable: true,
			notifyFunc: dropOntoCategorySelection,
			toolTipTitle: 'Tip: Group',
			toolTipTxt: 'Drag and drop clinical variables to associate copy number data with. Please keep in mind that only ' +
				'one variable can be compared, e.g. gender (female) with gender (male); not gender (female) with age ' +
				'(>60)!'
		},{
			title: 'Statistical Test',
			id: 'gt-input-stat-test',
			toolTipTitle: 'Tip: Statistical Test',
			toolTipTxt: '

Chi-square: test for association between aberration pattern and group label; can also do ' +
				'multiple comparisons

Wilcoxon: rank-sum test for two groups

Kruskal-Wallis: generalisation of ' +
				'Wilcoxon for more than two groups

'
		},{
			title: 'Alteration Type',
			id:  'gt-input-alteration',
			toolTipTitle: 'Tip: Alteration Type',
			toolTipTxt: 'Select type of chromosomal alteration to test the association.'
		}];

		// create child panels
		this.regionPanel = this.createChildPanel(childPanelConfig[0]);
		this.groupPanel = this.createChildPanel(childPanelConfig[1]);
		this.statTestPanel = this.createChildPanel(childPanelConfig[2]);
		this.alterationPanel = this.createChildPanel(childPanelConfig[3]);

		// create check boxes
		this.statTestPanel.add(this.createRadioBtnGroup(this.statRadios,'stat-test-chk-group'));
		this.alterationPanel.add(this.createRadioBtnGroup(this.alterationRadios,'alteration-types-chk-group'));

		// re-draw
		this.doLayout();
	}
});

/**
 * list of result grid columns
 * @type {Array}
 * @private
 */
var _grouptestgrid_columns = [{
    id: 'chromosome', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 })
    header: "chromosome",
    dataIndex: 'chromosome',
    width: 400,
    sortable: true
},{
    header: "cytoband",
    dataIndex: 'cytoband',
    width: 100,
    sortable: true
},{
    header: "start",
    dataIndex: 'start',
    width: 100,
    sortable: true
},{
    header: "end",
    dataIndex: 'end',
    width: 100,
    sortable: true
},{
    header: "pvalue",
    dataIndex: 'pvalue',
    width: 100,
    align: 'right',
    sortable: true
},{
    id: 'fdr',
    header: "fdr",
    dataIndex: 'fdr',
    align: 'right',
    width: 100,
    sortable: true
}];

/**
 * This object represents the intermediate result grid
 * @type {*|Object}
 */
var GroupTestResultGrid = Ext.extend(GenericAnalysisResultGrid, {

    jobName : '',

    constructor: function(config) {
        GroupTestResultGrid.superclass.constructor.apply(this, arguments);
        this.init();
    },

    init: function () {

    },

    /**
     * Get selected rows from the grid
     * @returns {*}
     */
    getSelectedRows: function () {
        return this.getSelectionModel().getSelections();
    },

    /**
     * Display selected rows in tab panel
     * @param selectedRegions
     */
    displaySurvivalPlot: function (selectedRegions) {

        var outer_scope = this;

        // creating tab panel
        if (this.plotCurvePanel == null) {
            this.plotCurvePanel =  new GenericTabPlotPanel({
                id: 'plotResultCurve',
                renderTo: 'plotResultWrapper'
            });
        }
    }
});

/**
 * This class represents the whole Group Test view
 * @type {*|Object}
 */
var GroupTestView = Ext.extend(GenericAnalysisView, {

	// input panel
	inputBar : null,

	// result panel
	resultPanel : null,

	// alteration
	alteration : '',

    // job info
    jobInfo : null,

	// constructor
	constructor: function() {
		this.init();
	},

	init: function() {

		// first of all, let's reset all major components
		this.resetAll();

		// draw input panel
		this.inputBar = new GroupTestInputWidget({
			id: 'gtInputPanel',
			title: 'Input Parameters',
			iconCls: 'newbutton',
			renderTo: 'gtContainer',
			bbar: this.createInputToolBar()
		});
	},

    redraw: function () {
        this.inputBar.doLayout();
    },

	resetAll: function () {
		this.tabIndex = 0;
		Ext.destroy(this.inputBar);
		Ext.destroy(this.resultPanel);
	},

	createInputToolBar: function() {
		var _this = this;
		return new Ext.Toolbar({
			height: 30,
			items: gtInputBarBtnList
		});
	},

	areAllMandatoryFieldsFilled: function() {
		var isValid = true;
		var invalidInputs = [];

		var statisticalVal;
		var alterationVal;

		// check if region panel is empty
		if (this.inputBar.regionPanel.isEmpty()) {
			invalidInputs.push(this.inputBar.regionPanel.title);
			isValid = false;
		}

		// check if group panel is empty
		if (this.inputBar.groupPanel.isEmpty()) {
			invalidInputs.push(this.inputBar.groupPanel.title);
			isValid = false;
		}

		//check if stat test values has been selected
		var statChkGroup = this.inputBar.statTestPanel.getComponent('stat-test-chk-group');
		statisticalVal =  statChkGroup.getXValues();
		if (statisticalVal.length < 1) {
			isValid = false;
			invalidInputs.push(this.inputBar.statTestPanel.title);
		}

		//check if alteration values has been selected
		var alterationChkGroup = this.inputBar.alterationPanel.getComponent('alteration-types-chk-group');
		alterationVal =  alterationChkGroup.getSelectedValue();

		if (!alterationVal) {
			isValid = false;
			invalidInputs.push(this.inputBar.alterationPanel.title);
		}

		if (!isValid) {
			var strErrMsg = 'Following needs to be defined: ';
			invalidInputs.each(function (item) {
				strErrMsg += '['+item + '] ';
			})

			// inform user on mandatory inputs need to be defined
			Ext.MessageBox.show({
				title: 'Missing mandatory inputs',
				msg: strErrMsg,
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.ERROR
			});

		}

		return isValid;
	},

	isGroupFieldValid: function() {
		if (this.inputBar.groupPanel.getConceptCodes().length < 2) {
			Ext.MessageBox.show({
				title: 'Number of groups',
				msg: 'There should be selected more than one group.',
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.ERROR
			});
			return false;
		}
		return true;
	},

	validateInputs: function() {
		return this.areAllMandatoryFieldsFilled() && this.isGroupFieldValid()
	},

	createResultPlotPanel: function (jobName, view) {

		var _this = view;

		// initialize image path
		var imagePath = '';

		// get image path
		Ext.Ajax.request({
			url: pageInfo.basePath+"/aCGHgroupTest/imagePath",
			method: 'POST',
			success: function(result, request){

				console.log('result', result.responseText);
				imagePath = result.responseText;

				_this.resultPanel = new GenericPlotPanel({
					id: 'plotResultCurve',
					renderTo: 'gtPlotWrapper',
					width:'100%',
					frame:true,
					height:600,
					defaults: {autoScroll:true}
				});

				// Getting the template as blue print for survival curve plot.
				// Template is defined in GroupTestaCGH.gsp
				var groupTestPlotTpl = Ext.Template.from('template-group-test-plot');

                var translatedAlteration = groupTestView.translateAlteration(
                    groupTestView.jobInfo.jobInputsJson.aberrationType
                );

                var groupVariable = groupTestView.jobInfo.jobInputsJson.groupVariable;
                var groupVariableHtml = groupVariable ? groupVariable.replace('|', '
') : '';
				// create data instance
				var region = {
					filename: imagePath,
                    jobName: groupTestView.jobInfo.name,
                    startDate: groupTestView.jobInfo.startDate,
                    runTime: groupTestView.jobInfo.runTime,
                    inputRegion: groupTestView.jobInfo.jobInputsJson.regionVariable,
                    inputGroupVariable: groupVariableHtml,
                    inputStatisticsType: groupTestView.jobInfo.jobInputsJson.statisticsType,
                    inputCohort1: groupTestView.jobInfo.jobInputsJson.result_instance_id1,
                    inputCohort2: groupTestView.jobInfo.jobInputsJson.result_instance_id2,
                    inputAlteration: translatedAlteration
				};

				// generate template
				groupTestPlotTpl.overwrite(Ext.get('gtPlotWrapper'), region);

				// generate download button
				var exportBtn = new Ext.Button ({
					text : 'Download Result',
					iconCls : 'downloadbutton',
					renderTo: 'downloadBtn',
					handler: function () {
						_this.downloadGroupTestResult(jobName);
					}
				});
			},
			params: {
				jobName: jobName,
				alteration: _this.alteration
			}
		});
	},

    /**
     * generates intermediate result in grid panel
     * @param data
     */
    generateResultGrid: function (jobName, view) {

        var _this = this;

        Ext.Ajax.request ({
            // retrieve information about the job (status, inputs, run-time, etc)
            url: pageInfo.basePath+"/asyncJob/getjobbyname",
            method: 'GET',
            success: function (result, request) {

                var resultJSON = JSON.parse(result.responseText);
                _this.jobInfo = resultJSON.jobs[0];

                // create store data
                var store = new Ext.data.JsonStore({
                    root: 'result',
                    totalProperty: 'totalCount',
                    idProperty: 'threadid',
                    remoteSort: true,   // can be enhanced with remote sort

                    baseParams: {jobName:jobName},

                    fields: [
                        'chromosome',
                        'cytoband',
                        {name: 'start', type: 'int'},
                        {name: 'end', type: 'int'},
                        {name: 'pvalue', type: 'float'},
                        {name: 'fdr', type: 'float'}
                    ],

                    // load using script tags for cross domain, if the data in on the same domain as
                    // this page, an HttpProxy would be better
                    proxy: new Ext.data.HttpProxy({
                        url: "../aCGHgroupTest/resultTable"
                    })

                });
                store.setDefaultSort('chromosome', 'asc');

                // create paging bar with related store
                var pagingbar = new Ext.PagingToolbar({
                    pageSize: GEN_RESULT_GRID_LIMIT,
                    store: store,
                    displayInfo: true,
                    displayMsg: 'Displaying topics {0} - {1} of {2}',
                    emptyMsg: "No topics to display"
                });

                // make sure no instance from previous job
                Ext.destroy(Ext.get('intermediateGridPanel'));

                // create new grid and render it
                view.intermediateResultGrid  = new IntermediateResultGrid({
                    id: 'intermediateGridPanel',
                    title: 'Intermediate Result - Job Name: ' + jobName ,
                    renderTo: 'intermediateResultWrapper',
                    trackMouseOver:false,
                    loadMask: true,
                    columns: _grouptestgrid_columns,
                    store: store,
                    bbar: pagingbar,
                    jobName: jobName
                });

                view.intermediateResultGrid.render();

                // finally load the data
                store.load({params:{start:0, limit:GEN_RESULT_GRID_LIMIT}});
            },
            failure: function (result, request) {
                console.log('failure ....')
            },
            params: {
                jobName: jobName
            }
        })

    },

	downloadGroupTestResult: function (jobName) {

		// clean up
		try {
			Ext.destroy(Ext.get('downloadIframe'));
		}
		catch(e) {}

		// get the file
		Ext.DomHelper.append(document.body, {
			tag: 'iframe',
			id:'downloadIframe',
			frameBorder: 0,
			width: 0,
			height: 0,
			css: 'display:none;visibility:hidden;height:0px;',
			src: pageInfo.basePath+"/aCGHgroupTest/zipFile?jobName=" + jobName
		});
	},

	onJobFinish: function(jobName, view) {
		GLOBAL.CurrentSubsetIDs[1] = null;
		GLOBAL.CurrentSubsetIDs[2] = null;

		this.renderResults(jobName, view);
	},

    renderResults: function(jobName, view) {
        this.generateResultGrid(jobName, view);
        this.createResultPlotPanel(jobName, view);
    },

	submitGroupTestJob: function () {
		if (this.validateInputs()) {

			var regionVal = this.inputBar.regionPanel.getConceptCode();
			var groupVals = this.inputBar.groupPanel.getConceptCodes();
			var statTestComponent = this.inputBar.statTestPanel.getComponent('stat-test-chk-group');
			var statTestVal =  statTestComponent.getSelectedValue();
			var alternationComponent = this.inputBar.alterationPanel.getComponent('alteration-types-chk-group');
			var alternationVal =  alternationComponent.getSelectedValue();

			this.alteration = this.translateAlteration(alternationVal);
			console.log('this.alteration->', this.alteration);

			// compose params
			var formParams = {
				regionVariable: regionVal,
				groupVariable: groupVals,
				statisticsType: statTestVal,
				aberrationType: alternationVal,
				jobType: GT_JOB_TYPE
			};

			var job = this.submitJob(formParams, this.onJobFinish, this);
		}

	}

});

/**
 * Invoked when user selects Group Test aCGH from Analysis combo box
 */
function loadGroupTestaCGHView() {
	// everything starts here ..
	groupTestView = new GroupTestView();
}
<|endoftext|>