/****************************************************************************
* SKCD  > skSelection                                                       *
*---------------------------------------------------------------------------*
* skSelection's goal is to obtain easily the text selected from a document  *
* (in our mozilla based application) under two formats: html (as much as    *
* possible) and unicode.                                                    *
*---------------------------------------------------------------------------*
* (c) IDM. 2003                                                             *
* Project  : sk                                                             *
* Language : javascript                                                     *
* Created  : MikaelLebrun, 14/02/2003                                       *
****************************************************************************/
const A_STYLE = new Array("border",
                          "margin",
                          "padding",
                          "clip",
                          "background-attachment",
                          "background-color",
                          "background-image",
                          "background-repeat",
                          "border-collapse",
                          "bottom",
                          "caption-side",
                          "clear",
                          "color",
                          "cursor",
                          "direction",
                          "display",
                          "empty-cells",
                          "float",
                          "font-family",
                          "font-size",
                          "font-size-adjust",
                          "font-style",
                          "font-variant",
                          "font-weight",
                          "height",
                          "left",
                          "letter-spacing",
                          "line-height",
                          "list-style-image",
                          "list-style-position",
                          "list-style-type",
                          "max-height",
                          "max-width",
                          "min-height",
                          "min-width",
                          "overflow",
                          "position",
                          "right",
                          "table-layout",
                          "text-align",
                          "text-decoration",
                          "text-indent",
                          "text-transform",
                          "top",
                          "unicode-bidi",
                          "vertical-align",
                          "visibility",
                          "white-space",
                          "width",
                          "word-spacing",
                          "z-index"
                          );

//skSelection Object
function skSelection() {
    this.skiDownload = Components.classes["@idm.fr/sk_download;1"].createInstance().QueryInterface(Components.interfaces.skIDownload);
    this.nbPicture = 1;
}

