<commit_before>/**
 * @fileOverview tests.js Gives testing code for the new page.
 * This spec is managed by the Jasmine testing library.
 *
 * More information on testing:
 * https://github.com/privly/privly-organization/wiki/Testing
 *
 **/

/**
 * Helper function for resetting the state of the application to the initial
 * state of the app. Since the application first checks its status with the
 * remote server, this function blocks execution until it knows the app is
 * intialized or there was likely an error.
 *
 * @param {boolean} loggedOut Indicates whether the user should be logged out
 * of the app (true) or logged into the app
 */
function initializeApp(loggedOut) {

  // used to check if asynchronous calls completed
  var initializationFlag = false;

  //clear the table
  var table = $.fn.dataTable.fnTables(true);
  if( table.length > 0 )
    $(table[0]).dataTable().fnDestroy(false);
  $("#table_body").children().remove();

  if(loggedOut) {

    //log the user out
    var domain = privlyNetworkService.contentServerDomain();
    privlyNetworkService.authToken = "auth_token=pE8dHprCJ79QENLedELx";
    $.post(domain + "/users/sign_out", "_method=delete", function(data) {
      privlyNetworkService.authToken = "";
      callbacks.pendingLogin();
    });
  } else {

    // Log the user in using the auth token for the user
    // danger.dont.use.bork.bork.bork@privly.org
    // You must create this user and assign it the auth token as
    // below
    privlyNetworkService.authToken = "auth_token=pE8dHprCJ79QENLedELx";
    callbacks.pendingLogin();
  }

  setTimeout(function() {
    if($('#loadingDiv').is(":hidden")) initializationFlag = true;
  }, 900);
}

var keys = Object.keys(__html__);
var selectKey;
keys.forEach(function(key) {
  if( key.indexOf("History/new.html") >= 0 ) {
    selectKey = key;
  }
});

// WARNING: These tests require the existence of an authentication_token
// equal to: "pE8dHprCJ79QENLedELx"
describe ("History Logged In New Suite", function() {

  // Get an HTML document defined by the pre-processor.
  // This is a rough hack because HTML2JS seems to assign the
  // key to the absolute URL, which is not reliable on
  // continuous integration.
  beforeEach(function() {
    document.body.innerHTML = __html__[selectKey];
    initializeApp(false);
  });

  it("initializes properly", function() {

    // Should initialize the navigation
    callbacks.pendingLogin();
    var domain = privlyNetworkService.contentServerDomain();

    if( privlyNetworkService.platformName() !== "HOSTED" ) {

      // if the app is not hosted, the user should first be directed to the
      // content_server page of the app bundle.
      expect($(".home_domain").attr("href")).toBe('../Help/content_server.html');
    } else {

      // if the application is hosted, the URL should connect to the domain's root
      expect($(".home_domain").attr("href")).toBe("http://" + window.location.href.split("/")[2]);
    }
    expect(domain.split("/")[2]).toBe($(".home_domain").text());
  });

  it("does not result in an error", function() {
    resizeIframePostedMessage({data: "1,1"});
    expect(true).toBe(true);
  });

  it("fill the history array", function() {

    // Exemple of output
    var data = {
      json: [
        {
          created_at: "Mar 01 2015",
          burn_after_date: "Mar 10 2015",
          updated_at: "Mar 05 2015",
          privly_URL: "testUrl",
          privly_application: "Tester"
        },
        {
          created_at: "Mar 01 2014",
          burn_after_date: "Mar 10 2014",
          updated_at: "Mar 05 2014",
          privly_URL: "testUrl2",
          privly_application: "Tester2"
        }
      ]
    };

    callbacks.postCompleted(data);

    //Check existence
    expect(jQuery("#table_body tr").length).toBe(2);
    expect(jQuery("#table_body tr:first td").length).toBe(4);
    expect(jQuery("#table_body tr:first td:first button").length).toBe(2);

    //Check content
    var cells = jQuery("#table_body tr:first td");
    var buttons = jQuery(cells[0]).children("button");

    expect(buttons[0].getAttribute("data-target")).toBe("#historyPreview");
    expect(buttons[0].textContent).toMatch(/Preview Tester/);
    expect(buttons[1].getAttribute("data-canonical-href")).toMatch(/show.html\?privlyOriginalURL\=testUrl/);

    for(var i = 1; i <= 3; i++)
      expect(cells[i].textContent).not.toBe(""); // TODO unit tests for parseDate

  });

});

describe ("History Logged out New Suite", function() {

  // Get an HTML document defined by the pre-processor.
  // This is a rough hack because HTML2JS seems to assign the
  // key to the absolute URL, which is not reliable on
  // continuous integration.
  beforeEach(function() {
    document.body.innerHTML = __html__[selectKey];
    initializeApp(true);
  });

  it("initializes properly", function() {

    // Should initialize the navigation
    callbacks.pendingLogin();
    var domain = privlyNetworkService.contentServerDomain();

    if( privlyNetworkService.platformName() !== "HOSTED" ) {

      // if the app is not hosted, the user should first be directed to the
      // content_server page of the app bundle.
      expect($(".home_domain").attr("href")).toBe('../Help/content_server.html');
    } else {

      // if the application is hosted, the URL should connect to the domain's root
      expect($(".home_domain").attr("href")).toBe("http://" + window.location.href.split("/")[2]);
    }
    expect(domain.split("/")[2]).toBe($(".home_domain").text());
  });

  it("does not result in an error", function() {
    resizeIframePostedMessage({data: "1,1"});
    expect(true).toBe(true);
  });

});

<commit_msg>Add some unit tests for getMessageDOM<commit_after>/**
 * @fileOverview tests.js Gives testing code for the new page.
 * This spec is managed by the Jasmine testing library.
 *
 * More information on testing:
 * https://github.com/privly/privly-organization/wiki/Testing
 *
 **/

/**
 * Helper function for resetting the state of the application to the initial
 * state of the app. Since the application first checks its status with the
 * remote server, this function blocks execution until it knows the app is
 * intialized or there was likely an error.
 *
 * @param {boolean} loggedOut Indicates whether the user should be logged out
 * of the app (true) or logged into the app
 */
function initializeApp(loggedOut) {

  // used to check if asynchronous calls completed
  var initializationFlag = false;

  //clear the table
  var table = $.fn.dataTable.fnTables(true);
  if( table.length > 0 )
    $(table[0]).dataTable().fnDestroy(false);
  $("#table_body").children().remove();

  if(loggedOut) {

    //log the user out
    var domain = privlyNetworkService.contentServerDomain();
    privlyNetworkService.authToken = "auth_token=pE8dHprCJ79QENLedELx";
    $.post(domain + "/users/sign_out", "_method=delete", function(data) {
      privlyNetworkService.authToken = "";
      callbacks.pendingLogin();
    });
  } else {

    // Log the user in using the auth token for the user
    // danger.dont.use.bork.bork.bork@privly.org
    // You must create this user and assign it the auth token as
    // below
    privlyNetworkService.authToken = "auth_token=pE8dHprCJ79QENLedELx";
    callbacks.pendingLogin();
  }

  setTimeout(function() {
    if($('#loadingDiv').is(":hidden")) initializationFlag = true;
  }, 900);
}

var keys = Object.keys(__html__);
var selectKey;
keys.forEach(function(key) {
  if( key.indexOf("History/new.html") >= 0 ) {
    selectKey = key;
  }
});

// WARNING: These tests require the existence of an authentication_token
// equal to: "pE8dHprCJ79QENLedELx"
describe ("History Logged In New Suite", function() {

  // Get an HTML document defined by the pre-processor.
  // This is a rough hack because HTML2JS seems to assign the
  // key to the absolute URL, which is not reliable on
  // continuous integration.
  beforeEach(function() {
    document.body.innerHTML = __html__[selectKey];
    initializeApp(false);
  });

  it("initializes properly", function() {

    // Should initialize the navigation
    callbacks.pendingLogin();
    var domain = privlyNetworkService.contentServerDomain();

    if( privlyNetworkService.platformName() !== "HOSTED" ) {

      // if the app is not hosted, the user should first be directed to the
      // content_server page of the app bundle.
      expect($(".home_domain").attr("href")).toBe('../Help/content_server.html');
    } else {

      // if the application is hosted, the URL should connect to the domain's root
      expect($(".home_domain").attr("href")).toBe("http://" + window.location.href.split("/")[2]);
    }
    expect(domain.split("/")[2]).toBe($(".home_domain").text());
  });

  it("does not result in an error", function() {
    resizeIframePostedMessage({data: "1,1"});
    expect(true).toBe(true);
  });

  it("fills the history array", function() {

    // Exemple of output
    var data = {
      json: [
        {
          created_at: "Mar 01 2015",
          burn_after_date: "Mar 10 2015",
          updated_at: "Mar 05 2015",
          privly_URL: "testUrl",
          privly_application: "Tester"
        },
        {
          created_at: "Mar 01 2014",
          burn_after_date: "Mar 10 2014",
          updated_at: "Mar 05 2014",
          privly_URL: "testUrl2",
          privly_application: "Tester2"
        }
      ]
    };

    callbacks.postCompleted(data);

    // Check existence
    expect(jQuery("#table_body tr").length).toBe(2);
    expect(jQuery("#table_body tr:first td").length).toBe(4);
    expect(jQuery("#table_body tr:first td:first button").length).toBe(2);

    // Check content
    var cells = jQuery("#table_body tr:first td");
    var buttons = jQuery(cells[0]).children("button");

    expect(buttons[0].getAttribute("data-target")).toBe("#historyPreview");
    expect(buttons[0].textContent).toMatch(/Preview Tester/);
    expect(buttons[1].getAttribute("data-canonical-href")).toMatch(/show.html\?privlyOriginalURL\=testUrl/);

    for(var i = 1; i <= 3; i++) {
      expect(cells[i].textContent).not.toBe(""); // TODO unit tests for parseDate
    }

  });

});

describe ("History Logged out New Suite", function() {

  // Get an HTML document defined by the pre-processor.
  // This is a rough hack because HTML2JS seems to assign the
  // key to the absolute URL, which is not reliable on
  // continuous integration.
  beforeEach(function() {
    document.body.innerHTML = __html__[selectKey];
    initializeApp(true);
  });

  it("initializes properly", function() {

    // Should initialize the navigation
    callbacks.pendingLogin();
    var domain = privlyNetworkService.contentServerDomain();

    if( privlyNetworkService.platformName() !== "HOSTED" ) {

      // if the app is not hosted, the user should first be directed to the
      // content_server page of the app bundle.
      expect($(".home_domain").attr("href")).toBe('../Help/content_server.html');
    } else {

      // if the application is hosted, the URL should connect to the domain's root
      expect($(".home_domain").attr("href")).toBe("http://" + window.location.href.split("/")[2]);
    }
    expect(domain.split("/")[2]).toBe($(".home_domain").text());
  });

  it("does not result in an error", function() {
    resizeIframePostedMessage({data: "1,1"});
    expect(true).toBe(true);
  });

});

