// Flashcard App v 3-02
//
// Copyright 2009, Bryan Abshier
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// Resources used:
// (JSDG) JavaScript the Definitive Guide 5th ed
//
// Conventions:
// String flags are either "yes" or "no"
//

// Global Constants
var BACKEND="http://teishoku.org/xoroka/orokapi.pl";

// Toolbar object
//
// on: "yes" or "no" ; Display this item in the toolbar?
// name: The name to be displayed on the toolbar.
// fname: The name of the javascript function link. (don't forget the '()');
// launch: A launcher for the hotkey so I don't have to use an eval.
// alias: an alternate hotkey, maps to the key it's an alias for
//
var tb = {};
tb.f = { on:"no", name:"Flip", fname:"flipCard()", launch: function() { flipCard() } };
tb.space = { alias:"f" };
tb.return = { alias:"f" };
tb.n = { on:"no", name:"Next", fname:"nextCard()", launch: function() { nextCard() } };
tb.right = { alias:"n" };
tb.l = { on:"no", name:"Last", fname:"lastCard()", launch: function() { lastCard() } };
tb.left = { alias:"l" };
tb.e = { on:"no", name:"Learn", fname:"learnCard()", launch: function() { learnCard() } };
tb.down = { alias:"e" };
tb.r = { on:"no", name:"Direction", fname:"cardDirection()", launch: function() { cardDirection() } };
tb.m = { on:"no", name:"Remove", fname:"removeCard()", launch: function() { removeCard() } };
tb.up = { alias:"m" };
tb.x = { on:"no", name:"Info", fname:"cardInfo()", launch: function() { cardInfo() } };
tb.s = { on:"yes", name:"Select", fname:"selCardsReset()", launch: function() { selCardsReset() } };
tb.i = { on:"yes", name:"Log In", fname:"logIn()", launch: function() { logIn() } };
tb.o = { on:"no", name:"Log Out", fname:"logOut()", launch: function() { logOut() } };
tb.d = { on:"no", name:"Load", fname:"loadSet()", launch: function() { loadSet() } };
tb.v = { on:"no", name:"Save", fname:"saveSet()", launch: function() { saveSet() } };
tb.t = { on:"no", name:"Delete", fname:"deleteSet()", launch: function() { deleteSet() } };
tb.p = { on:"no", name:"Append Cards", fname:"appendCards()", launch: function() {appendCards() } };
tb.g = { on:"no", name:"Get Cards", fname:"getCards()", launch: function() { getCards() } };
tb.w = { on:"no", name:"View", fname:"goBack()", launch: function() { goBack() } };
tb.a = { on:"no", name:"Main", fname:"mainMenu()", launch: function() { mainMenu() } };
tb.h = { on:"no", name:"Reshuffle", fname:"reShuffle()", launch: function() { reShuffle() } };


// Translation table for key codes used by the key event handler.
// Maybe I can limit my key map to keys which might actually be pressed?
var keyMap = {
    8:"backspace", 9:"tab", 13:"return", 19:"pause", 27:"escape", 32:"space",
    33:"pageup", 34:"pagedown", 35:"end", 36:"home", 37:"left", 38:"up",
    39:"right", 40:"down", 45:"insert", 46:"delete", 48:"0", 49:"1", 50:"2",
    51:"3", 52:"4", 53:"5", 54:"6", 55:"7", 56:"8", 57:"9", 59:";", 61:"=",
    65:"a", 66:"b", 67:"c", 68:"d", 69:"e", 70:"f", 71:"g", 72:"h", 73:"i",
    74:"j", 75:"k", 76:"l", 77:"m", 78:"n", 79:"o", 80:"p", 81:"q", 82:"r",
    83:"s", 84:"t", 85:"u", 86:"v", 87:"w", 88:"x", 89:"y", 90:"z", 107:"+",
    109:"-", 110:".", 188:","
}

// More global variables

// Set up sreq
var sreq = new XMLHttpRequest();
// XML object for the card-set index retreived from the server. (JSDG 503)
var cardex = document.implementation.createDocument("", "", null);
var cardsel = []; // "yes"/"no" Array for selected cards-sets.
var csseld = 0; // The number of card sets currently selected
// The object for card data.
var card = {};
// This will be extended with card[n] = {}, then
var cardRef = []; // The order in which to present cards
var cardCurrent = -1; // The number of the next card
var cardPart = 0; // Which side is currently being shown
var cardDir = 1;  // Which direction cards are being flipped (1/-1)
var cardDirRandom = 0;  // Set the flip direction 0=fwd,1=rnd,2=bck
var cardCN = "no"; // Call nextCard after we get data?
var cardSetName = null; // Name of the current card set
var cardAppend = "no"; // Append cards to current list?
var clearStat = "yes";
// Some other variables
var sessionNames = [];
var sessionOpp = null;
var userName = null;
var sCookName = "session";
var sCookVal  = null;
var sCookTime = (60*60*24*7);

// Variables which are local to a function, but need to persist over sucsessive calls.
selCards.loop = 0;

// Clearit: Remove all of the children from an element
function clearIt(eid) {
  var cell = document.getElementById(eid);
  while (cell.hasChildNodes()) { cell.removeChild(cell.firstChild); }
}

