#include "translations/i18n.jsx";

//les fichiers xml produits suite à la transformation tei2indd sont stockées
//dans le répertoire .indd (sous répertoire du répertoire XML)
var XML_AID_HIDDEN_REPOSITORY= ".indd";

var BOOK_MODE = "book_mode";
var DOC_MODE = "doc_mode";

//par défaut: mode d'importation document.
var IMPORT_MODE = DOC_MODE;

//style de bloc à appliquer aux images libres
var HEAP_IMAGES_STYLE = "bloc_icono";

//séparateur répertoire filesystem
var sepChemin = (File.fs == "Windows")  ? "\\" : "/";
//répertoire d'installation 'xslt-transformations'
var rep_tools = File($.fileName).parent.parent.fsName + sepChemin + "xslt-transformations" + sepChemin;
//répertoire des script pour stockage uiPalette
var rep_script = File($.fileName).parent.parent.fsName + sepChemin;
//paths des 4 xslt
var retro = rep_tools + "commons_2_metopes.xsl";
var palette_xsl = rep_tools + "buildUI.xslt";
var prev_xsl = rep_tools + "prev_tei2indd.xslt";
var last_xsl  = rep_tools +  "tei2indd.xslt" ;
//osx use
var saxon_path = rep_tools + sepChemin + "saxon9.jar";
//widows use
var transform_path = rep_tools + "saxon9b" + sepChemin+ "bin" + sepChemin + "Transform.exe";
//format de cross ref.
var lastCrossRefFormat = null;

var fbat0 = new File(File($.fileName).parent.fsName + sepChemin + "cmdTransform0.bat");
var fbat = new File(File($.fileName).parent.fsName + sepChemin + "cmdTransform.bat");

var IMAGE_RESIZE_VAL = 1;
var DEFAULT_P_STYLE_INDEX = 1;

var logger = null;
var pBar = null;
var myStory = null;
var myDocument =null;
var countNotes = 0;
var defaultPStyleName = null;

var textVariables = new Array()

//builds correct path for command line parameters
function makePlateformFileSystemPath(initialPath, escapedSeparator) {
    return (initialPath.split(" ").join(escapedSeparator));
}

//return true if indesign "client" version  false is server one
function isInDesignClient() {
    return app.name.indexOf("InDesign Server") == -1;
}



function importationXML(nouveauXML, maquette, options, nameXML) {
    var differentIndexName = false;

    logger.log(i18n('logimport') + nouveauXML + i18n('logIn') + maquette.fsName);

    // Ouvrir la maquette
    var noeudAfficher = options["noeudAfficher"];
    var appelFonction = options["appelFonction"];
    myDocument = app.open(File(maquette));

    defaultPStyleName = myDocument.paragraphStyles.item(DEFAULT_P_STYLE_INDEX);
    //Gestion des colonnes du style "paragraphe standard" on stocke les valeurs de la maquette et on passe en une colonne
    var indesignVersion = app.version.substring(0, 1);
    if (indesignVersion > 6)
    {
        if (myDocument.paragraphStyles.item(DEFAULT_P_STYLE_INDEX).spanColumnType != "SINGLE_COLUMN")
        {
            var originalColumnsSpan = myDocument.paragraphStyles.item(DEFAULT_P_STYLE_INDEX).spanColumnType;
            var originalColumnsSpanNum = myDocument.paragraphStyles.item(DEFAULT_P_STYLE_INDEX).spanSplitColumnCount;
            myDocument.paragraphStyles.item(DEFAULT_P_STYLE_INDEX).spanColumnType = SpanColumnTypeOptions.SINGLE_COLUMN;
        }
    }

    //logging des options d'import
    logger.log(i18n('logFig') + options["figuresMode"]);
    logger.cr();
    logger.log(i18n('logFigTitle'));
    if (options["figureVert"]) {
        logger.log(i18n('logFigbelow'));
    }
    else {
        logger.log(i18n('logFigright'));
    }
    logger.log(i18n('logTable') + options["table"]);
    logger.cr();
    logger.log(i18n('logAuthor') + options["authorLocation"]);
    logger.cr();
    logger.log(i18n('logDates') + options["datesLocation"]);
    logger.cr();
    logger.log(i18n('logAbsract') + options["withAbstract"]);
    logger.cr();
    logger.log(i18n('logLang') + options["lang"]);
    logger.cr();
    logger.log(i18n('logMeta') + options["meta"]);
    logger.cr();
    logger.log(i18n('logIconoPath') + options["iconoPath"]);
    logger.cr();
    logger.log(i18n('logResolution') + options["resolution"]);
    logger.cr();
    logger.log(i18n('logAppendix') + options["appendixLocation"]);
    logger.cr();
    logger.log(i18n('logBiography') + options["withLocation"]);
    logger.cr();
    logger.log(i18n('logChange') + options["commons"]);
    logger.cr();
    
    /*--FIN LOGGING des OPTIONS*/

    myDocument.importXML(File(nouveauXML));

    var docRoot = myDocument.xmlElements.item(0)

    //avec gestion des namespaces
    var ns = new Array();
    ns.push("tei")
    ns.push("http://www.tei-c.org/ns/1.0")
    var nsTab = new Array()
    nsTab.push(ns)
    var shortTitleTab = docRoot.evaluateXPathExpression("//tei:title[@type='short']",nsTab)
    var partTitleTab = docRoot.evaluateXPathExpression("//tei:title[@type='part']",nsTab)

    //sans namespaces, plus bricolage, à conserver le temps des tests
    //var shortTitleTab = docRoot.evaluateXPathExpression("//*[name()='title'][@type='short']")
    if (shortTitleTab.length > 0) {
	try{
            stv = myDocument.textVariables.item('title-short')
            stv.name // génére une exception si tv n'est pas ds la maquette, auquel cas il faudra l'ajouter
        }
        catch(e){
	    myDocument.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: 'title-short'})
	    stv = myDocument.textVariables.item('title-short')
        }
	
	stv.variableOptions.contents = shortTitleTab[0].contents
    }
    
    //titre de partie
    if (partTitleTab.length > 0) {
	try{
            stv = myDocument.textVariables.item('title-part')
            stv.name // génére une exception si tv n'est pas ds la maquette, auquel cas il faudra l'ajouter
        }
        catch(e){
	    myDocument.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: 'title-part'})
	    stv = myDocument.textVariables.item('title-part')
        }
	
	stv.variableOptions.contents = partTitleTab[0].contents
    }
    
    //ajout des éventuelles variables de texte préalablement collectées (volume)
    for(var i = 0; i < textVariables.length; i++){
        var tvName = textVariables[i].name
        var tv
        try{
            tv = myDocument.textVariables.item(tvName)
            tv.name // génére une exception si tv n'est pas ds la maquette, auquel cas il faudra l'ajouter
        }
        catch(e){
            myDocument.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: tvName})
        }


        //var tv = myDocument.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: textVariables[i].name})
        tv.variableOptions.contents = textVariables[i].variableOptions.contents

    }

    pBar = new ProgressBar(i18n('importXML') + nameXML);
    var myRuleSet = new Array(new AfficherElement(noeudAfficher));

    try {
        __processRuleSet(myDocument.associatedXMLElement, myRuleSet);
    }
    catch (e) {
        logger.alert("core / fonction importationXML" + e);
    }

    var countItalique = new TagCounter("//hi[@rend='italic']");
    var indexErrors;
    var countIndex = new CountIndex();
    var countErreurIndex = 0;

    __processRuleSet(myStory.associatedXMLElement, new Array(countItalique, new CountNotes, countIndex));

    pBar.reset(i18n('ital'), countItalique.count);
    __processRuleSet(myStory.associatedXMLElement, new Array(new Italique("//hi[@rend='italic']", pBar)));
    
    if (countIndex.countIndex > 0) {
        differentIndexName = false;
        var my_index = myDocument.indexes.add();
        var verif = new VerifIndexation;
        logger.log(i18n('checkindex'));
        __processRuleSet(myStory.associatedXMLElement, new Array(verif));
        differentIndexName = verif.differentIndexName;
        if (!differentIndexName) {
            if (isInDesignClient()){
                var messageUniqueIndexName = i18n('indexNamemsg');
                logger.log("\t" + messageUniqueIndexName);
                var myDialog = app.dialogs.add({name: messageUniqueIndexName, canCancel: true});
                myDialog.dialogColumns.add().borderPanels.add().staticTexts.add({staticLabel: i18n('indexNamequestion')});
                differentIndexName = !myDialog.show();
                if (differentIndexName) {
                    logger.log(i18n('indexNameNOdelete'));
                }
                else {
                    logger.log(i18n('indexNamedelete'));
                }
            }
            else{
                // *********************** Indesign Server ***********************
                differentIndexName = options["differentIndexName"] == undefined ? true : options["differentIndexName"];
           }
        }
        logger.cr();
    }

    // Impossible de savoir à l'avance combien de pages il faudra ajouter
    // mais il y a environ 2500 points d'insertions par page. (très aléatoire).
    pBar.reset(i18n('pagesprocess'), parseInt(myStory.insertionPoints.length / 2500));

    try {
        if (myStory.allGraphics.length > 0) {
            // Redimensionnement des tableaux sur les pages déjà créées
            images = myStory.allGraphics;
            var tabGraphics = new Array();

            //On force la réduction de taille des images pour assurer leur affichage
            for (var z = 0; z < images.length; z++) {

                // retaille de l'image
                images[z].resize(
                        CoordinateSpaces.INNER_COORDINATES,
                        AnchorPoint.CENTER_ANCHOR,
                        ResizeMethods.REPLACING_CURRENT_DIMENSIONS_WITH,
                        [IMAGE_RESIZE_VAL, IMAGE_RESIZE_VAL]);

                //retaille du parent (Rectangle) de l'image
                images[z].parent.resize(
                        CoordinateSpaces.INNER_COORDINATES,
                        AnchorPoint.CENTER_ANCHOR,
                        ResizeMethods.REPLACING_CURRENT_DIMENSIONS_WITH,
                        [IMAGE_RESIZE_VAL, IMAGE_RESIZE_VAL]);
                logger.log(images[z].horizontalScale + " - " + images[z].verticalScale);

            }
        }
        if (myStory.tables.length > 0) {
            // Règle tous les problèmes liés à la taille du contenu des tableaux
            var top = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[0];
            var bottom = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[2];
            var hTextFrame = bottom - top;
            // FIN Règle tous les problèmes liés à la taille du contenu des tableaux
            myStory.tables.everyItem().cells.everyItem().maximumHeight = hTextFrame;
            for (var cptTab = 0; cptTab < myStory.tables.length; cptTab++) {
                if (myStory.tables.item(cptTab).allGraphics == 0) {
                    try {
                        myStory.tables.item(cptTab).rows.everyItem(0).width = 10;
                    }
                    catch (err) {
                    }
                }
            }
        }

        ajoutPage(pBar);
        //Redimensionnement/Rétablissement de la taille des images
        logger.cr();
        logger.log(i18n('logimgprocess'));
        imageResizer(myDocument, options["figuresMode"]);

        ajoutPage(pBar);

        logger.log(i18n('logcrossref'));
        //gestion des ref. croisées.
        crossReferencer(myDocument, noeudAfficher);

        if (countIndex.countIndex > 0) {
            pBar.reset(i18n('logindexprocess'), countIndex.countIndex);

            logger.log(i18n('logTableMove'));
            var arrayInsertTab = new Array();
            for (var i = 0; i < myStory.tables.length; i++) {
                arrayInsertTab[i] = (myStory.tables.item(0)).storyOffset;
                c = resolve(
                        myStory.tables.item(0).storyOffset.toSpecifier().replace('insertion-point', 'character'));
                c.move(LocationOptions.AFTER, myStory.insertionPoints.lastItem());
            }

            var indexation = new Indexation(my_index, countIndex.errors, pBar, differentIndexName);
            __processRuleSet(myStory.associatedXMLElement, new Array(indexation));

            indexErrors = indexation.errors;

            logger.log(i18n('logTableReplace'))
            for (var i = 0; i < myStory.tables.length; i++) {
                c = resolve(
                        myStory.tables.item(i).storyOffset.toSpecifier().replace('insertion-point', 'character'));
                c.move(LocationOptions.AT_BEGINNING, arrayInsertTab[i]);
            }

        }

        //test activation de liens
        __processRuleSet(myStory.associatedXMLElement, new Array(new hyperlinksActivator(myDocument)));

        //traitement des surcharges locales au niveau des caractères
        __processRuleSet(myStory.associatedXMLElement, new Array(new gestionSurcharge(myDocument, "//hi[@style]")));

        //traitement des surcharges au niveau des paragraphes
        __processRuleSet(myStory.associatedXMLElement, new Array(new gestionSurcharge(myDocument, "//*[@rend]")));

        //traitement des surcharges des cellules de tableaux
        __processRuleSet(myStory.associatedXMLElement, new Array(new surchargeCellules(myDocument)));

        if (countNotes > 0) {
            pBar.reset(i18n('notesProcess'), countNotes);
            //création du style txt_Note si inexistant
            
            try{
                (myDocument.paragraphStyles.item('txt_Note')).name;
            }
            catch(e){
                var txt_Note = myDocument.paragraphStyles.item(1).duplicate();//1 -> parag. standard
                txt_Note.name = 'txt_Note';
                myDocument.paragraphStyles.add(txt_Note);
                logger.info('"txt_Note" inexistant -> Création et ajout du style');
            }
            //création du style txt_Note_suite si inexistant
            try {
                (myDocument.paragraphStyles.item('txt_Note_Suite')).name;
            }
            catch (e) {
                var txt_Note_suite = myDocument.paragraphStyles.item('txt_Note').duplicate();
                txt_Note_suite.name = 'txt_Note_Suite';
                myDocument.paragraphStyles.add(txt_Note_suite);
                logger.info('"txt_Note_suite" inexistant -> Création et ajout du style');
            }
	    var optionsNotesTab = options["notesTables"]
            __processRuleSet(myStory.associatedXMLElement, new Array(new PlacerNoteBasDePage("//note[@place='foot']", optionsNotesTab, pBar)));
	    __processRuleSet(myStory.associatedXMLElement, new Array(new PlacerNoteDeFin("//note[@place='end-indd']", pBar)));
        }

        try {
            if (myStory.tables.length > 0) {
                var arrayTab = new Array();
                logger.log(i18n('logTableRedim'));
                __processRuleSet(myStory.associatedXMLElement, new Array(new RedimensionnerTableau));
                logger.cr();
            }
        }
        catch (err) {
            logger.alert(i18n('logTableRedimFail') + err)
        }

        logger.log(i18n('logColumn'));
        //restauration du nombre de colonnes initial
        if (indesignVersion > 6) {
            myDocument.paragraphStyles.item(DEFAULT_P_STYLE_INDEX).spanColumnType = originalColumnsSpan;
            myDocument.paragraphStyles.item(DEFAULT_P_STYLE_INDEX).spanSplitColumnCount = originalColumnsSpanNum;
        }

        //recomposition des pages
        myDocument.recompose();

        return finScript(indexErrors, countIndex);

    }
    catch (err) {
        logger.alert(err + ", line = " + err.line + "-" + err.fileName)
    }
}





    /* ********************************************** */
    /* ***************** XML Rules ******************* */
    /* ********************************************** */


    function AfficherElement(noeud) {
        this.name = "AfficherElement";
        this.xpath = noeud;
        this.apply = function (myElement, myRuleProcessor) {
            try {
                myElement.placeXML(myDocument.pages.item(0).textFrames.item(0));
                myStory = myElement.parentStory;
            }
            catch (e) {
                logger.alert("core / function AfficherElement :" + e)
            }
        }
    }