describe("History side functions", function() {
  describe("getMessageDOM", function() {
    it("transforms a single JSON object to DOM array row", function() {

      var data = {
        created_at: "Mar 01 2015",
        burn_after_date: "Mar 10 2015",
        updated_at: "Mar 05 2015",
        privly_URL: "testUrl",
        privly_application: "Tester"
      };

      var DOM = getMessageDOM(data);
      var cells = jQuery(DOM).children("td");

      // Check existence
      expect(jQuery(cells[0]).children("button").length).toBe(2);
      expect(cells.length).toBe(4);

      // Check content
      expect(jQuery(cells[0]).children("button")[0].textContent).toMatch(/Preview Tester/);
      for(var i = 1; i <= 3; i++) {
        expect(cells[i].textContent).not.toBe(""); // TODO unit tests for parseDate
      }

    });
  });
});<|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.

// *********************** Test Framework of Sorts ************************* //

function loadScript(url) {
  // disallow loading remote URLs
  if((url.substr(0, 7) == "http://")
    || (url.substr(0, 2) == "//")
    || (url.substr(0, 5) == "data:")
    || (url.substr(0, 11) == "javascript:")) {
        throw "Not loading remote test scripts";
  }
  if (typeof document != "undefined") document.write('');
};

function patchTest(fun) {
  var source = fun.toString();
  var output = "";
  var i = 0;
  var testMarker = "T(";
  while (i < source.length) {
    var testStart = source.indexOf(testMarker, i);
    if (testStart == -1) {
      output = output + source.substring(i, source.length);
      break;
    }
    var testEnd = source.indexOf(");", testStart);
    var testCode = source.substring(testStart + testMarker.length, testEnd);
    output += source.substring(i, testStart) + "T(" + testCode + "," + JSON.stringify(testCode);
    i = testEnd;
  }
  try {
    return eval("(" + output + ")");
  } catch (e) {
    return null;
  }
}

function runAllTests() {
  var rows = $("#tests tbody.content tr");
  $("td", rows).text("");
  $("td.status", rows).removeClass("error").removeClass("failure").removeClass("success").text("not run");
  var offset = 0;
  function runNext() {
    if (offset < rows.length) {
      var row = rows.get(offset);
      runTest($("th button", row).get(0), function() {
        offset += 1;
        setTimeout(runNext, 100);
      }, false, true);
    } else {
      saveTestReport();
    }
  }
  runNext();
}

var numFailures = 0;
var currentRow = null;

