/*******************************************************************************
 * Utility
 *******************************************************************************/
function dog_alert(s) {
    //alert(s);
}
function sortedKeys(o, bAllKeys) {
    var keys = [];
    var key;
    for (key in o) {
        if (bAllKeys || o.hasOwnProperty(key)) {
            keys.push(key);
        }
    }
    return keys.sort();
}
function crString(a) {
    var string = '';
    for (var i in a) {
        string += a[i];
        string += '\n';
    }
    return string;
}
/*******************************************************************************
 * DfRoot
 *******************************************************************************/
var DfRoot = PRoot.create({
    setText: function(sText) {
        $('#tarText').val(sText);
        return this;
    }
});
var DfApp = DfRoot.create({
    bindEvents: function() {
        $('.top').click(this.event_div_clicked);
    },
    event_div_clicked: function(event) {
        //this is clicked dom element
        var keys = sortedKeys($(this), true);
        var string = crString(keys);
        DfRoot.setText(string);
        //alert(string);
        $(this).css('background-color', '#f30');
    }
});
var DfMain = DfRoot.create({
    ready: function() {
        DfApp.bindEvents();
        output(1, "Hello World, How are you?");
        var if_string
        if (false) {
            if_string = 'one'
        } else {
            if_string = 'two'
        }
        output(2, if_string);
        output(3, str_html('a', 'a_id'));
        output(4, todo_xhr.statusText || "[empty string]");
        output(5, todo_xhr.responseText);
        output(7, link_string('http://one', 'LABEL'));
        window["out" + "put"](8, "It Works");
        output(9, typeof(window["output"]));
        output(10, "hello");
        add_eval();
        add_links();
        show_eval("is_test_fname('test__one')");
        show_eval("is_test_fname('one')");
        show_eval("is_test_fname('test_one')");
        show_test("should_pass", true);
        show_test("should_fail", false);
        show_eval("test_fnames(window)");
        var visitor = new Visitor();
        visitor.tests();
        var stream = new WriteStream();
        stream.test1().test2().test3();
        run_tests();
    }
});
/*******************************************************************************
 * Constants
 *******************************************************************************/
var jquery_dir = "file:///C:\\Documents and Settings\\Stan\\My Documents\\_JQuery\\";
var links_id = '#links';
/*******************************************************************************
 * WriteStream
 *******************************************************************************/