// Compteur de balise note pour la progress bar
    function CountNotes() {
        this.name = "CountNotes";
        this.xpath = "//note";
        this.apply = function (myElement, myRuleProcessor) {
            with (myElement) {
                countNotes++;
            }
            return true;
        }
    }
// Compteur de balise hi pour la progress bar
    function CountItalique() {
        this.name = "CountItalique";
        this.xpath = "//hi[@rend='italic']";
        this.apply = function (myElement, myRuleProcessor) {
            with (myElement) {
                countItalique++;
            }
            return true;
        }
    }

// Afficher correctement les tableaux
    function RedimensionnerTableau() {
        this.name = "RedimensionnerTableau";
        this.xpath = "//Tableau";
        this.top = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[0];
        this.left = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[1];
        this.bottom = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[2];
        this.right = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[3];
        this.textFrameWidth = this.right - this.left;
        this.textFrameHeight = this.bottom - this.top;
        this.numTable = 0;
        this.apply = function (myElement, myRuleProcessor) {
            __skipChildren(myRuleProcessor);
            var styleTab = true;
            var styleNom = "";
            try {
                styleNom = myElement.xmlAttributes.item("aid5:tablestyle").value;
            }
            catch (e) {
                styleTab = false;
            }
            if (styleTab && styleNom == "tableau-exemple") {
                var nbCol = myElement.tables.item(0).columnCount;

                var tailleCol = (this.textFrameWidth - 12) / (nbCol - 1);
                myElement.tables.everyItem().columns.everyItem().width = tailleCol;
                myElement.tables.everyItem().columns.item(0).width = 12;
            }
            if (styleTab && styleNom == "tableau-exemple-commons") {
                var nbCol = myElement.tables.item(0).columnCount;

                var tailleCol = (this.textFrameWidth - 12) / (nbCol - 1);
                myElement.tables.everyItem().columns.everyItem().width = tailleCol;
                myElement.tables.everyItem().columns.item(0).width = 12;
            }
            else {
                var nbCol = myElement.tables.everyItem().columnCount;
                var tailleCol = this.textFrameWidth / nbCol;
                myElement.tables.everyItem().columns.everyItem().width = tailleCol;
            }
            this.numTable++;

            return true;
        }
    }