function runTest(button, callback, debug, noSave) {

  // offer to save admins
  if (currentRow != null) {
    alert("Can not run multiple tests simultaneously.");
    return;
  }
  var row = currentRow = $(button).parents("tr").get(0);
  $("td.status", row).removeClass("error").removeClass("failure").removeClass("success");
  $("td", row).text("");
  $("#toolbar li.current").text("Running: "+row.id);
  var testFun = couchTests[row.id];
  function run() {
    numFailures = 0;
    var start = new Date().getTime();
    try {
      if (debug == undefined || !debug) {
        testFun = patchTest(testFun) || testFun;
      }
      testFun(debug);
      var status = numFailures > 0 ? "failure" : "success";
    } catch (e) {
      var status = "error";
      if ($("td.details ol", row).length == 0) {
        $("

").appendTo($("td.details", row));
      }
      $("
Exception raised:
")
        .find("code").text(JSON.stringify(e)).end()
        .appendTo($("td.details ol", row));
      if (debug) {
        currentRow = null;
        throw e;
      }
    }
    if ($("td.details ol", row).length) {
      $("Run with debugger").click(function() {
        runTest(this, undefined, true);
      }).prependTo($("td.details ol", row));
    }
    var duration = new Date().getTime() - start;
    $("td.status", row).removeClass("running").addClass(status).text(status);
    $("td.duration", row).text(duration + "ms");
    $("#toolbar li.current").text("Finished: "+row.id);
    updateTestsFooter();
    currentRow = null;
    if (callback) callback();
    if (!noSave) saveTestReport();
  }
  $("td.status", row).addClass("running").text("running…");
  setTimeout(run, 100);
}

function showSource(cell) {
  var name = $(cell).text();
  var win = window.open("", name, "width=700,height=500,resizable=yes,scrollbars=yes");
  win.document.location = "script/test/" + name + ".js";
}

var readyToRun;
function setupAdminParty(fun) {
  if (readyToRun) {
    fun();
  } else {
    function removeAdmins(confs, doneFun) {
      // iterate through the config and remove current user last
      // current user is at front of list
      var remove = confs.pop();
      if (remove) {
        $.couch.config({
          success : function() {
            removeAdmins(confs, doneFun);
          }
        }, "admins", remove[0], null);
      } else {
        doneFun();
      }
    };
    $.couch.session({
      success : function(resp) {
        var userCtx = resp.userCtx;
        if (userCtx.name && userCtx.roles.indexOf("_admin") != -1) {
          // admin but not admin party. dialog offering to make admin party
          $.showDialog("dialog/_admin_party.html", {
            submit: function(data, callback) {
              $.couch.config({
                success : function(conf) {
                  var meAdmin, adminConfs = [];
                  for (var name in conf) {
                    if (name == userCtx.name) {
                      meAdmin = [name, conf[name]];
                    } else {
                      adminConfs.push([name, conf[name]]);
                    }
                  }
                  adminConfs.unshift(meAdmin);
                  removeAdmins(adminConfs, function() {
                    callback();
                    $.futon.session.sidebar();
                    readyToRun = true;
                    setTimeout(fun, 500);
                  });
                }
              }, "admins");
            }
          });
        } else if (userCtx.roles.indexOf("_admin") != -1) {
          // admin party!
          readyToRun = true;
          fun();
        } else {
          // not an admin
          alert("Error: You need to be an admin to run the tests.");
        };
      }
    });
  }
};

function updateTestsListing() {
  for (var name in couchTests) {
    var testFunction = couchTests[name];
    var row = $("

")
      .find("th").text(name).attr("title", "Show source").click(function() {
        showSource(this);
      }).end()
      .find("td:nth(0)").addClass("status").text("not run").end()
      .find("td:nth(1)").addClass("duration").end()
      .find("td:nth(2)").addClass("details").end();
    $("<button type='button' class='run' title='Run test'></button>").click(function() {
      this.blur();
      var self = this;
      // check for admin party
      setupAdminParty(function() {
        runTest(self);
      });
      return false;
    }).prependTo(row.find("th"));
    row.attr("id", name).appendTo("#tests tbody.content");
  }
  $("#tests tr").removeClass("odd").filter(":odd").addClass("odd");
  updateTestsFooter();
}

function updateTestsFooter() {
  var tests = $("#tests tbody.content tr td.status");
  var testsRun = tests.filter(".success, .error, .failure");
  var testsFailed = testsRun.not(".success");
  var totalDuration = 0;
  $("#tests tbody.content tr td.duration:contains('ms')").each(function() {
    var text = $(this).text();
    totalDuration += parseInt(text.substr(0, text.length - 2), 10);
  });
  $("#tests tbody.footer td").html(""+testsRun.length + " of " + tests.length +
    " test(s) run, " + testsFailed.length + " failures (" +
    totalDuration + " ms) ");
}

// make report and save to local db
// display how many reports need replicating to the mothership
// have button to replicate them

function saveTestReport(report) {
  var report = makeTestReport();
  if (report) {
    var db = $.couch.db("test_suite_reports");
    var saveReport = function(db_info) {
      report.db = db_info;
      $.couch.info({success : function(node_info) {
        report.node = node_info;
        db.saveDoc(report);
      }});
    };
    var createDb = function() {
      db.create({success: function() {
        db.info({success:saveReport});
      }});
    };
    db.info({error: createDb, success:saveReport});
  }
};

function makeTestReport() {
  var report = {};
  report.summary = $("#tests tbody.footer td").text();
  report.platform = testPlatform();
  var date = new Date();
  report.timestamp = date.getTime();
  report.timezone = date.getTimezoneOffset();
  report.tests = [];
  $("#tests tbody.content tr").each(function() {
    var status = $("td.status", this).text();
    if (status != "not run") {
      var test = {};
      test.name = this.id;
      test.status = status;
      test.duration = parseInt($("td.duration", this).text());
      test.details = [];
      $("td.details li", this).each(function() {
        test.details.push($(this).text());
      });
      if (test.details.length == 0) {
        delete test.details;
      }
      report.tests.push(test);
    }
  });
  if (report.tests.length > 0) return report;
};

function testPlatform() {
  var b = $.browser;
  var bs = ["mozilla", "msie", "opera", "safari"];
  for (var i=0; i < bs.length; i++) {
    if (b[bs[i]]) {
      return {"browser" : bs[i], "version" : b.version};
    }
  };
  return {"browser" : "undetected"};
}

function reportTests() {
  // replicate the database to couchdb.couchdb.org
}

// Use T to perform a test that returns false on failure and if the test fails,
// display the line that failed.
// Example:
// T(MyValue==1);
function T(arg1, arg2, testName) {
  if (!arg1) {
    if (currentRow) {
      if ($("td.details ol", currentRow).length == 0) {
        $("

").appendTo($("td.details", currentRow));
      }
      var message = (arg2 != null ? arg2 : arg1).toString();
      $("
Assertion " + (testName ? "'" + testName + "'" : "") + " failed:
")
        .find("code").text(message).end()
        .appendTo($("td.details ol", currentRow));
    }
    numFailures += 1;
  }
}

function TIsnull(actual, testName) {
  T(actual === null, "expected 'null', got '"
    + repr(actual) + "'", testName);
}

function TEquals(expected, actual, testName) {
  T(equals(expected, actual), "expected '" + repr(expected) +
    "', got '" + repr(actual) + "'", testName);
}

function TEqualsIgnoreCase(expected, actual, testName) {
  T(equals(expected.toUpperCase(), actual.toUpperCase()), "expected '" + repr(expected) +
    "', got '" + repr(actual) + "'", testName);
}

function equals(a,b) {
  if (a === b) return true;
  try {
    return repr(a) === repr(b);
  } catch (e) {
    return false;
  }
}

function repr(val) {
  if (val === undefined) {
    return null;
  } else if (val === null) {
    return "null";
  } else {
    return JSON.stringify(val);
  }
}

function makeDocs(start, end, templateDoc) {
  var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}";
  if (end === undefined) {
    end = start;
    start = 0;
  }
  var docs = [];
  for (var i = start; i < end; i++) {
    var newDoc = eval("(" + templateDocSrc + ")");
    newDoc._id = (i).toString();
    newDoc.integer = i;
    newDoc.string = (i).toString();
    docs.push(newDoc);
  }
  return docs;
}

function run_on_modified_server(settings, fun) {
  try {
    // set the settings
    for(var i=0; i < settings.length; i++) {
      var s = settings[i];
      var xhr = CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, {
        body: JSON.stringify(s.value),
        headers: {"X-Couch-Persist": "false"}
      });
      CouchDB.maybeThrowError(xhr);
      s.oldValue = xhr.responseText;
    }
    // run the thing
    fun();
  } finally {
    // unset the settings
    for(var j=0; j < i; j++) {
      var s = settings[j];
      if(s.oldValue == "\"\"\n") { // unset value
        CouchDB.request("DELETE", "/_config/" + s.section + "/" + s.key, {
          headers: {"X-Couch-Persist": "false"}
        });
      } else {
        CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, {
          body: s.oldValue,
          headers: {"X-Couch-Persist": "false"}
        });
      }
    }
  }
}

function stringFun(fun) {
  var string = fun.toSource ? fun.toSource() : "(" + fun.toString() + ")";
  return string;
}

function waitForSuccess(fun, tag) {
  var start = new Date();
  while(true) {
    if (new Date() - start > 5000) {
      throw("timeout: "+tag);
    } else {
      try {
        fun();
        break;
      } catch (e) {}
      // sync http req allow async req to happen
      CouchDB.request("GET", "/test_suite_db/?tag="+encodeURIComponent(tag));
    }
  }
}

function waitForRestart() {
  var waiting = true;
  // Wait for the server to go down but don't
  // wait too long because we might miss the
  // the unavailable period.
  var count = 25;
  while (waiting && count > 0) {
    count--;
    try {
      CouchDB.request("GET", "/");
    } catch(e) {
      waiting = false;
    }
  }
  // Wait for it to come back up
  waiting = true;
  while (waiting) {
    try {
      CouchDB.request("GET", "/");
      waiting = false;
    } catch(e) {
      // the request will fail until restart completes
    }
  }
};

function restartServer() {
  var xhr;
  try {
    CouchDB.request("POST", "/_restart");
  } catch(e) {
    // this request may sometimes fail
  }
  waitForRestart();
}

// legacy functions for CouchDB < 1.2.0
// we keep them to make sure we keep BC
CouchDB.user_prefix = "org.couchdb.user:";

CouchDB.prepareUserDoc = function(user_doc, new_password) {
  user_doc._id = user_doc._id || CouchDB.user_prefix + user_doc.name;
  if (new_password) {
    // handle the password crypto
    user_doc.salt = CouchDB.newUuids(1)[0];
    user_doc.password_sha = hex_sha1(new_password + user_doc.salt);
  }
  user_doc.type = "user";
  if (!user_doc.roles) {
    user_doc.roles = [];
  }
  return user_doc;
};
<commit_msg>improve the startup delay for js test harness<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.

// *********************** Test Framework of Sorts ************************* //

function loadScript(url) {
  // disallow loading remote URLs
  if((url.substr(0, 7) == "http://")
    || (url.substr(0, 2) == "//")
    || (url.substr(0, 5) == "data:")
    || (url.substr(0, 11) == "javascript:")) {
        throw "Not loading remote test scripts";
  }
  if (typeof document != "undefined") document.write('');
};

function patchTest(fun) {
  var source = fun.toString();
  var output = "";
  var i = 0;
  var testMarker = "T(";
  while (i < source.length) {
    var testStart = source.indexOf(testMarker, i);
    if (testStart == -1) {
      output = output + source.substring(i, source.length);
      break;
    }
    var testEnd = source.indexOf(");", testStart);
    var testCode = source.substring(testStart + testMarker.length, testEnd);
    output += source.substring(i, testStart) + "T(" + testCode + "," + JSON.stringify(testCode);
    i = testEnd;
  }
  try {
    return eval("(" + output + ")");
  } catch (e) {
    return null;
  }
}

function runAllTests() {
  var rows = $("#tests tbody.content tr");
  $("td", rows).text("");
  $("td.status", rows).removeClass("error").removeClass("failure").removeClass("success").text("not run");
  var offset = 0;
  function runNext() {
    if (offset < rows.length) {
      var row = rows.get(offset);
      runTest($("th button", row).get(0), function() {
        offset += 1;
        setTimeout(runNext, 100);
      }, false, true);
    } else {
      saveTestReport();
    }
  }
  runNext();
}

var numFailures = 0;
var currentRow = null;

function runTest(button, callback, debug, noSave) {

  // offer to save admins
  if (currentRow != null) {
    alert("Can not run multiple tests simultaneously.");
    return;
  }
  var row = currentRow = $(button).parents("tr").get(0);
  $("td.status", row).removeClass("error").removeClass("failure").removeClass("success");
  $("td", row).text("");
  $("#toolbar li.current").text("Running: "+row.id);
  var testFun = couchTests[row.id];
  function run() {
    numFailures = 0;
    var start = new Date().getTime();
    try {
      if (debug == undefined || !debug) {
        testFun = patchTest(testFun) || testFun;
      }
      testFun(debug);
      var status = numFailures > 0 ? "failure" : "success";
    } catch (e) {
      var status = "error";
      if ($("td.details ol", row).length == 0) {
        $("

").appendTo($("td.details", row));
      }
      $("
Exception raised:
")
        .find("code").text(JSON.stringify(e)).end()
        .appendTo($("td.details ol", row));
      if (debug) {
        currentRow = null;
        throw e;
      }
    }
    if ($("td.details ol", row).length) {
      $("Run with debugger").click(function() {
        runTest(this, undefined, true);
      }).prependTo($("td.details ol", row));
    }
    var duration = new Date().getTime() - start;
    $("td.status", row).removeClass("running").addClass(status).text(status);
    $("td.duration", row).text(duration + "ms");
    $("#toolbar li.current").text("Finished: "+row.id);
    updateTestsFooter();
    currentRow = null;
    if (callback) callback();
    if (!noSave) saveTestReport();
  }
  $("td.status", row).addClass("running").text("running…");
  setTimeout(run, 100);
}

function showSource(cell) {
  var name = $(cell).text();
  var win = window.open("", name, "width=700,height=500,resizable=yes,scrollbars=yes");
  win.document.location = "script/test/" + name + ".js";
}

var readyToRun;
function setupAdminParty(fun) {
  if (readyToRun) {
    fun();
  } else {
    function removeAdmins(confs, doneFun) {
      // iterate through the config and remove current user last
      // current user is at front of list
      var remove = confs.pop();
      if (remove) {
        $.couch.config({
          success : function() {
            removeAdmins(confs, doneFun);
          }
        }, "admins", remove[0], null);
      } else {
        doneFun();
      }
    };
    $.couch.session({
      success : function(resp) {
        var userCtx = resp.userCtx;
        if (userCtx.name && userCtx.roles.indexOf("_admin") != -1) {
          // admin but not admin party. dialog offering to make admin party
          $.showDialog("dialog/_admin_party.html", {
            submit: function(data, callback) {
              $.couch.config({
                success : function(conf) {
                  var meAdmin, adminConfs = [];
                  for (var name in conf) {
                    if (name == userCtx.name) {
                      meAdmin = [name, conf[name]];
                    } else {
                      adminConfs.push([name, conf[name]]);
                    }
                  }
                  adminConfs.unshift(meAdmin);
                  removeAdmins(adminConfs, function() {
                    callback();
                    $.futon.session.sidebar();
                    readyToRun = true;
                    setTimeout(fun, 500);
                  });
                }
              }, "admins");
            }
          });
        } else if (userCtx.roles.indexOf("_admin") != -1) {
          // admin party!
          readyToRun = true;
          fun();
        } else {
          // not an admin
          alert("Error: You need to be an admin to run the tests.");
        };
      }
    });
  }
};

function updateTestsListing() {
  for (var name in couchTests) {
    var testFunction = couchTests[name];
    var row = $("

")
      .find("th").text(name).attr("title", "Show source").click(function() {
        showSource(this);
      }).end()
      .find("td:nth(0)").addClass("status").text("not run").end()
      .find("td:nth(1)").addClass("duration").end()
      .find("td:nth(2)").addClass("details").end();
    $("<button type='button' class='run' title='Run test'></button>").click(function() {
      this.blur();
      var self = this;
      // check for admin party
      setupAdminParty(function() {
        runTest(self);
      });
      return false;
    }).prependTo(row.find("th"));
    row.attr("id", name).appendTo("#tests tbody.content");
  }
  $("#tests tr").removeClass("odd").filter(":odd").addClass("odd");
  updateTestsFooter();
}

function updateTestsFooter() {
  var tests = $("#tests tbody.content tr td.status");
  var testsRun = tests.filter(".success, .error, .failure");
  var testsFailed = testsRun.not(".success");
  var totalDuration = 0;
  $("#tests tbody.content tr td.duration:contains('ms')").each(function() {
    var text = $(this).text();
    totalDuration += parseInt(text.substr(0, text.length - 2), 10);
  });
  $("#tests tbody.footer td").html(""+testsRun.length + " of " + tests.length +
    " test(s) run, " + testsFailed.length + " failures (" +
    totalDuration + " ms) ");
}

// make report and save to local db
// display how many reports need replicating to the mothership
// have button to replicate them

function saveTestReport(report) {
  var report = makeTestReport();
  if (report) {
    var db = $.couch.db("test_suite_reports");
    var saveReport = function(db_info) {
      report.db = db_info;
      $.couch.info({success : function(node_info) {
        report.node = node_info;
        db.saveDoc(report);
      }});
    };
    var createDb = function() {
      db.create({success: function() {
        db.info({success:saveReport});
      }});
    };
    db.info({error: createDb, success:saveReport});
  }
};

function makeTestReport() {
  var report = {};
  report.summary = $("#tests tbody.footer td").text();
  report.platform = testPlatform();
  var date = new Date();
  report.timestamp = date.getTime();
  report.timezone = date.getTimezoneOffset();
  report.tests = [];
  $("#tests tbody.content tr").each(function() {
    var status = $("td.status", this).text();
    if (status != "not run") {
      var test = {};
      test.name = this.id;
      test.status = status;
      test.duration = parseInt($("td.duration", this).text());
      test.details = [];
      $("td.details li", this).each(function() {
        test.details.push($(this).text());
      });
      if (test.details.length == 0) {
        delete test.details;
      }
      report.tests.push(test);
    }
  });
  if (report.tests.length > 0) return report;
};

function testPlatform() {
  var b = $.browser;
  var bs = ["mozilla", "msie", "opera", "safari"];
  for (var i=0; i < bs.length; i++) {
    if (b[bs[i]]) {
      return {"browser" : bs[i], "version" : b.version};
    }
  };
  return {"browser" : "undetected"};
}

function reportTests() {
  // replicate the database to couchdb.couchdb.org
}

// Use T to perform a test that returns false on failure and if the test fails,
// display the line that failed.
// Example:
// T(MyValue==1);
function T(arg1, arg2, testName) {
  if (!arg1) {
    if (currentRow) {
      if ($("td.details ol", currentRow).length == 0) {
        $("

").appendTo($("td.details", currentRow));
      }
      var message = (arg2 != null ? arg2 : arg1).toString();
      $("
Assertion " + (testName ? "'" + testName + "'" : "") + " failed:
")
        .find("code").text(message).end()
        .appendTo($("td.details ol", currentRow));
    }
    numFailures += 1;
  }
}

function TIsnull(actual, testName) {
  T(actual === null, "expected 'null', got '"
    + repr(actual) + "'", testName);
}

function TEquals(expected, actual, testName) {
  T(equals(expected, actual), "expected '" + repr(expected) +
    "', got '" + repr(actual) + "'", testName);
}

function TEqualsIgnoreCase(expected, actual, testName) {
  T(equals(expected.toUpperCase(), actual.toUpperCase()), "expected '" + repr(expected) +
    "', got '" + repr(actual) + "'", testName);
}

function equals(a,b) {
  if (a === b) return true;
  try {
    return repr(a) === repr(b);
  } catch (e) {
    return false;
  }
}

function repr(val) {
  if (val === undefined) {
    return null;
  } else if (val === null) {
    return "null";
  } else {
    return JSON.stringify(val);
  }
}

function makeDocs(start, end, templateDoc) {
  var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}";
  if (end === undefined) {
    end = start;
    start = 0;
  }
  var docs = [];
  for (var i = start; i < end; i++) {
    var newDoc = eval("(" + templateDocSrc + ")");
    newDoc._id = (i).toString();
    newDoc.integer = i;
    newDoc.string = (i).toString();
    docs.push(newDoc);
  }
  return docs;
}

function run_on_modified_server(settings, fun) {
  try {
    // set the settings
    for(var i=0; i < settings.length; i++) {
      var s = settings[i];
      var xhr = CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, {
        body: JSON.stringify(s.value),
        headers: {"X-Couch-Persist": "false"}
      });
      CouchDB.maybeThrowError(xhr);
      s.oldValue = xhr.responseText;
    }
    // run the thing
    fun();
  } finally {
    // unset the settings
    for(var j=0; j < i; j++) {
      var s = settings[j];
      if(s.oldValue == "\"\"\n") { // unset value
        CouchDB.request("DELETE", "/_config/" + s.section + "/" + s.key, {
          headers: {"X-Couch-Persist": "false"}
        });
      } else {
        CouchDB.request("PUT", "/_config/" + s.section + "/" + s.key, {
          body: s.oldValue,
          headers: {"X-Couch-Persist": "false"}
        });
      }
    }
  }
}

function stringFun(fun) {
  var string = fun.toSource ? fun.toSource() : "(" + fun.toString() + ")";
  return string;
}

function waitForSuccess(fun, tag) {
  var start = new Date();
  while(true) {
    if (new Date() - start > 5000) {
      throw("timeout: "+tag);
    } else {
      try {
        fun();
        break;
      } catch (e) {}
      // sync http req allow async req to happen
      try {
        CouchDB.request("GET", "/test_suite_db/?tag="+encodeURIComponent(tag));
      } catch (e) {}
    }
  }
}

function waitForRestart() {
  var waiting = true;
  // Wait for the server to go down but don't
  // wait too long because we might miss the
  // the unavailable period.
  var count = 25;
  while (waiting && count > 0) {
    count--;
    try {
      CouchDB.request("GET", "/");
    } catch(e) {
      waiting = false;
    }
  }
  // Wait for it to come back up
  waiting = true;
  while (waiting) {
    try {
      CouchDB.request("GET", "/");
      waiting = false;
    } catch(e) {
      // the request will fail until restart completes
    }
  }
};

function restartServer() {
  var xhr;
  try {
    CouchDB.request("POST", "/_restart");
  } catch(e) {
    // this request may sometimes fail
  }
  waitForRestart();
}

// legacy functions for CouchDB < 1.2.0
// we keep them to make sure we keep BC
CouchDB.user_prefix = "org.couchdb.user:";

CouchDB.prepareUserDoc = function(user_doc, new_password) {
  user_doc._id = user_doc._id || CouchDB.user_prefix + user_doc.name;
  if (new_password) {
    // handle the password crypto
    user_doc.salt = CouchDB.newUuids(1)[0];
    user_doc.password_sha = hex_sha1(new_password + user_doc.salt);
  }
  user_doc.type = "user";
  if (!user_doc.roles) {
    user_doc.roles = [];
  }
  return user_doc;
};
<|endoftext|>


<commit_before>// --------------------
// Overlook
// Forms
// Validators
// --------------------

// exports

var validators = module.exports = {
	minLen: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if (val.length >= options.param) return;

		return '%label must be at least ' + options.param + ' letters';
	},
	maxLen: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if (val.length < options.param) return;

		return '%label cannot be longer than ' + options.param + ' letters';
	},

	int: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if ((val + '').match(/^-?[0-9]+$/)) return;

		return '%label must be a whole number';
	},
	intPos: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if ((val + '').match(/^[0-9]+$/)) return;

		return '%label must be a positive whole number';
	},

	decimal: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if (val.search(/^\-?([0-9]*\.)?[0-9]+$/) == -1) return '%label must be a decimal number';

		var pos = val.indexOf('.');
		if (pos == -1) return;

		if (options.scale && val.length - pos - 1 > options.scale) return '%label must have no more than ' + options.scale + ' decimal places';
	},

	date: function(val, options, fieldName, field, formData) { // jshint ignore:line
		// replace dots with slashes
		val = val.replace(/\./g, '/');

		var success = (function() {
			// check all characters valid
			if (val.search(/[^0-9\/]/) != -1) return false;

			// split into parts and check correct form
			var parts = val.split('/');
			if (parts.length < 2 || parts.length > 3) return false;

			// deal with year
			if (parts.length == 2) {
				parts[2] = new Date().getFullYear();
			} else if (parts[2].length == 2) {
				parts[2] = '20' + parts[2];
			} else if (parts[2].length != 4) {
				return false;
			}
			var year = parts[2] * 1;

			// check month
			var month = parts[1] * 1;
			if (month < 1 || month > 12) return false;

			// check day
			var day = parts[0] * 1;
			var monthDays = new Date(year, month, 0).getDate();
			if (day < 1 || day > monthDays) return false;

			// all ok
			return true;
		})();

		if (!success) return '%label must be a valid date (dd/mm/yyyy)';
	},
	time: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},
	dateTime: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},

	email: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},
	url: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},

	menuOpen: function(val, options, fieldName, field, formData) {
		if (field.required) {
			// check either id or name filled in
			var nameFieldName = fieldName.slice(0, -2) + 'Name';
			if (val == '' && formData[nameFieldName] == '') return field.label + ' is required';
		}

		// check id is valid
		if (val != '') return validators.intPos(val, options, fieldName, field, formData);
	}
};
<commit_msg>Fix: form field length validation<commit_after>// --------------------
// Overlook
// Forms
// Validators
// --------------------

