/*
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is SynchroEdit (www.synchroedit.com).
 *
 * The Initial Developer of the Original Code is
 * Kalle Alm (kalle@enrogue.com).
 * Portions created by the Initial Developer are Copyright (C) 2005
 * Alacrity Management Corporations. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** -->
 *
 * Script based on the  debug.js  file in the SynchroEdit codebase.
 */
var trace_stack = document.location.href.indexOf('tstack=1') != -1; // should we trace the stack? if the URL contains "tstack=1", we should.
var that = null; // here to prevent compile warnings

// (I thought this existed in JS.)
function trim(s)
{
    if (s == '') return s;
    while (s[0] == ' ' || s[0] == '\n') s = s.substr(1); var l = s.length-1;
    while (l > 0 && s[l] == ' ' || s[l] == '\n') l--;
    return s.substring(0, l+1);
}

// Constructor for debugger routine.
var stacktrace = function()
{
    this.id(this, "stacktrace");
}

/*
 * Register a function; this variant is used when registering a function within a prototype {} declaration, like so:
 * myClass.prototype = {
 *    // ...
 *    myFunction :
 *    _reg("myFunction", function(a, b, c) {
 *       // ...
 *    }),
 *    // ...
 * }
 */
stacktrace._reg = function funreg(name, func)
{
    if (!trace_stack) {
        // Do not trace stack; we need to mod func a little.
        var fstr = func.toString().replace(/that/g, "this");
        eval("func = " + fstr);
        return func;
    }
    // Trace stack; we need to mod func a little bit.
    var fstr = func.toString();
    var paren = fstr.indexOf("(");
    var eparen = fstr.indexOf(")");
    var thatdec = trim(fstr.substring(paren+1, eparen)) == "" ? "that" : "that, ";
    eval("func = " + fstr.substr(0, paren+1) + thatdec + fstr.substring(paren+1));
    return function (a1, a2, a3, a4, a5, a6, a7) {
        this._sti.push(this, name);
        return this._sti.pop(func(this, a1, a2, a3, a4, a5, a6, a7));
    };
};

// Shortcut to the above.
var _reg = stacktrace._reg;

var __regtarg = null;
/*
 * Register a function; this variant is used when setting functions directly outside of a prototype {} declaration, like so:
 * var myClass = function() {
 *   // constructor stuff
 * };
 * // Set the regtarget first.
 * __regtarget(myClass);
 * // Then register all functions.
 * __reg("myFunction", function (a, b, c) {
 *    // ...
 * });
 * Its foremost advantage is that you do not have to repeat the function name ("myFun = _reg('myFun', ..." versus "__reg('myFun', ...)")
 */
function __reg(name, func)
{
    __regtarg.prototype[name] = stacktrace._reg(name, func);
}
function __regtarget(target)
{
    __regtarg = target;
}

stacktrace.prototype =
    {soit : {},    // Stack Object Identifier Table
     stack : [],   // The stack map

     // Identify an object for the stack.
     id : function identify_object(obj, id)
     {
         obj._sti = this;
         return (this.soit[obj] = id);
     },

     // Add an entry to the top of the debugging stack.
     // This is called when entering a function.
     push : function push_stack(obj, fn)
     {
         return (this.stack.push(obj, fn));
     },

     // Remove the latest entry from the top of the debug stack.
     // This is called when leaving a function.
     // The value of rv is returned, thus allowing expressions like:
     // return this._sti.pop("some-return-value");
     pop : function pop_stack(rv)
     {
         // We pop it twice since all values are pushed in pairs.
         this.stack.pop();
         this.stack.pop();
         return rv;
     },

     // Produce stack trace.
     trace : function stack_trace()
     {
         if (!trace_stack) return "[stack trace not available]";
         try {
             var r = "";
             var ix = this.stack.length;
             var tabulate = "\t";
             for (var i = 0; i < ix; i += 2) {
                 r += ("trace:" + tabulate + this.soit[this.stack[i]] + ": " + this.stack[i+1] + "\n");
                 tabulate += "\t";
             }
             return r;
         } catch (e) {
             return "[failed to produce stack trace]";
         }
     }
    };