// Supprime les pages ajoutées en trop (au cours du traitement, le texte prend moins de place (car suppression des index)
    function supPagesVidesFin() {
        try {
            var i = myDocument.pages.count();
            try {
                if (myDocument.pages.item(i - 1).textFrames.item(0).characters.length == 1) {
                    myDocument.pages.item(i - 1).textFrames.item(0).characters.item(0).associatedXMLElements.markupTag.name;
                }
            }
            catch (e) {
                if (myDocument.pages.item(i - 1).textFrames.item(0).characters.item(0).appliedParagraphStyle.name != defaultPStyleName) {
                    myDocument.pages.item(i - 1).textFrames.item(0).characters.item(0).appliedParagraphStyle = myDocument.paragraphStyles.item(defaultPStyleName);
                }
            }

            while (myDocument.pages.item(i - 1).textFrames.item(0).contents == "") {
                myDocument.pages.item(i - 1).remove();
                i--;
            }
        }
        catch (err) {
            logger.alert(i18n('logDeletePages') + err);
        }
    }

// Fonction utilisée pour redimensionner les images après qu'elles aient été réduites
    function redimImage(graphic, numGraphic) {
        try {//alert("redimImage");
            var hRectangle = graphic.parent.geometricBounds[2] - graphic.parent.geometricBounds[0];
            var top = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[0];
            var bottom = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[2];
            var hTextFrame = bottom - top;
            var hOrigine = hRectangle * 100 / percent;
            var newHauteur = hTextFrame - 30;
            // Structure d'un graphic dans un tableau :
            // graphic.parent = Rectangle
            // graphic.parent.parent = Character
            // graphic.parent.parent.parent = Cell
            //graphic.parent.parent.parent.maximumHeight = hTextFrame;
            if (hOrigine < newHauteur) {
                graphic.parent.horizontalScale = percent * 100;
                graphic.parent.verticalScale = percent * 100;
                tabGraphics[numGraphic]["pourcent"] = 100;
            }
            else {
                var newpercent = Math.floor(newHauteur * 100 / hOrigine);// -1; // => sécurité puisque les variables sont des float
                graphic.parent.horizontalScale = newpercent * percent;
                graphic.parent.verticalScale = newpercent * percent;
                tabGraphics[numGraphic]["pourcent"] = newpercent;
            }
        }
        catch (e) {
            logger.alert(i18n('logImgRedim') + (numGraphic + 1) + " - " + e + " - line " + e.line);
        }
    }


    function finScript(indexErrors, countIndex) {
        try {
            ajoutPage(pBar);
            supPagesVidesFin();
            logger.log(i18n('logindexverif'));
            if (indexErrors != null && indexErrors.length > 0) {
                var phrase1 = indexErrors.length.length + i18n('logindexpb1');
                var phrase2 = i18n('logindexpb2');
                var strErreur = phrase1 + "\n" + phrase2 + "\n";
                logger.log("\t" + phrase1);
                logger.log("\t" + phrase2);
                logger.log(i18n('logindexpb3'));
                logger.log(i18n('logindexpb4'));
                for ($z = 0; $z < indexErrors.length; $z++)
                {
                    logger.log("\t" + indexErrors[$z] + "\n");
                }
                if (indexErrors.length > 0) {
                    logger.alert(i18n('logindexpb5'));
                }
            }
            else {
                logger.log(i18n('logindexok'));
            }

            logger.cr();
            var my_index = myDocument.indexes.everyItem().update();
            try {
                niveau = new Array(0, 0, 0, 0);
                pageRef1 = "";
                pageRef2 = "";
                pageRef3 = "";
                pageRef4 = "";
                pageRef1 = myDocument.indexes.item(0).topics.everyItem().pageReferences.everyItem().getElements();
                if (pageRef1.length != 0)
                    niveau[0] = 1;
                pageRef2 = myDocument.indexes.item(0).topics.everyItem().topics.everyItem().pageReferences.everyItem().getElements();
                if (pageRef2.length != 0)
                    niveau[1] = 1;
                pageRef3 = myDocument.indexes.item(0).topics.everyItem().topics.everyItem().topics.everyItem().pageReferences.everyItem().getElements();
                if (pageRef3.length != 0)
                    niveau[2] = 1;
                pageRef4 = myDocument.indexes.item(0).topics.everyItem().topics.everyItem().topics.everyItem().topics.everyItem().pageReferences.everyItem().getElements();
                if (pageRef4.length != 0)
                    niveau[3] = 1;
                // obligation de passer dans le catch donc affectation d'un objet inexistant à une variable
                pageRef5 = myDocument.indexes.item(0).topics.everyItem().topics.everyItem().topics.everyItem().topics.everyItem().topics.everyItem().pageReferences.everyItem().getElements();
            }
            catch (e) {
                var nbIndex = pageRef1.length + pageRef2.length + pageRef3.length + pageRef4.length;
            }
            if (isInDesignClient())
                myDocument.pages.item(0).textFrames.item(0).insertionPoints.item(0).select();
            pBar.close();
            // Fin du script
            var str = i18n('scriptEnd');

            if (countIndex.countIndex > 0 && (countIndex.countIndex != nbIndex)) {
                str += i18n('indexNumber')
                        + countIndex.countIndex + i18n('indexnbindoc') + nbIndex + i18n('indexlost') + (countIndex.countIndex - nbIndex) + i18n('indexword');
            }
            logger.log(i18n('logIndexImport'));
            logger.log(i18n('logIndexTags') + countIndex.countIndex);
            logger.log(i18n('logIndexTagLost') + nbIndex);
            logger.log(i18n('logIndexTagLost') + (countIndex.countIndex - nbIndex) + i18n('indexword'));
            logger.cr();
            if (myStory.allGraphics > 0) {
                logger.log(i18n('logImgRedim'));
                for (var i = 0; i < tabGraphics.length; i++) {
                    if (tabGraphics[i]["pourcent"] != "100") {
                        var percent = 100 - tabGraphics[i]["pourcent"];
                        str = str + i18n('image') + tabGraphics[i]["nom"] + i18n('imageReduction') + percent + "%.";
                        logger.log(i18n('logImage') + tabGraphics[i]["nom"] + i18n('imageReduction') + percent + "%.");
                    }
                }
            }
            logger.log(i18n('scriptEnd'));
            logger.close();
        }
        catch (err) {
            return err;
        }
        return str;
    }


// Mettre les notes en bas de pages
function PlacerNoteBasDePage(xpath, optionsNotesTab, pBar) {
        this.name = "PlacerNoteBasDePage";
        this.xpath = xpath; //"//note[@place='foot']";
        this.n = 0;
        this.apply = function (myElement, myRuleProcessor) {
            with (myElement) {
                //s'il manque des pages....
                if (myDocument.pages.lastItem().textFrames.item(0).overflows) {
                    if(isInDesignClient())
                        myDocument.pages.lastItem().textFrames.item(0).select();
                    ajoutPage(pBar);
                }
                this.n++;
                if (this.n % 1000 == 0) {
                    $.gc();
                }
                __skipChildren(myRuleProcessor);
                var bCell = false;
                var parentXML = myElement.parent;
                // On cherche si la note est dans un tableau.
                // Boucle car possibilité d'avoir une note dans un hi ou autre.
                while (!bCell) {
                    if (parentXML.markupTag.name == "Cellule") {
                        bCell = true;
                    }
                    else {
                        parentXML = parentXML.parent;
                    }
                    if (parentXML.markupTag.name == "body") {
                        break;
                    }
                }
                if (!bCell) { // Si la note n'est pas dans un tableau 
                    //$.writeln("traitement de la note " + myElement.xmlAttributes.item('n').value)
                    var myTextFrame = null
                    var myChar = null
                    var nbTry = 0
                    while(myChar === null && nbTry < 100){
                        try{
                            myTextFrame = myElement.insertionPoints[0].parentTextFrames[0]
                            myChar = myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index - 2);
                        }
                        catch(err){
                             myChar = null
                             //$.writeln('sleep...')
                             $.sleep(100)
                             nbTry ++
                        }

                    }
                    //var myChar = myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index - 2);
                    var myFootnote = myChar.insertionPoints.item(0).footnotes.add();
                    myElement.texts.item(0).move(LocationOptions.AT_END, myFootnote.texts.item(0));
                    //txt_Note_Suite pour tous les paragraphes de la note après le premier
                    for (i = 1; i < myFootnote.paragraphs.length; i++)
                    {
                        //myFootnote.paragraphs.item(i).appliedParagraphStyle = myDocument.paragraphStyles.item("txt_Note_Suite");
			myFootnote.paragraphs.item(i).applyParagraphStyle(myDocument.paragraphStyles.item("txt_Note_Suite"),false);
                    }
                    // Supprimer de les sauts de lignes suivants et précédents
                    myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index + 1).remove();
                    myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index - 2).remove();
                    myElement.remove();
                }
		else if (bCell && optionsNotesTab=='footnotes'){ //Si la note est dans un tableau et que la version d'indesign le gère
		    myChar = myElement.insertionPoints[0].parent.characters.item(myElement.insertionPoints[0].index - 2);
                    var myFootnote = myChar.insertionPoints.item(0).footnotes.add();
                    myElement.texts.item(0).move(LocationOptions.AT_END, myFootnote.texts.item(0));
                    //txt_Note_Suite pour tous les paragraphes de la note après le premier
                    for (i = 1; i < myFootnote.paragraphs.length; i++)
                    {
                        myFootnote.paragraphs.item(i).appliedParagraphStyle =
                                myDocument.paragraphStyles.item("txt_Note_Suite");
                    }
                    // Supprimer de les sauts de lignes suivants et précédents
                    myElement.insertionPoints[0].parent.characters.item(myElement.insertionPoints[0].index + 1).remove();
                    myElement.insertionPoints[0].parent.characters.item(myElement.insertionPoints[0].index - 2).remove();
                    myElement.remove();
		}