// exports

var validators = module.exports = {
	minLen: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if (val.length >= options.param) return;

		return '%label must be at least ' + options.param + ' letters';
	},
	maxLen: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if (val.length <= options.param) return;

		return '%label cannot be longer than ' + options.param + ' letters';
	},

	int: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if ((val + '').match(/^-?[0-9]+$/)) return;

		return '%label must be a whole number';
	},
	intPos: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if ((val + '').match(/^[0-9]+$/)) return;

		return '%label must be a positive whole number';
	},

	decimal: function(val, options, fieldName, field, formData) { // jshint ignore:line
		if (val.search(/^\-?([0-9]*\.)?[0-9]+$/) == -1) return '%label must be a decimal number';

		var pos = val.indexOf('.');
		if (pos == -1) return;

		if (options.scale && val.length - pos - 1 > options.scale) return '%label must have no more than ' + options.scale + ' decimal places';
	},

	date: function(val, options, fieldName, field, formData) { // jshint ignore:line
		// replace dots with slashes
		val = val.replace(/\./g, '/');

		var success = (function() {
			// check all characters valid
			if (val.search(/[^0-9\/]/) != -1) return false;

			// split into parts and check correct form
			var parts = val.split('/');
			if (parts.length < 2 || parts.length > 3) return false;

			// deal with year
			if (parts.length == 2) {
				parts[2] = new Date().getFullYear();
			} else if (parts[2].length == 2) {
				parts[2] = '20' + parts[2];
			} else if (parts[2].length != 4) {
				return false;
			}
			var year = parts[2] * 1;

			// check month
			var month = parts[1] * 1;
			if (month < 1 || month > 12) return false;

			// check day
			var day = parts[0] * 1;
			var monthDays = new Date(year, month, 0).getDate();
			if (day < 1 || day > monthDays) return false;

			// all ok
			return true;
		})();

		if (!success) return '%label must be a valid date (dd/mm/yyyy)';
	},
	time: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},
	dateTime: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},

	email: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},
	url: function(val, options, fieldName, field, formData) { // jshint ignore:line
		//xxx write this
	},

	menuOpen: function(val, options, fieldName, field, formData) {
		if (field.required) {
			// check either id or name filled in
			var nameFieldName = fieldName.slice(0, -2) + 'Name';
			if (val == '' && formData[nameFieldName] == '') return field.label + ' is required';
		}

		// check id is valid
		if (val != '') return validators.intPos(val, options, fieldName, field, formData);
	}
};
<|endoftext|>