// Put the time in the status bar
function statClock() {
    var newt = document.getElementById('clock');
    newt.innerHTML = "test";
    var d = new Date();
    var ap = " am";
    var hour = d.getHours();
    if (hour > 12) {
	hour -= 12;
	ap = " pm";
    }
    var min = d.getMinutes();
    if (min < 10) { min = "0"+min }
    newt.innerHTML = hour+":"+min+ap;
}

function clearId(x) {
    var newm = document.getElementById(x);
    newm.innerHTML = '';
}

// Print a message in the status line
function statMess(text,other) {
    var x = 'status';
    if (other != undefined) { x = other }
    else { clearStat = "no" }
    var newm = document.getElementById(x);
    if (text != null) {newm.innerHTML = text+" | "}
    else {newm.innerHTML = ""}
}

// Add a new child <p> element in the header
function inHeader(text,cls,id) {
    var newp = document.createElement('p');
    if (cls != null) { newp.setAttribute('class',cls) }
    if (id != null) { newp.setAttribute('id',id) }
    newp.innerHTML=text;
    document.getElementById('header').appendChild(newp);
}

// Add a new child <p> element in the cardbox (with class cls.)
function innerPs(text,cls,id) {
    var newp = document.createElement('p');
    if (cls == undefined) { cls = 'line' }
    newp.setAttribute('class',cls);
    if (id != undefined) { newp.setAttribute('id',id) }
    newp.innerHTML=text;
    document.getElementById('cbox').appendChild(newp);
}

// Display the toolbar from the global tb object
function showToolbar() {
    var pl= '';
    var test1 = '';
    var test2 = '';
    var holder = '';
    var occur = '';
    var x = '';

    // This is a good place to reset the onkey event handler
    keyEH('keyEvent(event)');

    var newt = document.getElementById('toolbar');

    for (x in tb) {
	if (tb[x].on == "yes" && tb[x].alias == undefined) {
	    test1 = tb[x].name.toLowerCase();
	    test2 = x.toLowerCase();
	    occur = test1.indexOf(test2);
	    if (occur >= 0) { holder = tb[x].name.slice(0,occur)+'<span class=underline>'+tb[x].name.slice(occur,occur+1)+'</span>'+tb[x].name.slice(occur+1) }
	    else { holder = tb[x].name }
	    pl = pl+'<a href="javascript:'+tb[x].fname+'">'+holder+'</a> | ';
	}
    }
    pl = pl.slice(0,pl.length-2);
    newt.innerHTML=pl;
}

// Toolbar setup when viewing card data
function cardToolbar() { // Flashcard menu
  tb.i.on = "no";
  tb.o.on = "no";
  tb.f.on = "yes";
  tb.n.on = "yes";
  tb.l.on = "yes";
  tb.r.on = "yes";
  tb.m.on = "yes";
  tb.e.on = "yes";
  tb.x.on = "yes";
  tb.g.on = "no";
  tb.w.on = "no";
  tb.s.on = "no";
  tb.a.on = "yes";
  tb.h.on = "no";
  tb.d.on = "no";
  tb.v.on = "no";
  tb.p.on = "no";
  showToolbar();
}

// Toolbar items that should be on when logged in
function selectLogged() {
  if (userName == null) {
    tb.i.on = "yes";
    tb.o.on = "no";
    tb.d.on = "no";
    tb.v.on = "no";
    tb.t.on = "no";
  }
  else {
    tb.t.on = "yes";
    tb.d.on = "yes";
    tb.i.on = "no";
    tb.o.on = "yes";
    selectSelected();
  }
}

// These items are displayed in the main menu when cards have been selected
function selectSelected() {
  if (cardRef.length > 0) {
    tb.w.on = "yes";
    tb.h.on = "yes";
    tb.v.on = "yes";
  }
  else {
    tb.w.on = "no";
    tb.h.on = "no";
    tb.v.on = "no";
  }
}

function selectToolbar() { // Card selection toolbar
    selectLogged();
    tb.w.on = "no";
    tb.h.on = "no";
    tb.f.on = "no";
    tb.n.on = "no";
    tb.l.on = "no";
    tb.r.on = "no";
    tb.m.on = "no";
    tb.e.on = "no";
    tb.s.on = "no";
    tb.g.on = "yes";
    tb.h.on = "no";
    tb.x.on = "no";
    if (cardRef.length > 0) {
      tb.a.on = "yes";
      tb.p.on = "yes";
    }
    else {
      tb.a.on = "no";
      tb.p.on = "no";
    }
    showToolbar();
}

function menuToolbar() { // Main menu
  selectLogged();
  tb.f.on = "no";
  tb.n.on = "no";
  tb.l.on = "no";
  tb.r.on = "no";
  tb.m.on = "no";
  tb.e.on = "no";
  tb.s.on = "yes";
  tb.g.on = "no";
  tb.a.on = "no";
  tb.x.on = "no";
  tb.p.on = "no";
  if (cardRef.length > 0) { tb.w.on = "yes" }
  else { tb.w.on = "no" }
  showToolbar();
}