//                else { // Si la note est dans un tableau. Indesign ne gère pas les notes dans les tableaux
//		    alert("dans le else")
//                }
                pBar.hit(); // progress bar
            }
            return true;
        }
    }

// Mettre les notes de fin de document
function PlacerNoteDeFin(xpath, pBar) {
        this.name = "PlacerNoteDeFin";
        this.xpath = xpath; //"//note[@place='end-indd']";
        this.n = 0;
        this.apply = function (myElement, myRuleProcessor) {
            with (myElement) {
                //s'il manque des pages....
                if (myDocument.pages.lastItem().textFrames.item(0).overflows) {
                    if(isInDesignClient())
                        myDocument.pages.lastItem().textFrames.item(0).select();
                    ajoutPage(pBar);
                }
                this.n++;
                if (this.n % 1000 == 0) {
                    $.gc();
                }
                __skipChildren(myRuleProcessor);
                var bCell = false;
                var parentXML = myElement.parent;
                // On cherche si la note est dans un tableau.
                // Boucle car possibilité d'avoir une note dans un hi ou autre.
                while (!bCell) {
                    if (parentXML.markupTag.name == "Cellule") {
                        bCell = true;
                    }
                    else {
                        parentXML = parentXML.parent;
                    }
                    if (parentXML.markupTag.name == "body") {
                        break;
                    }
                }
                if (!bCell) { // Si la note n'est pas dans un tableau 
                    //$.writeln("traitement de la note " + myElement.xmlAttributes.item('n').value)
                    var myTextFrame = null
                    var myChar = null
                    var nbTry = 0
                    while(myChar === null && nbTry < 100){
                        try{
                            myTextFrame = myElement.insertionPoints[0].parentTextFrames[0]
                            myChar = myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index - 1);
                        }
                        catch(err){
                             myChar = null
                             //$.writeln('sleep...')
                             $.sleep(100)
                             nbTry ++
                        }

                    }
                    //var myChar = myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index - 2);
                    var myEndnote = myChar.insertionPoints.item(0).createEndnote();
                    myElement.texts.item(0).move(LocationOptions.AT_END, myEndnote.texts.item(0));
                    //txt_Note_Suite pour tous les paragraphes de la note après le premier
                    /*for (i = 1; i < myEndnote.paragraphs.length; i++)
                    {
                        myEndnote.paragraphs.item(i).appliedParagraphStyle =
                                myDocument.paragraphStyles.item("txt_Note_Suite");
                    }*/
                    // Supprimer de les sauts de lignes suivants et précédents
                    myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index + 1).remove();
                    myTextFrame.parentStory.characters.item(myElement.insertionPoints[0].index - 1).remove();
                    myElement.remove();
                }
                pBar.hit(); // progress bar
            }
            return true;
        }
    }


//tag counter  according to an xpath
    function TagCounter(xpth)
    {
        this.name = "TagCounter";
        this.xpath = xpth;
        this.count = 0;
        this.apply = function (myElement, myRuleProcessor)
        {
            this.count++;
            return true;
        }
    }

// Compteur de balise index pour la progress bar
function CountIndex() {
    this.name = "CountIndex";
    this.xpath = "//index";
    this.countIndex = 0;
    this.errors = new Array();
    this.apply = function(myElement, myRuleProcessor){
        with(myElement){
            this.countIndex++;
            // Vérification que la balise index est placée au bon endroit :
            // Nous avons eu des exemples où certains index étaient placés après la fin de paragraphe.
            // Exemple : <p>...terme à indexer</p><index...
            // Dans ce cas, le marqueur d'index est placé sur le premier mot de la prochaine unité de type paragraphe (ou titre, ou autre)
            try {
                var nomTag = myElement.parent.markupTag.name;
                if (nomTag != "hi" && nomTag != "p" && nomTag != "head" && nomTag != "q") {
                    //myElement.parent.xmlAttributes.item("aid:pstyle").value;
                    if(!hasAncestor(myElement,"aid:pstyle"))
                        this.errors.push(new Array(this.countIndex, myElement.contents));
                }
            }
            catch(e) {
                logger.alert("CountIndex error" + e);
            }
            // Fin vérification
        }
        return true;

    }
}

// Pour savoir si l'attribut est toujours le même et pouvoir donc le supprimer dans l'index
// A supprimer ?
    function VerifIndexation() {
        this.name = "VerifIndexation";
        this.xpath = "//index";
        this.attribut = "";
        this.first = true;
        this.differentIndexName = false;
        this.apply = function (myElement, myRuleProcessor) {
            with (myElement) {
                try {
                    myAttr = myElement.xmlAttributes.item("indexName").value;
                    tabTopicName = myAttr.split(":");
                    attrN1 = tabTopicName[0];
                    if (!this.first) {
                        if (attrN1 != this.attribut) {
                            this.differentIndexName = true;
                        }
                    }
                    else {
                        this.first = false;
                        this.attribut = attrN1;
                    }
                }
                catch (e) {
                }
            }
            return true;
        }
    }

// Gérer les index
    function Indexation(my_index, errTab, pBar, differentIndexName) {
        this.name = "Indexation";
        this.xpath = "//index";
        this.errors = new Array();
        this.n = 0;
        this.apply = function (myElement, myRuleProcessor) {
            __skipChildren(myRuleProcessor);
            with (myElement) {
                this.n++;
                if (myDocument.pages.lastItem().textFrames.item(0).overflows) {
                    if (isInDesignClient())
                        myDocument.pages.lastItem().textFrames.item(0).select();
                    ajoutPage(pBar);
                }
                if (myElement.contents == null) {
                    doc = myDocument;
                    xmlTag = doc.xmlTags.item("term");
                    term = myElement.xmlElements.add(xmlTag);
                    term.contents = "EMPTY ENTRY";
                }
                else
                {
                    if (this.n % 50 == 0)
                        $.gc();
                    // Vérification de la présence de l'attribut indexName
                    try {
                        myAttr = myElement.xmlAttributes.item("indexName").value;
                        tabTopicName = myAttr.split(":");
                        var varTopic = my_index;
                        if (differentIndexName) {
                            for (var i = 0; i < tabTopicName.length; i++) {
                                varTopic = varTopic.topics.add(tabTopicName[i]);
                            }
                        }
                    }
                    catch (e) {
                        var varTopic = my_index;
                    }
                    myTopicName = myElement.xmlElements.item(0).contents;
                    myTopic = varTopic.topics.add(myTopicName);
                    var decalage_index = 2;
                    if (myElement.parent.markupTag.name == "Cellule") {
                        // Dans le cas d'un index dans un tableau, il faut  prendre en compte les points d'insertion de la cellule, pas de myStory
                        var myPosition = myElement.parent.texts.item(0).insertionPoints.item(myElement.xmlElements.item(0).insertionPoints.item(0).index - decalage_index).texts.item(0);
                    }
                    else if (myElement.parent.parent.markupTag.name == "Cellule")
                    {
                        var myPosition = myElement.parent.parent.texts.item(0).insertionPoints.item(myElement.xmlElements.item(0).insertionPoints.item(0).index - decalage_index).texts.item(0);
                    }
                    else {
                        // Si un index est immédiatement suivi par une note, il faut le décaler pour empêcher la suppression
                        if (myElement.parent.xmlElements.length > myElement.index + 1) {
                            if (myElement.parent.xmlElements.nextItem(myElement).markupTag.name == "note") {
                                var sep = myElement.parent.xmlElements.nextItem(myElement).texts.item(0).insertionPoints.item(0).index - myElement.insertionPoints.item(-1).index;
                                if (sep == 3) {
                                    decalage_index = 3;
                                }
                            }
                        }
                        var myPosition = myElement.parentStory.insertionPoints.item(myElement.xmlElements.item(0).insertionPoints.item(0).index - decalage_index).texts.item(0);
                    }
                    myElement.remove();
                    var myPR = myTopic.pageReferences.add(myPosition);
                    for (var cptI = 0; cptI < errTab.length; cptI++) {
                        if (this.n == errTab[cptI][0]) {
                            //alert("erreur");
                            numPage = myPR.sourceText.insertionPoints[0].parentTextFrames[0].parent.name;
                            detailIndexErreur = i18n('indexErrorPage') + numPage + i18n('indexErrorName') + errTab[cptI][1];
                            this.errors.push(detailIndexErreur);
                            //errTab[cptI][2] = myPR;
                        }
                    }
                    try {
                        myPR.sourceText.contents;
                    }
                    catch (e) {
                        logger.alert(i18n('logIndexErrorDeletion') + myTopicName + " " + myPosition.associatedXMLElements[0].markupTag.name);
                    }
                    pBar.hit(); // progress bar
                }
            }
            return true;
        }
    }



    function CollectAttributeNames(tagName) {

        this.name = "CollectAttributeNames";
        this.xpath = "//" + tagName;
        this.names = new Array();
        this.names.push(ALL_ATTRIBUTES);
        this.apply = function (myElement, myRuleProcessor)
        {

            for (i = 0; i < myElement.xmlAttributes.length; i++)
            {
                this.names.push(myElement.xmlAttributes.item(i).name);
            }
            return true;
        }

    }

    /**
     * collects all attribute for a specified tagName
     **/
    function CollectAttributeValues(tagName, attrName) {

        this.name = "CollectAttributeNames";
        this.xpath = "//" + tagName + "[@" + attrName + "]";
        this.values = new Array();
        this.values.push(ALL_VALUES);
        this.apply = function (myElement, myRuleProcessor)
        {
            this.values.push(myElement.xmlAttributes.itemByName(attrName).value);
            return true;
        }

    }

    /**
     * collects all elements according to a specified xpath
     **/
    function CollectElementsByXPath(xpath) {

        this.name = "CollectElementsByXPath";
        this.xpath = xpath;
        this.values = new Array();
        //this.values.push(ALL_VALUES);
        this.apply = function (myElement, myRuleProcessor)
        {
            this.values.push(myElement);
            return true;
        }

    }