<commit_before>$(function() {

    // The link to colorScheme.js is hidden by default
    // to not confuse users who don't have JavaScript
    // enabled. Make it visible now
    $('li a[href="colorScheme.html"]').css("display", "inline");

    // UI elements in colorScheme.html
    // these vars will not select anything if we are in a
    // different page
    var headerSelect = $("#headerSelect");
    var titleSelect = $("#titleSelect");
    var mainBodySelect = $("#mainBodySelect");
    var navSelect = $("#navSelect");
    var backgroundSelect = $("#backgroundSelect");

    // read the options for the selects into an array to use for
    // the random color button
    var selectOptions = [];
    $('#headerSelect > option').each(function(i, selected) {
        selectOptions[i] = $(selected).val();
    });

    function ColorSet(headerHex, titleHex, mainHex, sideHex, bgHex) {
        // Constructor for an object that stores a set of the hexcodes
        // for a specified color scheme
        this.headerHex = headerHex;
        this.titleHex = titleHex;
        this.mainHex = mainHex;
        this.sideHex = sideHex;
        this.bgHex = bgHex;
    }

    function defaultColorSet() {
        return new ColorSet('#0099FF', '#FFCC00', '#FFFFFF',
                            '#808080', '#B0C4DE');
    }

    function changeColor(selector, color) {
        // changes the background-color of the elements covered
        // by selector to color
        $(selector).css("background-color", color);
    }

    function animateColor(selector, color) {
        // like changeColor, but does the change with an
        // animation, using jquery.colors.js
        // we only animate the initial change of color when the
        // user first picks the new color, all other changes of
        // the css value are done instantly with changeColor
        $(selector).animate({backgroundColor: color}, 'slow');
    }

    function save() {
        var newColors = new ColorSet(headerSelect.val(),
                                     titleSelect.val(),
                                     mainBodySelect.val(),
                                     navSelect.val(),
                                     backgroundSelect.val());
        $.cookie("color", JSON.stringify(newColors),{expires: 7});
    }

    function selectChangeEvent(selector, color)
    {
        // event that is called when one of the selects changes.
        // The elements specified by selector animate a color
        // change to color, and the cookie is updated
        // accordingly
        animateColor(selector, color);
        save();
    }

    headerSelect.change(function() {
        selectChangeEvent("header", $(this).val());
    });

    titleSelect.change(function() {
        selectChangeEvent("h1", $(this).val());
    });

    mainBodySelect.change(function() {
        selectChangeEvent("#mainText", $(this).val());
    });

    navSelect.change(function() {
        // In case the user highlighted nav li's via
        // highlightNav.js, change them too
        selectChangeEvent("nav, nav li", $(this).val());
    });

    backgroundSelect.change(function() {
        selectChangeEvent("body", $(this).val());
    });

    function set(colSet, shouldAnimate) {
        // sets the selects' names and the background colors as
        // specified by colSet, animates if shouldAnimate is
        // true, else just changes the colors
        colSet = $.extend(defaultColorSet(), colSet);//remove null values just in case

        headerSelect.val(colSet.headerHex);
        titleSelect.val(colSet.titleHex);
        mainBodySelect.val(colSet.mainHex);
        navSelect.val(colSet.sideHex);
        backgroundSelect.val(colSet.bgHex);

	var colorFun = shouldAnimate ? animateColor : changeColor;

        colorFun("header", colSet.headerHex);
        colorFun("h1", colSet.titleHex);
        colorFun("#mainText", colSet.mainHex);
        colorFun("nav, nav li", colSet.sideHex);
        colorFun("body", colSet.bgHex);
        return true;
    }

    function resetCookie() {
        $.cookie("color", null);
    }

    $("#resetButton").click(function() {
        set(defaultColorSet(), true);
        resetCookie();
    });

    function randomNum(maxVal) {
        return Math.floor(Math.random() * maxVal);
    }

    $("#listRandom").click(function() {
        var randomColors = [];
        var numOptions = selectOptions.length;
        for (var i = 0; i < numOptions; i++) {
            randomColors[i] = selectOptions[randomNum(numOptions)];
        }
        var randCols = new ColorSet(randomColors[0], randomColors[1],
                                    randomColors[2], randomColors[3],
                                    randomColors[4]);
        set(randCols, true);
        save();
    });

    $("#expRandom").click(function() {

        function randRGBVal() {
            // FF in hex:
            var maxVal = 255;
            var colHex = randomNum(maxVal).toString(16);
            // pad it with leading 0's if needed
            while (colHex.length < 2) {
                colHex = "0" + colHex;
            }
            return colHex;
        }

        function getColorHex() {
            var red = randRGBVal();
            var green = randRGBVal();
            var blue = randRGBVal();
            return "#" + red + green + blue;
        }
	var newCols = [];
	$("select").each(function(index) {
	    var col = getColorHex().toUpperCase();
	    var self = $(this);
	    self.append('<option value=' + col +
			   '>' + col + '</option>');
	    self.val(col);
	    newCols[index] = col;
	});
	set(new ColorSet(newCols[0], newCols[1],
			 newCols[2], newCols[3],
			 newCols[4]), true);
        save();
    });

    function setColorsFromCookie() {
        // reads color and sets the colors accordingly
        // does NOT animate the color change, as the page should
        // load with the changed colors, and the user should not
        // see the original ones (they have already seen an
        // animation)
        set(JSON.parse($.cookie("color")), false);
    }

    setColorsFromCookie();

});
<commit_msg>Refactored the code to add new options to the select in exp random cols<commit_after>$(function() {

    // The link to colorScheme.js is hidden by default
    // to not confuse users who don't have JavaScript
    // enabled. Make it visible now
    $('li a[href="colorScheme.html"]').css("display", "inline");

    // UI elements in colorScheme.html
    // these vars will not select anything if we are in a
    // different page
    var headerSelect = $("#headerSelect");
    var titleSelect = $("#titleSelect");
    var mainBodySelect = $("#mainBodySelect");
    var navSelect = $("#navSelect");
    var backgroundSelect = $("#backgroundSelect");

    // read the options for the selects into an array to use for
    // the random color button
    var selectOptions = [];
    $('#headerSelect > option').each(function(i, selected) {
        selectOptions[i] = $(selected).val();
    });

    function ColorSet(headerHex, titleHex, mainHex, sideHex, bgHex) {
        // Constructor for an object that stores a set of the hexcodes
        // for a specified color scheme
        this.headerHex = headerHex;
        this.titleHex = titleHex;
        this.mainHex = mainHex;
        this.sideHex = sideHex;
        this.bgHex = bgHex;
    }

    function defaultColorSet() {
        return new ColorSet('#0099FF', '#FFCC00', '#FFFFFF',
                            '#808080', '#B0C4DE');
    }

    function changeColor(selector, color) {
        // changes the background-color of the elements covered
        // by selector to color
        $(selector).css("background-color", color);
    }

    function animateColor(selector, color) {
        // like changeColor, but does the change with an
        // animation, using jquery.colors.js
        // we only animate the initial change of color when the
        // user first picks the new color, all other changes of
        // the css value are done instantly with changeColor
        $(selector).animate({backgroundColor: color}, 'slow');
    }

    function save() {
        var newColors = new ColorSet(headerSelect.val(),
                                     titleSelect.val(),
                                     mainBodySelect.val(),
                                     navSelect.val(),
                                     backgroundSelect.val());
        $.cookie("color", JSON.stringify(newColors),{expires: 7});
    }

    function selectChangeEvent(selector, color)
    {
        // event that is called when one of the selects changes.
        // The elements specified by selector animate a color
        // change to color, and the cookie is updated
        // accordingly
        animateColor(selector, color);
        save();
    }

    headerSelect.change(function() {
        selectChangeEvent("header", $(this).val());
    });

    titleSelect.change(function() {
        selectChangeEvent("h1", $(this).val());
    });

    mainBodySelect.change(function() {
        selectChangeEvent("#mainText", $(this).val());
    });

    navSelect.change(function() {
        // In case the user highlighted nav li's via
        // highlightNav.js, change them too
        selectChangeEvent("nav, nav li", $(this).val());
    });

    backgroundSelect.change(function() {
        selectChangeEvent("body", $(this).val());
    });

    function set(colSet, shouldAnimate) {
        // sets the selects' names and the background colors as
        // specified by colSet, animates if shouldAnimate is
        // true, else just changes the colors
        colSet = $.extend(defaultColorSet(), colSet);//remove null values just in case
        headerSelect.val(colSet.headerHex);
        titleSelect.val(colSet.titleHex);
        mainBodySelect.val(colSet.mainHex);
        navSelect.val(colSet.sideHex);
        backgroundSelect.val(colSet.bgHex);

        var colorFun = shouldAnimate ? animateColor : changeColor;

        colorFun("header", colSet.headerHex);
        colorFun("h1", colSet.titleHex);
        colorFun("#mainText", colSet.mainHex);
        colorFun("nav, nav li", colSet.sideHex);
        colorFun("body", colSet.bgHex);
        return true;
    }

    function resetCookie() {
        $.cookie("color", null);
    }

    $("#resetButton").click(function() {
        set(defaultColorSet(), true);
        resetCookie();
    });

    function randomNum(maxVal) {
        return Math.floor(Math.random() * maxVal);
    }

    $("#listRandom").click(function() {
        var randomColors = [];
        var numOptions = selectOptions.length;
        for (var i = 0; i < numOptions; i++) {
            randomColors[i] = selectOptions[randomNum(numOptions)];
        }
        var randCols = new ColorSet(randomColors[0], randomColors[1],
                                    randomColors[2], randomColors[3],
                                    randomColors[4]);
        set(randCols, true);
        save();
    });

    $("#expRandom").click(function() {

        function randRGBVal() {
            // FF in hex:
            var maxVal = 255;
            var colHex = randomNum(maxVal).toString(16);
            // pad it with leading 0's if needed
            while (colHex.length < 2) {
                colHex = "0" + colHex;
            }
            return colHex;
        }

        function getColorHex() {
            var red = randRGBVal();
            var green = randRGBVal();
            var blue = randRGBVal();
            return "#" + red + green + blue;
        }
        var newCols = [];
        $("select").each(function(index) {
            var col = getColorHex().toUpperCase();
            var self = $(this);
	    var newColOption = $("<option>", {
	                         value : col,
	                         html : col});
	    self.append(newColOption);
	    self.val(col);
	    newCols[index] = col;
	});
	set(new ColorSet(newCols[0], newCols[1],
			 newCols[2], newCols[3],
			 newCols[4]), true);
        save();
    });

    function setColorsFromCookie() {
        // reads color and sets the colors accordingly
        // does NOT animate the color change, as the page should
        // load with the changed colors, and the user should not
        // see the original ones (they have already seen an
        // animation)
        set(JSON.parse($.cookie("color")), false);
    }

    setColorsFromCookie();

});
<|endoftext|>


<commit_before>// @flow
import {
  ACTIONS,
  Lbry,
  selectMyClaimsWithoutChannels,
  doNotify,
  MODALS,
  selectMyChannelClaims,
} from 'lbry-redux';
import { selectPendingPublishes } from 'redux/selectors/publish';
import type {
  UpdatePublishFormData,
  UpdatePublishFormAction,
  PublishParams,
} from 'redux/reducers/publish';

type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH };
type PromiseAction = Promise<Action>;
type Dispatch = (action: Action | PromiseAction | Array<Action>) => any;
type GetState = () => {};

export const doClearPublish = () => (dispatch: Dispatch): Action =>
  dispatch({ type: ACTIONS.CLEAR_PUBLISH });

export const doUpdatePublishForm = (publishFormValue: UpdatePublishFormData) => (
  dispatch: Dispatch
): UpdatePublishFormAction =>
  dispatch({
    type: ACTIONS.UPDATE_PUBLISH_FORM,
    data: { ...publishFormValue },
  });

export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) => {
  const {
    name,
    amount,
    channel_name: channelName,
    value: {
      stream: { metadata },
    },
  } = claim;
  const {
    author,
    description,
    // use same values as default state
    // fee will be undefined for free content
    fee = {
      amount: 0,
      currency: 'LBC',
    },
    language,
    license,
    licenseUrl,
    nsfw,
    thumbnail,
    title,
  } = metadata;

  const publishData = {
    name,
    channel: channelName,
    bid: amount,
    price: { amount: fee.amount, currency: fee.currency },
    contentIsFree: !fee.amount,
    author,
    description,
    fee,
    language,
    license,
    licenseUrl,
    nsfw,
    thumbnail,
    title,
    uri,
  };

  dispatch({ type: ACTIONS.DO_PREPARE_EDIT, data: publishData });
};

export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getState: () => {}) => {
  const state = getState();
  const myClaims = selectMyClaimsWithoutChannels(state);
  const myChannels = selectMyChannelClaims(state);

  const {
    name,
    bid,
    filePath,
    description,
    language,
    license,
    licenseUrl,
    thumbnail,
    nsfw,
    channel,
    title,
    contentIsFree,
    price,
    uri,
    sources,
  } = params;

  // get the claim id from the channel name, we will use that instead
  const namedChannelClaim = myChannels.find(myChannel => myChannel.name === channel);
  const channelId = namedChannelClaim ? namedChannelClaim.claim_id : '';

  let isEdit;
  const newPublishName = channel ? `${channel}/${name}` : name;
  for (let i = 0; i < myClaims.length; i += 1) {
    const { channel_name: claimChannelName, name: claimName } = myClaims[i];
    const contentName = claimChannelName ? `${claimChannelName}/${claimName}` : claimName;
    if (contentName === newPublishName) {
      isEdit = true;
      break;
    }
  }

  const fee = contentIsFree || !price.amount ? undefined : { ...price };

  const metadata = {
    title,
    nsfw,
    license,
    licenseUrl,
    language,
    thumbnail,
  };

  if (fee) {
    metadata.fee = fee;
  }

  if (description) {
    metadata.description = description;
  }

  const publishPayload = {
    name,
    channel_id: channelId,
    bid,
    metadata,
  };

  if (filePath) {
    publishPayload.file_path = filePath;
  } else {
    publishPayload.sources = sources;
  }

  dispatch({ type: ACTIONS.PUBLISH_START });

  const success = () => {
    dispatch({
      type: ACTIONS.PUBLISH_SUCCESS,
      data: { pendingPublish: { ...publishPayload, isEdit } },
    });
    dispatch(doNotify({ id: MODALS.PUBLISH }, { uri }));
  };

  const failure = error => {
    dispatch({ type: ACTIONS.PUBLISH_FAIL });
    dispatch(doNotify({ id: MODALS.ERROR, error: error.message }));
  };

  return Lbry.publish(publishPayload).then(success, failure);
};