skSelection.prototype = {
    QueryInterface:function(iid) {
        if (!iid.equals(Components.interfaces.skISelection)
            &&!iid.equals(Components.interfaces.nsISupports)) {
            throw Components.results.NS_ERROR_NO_INTERFACE;
        }
        return this;
    },
    
    /*********************************************************************************************
    FUNCTION : GetUnicode()
    This function return the unicode string corresponding to the text selection in the window passed
    in parameter
    ****INPUT: a window object [pxiWindow - the window in which there's a selection]
    ****OUPUT: a string [the unicode string]
    ********************************************************************************************/
    GetUnicode:function(pxiWindow) {
        return pxiWindow.getSelection().toString();
    },


    GetUnicodeNode:function(pxiNode, pxiWindow) {
        /*Create range to select node*/
        var range2=pxiWindow.document.createRange();
        range2.selectNodeContents(pxiNode);
        return range2.toString();
    },
    /*********************************************************************************************
    FUNCTION : GetHtml()
    This function return the html string corresponding to the text selection in the window passed
    in parameter
    ****INPUT: 1/ a window object [pxiWindow - the window in which there's a selection]
               2/ a string [psSavePath - the path of the directory in which downloaded files will be saved]
    ****OUPUT: a string [the html string]
    ********************************************************************************************/
    GetHtml:function(pxiWindow, psSavePath, pbReplaceClass) {
            this.selWindow = pxiWindow;
            this.savePath = psSavePath;
            var sSelection = this.selWindow.getSelection();
            var xiRange = sSelection.getRangeAt(sSelection.rangeCount-1).cloneRange();
            if (xiRange.collapsed) {
                return ""; //Nothing is selected !
            }
            //Convert the DOM XML into a string
            var xoDomSerializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"].getService(Components.interfaces.nsIDOMSerializer);
            var xoDomDocumentFragment = xiRange.cloneContents();
            var xoDomNodeList = xoDomDocumentFragment.childNodes;
            var sRangeSource = "";
            var i, xoDomNode;
            for (i=0; i<xoDomNodeList.length; i++) {
                xoDomNode = xoDomNodeList.item(i);
                //this.selWindow.alert(xoDomNode);
                if (xoDomNode.nodeName == "#text") {
                    sRangeSource += xoDomNode.nodeValue;
                } else {
                    this.searchImgInDOM(xoDomNode); //Search pictures in the range
                    if(pbReplaceClass) {
                        this.replaceClass(xoDomNode);
                    }
                    if(xoDomNode.nodeType != 8)
                        sRangeSource += xoDomSerializer.serializeToString(xoDomNode);
                }
            }
            xiRange.detach();
            return sRangeSource;
    },

    GetHtmlNode:function(pxiNode, psSavePath, pbReplaceClass) {
        try {
            this.selWindow = pxiNode.ownerDocument.defaultView
            this.savePath = psSavePath;
            //Convert the DOM XML into a string
            var xoDomSerializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"].getService(Components.interfaces.nsIDOMSerializer);
            var xoDomNodeList = pxiNode.childNodes;
            var sRangeSource = "";
            var i, xoDomNode;
            for (i=0; i<xoDomNodeList.length; i++) {
                xoDomNode = xoDomNodeList.item(i);
                if (xoDomNode.nodeName == "#text") {
                    sRangeSource += xoDomNode.nodeValue;
                } else {
                    this.searchImgInDOM(xoDomNode); //Search pictures in the range
                    if(pbReplaceClass) {
                        this.replaceClass(xoDomNode);
                    }
                    if(xoDomNode.nodeType != 8)
                        sRangeSource += xoDomSerializer.serializeToString(xoDomNode);
                }
            }
//             xiRange.detach();
            
            //dump(sRangeSource);
            return sRangeSource;
        } catch(err) { 
            dump(err);
            return ""; 
        }
    },

    GetHtmlDocument:function(pxiNode, psSavePath) {
        try {
            this.savePath = psSavePath;
            //Convert the DOM XML into a string
            var xoDomSerializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"].getService(Components.interfaces.nsIDOMSerializer);
            var xoDomNodeList = pxiNode.childNodes;
            var sRangeSource = "";
            var i, xoDomNode;
            for (i=0; i<xoDomNodeList.length; i++) {
                xoDomNode = xoDomNodeList.item(i);
                if (xoDomNode.nodeName == "#text") {
                    sRangeSource += xoDomNode.nodeValue;
                } else {
                    this.searchImgInDOM(xoDomNode); //Search pictures in the range
                    this.replaceClass(xoDomNode);
                    if(xoDomNode.nodeType != 8)
                        sRangeSource += xoDomSerializer.serializeToString(xoDomNode);
                }
            }
//             xiRange.detach();
            
            return sRangeSource;
        } catch(err) { 
            return ""; 
        }
    },

    /*********************************************************************************************
    FUNCTION : searchImgInDOM()
    This function search tags IMG in the DOM to modify its location with the new picture location 
    (previously downloaded in the user directory) 
    ****INPUT: a node object [poDomNode - the node in which we search picture nodes]
    ****OUPUT: NOTHING
    ********************************************************************************************/
    searchImgInDOM:function(poDomNode) {
        var i, sFilename;
        /*Init the count of picture in poDomNode*/
        var oxL_File = Components.classes["@mozilla.org/file/local;1"].createInstance().QueryInterface(Components.interfaces.nsILocalFile);
        var oxMyProfile = Components.classes["@mozilla.org/profile/manager;1"].createInstance().QueryInterface(Components.interfaces.nsIProfileInternal);
        var sPathProfile = oxMyProfile.getProfileDir(oxMyProfile.currentProfile); 
        oxL_File.initWithFile(sPathProfile);
        oxL_File.appendRelativePath(this.savePath);
        if (!oxL_File.exists())
            this.nbPicture = 1;
        else {
            var oxImg = oxL_File.clone().QueryInterface(Components.interfaces.nsILocalFile);
            var iIndice=1;
            var FilesList = oxImg.directoryEntries;
            if (FilesList.hasMoreElements()) {
                while(FilesList.getNext())
                    iIndice ++;
            }
            this.nbPicture = iIndice;
        }
        if (poDomNode.nodeName.toLowerCase() == "img" || poDomNode.nodeName.toLowerCase() == "embed") {
            sFilename = this.giveImgName(poDomNode);
            if (this.skiDownload.Get(poDomNode.src, sFilename, this.savePath)) {
                sFilename = this.giveTmpFileLocation(sFilename, this.savePath);
                poDomNode.setAttribute('src', sFilename); 
                this.nbPicture++;
            } else {
                //If the download of the file have failed - give the absolute path to the picture
                poDomNode.setAttribute('src', poDomNode.src); 
            }
        }
        //Parse the current node searching pictures tags (to copy them and replace the source property)
        if (!poDomNode.getElementsByTagName)
            return;

        var tags = new Array('img', 'embed');
        for(var j=0;j<tags.length;j++){
            var oPictureList = poDomNode.getElementsByTagName(tags[j]);
            if (oPictureList.length > 0) {
                for (i=0; i<oPictureList.length; i++) {
                    var oChildNode = oPictureList.item(i);
                    sFilename = this.giveImgName(oChildNode);
                    if (this.skiDownload.Get(oChildNode.src, sFilename, this.savePath)) {
                        sFilename = this.giveTmpFileLocation(sFilename, this.savePath);
                        oChildNode.setAttribute('src', sFilename);
                        this.nbPicture++;
                    } else {
                        //If the download of the file have failed - give the absolute path to the picture
                        oChildNode.setAttribute('src', oChildNode.src); 
                    }
                }
            }
        }
    },

    /*********************************************************************************************
    FUNCTION : giveImgName()
    This function build the name of a temporary picture file (it gives a unique name in order to copy
    a lot of pictures in the same directory).
    ****INPUT: a node object [poDomNode - the picture node]
    ****OUPUT: the picture name
    ********************************************************************************************/
    giveImgName:function(poDomNode) {
        var sName = poDomNode.getAttribute('src');
        var end = sName.length;
        if(sName.lastIndexOf('?') != -1){
          end = sName.lastIndexOf('?');
        }else if(sName.lastIndexOf('#') != -1){
          end = sName.lastIndexOf('#');
        }
        var name = sName.slice(sName.lastIndexOf('/')+1, end);
        var parts = name.split('.');
        sName = parts[0] + "_" + this.nbPicture + "." + parts[1];
        return sName;
    },

    /*********************************************************************************************
    FUNCTION : replaceClass()
    This function replace the class property by a set of style properties for the given node.
    ****INPUT: a node object [poNode - the node for which we want to translate the class property 
                              into a style property]
    ****OUPUT: NOTHING
    ********************************************************************************************/
    replaceClass:function(poNode) {
        try { 
            var sNodeClass, oComptStyle, sCompStyle, oChildList,  j, k;
            //If the element poNode have an attribute 'class', we add its contents into the style propery
            // + TEST if there is a global class on the current element (for example table{...} ) #### TO DO LATER... ?
            if (poNode.nodeType != 1)
                return;
            oComptStyle = this.selWindow.getComputedStyle(poNode, '');
            sCompStyle = "";
            //Build a string containing all properties from the class of the current element
            for (j=0; j<oComptStyle.length; j++) {
                if(this.testStyleProperty(oComptStyle.item(j)))
                    sCompStyle += oComptStyle.item(j) + ":" + oComptStyle.getPropertyValue(oComptStyle.item(j)) + ";" ;
            }
            if (poNode.attributes.getNamedItem("style"))
            poNode.attributes.getNamedItem("style").nodeValue = sCompStyle;
            else
            poNode.setAttribute('style', sCompStyle);
            // Parse all children nodes
            oChildList = poNode.childNodes;
            
            for (k=0; k<oChildList.length; k++) {
                this.replaceClass(oChildList[k]);
            }
        } catch(err) { dump(err); return false; }
    },

    /*********************************************************************************************
    FUNCTION : testStyleProperty()
    This function test if the style property passed in argument is contained in the style list A_STYLE.
    ****INPUT: a string [psProperty - the style property to search in the style list]
    ****OUPUT: a boolean
    ********************************************************************************************/
    testStyleProperty:function(psProperty) {
        var i=0;
        while (i < A_STYLE.length) {
            if(A_STYLE[i] == psProperty){ return true; }
            i++;
        }
        return false;
    },

    /*********************************************************************************************
    FUNCTION : giveTmpFileLocation()
    This function return the location of a downloaded file.
    ****INPUT: 1/ a string [psFileName - the filename of the file for which we want retrieve 
                            the location]
               2/ a string [psSavePath - the path of the directory in which the file is saved]
    ****OUPUT: a string [the file location]
    ********************************************************************************************/
    giveTmpFileLocation:function(psFileName, psSavePath) {
        var xoFileLocator = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
        var xoFile = xoFileLocator.get("ProfD", Components.interfaces.nsIFile);
        //Append the directory path to the location of the profile path
        if (psSavePath) {
            var aProfileDir = psSavePath.split("/");
            for (var i=0; i<aProfileDir.length; i++) {
                xoFile.append(aProfileDir[i]);
                if (!xoFile.exists())
                    xoFile.create(xoFile.DIRECTORY_TYPE, 0700);
            }
        }
        xoFile.append(psFileName);
        //Adapt the location to the OS
        var xiFilespec = Components.classes["@mozilla.org/filespec;1"].createInstance().QueryInterface(Components.interfaces.nsIFileSpec);
        xiFilespec.nativePath = xoFile.path;
        return xiFilespec.URLString;
    }

    /*
    prompt:function(psTitle, psText) {
        var xiPromptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].createInstance().QueryInterface(Components.interfaces.nsIPromptService);
        xiPromptService.alert(this.selWindow, psTitle, psText);
    }
    */
}

var skSelectionModule = {
    registerSelf: function(compMgr, fileSpec, location, type) {
        compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(this.myCID,
                                        "SK Selection Component",
                                        this.myProgID, 
                                        fileSpec,
                                        location,
                                        type);
    },

    getClassObject: function(compMgr, cid, iid) {
        if (!cid.equals(this.myCID))
            throw Components.results.NS_ERROR_NO_INTERFACE;
        if (!iid.equals(Components.interfaces.nsIFactory))
            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
        return this.myFactory;
    },

    myCID: Components.ID("4e810b4b-fbb6-401c-a21a-d0c17840246b"),

    myProgID: "@idm.fr/sk_selection;1",

    myFactory: {
        createInstance: function(outer, iid) {
            if (outer != null)
                throw Components.results.NS_ERROR_NO_AGGREGATION;
            return (new skSelection()).QueryInterface(iid);
        }
    },

    canUnload: function(compMgr) {
        return true;
    }
};

function NSGetModule(compMgr, fileSpec) { return skSelectionModule; }