//add the "unique" function to the Array's prototype
    Array.prototype.unique = function () {
        var r = new Array();
        o:for (var i = 0, n = this.length; i < n; i++)
        {
            for (var x = 0, y = r.length; x < y; x++)
            {
                if (r[x] == this[i])
                {
                    continue o;
                }
            }
            r[r.length] = this[i];
        }
        return r;
    }

//test if element as an ancestor with a specified attribute
    function hasAncestor(elem, attr) {
        while (elem.parent != null) {
            elem = elem.parent;
            if (elem.xmlAttributes.item("aid:pstyle") != null)
                return true;
        }
        return false;
    }



// Ajoute des pages tant qu'il y a trop de contenu
    function ajoutPage(pBar) {
        if (!isInDesignClient())
            return;
        myDocument.viewPreferences.rulerOrigin = RulerOrigin.pageOrigin;
        var top = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[0];
        var bottom = myDocument.pages.lastItem().textFrames.item(0).geometricBounds[2];
        if (myDocument.documentPreferences.facingPages) {
            var leftImpair = myDocument.pages.item(1).textFrames.item(0).geometricBounds[1];
            var leftPair = myDocument.pages.item(2).textFrames.item(0).geometricBounds[1];
            var rightImpair = myDocument.pages.item(1).textFrames.item(0).geometricBounds[3];
            var rightPair = myDocument.pages.item(2).textFrames.item(0).geometricBounds[3];
        }
        else {
            var leftImpair = myDocument.pages.item(0).textFrames.item(0).geometricBounds[1];
            var rightImpair = myDocument.pages.item(0).textFrames.item(0).geometricBounds[3];
        }
        var i = myDocument.pages.count();
        var limit = 150;
        // Vérification que le  script n'a pas planté : Si un objet prend trop de place les pages s'ajouteront indéfiniment
        while (myDocument.pages.lastItem().textFrames.item(0).overflows) {
            var continuerAjoutPage = "";
            if (i == limit) {
                if (isInDesignClient()) {
                    var myDialog = app.dialogs.add({name: i18n('caution'), canCancel: true});
                    with (myDialog) {
                        with (dialogColumns.add()) {
                            with (borderPanels.add()) {
                                staticTexts.add({staticLabel: i18n('already') + limit + i18n('questionFollow')});
                            }
                        }
                    }
                    var continuerAjoutPage = myDialog.show();
                }
                else {
                    //sur serveur, on stoppe l'ajout des pages quand on atteint la limite
                    var continuerAjoutPage = false;
                    this.logger.warn(i18n('stopaddingpages') + limit.toString() + i18n('pages'));
                }
            }
            var myNewPage = myDocument.pages.add();
            if (myNewPage.side == PageSideOptions.rightHand) {
                var left = leftPair;
                var right = rightPair;
            }
            else {
                var left = leftImpair;
                var right = rightImpair;
            }
            // Fait le lien entre les 2 pages (la précédente et celle venant d'être créée)
            var myTextFrameC = myNewPage.textFrames.add({geometricBounds: [top, left, bottom, right]});
            myTextFrameC.previousTextFrame = myDocument.pages.item(i - 1).textFrames.item(0);
            i++;
            if (continuerAjoutPage === true) {
                limit *= 2;
            }
            else if (continuerAjoutPage === false) {
                break;
            }
            if (pBar != null)
                pBar.hit(); // progress bar
        }
    }

    function inddFilter(myFile) {
        return (myFile.constructor.name == "Folder") ||
                (myFile.name.slice(-5) == ".indd") ||
                (myFile.name.slice(-5) == ".indt") ||
                (myFile.alias) ||
                (myFile.created === null) // pour les dossiers qui ne sont pas sur le disque
                ;
    }


    function xmlFilter(myFile) {
        return (myFile.constructor.name === "Folder") ||
                (myFile.name.slice(-4) === ".xml") ||
                (myFile.alias) ||
                (myFile.created === null) // pour les dossiers qui ne sont pas sur le disque
                ;
    }

    /************************Références croisées************************************/

    /**
     *Ref croisées pointant vers un autre doc. Cette fonction ne peut être
     *appelée que lorsque que les targets ont été posées.
     */
    function ExternalCrossReferenceRefs(doc, format) {
        this.name = "CrossedReferenceRefs";
        this.xpath = "//ref[@type='crossref']";
        this.doc = doc;
        this.sep = (File.fs == "Windows") ? "\\" : "/";
        this.apply = function (myElement, myRuleProcessor)
        {
            var id = myElement.xmlAttributes.item("target").value;
            var tab = id.split("#");
            if (tab[0].indexOf(".xml") != -1) {
                try {
                    //alert("Référence croisées entre document détectée - traitement actuellement impossible (" +doc.name +" - " + id +")");
                    var book = app.books[0];
                    var targetDocPath = doc.filePath + this.sep + tab[0].substr(0, tab[0].indexOf(".xml")) + ".indd";
                    var targetDoc = app.open(File(targetDocPath));
                    var id = "#" + tab[1];
                    var target = targetDoc.hyperlinkTextDestinations.item(id);
                    var xRefForm = this.doc.crossReferenceFormats.item(format);

                    var source = this.doc.crossReferenceSources.add(myElement.insertionPoints.lastItem(), xRefForm);
                    var myLink = this.doc.hyperlinks.add(source, target);
                    return true;
                }
                catch (e) {
                    logger.alert(i18n('crossrefExt') + e);
                }
            }
        }
    }

    /*
     *Réf. croisées à l'échelle du même doc.
     */
    function CrossedReferenceRefs(doc, format) {
        this.name = "CrossedReferenceRefs";
        this.xpath = "//ref[@type]";
        this.doc = doc;
        this.apply = function (myElement, myRuleProcessor)
        {

            try {
                var id = myElement.xmlAttributes.item("target").value;
                var type = myElement.xmlAttributes.item("type").value;
                if (type == "Extlink" || type == "biography" || type == "affiliation" || type == "bibl" || type == "book") { // on ne traite ici que les types='link' et type='crossref'
                    return true;
                }
                var tab = id.split("#");
                if (tab[0].indexOf(".xml") != -1) {
                    //cas géré au niveau du livre.

                    return true;
                }
                else {
                    try {
                        var target = this.doc.hyperlinkTextDestinations.item(id);
                        var source;
                        if (type == "crossref") {
                            var xRefForm = this.doc.crossReferenceFormats.item(format);
                            var source = this.doc.crossReferenceSources.add(myElement.insertionPoints.lastItem(), xRefForm);
                        }
                        else {//inner links (@type='link')
                            var source = this.doc.hyperlinkTextSources.add(myElement.texts[0]);
                        }
                        var myLink = this.doc.hyperlinks.add(source, target);
                    }
                    catch (e) {
                        logger.alert(e + " - " + e.line + "    - source=" + myElement.texts[0] +"/target="+id);
                    }
                    return true;
                }
            }
            catch (e) {
                // TODO gestion catch moins rugueuse
                //logger.alert("crossref error  - " + e + " - " + e.line)
            }
        }
    }


      function CrossedReferenceAnchors(doc) {
        this.name = "CrossedReferenceAnchors";
        this.xpath = "//anchor";
        this.doc = doc;
        this.apply = function (myElement, myRuleProcessor)
        {
	    var nbTry = 0;
	    try {
                var targetID = myElement.xmlAttributes.item("id").value;
                var item = null;
                while (item === null && nbTry < 2500) {//2500 = 25 seconds
                    try {
                        item = myElement.insertionPoints[0].texts.firstItem();
                        nbTry ++;
                    }
                    catch (err) {
                        item = null;
                        $.sleep(10);
                    }

                }
                var linkTarget = this.doc.hyperlinkTextDestinations.add(item, {name: "#" + targetID});
            }
            catch (err) {
                logger.alert("CrossReferenceAnchors error - " + err + "(" + err.line + ") - NB try = " + nbTry);
            }
            return true;
        }
    }

    function crossedReferencesPane(doc) {
        try {

            /*
             *On demande quel format de réf. croisée à appliquer (par une boîte de dialogue)
             */
            if (lastCrossRefFormat == null) {//toujours vrai pour l'import d'un doc simple, dans le cas du livre, vrai uniquement pr le premier doc du livre

                if (isInDesignClient()) {
                    var crossRefList = new Array();
                    for (var i = 0; i < doc.crossReferenceFormats.length; i++) {
                        crossRefList[i] = doc.crossReferenceFormats[i].name;
                    }

                    //titre de la boîte de dial. différent selon le contexte: échelle du doc ou du livre.
                    var title = i18n('crossref');


                    var myDialog = app.dialogs.add({name: title});
                    with (myDialog.dialogColumns.add().dialogRows.add()) {
                        staticTexts.add({staticLabel: i18n('crossrefFormat')});
                        var formatDropDown = dropdowns.add({stringList: crossRefList, selectedIndex: 0});
                    }

                    if (myDialog.show() == true) {
                        lastCrossRefFormat = crossRefList[formatDropDown.selectedIndex];
                        myDialog.destroy();
                    }
                    else {
                        myDialog.destroy();
                        return null;
                    }
                }
                else {
                    // *********************** Indesign Server ***********************
                    lastCrossRefFormat = "CrossRef";    // Note : Utiliser un attribut afin de gérer plusieurs formats ???

                    // Si le format n'existe pas on le crée
                    if(doc.crossReferenceFormats.itemByName(lastCrossRefFormat).isValid == false) {
                        doc.crossReferenceFormats.add(lastCrossRefFormat).buildingBlocks.add(BuildingBlockTypes.PARAGRAPH_NUMBER_BUILDING_BLOCK);
                    }
                }
            }//fin format == null

            //on distingue le cas livre du cas document
            //dans le cas document, il faut traiter les cibles dans un premier tps
            if (IMPORT_MODE == DOC_MODE) {
                var anchorRuleSet = new Array(new CrossedReferenceAnchors(doc));
                var refRuleSet = new Array(new CrossedReferenceRefs(doc, lastCrossRefFormat));
                __processRuleSet(doc.associatedXMLElement, anchorRuleSet);
                __processRuleSet(doc.associatedXMLElement, refRuleSet);
                return lastCrossRefFormat;
            }
            //dans le cas du livre, les cibles ont déjà été traitées au niveau de l'import des docs, il ne reste qu'a gérer les refs.
            else {
                __processRuleSet(doc.associatedXMLElement, new Array(new ExternalCrossReferenceRefs(doc, lastCrossRefFormat)));
                doc.close(SaveOptions.YES);
                return lastCrossRefFormat
            }

        }
        catch (e) {
            logger.alert(i18n('crossrefError') + e + i18n('line') + e.line);
        }

    }



    function crossReferencer(aDocument, nodePath) {

        var crossRefCounter;
        if (nodePath == null)
            nodePath = "";
        if (IMPORT_MODE == DOC_MODE){
            crossRefCounter = new TagCounter(nodePath + "//anchor");
        }
        else{ //dans le cas d'un livre, à ce niveau, les cibles ont déjà été placées
            crossRefCounter = new TagCounter(nodePath + "//ref[@type='crossref']");
        }
        var crossRuleSet = new Array(crossRefCounter);
        __processRuleSet(aDocument.associatedXMLElement, crossRuleSet);
        //s'il en existe, on affiche alors une boîte de dialogue demandant le format
        //à appliquer

        if (crossRefCounter.count > 0) {
            return crossedReferencesPane(aDocument);
        }
    }

