//
// bingo.js -- buzzword bingo card generator
//
// Copyright 2007 Chris Pirazzi chris@pirazzi.net
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
// 
// You can find a copy of the GNU Affero General Public License
// at <http://lurkertech.com/agpl-3.0.txt> and also at
// <http://www.gnu.org/licenses/>.
//

function ei(obj) { return document.getElementById(obj); }

var user_agent = navigator.userAgent.toLowerCase();
var n4=(document.layers);
var n6=(document.getElementById&&!document.all);
var ie=(document.all);
var o6=(user_agent.indexOf("opera") != -1);
var safari=(user_agent.indexOf("safari") != -1);
var msie  =(user_agent.indexOf("msie") != -1) && 
           (user_agent.indexOf("opera") == -1);

function assert(val)
{
    if (!val)
    {
        debugger;
        alert('assertion failure');
    }
}

function is_dom_node(obj)
{
    return ('object' == typeof(obj)) && ('nextSibling' in obj);
}

// apply specified function to arg and return result
function appl(func, x)
{
    return func(x);
}

// make DOM text node
//
function telt(text)
{
    return document.createTextNode(text);
}

// you can call the function two ways:
//    elt(tag, attrs,       kids)
//    elt(tag, attrs, func, kids)
// - attrs must be an object.  it cannot be undefined.
// - in the second form, func must be defined and it must
//   be a function object.
//
// either way, this function:
// - creates DOM object obj with specified tag
// - assigns HTML attributes (not javascript properties)
//   specified by the javascript object attrs 
// - initializes obj._tips to {}
// - if kids is defined,
//   - if kids is not an array, it is replaced with [kids]
//   - foreach kid
//     - if kid is not a DOM node, it is made into a text node.
//     - otherwise kid must be a DOM node already
//     - place kid underneath new obj
//     - if kid._tips is defined, then 
//       - foreach property x of kid._tips, set obj._tips.x = kid._tips.x
// - if func is defined 
//   (and if you specify it, it must be defined), calls func(obj)
// - returns obj
// 
// you can implement "synthesized tips," which bubble up from children
// to parent, by setting fields of obj._tips like so:
//
//       elt('tr', attrs,
//           function(obj) { obj._tips.foobar = 10; },
//           kids);
//
// by default, the tips of the kids are merged into the tips
// of the parent as shown above.  if you need to combine the
// tips in another way, you can do it like so:
//
//       elt('tr', attrs
//           function(obj) 
//           {
//               obj._tips.baz = 
//                   obj.childNodes.map(function(kid) 
//                                      {
//                                          return kid._tips.baz; 
//                                      });
//           },
//           kids);
//
// you can implement "inherited tips," which trickle down from parent
// to child, by using the function get_itip() below.
//
function elt()
{
    var args = Array.prototype.slice.call(arguments);

    var tag = args.shift();
    assert('string' == typeof(tag));
    var attrs = args.shift();
    assert(undefined != attrs &&
           'object' == typeof(attrs));
    var func = (args.length > 0 &&
                'function' == typeof(args[0])) ? args.shift() : undefined;
    assert(undefined == func ||
           'function' == typeof(func));
    var kids = args.shift();
    assert(0 == args.length);

    var obj = document.createElement(tag);
    for (var attr in attrs)
    {
        // see http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
        if (!attrs.hasOwnProperty(attr))
            continue;
        
        obj.setAttribute(attr, attrs[attr]);
    }

    obj._tips = {};
    
    if (undefined != kids)
    {
        if (!(kids instanceof Array))
        {
            kids = [ kids ];
        }
        assert(kids instanceof Array);
        for (var kid_idx=0; kid_idx < kids.length; kid_idx++)
        {
            var kid = kids[kid_idx];
            assert(undefined != kid);
            if (!is_dom_node(kid))
            {
                assert('string' == typeof(kid) ||
                       'number' == typeof(kid));

                kid = document.createTextNode(kid);
            }
           assert('object' == typeof(kid));
            assert(is_dom_node(kid));
            obj.appendChild(kid);
            if (undefined != kid._tips)
            {
                for (var prop in kid._tips)
                {
                    // see http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
                    if (!kid._tips.hasOwnProperty(prop))
                        continue;
                    
                    obj._tips[prop] = kid._tips[prop];
                }
            }
        }
    }
    
    if (undefined != func)
    {
        func(obj);
    }

    return obj;
}

