/*
 *  ---------------  TABLE OF CONTENTS ---------------
 *
 * INITIALIZATION
 *
 * STATUS BAR
 * STATUS BAR - REVIEW AND ADD BARS
 * STATUS BAR - TIMES
 * STATUS BAR - VOLUME
 *
 * PROMPT
 * PROMPT - TEST
 * PROMPT - BUFFER
 * PROMPT - BUILD
 * PROMPT - FRONT SETTERS
 * PROMPT - CLEAR
 * PROMPT - CHAR PROMPT
 * PROMPT - LOOKUP BOXES
 * PROMPT - MISC INTERNAL
 * PROMPT - ACTIONS
 *
 * LIGHT BOXES
 * LIGHT BOXES - ACTIVE LISTS
 * LIGHT BOXES - SETTINGS
 * LIGHT BOXES - FEEDBACK
 * LIGHT BOXES - CRAM
 *
 * ERROR MESSAGES
 *
 * UTILITIES
 *
 * NICK'S FLASH STUFF
 *
 * NICK'S - STARTUP
 * NICK'S - KEYBOARD
 * NICK'S - MOUSE
 *
 */


BLANKS = false;

// --------~-------- INITIALIZATION

// AJAX data caches
var addable_cache;
var containing_vocab_cache;



$(document).ready(function()
  {
    // init the caches
    addable_cache = new DataCache(20);
    containing_vocab_cache = new DataCache(20);

    // for hiding options for other language
    $('#id_lang').change(changeLang);
    changeLang();

    // make the sliders
    new SliderWidget($("#anim_speed_bar"),
		     parseFloat($("#id_anim_speed").val()),
		     0.05,animSpeedCallback);
    new SliderWidget($("#order_weight_bar"),
		     parseFloat($("#id_order_weight").val()),
		     0.05,orderWeightCallback);
    volume_slider = new SliderWidget($("#volume_bar"),
		      init_volume, 0.05,volumeCallback);
    makeAutoManualVisualChanges(); // do css for active lists box
    setVolumeSpriteBackground();   // init volume

    // just for until we enable reading/defn practice
    $('.disable_me input').attr('disabled',true);
    $('.disable_me').css('color','#aaa');
    // if you sneak around this, dear user, you will only break your account!

    empty_prompt = getPrompt().clone().removeClass('hidden');

    var cram_title = $('#cram_title');
    if (cram_title.length==1) makeIntoOneLine(cram_title[0]);

    $('#id_cram_0').click(function() { setCramOptionsDisabled(true); });
    $('#id_cram_1').click(function() { setCramOptionsDisabled(false); });

    //testAnimate();
  });


function setCramOptionsDisabled(disabled) {
  var cb  = $('#cram_box');
  $('div.to_disable input, div.to_disable select',cb).attr('disabled',disabled);
}



// --------~-------- STATUS BAR

// --------~-------- STATUS BAR - REVIEW AND ADD BARS

function setReviewBar(num)
{
  if (BLANKS) return;
  if (combostodo > 0) return;    // while refreshing, ignore update from Flash
  var fraction = num / 100.0;

  fraction = Math.max(0.0, Math.min(1.0, fraction));
  var full_width = parseInt($('#to_review_bar').width());
  var width = Math.round(fraction * full_width);
  if (width > 10) {
    $('#review_fore_bar').css("width", width - 10);
    $('#review_fore_end').css({width: 10, left: -10});
  }
  else {
    $('#review_fore_bar').css("width", 0);
    $('#review_fore_end').css({width: width, left: 0-width});
  }
  if (num > 9000) {
    $('#to_review_bar').attr("title", "Too many to count!");
    $('#to_review_bar span').text("over 9000!!");
  }
  else {
    $('#to_review_bar').attr("title",
      (num > 0 ? num : "No") + " items due for review");
    $('#to_review_bar span').text(num + " to review");
  }

}


function setAddedBar(num)
{
  if (BLANKS) return;
  var fraction = num / (added_div*20.0);

  fraction = Math.max(0.0, Math.min(1.0, fraction));
  var full_width = parseInt($('#added_bar').width());
  var width = Math.round(fraction * 125);
  if (width > 10) {
    $('#added_fore_bar').css("width", width - 10);
    $('#added_fore_end').css({width: 10, right: -10});
  }
  else {
    $('#added_fore_bar').css("width", 0);
    $('#added_fore_end').css({width: width, right: 0-width});
  }
  $('#added_bar').attr("title", num + " new items added today");
  $('#added_bar span').text("added " + num);
}



// getReviewValue uses AJAX to fetch the current review bar value
var combostodo = 0;
var to_review_total = 0;
function getReviewValue(partstyles)
{
  combostodo = 0;
  to_review_total = 0;
  for (var i=0; i<partstyles.length; i++) {
    loadSingleWordCount(partstyles[i].part, partstyles[i].style);
    combostodo += 1;
  }
}

function loadSingleWordCount(part, style, delay)
{
  if (!delay) delay = 200;
  else delay *= 2;
  $.get('/getnumtodo', {part: part, style: style}, function(response)
  {
    if (response == "timeout")
      setTimeout("loadSingleWordCount('"+part+"','"+style+"',"+delay+");",
		 delay);
    else {
      to_review_total += parseInt(response);
      if (--combostodo == 0) skritter.setToReview(to_review_total);
    }
  });
}


function manuallyAddWord() {
  if (study_mode=="demo") return;
  skritter.addWord();
}


function setAddWordButton(state) {
  if (BLANKS) return;
  $('#add_word_button').toggleClass("enabled", state)
    .toggleClass("disabled", !state)
    .toggleClass("clickable", state);
}



// --------~-------- STATUS BAR - TIMES

function setTimes(sessionTime, promptTime)
{
  // up to 2x faster than .text()
  $('#session_time')[0].innerHTML = sessionTime;
  $('#prompt_time')[0].innerHTML = promptTime;
}

// --------~-------- STATUS BAR - VOLUME

var volume_prev;
var volume_slider;

function volumeCallback(slider)
{
  saveVolumePref(volume_slider.value);
  setVolumeSpriteBackground();
  if (volume_slider.value == 0.0) {
    setSpeaker('word', 'unavailable');
    setSpeaker('char', 'unavailable');
  }
  skritter.setVolume(volume_slider.value);
  setVolumeTooltip(volume_slider.value);
}

function getIntVolume()
{
  var volume = volume_slider.value;
  var int_volume = parseInt(3*(volume+0.2));
  if (volume > 0.0) int_volume = Math.max(1,int_volume);
  if (volume < 1.0) int_volume = Math.min(2,int_volume);
  return int_volume;
}

