<html>
<head>
<title>An Online Bookstore</title>
<script language="JavaScript">
<!-- begin script
//******************************************
// QuickSort Array Sorting Functions
// JavaScript implementation by Achille Hui
// of Stanford University Dept. of Physics
//******************************************
function _pm_array_qsort(vec,lo,up,cmp_fun){
  var i, j, t;
  while(up > lo){
    i = lo;
    j = up;
    t = vec[lo];
    while(i < j){
      while(cmp_fun(vec[j],t) > 0)
        j -= 1;
      vec[i] = vec[j];
      while((i < j) && (cmp_fun(vec[i],t) <= 0))
        i++;
      vec[j] = vec[i];
    }
    vec[i] = t;
    if(i - lo < up - i){
      _pm_array_qsort(vec,lo,i-1,cmp_fun); lo = i+1;
    } else {
      _pm_array_qsort(vec,i+1,up,cmp_fun); up = i-1;
    }
  }
}
function pm_array_qsort(vec,lo,hi,cmp_fun){
  if(vec == null){
    return;
  } else if(cmp_fun == null){
    _pm_array_qsort(vec,lo,hi,_pm_array_defcmp);
  } else {
    _pm_array_qsort(vec,lo,hi,cmp_fun);
  }
}
function _pm_array_defcmp(a,b){
  return (a == b) ? 0 : (a > b) ? 1 : -1;
}
//********************************************
function isStopword (word) {
  var wd = word.toLowerCase();
  if (wd == "a" || wd == "an" || wd == "and" ||
      wd == "or" || wd == "the")
    return true;  
  return false;
}
function isVowel (ch) {
  if (ch == 'a' || ch == 'e' || ch == 'i' ||
      ch == 'o' || ch == 'u')
    return true;
  return false;
}
function isNumeric (str) {
  for (var i=0; i < str.length; i++) {
    var ch = str.charAt(i);
    if (ch < '0' || ch > '9')
      return false;
  }
  return true;  
}

function isWhitespace (ch) {
  if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' ||
      ch == '\v' || ch == '\b')
    return true;
  return false;
}
function isDelimiter (ch) {
  if (ch == ',' || ch == '?' || ch == '-' || ch == '.' ||
      ch == '\\' || ch == '/')
    return true;
  return false;
}

// *** Note on stop words ***
// *** Note on false matches vs. no matches ***
// *** Note on problem with non-English languages *** (escape and rules)
function normalizeWord (keyword) {
  var esc = escape (keyword.toLowerCase());
  var kw = "";
  for (var i=0; i < esc.length; i++) {
    var ch = esc.charAt(i);
    if (ch == '%')
      i += 2;
    else
      kw += ch;
  }
  var len = kw.length;
  if (kw.charAt(len-1) == "s" && kw.charAt(len-2) != "s") {
    kw = kw.substring(0,len-1);
    len--;
  }
  if (kw.substring(len-2,len) == "ly") {
    kw = kw.substring(0,len-2);
    len -= 2;
  }
  if (kw.substring(len-2,len) == "ed") {
    kw = kw.substring(0,len-1);
    len--;
  }
  if (kw.substring(len-2,len) == "er") {
    kw = kw.substring(0,len-1);
    len--;
  }
  if (kw.substring(len-2,len) == "ie") {
    kw = kw.substring(0,len-2) + "y";
    len--;
  }
  if (kw.substring(len-3,len) == "ing" && len > 5) {
    kw = kw.substring(0,len-3);
    len -= 3;
    if (isVowel(kw.charAt(len-2)) && !isVowel(kw.charAt(len-3))) {
      kw += "e";
      len++;
    }
  }
  if (kw.charAt(len-1) == "e")
    if (!isVowel(kw.charAt(len-3))) {
      kw = kw.substring(0,len-1);
      len--;
    }
  if (len > 1 && (kw.charAt(len-1) == kw.charAt(len-2))) {
    kw = kw.substring(0,len-1);
    len--;
  }
  return kw;
}