// Calls claim_list_mine until any pending publishes are confirmed
export const doCheckPendingPublishes = () => (dispatch: Dispatch, getState: GetState) => {
  const state = getState();
  const pendingPublishes = selectPendingPublishes(state);
  const myClaims = selectMyClaimsWithoutChannels(state);

  let publishCheckInterval;

  const checkFileList = () => {
    Lbry.claim_list_mine().then(claims => {
      const claimsWithoutChannels = claims.filter(claim => !claim.name.match(/^@/));
      if (myClaims.length !== claimsWithoutChannels.length) {
        const pendingPublishMap = {};
        pendingPublishes.forEach(({ name }) => {
          pendingPublishMap[name] = name;
        });

        claims.forEach(claim => {
          if (pendingPublishMap[claim.name]) {
            dispatch({
              type: ACTIONS.REMOVE_PENDING_PUBLISH,
              data: {
                name: claim.name,
              },
            });
            dispatch({
              type: ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED,
              data: {
                claims,
              },
            });

            delete pendingPublishMap[claim.name];
          }
        });

        clearInterval(publishCheckInterval);
      }
    });
  };

  if (pendingPublishes.length) {
    checkFileList();
    publishCheckInterval = setInterval(() => {
      checkFileList();
    }, 10000);
  }
};
<commit_msg>add doResetThumbnailStatus and doUploadThumbnail actions<commit_after>// @flow
import {
  ACTIONS,
  Lbry,
  selectMyClaimsWithoutChannels,
  doNotify,
  MODALS,
  selectMyChannelClaims,
  STATUSES
} from 'lbry-redux';
import { selectPendingPublishes } from 'redux/selectors/publish';
import type {
  UpdatePublishFormData,
  UpdatePublishFormAction,
  PublishParams,
} from 'redux/reducers/publish';
import fs from 'fs';
import path from 'path';

type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH };
type PromiseAction = Promise<Action>;
type Dispatch = (action: Action | PromiseAction | Array<Action>) => any;
type GetState = () => {};

export const doClearPublish = () => (dispatch: Dispatch): Action =>
  dispatch({ type: ACTIONS.CLEAR_PUBLISH });

export const doUpdatePublishForm = (publishFormValue: UpdatePublishFormData) => (
  dispatch: Dispatch
): UpdatePublishFormAction =>
  dispatch({
    type: ACTIONS.UPDATE_PUBLISH_FORM,
    data: { ...publishFormValue },
  });

export const doResetThumbnailStatus = () => (dispatch: Dispatch): Action =>
  fetch('https://spee.ch/api/channel/availability/@testing')
    .then(() =>
      dispatch({
        type: ACTIONS.UPDATE_PUBLISH_FORM,
        data: { thumbnailStatus: STATUSES.READY },
      })
    )
    .catch(() =>
      dispatch({
        type: ACTIONS.UPDATE_PUBLISH_FORM,
        data: { thumbnailStatus: STATUSES.DOWN },
      })
    );

export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch: Dispatch) => {
  const thumbnail = fs.readFileSync(filePath);
  const fileExt = path.extname(filePath);

  const makeid = () => {
    let text = '';
    const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    for (let i = 0; i < 24; i += 1) text += possible.charAt(Math.floor(Math.random() * 62));
    return text;
  };

  const uploadError = (error: string = '') =>
    dispatch(
      batchActions(
        {
          type: ACTIONS.UPDATE_PUBLISH_FORM,
          data: { thumbnailStatus: STATUSES.DOWN },
        },
        doAlertError(error)
      )
    );

  dispatch({
    type: ACTIONS.UPDATE_PUBLISH_FORM,
    data: { thumbnailStatus: STATUSES.IN_PROGRESS },
  });

  const data = new FormData();
  const name = makeid();
  const blob = new Blob([thumbnail], { type: `image/${fileExt.slice(1)}` });
  data.append('name', name);
  data.append('file', blob);
  data.append('nsfw', nsfw.toString());
  return fetch('https://spee.ch/api/claim/publish', {
    method: 'POST',
    body: data,
  })
    .then(response => response.json())
    .then(
      json =>
        json.success
          ? dispatch({
              type: ACTIONS.UPDATE_PUBLISH_FORM,
              data: {
                thumbnailStatus: STATUSES.COMPLETE,
                thumbnailUrl: `${json.data.url}${fileExt}`,
              },
            })
          : uploadError()
    )
    .catch(() => uploadError());
};

export const doPrepareEdit = (claim: any) => (dispatch: Dispatch) => {
  const { name, amount, channel_name: channelName, value: { stream: { metadata } } } = claim;

  const {
    author,
    description,
    // use same values as default state
    // fee will be undefined for free content
    fee = {
      amount: 0,
      currency: 'LBC',
    },
    language,
    license,
    licenseUrl,
    nsfw,
    thumbnail,
    title,
  } = metadata;

  const publishData = {
    name,
    channel: channelName,
    bid: amount,
    price: { amount: fee.amount, currency: fee.currency },
    contentIsFree: !fee.amount,
    author,
    description,
    fee,
    language,
    license,
    licenseUrl,
    nsfw,
    thumbnail,
    title,
    uri,
  };

  dispatch({ type: ACTIONS.DO_PREPARE_EDIT, data: publishData });
};

export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getState: () => {}) => {
  const state = getState();
  const myClaims = selectMyClaimsWithoutChannels(state);
  const myChannels = selectMyChannelClaims(state);

  const {
    name,
    bid,
    filePath,
    description,
    language,
    license,
    licenseUrl,
    thumbnail,
    nsfw,
    channel,
    title,
    contentIsFree,
    price,
    uri,
    sources,
  } = params;

  // get the claim id from the channel name, we will use that instead
  const namedChannelClaim = myChannels.find(myChannel => myChannel.name === channel);
  const channelId = namedChannelClaim ? namedChannelClaim.claim_id : '';

  let isEdit;
  const newPublishName = channel ? `${channel}/${name}` : name;
  for (let i = 0; i < myClaims.length; i += 1) {
    const { channel_name: claimChannelName, name: claimName } = myClaims[i];
    const contentName = claimChannelName ? `${claimChannelName}/${claimName}` : claimName;
    if (contentName === newPublishName) {
      isEdit = true;
      break;
    }
  }

  const fee = contentIsFree || !price.amount ? undefined : { ...price };

  const metadata = {
    title,
    nsfw,
    license,
    licenseUrl,
    language,
    thumbnail,
  };

  if (fee) {
    metadata.fee = fee;
  }

  if (description) {
    metadata.description = description;
  }

  const publishPayload = {
    name,
    channel_id: channelId,
    bid,
    metadata,
  };

  if (filePath) {
    publishPayload.file_path = filePath;
  } else {
    publishPayload.sources = sources;
  }

  dispatch({ type: ACTIONS.PUBLISH_START });

  const success = () => {
    dispatch({
      type: ACTIONS.PUBLISH_SUCCESS,
      data: { pendingPublish: { ...publishPayload, isEdit } },
    });
    dispatch(doNotify({ id: MODALS.PUBLISH }, { uri }));
  };

  const failure = error => {
    dispatch({ type: ACTIONS.PUBLISH_FAIL });
    dispatch(doNotify({ id: MODALS.ERROR, error: error.message }));
  };

  return Lbry.publish(publishPayload).then(success, failure);
};

// Calls claim_list_mine until any pending publishes are confirmed
export const doCheckPendingPublishes = () => (dispatch: Dispatch, getState: GetState) => {
  const state = getState();
  const pendingPublishes = selectPendingPublishes(state);
  const myClaims = selectMyClaimsWithoutChannels(state);

  let publishCheckInterval;

  const checkFileList = () => {
    Lbry.claim_list_mine().then(claims => {
      const claimsWithoutChannels = claims.filter(claim => !claim.name.match(/^@/));
      if (myClaims.length !== claimsWithoutChannels.length) {
        const pendingPublishMap = {};
        pendingPublishes.forEach(({ name }) => {
          pendingPublishMap[name] = name;
        });

        claims.forEach(claim => {
          if (pendingPublishMap[claim.name]) {
            dispatch({
              type: ACTIONS.REMOVE_PENDING_PUBLISH,
              data: {
                name: claim.name,
              },
            });
            dispatch({
              type: ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED,
              data: {
                claims,
              },
            });

            delete pendingPublishMap[claim.name];
          }
        });

        clearInterval(publishCheckInterval);
      }
    });
  };

  if (pendingPublishes.length) {
    checkFileList();
    publishCheckInterval = setInterval(() => {
      checkFileList();
    }, 10000);
  }
};
<|endoftext|>


<commit_before>'use strict';

/* global spawn, catchify,
    pick, omit, keys, defaults, groupBy, mapValues,
    mongoose, Schema */

let Sched;
let Room;
let Prof;

let schema = new Schema({
  name: String,
  room: {type: String, ref: 'Room'},
  day: Number,
  time: Number,
  week: Number,
  group: {type: String, ref: 'Group'},
  subgroup: Number,
  subject: {type: String, ref: 'Subject'},
  subjectType: {type: String, ref: 'SubjectType'},
  prof: {type: String, ref: 'Prof'}
}, {
  collection: 'sched',
});

let byRoom = {name: 1, room: 1, day: 1, time: 1, week: 1};
schema.index(byRoom, {unique: true});

let byProf = {name: 1, prof: 1, day: 1, time: 1, week: 1};
schema.index(byProf, {unique: true});

let S = schema.statics;

// {
//   name: String, //required
//   room: String, // \_ either
//   prof: String, // /
//   day: Number,
//   time: Number,
//   week: Number,
// }
S.get = (arg) => spawn(function*() {
  if (!arg.room && !arg.prof) throw ['badArgument'];

  let queryProj = {name: 1, room: 1, prof: 1, day: 1, time: 1, week: 1};
  let query = arg [pick](queryProj [keys]()) [pick](v => v != null);

  let resolved = yield Promise.all([
    !query.room ? {} : Room.resolve({items: [{name: query.room}]}),
    !query.prof ? {} : Prof.resolve({items: [{name: query.prof}]})
  ]);

  let resolvedMap = resolved.map(item => item [map]());

  if (query.room) {
    if (!resolvedMap[0].length) return null;
    query.room = resolvedMap[0][0]._id;
  }

  if (query.prof) {
    if (!resolvedMap[1].length) return null;
    query.prof = resolvedMap[1][0]._id;
  }

  let proj = ({group: 1, subgroup: 1, subject: 1, subjectType: 1}) [defaults](queryProj);
  let sort = arg.prof ? byProf : byRoom;

  let cmd = Sched.find(query, proj).sort(sort);

  cmd.populate('group', {name: 1});
  cmd.populate('subject', {name: 1});
  cmd.populate('subjectType', {name: 1})

  if (!arg.room) cmd.populate('room', {name: 1});
  if (!arg.prof) cmd.populate('prof', {name: 1});

  let found = yield cmd.exec() [catchify]();

  let result = (
    found [groupBy](item => item.day) [mapValues](itemsByDay => (
      itemsByDay [groupBy](item => item.time) [mapValues](itemsByTime => (
        itemsByTime [groupBy](item => item.week) [mapValues](itemsByWeek => (
          itemsByWeek [map](item => ({
            room: arg.room || (item.room && item.room.name),
            group: item.group && item.group.name,
            subgroup: item.subgroup,
            subject: item.subject && item.subject.name,
            subjectType: item.subjectType && item.subjectType.name,
            prof: arg.prof && (item.prof && item.prof.name)
          }))
        ))
      ))
    ))
  );

  return result;
});