function WriteStream(sInitial, bTabUseSpaces, iTabSize) {

    //====================
    // instance variables
    //====================
    this.string = sInitial || '';
    this.tabUseSpaces = bTabUseSpaces;
    if (typeof(bTabUseSpaces) == 'undefined') {
        this.tabUseSpaces = true
    }
    this.tabSize = iTabSize || 4;
    this.indents = 0;
    //=========
    // methods
    //=========
    var proto = WriteStream.prototype;
    if (proto._isSet) {
        return this
    }
    proto._isSet = true;
    //---------------------------
    // add a string or character
    //---------------------------
    proto.add = function (string, n) {
        if (n == 0) {
            return this
        }
        n = n || 1;
        for (var i = 0; i < n; i++) {
            this.string += string
        }
        return this;
    }
    //------------
    // whitespace
    //------------
    proto.cr = function (n) {
        return this.add('\n', n)
    }
    proto.space = function (n) {
        return this.add(' ', n)
    }
    proto.sp = proto.space;
    proto.tab = function (n) {
        if (n == 0) {
            return this
        }
        n = n || 1;
        if (this.tabUseSpaces) {
            return this.space(this.tabSize * n)
        }
        return this.add('\t', n);
    }
    //-------
    // lines
    //-------
    proto.line = function (n) {
        return this.add('=', n)
    }
    proto.dash = function (n) {
        return this.add('-', n)
    }
    proto.star = function (n) {
        return this.add('*', n)
    }
    //--------
    // indent
    //--------
    proto.incrementIndent = function () {
        this.indents += 1;
        return this
    }
    proto.inc = proto.incrementIndent;
    proto.decrementIndent = function () {
        this.indents = Math.max(this.indents - 1, 0);
        return this
    }
    proto.dec = proto.decrementIndent;
    proto.clearIndent = function () {
        this.indents = 0;
        return this
    }
    proto.indent = function () {
        return this.tab(this.indents)
    }
    proto.crIndent = function () {
        return this.cr().indent()
    }
    proto.crIn = proto.crIndent;
    //--------
    // column
    //--------
    proto.column = function (n) {
        var desired = this.lastCrPosition() + n;
        var actual = this.string.length;
        var delta = desired - actual;
        return this.space(delta);
    }
    //-------
    // clear
    //-------
    proto.clear = function () {
        this.string = '';
        this.indents = 0;
        return this
    }
    //==============
    // test methods
    //==============
    proto.alert = function () {
        var string = '[' + this.string + ']';
        string += '\n\n' + this.indents + ' ' + this.tabUseSpaces + ' ' + this.tabSize;
        dog_alert(string);
        return this;
    }
    proto.test1 = function () {
        var stream = new WriteStream();
        stream.add('Hello').space().add('World').alert();
        stream.incrementIndent().alert();
        stream.crIndent().add('How are you?').alert();
        stream.decrementIndent().alert();
        stream.crIndent().line(16).alert();
        stream.alert();
        return this;
    }
    proto.test2 = function () {
        var stream = new WriteStream();
        // replace the space method only in this instance
        stream.space = function (n) {
            return this.add('.', n)
        }
        stream.sp = stream.space;
        // add to the tab method only in this instance
        stream.tab = function (n) {
            this.add('+' + n);
            proto.tab.call(this, n);
            return this;
        }
        stream.add('Hello').sp().space().sp(2).space(4).add('Space World');
        stream.cr().add('Hello').tab(0).tab().tab(1).tab(2).add('Tab World');
        stream.inc().cr().indent().add('^');
        stream.incrementIndent().crIndent().add('^');
        stream.dec().crIn().add('^');
        stream.crIn().line(16);
        stream.crIn().dash(16);
        stream.crIn().star(16);
        stream.decrementIndent().crIn().add('^');
        stream.inc().crIn().add('How are you?');
        stream.dec().crIn().add('Hello').sp().add('Moon');
        stream.inc().crIn().add('How are you?');
        stream.alert();
        return this;
    }
    proto.test3 = function () {
        var stream = new WriteStream('start ', false);
        // replace space method only in this instance
        stream.space = function (n) {
            return this.add('.', n)
        }
        stream.sp = stream.space;
        // add to tab method only in this instance
        stream.tab = function (n) {
            this.add('+' + n);
            return proto.tab.call(this, n);
        }
        stream.add('Hello').tab(2).add('World').alert();
        stream.clear().alert();
        return this;
    }
}
/*******************************************************************************
 * Visitor
 *******************************************************************************/
function Visitor() {
    this.visit = function (target) {
        var selector = this.get_selector(target);
        return this[selector](target);
    }
    this.get_selector = function (target) {
        var selector = this.constructor_selector(target);
        if (this[selector]) {
            return selector
        }
        selector = this.typeof_selector(target);
        if (this[selector]) {
            return selector
        }
        return this.any_selector();
    }
    this.constructor_selector = function (target) {
        return 'visit_' + (target.constructor.name || this.constructor_name(target));
    }
    this.constructor_name = function (target) {
        //Parse the function name from the function source string
        return target.constructor.toString().split(' ')[1].slice(0, -2);
    }
    this.typeof_selector = function (target) {
        return 'visit_' + typeof(target);
    }
    this.any_selector = function () {
        return 'visit_any';
    }
    this.test = function (string) {
        show(string, this.get_selector(eval(string)));
    }
    this.tests = function () {
        this.test("17");
        this.test("[1, 2]");
        this.test("'hello'");
        this.test("new Date(2009)");
        this.test("new Function()");
        this.test("new Object()");
    }
    this.visit_number = function () {
    }
    this.visit_object = function () {
    }
    this.visit_Array = function () {
    }
    this.visit_Function = function () {
    }
}
/*******************************************************************************
 * Show
 *******************************************************************************/