// Obtain all text from all children beneath a givin DOM node. (JSDG p319)
function getText(n)  {
    var strings = [];
    getStrings(n, strings);
    return strings.join("");

    function getStrings(n, strings) {
	if (n.nodeType == 3) { // for a text node
	    strings.push(n.data);
	}
	else if (n.nodeType == 1) { // for an element node
	    for (var m = n.firstChild; m != null; m = m.nextSibling) {
		getStrings(m, strings);
	    }
	}
    }
}

// Main menu space
function mainMenu() {
    menuToolbar();
    clearIt("cbox");
}

// After we get the cards from the server, we can actually select them.
function selCards(x) {
    var LIST = 12;  // How many cards do we list at a time
    var i = 0; // an indexer
    var lb = 0; // lower bound
    var ub = 0; // upper bound
    var selbar = ''; // a string for the selection bar
    var titem = ''; // a string for card item titles
    var rtext = '';
    var stext = '';

    // Clear the status line
    clearId('status');
    clearId('title');

    // Get the titl things out of the globar card index object
    var titl = cardex.getElementsByTagName('titl');

    // If there's nothing here, try getting it again
    if (titl.length == 0) {
	if (selCards.loop < 5) { selCardsReset() }
	else { innerPs('Sorry, there was a problem...') }
	selCards.loop++;  // Don't want any evil looping...
	return;
    }
    selCards.loop = 0;

    // Make the most recent cards the default
    if ( x == undefined || x == null) {
	x = Math.floor(titl.length/LIST);
	if ( x > 0) { if (titl.length/x == LIST) { x = x-1 } }
    }
    // Maybe build some sanity checking for x here???
    // Build the selection toolbar
    for (i = 0; i < (titl.length/LIST); i++) {
	lb = i*LIST+1;
	ub = i*LIST+LIST;
	if (i == x) { selbar = selbar+'<b>'; }
	selbar = selbar+'<a href="javascript:selCards('+i+')">['+lb+'-'+ub+']</a>';
	if (i == x) { selbar = selbar+'</b>'; }
    }
    selbar = selbar + ' -- <a href="javascript:selCardsReset()>Reset</a>';
    // Clear out the cardbox and display the stuff
    clearIt('cbox');
    innerPs(selbar,'toolbar');
    lb = x*LIST;
    ub = x*LIST+LIST;
    if (ub > titl.length) { ub = titl.length }
    innerPs("&nbsp;",'ljmini');
    for (i = lb; i < ub; i++) {
	var color = "csoff";
	if (cardsel[i] == "yes") { color = "cson" }
	rtext = getText(titl[i]);
	stext = '<a class="'+color+'" href="javascript:selCardsSel('+i+')">'+rtext+'</a>';
	innerPs(stext,'ljmini','sbid'+i);
    }
}

// Select a card-set, x is the number of the card-set selected
function selCardsSel(x) {
    var mine = document.getElementById('sbid'+x);
    var itex = getText(mine);

    if (cardsel[x] == "yes") {
	itex = '<a class="csoff" href="javascript:selCardsSel('+x+')">'+itex+'</a>';
	cardsel[x] = "no";
	csseld--;
    }
    else {
	itex = '<a class="cson" href="javascript:selCardsSel('+x+')">'+itex+'</a>';
	cardsel[x] = "yes";
	csseld++;
    }
    statMess(csseld+' selected');
    mine.innerHTML = itex;
}

// Reset the selection and reload the index
function selCardsReset() {
    var name = [];
    var tag = [];
    var url = BACKEND+'?iList';

    // Adjust the menu options
    selectToolbar();

    clearIt("cbox");
    innerPs("Please wait...");

    // Clear the card index object and array
    cardex = {};
    cardsel = [];
    csseld = 0; // Global, number of cards selected.

    // Get the index data
    sreq = new XMLHttpRequest();
    sreq.onreadystatechange = gotSome;
    sreq.open("GET", url, true);
    sreq.send(null);
}

// Shuffle the given array
// http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
function shuffleCards(array) {
    var temp;
    var k;

    var n = array.length;
    while (n > 1) {
	k = Math.floor(Math.random()*n);  // 0 <= k < n
	n--;
	temp = array[n];
	array[n] = array[k];
	array[k] = temp;
    }
}

function reShuffle() {

    shuffleCards(cardRef);
    statMess('Cards Reshuffled');
    goBack();
}

// Set the append flag for downstream functions
function appendCards() {
  cardAppend = "yes";
  getCards();
}

// Request a set of cards based on cardex
function getCards() {
    var cstags = '';
    var csname = '';
    var url = BACKEND+'?';
    var i = 0;

    // Get the card set names out of the global card index object
    var cnam = cardex.getElementsByTagName('name');

    // Create the url arguments
    for (i = 0; i < cardsel.length; i++) {
      if ( cardsel[i] == 'yes') {
	csname = getText(cnam[i]);
	cstags = cstags + 'cset='+csname+'&';
      }
    }

    // Send the request
    if ( cstags.length > 1 ) {
	clearIt('cbox');
	innerPs('Please wait');

	url = url+cstags;
	sreq = new XMLHttpRequest();
	sreq.onreadystatechange = gotSome;
	sreq.open("GET", url, true);
	sreq.send(null);

	// Reset the card counter
	cardCurrent = -1;
    }
    else { statMess("No card sets are selected") }
}