function toggleMuteVolume() {
  var volume_prev = volume_slider.prev_value;
  if (volume_slider.value == 0) {
    if (volume_prev > 0) volume_slider.setValue(volume_prev);
    else volume_slider.setValue(0.5);
  }
  else
    volume_slider.setValue(0.0);
  setVolumeSpriteBackground();
  setVolumeTooltip(volume_slider.value);
  skritter.setVolume(volume_slider.value);
  saveVolumePref(volume_slider.value);
}

function muteVolume()
{
  volume_slider.setValue(0.0);
  setVolumeTooltip(0);
  setSpeaker('word', 'unavailable');
  setSpeaker('char', 'unavailable');
  debug.warn("Sound is not functioning, turning volume off.");
}

var volumeHighlight = false;
function highlightVolumeSprite(over)
{
  volumeHighlight = over;
  setVolumeSpriteBackground();
}

function setVolumeSpriteBackground()
{
  var int_vol = getIntVolume();
  var ofs = (volumeHighlight ? "0 " : "-18px ") + "-"+(int_vol*14)+"px";
  $('#volume_sprite').css("backgroundPosition", ofs).removeClass('hidden');;
}

function setVolumeTooltip(vol)
{
  $('#volume_control').attr("title", vol ? parseInt(100*vol) + "%" : "muted");
}

function saying(on)
{
  volumeHighlight = on;
  setVolumeSpriteBackground();
}

function saveVolumePref(pref) { savePref('volume', {volume: pref}); }



// --------~-------- PROMPT

var part_studying;

// --------~-------- PROMPT - TEST


function fillWithSampleText()
{
  addMarkedText('あ','word','rune');
  addMarkedText('い','word','rune');
  addMarkedText('う','word','rune');

  addMarkedText('Augh','word','rdng');
  addFillerText('; ','word','rdng');
  addMarkedText('bègl','word','rdng',2);

  addMarkedText('defn here','word','defn');

  addMarkedText('あ','char','rune');
  addMarkedText('chur','char','rdng');
  addMarkedText('It all comes down to who does the dishes.','char','defn');

  addFillerText('中国','lookup_word','rune');
  setLookupTrad('-機','word');
  addMarkedText('Augh','lookup_word','rdng');
  addFillerText('; ','lookup_word','rdng');
  addMarkedText('bègl','lookup_word','rdng',2);
  addFillerText('defn here','lookup_word','defn');
  addRelatedVocab('中', '', 'ちゅう','middlerune', 'zh-中-0', 'word');
  addRelatedVocab('国', '機','ごく','country', 'zh-国-0', 'word');

  addFillerText('国','lookup_char','rune');
  setLookupTrad('機','char');
  addMarkedText('jerbs','lookup_char','rdng',1);
  addFillerText('defn there','lookup_char','defn');
  addRelatedVocab('外国人', '', 'がいこくじん','foreigner', 'ja-外国人-0', 'char');
  addRelatedVocab('韓国', '-機','かんこく','North Korea', 'ja-韓国-0', 'char');
}

function testAnimate(animate) {
  initBackBuffer();
  clearEntirePrompt();
  fillWithSampleText();
  jaRuneInit(1);
  flip(animate);
  setScore(2,'rune',true);
}




// --------~-------- PROMPT - BUFFER

var back_buffer;
var current_prompt;
var value_dict = {};
var clone_sample;
var buffer_counter = 0;

function getPrompt() {
  //return (back_buffer == undefined) ? $('.prompt') : back_buffer;
  if (current_prompt == undefined) current_prompt = $('.prompt');
  return (back_buffer == undefined) ? current_prompt : back_buffer;
}

function initBackBuffer() {
  if (BLANKS) return;
  index_tracker = {};
  back_buffer = $('.prompt').clone();
  back_buffer.removeClass('hidden');
  buffer_counter += 1;
  fillPromptValues();
  clearPromptMostly();
}

function flip(animate) {
  if (BLANKS) return;
  animate = false;
  var speed = (animate) ? 300: 0;

  var prompt = $('.prompt');
  if (speed>0) {
    prompt.css('position','absolute');
    back_buffer.css('position','absolute');

    var width = parseInt(prompt.css('width'));

    // It's possible to trigger an infinite loop here
    prompt.after(back_buffer);

    prompt.addClass('to_delete');
    back_buffer.css('left',width);

    prompt.animate({'left':-width},speed);
    back_buffer.animate({'left':0},speed);
    var new_prompt = back_buffer;
    setTimeout(function() { $('.to_delete').remove();
			    new_prompt.css('position','inherit');
			  },speed);
  }
  else {
    prompt.replaceWith(back_buffer);
  }
  current_prompt = back_buffer;
  back_buffer = undefined;

  // need to set the dictionary after onload for some reason
  setDictionary(dict);
}

function getPromptValue(id) {
  if (value_dict[id]==undefined) {
    value_dict[id] = $('#'+id, getPrompt());
    debug.log('HAD TO FETCH VALUE WITH GET PROMPT VALUE! ' + id);
  }
  return value_dict[id];
}

function fillPromptValues() {
  value_dict = {};
  $('.value, .to_cache',getPrompt()).each( function()
  {
    value_dict[this.id] = $(this);
    //debug.log('filled prompt value: '+this.id);
  });
}



// --------~-------- PROMPT - BUILD

// stores the index number each prompt is at for marked spans
var index_tracker = {};

function addFillerText(text, kind, part)
{
  if (BLANKS) return;
  if (kind=='char') showCharacterPrompt();
  var id = kind + '_' + part + '_value';
  getPromptValue(id).append('<span class="filler">'+text+'</span>');
}

function addMarkedText(text, kind, part, spansize)
{
  if (BLANKS) return;
  var prompt = getPrompt();
  if (kind=='char') showCharacterPrompt();
  if (typeof(spansize)=='undefined') spansize = 1;
  var class_name = kind + '_' + part;
  if (typeof(index_tracker[class_name])=="undefined")
    index_tracker[class_name] = 0;

  var markup = $("<span class='outer'><span class='inner'>"
		 +text+"</span></span>");
  for (var i = 0; i<spansize; i++)
    markup.addClass("i"+(i+index_tracker[class_name]));
  if (part == "rdng")
    colorizeTones(markup);
  index_tracker[class_name] += spansize;
  getPromptValue(class_name+'_value').append(markup);
}



// --------~-------- PROMPT - FRONT SETTERS

function setStyle(style, kind) {
  if (BLANKS) return;
  var s = '';
  switch(style) {
  case 'simp': s = 'simp'; break;
  case 'trad': s = 'trad'; break;
  default: s = '';
  }
  var rune = getPromptValue(kind + '_rune');
  if (s=='')
    rune.addClass('no_style').removeClass('simp').removeClass('trad');
  else
    rune.removeClass('no_style').addClass(s);
  if (kind == "word")
    getPromptValue('style_value').text(s);
}

