<commit_before>/*
google apps script

  install:
    Gogole Drive > create spread sheet > tools > script editor > paste this file > save > run onOpen

  schedule:
    Tools > Script editor... > Resources > Current project's triggers > select healthCheck as a function
*/

var SLACK_WEBHOOK_URL = "SLACK_WEBHOOK_URL";
var SLACK_CHANNEL = "SLACK_CHANNEL";

var GREEN1 = "#8ACE6B";
var RED1 = "#F6898A";
var GRAY1 = "#E0E0E0";

function toBool(str) {
  if (typeof str !== "string") return !!str;
  return str.toLowerCase() !== "false";
}

function isEmptyObj(obj) {
  if (!obj) return true;
  if (typeof obj !== "object") return true;
  if (!Object.keys(obj).length) return true;
  return false;
}

function notEmptyObj(obj) { return !isEmptyObj(obj); }

function parseJSON(str) {
  try {
    return JSON.parse(str);
  } catch (err) {
    return str;
  }
}

function jsonToString(obj) {
  return JSON.stringify(obj, null, "  ");
}

function getSheet() { return SpreadsheetApp.getActiveSheet(); }
function ss() { return SpreadsheetApp.getActiveSpreadsheet(); }

function setColor(range, params) {
  params = params || {};
  bgColor = params.bgColor || "#ffffff";
  fontColor = params.fontColor || "#222222";
  range.setBackground(bgColor);
  range.setFontColor(fontColor);
}

function getProps() {
  var dp, slackWebhookUrl, slackChannel;
  dp = PropertiesService.getDocumentProperties();
  slackWebhookUrl = dp.getProperty(SLACK_WEBHOOK_URL);
  slackChannel = dp.getProperty(SLACK_CHANNEL);
  if (!slackWebhookUrl) {
    ss().toast("notification settings not found. see Custom Menu > notification settings");
    return null;
  }
  return {
    slackWebhookUrl: slackWebhookUrl,
    slackChannel: slackChannel
  };
}

// assume the first row is the fields
function getFields(sheet) {
  if (!sheet) sheet = getSheet();

  var rows = sheet.getDataRange();
  var values = rows.getValues();

  if (!values.length) return [];

  return values.shift();
}

function getSheetRows(sheet) {
  if (!sheet) sheet = getSheet();
  var rows = sheet.getDataRange();
  var values = rows.getValues();
  var fields = values.shift();

  if (!values.length) return;

  // skip rows below the lines begin with '#'
  var i = 0;
  while (values[i] && values[i][0].indexOf("#") !== 0) { i++; }
  values = values.slice(0, i);

  var arr = values.map(function(row) {
    var o = {};
    fields.forEach(function(field, i) {
      if (!field) return;
      if (row[i] === "") return;
      o[field] = parseJSON(row[i]);
    });
    return o;
  });

  arr = arr.filter(function(obj) { return notEmptyObj(obj); });
  return arr;
}

function alert(msg) {
  if (msg && Browser && Browser.msgBox) Browser.msgBox(msg);
}

function onOpen() {
  ss().addMenu('Custom Menu', [
    {
      name: 'health check list',
      functionName: 'printContent'
    }, {
      name: 'check right now',
      functionName: 'healthCheck'
    }, {
      name: 'notification settings',
      functionName: 'setDestination'
    }, {
      name: 'help',
      functionName: 'showHelp'
    }
  ]);
}

function printContent() {
  var sheet = getSheetRows();
  if (sheet) alert(jsonToString(sheet));
}

function healthCheck() {
  var sheet = getSheet();
  var rows = getSheetRows();
  if (!rows || !rows.length) alert("health check list is invalid. see Custom Menu > help");
  rows.forEach(function(row, i) { sendRequest(row, i+1, sheet); });
}

function setDestination() {
  var dp = PropertiesService.getDocumentProperties();
  var slackWebhookUrl = Browser.inputBox('Enter Slack Webhook URL (required)', Browser.Buttons.OK_CANCEL);
  var slackChannel = Browser.inputBox('Enter Slack Channel (optional)', Browser.Buttons.OK_CANCEL);

  // slack channel is expected to begin with #
  if (slackChannel && slackChannel.indexOf("#") !== 0) slackChannel = "#" + slackChannel;

  dp.setProperty(SLACK_WEBHOOK_URL, slackWebhookUrl);
  dp.setProperty(SLACK_CHANNEL, slackChannel);
}

function showHelp() {
  alert("no message");
}

function sendMessage(message) {
  sendMessageSlack(message);
}

// curl -X POST --data-urlencode 'payload={"channel": "#general", "text": ""}' https://hooks.slack.com/services/...
function sendMessageSlack(message) {
  var props = getProps() || {};
  var slackWebhookUrl = props.slackWebhookUrl;
  var slackChannel = props.slackChannel;
  if (!slackWebhookUrl) return;

  var payload = JSON.stringify({
    channel: slackChannel || undefined,
    text: message
  });

  var url = slackWebhookUrl;
  var params = {
    method: "post",
    payload: "payload="+payload,
  };

  var response = UrlFetchApp.fetch(url, params);
}

function checkResponseHealth(expect, result) {
  expect = expect || {};

  var success = true;
  Object.keys(expect).forEach(function(key) {
    if (expect[key] && expect[key] !== result[key]) success = false;
  });
  return success;
}

function sendRequest(row, rowIndex, sheet) {
  var url = row.url;
  var params = {
    contentType: row.contentType || undefined,
    headers: row.headers || undefined,
    method: row.method || undefined,
    payload: row.payload || undefined,
    validateHttpsCertificate: toBool(row.validateHttpsCertificate),
    muteHttpExceptions: true
  };
  var expectResponse = parseJSON(row.expectResponse) || {};
  expectResponse.status = expectResponse.status || 200;

  if (!url) return setStatus("URL NOT FOUND", rowIndex, sheet);

  var response;
  try {
    response = UrlFetchApp.fetch(url, params);
  } catch (err) {
    sendMessage(err.toString());
    return setStatus(err, rowIndex, sheet);
  }
  var status = response.getResponseCode();
  var body = response.getContentText();

  var success = checkResponseHealth(expectResponse, { status: status, body: body });
  var failed = !success;

  if (failed) {
    sendMessage("health check failed!\n"+url+"\nstatus: " + status);
  }

  setStatus(status, rowIndex, failed, sheet);
  setBody(body, rowIndex, sheet);
}

function setStatus(status, rowIndex, failed, sheet) {
  var fields = getFields();

  // find index of status field
  var statusIndex = -1;
  fields.forEach(function(el, i) { if (el.toLowerCase() === "status") statusIndex = i; });

  // create fields of status if not exists
  if (statusIndex < 0) {
    getCell(fields.length, 0, sheet).setValue("status");
    statusIndex = fields.length;
  }

  // set value
  var cell = getCell(statusIndex, rowIndex, sheet);
  cell.setValue(status);
  setColor(cell, { bgColor: failed ? RED1 : GREEN1  });
}

function setBody(body, rowIndex, sheet) {
  body = body || "";
  body = body.slice(0, 1024);

  var fields = getFields();

  // find index of body field
  var bodyIndex = -1;
  fields.forEach(function(el, i) { if (el.toLowerCase() === "body") bodyIndex = i; });

  // create fields of body if not exists
  if (bodyIndex < 0) {
    getCell(fields.length, 0, sheet).setValue("body");
    bodyIndex = fields.length;
  }

  // set value
  var cell = getCell(bodyIndex, rowIndex, sheet);
  cell.setValue(body);
  setColor(cell, { bgColor: GRAY1  });
}

// x, y start from 0 (means top cell's coordinate is (0,0))
function getCell(x, y, sheet) {
  if (!sheet) sheet = getSheet();
  return sheet.getRange(y+1, x+1);
}
<commit_msg>help message<commit_after>/*
google apps script

  install:
    Gogole Drive > create spread sheet > tools > script editor > paste this file > save > run onOpen

  schedule:
    Tools > Script editor... > Resources > Current project's triggers > select healthCheck as a function
*/

var SLACK_WEBHOOK_URL = "SLACK_WEBHOOK_URL";
var SLACK_CHANNEL = "SLACK_CHANNEL";

var GREEN1 = "#8ACE6B";
var RED1 = "#F6898A";
var GRAY1 = "#E0E0E0";

function toBool(str) {
  if (typeof str !== "string") return !!str;
  return str.toLowerCase() !== "false";
}

function isEmptyObj(obj) {
  if (!obj) return true;
  if (typeof obj !== "object") return true;
  if (!Object.keys(obj).length) return true;
  return false;
}

function notEmptyObj(obj) { return !isEmptyObj(obj); }

function parseJSON(str) {
  try {
    return JSON.parse(str);
  } catch (err) {
    return str;
  }
}

function jsonToString(obj) {
  return JSON.stringify(obj, null, "  ");
}

function getSheet() { return SpreadsheetApp.getActiveSheet(); }
function ss() { return SpreadsheetApp.getActiveSpreadsheet(); }

function setColor(range, params) {
  params = params || {};
  bgColor = params.bgColor || "#ffffff";
  fontColor = params.fontColor || "#222222";
  range.setBackground(bgColor);
  range.setFontColor(fontColor);
}

function getProps() {
  var dp, slackWebhookUrl, slackChannel;
  dp = PropertiesService.getDocumentProperties();
  slackWebhookUrl = dp.getProperty(SLACK_WEBHOOK_URL);
  slackChannel = dp.getProperty(SLACK_CHANNEL);
  if (!slackWebhookUrl) {
    ss().toast("notification settings not found. see Custom Menu > notification settings");
    return null;
  }
  return {
    slackWebhookUrl: slackWebhookUrl,
    slackChannel: slackChannel
  };
}