//
// you can implement "inherited tips," which trickle down from parent
// to child, by using this function.
//
// - keep in mind that we build the DOM tree from the bottom up, so
// you can only use get_itip() once the parent DOM node has also been
// constructed and connected to the child.
//
// - also keep in mind this function is linear in the number
// of levels from obj to the DOM object with the property.
// XXX could do caching if needed.
//
// for each node from obj up its parent chain,
//   if node._tips[prop] exists, returns that
// else ASSERTION FAILS.
//
function get_itip(obj, prop)
{
    while (undefined != obj)
    {
        if (obj.hasOwnProperty('_tips') &&
            obj._tips.hasOwnProperty(prop))
        {
            return obj._tips[prop];
        }
        
        obj = obj.parentNode;
    }
    assert(0);
}

function get_word(wordset)
{
    if (0 == wordset.length)
        return "type in more buzzwords!";

    var idx = Math.floor((Math.random() * wordset.length));
    return wordset.splice(idx, 1);
}

function change_type()
{
    make_card();
}

function print_card()
{
    var w = window.open('',
        '_blank',
        'width=750,height=600'
        +',menubar=1'
        +',toolbar=0'
        +',status=1'
        +',scrollbars=1'
        +',resizable=1');

    w.document.title = "Buzzword Bingo Card";

    // would MUCH prefer to do this by copying the
    // existing DOM node (ei('cardoutside')), however
    // IE makes this next to impossible with its
    // many many bugs.  IE cannot copy DOM nodes
    // cross-document and does not offer the DOM
    // importNode() which was meant for this,
    // not to mention that it cannot handle DOM
    // style nodes (must use IE-specific
    // createStyleSheet()), .. the list goes on.
    // IE sucks.

    var html = '';
    
    html +=
        '<title>Buzzword Bingo Card</title>' +
        '<style media="print">\n' +
        '.noprint { display: none }\n' +
        '</style>\n' +
        base_font_style +
        '\n' +
        '<center>\n' +
        '<br>\n' +
        '<input type="button" value="Print Bingo Card Now" class="noprint" onclick="window.print();"><br>\n' +
        '<br>\n' +
        ei('cardoutside').innerHTML +
        '<br>\n' +
        '<br>\n' +
        'Print your own card at <b>http://sfgreens.org/bingo/</b> !\n';
    
    w.document.writeln(html);
    w.document.close();
}

function make_card()
{
    var div = ei('card');
    var wordset = [];

    wordset = [].concat(buzzwords.generic);

    var size = 5;

    var rows = [];
    for(var y=0; y < size; y++)
    {
        var cells = [];
        for(var x=0; x < size; x++)
        {
            if (Math.floor(size/2) == x && 
                Math.floor(size/2) == y)
            {
                word = elt('font',
                           {
                               color: '#ff0000'
                           }, 'FREE (Josh Wolf)');
            }
            else
            {
                word = get_word(wordset);
            }
            cells.push(elt('td', 
                           {
                               width: 90, // causes probs in IE, sigh.
                               align: 'center'
                           },
                           word));
        }
        rows.push(elt('tr',
                      {
                          height: 90,
                          valign: 'middle'
                      },
                      cells));
    }

    var table = 
        elt('table',
            {
                border: 1,
                bordercolor: '#000000',
                cellpadding: 8,
                cellspacing: 0,
                width: '80%'
            },
            elt('tbody', {}, rows));

    while (undefined != div.firstChild)
        div.removeChild(div.firstChild);
    
    div.appendChild(table);

    if (msie)
    {
        // sucks to be you. this fixes some probs, not all.
        // in particular, the table borders still come out weird.
        div.innerHTML = div.innerHTML;
    }
}