//hyperliens
    function hyperlinksActivator(doc) {
        this.name = "hyperlinksActivator";
        this.xpath = "//ref[@type='Extlink']";
        this.doc = doc;
        this.apply = function (elem, myRuleProcessor)
        {
            if (elem.texts.length > 0) // test un peu faible?
            {
                var elemText = elem.texts[0];
                var linkURL = elem.xmlAttributes.itemByName("target").value;
                var linkSource = this.doc.hyperlinkTextSources.add(elemText);
                var linkDest = this.doc.hyperlinkURLDestinations.add(linkURL);
                this.doc.hyperlinks.add(linkSource, linkDest);
            }
            return true;
        }
    }

//surcharge
    function gestionSurcharge(doc, myXpath) {
        this.name = "gestionSurcharge";
        this.xpath = myXpath;
        this.doc = doc;
        this.apply = function (elem, myRuleProcessor)
        {
            if (elem.texts.item(0).contents != "")
            {
                if (myXpath == "//hi[@style]")
                {
                    var surcharge = elem.xmlAttributes.itemByName("style").value;
                }
                else
                {
                    var surcharge = elem.xmlAttributes.itemByName("rend").value;
                }

                var tabSurcharge = surcharge.split(" ");
                for ($k = 0; $k < tabSurcharge.length; $k++)
                {
                    var cleValeur = tabSurcharge[$k].split("_");
                    var cle = cleValeur[0];
                    var valeur = cleValeur[1];
                    if (valeur)
                    {
                        switch (cle)
                        {
                            case "color":
                                var myColorValue = valeur.substring(1, 7);
                                myColorAdd(this.doc, myColorValue, ColorModel.PROCESS, myColorValue);
                                elem.texts.item(0).fillColor = this.doc.colors.item(myColorValue);
                                break;
                            case "background-color":
                                var myColorValue = valeur.substring(1, 7);
                                myColorAdd(this.doc, myColorValue, ColorModel.PROCESS, myColorValue);
                                elem.texts.item(0).underline = true;
                                elem.texts.item(0).underlineWeight = elem.texts.item(0).pointSize;
                                elem.texts.item(0).underlineOffset = -(elem.texts.item(0).pointSize) / 3;
                                elem.texts.item(0).underlineColor = this.doc.colors.item(myColorValue);
                                break;
                            case "align":
                                switch (valeur)
                                {
                                    case "start" :
                                        elem.texts.item(0).justification = Justification.LEFT_ALIGN;
                                        break;
                                    case "center" :
                                        elem.texts.item(0).justification = Justification.CENTER_ALIGN;
                                        break;
                                    case "end" :
                                        elem.texts.item(0).justification = Justification.RIGHT_ALIGN;
                                        break;
                                }
                                break;
                        }
                    }
                }
            }
            return true;
        }
    }