// assume the first row is the fields
function getFields(sheet) {
  if (!sheet) sheet = getSheet();

  var rows = sheet.getDataRange();
  var values = rows.getValues();

  if (!values.length) return [];

  return values.shift();
}

function getSheetRows(sheet) {
  if (!sheet) sheet = getSheet();
  var rows = sheet.getDataRange();
  var values = rows.getValues();
  var fields = values.shift();

  if (!values.length) return;

  // skip rows below the lines begin with '#'
  var i = 0;
  while (values[i] && values[i][0].indexOf("#") !== 0) { i++; }
  values = values.slice(0, i);

  var arr = values.map(function(row) {
    var o = {};
    fields.forEach(function(field, i) {
      if (!field) return;
      if (row[i] === "") return;
      o[field] = parseJSON(row[i]);
    });
    return o;
  });

  arr = arr.filter(function(obj) { return notEmptyObj(obj); });
  return arr;
}

function alert(msg) {
  if (msg && Browser && Browser.msgBox) Browser.msgBox(msg);
}

function onOpen() {
  ss().addMenu('Custom Menu', [
    {
      name: 'health check list',
      functionName: 'printContent'
    }, {
      name: 'check right now',
      functionName: 'healthCheck'
    }, {
      name: 'notification settings',
      functionName: 'setDestination'
    }, {
      name: 'help',
      functionName: 'showHelp'
    }
  ]);
}

function printContent() {
  var sheet = getSheetRows();
  if (sheet) alert(jsonToString(sheet));
}

function healthCheck() {
  var sheet = getSheet();
  var rows = getSheetRows();
  if (!rows || !rows.length) alert("health check list is invalid. see Custom Menu > help");
  rows.forEach(function(row, i) { sendRequest(row, i+1, sheet); });
}

function setDestination() {
  var dp = PropertiesService.getDocumentProperties();
  var slackWebhookUrl = Browser.inputBox('Enter Slack Webhook URL (required)', Browser.Buttons.OK_CANCEL);
  var slackChannel = Browser.inputBox('Enter Slack Channel (optional)', Browser.Buttons.OK_CANCEL);

  // slack channel is expected to begin with #
  if (slackChannel && slackChannel.indexOf("#") !== 0) slackChannel = "#" + slackChannel;

  dp.setProperty(SLACK_WEBHOOK_URL, slackWebhookUrl);
  dp.setProperty(SLACK_CHANNEL, slackChannel);
}

function showHelp() {
  alert("https://github.com/kei-sato/gas-health-check");
}

function sendMessage(message) {
  sendMessageSlack(message);
}

// curl -X POST --data-urlencode 'payload={"channel": "#general", "text": ""}' https://hooks.slack.com/services/...
function sendMessageSlack(message) {
  var props = getProps() || {};
  var slackWebhookUrl = props.slackWebhookUrl;
  var slackChannel = props.slackChannel;
  if (!slackWebhookUrl) return;

  var payload = JSON.stringify({
    channel: slackChannel || undefined,
    text: message
  });

  var url = slackWebhookUrl;
  var params = {
    method: "post",
    payload: "payload="+payload,
  };

  var response = UrlFetchApp.fetch(url, params);
}

function checkResponseHealth(expect, result) {
  expect = expect || {};

  var success = true;
  Object.keys(expect).forEach(function(key) {
    if (expect[key] && expect[key] !== result[key]) success = false;
  });
  return success;
}

function sendRequest(row, rowIndex, sheet) {
  var url = row.url;
  var params = {
    contentType: row.contentType || undefined,
    headers: row.headers || undefined,
    method: row.method || undefined,
    payload: row.payload || undefined,
    validateHttpsCertificate: toBool(row.validateHttpsCertificate),
    muteHttpExceptions: true
  };
  var expectResponse = parseJSON(row.expectResponse) || {};
  expectResponse.status = expectResponse.status || 200;

  if (!url) return setStatus("URL NOT FOUND", rowIndex, sheet);

  var response;
  try {
    response = UrlFetchApp.fetch(url, params);
  } catch (err) {
    sendMessage(err.toString());
    return setStatus(err, rowIndex, sheet);
  }
  var status = response.getResponseCode();
  var body = response.getContentText();

  var success = checkResponseHealth(expectResponse, { status: status, body: body });
  var failed = !success;

  if (failed) {
    sendMessage("health check failed!\n"+url+"\nstatus: " + status);
  }

  setStatus(status, rowIndex, failed, sheet);
  setBody(body, rowIndex, sheet);
}

function setStatus(status, rowIndex, failed, sheet) {
  var fields = getFields();

  // find index of status field
  var statusIndex = -1;
  fields.forEach(function(el, i) { if (el.toLowerCase() === "status") statusIndex = i; });

  // create fields of status if not exists
  if (statusIndex < 0) {
    getCell(fields.length, 0, sheet).setValue("status");
    statusIndex = fields.length;
  }

  // set value
  var cell = getCell(statusIndex, rowIndex, sheet);
  cell.setValue(status);
  setColor(cell, { bgColor: failed ? RED1 : GREEN1  });
}

function setBody(body, rowIndex, sheet) {
  body = body || "";
  body = body.slice(0, 1024);

  var fields = getFields();

  // find index of body field
  var bodyIndex = -1;
  fields.forEach(function(el, i) { if (el.toLowerCase() === "body") bodyIndex = i; });

  // create fields of body if not exists
  if (bodyIndex < 0) {
    getCell(fields.length, 0, sheet).setValue("body");
    bodyIndex = fields.length;
  }

  // set value
  var cell = getCell(bodyIndex, rowIndex, sheet);
  cell.setValue(body);
  setColor(cell, { bgColor: GRAY1  });
}

// x, y start from 0 (means top cell's coordinate is (0,0))
function getCell(x, y, sheet) {
  if (!sheet) sheet = getSheet();
  return sheet.getRange(y+1, x+1);
}
<|endoftext|>