// {
//   scheds: [{
//     name: String,
//     room: String,
//     day: Number,
//     time: Number,
//     week: Number,
//     group: String,
//     subgroup: Number | null,
//     subject: String,
//     subjectType: String,
//     prof: String | null,
//   }]
// }
S.set = (arg) => spawn(function*() {
  /*if (arg.scheds.length) {
    let removes = arg.scheds.map(sched => sched [pick](byRoom [keys]()));
    yield Sched.remove({$or: removes}) [catchify]();
  }*/

  yield Sched.create(arg.scheds [filter](sched => sched.subject)) [catchify]();
});

S.clear = (arg) => spawn(function*() {
  yield Sched.remove({name: arg.name}) [catchify]();
});

// {
//   scheds: [{
//     name: String,
//     room: String,
//     day: Number,
//     time: Number,
//     week: Number
//   }],
//   prof: Prof,
// }
S.setProf = (arg) => spawn(function*() {
  if (arg.scheds.length) {
    yield Sched.update(
      {$or: arg.scheds [map](sched => sched [pick](byRoom [keys]()))},
      {$set: {prof: arg.prof}},
      {multi: true}
    ) [catchify]();
  }
});

Sched = module.exports = mongoose.model('Sched', schema);
Room = require('model/room');
Prof = require('model/prof');
<commit_msg>get sched by prof from db; bugfix<commit_after>'use strict';

/* global spawn, catchify,
    pick, omit, keys, defaults, groupBy, mapValues,
    mongoose, Schema */

let Sched;
let Room;
let Prof;

let schema = new Schema({
  name: String,
  room: {type: String, ref: 'Room'},
  day: Number,
  time: Number,
  week: Number,
  group: {type: String, ref: 'Group'},
  subgroup: Number,
  subject: {type: String, ref: 'Subject'},
  subjectType: {type: String, ref: 'SubjectType'},
  prof: {type: String, ref: 'Prof'}
}, {
  collection: 'sched',
});

let byRoom = {name: 1, room: 1, day: 1, time: 1, week: 1};
schema.index(byRoom, {unique: true});

let byProf = {name: 1, prof: 1, day: 1, time: 1, week: 1};
schema.index(byProf, {unique: true});

let S = schema.statics;

// {
//   name: String, //required
//   room: String, // \_ either
//   prof: String, // /
//   day: Number,
//   time: Number,
//   week: Number,
// }
S.get = (arg) => spawn(function*() {
  if (!arg.room && !arg.prof) throw ['badArgument'];

  let queryProj = {name: 1, room: 1, prof: 1, day: 1, time: 1, week: 1};
  let query = arg [pick](queryProj [keys]()) [pick](v => v != null);

  let resolved = yield Promise.all([
    !query.room ? {} : Room.resolve({items: [{name: query.room}]}),
    !query.prof ? {} : Prof.resolve({items: [{name: query.prof}]})
  ]);

  let resolvedMap = resolved.map(item => item [map]());

  if (query.room) {
    if (!resolvedMap[0].length) return null;
    query.room = resolvedMap[0][0]._id;
  }

  if (query.prof) {
    if (!resolvedMap[1].length) return null;
    query.prof = resolvedMap[1][0]._id;
  }

  let proj = ({group: 1, subgroup: 1, subject: 1, subjectType: 1}) [defaults](queryProj);
  let sort = arg.prof ? byProf : byRoom;

  let cmd = Sched.find(query, proj).sort(sort);

  cmd.populate('group', {name: 1});
  cmd.populate('subject', {name: 1});
  cmd.populate('subjectType', {name: 1})

  if (!arg.room) cmd.populate('room', {name: 1});
  if (!arg.prof) cmd.populate('prof', {name: 1});

  let found = yield cmd.exec() [catchify]();

  let result = (
    found [groupBy](item => item.day) [mapValues](itemsByDay => (
      itemsByDay [groupBy](item => item.time) [mapValues](itemsByTime => (
        itemsByTime [groupBy](item => item.week) [mapValues](itemsByWeek => (
          itemsByWeek [map](item => ({
            room: arg.room || (item.room && item.room.name),
            group: item.group && item.group.name,
            subgroup: item.subgroup,
            subject: item.subject && item.subject.name,
            subjectType: item.subjectType && item.subjectType.name,
            prof: arg.prof || (item.prof && item.prof.name)
          }))
        ))
      ))
    ))
  );

  return result;
});

// {
//   scheds: [{
//     name: String,
//     room: String,
//     day: Number,
//     time: Number,
//     week: Number,
//     group: String,
//     subgroup: Number | null,
//     subject: String,
//     subjectType: String,
//     prof: String | null,
//   }]
// }
S.set = (arg) => spawn(function*() {
  /*if (arg.scheds.length) {
    let removes = arg.scheds.map(sched => sched [pick](byRoom [keys]()));
    yield Sched.remove({$or: removes}) [catchify]();
  }*/

  yield Sched.create(arg.scheds [filter](sched => sched.subject)) [catchify]();
});

S.clear = (arg) => spawn(function*() {
  yield Sched.remove({name: arg.name}) [catchify]();
});

// {
//   scheds: [{
//     name: String,
//     room: String,
//     day: Number,
//     time: Number,
//     week: Number
//   }],
//   prof: Prof,
// }
S.setProf = (arg) => spawn(function*() {
  if (arg.scheds.length) {
    yield Sched.update(
      {$or: arg.scheds [map](sched => sched [pick](byRoom [keys]()))},
      {$set: {prof: arg.prof}},
      {multi: true}
    ) [catchify]();
  }
});

Sched = module.exports = mongoose.model('Sched', schema);
Room = require('model/room');
Prof = require('model/prof');
<|endoftext|>


<commit_before>/*

Tiny, bespoke DOM manipulation lib since
loading something like jQuery seems excessive.

(if I had a dollar for every time some bespoke DOM
lib got outta hand...)

If this spirals out of control it's almost certainly
worth swapping to a more developed solution but we shouldn't
be doing much DOM stuff anyways.

API:

  ===================================================================
  EventBase (mixin, adds event helpers to a target)
  ===================================================================

  .on([String] event, [Function] fn)
    - Adds an event listener, returns self

  .off([String] event, [Function] fn)
    - Removes an event listener, returns self

  .onOff([String] event, [Function] fn)
    - Adds an event listener, returns a function that
      removes that listener

  .one([String] event, [Function] fn)
    - Adds an event listener that is removed after being
      fired once.

  ===================================================================
  NodeBase (mixin, adds helpers for retrieving nodes)
  ===================================================================

  .get ([String] selector)
    - runs node.querySelector
    - returns instance of El

  .getAll ([String] selector)
    - runs node.querySelectorAll
    - returns array of Els

  .exists ([String] selector, [Boolean?] forceBool = false)
    - runs node.get
    - if node.get.node is undefined, returns false
    - otherwise if forceBool is false, returns instance of El
    - if forceBool is true, returns true

  ===================================================================
  Doc (public, mixes in NodeBase)
  ===================================================================

  .constructor ([Node?] document = window.document)
    - creates instance of NodeBase with document

  ===================================================================
  DOM (public/default export, mixes in EventBase, extends Doc)
  ===================================================================

  .create ([String] tag, [PlainObject?] attributes)
    - runs document.createElement
    - adds attributes, given in the form of { attribute: value, ... }
    - returns instance of El

  .body
    - instance of El with document.body as the node

  .window
    - instance of EventBase with the window object

  ===================================================================
  El (private, mixes in NodeBase and EventBase, exposed via Doc/DOM)
  ===================================================================

  All methods return self (i.e. are chainable) unless otherwise noted.

  .node
    - native dom node

  .remove ()
    - removes node from the DOM

  .wrap ([El] wrapper)
    - wraps self.node with wrapper.node

  .clone ()
    - returns new instance of El with a clone of self.node

  .attr ([String|PlainObject] attributes, [String?] val)
    - sets attributes
    - accepts ("attribute", "value") or { attribute: "value", ...}
    - if attribute is a string and no value is specified, returns
      the value of that attribute.

  .css ([String|PlainObject] props, [String?] val)
    - sets style props
    - accepts ("property", "value") or { property: "value", ... }

  .addClass ([String] classNames)
    - adds one or more classes (space separated)

  .rmClass ([String] classNames)
    - removes one or more classes (space separated)

  .parent ()
    - returns new instance of El with self.node's parentNode

  .append ([El] el)
    - appends el.node to self.node

  .appendTo ([El] el)
    - appends self.node to el.node

  .after ([El] el) {
    - inserts el.node after self.node

  .before ([El] el) {
    - inserts el.node before self.node

  .width ([String?] val)
    - if val is specified, sets style.width to val
    - otherwise returns node.offsetWidth

  .height ([String?] val)
    - if val is specified, sets style.height to val
    - otherwise returns node.offsetHeight

  .html ([String?] text)
    - if text is specified, sets node.innerHTML to text
    - otherwise returns node.innerHTML

  .text ([String?] text)
    - if text is specified, sets node.innerText to text
    - otherwise returns node.innerText
*/

class EventBase {
  constructor (target) {
    this.target = target;
    return {
      on: this.on.bind(this),
      off: this.off.bind(this),
      onOff: this.onOff.bind(this),
      one: this.one.bind(this)
    };
  }

  on (event, fn) {
    this.target.addEventListener(event, fn);
    return this;
  }

  off (event, fn) {
    this.target.removeEventListener(event, fn);
    return this;
  }

  onOff (event, fn) {
    this.on(event, fn);
    return () => { this.off(event, fn); };
  }

  one (event, fn) {
    const off = this.onOff(event, () => {
      fn();
      off();
    });
    return this;
  }
}

class NodeBase {
  constructor (node) {
    this.node = node;
    return {
      node,
      get: this.get.bind(this),
      getAll: this.getAll.bind(this),
      exists: this.exists.bind(this)
    };
  }

  get (selector) {
    return new El(this.node.querySelector(selector));
  }

  getAll (selector) {
    const nodes = this.node.querySelectorAll(selector);
    return [...nodes].map(node => new El(node));
  }

  exists (selector, forceBool = false) {
    const el = this.get(selector);
    if (!el.node) {
      return false;
    } else {
      return forceBool ? true : el;
    }
  }
}

export class Doc {
  constructor (document = window.document) {
    Object.assign(this, new NodeBase(document));
  }
}