function setReadiness(readiness, tooltip) {
  if (BLANKS) return;
  getPromptValue('readiness_value')
    .html('<span>'+readiness+'</span>')
    .attr('title',tooltip);
  if (study_mode == "standard")
    trackTimeIfNecessary();
}

var SCORE_MESSAGES = ["don't know","forgot","so-so","got it","too easy"];
var SCORE_TOOLTIPS = [
  "You don't know this word yet.", "You forgot this word.",
  "You mostly remembered this word, but it was too hard.",
  "You remembered this word just fine.",
  "This word was way too easy and will be scheduled much later next time."];
var PART_NAMES = {rune: "writing", tone: "tone", rdng: "reading", defn: "def."};

var last_timeout;
var last_nice;

function setScore(score, part, nice) {
  if (BLANKS) return;
  var prompt = getPrompt();
  var class_name = "word_" + (part == "tone" ? "rdng" : part);
  $('.score_msg',prompt).remove();
  var msg_index = nice && score == 1 ? 0 : score;
  var score_msg = $('<span>'+SCORE_MESSAGES[msg_index]+' ('+PART_NAMES[part]+')</span>')
    .addClass('score_msg');
  $('.grader', getPromptValue(class_name))
    .attr('className','grader clickable')
    .addClass('grader'+score)
    .attr('title',SCORE_TOOLTIPS[msg_index] + " (Click to toggle.)")
    .after(score_msg);
  if (last_timeout!=undefined) clearTimeout(last_timeout);
  last_timeout = setTimeout(function()
    { $('.score_msg',prompt).fadeOut(1000); },2000);
  last_nice = nice;
}

function setSpeaker(kind, state) {
  if (BLANKS) return;
  if (volume_slider.value == 0.0 && state != "unavailable") return;
  var speaker = getPromptValue(kind+'_play_button');
  if (state=="playing") speaker.addClass('playing');
  else if (state=="stopped") speaker.removeClass('playing');
  if (state=="unavailable") speaker.hide();
  else speaker.show();
}

function setStar(kind,on) {
  var button = getPromptValue(kind+'_star_button');
  if (on) button.removeClass('offstar');
  else button.addClass('offstar');
}

function setVocabKey(key, kind) {
  if (BLANKS) return;
  getPromptValue(kind+'_vocab_key').val(key);
}


// --------~-------- PROMPT - CLEAR

function clearClasses(elem) {
  for (var i=0; i<P_CLASSES.length; i++)
    $('.outer.'+P_CLASSES[i],elem).removeClass(P_CLASSES[i]);
}

function clearPrompt(kind, part) {
  getPromptValue(kind+'_'+part+'_value').text('');
  index_tracker[class_name] = 0;
}

function clearPromptMostly(skip_some) {
  var prompt = getPrompt();
  if (!skip_some) {
    $('.value.toclear',prompt).each(function()
    {
      if (this.tagName=="INPUT") $(this).val(' ');
      else $(this).text(' ');
    });
  }
  setStar('word',false); setStar('char',false);
  closeCharacterBubble();
  hideCharacterPrompt();
  setStyle('', 'word');
  setStyle('', 'char');
  getPromptValue('char_prompt').attr("style"," ");
  $('.score_msg',prompt).remove();
  closeLookupBox("word", 0);
  closeLookupBox("char", 0);
  clearCharLookupBox();
}

var empty_prompt;

function clearEntirePrompt() {
  if (BLANKS) return;
  if (back_buffer==undefined) $('.prompt').replaceWith(empty_prompt.clone());
  else back_buffer = empty_prompt.clone();
  current_prompt = undefined;
  buffer_counter += 1;
  fillPromptValues();
}


// --------~-------- PROMPT - CHAR PROMPT

function hideCharacterPrompt() {
  getPromptValue('char_prompt').addClass('hidden');
  getPromptValue('word_char_divider').addClass('hidden');
}

function showCharacterPrompt() {
  getPromptValue('char_prompt').removeClass('hidden');
  getPromptValue('word_char_divider').removeClass('hidden');
}

function openCharacterBubble(instant) {
  if (BLANKS) return;
  var prompt = getPrompt();
  var speed = 300;
  var c_b = getPromptValue('char_bubble', prompt);
  if (!c_b.hasClass('clickable')) return;
  c_b.css('min-height',c_b.height())
    .css('min-width',c_b.width())
    .removeClass('clickable');
  var cbt = getPromptValue('char_bubble_text');

  if (!instant) {
    setTimeout(function () { c_b.css('border-color','#f3f3f3'); },speed*.3);
    setTimeout(function () { c_b.css('border-color','#f6f6f6'); },speed*.6);
    setTimeout(function () { c_b.css('border',0).css('width','100%');
			     cbt.css('width','100%');
			   }, speed);
    cbt.show(speed).css('visibility','hidden');
    setTimeout(function () {
      cbt.css('visibility','visible').hide().fadeIn(speed); },speed+10);
  }
  else {
    c_b.css('border',0).css('width','100%');
    cbt.css('width','100%');
    cbt.show();
  }

  getPromptValue('char_bubble_label').hide();
}

function closeCharacterBubble() {
  getPromptValue('char_bubble').addClass('clickable').attr('style',' ');
  getPromptValue('char_bubble_text').attr('style',' ').hide();
  getPromptValue('char_bubble_label',prompt).attr('style',' ').show();
}


function setCharBubbleText(part) {
  if (part=='rdng'||part=='rune'||part=='defn')
    getPromptValue('char_bubble_label')
      .html(getPromptValue('char_'+part+'_value').html());
  else {
    getPromptValue('char_bubble_label').html(part);
  }
}



// --------~-------- PROMPT - LOOKUP BOXES

var lookup_box_states = {"word": false, "char": false};
var lookup_box_animating = {"word": false, "char": false};

function openLookupBox(kind) {
  if (lookup_box_animating[kind] || lookup_box_states[kind]) return;
  lookup_box_animating[kind] = true;
  var speed = 300;
  var prompt_kind = kind+'_prompt';
  var lb = getPromptValue(kind+'_lookup_box').attr('style',' ');
  var height = parseInt(lb.height()) + 30;
  getPromptValue(prompt_kind).animate({minHeight:height},speed);
  lb.hide().animate({height: "show"}, speed);
  toggleInback(lb,false);
  lb.css('z-index',5);
  setTimeout(function() { lookup_box_animating[kind] = false; }, speed);
  getPromptValue(kind+'_lookup_button').addClass('open');

  lookup_box_states[kind] = true;

  setDictionaryLinks();
  addAddButtons(kind);
}