function spewCards() {
    for (var i = 0; i < cardRef.length; i++) {
	innerPs('Spew: '+cardRef[i],'ljmini');
    }
}

// Deal with returned card data
// (cdom is a DOM object with the returned card names)
function getCards2(cdom) {
  var i = 0;
  var j = 0;
  var cardname = '';
  var test = '';

  // This becomes an array-like object of DOM nodes with the tag 'card'
  var cnamexml = cdom.getElementsByTagName('card');

  if (cnamexml.length > 0) {

    innerPs('cardAppend: '+cardAppend);
    if (cardAppend == "no") {
      card = {}; // Reset the global card object
      cardRef = []; // Reset the card-ref array
    }
    for (i = 0; i < cnamexml.length; i++) {
      cardname = getText(cnamexml[i]); // Pull the text from DOM nodes
      if (cardAppend == "no") {
	card[cardname] = { }; // Create an object within this object
	//card[i].name = getText(cnamexml[i]); // Pull the text from DOM nodes
	cardRef.push(cardname);
      }
      else {
	// Maybe there's a better way to search for matches...
	// This is noticably slow.
	test = "no";
	for (j = 0; j < cardRef.length; j++) {
	  if (cardRef[j] == cardname) {
	    test = "yes";
	    j = cardRef.length;
	  }
	}
	if (test == "no") {
	  card[cardname] = { };
	  cardRef.push(cardname);
	}
      }
    }
    // card.length = i;  // This is not a universal object property
    if (cardAppend == "no") { shuffleCards(cardRef); }
    //spewCards();
    cardAppend = "no";
    cardToolbar();
    nextCard();
  }
  else {
    innerPs('There was a problem, please try selecting your card sets again.');
  }
}

function cardDirection() {
    inkDec("dec");
    cardDirRandom++;
    if (cardDirRandom == 1) { statMess('Random') }
    else if (cardDirRandom == 2) { statMess('Reverse') }
    else {
	cardDirRandom = 0;
	statMess('Forward');
    }
    nextCard();
}

//Show the next pard of the card
function flipCard() {
  var max = cardRef.length;
  // Pulled the card title out of the status bar,
  // and added the "Info" option "card[cardRef[cardCurrent]].title"
  statMess(cardCurrent+1+" of "+max,'title');
  if (card[cardRef[cardCurrent]].q[cardPart] != undefined) {
    innerPs(card[cardRef[cardCurrent]].q[cardPart]);
    cardPart = cardPart+cardDir;
  }
  else { nextCard() }
}

// Show the card information.
function cardInfo() {
  clearIt('cbox');
  innerPs('&nbsp;');
  innerPs('Card: '+cardRef[cardCurrent]);
  innerPs(card[cardRef[cardCurrent]].title);
  inkDec('dec');
  innerPs('&nbsp;');
  innerPs('<a href="javascript:nextCard()">Return</a>');
}

//Remove a card from the active deck
function removeCard() {
    if (cardRef.length == 1) { statMess('There is only one card left') }
    else {
	statMess('Card '+cardRef[cardCurrent]+' removed');
	cardRef.splice(cardCurrent,1);
	inkDec('dec');
	nextCard();
    }
}

//Go back to the cards from the select screen, without doing the selection.
function goBack() {
    inkDec('dec');
    cardToolbar()
    nextCard();
}

//Learn the card
function learnCard() {
    if (cardDir == 1 && cardPart == 1) {
	statMess('View card first');
	return;
    }
    var silly = card[cardRef[cardCurrent]].q.length-2;
    if (cardDir == -1 && cardPart == silly) {
	statMess('View card first');
	return;
    }

    var uninc = 'yes';
    var adv = Math.floor(Math.random()*4)+4; // Random number between 4 and 7
    var insert = cardCurrent + adv;
    var pull = cardRef.splice(cardCurrent,1);
    if (insert >= cardRef.length) {
	insert = insert - cardRef.length;
	uninc = 'no';
    }
    cardRef.splice(insert,0,pull);
    var iplus = insert+1;
    statMess('Pull: '+pull+' to: '+iplus);
    if (uninc == 'yes') { inkDec('dec') }
    nextCard();
}

//Get the data for the next 10 cards
function fillNextTen() {
    var i = 0;
    var ctg = 20; // Yeah, I started with 10...
    var url = BACKEND;
    var urlargs = '?';

    // Don't try to get more cards then there are
    var max = cardRef.length;
    var top = cardCurrent + ctg;
    if ( top > max ) { top = max }

    // innerPs('cur: '+cardCurrent+' max: '+max+' top:'+top);

    for (i=cardCurrent; i < top; i++) {
	if (card[cardRef[i]].title == undefined) {
		urlargs = urlargs+'cDat='+cardRef[i]+'&';
	}
    }

    // innerPs('args: '+urlargs,"ljmini");

    if (urlargs.length > 1) {
	url=url+urlargs;
	sreq = new XMLHttpRequest;
	sreq.onreadystatechange = gotSome;
	sreq.open("GET", url, true);
	sreq.send(null);
    }
    else { statMess("Weird, there are no cards in the selected sets..."); }
}

