text: /**
   * Javascript debugger for printing native types (number, boolean, string, date, 
   * array), functions and objects of any type native, host or custom in the form
   * of:
   * ': -> value' for simple types or, 
   * 'depth: key -> value' for complex ones.
   *
   * @version 0.1.2
   * @author Scripto JS Editor by Centurian Comet.
   * @copyright MIT licence.
   */
  (function(g3, $, window, document, undefined){
     /*
      * Add necessary functions from 'g3.utils' namespace.
      */
     g3.utils = g3.utils || {};
     g3.utils.typeOf = function(value) {
        var s = typeof value;
        if (s === 'object') {
           if (value) {
              if (Object.prototype.toString.call(value) === Object.prototype.toString.call([])) { //'[object Array]'
                 s = 'array';
              }
           } else {
              s = 'null';
           }
        }
        return s;
     };
     g3.utils.isEmptyObject = function(obj){
        var result = true;
        if(obj === null)
           return result;
        if((typeof obj === 'object') || (typeof obj === 'function')){
           for(var prop in obj){
              if(Object.prototype.hasOwnProperty.call(obj, prop)){
                 result = false;
                 break;
              }
           }
           //overwrite previous result! (new ECMA 5 properties)
           //FF Error: returns 5 prototype properties on functions and 1 on arrays
           //as their own members!
           if((typeof Object.getOwnPropertyNames === 'function') && (g3.utils.typeOf(obj) === 'object')){
              result = (Object.getOwnPropertyNames(obj).length === 0);
           }
           return result;
        }
        return result;
     };
     g3.utils.htmlList = function(type) {
        if(!type || (g3.utils.typeOf(type) != 'string') || ((type.toLowerCase() != 'u') && 
          (type.toLowerCase() != 'o')) || (arguments.length <= 1))
           return false;
        var result = "<" + type.toLowerCase() + "l><li>";
        var args;
        if(g3.utils.typeOf(arguments[1]) === 'array')
           args = arguments[1];
        else
           args = Array.prototype.slice.call(arguments, 1);
        result += args.join("</li><li>");
        result += "</li></" + type.toLowerCase() + "l>";
        return result;
     };
     
  /*********************************Object debug()********************************
  * Returns a stringify form of an object for debugging purposes. Internally, it 
  * stores a flattened representation of the object's tree-structure.
  * @module {g3.debug}
  * @constructor
  * @param {Object} 'obj' the object to stringify.
  * @param {Number} 'maxDepth' the maximum depth to look for when a property is an 
  * object reference which in turn can contain other object references. It's 
  * 0-based starting with the first level childs.
  * @param {Boolean} 'force' If it evaluates to true, it forces debugger to  
  * analyse the first argument bypassing the result of 'g3.utils.isEmptyObject()'
  * which fails on css host objects. 
  * @return {Object} Returns an object. Circular references are not followed 
  * because they are recognised on first meet. The internal structure that is 
  * built is a flattened representation of a tree which is a 2-dimensional 
  * array(i, j) whose index i stores a 3-cell linear sub-array where: i. index 0, 
  * at (i, 0), keeps the depth of the current member, ii. index 1, at (i, 1), 
  * keeps the name of the member and iii. index 2, at (i, 2), keeps the member's 
  * value. 
  * This representation exchibits some interesting behaviours:
  * 1) Between two successive depths with the same value, n <- (i, 0) & (i+j, 0), 
  * exist all members of the member-object at (i, 1) as in the following example:
  * members-objects|(i+j,0)->depth|(i+j,1)->member|(i+j, 2)->value
  * .............  | .........    | .........     | .........
  * |--[a]         |(i,   0)->n   |(i,   1)->'a'  |(i,   1)->'custom object' <---|
  * |   |--[b]     |(i+1, 0)->n+1 |(i+1, 1)->'b'  |(i+1, 1)->'custom object' \   | members of
  * |   |   |--[c] |(i+2, 0)->n+2 |(i+2, 1)->'c'  |(i+2, 1)->'value of c'    |----
  * |   |--[d]     |(i+3, 0)->n+1 |(i+3, 1)->'d'  |(i+3, 1)->'value of d'    /
  * |--[e]         |(i+4, 0)->n   |(i+4, 1)->'e'  |(i+4, 1)->'value of e'
  * ............   | .........    | .........     | .........
  * 2) Similar, successive entries with the same depth n are all members of the 
  * object at depth n-1 at the entry immediate before them. Example: at n+1 we can 
  * see 2 members of an object at depth (n+1)-1 = n <- (i, 0).
  * @function {g3.debug.toString}
  * @public
  * @return {String} A string representation of the structure based on key-value
  * pairs in depth defined during construction. Each pair ends with a newline 
  * character, i.e. '\n'.
  * @function {g3.debug.toHtml}
  * @public
  * @return {String} An html representation of the structure based on key-value
  * pairs in depth defined during construction. Each pair ends with a break 
  * character, i.e. '<br \>'.
  * @function {g3.debug.formatRow}
  * @public
  * Formats a single array that is passed as argument to an html string. It's a 
  * helper function to be used by 'toHtml()' and 'popup()' methods.
  * @param {Array} 'arr' A single array with 3 entries: [depth, key, value].
  * @return {String} An html representation of the array entry. Newlines '\n' in 
  * string values are converted to breaks, '<br \>'.
  * @function {g3.debug.popup}
  * @public
  * Opens a new window and writes an html string with formation that follows the 
  * passed argument.
  * @param {String} 'tag' One of the strings: ['pre', 'o', 'u'] with default value 
  * 'pre'.
  * @return {Null}
  *
  * @author Scripto JS Editor by Centurian Comet.
  * @copyright MIT licence.
  *******************************************************************************/
     g3.debug = function(obj, maxDepth, force){ //construct with argument
        var tree = [], refs = [], max;
        refs.push( [ 0, obj ] );
        if((maxDepth === 0) || ((g3.utils.typeOf(maxDepth) === 'number') && (maxDepth > 0)))
           max = maxDepth;
        else
           max = -1;
        function traverse(obj, i){
           var value, str = '', tmp = '', circular, found, iterate = false;
           for (var property in obj){
              iterate = true;
              found = false;
              //case: Mozila's number-properties of 'style' object, ha-ha-ha!!!
              property += '';
              try{
                 value = obj[property];
              }catch(e){
                 str = [-1, 'Error:', e];
                 //new record
                 tree.push(str);
                 break;
              }
              str = [i, property, value];
              //new record
              tree.push('');
              /* 1. not recursion
               * ----------------
               */
              if(value === null){
                 tmp = 'null';
                 found = true;
              }else if(g3.utils.typeOf(value) === 'undefined'){
                 tmp = 'undefined';
                 found = true;
              }else if(value === ''){
                 tmp = '\'\'';
                 found = true;
              }else if (g3.utils.typeOf(value) === 'boolean'){
                 tmp = (value)?'\'true\'':'\'false\'';
                 found = true;
              }else if (g3.utils.typeOf(value) === 'string'){
                 tmp = '\'' + value + '\'';
                 found = true;
              }else if (g3.utils.typeOf(value) === 'number'){
                 tmp = value;
                 found = true;
              }else if(g3.utils.typeOf(value) === 'array'){
                 tmp = '[' + value + ']';
                 found = true;
              }
              if(found){
                 str[2] = tmp;
                 tree[tree.length - 1] = str;
              /* 2. recursion or not?
               * --------------------
               */
              }else if ((g3.utils.typeOf(value) === 'function') || (g3.utils.typeOf(value) === 'object')){
                 circular = false;
                 for(var j = 0; j < refs.length; j++){
                    if(refs[j][1] === value){
                       tmp = '\'contains a circular reference to object at index ' + refs[j][0]+'\'';
                       circular = true;
                       break;
                    }
                 }
                 if(!circular){
                    if(g3.utils.typeOf(value) === 'object'){
                       refs.push( [ tree.length - 1, value ] );
                       tmp = '\'object\'';
                       if(value.toString)
                          tmp += value.toString() + '';
                    }else if(g3.utils.typeOf(value) === 'function'){
                       tmp = '\'function\'';
                       if(value.toString){
                          tmp += ' [' + value.toString().slice(0, value.toString().indexOf('{')+1).replace(/\n|\r|[\b]/g, '') + '...]';
                       }
                    }
                 }
                 str[2] = tmp;
                 tree[tree.length - 1] = str;
                 //recursion with new 2nd arg but keep original!
                 if(!circular && ((max < 0) || (i < max))){
                    traverse(value, i+1);
                 }
              /* 3. fallback
               * -----------
               */
              }else{ //some value check was missed!
                 str[2] = 'value check was missed: ' + str[2];
                 tree[tree.length - 1] = str;
              }
           }
           //even a debugger needs to be debugged! Not for production use!
           //new record
           /*if(!iterate){
              str = [i, '\'can\'t iterate on object/function\'', obj];
              tree.push(str);
           }*/
        };
        //never enters for..in loop on functions with no members, null, empty 
        //objects, empty arrays, booleans, dates, numbers and strings also,
        //'g3.utils.isEmptyObject()' fails on css host objects so we need 'force'
        if(!force && g3.utils.isEmptyObject(obj)){
           var str;
           if(typeof obj === 'undefined')
              str = 'undefined';
           else if(obj === null)
              str = 'null';
           else if(obj === '')
              str = '\'\'';
           else
              str = obj.toString();
           if(g3.utils.typeOf(obj) === 'array')
              str = '[]';
           else if(g3.utils.typeOf(obj) === 'object')
              str = '{}';
           else if(g3.utils.typeOf(obj) === 'function')
              str = 'function(){}';
           tree.push([null, null, str]);
        }else
           traverse(obj, 0);
        return {
           toString: function(){
              var tmp ='';
              for(var i = 0; i < tree.length; i++){
                 if(tree[i][0] !== null)
                    tmp += tree[i][0] + ': ';
                 if(tree[i][1] !== null)
                    tmp += tree[i][1] + ' -> ';
                 tmp += tree[i][2] + '\n';
              }
              return tmp;
           },
           toHtml: function(){
              var tmp ='';
              for(var i = 0; i < tree.length; i++)
                 tmp += '<span style="margin-left: '+tree[i][0]*2+'em">'+this.formatRow(tree[i])+'</span>'+'<br />';
              return tmp;
           },
           formatRow: function(arr){
              var quotes = ['\'object\'', '\'function\'', 'circular reference'], 
              pos = -1, circular = false;
              if(arr[0] == -1)
                 return '<span style="color: red">' + arr[0] + ': ' + arr[1] + ' -> ' + arr[2] + '</span>';
              for(var i = 0; i < quotes.length; i++){
                 pos = (arr[2] + '').lastIndexOf(quotes[i]);
                 if(pos >= 0){
                    if(i === 2)
                       circular = true;
                    break;
                 }
              }
              if(pos >= 0)
                 arr[2] = '<span style="color: ' + ((circular)? 'red': 'blue') + '">' + arr[2] + '</span>';
              //replace newlines in strings with breaks, '<br />'
              if(arr[2] && (typeof arr[2] === 'string'))
                 arr[2] = arr[2].replace(/\n/g, '<br />');
              var tmp ='';
              if(arr[0] !== null)
                 tmp += arr[0] + ': ';
              if(arr[1] !== null)
                 tmp += arr[1] + ' -> ';
              tmp += arr[2];
              return tmp;
           },
           popup: function(tag){
              if(!tag || (typeof tag !== 'string'))
                 tag = 'pre';
              tag = tag.toLowerCase();
              var tags = ['pre', 'o', 'u'];
              var found = false;
              for(var i = 0; i < tags.length; i++)
                 if(tag === tags[i]){
                    found = true;
                    break;
                 }
              if(!found)
                 tag = 'pre';
              var w = window.open("about:blank");
              w.document.open();
              w.document.writeln("<HTML><BODY>");
              if(tag === 'pre'){
                 w.document.writeln("<PRE>");
                 w.document.writeln(this.toString());
                 w.document.writeln("</PRE>");
              }
              if((tag === 'u') || (tag === 'o')){
                 var list = [];
                 for(var i = 0; i < tree.length; i++)
                    list[i] = '<span style="margin-left: '+tree[i][0]*2+'em">'+this.formatRow(tree[i])+'</span>';
                 w.document.writeln(g3.utils.htmlList(tag, list));
              }
              w.document.writeln("</BODY></HTML>");
              w.document.close();
           }
        };
     };
  }(window.g3 = window.g3 || {}, jQuery, window, document));
  