function closeLookupBox(kind, speed) {
  if (speed == null)
    {
      if (lookup_box_animating[kind] || !lookup_box_states[kind]) return;
      lookup_box_animating[kind] = true;
      speed = 300;
    }
  else if (!lookup_box_animating[kind] && !lookup_box_states[kind]) return;
  var prompt_kind = kind+'_prompt';
  var lb = getPromptValue(kind+'_lookup_box').attr('style',' ');
  getPromptValue(prompt_kind).animate({minHeight:0},speed);
  lb.animate({height: "hide"}, speed);
  setTimeout(function() { lb.show().css('z-index',-1);
			  toggleInback(lb, true); }, speed);
  setTimeout(function() { lookup_box_animating[kind] = false; }, speed);
  getPromptValue(kind+'_lookup_button').removeClass('open');

  lookup_box_states[kind] = false;
}

function clearCharLookupBox() {
  $('.value, ul', getPromptValue('char_lookup_box')).text('');
  getPromptValue('char_lookup_button').removeClass('open');
}

function setLookupTrad(text, kind) {
  if (BLANKS) return;
  getPromptValue('lookup_'+kind+'_trad_value').html('<span>['+text+']</span>');
}

function addRelatedVocab(rune, trad, rdng, defn, key, kind) {
  if (BLANKS) return;
  trad = (trad=='') ? '' : '['+trad+']';
  var add_button = $('<div class="add_button hidden"></div>')
    .click(function() { $(this).hide(); addVocab(key); });
  var key_store = $('<input type="hidden"></input>').val(key);
  var li = $('<li>'+rune+' '+trad+' '+rdng+': '+defn+'</li>')
	     .data('key',key)
	     .prepend(add_button)
	     .append(key_store);
  var show_add_button = addable_cache.getValue(key);
  if (show_add_button==undefined) li.addClass('stub');
  else if (show_add_button) $('.add_button',li).removeClass('hidden');
  getPromptValue(kind+'_related_vocab').append(li);
}

function addVocab(key) {
  addable_cache.setValue(key,false);
  $.retried_post('vocab/add',{ key:key }, function() { ; });
}

function setDictionaryLinks() {
  $('.lookup_box',getPrompt()).each(function()
  {
    var rune = $('.rune',$(this)).text();
    var url = '';
    if (dict=='mdbg')
      url = "http://www.mdbg.net/chindict/chindict.php?page=worddict&wdrst=0&wdqb=";//http://us.mdbg.net/chindict/chindict.php?page=worddictbasic&wdqb=";
    else if (dict=='nciku') url = "http://www.nciku.com/search/all/";
    else if (dict=='dictcn') url = "http://dict.cn/";
    else if (dict=='yellowbridge') {
      var key = $('#word_vocab_key').val();
      var mode = (key[key.length-1]!='0') ? 't' : 's';
      url = "http://www.yellowbridge.com/chinese/wordsearch.php";
      url += "?searchMode=C&characterMode="+mode+"&word=";
    }
    else if (dict=='iciba') url = "http://www.iciba.com/";
    else if (dict=='reganmian') url = "http://reganmian.net/en-zh/";
    else if (dict=='jisho') url = "http://jisho.org/words?eng=&dict=edict&jap=";
    else if (dict=='tangorin') url = "http://tangorin.com/words/";
    else if (dict=='goo') url = "http://dictionary.goo.ne.jp/srch/je/";
    else if (dict=='nihongodict') url = "http://www.nihongodict.com/w/27885/";
    else if (dict=='wwwjdic') url = "http://www.csse.monash.edu.au/~jwb/cgi-bin/wwwjdic.cgi?1MUJ";
    // hmm, how can I best selectively do single-character-only dictionaries?
    // else if (dict=='chineseetymology') url="http://www.chineseetymology.org/"
    //   + "CharacterASP/CharacterEtymology.aspx?characterInput=";
    url += encodeURI(rune);
    if (dict=='goo') url += "/m0u/";
    else if (dict=='nihongodict') url += '/';
    $('a.dict_link',$(this)).attr('href',url).attr('target', dict);
  });
}

function setDictionary(new_dict,save) {
  dict = new_dict;
  $(".lookup_box select",getPrompt()).val(dict);
  setDictionaryLinks();
  if (save) saveDictionary(dict);
}

function addAddButtons(kind) {
  var keys = [];
  var stubs = $('li.stub', getPromptValue(kind+'_related_vocab'));
  if (stubs.length==0) return;
  stubs.each(function() { keys.push($('input',$(this)).val()); });

  $.retried_get('checkvocabenabled',{keys:keys}, function(response)
  {
    var addable_keys = response;
    stubs.each(function()
    {
      var elem = $(this);
      var key = $('input',elem).val();
      var addable = addable_keys.indexOf(key)>-1;
      if (addable)
	$('div',elem)
	  .removeClass('hidden')
	  .click(function() { addVocab(key); $(this).hide(); });
      addable_cache.setValue(key,addable);
      elem.removeClass('stub');

    });
  });
}

function saveDictionary(pref) {
  var key = 'dict_'+lang;
  var prefs = {};
  prefs[key] = pref;
  savePref(key, prefs);
}

function setListSect(list, sect) {
  if (sect) list += ':';
  getPromptValue('list_value').text(list);
  getPromptValue('sect_value').text(sect);
  getPromptValue('list_sect').show().removeClass('hidden');
}

function deleteVocab(button) {
  var vocab = getPromptValue("lookup_word_rune_value").text();
  var key = getPromptValue('word_vocab_key').val();
  if (!confirm('Are you sure you want to remove '+vocab+' from further study?'))
    return;

  $(button).attr('disabled',true);
  $.retried_post('/vocab/delete',{key:key},function() { });
  skritter.deleteWord();
}



// --------~-------- PROMPT - MISC ITERNAL

var P_CLASSES = [
  'target_hide',     // hides the text and underlines it
  'target_show',     // shows the text and underlines it
  'non_target_hide', // hides the text, underlines it less
  'deemphasize',     // sets color to gray
  'hide_complete'    // hides and keeps from using up space
];

var TONES = ['*āáǎà','*ōóǒò','*ēéěè','*īíǐì','*ūúǔù','*ǖǘǚǜ',
	     '*ĀÁǍÀ','*ŌÓǑÒ','*ĒÉĚÈ','*ĪÍǏÌ','*ŪÚǓÙ','*ǕǗǙǛ'];

function colorizeTones(syllable) {
  for (var i=0; i<TONES.length; i++)
    for (var j=0; j<TONES[i].length; j++)
      if (syllable.text().indexOf(TONES[i][j]) > -1)
	{
	  syllable.addClass('tone'+j);
	  return;
	}
}

function toggleScore(part, grader) {
  var class_name = "word_"+part;
  grader = $(grader);
  var score = (grader.hasClass('grader1')) ? 1 :
    (grader.hasClass('grader2')) ? 2:
    (grader.hasClass('grader3')) ? 3: 4;
  score = Math.max((score + 1) % 5, 1);
  setScore(score, part, last_nice);
  skritter.scoreToggled(score, part);
}