// Account for the card counter wraping around
function inkDec(id) {
    if (id == "inc") { cardCurrent++; }
    else { cardCurrent--; }
    if (cardCurrent >= cardRef.length) { cardCurrent = 0 }
    if (cardCurrent < 0) { cardCurrent = cardRef.length - 1 }
}

// Show the card before this card
function lastCard() {
    inkDec('dec');
    inkDec('dec');
    nextCard();
}

// Display the questions from the next card
function nextCard() {
  var counter = 0;
  inkDec('inc');
  var kpf = cardCurrent+5;

  // If there's no data, get some
  if (card[cardRef[cardCurrent]].title == undefined) {
    cardCN = "yes"; // Call this function again when we get data
    fillNextTen();
    return;
  }
  if (kpf < cardRef.length && card[cardRef[kpf]].title == undefined) { fillNextTen(); }

  // Set the card direction
  if (cardDirRandom == 0) { cardDir = 1; }
  else if (cardDirRandom == 1) {
    if (Math.floor(Math.random()*2) > 0) { cardDir = 1; }
    else { cardDir = -1; }
  }
  else { cardDir = -1; }
  if (cardDir == 1) { cardPart = 0; }
  else { cardPart = card[cardRef[cardCurrent]].q.length-1; }

  if (clearStat != "no") { clearId('status'); }
  else { clearStat = "yes"; }

  clearIt('cbox');
  innerPs('&nbsp;');
  flipCard();
}

function cardUp(cdom) { // cdom = dom object with the card data
    var i = 0;
    var j = 0;
    var holder = '';
    var cardname = '';

    for (i = 0; i < cdom.length; i++) {
 	var ink = cdom[i].getElementsByTagName('name');
	cardname = getText(ink[0]);
	var ink = cdom[i].getElementsByTagName('title');
	holder = getText(ink[0]);
	card[cardname].title = holder;
	// innerPs('Title '+i+': '+holder,'ljmini');
	var quest = cdom[i].getElementsByTagName('q');
	if (quest.length > 0) {
	    // Initialize the relivant part of the crazy card object if necessary.
	    if (card[cardname].q == undefined) { card[cardname].q = [] }
	    for (j = 0; j < quest.length; j++) {
		holder = getText(quest[j]);
		card[cardname].q[j] = holder;
		//innerPs('Question '+i+'-'+j+': '+holder,'ljmini');
	    }
	}
    }

    // nextCard was unable to display any cards.
    if (cardCN == "yes") {
	cardCN = "no";
	inkDec("dec");
	nextCard();
    }
}

// Set the key event handler to the function handler
function keyEH(handler) {
    if (handler == undefined) { handler = ""; }
    // Get the first and only <body>
    var newk = document.getElementsByTagName("body")[0];
    newk.setAttribute('onkeyup',handler);
}

// I have to do this all of twice :)
function lline (title,type) {
    var head = document.createElement('tr');
    var tcol = document.createElement('td');
    var icol = document.createElement('td');
    var inpu = document.createElement('input');

    inpu.id = title;
    inpu.setAttribute('onchange','filterWeirdness("'+title+'")');
    if (type != undefined) { inpu.type = type }
    tcol.appendChild(document.createTextNode(title));
    head.appendChild(tcol);
    icol.appendChild(inpu);
    head.appendChild(icol);

    return head;
}

// logIn
function logIn () {
  keyEH('textKeyEvent(event)'); // Change the key event handler
  clearIt("cbox");

  innerPs('&nbsp;');

  // Build the login form thing
  var form = document.createElement('form');
  form.name = 'aform';
  form.action = ''; // Submission is handled by the key event manager

  var elem = document.createElement('table');
  elem.border = 0;
  elem.align = "center";
  var head = lline('Login');
  elem.appendChild(head);
  head = lline('Password','password');
  elem.appendChild(head);
  form.appendChild(elem);
  document.getElementById('cbox').appendChild(form);

  innerPs('&nbsp;');
  innerPs('<a href="javascript:startLogIn()">Submit</a>');
}

// Unset user related stuff
function unsetLoginStuff() {
    userName = null;
    sCookVal = null;
    document.cookie = sCookName+"="+sCookVal+"; max-age=0";
    statMess(null,"login");
}

// startLogIn
function startLogIn() {
    var url = BACKEND+'?';

    var rege = /\W/g; // No special characters
    var foo = document.getElementById("Login");
    var login = foo.value.replace(rege,"");
    foo = document.getElementById("Password");
    var pass = foo.value.replace(rege,"");

    unsetLoginStuff();

    // Print some text
    clearIt("cbox");
    innerPs("Please Wait");

    url = url+'login='+login+'&pass='+pass;
    // innerPs("url is "+url);

    sreq = new XMLHttpRequest();
    sreq.onreadystatechange = gotSome;
    sreq.open("GET", url, true);
    sreq.send(null);
}