<commit_before>(function () {
    var cogClass = function () {};
    cogClass.prototype.exec = function (params, request, response) {
        var oops = this.utils.apiError;
        var sys = this.sys;
        var classID = params.classid;
        var quizNumber = params.quizno;
        var studentID = params.studentid;
        var studentLang;
        var items = [];
        var locale = this.sys.locale;

        getStudentLanguage();

        function getStudentLanguage () {
            var sql = 'SELECT lang FROM students WHERE studentID=?';
            sys.db.get(sql,[studentID],function(err,row){
                if (err) {return oops(response,err,'*quiz/myquizresult(0)')};
                studentLang = row.lang;
                setEntryPlaceholders();
            });
        }

        function setEntryPlaceholders () {
            var sql = 'SELECT questionNumber '
                + 'FROM answers '
                + 'NATURAL JOIN questions '
                + 'JOIN quizzes USING (quizID) '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND answers.studentID=? AND NOT answers.choice=questions.correct';
            sys.db.all(sql,[classID,quizNumber,studentID],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresult(1)')};
                if (rows.length) {
                    for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                        var row = rows[i];
                        items.push({questionNumber:row.questionNumber});
                    }
                    getMistakes(0,items.length);
                } else {
                    var data = JSON.stringify([])
                    response.writeHead(200, {'Content-Type': 'application/json'});
                    response.end(data);
                }
            });
        }

        function getMistakes (pos,limit) {
            if (pos === limit) {
                getGoodAnswerStudents(0,limit);
                return;
            }
            var sql = 'SELECT rubric.string AS rubric,right.string AS right,wrong.string AS wrong,wrong.choice AS wrongChoice '
                + 'FROM quizzes '
                + 'NATURAL JOIN questions '
                + 'JOIN answers USING(questionID) '
                + 'JOIN strings AS rubric USING(stringID) '
                + 'JOIN ('
                +     'SELECT questionID,string '
                +     'FROM questions '
                +     'JOIN choices USING(questionID) '
                +     'JOIN strings ON strings.stringID=choices.stringID '
                +     'WHERE choices.choice=questions.correct'
                + ') AS right ON right.questionID=answers.questionID '
                + 'JOIN ('
                +     'SELECT questionID,string,answers.choice '
                +     'FROM questions '
                +     'JOIN answers USING(questionID) '
                +     'JOIN choices USING(questionID,choice) '
                +     'JOIN strings ON strings.stringID=choices.stringID '
                +     'WHERE choices.choice=answers.choice AND answers.studentID=? '
                + ') AS wrong ON wrong.questionID=answers.questionID '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND questions.questionNumber=? AND answers.studentID=?'
            var questionNumber = items[pos].questionNumber;
            sys.db.get(sql,[studentID,classID,quizNumber,questionNumber,studentID],function(err,row) {
                if (err||!row) {return oops(response,err,'*quiz/myquizresult(2)')}
                items[pos].rubric = row.rubric;
                items[pos].right = row.right;
                items[pos].wrong = row.wrong;
                items[pos].wrongChoice = row.wrongChoice;
                getMistakes(pos+1,limit);
            });
        };

        function getGoodAnswerStudents(pos,limit) {
            if (pos === limit) {
                getRules(0,limit);
                return;
            }
            var sql = 'SELECT name '
                + 'FROM quizzes '
                + 'JOIN questions USING(quizID) '
                + 'JOIN answers USING(questionID) '
                + 'JOIN students USING(studentID) '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND questions.questionNumber=? AND questions.correct=answers.choice'
            var questionNumber = items[pos].questionNumber;
            sys.db.all(sql,[classID,quizNumber,questionNumber],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresult(3)')}
                items[pos].goodAnswerStudents = [];
                for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                    var row = rows[i];
                    items[pos].goodAnswerStudents.push(row.name);
                }
                getGoodAnswerStudents(pos+1,limit);
            });
        };

        function getRules(pos,limit) {
            if (pos === limit) {
                getComments(0,limit);
                return;
            }
            var sql = 'SELECT r.string AS ruleText,'
                + 'CASE WHEN rtO.string IS NOT NULL THEN rtO.string ELSE CASE WHEN rtE.string IS NOT NULL THEN rtE.string ELSE \'\' END END AS ruleGloss '
                + 'FROM quizzes '
                + 'JOIN questions USING(quizID) '
                + 'JOIN choices USING(questionID) '
                + 'JOIN answers ON answers.questionID=choices.questionID AND answers.choice=choices.choice '
                + 'JOIN students USING(studentID) '
                + 'JOIN rulesToChoices ON rulesToChoices.choiceID=choices.choiceID '
                + 'JOIN rules USING(ruleID) '
                + 'JOIN ruleStrings AS r USING(ruleStringID) '
                + 'LEFT JOIN ('
                +   'SELECT ruleID,string '
                +   'FROM ruleTranslations '
                +   'WHERE lang=\'en\''
                + ') AS rtE USING(ruleID) '
                + 'LEFT JOIN ('
                +   'SELECT ruleID,string '
                +   'FROM ruleTranslations '
                +   'WHERE lang=?'
                + ') AS rtO USING(ruleID) '
                + 'WHERE quizzes.classID=? AND quizNumber=? AND questionNumber=? and answers.studentID=?';
            var questionNumber = items[pos].questionNumber;
            sys.db.all(sql,[studentLang,classID,quizNumber,questionNumber,studentID],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresult(4)')};
                items[pos].rules = [];
                for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                    var row = rows[i];
                    items[pos].rules.push({ruleText:row.ruleText,ruleGloss:row.ruleGloss});
                }
                getRules(pos+1,limit);
            });
        };

        function getComments(pos,limit) {
            if (pos === limit) {
                response.writeHead(200, {'Content-Type': 'application/json'});
                response.end(JSON.stringify(items));
                return;
            }
            var sql = 'SELECT admin.name AS commenter,s.string AS comment,'
                + "SUM("
                +   "CASE WHEN lang=? OR lang=? THEN 1 ELSE 0 END "
                + ") AS UseMe "
                + 'FROM quizzes '
                + 'NATURAL JOIN questions '
                + 'JOIN choices USING(questionID) '
                + 'JOIN comments USING(choiceID) '
                + 'JOIN admin USING(adminID) '
                + "JOIN adminLanguages USING(adminID) "
                + 'LEFT JOIN strings s ON s.stringID=comments.stringID '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND questions.questionNumber=? AND choices.choice=? '
                + "GROUP BY commentID "
                + "HAVING UseMe > 0;"
            var questionNumber = items[pos].questionNumber;
            var wrongChoice = items[pos].wrongChoice;
            sys.db.all(sql,[locale,studentLang,classID,quizNumber,questionNumber,wrongChoice],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresults(5)')};
                items[pos].comments = [];
                for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                    var row = rows[i];
                    items[pos].comments.push({commenter:row.commenter,comment:row.comment});
                }
                getComments(pos+1,limit);
            });
        };
    }
    exports.cogClass = cogClass;
})();
<commit_msg>Mask external members from correct-answer-student lists<commit_after>(function () {
    var cogClass = function () {};
    cogClass.prototype.exec = function (params, request, response) {
        var oops = this.utils.apiError;
        var sys = this.sys;
        var classID = params.classid;
        var quizNumber = params.quizno;
        var studentID = params.studentid;
        var studentLang;
        var items = [];
        var locale = this.sys.locale;

        getStudentLanguage();

        function getStudentLanguage () {
            var sql = 'SELECT lang FROM students WHERE studentID=?';
            sys.db.get(sql,[studentID],function(err,row){
                if (err) {return oops(response,err,'*quiz/myquizresult(0)')};
                studentLang = row.lang;
                setEntryPlaceholders();
            });
        }

        function setEntryPlaceholders () {
            var sql = 'SELECT questionNumber '
                + 'FROM answers '
                + 'NATURAL JOIN questions '
                + 'JOIN quizzes USING (quizID) '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND answers.studentID=? AND NOT answers.choice=questions.correct';
            sys.db.all(sql,[classID,quizNumber,studentID],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresult(1)')};
                if (rows.length) {
                    for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                        var row = rows[i];
                        items.push({questionNumber:row.questionNumber});
                    }
                    getMistakes(0,items.length);
                } else {
                    var data = JSON.stringify([])
                    response.writeHead(200, {'Content-Type': 'application/json'});
                    response.end(data);
                }
            });
        }

        function getMistakes (pos,limit) {
            if (pos === limit) {
                getGoodAnswerStudents(0,limit);
                return;
            }
            var sql = 'SELECT rubric.string AS rubric,right.string AS right,wrong.string AS wrong,wrong.choice AS wrongChoice '
                + 'FROM quizzes '
                + 'NATURAL JOIN questions '
                + 'JOIN answers USING(questionID) '
                + 'JOIN strings AS rubric USING(stringID) '
                + 'JOIN ('
                +     'SELECT questionID,string '
                +     'FROM questions '
                +     'JOIN choices USING(questionID) '
                +     'JOIN strings ON strings.stringID=choices.stringID '
                +     'WHERE choices.choice=questions.correct'
                + ') AS right ON right.questionID=answers.questionID '
                + 'JOIN ('
                +     'SELECT questionID,string,answers.choice '
                +     'FROM questions '
                +     'JOIN answers USING(questionID) '
                +     'JOIN choices USING(questionID,choice) '
                +     'JOIN strings ON strings.stringID=choices.stringID '
                +     'WHERE choices.choice=answers.choice AND answers.studentID=? '
                + ') AS wrong ON wrong.questionID=answers.questionID '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND questions.questionNumber=? AND answers.studentID=?'
            var questionNumber = items[pos].questionNumber;
            sys.db.get(sql,[studentID,classID,quizNumber,questionNumber,studentID],function(err,row) {
                if (err||!row) {return oops(response,err,'*quiz/myquizresult(2)')}
                items[pos].rubric = row.rubric;
                items[pos].right = row.right;
                items[pos].wrong = row.wrong;
                items[pos].wrongChoice = row.wrongChoice;
                getMistakes(pos+1,limit);
            });
        };

        function getGoodAnswerStudents(pos,limit) {
            if (pos === limit) {
                getRules(0,limit);
                return;
            }
            var sql = 'SELECT name '
                + 'FROM quizzes '
                + 'JOIN questions USING(quizID) '
                + 'JOIN answers USING(questionID) '
                + 'JOIN students USING(studentID) '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND questions.questionNumber=? AND questions.correct=answers.choice AND privacy=0 '
            var questionNumber = items[pos].questionNumber;
            sys.db.all(sql,[classID,quizNumber,questionNumber],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresult(3)')}
                items[pos].goodAnswerStudents = [];
                for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                    var row = rows[i];
                    items[pos].goodAnswerStudents.push(row.name);
                }
                getGoodAnswerStudents(pos+1,limit);
            });
        };

        function getRules(pos,limit) {
            if (pos === limit) {
                getComments(0,limit);
                return;
            }
            var sql = 'SELECT r.string AS ruleText,'
                + 'CASE WHEN rtO.string IS NOT NULL THEN rtO.string ELSE CASE WHEN rtE.string IS NOT NULL THEN rtE.string ELSE \'\' END END AS ruleGloss '
                + 'FROM quizzes '
                + 'JOIN questions USING(quizID) '
                + 'JOIN choices USING(questionID) '
                + 'JOIN answers ON answers.questionID=choices.questionID AND answers.choice=choices.choice '
                + 'JOIN students USING(studentID) '
                + 'JOIN rulesToChoices ON rulesToChoices.choiceID=choices.choiceID '
                + 'JOIN rules USING(ruleID) '
                + 'JOIN ruleStrings AS r USING(ruleStringID) '
                + 'LEFT JOIN ('
                +   'SELECT ruleID,string '
                +   'FROM ruleTranslations '
                +   'WHERE lang=\'en\''
                + ') AS rtE USING(ruleID) '
                + 'LEFT JOIN ('
                +   'SELECT ruleID,string '
                +   'FROM ruleTranslations '
                +   'WHERE lang=?'
                + ') AS rtO USING(ruleID) '
                + 'WHERE quizzes.classID=? AND quizNumber=? AND questionNumber=? and answers.studentID=?';
            var questionNumber = items[pos].questionNumber;
            sys.db.all(sql,[studentLang,classID,quizNumber,questionNumber,studentID],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresult(4)')};
                items[pos].rules = [];
                for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                    var row = rows[i];
                    items[pos].rules.push({ruleText:row.ruleText,ruleGloss:row.ruleGloss});
                }
                getRules(pos+1,limit);
            });
        };

        function getComments(pos,limit) {
            if (pos === limit) {
                response.writeHead(200, {'Content-Type': 'application/json'});
                response.end(JSON.stringify(items));
                return;
            }
            var sql = 'SELECT admin.name AS commenter,s.string AS comment,'
                + "SUM("
                +   "CASE WHEN lang=? OR lang=? THEN 1 ELSE 0 END "
                + ") AS UseMe "
                + 'FROM quizzes '
                + 'NATURAL JOIN questions '
                + 'JOIN choices USING(questionID) '
                + 'JOIN comments USING(choiceID) '
                + 'JOIN admin USING(adminID) '
                + "JOIN adminLanguages USING(adminID) "
                + 'LEFT JOIN strings s ON s.stringID=comments.stringID '
                + 'WHERE quizzes.classID=? AND quizzes.quizNumber=? AND questions.questionNumber=? AND choices.choice=? '
                + "GROUP BY commentID "
                + "HAVING UseMe > 0;"
            var questionNumber = items[pos].questionNumber;
            var wrongChoice = items[pos].wrongChoice;
            sys.db.all(sql,[locale,studentLang,classID,quizNumber,questionNumber,wrongChoice],function(err,rows){
                if (err||!rows) {return oops(response,err,'*quiz/myquizresults(5)')};
                items[pos].comments = [];
                for (var i=0,ilen=rows.length;i<ilen;i+=1) {
                    var row = rows[i];
                    items[pos].comments.push({commenter:row.commenter,comment:row.comment});
                }
                getComments(pos+1,limit);
            });
        };
    }
    exports.cogClass = cogClass;
})();
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

