/**
 * <div class="ngstk.anuncio"><b>Autor:</b> Daniel Mart&iacute;n Fern&aacute;ndez<br />
 * <b>Web:</b> <a href="http://www.naguissa.com">http://www.naguissa.com</a><br />
 * <b>Nombre:</b> Naguissa Toolkit (ngstk)<br />
 * <b>Descripci&oacute;n:</b> Toolkit JavaScript y PHP para creaci&oacute;n, manipulaci&oacute;n y transformaci&oacute;n de documentos web.<br />
 * <b>Secci&oacute;n:</b> Funciones y definiciones base. No son vitales para el funcionamiento, de los componentes, pero al haber definiciones y funciones de check los scripts fallaran si no se carga.<br />
 * <b>Licencia: GPL</b></div>
 */


/**
 *  Base object
 *
 * @property singleton
 */
// Protect variables.
ngstk = new function() {
    // Protect 'this' object.
    var self=this;


    /**
     * Public configuration array.
     *
     * It has a base config and is public-accessible
     * to change configuration aspect externally.
     * Provides reutilization facilities.
     */
    this.config=[];
//    this.config["BASE_URL"] = "..";
    this.config["BASE_URL"] = "http://www.naguissa.com";
    this.config["IMAGE_URL"] = this.config["BASE_URL"] + "/ngstk/image/";
    this.config["BROWSER_URL"] = this.config["BASE_URL"] + "/usuarios.php?espacio";
    this.config["SATAY_URL"] = this.config["BASE_URL"] + "/ngstk/satay.swf";
    this.config["SYNTAX"] = false;       // Para el editor RTE. Si se activa se han de cargar los archivo de Syntax Highlighter JS en el resultado. Estan en la carpeta del toolkit.
    this.config["SYNTAX_URL"] = this.config["BASE_URL"] + "/ngstk/syntax/";


    /**
     * Private counter.
     *
     * It's used to make unique IDs.
     */
    var uniqueid=1;



    /**
     *  Private image objects array.
     *
     * It's used to prefetch images.
     */
    var imgs = [];



    /**
     * Returns numeric unique IDs.
     *
     * It uses uniqueid private variable to return a numeric
     * ID and incrase the count.
     *
     * @return      integer     ID (count).
     *
     * @see uniqueid
     */
    this.getUniqueID = function ngstk_getUniqueID() {
        return self.uniqueid++;
        }


    /**
     * Preload images.
     *
     * Preloads all images from an image array or structure.
     *
     * @param   ima     array/structure    Set of images to preload.
     *
     * @see imgs
     */
    this.preloadimg = function ngstk_preloadimg( imga ) {
        for( var im in imga ) {
            imgs[imgs.length] = new Image();
            imgs[imgs.length-1].src = imga[im];
            }
        }



    /**
     * Returns encoded string.
     *
     * It's a url conversion method equivalent to PHP's
     * urlencode one.
     *
     * @param   str     string      String to encode.
     *
     * @return      string          Encoded string.
     *
     * @see urldecode
     */
    this.urlencode = function ngstk_urlencode(str) {
        str = escape(str);
        str = str.replace('+', '%2B');
        str = str.replace('%20', '+');
        str = str.replace('*', '%2A');
        str = str.replace('/', '%2F');
        str = str.replace('@', '%40');
        return str;
        }


    /**
     * Returns decoded string.
     *
     * It's a url conversion method equivalent to PHP's
     * urldecode one.
     *
     * @param   str     string      String to encode.
     *
     * @return      string          Decoded string.
     *
     * @see urlencode
     */
    this.urldecode = function ngstk_urldecode(str) {
        return unescape( str.replace('+', ' ') );
        }


    /**
     * Returns escaped string.
     *
     * Replaces <, > and & for their hexadecimal entities HTML and XML valid.
     *
     * @param       str     String      String to encape.
     * @param       euro    Boolean     Escapes Euro symbol too.
     */
    this.escapeXML = function ngstk_escapeXML(str,euro) {
        if( euro ) return str.split("&").join("&#38;").split(String.fromCharCode(8364)).join("&#128;").split("<").join("&#60;").split(">").join("&#62;").split("&").join("&#38;");
          return str.split("&").join("&#38;").split("<").join("&#60;").split(">").join("&#62;").split("&").join("&#38;");
        }



    /**
     * Sets display property of an object
     *
     * It's a multimode function to handle display
     * css property.
     *
     * @param   e   string/DOM fragment     Object of DOM to handle or his ID.
     * @param   s   string/boolean          Optional. "block"/"none" or true/false. Case insensitive. If defined and none of this four cases returns false.
     *
     * @return      string/boolean          returns (new?) status of display property of false if error happens.
     */
    this.display = function ngstk_display(e,s){
        // Normalizamos el argumento
        if( typeof s !== 'undefined' ) {
            if( s === true ) s = 'block';
             else if( s === false ) s = 'none';
              else s = s.toLowerCase(s+"");
            if( s != 'block' && s != 'none') return false;
            }

        // Normalizamos el objeto
        if( typeof e === 'string' ) e=self.getElementById(e);
        if( !e ) return false;
        if(e.style && typeof e.style.display !== 'undefined') {
            if( typeof s !== 'undefined' ) e.style.display = s;
            return e.style.display;
            }
        return false;
        }



    /**
     * Switches display property of an object.
     *
     * It's a multimode function to switch display
     * css property of an object.
     *
     * @param   e   string/DOM fragment     Object of DOM to handle or his ID.
     *
     * @return      string/boolean          returns new status of display property of false if error happens.
     */
    this.showhide = function ngstk_showhide(e) {
        var d = self.display(e);
        if( d === false) return false;
        return self.display(e, d == 'none' );
        }


    /**
     * Gets a DOM element by it's ID.
     *
     * @param   e   string                  ID of te object to get.
     *
     * @return      DOM fragment/boolean    Returns the element of the DOM or false if fails.
     */
    this.getElementById = function ngstk_getElementById(e){
        if(typeof e==='string') {
            if(typeof document.getElementById != 'undefined' ) return document.getElementById(e);
            if(document.all) return document.all[e];
            }
        return false;
        }


    /**
     * Attach a function to a event in an object.
     *
     * It handles differences between browsers and is
     * multi-mode input.
     *
     * @param       obj         string/DOM fragment     Object (or id) where attach the function event.
     * @param       type        string                  Event name (without the "on" prefix).
     * @param       fn          string/DOM fragment     Function to attach.
     *
     * @return      DOM fragment/boolean            Original function return or false if something fails.
     *
     * @see removeEvent
     * @see stopEvent
     */
    this.addEvent = function ngstk_addEvent(obj,type,fn) {
        // normalizamos el objeto
        if( typeof obj == 'string' ) obj = self.getElementById( obj );

        // If something undefined, return false.
        if( !obj || typeof obj != 'object' || !type || typeof type != 'string' || !fn || typeof fn != 'function' || !obj ) return false;

        if (typeof obj.addEventListener != 'undefined' ) return obj.addEventListener( type, fn, false );
         else if ( typeof obj.attachEvent != 'undefined' ) return obj.attachEvent( "on"+type, fn );
          else return false;
        }


    /**
     * Deattach a function to a event in an object.
     *
     * It handles differences between browsers and is
     * multi-mode input.
     *
     * @param       obj         string/DOM fragment     Object (or id) where deattach the function event.
     * @param       type        string                  Event name (without the "on" prefix).
     * @param       fn          string/DOM fragment     Function to deattach.
     *
     * @return      DOM fragment/boolean            Original function return or false if something fails.
     *
     * @see addEvent
     * @see stopEvent
     */
    this.removeEvent = function ngstk_removeEvent( obj, type, fn ) {
        // normalizamos el objeto
        if( typeof obj == 'string' ) obj = self.getElementById( obj );
        if( typeof obj != 'object' || !obj ) return false;
        if (typeof obj.removeEventListener != 'undefined' ) obj.removeEventListener( type, fn, false );
         else if ( typeof obj.detachEvent != 'undefined' ) obj.detachEvent( "on"+type, obj[type+fn] );
          else return false;
        }



    /**
     * stop propagation of a event.
     *
     * Cross-browser event propagation prevention
     *
     * @param       String  e   (Optional). The event to stop. Mandatory in Mozilla browsers, optional in I.Explorer.
     *
     * @see ngstk.attachEvent
     * @see ngstk.removeEvent
     */
    this.stopEvent = function(e) {
        if( !e ) e = window.event;
        e.cancelBubble = true;
        e.returnValue = false;
        if (e.stopPropagation) {
            e.stopPropagation();
            e.preventDefault();
            }
        };


    /**
     * Object placement.
     *
     * Grabs an object, positions it absolutely to the given coordinates.
     * Multi-mode input.
     *
     * @param       obj         string/DOM fragment     Object (or id) to move.
     * @param       posx        decimal                 Destination horitzontal position inside the page.
     * @param       posy        decimal                 Destination vertical position inside the page.
     *
     * @return      boolean            true if OK, false if fails.
     *
     * @see addEvent
     */
    this.moveObjectToXY = function ngstk_moveObjectToXY(obj, posx, posy) {
        // normalizamos el objeto
        if( typeof obj == 'string' ) obj = self.getElementById( obj );
        if( !obj || typeof obj != 'object' ) return false;
        obj.style.position = "absolute";
        obj.style.left = posx;
        obj.style.top = posy;
        return true;
        }


    /**
     * Object creation.
     *
     * Uses DOM methods to create an object, apply its attributes and insert
     * its innerHTML. Handles style attribute correctly.
     *
     * @param       tipo    string      Kind of the element to create.
     * @param       attr    Object      Object or Array with attributes coded as key="value".
     * @param       cod     String      Node's innerHTML.
     *
     * @return      DOM fragment.
     *
     * @see deteleObject
     */
    this.createObject = function ngstk_createObject(tipo,attr,cod) {
        var z = document.createElement(tipo.toUpperCase());
        for( var a in attr ) {
            if( a == "style" ) self.applyCSS(z, attr[a]);
              else z.setAttribute(a, attr[a]);
            }
        if( typeof cod == 'string' ) z.innerHTML = cod;
        if( typeof cod == 'object' && cod ) z.appendChild( cod );
        return z;
        }


    /**
     * Object deletion.
     *
     * Uses DOM methods to delete an object. Can be used with a DOM
     * node or with an ID.
     *
     * @param       obj     DOM fragment/String       DOM fragment (or id) to delete.
     *
     * @return      bool        Original function return if OK, false if error.
     *
     * @see deteleObject
     */
    this.deleteObject = function ngstk_deleteObject(obj) {
        // normalizamos el objeto
        if( typeof obj == 'string' ) obj = self.getElementById( obj );
        if( !obj || typeof obj != 'object' ) return false;
        obj.innerHTML = "";
        return obj.parentNode.removeChild(obj);
        }



    /**
     * Find Object position.
     *
     * Uses DOM methods to find an object coordinates. Can be used with a DOM
     * node or with an ID.
     *
     * @param       obj     DOM fragment/String       DOM fragment (or id) to delete.
     *
     * @return      decimal Object:
     *      0/left:     left position.
     *      1/top:      top position.
     *      2/right:    right position.
     *      3/bottom:   bottom position.
     */
    this.findPos = function ngstk_findPos(obj) {
        // normalizamos el objeto
        if( typeof obj == 'string' ) obj = self.getElementById( obj );
        if( typeof obj != 'object' || !obj ) return false;

        var curleft = curtop = 0;
        var W = obj.offsetWidth;
        var H = obj.offsetHeight;
        if (obj.offsetParent) do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
            } while (obj = obj.offsetParent);
        return {0:curleft, 1:curtop, 2:curleft+W, 3:curtop+H, left:curleft, top:curtop, right:curleft+W, bottom:curtop+H};
        }


    /**
     * Left trim.
     *
     * Trims extra spaces at the left side of a array.
     *
     * @param       str     String      String to trim.
     *
     * @return      String      Trimmed string. "" if error.
     *
     * @see trim
     * @see trimR
     */
    this.trimL = function ngstk_trimL(str) {
        if( typeof str == 'string' ) return str.replace(/^\s+/,'');
         else return "";
        }


    /**
     * Right trim.
     *
     * Trims extra spaces at the right side of a string.
     *
     * @param       str     String      String to trim.
     *
     * @return      String      Trimmed string. "" if error.
     *
     * @see trim
     * @see trimL
     */
    this.trimR = function ngstk_trimR(str) {
        if( typeof str == 'string' ) return str.replace(/\s+$/,'');
         else return "";
        }


    /**
     * Trim.
     *
     * Trims extra spaces at the left and right sides of a string.
     *
     * @param       str     String      String to trim.
     *
     * @return      String      Trimmed string. "" if error.
     *
     * @see trimR
     * @see trimL
     */
    this.trim = function ngstk_trim(str) {
        if( typeof str == 'string' ) return str.replace(/^\s+/,'').replace(/\s+$/,'');
         else return "";
        }


    /**
     * Applyes CSS.
     *
     * Applyes a CSS definition string to a node. It's a cross-browsers
     * method.
     *
     * @param       o       DOM node/String     DOM node (or id) to apply CSS definition.
     * @param       css     String              CSS definition.
     *
     * @return      boolean     true when OK, false if error.
     */
    this.applyCSS = function ngstk_applyCSS(o, css) {
        var ret = true;
        // normalizamos el objeto
        if( typeof o == 'string' ) o = self.getElementById( o );
        if( !o || typeof o != 'object' ) return false;

        if( typeof css != 'string' ) return false;

        var props = css.split(";");
        for( var i in props ) if( typeof(props[i]) == "string" && self.trim(props[i]) != "") {
            var tmp = self.trim(props[i]);
            var item = tmp.split(':');
            if( item[0] && item[1] ) {
                v = self.trim(item[1]);
                if( self.trim(item[0]) != "" ) switch( self.trim(item[0]) ) {
                    case "background": o.style.background = v; break;
                    case "background-attachment": o.style.backgroundAttachment = v; break;
                    case "background-color": o.style.backgroundColor = v; break;
                    case "background-image": o.style.backgroundImage = v; break;
                    case "background-position": o.style.backgroundPosition = v; break;
                    case "background-repeat": o.style.backgroundRepeat = v; break;
                    case "border": o.style.border = v; break;
                    case "border-bottom": o.style.borderBottom = v; break;
                    case "border-bottom-color": o.style.borderBottomColor = v; break;
                    case "border-bottom-style": o.style.borderBottomStyle = v; break;
                    case "border-bottom-width": o.style.borderBottomWidth = v; break;
                    case "border-color": o.style.borderColor = v; break;
                    case "border-left": o.style.borderLeft = v; break;
                    case "border-left-color": o.style.borderLeftColor = v; break;
                    case "border-left-style": o.style.borderLeftStyle = v; break;
                    case "border-left-width": o.style.borderLeftWidth = v; break;
                    case "border-right": o.style.borderRight = v; break;
                    case "border-right-color": o.style.borderRightColor = v; break;
                    case "border-right-style": o.style.borderRightStyle = v; break;
                    case "border-right-width": o.style.borderRightWidth = v; break;
                    case "border-style": o.style.borderStyle = v; break;
                    case "border-top": o.style.borderTop = v; break;
                    case "border-top-color": o.style.borderTopColor = v; break;
                    case "border-top-style": o.style.borderTopStyle = v; break;
                    case "border-top-width": o.style.borderTopWidth = v; break;
                    case "border-width": o.style.borderWidth = v; break;
                    case "clear": o.style.clear = v; break;
                    case "clip": o.style.clip = v; break;
                    case "color": o.style.color = v; break;
                    case "cursor": o.style.cursor = v; break;
                    case "display": o.style.display = v; break;
                    case "filter": o.style.filter = v; break;
                    case "font": o.style.font = v; break;
                    case "font-family": o.style.fontFamily = v; break;
                    case "font-size": o.style.fontSize = v; break;
                    case "font-variant": o.style.fontVariant = v; break;
                    case "font-weight": o.style.fontWeight = v; break;
                    case "height": o.style.height = v; break;
                    case "left": o.style.left = v; break;
                    case "letter-spacing": o.style.letterSpacing = v; break;
                    case "line-height": o.style.lineHeight = v; break;
                    case "list-style": o.style.listStyle = v; break;
                    case "list-style-image": o.style.listStyleImage = v; break;
                    case "list-style-position": o.style.listStylePosition = v; break;
                    case "list-style-type": o.style.listStyleType = v; break;
                    case "margin": o.style.margin = v; break;
                    case "margin-bottom": o.style.marginBottom = v; break;
                    case "margin-left": o.style.marginLeft = v; break;
                    case "margin-right": o.style.marginRight = v; break;
                    case "margin-top": o.style.marginTop = v; break;
                    case "overflow": o.style.overflow = v; break;
                    case "padding": o.style.padding = v; break;
                    case "padding-bottom": o.style.paddingBottom = v; break;
                    case "padding-left": o.style.paddingLeft = v; break;
                    case "padding-right": o.style.paddingRight = v; break;
                    case "padding-top": o.style.paddingTop = v; break;
                    case "page-break-after": o.style.pageBreakAfter = v; break;
                    case "page-break-before": o.style.pageBreakBefore = v; break;
                    case "position": o.style.position = v; break;
                    case "float": o.style.styleFloat = v; break;
                    case "right": o.style.right = v; break;
                    case "bottom": o.style.bottom = v; break;
                    case "text-align": o.style.textAlign = v; break;
                    case "text-decoration": o.style.textDecoration = v; break;
                    case "text-indent": o.style.textIndent = v; break;
                    case "text-transform": o.style.textTransform = v; break;
                    case "top": o.style.top = v; break;
                    case "vertical-align": o.style.verticalAlign = v; break;
                    case "visibility": o.style.visibility = v; break;
                    case "width": o.style.width = v; break;
                    case "z-index": o.style.zIndex = v; break;
                    default: ret = false;
                    }
                }
            }
        return ret;
        }


    /**
     * Copy CSS.
     *
     * Applyes a CSS from an origin DOM node to a destiny one. It uses
     * JavaScript style access to be cross-browser.
     *
     * @param       o       DOM node/String     DOM node (or id) to get CSS definition, source.
     * @param       d       DOM node/String     DOM node (or id) to apply CSS definition, destiny.
     *
     * @return      boolean     true when OK, false if error.
     */
    this.copyCSS = function ngstk_copyCSS(o, d) {
        // normalizamos el objeto
        if( typeof o == 'string' ) o = self.getElementById( o );
        if( !o || typeof o != 'object' ) return false;

        // normalizamos el objeto
        if( typeof d == 'string' ) d = self.getElementById( d );
        if( !d || typeof d != 'object' ) return false;

        if(o.style.background) d.style.background = o.style.background;
        if(o.style.backgroundAttachment) d.style.backgroundAttachment = o.style.backgroundAttachment;
        if(o.style.backgroundColor) d.style.backgroundColor = o.style.backgroundColor;
        if(o.style.backgroundImage) d.style.backgroundImage = o.style.backgroundImage;
        if(o.style.backgroundPosition) d.style.backgroundPosition = o.style.backgroundPosition;
        if(o.style.backgroundRepeat) d.style.backgroundRepeat = o.style.backgroundRepeat;
        if(o.style.border) d.style.border = o.style.border;
        if(o.style.borderBottom) d.style.borderBottom = o.style.borderBottom;
        if(o.style.borderBottomColor) d.style.borderBottomColor = o.style.borderBottomColor;
        if(o.style.borderBottomStyle) d.style.borderBottomStyle = o.style.borderBottomStyle;
        if(o.style.borderBottomWidth) d.style.borderBottomWidth = o.style.borderBottomWidth;
        if(o.style.borderColor) d.style.borderColor = o.style.borderColor;
        if(o.style.borderLeft) d.style.borderLeft = o.style.borderLeft;
        if(o.style.borderLeftColor) d.style.borderLeftColor = o.style.borderLeftColor;
        if(o.style.borderLeftStyle) d.style.borderLeftStyle = o.style.borderLeftStyle;
        if(o.style.borderLeftWidth) d.style.borderLeftWidth = o.style.borderLeftWidth;
        if(o.style.borderRight) d.style.borderRight = o.style.borderRight;
        if(o.style.borderRightColor) d.style.borderRightColor = o.style.borderRightColor;
        if(o.style.borderRightStyle) d.style.borderRightStyle = o.style.borderRightStyle;
        if(o.style.borderRightWidth) d.style.borderRightWidth = o.style.borderRightWidth;
        if(o.style.borderStyle) d.style.borderStyle = o.style.borderStyle;
        if(o.style.borderTop) d.style.borderTop = o.style.borderTop;
        if(o.style.borderTopColor) d.style.borderTopColor = o.style.borderTopColor;
        if(o.style.borderTopStyle) d.style.borderTopStyle = o.style.borderTopStyle;
        if(o.style.borderTopWidth) d.style.borderTopWidth = o.style.borderTopWidth;
        if(o.style.borderWidth) d.style.borderWidth = o.style.borderWidth;
        if(o.style.bottom) d.style.bottom = o.style.bottom;
        if(o.style.clear) d.style.clear = o.style.clear;
        if(o.style.clip) d.style.clip = o.style.clip;
        if(o.style.color) d.style.color = o.style.color;
        if(o.style.cursor) d.style.cursor = o.style.cursor;
        if(o.style.display) d.style.display = o.style.display;
        if(o.style.filter) d.style.filter = o.style.filter;
        if(o.style.font) d.style.font = o.style.font;
        if(o.style.fontFamily) d.style.fontFamily = o.style.fontFamily;
        if(o.style.fontSize) d.style.fontSize = o.style.fontSize;
        if(o.style.fontVariant) d.style.fontVariant = o.style.fontVariant;
        if(o.style.fontWeight) d.style.fontWeight = o.style.fontWeight;
        if(o.style.height) d.style.height = o.style.height;
        if(o.style.left) d.style.left = o.style.left;
        if(o.style.letterSpacing) d.style.letterSpacing = o.style.letterSpacing;
        if(o.style.lineHeight) d.style.lineHeight = o.style.lineHeight;
        if(o.style.listStyle) d.style.listStyle = o.style.listStyle;
        if(o.style.listStyleImage) d.style.listStyleImage = o.style.listStyleImage;
        if(o.style.listStylePosition) d.style.listStylePosition = o.style.listStylePosition;
        if(o.style.listStyleType) d.style.listStyleType = o.style.listStyleType;
        if(o.style.margin) d.style.margin = o.style.margin;
        if(o.style.marginBottom) d.style.marginBottom = o.style.marginBottom;
        if(o.style.marginLeft) d.style.marginLeft = o.style.marginLeft;
        if(o.style.marginRight) d.style.marginRight = o.style.marginRight;
        if(o.style.marginTop) d.style.marginTop = o.style.marginTop;
        if(o.style.overflow) d.style.overflow = o.style.overflow;
        if(o.style.padding) d.style.padding = o.style.padding;
        if(o.style.paddingBottom) d.style.paddingBottom = o.style.paddingBottom;
        if(o.style.paddingLeft) d.style.paddingLeft = o.style.paddingLeft;
        if(o.style.paddingRight) d.style.paddingRight = o.style.paddingRight;
        if(o.style.paddingTop) d.style.paddingTop = o.style.paddingTop;
        if(o.style.pageBreakAfter) d.style.pageBreakAfter = o.style.pageBreakAfter;
        if(o.style.pageBreakBefore) d.style.pageBreakBefore = o.style.pageBreakBefore;
        if(o.style.position) d.style.position = o.style.position;
        if(o.style.right) d.style.right = o.style.right;
        if(o.style.styleFloat) d.style.styleFloat = o.style.styleFloat;
        if(o.style.textAlign) d.style.textAlign = o.style.textAlign;
        if(o.style.textDecoration) d.style.textDecoration = o.style.textDecoration;
        if(o.style.textDecorationBlink) d.style.textDecorationBlink = o.style.textDecorationBlink;
        if(o.style.textDecorationLineThrough) d.style.textDecorationLineThrough = o.style.textDecorationLineThrough;
        if(o.style.textDecorationNone) d.style.textDecorationNone = o.style.textDecorationNone;
        if(o.style.textDecorationOverline) d.style.textDecorationOverline = o.style.textDecorationOverline;
        if(o.style.textDecorationUnderline) d.style.textDecorationUnderline = o.style.textDecorationUnderline;
        if(o.style.textIndent) d.style.textIndent = o.style.textIndent;
        if(o.style.textTransform) d.style.textTransform = o.style.textTransform;
        if(o.style.top) d.style.top = o.style.top;
        if(o.style.verticalAlign) d.style.verticalAlign = o.style.verticalAlign;
        if(o.style.visibility) d.style.visibility = o.style.visibility;
        if(o.style.width) d.style.width = o.style.width;
        if(o.style.zIndex) d.style.zIndex = o.style.zIndex;
        return true;
        }



    /**
     * Dump object.
     *
     * Debug function which returns an XHTML dump of an object.
     *
     * @param   o       Object      Object to dump.
     *
     * @return      String      Object dump.
     */
    this.dumpObject = function ngstk_dumpObject(o) {
        if( typeof o === 'object' ) {
            var t="&lt;object&gt; {<dir>";
            for( var i in o) {
                t+=i+": "+self.dumpObject(o[i])+"<br />";
                }
            t+="}</dir>";
            return t;
            }
        return ("&lt;"+(typeof o)+"&gt; " + o);
        }



    /**
     * Window properties.
     *
     * Returns an object with this properties:
     *      0 and windowWidth: Inner width of the window.
     *      1 and windowHeight: Inner height of the window.
     *      2 and documentWidth: Width of the document (with scrolls).
     *      3 and documentHeight: Height of the document (with scrolls).
     *      4 and scrollX: Actual horitzontal scroll.
     *      5 and scrollY: Actual vertical scroll.
     *
     * @return      Integers Array      False if error.
     */
    this.windowProperties = function ngstk_windowProperties() {
        if( document.body ) {
            var Width = document.compatMode=='CSS1Compat' && !window.opera ? document.documentElement.clientWidth:document.body.clientWidth;
            var Height = document.compatMode=='CSS1Compat' && !window.opera ? document.documentElement.clientHeight:document.body.clientHeight;
            var scrOfX = false;
            var scrOfY = false;
            var xWithScroll = false;
            var yWithScroll = false;
            if( window.scrollX ) {
                scrOfX = window.scrollX;
                scrOfY = window.scrollY;
                }
            //if ( document.documentElement && document.body && document.body.scrollTop ) {
             else {
                scrOfX = document.documentElement.scrollLeft + document.body.scrollLeft;
                scrOfY = document.documentElement.scrollTop + document.body.scrollTop;
                }
            if (window.innerHeight && window.scrollMaxY) { // FF
                yWithScroll = window.innerHeight + window.scrollMaxY;
                xWithScroll = window.innerWidth + window.scrollMaxX;
                }
             else if (document.body.scrollHeight > document.body.offsetHeight){ // Todo menos IE-Mac
                yWithScroll = document.body.scrollHeight;
                xWithScroll = document.body.scrollWidth;
                }
              else { // IE6 Strict, Mozilla (no FF) y Safari
                yWithScroll = document.body.offsetHeight;
                xWithScroll = document.body.offsetWidth;
                }
            return {
                0: Width,
                1: Height,
                2: xWithScroll,
                3: yWithScroll,
                4: scrOfX,
                5: scrOfY,
                "windowWidth": Width,
                "windowHeight": Height,
                "documentWidth": xWithScroll,
                "documentHeight": yWithScroll,
                "scrollX": scrOfX,
                "scrollY": scrOfY
                }
            }
        else return false;
        }


        /**
         * Opens a new window.
         *
         * Opens a new window centered in the screen.
         */
        this.openWindow = function(url, width, height) {
            var x = (screen.width/2-width/2);
            var y = (screen.height/2-height/2);
            window.open(url, "", "scrollbars=auto,width="+width+",height="+height+",screenX="+(x)+",screenY="+y+",left="+x+",top="+y);
            };





















    this.xhtml = {
        /**
         * Transforms code to XHTML.
         *
         * Gets xhtml valid code from a node.
         *
         * @param       String  t   Code to be transformed.
         *
         * @return      String  XHTML valid equivalent code.
         */
        "getFromCode": function ngstk_xhtml_getFromCode(t) {
            var n = self.createObject("DIV", null, t);
            return self.getXhtml(t);
            },


        /**
         * Transforms code to XHTML.
         *
         * Gets xhtml valid code from a node.
         *
         * @param       node            DOM node    DOM node to get CSS definition, source.
         * @param       other params    optional, used for recursivity.
         *
         * @return      string      XHTML valid equivalent code.
         */
        "getFromNode": function ngstk_xhtml_getFromNode(node, lang, encoding, need_nl, inside_pre) {
            var need_nl_before = '|div|p|table|tbody|tr|td|th|title|head|body|script|comment|li|meta|h1|h2|h3|h4|h5|h6|hr|ul|ol|option|link|';
            var need_nl_after = '|html|head|body|p|th|style|';
            var re_comment = new RegExp("^<!--(([a]|[^a])*)-->$");
            var i;
            var text = '';
            var children = node.childNodes;
            var child_length = children.length;
            var tag_name;
            var do_nl = need_nl?true:false;
            var page_mode = true;
            var media_align = '';
            var re_parsed_val = new Date().getTime();
            for (i=0;i<child_length;i++) {
                var child = children[i];
                if (document.all) {
                    if (child.getAttribute && child.getAttribute('re_parsed') == re_parsed_val) continue;
                    if (child.setAttribute) child.setAttribute('re_parsed', re_parsed_val);
                    }
                if (child.parentNode && String(node.tagName).toLowerCase() != String(child.parentNode.tagName).toLowerCase()) continue;
                switch (child.nodeType) {
                    case 1: { //ELEMENT_NODE
                        var tag_name = String(child.tagName).toLowerCase();
                        //store value of align attribute as IE cannot handle it properly
                        if (document.all && tag_name == 'embed') {
                            var parameter = /align=("[^\"]*"|'[^\']*'|[^\"\'\s]*)(\s|\>)/i;
                            var align_code = String(child.outerHTML).match(parameter);
                            if (align_code) {
                                align_code = align_code[1];
                                media_align = align_code.replace(/("|')/g,"");
                                }
                            }
                        if (tag_name == '') break;
                        if (tag_name == 'meta') {
                            var meta_name = String(child.name).toLowerCase();
                            if (meta_name == 'generator') break;
                            }
                        if (document.all && tag_name == 'object' && !(child.canHaveChildren || child.hasChildNodes())) {
                            text += self.xhtml.fixObjectCode(child.outerHTML);
                            continue;
                            }
                        if (!need_nl && tag_name == 'body') page_mode = false;
                        if (tag_name == '!') {
                            var parts = re_comment.exec(child.text);
                            if (parts) {
                                //the last char of the comment text must not be a hyphen
                                var inner_text = parts[1];
                                text += self.xhtml.fixComment(inner_text);
                                }
                            }
                        else {
                            if (tag_name == 'html') text = '<.?xml version="1.0" encoding="'+encoding+'"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n';
                            if (need_nl_before.indexOf('|'+tag_name+'|') != -1) {
                                if ((do_nl || text != '') && !inside_pre) text += '\n';
                                else do_nl = true;
                                }
                            text += '<'+tag_name;
                            //add attributes
                            var attr = child.attributes;
                            var attr_length = attr.length;
                            var attr_value;
                            var attr_lang = false;
                            var attr_xml_lang = false;
                            var attr_xmlns = false;
                            var is_alt_attr = false;
                            for (j=0;j<attr_length;j++) {
                                var attr_name = attr[j].nodeName.toLowerCase();
                                if (attr_name == 're_parsed') continue;
                                if (!attr[j].specified && attr_name != 'selected' && attr_name != 'style' && attr_name != 'value' && attr_name != 'shape' && attr_name != 'coords') continue; //IE 5.0
                                if ((attr_name == 'shape' || attr_name == 'coords') && tag_name != 'area') continue;
                                if (attr_name == 'selected' && !child.selected || attr_name == 'style' && child.style.cssText == '') continue;
                                if (attr_name == '_moz_dirty' || attr_name == '_moz_resizing' || attr_name == '_moz-userdefined' || tag_name == 'br' && attr_name == 'type' && child.getAttribute('type') == '_moz') continue;
                                var valid_attr = true;
                                switch (attr_name) {
                                    case "style" : attr_value = child.style.cssText; break;
                                    case "class" : attr_value = child.className; break;
                                    case "http-equiv": attr_value = child.httpEquiv; break;
                                    case "noshade": //this set of choices will extend
                                    case "checked":
                                    case "selected":
                                    case "multiple":
                                    case "nowrap":
                                    case "disabled": attr_value = attr_name; break;
                                    case "name": attr_value = child.name?child.name:child.getAttribute('name'); break;
                                    case "for": attr_value = child.htmlFor; break;
                                    default: try {
                                        attr_value = child.getAttribute(attr_name, 2);
                                        }
                                    catch (e) {
                                        valid_attr = false;
                                        }
                                    }
                                if (tag_name == 'embed') {
                                    switch (attr_name) {
                                        case 'align':
                                            if (media_align) attr_value = media_align;
                                            else attr_value = eval('child.'+attr_name);
                                            break;
                                        case 'showstatusbar':
                                        case 'showcontrols':
                                        case 'autostart':
                                        case 'type': attr_value = attr[j].nodeValue; break;
                                        default: break;
                                        }
                                    }
                                if (attr_name == 'lang' && tag_name == 'html') {
                                    attr_lang = true;
                                    attr_value = lang;
                                    }
                                if (attr_name == 'xml:lang') {
                                    attr_xml_lang = true;
                                    attr_value = lang;
                                    }
                                if (attr_name == 'xmlns') attr_xmlns = true;
                                if (tag_name == 'object' && attr_name == 'src' && document.all) attr_value = self.xhtml.fixObjectSrc(child.outerHTML);
                                if (valid_attr) if (!(tag_name == 'li' && attr_name == 'value')) text += ' '+attr_name+'="'+self.xhtml.fixAttribute(attr_value)+'"';
                                if (attr_name == 'alt') is_alt_attr = true;
                                }
                            if (tag_name == 'img' && !is_alt_attr) text += ' alt=""';
                            if (tag_name == 'html') {
                                if (!attr_lang) text += ' lang="'+lang+'"';
                                if (!attr_xml_lang) text += ' xml:lang="'+lang+'"';
                                if (!attr_xmlns) text += ' xmlns="http://www.w3.org/1999/xhtml"';
                                }
                            if (child.canHaveChildren || child.hasChildNodes()){
                                text += '>';
                                text += self.getXhtml(child, lang, encoding, true, inside_pre||tag_name=='pre'?true:false);
                                text += '</'+tag_name+'>';
                                }
                            else {
                                if (tag_name == 'style' || tag_name == 'title' || tag_name == 'script' || tag_name == 'textarea' || tag_name == 'a') {
                                    text += '>';
                                    var inner_text;
                                    if (tag_name == 'script') inner_text = child.text;
                                    else inner_text = child.innerHTML;
                                    if (tag_name == 'style') inner_text = String(inner_text).replace(/[\n]+/g,'\n');
                                    text += inner_text+'</'+tag_name+'>';
                                    }
                                else text += ' />';
                                }
                            }
                        break;
                        }
                    case 3: {
                        if (!inside_pre) {
                            if (child.nodeValue != '\n') text += fix_entities(self.xhtml.fixText(child.nodeValue));
                            }
                        else text += child.nodeValue;
                        break;
                        }
                    case 8: text += self.xhtml.fixComment(child.nodeValue); break;
                    default: break;
                    }
                }
            if (!need_nl && !page_mode) text = text.replace(/<\/?head>[\n]*/gi, "").replace(/<head \/>[\n]*/gi, "").replace(/<\/?body>[\n]*/gi, "");
            return text;
            },


        /**
         * Fixes comments.
         *
         * Last character of a comment can't be a hyphen. If it's
         * the case, we add a trailing space.
         *
         * @param       t       String      Original string.
         *
         * @return      String      Fixed string.
         */
        "fixComment": function ngstk_xhtml_fixComment(t) {
            var re_hyphen = new RegExp("-$");
            t = t.replace(/--/g, "__");
            if(re_hyphen.exec(t)) t+=" ";
            return "<!--"+t+"-->";
            },

        /**
         * Fixes text.
         *
         * Replaces some entities and delete extra spaces.
         *
         * @param       t       String      Original string.
         *
         * @return      String      Fixed string.
         */
        "fixText": function ngstk_xhtml_fixText(t) {
/*ORIGINAL:
            var tt = String(t).replace(/\&lt;/g, "#h2x_lt").replace(/\&gt;/g, "#h2x_gt");
            tt = tt.replace(/\n{2,}/g, "\n").replace(/\&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\u00A0/g, "&nbsp;");
            return tt.replace(/#h2x_lt/g, "&lt;").replace(/#h2x_gt/g, "&gt;");
*/
            return  String(t).replace(/\n{2,}/g, "\n").replace(/\&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\u00A0/g, "&nbsp;");
            },



        /**
         * Fixes attributes.
         *
         * Replaces some entities.
         *
         * @param       t       String      Original string.
         *
         * @return      String      Fixed string.
         */
        "fixAttribute": function ngstk_xhtml_fixAttribute(t) {
/*ORIGINAL:
            var tt = String(t).replace(/\&lt;/g, "#h2x_lt").replace(/\&gt;/g, "#h2x_gt");
            tt = tt.replace(/\&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\"/g, "&quot;");
            return tt.replace(/#h2x_lt/g, "&lt;").replace(/#h2x_gt/g, "&gt;");
*/
            return  String(t).replace(/\&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
            },




        /**
         * Fixes object src attribute.
         *
         * Search for all ways of set src attribute:
         *      - src=xxxxxx.
         *      - src='xxxxxx'.
         *      - src="xxxxxx".
         *  In all cases it will return xxxxxx.
         *
         * @param       t       String      Object string.
         *
         * @return      String      src attribute from object or empty string.
         */
        "fixObjectSrc": function ngstk_xhtml_fixObjectSrc(t) {
            var temp = String(t);
            var obj_tag_parts = t.match(/<object ([^>]+)>/i);
            if(obj_tag_parts){
                var src_value = obj_tag_parts[1].match(/src="([^"]+)"/i);
                if (!src_value) {
                    src_value = obj_tag_parts[1].match(/src='([^']+)'/i);
                    if (!src_value) src_value = obj_tag_parts[1].match(/src=([^ ]+)/i);
                    }
                if (src_value) return src_value[1];
                }
            return '';
            },


        /**
         * Fixes object code lowercasing all attributes.
         *
         * Search for all attributes and lowercase them.
         *
         * @param       t       String      Object string.
         *
         * @return      String      Same string lowercased.
         */
         "fixObjectCode": function ngstk_xhtml_fixObjectCode(text) {
/*TODO: Se puede eliminar el EMBED... */
            return String(text).replace(/ style=/gi, ' style=').replace(/ codeBase=/gi, ' codebase=').replace(/ height=/gi, ' height=').replace(/ width=/gi, ' width=').replace(/ align=/gi, ' align=').replace(/ classid=/gi, ' classid=').replace(/ src=/gi, ' src=').replace(/ NAME=/gi, ' name=').replace(/ VALUE=/gi, ' value=').replace(/ quality=/gi, ' quality=').replace(/ TYPE=/gi, ' type=').replace(/ PLUGINSPAGE=/gi, ' pluginspage=').replace(/<OBJECT /gi, '<object ').replace(/<\/OBJECT>/gi, '</object>').replace(/<PARAM /gi, '<param ').replace(/<\/PARAM>/gi, '</param>').replace(/<EMBED /gi, '<embed ').replace(/<\/EMBED>/gi, '</embed>');
            },



        /**
         * Array of charcodes and its entities.
         *
         * It's used to replace all ocurrences of these
         * special characters to its entity.
         */
        "ents": {
            8364 : "euro",
            402  : "fnof",
            8240 : "permil",
            352  : "Scaron",
            338  : "OElig",
            381  : "#381",
            8482 : "trade",
            353  : "scaron",
            339  : "oelig",
            382  : "#382",
            376  : "Yuml",
            162  : "cent",
            163  : "pound",
            164  : "curren",
            165  : "yen",
            166  : "brvbar",
            167  : "sect",
            168  : "uml",
            169  : "copy",
            170  : "ordf",
            171  : "laquo",
            172  : "not",
            173  : "shy",
            174  : "reg",
            175  : "macr",
            176  : "deg",
            177  : "plusmn",
            178  : "sup2",
            179  : "sup3",
            180  : "acute",
            181  : "micro",
            182  : "para",
            183  : "middot",
            184  : "cedil",
            185  : "sup1",
            186  : "ordm",
            187  : "raquo",
            188  : "frac14",
            189  : "frac12",
            190  : "frac34",
            191  : "iquest",
            192  : "Agrave",
            193  : "Aacute",
            194  : "Acirc",
            195  : "Atilde",
            196  : "Auml",
            197  : "Aring",
            198  : "AElig",
            199  : "Ccedil",
            200  : "Egrave",
            201  : "Eacute",
            202  : "Ecirc",
            203  : "Euml",
            204  : "Igrave",
            205  : "Iacute",
            206  : "Icirc",
            207  : "Iuml",
            208  : "ETH",
            209  : "Ntilde",
            210  : "Ograve",
            211  : "Oacute",
            212  : "Ocirc",
            213  : "Otilde",
            214  : "Ouml",
            215  : "times",
            216  : "Oslash",
            217  : "Ugrave",
            218  : "Uacute",
            219  : "Ucirc",
            220  : "Uuml",
            221  : "Yacute",
            222  : "THORN",
            223  : "szlig",
            224  : "agrave",
            225  : "aacute",
            226  : "acirc",
            227  : "atilde",
            228  : "auml",
            229  : "aring",
            230  : "aelig",
            231  : "ccedil",
            232  : "egrave",
            233  : "eacute",
            234  : "ecirc",
            235  : "euml",
            236  : "igrave",
            237  : "iacute",
            238  : "icirc",
            239  : "iuml",
            240  : "eth",
            241  : "ntilde",
            242  : "ograve",
            243  : "oacute",
            244  : "ocirc",
            245  : "otilde",
            246  : "ouml",
            247  : "divide",
            248  : "oslash",
            249  : "ugrave",
            250  : "uacute",
            251  : "ucirc",
            252  : "uuml",
            253  : "yacute",
            254  : "thorn",
            255  : "yuml",
            913  : "Alpha",
            914  : "Beta",
            915  : "Gamma",
            916  : "Delta",
            917  : "Epsilon",
            918  : "Zeta",
            919  : "Eta",
            920  : "Theta",
            921  : "Iota",
            922  : "Kappa",
            923  : "Lambda",
            924  : "Mu",
            925  : "Nu",
            926  : "Xi",
            927  : "Omicron",
            928  : "Pi",
            929  : "Rho",
            931  : "Sigma",
            932  : "Tau",
            933  : "Upsilon",
            934  : "Phi",
            935  : "Chi",
            936  : "Psi",
            937  : "Omega",
            8756 : "there4",
            8869 : "perp",
            945  : "alpha",
            946  : "beta",
            947  : "gamma",
            948  : "delta",
            949  : "epsilon",
            950  : "zeta",
            951  : "eta",
            952  : "theta",
            953  : "iota",
            954  : "kappa",
            955  : "lambda",
            956  : "mu",
            957  : "nu",
            968  : "xi",
            969  : "omicron",
            960  : "pi",
            961  : "rho",
            962  : "sigmaf",
            963  : "sigma",
            964  : "tau",
            965  : "upsilon",
            966  : "phi",
            967  : "chi",
            968  : "psi",
            969  : "omega",
            8254 : "oline",
            8804 : "le",
            8260 : "frasl",
            8734 : "infin",
            8747 : "int",
            9827 : "clubs",
            9830 : "diams",
            9829 : "hearts",
            9824 : "spades",
            8596 : "harr",
            8592 : "larr",
            8594 : "rarr",
            8593 : "uarr",
            8595 : "darr",
            8220 : "ldquo",
            8221 : "rdquo",
            8222 : "bdquo",
            8805 : "ge",
            8733 : "prop",
            8706 : "part",
            8226 : "bull",
            8800 : "ne",
            8801 : "equiv",
            8776 : "asymp",
            8230 : "hellip",
            8212 : "mdash",
            8745 : "cap",
            8746 : "cup",
            8835 : "sup",
            8839 : "supe",
            8834 : "sub",
            8838 : "sube",
            8712 : "isin",
            8715 : "ni",
            8736 : "ang",
            8711 : "nabla",
            8719 : "prod",
            8730 : "radic",
            8743 : "and",
            8744 : "or",
            8660 : "hArr",
            8658 : "rArr",
            9674 : "loz",
            8721 : "sum",
            8704 : "forall",
            8707 : "exist",
            8216 : "lsquo",
            8217 : "rsquo",
            161  : "iexcl",
            977  : "thetasym",
            978  : "upsih",
            982  : "piv",
            8242 : "prime",
            8243 : "Prime",
            8472 : "weierp",
            8465 : "image",
            8476 : "real",
            8501 : "alefsym",
            8629 : "crarr",
            8656 : "lArr",
            8657 : "uArr",
            8659 : "dArr",
            8709 : "empty",
            8713 : "notin",
            8727 : "lowast",
            8764 : "sim",
            8773 : "cong",
            8836 : "nsub",
            8853 : "oplus",
            8855 : "otimes",
            8901 : "sdot",
            8968 : "lceil",
            8969 : "rceil",
            8970 : "lfloor",
            8971 : "rfloor",
            9001 : "lang",
            9002 : "rang",
            710  : "circ",
            732  : "tilde",
            8194 : "ensp",
            8195 : "emsp",
            8201 : "thinsp",
            8204 : "zwnj",
            8205 : "zwj",
            8206 : "lrm",
            8207 : "rlm",
            8211 : "ndash",
            8218 : "sbquo",
            8224 : "dagger",
            8225 : "Dagger",
            8249 : "lsaquo",
            8250 : "rsaquo"
            },

        /**
         * Replaces special chars for its entities.
         *
         * @param       text    String      Object string.
         *
         * @return      String      Fixed string.
         *
         * @see ngstk.xhtml.ents
         */
        "fixEntities": function ngstk_xhtml_fixEntities(text) {
            var i;
            var new_text = '';
            var temp = new RegExp("[a]|[^a]", "g");
            var parts = text.match(temp);
            if (!parts) return text;
            for (i=0; i<parts.length; i++) {
                var c_code = parseInt(parts[i].charCodeAt());
                if (self.xhtml.ents[c_code]) new_text += "&"+self.xhtml.ents[c_code]+";";
                else new_text += parts[i];
                }
            return new_text;
            }

        };
































    this.mouse = {
        /**
         * Mouse position objects:
         *
         *   ngstk.mouse.Xarea       X position in the viewing window.
         *   ngstk.mouse.Yarea       Y position in the viewing window.
         *   ngstk.mouse.Xpage       X position at whole document.
         *   ngstk.mouse.Ypage       Y position at whole document.
         */
        "Xarea": 0,
        "Yarea": 0,
        "Xpage": 0,
        "Ypage": 0,

        /**
         * Variable to store dragging object and properties.
         */
        "dragObj": {},


        /**
         * Tracking mouse function. It's attached to mousemove event.
         *
         *   @param     e       Recieves the event in Mozilla browsers.
         */
        "onMove": function ngstk_mouse_onMove(e) {
            if (!e) e = window.event;
            self.mouse.Xarea = e.clientX;
            self.mouse.Yarea = e.clientY;
            if ( document.documentElement && document.body && document.body.scrollTop ) {
                self.mouse.Xscroll = document.documentElement.scrollLeft + document.body.scrollLeft;
                self.mouse.Yscroll = document.documentElement.scrollTop + document.body.scrollTop;
                }
              else {
                self.mouse.Xscroll = window.scrollX;
                self.mouse.Yscroll = window.scrollY;
                }
            self.mouse.Xpage = self.mouse.Xarea + self.mouse.Xscroll;
            self.mouse.Ypage = self.mouse.Yarea + self.mouse.Yscroll;

            // Si hay arrastre de ventana, lo realizamos y cortamos el evento
            if( typeof self.mouse.dragObj.elNode === 'object' ) {
                self.mouse.dragObj.elNode.style.left = (self.mouse.dragObj.elStartLeft + self.mouse.Xpage - self.mouse.dragObj.cursorStartX) + "px";
                self.mouse.dragObj.elNode.style.top  = (self.mouse.dragObj.elStartTop  + self.mouse.Ypage - self.mouse.dragObj.cursorStartY) + "px";
                if ( typeof e.event != 'undefined') {
                    if ( typeof e.cancelBubble != 'undefined') e.cancelBubble = true;
                    if ( typeof e.returnValue != 'undefined') e.returnValue = false;
                    }
                if ( typeof e.preventDefault != 'undefined' ) e.preventDefault();
                }
            },


        /**
         * Dragg start action
         *
         * Get initial values to start a dragg:
         *      Stores object to dragg.
         *      Stores inicial X and Y.
         *      Incrases object's z-index by 1.
         *
         * @param     obj     DOM Object/String       DOM object (or its id) to dragg.
         *
         * @return      bool        true if OK, false if object doesn't exist.
         */
        "dragStart": function ngstk_mouse_dragStart(obj) {
            // Normalizamos el objeto
            if( typeof obj == 'string') obj = self.getElementById(obj);
            if( typeof obj != 'object' ) {
                return false;
                }
            self.mouse.dragObj.elNode = obj;
            // Guardamos las posiciones iniciales del cursor y del elemento.
            self.mouse.dragObj.cursorStartX = self.mouse.Xpage;
            self.mouse.dragObj.cursorStartY = self.mouse.Ypage;
            var tmp = self.findPos(self.mouse.dragObj.elNode);
            self.mouse.dragObj.elStartLeft = tmp[0];
            self.mouse.dragObj.elStartTop = tmp[1];
//            if ( isNaN(self.mouse.dragObj.elStartLeft) ) self.mouse.dragObj.elStartLeft = self.mouse.Xpage;
//            if ( isNaN(self.mouse.dragObj.elStartTop) )  self.mouse.dragObj.elStartTop  = self.mouse.Ypage;
            // Actualizamos el z-index del objeto.
            self.mouse.dragObj.elNode.style.zIndex++;
            return true;
            },


        /**
         * Dragg stop action.
         *
         * Ends dragging action, clean dragging object data
         * and decrases z-index by 1 (restores original).
         */
        "dragStop": function ngstk_mouse_dragStop() {
            if( typeof self.mouse.dragObj.elNode == 'object' ) {
                self.mouse.dragObj.elNode.style.zIndex--;
                self.mouse.dragObj = {};
                }
            }

        // Final del objeto ngstk.mouse
        };


    // Iniciamos el rastreo del raton y el liberar arrastre
    self.addEvent(document,"mousemove",self.mouse.onMove);
    self.addEvent(document,"mouseup",self.mouse.dragStop);















    this.mouse.animation = {
        /**
         * Pointer movement from actual position to indicated point.
         *
         * Moves a false pointer from actual mouse position
         * to a specified X-Y point of the page. Note that this point is a point
         * of the page's canvas, not of viewing port.
         *
         * @param     destx     integer     X destination position.
         * @param     desty     integer     Y destination position.
         * @param     milis     integer     Miliseconds of the animation.
         * @param     wait      integer     Miliseconds who stands at destiny before dissappear.
         */
        "actualToPoint": function ngstk_mouse_animation_actualToPoint(destx, desty, milis, wait) {
            self.mouse.animation.areaToArea(self.mouse.Xpage,self.mouse.Ypage, destx, desty, milis, wait);
            },


        /**
         * Pointer movement from actual position to the center of an object.
         *
         * Moves a false pointer from actual mouse position
         * to the center of a specified DOM node.
         *
         * @param     obj       DOM Object/String   Destiny's DOM node (or id).
         * @param     milis     integer     Miliseconds of the animation.
         * @param     wait      integer     Miliseconds who stands at destiny before dissappear.
         *
         * @return      bool    true if OK, false if object not found.
         */
        "actualToObject": function ngstk_mouse_animation_actualToObject(obj, milis, wait) {
            // normalizamos el objeto
            if( typeof obj == 'string' ) obj = self.getElementById( obj );
            if( typeof obj != 'object' || !obj ) return false;
            var dest = self.findPos(obj);
            self.mouse.animation.areaToArea(self.mouse["Xpage"],self.mouse["Ypage"],(dest[0]+dest[2])/2, (dest[1]+dest[3])/2, milis, wait);
            return true;
            },


        /**
         * Pointer movement from actual position to indicated point.
         *
         * Moves a false pointer from an X-Y point of a page to a
         * specified X-Y point of the page. Note that these points are a points
         * of the page's canvas, not of viewing port.
         *
         * @param     origx     integer     X origin position.
         * @param     origy     integer     Y origin position.
         * @param     destx     integer     X destination position.
         * @param     destY     integer     Y destination position.
         * @param     milis     integer     Miliseconds of the animation.
         * @param     wait      integer     Miliseconds who stands at destiny before dissappear.
         */
        "areaToArea": function ngstk_mouse_animation_areaToArea(origx, origy, destx, desty, milis, wait) {
            origx = parseInt(origx);
            origy = parseInt(origy);
            destx = parseInt(destx);
            desty = parseInt(desty);
            wait = parseInt(wait);
            milis = parseInt(milis);
            var actx = origx;
            var acty = origy;
            if( typeof destx != 'number' ) destx = -1;
            if( typeof desty != 'number' ) desty = -1;
            if( typeof milis != 'number' ) milis = 1;
            if( typeof wait != 'number' ) wait = 2000;
            if( milis < 1 ) milis = 1;
            if( wait < 1 ) wait = 1;
            if( destx >= 0 && desty >= 0 ) {
                // Creamos la imagen del puntero:
                var a = {
                    "src": self.config["IMAGE_URL"]+"puntero.gif",
                    "alt": "i",
                    "id": "ngstk_pointer",
                    "title": "Puntero simulado",
                    "border": "0",
                    "style": "position:absolute;top:" + self.mouse.Ypage + "px;left:" + self.mouse.Xpage + "px;z-index:99;"
                    };
                document.body.appendChild(self.createObject("img",a));

                // Programamos sus moviemientos (maximo 100):
                var ciclos = milis;
                if (milis > 100) ciclos = 100;
                var retardo = milis/ciclos;
                incx = (destx - actx) / ciclos;
                incy = (desty - acty) / ciclos;
                for( var i = 1; i < ciclos; i++) {
                    actx += incx;
                    acty += incy;
                    setTimeout("ngstk.moveObjectToXY('ngstk_pointer','" + actx +"px', '" + acty + "px')", parseInt(retardo*i));
                    }
                setTimeout("ngstk.moveObjectToXY('ngstk_pointer', '" + destx + "px', '" + desty + "px')", parseInt(retardo*i));
                setTimeout("ngstk.deleteObject('ngstk_pointer')", parseInt(milis+wait));
                }
            }
        };

    // Precargamos la imagen del puntero
    self.preloadimg( [ self.config["IMAGE_URL"]+"puntero.gif" ] );











    this.ajax = new function() {

        function xmlreq() {
            /**
             * XML request internal function.
             *
             * Handles cross-browser AJAX requests, parameters and optins.
             *
             * @param     url       String          URL to make request.
             * @param     params    String/Object   Optional. Parameters to send. False, null or undefined to send nothing.
             * @param     metodo    String          Optional. Method/verb to use: GET, POST, PUT, UPLOAD.... (GET in default).
             * @param     okfn      Function        Optional. Function called when OK.
             * @param     statusfn  Function        Optional. Function called when status changes.
             * @param     errorfn   Function        Optional. Function called when error occurs.
             * @param     extra     Object          Optional. Extra parameter passed to okfn, statusfn, errorfn as second parameter.
             *
             * @return      Boolean     True if OK, false if no AJAX support.
             */
            this.ajax = function(url, params, metodo, okfn, statusfn, errorfn, extra, headers) {
                // Add extra parameter to avoid cache
                if( typeof url !== 'string' ) return false;
                url = url.replace(/#.*$/,''); // Eliminamos las anclas.
                url += ( (url.indexOf( "?" ) ) ? "&":"?");  // Concatenamos un parametro
                url += Math.random(1000);                   // random para que no haga cache.

                // Normalize parameters
                if( typeof params == 'object' ) {
                    var tmp = "";
                    for( var idx in params ) {
                        if( tmp != "" ) tmp += "&";
                        tmp += encodeURIComponent(idx) + "=" + encodeURIComponent( params[idx] );
                        }
                    params = tmp;
                    }
                if( typeof params != 'string' || !params ) params = "";

                // Normalize method
                metodo = (""+metodo).toUpperCase();

                // if we use GET we have to concatenate the parameters
                if( metodo != "POST" && params != "" ) {
                    // Take in mind we have at least the nocache parameter, so always use '&' to concatenate
                    url += "&" + params;
                    }

                // Get the xmlreq object
                var xmlreq;
                if ( typeof ActiveXObject != "undefined" ) {
                    xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
                    }
                  else if ( XMLHttpRequest ){
                    xmlreq = new XMLHttpRequest();
                    }

                // Do the xml-request
                if( xmlreq ) {
                    xmlreq.onreadystatechange = function() {
                        if(typeof statusfn === 'function') statusfn(xmlreq,extra);
                        if (xmlreq.readyState == 4) {
                            if (xmlreq.status >= 200 && xmlreq.status < 400) {
                                if(typeof okfn === 'function') okfn(xmlreq,extra);
                                }
                              else {
                                if(typeof errorfn === 'function') errorfn(xmlreq,extra);
                                }
                            }
                        }; // Fin de la funcion status.

                    // Process the headers
                    if (headers) {
                        for (var k in headers) xmlreq.setRequestHeader(k, headers[k]);
                    }

                    xmlreq.open(metodo, url, true);
                    xmlreq.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                    if( metodo == "POST" ) {
                        xmlreq.setRequestHeader("Content-length", params.length);
                        xmlreq.setRequestHeader("Connection", "close");
                        xmlreq.send(params);
                        }
                    // Default method is GET
                      else {
                        if (window.ActiveXObject) xmlreq.send();
                          else xmlreq.send(null);
                        }
                    }
                  else {
 /*TODO: Implement support to xml-rpc throught iframes and return true */
                    return false;
                    }
                return true;
                }

            // Subclass end
            };


        /**
         * XML request manager core function.
         *
         * Handles cross-browser AJAX requests, parameters and optins and
         * allows send and recieve data.
         *
         * @param     url       String          URL to make request.
         * @param     params    String/Object   Optional. Parameters to send. False, null or undefined to send nothing.
         * @param     metodo    String          Optional. Method/verb to use: GET, POST, PUT, UPLOAD.... (GET in default).
         * @param     okfn      Function        Optional. Function called when OK.
         * @param     statusfn  Function        Optional. Function called when status changes.
         * @param     errorfn   Function        Optional. Function called when error occurs.
         * @param     extra     Object          Optional. Extra parameter passed to okfn, statusfn, errorfn as second parameter.
         *
         * @return      Boolean     True if OK, false if no AJAX support.
         */
        this.sendAndRecieve=function(url, params, metodo, okfn, statusfn, errorfn, extra) {
            return (new xmlreq()).ajax(url, params, metodo, okfn, statusfn, errorfn, extra);
            }


        /**
         * Function alias.
         *
         * @see ngstk.ajax.sendAndRecieve
         */
        this.send=this.sendAndRecieve;


        /**
         * XML request manager recieve function.
         *
         * Alias to AJAX core function without data to send.
         *
         * @param     url       String          URL to make request.
         * @param     metodo    String          Optional. Method/verb to use: GET, POST, PUT, UPLOAD.... (GET in default).
         * @param     okfn      Function        Optional. Function called when OK.
         * @param     statusfn  Function        Optional. Function called when status changes.
         * @param     errorfn   Function        Optional. Function called when error occurs.
         * @param     extra     Object          Optional. Extra parameter passed to okfn, statusfn, errorfn as second parameter.
         *
         * @return      Boolean     True if OK, false if no AJAX support.
         */
        this.recieve=function(url, metodo, okfn, statusfn, errorfn, extra) {
            return self.ajax.sendAndRecieve(url, false, metodo, okfn, statusfn, errorfn, extra);
            }

        }; // End of ajax object





















        /**
         * Audio functions. Handles .mid and .wav audio.
         */
    this.audio = new function() {

        /**
         * Internal active audio playing count.
         */
        var active = 0;


        /**
         * Audio activation variable. Permits override all audio commands.
         */
        this.active = true;


        /**
         * Plays an audio file.
         *
         * Builds audio object with some optional delay.
         *
         * @param     audioFile String          URL to play.
         * @param     dur       Integer         Time to maintain audio object in seconds. It has to be little longer than audio file.
         * @param     del       Integer         Optional. Time to wait before audio play, in miliseconds.
         * @param     id        String          Optional. ID of audio object. If not pressent, one uniqueID will be assigned.
         * @param     ovln      Boolean         Optional. Permits next audio files to be overlapped with this.
         * @param     force     Boolean         Optional. Forces playing althrought there's another file playing.
         *
         * @return      Boolean     True if OK, false if error.
         */
        this.play = function(audioFile, dur, del, id, ovln, force) {
            if( self.audio.active && typeof dur === 'number' && typeof audioFile === 'string' ) {
                if( typeof del !== 'number' ) del = 0;
                if( typeof id !== 'string' ) id="ngstk_audioObject" + self.getUniqueID();
                if( !ovln ) ovln = false;
                if( !force ) force = false;
                duration = (dur*1000)+del+300;
                setTimeout( function() { self.audio.build(audioFile, id, duration, false, ovln, force); }, del);
                return true;
                }
            return false;
            }


        /**
         * Decrases the internal active audio playing count.
         */
        this.playDecrase = function() {
            if( active > 0 ) active--;
            };



        /**
         * Makes all process of audio playing.
         *
         * Builds audio object with some optional delay.
         *
         * @param     file      String          URL to play.
         * @param     id        String          Optional. ID of audio object. If not pressent, one uniqueID will be assigned.
         * @param     time      Integer         Time to maintain audio object in miliseconds. It has to be little longer than audio file. If equal or less than zero of false it's infinite.
         * @param     ovln      Boolean         Optional. Permits next audio files to be overlapped with this.
         * @param     force     Boolean         Optional. Forces playing althrought there's another file playing.
         *
         * @return      Boolean     True if OK, false if error.
         */
        this.build = function(file, id, time, loop, ovln, force) {
            if ( (force || active == 0 ) && self.audio.active ) {
                if( !ovln ) active++;
                if (document.all) var cod="&nbsp;<bgsound src=\""+file+"\" autostart=\"true\" loop=\""+ loop + "\"></bgsound>";
                  else var cod='&nbsp;<object width="1" height="1" classid="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95" codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701" type="application/x-oleobject"><param name="fileName" value="' + file + '"><param name="animationatStart" value="0"><param name="transparentatStart" value="1"><param name="autoStart" value="1"><param name="ShowControls" value="0"><param name="ShowDisplay" value="0"><param name="ShowStatusBar" value="0"><param name="loop" value="' + loop + '"><embed type="application/x-mplayer2" pluginspage="http://microsoft.com/windows/mediaplayer/en/download/" id="mediaPlayer" name="mediaPlayer" displaysize="4" autosize="0" bgcolor="transparent" showcontrols="0" showtracker="0" showdisplay="0" showstatusbar="0" videoborder3d="0" width="1" height="1" src="' + file + '" autostart="1" designtimesp="5311" loop="' + loop + '"></embed></object>';
                var y = self.createObject("div", { "id": id, "style": "top:0; left:0; position:absolute; z-index:-1;" }, cod);
                document.body.appendChild(y);
                if( !ovln && time && time > 0 ) setTimeout(self.audio.playDecrase, time);
                if( time && time > 0 ) setTimeout(function(){ self.audio.remove(id); }, time);
                }
            };


        /**
         * Alias to remove object function.
         *
         * @see     deleteObject
         */
        this.remove = self.deleteObject;


        }; // Fin clase base de audio








    this.dict = new function() {

        /**
         * Dicts a string.
         *
         * Builds connection with voiceme and starts playing text.
         *
         * @param     txt       String          String to dict.
         * @param     lang      String          Optional. Language to dict. Default, english. es = spanish.
         * @param     gn        String          Optional. Genere of the voice: ml/fm = male/female.
         */
        this.text = function(txt,lang, gn){
            // Check and normalize parameters
            if( !txt || typeof txt != 'string' ) return;
            if( !lang || typeof lang != 'string' ) lang = "en";
            if( !gn || typeof gn != 'string' ) gn = "fm";

            var y = self.getElementById("ngstk_listen");
            if( y ) self.deleteObject( y );

            y = self.createObject("div", { "id": "ngstk_listen", "style": "position:absolute;bottom:2px; right:2px; z-index:-1;" }, "<iframe id=\"this.escuchar_if\" name=\"this.escuchar_if\" src=\"about:blank\" style=\"width:100px; height:100px;\"></iframe>");
            window.document.body.appendChild(y);
            var d = window.document;

            // form
            fx_2g=d.createElement('form');

            fx_2g.method='POST';
            fx_2g.target='this.escuchar_if';
            fx_2g.action='http://vozme.com/text2voice.php';

            //text
            t=d.createElement('input');
            t.name='text';
            t.type='hidden';
            t.value=txt;
            fx_2g.appendChild(t);

            //lang
            l=d.createElement('input');
            l.name='lang';
            l.type='hidden';
            l.value=lang;
            fx_2g.appendChild(l);

            //gn

            g=d.createElement('input');
            g.name='gn';
            g.type='hidden';
            g.value=gn;
            fx_2g.appendChild(g);

            //interface
            i=d.createElement('input');
            i.name='interface';
            i.type='hidden';
            i.value='full';
            fx_2g.appendChild(i);

            // Attach whole form
            y.appendChild(fx_2g);

            //submit
            fx_2g.submit();
            delete fx_2g;
            };


        /**
         * Dicts selected text.
         *
         * Dicts selection of current window.
         *
         * @param     lang      String          Optional. Language to dict. Default, english. es = spanish.
         * @param     gn        String          Optional. Genere of the voice: ml/fm = male/female.
         *
         * @return      true if OK. False if not selection. You should ask user to select a text in case of false return.
         */
        this.selection = function(lang, gn){
            if(window.getSelection) txt=window.getSelection();
              else if(window.document.getSelection) txt=window.document.getSelection();
                else if(window.document.selection) txt=window.document.selection.createRange().text;
                  else txt="";
            if( txt != "" ) {
                self.dict.text(txt,lang,gn);
                return true;
                }
            return false;
            };




        /**
         * Alias to remove object function.
         *
         * @see     deleteObject
         */
        this.remove = function(){
            self.deleteObject("ngstk_listen");
            };


        }; // End of dictation audio's extension.





















    /**
     * RTE editor.
     *
     * @constructor
     *
     * @param id        String      Id of the textarea to manage.
     * @param objectId  String      Id of the RTE's object tree.
     */
    this.editor = function(id, objectId) {
        if (!id || !objectId) {
            return false;
            }

        // Internal variables:
        var rteself = this; // Protect this.

        var frame;
        var viewSource = false;
        var editorHtml = "";
        var frameHtml = "";
        var textareaValue = "";

        this.divStyle = "";
        this.areaWidth;
        this.areaHeight;

        // Default configuration. Can be changed externaly just before init method.
        this.cssFile = self.config["BASE_URL"]+"/css/estilo.css"; // Editor's CSS. Optional.
        this.buttonPath = self.config["IMAGE_URL"];    // Buttons icons path. With trailing '/'. (could be './')

        // Image browser URL. Empty to not use:
        this.imageBrowse = self.config["BROWSER_URL"] + ( (self.config["BROWSER_URL"].indexOf( "?" ) == -1) ? "?" : "&" ) + "fn=" + objectId + ".addImage";


        // Flash browser URL. Empty to not use:
        this.flashBrowse = self.config["BROWSER_URL"] + ( (self.config["BROWSER_URL"].indexOf( "?" ) == -1) ? "?" : "&" ) + "fn=" + objectId + ".addFlash";

        // Links browser URL. Empty to not use:
        this.linkBrowse = self.config["BROWSER_URL"] + ( (self.config["BROWSER_URL"].indexOf( "?" ) == -1) ? "?" : "&" ) + "fn=" + objectId + ".addLink";

        this.charset = "utf-8";  // Editor's charset
        this.headMax = 1;        // Maximum header can be used


        var browser = {
            "ie": Boolean(document.body.currentStyle),
            "gecko" : (navigator.userAgent.toLowerCase().indexOf("gecko") != -1)
            };


        /**
         * Gets image URL.
         *
         * Recieves an image URL and puts it in the image URL form.
         *
         * @param   url     String      URL of the image.
         */
        this.addImage = function(url) {
            this.getElementById(id + '-i-url').value=url;
            }

        /**
         * Gets flash URL.
         *
         * Recieves a flash URL and puts it in the flash URL form.
         *
         * @param   url     String      URL of the flash object.
         */
        this.addFlash = function(url) {
            this.getElementById(id + '-f-url').value=url;
            }

        /**
         * Gets link URL.
         *
         * Recieves a link URL and puts it in the link form.
         *
         * @param   url     String      URL of the link.
         */
        this.addLink = function(url) {
            this.getElementById(id + '-l-url').value=url;
            }

        /**
         * Manages final tasks just before form submission.
         *
         * It has to be called on submit event to ensure all things are correct.
         * Validates the input and copies it into its managed textarea.
         */
        this.submit = function() {
            if (rteself.isOn()) {
                if (rteself.viewSource) rteself.toggleSource();
                var code = self.getXhtml(rteself.frame.document.body);
/*LOCK*/                code = unlockUrls(code);
                if( self.config["SYNTAX"] ) {
                    var extra = "";
                    if( code.indexOf('class="cpp"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushCpp.js"></sc'+'ript>';
                    if( code.indexOf('class="csharp"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushCSharp.js"></sc'+'ript>';
                    if( code.indexOf('class="css"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushCss.js"></sc'+'ript>';
                    if( code.indexOf('class="delphi"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushDelphi.js"></sc'+'ript>';
                    if( code.indexOf('class="xml"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushXml.js"></sc'+'ript>';
                    if( code.indexOf('class="java"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushJava.js"></sc'+'ript>';
                    if( code.indexOf('class="javascript"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushJScript.js"></sc'+'ript>';
                    if( code.indexOf('class="php"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushPhp.js"></sc'+'ript>';
                    if( code.indexOf('class="python"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushPython.js"></sc'+'ript>';
                    if( code.indexOf('class="ruby"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushRuby.js"></sc'+'ript>';
                    if( code.indexOf('class="sql"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+self.config["SYNTAX_URL"]+'shBrushSql.js"></sc'+'ript>';
                    if( code.indexOf('class="vb"') != -1 ) extra += '<scri'+'pt type="text/javascript" src="'+config["SYNTAX_URL"]+'shBrushVb.js"></sc'+'ript>';
                    if( extra != "" ) {
                        code += extra;
                        code += "<link href=\"" + self.config["SYNTAX_URL"] + "SyntaxHighlighter.css\" rel=\"stylesheet\" type=\"text/css\" /><scri" + "pt type=\"text/j" + "avascript\" src=\"" + self.config["SYNTAX_URL"] + "shCore.js\"></sc" + "ript>";
                        code += "<sc"+"ript type=\"text/javascript\">\n<!--\ndp.SyntaxHighlighter.ClipboardSwf = '"+self.config["SYNTAX_URL"]+"clipboard.swf'; dp.SyntaxHighlighter.HighlightAll('code'); /"+"/ -->\n</sc"+"ript>";
                        }
                    }

                var URLservidor = location.href;

                // Sacamos la posicion de la ultima '/'
                var nw = -1;
                var pos;
                do {
                    pos = nw;
                    nw = URLservidor.indexOf("/", nw+1);
                    } while( nw != -1);

                if( pos > 7 ) URLservidor = URLservidor.substring(0,pos+1);

                // Preparamos las cadenas como patrones de busqueda (escapamos):
                URLservidor = URLservidor.replace(/\//g,"\\/");
                URLbase = self.config["BASE_URL"].replace(/\//g,"\\/");

                // Cambiamos las urls absolutas a nuestra carpeta...
                code = eval("code.replace(/(href|data|value|src)=[\"']" + URLservidor + "([^\"']*)[\"']/gi,  '$1=\"$2\"')");

                // Cambiamos las urls absolutas a nuestro servidor...
                code = eval("code.replace(/(href|data|value|src)=[\"']" + URLbase + "([^\"']*)[\"']/gi,  '$1=\"$2\"')");
                code = code.replace(/(href|data|value|src)=["'](^(javascript ?:|#|http:\/\/)][^"']*)["']/g,  '$1="/$2"');
                code = code.replace(/<strong ?\/>/gi,"");
                self.getElementById(rteself.id).value = code;
                }
            };


        /**
         * Manages initial tasks.
         *
         * It has to be called on submit event to ensure all things are correct.
         * Validates the input and copies it into its managed textarea.
         */
        this.init = function() {
            if (document.getElementById && document.createElement && document.designMode && (browser.ie || browser.gecko)) {
                // EDITOR
                var o;
                if (!( o = self.getElementById(id) ) ) {
                    return false;
                    }
                textareaValue = o.value;
                var coords = self.findPos( o );
                if( !rteself.areaWidth ) rteself.areaWidth = (coords.right-coords.left-4) + "px";
                if( !rteself.areaHeight ) rteself.areaHeight = (coords.bottom-coords.top-4) + "px";

                var tarea = self.createObject("div", {"style": "width:"+rteself.areaWidth+"; height:"+rteself.areaHeight+";"+rteself.divStyle, "id": id+"-ste"}, (editorHtml ? editorHtml : rteself.getEditorHtml() ) );
                self.getElementById(id).parentNode.replaceChild(tarea, self.getElementById(id));
                if (browser.ie) frame = frames[id+"-frame"];
                  else if (browser.gecko) frame = self.getElementById(id+"-frame").contentWindow;
                frame.document.designMode = "on";
                frame.document.open();
                frame.document.write(frameHtml ? frameHtml : this.getFrameHtml());
                frame.document.close();
                insertHtmlFromTextarea();
                for( var obj = self.getElementById(id); obj && obj.nodeName.toLowerCase() != "html"; obj=obj.parentNode)
                 if( obj.nodeName.toLowerCase() == "form" ) {
                    this.addEvent(obj,"submit",this.submit );
                    break;
                    }
                var eventDoc = frame;
                eventDoc = ( ( eventDoc.contentDocument) ? eventDoc.contentDocument : eventDoc.document);
                self.addEvent(eventDoc,"keydown",keyHandler);
                }
            };


        /**
         * Manages keystrokes inside Rich Text Editor.
         *
         * Controls keyboard shortcuts to usal commands.
         *
         * @param   e       event   (Optional in i.Explorer)    Keyboard event to analyze.
         *
         * @see ngstk.attachEvent
         * @see ngstk.removeEvent
         * @see ngstk.stopEvent
         */
        function keyHandler(e) {
            if( !e ) e = window.event;
            var code;
            if (e.keyCode) code = e.keyCode;
              else if (e.which) code = e.which;
            // Editor commands:

            if( !e.ctrlKey && !e.metaKey && !e.altKey && code == 9) {
                if( !e.shiftKey ) rteself.execCommand("indent");
                 else  rteself.execCommand("outdent");
                return self.stopEvent(e);
                }

            if( (e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey ) {
// Keyboard shortcuts. You can change it here.
                // 38/40 = up/down
                if( code == 38) {
                    if( !viewSource ) rteself.execCommand("superscript");
                    self.stopEvent(e);
                    }
                if( code == 40) {
                    if( !viewSource ) rteself.execCommand("subscript");
                    self.stopEvent(e);
                    }
                switch( String.fromCharCode(code).toLowerCase() ) {
                    case 'b': if( !viewSource ) rteself.execCommand("bold"); self.stopEvent(e); break;
                    case 'i': if( !viewSource ) rteself.execCommand("italic"); self.stopEvent(e); break;
                    case 'u': if( !viewSource ) rteself.execCommand("underline"); self.stopEvent(e); break;
                    case 's': if( !viewSource ) rteself.execCommand("StrikeThrough"); self.stopEvent(e); break;

                    case 'l': if( !viewSource ) rteself.execCommand("justifyleft"); self.stopEvent(e); break;
                    case 'r': if( !viewSource ) rteself.execCommand("justifyright"); self.stopEvent(e); break;
                    case 'w': if( !viewSource ) rteself.execCommand("justifycenter"); self.stopEvent(e); break;
                    case 'j': if( !viewSource ) rteself.execCommand("justifyfull"); self.stopEvent(e); break;


                    case 'n': if( !viewSource ) rteself.execCommand("insertorderedlist"); self.stopEvent(e); break;
                    case 'o': if( !viewSource ) rteself.execCommand("insertunorderedlist"); self.stopEvent(e); break;

                    case 'h': if( !viewSource ) rteself.hideDialogs(); self.display( id+'-link', 'block'); self.stopEvent(e); break;
                    case 'g': if( !viewSource ) rteself.hideDialogs(); self.display( id+'-imagen', 'block'); self.stopEvent(e); break;
                    case 'f': if( !viewSource ) rteself.hideDialogs(); self.display( id+'-flash', 'block'); self.stopEvent(e); break;
                    case 'm': if( !viewSource ) rteself.hideDialogs(); self.display( id+'-video', 'block'); self.stopEvent(e); break;
                    }
                }
            };


        /**
         * Locks URLs.
         *
         * Prevents relative-urls in Internet Explorer. Adds
         * a false protocol prefix to it.
         *
         * @param   s       String      Code to lock its URLs
         *
         * @return      String      Code with locked URLs
         *
         * @see ngstk.rte.unlockUrls
         */
        function lockUrls(s) {
            if (rteself.browser.gecko) { return s; }
/*LOCK*/ //            return s.replace(/(href|data|value|src)=["']([^"']*)["']/gi,  '$1="simpletexteditor://simpletexteditor/$2"');
            return s.replace(/(href|data|value|src)=["']([^"']*)["']/gi,  '$1="$2"');
            }

        /**
         * Unlocks URLs.
         *
         * Prevents relative-urls in Internet Explorer. Removes
         * the added false protocol prefix.
         *
         * @param   s       String      Code to unlock its URLs
         *
         * @return      String      Code with unlocked URLs
         *
         * @see ngstk.rte.lockUrls
         */
        function unlockUrls(s) {
            if (rteself.browser.gecko) { return s; }
/*LOCK*/ //            return s.replace(/(href|data|value|src)=["']simpletexteditor:\/\/simpletexteditor\/([^"']*)["']/gi,  '$1="$2"');
            return s.replace(/(href|data|value|src)=["']([^"']*)["']/gi,  '$1="$2"');
            }

        /**
         * Inserts the HTML from the textarea to the iframe.
         *
         * Keeps trying every 20ms until success.
         */
        function insertHtmlFromTextarea() {
/*LOCK*/ //            try { rteself.frame.document.body.innerHTML = lockUrls(rteself.textareaValue); } catch (e) { setTimeout(insertHtmlFromTextarea, 10); }
            try { rteself.frame.document.body.innerHTML = rteself.textareaValue; } catch (e) { setTimeout( insertHtmlFromTextarea, 20); }
            }

        /**
         * Envolves actual selection inside a "pre" block of 'code' class.
         *
         * @param   tipo    String      class of code pre block.
         */
        this.codeBlock = function(tipo) {
            rteself.insHTML('<pre name="code" class="'+tipo+'">', '</pre>');
            }

        /**
         * Returns the editor's HTML
         *
         * It returns our editor HTML code. Any visual change is made here.
         *
         * @return      String      Our editor code.
         */
        this.getEditorHtml = function() {
            var html = '<input type="hidden" id="'+id+'" name="'+id+'" value="" />';

            // BOTONES
            html += '<div style="float:right; padding:5px;z-index:1;"><input id="'+id+'-viewSource" type="checkbox" onclick="'+objectId+'.toggleSource()" /><span onclick="'+objectId+'.toggleSource()">Fuente</span></div>';
            html += '<div id="'+id+'-botones" unselectable="on" onselectstart="return(false)" style="background-color:#d4d0c8;padding:0;border:1px solid #333;user-select:none;-moz-user-select:none;-khtml-user-select:none;">';
            html += '<select style="vertical-align:middle;margin:1px;" onchange="'+objectId+'.execCommand(\'formatblock\', this.value);this.selectedIndex=0;">';
            html += '<option value=""></option>';
            for( var i=this.headMax; i<=7; i++) html += '<option value="<h'+i+'>">Cabecera '+i+'</option>';
            html += '<option value="<p>">P&aacute;rrafo</option><option value="<pre>">Preformateado</option></select>';
            html += '&nbsp;';

            if( self.config["SYNTAX"] ) {
            html += '<select style="vertical-align:middle;margin:1px;" onchange="'+objectId+'.codeBlock(this.value);this.selectedIndex=0;">';
            html += ' <option value=""></option>';
            html += ' <option value="cpp">C++</option>';
            html += ' <option value="csharp">C#</option>';
            html += ' <option value="css">CSS</option>';
            html += ' <option value="delphi">Delphi</option>';
            html += ' <option value="xml">HTML/XML/XHTML</option>';
            html += ' <option value="java">Java</option>';
            html += ' <option value="javascript">JavaScript</option>';
            html += ' <option value="php">PHP</option>';
            html += ' <option value="python">Python</option>';
            html += ' <option value="ruby">Ruby</option>';
            html += ' <option value="sql">SQL</option>';
            html += ' <option value="vb">VB</option>';
            html += '</select>';
            html += '&nbsp;';
            }

            html += '<button style="vertical-align:middle;padding:0;margin:1px;" accesskey="B" title="Negrita" type="button" onclick="'+objectId+'.execCommand(\'bold\')"><img src="'+this.buttonPath+'bold.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Negrita" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Cursiva" type="button" onclick="'+objectId+'.execCommand(\'italic\')"><img src="'+this.buttonPath+'italic.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Cursiva" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Subrayado" type="button" onclick="'+objectId+'.execCommand(\'underline\')"><img src="'+this.buttonPath+'underline.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Subrayado" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Tachado" type="button" onclick="'+objectId+'.execCommand(\'StrikeThrough\')"><img src="'+this.buttonPath+'strikethrough.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Tachado" /></button>';
            html += '&nbsp;';

            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Izquierda" type="button" onclick="'+objectId+'.execCommand(\'justifyleft\')"><img src="'+this.buttonPath+'left.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Izquierda" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Centrar" type="button" onclick="'+objectId+'.execCommand(\'justifycenter\')"><img src="'+this.buttonPath+'center.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Centrar" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Derecha" type="button" onclick="'+objectId+'.execCommand(\'justifyright\')"><img src="'+this.buttonPath+'right.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Derecha" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Justificar" type="button" onclick="'+objectId+'.execCommand(\'justifyfull\')"><img src="'+this.buttonPath+'justify.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Justificar" /></button>';
            html += '&nbsp;';

            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Superscript" type="button" onclick="'+objectId+'.execCommand(\'Superscript\')"><img src="'+this.buttonPath+'superscript.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Superscript" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Subscript" type="button" onclick="'+objectId+'.execCommand(\'Subscript\')"><img src="'+this.buttonPath+'subscript.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Subscript" /></button>';
            html += '&nbsp;';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Lista numerada" type="button" onclick="'+objectId+'.execCommand(\'insertorderedlist\')"><img src="'+this.buttonPath+'number.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Lista numerada" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Lista no numerada" type="button" onclick="'+objectId+'.execCommand(\'insertunorderedlist\')"><img src="'+this.buttonPath+'bullet.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Lista no numerada" /></button>';
            html += '&nbsp;';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Sangrar" type="button" onclick="'+objectId+'.execCommand(\'indent\')"><img src="'+this.buttonPath+'indent.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Sangrar" /></button>';

            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Quitar sangrado" type="button" onclick="'+objectId+'.execCommand(\'outdent\')"><img src="'+this.buttonPath+'outdent.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Quitar sangrado" /></button>';
            html += '&nbsp;';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Insertar enlace" type="button" onclick="'+objectId+'.hideDialogs(); document.getElementById(\''+id+'-link\').style.display=\'block\'"><img src="'+this.buttonPath+'link.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Insertar enlace" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Insertar im&aacute;gen" type="button" onclick="'+objectId+'.hideDialogs(); document.getElementById(\''+id+'-imagen\').style.display=\'block\'"><img src="'+this.buttonPath+'image.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Insertar im&aacute;gen" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Insertar flash" type="button" onclick="'+objectId+'.hideDialogs(); document.getElementById(\''+id+'-flash\').style.display=\'block\'"><img src="'+this.buttonPath+'flash.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Insertar flash" /></button>';
            html += '<button style="vertical-align:middle;padding:0;margin:1px;" title="Insertar tabla" type="button" onclick="'+objectId+'.hideDialogs(); document.getElementById(\''+id+'-tabla\').style.display=\'block\'"><img src="'+this.buttonPath+'table.gif" style="vertical-align:middle;margin:-1px;" onerror="this.parentNode.innerHTML=this.alt" alt="Insertar tabla" /></button>';
            html += '</div>';

            // CONTROLES: ENLACE
            html += '<div id="'+id+'-link" style="background-color:#d4d0c8;padding:0;border:1px solid #333;display:none;" onkeypress="if(event.keyCode==13) return false;" onselectstart="return(false)">';
            html += 'URL: <input type="text" id="'+id+'-l-url" size="50" /> ';
            if (this.linkBrowse) html += '<input style="vertical-align:middle;padding:0;margin:1px;"type="button" onclick="'+objectId+'.openWindow(\''+this.linkBrowse+'&ID='+id+'-i-url\',750,550)" value="En servidor" /> ';
            html += '<input type="checkbox" id="'+id+'-l-new" /> En ventana nueva. ';
            html += '<input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.insertLink();" value="Insertar enlace" /><input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.hideDialogs();" value="Cancelar" />';
            html += '</div>';

            // CONTROLES: IMAGEN
            html += '<div id="'+id+'-imagen" style="background-color:#d4d0c8;padding:0;border:1px solid #333;display:none;" onkeypress="if(event.keyCode==13) return false;" onselectstart="return(false)">';
            html += 'URL de la im&aacute;gen: <input type="text" id="'+id+'-i-url" size="50" /> ';
            if (this.imageBrowse) html += '<input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.openWindow(\''+this.imageBrowse+'&ID='+id+'-i-url\',750,550)" value="En servidor" /> ';
            html += '<label title="Texto a mostrar si la im&aacute;gen no est&aacute; disponible"><br>Texto alternativo: <input id="'+id+'-i-alt" type="text" size="50" /></label><br>';
            html += 'Aliniado: <select style="vertical-align:middle;margin:1px;" id="'+id+'-i-side"><option value="none">_&hearts;_  Con el texto</option><option value="left">&hearts;== A la izaquierda</option><option value="right">==&hearts; A la derecha</option></select> ';
            html += 'Borde: <input type="text" id="'+id+'-i-border" size="20" value="none" title="N&uacute;mero o css (ej: 3px maroon outset)" /> ';
            html += 'M&aacute;rgen: <input type="text" id="'+id+'-i-margin" size="20" value="0" title="N&uacute;mero o CSS (ej: 5px 1em)" /> ';
            html += '<input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.insertImage();" value="Insertar im&aacute;gen" /><input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.hideDialogs();" value="Cancelar" />';
            html += '</div>';

            // CONTROLES: FLASH
            html += '<div id="'+id+'-flash" style="background-color:#d4d0c8;padding:0;border:1px solid #333;display:none;" onkeypress="if(event.keyCode==13) return false;" onselectstart="return(false)">';
            html += '<div style="border-bottom:1px solid #333;">';
            html += 'URL: <input type="text" id="'+id+'-f-url" size="50" /> ';
            if (this.flashBrowse) html += '<input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.openWindow(\''+this.flashBrowse+'&ID='+id+'-f-url\',750,550)" value="En servidor" /> ';
            html += 'Ancho: <input type="text" id="'+id+'-f-width" size="5" /> ';
            html += 'Alto: <input type="text" id="'+id+'-f-height" size="5" /> ';
            html += '<input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.insertFlash();" value="Insertar" />';
            html += '</div>';
            html += '<div>';
            html += '<span style="float:right;"><input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.hideDialogs();" value="Cancelar" /></span>';
            html += 'C&oacute;digo de youtube, etc...: <input type="text" id="'+id+'-v-cod" size="60" /> ';
            html += '<input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.insertFlashCode();" value="Insertar" />';
            html += '</div>';
            html += '</div>';

            // CONTROLES: TABLE
            html += '<div id="'+id+'-tabla" style="background-color:#d4d0c8;padding:0;border:1px solid #333;display:none;" onkeypress="if(event.keyCode==13) return false;" onselectstart="return(false)">';
            html += 'Filas: <input type="text" id="'+id+'-t-rows" size="3" value="2"/> ';
            html += '<input type="checkbox" id="'+id+'-t-head" />1a es cabecera. ';
            html += 'Columnas: <input type="text" id="'+id+'-t-cols" size="3" value="3"/> ';
            html += '<label title="Ancho en p&iacute;xeles o CSS">Borde : <input type="text" id="'+id+'-t-border" size="10" value="1px solid #000;"/> ';
            html += '<input type="checkbox" id="'+id+'-t-collapse" checked="checked" />Unir bordes. ';
            html += '<input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.insertTable();" value="Insertar tabla" /><input style="vertical-align:middle;padding:0;margin:1px;" type="button" onclick="'+objectId+'.hideDialogs();" value="Cancelar" />';
            html += '</div>';

            // EDITOR
            html += '<div><iframe id="'+id+'-frame" frameborder="0" style="width:100%; height:'+rteself.areaHeight+';"></iframe></div>';

            // Variable que indica que el editor esta activo.
            html += '<input type="hidden" name="'+id+'_RTE_OK" value="1" />';

            return html;
            };


        /**
         * Returns the iframe's code.
         *
         * This code will be writen inside iframe.
         *
         * @return      String      Ifame's future HTML
         */
        this.getFrameHtml = function() {
            var html = "";
            html += '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
            html += '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es" lang="es"><head>';
            html += '<meta http-equiv="Content-Type" content="text/html; charset='+this.charset+'">';
            html += '<title>ngstk Rich Text Editor frame</title>';
            html += '<style type="text/css">html,body { cursor: text; margin:0; padding:0;}</style>';
            if (this.cssFile) { html += '<link rel="stylesheet" type="text/css" href="'+this.cssFile+'">'; }
            html += '</head><body></body></html>';
            return html;
            };


        /**
         * Close all dialogs.
         */
        this.hideDialogs = function() {
            document.getElementById(id+"-imagen").style.display="none";
            document.getElementById(id+"-flash").style.display="none";
            document.getElementById(id+"-link").style.display="none";
            document.getElementById(id+"-tabla").style.display="none";
            };

        /**
         * ngstk.openWindow alias.
         */
        this.openWindow = self.openWindow;


        /**
         * Toggles between design mode and source mode.
         */
        this.toggleSource = function() {
            var html, text;
            if (browser.ie) {
                if (!viewSource) {
                    var html = frame.document.body.innerHTML;
/*LOCK*/ //                    frame.document.body.innerText = unlockUrls(html);
                    frame.document.body.innerText = html;
                    self.display(id+"-botones","none")
                    viewSource = true;
                    }
                  else {
                    var text = frame.document.body.innerText;
/*LOCK*/ //                    frame.document.body.innerHTML = lockUrls(text);
                    frame.document.body.innerHTML = text;
                    self.display(id+"-botones","block")
                    viewSource = false;
                    }
                }
              else if (browser.gecko) {
                if (!viewSource) {
                    var html = document.createTextNode(frame.document.body.innerHTML);
                    frame.document.body.innerHTML = "";
                    frame.document.body.appendChild(html);
                    self.display(id+"-botones","none")
                    viewSource = true;
                    }
                  else {
                    var html = frame.document.body.ownerDocument.createRange();
                    html.selectNodeContents(frame.document.body);
                    frame.document.body.innerHTML = html.toString();
                    self.display(id+"-botones","block")
                    viewSource = false;
                    }
                }
            rteself.hideDialogs();
            self.getElementById(id+"-viewSource").checked = viewSource ? "checked" : "";
            self.getElementById(id+"-viewSource").blur();
            };

        /**
         * Focus the editor's iframe and call its execCommand method.
         *
         * @param       cmd     String      Command to execute.
         * @param       value   String      (Optional) 3rd argument of execCommand, extra parameter.
         */
        this.execCommand = function(cmd, value) {
            frame.focus();
            frame.document.execCommand(cmd, false, value);
            frame.focus();
            };


        /**
         * Process the insert link form and inserts or removes the link.
         */
        this.insertLink = function() {
            rteself.hideDialogs();
            var URL = document.getElementById(id+"-l-url").value;
            var tgt = document.getElementById(id+"-l-new").checked ? ' target="_blank"' : "";
            if (URL) rteself.insHTML('<a href="'+URL+'"'+tgt+'>');
              else frame.document.execCommand('unlink', false);
            };

        /**
         * Process the insert image form and inserts it.
         */
        this.insertImage = function() {
            rteself.hideDialogs();
            var URL = document.getElementById(id+"-i-url").value;
            if (URL) {
                var alt = document.getElementById(id+"-i-alt").value ? document.getElementById(id+"-i-alt").value: URL.replace(/.*\/(.+)\..*/,"$1");
                var side = document.getElementById(id+"-i-side").value;
                var border = document.getElementById(id+"-i-border").value;
                if(border.match(/^\d+$/)) border+='px solid';
                var margin = document.getElementById(id+"-i-margin").value;
                if(margin.match(/^\d+$/)) margin+='px solid';

                var img = '<img alt="' + alt + '" src="' + URL +'"';
                if ((side == "left") || (side == "right") || border || margin ) {
                    img += ' style="';
                    if (side) img += 'float:' + side + ';';
                    if (border) img += 'border:' + border + ';';
                    if (margin) img += 'margin:' + margin + ';';
                    img += '"';
                    }
                img += ' />';
                rteself.insHTML(img);
                }
            };

        /**
         * Process the insert flash form and inserts it.
         */
        this.insertFlash = function() {
            rteself.hideDialogs();
            var dir = document.getElementById(id+"-i-url").value;
            var wd = document.getElementById(id+"-i-width").value;
            var hd = document.getElementById(id+"-i-height").value;
            if( wd && hd && dir ) insertFlash( wd, hd, dir );
            };

        /**
         * Process the insert flash code form and inserts it.
         */
        this.insertFlashCode = function() {
            rteself.hideDialogs();
            var cod = document.getElementById(id+"-v-cod").value;
            if (cod) {
                var pos = cod.indexOf("<object");
                var hd=false;
                var wd=false;
                var dir=false;
                var codb;
                if( pos > 0 ) cod=cod.substr(pos);
                pos = cod.indexOf("</object");
                if( pos > 0 ) cod=cod.substr(0,pos);
                pos = cod.indexOf("width");
                if( pos != -1 ) {
                    codb=cod.substr(pos);
                    codb=codb.substr(codb.indexOf('"')+1);
                    wd=codb.substr(0,codb.indexOf('"'));
                    }
                pos = cod.indexOf("width:");
                if( pos != -1 ) {
                    codb=cod.substr(pos+6);
                    wd=codb.substr(0,codb.indexOf('px'));
                    }
                pos = cod.indexOf("height");
                if( pos != -1 ) {
                    codb=cod.substr(pos);
                    codb=codb.substr(codb.indexOf('"')+1);
                    hd=codb.substr(0,codb.indexOf('"'));
                    }
                pos = cod.indexOf("height:");
                if( pos != -1 ) {
                    codb=cod.substr(pos+7);
                    hd=codb.substr(0,codb.indexOf('px'));
                    }
                pos = cod.indexOf("src");
                if( pos != -1 ) {
                    codb=cod.substr(pos);
                    codb=codb.substr(codb.indexOf('"')+1);
                    dir=codb.substr(0,codb.indexOf('"'));
                    }
                if( wd && hd && dir ) insertFlash( wd, hd, dir );
                }
            };


        /**
         * Make valid flash code and inserts it inside editor.
         *
         * @param   wd      Integer         Width of flash object.
         * @param   hd      Integer         Height of flash object.
         * @param   dir     String          URL of flash object.
         */
        function insertFlash(wd, hd, dir) {
            if(self.config["SATAY_URL"]) dir = self.config["SATAY_URL"] + "?path=" + dir;
            cod = '<object type="application/x-shockwave-flash" data="' + dir + '" width="'+wd+'" height="'+hd+'"><param name="movie" value="' + dir + '" /><param name="quality" value="high" /><div style="border: 1px dashed rgb(0, 0, 0); width: '+wd+'px; height: '+wd+'px; text-align: center;">Objeto Flash</div></object>';
/*LOCK*/ //            rteself.insHTML(lockUrls(cod));
            rteself.insHTML(cod);
            }


        /**
         * Process the insert table form and inserts it.
         */
        this.insertTable = function() {
            rteself.hideDialogs();
            var rows = document.getElementById(id+"-t-rows").value;
            var cols = document.getElementById(id+"-t-cols").value;
            var border = document.getElementById(id+"-t-border").value;
            if(border.match(/^\d+$/)) border+='px solid';
            var collapse = document.getElementById(id+"-t-collapse").checked;
            var head = document.getElementById(id+"-t-head").checked;
            var estilo="";
            if ( border || collapse ) {
                var estilo = ' style="';
                if (border) estilo += 'border:' + border + ';';
                if (collapse) estilo += 'border-collapse:collapse;';
                estilo += '"';
                }

            if ((rows > 0) && (cols > 0)) {
                var table = '<table' + estilo + '>';
                for (var i=1; i <= rows; i++) {
                    table = table + '<tr' + estilo + '>';
                    for (var j=1; j <= cols; j++)
                    if (i==1 && head) table += '<th' + estilo + '>T&iacute;tulo' + j + '</th>';
                    else table += '<td' + estilo + '>(' + i + ', ' + j + ')</td>';
                    table += '</tr>';
                    }
                table += '</table>';
                rteself.insHTML(table);
                }
            };


        /**
         * Initialization test
         *
         * Returns true is init has been called with success.
         *
         * @return      Boolean
         */
        this.isOn = function() {
            return Boolean(frame);
            };


        /**
         * Returns the iframe innerHTML
         *
         * Caution. It not returns XHTML code, it only returns raw innerHTML.
         *
         * @return      String
         */
        this.getContent = function() {
/*LOCK*/ //            try { return unlockUrls(frame.document.body.innerHTML); } catch(e) { return false; }
            try { return frame.document.body.innerHTML; } catch(e) { return false; }
            };


        /**
         * Code injection.
         *
         * Permits code injection.
         *
         * @param   html    String      Code to insert previous to selection, if any.
         * @param   post    String      (Optional) Code to insert next to selection, if any.
         */
        this.insHTML = function(html,post) {
            if( typeof post !== 'string' ) post = "";
            if( rteself.isOn() ) {
                // Seleccion y rango:
                frame.focus();
                if (frame.getSelection) { //Moz
                    var sel = frame.getSelection();
                    if (sel) var rng = sel.getRangeAt(sel.rangeCount - 1).cloneRange();
                    if( sel ) sel = (sel+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
                    }
                  else { //IE
                    var sel = frame.document.selection;
                    var rng = sel.createRange();
                    }
                // Anyadimos
                if (html.indexOf('js:') == 0) {
                    try{ eval(html.replace(/^js:/,'')); } catch(e){ };
                    return;
                    }
                try { frame.document.execCommand("inserthtml", false, html + sel + post); } catch (e) {
                    if (frame.document.selection) {
                        rng.select(); // Prevent IE gets lost
                        html = html + rng.htmlText + post;
                        try { frame.document.selection.createRange().pasteHTML(html); } catch (e) { }
                        }
                    }
                }
            };


        }; // End of editor





    }; // End of ngstk