class DOM extends Doc {
  constructor () {
    super();
    Object.assign(this, new EventBase(this.node));
    this.window = new EventBase(window);
    this.body = this.get('body');
  }

  create (tag, attributes) {
    const el = new El(this.node.createElement(tag));
    if (attributes) {
      el.attr(attributes);
    }
    return el;
  }
}

class El {
  constructor (node) {
    this.node = node;
    Object.assign(this, new NodeBase(node), new EventBase(node));
  }

  remove () {
    const { node } = this;
    node.parentNode.removeChild(node);
    return this;
  }

  wrap (wrapper) {
    const parent = this.parent();
    wrapper = wrapper.clone();
    parent.append(wrapper);
    wrapper.append(this);
    return this;
  }

  clone () {
    return new El(this.node.cloneNode());
  }

  attr (attributes, val) {
    const { node } = this;

    const handleString = () => {
      if (typeof val !== 'undefined') {
        if (val === null) {
          node.removeAttribute(attributes);
        } else {
          node.setAttribute(attributes, val);
        }
        return this;
      } else {
        return node.getAttribute(attributes);
      }
    };

    if (typeof attributes === 'string') {
      return handleString();
    } else {
      for (const attribute in attributes) {
        node.setAttribute(attribute, attributes[attribute]);
      }
    }
    return this;
  }

  css (props, val) {
    const { node } = this;
    if (typeof props === 'string') {
      node.style[props] = val;
    } else {
      for (const prop in props) {
        node.prop[prop] = props[prop];
      }
    }
    return this;
  }

  addClass (classNames) {
    classNames = classNames.split(' ');
    classNames.forEach(className => {
      this.node.classList.add(className);
    });
    return this;
  }

  rmClass (classNames) {
    classNames = classNames.split(' ');
    classNames.forEach(className => {
      this.node.classList.remove(className);
    });
    return this;
  }

  parent () {
    return new El(this.node.parentNode);
  }

  append (el) {
    this.node.appendChild(el.node);
    return this;
  }

  appendTo (el) {
    el.append(this);
    return this;
  }

  after (el) {
    const { nextSibling } = this.node;
    const { parentNode } = this.node;
    if (nextSibling) {
      parentNode.insertBefore(el.node, nextSibling);
    } else {
      parentNode.appendChild(el.node);
    }
    return this;
  }

  before (el) {
    const { node } = this;
    const { parentNode } = node;
    parentNode.insertBefore(el.node, node);
    return this;
  }

  width (val) {
    return (typeof val === 'undefined')
      ? this.node.offsetWidth
      : this.css('width', val);
  }

  height (val) {
    return (typeof val === 'undefined')
      ? this.node.offsetHeight
      : this.css('height', val);
  }

  html (text) {
    const { node } = this;
    if (typeof text !== 'undefined') {
      node.innerHTML = text;
      return this;
    } else {
      return node.innerHTML;
    }
  }

  text (text) {
    const { node } = this;
    if (typeof text !== 'undefined') {
      node.innerText = text;
      return this;
    } else {
      return node.innerText;
    }
  }
}

export default new DOM();
<commit_msg>Add .toggleClass to dom helpers<commit_after>/*

Tiny, bespoke DOM manipulation lib since
loading something like jQuery seems excessive.

(if I had a dollar for every time some bespoke DOM
lib got outta hand...)

If this spirals out of control it's almost certainly
worth swapping to a more developed solution but we shouldn't
be doing much DOM stuff anyways.

API:

  ===================================================================
  EventBase (mixin, adds event helpers to a target)
  ===================================================================

  .on([String] event, [Function] fn)
    - Adds an event listener, returns self

  .off([String] event, [Function] fn)
    - Removes an event listener, returns self

  .onOff([String] event, [Function] fn)
    - Adds an event listener, returns a function that
      removes that listener

  .one([String] event, [Function] fn)
    - Adds an event listener that is removed after being
      fired once.

  ===================================================================
  NodeBase (mixin, adds helpers for retrieving nodes)
  ===================================================================

  .get ([String] selector)
    - runs node.querySelector
    - returns instance of El

  .getAll ([String] selector)
    - runs node.querySelectorAll
    - returns array of Els

  .exists ([String] selector, [Boolean?] forceBool = false)
    - runs node.get
    - if node.get.node is undefined, returns false
    - otherwise if forceBool is false, returns instance of El
    - if forceBool is true, returns true

  ===================================================================
  Doc (public, mixes in NodeBase)
  ===================================================================

  .constructor ([Node?] document = window.document)
    - creates instance of NodeBase with document

  ===================================================================
  DOM (public/default export, mixes in EventBase, extends Doc)
  ===================================================================

  .create ([String] tag, [PlainObject?] attributes)
    - runs document.createElement
    - adds attributes, given in the form of { attribute: value, ... }
    - returns instance of El

  .body
    - instance of El with document.body as the node

  .window
    - instance of EventBase with the window object

  ===================================================================
  El (private, mixes in NodeBase and EventBase, exposed via Doc/DOM)
  ===================================================================

  All methods return self (i.e. are chainable) unless otherwise noted.

  .node
    - native dom node

  .remove ()
    - removes node from the DOM

  .wrap ([El] wrapper)
    - wraps self.node with wrapper.node

  .clone ()
    - returns new instance of El with a clone of self.node

  .attr ([String|PlainObject] attributes, [String?] val)
    - sets attributes
    - accepts ("attribute", "value") or { attribute: "value", ...}
    - if attribute is a string and no value is specified, returns
      the value of that attribute.

  .css ([String|PlainObject] props, [String?] val)
    - sets style props
    - accepts ("property", "value") or { property: "value", ... }

  .addClass ([String] classNames)
    - adds one or more classes (space separated)

  .rmClass ([String] classNames)
    - removes one or more classes (space separated)

  .toggleClass([String] classnames, [Boolean?] force)
    - toggles one or more classes (space separated
    - can force class state with the `force` param

  .parent ()
    - returns new instance of El with self.node's parentNode

  .append ([El] el)
    - appends el.node to self.node

  .appendTo ([El] el)
    - appends self.node to el.node

  .after ([El] el) {
    - inserts el.node after self.node

  .before ([El] el) {
    - inserts el.node before self.node

  .width ([String?] val)
    - if val is specified, sets style.width to val
    - otherwise returns node.offsetWidth

  .height ([String?] val)
    - if val is specified, sets style.height to val
    - otherwise returns node.offsetHeight

  .html ([String?] text)
    - if text is specified, sets node.innerHTML to text
    - otherwise returns node.innerHTML

  .text ([String?] text)
    - if text is specified, sets node.innerText to text
    - otherwise returns node.innerText
*/

class EventBase {
  constructor (target) {
    this.target = target;
    return {
      on: this.on.bind(this),
      off: this.off.bind(this),
      onOff: this.onOff.bind(this),
      one: this.one.bind(this)
    };
  }

  on (event, fn) {
    this.target.addEventListener(event, fn);
    return this;
  }

  off (event, fn) {
    this.target.removeEventListener(event, fn);
    return this;
  }

  onOff (event, fn) {
    this.on(event, fn);
    return () => { this.off(event, fn); };
  }

  one (event, fn) {
    const off = this.onOff(event, () => {
      fn();
      off();
    });
    return this;
  }
}

class NodeBase {
  constructor (node) {
    this.node = node;
    return {
      node,
      get: this.get.bind(this),
      getAll: this.getAll.bind(this),
      exists: this.exists.bind(this)
    };
  }

  get (selector) {
    return new El(this.node.querySelector(selector));
  }

  getAll (selector) {
    const nodes = this.node.querySelectorAll(selector);
    return [...nodes].map(node => new El(node));
  }

  exists (selector, forceBool = false) {
    const el = this.get(selector);
    if (!el.node) {
      return false;
    } else {
      return forceBool ? true : el;
    }
  }
}

export class Doc {
  constructor (document = window.document) {
    Object.assign(this, new NodeBase(document));
  }
}

class DOM extends Doc {
  constructor () {
    super();
    Object.assign(this, new EventBase(this.node));
    this.window = new EventBase(window);
    this.body = this.get('body');
  }

  create (tag, attributes) {
    const el = new El(this.node.createElement(tag));
    if (attributes) {
      el.attr(attributes);
    }
    return el;
  }
}

class El {
  constructor (node) {
    this.node = node;
    Object.assign(this, new NodeBase(node), new EventBase(node));
  }

  remove () {
    const { node } = this;
    node.parentNode.removeChild(node);
    return this;
  }

  wrap (wrapper) {
    const parent = this.parent();
    wrapper = wrapper.clone();
    parent.append(wrapper);
    wrapper.append(this);
    return this;
  }

  clone () {
    return new El(this.node.cloneNode());
  }

  attr (attributes, val) {
    const { node } = this;

    const handleString = () => {
      if (typeof val !== 'undefined') {
        if (val === null) {
          node.removeAttribute(attributes);
        } else {
          node.setAttribute(attributes, val);
        }
        return this;
      } else {
        return node.getAttribute(attributes);
      }
    };

    if (typeof attributes === 'string') {
      return handleString();
    } else {
      for (const attribute in attributes) {
        node.setAttribute(attribute, attributes[attribute]);
      }
    }
    return this;
  }

  css (props, val) {
    const { node } = this;
    if (typeof props === 'string') {
      node.style[props] = val;
    } else {
      for (const prop in props) {
        node.prop[prop] = props[prop];
      }
    }
    return this;
  }

  addClass (classNames) {
    classNames = classNames.split(' ');
    classNames.forEach(className => {
      this.node.classList.add(className);
    });
    return this;
  }

  rmClass (classNames) {
    classNames = classNames.split(' ');
    classNames.forEach(className => {
      this.node.classList.remove(className);
    });
    return this;
  }

  toggleClass (classNames, force) {
    classNames = classNames.split(' ');
    classNames.forEach(className => {
      this.node.classList.toggle(className, force);
    });
  }

  parent () {
    return new El(this.node.parentNode);
  }

  append (el) {
    this.node.appendChild(el.node);
    return this;
  }

  appendTo (el) {
    el.append(this);
    return this;
  }

  after (el) {
    const { nextSibling } = this.node;
    const { parentNode } = this.node;
    if (nextSibling) {
      parentNode.insertBefore(el.node, nextSibling);
    } else {
      parentNode.appendChild(el.node);
    }
    return this;
  }

  before (el) {
    const { node } = this;
    const { parentNode } = node;
    parentNode.insertBefore(el.node, node);
    return this;
  }

  width (val) {
    return (typeof val === 'undefined')
      ? this.node.offsetWidth
      : this.css('width', val);
  }

  height (val) {
    return (typeof val === 'undefined')
      ? this.node.offsetHeight
      : this.css('height', val);
  }

  html (text) {
    const { node } = this;
    if (typeof text !== 'undefined') {
      node.innerHTML = text;
      return this;
    } else {
      return node.innerHTML;
    }
  }

  text (text) {
    const { node } = this;
    if (typeof text !== 'undefined') {
      node.innerText = text;
      return this;
    } else {
      return node.innerText;
    }
  }
}

export default new DOM();
<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
<commit_msg>Correcting style<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
 <|endoftext|>