function show(str_label, str_text) {
    p_string = '<p>    ' + str_label + ' => ' + str_text + '</p>';
    show_p(p_string);
}
function show_test(str_function_name, bool_result) {
    var status = bool_result ? 'passed' : 'failed';
    p_string = '<p>    ' + str_function_name + ' => ' + status + '</p>';
    show_p(p_string);
}
function show_eval(str_code) {
    result = eval(str_code);
    show(str_code, result);
}
function show_title(str_title) {
    p_string = '<p>' + str_title + '</p>';
    show_p(p_string);
}
function show_p(p_string) {
    $('#show').append(p_string);
}
/*******************************************************************************
 * JQunit
 *******************************************************************************/
function test_fnames(object) {
    var fnames = [];
    for (var key in object) {
        if (is_test_fname(key)) {
            fnames.push(key)
        }
    }
    return fnames;
}
function is_test_fname(string) {
    var prefix = 'test__';
    var length = prefix.length;
    if (string.length < length) {
        return false
    }
    if (string.substr(0, length) == prefix) {
        return true
    }
    return false;
}
function run_tests(object) {
    object = object || window;
    var fnames = test_fnames(object);
    for (var i = 0; i < fnames.length; i++) {
        run_test(object, fnames[i]);
    }
}
function run_test(object, str_fname) {
    var result = object[str_fname]();
    show_test(str_fname, result);
}
function test__should_pass() {
    return true
}
function test__should_fail() {
    return false
}
/*******************************************************************************
 * HTML functions
 *******************************************************************************/
function str_html(str_element, str_id, i_height, i_width) {
    var result = '<' + str_element;
    if (str_id) {
        result += " id='" + str_id + "'"
    }
    result += '></' + str_element + '>';
    return result;
}
function link_string(str_link, str_label) {
    return "<a href='" + str_link + "'>" + str_label + "</a>";
}
function textarea_string(str_id, rows, cols) {
    var result = '<textarea';
    if (str_id) {
        result += " id='" + str_id + "'"
    }
    if (rows) {
        result += " rows='" + rows + "'"
    }
    if (cols) {
        result += " cols='" + cols + "'"
    }
    result += '></textarea>';
    return result;
}
function button_string(str_text, str_id) {
    var result = '<button';
    if (str_id) {
        result += " id='" + str_id + "'"
    }
    result += '>' + str_text + '</button>';
    return result;
}
/*******************************************************************************
 * Output function
 *******************************************************************************/
function output(aNumber, aString) {
    var id = "#div" + aNumber;
    $(id).text(aString);
}
/*******************************************************************************
 * Eval
 *******************************************************************************/
function handler_eval_button(event) {
    var expression = $('#eval_in').val();
    var result = eval(expression);
    var expression = $('#eval_out').val(result);
}
function add_eval() {
    $('#eval').text("This is the eval div that is inserted after");
    $(button_string('EVAL', 'eval_button')).insertAfter('#eval');
    $(textarea_string('eval_out', 10, 30)).insertAfter('#eval');
    $(textarea_string('eval_in', 10, 30)).insertAfter('#eval');
    $('#eval_button').click(handler_eval_button);
}
/*******************************************************************************
 * Todo
 *******************************************************************************/
var todo_filename = jquery_dir + "dogfood_todo.txt";
var todo_xhr = $.get(todo_filename);
/*******************************************************************************
 * Links
 *******************************************************************************/
function add_links() {
    var json_filename = jquery_dir + "dogfood_links.jsn";
    $.getJSON(json_filename, {}, callback_add_links);
}
function callback_add_links(object, status) {
    populate_links('#links', object);
}
function make_link(jsn_link) {
    var string = link_string(jsn_link.link, jsn_link.name);
    return $(string);
}
function populate_link(str_id, jsn_link) {
    $(str_id).append('<br>');
    $(str_id).append(make_link(jsn_link));
}
function populate_links(str_id, ar_jsn_links) {
    $.each(ar_jsn_links, function () {
        populate_link(str_id, this);
    });
}