function toggleStar(kind) {
  getPromptValue(kind+'_star_button').toggleClass('offstar');
}



// --------~-------- PROMPT - ACTIONS

/* ZH RUNE */

function zhRuneInit(index, hide_rdng) {
  if (BLANKS) return;
  part_studying = 'rune';
  var rune = getPromptValue('word_rune_value');
  clearClasses(rune);
  for (var i = 0; i < index_tracker['word_rune']; i++)
    if (i > index)  $('.i'+i,rune).addClass('non_target_hide');
  $('.i'+index,rune).addClass('target_hide').removeClass('non_target_hide');
  getPromptValue('char_rune_value').addClass('target_hide');

  var rdng = getPromptValue('word_rdng_value');
  clearClasses(rdng);

  for (i = 0; i < index_tracker['word_rdng']; i++)
    if (i != index) $('.i'+i,rdng).addClass('deemphasize');
  var rdng_text = $('.i'+index,rdng).removeClass('deemphasize').text();

  setCharBubbleText(hide_rdng ? "..." : rdng_text);  // mebbe hide bubble pinyin

  if (hide_rdng)  // need to hide the pinyins in prompt
    getPromptValue('word_rdng_value').addClass("obscured");
}

// They clicked on the rdng to show it or they finished the prompt; show the
// hidden rdngs.
function hiddenRdngShow() {
  getPromptValue('word_rdng_value').removeClass('obscured');
}

function zhRuneShow(instant) {
  if (BLANKS) return;
  var prompt = getPrompt();
  $('.target_hide',getPrompt()).removeClass('target_hide');
  openCharacterBubble(instant);
  hiddenRdngShow();
}

function zhRuneHide(index) {
  if (BLANKS) return;
  var rune = getPromptValue('word_rune_value');
  clearClasses(rune);
  for (var i = 0; i < index_tracker['word_rune']; i++)
    if (i > index)  $('.i'+i,rune).addClass('non_target_hide');
}

/* ZH RDNG */

function zhRdngInit(index) {
  var rune = getPromptValue('word_rune_value');
  part_studying = 'rdng';
  clearClasses(rune);
  for (var i = 0; i < index_tracker['word_rune']; i++) {
    if (i != index) $('.i'+i,rune).addClass('deemphasize');
  }

  var rdng = getPromptValue('word_rdng_value');
  clearClasses(rdng);

  rdng.addClass('target_hide');

  //for (i = 0; i < index_tracker['word_rdng']; i++) {
  //  if (i == index)  $('.i'+i,rdng).addClass('target_show');
  //  if (i >= index)  $('.i'+i+' .inner',rdng).text('');
  //}

  $('.outer', getPromptValue('word_defn_value')).addClass('hide_complete');

  setCharBubbleText('rune');
}

function zhRdngUpdate(text) {
  $('.target_show .inner',getPromptValue('word_rdng')).text(text);
}

function zhRdngShow() {
  $('.hide_complete',getPromptValue('word_defn_value'))
    .removeClass('hide_complete');
  getPromptValue('word_rdng_value').removeClass('target_hide');
  openCharacterBubble();
}

function zhAltRdngInit() {
  part_studying = 'rdng';
  clearPrompt('word','rdng');
  addMarkedText('','word','rdng');
  setCharBubbleText('rune');
}

function zhAltRdngShow(rdngs) {
  clearPrompt('word','rdng');
  getPromptValue('word_rdng_value').text(rdngs.join('; '));
  openCharacterBubble();
}


/* ZH DEFN */

function zhDefnInit() {
  part_studying = 'defn';
  $('.outer',getPromptValue('word_defn_value')).addClass('hide_complete');
  $('*',getPromptValue('word_rdng_value')).addClass('hide_complete');
  setCharBubbleText('rune');
}

function zhDefnShow() {
  $('.outer',getPromptValue('word_defn_value')).removeClass('hide_complete');
  $('*',getPromptValue('word_rdng_value')).removeClass('hide_complete');
  openCharacterBubble();
}


/* ZH TONE */

function zhToneInit(index) {
  if (BLANKS) return;
  part_studying = 'tone';
  var rdng = getPromptValue('word_rdng_value');
  clearClasses(rdng);
  getPromptValue('char_rune_value').removeClass('target_hide');

  for (var i = 0; i < index_tracker['word_rdng']; i++) {
    if (i != index) $('.i'+i,rdng).addClass('deemphasize');
  }
  setCharBubbleText('rune');
}


// Not used for char level any more...
function zhToneReplace(index, text, kind) {
  if (BLANKS) return;
  var rdng_value = getPromptValue(kind+'_rdng_value');
  colorizeTones($('.i'+index+' .inner', rdng_value).text(text));
  //openCharacterBubble();
}

function checkRdngConsistency() {
  var in_prompt = getPromptValue("word_rdng_value").text().trim();
  var in_lookup = getPromptValue("lookup_word_rdng_value").text().trim();
  if (in_prompt != in_lookup && false)  // too many of these to handle
    logServerError({message: in_prompt + " not the same as " + in_lookup + "!"
		    + "(user is " + username + ")",
		    fileName: "flashfuncs.js", lineNumber: 975, stack: "Stack!",
		    name: "Dawdling Pinyin Error"});
}


/* JA RUNE */

function jaRuneInit(index, hide_rdng) {
  if (BLANKS) return;
  part_studying = 'rune';
  var rune = getPromptValue('word_rune_value');
  clearClasses(rune);
  for (var i = 0; i < index_tracker['word_rune']; i++)
    if (i > index)  $('.i'+i,rune).addClass('non_target_hide');
  $('.i'+index,rune).addClass('target_hide').removeClass('non_target_hide');
  getPromptValue('char_rune_value').addClass('target_hide');

  var rdng = getPromptValue('word_rdng_value');
  $('.i'+index,rdng).addClass('highlight');

  setCharBubbleText(hide_rdng ? "..." : 'rdng');  // mebbe hide bubble rdng

  if (hide_rdng)  // need to hide the pinyins in prompt
    getPromptValue('word_rdng_value').addClass("obscured");
}
function jaRuneShow(instant) { zhRuneShow(instant); }


/* JA RDNG */

function jaRdngInit() {
  //part_studying = 'rdng';
  $('.filler',getPromptValue('word_rune_value'))
    .addClass('hide_complete')
    .after('<span class="ellipsis">...</span>');
  // do these ones later once the active rdng is in instead
  // clearPrompt('word','rdng');
  // addMarkedText('','word','rdng');
  //$('.outer',getPromptValue('word_rdng_value')).addClass('target_show');
  getPromptValue('word_rdng_value').addClass('hide_complete');
  setCharBubbleText('rune');
}

function jaRdngUpdate(text) { zhRdngUpdate(text); }

function jaRdngShow(answer) {
  var prompt = getPrompt();
  $('.word_rune .ellipsis',prompt).remove();
  getPromptValue('word_rdng_value').removeClass('hide_complete');
  //jaRdngUpdate(answer);  // for when active rdng is in
  openCharacterBubble();
}

/* JA DEFN */

function jaDefnInit() {
  part_studying = 'defn';
  $('.outer',getPromptValue('word_defn_value')).addClass('hide_complete');
  setCharBubbleText('rune');
}

function jaDefnShow() {
  $('.outer',getPromptValue('word_defn_value')).removeClass('hide_complete');
  openCharacterBubble();
}

// --------~-------- LIGHT BOXES

function toggleInback(elem,haveit) {
  if      (haveit==true) elem.addClass('inback');
  else if (haveit==false) elem.removeClass('inback');
  else    elem.toggleClass('inback');

  if ($.browser.msie && parseInt($.browser.version) < 8) {
    var value = (elem.hasClass('inback')) ? 'hidden' : 'visible';
    $('*',elem).each(function ()
    {
      if (!$(this).hasClass('hidden'))
	$(this).css('visibility', value);
    });
  }
}

function toggleActiveListsWindow() {
  if (study_mode=="demo") return;
  if ($('#active_lists_box').hasClass('inback')) closeLightBoxes();
  toggleInback($('#active_lists_box'));
  $('#active_lists_button')
    .toggleClass('unactive_button')
    .toggleClass('active_button');
  concatListInfos();
  checkLightboxes();
}

function toggleSettingsWindow() {
  if (study_mode=="demo") return;
  if ($('#settings_box').hasClass('inback')) closeLightBoxes();
  toggleInback($('#settings_box'));
  $('#settings_button')
    .toggleClass('unactive_button')
    .toggleClass('active_button');
  checkLightboxes();
}

function toggleCramWindow() {
  if (study_mode=="demo") return;
  if ($('#cram_box').hasClass('inback')) closeLightBoxes();
  toggleInback($('#cram_box'));
  $('#cram_button')
    .toggleClass('unactive_button')
    .toggleClass('active_button');
  checkLightboxes();
}

function closeLightBoxes() {
  toggleInback($('.litebox'),true);
  $('.tools_settings_button')
    .addClass('unactive_button')
    .removeClass('active_button');
  closeFeedbackBox();
  checkLightboxes(false);
}

function checkLightboxes(open) {
  if (open == null)
    open = $('#settings_box.inback')
      .add('#active_lists_box.inback')
      .add('#cram_box.inback').length < 3;
  if (open)
    $('body').click(mouseClickCloseBoxes); // enable click off lightboxes
  else
    $('body').unbind('click', mouseClickCloseBoxes);
  if (skritter)
    skritter.giveMacMouse(open);
}

function mouseClickCloseBoxes(e) {
  var elem = e.target;
  try {
    while (! $(elem).hasClass('litebox') &&
	   ! $(elem).hasClass('tools_settings_button') &&
	   elem.id!='feedback_tab')
      elem = elem.parentNode;
  }
  catch (e) { closeLightBoxes(); }
  return;
}


// --------~-------- LIGHT BOXES - ACTIVE LISTS


// press the play or pause button
function toggleUVLPMode(button, params)
{
  if (!$(button).is('.clickable')) return;
  $('div', button.parentNode).toggleClass("enabled")
    .toggleClass("disabled").toggleClass("clickable");
  var play = ($(button).hasClass('playbutton')) ? 'true' : 'false';
  params += "&add="+play;
  $.retried_post("toggleuvlpmode", makeUVLPParams(params),
    function(response) {
      if (response == "Add")
	skritter.setAddable(true);
    });
}

function setUVLPMode(row, enabled) {
  var onbutton = (enabled) ? 'play' : 'pause';
  var offbutton = (enabled) ? 'pause' : 'play';
  $('.'+onbutton+'button',row)
    .addClass('enabled').removeClass('clickable').removeClass('disabled');
  $('.'+offbutton+'button',row)
    .removeClass('enabled').addClass('clickable').addClass('disabled');
}

var counter = 0;
var firstUpdate = true;
var loading = false;
// fetch the latest set of lists through ajax
function updateUVLists()
{
  ++counter;
  $.retried_get("getuvlists", {counter: counter}, function(response)
  {
    var oldClass = $("#uvliststable").attr("class");
    $('#uvliststable').replaceWith(response);
    if (firstUpdate && oldClass != "hidden") {
      concatListInfos();
      firstUpdate = false;
    }
    else
      $('#uvliststable').attr('class', oldClass);
    concatListInfos();
    makeAutoManualVisualChanges();
  });
}

function concatListInfos()
{
  $('#uvliststable span').each(function () { makeIntoOneLine(this); });
}

// called from python to update values
var lastvalues = null;
function updateLists(values)
{
  lastvalues = values;
  var table = $('#uvliststable');
  var rows = $("tr", table);
  if (rows.length != values.length) {
    updateUVLists();
    return;
  }
  for (var i=0; i<values.length; i++) {
    var listnum = values[i][0];
    var pct = values[i][1];
    var row = rows[i];
    if (row.id != "list_"+listnum) {
      updateUVLists();
      return;
    }
    $('div.fore', row).css("width", pct+"%");
    setUVLPMode(row, values[i][2]);
  }
}

function makeAutoManualVisualChanges() {
  var auto = $('#id_add_auto').attr('checked');
  $('#add_words_ul input').attr('disabled',!auto);
}

// for Flash
function hasActiveLists() { return $('.playbutton.enabled').length > 0; }
function hasAnyLists() { return $('.playbutton').length > 0; }

function saveAutoAddWords(pref) {
  savePref('auto_add_words', {auto_add_words: pref});
  makeAutoManualVisualChanges();
  skritter.setAddable(pref);
  skritter.settings({auto_add_words: pref});
}
function saveAutoMoveSect(pref) {
  savePref('auto_move_sect', {auto_move_sect: pref}); }



// --------~-------- LIGHT BOXES - SETTINGS


function setOrAppend(params, name, value) {
  if (params[name]==undefined) params[name] = [value];
  else params[name].push(value);
}

function submitSettings() {
  if ($('#settings_box input[name="zh_parts"]:checked').length==0 ||
      $('#settings_box input[name="ja_parts"]:checked').length==0) {
    alert("You must have at least one part selected for study.");
    return;
  }

  $('#settings_box input[type="button"]')
    .attr('disabled',true)
    .attr('value','Saving');

  var params = {};
  $("input, select", $('#settings_box')).each(function()
    {
      if (this.id.indexOf("id_") == 0) {
	if ($(this).attr("type")=="checkbox") {
	  if ($(this).is(':checked'))
	    setOrAppend(params,$(this).attr('name'),$(this).val());
	}
	else if ($(this).attr("type")=="radio") {
	  if ($(this).attr("checked"))
	    params[$(this).attr('name')] = $(this).val();
	}
	else
	  params[this.id.replace("id_", "")] = this.value;
      }
    });
  $.retried_post("submitsettings", params, function(response)
  {
    $('#settings_box input[type="button"]')
      .attr('disabled',false)
      .attr('value','Save Changes');

    if ($('#id_color_tones').attr('checked'))
      $('#prompt_wrapper').addClass('tones_on');
    else
      $('#prompt_wrapper').removeClass('tones_on');

    if ($('input[name="canvas_right"]:checked').val()=="True") {
      $('#interactive_frame').css('float','right');
      canvas_help_side = 'left'; prompt_help_side = 'right';
    }
    else {
      $('#interactive_frame').css('float','left');
      canvas_help_side = 'right'; prompt_help_side = 'left';
    }

    if (response=="refresh") location.reload(true);
    else {
      closeLightBoxes();
      skritter.settings(eval('('+response+')'));
    }
  });
}

function changeLang() {
  var lang = $('#id_lang').val();
  $('#settings_box .'+lang).show();
  $('#settings_box .'+((lang=='zh')?'ja':'zh')).hide();
}

function animSpeedCallback(slider) {
  $('#id_anim_speed').val(slider.value);
}

function orderWeightCallback(slider) {
  $('#id_order_weight').val(slider.value);
}



// --------~-------- LIGHT BOXES - FEEDBACK

var msgs = {
thanks: "Thank you for your feedback!",
order: "Thanks; we'll investigate this stroke order."};

function showFeedbackBox() {
  toggleInback($('#user_inputs_div').show(),false);
  if (!$.browser.msie || parseInt($.browser.version)>7) $('#screen').show();
  $('#feedback').focus();
}

function closeFeedbackBox() {
  toggleInback($('#user_inputs_div'),true);
  $('#screen').hide();
}

function sendFeedback()
{
  var feedbackBox = $('#feedback');
  var msg = feedbackBox.val();
  if (!skritter) {
    $('#feedback_box p.error').show();
    return;
  }
  if (msg == msgs.thanks || msg == msgs.order)
    {
      feedbackBox.focus();
      return;
    }
  skritter.sendComment(msg, $("#feedback_box input[type=radio]:checked").val());
  feedbackBox.val(msgs.thanks);
  setTimeout(function()
  {
    closeFeedbackBox();
    feedbackBox.val("");
    //$("#feedback_comment").click(); // reset to "other comment""
  }, 2000);
}

function clearThanks()
{
  var feedbackBox = $('#feedback');
  var msg = feedbackBox.val();
  if (msg == msgs.start || msg == msgs.thanks || msg == msgs.order)
    feedbackBox.val('');
}


// --------~-------- LIGHT BOXES - CRAM

function setCramOptionsDisabled(disabled) {
  var cb  = $('#cram_box');
  $('div.to_disable input, div.to_disable select',cb).attr('disabled',disabled);
}


// --------~-------- ERROR MESSAGES

function noWords(isExpired)
{
  $('#no_words').show();
  if (isExpired) $('#is_expired').show();
  $("#skritter_window").css("opacity", .1);
}

function flashVersionWarning(version)
{
  var warning = $('#flash_version_warning');
  $('span',warning.show()).text(version);
  warning.fadeOut(10000);
}

function siteDisabled()
{
  top.location.href = '/disabled';
}




// --------~-------- UTILITIES


function savePref(pool, prefs, delay)
{
  if (username == "anonymous") return;
  if (!delay) delay = 2000;
  $.delayed_ajax({type: "POST", url: "/save_pref", data: prefs,
		  request_type: pool, delay: delay});
}


// http://www.vanyli.net/?p=3
// could extend this to retry timeouts with a failure func, too
$.delayed_ajax = function(options) {
  options.tstamp = (new Date().getTime() + new Date().getMilliseconds());
  options.success_original = options.success;
  options.success = function(data, status) {
    var last = $('body').data('ajax_pool' + options.request_type);
    if (last.tstamp == options.tstamp && options.success_original)
      options.success_original(data, status);
  };

  $('body').data('ajax_pool' + options.request_type, options);

  if ($('body').data('timeout_pool' + options.request_type))
    clearTimeout($('body').data('timeout_pool' + options.request_type));

  $('body').data('timeout_pool' + options.request_type,
		 setTimeout("do_request('" + options.request_type + "')",
                 options.delay));

  do_request = function(request_type) {
    $.ajax($('body').data('ajax_pool' + request_type));
  };
};




/*
 * Useful little object that can be used to retain a limited amount of
 * data. It will store up to size number of objects then begin deleting
 * them based on when it was last put in or accessed. Both put and get
 * are O(n), so it's not particularly efficient.
 */
function DataCache(size) {
  this.dict = {};
  this.queue = [];

  this.getValue = function(key) {
    if (this.dict[key]!=undefined) this.moveToEndOfLine(key);
    return this.dict[key];
  };

  this.moveToEndOfLine = function(key) {
    // need to make sure it's not in the line first
    if (this.dict[key]!=undefined) {
      for (var i=0; i<this.queue.length; i++)
	if (this.queue[i]==key) {
	  this.queue.splice(i,1);
	  break;
	}
    }
    this.queue.push(key);
  };

  this.setValue = function(key, value) {
    this.moveToEndOfLine(key);
    this.dict[key] = value;
    this.trim();
  };

  this.trim = function() {
    while (this.queue.length > size)
      delete this.dict[this.queue.shift()];
  };
}


/* Sliders */

function SliderWidget(div, value, snap_radius, callback) {
  this.value = value;
  this.snap_radius = snap_radius;

  /* setup */

  this.bar = $('<div class="slider_bar"></div>');
  this.knob = $('<div class="slider_knob"></div>');
  div.replaceWith(this.bar.append(this.knob));

  var width = parseInt(this.bar.width());

  var area = $('<div class="slider_select_area"></div>');
  area.css('width',width);
  this.bar.append(area);
  this.value_max = width - 1;

  this.bar.append($('<div class="slider_marker"></div>').css('left',width/2));


  /* functions */

  this.setValue = function(value) {
    this.prev_value = this.value;
    if (Math.abs(value-0.5)<this.snap_radius) value = 0.5;
    if (Math.abs(value-1.0)<this.snap_radius) value = 1.0;
    if (Math.abs(value-0.0)<this.snap_radius) value = 0.0;
    slider.value = value;
    slider.moveKnob();
  };

  this.moveKnob = function() {
    this.knob.css('left',parseInt(this.value * width));
  };
  this.moveKnob();

  /* event handlers */

  var slider = this;

  this.bar.mousedown(function(e)
  {
    var elem = $(e.target);
    if (!elem.hasClass('slider_select_area')) return;
    var x = e.pageX-elem.offset().left;
    slider.setValue(x/slider.value_max);
    slider.bar.mousemove(slider.mousemove);
    $("body").mouseup(function(e) {
      callback(slider);
      $("body").unbind("mouseup", arguments.callee);
      slider.bar.unbind("mousemove", slider.mousemove);
    });
  });

  this.mousemove = function(e)
  {
    var elem = $(e.target);
    if (!elem.hasClass('slider_select_area')) return;
    var x = e.pageX-elem.offset().left;
    slider.setValue(x/slider.value_max);
  };

}



