/**
 * The malevolent Douglas Crockford's prototypal inheritance pattern:
 *
 * @usage: newObject = Object.create(oldObject);
 * @see http://javascript.crockford.com/prototypal.html
 */
if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

var hifi = {
    /*  @siteUrl - can be used in ajax requests or if you need to access a resource such as an image.
     *  If the host contains "dev" or "local" then the first directory past the host is part of the site name,
     *  i.e. localhost/rootstrata or dev.brokendisk.com/rootstrata
     *  which is the development convention, otherwise we are on live and the siteUrl is just the domain.    */
    siteUrl: (window.location.host == 'localhost' || window.location.host.indexOf('dev') >= 0) ?
                (   window.location.protocol + '//' + window.location.host + '/' +
                    window.location.pathname.substr(1).substring(0, window.location.pathname.substring(1).indexOf('/')) + '/' ) :
                window.location.protocol + '//' + window.location.host + '/',
    /*  safe logging to console, the intention is a logging function that
     *  will not throw errors in IE, or in FF if FireBug is disabled, etc..
     *
     * usage: log('inside coolFunc',this,arguments);
     * http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/    */
    log: function() {
        hifi.log_history = hifi.log_history || []; // store logs to an array for reference
        hifi.log_history.push(arguments);
        if (this.console || window.console) {
            console.log( Array.prototype.slice.call(arguments) );
        }
    },
    /**
      * alert a message to the user, generally some kind of error
      */
    alert: function(errorMessage, errorTitle, errorClasses) {
        errorTitle = errorTitle || 'very emergency!';
        errorClasses = errorClasses || 'persistent';

        var currentTime = new Date();
        var hours = currentTime.getHours();
        var minutes = currentTime.getMinutes();
        var seconds = currentTime.getSeconds();
        if (hours < 10) {
            hours = "0" + hours;
        }
        if (minutes < 10) {
            minutes = "0" + minutes;
        }
        if (seconds < 10) {
            seconds = "0" + seconds;
        }
        $('#error_console.dialog .content .body span').addClass('old');
        var errorString = '<span class="timestamp"">' + hours +  ":" + minutes + ":" + seconds + ' - </span>' +
            '<span class="message">' + errorMessage + '</span><br/>';
        dialog.open('error_console', {
            title: errorTitle,
            classes: errorClasses,
            body: errorString,
            prepend: errorString
        });
        return false;
    },
    //todo: do this
    confirm: function(instructionsString) {
        // present a string to a user in dialog box with OK/Cancel buttons, return true if they click OK, false if they click cancel
    },

	// encapsulate JSON parsing, prefer native browser or Crockford's json2 methods when available.
	parseJson: function(data) {
		return window.JSON && JSON.parse(data) || eval('('+data+')');
	},

    // functions for enabling default text in a text input
    // pretty inefficient, re-does *every* element every time
    // pass "el" parameter, a jquery object of elements with the
    //  'data-defualt_text' property, or otherwise it will map to all such els.
    // todo: use browser native property "placeholder" for placeholder text,
    //       when it's widely supported.
	//		 or.. https://github.com/mathiasbynens/Placeholder-jQuery-Plugin/blob/master/jquery.placeholder.js
    //       Remove this function when all sites are moved over.
    enableDefaultText: function(el) {
        elements = el || $('[data-default_text]');
        elements.each(function() {
            hifi.restoreDefaultText($(this));
            $(this).unbind('focus blur');

            $(this).bind('focus', function() {
                var value = this.value;
                if (value == $(this).attr('data-default_text')) {
                    hifi.removeDefaultText($(this));
                }
            });
            $(this).bind('blur', function() {
                var value = this.value;
                if (value == '') {
                    hifi.restoreDefaultText($(this));
                }
            });
        });
    },
    removeDefaultText: function(el) {
        if (el.filter('textarea').length > 0) {
            el.removeClass('default').text('');
        }
        else {
            el.removeClass('default').val('');
        }
    },
    restoreDefaultText: function(el, force) {
        force = force | false;
        if (el.val() == '' || force) {
            // todo: I think this is actually not necessary,
            // .val should know what to do with textareas already.
            if (el.is('textarea')) {
                el.addClass('default').html(el.attr('data-default_text'));
            }
            else {
                el.addClass('default').val(el.attr('data-default_text'));
            }
        }
    },

    /* browsers want to send too many signal, only process one signal */
    isAdjusting: false,
    /*  requires  the header be in the header tag,
        footer be in the footer tag,
        and body be determined by the height of section#main
        @option verbose boolean - whether to log output or not */
    //todo: use the fixed method you've been experimenting with, like on onland
    adjustFooter: function(verbose) {
        verbose = verbose | false;

        if (! this.isAdjusting) {
            this.isAdjusting = true;

            var footer = $('footer');

            if (verbose && footer.css('margin-top') != '0px') {
                hifi.log('Warning: While adjusting footer, the footer element should ' +
                           'have no top margin, use padding instead to enforce top spacing.' +
                           ' (footer.margin-top: ' + footer.css('margin-top') + ')');
            }
            // if the visible window is the whole document
            if (parseInt($(window).height()) >= parseInt($(document).height())) {
                // then bump the footer down to the bottom
                var offset = $(document).height() - (footer.outerHeight() + $('header').outerHeight(true) + $('#main').outerHeight(true) + 1 );
                // why +1? not sure, seems necessary.
            }
            else {
                var offset = '0';
            }
            //hifi.log('adjusting to ' + offset, parseInt($(document).height()), parseInt($(window).height()));
            footer.css('margin-top', offset+'px').animate({ opacity:1 }, 1000);
            this.isAdjusting = false;
        }
    }
};