// Surcharge des cellules
    function surchargeCellules(doc) {
        this.name = "surchargeCellules";
        this.xpath = "//Cellule[@rend]";
        this.doc = doc;
        this.apply = function (elem, myRuleProcessor)
        {  //récupération et stockage de la liste des propriétés CSS dans l'attribut @rend séparées par des "; "
            var surcharge = elem.xmlAttributes.itemByName("rend").value;
            var tabSurcharge = surcharge.split(";");
            for (var k = 0; k < tabSurcharge.length; k++)
            {
                //séparation propriété/valeur
                var cleValeur = tabSurcharge[k].split(":");
                var cle = cleValeur[0];
                var valeur = cleValeur[1];

                if (valeur)
                {
                    //récupération de la couleur en hexa, si elle existe, c'est la troisième séquence de la valeur. La seconde séquence contient le type de bordure. Peut-être à exploiter plus tard?
                    var tabValeur = valeur.split(" ");
                    if (tabValeur.length > 2)
                    {
                        var couleur = tabValeur[2];
                        var myColorValue = couleur.substring(1, 7);
                        myColorAdd(this.doc, myColorValue, ColorModel.PROCESS, myColorValue);
                    }
                    switch (cle)
                    {//soit la cellule n'a aucune bordure soit elles sont toutes identiques
                        case "border":
                            if (valeur == "none")
                            {
                                elem.texts.item(0).parent.topEdgeStrokeWeight = "0";
                                elem.texts.item(0).parent.bottomEdgeStrokeWeight = "0";
                                elem.texts.item(0).parent.leftEdgeStrokeWeight = "0";
                                elem.texts.item(0).parent.rightEdgeStrokeWeight = "0";
                            }
                            else
                            {
                                elem.texts.item(0).parent.topEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.bottomEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.leftEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.rightEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.topEdgeStrokeWeight = "0.5";
                                elem.texts.item(0).parent.bottomEdgeStrokeWeight = "0.5";
                                elem.texts.item(0).parent.leftEdgeStrokeWeight = "0.5";
                                elem.texts.item(0).parent.rightEdgeStrokeWeight = "0.5";
                            }
                            break;
                        case "border-bottom":
                            if (valeur == "none")
                            {
                                elem.texts.item(0).parent.topEdgeStrokeWeight = "0";
                            }
                            else
                            {
                                elem.texts.item(0).parent.bottomEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.bottomEdgeStrokeWeight = "0.5";
                            }
                            break;
                        case "border-top":
                            if (valeur == "none")
                            {
                                elem.texts.item(0).parent.topEdgeStrokeWeight = "0";
                            }
                            else
                            {
                                elem.texts.item(0).parent.topEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.topEdgeStrokeWeight = "0.5";
                            }
                            break;
                        case "border-right":
                            if (valeur == "none")
                            {
                                elem.texts.item(0).parent.rightEdgeStrokeWeight = "0";
                            }
                            else
                            {
                                elem.texts.item(0).parent.rightEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.rightEdgeStrokeWeight = "0.5";
                            }
                            break;
                        case "border-left":
                            if (valeur == "none")
                            {
                                elem.texts.item(0).parent.leftEdgeStrokeWeight = "0";
                            }
                            else
                            {
                                elem.texts.item(0).parent.leftEdgeStrokeColor = this.doc.colors.item(myColorValue);
                                elem.texts.item(0).parent.leftEdgeStrokeWeight = "0.5";
                            }
                            break;
                        case "vertical-align":
                            switch (valeur)
                            {
                                case "top":
                                    elem.texts.item(0).parent.verticalJustification = VerticalJustification.TOP_ALIGN;
                                    break;
                                case "middle":
                                    elem.texts.item(0).parent.verticalJustification = VerticalJustification.CENTER_ALIGN;
                                    break;
                                case "bottom":
                                    elem.texts.item(0).parent.verticalJustification = VerticalJustification.BOTTOM_ALIGN;
                                    break;
                            }
                            break;
                        case "background-color":
                            var couleur = valeur;
                            var myColorValue = couleur.substring(1, 7);
                            myColorAdd(this.doc, myColorValue, ColorModel.PROCESS, myColorValue);
                            elem.texts.item(0).parent.fillColor = this.doc.colors.item(myColorValue);
                            break;
                        default:
                            elem.texts.item(0).parent.topEdgeStrokeWeight = "0.5";
                            elem.texts.item(0).parent.bottomEdgeStrokeWeight = "0.5";
                            elem.texts.item(0).parent.leftEdgeStrokeWeight = "0.5";
                            elem.texts.item(0).parent.rightEdgeStrokeWeight = "0.5";
                    }
                }
            }
        }
    }

    function myColorAdd(myDocument, myColorName, myColorModel, myColorValue) {
        if (myColorValue instanceof Array == false) {
            myColorValue = [(parseInt(myColorValue, 16) >> 16) & 0xff, (parseInt(myColorValue, 16) >> 8) & 0xff, parseInt(myColorValue, 16) & 0xff];
            myColorSpace = ColorSpace.RGB;
        } else {
            if (myColorValue.length == 3)
                myColorSpace = ColorSpace.RGB;
            else
                myColorSpace = ColorSpace.CMYK;
        }
        try {
            myColor = myDocument.colors.item(myColorName);
            myName = myColor.name;
        }
        catch (myError) {
            myColor = myDocument.colors.add();
            myColor.properties = {name: myColorName, model: myColorModel, space: myColorSpace, colorValue: myColorValue};
        }
        return myColor;
    }

    function getXPATH(node) {
        try {
            var path = ""
            while (!(node instanceof Document)) {
                path = "/*" + "[" + (node.index + 1) + "]" + path;
                node = node.parent;
            }
            return path;
        }
        catch (e) {
            logger.alert(e + "/" + node + "PATH = " + path);
        }

    }

    /*
     Retaille des images (elles ont été réduites à IMAGE_SCALE_PERCENT dans un premier temps)
     imageMode: 'heap' (libre) ou 'tab' si dans un tableau.
     La fonction retourne une chaîne de caractères de logs.
     */
    function imageResizer(doc, imageMode) {
        //récupération de la taille du bloc de la page 2 de la maquette
        var refFrame = doc.pages.item(1).textFrames.item(0);
        var frameWidth = refFrame.geometricBounds[3] - refFrame.geometricBounds[1];
        var frameHeight = refFrame.geometricBounds[2] - refFrame.geometricBounds[0];

        //stockage des dimensions de blocs avant rescaling.
        //Première boucle nécessaire pour ne pas être dépendant d'un thread de recomposition
        var bounds = new Array();

        var timer = 0;
        var TIMEOUT = 10000; // 10 seconds
        var SLEEPING_TIME = 10
        for (var a = 0; a < doc.allGraphics.length; a++) {
            var isReady = false;

	    if (!isInDesignClient()) {
		doc.allGraphics[a].absoluteHorizontalScale = 100;
		doc.allGraphics[a].absoluteVerticalScale = 100;
		doc.allGraphics[a].parent.fit(FitOptions.FRAME_TO_CONTENT);
	    }

            while (!isReady && timer < TIMEOUT) {
                try {
                    bounds[a] = doc.allGraphics[a].parent.geometricBounds;
                    bounds[a]["id"] = doc.allGraphics[a].id;
                    isReady = true;
                }
                catch (e) {
                    doc.allGraphics[a].parent.parent.recompose();
                    $.sleep(SLEEPING_TIME);
                }
                timer += SLEEPING_TIME;
            }
        }
        logger.log(i18n('logImageMode') + imageMode);
        logger.log(i18n('blocSize') + frameWidth + " x " + frameHeight);
        for (var b = 0; b < bounds.length; b++) {
            var IMAGE_W_SCALE_PERCENT = doc.textFrames.itemByID(bounds[b]["id"]).absoluteHorizontalScale;
            var IMAGE_H_SCALE_PERCENT = doc.textFrames.itemByID(bounds[b]["id"]).absoluteVerticalScale;
            //récupération du style de bloc à appliquer si existant (+ vérification que le style est valide)
            //même si le style n'existe pas, itemByName retourne un style (vide)
            var objectStyle = doc.objectStyles.itemByName(HEAP_IMAGES_STYLE);
            try {
                objectStyle.name;
            }
            catch (e) {
                objectStyle = null;
            }
            //calcul de la taille de l'image réduite (à  IMAGE_SCALE_W/H_PERCENT % de sa taille) -
            var reducedWidth = Math.abs(bounds[b][3] - bounds[b][1]);
            var reducedHeight = Math.abs(bounds[b][2] - bounds[b][0]);
            //calcul de la taille originale de l'image
            var initialWidth = reducedWidth * (100 / IMAGE_W_SCALE_PERCENT);
            var initialHeight = reducedHeight * (100 / IMAGE_H_SCALE_PERCENT);
            logger.cr();
            logger.log(i18n('logImageTreatment') + b + i18n('logImageID') + doc.textFrames.itemByID(bounds[b]["id"]).id + ")");
            //retaille d'image trop grande
            if (initialWidth > frameWidth || initialHeight > frameHeight) {
                logger.log(i18n('logImageDetail') + b + i18n('logImageShouldRedim'));
                var reduceRatio = (initialWidth - frameWidth) > (initialHeight > frameHeight)
                        ? (initialWidth / frameWidth)
                        : (initialHeight / frameHeight)
                //debugging
                var diff;
                if ((initialWidth - frameWidth) > 0) {
                    diff = initialWidth - frameWidth;
                    logger.log(i18n('logImageWidth') + diff + ")");
                }
                else {
                    diff = initialHeight - frameHeight;
                    logger.log(i18n('logImageHeight') + diff + ")");
                }
                //Fin debugging

                //arrondi à l'entier supérieur le ratio pour les cas limites provoquants de l'overflow
                reduceRatio = Math.ceil(reduceRatio)

                var W_RESCALING = (100 * (100 / IMAGE_W_SCALE_PERCENT)) / reduceRatio;
                var H_RESCALING = (100 * (100 / IMAGE_H_SCALE_PERCENT)) / reduceRatio;
                doc.textFrames.itemByID(bounds[b]["id"]).parent.horizontalScale = W_RESCALING;
                doc.textFrames.itemByID(bounds[b]["id"]).parent.verticalScale = H_RESCALING;
                logger.log(i18n('logImageScale') + reduceRatio);
            }
            else {//rétablissement de la taille d'origine: pas d'overflow
                doc.textFrames.itemByID(bounds[b]["id"]).parent.horizontalScale = 100 * (100 / IMAGE_W_SCALE_PERCENT);
                doc.textFrames.itemByID(bounds[b]["id"]).parent.verticalScale = 100 * (100 / IMAGE_H_SCALE_PERCENT);
                logger.log(i18n('logImageNoRedim'));
            }

            //application du style d'objet - on le crée dans un premier temps si inexistant dans la maquette
            if (imageMode == "heap") {
                if (objectStyle == null) {
                    logger.log(i18n('logBlocStyleNew') + HEAP_IMAGES_STYLE)
                    objectStyle = doc.objectStyles.add();
                    objectStyle.name = HEAP_IMAGES_STYLE;
                }

                doc.textFrames.itemByID(bounds[b]["id"]).parent.applyObjectStyle(
                        objectStyle,
                        clearingOverrides = false,
                        clearingOverridesThroughRootObjectStyle = true);
            }

        }
}

function saveDocument(doc, xmlOrigPath, inddOrigPath){
            var dest = xmlOrigPath.text.substring(0, xmlOrigPath.text.lastIndexOf(".")) + ".indd";
            var xmlName =  xmlOrigPath.text.substring(xmlOrigPath.text.lastIndexOf(sepChemin))
            var newDocName =  xmlName.substring(0, xmlName.lastIndexOf(".")) + ".indd";
            var dest = inddOrigPath.text.substring(0, inddOrigPath.text.lastIndexOf(sepChemin)) + newDocName;
            doc.save(new File(dest))
}
/****BOOK FUNCTION****/
function bookCrossReferences(aBook){
    var nDocs = aBook.bookContents.length;
    IMPORT_MODE = BOOK_MODE;
    for(var i = 0; i < nDocs; i++){
        var currentDoc = app.open(File(aBook.bookContents[i].fullName));
        crossReferencer(currentDoc);
    }
}