couchTests.reduce = function(debug) {
  var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
  db.deleteDb();
  db.createDb();
  if (debug) debugger;
  var numDocs = 500;
  var docs = makeDocs(1,numDocs + 1);
  db.bulkSave(docs);
  var summate = function(N) {return (N+1)*N/2;};

  var map = function (doc) {
      emit(doc.integer, doc.integer);
      emit(doc.integer, doc.integer);
  };
  var reduce = function (keys, values) { return sum(values); };
  var result = db.query(map, reduce);
  T(result.rows[0].value == 2*summate(numDocs));

  result = db.query(map, reduce, {startkey: 4, endkey: 4});
  T(result.rows[0].value == 8);

  result = db.query(map, reduce, {startkey: 4, endkey: 5});
  T(result.rows[0].value == 18);

  result = db.query(map, reduce, {startkey: 4, endkey: 6});
  T(result.rows[0].value == 30);

  result = db.query(map, reduce, {group:true, limit:3});
  T(result.rows[0].value == 2);
  T(result.rows[1].value == 4);
  T(result.rows[2].value == 6);

  for(var i=1; i<numDocs/2; i+=30) {
    result = db.query(map, reduce, {startkey: i, endkey: numDocs - i});
    T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
  }

  db.deleteDb();
  db.createDb();

  for(var i=1; i <= 5; i++) {

    for(var j=0; j < 10; j++) {
      // these docs are in the order of the keys collation, for clarity
      var docs = [];
      docs.push({keys:["a"]});
      docs.push({keys:["a"]});
      docs.push({keys:["a", "b"]});
      docs.push({keys:["a", "b"]});
      docs.push({keys:["a", "b", "c"]});
      docs.push({keys:["a", "b", "d"]});
      docs.push({keys:["a", "c", "d"]});
      docs.push({keys:["d"]});
      docs.push({keys:["d", "a"]});
      docs.push({keys:["d", "b"]});
      docs.push({keys:["d", "c"]});
      db.bulkSave(docs);
      T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
    }

    map = function (doc) { emit(doc.keys, 1); };
    reduce = function (keys, values) { return sum(values); };

    var results = db.query(map, reduce, {group:true});

    //group by exact key match
    T(equals(results.rows[0], {key:["a"],value:20*i}));
    T(equals(results.rows[1], {key:["a","b"],value:20*i}));
    T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i}));
    T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i}));

    // test to make sure group reduce and limit params provide valid json
    var results = db.query(map, reduce, {group: true, limit: 2});
    T(equals(results.rows[0], {key: ["a"], value: 20*i}));
    T(equals(results.rows.length, 2));

    //group by the first element in the key array
    var results = db.query(map, reduce, {group_level:1});
    T(equals(results.rows[0], {key:["a"],value:70*i}));
    T(equals(results.rows[1], {key:["d"],value:40*i}));

    //group by the first 2 elements in the key array
    var results = db.query(map, reduce, {group_level:2});
    T(equals(results.rows[0], {key:["a"],value:20*i}));
    T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    T(equals(results.rows[3], {key:["d"],value:10*i}));
    T(equals(results.rows[4], {key:["d","a"],value:10*i}));
    T(equals(results.rows[5], {key:["d","b"],value:10*i}));
    T(equals(results.rows[6], {key:["d","c"],value:10*i}));
  }

  // now test out more complex reductions that need to use the combine option.

  db.deleteDb();
  db.createDb();

  var map = function (doc) { emit(doc.val, doc.val); };
  var reduceCombine = function (keys, values, rereduce) {
      // This computes the standard deviation of the mapped results
      var stdDeviation=0.0;
      var count=0;
      var total=0.0;
      var sqrTotal=0.0;

      if (!rereduce) {
        // This is the reduce phase, we are reducing over emitted values from
        // the map functions.
        for(var i in values) {
          total = total + values[i];
          sqrTotal = sqrTotal + (values[i] * values[i]);
        }
        count = values.length;
      }
      else {
        // This is the rereduce phase, we are re-reducing previosuly
        // reduced values.
        for(var i in values) {
          count = count + values[i].count;
          total = total + values[i].total;
          sqrTotal = sqrTotal + values[i].sqrTotal;
        }
      }

      var variance =  (sqrTotal - ((total * total)/count)) / count;
      stdDeviation = Math.sqrt(variance);

      // the reduce result. It contains enough information to be rereduced
      // with other reduce results.
      return {"stdDeviation":stdDeviation,"count":count,
          "total":total,"sqrTotal":sqrTotal};
    };

    // Save a bunch a docs.

  for(var i=0; i < 10; i++) {
    var docs = [];
    docs.push({val:10});
    docs.push({val:20});
    docs.push({val:30});
    docs.push({val:40});
    docs.push({val:50});
    docs.push({val:60});
    docs.push({val:70});
    docs.push({val:80});
    docs.push({val:90});
    docs.push({val:100});
    db.bulkSave(docs);
  }

  var results = db.query(map, reduceCombine);

  var difference = results.rows[0].value.stdDeviation - 28.722813232690143;
  // account for floating point rounding error
  T(Math.abs(difference) < 0.0000000001);

};
<commit_msg>COUCHDB-1047 - support inclusive_end with reduce calls<commit_after>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

couchTests.reduce = function(debug) {
  var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
  db.deleteDb();
  db.createDb();
  if (debug) debugger;
  var numDocs = 500;
  var docs = makeDocs(1,numDocs + 1);
  db.bulkSave(docs);
  var summate = function(N) {return (N+1)*N/2;};

  var map = function (doc) {
      emit(doc.integer, doc.integer);
      emit(doc.integer, doc.integer);
  };
  var reduce = function (keys, values) { return sum(values); };
  var result = db.query(map, reduce);
  T(result.rows[0].value == 2*summate(numDocs));

  result = db.query(map, reduce, {startkey: 4, endkey: 4});
  T(result.rows[0].value == 8);

  result = db.query(map, reduce, {startkey: 4, endkey: 5});
  T(result.rows[0].value == 18);

  result = db.query(map, reduce, {startkey: 4, endkey: 6});
  T(result.rows[0].value == 30);

  result = db.query(map, reduce, {group:true, limit:3});
  T(result.rows[0].value == 2);
  T(result.rows[1].value == 4);
  T(result.rows[2].value == 6);

  for(var i=1; i<numDocs/2; i+=30) {
    result = db.query(map, reduce, {startkey: i, endkey: numDocs - i});
    T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
  }

  db.deleteDb();
  db.createDb();

  for(var i=1; i <= 5; i++) {

    for(var j=0; j < 10; j++) {
      // these docs are in the order of the keys collation, for clarity
      var docs = [];
      docs.push({keys:["a"]});
      docs.push({keys:["a"]});
      docs.push({keys:["a", "b"]});
      docs.push({keys:["a", "b"]});
      docs.push({keys:["a", "b", "c"]});
      docs.push({keys:["a", "b", "d"]});
      docs.push({keys:["a", "c", "d"]});
      docs.push({keys:["d"]});
      docs.push({keys:["d", "a"]});
      docs.push({keys:["d", "b"]});
      docs.push({keys:["d", "c"]});
      db.bulkSave(docs);
      T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
    }

    map = function (doc) { emit(doc.keys, 1); };
    reduce = function (keys, values) { return sum(values); };

    var results = db.query(map, reduce, {group:true});

    //group by exact key match
    T(equals(results.rows[0], {key:["a"],value:20*i}));
    T(equals(results.rows[1], {key:["a","b"],value:20*i}));
    T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i}));
    T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i}));

    // test to make sure group reduce and limit params provide valid json
    var results = db.query(map, reduce, {group: true, limit: 2});
    T(equals(results.rows[0], {key: ["a"], value: 20*i}));
    T(equals(results.rows.length, 2));

    //group by the first element in the key array
    var results = db.query(map, reduce, {group_level:1});
    T(equals(results.rows[0], {key:["a"],value:70*i}));
    T(equals(results.rows[1], {key:["d"],value:40*i}));

    //group by the first 2 elements in the key array
    var results = db.query(map, reduce, {group_level:2});
    T(equals(results.rows[0], {key:["a"],value:20*i}));
    T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    T(equals(results.rows[3], {key:["d"],value:10*i}));
    T(equals(results.rows[4], {key:["d","a"],value:10*i}));
    T(equals(results.rows[5], {key:["d","b"],value:10*i}));
    T(equals(results.rows[6], {key:["d","c"],value:10*i}));

    // endkey test with inclusive_end=true
    var results = db.query(map, reduce, {group_level:2,endkey:["d"],inclusive_end:true});
    T(equals(results.rows[0], {key:["a"],value:20*i}));
    T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    T(equals(results.rows[3], {key:["d"],value:10*i}));
    TEquals(4, results.rows.length);

    // endkey test with inclusive_end=false
    var results = db.query(map, reduce, {group_level:2,endkey:["d"],inclusive_end:false});
    T(equals(results.rows[0], {key:["a"],value:20*i}));
    T(equals(results.rows[1], {key:["a","b"],value:40*i}));
    T(equals(results.rows[2], {key:["a","c"],value:10*i}));
    TEquals(3, results.rows.length);
  }

  // now test out more complex reductions that need to use the combine option.

  db.deleteDb();
  db.createDb();

  var map = function (doc) { emit(doc.val, doc.val); };
  var reduceCombine = function (keys, values, rereduce) {
      // This computes the standard deviation of the mapped results
      var stdDeviation=0.0;
      var count=0;
      var total=0.0;
      var sqrTotal=0.0;

      if (!rereduce) {
        // This is the reduce phase, we are reducing over emitted values from
        // the map functions.
        for(var i in values) {
          total = total + values[i];
          sqrTotal = sqrTotal + (values[i] * values[i]);
        }
        count = values.length;
      }
      else {
        // This is the rereduce phase, we are re-reducing previosuly
        // reduced values.
        for(var i in values) {
          count = count + values[i].count;
          total = total + values[i].total;
          sqrTotal = sqrTotal + values[i].sqrTotal;
        }
      }

      var variance =  (sqrTotal - ((total * total)/count)) / count;
      stdDeviation = Math.sqrt(variance);

      // the reduce result. It contains enough information to be rereduced
      // with other reduce results.
      return {"stdDeviation":stdDeviation,"count":count,
          "total":total,"sqrTotal":sqrTotal};
    };

    // Save a bunch a docs.

  for(var i=0; i < 10; i++) {
    var docs = [];
    docs.push({val:10});
    docs.push({val:20});
    docs.push({val:30});
    docs.push({val:40});
    docs.push({val:50});
    docs.push({val:60});
    docs.push({val:70});
    docs.push({val:80});
    docs.push({val:90});
    docs.push({val:100});
    db.bulkSave(docs);
  }

  var results = db.query(map, reduceCombine);

  var difference = results.rows[0].value.stdDeviation - 28.722813232690143;
  // account for floating point rounding error
  T(Math.abs(difference) < 0.0000000001);

};
<|endoftext|>


