IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

[AJAX] [Scriptaculous] Prog sans erreur mais sans aucun résultat


Sujet :

JavaScript

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    268
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 268
    Points : 128
    Points
    128
    Par défaut [AJAX] [Scriptaculous] Prog sans erreur mais sans aucun résultat
    Bonsoir à tous,

    J'étais en train de déboguer un script assez basique qui doit mettre à jour une série d'images lorsqu'on clique sur un lien (une sorte de diaporama).
    Voici la fonction js appellée :

    ajax_images.js
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    function ajax_images(page, id, nb_img)
    {
    	var xhr=null;
     
        if (window.XMLHttpRequest)
            xhr = new XMLHttpRequest();
        else if (window.ActiveXObject)
            xhr = new ActiveXObject("Microsoft.XMLHTTP");
     
        //on définit l'appel de la fonction au retour serveur
        xhr.onreadystatechange = function() { 
      if (xhr.readyState == 4) { // la réponse a bien été renvoyée
         if (xhr.status == 200) {// on vérifie qu'il n'y a pas eu d'erreur
            maj_ajax(xhr); // on met à jour
         }
         else // si la page n'est pas trouvée (404), les droits sont insuffisants (501), ...
            alert("Erreur : " + xhr.status);
      }
    }
     
      //on appelle le fichier ajax_images.php qui retourne un fichier XML
    	var url= "http://127.0.0.1/ajax_images.php?id="+id+"&page="+page+"&nb_img"+nb_img;
    	xhr.open("GET", url);
    	xhr.send(null);
    }
     
    function maj_ajax(xhr)
    {
    	alert("Request ok");
    }
    Me soucis c'est que alert("Request ok") ne se produit jamais
    et pourtant aucune erreur ne se déclenche (Firebug).
    A ce stade je ne sais plus quoi faire ...

    (Le fichier "ajax_images.php" génère un fichier XML valide (testé et vérifié).
    Les paramètres passés à ce fichier sont ok)

    P.S. : Existe-t-il un site qui référence les fonctions javascript un peu à la manière de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    http://fr2.php.net/manual/fr/
    ?

    Merci de votre aide

  2. #2
    Membre actif
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    Points : 209
    Points
    209
    Par défaut
    Salut,

    Le code javascript n'a pas l'air de poser de problème. En revanche il manque un égal dans ton url!
    Si ca ne marche toujours pas essaie de regarder si tu récupères bien les paramètres dans ta page php.

    Sinon essaie de formater ton code correctement sinon ca devient vite illisible.
    On met des accolades même s'il n'y a qu'une seule ligne.
    On met des espaces entre les '+' et les objetx qu'il sépare, c'est beaucoup plus clair.
    En France on place la première accolade sous la condition.
    Et pour finir, on essaye de faire des fonctions réutilisables, qui font donc le moins de truc possible.

    Un exemple de ce qui est possible avec ces conditions réunis:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    function createXHR()
    {
        var xhr = false;
        if(window.XMLHttpRequest)
        {
            xhr = new XMLHttpRequest();
        }
        else if(window.ActiveXOject)
        {    
    	xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }
        return xhr;
    }
     
    function ajax_images(page, id, nb_img)
    {
        var url= "http://127.0.0.1/ajax_images.php?id=" + id + "&page=" + page + "&nb_img=" + nb_img;
        var xhr = createXHR();
     
        xhr.open("get", url, true);
        xhr.onreadystatechange = function ()
        {
            if(xhr.readyState == 4)
            {
                if (xhr.status == 200)
                { 
                    maj_ajax(xhr);
                }
            }
        }
        xhr.send(null);
    }

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    268
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 268
    Points : 128
    Points
    128
    Par défaut
    J'ai donc créé 3 lignes supplémentaires qui font un echo des paramètres reçus. Pour visualiser ces modifications, j'écris le flux XML dans un fichier pour pouvoir l'ouvrir et voir ce qui s'y passe. Quand je lance l'url du fichier PHP suivant dans le navigateur tout se passe bien. Tandis que lorsque c'est la fonction AJAX qui fait appel, il ne se passe rien, il ne crée pas le fichier ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     
    <?php
    include "classes/imageClass.php";
     
    // ON CREE UN DOCUMENT XML
    header('Content-Type: text/xml'); 
     
    $id = $_GET['id'];
    $page = $_GET['page'];
    $nb_img = $_GET['nb_img'];
     
    // j'essaye de voir si les variables sont biens passées
    $str.= $id . $page . $nb_img;
     
    $oimages = new imageClass();
    $result = $oimages->get_URLs_page($id, $page);
    $nb_images = $oimages->count_images($id);
     
    $handle = fopen("test_01.xml","w");
    $str .= "<?xml version=\"1.0\"?>\n";
    $str .= "<media>\n";
    while($images = mysql_fetch_array($result, MYSQL_ASSOC)){
    	$str .= "<image>" . $images['URL'] . "</image>" . "\n";
    }
    $str .= "</media>\n";
     
    fwrite($handle,$str);
    fclose($handle);
     
    ?>

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    268
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 268
    Points : 128
    Points
    128
    Par défaut
    J'ai mis à jour le code de la page php appelée mais rien n'y fait, il ne crée pas le fichier XML ... pourtant je la bonne URL, je comprends pas

    PHP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?php
    include "classes/imageClass.php";
     
    // ON CREE UN DOCUMENT XML
    //header('Content-Type: text/xml'); 
     
    $id = (isset($_GET['id'])) ? $_GET['id'] : '';
    $page =  (isset($_GET['page'])) ? $_GET['page'] : '';
    $nb_img =  (isset($_GET['nb_img'])) ? $_GET['nb_img'] : '';
     
    $str.= $id . $page . $nb_img;
     
    $oimages = new imageClass();
    $result = $oimages->get_URLs_page($id, $page);
    $nb_images = $oimages->count_images($id);
     
    $handle = fopen("test_01.xml","w");
    $str .= '<?xml version=\"1.0\"?>\n';
    $str .= "<media>\n";
    while($images = mysql_fetch_array($result, MYSQL_ASSOC)){
    	$str .= "<image>" . $images['URL'] . "</image>" . "\n";
    }
    $str .= "</media>\n";
     
    fwrite($handle,$str);
    fclose($handle);
     
    ?>
    JAVASCRIPT
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
    function createXHR()
    {
        var xhr = false;
        if(window.XMLHttpRequest)
        {
            xhr = new XMLHttpRequest();
        }
        else if(window.ActiveXOject)
        {    
    	xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }
        return xhr;
    }
     
    function ajax_images(page, id, nb_img)
    {
        var url= "http://127.0.0.1/ajax_images.php?id=" + id + "&page=" + page + "&nb_img=" + nb_img;
        var xhr = createXHR();
     
        xhr.open("get", url, true);
        xhr.onreadystatechange = function ()
        {
            if(xhr.readyState == 4)
            {
                if (xhr.status == 200)
                { 
                    maj_ajax(xhr);
                }
            }
        }
        xhr.send(null);
    }
     
    function maj_ajax(xhr)
    {
    	alert("ProcessOK");
    }

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    Points : 209
    Points
    209
    Par défaut
    Etant donné que je n'ai jamais fait d'ajax avec php, je ne vais pas pouvoir beaucoup t'aider.

    Mais essaie qu'en même ca :

    fichier ajax_images.php :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    <?php
        echo $_GET['id'];
    ?>
    fonction maj_ajax():
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    function maj_ajax(xhr)
    {
        var id = xhr.responseText;
        alert(id);
    }
    Mais je ne comprends pas vraiment ce que tu veux faire. Pourquoi utiliser de l'ajax, juste pour créer un fichier xml?

    Le principe de l'ajax est le suivant : à la suite d'un évenement (onblur, onmouseclick, etc.) on appelle une fonction js qui au cours de son déroulement va :
    - appeler une page (php dans ton cas) qui va créer une réponse
    - récupérer la réponse par une fonction callback (), qui serait dans ton cas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function ajax_images(page, id, nb_img)
    {
    ...
        xhr.onreadystatechange = callbackImage;
    ...
    }
     
    function callbackImage()
    {
            // En supposant que la variable xhr est une variable globale
            if(xhr.readyState == 4)
            {
                if (xhr.status == 200)
                { 
                    var id = xhr.responseText;
                    alert(id);
                }
            }
        }
    Tu remarqueras que le découpage est différent de celui que je t'ai proposé. Ce découpage permet de clarifier l'explication, mais l'utilisation est très peu pratique. Il est donc conseillé de faire une fonction callback anonyme qui va appeler une fonction qui traite la réponse. D'où:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function maj_ajax(xhr)
    {
        var id = xhr.responseText;
        alert(id);
    }
    qui marche puisqu'on ne veut que du "plain text", mais pour du xml ca serait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function maj_ajax(xhr)
    {
        var response = xhr.responseXML;
        /* ici il faut traiter la réponse :
            - soit en déparsant ton xml à la main
            - soit en appelant un gabarit xsl */
    }
    Pour faire suite au commentaire, dans ton cas, la solution la plus simple pour parser la réponse serait d'utiliser la méthode JSON qui est beaucoup moins lourde à mettre en place pour de petits projets!

    Tout ca pour te montrer que l'ajax s'utilise sur des informations non persistantes en retour (donc pas de création de fichiers par exemple).
    En revanche en entrée, ton fichier php peut très bien récupérer l'info d'un fichier ou d'une base de données. Mais (je me répète) ne crée rien, et n'altère aucunement les données d'une base.
    Toute modif en dur se fera lors du rechargement de la page.

    A noter que certains développeurs altèrent leur base en utilisant ajax, notamment sur les sites d'ecommerces. Le Drag'n drop de scriptaculous remplit le caddie et le script "handler" va ajouter l'article dans la panier de l'utilisateur sur la base de données. Ce qui est une méthode contestable mais bon, c'est une autre histoire...

  6. #6
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    268
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 268
    Points : 128
    Points
    128
    Par défaut
    En fait j'ai :

    L'idée c'est de faire HTML->JS->PHP->XML->JS->HTML

    L'idée c'est de faire un diaporama :
    J'ai une page HTML avec :
    - 2 images
    - 2 liens : "image précédente" et "image suivante"

    Quand je cliques sur un des 2 boutons, il appelle un script JS qui appelle une fonction PHP qui interroge la BDD des images pour savoir quelles sont les images à afficher.
    PHP répond à JS en lui envoyant un flux XML
    JS le reçoit et met à jour la page HTML.

    Mais si je comprends bien la mise à jour des images ne peut se faire que dés lors qu'on a rafraichit toute la page => c'est pas possible avec AJAX alors ?


    Je viens de trouver ce qui n'allait pas :
    Dans ma page HTML, j'avais un lien du type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     <a href="" onclick="ajax"></a>
    De ce fait le script JS se lançait bien mais il y avait une exception qui se lançait mais qui disparaissait instantanément sous Firebug (à cause du lien href=""). Cette exception empéchait l'appel à XHR à cause de la référence à l'URL
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    http://127.0.0.1/monscript.php
    au lieu de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    http://localhost/monscript.php
    ,
    c'est un paramétre de sécurité du navigateur apparemment, pour éviter d'agir sur d'autres domaines sans autorisation.

    (je teste la suite du script)

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    268
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 268
    Points : 128
    Points
    128
    Par défaut
    Bon, il se passe toujours quelque chose de bizarre avec AJAX ...
    La mise à jour des images ne se fait pas,
    j'essaye de contrôler avec un alert() mais il ne l'execute pas et Firebug ne me donne aucun indice encore une fois.

    Voici le code de ce qui fonctionne le moins mal pour l'instant :
    HTML/PHP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    <img id="img_photo_left" src= ...>
    <img id="img_photo_right" src=...>
    ...
    $A.='<a onClick=ajax_images(' . $page  . ',' . $id .  ','  . $nb_img .')> ' . $page  . '</a> | ';
    echo $A;
    JS
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    function ajax_images(page, id, nb_img)
    {
        var xhr=null;
     
        if (window.XMLHttpRequest)
            xhr = new XMLHttpRequest();
        else if (window.ActiveXObject)
            xhr = new ActiveXObject("Microsoft.XMLHTTP");
     
        //on définit l'appel de la fonction au retour serveur
        xhr.onreadystatechange = function() { 
      if (xhr.readyState == 4) { // la réponse a bien été renvoyée
         if (xhr.status == 200) {// on vérifie qu'il n'y a pas eu d'erreur
            maj_ajax(xhr); // on met à jour
         }
         else // si la page n'est pas trouvée (404), les droits sont insuffisants (501), ...
            alert ("Erreur : " + xhr.status);
      }
    } 
     
    	//on appelle le fichier reponse.txt en passant les paramètres
    	 var url= "http://localhost/ajax_images.php?id=" + id + "&page=" + page + "&nb_img=" + nb_img;
    	xhr.open("GET", url, true);
    	xhr.send(null);
    }
     
    function maj_ajax(xhr)
    {
    var docXML= xhr.responseXML.documentElement;
     
    // ICI L ALERT NE SE PASSE PAS
     
    alert(docXML.getElementsByTagName("image")[0].firstChild.data);
     
        document.getElementById('img_photo_left').src.value = docXML.getElementsByTagName("image")[0].firstChild.data;
    	document.getElementById('img_photo_right').src.value = docXML.getElementsByTagName("image")[1].firstChild.data;
    	alert(id);
     
    }
    XML GENERE PAR LE SCRIPT PHP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    <?xml version="1.0"?>
    <media>
         <image>http://localhost/img1.jpg</image>
         <image>http://localhost/img2.jpg</image>
    </media>

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    Points : 209
    Points
    209
    Par défaut
    Citation Envoyé par popovitch130 Voir le message
    Mais si je comprends bien la mise à jour des images ne peut se faire que dés lors qu'on a rafraichit toute la page => c'est pas possible avec AJAX alors ?
    Tu ne m'as pas compris. C'est tout à fait possible mais il ne faut pas créer un fichier xml en dur. Il faut que ta page php renvoit une réponse au format xml c'est tout.
    Ensuite ta fonction js de lecture va déparser la réponse et afficher ce que tu veux.

    Et sinon, on ne fait pas d'ajax avec des liens ou des boutons de type "submit" (tous les trucs qui rechargent une page) donc bouton de type "button" c'est bon

    Petit exemple super simple (ca ne sert à rien de faire quelque chose de compliqué au début surtout si tu ne connais pas ajax)
    J'ai opté pour une solution sans xml tu verras que c'est beaucoup plus simple.

    fichier index.html
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    ...
    <head>
        <script type="text/javascript" src="ajax.js" />
    </head>
    ...
    <p><img id="picture" src="pic1.jpg" alt="description" /></p>
    <p>
        <span id="prev">Image Précédente</span>
        <span id="next">Image Suivante</span>
    </p>
    ...

    fichier ajax.js
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
     
    window.onload = function ()
    {
        document.getElementById("prev").onclick = previousPicture;
        document.getElementById("next").onclick = nextPicture;
    }
     
    function createXHR()
    {
    ...
    }
     
    function previousPicture()
    {
        ajax_images(-1);
    }
     
    function nextPicture()
    {
        ajax_images(1);
    }
     
    function ajax_images(navigation)
    {
        var image = document.getElementById("picture").src;
        // Plus traitemenr de la source l'image si nécessaire 
        // Par exemple : images/toto.jpg
        // la seule chose qui nous intéresse c'est toto donc une petite regexp et c'est bon
     
        var url= "http://127.0.0.1/ajax_images.php?id=" + id + "&navigation=" + navigation;
        ...
    }
     
    function maj_ajax(xhr)
    {
        //Technique JSON
        var res = eval('(' + xhr.responseText + ')');
        ...
        //en imaginant que tu aies une clé nommé source
        // On met à jour l'image
        document.getElementById("picture").src = "images/" + res.source + ".jpg";
        ...
    }
    J'ai supprimé tous les arguments de ta fonction ajax_images puisque tu ne devrais pas en avoir besoin si tu te débrouilles bien.

    J'ai aussi supprimé tout le code js de la page html. Puisque du html c'est fait pour afficher et le js pour traiter. C'est le même principe que la séparation html/css
    La fonction init applique des écouteurs aux 2 spans. (Pour que ce soit clair tu peux changer le type de curseur des span (cursor: pointer) et leur faire un design type lien avec un hover. Mais ca reste des span!)

    le reste me semble clair.

    Note sur JSON avec l'exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    <?php
    ...
    // Requête et récupération de données (ici uniquement le nom de l'image sans extension)
    echo  '{"source" : "' . $image . '"}';
    ...
    ?>
    Note : Pour ta requête sql je ne vois pas l'intérêt de faire un while puisque tu ne récupères qu'une seule image.

  9. #9
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    268
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 268
    Points : 128
    Points
    128
    Par défaut
    - Tout d'abord merci, de m'aider à cette heure tardive.

    - Merci pour l'info sur les éléments HTML sur lesquels on enregistre un écouteur, c'était pas très clair pour moi.

    - Passer par un format JSON c'est je pense aussi plus adapté. Par précipitation, j'ai repris un ancien scripts mais c'est vrai que c'est un peu tuer la mouche à la massue que de passer par xml pour générer 2 valeurs !? Qu'en penses-tu ?

    Merci beaucoup de ton aide @Ikey,

    Je vais tester tout ça

  10. #10
    Membre habitué
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    268
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 268
    Points : 128
    Points
    128
    Par défaut
    La version JSON et la version XML fonctionne.

    Merci beaucoup pour ce cours particulier qui m'a été fort utile

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 9
    Dernier message: 01/12/2010, 09h57
  2. Réponses: 3
    Dernier message: 26/11/2010, 08h03
  3. Insert dans base Access qui ne se fait pas, mais sans erreur
    Par muppetshow dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 28/01/2010, 21h37
  4. Réponses: 2
    Dernier message: 27/04/2009, 13h09
  5. Mon code sans erreurs mais ne marche pas
    Par acacia dans le forum C
    Réponses: 49
    Dernier message: 25/01/2008, 16h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo