/*jsl:option explicit*/

if (!window.Ext) {
  if (navigator.userAgent.toLowerCase().indexOf('safari/12') != -1) {
    alert('Safari 1.2 does not work with GAMENAME.\n\n'+
          'If you can, upgrade to a newer version, or try FireFox.\n'+
          'I am trying to support this version, but it may take a while.');
  } else {
  alert('Code loading failed.  Please try these steps:\n\n'+
        '1. Reload this page, twice.\n'+
        "2. Empty your browser's cache.\n"+
        "3. If you use Norton or McAfee protection software, try this:\n"+
        "   Close your browser window(s), turn the software completely off,\n"+
        "   re-open your browser, and return to this site.\n"+
        "   If that works, you need to check your sofware's\n"+
        "   documentation to learn how to keep it from blocking\n"+
        "   this site.\n\n"+
        "If all that fails, let me know at evan@4-am.com");
  }
}

Ext.BLANK_IMAGE_URL = '/lib/ext2.0.1/resources/images/default/s.gif';
var dummyParams = 'x';
if (Ext.isSafari) { dummyParams = ''; }

/* Deal with Norton Nonsense */
var nepo = 'SymReal'+'WinOpen';
if (window[nepo]) { window['open'] = window[nepo]; }
nepo = 'open';

/* Cache some data in parent so that we don't have to repeat initialization */
var d_ = {};
if (parent.cache_data) { d_ = parent.cache_data; } else { parent.cache_data = d_; }


/* Classes and objects */

function Main$__init__(){
    this.stat_bases={load: 'Loading ... ', pre: 'New game begins in', run: 'Time left:', score: 'Scoring ...', stale: 'Trying again in'};
    this.jstxt='';
    this.acct_cmps = ['prep_acct_btn', 'input_form'];

  }
function Main$init (){
    this.flags = new Flags(Model.MainFlags, {
      gamepage_round: { fn: Delayed.setter('size_portlets') }
    });
    this.flags.on_first('games', this.init_all_games, this);
    this.flags.on_first('playing', this.first_playing, this);
    this.flags.on_first('P_games', this.first_page, this);
    this.flags.on({bog: this.sound_start, scope: this});
    GameLayout.init();
    Login.init();
    AllGames.load();
    Calibrate.check();

    this.sidemenus = {prep_acct:1};
    for (var mb in this.sidemenus) { Ext.menu.MenuMgr.get(mb).on({ itemclick: main2.menuclick }); }
    Ext.getCmp('prep_acct_btn').disable();

    clock();
  }
function Main$first_page(){
    AllGames.list_games();
    var acct, go_to = get_cookie('goto');
    if (go_to) {
      set_cookie('goto', '', -100, '/');
      if (go_to.indexOf('/acct/') === 0) {
        acct = get_query_args(go_to.split('?')[1]).a;
        this.to_acct(dynop('activate', [acct]));
        return;
      }
    }
if (top.location.search.substr(0, 6) == '?tests') { this.tests(); return; }
    acct = get_cookie('acct');
    if (acct) { this.to_acct(dynop('login_acct', [acct])); } else { this.prep_log(); }

  }
function Main$call_dynop (page, cb){
    call_server2(page +'&tz='+ (new Date()).getTimezoneOffset(), {on_result: cb, scope: this, evalR: 1, timeout: 5000 });

  }
function Main$to_acct (page){
    this.stop_play();
    P_infopage.body.update(templates.contacting());
    call_server2(page +'&tz='+ (new Date()).getTimezoneOffset(), {on_result: 'attempt', scope: Login, evalR: 1, timeout: 5000 });
    P_main.layout.setActiveItem(P_infopage);

  }
function Main$require_acct (){
    var acct = get_cookie('acct');
    if (acct) { return true; }
    this.prep_log();
    return false;

  }
function Main$sidebarAction (e, t){
    if (t.className == 'external') { return; }
    var mb = e.button;
    e.stopEvent();
    refocus(); 
    if (mb) { return; }
    var fn = t.id.replace('-', '_');
    if (this[fn]) { this[fn](fn); }
    else if (this.sidemenus[fn]) { Ext.getCmp(fn +'_btn').showMenu(); }
  }
function Main$game_news (){
 this.load_infopage('news');
  
  }
function Main$prep_rules(){
 this.load_infopage('rules');
  }
function Main$prep_name (fn){
    var gamename = get_cookie('name') || '';
    if (fn != 'prep_name' && gamename) { return; }
    var cat, team = '';
    if (gamename.substr(0, 5) == 'Team ' && ((cat = gamename.indexOf(':')) != -1)) {
      team = gamename.substr(5, cat - 5); gamename = gamename.substr(cat + 2);
    }
    if (team.toLowerCase().substr(0, 5) == 'team ') { team = team.substr(5); }
    View.gamename.form_values = { gamename: gamename, team: team };
    (new Ext.Window(View.gamename)).show();
  }
function Main$savename (fv){
    var n = fv.gamename.replace(/[,;=:]/g, '').replace(/[\s\n]+/g,' ').replace(/^ | $/g, '').substr(0, 30);
    var t = fv.team.replace(/[,;=:]/g, '').replace(/[\s\n]+/g,' ').replace(/^ | $/g, '').substr(0, 30);
    if (t) { n = 'Team '+ t +': '+ n; }
    set_cookie('name', n, 365*24, '/');
    window.Notifier1.show(templates.name_changed({n: n}));

  }
function Main$learn_about(){
 this.load_infopage('about');
  }
function Main$learn_oldnews(){
 this.load_infopage('oldnews');
  }
function Main$learn_contact(){
 this.load_infopage('contact');

  }
function Main$play_4x4(){
 this.play_sq('4x4');
  }
function Main$play_5x5(){
 this.play_sq('5x5');
  }
function Main$play_sq(name){
    this.stop_play();
    if (!this.game || name != this.game.name) {
      this.game = this.games[name];
      this.game.flags.on({ round_cells: this.on_round, prior: this.on_prior, scope: this });
      GameLayout.boardsize = null;
    }
    this.flags.set('playing');
    this.game.flags.set('playing', true);
    AllGames.load();

    this.lock();
    this.paused = false;
    P_main.layout.setActiveItem('blankpage');
    P_main.layout.setActiveItem('gamepage');
    window.keep_focus = P_input;
    if (d_.acct) { P_input_form.enable(); P_input_form.show(); P_input.repaint(); P_guesses.expand(false); }
            else { P_guesses.setTitle('Log in and Play!'); }
    if (d_.opts.P.collapsesidebar) { P_sidebar.collapse(true); }

  }
function Main$stop_play(){
    this.paused = true;
    window.keep_focus = null;
////    P_input_form.disable();
    Ext.getCmp('b0').show();
    Ext.getCmp('b4x4').hide();
    Ext.getCmp('b5x5').hide();

    if (this.game) {
      this.lock();
      this.game.stop();
      this.flags.unset('playing', 'round', 'prior');
      this.game = null;
    }
  }
function Main$first_playing(){
    GameLayout.board_setup();
    GameLayout.init_cols();
    if (this.first_playing2) { this.first_playing2(); }

  }
function Main$prep_log(){
    this.logout();
    this.stop_play();
    P_main.layout.setActiveItem(P_loginpage);

  }
function Main$logout(){
    set_cookie('acct','',-2400);
    d_.acct = null;
    var c;
    for (var i_c=0;(i_c<this.acct_cmps.length)&&((c=this.acct_cmps[i_c])||1);i_c++) { Ext.getCmp(c).disable(); }
    Ext.get('prep-log').dom.lastChild.nodeValue = ' Log in';
    this.flags.set('logout', true);
    this.flags.unset('logout');

  }
function Main$final_round_end(){
    delete d_.final_round;
    this.logout();
    this.paused = true;

  }
function Main$change_email (){
    View.email.form_values = { email: '' };
    (new Ext.Window(View.email)).show();

  }
function Main$saveemail(fv){
    call_server2(dynop('change_email', [fv.email]) +'&tz='+ (new Date()).getTimezoneOffset(), {on_result: 'savedemail', scope: this, evalR: 1, timeout: 5000 });
  }
function Main$savedemail(sc, data){
    if (sc.failed) { Ext.Msg.alert('', 'Server did not respond to email change attempt!'); return; }
    if (data.error) { Ext.Msg.alert('', decodeURI(data.error)); return; }
    if (data.msgid) {
      this.stop_play();
      Login.message_id = decodeURI(data.msgid);
      P_infopage.body.update(templates.email_status());
      P_main.layout.setActiveItem(P_infopage);
      Delayed.set('poll_email_status');
    }

  }
function Main$savepwd(fv){
 this.call_dynop(dynop('password', [fv.pwd]), 'savedpwd');
  }
function Main$savedpwd(sc, success){
 if (success) { window.Notifier1.show(success[1]); }
  }
function Main$change_pwd(){
 (new Ext.Window(View.password)).show();

  }
function Main$other_snap (){
    Ext.Msg.show({
   title:'Javascript code',
   msg: 'Code to execute:',
   buttons: Ext.Msg.OKCANCEL,
   fn: this.processJS,
   scope: this,
   icon: Ext.MessageBox.QUESTION,
   value: this.jstxt,
   multiline: true,
   minWidth: 800
    });
  }
function Main$other_reload(){
 window.location.reload(true);
  }
function Main$warn_reload(){
    setTimeout(this.other_reload, 5000);
    alert_box('WordSplay has been upgraded.  Reloading in 5 second.');

  }
function Main$load_infopage(p, cb){
    this.stop_play();
    var opts = {url: '/w29/'+ p +'.html', disableCaching: false, scripts: true};
    if (cb) { opts.callback = cb; opts.scope = this; }
    P_infopage.load(opts);
    P_main.layout.setActiveItem(P_infopage);

  }
function Main$processJS(bid, txt){
    if (bid == 'ok') {
      this.jstxt = txt;
      var f = eval('[function () {' + txt +'}][0]');
      f.call(window);
    }

  }
function Main$lock(lazy){
    this.locked = true;
    P_boards.addClass("timeup");
    P_status.setTitle('');
////    this.no_guess();
    this.guess_path = [];
    this.guess = '';
    if (this.game) { this.game.clear_path(); }
////    P_input_form.disable();
    if (lazy) { handoff(this, 'lock2'); } else { this.lock2(); }

  }
function Main$lock2(){
////    View.prior_scores_store.loadData({scores: []});
    View.prior_scores_store.loadData([]);
    GameLayout.scoreboard_set('Last Round', 'Loading ...');
    View.prior_words_store.loadData({words: []});
  }
function Main$unlock (){
    var g = this.game.round;
    this.game.set_letters();
    this.show_letters();
    P_boards.removeClass("timeup");
    if (!d_.acct) { return; }
    this.locked = g.board.buffer_guesses = false;
    this.set_score(g.board.score);
    View.guesses_store.loadData(g.board.guesses);
    P_input_form.enable(); P_input.repaint(); P_guesses.expand(false);
    this.no_guess();
  }
function Main$set_score(s){
 P_guesses.setTitle('Guesses (Score: '+ s +')');
  }
function Main$show_letters(){
    var cell, cells = this.game.cells;
    for (var i_cell=0;(i_cell<cells.length)&&((cell=cells[i_cell])||1);i_cell++) { cell.el.update(get_cell_letter(cell, d_.opts.B.lowercase)); }
  }
function Main$rotate(){
    var cell, cells = (this.game.hasOwnProperty("cells") ? this.game["cells"]: undefined);
    if (!cells) { return; }
    var rotlet = new Array(this.game.size);
    for (var i_cell=0;(i_cell<cells.length)&&((cell=cells[i_cell])||1);i_cell++) { rotlet[cell.rota] = cell.letter; }
    for (var i_cell=0;(i_cell<cells.length)&&((cell=cells[i_cell])||1);i_cell++) { cell.letter = rotlet[i_cell]; }
    var step, path = this.guess_path;
    if (path && path.length) {
      this.game.clear_path();
      this.show_letters();
      for (var i_step=0;(i_step<path.length)&&((step=path[i_step])||1);i_step++) { path[i_step] = cells[step].rota; }
      this.draw_path(path);
    } else { this.show_letters(); }
    refocus();
  }
function Main$draw_path(path){
    if (!d_.opts.B.mark && !d_.opts.B.click2) { return; }
    if (!(path && path.length)) { return; }
    var i = path.length - 1;
    var cells = this.game.cells;
    cells[path[i]].el2.addClass('cursor');
    while (--i >= 0) { cells[path[i]].el2.addClass('inword'); }
  }
function Main$redraw_path(){
    this.game.clear_path();
    this.draw_path(this.guess_path);
  }
function Main$tick(){
    if (this.paused || !this.flags.get.round) { return; }
    var status = this.status;
    var status_text = (d_.acct? '': 'Log in and Play! ')+ this.status_text;
    var t = now() - d_.ms_off; // Server time estimate
    var t2 = Math.floor((this.status_t - t) / 1000);
    if (t2 > 0) {
      // Count down
      if (status == 'run') {
        // Buffer guesses until the final ten seconds of the round
        var b = this.game.round.board;
        if (b.buffer_guesses && (t2 <= 10) && b.buffered.length) { this.game.submit_guesses(); }
        b.buffer_guesses = (t2 > 10);
        switch (d_.opts.S.timer) {
          case 2: return;
          case 1: if (t2 > 5 && (t2 > 15 || t2 % 5) && (t2 % 30)) { return; }
                  if (t2 > 5) { status_text += ' &lt;'; }
            break;
          default: break;
        }
        if (t2 < 4) { this.sound_pop(); }
      }
      P_status.setTitle(status_text +' '+ min_sec(t2));
    } else if (status == 'load') {
      // Time to load next round.
      this.status = 'loading';
      P_status.setTitle('Loading ...');
      // Try to get results into cache early
      this.game.end_round();
    } else if (status == 'reset') {
      alert('Due to server maintenance, WordSplay will reload now.');
      window.top.location.reload(true);
    } else {
      var b = this.game.round.board;
      /* What phase is the current board in? */
      var s = 'load';
      if      (t < (t2 = b.begins - 1000)) { s = 'pre'; }
      else if (t < (t2 = b.ends))          { s = 'run'; }
      else if (t > (t2 = b.ends + 2000))   { t2 = t + 2000; }
      this.status_t = t2; 
      if (s != status) {
        // New phase.  Lock the board if the game was running, unlock it if it is running now.
        if (status == 'run') {
          this.lock(s != 'run');
          this.flags.set('eog'); this.flags.unset('bog');
          if (d_.final_round && d_.final_round.left === 0) {
            // Show final round message, and log out once the prior round has loaded.
            P_input_form.hide();
            var msg = View.plugout.html = d_.final_round.msg;
            P_status.add(View.plugout);
            P_status.doLayout();
            this.flags.on_first('prior', this.final_round_end, this);
            alert_box(msg);
          }
        }
        this.status = s;
        this.status_text = this.stat_bases[s];
        if (s == 'run') {
          if (status == 'pre' && d_.final_round) {
            // Count down to final round, and clean up if prior round trigger failed.
            if (d_.final_round.left === 0) { this.final_round_end(); } else { d_.final_round.left--; }
          }
          this.unlock();
          this.flags.set('bog'); this.flags.unset('eog');
          P_status.setTitle(d_.opts.S.timer == 2 ? 'Game in Progress' : this.status_text +' '+ min_sec((t2 - t) / 1000));
        }
      }
    }
  }
function Main$init_all_games(){
    this.flags.on({ games: this.got_all_games, scope: this });
    var glist = AllGames.glist;
    var g, games = this.games = {};
    for (var i_g=0;(i_g<glist.length)&&((g=glist[i_g])||1);i_g++) { games[g.name] = new Game(g); }
  }
function Main$got_all_games(){
    var gsall = AllGames.gsall;
    if (gsall.hasOwnProperty("status")) {
      // The server is forcing a special status.
      this.status = gsall.status;
      this.status_t = gsall.status_wait + now();
      this.status_text = gsall.status_text;
      if (gsall.hasOwnProperty("message")) { alert_box(gsall.message); }
    }
  }
function Main$on_round(){
    this.status = 'loaded';
    this.status_t = 0;
    this.status_text = 'Loaded';
    this.flags.set('round');
  }
function Main$on_prior(){
    this.flags.set('prior');
    var p = this.game.prior;
    GameLayout.update_prior(p);
    if (!this.flags.get.games) { AllGames.load(); }
    var i, pbg = p.board.guesses;
    if (pbg && p.me && (i = pbg.length - p.me.guessct - 1) >= 0) {
      for (var words = []; i >= 0; i--) { if (pbg[i][2]) { words.push(pbg[i][0]); } }
      if (words.length) { window.Notifier1.show(templates.late_words() + words.join(', ')); }
    }
  }
function Main$click_cube(c){
    if (this.locked) { return; }
    P_input.dom.value = this.guess = this.next_cell(c);
    this.redraw_path();
    refocus();
  }
function Main$next_cell(c){
    var ci, cell, cells = this.game.cells, path = this.guess_path;
    var pl = path.length;
    if (pl) {
      var neighbors = cells[path[pl - 1]].neighbors;
      for (var i_ci=0;(i_ci<neighbors.length)&&((ci=neighbors[i_ci])||1);i_ci++) { cell = cells[ci]; if (cell.el === c) { path.push(ci); return this.guess + get_cell_letter(cell, true); } }
    }
    for (var i_cell=0;(i_cell<cells.length)&&((cell=cells[i_cell])||1);i_cell++) { if (cell.el === c) { this.guess_path = [i_cell]; return get_cell_letter(cell, true); } }
    return '';
  }
function Main$no_guess(){
    this.guess_path = [];
    this.guess = P_input.dom.value = '';
    if (this.game) { this.game.clear_path(); }
    P_output.body.update('<!-- -->&nbsp;');
  }