<commit_before>/* Global variables and objects */

var use_fake_data = true;

// Sounds and content
var sounds = [];
var default_query = "instrument note"
var extra_descriptors = "lowlevel.mfcc.mean";
var map_similarity_feature = "lowlevel.mfcc.mean";
var n_pages = 1;
var n_pages_received = 0;
var all_loaded = false;

// t-sne
var max_tsne_iterations = 500;
var current_it_number = 0;
var epsilon = 10;
var perplexity = 10;
var tsne = undefined;
var runner;
var Y;

// Map
var w = window.innerWidth;
var h = window.innerHeight;
var tx=0;
var ty=0;
var ss=1;
var min_zoom = 0.2;
var max_zoom = 15;
var map_scale_factor = 20;
var svg;

// Sounds display
var default_radius = 30;
var default_opacity = 0.7;
var default_stroke_width = 2;
var sound_selected_colour = "#ffffff";
var sound_playing_colour = "#ffffff";

// UI
var hover_playing_mode = false;

function start(){
    /* Prepare app to load data and display map */

    // Sounds
    sounds = [];
    n_pages_received = 0;
    all_loaded = false;

    // Map
    var map = document.getElementById("map");
    map.innerHTML = null;
    w = window.innerWidth;
    h = window.innerHeight;

    // t-sne
    current_it_number = 0;
    var opt = {}
    opt.epsilon = epsilon; // epsilon is learning rate (10 = default)
    opt.perplexity = perplexity; // roughly how many neighbors each point influences (30 = default)
    opt.dim = 2; // dimensionality of the embedding (2 = default)
    tsne = new tsnejs.tSNE(opt); // create a tSNE instance

    if (!use_fake_data){
        // Search sounds and start loading them
        var query = document.getElementById('query_terms_input').value;
        if ((query == undefined) || (query=="")){
            query = default_query;
        }
        for (var i=0; i<n_pages; i++){
            var url = "https://freesound.org/apiv2/search/text/?query=" + query + "&" +
            "group_by_pack=0&filter=duration:[0+TO+2]&fields=id,previews,name,analysis,url,username" +
            "&descriptors=sfx.tristimulus.mean," + extra_descriptors + "&page_size=150" +
            "&token=eecfe4981d7f41d2811b4b03a894643d5e33f812&page=" + (i + 1);
            loadJSON(function(data) { loadDataFromFreesoundResponse(data); }, url);
        }
    } else {
        query = "fake data";
        loadFakeData();
    }

    // Ui
    document.getElementById('query_terms_input').value = query;
    document.getElementById('info_placeholder').innerHTML = "Searching...";
    document.onkeydown = onKeyDown;
    document.onkeyup = onKeyUp;
}

function loadDataFromFreesoundResponse(data){
    for (i in data['results']){
        var sound_json = data['results'][i];
        if (sound_json['analysis'] != undefined){
            var sound = new SoundFactory(
                id=sound_json['id'],
                preview_url=sound_json['previews']['preview-lq-mp3'],
                analysis=sound_json['analysis'],
                url=sound_json['url'],
                name=sound_json['name'],
                username=sound_json['username']
            );
            sounds.push(sound);
        }
    }
    if (n_pages_received == n_pages){
        // Init t-sne with sounds features
        var X = [];
        for (i in sounds){
            sound_i = sounds[i];
            var feature_vector = Object.byString(sound_i, 'analysis.' + map_similarity_feature);
            X.push(feature_vector);
        }
        tsne.initDataRaw(X);
        all_loaded = true;
        console.log('Loaded tsne with ' + sounds.length + ' sounds')
        drawMap();
        runner = setInterval(step, 0);
    }
}

/* Event handlers */

function onKeyDown(evt) {
    if (evt.altKey) {
        hover_playing_mode = true;
    }
}

function onKeyUp(evt) {
    hover_playing_mode = false;
}

(function() {
  var formSubmitHandler = function formSubmitHandler(event) {
    event.preventDefault();
    start();
  }
  document.getElementById('query-form').onsubmit = formSubmitHandler;
})()
<commit_msg>Set use fake data to false<commit_after>/* Global variables and objects */

var use_fake_data = false;

// Sounds and content
var sounds = [];
var default_query = "instrument note"
var extra_descriptors = "lowlevel.mfcc.mean";
var map_similarity_feature = "lowlevel.mfcc.mean";
var n_pages = 1;
var n_pages_received = 0;
var all_loaded = false;

// t-sne
var max_tsne_iterations = 500;
var current_it_number = 0;
var epsilon = 10;
var perplexity = 10;
var tsne = undefined;
var runner;
var Y;

// Map
var w = window.innerWidth;
var h = window.innerHeight;
var tx=0;
var ty=0;
var ss=1;
var min_zoom = 0.2;
var max_zoom = 15;
var map_scale_factor = 20;
var svg;

// Sounds display
var default_radius = 30;
var default_opacity = 0.7;
var default_stroke_width = 2;
var sound_selected_colour = "#ffffff";
var sound_playing_colour = "#ffffff";

// UI
var hover_playing_mode = false;

function start(){
    /* Prepare app to load data and display map */

    // Sounds
    sounds = [];
    n_pages_received = 0;
    all_loaded = false;

    // Map
    var map = document.getElementById("map");
    map.innerHTML = null;
    w = window.innerWidth;
    h = window.innerHeight;

    // t-sne
    current_it_number = 0;
    var opt = {}
    opt.epsilon = epsilon; // epsilon is learning rate (10 = default)
    opt.perplexity = perplexity; // roughly how many neighbors each point influences (30 = default)
    opt.dim = 2; // dimensionality of the embedding (2 = default)
    tsne = new tsnejs.tSNE(opt); // create a tSNE instance

    if (!use_fake_data){
        // Search sounds and start loading them
        var query = document.getElementById('query_terms_input').value;
        if ((query == undefined) || (query=="")){
            query = default_query;
        }
        for (var i=0; i<n_pages; i++){
            var url = "https://freesound.org/apiv2/search/text/?query=" + query + "&" +
            "group_by_pack=0&filter=duration:[0+TO+2]&fields=id,previews,name,analysis,url,username" +
            "&descriptors=sfx.tristimulus.mean," + extra_descriptors + "&page_size=150" +
            "&token=eecfe4981d7f41d2811b4b03a894643d5e33f812&page=" + (i + 1);
            loadJSON(function(data) { loadDataFromFreesoundResponse(data); }, url);
        }
    } else {
        query = "fake data";
        loadFakeData();
    }

    // Ui
    document.getElementById('query_terms_input').value = query;
    document.getElementById('info_placeholder').innerHTML = "Searching...";
    document.onkeydown = onKeyDown;
    document.onkeyup = onKeyUp;
}