function sCookieSet(session) {
    var ses = getText(session[0]);
    sCookVal = ses;
    //innerPs(sCookName+"="+sCookVal+";max-age="+sCookTime);
    document.cookie = sCookName+"="+sCookVal+";max-age="+sCookTime;
}

function sCookieGet() {
    var value = "none";
    var allcook = document.cookie;
    var cooks = allcook.split(";");
    for (i = 0; i < (cooks.length); i++) {
	var parts = cooks[i].split("=");
	if (parts[0] == sCookName) { value = parts[1] }
    }
    return value;
}

function checkSession() {
    var ses = sCookieGet();
    if (ses != "none") {
	userName = getUserName(ses);
    }
}

function getUserName(session) {
    var url = BACKEND+"?gUser=yes&session="+session;

    // Get the index data
    sreq = new XMLHttpRequest();
    sreq.onreadystatechange = gotSome;
    sreq.open("GET", url, true);
    sreq.send(null);
}

function setUserName(name) {
    var un = getText(name[0]);
    userName = un;
    statMess(un,"login");
    selectLogged();
    showToolbar();
}

function loginResult(login) {
    var loged = getText(login[0]);

    clearIt("cbox");

    if (loged == "yes") { innerPs("Logged in as "+userName); }
    else { innerPs("Username and/or password is incorrect"); }

    // innerPs("(session is "+sCookVal+")");
    menuToolbar();
}

function logOut() {
    clearIt("cbox");
    unsetLoginStuff();
    innerPs("You have logged out");
    menuToolbar();
}

function saveSet() {
  sessionOpp = "save";
  if (cardRef.length == 0) {
    clearIt("cbox");
    innerPs("There are no cards to save");
  }
  else { setFiles(); }
}

function saveSetAs(no) {
  keyEH(''); // Really unset the key event handler
  if (no == undefined) { clearIt("cbox"); }
  innerPs('&nbsp;');
  innerPs("Save session as");
  // Build the Save as form
  var form = document.createElement('form');
  form.name = 'aform';
  form.action = 'javascript:saveSetAs2()';
  var newp = document.createElement('p');
  newp.setAttribute("align","center");
  var inpu = document.createElement('input');
  inpu.id = "saveas";
  inpu.type = "text";
  inpu.setAttribute('onchange','filterWeirdness("saveas","sok")');
  newp.appendChild(inpu);
  form.appendChild(newp);
  document.getElementById('cbox').appendChild(form);

  inpu.focus(); // Put the focus in the input box

  innerPs('&nbsp;');
  innerPs('<a href="javascript:saveSetAs2()">Submit</a>');
}

function WTF() {
  // Does this crap really happen on submit?
  alert("Well it run after being submitted");
  return false;
}

// Saves the user's current list of cards and meta-data
function saveSetAs2(saveas) {
  var session = sCookieGet();
  var url = BACKEND+"?sUserSet=yes&session="+session;

  // Get the filename and make sure it's okay
  if (saveas == undefined) {
    foo = document.getElementById("saveas");
    saveas = foo.value;
  }
  else { saveas = sessionNames[i]; }
  var rege = /\s/g;
  saveas = saveas.replace(rege,"_");
  rege = /\W/g;
  saveas = saveas.replace(rege,"");
  url = url+"&saveAs="+saveas;

  // Card set place markers
  url = url+"&cardCur="+cardCurrent;
  url = url+"&cardPar="+cardPart;
  url = url+"&cardDir="+cardDirRandom;

  // List of cards
  clearIt("cbox");
  if ( cardRef.length <= 0 ) {
    innerPs("No cards have been selected");
    return;
  }
   if ( saveas == "" ) {
     innerPs("Please choose a name for the session");
     saveSet("no"); // "no" to suppress clearIt
     return;
   }
   for ( i=0; i<cardRef.length; i++) {
     url=url+"&crd="+cardRef[i];
   }

   innerPs("Saving cards");
   // innerPs("url=\""+url+"\"");

   // Get the index data
   sreq = new XMLHttpRequest();
   sreq.onreadystatechange = gotSome;
   sreq.open("GET", url, true);
   sreq.send(null);
 }

 function saveSetResult(result) {
   //var mes = getText(result[0]);
   //innerPs("wtf->"+mes);
   clearIt('cbox');
   innerPs("Cards Saved");
 }

function loadSet() {
  sessionOpp = "load";
  setFiles();
}

function deleteSet() {
  sessionOpp = "delete";
  setFiles();
}

// Get the list of saved card sets
function setFiles() {
  var session = sCookieGet();
  var url=BACKEND+'?xUserSet=yes&session='+session;

  clearIt('cbox');
  innerPs('Please wait');

  sreq = new XMLHttpRequest();
  sreq.onreadystatechange = gotSome;
  sreq.open("GET", url, true);
  sreq.send(null);
}