function KeywordList () {
  this.count = 0;
  this.word = new Object ();
  this.add = AddKeyword;
  return this;
}
function AddKeyword (word) {
  for (var i = 0; i < this.count; i++)
    if (this.word[i] == word)
      return;
  this.word[this.count++] = word;
}
function Result (object, score) {
  this.object = object;
  this.score = score;
  return this;
}
function ResultList () {
  this.count = 0;
  this.item = new Object();
  this.add = AddResult;
  this.sort = SortResults;
  return this;
}
function AddResult (object) {
  for (var i = 0; i < this.count; i++)
    if (this.item[i].object == object) {
      this.item[i].score++;
      return;
    }
  this.item[this.count++] = new Result (object,1);
}
function SortResults () {
  pm_array_qsort (this.item,0,this.count - 1,CompareResults);
}
function CompareResults (a,b) {
  return (a.score == b.score) ? 0 : (a.score <  b.score) ? 1 : -1;
}
function IndexItem (object, keyword) {
  this.object = object;
  this.keyword = keyword;
  return this;
}
function IndexItemList () {
  this.count = 0;
  this.item = new Object();
  this.add = AddIndexItem;
  return this;
}
function AddIndexItem (object) {
  this.item[this.count++] = object;
}
function Index () {
  this.count = 0;
  this.item = new Object();
  this.add = AddToIndex;
  this.find = SearchIndex;
  return this;
}
function AddToIndex (object, keywords) {
  for (var i = 0; i < keywords.count; i++) {
    var kw = keywords.word[i];
    var ilist = this.item[kw];
    if (ilist == null) {
      ilist = new IndexItemList();
      this.item[kw] = ilist;
    }
    ilist.add(object);
  }
}
function SearchIndex (keywords) {
  var rlist = new ResultList();
  for (var i = 0; i < keywords.count; i++) {
    var kw = keywords.word[i];
    var ilist = this.item[kw];
    if (ilist != null)
      for (var j = 0; j < ilist.count; j++)
        rlist.add(ilist.item[j]);
  }
  rlist.sort();
  return rlist;
}

function parseKeywords (str) {
  var list = new KeywordList ();
  var inword = false;
  var word = "";
  var len = str.length;
  for (var i = 0; i < len; i++) {
    var ch = str.charAt(i);
    if (isWhitespace(ch) || isDelimiter(ch)) {
      if (inword) {
        if (!isStopword(word))
          list.add(normalizeWord(word));
        word = "";
        inword = false;
      }
    }
    else {
      word += ch;
      inword = true;
    }
    if (i + 1 == len && inword)
      if (!isStopword(word))
        list.add(normalizeWord(word));
  }
  return list;
}


function Book (author, title, subject, code, price) {
  this.author = author;
  this.title = title;
  this.subject = subject;
  this.code = code;
  this.price = price;
  return this;
}

function Catalog () {
  this.count = 0;
  this.book = new Object;
  this.author = new Index();
  this.title = new Index();
  this.subject = new Index();
  this.add = AddToCatalog;
  return this;
}
function AddToCatalog (book) {
  this.book[this.count++] = book;
  this.author.add(book,parseKeywords(book.author));
  this.title.add(book,parseKeywords(book.title));
  this.subject.add(book,parseKeywords(book.subject));  
}


var cat = new Catalog();
cat.add (new Book ("Kingsolver, Barbara", "Animal Dreams",
  "fiction animals dreams environment Native-American love",
  "ISBN 0-06-092114-5", "13.00"));
cat.add (new Book ("Calasso, Roberto", "The Marriage of Cadmus and Harmony",
  "fiction greek myth mythology Zeus Athens",
  "ISBN 0-679-73348-5", "13.00"));
cat.add (new Book ("Le Carre, John", "The Night Manager",
  "fiction suspense spy arms drugs",
  "ISBN 0-345-38576-4", "6.99"));
cat.add (new Book ("Rice, Anne", "Interview with the Vampire",
  "fiction vampire New Orleans gothic horror",
  "ISBN 0-345-33766-2", "4.95"));