function loadDataFromFreesoundResponse(data){
    for (i in data['results']){
        var sound_json = data['results'][i];
        if (sound_json['analysis'] != undefined){
            var sound = new SoundFactory(
                id=sound_json['id'],
                preview_url=sound_json['previews']['preview-lq-mp3'],
                analysis=sound_json['analysis'],
                url=sound_json['url'],
                name=sound_json['name'],
                username=sound_json['username']
            );
            sounds.push(sound);
        }
    }
    if (n_pages_received == n_pages){
        // Init t-sne with sounds features
        var X = [];
        for (i in sounds){
            sound_i = sounds[i];
            var feature_vector = Object.byString(sound_i, 'analysis.' + map_similarity_feature);
            X.push(feature_vector);
        }
        tsne.initDataRaw(X);
        all_loaded = true;
        console.log('Loaded tsne with ' + sounds.length + ' sounds')
        drawMap();
        runner = setInterval(step, 0);
    }
}

/* Event handlers */

function onKeyDown(evt) {
    if (evt.altKey) {
        hover_playing_mode = true;
    }
}

function onKeyUp(evt) {
    hover_playing_mode = false;
}

(function() {
  var formSubmitHandler = function formSubmitHandler(event) {
    event.preventDefault();
    start();
  }
  document.getElementById('query-form').onsubmit = formSubmitHandler;
})()
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

couchTests.rewrite = function(debug) {
  // this test _rewrite handler

  var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
  db.deleteDb();
  db.createDb();

  if (debug) debugger;
  run_on_modified_server(
    [{section: "httpd",
      key: "authentication_handlers",
      value: "{couch_httpd_auth, special_test_authentication_handler}"},
     {section:"httpd",
      key: "WWW-Authenticate",
      value: "X-Couch-Test-Auth"}],

      function(){
        var designDoc = {
          _id:"_design/test",
          language: "javascript",
           _attachments:{
              "foo.txt": {
                content_type:"text/plain",
                data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
              }
            },
          rewrites: [
            {
              "from": "foo",
              "to": "foo.txt"
            },
            {
              "from": "foo2",
              "to": "foo.txt",
              "method": "GET"
            },
            {
              "from": "hello/:id",
              "to": "_update/hello/:id",
              "method": "PUT"
            },
            {
              "from": "/welcome",
              "to": "_show/welcome"
            },
            {
              "from": "/welcome/:name",
              "to": "_show/welcome",
              "query": {
                "name": ":name"
              }
            },
            {
              "from": "/welcome2",
              "to": "_show/welcome",
              "query": {
                "name": "user"
              }
            },
            {
              "from": "/welcome3/:name",
              "to": "_update/welcome2/:name",
              "method": "PUT"
            },
            {
              "from": "/welcome3/:name",
              "to": "_show/welcome2/:name",
              "method": "GET"
            },
            {
              "from": "simpleForm/basicView",
              "to": "_list/simpleForm/basicView",
            },
            {
              "from": "simpleForm/basicViewFixed",
              "to": "_list/simpleForm/basicView",
              "query": {
                "startkey": 3,
                "endkey": 8
              }
            },
            {
              "from": "simpleForm/basicViewPath/:start/:end",
              "to": "_list/simpleForm/basicView",
              "query": {
                "startkey": ":start",
                "endkey": ":end"
              }
            },
            {
              "from": "simpleForm/complexView",
              "to": "_list/simpleForm/complexView",
              "query": {
                "key": [1, 2]
              }
            },
            {
              "from": "simpleForm/complexView2",
              "to": "_list/simpleForm/complexView",
              "query": {
                "key": ["test", {}]
              }
            },
            {
              "from": "simpleForm/complexView3",
              "to": "_list/simpleForm/complexView",
              "query": {
                "key": ["test", ["test", "essai"]]
              }
            },
            {
              "from": "simpleForm/complexView4",
              "to": "_list/simpleForm/complexView2",
              "query": {
                "key": {"c": 1}
              }
            },
            {
              "from": "simpleForm/complexView5/:a/:b",
              "to": "_list/simpleForm/complexView3",
              "query": {
                "key": [":a", ":b"]
              }
            }
          ],
          lists: {
            simpleForm: stringFun(function(head, req) {
              log("simpleForm");
              send('
');
              var row, row_number = 0, prevKey, firstKey = null;
              while (row = getRow()) {
                row_number += 1;
                if (!firstKey) firstKey = row.key;
                prevKey = row.key;
                send('\n
Key: '+row.key
                +' Value: '+row.value
                +' LineNo: '+row_number+'
');
              }
              return '

FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'
';
            }),
          },
          shows: {
            "welcome": stringFun(function(doc,req) {
              return "Welcome " + req.query["name"];
            }),
            "welcome2": stringFun(function(doc, req) {
              return "Welcome " + doc.name;
            }),
          },
          updates: {
            "hello" : stringFun(function(doc, req) {
              if (!doc) {
                if (req.id) {
                  return [{
                    _id : req.id
                  }, "New World"]
                }
                return [null, "Empty World"];
              }
              doc.world = "hello";
              doc.edited_by = req.userCtx;
              return [doc, "hello doc"];
            }),
            "welcome2": stringFun(function(doc, req) {
              if (!doc) {
                if (req.id) {
                  return [{
                    _id: req.id,
                    name: req.id
                  }, "New World"]
                }
                return [null, "Empty World"];
              }
              return [doc, "hello doc"];
            })
          },
          views : {
            basicView : {
              map : stringFun(function(doc) {
                if (doc.integer) {
                  emit(doc.integer, doc.string);
                }

              })
            },
            complexView: {
              map: stringFun(function(doc) {
                if (doc.type == "complex") {
                  emit([doc.a, doc.b], doc.string);
                }
              })
            },
            complexView2: {
              map: stringFun(function(doc) {
                if (doc.type == "complex") {
                  emit(doc.a, doc.string);
                }
              })
            },
            complexView3: {
              map: stringFun(function(doc) {
                if (doc.type == "complex") {
                  emit(doc.b, doc.string);
                }
              })
            }
          }
        }

        db.save(designDoc);

        var docs = makeDocs(0, 10);
        db.bulkSave(docs);

        var docs2 = [
          {"a": 1, "b": 1, "string": "doc 1", "type": "complex"},
          {"a": 1, "b": 2, "string": "doc 2", "type": "complex"},
          {"a": "test", "b": {}, "string": "doc 3", "type": "complex"},
          {"a": "test", "b": ["test", "essai"], "string": "doc 4", "type": "complex"},
          {"a": {"c": 1}, "b": "", "string": "doc 5", "type": "complex"}
        ];

        db.bulkSave(docs2);

        // test simple rewriting

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/foo");
        T(req.responseText == "This is a base64 encoded text");
        T(req.getResponseHeader("Content-Type") == "text/plain");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/foo2");
        T(req.responseText == "This is a base64 encoded text");
        T(req.getResponseHeader("Content-Type") == "text/plain");

        // test POST
        // hello update world

        var doc = {"word":"plankton", "name":"Rusty"}
        var resp = db.save(doc);
        T(resp.ok);
        var docid = resp.id;

        xhr = CouchDB.request("PUT", "/test_suite_db/_design/test/_rewrite/hello/"+docid);
        T(xhr.status == 201);
        T(xhr.responseText == "hello doc");
        T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")))

        doc = db.open(docid);
        T(doc.world == "hello");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome?name=user");
        T(req.responseText == "Welcome user");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome/user");
        T(req.responseText == "Welcome user");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome2");
        T(req.responseText == "Welcome user");

        xhr = CouchDB.request("PUT", "/test_suite_db/_design/test/_rewrite/welcome3/test");
        T(xhr.status == 201);
        T(xhr.responseText == "New World");
        T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome3/test");
        T(xhr.responseText == "Welcome test");

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicView?startkey=3&endkey=8");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewFixed");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewFixed?startkey=4");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewPath/3/8");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView");
        T(xhr.status == 200, "with query params");
        T(/FirstKey: [1, 2]/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView2");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 3/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView3");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 4/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView4");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 5/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView5/test/essai");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 4/.test(xhr.responseText));

        // test path relative to server
        designDoc.rewrites.push({
           "from": "uuids",
           "to": "../../../_uuids"
        });
        T(db.save(designDoc).ok);

        var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/uuids");
        T(xhr.status == 500);
        var result = JSON.parse(xhr.responseText);
        T(result.error == "insecure_rewrite_rule");

        run_on_modified_server(
          [{section: "httpd",
            key: "secure_rewrites",
            value: "false"}],
            function() {
              var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/uuids?cache=bust");
              T(xhr.status == 200);
              var result = JSON.parse(xhr.responseText);
              T(result.uuids.length == 1);
              var first = result.uuids[0];
        });

  });

}<commit_msg>fix #COUCHDB-787 . thanks!<commit_after>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