function setFiles2(result) {
  var i = 0;
  var setname = "";
  var ses = result.getElementsByTagName('session');

  // Clear out sessionNames
  sessionNames = [];

  // Get the cards out of the lump
  // var sets = result.getElementsByTagName('slist');
  if (ses.length > 0) {
    for ( i=0; i < ses.length; i++ ) {
      sessionNames[i] = getText(ses[i]);
      innerPs('session='+setname);
    }
    setFilesSelect();
  }
  else {
    clearIt('cbox');
    innerPs('There are no saved sessions');
    if (sessionOpp == "save") { saveSetAs();  }
  }
}

function setFilesSelect(x) {
   var LIST = 10; // How many sessions do we list on a page
   var i = 0;
   var lb = 0; // Lower bound
   var ub = 0; // Upper bound
   var max = sessionNames.length;
   var selbar = ''; // Selection bar

   if (sessionOpp == undefined || sessionOpp == null) { sessionOpp = "save"; }

   clearId('status');
   clearId('title');

   if ( x==undefined || x==null) {
     x=Math.floor(max/LIST);
     if (x>0) { if (max/x == LIST) { x = x -1; } }
   }

   // Load-up the selection bar
  for (i=0; i<(max/LIST);i++) {
    lb = i*LIST+1;
    ub = i*LIST+LIST;
    if (i == x) { selbar = selbar+'<b>'; }
    selbar = selbar+'<a href="javascript:setFilesSelect('+i+')">['+lb+'-'+ub+']</a>';
    if (i == x) { selbar = selbar+'</b>'; }
  }
  if (sessionOpp == "save") { selbar = selbar+'<a href="javascript:saveSetAs()> -- Save As</a>'; }

  // Display stuff
  clearIt('cbox');

  if (sessionOpp == "load") { innerPs('Load card set','toolbar'); }
  else if (sessionOpp == "save") { innerPs('Save card set','toolbar'); }
  else if (sessionOpp == "delete") { innerPs('Delete card set','toolbar'); }

  innerPs(selbar,'toolbar');
   lb = x*LIST;
   ub = x*LIST+LIST;
   if (ub > max) {
     ub = max;
   }
   innerPs('&nbsp;','ljmini');
   for (i=lb; i<ub; i++) {
     if (sessionOpp == "load") { innerPs('<center><a class="soff" href="javascript:loadSet2('+i+')">'+sessionNames[i]+'</a></center>','ljmini'); }
     else if (sessionOpp == "save") { innerPs('<center><a class="soff" href="javascript:saveSetAs2('+i+')">'+sessionNames[i]+'</a></center>','ljmini'); }
     else if (sessionOpp == "delete") { innerPs('<center><a class="soff" href="javascript:deleteSet2('+i+')">'+sessionNames[i]+'</a></center>','ljmini'); }
   }
}

function deleteSet2(set) {
  clearIt('cbox');
  innerPs('&nbsp;');
  innerPs('Delete card set:');
  innerPs('"'+sessionNames[set]+'"');
  innerPs('&nbsp;');
  innerPs('<a href="javascript:deleteSet3('+set+')">Yes</a>   <a href="javascript:deleteSet()">No</a>');
}

function deleteSet3(set) {
  var session = sCookieGet();
  var url = BACKEND+"?session="+session;

  var name = sessionNames[set];
  url=url+"&dUserSet=yes&setName="+name;

  clearIt('cbox');
  innerPs('Deleting session:');
  innerPs(name);

  // Send the delete request
  sreq = new XMLHttpRequest();
  sreq.onreadystatechange = gotSome;
  sreq.open("GET", url, true);
  sreq.send(null);
}

function deleteSet4(result) {
  var mes = getText(result);
  innerPs(mes);
}

function loadSet2(ses) {
  var session = sCookieGet();
  var url = BACKEND+"?session="+session;

  var name = sessionNames[ses];
  url=url+"&gUserSet=yes&setName="+name;

  clearIt('cbox');
  innerPs('Loading session '+name);
  // innerPs('url='+url);
  cardSetName = name; // But we don't know for sure whether we have it...

  // Get the index data
  sreq = new XMLHttpRequest();
  sreq.onreadystatechange = gotSome;
  sreq.open("GET", url, true);
  sreq.send(null);

}

function loadSet3(cdom) {
  var cardname = '';
  var cnamex = cdom.getElementsByTagName('card');

  innerPs('Got some cards (maybe)');
  if (cnamex.length > 0) {
    // Reset the card data variables
    card = {};
    cardRef = [];
    cardCurrent = -1;
    cardPart = 0;
    cardDir = 0;

    // Load up the card index.
    for (i = 0; i < cnamex.length; i++) {
      cardname = getText(cnamex[i]);
      cardRef[i] = cardname;
      card[cardname] = { };
      //innerPs('Card '+i+' is '+cardname);
    }
    // card.length = i; // This is not a universal object property

    // Get the card placement variables.
    if (cdom.getElementsByTagName('cardCur') != undefined) {
      var foo = cdom.getElementsByTagName('cardCur');
      cardCurrent = getText(foo[0]);
      cardCurrent = cardCurrent - 1; // Backstep for advances
    }
    if (cdom.getElementsByTagName('cardPar') != undefined) {
      var foo = cdom.getElementsByTagName('cardPar');
      cardPart = getText(foo[0]);
    }
    if (cdom.getElementsByTagName('cardDir') != undefined) {
      var foo = cdom.getElementsByTagName('cardDir');
      cardDirRandom = getText(foo[0]);
    }

    // Go to the Cards
    cardToolbar();
    nextCard();
  }
  else {
    innerPs('Strange, there are no saved cards');
  }
}