function Main$typing(delayed){
    var g = P_input.dom.value;
    if (g.match(/^['"]/) || this.locked) { return false; }
    var word, words = g.toLowerCase().replace(/[ .,]+/g, ' ').replace(/[^a-z ]|^ /g, '').split(' ');
    g = words.pop();
    if (words.length) {
      for (var i_word=0;(i_word<words.length)&&((word=words[i_word])||1);i_word++) { this.guess_submit(word); }
      P_input.dom.value = g;
    }
    this.text_guess(g);
    return false;
  }
function Main$text_guess (guess){
    this.guess_path = pathFinder.find(guess, this.game.cells);
    this.guess = guess.substr(0, pathFinder.found);
    if (d_.opts.S.mistypes) { P_input.dom.value = this.guess; } else
    if (this.guess != guess) { this.guess_path = []; }
    this.redraw_path();
  }
function Main$guess_submit(guess){
    // Check their guess
    if (!guess) {
      guess = P_input.dom.value;
      this.no_guess();
    }
    if (guess.match(/^['"]/)) { this.guess_chat(guess); return; }
    if (this.locked) { return; }
    var b = this.game.round.board;
    b.guessct += 1;
    // Eliminate non-letters
    guess = guess.toLowerCase().replace(/[^a-z]/gi, '').substring(0, this.game.size);
    if (!guess) { return; }
    handoff(this, 'guess_submit2', [b.guesses, guess, View.guesses_store]);
  }
function Main$guess_submit2(guesses, guess, store){
    window.trace = ['recording', guess];
    var g = this.game.round.words.get(guess);
    if (!g) {
      pathFinder.find(guess, this.game.cells);
      if (guess.length != pathFinder.found) { this.guess_output(templates.not_on({guess: guess})); window.trace = null; this.sound_bad(); return; }
    }
    if (store.getById(guess)) {
      window.trace = ['repeated', guess];
      this.guess_output(templates.guessed({guess: guess}));
      window.trace = null;
      return;
    }
    window.trace = ['valid?', guess];
    g = g? this.guess_valid(g) : this.guess_invalid(guess);
    window.trace = ['show', guess];
    guesses.unshift(g);
    if (g[2] || d_.opts.G.wrong) { store.loadData(guesses); }
    window.trace = null;
  }
function Main$guess_output (msg, good){
    handoff(this, 'guess_output2', [msg, good]);
  }
function Main$guess_output2 (msg, good){
    window.trace = [msg];
    var om = d_.opts.S[good ? 'good' : 'feedback'];
    if (om == 1) { GameLayout.guess_out.show(msg); }
    else if (om == 2) { P_output.body.update(msg); }
    window.trace = null;
  }
function Main$guess_invalid (guess){
    this.guess_output(templates.bad_guess({guess:guess}));
    this.sound_bad();
    return [guess, 'bad', 0];
  }
function Main$guess_valid (g){
    this.set_score(this.game.guess(g));
    this.guess_output(g.word + ': ' + g.value + ' point' + (g.value > 1? 's!':'!'), true);
    return [g.word, g.cls, g.value];
  }
function Main$score_word (guess){
 return this.score_array[guess.length];

  }
function Main$guess_chat(guess){
    var q = guess.charAt(0);
    var r = new RegExp('^'+ q +'|'+ q +'$', 'g')
    send_server('/misc/w29/chat/'+ this.game.name +'?'+ guess.replace(r, '').substr(0, 100));
    this.guess_output('Sent to chat', 'good');
    P_chat.expand();
  }
function Main$get_chat(delayed){
    get_server('/gs/chat.txt', { on_result: 'got_chat', scope: this }, true);
    return true;
  }
function Main$got_chat(sc, h){
    if (sc.failed) { return; }
    var i, d = d_.chat_data;
    var hlength = h.length;
    if (d.stamp && d.stamp == h.substr(0, d.stamp.length)) {
      if (hlength == d.processed) { return; }
      i = d.processed;
    } else {
      var header = h.split('\n',3);
      // TODO: Get unprocessed bits of old chat file
      d.processed = hlength;
      d.stamp = header[0];
      d.names = {};
      i = header.join('\n').length + 1;
    }
    if (i >= hlength) { return; }
    var ts1 = d.stamp.substr(0,4) +'/'+ d.stamp.substr(4,2) +'/'+ d.stamp.substr(6,2) +' '+ d.stamp.substr(9,2) +':';
    var mine = d_.acct && d_.acct.substr(0, 4);
    var recs = []
    while (i < hlength) {
      var i2 = h.indexOf('\n', i);
      if (i2 < 0) { break; }
      var m = h.slice(i, i2).match(/^([0-9][0-9])([0-9][0-9])(...)([A-Z0-9]+)(=[^=:]+)?:(.+)$/);
      i = i2 + 1;
      if (m) {
        var ts = Date.parse(ts1 + m[1] +':'+ m[2] + ' GMT');
        var channel = m[3]
        var code = m[4];
        var name = m[5];
        var team = '';
        var line = m[6];
        var c = (code == mine) ? 'me' : '';
        if (code == '1SG8') { name = ' Evan'; c = 'evan'; }
        if (name) {
          name = name.substr(1);
          if (name.charAt(0) == "'") { name = unescape(name.substr(1)); }
          m = name.match(/^(team[^:]*): (.*)$/i);
          if (m) { team = m[1]; name = m[2]; }
          d.names[code] = [team, name];
        } else {
          m = d.names[code];
          team = m[0]; name = m[1];
        }
        recs.push([ts, c, channel, code, name, team, line]);
      }
      d.processed = i;
    }
    var s = View.chat_store;
    recs = s.reader.readRecords(recs.reverse()).records;
    s.clearFilter();
    s.insert(0, recs);
    s.applySort();
    var row, xts = ts - 3600000;
    var rct = s.getCount();
    while ((row = s.getAt(--rct)).data.ts < xts || rct > 99) {
      s.remove(row);
    }
    this.filter_chat()

  }
function Main$filter_chat(){
    View.chat_store.filterBy(function (rec) { return !(d_.banned).hasOwnProperty( rec.get('code')); });

  }
function Main$tests(){
    P_main.layout.setActiveItem(P_infopage);

flds1 = '<div tabindex="-1" class="x-form-item"><label class="x-form-item-label" style="width: 65px;" for="tstext-comp-1028">Your Email:</label><div style="padding-left: 70px;" id="tstx-form-el-ext-comp-1028" class="x-form-element"><input name="tstemail" id="tstext-comp-1028" class="x-form-text x-form-field focus_first" style="width: 392px;"/></div><div class="x-form-clear-left"/></div><div tabindex="-1" class="x-form-item"><label class="x-form-item-label" style="width: 65px;" for="tstext-comp-1029">Password:</label><div style="padding-left: 70px;" id="tstx-form-el-ext-comp-1029" class="x-form-element"><div class="x-form-field-wrap" id="tstext-gen153" style="width: 0px;"><input type="password" name="tstpw" id="tstext-comp-1029" class="x-form-text x-form-field" style="width: 292px;"/><span class="fine" id="tstext-gen154"> (if you dont have one, just make one up!)</span></div></div><div class="x-form-clear-left"/></div>';
flds2 = '<div tabindex="-1" class="x-form-item"><label class="x-form-item-label" style="width: 65px;" for="tst2ext-comp-1028">Your Email:</label><div style="padding-left: 70px;" id="tstx-form-el-ext-comp-1028" class="x-form-element"><input name="tst2email" id="tst2ext-comp-1028" style="width: 392px;"/></div><div class="x-form-clear-left"/></div><div tabindex="-1" class="x-form-item"><label class="x-form-item-label" style="width: 65px;" for="tst2ext-comp-1029">Password:</label><div style="padding-left: 70px;" id="tstx-form-el-ext-comp-1029" class="x-form-element"><div class="x-form-field-wrap" id="tst2ext-gen153" style="width: 0px;"><input type="password" name="tst2pw" id="tst2ext-comp-1029" style="width: 292px;"/><span class="fine" id="tst2ext-gen154"> (if you dont have one, just make one up!)</span></div></div><div class="x-form-clear-left"/></div>';
flds3 = '<div tabindex="-1"><label style="width: 65px;" for="tst3ext-comp-1028">Your Email:</label><div style="padding-left: 70px;" id="tstx-form-el-ext-comp-1028"><input name="tst3email" id="tst3ext-comp-1028" /></div><div class="x-form-clear-left"/></div><div tabindex="-1" ><label style="width: 65px;" for="tst3ext-comp-1029">Password:</label><div style="padding-left: 70px;" id="tstx-form-el-ext-comp-1029" ><div id="tst3ext-gen153" style="width: 0px;"><input type="password" name="tst3pw" id="tst3ext-comp-1029" /></div></div><div class="x-form-clear-left"/></div>';
testform = '<h3>Are there six input fields below (3 sets of 2)?</h3><div style="width:600px"><form id="testloginform" method="post" class="x-form" target="_blank" action="about:"><div class="x-panel x-form-label-right" id="tstext-comp-1027"><div class="x-panel-bwrap" id="tstext-gen120"><div class="x-panel-body x-panel-body-noheader" id="tstext-gen121">'+flds1+flds2+flds3+'</div></div></form></div>';
    P_infopage.body.update(templates.tests() + testform);
    handoff(Main, 'test1');
    handoff(Main, 'test2');
    handoff(Main, 'test3');
    handoff(Main, 'test4');
  }
function Main$test1(){
    var out = Ext.get('test1');
    out.update('Running test...');
    set_cookie('ctest', 'v1', 0, '/');
    if (get_cookie('ctest') != 'v1') { out.update('Failed at step 1'); return; }
    set_cookie('ctest', 'v2', 100, '/');
    if (get_cookie('ctest') != 'v2') { out.update('Failed at step 2'); return; }
    set_cookie('ctest', '', -1, '/');
    if (get_cookie('ctest')) { out.update('Failed at step 3'); return; }
    out.update('Test passed.');
  }
function Main$test2(){
    var out = Ext.get('test2');
    out.update('Running test...');
    set_cookie('sctest', 'v', 0, '/acct/');
    this.call_acct('test2cb', 'echo', 'sctest');
  }
function Main$test2cb(sc, success){
    var out = Ext.get('test2');
    if (!(success && success[0] == 'v')) { out.update('Failed at step 1: '+ repr(success)); return; }
    set_cookie('sctest', '', -1, '/acct/');
    this.call_acct('test2cb2', 'echo', 'sctest');
  }
function Main$test2cb2(sc, success){
    var out = Ext.get('test2');
    if (sc.failed || !(success && success[0] === '')) { out.update('Failed at step 2:'+ sc.failed +', '+ repr(success)); return; }
    out.update('Test passed.');
  }
function Main$test3(){
    var out = Ext.get('test3');
    out.update('Running test...');
    call_server('/testscript.html', {on_result: 'test3cb', scope: this});
  }
function Main$test3cb(sc, success){
    var out = Ext.get('test3');
    if (sc.failed) { out.update('Failed at step 1'); return; }
    Ext.get('test3r').update(HTMLescape(success));
    out.update(success, true);
  }
function Main$test3passed(){
 Ext.get('test3').update('Test passed.  Result text was:');
  }
function Main$test4(){
    var out = Ext.get('test4');
    out.update('Running test...');
    call_server('/testobscene', {on_result: 'test4cb', scope: this});
  }
function Main$test4cb(sc, success){
    var out = Ext.get('test4');
    var txt = 'd8hpm{l8%?D8yj|t83?8lwvk83?8|p|t83?8ozqvl8Bb';
    if (sc.failed || !(success && success.length == txt.length)) { out.update('Failed at step 1'); return; }
    var k1 = " ',:[]cedfihkonpsrutw{}";
    var k2 = '?83%DB|z{yvwtpqolmjkhdb';
    for (var i=0; i < txt.length; i++) { if (k2.charAt(k1.indexOf(success.charAt(i))) != txt.charAt(i)) {
      out.update('Failed at step 2'); 
      return;
    }}
    out.update('Test passed.');

  }
function Main$opts_changed (o1, o2, v){
    switch (o1 + o2) {
      case 'Rall': if (v) { P_scores.getStore().each(function (rec) { rec.set('x', 0); }); } else { GameLayout.abbreviate_scores(); break; }
      case 'Rdisemvowel': P_scores.getView().refresh(); break;

      case 'Wsort':
      case 'Rteams': if (this.flags.get.prior) { this.flags.unset('prior'); this.game.reload_prior(); } break;

      case 'Blowercase': this.show_letters(); break;
      case 'Bmark': this.redraw_path();  break;
      case 'Bclick2': GameLayout.board_setup(); break;

      case 'Gwrong': if (v) { View.guesses_store.clearFilter(); } else { View.guesses_store.filterBy(function (rec) { return rec.get('score') > 0; }); }
        break;
      case 'Stimer': if (this.status == 'run' && v == 2) { P_status.setTitle('Game in Progress'); }
        break;
        break;
      case 'Sfeedback':
      case 'Sgood': if (d_.opts.S.good == 2 || d_.opts.S.feedback == 2) { P_output.show(); } else { P_output.hide(); }
        break;
      case 'Wgroup': P_prior_words.layout.setActiveItem(v); Delayed.set('size_portlets');
        break;

      case 'Ptheme': GameLayout.set_theme(v);
        break;

      case 'Rheight': GameLayout.setup_grid();
      case 'Wheight': Delayed.set('size_portlets');
        break;

      case 'Wreverse': View.prior_words_store.loadData({words: GameLayout.sorted_prior_words.reverse()});
        break;

      default: break;
    }
    this.save_options();
  }
function Main$save_pref(pref, msg){
    if (!d_.acct) { return; }
    call_server('/misc/w29/save_prefs?cols='+ d_.cols +'&optss='+ d_.optss, {on_result: 'save_pref_done', on_error: 'alert', scope: this, msg: msg, timeout: 10000});
  }
function Main$save_pref_done(sc){
 window.Notifier1.show(sc.data.msg);
  }
function Main$save_columns(){
    var col, cols = P_gamepage.items.items;
    var pcodes = '';
    for (var i_col=0;(i_col<cols.length)&&((col=cols[i_col])||1);i_col++) {
      pcodes += '-';
      var p, portlets = col.items.items;
      for (var i_p=0;(i_p<portlets.length)&&((p=portlets[i_p])||1);i_p++) if (p.pcode) {
        pcodes += p.pcode;
      }
    }
    pcodes = pcodes.substr(1);
    if (d_.cols != pcodes) {
      d_.cols = pcodes;
      this.save_pref('cols', 'New game board layout saved.');
    }
  }
function Main$def_columns(){
    delete d_.cols;
    this.save_pref('cols', 'Game board layout reverted to default.');
    GameLayout.init_cols();
    P_gamepage.doLayout();
  }
function Main$save_options(){
    var v, olist = [];
    for (var c in all_opts) {
      var a2 = ((all_opts).hasOwnProperty( c) ? (all_opts)[ c]: undefined);
      olist.push(c);
      for (var c2 in a2) {
        if ((a2).hasOwnProperty( c2) && (v=d_.opts[c][a2[c2]])) {
          olist.push(c2);
          if (v > 1) { olist.push(''+ v); }
        }
      }
    }
    d_.optss = olist.join('');
    this.save_pref('optss', 'Options saved.');

  }
function Main$set_omg_all(fv, name){
    var h = fv.nhours && parseInt(fv.nhours, 10);
    var n = parseInt(fv.nrounds, 10);
    var t = parseInt(fv.nminutes, 10);
    t = (t > 0? t: 0) + (h > 0? h * 60: 0);
    if (n > 0) {
      var msg, fr = { n: n, t: t, ns: n === 1? '': 's'}; 
      if (t > 0) { msg = templates.final_round_t(fr); } else { msg = templates.final_round(fr); }
      d_.final_round = { msg: msg, left: n };
    } else { n = 0; }
    if (n > 0 || t > 0) {
      var nt = '';
      if (n > 0 && t > 0) {
        var b = this.game.round.board;
        var bb = Math.floor((now() - d_.ms_off - b.begins) / 1000); // Seconds until or since start of round
        nt = '/' + ((b.duration + b.interval) * n - bb + (bb < 0? -b.interval: b.duration)); // Seconds until n rounds end
      }
      send_server('/misc/'+ name +'/'+ n +'/'+ t + nt);
    }
  }
function Main$omg_w(conf){
    var w = new Ext.Window(conf);
    w.mainpaused = false;
    w.show();

  }
function Main$plugin_omg(){
    this.flags.on_first('gamepage_round', this.show_omg0, this);
  }
function Main$show_omg0(){
    View.omg.form_values = { nrounds: '0', nhours: '1', nminutes: '0' };
    this.omg_w(View.omg);
  }
function Main$set_omg (fv){
    this.set_omg_all(fv, 'omg');
  }
function Main$omg_explain(){
    var el = Ext.getCmp('omg_explain').el;
    el.removeClass('tright');
    el.update(templates.omg_explain());

  }
function Main$plugin_omg1(){
    this.flags.on_first('gamepage_round', this.show_omg1, this);
  }
function Main$show_omg1(){
    View.omg1.form_values = { nrounds: '0' };
    ////if (d_.plugins.omg2 || d_.plugins.omg3) {
    ////  var tp = View.omg1.items[0].items[0];
    ////  tp.html = templates.omg1() + '<h4>There will also be a button in-game that allows you to commit ending the game one round ahead.</h4>';
    ////}
    this.omg_w(View.omg1);
  }
function Main$set_omg1(fv){
    if (parseInt(fv.nrounds, 10) > 0) { fv.nminutes = '60'; }
    this.set_omg_all(fv, 'omg1');

  }
function Main$plugin_omg2(){
    this.add_omg(View.omg2btn);
  }
function Main$show_omg2(){
    Main.omg_w(View.omg2);
  }
function Main$set_omg2(yn){
    this.set_omg_all({nrounds: '1', nminutes: '60'}, 'omg2');
    this.remove_omg();

  }
function Main$add_omg(btn){
    P_status.add(btn);
    P_status.doLayout();
    Ext.getCmp(btn.id).hide();
    d_.omgbpop = (get_cookie('omgb') != '0');
    this.flags.on_first('logout', this.remove_omg, this);
    this.flags.on({ bog: this.hide_omg, eog: this.show_omg, scope: this });
  }
function Main$remove_omg(){
    d_.omgbpop = false;
    var c, cmps = ['omg2btn', 'omg3btn'];
    for (var i_c=0;(i_c<cmps.length)&&((c=cmps[i_c])||1);i_c++) { P_status.remove(c); }
  }
function Main$hide_omg(){
    var c, cmps = ['omg2btn', 'omg3btn'];
    for (var i_c=0;(i_c<cmps.length)&&((c=cmps[i_c])||1);i_c++) { if (c = Ext.getCmp(c)) { c.hide(); } }
  }
function Main$show_omg(){
    var c, cmps = ['omg2btn', 'omg3btn'];
    for (var i_c=0;(i_c<cmps.length)&&((c=cmps[i_c])||1);i_c++) { if (c = Ext.getCmp(c)) { c.show(); } }
    if (d_.omgbpop) {
      d_.omgbpop = false;
      this.omg_w(View.omgbpop);
    }
  }
function Main$omgbpop_stop(){
    set_cookie('omgb', '0', 365*24, '/'); 

  }
function Main$plugin_omg3(){
    this.add_omg(View.omg3btn);
  }
function Main$show_omg3(){
    Main.omg_w(View.omg3);
  }
function Main$set_omg3(fv){
    fv.nrounds = '1';
    this.set_omg_all(fv, 'omg3');
    this.remove_omg();

  }
function Main$plugin_donate(){
    Ext.Msg.alert('', 'If you enjoy the game, please consider <a href="https://www.paypal.com/xclick/business=game@shackworks.com&item_name=WordSplay%20donation" target="_blank">donating</a> today!  I could use a bit of help.');

  }
function Main$plugin_omgsurv(){
    this.flags.on_first('gamepage_round', this.show_omgsurv, this);
  }
function Main$show_omgsurv(){
    var b, w = new Ext.Window(View.omgsurv0);
    for (var i_b=0;(i_b<w.buttons.length)&&((b=w.buttons[i_b])||1);i_b++) { b.scope = w; }
    w.mainpaused = false;
    w.show();
  }
function Main$show_omgsurv_click(o){
    var c = o.text.substr(0,1);
    this.close();
    if (c == 'M') { return; }
    if (c == 'N') { send_server('/misc/omgsurv/No'); return; }
    Main.load_infopage('omgsurv');
  }
function Main$step1_omgsurv(q1){
    d_.omgsurv = q1;
    if (q1 > 3) {
      Main.load_infopage('omgsurv4');
    } else {
      Main.load_infopage('omgsurv2');
    }
  }
function Main$submit_omgsurv(f){
    var q1 = d_.omgsurv;
    send_server('/misc/omgsurv/q1/'+ q1);

    function sendtext(q, ta) {
      send_server('/misc/omgsurv/'+ q +'/'+ encodeURIComponent(ta.value).replace(/%(25|0A|0C|2F)/g, '%25$1'));
    }

    if (q1 > 3) {
      var q4;
      for (var i_q4=0;(i_q4<f.elements.length)&&((q4=f.elements[i_q4])||1);i_q4++) { if (q4.name == 'q4' && q4.checked) {
        send_server('/misc/omgsurv/q4/'+ q4.value);
        if (q4.value == '3') { sendtext('q4a3', f.q4a3); }
        break;
      }}
      sendtext('q5', f.q5);
    } else {
      sendtext('q2', f.q2);
      sendtext('q3', f.q3);
    }
    Main.load_infopage('omgsurvdone');
    return false;

  }
function Main$plugin_todosurv(){
    this.flags.on_first('gamepage_round', this.show_todosurv, this);
  }
function Main$show_todosurv(){
    Main.load_infopage('todosurv');
  }
function Main$pick_todosurv(qn, an){
    send_server('/misc/todosurv/q'+ qn +'/'+ an);
  }
function Main$done_todosurv(f){
    send_server('/misc/todosurv/done');
    Main.load_infopage('todosurvdone');
    return false;

  }
function Main$sound_start(){
    if (!d_.opts.P.sound) { return; }
    try { Ext.get('sound_start').dom.Play(); } catch (e) {}
  }
function Main$sound_pop(){
    if (!d_.opts.P.sound) { return; }
    try { Ext.get('sound_pop').dom.Play(); } catch (e) {}
  }
function Main$sound_bad(){
    if (!d_.opts.P.sound) { return; }
    try { Ext.get('sound_bad').dom.Play(); } catch (e) {}}

var Main = {__init__:Main$__init__,
init:Main$init,
first_page:Main$first_page,
call_dynop:Main$call_dynop,
to_acct:Main$to_acct,
require_acct:Main$require_acct,
sidebarAction:Main$sidebarAction,
game_news:Main$game_news,
prep_rules:Main$prep_rules,
prep_name:Main$prep_name,
savename:Main$savename,
learn_about:Main$learn_about,
learn_oldnews:Main$learn_oldnews,
learn_contact:Main$learn_contact,
play_4x4:Main$play_4x4,
play_5x5:Main$play_5x5,
play_sq:Main$play_sq,
stop_play:Main$stop_play,
first_playing:Main$first_playing,
prep_log:Main$prep_log,
logout:Main$logout,
final_round_end:Main$final_round_end,
change_email:Main$change_email,
saveemail:Main$saveemail,
savedemail:Main$savedemail,
savepwd:Main$savepwd,
savedpwd:Main$savedpwd,
change_pwd:Main$change_pwd,
other_snap:Main$other_snap,
other_reload:Main$other_reload,
warn_reload:Main$warn_reload,
load_infopage:Main$load_infopage,
processJS:Main$processJS,
lock:Main$lock,
lock2:Main$lock2,
unlock:Main$unlock,
set_score:Main$set_score,
show_letters:Main$show_letters,
rotate:Main$rotate,
draw_path:Main$draw_path,
redraw_path:Main$redraw_path,
tick:Main$tick,
init_all_games:Main$init_all_games,
got_all_games:Main$got_all_games,
on_round:Main$on_round,
on_prior:Main$on_prior,
click_cube:Main$click_cube,
next_cell:Main$next_cell,
no_guess:Main$no_guess,
typing:Main$typing,
text_guess:Main$text_guess,
guess_submit:Main$guess_submit,
guess_submit2:Main$guess_submit2,
guess_output:Main$guess_output,
guess_output2:Main$guess_output2,
guess_invalid:Main$guess_invalid,
guess_valid:Main$guess_valid,
score_word:Main$score_word,
guess_chat:Main$guess_chat,
get_chat:Main$get_chat,
got_chat:Main$got_chat,
filter_chat:Main$filter_chat,
tests:Main$tests,
test1:Main$test1,
test2:Main$test2,
test2cb:Main$test2cb,
test2cb2:Main$test2cb2,
test3:Main$test3,
test3cb:Main$test3cb,
test3passed:Main$test3passed,
test4:Main$test4,
test4cb:Main$test4cb,
opts_changed:Main$opts_changed,
save_pref:Main$save_pref,
save_pref_done:Main$save_pref_done,
save_columns:Main$save_columns,
def_columns:Main$def_columns,
save_options:Main$save_options,
set_omg_all:Main$set_omg_all,
omg_w:Main$omg_w,
plugin_omg:Main$plugin_omg,
show_omg0:Main$show_omg0,
set_omg:Main$set_omg,
omg_explain:Main$omg_explain,
plugin_omg1:Main$plugin_omg1,
show_omg1:Main$show_omg1,
set_omg1:Main$set_omg1,
plugin_omg2:Main$plugin_omg2,
show_omg2:Main$show_omg2,
set_omg2:Main$set_omg2,
add_omg:Main$add_omg,
remove_omg:Main$remove_omg,
hide_omg:Main$hide_omg,
show_omg:Main$show_omg,
omgbpop_stop:Main$omgbpop_stop,
plugin_omg3:Main$plugin_omg3,
show_omg3:Main$show_omg3,
set_omg3:Main$set_omg3,
plugin_donate:Main$plugin_donate,
plugin_omgsurv:Main$plugin_omgsurv,
show_omgsurv:Main$show_omgsurv,
show_omgsurv_click:Main$show_omgsurv_click,
step1_omgsurv:Main$step1_omgsurv,
submit_omgsurv:Main$submit_omgsurv,
plugin_todosurv:Main$plugin_todosurv,
show_todosurv:Main$show_todosurv,
pick_todosurv:Main$pick_todosurv,
done_todosurv:Main$done_todosurv,
sound_start:Main$sound_start,
sound_pop:Main$sound_pop,
sound_bad:Main$sound_bad};
Main.__init__();

function Login$__init__(){
    this.message_id = null;
  }
function Login$init(){
    Delayed.add('poll_email_status', this.poll_email_status, this, 1500);
  }
function Login$submit(e){
    e.stopEvent();
    var fv = this.getValues();
    fv.act = '';
    this.setValues({pw: ''});
    Login.send(fv);
    return false;
  }
function Login$button(btn){
    var f = Ext.getCmp('login').getForm();
    var fv = f.getValues();
    fv.act = btn.lpbtn;
    f.setValues({pw: ''});
    Login.send(fv);

  }
function Login$send(fv){
    d_.is_public = fv.pub;
    this.fv = fv;
    if (fv.email.indexOf('@shackworks.') > 0) { alert_box('YOUR email address, not mine.'); return; }
    Main.to_acct(dynop('login_ep', [fv.email, fv.pw], ['c', fv.act]));

  }
function Login$attempt(sc, data){
    if (sc.failed) { this.error('Server did not respond to login attempt!'); return; }
    window.trace = [data];
    if (data.blocked) {
      P_infopage.body.update(templates.blocked({t: data.blocked, d: data.blockd}));
      return;
    }
    if (data.acct) { handoff(this, 'success', [data]); }

  }
function Login$success(data){
    d_.acct = data.acct;
    d_.cols = data.cols || '';
    d_.optss = data.optss || '';
    set_cookie('acct', d_.acct, d_.is_public? 0: 365*24, '/');
    d_.opts = unpack_options(d_.optss);

    Ext.get('prep-log').dom.lastChild.nodeValue = ' Log out';
    var c;
    for (var i_c=0;(i_c<Main.acct_cmps.length)&&((c=Main.acct_cmps[i_c])||1);i_c++) { Ext.getCmp(c).enable(); }
    P_status.remove('plugout');
    d_.plugins = {};
    if (data.plugins) {
      var p;
      for (var i_p=0;(i_p<data.plugins.length)&&((p=data.plugins[i_p])||1);i_p++) { d_.plugins[p] = 1; }
      for (var i_p=0;(i_p<data.plugins.length)&&((p=data.plugins[i_p])||1);i_p++) { if (Main.hasOwnProperty('plugin_'+ p)) { Main['plugin_'+ p](); } }
    }
    P_infopage.body.update(templates.logged_in(), false, Login.complete);

  }
function Login$complete(){
    switch (d_.opts.P.play) {
      case 1: Main.play_4x4(); break;
      case 2: Main.play_5x5(); break;
      default: Main.load_infopage('welcome', Main.prep_name); break;
    }

  }
function Login$error(err){
    Ext.Msg.alert('', decodeURI(err), Main.prep_log, Main);
    return err;
  }
function Login$activate(mid, dup){
    this.message_id = decodeURI(mid);
    P_infopage.body.update(templates.activate({email: this.fv.email, dup: (dup? 'other': '')})
                          +templates.email_status());
    Delayed.set('poll_email_status');

  }
function Login$badpw(){
    Ext.Msg.confirm('Incorrect Password', 'Shall I email your password to you?', this.remind, this);
  }
function Login$remind(yn){
    if (yn == 'yes') {
      var fv = this.fv; 
      Main.to_acct(dynop('login_ep', [fv.email, fv.pw], ['c', 'pw']));
    } else {
      Main.prep_log();
    }
  }
function Login$remind_sent(mid){
    this.message_id = decodeURI(mid);
    P_infopage.body.update(templates.remind({email: this.fv.email})
                          +templates.email_status());
    Delayed.set('poll_email_status');
  }
function Login$bademail(){
    Ext.Msg.confirm('Unknown email address', 'Would you like to register "'+ this.fv.email +'"?', this.register, this);
  }
function Login$register(yn){
    if (yn == 'yes') {
      var fv = this.fv; 
      Main.to_acct(dynop('login_ep', [fv.email, fv.pw], ['c', 'reg']));
    } else {
      Main.prep_log();
    }

  }
function Login$poll_email_status(delayed){
    delayed.done = true;
    if (this.message_id) {
      get_server('/gs/email/msgs/' + this.message_id, { on_result: 'poll_result', scope: this, force: true }, true);
    }
    return false;
  }
function Login$poll_result(sc, status){
    if (sc.failed && sc.status != 304) { Delayed.set('poll_email_status'); return; }
    this.message_id = null;
    status = '<code>'+status.substr(status.indexOf('\n'))+'</code>';
    if (status.indexOf('transport succeeded') > 0) { status = '<h4>Sent!</h4>'+status; }
    Ext.get('email-status').update(status);}

var Login = {__init__:Login$__init__,
init:Login$init,
submit:Login$submit,
button:Login$button,
send:Login$send,
attempt:Login$attempt,
success:Login$success,
complete:Login$complete,
error:Login$error,
activate:Login$activate,
badpw:Login$badpw,
remind:Login$remind,
remind_sent:Login$remind_sent,
bademail:Login$bademail,
register:Login$register,
poll_email_status:Login$poll_email_status,
poll_result:Login$poll_result};
Login.__init__();

function Game() {
  if (this.constructor !== Game) { throw "Game constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
Game.prototype.__init__ = function () {
  var P=Game.prototype;
function Game$__init__(g){
DEBUG && console.log(Game$__init__, arguments);    this.name = g.name;
    var size = g.name.split('x');
    this.size = parseInt(size[0], 10) * parseInt(size[1], 10);
    this.cells = Model['cells'+ g.name];
    this.submit_guess = '';
    this.flags = new Flags(Model.GameFlags, {
      playing: { fn: this.get_cells_el, scope: this, single: true }
    });
    this.flags.on_first('playing', this.start, this);
  }
P.__init__=Game$__init__;
function Game$start(){
DEBUG && console.log(Game$start, arguments); Main.flags.on({playing_games: this.new_games, scope: this});
  }
P.start=Game$start;
function Game$stop(){
DEBUG && console.log(Game$stop, arguments);
    this.flags.observe.purgeListeners();
    this.flags.unset('playing', 'round', 'prior');
    if (this.round) { this.round.stop(); }
    if (this.prior) { this.prior.stop(); }
    this.round = this.prior = null;
  }
P.stop=Game$stop;
function Game$get_cells_el(){
DEBUG && console.log(Game$get_cells_el, arguments);    var cells = this.cells;
    Ext.getCmp('b'+ this.name).body.select('td', true).each(function (cell, coll, i) {
      var el = cells[i].el = cell;
      cells[i].el2 = el;
    });
    this.flags.set('cells');
  }
P.get_cells_el=Game$get_cells_el;
function Game$new_games(){
DEBUG && console.log(Game$new_games, arguments);    if (!this.flags.get.playing) { return; }
    this.g = AllGames.games[this.name];
    this.reload_prior();
    this.reload_round();
  }
P.new_games=Game$new_games;
function Game$reload_round(){
DEBUG && console.log(Game$reload_round, arguments);    if (this.round) { this.round.stop(); this.flags.unset('round'); }
    this.submit_guess = '';
    (this.round = new GameRound(this.g, {board: this.got_round, scope: this})).start();
  }
P.reload_round=Game$reload_round;
function Game$reload_prior(){
DEBUG && console.log(Game$reload_prior, arguments);    if (this.prior) {
      if (this.prior.stamp == this.g.prior) { this.flags.set('prior'); return; }
      this.prior.stop(); this.flags.unset('prior');
    }
    (this.prior = new GameRound(this.g, {result: this.got_prior, scope: this}, 2)).start();
  }
P.reload_prior=Game$reload_prior;
function Game$end_round(){
DEBUG && console.log(Game$end_round, arguments);    this.submit_guess = '';
    if (this.prior) { this.prior.stop(); this.flags.unset('prior'); Main.flags.unset('games', 'round', 'prior'); }
    (this.prior = new GameRound(this.g, {result: this.got_prior, scope: this}, 1)).start();

  }
P.end_round=Game$end_round;
function Game$got_round(){
DEBUG && console.log(Game$got_round, arguments);    var b = this.round.board;
    if (!b.hasOwnProperty("guesses")) {
      b.guesses = [];
      b.pending = [];
      b.buffered = [];
      b.guessct = b.submitted = b.acked = b.score = 0;
    }
    this.submit_guess = '/dyn/'+ this.name +'/'+ b.stamp +'/guess/';
    this.flags.set('round');
  }
P.got_round=Game$got_round;
function Game$got_prior(){
DEBUG && console.log(Game$got_prior, arguments); this.flags.set('prior');
  }
P.got_prior=Game$got_prior;
function Game$set_letters(){
DEBUG && console.log(Game$set_letters, arguments);    var letters = this.round.board.board;
    var cells = this.cells;
    for (var i=0; i < this.size; i++) { cells[i].letter = letters.charAt(i); }
  }
P.set_letters=Game$set_letters;
function Game$clear_path(){
DEBUG && console.log(Game$clear_path, arguments);    for (var i=0; i < this.size; i++) {
      var el = this.cells[i].el2;
      el.removeClass('cursor');
      el.removeClass('inword');
    }
  }
P.clear_path=Game$clear_path;
function Game$guess(g){
DEBUG && console.log(Game$guess, arguments);    var b = this.round.board;
    if (b.buffer_guesses) { b.buffered.push(g.word); if (b.buffered.length > 9) { this.submit_guesses(); } } else {
      b.pending.push(g.word);
      var cutoff = ++b.submitted;
      call_server(this.submit_guess + b.pending.join('/') +'/'+ b.guessct,
        {on_result: 'guess_ack', scope: this, cutoff: cutoff});
    }
    b.score += g.value;
    return b.score;
  }
P.guess=Game$guess;
function Game$submit_guesses(){
DEBUG && console.log(Game$submit_guesses, arguments);    var b = this.round.board;
    b.submitted += b.buffered.length;
    b.pending = b.pending.concat(b.buffered);
    b.buffered = [];
    call_server(this.submit_guess + b.pending.join('/') +'/'+ b.guessct,
      {on_result: 'guess_ack', scope: this, cutoff: b.submitted});

  }
P.submit_guesses=Game$submit_guesses;
function Game$guess_ack(sc){
DEBUG && console.log(Game$guess_ack, arguments);    var b = this.round.board;
    var cutoff = sc.data.cutoff;
    var sg = this.submit_guess;
    if (!b || !b.pending || !sg || cutoff < b.acked ||
        (sc.failed && cutoff < b.submitted) ||
        sc.calling.substr(0, sg.length) != sg) { return; } // Between rounds, taken care of in later submission, or already being retried
    if (sc.failed) {
      call_server(sg + b.pending.join('/') +'/'+ b.guessct, {on_result: 'guess_ack', scope: this, cutoff: cutoff});
    } else {
      b.pending = b.pending.slice(cutoff - b.acked);
      b.acked = cutoff;
    }}
P.guess_ack=Game$guess_ack;
  Game$__init__.apply(this, arguments);
};
function BoardLoader() {
  if (this.constructor !== BoardLoader) { throw "BoardLoader constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
BoardLoader.prototype.__init__ = function () {
  var P=BoardLoader.prototype;
function BoardLoader$__init__(gr){
DEBUG && console.log(BoardLoader$__init__, arguments);    this.gr = gr;
    gr._boardloader = this;
    this.board = { source: gr.source, begins: stamp_time(gr.stamp) };
  }
P.__init__=BoardLoader$__init__;
function BoardLoader$load(attempt){
DEBUG && console.log(BoardLoader$load, arguments);    this.attempt = (attempt || 0) +1;
    this.t = now();
    get_server(this.board.source, { on_result: 'result', scope: this });
  }
P.load=BoardLoader$load;
function BoardLoader$result(sc, b){
DEBUG && console.log(BoardLoader$result, arguments);    if (sc.failed || !b) { this.error(sc); return; }
    b = Ext.applyIf(this.board, b);
    b.ends = b.begins + b.duration * 1000;
    var word, w, raw;
    if (b.words.charAt(0) == '.') {
      // Words are obfuscated to avoid nannyware
      raw = []; word = [];
      var encoding = '5z8hwmqc9jnpk6yx3417drb2gv';
      var encoding2 = 'N)YJB.!VKG$CWXDZ]H*?Q;R#MP';
      for (var i=1; i < b.words.length; i++) {
        var cc = b.words.charCodeAt(i) - 97;
        if ((w=encoding.charAt(cc))) { word.push(w); continue; }
        if ((w=encoding2.charAt(cc))) { word.push(w); raw.push(word.join('')); word =  []; }
      }
    } else {
      raw = b.words.split(','); // Unencoded
    }
    b.raw = raw;
    getset(d_.boards, this.gr.name, Object)[b.stamp] = this.gr.board = b;
    this.done('boardloaded');
  }
P.result=BoardLoader$result;
function BoardLoader$error(sc){
DEBUG && console.log(BoardLoader$error, arguments);    if (this.attempt > 10 || !this.gr.flags.get.live) { this.done('boardfailed'); return; }
    var t = this.t + 2000 - now();
    handoff(this, 'load', [this.attempt], t > 0? t: 1);
  }
P.error=BoardLoader$error;
function BoardLoader$done(flag){
DEBUG && console.log(BoardLoader$done, arguments);    delete this.gr._boardloader;
    this.gr.flags.set(flag);}
P.done=BoardLoader$done;
  BoardLoader$__init__.apply(this, arguments);
};
function ResultLoader() {
  if (this.constructor !== ResultLoader) { throw "ResultLoader constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
ResultLoader.prototype.__init__ = function () {
  var P=ResultLoader.prototype;
function ResultLoader$__init__(gr){
DEBUG && console.log(ResultLoader$__init__, arguments);    this.gr = gr;
    gr._resultloader = this;
    this.source = gr.source +'r';
  }
P.__init__=ResultLoader$__init__;
function ResultLoader$load(attempt){
DEBUG && console.log(ResultLoader$load, arguments);    this.attempt = (attempt || 0) +1;
    this.t = now();
    if (this.attempt == 1) {
      var t = stamp_time(this.gr.stamp) + this.gr.duration * 1000 + 4000 + d_.ms_off - this.t;
      if (t > 0) {
        this.t += t;
        handoff(this, 'load', [0], t);
        return;
      }
    }
    get_server(this.source, { on_result: 'result', scope: this });
  }
P.load=ResultLoader$load;
function ResultLoader$result(sc, data){
DEBUG && console.log(ResultLoader$result, arguments);    if (sc.failed || !data) { this.error(sc); return; }
    this.gr.result = data;
    this.done('resultloaded');
  }
P.result=ResultLoader$result;
function ResultLoader$error(sc){
DEBUG && console.log(ResultLoader$error, arguments);    if (this.attempt > 10 || !this.gr.flags.get.live) { this.done('resultfailed'); return; }
    var t = this.t + 2000 - now();
    handoff(this, 'load', [this.attempt], t > 0? t: 1);
  }
P.error=ResultLoader$error;
function ResultLoader$done(flag){
DEBUG && console.log(ResultLoader$done, arguments);    this.gr.flags.set(flag);
    delete this.gr._resultloader;}
P.done=ResultLoader$done;
  ResultLoader$__init__.apply(this, arguments);
};
function GameRound() {
  if (this.constructor !== GameRound) { throw "GameRound constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
GameRound.prototype.__init__ = function () {
  var P=GameRound.prototype;
function GameRound$__init__(g, listeners, prior){
DEBUG && console.log(GameRound$__init__, arguments);    Ext.apply(this, g);
    this.get_result = prior;
    if (prior > 1) { this.stamp = this.prior; }
    this.flags = new Flags(Model.RoundFlags, listeners);
    this.flags.on({live_boardloaded: this.process_board, live_board_resultloaded: this.loaded_result, scope: this});
  }
P.__init__=GameRound$__init__;
function GameRound$start(){
DEBUG && console.log(GameRound$start, arguments);    var prior = this.get_result;
    this.flags.set('live');
    var b = this.board = d_.boards[this.name] && d_.boards[this.name][this.stamp];
    this.source = '/boards/' + this.name +'/'+ this.stamp.substr(0, 8) +'/'+ this.stamp.substr(8);
    if (b) {
      this.process_board();
      if (prior) {
        if (b.result) { this.process_result(); } else { (new ResultLoader(this)).load(); }
      }
    } else {
      (new BoardLoader(this)).load();
      if (prior) { (new ResultLoader(this)).load(); }
    }
  }
P.start=GameRound$start;
function GameRound$stop(){
DEBUG && console.log(GameRound$stop, arguments);    this.flags.unset('live');
    this.flags.observe.purgeListeners();
  }
P.stop=GameRound$stop;
function GameRound$process_board(){
DEBUG && console.log(GameRound$process_board, arguments);    if (!this.flags.get.live) { return; }
    var w, word, raw = this.board.raw;
    var scoring = load_scoring(this.name);
    /* Words */

    this.words = new ArrBag('word', 'i');
    this.raw_words = new ArrBag('word');
    for (var i_w=0;(i_w<raw.length)&&((w=raw[i_w])||1);i_w++) {
      word = {word: w, players: [], teams: [], who: 'none', value: scoring.word(w)};
      this.raw_words.put(word);
      if (word.value > 0) { this.words.put(word); }
    }
    this.flags.set('board');

  }
P.process_board=GameRound$process_board;
function GameRound$loaded_result(){
DEBUG && console.log(GameRound$loaded_result, arguments);    var b = this.board;
    Ext.applyIf(b, this.result);
    b.result = true;
    this.process_result();
  }
P.loaded_result=GameRound$loaded_result;
function GameRound$process_result(){
DEBUG && console.log(GameRound$process_result, arguments);    var b = this.board;

    /* Players and Teams */
    var raw = b.players;
    var p, players = new ArrBag('raw');
    var t, teams = new ArrBag('name');
    var pi = 0; var ti = 0;
    var mine = d_.acct && d_.acct.substr(0, 4);
    this.me = null;
    for (var i=0; i < raw.length; i++) {
      var nca = raw[i].split(';');
      /* This is where blacklisting / private games code goes */
      if ((d_.banned).hasOwnProperty( nca[1])) { continue; }
      p = {raw: raw[i], name: nca[0], code: nca[1], words: [], guessct: b.guessed[i], i: pi++}; 
      if (nca[2]) { p.source = player_source[nca[2]]; }
      players.put(p);
      if (p.code == mine) { p.is_me = true; this.me = p; }
      if (p.name.match(/^team /i)) {
        t = p.name.substr(5).replace(/:.*/, '');
        if (!teams.has(t)) { teams.put({name: t, words: [], members: [], i: ti++}); }
        p.team = teams.get(t);
        p.team.members.push(p.i);
      }
    }
    this.players = players;
    this.teams = teams;

    /* Found Words*/
    raw = b.found.split('|');
    var me_map = {none: 'only', only: 'only', me: 'me', some: 'me'};
    var not_me_map = {none: 'some', only: 'me', me: 'me', some: 'some'};
    var j, k, code, word, pis;
    for (i=0; i < raw.length; i++) {
      code = raw[i];
      word = this.raw_words.arr[i];
      if (!word.value || !code) { continue; }
      if (code.charAt(0) >='0' && code.charAt(0) <='9') {
        /* Integer list */
        pis = code.split(',');
        for (j = 0; j < pis.length; j++) { pis[j] = parseInt(pis[j], 10); }
      } else {
        /* Bitstring */
        pis = [];
        for (j=0; j < code.length; j++) {
          var c = code.charAt(j);
          for (k=0; k < 6; k++) {
            if (found_bitmap[k].indexOf(c) >=0 ) { pis.push(j * 6 + k); }
          }
        }
      }
      for (var i_j=0;(i_j<pis.length)&&((j=pis[i_j])||1);i_j++) {
        p = players.get(b.players[j]);
        if (!p) { continue; }
        p.words.push(word.i);
        if (p.team) {
          var w = p.team.words;
          if (!w.length || w[w.length - 1] != word.i) { w.push(word.i); word.teams.push(p.team.i); }
        }
        word.players.push(p.i);
        word.who = (p.is_me? me_map: not_me_map)[word.who];
      }
    }

    /* Scores, total, and perfect */
    var words = this.words;
    var scoring = load_scoring(this.name);
    for (var i_p=0;(i_p<players.arr.length)&&((p=players.arr[i_p])||1);i_p++) { scoring.score(this, p); }
    for (var i_p=0;(i_p<teams.arr.length)&&((p=teams.arr[i_p])||1);i_p++)   { scoring.score(this, p); }
    this.perfect = 0; this.total = 0;
    for (var i_word=0;(i_word<words.arr.length)&&((word=words.arr[i_word])||1);i_word++) {
      var ws = word.value; 
      this.perfect += ws;
      if (word.who != 'none') { this.total += ws; }
    }

    /* Score rows for display */
    var s, top, cls, mark, tsize, tname;
    var rows = this.scores = [];
    var myteam = (this.me && this.me.team) || 'no';
    i = 0;
    for (var i_p=0;(i_p<players.arr.length)&&((p=players.arr[i_p])||1);i_p++) {
      if (p.is_me || !p.team || p.team == myteam) {
        cls = p.is_me ? ['me']:(p.team == myteam? ['mine']: []);
        if ((mark = ((d_.marked).hasOwnProperty( p.code) ? (d_.marked)[ p.code]: undefined))) { cls.push(mark); }
        if (p.hasOwnProperty("source")) { cls.push(p.source); }
////        rows.push({c: cls.join(' '), name: HTMLescape(p.name), who: p, score: p.score, i: i++});
        rows.push(['', HTMLescape(p.name), p.score, cls.join(' '), p, i++, 0]);
      }
    }
    if (d_.opts.R.teams) {
      for (var i_t=0;(i_t<teams.arr.length)&&((t=teams.arr[i_t])||1);i_t++) {
        tsize = t.members.length;
        if (tsize > 1) { tname = 'Team '+ HTMLescape(t.name) +' ('+ tsize +')'; }
                  else { if (t == myteam) { continue; } tname = HTMLescape(players.arr[t.members[0]].name); }
////        rows.push({c: 'team', name: 'Team '+ HTMLescape(t.name) +' ('+t.members.length+')', who: t, score: t.score, i: i++});
        cls = ['team'];
        if (t == myteam) { cls.push('mine'); }
        if ((mark = ((d_.marked).hasOwnProperty( 'team '+ t.name) ? (d_.marked)[ 'team '+ t.name]: undefined))) { cls.push(mark); }
        rows.push(['', tname, t.score, cls.join(' '), t, i++, 0]);
      }
    }
/*    if (rows.length) {
      rows.sort(sort_score);
      top = rows[0].score;
      for (var i_s=0;(i_s<rows.length)&&((s=rows[i_s])||1);i_s++) {
        s.rank = (i_s + 1) +'.';
        s.who.rank = i_s;
        if (s.score == top) { s.c += ' top'; }
      }
    }
*////
    if (rows.length) {
      rows.sort(sort_score);
      top = rows[0][2];
      for (var i_s=0;(i_s<rows.length)&&((s=rows[i_s])||1);i_s++) {
        s[0] = (i_s + 1) +'.';
        s[4].rank = i_s;
        if (s[2] == top) { s[3] += ' top'; }
      }
    }
    this.flags.set('result');}
P.process_result=GameRound$process_result;
  GameRound$__init__.apply(this, arguments);
};
function Scoring() {
  if (this.constructor !== Scoring) { throw "Scoring constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
Scoring.prototype.__init__ = function () {
  var P=Scoring.prototype;
function Scoring$__init__(s){
DEBUG && console.log(Scoring$__init__, arguments);    this.by_length = [0, 0, 0, 1, 1, 2, 3, 5, 11, 11, 11, 11, 11, 11, 11, 11, 11];
    this.min_length = 3;
    for (var k in s) { if (s.hasOwnProperty(k)) { this[k] = s[k]; }}
  }
P.__init__=Scoring$__init__;
function Scoring$score(round, p){
DEBUG && console.log(Scoring$score, arguments);    var t = 0;
    var wi, gwa = round.words.arr;
    /* We assume that words have been assigned raw values already */
    for (var i_wi=0;(i_wi<p.words.length)&&((wi=p.words[i_wi])||1);i_wi++) { t += gwa[wi].value; }
    p.score = t;
 }
P.score=Scoring$score;
function Scoring$word(w){
DEBUG && console.log(Scoring$word, arguments);    if (w.length < this.min_length) { return 0; }
    return this.by_length[w.length];
 }
P.word=Scoring$word;
function Scoring$pickle(){
DEBUG && console.log(Scoring$pickle, arguments);    /* Encapsulate data needed to recreate this scoring object */
    return { min_length: this.min_length };}
P.pickle=Scoring$pickle;
  Scoring$__init__.apply(this, arguments);
};
function pathFinder$find ( word, cells ){
    if (!word) { this.found = 0; return []; }
    word = word.toUpperCase();
    // Make an array of characters, converting 'QU' to 'Q'
    var chars = [];
    for (i=0; i < word.length; i++) {
      var ch = word.charAt(i);
      if (ch == "Q") {
        i++;
        if (i < word.length && word.charAt(i) != "U") { break; }
      }
      chars.push(ch);
    }
    this.chars = chars;

    this.seen = new Array(cells.length);
    this.cells = cells;
    for (var i=0; i < cells.length; i++) { this.seen[i] = false; }
    this.longest = [];
    ch = chars[0];
    for (i=0; i < cells.length; i++) {
      if (ch == cells[i].letter) {
        this.path = [];
        if (this._extend_path(0, i)) { break; }
      }
    }
    var wlen = 0;
    i = this.longest.length;
    while (--i >= 0) {
      wlen++;
      if (chars[i] == 'Q') { wlen++; }
    }
    this.found = wlen;
    this.cells = null;
    return this.longest;
  }
function pathFinder$_extend_path ( ci, last ){
    this.path[ci] = last;
    if (++ci == this.chars.length) {
      // This is a full path!
      this.longest = this.path;
      return true;
    }
    var seen = this.seen;
    seen[last] = true;
    var ch = this.chars[ci];
    var nbi, na = this.cells[last].neighbors;
    for (var i_nbi=0;(i_nbi<na.length)&&((nbi=na[i_nbi])||1);i_nbi++) {
      if (!seen[nbi] && this.cells[nbi].letter == ch) { if (this._extend_path(ci, nbi)) { return true; } }
    }
    // Remember the high-water mark
    if (ci > this.longest.length) { this.longest = this.path.slice(0, ci); }
    // We didn't find a full path
    seen[last] = false;
    return false;}

var pathFinder = {find:pathFinder$find,
_extend_path:pathFinder$_extend_path};

function GameLayout$init(){
    this.flags = Main.flags;
    // Fix broken behavior
    var dd = Ext.dd.DragDropMgr;
    function ddhmu(e) { if (!this.dragCurrent) { return; } this.handleMouseUp(e); }
    Ext.EventManager.un(Ext.getDoc(), "mouseup",  dd.handleMouseUp, dd);
    Ext.EventManager.on(Ext.getDoc(), "mouseup",  ddhmu, dd);
    Ext.QuickTips.init();
    Ext.apply(Ext.QuickTips.getQuickTip(), { showDelay: 0, dismissDelay: 0 });
    Ext.QuickTips.disable();
    Ext.state.Manager.setProvider(new C_GameStateProvider());
    HoverTrack.init();

    this.colsizes = null;
    this.boardsize = null;
    this.portlets_done = false;
    Delayed.add('size_portlets', this.size_portlets, this, 100);
    Delayed.add('show_prior_scores', this.show_prior_scores, this, 100);
    Delayed.add('show_prior_words', this.show_prior_words, this, 50);
    Delayed.add('player_focus', this.player_focus, this, 50);
    Delayed.add('typing', Main.typing, Main, 100);
    Delayed.add('get_chat', Main.get_chat, Main, 2000);
    this.gc_extra = Ext.isGecko ? 0 : 8;

    window.L_viewport = new Ext.Viewport(View.viewport);
    window.P_main = Ext.getCmp('main');
    P_main.add(View.loginpage);
    P_main.add(View.gamepage);
    window.P_gamepage = Ext.getCmp('gamepage');
    P_gamepage.items.first().add(View.round_status);
    P_gamepage.items.first().add(View.guesses);
    P_gamepage.items.first().add(View.prior_words_list);
    P_gamepage.items.first().add(View.chat);

    window.Notifier1 = new Notifier('general-notify', 'main', ['main', 'tr-tr', [-10, 10]]);
    Ext.DomHelper.append('main', {id: 'testrow'});

    var pn, P_ = ['sidebar', 'main', 'infopage', 'gamepage', 'loginpage', 'status', 'scores', 'scoreboard',
      'board', 'boards', 'guesses', 'guesses_dv', 'prior_words', 'output', 'input_form', 'chat', 'chat_grid'];
    for (var i_pn=0;(i_pn<P_.length)&&((pn=P_[i_pn])||1);i_pn++) { window['P_'+ pn] = Ext.getCmp(pn); }
    this.flags.set('P');

    P_main.doLayout();
    Ext.get('loginform').set({target: '_blank', action: 'about:'});
    P_input = Ext.get('input', true);
    P_input.on({keypress: this.guess_pressed, scope: this, keydown: this.guess_pressed2});
    Ext.QuickTips.register({target: P_input, showDelay: 100, hideDelay: 1000,
      text: "Type your guess here<br>Press Enter or the spacebar to submit it<br>Press Del to erase it"});
    Ext.getDoc().on('keydown', this.guess_pressed3, this);

    P_sidebar.body.on({
        mousedown: { fn: Main.sidebarAction, scope: Main, delegate:'a' },
        click: { fn: function (e, t) { if (t.className != 'external') { e.stopEvent(); } }, delegate: 'a' }
    });

    /* Make all words clickable and hoverable */
    this.wordtip = new Ext.ToolTip({showDelay: 0, hideDelay: 1000, trackMouse: true,
      html: "Click to look words up in a free online dictionary.<br>(Opens in a new window)"});

    P_scores.on({ delegate: P_scores.getView().rowSelector, scope: this, mouseover: this.player_on });
    P_scores.on({rowclick: gl.scores_rowclick});
    P_scores.getSelectionModel().lock();
    var tpl = P_scores.getView().templates;
    tpl.row = new Ext.XTemplate(tpl.row.html.replace('tr><tr', 'tr><tpl if="body"><tr').replace('/tr></', '/tr></tpl></'));

    this.scoremenu_p = new Ext.menu.Menu(View.scoremenu);
    this.scoremenu_t = new Ext.menu.Menu(View.scoremenu_t);
    this.scoremenu_p.on({ itemclick: gl.menuclick });
    this.scoremenu_t.on({ itemclick: gl.menuclick });
    this.scoremenu = { hide: Ext.emptyFn, isVisible: Ext.emptyFn };
    this.scoremenurow = null;

    P_boards.relayEvents(P_boards.body, ['mousedown']);
    var rsz = new Ext.Resizable("boards", { handles: 's', minHeight: 100, maxHeight: 1000, pinned: true });
    rsz.on('resize', gl.board_resize);

    P_chat.on({expand: gl.chat_started, collapse: gl.chat_stopped});
    View.chat_store.loadData([]);

    this.guess_out = new Notifier('guess_out', 'main', [P_input, 'l-r?', [4, 0]]);

    P_gamepage.on({drop: main2.portlet_drop});
    P_main.on('resize', Delayed.setter('size_portlets'));
    P_main.on('afterlayout', gl.main_active);

    var store = View.prior_words_store;
    function substore(who) {
      var pstore = View['prior_words_'+ who + '_store'];
      pstore.proxy = new C_StoreProxy(store, words_who(who));
      store.on('load', pstore.load, pstore);
    }
    substore('none');
    substore('some');
    substore('me');
    substore('only');

  }
function GameLayout$board_setup(){
    /* Set up either click-to-pick or click-to-rotate */
    P_boards.purgeListeners();
    if (d_.opts.B.click2) { 
      P_boards.on({mousedown: this.board_click, scope: this, delegate: 'td.cell', stopEvent: true});
    } else {
      P_boards.on({mousedown: main2.rotate, stopEvent: true});
    }
//    P_boards.on('afterlayout', gl.board_size);

  }
function GameLayout$init_cols(){
    var col, cols = P_gamepage.items.items;
var trace = window.trace = ['init_cols', cols.length];
    var portlets = {};
    for (var i_col=0;(i_col<cols.length)&&((col=cols[i_col])||1);i_col++) {
      var p, cportlets = col.items.items;
      for (var i_p=0;(i_p<cportlets.length)&&((p=cportlets[i_p])||1);i_p++) { portlets[p.pcode] = p; }
//window.trace.push(p.pcode +':'+ p.id); }
    }
    var cpcodes = (d_.cols || 'AB-CD-EFGX').split('-');
//window.trace.push(cpcodes.length);
    for (var i_col=0;(i_col<cols.length)&&((col=cols[i_col])||1);i_col++) {
      var pcodes = cpcodes[i_col];
//window.trace.push('c'+ i_col +':'+ pcodes);
      for (var i=0; i < pcodes.length; i++) {
//window.trace = trace; trace.push('p'+ i +':'+ pcodes.charAt(i));
        var portlet = portlets[pcodes.charAt(i)];
        if (!portlet) { continue; }
        if ((p = col.items.items[i]) != portlet) {
          portlet.el.dom.parentNode.removeChild(portlet.el.dom);
          if (p) { col.insert(i, portlet); } else { col.add(portlet); }
        }
        if (!this.portlets_done) { portlet.on({expand: gl.portlet_sizing, collapse: gl.portlet_sizing}); }
      }
    }
    if (!this.portlets_done) {
      P_sidebar.on({expand: gl.portlet_sizing, collapse: gl.portlet_sizing});
      this.portlets_done = true;
    }
window.trace = null;

  }
function GameLayout$size_portlets(delayed){
    if (!(P_gamepage && P_gamepage.rendered && P_gamepage.isVisible())) { return true; }
    delayed.done = true;
    if (!Main.flags.get.gamepage_round) { return false; }

    var i, el, col, cols, sz, nsz, gv, v, pws, maxh, slack, p, gcah, omh, th, thc, h, nh, ps, ah, nah, nahc, nhr, freed;
    sz = this.colsizes;
    pws = P_prior_words.items.items.concat();
    if (pws[1] == P_prior_words.layout.activeItem) { pws.reverse(); }
    gv = P_scores.getView();
    ps = [
      [P_scoreboard, gv.scroller, gv.el, P_scores.body],
      [P_prior_words, pws[0].body, pws[1].body]
    ];
    freed = [d_.opts.R.height, d_.opts.W.height];

    if (!sz) {
      // initial setup
      pws[0].body.addClass('scrolly');
      pws[1].body.addClass('scrolly');
      P_gamepage.doLayout();
    }

    function auto_h(pi, h) {
      var el, p, pc, sc;
      p = ps[pi]; sc = p[1].dom; pc = p[1];
      if (h !== false) {
        Ext.util.CSS.updateRule('#'+ p[0].id +' .scrolly', 'height', h +'px');
      }
      if (pc.autoH || (sc.scrollHeight > sc.clientHeight)) { return false; }
      pc.autoH = true;
      for (var i_el=0;(i_el<p.length)&&((el=p[i_el])||1);i_el++) { el.setHeight(); }
      Ext.util.CSS.updateRule('#'+ p[0].id +' .scrolly', 'height', 'auto');
      return true;
    }
    for (i=0; i < ps.length; i++) { if (!freed[i]) { auto_h(i, false); } }
    
    nsz = this.colsizes = {gamepage: P_gamepage.getSize()};
    cols = P_gamepage.items.items;
    for (var i_col=0;(i_col<cols.length)&&((col=cols[i_col])||1);i_col++) { nsz[col.id] = col.getSize(); }

    // Need to redo layout?
    if (sz && nsz.gamepage.width != sz.gamepage.width) {
      P_input_form.doLayout();
      P_gamepage.doLayout();
      gl.board_size();
    }

    function set_h(p, h, pi) {
      p.autoH = false;
      Ext.util.CSS.updateRule('#'+ ps[pi][0].id +' .scrolly', 'height', h +'px');
    }

    maxh = P_main.ownerCt.el.getViewSize().height - 8;
    col = ps[0][0].ownerCt;
    if (!freed[0] && !freed[1] && col == ps[1][0].ownerCt) {
      // one column has all resizables
      slack = maxh - col.getSize().height;
      h = a_map(ps, function (p) { return p[1].getSize().height; });
      ah = a_map(ps, function (p) { return p[1].autoH; });
      
      if (slack > 0) {
        // Collect non-autoHeight panels and grow them.
        nah = [];
        for (var i_v=0;(i_v<ah.length)&&((v=ah[i_v])||1);i_v++) { if (!v) { nah.push(i_v); } } // panels to grow
        th = slack;
        while (th > 0 && (nahc = nah.length) > 0) {
          for (var i_v=0;(i_v<nah.length)&&((v=nah[i_v])||1);i_v++) { th += h[v]; } // Total height to be divided
          ah = nah; nah = [];
          for (var i_v=0;(i_v<ah.length)&&((v=ah[i_v])||1);i_v++) {
            if (!auto_h(v, Math.floor(th / nahc-- + 0.1))) { nah.push(v); }
            h[v] = ps[v][1].getSize().height;
            th -= h[v];
          }
        }
      } else {
        // Collect panels over the minimum size and shrink them.
        omh = [];
        for (var i_v=0;(i_v<h.length)&&((v=h[i_v])||1);i_v++) { if (v > 200) { omh.push([v, ps[i_v][1], i_v]); } } // panels to shrink
        omh.sort(function (a, b) { return b[0] - a[0]; }); // sort by height, descending
        th = slack;
        for (var i_v=0;(i_v<omh.length)&&((v=omh[i_v])||1);i_v++) {
          th += v[0];
          thc = i_v + 1;
          if (thc == omh.length || th >= omh[thc] * thc) {
            nh = Math.floor(th / thc + 0.1);
            nhr = th - nh * thc;
            while (thc-- > 0) { set_h(omh[thc][1], nh + (nhr-- > 0 ? 1:0), omh[thc][2]); }
          }
        }
      }
    } else {
      // Each resizable in its own column
      for (var i_p=0;(i_p<ps.length)&&((p=ps[i_p])||1);i_p++) {
        if (freed[i_p]) { continue; }
        slack = maxh - p[0].ownerCt.getSize().height;
        p = p[1];
        if (slack !== 0) {
          h = p.getSize().height;
          if (slack > 0) {
            if (!p.autoH) { auto_h(i_p, h + slack); }
          } else {
            if (h > 200) { set_h(p, Math.max(200, h + slack), i_p); }
          }
        }
      }
    }

    if (Ext.isIE) { Ext.select('.x-toolbar').repaint(); }
    // Need to adjust grid columns?
    col = ps[0][0].ownerCt.id;
    if (!sz || nsz[col].width != sz[col].width) { gv.fitColumns(); }
    return false;

  }
function GameLayout$update_prior(prior){
    if (this.prior === prior) { return; }
    this.prior = prior;
    this.player_args = {};
    this.scoremenu.hide();
    var mine = 'No players';
    if (prior && prior.scores && prior.scores.length) {
      var scores = prior.scores;
      if (prior.me) {
        mine = prior.me.rank + 1;
        if (mine % 10 > 3 || (mine > 3 && mine < 20)) { mine += 'th'; } else { mine += ['th', 'st', 'nd', 'rd'][mine % 10]; }
        mine += ' of ' + scores.length;
      } else { mine = scores.length + ' players'; }
      Delayed.set('show_prior_scores');
    }
    this.scoreboard_set('Last Round: '+ mine, templates.score_summ(prior));
    var wall = prior.words.arr;
    if (d_.opts.W.sort == 1 && wall.length > 12) { wall = this.shuffled(wall, 12); }
    if (d_.opts.W.sort == 2) { wall = wall.concat(); }
    else { wall = wall.concat(); wall.sort(function(a, b) { return b.value - a.value; }); }
    if (d_.opts.W.reverse) { wall.reverse(); }
    this.sorted_prior_words = wall;
    View.prior_words_store.loadData({words: wall});
    Delayed.set('size_portlets');

  }
function GameLayout$show_prior_scores(delayed){
    var rows = Main.game.prior.scores;
    if (!rows.length) { return false; }
    P_scores.body.addClass('x-hide-visibility');
    var sizerow = rows[0].concat();
    sizerow[0] = rows[rows.length - 1][0];
    View.prior_scores_store.loadData([sizerow]);
      var cm = P_scores.getColumnModel();
      var v = P_scores.getView();
      var cell = v.getCell(0, 0).firstChild;
      var w = Ext.fly(cell).getTextWidth() + Ext.fly(cell).getPadding('lr');
      cm.setColumnWidth(0, w);
      cell = v.getCell(0, 2).firstChild;
      w = Ext.fly(cell).getTextWidth() + Ext.fly(cell).getPadding('lr');
      cm.setColumnWidth(2, w);
      v.fitColumns();
    this.abbreviate_scores(rows);
    P_scores.body.removeClass('x-hide-visibility');
    delayed.done = true;
    return false;
  }
function GameLayout$abbreviate_scores(rows){
    rows = rows || Main.game.prior.scores;
    if (rows.length > 25 && !d_.opts.R.all) {
      // Initially show only "interesting" scores: top 5, at least 2 above & below marked players
      var row, exrow, ctdown = 4;
      var dull = [];
      for (var i_row=0;(i_row<rows.length)&&((row=rows[i_row])||1);i_row++) {
        if (row[3] && row[3] != 'team') {
          if (ctdown < 0) {
            if (ctdown < -4) { exrow[6] = -2-ctdown; ctdown = -2; }
            dull.length += ctdown;
          }
          if (ctdown < 2) { ctdown = 2; }
          exrow = row;
        } else {
          if (ctdown-- < 1) { dull.push(row); } else { exrow = row; }
        }
      }
      if (ctdown < 0) { if (ctdown < -4) { dull.length -= 2; exrow[6] = -2-ctdown; } else { dull.length += ctdown; } }
      for (var i_row=0;(i_row<dull.length)&&((row=dull[i_row])||1);i_row++) { row[6] = -1; }
    }
    View.prior_scores_store.loadData(rows);

  }
function GameLayout$show_prior_words(delayed){
    var wall = this.sorted_prior_words;
    if (d_.opts.W.reverse) { wall.reverse(); }
    View.prior_words_store.loadData({words: wall});
    delayed.done = true;
    return false;

  }
function GameLayout$guess_pressed(e){
////    if (Main.locked) { e.stopEvent(); return; }
    var c = e.getCharCode();
    if (c == e.RETURN) { e.stopEvent(); Main.guess_submit(); return; }
    Delayed.reset('typing');
  }
function GameLayout$guess_pressed2(e){
    var c = e.getCharCode();
    if (c == e.TAB) { e.stopEvent(); Main.rotate(); return; }
////    if (Main.locked) { e.stopEvent(); return; }
    if (c == e.BACKSPACE) { Delayed.reset('typing'); return; }
    if (c == e.DELETE) { Main.no_guess(); }
  }
function GameLayout$guess_pressed3(e, t){
    if (t.tagName == 'INPUT' || t.tagName == 'TEXTAREA' || t.form) { return; }
////    if (Main.locked) {
////      var c = e.getCharCode();
////      if (c == e.BACKSPACE) { e.stopEvent(); }
////      return;
////    }
    this.guess_pressed2(e);
    if (refocus() && Ext.isIE) {
      var r = P_input.dom.createTextRange();
      r.move('textedit');
      r.select();
    }

  }
function GameLayout$board_click(ev, target, o){
    var cell = Ext.get(target);
    if (cell.hasClass('cursor')) { Main.guess_submit(); return; }
    Main.click_cube(cell);
  }
function GameLayout$board_resize(h, setH){
    var maxw = P_board.getInnerWidth();
    if (maxw <= 0) { return; }
    if (!h || h == 1 || h > maxw) { h = maxw; }
    var sz = this.boardsize;
    var nsz = P_board.getSize();
    if (sz && sz.height == h && sz.width == nsz.width) { if (Ext.isGecko) { P_boards.el.setSize('auto', h); } return; }

//    P_boards.addClass('x-hide-visibility');
    var n = (Main.game.name == '4x4') ? 4 : 5;
    var th = h; th = th - (th % n);

    var fss = Ext.util.CSS.getRule('#boards .cell').style;
    var bsb = [8, 200];
    var fs = 70;
    var fstmp, fsp = 4;

    var testcell='<div class="cell">Qu</div>';
    var testel = Ext.get('testrow');
    testel.update(testcell+testcell+testcell+testcell+(n > 4? testcell: ''));
    var fsg = testel.dom.style;
    fsg.fontSize = fs +'px';
    while (bsb[0] < bsb[1]) {
      if (testel.getWidth()*n > th) { bsb[1] = fs - 1; } else { bsb[0] = fs; }
      fs = Math.floor((bsb[0] + bsb[1] + 1.5) / 2);
      fsg.fontSize = fs +'px';
    }
    fss.fontSize = fs +'px';
    var w = testel.getWidth();

    if (!sz) { P_boards.body.addClass('boards-body'); }
    P_boards.setSize('auto', 'auto');
    P_boards.el.setSize('auto', 'auto');
    fss.height = w +'px';
    fss.width = w +'px';
    var bh = P_boards.body.getHeight();
    if (h > bh) { if (h >= maxw) { h = maxw; } else { h = bh; } }

    this.boardsize = { height: h, width: P_board.getSize().width };
//    P_boards.removeClass('x-hide-visibility');
    if (h == maxw) { h = 1; } // Indicates max height
    if (setH && d_.opts.B.height != h) {
      d_.opts.B.height = h;
      Main.save_options();
    }

  }
function GameLayout$player_on (ev, target, o){
    var args, row, id, gv, m, dv, dvs, rec, wi, words, focus, refocus;
    if (!(args = this.player_args)) { return; }
    target = Ext.get(target);
    if (target.findParent('tr.x-grid3-row-body-tr')) { this.player_out(); return; }

    gv = P_scores.getView();
    id = gv.findRowIndex(target.dom);
    row = Ext.get(target.findParent(gv.rowSelector));

    m = this.scoremenu;
    if (id !== this.scoremenurow === m.isVisible()) { this.scoremenurow = null; m.hide(); } 

    if (args.hover) { args.hover.removeClass("x-grid3-row-over"); args.hover=null;}
    refocus = true;
    if ((focus = args.focus)) {
      if ((refocus = !(args.locked || focus.id == id)) && focus.focused) {
        dvs = this.player_word_dvs();
        for (var i_dv=0;(i_dv<dvs.length)&&((dv=dvs[i_dv])||1);i_dv++) { dv.clearSelections(true); }
      }
      if (refocus) { args.focus = null; }
    }
    if (!id && id !== 0) { return; }
    HoverTrack.track(['scores', 'x-menu', 'x-shadow', 'x-ie-shadow'], this, this.player_out);
    (args.hover = row).addClass("x-grid3-row-over");
    if (!refocus) { return; }
    args.focus = { id: id, row: row, focused: false };
    Delayed.set('player_focus');

  }
function GameLayout$player_focus(delayed){
    var focus, args = this.player_args;
    if (!args || !(focus = args.focus) || focus.focused) { return false; }
    delayed.done = focus.focused = true;
    var wi, words = P_scores.getStore().getAt(focus.id).data.who.words;
    var dv, dvs = this.player_word_dvs();
    for (var i_wi=0;(i_wi<words.length)&&((wi=words[i_wi])||1);i_wi++) {
      for (var i_dv=0;(i_dv<dvs.length)&&((dv=dvs[i_dv])||1);i_dv++) { dv.select(dv.idpref + wi, true, true); }
    }
    return false;

  }
function GameLayout$player_word_dvs(){
    return a_filter(a_map(['prior_words_dv', 'prior_words_some', 'prior_words_me'], function (item) { return Ext.getCmp(item); }));
  }
function GameLayout$player_out (){
    this.scoremenu.hide();
    var focus, args, dv, dvs;
    if (!(args = this.player_args)) { return; }
    if (args.hover) { args.hover.removeClass("x-grid3-row-over"); args.hover = null; }
    if (args.locked || !(focus = args.focus)) { return; }
    args.focus = null;
    if (!focus.focused) { return; }
    dvs = this.player_word_dvs();
    for (var i_dv=0;(i_dv<dvs.length)&&((dv=dvs[i_dv])||1);i_dv++) { dv.clearSelections(true); }

  }
function GameLayout$scores_rowclick(rowi, x, y){
    refocus();
    var row = Ext.get(P_scores.getView().getRow(rowi));
    if (this.scoremenurow == rowi) { this.scoremenurow = null; this.scoremenu.hide(); return; }
    this.scoremenurow = rowi;
    var m = this.scoremenu = row.hasClass('team')? this.scoremenu_t: this.scoremenu_p;
    m.showAt([x, y]);

  }
function GameLayout$scores_expclick(rowi, tr){
    refocus();
    var row = P_scores.getStore().getAt(rowi);
    Ext.fly(tr).remove();
    var v = P_scores.getView();
    Ext.fly(v.getRow(rowi)).removeClass('x-grid3-row-over');
    var x = row.get('x'); row.set('x', 0);
    handoff(this, 'scores_expclick2', [v, x, rowi + 1], 50);
  }
function GameLayout$scores_expclick2(v, x, rowi){
    var s = P_scores.getStore();
    for (;x--;rowi++) {
      Ext.fly(v.getRow(rowi)).removeClass('dull');
      s.getAt(rowi).set('x', 0);
      if (x % 20 == 9) { handoff(this, 'scores_expclick2', [v, x, rowi + 1], 50); return; }
    }
    Delayed.set('size_portlets');

  }
function GameLayout$guess_over(dv, i, item, e){
    if (!Ext.QuickTips.isEnabled()) { return; }
    HoverTrack.track([dv.el], this.wordtip, this.wordtip.hide);
    this.wordtip.onMouseMove(e);
    this.wordtip.show();
  }
function GameLayout$word_over(dv, i, item, e){
    if (Ext.QuickTips.isEnabled()) {
      this.wordtip.onMouseMove(e);
      this.wordtip.show();
    }
    var pr = Main.game && Main.game.prior;
    if (!(pr && pr.words && pr.players)) { return; }
    item = dv.getRecord(item).id;
    var pi, word = pr.words.get(item);
window.trace = [item, i, repr(word), pr.words.arr.length];
    if (!word) { return; }
    HoverTrack.track([dv.el], this, this.word_out);
    var rows = [];
    var pis = word.players;
    var arr = pr.players.arr;
    for (var i_pi=0;(i_pi<pis.length)&&((pi=pis[i_pi])||1);i_pi++) { rows.push(arr[pi].rank); }
    pis = word.teams;
    arr = pr.teams.arr;
    for (var i_pi=0;(i_pi<pis.length)&&((pi=pis[i_pi])||1);i_pi++) { rows.push(arr[pi].rank); }
window.trace=null;
    var sm = P_scores.getSelectionModel();
    sm.unlock(); sm.selectRows(rows); sm.lock();

  }
function GameLayout$word_out (ev, target, o){
    this.wordtip.hide();
    var sm = P_scores.getSelectionModel();
    sm.unlock(); sm.clearSelections(); sm.lock();
  }
function GameLayout$word_click (dv, i, item, ev){
    refocus();
    window[nepo]('http://www.thefreedictionary.com/' + dv.getRecord(item).id, '_blank');
    return false;
  }
function GameLayout$shuffled(wall, n){
    var word, words = [];
    for (var i_word=0;(i_word<wall.length)&&((word=wall[i_word])||1);i_word++) { 
      words.push(word);
      word.rnd = Math.random();
    }
    words.sort(function(a, b) { return a.rnd - b.rnd; });
    var more = words.length - n;
    words.length = n;
    // words.push({sep: ', and ' + more + ' more.', word: ''});
    return words;    
  }
function GameLayout$ban_offensive (item, ev){
 this.ban('off');
  }
function GameLayout$ban_cheat (item, ev){
 this.ban('cheat');
  }
function GameLayout$ban_other (item, ev){
 this.ban();
  }
function GameLayout$ban(reason){
    var rec = P_scores.getStore().getAt(this.scoremenurow);
    var ps, p = rec.data.who;
    if (p.members) {
      var players = this.prior.players.arr;
      ps = p.members.concat();
      for (var i_p=0;(i_p<ps.length)&&((p=ps[i_p])||1);i_p++) { ps[i_p] = players[p]; }
    } else { ps = [p]; }
    for (var i_p=0;(i_p<ps.length)&&((p=ps[i_p])||1);i_p++) { d_.banned[p.code] = p.name; }
    Main.filter_chat();
    if (reason && d_.acct) { for (var i_p=0;(i_p<ps.length)&&((p=ps[i_p])||1);i_p++) { send_server('/misc/ban/'+ reason +'/'+ p.code); } }
    this.player_out();
    P_scores.getStore().remove(rec);
  }
function GameLayout$mark_friend (item, ev){
 this.mark('friend');
  }
function GameLayout$mark_watch (item, ev){
 this.mark('watch');
  }
function GameLayout$mark(reason){
    var rec = P_scores.getStore().getAt(this.scoremenurow);
    var p = rec.data.who;
    d_.marked[p.code || 'team '+ p.name] = reason;
    P_scores.getView().refreshRow(rec);
    P_chat_grid.getView().refresh();
  }
function GameLayout$join_t(item, ev){
    var rec = P_scores.getStore().getAt(this.scoremenurow);
    var cat, n = get_cookie('name') || '';
    if (n.substr(0, 5) == 'Team ' && ((cat = n.indexOf(':')) != -1)) { n = n.substr(cat + 2); }
    n = 'Team '+ rec.data.who.name +': '+ n;
    set_cookie('name', n, 365*24, '/');
    window.Notifier1.show(templates.name_changed({n: n}));
  }
function GameLayout$scoreboard_set(title, footer){
    window.trace = ['score', title];
    P_scoreboard.setTitle(title);
    window.trace = ['summ', footer];
    Ext.get(P_scoreboard.getBottomToolbar().items.first().el).update(footer);
    window.trace = null;
  }
function GameLayout$set_theme(i){
    var theme = View.themes[i];
    if (theme == this.active_theme) { return; }
    this.active_theme = theme;
    switch (theme.src) {
      case 0: Ext.util.CSS.removeStyleSheet('altss'); break;
      case 1: Ext.util.CSS.swapStyleSheet('altss', '/lib/ext2.0.1/resources/css/xtheme-'+ theme.url +'.css'); break;
      default: Ext.util.CSS.swapStyleSheet('altss', theme.url); break;
    }
  }
function GameLayout$reload_gamepage(){
    var tool;
    Ext.getCmp('b0').hide();
    Ext.getCmp('b4x4').hide();
    Ext.getCmp('b5x5').hide();
    gl.board_size();
    Ext.getCmp('b'+ Main.game.name).show();

    P_prior_words.layout.setActiveItem(d_.opts.W.group || 0);
    if (d_.opts.P.theme) { this.set_theme(d_.opts.P.theme); }
    if (d_.opts.S.good == 2 || d_.opts.S.feedback == 2) { P_output.show(); } else { P_output.hide(); }

    this.setup_grid();

    this.set_tool_image(null, ['plus', 'minus'], d_.opts.R.height, P_scoreboard);
    this.set_tool_image(null, ['plus', 'minus'], d_.opts.W.height, P_prior_words);
    this.set_tool_image(null, ['pin', 'unpin'], Main.paused, P_status);

    Main.flags.set('gamepage');
  }
function GameLayout$setup_grid(){
    var ah = d_.opts.R.height;
    var gv = P_scores.getView();
    gv.scrollOffset = ah? 0: 19;
    var el, sels = [P_scoreboard, gv.scroller, gv.el, P_scores.body];
    for (var i_el=0;(i_el<sels.length)&&((el=sels[i_el])||1);i_el++) { el.setHeight(); }
    if (ah) { gv.scroller.removeClass('scrolly'); } else { gv.scroller.addClass('scrolly'); }
    gv.fitColumns();

  }
function GameLayout$set_tool_image(tool, choices, test, p){
    test = test? 1: 0;
    if (!(tool || (tool = p.header.child('.x-tool-'+ choices[1 - test])))) { return; }
    tool.addClass('x-tool-'+ choices[test]);
    tool.removeClass('x-tool-'+ choices[1 - test]);}

var GameLayout = {init:GameLayout$init,
board_setup:GameLayout$board_setup,
init_cols:GameLayout$init_cols,
size_portlets:GameLayout$size_portlets,
update_prior:GameLayout$update_prior,
show_prior_scores:GameLayout$show_prior_scores,
abbreviate_scores:GameLayout$abbreviate_scores,
show_prior_words:GameLayout$show_prior_words,
guess_pressed:GameLayout$guess_pressed,
guess_pressed2:GameLayout$guess_pressed2,
guess_pressed3:GameLayout$guess_pressed3,
board_click:GameLayout$board_click,
board_resize:GameLayout$board_resize,
player_on:GameLayout$player_on,
player_focus:GameLayout$player_focus,
player_word_dvs:GameLayout$player_word_dvs,
player_out:GameLayout$player_out,
scores_rowclick:GameLayout$scores_rowclick,
scores_expclick:GameLayout$scores_expclick,
scores_expclick2:GameLayout$scores_expclick2,
guess_over:GameLayout$guess_over,
word_over:GameLayout$word_over,
word_out:GameLayout$word_out,
word_click:GameLayout$word_click,
shuffled:GameLayout$shuffled,
ban_offensive:GameLayout$ban_offensive,
ban_cheat:GameLayout$ban_cheat,
ban_other:GameLayout$ban_other,
ban:GameLayout$ban,
mark_friend:GameLayout$mark_friend,
mark_watch:GameLayout$mark_watch,
mark:GameLayout$mark,
join_t:GameLayout$join_t,
scoreboard_set:GameLayout$scoreboard_set,
set_theme:GameLayout$set_theme,
reload_gamepage:GameLayout$reload_gamepage,
setup_grid:GameLayout$setup_grid,
set_tool_image:GameLayout$set_tool_image};

function AllGames$__init__(){
    this.gsall = this.glist = this.games = this.loading = this.announce_text = null;
  }
function AllGames$load(){
    if (this.loading) { // Retrying
      if (this.gsall) { this.loading = false; return; }
    } else {
      this.gsall = this.glist = this.games = null;
      this.loading = true;
      Main.flags.unset('games', 'round', 'prior');
    }
    get_server('/gs/all', {on_result: 'loaded', on_error: 'error', scope: this});
    get_server('/gs/announce.txt', {on_result: 'announce', scope: this}, true);
  }
function AllGames$loaded(sc, gsall){
    if (!gsall) { this.error(sc); return; }
    if (gsall.webv > d_.html_version) { Main.warn_reload(); return; }
    this.gsall = gsall;
    this.loading = false;
    if (gsall.status) { return; }

    var game, glist = [];
    var games = this.games = gsall.games;
    for (var name in games) { glist.push(name); }
    glist.sort();
    for (var i_game=0;(i_game<glist.length)&&((game=glist[i_game])||1);i_game++) { glist[i_game] = games[game]; glist[i_game].name = game; }
    this.glist = glist;
    Main.flags.set('games');
  }
function AllGames$error(sc){
    if (this.gsall) { this.loading = false; return; }
    var t = sc.sent_at + 5000 - now();
    handoff(this, 'load', [], t > 0? t: 1);
  }
function AllGames$list_games(){
    Ext.getCmp('sidebar-play').body.update((templates.game_choices({'@games': {'*': this.glist}})));
  }
function AllGames$announce(sc, txt){
    if (sc.failed || this.announce_text == txt) { return; }
    this.announce_text = txt;
    if (txt) { alert_box(txt, { title: 'Announcement!' }); }}

var AllGames = {__init__:AllGames$__init__,
load:AllGames$load,
loaded:AllGames$loaded,
error:AllGames$error,
list_games:AllGames$list_games,
announce:AllGames$announce};
AllGames.__init__();

function Calibrate$check(attempt){
    this.attempt = (attempt || 0) +1;
    call_server('/calibrate', {on_result: 'recheck', scope: this, calibrate: 1});
  }
function Calibrate$recheck(sc){
    if (this.attempt < 3 || (d_.ms_off_gap > 2000 && this.attempt < 7)) { handoff(this, 'check', [this.attempt]); return; }}

var Calibrate = {check:Calibrate$check,
recheck:Calibrate$recheck};

function Observe(o) {
  this.events = {};
  this.on(o);
}
Ext.extend(Observe, Ext.util.Observable);

function Flags() {
  if (this.constructor !== Flags) { throw "Flags constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
Flags.prototype.__init__ = function () {
  var P=Flags.prototype;
function Flags$__init__(flags, listeners){
DEBUG && console.log(Flags$__init__, arguments);    this.flags = flags;
    var get = this.get = {};
    var flag, fset = flags.fset;
    for (var i_flag=0;(i_flag<fset.length)&&((flag=fset[i_flag])||1);i_flag++) { get[flag] = false; }
    this.dep = flags.fdep.concat();
    this.observe = new Observe(listeners);
  }
P.__init__=Flags$__init__;
function Flags$set(flag, force){
DEBUG && console.log(Flags$set, arguments);    var collector = [];
    if (force) { this.unset(flag); }
    if (!this._set(flag, collector)) { return; }
    var ob = this.observe;
    for (var i_flag=0;(i_flag<collector.length)&&((flag=collector[i_flag])||1);i_flag++) { ob.fireEvent(flag); }
window.trace = [flag];
  }
P.set=Flags$set;
function Flags$_set(flag, collector){
DEBUG && console.log(Flags$_set, arguments);    var flags = this.flags;
    if (this.get[flag]) { return false; }
    this.get[flag] = true;
    collector.push(flag);

    var check, checks = flags.check[flag];
    if (checks) { for (var i_check=0;(i_check<checks.length)&&((check=checks[i_check])||1);i_check++) {
      if (!--this.dep[check]) { this._set(flags.names[check], collector); }
    }}
    return true;
  }
P._set=Flags$_set;
function Flags$unset(){
DEBUG && console.log(Flags$unset, arguments);    var flags = this.flags;
    var flag, args = arguments;
    for (var i_flag=0;(i_flag<args.length)&&((flag=args[i_flag])||1);i_flag++) {
      if (this.get[flag]) {
        this.get[flag] = false;
        var check, checks = flags.check[flag];
        if (checks) { for (var i_check=0;(i_check<checks.length)&&((check=checks[i_check])||1);i_check++) {
            this.dep[check]++;
            this.unset(flags.names[check]);
        }}
      }
    }
  }
P.unset=Flags$unset;
function Flags$reset(){
DEBUG && console.log(Flags$reset, arguments);    var flag, flags = this.flags;
    var fset = flags.fset;
    for (var i_flag=0;(i_flag<fset.length)&&((flag=fset[i_flag])||1);i_flag++) { this.get[flag] = false; }
    this.dep = flags.fdep.concat();
  }
P.reset=Flags$reset;
function Flags$on(){
DEBUG && console.log(Flags$on, arguments);    var arg, args = arguments;
    for (var i_arg=0;(i_arg<args.length)&&((arg=args[i_arg])||1);i_arg++) { this.observe.on(arg); }
  }
P.on=Flags$on;
function Flags$on_first(flag, fn, scope, opts){
DEBUG && console.log(Flags$on_first, arguments);    if (this.get[flag]) { fn.apply(scope); return; }
    opts = opts || {}; opts.single = true;
    this.observe.on(flag, fn, scope, opts);}
P.on_first=Flags$on_first;
  Flags$__init__.apply(this, arguments);
};
function HoverTrack$init(){
    this.zone = this.supps = this.callback = this.node = null;
    Ext.getDoc().on({mouseover: this.movement, scope: this});
  }
function HoverTrack$track(zones, context, fn, args){
    var zone, supps;
    if (zones.length) {
      zone = Ext.get(zones.shift());
      if (zones.length) { supps = new RegExp(' ('+ zones.join('|') +') '); }
    }
    if (this.callback && this.leftZone(this.zone, this.supps)) { this.fire(); }
    ////if (this.leftZone(zone, supps)) { return false; }
    this.zone = zone;
    this.supps = supps;
    args = args || [];
    this.callback = function () { fn.apply(context, args); };
    return true;
  }
function HoverTrack$movement(ev, node){
    node = Ext.get(node);
    if (node == this.node) { return true; }
    this.node = node;
    if (this.callback && this.leftZone(this.zone, this.supps)) { this.fire(); }
    return true;
  }
function HoverTrack$fire(){
    setTimeout(this.callback, 1);
    this.zone = this.supps = this.callback = null;
  }
function HoverTrack$leftZone(zone, supps){
    var node = this.node;
    if (!node || !zone || zone.contains(node)) { return false; }
    if (!supps) { return true; }
    var p = node.dom;
    var b = document.body;
    while (p && p.nodeType == 1 && p != b) {
      if (p.className && (' '+ p.className +' ').search(supps) != -1) { return false; }
      p = p.parentNode;
    }
    return true;}

var HoverTrack = {init:HoverTrack$init,
track:HoverTrack$track,
movement:HoverTrack$movement,
fire:HoverTrack$fire,
leftZone:HoverTrack$leftZone};

function Delayed$__init__(){
 this.reg = {};
  }
function Delayed$_run(t){
    if (t.one) { try { return t.fn.apply(t.scope, [t]); } catch (e) { return false; } }
    t.one = true;
    return true;
  }
function Delayed$add(name, fn, scope, delay){
    var t = this.reg[name] = { run: this._run, fn: fn, scope: scope, interval: delay, args: [], done: true };
    t.args.push(t);
    t.onStop = function () { t.done = true; };
  }
function Delayed$set(name){
    var t = this.reg[name];
    if (t && t.done) { t.done = t.one = false; Ext.TaskMgr.start(t); }
  }
function Delayed$stop(name){
    var t = this.reg[name];   
    if (t && !t.done) { t.done = true; Ext.TaskMgr.stop(t); }
  }
function Delayed$reset(name){
    var t = this.reg[name];
    if (!t) { return; }
    if (!t.done) { t.done = true; Ext.TaskMgr.stop(t); }
    t.done = t.one = false;
    Ext.TaskMgr.start(t);
  }
function Delayed$setter(name){
    return function () { Delayed.set(name); };}

var Delayed = {__init__:Delayed$__init__,
_run:Delayed$_run,
add:Delayed$add,
set:Delayed$set,
stop:Delayed$stop,
reset:Delayed$reset,
setter:Delayed$setter};
Delayed.__init__();

/* 'data' optionally contains 'on_result', 'on_error', and general data.
   'on_result' gets the ServerCall object and the result text.
   'on_error' just gets the ServerCall object (which has a 'status'). */
function ServerCall() {
  if (this.constructor !== ServerCall) { throw "ServerCall constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
ServerCall.prototype.__init__ = function () {
  var P=ServerCall.prototype;
function ServerCall$__init__(calling, data){
DEBUG && console.log(ServerCall$__init__, arguments);    this.calling = calling;
    this.data = data;
    this.done = false;
    this.attempts = 1;
    this.failed = 0;
    var control = { url: calling, success: this.success, failure: this.failure, scope: this, argument: data.scope, timeout: data.timeout || 5000 };
    if (data.method != 'GET' || data.calibrate) {
      this.sent_at = now();
      control.method = data.method || 'POST';
      control.params = data.send || dummyParams;
    }
    var conn1 = new Ext.data.Connection({ disableCaching: (data.force || false) });
    conn1.request(control);
  }
P.__init__=ServerCall$__init__;
function ServerCall$success(r, opts){
DEBUG && console.log(ServerCall$success, arguments);    if (this.sent_at) {
      /* Always try to calibrate the time difference on a POST */
      var ct = now();
      var st = Date.parse(r.getResponseHeader['Date']);
      /* Recalibrate if never done or smaller offset found
         or if offset is clearly wrong - sent after it was received (clock may have changed). */
      if (!d_.ms_off || ct < st + d_.ms_off || this.sent_at > st + d_.ms_off + 1000) {
        d_.ms_off = ct - st;
        d_.ms_off_gap = ct - this.sent_at;

      }
    }
    if (this.done) { return; }
    this.done = true;
    var cb = this.data.on_result;
    if (cb) { handoff(this, 'success_notify', [r.responseText, opts.argument, cb, (this.data.hasOwnProperty("evalR") ? this.data["evalR"]: undefined)]); }
  }
P.success=ServerCall$success;
function ServerCall$success_notify(r, scope, cb, evalR){
DEBUG && console.log(ServerCall$success_notify, arguments);    if (evalR && r) {
      var good = false;
      if (r.charAt(0) == '{' || r.charAt(0) == '[') {
        window.trace = ['call eval', r];
        try { r = eval('(' + r + ')'); good = true; } catch (e) { }
        window.trace = null;
      }
      if (!good) {
        window.badR = r;
        alert_box(templates.bad_eval({r: r}), { title: 'Problem!', width: 600 });
        return;
      }
    }
    scope[cb](this, r);
  }
P.success_notify=ServerCall$success_notify;
function ServerCall$failure(r, opts){
DEBUG && console.log(ServerCall$failure, arguments);    if (this.done) { return; }
    this.done = true;
    var data = this.data;
    this.failed += 1;
    this.status = r.status;
    if (this.failed >= this.attempts) { this.done = true; }
    var on_error = data.on_error || data.on_result;
    if (on_error == 'alert') { window.Notifier1.show('Connection to server failed!'); return; }
    if (on_error) { handoff(opts.argument, on_error, [this]); }}
P.failure=ServerCall$failure;
  ServerCall$__init__.apply(this, arguments);
};
function ServerCall2() {
  if (this.constructor !== ServerCall2) { throw "ServerCall2 constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
ServerCall2.prototype.__init__ = function () {
  var P=ServerCall2.prototype;
function ServerCall2$__init__(calling, data){
DEBUG && console.log(ServerCall2$__init__, arguments);    this.calling = calling;
    this.data = data;
    this.done1 = this.done2 = false;
    this.failed = 0

    var control = { url: calling, success: this.success1, failure: this.failure1, scope: this, argument: data.scope, timeout: data.timeout || 5000, method: 'POST', params: dummyParams };
    this.sent_at = now();
    var conn1 = new Ext.data.Connection({ disableCaching: false });
    conn1.request(control);
  }
P.__init__=ServerCall2$__init__;
function ServerCall2$success1(r, opts){
DEBUG && console.log(ServerCall2$success1, arguments);    /* Always try to calibrate the time difference */
    var ct = now();
    var st = Date.parse(r.getResponseHeader['Date']);
    /* Recalibrate if never done or smaller offset found
       or if offset is clearly wrong - sent after it was received (clock may have changed). */
    if (!d_.ms_off || ct < st + d_.ms_off || this.sent_at > st + d_.ms_off + 1000) {
      d_.ms_off = ct - st;
      d_.ms_off_gap = ct - this.sent_at;
    }

    if (this.done1) { return; }
    var ticket = r.responseText;
    ticket = ticket.replace(/[^a-zA-Z0-9@-]/g,'');
    this.done1 = true;
    var M = Math.floor((new Date(st)).getUTCSeconds() / 20);
    this.calling2 = '/gs/op/'+ M +'/'+ ticket;
    this.try_until = now() + 20000;
    handoff(this, 'call2', [], 250); // Wait a quarter second before fetching the result
  }
P.success1=ServerCall2$success1;
function ServerCall2$failure1(call1, r){
DEBUG && console.log(ServerCall2$failure1, arguments);    if (this.done1) { return; }
    this.done1 = true;
    this.failure(r);

  }
P.failure1=ServerCall2$failure1;
function ServerCall2$call2(){
DEBUG && console.log(ServerCall2$call2, arguments);    var control = { url: this.calling2, success: this.success2, failure: this.failure2, scope: this, timeout: 5000 };
    var conn2 = new Ext.data.Connection({ disableCaching: true });
    conn2.request(control);

  }
P.call2=ServerCall2$call2;
function ServerCall2$success2(r, opts){
DEBUG && console.log(ServerCall2$success2, arguments);    if (this.done2) { return; }
    this.done2 = true;
    var data = this.data;
    handoff(this, 'success_notify', [r.responseText, data.scope, data.on_result, (data.hasOwnProperty("evalR") ? data["evalR"]: undefined)]);

  }
P.success2=ServerCall2$success2;
function ServerCall2$success_notify(r, scope, cb, evalR){
DEBUG && console.log(ServerCall2$success_notify, arguments);    if (evalR && r) {
      var good = false;
      if (r.charAt(0) == '{' || r.charAt(0) == '[') {
        window.trace = ['call eval', r];
        try { r = eval('(' + r + ')'); good = true; } catch (e) { }
        window.trace = null;
      }
      if (!good) {
        window.badR = r;
        alert_box(templates.bad_eval({r: r}), { title: 'Problem!', width: 600 });
        return;
      }
    }
    scope[cb](this, r);

  }
P.success_notify=ServerCall2$success_notify;
function ServerCall2$failure2(r, opts){
DEBUG && console.log(ServerCall2$failure2, arguments);    if (this.done2) { return; }
    if (now() < this.try_until) {
      // Until time is up, keep trying once a second
      handoff(this, 'call2', [], 1000);
    } else {
      this.done2 = true;
      this.failure(r);
    }

  }
P.failure2=ServerCall2$failure2;
function ServerCall2$failure(r){
DEBUG && console.log(ServerCall2$failure, arguments);    var data = this.data;
    this.status = r.status;
    this.failed = 1;
    var on_error = data.on_error || data.on_result;
    if (on_error == 'alert') { window.Notifier1.show('Connection to server failed!'); return; }
    if (on_error) { handoff(data.scope, on_error, [this]); }}
P.failure=ServerCall2$failure;
  ServerCall2$__init__.apply(this, arguments);
};
C_DataView2 = Ext.extend(Ext.DataView, {
    onMouseOver: function(e) {
      var item = e.getTarget(this.itemSelector, this.el);
        if(item && item !== this.lastItem){
            this.lastItem = item;
            Ext.fly(item).addClass(this.overClass);
            this.fireEvent("itemover", this, this.indexOf(item), item, e);
        }
    },
    onMouseOut: function(e) {
        var item = this.lastItem;
        if (item) {
            if(!e.within(item, true)){
                Ext.fly(item).removeClass(this.overClass);
                delete this.lastItem;
                this.fireEvent("itemout", this, this.indexOf(item), item, e);
            }
        }
    }
});
Ext.reg('dataview2', C_DataView2);

C_GameLayout = Ext.extend(Ext.layout.ContainerLayout, {
    monitorResize:true,
    extraCls: 'x-column',
    scrollOffset : 19,

    isValidParent : function(c, target){
        return c.getEl().dom.parentNode == this.innerCt.dom;
    },
    onLayout : function(ct, target){
        var cs = ct.items.items, len = cs.length, c, i;

        if(!this.innerCt){
            target.addClass('x-column-layout-ct');
            this.innerCt = target.createChild({cls:'x-column-inner'});
            this.innerCt.createChild({cls:'x-clear'});
        }
        this.renderAll(ct, this.innerCt);

        var size = target.getSize(true);
        if(size.width < 1 && size.height < 1){ return; }

        var w = size.width - target.getPadding('lr') - this.scrollOffset,
            h = size.height - target.getPadding('tb');

        this.innerCt.setWidth(w);

        for(i = 0; i < len; i++){
            c = cs[i];
            if(!c.columnWidth){ w -= (c.getSize().width + c.getEl().getMargins('lr')); }
        }

        w = w < 0 ? 0 : w;
        for(i = 0; i < len; i++){
            c = cs[i];
            if(c.columnWidth) { c.setSize(Math.floor(c.columnWidth*w) - c.getEl().getMargins('lr')); }
        }

        var coltop = cs[0].getPosition()[1];
        var rightcol = cs[len - 1].getEl();
        if (rightcol.getTop() <= coltop) { return; }

        // IE6 Bug
        target.addClass('x-hide-visibility');
        try {
          var sw = cs[len - 1].getSize().width;
          var bsb = [sw - 15, sw - 1];
          var nw = 0;
          while (bsb[0] < bsb[1]) {
            if (nw) { if (rightcol.getTop() > coltop) { bsb[1] = nw - 1; } else { bsb[0] = nw; } }
            nw = Math.floor((bsb[0] + bsb[1] + 1.5) / 2);
            for (i=0; i < len; i++) { cs[i].setSize(nw); }
          }
          if (rightcol.getTop() > coltop) { alert_box('Unable to fix column widths!'); }
          else {
            nw = nw * cs[0].columnWidth / sw;
            for (i=0; i < len; i++) { cs[i].columnWidth = nw; }
          }
        } finally { target.removeClass('x-hide-visibility'); }
      }
});
Ext.Container.LAYOUTS['gcolumn'] = C_GameLayout;

C_GamePage = Ext.extend(Ext.Panel, {
    layout: 'gcolumn',
    cls:'x-game',
    defaultType: 'gamecolumn',

    initComponent : function(){
        C_GamePage.superclass.initComponent.call(this);
        this.addEvents({
            validatedrop:true,
            beforedragover:true,
            dragover:true,
            beforedrop:true,
            drop:true
        });
    },

    initEvents : function(){
        C_GamePage.superclass.initEvents.call(this);
        this.dd = new C_GamePage.DropZone(this, this.dropConfig);
    }
});
Ext.reg('gamepage', C_GamePage);


C_GamePage.DropZone = function(page, cfg){
    this.page = page;
    Ext.dd.ScrollManager.register(page.body);
    C_GamePage.DropZone.superclass.constructor.call(this, page.bwrap.dom, cfg);
    page.body.ddScrollConfig = this.ddScrollConfig;
};

Ext.extend(C_GamePage.DropZone, Ext.dd.DropTarget, {
    ddScrollConfig : {
        vthresh: 50,
        hthresh: -1,
        animate: true,
        increment: 200
    },
    createEvent : function(dd, e, data, col, c, pos){
        return {
            page: this.page,
            panel: data.panel,
            columnIndex: col,
            column: c,
            position: pos,
            data: data,
            source: dd,
            rawEvent: e,
            status: this.dropAllowed
        };
    },
    notifyOver : function(dd, e, data){
        var xy = e.getXY(), page = this.page, px = dd.proxy;

        // case column widths
        if(!this.grid) { this.grid = this.getGrid(); }

        // handle case scroll where scrollbars appear during drag
        var cw = page.body.dom.clientWidth;
        if(!this.lastCW){ this.lastCW = cw; }
        else if(this.lastCW != cw){
            this.lastCW = cw;
            page.doLayout();
            this.grid = this.getGrid();
        }

        // determine column
        var col = 0, xs = this.grid.columnX, cmatch = false;
        for(var len = xs.length; col < len; col++){
            if(xy[0] < (xs[col].x + xs[col].w)){
                cmatch = true;
                break;
            }
        }
        // no match, fix last index
        if(!cmatch){ col--; }

        // find insert position
        var p, match = false, pos = 0,
            c = page.items.itemAt(col),
            items = c.items.items;

        for(len = items.length; pos < len; pos++){
            p = items[pos];
            var h = p.el.getHeight();
            if(h !== 0 && (p.el.getY()+(h/2)) > xy[1]){
                match = true;
                break;
            }
        }

        var overEvent = this.createEvent(dd, e, data, col, c,
                match && p ? pos : c.items.getCount());

        if(page.fireEvent('validatedrop', overEvent) !== false &&
           page.fireEvent('beforedragover', overEvent) !== false){

            // make sure proxy width is fluid
            px.getProxy().setWidth('auto');

            if(p){ px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null); }
            else { px.moveProxy(c.el.dom, null); }

            this.lastPos = {c: c, col: col, p: match && p ? pos : false};
            this.scrollPos = page.body.getScroll();

            page.fireEvent('dragover', overEvent);
        }
        return overEvent.status;
    },

    notifyOut : function(){
        delete this.grid;
    },
    notifyDrop : function(dd, e, data){
        delete this.grid;
        if (!this.lastPos) { return; }
        var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;

        var dropEvent = this.createEvent(dd, e, data, col, c,
                pos !== false ? pos : c.items.getCount());

        if(this.page.fireEvent('validatedrop', dropEvent) !== false &&
           this.page.fireEvent('beforedrop', dropEvent) !== false){

            dd.proxy.getProxy().remove();
            dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
            if(pos !== false){ c.insert(pos, dd.panel); } else { c.add(dd.panel); }
            
            c.doLayout();

            this.page.fireEvent('drop', dropEvent);

            // scroll position is lost on drop, fix it
            var st = this.scrollPos.top;
            if(st){
                var d = this.page.body.dom;
                setTimeout(function(){ d.scrollTop = st; }, 10);
            }

        }
    },

    // internal cache of body and column coords
    getGrid : function(){
        var box = this.page.bwrap.getBox();
        box.columnX = [];
        this.page.items.each(function(c){
             box.columnX.push({x: c.el.getX(), w: c.el.getWidth()});
        });
        return box;
    }
});

C_GameColumn = Ext.extend(Ext.Container, {
    layout: 'anchor',
    autoEl: 'div',
    defaultType: 'portlet',
    cls:'x-game-column',
    afterRender : function(){
        C_GameColumn.superclass.afterRender.call(this);
        this.on('add', this.items_changed);
        this.on('remove', this.items_changed);
        this.items_changed(this);
    },
    items_changed: function (c) {
      c.items.each(function (item, i) { if (i) { item.removeClass('x-portlet-first'); } else { item.addClass('x-portlet-first'); } });
      Delayed.set('size_portlets');
    }
});
Ext.reg('gamecolumn', C_GameColumn);

C_Portlet = Ext.extend(Ext.Panel, {
    anchor: '100%',
    frame:true,
    collapsible:true,
    collapseFirst:false,
    closable:false,
    draggable:true,
    cls:'x-portlet'
});
Ext.reg('portlet', C_Portlet);

C_SideBar = Ext.extend(Ext.Panel, {
    frame: false,
    border: false,
    collapsible: true,
    titleCollapse: true,
    cls:'x-sidebar'
});
Ext.reg('sidebar', C_SideBar);

C_GameStateProvider = function() {
  C_GameStateProvider.superclass.constructor.call(this);
  this.readState();
};

Ext.extend(C_GameStateProvider, Ext.state.Provider, {
  set: function(name, value){
    if (typeof value == "undefined" || value === null) { this.clear(name); return; }
    this.setState(name, value);
    C_GameStateProvider.superclass.set.call(this, name, value);
  },
  clear: function(name) {
    this.clearState(name);
    C_GameStateProvider.superclass.clear.call(this, name);
  },
  readState: function() {
    this.state = {};
  },
  setState: function(name, value) {
    
  },
  clearState: function(name) {
    
  }
});

C_Text = Ext.extend(Ext.Container, {
    onRender : function(ct, position){
        C_Text.superclass.onRender.call(this, ct, position);
        this.el = ct.createChild({ id: this.id, cls: this.cls || '', style: this.style || '' }, position);
    },

    afterRender : function(){
        if (this.html) {
          this.el.update(typeof this.html == 'object' ? Ext.DomHelper.markup(this.html) : this.html);
          delete this.html;
        }
        C_Text.superclass.afterRender.call(this); // do sizing calcs last
    },
    
    getLayoutTarget : function(){ return this.el; }
});
Ext.reg('textpanel', C_Text);

function Notifier() {
  if (this.constructor !== Notifier) { throw "Notifier constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
Notifier.prototype.__init__ = function () {
  var P=Notifier.prototype;
function Notifier$__init__(id, ct, align){
DEBUG && console.log(Notifier$__init__, arguments);    var el = this.el = Ext.DomHelper.append(ct, {id: id, cls:'notifier'}, true).boxWrap();
    this.sp = Ext.get(id);
    this.sp.insertHtml('afterEnd', '<div class="notifier-after"><!-- --><\/div>');
    this.align = align;
  }
P.__init__=Notifier$__init__;
function Notifier$show(msg){
DEBUG && console.log(Notifier$show, arguments);    var el = this.el;
    el.stopFx();
    if (!msg) { el.hide(); return; }
    var sp = this.sp;
    sp.update(msg);
    el.setWidth(sp.dom.scrollWidth+32);
    el.alignTo.apply(el, this.align);
    el.show().fadeIn().pause(4).ghost('t');}
P.show=Notifier$show;
  Notifier$__init__.apply(this, arguments);
};
function patchFx$__init__(){
    this._afterFx = Ext.Element.prototype.afterFx;
    Ext.override(Ext.Element, {afterFx: this.afterFx, queueFx: this.queueFx, stopFx: this.stopFx});
  }
function patchFx$afterFx(o){
    if (!o.stopped) { patchFx._afterFx.call(this, o); }
  }
function patchFx$stopFx(){
    var q = this.fxQueue;
    var fx = q && q[0];
    if (fx) {
      if (fx.anim && fx.anim.isAnimated()){ q.length = 1; fx.anim.stop(false); } else { q.length = 0; fx.origConfig.stopped = true; }
    }
  }
function patchFx$queueFx(o, fn){
    var q = this.fxQueue;
    if (!q){ this.fxQueue = q = []; }
    if (!this.hasFxBlock()) {
      Ext.applyIf(o, this.fxDefaults);
      if (!o.concurrent){
        var run = this.beforeFx(o);
        fn.block = o.block;
        fn.origConfig = o;
        q.push(fn);
        if(run){ this.nextFx(); }
      } else { fn.call(this); }
    }
    return this;}

var patchFx = {__init__:patchFx$__init__,
afterFx:patchFx$afterFx,
stopFx:patchFx$stopFx,
queueFx:patchFx$queueFx};
patchFx.__init__();

function ArrBag() {
  if (this.constructor !== ArrBag) { throw "ArrBag constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
ArrBag.prototype.__init__ = function () {
  var P=ArrBag.prototype;
function ArrBag$__init__(key, auto){
DEBUG && console.log(ArrBag$__init__, arguments);    this.arr = [];
    this.bag = {};
    this.key = key;
    this.auto = auto;
  }
P.__init__=ArrBag$__init__;
function ArrBag$put(x){
DEBUG && console.log(ArrBag$put, arguments);    /* Content objects must have 'key' */
    var k = ((x).hasOwnProperty( this.key) ? (x)[ this.key]: undefined);
    if (typeof k == 'undefined') { return null; }
    k = k + '$'; // Prevent collision with builtin names
    /* Add to array if name is new */
    var is_new = !(this.bag).hasOwnProperty( k);
    if (this.auto) { x[this.auto] = is_new ? this.arr.length : this.bag[k][this.auto]; }
    if (is_new) { this.arr.push(x); }
    this.bag[k] = x;
    return x;
  }
P.put=ArrBag$put;
function ArrBag$extend(a){
DEBUG && console.log(ArrBag$extend, arguments);    var arr = this.arr;
    var bag = this.bag;
    var key = this.key;
    var auto = this.auto;
    for (var i = 0; i < a.length; i++) {
      var x = a[i];
      var k = ((x).hasOwnProperty( key) ? (x)[ key]: undefined);
      if (typeof k == 'undefined') { continue; }
      k = k + '$';
      var is_new = !(bag).hasOwnProperty( k);
      if (auto) { x[auto] = is_new ? arr.length : bag[k][auto]; }
      if (is_new) { arr.push(x); }
      bag[k] = x;
    }
    return this;
  }
P.extend=ArrBag$extend;
function ArrBag$length(){
DEBUG && console.log(ArrBag$length, arguments); return this.arr.length;
  }
P.length=ArrBag$length;
function ArrBag$get(k){
DEBUG && console.log(ArrBag$get, arguments);    var bag = this.bag;
    return ((bag).hasOwnProperty( k + '$') ? (bag)[ k + '$']: undefined);
  }
P.get=ArrBag$get;
function ArrBag$has(k){
DEBUG && console.log(ArrBag$has, arguments);    var bag = this.bag;
    return (bag).hasOwnProperty( k + '$');   }
P.has=ArrBag$has;
  ArrBag$__init__.apply(this, arguments);
};
function Tasks() {
  if (this.constructor !== Tasks) { throw "Tasks constructor called without 'new'"; }
  this.__init__.apply(this, arguments);
}
Tasks.prototype.__init__ = function () {
  var P=Tasks.prototype;
function Tasks$__init__(scope){
DEBUG && console.log(Tasks$__init__, arguments);    this.scope = scope;
    this.tasks = {};
    this.ready = [];
    this.unready = {};
    this.working = {};
    this.failed = {};
    this.completed = {};
    for (var i=1; i < arguments.length; i++) { this.add(arguments[i], {}); } 
  }
P.__init__=Tasks$__init__;
function Tasks$add(name, other, ready){
DEBUG && console.log(Tasks$add, arguments);    this.tasks[name] = {name: name, q: null, other: other };
    if (ready) { this.enq(this.tasks[name], 'ready'); }
    return this;
  }
P.add=Tasks$add;
function Tasks$set(tsets, poll){
DEBUG && console.log(Tasks$set, arguments);    for (var tname in tsets) { this.enq(this.tasks[tname], tsets[tname]); }
    if (poll) { this.poll(); }
  }
P.set=Tasks$set;
function Tasks$halt(){
DEBUG && console.log(Tasks$halt, arguments);    for (var tname in this.tasks) { this.enq(this.tasks[tname], 'unready'); }
  }
P.halt=Tasks$halt;
function Tasks$enq(task, q){
DEBUG && console.log(Tasks$enq, arguments);    var oldq = task.q;
    if (oldq == 'working' && q == 'ready') { return; /* Forbid this transition */ }
    task.t = now();
    if (q) { task.q = q; } else { q = task.q; }
    if (oldq && oldq != 'ready') { delete this[oldq][task.name]; }
    if (q == 'ready') { this.ready.push(task); } else { this[q][task.name] = task; }
  }
P.enq=Tasks$enq;
function Tasks$poll(){
DEBUG && console.log(Tasks$poll, arguments);    var i, task, tname;
    var t = now();
    var q = this.ready;
    while (q.length) {
      task = q.pop();
      this.enq(task, 'working');
      this.scope[task.name](task);
    }
    q = this.working;
    for (tname in q) {
      task = q[tname];
      if (task.q != 'working') { delete q[tname]; this.enq(task); }
      if (t > task.t + (task.other.timeout || 60000)) { this.enq(task, 'failed'); }
    }
    q = this.failed;
    for (tname in q) {
      task = q[tname];
      if (task.q != 'failed') { delete q[tname]; this.enq(task); }
      if (t > task.t + (task.other.retry || 6000)) { this.enq(task, 'ready'); }
    }}
P.poll=Tasks$poll;
  Tasks$__init__.apply(this, arguments);
};
/* Misc */

Object.prototype.hasOwnProperty || // undefined in Safari<2.0.2 
(Object.prototype.hasOwnProperty = function(name) { 
  var val = this[name], saved, proto = this.__proto__; 
  if (!proto) return typeof val != 'undefined'; 
  if (val !== (saved = proto[name])) return true; 
  var mustReplace = proto.hasOwnProperty(name);
  var sentinel = proto[name] = {};
  var hasOwn = (this[name] !== sentinel); 
  delete proto[name]; 
  if (mustReplace) proto[name] = saved; 
  return hasOwn; 
}); 

function getset(ob, k, Constr) {
  if (!(ob).hasOwnProperty( k)) { ob[k] = new Constr(); }
  return ob[k];
}

function a_filter(a, filter) {
  var item, a2 = [];
  switch(filter) {
    case true: filter = function () { return true; }; break;
    case undefined: filter = function (item) { return item; }; break;
    default: break;
  }
  for (var i_item=0;(i_item<a.length)&&((item=a[i_item])||1);i_item++) { if (filter ? filter(item, i_item) : item) { a2.push(item); } }
  return a2;
}
function a_map(a, map) {
  var item, a2 = [];
  for (var i_item=0;(i_item<a.length)&&((item=a[i_item])||1);i_item++) { a2.push(map(item)); }
  return a2;
}

function handoff(context, f, args, t) {
  if (!args) { args = []; }
  var h = function () { context[f].apply(context, args); };
  setTimeout(h, t || 1);
}

function alert_box(msg, o) {
  Ext.Msg.getDialog().anchorTo(P_main.el, 'tr-tr?').fixedcenter = false;
  Ext.Msg.show(Ext.apply({
     title: 'Alert!',
     msg: msg,
     width: 300,
     minWidth:300,
     buttons: Ext.MessageBox.OK,
     modal: false
  }, o || {}));
}

function logcall() {

}

function repr(o, rl) {
  var s = o + '';
  if (!o) { return s; }
  switch (typeof o) {
    case 'string':
      if (o.search(/['\\]/) > -1) { return "'" + o.replace(/(['\\])/g, '\\$1') + "'"; }
      return "'" + o + "'";
    case 'object':
      if (s != '[object Object]') { return s; }
      break;
    default:
      return s;
      break;
  }
  /* Limit recursion */
  if (rl) { rl += 1; } else { rl = 1; }
  if (rl > 10) { return '...'; }
  var i, r, k;
  /* Array */
  if (o.hasOwnProperty("__proto__") && (o.__proto__).hasOwnProperty( 'length')) {
    r = new Array(o.length);
    for (i = 0; i < o.length; i++) { r[i] = repr(o[i], rl); }
    return '[' + r.join(', ') + ']';
  }
  /* Object */
  if (o + '' != '[object Object]') { return o + ''; }
  r = new Array();
  for (k in o) {
    if ((o).hasOwnProperty( k)) {
      r.push(k + ': ' + repr(o[k], rl));
    }
  }
  return '{' + r.join(', ') + '}';
}

function get_query_args(q) {
  if (!q || q == '?') { return {}; }
  var kv, pargs = {};
  q = q.split('&');
  for (var i_kv=0;(i_kv<q.length)&&((kv=q[i_kv])||1);i_kv++) { 
    kv = kv.split('='); 
    pargs[kv[0]] = decodeURIComponent(kv[1]);
  }
  return pargs;
}

function get_cookie(cname) {
  if (document.cookie) {
    var cval, clist = document.cookie.split(";");
    for (var i_cval=0;(i_cval<clist.length)&&((cval=clist[i_cval])||1);i_cval++) {
      var k = cval.split("=", 1)[0];
      if (k == cname || k == " " + cname) {
        var v = cval.substr(k.length + 1);
        if (v && v.charAt(0) == "'") { return unescape(v.substr(1)); }
        return v; 
      }
    }
  }
  return null;
}

function set_cookie(cname, v, exp_h, p) {
  v = v + '';
  if (v.search(/[, ;]/) >= 0) { v = "'" + escape(v); }
  v = v + '; path=' + (p || '/');
  if (exp_h) {
    var t = new Date();
    t.setTime( t.getTime() + exp_h*60*60*1000 );
    document.cookie = cname + "=" + v + "; expires=" + t.toGMTString();
  } else {
    document.cookie = cname + "=" + v;
  }
}

function unpack_options(s) {
  var r = {};
  for (var c in all_opts) {
    if (c.length > 1) { continue; }
    var rc = r[c] = {};
    var oc = all_opts[c];
    for (var c2 in oc) {
      if ((oc).hasOwnProperty( c2) && c2.length == 1) { rc[oc[c2]] = 0; }
    }
  }
  s = s || 'BmGwRt';
  oc = {};
  for (var i=0; i < s.length; i++) {
    c = s.charAt(i);
    if ((all_opts).hasOwnProperty( c)) { rc = r[c]; oc = all_opts[c]; continue; }
    if ((oc).hasOwnProperty( c)) {
      rc[oc[c]] = 1;
      var n = s.substr(i + 1).match(/^[0-9]+/);
      if (n) { rc[oc[c]] = parseInt(n[0], 10); i+=n[0].length; }
    }
  }
  return r;
}

function disemvowel(s) {
  return s.replace(/[aeiou]/gi, '.');
}

function HTMLescape(s) {
  s = s.replace(/&/g, '&amp;');
  s = s.replace(/[<]/g, '&lt;');
  s = s.replace(/>/g, '&gt;');
  return s;
}

/* Time functions */

function now() { return (new Date()).getTime(); }

function min_sec(t) {
  var m = Math.floor(t / 60);
  var s = Math.floor(t) % 60;
  if (s < 10) { return m + ':0' + s; }
  return m + ':' + s;
}

/* Indirection to simplify handlers */

function main2$rotate(){
 Main.rotate.apply(Main, arguments);
  }
function main2$no_guess(){
 Main.no_guess.apply(Main, arguments); refocus();
  }
function main2$guess_submit(){
 Main.guess_submit();
  }
function main2$menuclick(mi){
 refocus(); if (mi.menu) { return false; } handoff(Main, mi.id); return true;
  }
function main2$portlet_drop(){
 refocus(); handoff(Main, 'save_columns');
  }
function main2$gear(ev, tool, p){
 Ext.menu.MenuMgr.get((p.gmenu || p.id) +'menu').show(tool);
  }
function main2$tests(){
 Main.tests();  return false;

  }
function main2$pause(ev, tool, p){
    Main.paused = !Main.paused;
    GameLayout.set_tool_image(tool, ['pin', 'unpin'], Main.paused);
    if (Main.paused) { P_status.setTitle('Play Paused'); } else { window.Notifier1.show('Play Resumed'); }

  }
function main2$plusminus(ev, tool, p){
    var o1 = p == P_scoreboard? 'R': 'W';
    var opt = d_.opts[o1];
    opt.height = opt.height ? 0: 1;
    GameLayout.set_tool_image(tool, ['plus', 'minus'], opt.height);
    handoff(Main, 'opts_changed', [o1, 'height', opt.height]);

  }
function main2$prefshow(m){
    var mi;
    for (var i_mi=0;(i_mi<m.items.items.length)&&((mi=m.items.items[i_mi])||1);i_mi++) {
      if (mi.initialConfig.checked && mi.id.substr(0, 4) == 'opts') {
        var o1 = mi.id.charAt(4);
        var o2 = all_opts[o1][mi.id.charAt(5)];
        var v = d_.opts[o1][o2];
        if (mi.group) { v = (mi.id.charAt(6) == (''+ v)); }
        mi.setChecked(v, true);
      }
      if (mi.menu) { main2.prefshow(mi.menu); }
    }

  }
function main2$prefck(mi, v){
    if (mi.group) { if (!v) { return; } v = parseInt(mi.id.replace(mi.group, ''), 10); }
    else          { v = v ? 1 : 0; }
    var o1 = mi.id.charAt(4);
    var o2 = all_opts[o1][mi.id.charAt(5)];
    d_.opts[o1][o2] = v;
    handoff(Main, 'opts_changed', [o1, o2, v]);

  }
function main2$render_player_name(v, metadata, p){
    p = p.data.who;
    var mark = d_.marked[p.code || 'team '+ p.name];
    if (!p.is_me && d_.opts.R.disemvowel && !mark) { v = disemvowel(v); }
    return templates.player_marker({'@source':(p.source? {icon: p.source}:0), '@mark': (mark? {icon: mark}:0)}) + v;
}

var main2 = {rotate:main2$rotate,
no_guess:main2$no_guess,
guess_submit:main2$guess_submit,
menuclick:main2$menuclick,
portlet_drop:main2$portlet_drop,
gear:main2$gear,
tests:main2$tests,
pause:main2$pause,
plusminus:main2$plusminus,
prefshow:main2$prefshow,
prefck:main2$prefck,
render_player_name:main2$render_player_name};

function gl$menuclick(mi){
 refocus(); if (mi.menu) { return false; } handoff(GameLayout, mi.id); return true; 
  }
function gl$portlet_sizing(){
 Delayed.set('size_portlets'); 
  }
function gl$board_resize(rsz, w, h, ev){
 GameLayout.board_resize(h, true); 
  }
function gl$board_size(){
 GameLayout.board_resize(d_.opts.B.height || 0); 
  }
function gl$scores_layout(){
 this.scroller.dom.style.overflow = 'auto';
  }
function gl$dv_rendered(dv){
 dv.on({itemover: GameLayout.word_over, beforeclick: GameLayout.word_click, scope: GameLayout});

  }
function gl$help_toggle2(){
 handoff(window, 'gl$help_toggle');
  }
function gl$help_toggle(){
    var q = Ext.QuickTips;
    var qe = q.isEnabled();
    qe ? q.disable(): q.enable();
    Ext.fly('tip-status').update('Tips are o'+ (qe? 'ff': 'n'));

  }
function gl$main_active(){
    switch (P_main.layout.activeItem.id) {
      case 'blankpage': P_main.layout.setActiveItem('gamepage');
        break;
      case 'gamepage':
        GameLayout.reload_gamepage();
        break;
      case 'loginpage':
        Ext.getCmp('login').getForm().items.first().focus();
      default:
        Main.flags.unset('gamepage');
        break;
    }
    Login.message_id = null;
    Delayed.stop('poll_email_status');

  }
function gl$scores_rowclick(g, rowi, ev){
    var t = ev.getTarget('tr.x-grid3-row-body-tr');
    if (t) { handoff(GameLayout, 'scores_expclick', [rowi, t]); return; }
    t = Ext.get(ev.getTarget());
    if (t.hasClass('icon-menu')) { GameLayout.scores_rowclick(rowi, t.getLeft(), t.getBottom()); return; }
    refocus();

    var args = GameLayout.player_args;
    if (!(args && args.focus)) { return; }
    var row = args.focus.row;
    if (args.locked) {
      args.locked = false;
      row.removeClass('scorelock');
      if (args.focus.id != rowi) {
        GameLayout.player_on(ev, t);
        args.locked = true;
        row = args.focus.row;
      }
    } else { args.locked = true; }
    if (args.locked) { row.addClass('scorelock'); }

  }
function gl$score_row(record, rowi, extra, store){
    var c = record.data.c;
    var x = record.data.x;
    if (x > 0) { extra.body = 'Click to see players ranked '+ (rowi+2) +'-'+ (rowi+x+1); } else { extra.body = null; }
    return c + ((x < 0)? ' dull':'');

  }
function gl$chat_row(record, rowi, extra, store){
    var c = record.data.c;
    var m = ((d_.marked).hasOwnProperty( record.data.code) ? (d_.marked)[ record.data.code]: undefined) 
    if (m) { c +=' '+ m; }
    return c;

  }
function gl$chat_started(){
    Delayed.set('get_chat');
  }
function gl$chat_stopped(){
    Delayed.stop('get_chat');}

var gl = {menuclick:gl$menuclick,
portlet_sizing:gl$portlet_sizing,
board_resize:gl$board_resize,
board_size:gl$board_size,
scores_layout:gl$scores_layout,
dv_rendered:gl$dv_rendered,
help_toggle2:gl$help_toggle2,
help_toggle:gl$help_toggle,
main_active:gl$main_active,
scores_rowclick:gl$scores_rowclick,
score_row:gl$score_row,
chat_row:gl$chat_row,
chat_started:gl$chat_started,
chat_stopped:gl$chat_stopped};

function get_cell_letter(cell, low) {
  var c = cell.letter;
  if (!c) { return '-'; }
  if (c == "Q") { c = "Qu"; }
  if (low) { c = c.toLowerCase(); }
  return c;
}

function words_who(who) {
  return function (rec) { return rec.get('who') == who; };
}

function clock() {
  // Call Main.tick() once per second, near the "beginning" of the second.
  var d = new Date();
  setTimeout(clock, 1050 - d.getMilliseconds());
  Main.tick(d);
}

/*function save_acct_setting(s) {
  set_cookie(s, d_[s] || '', d_.is_public? 0: 365*24, '/acct/');
}*////

function stamp_time(s) {
  return Date.parse(s.substr(0,4) +'/'+ s.substr(4,2) +'/'+ s.substr(6,2) +' '+ s.substr(8,2) +':'+ s.substr(10,2) +':'+ s.substr(12,2) +' GMT');
}

/*function sort_score(a, b) {
  if (a.score == b.score) { return (b.who && b.who.is_me) ? 1 : 0; }
  return b.score - a.score;
}*////
function sort_score(a, b) {
  if (a[2] == b[2]) { return (b[4] && b[4].is_me) ? 1 : 0; }
  return b[2] - a[2];
}

function load_scoring(name) { return new Scoring(d_.scoring[name]); }

var keep_focus;
function refocus() {
  if (window.keep_focus && keep_focus.isVisible(true)) {
    keep_focus.focus();
    return true;
  }
  return false;
}

function toggle_fb(btn, e) {
  Ext.get('prior_words')[btn.pressed ? 'removeClass' : 'addClass']('fb_'+ btn.name +'_off');
  refocus();
}

function capsLockWarn(e) {
  var kc = e.getCharCode();
  var sk = e.shiftKey? e.shiftKey : (kc == 16);
  if (sk? (kc >= 97 && kc <= 122) : (kc >= 65 && kc <= 90)) {
    alert_box("Your Caps Lock key is on!  You probably should turn it off.");
  }
}


function call_server(calling, data) { return new ServerCall(calling, data); }
function call_server2(calling, data) { return new ServerCall2(calling, data); }
function get_server(calling, data, noEval) {
  data.evalR = !noEval;
  data.method = 'GET';
  return new ServerCall(calling, data);
}
function send_server(u) { return new ServerCall(u, {}); }

function dynop(f, posargs, keyargs) {
  var args = '';
  for (var i=0; i < posargs.length; i++) { args = args +'&'+ escape(posargs[i]); }
  if (keyargs) {
    for (i=0; i < keyargs.length; i+=2) { args = args +'&'+ keyargs[i] +'='+ escape(keyargs[i+1]); }
  }
  return '/dyn/op/w29/'+ f +'?'+ args.substr(1);
}


function TData(data) {
  this.ti = 0;
  this.p = [0];
  this.d = data || {};
  this.dstack = [[this.d], [this.d]];
  this.di = [0];
}
TData.prototype = {
  n: function (ti, p, subs) {
    if (ti > this.ti) {
      this.ti = ti;
      this.di[ti] = 0;
      this.d = this.dstack[ti][0];
      return true;
    }
    var d = this.dstack[ti];
    var di = this.di[ti];
    d[di] = p.join('');
    di += 1;
    if (di < d.length) { this.d = d[di]; this.di[ti] = di; return true; }
    this.d = d.join('');
    return false;
  }
, r: function (id) {
    var v = this.v(id);
    if (!v) { return false; }
    var vr, name, d = Ext.apply({}, this.d);
    for (name in d) { if (name.charAt(0) == '@') { d[name] = null; } }
    if ((v).hasOwnProperty( '*')) { 
      if (!(v['*'] && v['*'].length)) { return false; }
      Ext.applyIf(d, v);
      d['*'] = null;
      v = v['*'];
      for (var i_vr=0;(i_vr<v.length)&&((vr=v[i_vr])||1);i_vr++) { v[i_vr] = Ext.apply({}, vr, d); }
    } else { v = [Ext.applyIf(d, v)]; }
    this.dstack[this.ti + 1] = v;
    return v;
  }
, q: function (b) {
    if (!b) { return false; }
    var name, d = Ext.apply({}, this.d);
    for (name in d) { if (name.charAt(0) == '@') { d[name] = null; } }
    this.dstack[this.ti + 1] = d;
    return true;
  }
, b: function (p) { this.p[this.ti] = p; }
, e: function (ti, pi) { var p = this.p[ti]; p[pi] = this.d; this.ti = ti; this.d = this.dstack[ti][this.di[ti]]; return p; }
, v: function (name) {
    var ti = this.ti;
    var di = this.di[ti];
    if (name == '*i') { return di; }
    var d = this.d;
    if (d && (d).hasOwnProperty( name)) { return d[name]; }
    return '';
  }
, c: function () {
    var name, args = TData.prototype.c.arguments;
    for (var i_name=0;(i_name<args.length)&&((name=args[i_name])||1);i_name++) {
      var v = this.v(name);
      if (!v) { return ''; }
    }
    return v;
  }
};

/* Plugins */

// Uses main_submit and form_values
var plug_form_dialog = {
  init: function (o) {
    o.on({beforeshow: this.setup, close: this.cleanup});
  }
, setup: function (w) {
    var fp = w.items.items[0];
    var bsubn = w.main_submit_btn || 0;
    var bsub = fp.buttons[bsubn];

    bsub.on('click', function () {
      var invalid = false;
      fp.getForm().items.each(function(f){ if (f.validate()) { return true; } f.focus(); invalid = true; return false; });
      if (invalid) { return; }
      var fv = fp.getForm().getValues();
      this.close();
      Main[this.main_submit](fv);
    }, w);
    fp.buttons[1 - bsubn].on('click', w.close, w);
    var f = fp.getForm();
    f.setValues(w.form_values);
    w.el.child('form').on('submit', function () { bsub.fireEvent('click'); });
    if (f.items.length) { w.defaultButton = f.findField(0).el; }
    if (!w.hasOwnProperty("mainpaused")) { w.mainpaused = Main.paused; }
    Main.paused = true;
  }
, cleanup: function (w) {
    Main.paused = w.mainpaused;
    refocus();
  }
};

var plug_dialog = {
  init: function (o) {
    o.on({beforeshow: this.setup, close: this.cleanup});
  }
, setup: function (w) {
    if (!w.hasOwnProperty("mainpaused")) { w.mainpaused = Main.paused; }
    Main.paused = true;
  }
, cleanup: function (w) {
    Main.paused = w.mainpaused;
    refocus();
  }
};

C_StoreProxy = function(store, fn) {
    Ext.data.DataProxy.superclass.constructor.call(this);
    this.base_store = store;
    this.query_fn = fn;
};
Ext.extend(C_StoreProxy, Ext.data.DataProxy, {
    load : function(params, reader, callback, scope, arg){
        params = params || {};
        var recs = [];
        var fn = this.query_fn;
        try {
            this.base_store.each(function (item) { if (fn(item)) { recs.push(item.copy()); } });
        }catch(e){
            this.fireEvent("loadexception", this, arg, null, e);
            callback.call(scope, null, arg, false);
            return;
        }
        callback.call(scope, { records: recs }, arg, true);
    },
    update : function(params, records) {}
});

var all_opts = {
  R: { v: 'disemvowel', t: 'teams', h: 'height', a: 'all' },
  B: { l: 'lowercase', c: 'click2', m: 'mark', h: 'height' },
  G: { w: 'wrong' },
  S: { m: 'mistypes', t: 'timer', g: 'good', f: 'feedback' },
  W: { s: 'sort', g: 'group', r: 'reverse', h: 'height' },
  P: { p: 'play', c: 'collapsesidebar', t: 'theme', s: 'sound'}
};

var found_bitmap = ['+-:<>@BDFHJLNPRTVXZbdfhjlnprtvxz', ',-;<?@CDGHKLOPSTWXabefijmnqruvyz', '.:;<ABCDIJKLQRSTYZabghijopqrwxyz',
                    '=>?@ABCDMNOPQRSTcdefghijstuvwxyz', 'EFGHIJKLMNOPQRSTklmnopqrstuvwxyz', 'UVWXYZabcdefghijklmnopqrstuvwxyz'];

var player_source = { M: 'mobile' };

var templates = {dummy: ''
, gamename: function (data) {
  return '<ul class="checklist"><li >You can use the side menu to come back here and choose a new name at any time!<\/li><li><b >TEAMS:<\/b> Choose a team name only if you want to join (or invite others to join) a team.<\/li><\/ul>';
}
, teamname: function (data) {
  return '<ul class="checklist"><li>Members of team "XYZ" appear in the score list as a single player "Team XYZ".<\/li><li>Team members also see scores of each player on the same team as "Team XYZ: Pat".<\/li><\/ul>';
}
, email: function (data) {
  return '<ul class="checklist"><li >I will send a confirmation email to your new address, with a link in it.<\/li><li >Your account\'s email address will not change until you follow the link.<\/li><\/ul>';
}
, score_summ: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['Total: ', x, ' of ', x, ' possible points scored'];
  while (d.n(1, p)) {
   p[3]=d.v('perfect');p[1]=d.v('total');
  }
  return p.join('');
}
, scorelock: function (data) {
  return '<div id="scorelock" ext:qtip="Click to lock/unlock highlighting of words" ext:width="150" style="float: right; display: none"><img src="/images/default/menu/unchecked.gif"><\/div>';
}
, scoremenu: function (data) {
  return '<div id="scoremenu" style="float:left; display: none"><div id="scoremenu_btn"><\/div><\/div>';
}
, notes: function (data) {
  return '<ul class="checklist"><li><b>Learn:<\/b> Click <img src="/images/s.gif" class="icon x-tool x-tool-help inert" onclick="void gl_help_toggle2();">, then point your mouse at icons and parts of the game to see what they do (<b id="tip-status">Tips are off<\/b>).<\/li><li><b>Rearrange:<\/b> Drag play-area panels around by the title bar. Click a panel\'s <img src="/images/s.gif" class="icon x-tool x-tool-toggle inert"> to roll it up, then <span class="x-panel-collapsed"><img src="/images/s.gif" class="icon x-tool x-tool-toggle inert"><\/span> to unroll it. Use <img src="/images/s.gif" class="icon x-tool x-tool-collapse-west inert"> to close the side-bar menu, and <img src="/images/s.gif" class="icon x-tool x-tool-expand-west inert"> to open it. Drag the thin bar below the letters in the Game Board panel up or down to resize the board.<\/li><li><b>Customize:<\/b> Click each <img src="/images/s.gif" class="icon x-tool x-tool-gear inert"> to customize part of the game.<\/li><li><b>Chat:<\/b> You can chat with other players by typing in the same place that you type your word guesses. The only difference is that, to say something, you type a quote mark (either \' or &quot;) in front of whatever you want to say. Press the Enter key when you\'re done typing. When you say something, the Chat panel will open. You can also click the Chat panel\'s <span class="x-panel-collapsed"><img src="/images/s.gif" class="icon x-tool x-tool-toggle inert"><\/span> to see what other people are saying, and <img src="/images/s.gif" class="icon x-tool x-tool-toggle inert"> to close it.<\/li><li >Point at a player to see which words they found, and click to lock and unlock word markings.<\/li><\/ul>';
}
, game_choices: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<ul>', x, '<\/ul>'];
  while (d.n(1, p)) {
   d.b(p);
   if (d.r('@games')) {
    p = ['<li><a href="#" id="play-', x, '"><img src="/images/s.gif" class="icon"> <span >Play ', x, '<\/span><\/a><\/li>'];
    while (d.n(2, p)) {
     p[1]=d.v('name');p[3]=d.v('title');
    }
    p=d.e(1,1);
   }
  }
  return p.join('');
}
, tests: function (data) {
  return '<div><h3 >Test 1: Local Cookies<\/h3><div id="test1" >Not yet run...<\/div><\/div><div><h3 >Test 2: Server Cookies<\/h3><div id="test2" >Not yet run...<\/div><\/div><div><h3 >Test 3: Scripts from Server<\/h3><div id="test3" >Not yet run...<\/div><pre style="border: 1px solid black; padding: 4px;"><div id="test3r" >No data.<\/div><\/pre><\/div><div><h3 >Test 4: Obscenity Filter<\/h3><div id="test4" >Not yet run...<\/div><\/div>';
}
, bad_eval: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['Either there has been a server error, or you have software installed that is interfering with Internet access.<br>Here is the result:<pre >', x, '<\/pre>'];
  while (d.n(1, p)) {
   p[1]=d.v('r');
  }
  return p.join('');
}
, late_words: function (data) {
  return '<b>Some of your words did not reach the server in time to be scored:<\/b><br>';
}
, player_marker: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<div class="player-marker"><img src="/images/s.gif" class="icon icon-menu" qtip="Player menu">&nbsp;<img src="/images/s.gif" class="icon icon-scorelock" qtip="Word list is locked to this player">', x, x, '<\/div>'];
  while (d.n(1, p)) {
   d.b(p);
   if (d.r('@source')) {
    p = ['&nbsp;<img src="/images/s.gif" class="icon icon-', x, '" qtip="', x, '">'];
    while (d.n(2, p)) {
     p[1]=p[3]=d.v('icon');
    }
    p=d.e(1,1);
   }
   if (d.r('@mark')) {
    p = ['&nbsp;<img src="/images/s.gif" class="icon icon-', x, '" qtip="', x, '">'];
    while (d.n(2, p)) {
     p[1]=p[3]=d.v('icon');
    }
    p=d.e(1,2);
   }
  }
  return p.join('');
}
, name_changed: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ["<b>Name Changed to '", x, "'.<\/b><br><br>Choose a game to start playing!"];
  while (d.n(1, p)) {
   p[1]=d.v('n');
  }
  return p.join('');
}
, not_on: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<span class="bad">', x, '<\/span>: not on the board!'];
  while (d.n(1, p)) {
   p[1]=d.v('guess');
  }
  return p.join('');
}
, guessed: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<span class="dim">', x, '<\/span>: already guessed.'];
  while (d.n(1, p)) {
   p[1]=d.v('guess');
  }
  return p.join('');
}
, bad_guess: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<span class="bad">', x, '<\/span>: not a legal word.'];
  while (d.n(1, p)) {
   p[1]=d.v('guess');
  }
  return p.join('');
}
, time_manage: function (data) {
  return '<div id="playtime">You have been playing for: 0 sec<\/div><p><input type="button" id="blockbutton" value="Make the next game my last one" onclick="Main.setlastgame()"><\/input><br>and block me for<select size="1" id="blockdur" name="blockdur"><option>15 min<\/option><option>30 min<\/option><option>45 min<\/option><option>1 hr<\/option><option>2 hr<\/option><option>6 hr<\/option><option>1 day<\/option><\/select><\/p><div id="blockmsg">No block in place<\/div>';
}
, login_header: function (data) {
  return '<h1>Welcome to the Game!<\/h1>';
}
, login_footer: function (data) {
  return '<p class="fine">I promise not to sell or abuse your email address.  <a href="#" onclick="void Main.load_infopage(\'explain_login\'); return false;">This<\/a> explains why I need it.<\/p><br><ul class="checklist"><li><b>First time logging in?<\/b> I\'ll send you a confirmation email (from game@shackworks.com).<\/li><li><b>Follow the link in the email<\/b> to finish logging in.  You will only need to do this once per email address.<\/li><li><b>After that<\/b>, you can use your email and password to log in from any computer.<\/li><li><b>Also, while logged in<\/b>, you can create sub-accounts: passwords for other people to use.<\/li><\/ul><br><hr><\/hr><h1>This site is the successor to <img src="/images/weboggle.png" alt="WEBoggle" align="absmiddle"><\/h1><p>WordSplay is a massively multiplayer word-finding game inspired by Hasbro\'s classic board game Boggle&trade;.<\/p><p>Boggle&trade; is a registered trademark of Hasbro, Inc.  This web site is not affiliated with Hasbro in any way.<\/p>';
}
, contacting: function (data) {
  return '<div class="loading-box"><div class="loading-indicator">Contacting Server...<\/div><\/div>';
}
, logged_in: function (data) {
  return '<h1>Logged in!<\/h1><div class="loading-box"><div class="loading-indicator">Setting up...<\/div><\/div>';
}
, activate: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<h1>Activate your Account<\/h1><br><h2>ONE MORE STEP - and you\'ll be able to play:<\/h2><ul class="checklist"><li>I have emailed an', x, ' activation link to <b>', x, '<\/b>.<\/li><li>Make sure that you can receive email from <b>"game@shackworks.com"<\/b>, so that the message isn\'t blocked as spam.<\/li><li>You should receive it quickly - in less than a minute!  If not, double-check that your email address is properly typed (no "hotamil.com" or "eathlink.net" typos) and that the email isn\'t being blocked by a system - such as EarthLink\'s - that holds back mail until the sender follows a verification link.<\/li><li style="color: green; font-face: bold;">Use the link in that email to activate your account and finish logging in.<\/li><li><b style="color: red">If you have already activated and logged in with "', x, '", you shouldn\'t be seeing this message!Double-check for typos.<\/b><\/li><\/ul>'];
  while (d.n(1, p)) {
   p[1]=d.v('dup');p[3]=p[5]=d.v('email');
  }
  return p.join('');
}
, remind: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<h1>Password Reminder Sent<\/h1><br><ul class="checklist"><li>I have emailed your password to <b>', x, '<\/b>.<\/li><li>Make sure that you can receive email from <b>"game@shackworks.com"<\/b>, so that the message isn\'t blocked as spam.<\/li><\/ul>'];
  while (d.n(1, p)) {
   p[1]=d.v('email');
  }
  return p.join('');
}
, email_status: function (data) {
  return '<br><h1>Status of the Email Message:<\/h1><div id="email-status" class="loading-box"><div class="loading-indicator">Sending ...<\/div><\/div>';
}
, omg: function (data) {
  return '<p >This device allows you to limit how long you play.<\/p><p >How many games do you want to play?<\/p>';
}
, omgt: function (data) {
  return '<p >How long after that do you want to be blocked from playing?<\/p>';
}
, omg1: function (data) {
  return '<p >After that you will be blocked from playing for one hour.<\/p>';
}
, omg2: function (data) {
  return '<p >Click "Limit Me!" and the game will log you out after one more game.<\/p><p >After that you will be blocked from playing for one hour.<\/p>';
}
, omg3: function (data) {
  return '<p >Click "Limit Me!" and the game will log you out after one more game.<\/p><p >How long after that do you want to be blocked from playing?<\/p>';
}
, omg_explain: function (data) {
  return '<p >I am trying out different devices to allow people to limit how long they play. On the 8th of the month for the next three months, your options for limiting how long you play will change. This is so I can figure out which device(s) work best.<\/p>';
}
, omgbpop: function (data) {
  return '<p >In between games you can choose to limit yourself to one more game.<\/p><p >A button will appear below the "Guess" box between games.<\/p>';
}
, omgsurv0: function (data) {
  return '<div style="margin: 8px; font-size: 12pt"><p >For the past 3 months I have been testing two different tools to allow players to control how long they play.<\/p><br><p >It would be very helpful to me if you could take a couple of minutes to answer a few quick questions.<\/p><\/div>';
}
, final_round: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<h4>That was your last game!<\/h4><p>You asked to be logged off after ', x, ' round', x, '.  You can <a href="#" onclick="P_main.layout.setActiveItem(P_loginpage);">log in<\/a> again later.<\/p>'];
  while (d.n(1, p)) {
   p[3]=d.v('ns');p[1]=d.v('n');
  }
  return p.join('');
}
, final_round_t: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<h4>That was your last game!<\/h4><p>You asked to be logged off after ', x, ' round', x, ', and to be blocked from logging back in for ', x, ' minutes. You will need to <a href="#" onclick="P_main.layout.setActiveItem(P_loginpage);">log in<\/a> again after your block is over.<\/p>'];
  while (d.n(1, p)) {
   p[3]=d.v('ns');p[5]=d.v('t');p[1]=d.v('n');
  }
  return p.join('');
}
, blocked: function (data) {
  var x,d,p,r;x='';d=new TData(data);
  p = ['<h1>Login Blocked!<\/h1><h4>You asked to be blocked for ', x, ' hours:minutes.<\/h4><h4>You have ', x, ' left before you can log in again.<\/h4>'];
  while (d.n(1, p)) {
   p[1]=d.v('d');p[3]=d.v('t');
  }
  return p.join('');
}
};


var View={plusminus:{qtip:'Free/Restrict Height'
,handler:main2.plusminus
,id:'plus'
}};
View.help={handler:gl.help_toggle
,id:'help'
};
View.plugout={html:''
,border:false
,xtype:'panel'
,baseCls:'x-output'
,id:'plugout'
};
View.themes=[{src:0
,name:'Standard'
}, {url:'/w29/bare.css'
,src:2
,name:'Bare Bones'
}, {url:'gray'
,src:1
,name:'Gray'
}, {url:'darkgray'
,src:1
,name:'Dark Gray'
}, {url:'olive'
,src:1
,name:'Olive'
}];
View.prefmenu_listeners={beforeshow:{options:{single:true
}
,fn:main2.prefshow
}
};
View.scoresmenu={items:[{text:'"Disemvowel" player names'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsRv'
}, {text:'Show Teams'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsRt'
}, '-', {text:"Show all scores, not 'abbreviated' list"
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsRa'
}, {text:"Don't restrict this panel's height"
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsRh'
}]
,listeners:View.prefmenu_listeners
,id:'scoresmenu'
,defaults:{hideOnClick:false
}
};
View.boardmenu={items:[{text:'Lower-case letters'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsBl'
}, '-', {text:'Mark each letter as it is used (color highlighting)'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsBm'
}, {text:'Click to select letters'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsBc1'
,group:'optsBc'
}, {text:'Click to rotate the board'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsBc0'
,group:'optsBc'
}]
,listeners:View.prefmenu_listeners
,id:'boardmenu'
,defaults:{hideOnClick:false
}
};
View.guessesmenu={items:[{text:'Show wrong guesses'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsGw'
}]
,listeners:View.prefmenu_listeners
,id:'guessesmenu'
};
View.statusmenu={items:[{text:'Ignore typing of invalid letters'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSm'
}, '-', {text:'Show countdown timer'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSt0'
,group:'optsSt'
}, {text:'Show warning timer'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSt1'
,group:'optsSt'
}, {text:'No timer'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSt2'
,group:'optsSt'
}, '-', {menu:[{text:'None'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSg0'
,group:'optsSg'
}, {text:'Hovering beside input area'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSg1'
,group:'optsSg'
}, {text:'Below input area'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSg2'
,group:'optsSg'
}]
,text:'Feedback for new, correct guesses:'
}, {menu:[{text:'None'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSf0'
,group:'optsSf'
}, {text:'Hovering beside input area'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSf1'
,group:'optsSf'
}, {text:'Below input area'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsSf2'
,group:'optsSf'
}]
,text:'Feedback for other guesses:'
}]
,listeners:View.prefmenu_listeners
,id:'statusmenu'
,defaults:{hideOnClick:false
}
};
View.prior_wordsmenu={items:[{text:'Divide the word list into four groups'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsWg'
}, '-', {text:'Sort by length'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsWs0'
,group:'optsWs'
}, {text:'Random selection'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsWs1'
,group:'optsWs'
}, {text:'Sort alphabetically'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsWs2'
,group:'optsWs'
}, {text:'Reverse sort order'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsWr'
}, '-', {text:"Don't restrict this panel's height"
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsWh'
}]
,listeners:View.prefmenu_listeners
,id:'prior_wordsmenu'
,defaults:{hideOnClick:false
}
};
View.playmenu={items:[{text:'Show welcome page'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPp0'
,group:'optsPp'
}, {text:'Go to 4x4 board after login'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPp1'
,group:'optsPp'
}, {text:'Go to 5x5 board after login'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPp2'
,group:'optsPp'
}, '-', {text:'Collapse the sidebar menu during play'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPc'
}, {text:'Play sound effects'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPs'
}, {text:'Reset the game board layout'
,handler:main2.menuclick
,id:'def_columns'
}, {menu:[{text:'Standard'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPt0'
,group:'optsPt'
}, {text:'Bare Bones'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPt1'
,group:'optsPt'
}, {text:'Gray'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPt2'
,group:'optsPt'
}, {text:'Dark Gray'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPt3'
,group:'optsPt'
}, {text:'Olive'
,listeners:{checkchange:main2.prefck
}
,checked:true
,id:'optsPt4'
,group:'optsPt'
}]
,text:'Choose a theme:'
}]
,listeners:View.prefmenu_listeners
,id:'playmenu'
,defaults:{hideOnClick:false
}
};
View.prefmenu={items:["You can also change these via 'gear' buttons", '-', {menu:View.scoresmenu
,text:'Last Round Scores'
}, {menu:View.boardmenu
,text:'Game Board (letters)'
}, {menu:View.guessesmenu
,text:'Guesses'
}, {menu:View.statusmenu
,text:'Status / guess input'
}, {menu:View.prior_wordsmenu
,text:'Last Round Words'
}, {menu:View.playmenu
,text:'Play'
}]
,id:'prefmenu'
,defaults:{hideOnClick:false
}
};
View.prep_acct={items:[{text:'Change Email Address...'
,id:'change_email'
}, {text:'Change Password...'
,id:'change_pwd'
}, {menu:View.prefmenu
,id:'prefs'
,text:'Change Play Settings'
}]
,id:'prep_acct'
};
View.gear={qtip:'Customize this part of the game'
,handler:main2.gear
,id:'gear'
};
View.sidebar=[{items:[{html:'<ul><li><a href="mailto:game@shackworks.com" class="external" id="game-name" ext:qtip="If you can come up with a better name than <i>WordSplay<\/i>, I may use it!<br><br>It must not be similar to existing tradmarks, such as <i>Boggle<\/i>." ><img src="/images/s.gif" class="icon"> Name this Game!<\/a><\/li><li><a href="https://www.paypal.com/xclick/business=game@shackworks.com&amp;item_name=WordSplay%20donation" target="_blank" class="external" id="game-donate" ext:qtip="Want to donate, but not using PayPal?  Contact Me!" ><img src="/images/s.gif" class="icon"> Please Donate<\/a><\/li><li><a href="about:" id="game-news"><img src="/images/s.gif" class="icon"> Game News<\/a><\/li><\/ul>'
,border:false
}]
,id:'sidebar-game'
,title:''
}, {items:[{html:'<ul><li><a href="about:" id="prep-rules"><img src="/images/s.gif" class="icon"> How to Play<\/a><\/li><li><a href="about:" id="prep-name" ext:qtip="You can change your game name at any time." ><img src="/images/s.gif" class="icon"> Your Game Name<\/a><\/li><li><a href="about:" id="prep-log"><img src="/images/s.gif" class="icon"> Log in<\/a><\/li><\/ul>'
,border:false
}, {xtype:'button'
,menu:View.prep_acct
,tooltip:'Show/Hide menu'
,menuAlign:'tl-tr?'
,text:'Manage Account'
,id:'prep_acct_btn'
}]
,id:'sidebar-prep'
,title:'Getting Ready'
}, {items:[{html:'<ul><\/ul>'
,border:false
}]
,gmenu:'play'
,tools:[View.gear]
,id:'sidebar-play'
,title:'Play!'
}, {items:[{html:'<ul><li><a href="about:" id="learn-about"><img src="/images/s.gif" class="icon"> About the Game<\/a><\/li><li><a href="about:" id="learn-oldnews"><img src="/images/s.gif" class="icon"> Old News<\/a><\/li><li><a href="about:" id="learn-contact"><img src="/images/s.gif" class="icon"> Contact Me (+FAQ)<\/a><\/li><li><a href="http://wordgameguy.blogspot.com" target="_blank" class="external" id="learn-blog" ext:qtip="Opens my blog in a new window." ><img src="/images/s.gif" class="icon"> Visit the Blog<\/a><\/li><\/ul>'
,border:false
}]
,id:'sidebar-learn'
,title:'Learn More'
}, {items:[{html:'<ul><li><a href="about:" id="other-snap" ext:qtip="Not yet implemented." ><img src="/images/s.gif" class="icon"> Snapshot<\/a><\/li><li><a href="about:" id="other-reload" ext:qtip="Try this if the page seems broken." ><img src="/images/s.gif" class="icon"> Reload Page<\/a><\/li><\/ul>'
,border:false
}]
,id:'sidebar-other'
,title:'Other Stuff'
}];
View.prior_scores_grid_sel=new Ext.grid.RowSelectionModel({singleSelect:false});
View.omg1={autoHeight:true
,modal:true
,title:'Limit Your Play Time?'
,items:[{buttons:[{text:'Limit Me!'
}, {text:'No Thanks!'
}]
,bodyStyle:'padding: 4px'
,layout:'table'
,xtype:'form'
,defaults:{bodyStyle:'padding-bottom: 8px'
,border:false
,xtype:'panel'
}
,items:[{colspan:6
,html:templates.omg()
,xtype:'textpanel'
}, {html:'&nbsp;'
,xtype:'textpanel'
}, {items:[{selectOnFocus:'true'
,name:'nrounds'
,xtype:'textfield'
,maskRe:/[1-9][0-9]*|0/
,defaultAutoCreate:{autocomplete:'off'
,tag:'input'
,type:'text'
,size:'5'
}
}]
,xtype:'textpanel'
,cls:'tright'
}, {colspan:2
,html:'&nbsp;'
,xtype:'textpanel'
}, {colspan:2
,style:'width: 120px'
,html:'&nbsp;'
,xtype:'textpanel'
}, {colspan:6
,html:templates.omg1()
,xtype:'textpanel'
}, {html:'<a href="#" onclick="Main.omg_explain();">What\'s this all about?<\/a>'
,xtype:'textpanel'
,colspan:6
,id:'omg_explain'
,cls:'tright'
}]
,layoutConfig:{columns:6
}
,id:'omg1form'
}]
,width:420
,hideBorders:true
,plugins:[plug_form_dialog]
,main_submit:'set_omg1'
,cls:'omg'
};
View.omg3={autoHeight:true
,modal:true
,title:'Limit Your Play Time?'
,items:[{buttons:[{text:'Limit Me!'
}, {text:'No Thanks!'
}]
,bodyStyle:'padding: 4px'
,layout:'table'
,xtype:'form'
,defaults:{bodyStyle:'padding-bottom: 8px'
,border:false
,xtype:'panel'
}
,items:[{colspan:6
,html:templates.omg3()
,xtype:'textpanel'
}, {html:'&nbsp;'
,xtype:'textpanel'
}, {items:[{fieldLabel:'Hours'
,xtype:'textfield'
,name:'nhours'
,selectOnFocus:'true'
,maskRe:/[1-9][0-9]*|0/
,value:'1'
,defaultAutoCreate:{autocomplete:'off'
,tag:'input'
,type:'text'
,size:'5'
}
}]
,xtype:'textpanel'
,cls:'tright'
}, {html:'&nbsp;:&nbsp;'
,xtype:'textpanel'
}, {fieldLabel:'Minutes'
,xtype:'textfield'
,name:'nminutes'
,selectOnFocus:'true'
,maskRe:/[1-9][0-9]*|0/
,value:'0'
,defaultAutoCreate:{autocomplete:'off'
,tag:'input'
,type:'text'
,size:'5'
}
}, {colspan:2
,html:'&nbsp;'
,xtype:'textpanel'
}, {html:'&nbsp;'
,xtype:'textpanel'
}, {html:'Hours'
,xtype:'textpanel'
,cls:'tright'
}, {html:'&nbsp;:&nbsp;'
,xtype:'textpanel'
}, {html:'Minutes'
,xtype:'textpanel'
}, {colspan:2
,style:'width: 120px'
,html:'&nbsp;'
,xtype:'textpanel'
}, {html:'<a href="#" onclick="Main.omg_explain();">What\'s this all about?<\/a>'
,xtype:'textpanel'
,colspan:6
,id:'omg_explain'
,cls:'tright'
}]
,layoutConfig:{columns:6
}
,id:'omg3form'
}]
,width:420
,hideBorders:true
,plugins:[plug_form_dialog]
,main_submit:'set_omg3'
,cls:'omg'
};
View.omg2={autoHeight:true
,modal:true
,title:'Limit Your Play Time?'
,items:[{buttons:[{text:'Limit Me!'
}, {text:'No Thanks!'
}]
,bodyStyle:'padding: 4px'
,layout:'table'
,xtype:'form'
,defaults:{bodyStyle:'padding-bottom: 8px'
,border:false
,xtype:'panel'
}
,items:[{colspan:6
,html:templates.omg2()
,xtype:'textpanel'
}, {html:'<a href="#" onclick="Main.omg_explain();">What\'s this all about?<\/a>'
,xtype:'textpanel'
,colspan:6
,id:'omg_explain'
,cls:'tright'
}]
,layoutConfig:{columns:6
}
,id:'omg2form'
}]
,width:420
,hideBorders:true
,plugins:[plug_form_dialog]
,main_submit:'set_omg2'
,cls:'omg'
};
View.lpfooter={html:templates.login_footer()
,border:false
};
View.prior_words_none_store=new Ext.data.Store();
View.prior_scores_grid_cols=[{width:30
,align:'right'
,fixed:true
,dataIndex:'rank'
,header:'Rank'
}, {id:'name'
,width:120
,renderer:main2.render_player_name
,dataIndex:'name'
,header:'Name'
}, {width:30
,align:'right'
,fixed:true
,dataIndex:'score'
,header:'Score'
}];
View.prior_scores_grid_cm=new Ext.grid.ColumnModel(View.prior_scores_grid_cols);
View.prior_scores_store_cfg={fields:['rank', 'name', 'score', 'c', 'who', 'i', 'x']
,id:'prior_scores'
};
View.prior_scores_store=new Ext.data.SimpleStore(View.prior_scores_store_cfg);
View.prior_scores_grid={autoHeight:true
,xtype:'grid'
,cm:View.prior_scores_grid_cm
,loadMask:true
,trackMouseOver:false
,sm:View.prior_scores_grid_sel
,iconCls:'icon-grid'
,viewConfig:{enableRowBody:true
,getRowClass:gl.score_row
,forceFit:true
,emptyText:'<span class="dim">No scores last round.<\/span>'
,autoFill:false
,onLayout:gl.scores_layout
}
,border:true
,id:'scores'
,store:View.prior_scores_store
};
View.b0={html:'Loading board ...'
,id:'b0'
};
View.b4x4={html:'<table cellspacing=0 cellpadding=0><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><\/table>'
,id:'b4x4'
};
View.b5x5={html:'<table cellspacing=0 cellpadding=0><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><tr><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><td class="cell">-<\/td><\/tr><\/table>'
,id:'b5x5'
};
View.boards={items:[View.b0, View.b4x4, View.b5x5]
,id:'boards'
,defaults:{autoHeight:true
,autoWidth:true
}
};
View.board={autoHeight:true
,title:'Game Board'
,items:[View.boards]
,autoWidth:true
,bbar:[{text:'Rotate'
,handler:main2.rotate
,tooltip:'Rotate the board'
}, {text:'Clear'
,handler:main2.no_guess
,tooltip:'Clear away your guess'
}, {text:'Submit'
,handler:main2.guess_submit
,tooltip:'Submit your guess'
}, '->']
,pcode:'A'
,id:'board'
};
View.th={url:'olive'
,src:1
,name:'Olive'
};
View.testfields={items:[{fieldLabel:'Field 1'
,xtype:'textfield'
,name:'tst1'
,width:400
,maxLength:80
,defaultAutoCreate:{tag:'input'
}
}, {fieldLabel:'Field 2'
,xtype:'textfield'
,width:400
,fieldClass:''
,maxLength:80
,defaultAutoCreate:{tag:'input'
}
,name:'tst2'
}, {triggerConfig:{html:' ... '
,tag:'span'
,cls:'fine'
}
,fieldLabel:'Field 3'
,xtype:'trigger'
,width:300
,maxLength:80
,defaultAutoCreate:{tag:'input'
,type:'password'
}
,name:'tst3'
}, {triggerConfig:{html:' ... '
,tag:'span'
,cls:'fine'
}
,fieldLabel:'Field 4'
,xtype:'trigger'
,width:300
,fieldClass:''
,maxLength:80
,defaultAutoCreate:{tag:'input'
,type:'password'
}
,name:'tst4'
}]
,border:'false'
,xtype:'panel'
,layout:'form'
};
View.prior_words_store_cfg={fields:['word', 'who', 'value', 'i', 'rnd']
,root:'words'
,id:'word'
,data:{words:[]
}
};
View.prior_words_store=new Ext.data.JsonStore(View.prior_words_store_cfg);
View.prior_words_dv={xtype:'dataview2'
,overClass:'x-view-over'
,tpl:new Ext.XTemplate(
            '<tpl for=".">',
            '{[xindex > 1 ? ", " : ""]}<div id="pw-{i}" class="fb_{who} word"><span class="word">{word}<\/span><\/div>',
            '<\/tpl>'
        )
,idpref:'pw-'
,emptyText:'<span class="dim">No words.<\/span>'
,listeners:{render:gl.dv_rendered
}
,multiSelect:true
,store:View.prior_words_store
,selectedClass:'lit'
,id:'prior_words_dv'
,itemSelector:'div.word'
};
View.prior_words_tbar=['Show:', {name:'some'
,text:'Some'
,tooltip:'Show/Hide: Words you did not find, but someone did'
,handler:toggle_fb
,pressed:true
,iconCls:'fb_some'
,enableToggle:true
,cls:'pwbtn'
}, {name:'me'
,text:'You'
,tooltip:'Show/Hide: Words you and other players found'
,handler:toggle_fb
,pressed:true
,iconCls:'fb_me'
,enableToggle:true
,cls:'pwbtn'
}, {name:'only'
,text:'Only'
,tooltip:'Show/Hide: Words only you found'
,handler:toggle_fb
,pressed:true
,iconCls:'fb_only'
,enableToggle:true
,cls:'pwbtn'
}, {name:'none'
,text:'No one'
,tooltip:'Show/Hide: Words no one found'
,handler:toggle_fb
,pressed:true
,iconCls:'fb_none'
,enableToggle:true
,cls:'pwbtn'
}];
View.prior_words1={autoHeight:true
,items:[View.prior_words_dv]
,minButtonWidth:10
,id:'prior_words_ng'
,tbar:View.prior_words_tbar
};
View.prior_words_only_store=new Ext.data.Store();
View.prior_words_only_dv={xtype:'dataview2'
,overClass:'x-view-over'
,tpl:new Ext.XTemplate(
            '<tpl for=".">',
            '{[xindex > 1 ? ", " : ""]}<div id="pwo-{i}" class="word"><span class="word">{word}<\/span><\/div>',
            '<\/tpl>'
        )
,idpref:'pwo-'
,emptyText:'<span class="dim">No words.<\/span>'
,listeners:{render:gl.dv_rendered
}
,multiSelect:true
,store:View.prior_words_only_store
,selectedClass:'lit'
,id:'prior_words_only'
,itemSelector:'div.word'
};
View.prior_words_only={items:[View.prior_words_only_dv]
,collapsible:true
,title:'Words <b>only<\/b> you found'
};
View.prior_words_me_store=new Ext.data.Store();
View.prior_words_me_dv={xtype:'dataview2'
,overClass:'x-view-over'
,tpl:new Ext.XTemplate(
            '<tpl for=".">',
            '{[xindex > 1 ? ", " : ""]}<div id="pwm-{i}" class="word"><span class="word">{word}<\/span><\/div>',
            '<\/tpl>'
        )
,idpref:'pwm-'
,emptyText:'<span class="dim">No words.<\/span>'
,listeners:{render:gl.dv_rendered
}
,multiSelect:true
,store:View.prior_words_me_store
,selectedClass:'lit'
,id:'prior_words_me'
,itemSelector:'div.word'
};
View.prior_words_me={items:[View.prior_words_me_dv]
,collapsible:true
,title:'Words you found'
};
View.prior_words_some_store=new Ext.data.Store();
View.prior_words_some_dv={xtype:'dataview2'
,overClass:'x-view-over'
,tpl:new Ext.XTemplate(
            '<tpl for=".">',
            '{[xindex > 1 ? ", " : ""]}<div id="pws-{i}" class="word"><span class="word">{word}<\/span><\/div>',
            '<\/tpl>'
        )
,idpref:'pws-'
,emptyText:'<span class="dim">No words.<\/span>'
,listeners:{render:gl.dv_rendered
}
,multiSelect:true
,store:View.prior_words_some_store
,selectedClass:'lit'
,id:'prior_words_some'
,itemSelector:'div.word'
};
View.prior_words_some={items:[View.prior_words_some_dv]
,collapsible:true
,title:'Words other players found'
};
View.prior_words_none_dv={xtype:'dataview2'
,overClass:'x-view-over'
,tpl:new Ext.XTemplate(
            '<tpl for=".">',
            '{[xindex > 1 ? ", " : ""]}<div id="pwn-{i}" class="word"><span class="word">{word}<\/span><\/div>',
            '<\/tpl>'
        )
,idpref:'pwn-'
,emptyText:'<span class="dim">No words.<\/span>'
,listeners:{render:gl.dv_rendered
}
,multiSelect:true
,itemSelector:'div.word'
,selectedClass:'lit'
,id:'prior_words_none'
,store:View.prior_words_none_store
};
View.prior_words_none={items:[View.prior_words_none_dv]
,collapsible:true
,title:'Words no one found'
};
View.prior_words2={items:[View.prior_words_only, View.prior_words_me, View.prior_words_some, View.prior_words_none]
,autoHeight:true
,id:'prior_words_g'
};
View.prior_words_list={activeItem:0
,autoHeight:true
,layout:'card'
,title:'Word List for Last Round'
,items:[View.prior_words1, View.prior_words2]
,pcode:'E'
,tools:[View.plusminus, View.gear]
,id:'prior_words'
};
View.testform={onSubmit:Ext.emptyFn
,labelWidth:65
,xtype:'form'
,title:'There should be four active text entry areas in this box:'
,items:[View.testfields]
,frame:true
,submit:Ext.emptyFn
,formId:'testform'
,width:600
,id:'testform'
,labelAlign:'right'
};
View.password={autoHeight:true
,hideBorders:true
,title:'Change Password for Account'
,items:[{buttons:[{text:'Change Password'
}, {text:'Cancel'
}]
,items:[{fieldLabel:'New password'
,xtype:'textfield'
,listeners:{keypress:capsLockWarn
}
,allowBlank:false
,selectOnFocus:true
,anchor:'100%'
,name:'pwd'
}]
,bodyStyle:'padding: 4px'
,labelWidth:100
,xtype:'form'
}]
,width:500
,modal:true
,plugins:[plug_form_dialog]
,main_submit:'savepwd'
};
View.notes={pcode:'F'
,html:templates.notes()
,tools:[View.help]
,id:'notes'
,title:'Notes and Tips'
};
View.omgsurv0={autoHeight:true
,modal:true
,title:'Short Survey'
,items:[{html:templates.omgsurv0()
,xtype:'textpanel'
}]
,buttons:[{text:'Yes, ask me'
,handler:Main.show_omgsurv_click
}, {text:'No'
,handler:Main.show_omgsurv_click
}, {text:'Maybe Later'
,handler:Main.show_omgsurv_click
}]
,width:500
,hideBorders:true
,plugins:[plug_dialog]
,cls:'omg'
};
View.close={handler:function(e, target, panel){panel.ownerCt.remove(panel, true);}
,id:'close'
};
View.guesses_store=new Ext.data.SimpleStore({data:{words:[]}, id:0, fields:['word', 'cls', 'value']});
View.pinpause={qtip:'Pause/Resume Play'
,handler:main2.pause
,id:'pin'
};
View.prior_round={autoHeight:true
,layout:'fit'
,title:'Last Round'
,items:[View.prior_scores_grid]
,gmenu:'scores'
,bbar:['Total: (unknown)', '->']
,pcode:'D'
,tools:[View.plusminus, View.gear]
,id:'scoreboard'
};
View.scoremenu_t={items:[{text:'Join this team!'
,id:'join_t'
}, {menu:[{text:'as a friend'
,handler:gl.menuclick
,id:'mark_friend'
}, {text:'as someone to watch'
,handler:gl.menuclick
,id:'mark_watch'
}]
,id:'mark_t_as'
,text:'Mark this team:'
}, {menu:[{text:'for using offensive names'
,handler:gl.menuclick
,id:'ban_offensive'
}, {text:'for cheating'
,handler:gl.menuclick
,id:'ban_cheat'
}, {text:'for some other reason'
,handler:gl.menuclick
,id:'ban_other'
}]
,id:'ban_t_for'
,text:'Ban (hide) this team and all of its members:'
}]
,id:'scoremenu_t'
};
View.lpfields={bbar:[{lpbtn:''
,handler:Login.button
,type:'submit'
,text:'Log in'
}, '-', {lpbtn:'reg'
,handler:Login.button
,text:'Register my Email'
}, '->', {lpbtn:'pw'
,handler:Login.button
,text:'Send me a Password Reminder'
}]
,items:[{fieldLabel:'Your Email'
,xtype:'textfield'
,name:'email'
,width:400
,maxLength:80
,cls:'focus_first'
,defaultAutoCreate:{tag:'input'
}
}, {triggerConfig:{html:" (if you don't have one, just make one up!)"
,tag:'span'
,cls:'fine'
}
,fieldLabel:'Password'
,xtype:'trigger'
,width:200
,listeners:{render:function (pw) { pw.el.on({keypress: capsLockWarn}); }
}
,maxLength:80
,defaultAutoCreate:{tag:'input'
,type:'password'
}
,name:'pw'
}, {fieldLabel:'&nbsp;'
,xtype:'checkbox'
,boxLabel:'This is a public or shared computer, so log out automatically.'
,labelSeparator:''
,inputValue:'Y'
,anchor:'-4'
,name:'pub'
}]
,border:'false'
,xtype:'panel'
,layout:'form'
};
View.lpform={onSubmit:Login.submit
,labelWidth:65
,xtype:'form'
,title:'Please log in, or register your email address so that you can log in'
,items:[View.lpfields]
,frame:true
,submit:Login.submit
,formId:'loginform'
,width:600
,id:'login'
,labelAlign:'right'
};
View.round_status={items:[{labelWidth:38
,xtype:'form'
,items:[{xtype:'textfield'
,fieldLabel:'Guess'
,id:'input'
,name:'n'
,anchor:'-8'
}, {html:''
,border:false
,xtype:'panel'
,baseCls:'x-output'
,id:'output'
}]
,disabled:true
,bodyStyle:'padding:5px;'
,id:'input_form'
}]
,pcode:'B'
,tools:[View.pinpause, View.gear]
,id:'status'
,title:'Status'
};
View.email={autoHeight:true
,hideBorders:true
,title:'Change Email for Account'
,items:[{buttons:[{text:'Change Email'
}, {text:'Cancel'
}]
,items:[{fieldLabel:'Your email address'
,xtype:'textfield'
,name:'email'
,maskRe:/[^ ]/
,selectOnFocus:true
,anchor:'100%'
,allowBlank:false
}, {html:templates.email()
,border:false
,xtype:'panel'
}]
,bodyStyle:'padding: 4px'
,labelWidth:120
,xtype:'form'
}]
,width:400
,modal:true
,plugins:[plug_form_dialog]
,main_submit:'saveemail'
};
View.omg={autoHeight:true
,modal:true
,title:'Limit Your Play Time?'
,items:[{buttons:[{text:'Limit Me!'
}, {text:'No Thanks!'
}]
,bodyStyle:'padding: 4px'
,layout:'table'
,xtype:'form'
,defaults:{bodyStyle:'padding-bottom: 8px'
,border:false
,xtype:'panel'
}
,items:[{colspan:6
,html:templates.omg()
,xtype:'textpanel'
}, {html:'&nbsp;'
,xtype:'textpanel'
}, {items:[{selectOnFocus:'true'
,name:'nrounds'
,xtype:'textfield'
,maskRe:/[1-9][0-9]*|0/
,defaultAutoCreate:{autocomplete:'off'
,tag:'input'
,type:'text'
,size:'5'
}
}]
,xtype:'textpanel'
,cls:'tright'
}, {colspan:4
,html:'&nbsp;'
,xtype:'textpanel'
}, {colspan:6
,html:templates.omgt()
,xtype:'textpanel'
}, {html:'&nbsp;'
,xtype:'textpanel'
}, {items:[{fieldLabel:'Hours'
,xtype:'textfield'
,name:'nhours'
,selectOnFocus:'true'
,maskRe:/[1-9][0-9]*|0/
,defaultAutoCreate:{autocomplete:'off'
,tag:'input'
,type:'text'
,size:'5'
}
}]
,xtype:'textpanel'
,cls:'tright'
}, {html:'&nbsp;:&nbsp;'
,xtype:'textpanel'
}, {fieldLabel:'Minutes'
,xtype:'textfield'
,name:'nminutes'
,selectOnFocus:'true'
,maskRe:/[1-9][0-9]*|0/
,defaultAutoCreate:{autocomplete:'off'
,tag:'input'
,type:'text'
,size:'5'
}
}, {colspan:2
,html:'&nbsp;'
,xtype:'textpanel'
}, {html:'&nbsp;'
,xtype:'textpanel'
}, {html:'Hours'
,xtype:'textpanel'
,cls:'tright'
}, {html:'&nbsp;:&nbsp;'
,xtype:'textpanel'
}, {html:'Minutes'
,xtype:'textpanel'
}, {colspan:2
,style:'width: 120px'
,html:'&nbsp;'
,xtype:'textpanel'
}, {html:'<a href="#" onclick="Main.omg_explain();">What\'s this all about?<\/a>'
,xtype:'textpanel'
,colspan:6
,id:'omg_explain'
,cls:'tright'
}]
,layoutConfig:{columns:6
}
,id:'omgform'
}]
,width:420
,hideBorders:true
,plugins:[plug_form_dialog]
,main_submit:'set_omg'
,cls:'omg'
};
View.chat_grid_cols=[{width:50
,dataIndex:'name'
,fixed:true
,id:'name'
,header:'Name'
}, {width:30
,dataIndex:'line'
,id:'line'
,header:'Line'
}];
View.chat_grid_cm=new Ext.grid.ColumnModel(View.chat_grid_cols);
View.omgbpop={autoHeight:true
,modal:true
,title:'Limit Your Play Time?'
,items:[{buttons:[{text:'OK'
}, {text:'Stop showing this message'
}]
,bodyStyle:'padding: 4px'
,layout:'table'
,xtype:'form'
,defaults:{bodyStyle:'padding-bottom: 8px'
,border:false
,xtype:'panel'
}
,items:[{colspan:6
,html:templates.omgbpop()
,xtype:'textpanel'
}, {html:'<a href="#" onclick="Main.omg_explain();">What\'s this all about?<\/a>'
,xtype:'textpanel'
,colspan:6
,id:'omg_explain'
,cls:'tright'
}]
,layoutConfig:{columns:6
}
,id:'omgbform'
}]
,width:420
,hideBorders:true
,plugins:[plug_form_dialog]
,main_submit:'omgbpop_stop'
,main_submit_btn:1
,cls:'omg'
};
View.chat_grid_sel=new Ext.grid.RowSelectionModel({singleSelect:false});
View.chat_store=new Ext.data.SimpleStore({data:{}, fields:['ts', 'c', 'channel', 'code', 'name', 'team', 'line'], sortInfo:{field:'ts', direction:'DESC'}});
View.chat_grid={xtype:'grid'
,cm:View.chat_grid_cm
,loadMask:true
,trackMouseOver:false
,height:200
,sm:View.chat_grid_sel
,iconCls:'icon-grid'
,viewConfig:{enableRowBody:true
,getRowClass:gl.chat_row
,autoFill:false
,forceFit:true
,emptyText:'<span class="dim">No one has said anything!<\/span>'
}
,border:true
,id:'chat_grid'
,store:View.chat_store
};
View.guesses_dv={xtype:'dataview2'
,overClass:'x-view-over'
,tpl:new Ext.XTemplate(
            '<tpl for=".">',
            '{[xindex > 1 ? ", " : ""]}<div id="gw-{word}" class="{cls} word"><span class="word">{word}',
            '<tpl if="values.value &gt; 1"><span class="points">:{value}<\/span><\/tpl><\/span><\/div>',
            '<\/tpl>'
        )
,emptyText:'<span class="dim">No words guessed.<\/span>'
,listeners:{render:gl.dv_rendered
}
,multiSelect:false
,store:View.guesses_store
,id:'guesses_dv'
,itemSelector:'div.word'
};
View.omg3btn={buttons:[{text:'Log me off after one more game!'
,handler:Main.show_omg3
}]
,autoWidth:true
,autoHeight:true
,hideBorders:true
,id:'omg3btn'
};
View.west={collapsible:true
,title:'WordSplay'
,items:View.sidebar
,region:'west'
,minSize:50
,width:150
,maxSize:400
,split:true
,animCollapse:false
,cmargins:'0 0 0 0'
,margins:'0 0 0 0'
,defaultType:'sidebar'
,id:'sidebar'
};
View.gamename={autoHeight:true
,hideBorders:true
,title:'Choose Your Game Name'
,items:[{labelWidth:65
,xtype:'form'
,items:[{fieldLabel:'Your name'
,allowBlank:false
,maskRe:/[^,;=:]/
,name:'gamename'
,anchor:'100%'
,xtype:'textfield'
}, {bodyStyle:'padding-bottom: 8px'
,html:templates.gamename()
,border:false
,xtype:'panel'
}, {maskRe:/[^,;=:]/
,fieldLabel:'Your team'
,xtype:'textfield'
,name:'team'
,anchor:'100%'
}, {html:templates.teamname()
,border:false
,xtype:'panel'
}]
,buttons:[{selectOnFocus:true
,text:'Use this Name!'
}, {text:'Cancel'
}]
,bodyStyle:'padding: 4px'
,id:'gamenameform'
}]
,width:500
,modal:true
,plugins:[plug_form_dialog]
,main_submit:'savename'
};
View.lpheader={html:templates.login_header()
,border:false
};
View.loginpage={autoScroll:true
,items:[View.lpheader, View.lpform, View.lpfooter]
,bodyStyle:'padding:4px'
,id:'loginpage'
};
View.scoremenu={items:[{menu:[{text:'as a friend'
,handler:gl.menuclick
,id:'mark_friend'
}, {text:'as someone to watch'
,handler:gl.menuclick
,id:'mark_watch'
}]
,id:'mark_as'
,text:'Mark this player:'
}, {menu:[{text:'for using offensive names'
,handler:gl.menuclick
,id:'ban_offensive'
}, {text:'for cheating'
,handler:gl.menuclick
,id:'ban_cheat'
}, {text:'for some other reason'
,handler:gl.menuclick
,id:'ban_other'
}]
,id:'ban_for'
,text:'Ban (hide) this player:'
}]
,id:'scoremenu'
};
View.omg2btn={buttons:[{text:'Log me off after one more game!'
,handler:Main.show_omg2
}]
,autoWidth:true
,autoHeight:true
,hideBorders:true
,id:'omg2btn'
};
View.infopage={autoScroll:true
,bodyStyle:'padding:4px'
,id:'infopage'
};
View.blankpage={html:''
,id:'blankpage'
};
View.center={activeItem:0
,layout:'card'
,xtype:'panel'
,items:[View.infopage, View.blankpage]
,region:'center'
,defaults:{margins:'0 0 0 0'
,xtype:'panel'
}
,margins:'0 0 0 0'
,border:false
,id:'main'
};
View.viewport={items:[View.west, View.center]
,layout:'border'
};
View.guesses={autoHeight:true
,title:'Guesses'
,items:[View.guesses_dv]
,collapsed:true
,pcode:'C'
,id:'guesses'
};
View.chat={layout:'fit'
,title:'Chat: see Notes panel'
,items:[View.chat_grid]
,collapsed:true
,pcode:'G'
,tools:[View.gear]
,id:'chat'
};
View.menuclick={itemclick:gl.menuclick
};
View.i=4;
View.gamepage={items:[{columnWidth:0.333
,defaults:{tools:[View.gear]
}
,items:[View.board]
}, {columnWidth:0.333
,defaults:{tools:[View.gear]
}
,items:[View.prior_round]
}, {columnWidth:0.333
,defaults:{tools:[View.gear]
}
,items:[View.notes]
}]
,layoutConfig:{scrollOffset:19
}
,xtype:'gamepage'
,id:'gamepage'
};
var Model={RoundFlags:{fdep:[2, 3]
,fset:['boardloaded', 'resultloaded', 'live_boardloaded', 'live', 'board', 'live_board_resultloaded']
,names:['live_boardloaded', 'live_board_resultloaded']
,check:{resultloaded:[1]
,live:[0, 1]
,boardloaded:[0]
,board:[1]
}
}};
Model.cells4x4=[{rota:3
,neighbors:[1, 4, 5]
,y:0
,x:0
}, {rota:7
,neighbors:[0, 2, 4, 5, 6]
,y:0
,x:1
}, {rota:11
,neighbors:[1, 3, 5, 6, 7]
,y:0
,x:2
}, {rota:15
,neighbors:[2, 6, 7]
,y:0
,x:3
}, {rota:2
,neighbors:[0, 1, 5, 8, 9]
,y:1
,x:0
}, {rota:6
,neighbors:[0, 1, 2, 4, 6, 8, 9, 10]
,y:1
,x:1
}, {rota:10
,neighbors:[1, 2, 3, 5, 7, 9, 10, 11]
,y:1
,x:2
}, {rota:14
,neighbors:[2, 3, 6, 10, 11]
,y:1
,x:3
}, {rota:1
,neighbors:[4, 5, 9, 12, 13]
,y:2
,x:0
}, {rota:5
,neighbors:[4, 5, 6, 8, 10, 12, 13, 14]
,y:2
,x:1
}, {rota:9
,neighbors:[5, 6, 7, 9, 11, 13, 14, 15]
,y:2
,x:2
}, {rota:13
,neighbors:[6, 7, 10, 14, 15]
,y:2
,x:3
}, {rota:0
,neighbors:[8, 9, 13]
,y:3
,x:0
}, {rota:4
,neighbors:[8, 9, 10, 12, 14]
,y:3
,x:1
}, {rota:8
,neighbors:[9, 10, 11, 13, 15]
,y:3
,x:2
}, {rota:12
,neighbors:[10, 11, 14]
,y:3
,x:3
}];
Model.cells5x5=[{rota:4
,neighbors:[1, 5, 6]
,y:0
,x:0
}, {rota:9
,neighbors:[0, 2, 5, 6, 7]
,y:0
,x:1
}, {rota:14
,neighbors:[1, 3, 6, 7, 8]
,y:0
,x:2
}, {rota:19
,neighbors:[2, 4, 7, 8, 9]
,y:0
,x:3
}, {rota:24
,neighbors:[3, 8, 9]
,y:0
,x:4
}, {rota:3
,neighbors:[0, 1, 6, 10, 11]
,y:1
,x:0
}, {rota:8
,neighbors:[0, 1, 2, 5, 7, 10, 11, 12]
,y:1
,x:1
}, {rota:13
,neighbors:[1, 2, 3, 6, 8, 11, 12, 13]
,y:1
,x:2
}, {rota:18
,neighbors:[2, 3, 4, 7, 9, 12, 13, 14]
,y:1
,x:3
}, {rota:23
,neighbors:[3, 4, 8, 13, 14]
,y:1
,x:4
}, {rota:2
,neighbors:[5, 6, 11, 15, 16]
,y:2
,x:0
}, {rota:7
,neighbors:[5, 6, 7, 10, 12, 15, 16, 17]
,y:2
,x:1
}, {rota:12
,neighbors:[6, 7, 8, 11, 13, 16, 17, 18]
,y:2
,x:2
}, {rota:17
,neighbors:[7, 8, 9, 12, 14, 17, 18, 19]
,y:2
,x:3
}, {rota:22
,neighbors:[8, 9, 13, 18, 19]
,y:2
,x:4
}, {rota:1
,neighbors:[10, 11, 16, 20, 21]
,y:3
,x:0
}, {rota:6
,neighbors:[10, 11, 12, 15, 17, 20, 21, 22]
,y:3
,x:1
}, {rota:11
,neighbors:[11, 12, 13, 16, 18, 21, 22, 23]
,y:3
,x:2
}, {rota:16
,neighbors:[12, 13, 14, 17, 19, 22, 23, 24]
,y:3
,x:3
}, {rota:21
,neighbors:[13, 14, 18, 23, 24]
,y:3
,x:4
}, {rota:0
,neighbors:[15, 16, 21]
,y:4
,x:0
}, {rota:5
,neighbors:[15, 16, 17, 20, 22]
,y:4
,x:1
}, {rota:10
,neighbors:[16, 17, 18, 21, 23]
,y:4
,x:2
}, {rota:15
,neighbors:[17, 18, 19, 22, 24]
,y:4
,x:3
}, {rota:20
,neighbors:[18, 19, 23]
,y:4
,x:4
}];
Model.MainFlags={fdep:[2, 2, 2]
,fset:['gamepage', 'gamepage_round', 'P_games', 'round', 'P', 'games', 'playing', 'playing_games']
,names:['P_games', 'playing_games', 'gamepage_round']
,check:{P:[0]
,gamepage:[2]
,games:[0, 1]
,playing:[1]
,round:[2]
}
};
Model.GameFlags={fdep:[2]
,fset:['cells', 'round_cells', 'round']
,names:['round_cells']
,check:{cells:[0]
,round:[0]
}
};


if (!(d_.html_version && ('/w'+ d_.html_version +'/') == '/w29/')) { d_.init_done = 0; }

if (!d_.init_done) { 
  d_.std4x4 = (new Scoring()).pickle();
  d_.std5x5 = (new Scoring({min_length: 4})).pickle();
  /* Default to standard scoring */
  d_.scoring = {'4x4': d_.std4x4, '5x5': d_.std5x5};

  d_.boards = {};
  d_.banned = {};
  d_.marked = {};
  d_.opts = unpack_options('');
  d_.chat = false;
  d_.chat_data = {};

  d_.html_version = parseInt('/w29/'.replace(/[^0-9]/g, ''), 10);
  d_.init_done = 1;
}

Ext.onReady(function(){
    clearTimeout(window.failure_timer);
    Ext.getBody().removeClass('loading');
    Ext.get('loading').remove();
    Main.init();
});