/**
 *
 * @param {type} fichierXML
 * @param {type} maquetteFront
 * @param {type} maquettesByType
 * @param {type} options
 * @returns {String}
 */
function makeBook(fichierXML, maquetteFront, maquettesByType, options){

   try{
    var conteneurDoc = app.documents.add();
    try{
        conteneurDoc.importXML(File(fichierXML));
    }
    catch(err){logger.alert(err + '(' + fichierXML+')')}

       var bookVariablesXPaths = new Array()
       bookVariablesXPaths.push("//title[@type='short']")
       bookVariablesXPaths.push("//title[@type='series']")
       bookVariablesXPaths.push("//ab[@type='book']/bibl/date[@type='publishing']")

       for (i=0;i<bookVariablesXPaths.length;i++) {
	   var myRuleSet = new Array(new CollectTextVariables(conteneurDoc, textVariables,bookVariablesXPaths[i]));
	   try {
               __processRuleSet(conteneurDoc.associatedXMLElement, myRuleSet);
	   }
	   catch (e) {
               logger.alert(e)
               //ogger.alert("core / fonction importationXML" + e);
	   }	   
       }
       
    var fichierBook =  new File(Folder.temp.fsName + sepChemin + "book" + (new Date().getTime()));
    //création et sauvegarde du book
    var book = app.books.add(fichierBook);
    book.save();
    //paramétrage du book
    book.automaticPagination = true;
    book.insertBlankPage = true;
    book.repaginationOption = RepaginateOption.NEXT_ODD_PAGE;
       
    __processRuleSet(conteneurDoc.xmlElements, new Array (new includeRuleSet (book, "//front/descendant::*",fichierXML, maquetteFront, maquetteFront,options)));
    __processRuleSet(conteneurDoc.associatedXMLElement, new Array (new includeRuleSet (book, "//group/descendant::*",fichierXML, maquetteFront, maquettesByType,options)));

    conteneurDoc.close(SaveOptions.NO);

     //réf. croisées de niveau livre:
     bookCrossReferences(book);
   }
    catch(err){logger.alert( err + ", line = " + err.line + "-"+err.fileName);}

    //paletteBook.close();

    //saves book
    var INDDPath = (new File(maquetteFront)).parent.fsName + sepChemin;
    var bookFinalFile = INDDPath + ((new File(fichierXML)).name).split(".")[0] + ".indb";

    book.save(bookFinalFile);
    return (i18n('importBookEnd'));


}

/**
 *
 * @param  book
 * @param  xpath
 * @param  conteneur
 * @param  maquetteFront
 * @param  maquetteByType
 * @param  options
 * @returns void
 */
function includeRuleSet(book, xpath, conteneur, maquetteFront, maquetteByType, options){
    this.name = "includeRuleSet";
     this.xpath = xpath;
     this.book = book;
     this.maquetteFront = new File(maquetteFront);
     this.folder = (File(conteneur)).parent.fsName + sepChemin;
     this.conteneur=conteneur;

     this.apply = function(element, myRuleProcessor){

            if(element.markupTag.name=='xi:include' && element.xmlAttributes.item("xpointer").value=='text'){
                var maquetteType = element.parent.xmlAttributes.itemByName("type").value;
                var maquette = new File(maquetteByType[maquetteType]);
                var absolutePath = this.folder + element.xmlAttributes.item("href").value;
                var xslParams = prepareXSLParams(options);
                var xmlTransform = prepareImport(absolutePath, xslParams);
                if(xmlTransform == false)
                            return;
                include(xmlTransform, this.book, maquette, "//text",options);

            }

           else if((element.markupTag.name=='titlePage' && element.xmlAttributes.item("type").value=='pagedetitre' && element.xmlAttributes.item("xml:id").value=='pdt1') || (element.markupTag.name=='div' && element.xmlAttributes.item("type").value=='titlePage_PAO' && element.xmlAttributes.item("xml:id").value=='pdt1') || (element.markupTag.name=='div' && element.xmlAttributes.item("type").value=='liminaires')){
                         if(element.xmlAttributes.item("xml:id") == null){
                            logger.alert(i18n('logBookunvalid')+element.xmlAttributes.item("type")+i18n('logBookTypeId'));
                            return;
                         }
                         var xslParams = prepareXSLParams(options);
                         var xmlTransform = prepareImport(this.conteneur, xslParams);
                         if(xmlTransform == false)
                            return;
                        include(xmlTransform, this.book, this.maquetteFront, "//*[@id='"+element.xmlAttributes.item("xml:id").value+"']", options, element.xmlAttributes.item("xml:id").value);
             }
          //else if((element.markupTag.name=='head' && element.parent.markupTag.name=='group') || (element.markupTag.name=='p' && element.parent.markupTag.name=='group')){
         else if(element.markupTag.name=='group' &&  element.xmlAttributes.item("type").value=='section1'){
            if(File.fs == "Windows"){
                var xslParams = prepareXSLParams(options);
                var xmlTransform = prepareImport(this.conteneur, xslParams);
                                if(xmlTransform == false)
                                    return;
                                include(xmlTransform, this.book, this.maquetteFront, getXPATH(element), options, "head_"+element.id/*element.xmlAttributes.item("xml:id").value*/);

             }
            else{
                head = element.xmlElements.item(0).contents
                // ligne ajoutée pour prévenir les erreurs "premature end of file" qui se produisent lorsque le titre de section comporte une apostrophe droite :  
                sanitizedHead = head.replace('\'', '’')
                options["titrePartie"]="'"+sanitizedHead+"'"
                var xslParams = prepareXSLParams(options);
                var xmlTransform = prepareImport(this.conteneur, xslParams);
                                if(xmlTransform == false)
                                    return;
                                include(xmlTransform, this.book, this.maquetteFront, getXPATH(element), options, "head_"+element.id/*element.xmlAttributes.item("xml:id").value*/);

            }
        }
     }

}

/**
 * Inclusion d'un document XML au sein d'un livre aid
 * @param path: chemin du fichier source XML transformé à inclure
 * @param book: book indd recevant l'import
 * @param maquette: maquette indd à appliquer
 * @param xpath: xpath du noeud principal à importer
 * @param options: options d'import
 * @param documentName: nom du document
 * @returns void
 */
function include(path, book, maquette, xpath, options, documentName){
    try{
            var pathIndd = path.replace("indesign_","");
            var nomFichier =  documentName == null ? pathIndd.substr(path.lastIndexOf(sepChemin)+1) : documentName + ".xml";
            //copie la maquette pour le chapitre courant et y importe le XML
            var nomFichierChapitre = (File(pathIndd)).parent.parent.parent.fsName+sepChemin+"indd"+sepChemin +nomFichier.substr(0, nomFichier.lastIndexOf("."))+".indd";
            maquette.copy(nomFichierChapitre) ;
            var fichierChapitre = new File(nomFichierChapitre);
            options["noeudAfficher"] = xpath ;
            options["appelFonction"] = "importXML";
            importationXML(path, fichierChapitre, options, nomFichier);
            myDocument.close(SaveOptions.YES);
            book.bookContents.add(fichierChapitre);

       }
        catch(err){logger.alert("Error: " +  err + ", line = " + err.line + "-"+err.fileName);}
}

/*Récupération des variables de texte déclarée dans un volume*/
function CollectTextVariables(vol, list,xpath){
        this.name = "affectTextVariables";
        //this.xpath = "//title[@type='short']";
        //this.xpath = "//teiHeader/fileDesc/publicationStmt/ab[@type='papier']/variable";
        this.xpath = xpath;
        this.doc = vol;
        this.tvList = list
        this.apply = function (element, myRuleProcessor)
        {
            //var tvName = "vol-title-short";
            var tvName = "vol-" + element.markupTag.name + "-" + element.xmlAttributes.item("type").value
            var tv
            try{
                tv = vol.textVariables.item(tvName)
                tv.name // génére une exception si tv n'est pas ds la maquette, auquel cas il faudra l'ajouter
            }
            catch(e){
                vol.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: tvName})
            }
            // var tv = vol.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: tvName})
            if (element.markupTag.name == "date") {
                var whenAttribute = element.xmlAttributes.item("when");
                if (whenAttribute != null) {
                    tv.variableOptions.contents = whenAttribute.value.substring(0, 4);
                } else {
                    tv.variableOptions.contents = element.contents;
                }
            } else {
                tv.variableOptions.contents = element.contents;
            }
            this.tvList.push(tv)
            //$.writeln('Variable de texte ' + doc.textVariables.item(tvName).name +' créée')

	    if (element.xmlAttributes.item("n") != null)
	    {
		var tvName = "vol-" + element.markupTag.name + "-num"
		var tv
		try{
                    tv = vol.textVariables.item(tvName)
                    tv.name // génére une exception si tv n'est pas ds la maquette, auquel cas il faudra l'ajouter
		}
		catch(e){
                    vol.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: tvName})
		}
		// var tv = vol.textVariables.add({variableType: VariableTypes.CUSTOM_TEXT_TYPE, name: tvName})
		tv.variableOptions.contents = element.xmlAttributes.item("n").value
		this.tvList.push(tv)
	    }
	    
        }
}


/**************************************************/
var ALL_ATTRIBUTES = i18n('allAttributes');
var ALL_VALUES = i18n('allValues');