// --------~-------- NICK'S FLASH STUFF

var skritter;
var wacom = false;

var skritterFocused = false;
var absorbKeys = false;
var SHIFT = false;
var CTRL = false;
var ALT = false;


// --------~-------- NICK'S - STARTUP
function setup()
{
  skritter = document.skritter || window.skritter;

  $(document).keydown(browserKeyDown);
  $(document).keyup(browserKeyUp);
  $(document).mouseup(mouseUp);
  //$(document).mousemove(mouseMove);
  if(window.addEventListener)  // Firefox
    window.addEventListener("mousemove", mouseMove, true);
  else
    window.onmousemove = document.onmousemove = mouseMove;

  $('#interactive_frame :first').focusin(function() {
    skritterFocused = true;
    skritter.focused(1);
  });
  $('#interactive_frame :first').focusout(function() {
    skritterFocused = false;
    skritter.focused(0);
  });

  if(use_wacom)
    {
      // ------------ IE --//------------------- FireFox --//
      wacom = window.Wacom || document.embeds["wacom-plugin"];
      if(!wacom || !wacom.isWacom)
	{
	  wacom = false;
	  $("#wacom_plugin_warning").show();
	}
    }

  if(username != "anonymous")
    updateUVLists();
}



// --------~-------- NICK'S - KEYBOARD

// try to grab focus for typing stuff
function focusFlash()
{
  if(!skritterFocused && $.browser.msie)  // only works in IE
    skritter.focus();
}

var z = false;
var r = false;
var zr = false;

function browserKeyDown(evt)
{
  //debug.log("browserKeyDown", evt.keyCode);
  if(absorbKeys)
    return;
  switch(evt.keyCode)
  {
  case 16:
    SHIFT = true;
    break;
  case 17:
    CTRL = true;
    //debug.log("CTRL pressed");
    break;
  case 18:
    ALT = true;
    break;
  case 32: // space
    if(!skritterFocused)
      preventDefault(evt);
    break;
  case 82: //Keyboard.R:
    if(zr && z)
      skritter.roll();
    zr = z || zr;
    r = true;
    setTimeout("z = r = zr = false;", 1500);
    break;
  case 90: //Keyboard.Z:
    if(zr && r)
      skritter.roll();
    zr = r || zr;
    z = true;
    setTimeout("z = r = zr = false;", 1500);
  }
  // if Flash won't get the keypress, pass it on
  if(!skritterFocused)
    skritter.keypress(evt.keyCode, true, CTRL, ALT, SHIFT);
}

function browserKeyUp(evt)
{
  //debug.log("browserKeyUp", evt.keyCode);
  if(absorbKeys)
    return;
  switch(evt.keyCode)
  {
  case 16:
    SHIFT = false;
    break;
  case 17:
    CTRL = false;
    //debug.log("CTRL unpressed");
    break;
  case 18:
    ALT = false;
    break;
  case 32: // space
  // case 45: // ins  (# 0)
  // case 35: // end  (# 1)
  // case 40: // down (# 2)
  // case 34: // pgdn (# 3)
  // case 37: // left (# 4)
  // case 12: // ??   (# 5)
    if(!skritterFocused)
      preventDefault(evt);
    break;
  case 82: //Keyboard.R:
    r = false;
    break;
  case 90: //Keyboard.Z:
    z = false;
    break;
  }
  // if Flash won't get the keypress, pass it on
  if(!skritterFocused)
    skritter.keypress(evt.keyCode, false, CTRL, ALT, SHIFT);
}

function preventDefault(evt)
{
  if(evt.preventDefault)
    evt.preventDefault();
  else
    evt.returnValue = false;
}


// --------~-------- NICK'S - MOUSE

function mouseUp(evt)
{
  //var ofs = $(skritter).offset();
  //console.log("x:", evt.pageX - ofs.left, "y:", evt.pageY - ofs.top);
  if(skritter && evt.button != 2)
    setTimeout("skritter.browserMouseUp()",20); //give real clicks precedence
}

var sk_ofs;
var last_pressure = -1;
function mouseMove(evt)
{
  // recalculate sk_ofs every once a while, but mostly cache it
  if(Math.random() < .01 || sk_ofs == undefined)
    sk_ofs = $(skritter).offset();

  if(!evt)  // IE, fix evt.pageX and evt.pageY
    {
      evt = window.event;
      var doc = document.documentElement, body = document.body;
      evt.pageX = evt.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
      evt.pageY = evt.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
    }

  if(skritter)
    {
      skritter.browserMouseMove(evt.pageX - sk_ofs.left, evt.pageY -sk_ofs.top);
      if(wacom)
	{
	  var p = wacom.pressure;
	  if(p != last_pressure)
	    skritter.setPressure(p);
	  last_pressure = p;
	}
    }
}


var TimeTracker = {
  setBuckets: function(b) { this.bucket = b; },
  bucket: [100, 500, 1500, 2500, 5000],
  getTimeDiff: function() { return (this.endTime - this.startTime); },
  recordStartTime: function(opt_time) {
    this.startTime = opt_time || (new Date()).getTime();
  },
  recordEndTime: function(opt_time) {
    this.endTime = opt_time || (new Date()).getTime();
  },
  track: function(tracker, opt_event_obj_name, opt_event_label) {
    var eventTracker = tracker._createEventTracker(
      opt_event_obj_name || "TimeTracker");
    var bucketString;
    var t = this.getTimeDiff();
    for(var i = 0; i < this.bucket.length; i++) {
      if(t < this.bucket[i]) {
	if(i == 0) {
          bucketString = "0-" + (this.bucket[0]);
          break;
	} else {
          bucketString = this.bucket[i - 1] + "-" + (this.bucket[i] - 1);
          break;
	}
      }
    }
    if(!bucketString)
      bucketString = this.bucket[i - 1] + "+";
    eventTracker._trackEvent(bucketString, opt_event_label, t);
  }
};

var pageLoadComplete = false;
function trackTimeIfNecessary()
{
  try {
    if(TimeTracker && !pageLoadComplete) {
      TimeTracker.recordEndTime();
      debug.log("practice page load time: "+TimeTracker.getTimeDiff()+"ms");
      TimeTracker.track(pageTracker, "TimeTracker", "Practice Page Load");
    }
  }
  catch(err) {};
  pageLoadComplete = true;
}