// busy and unbusy: create and destroys image with the 'waiting' icon
// I extended jquery with this in order to chain it to animations
// todo: make this a proper plugin, you know how to do that now.
$.fn.busy = function(callback) {
    if (callback) { callback() };
    return $('<img>').attr('src', hifi.siteUrl + 'media/hifi/busy.gif').attr('id', 'busy_icon');
}

// usually it happens so fast you never see this, so guarantee it'll be on
// screen for at least a fraction of a second with setTimeout
$.fn.unbusy = function(callback) {
    setTimeout("$('#busy_icon').remove();", 100);
    if (callback) { callback() };
    return $(this);
}

function encode_utf8( s ) {
      return unescape( encodeURIComponent( s ) );
}

function decode_utf8( s ) {
      return decodeURIComponent( escape( s ) );
}

// http://4umi.com/web/javascript/rot13.php
function rot( t, u, v ) {
    return String.fromCharCode( ( ( t - u + v ) % ( v * 2 ) ) + u );
}

function rot13( s ) {
    var b = [], c, i = s.length,
        a = 'a'.charCodeAt(), z = a + 26,
        A = 'A'.charCodeAt(), Z = A + 26;
    while(i--) {
        c = s.charCodeAt( i );
        if( c>=a && c<z ) { b[i] = rot( c, a, 13 ); }
        else if( c>=A && c<Z ) { b[i] = rot( c, A, 13 ); }
        else { b[i] = s.charAt( i ); }
    }
    return b.join( '' );
}
// end rot encode


// Setup global error handler for transport/network level AJAX issues -
// Allah alone knows if this works...
$('body').ajaxError(function(evt, xhr, opts, except) {
    hifi.alert('Ajax error (' + xhr.status + ') to ' + opts.url);
});

// force pre-fetching in browsers that don't support it
// todo: put them all in a single statement you lazy piece of shit.
// also, make sure it's case-insensitive
// also, this js is at the bottom of the doc so this doc.ready isn't needed.
$(document).ready(function() {
    $('link[rel=prefetch][href$=.jpg]').each(function() {
        new Image().src = this.href;
    });
    $('link[rel=prefetch][href$=.jpeg]').each(function() {
        new Image().src = this.href;
    });
    $('link[rel=prefetch][href$=.gif]').each(function() {
        new Image().src = this.href;
    });
    $('link[rel=prefetch][href$=.png]').each(function() {
        new Image().src = this.href;
    });
});

// Ensure that no stay console.log calls break functionality by defining an
// empty console object and log function in browsers where they do not exist.
if (typeof console == "undefined" || typeof console.log == "undefined") {
	var console = { log: function() {} };
	window.console = { log: function() {} };
}