cat.add (new Book ("Garcia Marquez, Gabriel", "One Hundred Years of Solitude",
  "fiction South America magic dreams war love",
  "ISBN 0-06-091965-5", "13.00"));
cat.add (new Book ("Barkakati, Naba", "Object-Oriented Programming in C++",
  "nonfiction computer language programming object C",
  "ISBN 0-672-22800-9", "29.95"));
cat.add (new Book ("Petzold, Charles", "Programming Windows",
  "nonfiction computer programming C windows",
  "ISBN 1-55615-264-7", "29.95"));


var results = null;

function doSearch () {
  var index = self.control.document.cont.stype.selectedIndex;
  var keywords = parseKeywords (self.control.document.cont.keywords.value);
  if (index == 0)
    results = cat.title.find (keywords);
  else if (index == 1)
    results = cat.author.find (keywords);
  else
    results = cat.subject.find (keywords);
  self.show.location = "javascript:parent.showList()";
}

function showBook (item) {
  var book = results.item[item].object;
  var detail = book.author + '<br>' + book.title + '<br>' +
    book.subject + '<br>' + book.code + '<br>$' + book.price + '<br>' +
    '<h3><a href="javascript:parent.showList()">Return to list</h3>';
  return '<html><body bgcolor="#FFFFFF" text="#000000" link="#0000FF" ' +
    'alink="#FF0000"><div align="center"><table border=0><tr><td>' +
    detail + '</td></tr></table></body></html>';
}

function showList () {
  var list = "";
  for (var i = 0; i < results.count; i++)
    list += '<a href="javascript:parent.showBook(' + i + ')">' +
      '(' + results.item[i].score + ')&nbsp&nbsp' +
      results.item[i].object.author + ':&nbsp&nbsp' +
      results.item[i].object.title + '</a><br>';
  if (list.length == 0)
    list = '<h2 align="center">Sorry, no matches found</h2>';
  return '<html><body bgcolor="#FFFFFF" text="#000000" link="#0000FF" ' +
    'alink="#FF0000"><div align="center"><table border=0><tr><td>' +
    list + '</td></tr></table></body></html>';
}

var headFrame =
  '<html><body bgcolor="#500010" text="#FFFFFF">' +
  '<h1 align="center">JS-Books</h1>' +
  '</body></html>';

var welcomeFrame =
  '<html><body bgcolor="#FFFFFF" text="#0000FF">' +
  '<h1 align="center">Welcome to JS-Books!</h1>' +
  '<h3 align="center">Select a search by title, subject or author ' +
  'below.<br> Enter keywords, and then press the Search button</h3>' +
  '</body></html>';

var controlFrame =
  '<html><body bgcolor="#808080" text="#FFFFFF">' +
  '<form name="cont">' +
  '<table border=0 width=100% height=100% cellpadding=0 cellspacing=0>' +
  '<tr align="center" valign="center">' +
  '<td><b>Search by: </b><select name="stype">' +
  '<option selected>Title' +
  '<option>Author' +
  '<option>Subject' +
  '</select></td>' +
  '<td><b>Keywords: </b><input size=30 name="keywords"></td>' +
  '<td><input type="button" value="Search" onclick="parent.doSearch()"></td>' +
  '</tr></table>' +
  '</form>' +
  '</body></html>';

var emptyFrame = '<html></html>';

function initialize() {
  self.head.location = "javascript:parent.headFrame";
  self.show.location  = "javascript:parent.welcomeFrame";
  self.control.location = "javascript:parent.controlFrame";
}
// end script -->
</script>
</head>
<frameset rows="52,*,100" onLoad="initialize()">
  <frame name="head" src="javascript:parent.emptyFrame" scrolling="no" noresize
     marginwidth=1 marginheight=1>
  <frame name="show" src="javascript:parent.emptyFrame" noresize
     marginwidth=1 marginheight=1>
  <frame name="control" src="javascript:parent.emptyFrame" scrolling="no" noresize
     marginwidth=1 marginheight=1>
</frameset>