couchTests.rewrite = function(debug) {
  // this test _rewrite handler

  var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
  db.deleteDb();
  db.createDb();

  if (debug) debugger;
  run_on_modified_server(
    [{section: "httpd",
      key: "authentication_handlers",
      value: "{couch_httpd_auth, special_test_authentication_handler}"},
     {section:"httpd",
      key: "WWW-Authenticate",
      value: "X-Couch-Test-Auth"}],

      function(){
        var designDoc = {
          _id:"_design/test",
          language: "javascript",
           _attachments:{
              "foo.txt": {
                content_type:"text/plain",
                data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
              }
            },
          rewrites: [
            {
              "from": "foo",
              "to": "foo.txt"
            },
            {
              "from": "foo2",
              "to": "foo.txt",
              "method": "GET"
            },
            {
              "from": "hello/:id",
              "to": "_update/hello/:id",
              "method": "PUT"
            },
            {
              "from": "/welcome",
              "to": "_show/welcome"
            },
            {
              "from": "/welcome/:name",
              "to": "_show/welcome",
              "query": {
                "name": ":name"
              }
            },
            {
              "from": "/welcome2",
              "to": "_show/welcome",
              "query": {
                "name": "user"
              }
            },
            {
              "from": "/welcome3/:name",
              "to": "_update/welcome2/:name",
              "method": "PUT"
            },
            {
              "from": "/welcome3/:name",
              "to": "_show/welcome2/:name",
              "method": "GET"
            },
            {
              "from": "simpleForm/basicView",
              "to": "_list/simpleForm/basicView",
            },
            {
              "from": "simpleForm/basicViewFixed",
              "to": "_list/simpleForm/basicView",
              "query": {
                "startkey": 3,
                "endkey": 8
              }
            },
            {
              "from": "simpleForm/basicViewPath/:start/:end",
              "to": "_list/simpleForm/basicView",
              "query": {
                "startkey": ":start",
                "endkey": ":end"
              }
            },
            {
              "from": "simpleForm/complexView",
              "to": "_list/simpleForm/complexView",
              "query": {
                "key": [1, 2]
              }
            },
            {
              "from": "simpleForm/complexView2",
              "to": "_list/simpleForm/complexView",
              "query": {
                "key": ["test", {}]
              }
            },
            {
              "from": "simpleForm/complexView3",
              "to": "_list/simpleForm/complexView",
              "query": {
                "key": ["test", ["test", "essai"]]
              }
            },
            {
              "from": "simpleForm/complexView4",
              "to": "_list/simpleForm/complexView2",
              "query": {
                "key": {"c": 1}
              }
            },
            {
              "from": "simpleForm/complexView5/:a/:b",
              "to": "_list/simpleForm/complexView3",
              "query": {
                "key": [":a", ":b"]
              }
            },
            {
              "from": "simpleForm/complexView6",
              "to": "_list/simpleForm/complexView3",
              "query": {
                "key": [":a", ":b"]
              }
            }
          ],
          lists: {
            simpleForm: stringFun(function(head, req) {
              log("simpleForm");
              send('
');
              var row, row_number = 0, prevKey, firstKey = null;
              while (row = getRow()) {
                row_number += 1;
                if (!firstKey) firstKey = row.key;
                prevKey = row.key;
                send('\n
Key: '+row.key
                +' Value: '+row.value
                +' LineNo: '+row_number+'
');
              }
              return '

FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'
';
            }),
          },
          shows: {
            "welcome": stringFun(function(doc,req) {
              return "Welcome " + req.query["name"];
            }),
            "welcome2": stringFun(function(doc, req) {
              return "Welcome " + doc.name;
            }),
          },
          updates: {
            "hello" : stringFun(function(doc, req) {
              if (!doc) {
                if (req.id) {
                  return [{
                    _id : req.id
                  }, "New World"]
                }
                return [null, "Empty World"];
              }
              doc.world = "hello";
              doc.edited_by = req.userCtx;
              return [doc, "hello doc"];
            }),
            "welcome2": stringFun(function(doc, req) {
              if (!doc) {
                if (req.id) {
                  return [{
                    _id: req.id,
                    name: req.id
                  }, "New World"]
                }
                return [null, "Empty World"];
              }
              return [doc, "hello doc"];
            })
          },
          views : {
            basicView : {
              map : stringFun(function(doc) {
                if (doc.integer) {
                  emit(doc.integer, doc.string);
                }

              })
            },
            complexView: {
              map: stringFun(function(doc) {
                if (doc.type == "complex") {
                  emit([doc.a, doc.b], doc.string);
                }
              })
            },
            complexView2: {
              map: stringFun(function(doc) {
                if (doc.type == "complex") {
                  emit(doc.a, doc.string);
                }
              })
            },
            complexView3: {
              map: stringFun(function(doc) {
                if (doc.type == "complex") {
                  emit(doc.b, doc.string);
                }
              })
            }
          }
        }

        db.save(designDoc);

        var docs = makeDocs(0, 10);
        db.bulkSave(docs);

        var docs2 = [
          {"a": 1, "b": 1, "string": "doc 1", "type": "complex"},
          {"a": 1, "b": 2, "string": "doc 2", "type": "complex"},
          {"a": "test", "b": {}, "string": "doc 3", "type": "complex"},
          {"a": "test", "b": ["test", "essai"], "string": "doc 4", "type": "complex"},
          {"a": {"c": 1}, "b": "", "string": "doc 5", "type": "complex"}
        ];

        db.bulkSave(docs2);

        // test simple rewriting

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/foo");
        T(req.responseText == "This is a base64 encoded text");
        T(req.getResponseHeader("Content-Type") == "text/plain");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/foo2");
        T(req.responseText == "This is a base64 encoded text");
        T(req.getResponseHeader("Content-Type") == "text/plain");

        // test POST
        // hello update world

        var doc = {"word":"plankton", "name":"Rusty"}
        var resp = db.save(doc);
        T(resp.ok);
        var docid = resp.id;

        xhr = CouchDB.request("PUT", "/test_suite_db/_design/test/_rewrite/hello/"+docid);
        T(xhr.status == 201);
        T(xhr.responseText == "hello doc");
        T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")))

        doc = db.open(docid);
        T(doc.world == "hello");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome?name=user");
        T(req.responseText == "Welcome user");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome/user");
        T(req.responseText == "Welcome user");

        req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome2");
        T(req.responseText == "Welcome user");

        xhr = CouchDB.request("PUT", "/test_suite_db/_design/test/_rewrite/welcome3/test");
        T(xhr.status == 201);
        T(xhr.responseText == "New World");
        T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome3/test");
        T(xhr.responseText == "Welcome test");

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicView?startkey=3&endkey=8");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewFixed");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewFixed?startkey=4");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewPath/3/8");
        T(xhr.status == 200, "with query params");
        T(!(/Key: 1/.test(xhr.responseText)));
        T(/FirstKey: 3/.test(xhr.responseText));
        T(/LastKey: 8/.test(xhr.responseText));

        // get with query params
        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView");
        T(xhr.status == 200, "with query params");
        T(/FirstKey: [1, 2]/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView2");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 3/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView3");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 4/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView4");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 5/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView5/test/essai");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 4/.test(xhr.responseText));

        xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView6?a=test&b=essai");
        T(xhr.status == 200, "with query params");
        T(/Value: doc 4/.test(xhr.responseText));

        // test path relative to server
        designDoc.rewrites.push({
           "from": "uuids",
           "to": "../../../_uuids"
        });
        T(db.save(designDoc).ok);

        var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/uuids");
        T(xhr.status == 500);
        var result = JSON.parse(xhr.responseText);
        T(result.error == "insecure_rewrite_rule");

        run_on_modified_server(
          [{section: "httpd",
            key: "secure_rewrites",
            value: "false"}],
            function() {
              var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/uuids?cache=bust");
              T(xhr.status == 200);
              var result = JSON.parse(xhr.responseText);
              T(result.uuids.length == 1);
              var first = result.uuids[0];
        });

  });

}<|endoftext|>


<commit_before>var LABEL_WIDTH = 200,
	GRAPH_WIDTH = 400, // Horizontal distance between centers of vertices on opposite sides
	VTX_RAD = 4,
	VTX_DIST = 40, // Distance between centers of two consecutive vertices
	LABEL_OFFSET = 10, // Distance between label and center of vertex
	MARGIN = {top: VTX_RAD, bottom: VTX_RAD, left: 0, right: 0};

// Overall width of the graphics
var WIDTH = GRAPH_WIDTH + 2 * (LABEL_WIDTH + LABEL_OFFSET);

// Overall height of the graphics
var HEIGHT = 2 * VTX_RAD + MARGIN.top + MARGIN.bottom + VTX_DIST * (Math.max(tac_chars.length, asoiaf_chars.length));

var svg = d3.select("svg");

svg.attr("width", WIDTH)
	.attr("height", HEIGHT);

//////////// Draw the vertices

// TAC vertices
var tac_vertex = svg.selectAll("dummy")
	.data(tac_chars)
  .enter().append("g")
	.attr("transform", function(d, i) {

			// Store each vertex's vertical position
			char_dict[d].y = VTX_RAD + MARGIN.top + i * VTX_DIST;
			// Translate to vertical position of the vertex's center
			return "translate(0," + char_dict[d].y + ")";
		});

tac_vertex.append("circle")
	.attr("cx", LABEL_WIDTH + LABEL_OFFSET)
	.attr("cy", 0)
	.attr("r", VTX_RAD);

// Label each vertex with the character's full name
tac_vertex.append("text")
	.text(function(d) {return char_dict[d].name;})
	.attr("text-anchor", "end")
	.attr("x", LABEL_WIDTH)
	.attr("y", VTX_RAD);

// ASOIAF vertices
var asoiaf_vertex = svg.selectAll("dummy") // So that we get an empty selection
	.data(asoiaf_chars)
  .enter().append("g")
	.attr("transform", function(d, i) {
			char_dict[d].y = VTX_RAD + MARGIN.top + i * VTX_DIST;
			return "translate(" + (LABEL_WIDTH + LABEL_OFFSET + GRAPH_WIDTH) + "," + char_dict[d].y + ")";
		});

asoiaf_vertex.append("circle")
	.attr("cx", 0)
	.attr("cy", 0)
	.attr("r", VTX_RAD);

// Labels
asoiaf_vertex.append("text")
	.text(function(d) {return char_dict[d].name;})
	.attr("text-anchor", "start")
	.attr("x", LABEL_OFFSET)
	.attr("y", VTX_RAD);