// filterWeirdness: Don't let the user type in anything too weird
// This is called by an "onchange" in input forms.
function filterWeirdness(feild,space) {
  var foo = document.getElementById(feild);
  var test = foo.value;
  if (space == "sok" ) { // Allow spaces
    var rege = /\s/g;
    test = test.replace(rege,"_");
  }
  var rege = /\W/g;
  test = test.replace(rege,"");
  foo.value = test;
}

// Deal with incoming data from XMLHttpRequest events
function gotSome() {

    if (sreq.readyState != 4) {
	statMess("Waiting");
	return;
    }
    if (sreq.status != 200) {
	clearIt("cbox");
	innerPs("Could not contact server");
	statMess("Server doesn't like me");
    }
    else {
      //statMess("Yay!  "+sreq.status);
      statMess("Dat rec");
      var lump = sreq.responseXML; // Got a big xml object
      // Now try to get some text out of it...
      // Note than getElementsByTagName() returns an array-like object
      if (lump.getElementsByTagName('session')[0] != undefined) {
	// Update the session if needed.
	var il = lump.getElementsByTagName('session');
	sCookieSet(il);
      }
      if (lump.getElementsByTagName('error')[0] != undefined) {
	// If there's an error, just print it, don't process any more.
	var il = lump.getElementsByTagName('error');
	var te = getText(il[0]);
	clearIt("cbox");
	innerPs(te);
      }
      else { // If there's no error, check for the rest of the options
	if (lump.getElementsByTagName('indexlist')[0] != undefined) {
	  var il = lump.getElementsByTagName('indexlist');
	  // il[0] is the xml object <indexlist> ... </indexlist>
	  cardex = il[0];  // Set the global card index XML object
	  selCards();
	}
	if (lump.getElementsByTagName('cardlist')[0] != undefined) {
	  var il = lump.getElementsByTagName('cardlist');
	  getCards2(il[0]);
	}
	if (lump.getElementsByTagName('carddata')[0] != undefined) {
	  var il = lump.getElementsByTagName('carddata');
	  cardUp(il);
	}
	if (lump.getElementsByTagName('username')[0] != undefined) {
	  var il = lump.getElementsByTagName('username');
	  setUserName(il);
	}
	if (lump.getElementsByTagName('login')[0] != undefined) {
	  var il = lump.getElementsByTagName('login');
	  loginResult(il);
	}
	if (lump.getElementsByTagName('csave')[0] != undefined) {
	  var il = lump.getElementsByTagName('csave');
	  saveSetResult(il);
	}
	if (lump.getElementsByTagName('sesList')[0] != undefined) {
	  var il = lump.getElementsByTagName('sesList');
	  setFiles2(il[0]);
	}
	if (lump.getElementsByTagName('setData')[0] != undefined) {
	  var il = lump.getElementsByTagName('setData');
	  loadSet3(il[0]);
	}
	if (lump.getElementsByTagName('dSetResult')[0] != undefined) {
	  var il = lump.getElementsByTagName('dSetResult');
	  deleteSet4(il[0]);
	}
      }
    }
}

// This is the general onkeyup event handler
function keyEvent(event) {
    var e = event || window.event; // For IE event model
    var code = e.keyCode;
    var kname = keyMap[code];

    if (tb[kname] == undefined) { return }
    // I don't know why I can't put this ^ below that v....
    if (tb[kname].alias != undefined) { kname = tb[kname].alias }
    tb[kname].launch();
}

// This is called onkeyup by the login function
function textKeyEvent(event) {
  var e = event || window.event;
  var code = e.keyCode;
  if (code == '13') {
    startLogIn();
  }
}


// Welcome message
function welcomeMess() {
    // Clear the cardbox
    clearIt("cbox");

    // Display the welcome message
    var style = "ljmini";
    innerPs("&nbsp;",style);
    innerPs("&nbsp;",style);
    innerPs("Welcome to the experimental flashcard app.",style);
    innerPs("&nbsp;",style);
    innerPs('Go ahead; click on "Select" to select some cards ^^',style);
    innerPs("&nbsp;",style);
    innerPs("Note:  This is a single page javascript application.",style);
    innerPs('So far it has only been tested on Mozilla/Firefox type',style);
    innerPs("Browsers.  It probably won't work on IE because I haven't",style);
    innerPs("added any of the silly, non-standard MS code.",style);
}

// Called when the document is loaded
function docLoad() {
    // Set-up the header
    clearIt('header');
    inHeader('愚かな　フラッシュカード','line');
    inHeader('','toolbar','toolbar');

    // Interval timer for the clock (after the document has loaded)
    statClock();
    setInterval('statClock()',60000);
    // Check for a cookie to see if we're logged in

    // Display the toolbar
    showToolbar();

    // Show the welcome message
    welcomeMess();

    // Check to see if we have a session
    checkSession();
}