<commit_msg>Add helper variables LEFT_X and RIGHT_X<commit_after>var LABEL_WIDTH = 200,
	GRAPH_WIDTH = 400, // Horizontal distance between centers of vertices on opposite sides
	VTX_RAD = 4,
	VTX_DIST = 40, // Distance between centers of two consecutive vertices
	LABEL_OFFSET = 10, // Distance between label and center of vertex
	MARGIN = {top: VTX_RAD, bottom: VTX_RAD, left: 0, right: 0};

// Overall width of the graphics
var WIDTH = GRAPH_WIDTH + 2 * (LABEL_WIDTH + LABEL_OFFSET);

// Overall height of the graphics
var HEIGHT = 2 * VTX_RAD + MARGIN.top + MARGIN.bottom + VTX_DIST * (Math.max(tac_chars.length, asoiaf_chars.length));

// x-coordinates of vertices in left and right parts of the graph
var LEFT_X = MARGIN.left + LABEL_WIDTH + LABEL_OFFSET,
	RIGHT_X = LEFT_X + GRAPH_WIDTH;

var svg = d3.select("svg");

svg.attr("width", WIDTH)
	.attr("height", HEIGHT);

//////////// Draw the vertices

// TAC vertices
var tac_vertex = svg.selectAll("dummy")
	.data(tac_chars)
  .enter().append("g")
	.attr("transform", function(d, i) {

			// Store each vertex's vertical position
			char_dict[d].y = VTX_RAD + MARGIN.top + i * VTX_DIST;
			// Translate to vertical position of the vertex's center
			return "translate(" + MARGIN.left + "," + char_dict[d].y + ")";
		});

tac_vertex.append("circle")
	.attr("cx", LABEL_WIDTH + LABEL_OFFSET)
	.attr("cy", 0)
	.attr("r", VTX_RAD);

// Label each vertex with the character's full name
tac_vertex.append("text")
	.text(function(d) {return char_dict[d].name;})
	.attr("text-anchor", "end")
	.attr("x", LABEL_WIDTH)
	.attr("y", VTX_RAD);

// ASOIAF vertices
var asoiaf_vertex = svg.selectAll("dummy") // So that we get an empty selection
	.data(asoiaf_chars)
  .enter().append("g")
	.attr("transform", function(d, i) {
			char_dict[d].y = VTX_RAD + MARGIN.top + i * VTX_DIST;
			return "translate(" + RIGHT_X + "," + char_dict[d].y + ")";
		});

asoiaf_vertex.append("circle")
	.attr("cx", 0)
	.attr("cy", 0)
	.attr("r", VTX_RAD);

// Labels
asoiaf_vertex.append("text")
	.text(function(d) {return char_dict[d].name;})
	.attr("text-anchor", "start")
	.attr("x", LABEL_OFFSET)
	.attr("y", VTX_RAD);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Jacob Lauritzen.
 *
 * Licensed under MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, $, brackets, window, CodeMirror */

define(function (require, exports, module) {
    "use strict";

    var CommandManager      = brackets.getModule("command/CommandManager"),
        Menus               = brackets.getModule("command/Menus"),
        PreferencesManager  = brackets.getModule("preferences/PreferencesManager"),
        ExtensionUtils      = brackets.getModule("utils/ExtensionUtils"),
        NativeFileSystem    = brackets.getModule("file/NativeFileSystem").NativeFileSystem;

	var preferences = PreferencesManager.getPreferenceStorage("extensions.Themes-for-brackets"),
        menu = Menus.addMenu("Themes", "themes-for-brackets", Menus.AFTER, Menus.AppMenuBar.VIEW_MENU),
        moduleThemesDir = ExtensionUtils.getModulePath(module, "themes/");

	// If there is no currently selected theme, use default
	var __theme = preferences.getValue("theme");
	if (__theme === undefined) {
		preferences.setValue("theme", "default");
		return;
	}

	var Themes = {};
	Themes.currentTheme = __theme;
	Themes.getName = function (theme) {
		theme = theme || Themes.currentTheme;
		theme = theme.replace(new RegExp("-", "g"), " ");
		return theme.charAt(0).toUpperCase() + theme.slice(1);
	};
	Themes.load = function (theme) {
		Themes.setCommand(Themes.currentTheme, false);
		if (theme) {
			Themes.currentTheme = theme;
		}
		$("#currentTheme").attr("href", moduleThemesDir + Themes.currentTheme + ".css");
		Themes.setCommand(Themes.currentTheme, true);
		preferences.setValue("theme", Themes.currentTheme);
		CodeMirror.defaults.theme = Themes.currentTheme;
		$("#editor-holder .CodeMirror").addClass("cm-s-" + Themes.currentTheme);
	};
	Themes.setCommand = function (theme, val) {
		CommandManager.get("jacse.themes-for-brackets.changetheme_" + theme).setChecked(val);
	};

	function Theme(theme) {
		this.theme = theme;
		this.command_id = "jacse.themes-for-brackets.changetheme_" + theme;
		var that = this;
		CommandManager.register(Themes.getName(this.theme), this.command_id, function () {
			Themes.load(that.theme);
		});
		menu.addMenuItem(this.command_id);
	}

	// Pass file names as an array and create the Themes
    Themes.getDirFiles = function (themesNameArray) {
        var i;
        Themes.allThemes = [];
        for (i = 0; i < themesNameArray.length; i += 1) {
            Themes.allThemes.push(new Theme(themesNameArray[i]));
        }
        $("body").append('<link id="currentTheme" rel="stylesheet"/>');
        $("body").append('<style>.CodeMirror-scroll{background-color:transparent;}.CodeMirror-gutters{border-right:none;</style>');
        Themes.load(Themes.currentTheme);
    };

    // Get the theme directory file names without the .css extension
    NativeFileSystem.requestNativeFileSystem(moduleThemesDir, function (rootEntry) {
        rootEntry.root.createReader().readEntries(function (entries) {
            var i,
                fetching = [],
                len = entries.length;
            for (i = 0; i < len; i += 1) {
                fetching.push(entries[i].name.replace(".css", ""));
            }
            $.when.apply(module, fetching).done(Themes.getDirFiles(fetching));
        });
    });
});<commit_msg>Add default theme to the top of the menu<commit_after>/*
 * Copyright (c) 2013 Jacob Lauritzen.
 *
 * Licensed under MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, $, brackets, window, CodeMirror */

define(function (require, exports, module) {
    "use strict";

    var CommandManager      = brackets.getModule("command/CommandManager"),
        Menus               = brackets.getModule("command/Menus"),
        PreferencesManager  = brackets.getModule("preferences/PreferencesManager"),
        ExtensionUtils      = brackets.getModule("utils/ExtensionUtils"),
        NativeFileSystem    = brackets.getModule("file/NativeFileSystem").NativeFileSystem;

	var preferences = PreferencesManager.getPreferenceStorage("extensions.Themes-for-brackets"),
        menu = Menus.addMenu("Themes", "themes-for-brackets", Menus.AFTER, Menus.AppMenuBar.VIEW_MENU),
        moduleThemesDir = ExtensionUtils.getModulePath(module, "themes/");

	// If there is no currently selected theme, use default
	var __theme = preferences.getValue("theme");
	if (__theme === undefined) {
		preferences.setValue("theme", "default");
		return;
	}

	var Themes = {};
	Themes.currentTheme = __theme;
	Themes.getName = function (theme) {
		theme = theme || Themes.currentTheme;
		theme = theme.replace(new RegExp("-", "g"), " ");
		return theme.charAt(0).toUpperCase() + theme.slice(1);
	};
	Themes.load = function (theme) {
		Themes.setCommand(Themes.currentTheme, false);
		if (theme) {
			Themes.currentTheme = theme;
		}
		$("#currentTheme").attr("href", moduleThemesDir + Themes.currentTheme + ".css");
		Themes.setCommand(Themes.currentTheme, true);
		preferences.setValue("theme", Themes.currentTheme);
		CodeMirror.defaults.theme = Themes.currentTheme;
		$("#editor-holder .CodeMirror").addClass("cm-s-" + Themes.currentTheme);
	};
	Themes.setCommand = function (theme, val) {
		CommandManager.get("jacse.themes-for-brackets.changetheme_" + theme).setChecked(val);
	};

	function Theme(theme) {
		this.theme = theme;
		this.command_id = "jacse.themes-for-brackets.changetheme_" + theme;
		var that = this;
		CommandManager.register(Themes.getName(this.theme), this.command_id, function () {
			Themes.load(that.theme);
		});
		menu.addMenuItem(this.command_id);
	}

	// Pass file names as an array and create the Themes
    Themes.getDirFiles = function (themesNameArray) {
        var i,
            len = themesNameArray.length,
            findDefault = themesNameArray.indexOf('default');
        if (findDefault !== -1) {
            themesNameArray = themesNameArray.splice(findDefault, 1).concat(themesNameArray);
        }
        Themes.allThemes = [];
        for (i = 0; i < len; i += 1) {
            Themes.allThemes.push(new Theme(themesNameArray[i]));
        }
        $("body").append('<link id="currentTheme" rel="stylesheet"/>');
        $("body").append('<style>.CodeMirror-scroll{background-color:transparent;}.CodeMirror-gutters{border-right:none;</style>');
        Themes.load(Themes.currentTheme);
    };

    // Get the theme directory file names without the .css extension
    NativeFileSystem.requestNativeFileSystem(moduleThemesDir, function (rootEntry) {
        rootEntry.root.createReader().readEntries(function (entries) {
            var i,
                fetching = [],
                len = entries.length;
            for (i = 0; i < len; i += 1) {
                fetching.push(entries[i].name.replace(".css", ""));
            }
            $.when.apply(module, fetching).done(Themes.getDirFiles(fetching));
        });
    });
});<|endoftext|>