PNewCode Posted February 13, 2023 Share Posted February 13, 2023 Hello everyone. Today I'm working on adding a counter to my working mp3 player (count each time a song is played). Right now it successfully grabs the id and plays the correct song without issue. Now, I have added to my db "pcount" (for plays count). I looked around google and some other forums but all I can find is how to add a complete player, or how to add view counts to a different kind of music player completely (that I don't understand) This is meant to be basic and easy. Below is what I have. I'm only including the parts that are relevant. Any ideas? $sql_l = "SELECT * FROM audio"; <source src="audio/'.$row["audio"].'" type="audio/mpeg"> Your browser does not support the audio element. </audio> Quote Link to comment Share on other sites More sharing options...
ginerjm Posted February 13, 2023 Share Posted February 13, 2023 Not sure what you post is supposed to show us. The code sample makes no sense since it is incomplete(?) and disjointed. You show us a query. You show us an html source(?) tag. You show us an ending audio tag. Where's the part that is supposed be trying to add a counter to a table? Where is the part that is supposed to populate the source tag with a query result? Where is the fetch for that query result? Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 13, 2023 Author Share Posted February 13, 2023 (edited) @ginerjm I didn't show the rest because on some sites I get fussed at if I show too much. If I show too little then I get fussed at. So I only show the parts that I am having trouble with. If I showed all the stuff that is working then what would be the point of that? Know that the player is working. It plays the songs. A person uploads a song, it goes to the folder, the database stores the link, then the player catches the link and people can play the song. I only need help with getting a count to the db column "pcount" when people click on play. Other than that there's nothing to show. If you REALLY want to see the rest of the pages script then sure I can comment with that too. But I don't want to be fussed at for putting in too much since I posted what is relevant. Edit: you ask "where is this and that"... if I had that stuff for this task then I would have posted that. I don't have it, because I don't know how to add it. Which is the entire purpose of me asking for help Edited February 13, 2023 by PNewCode Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 13, 2023 Author Share Posted February 13, 2023 @ginerjm Here is the entire page. Please don't ask me "Why did you do this and that". I just need help getting the counter because everything else is working. And as long as it's working, I don't care about the 1,000 different ways I should have done it, please. <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> * { box-sizing: border-box; } #myInput { background-image: url('searchicon.png'); background-position: 10px 10px; background-repeat: no-repeat; width: 100%; font-size: 16px; padding: 12px 20px 12px 40px; border: 1px solid #ddd; margin-bottom: 12px; } #myTable { border-collapse: collapse; width: 100%; border: 1px solid #ddd; font-size: 18px; } #myTable th, #myTable td { text-align: left; padding: 12px; } #myTable tr { border-bottom: 1px solid #ddd; } #myTable tr.header, #myTable tr:hover { background-color: #330000; } </style> </head> <body> <font face="Verdana, Arial, Helvetica, sans-serif"> <script> document.addEventListener('contextmenu', event => event.preventDefault()); </script> <style type="text/css"> body {background:none transparent; } </style> <style> img { border: 2px solid #330000; } </style> <input type="text" id="myInput" onkeyup="searchTable()" placeholder="Search by Band or Song Title" title="Type in a name"> </font> <table id="myTable" cellpadding="0" cellspacing="0"> <tr class="header"> <th > <center> <font face="Verdana, Arial, Helvetica, sans-serif" color="#CCCCCC">Artist</font> </center> </th> <th > <center> <font face="Verdana, Arial, Helvetica, sans-serif" color="#CCCCCC">Cover</font> </center> </th> <th ><font face="Verdana, Arial, Helvetica, sans-serif"> <center> <font color="#CCCCCC"> Song Title </font> </center> </font></th> <th ><font face="Verdana, Arial, Helvetica, sans-serif"> <center> <font color="#CCCCCC">Song</font> </center> </font></th> <th ><font face="Verdana, Arial, Helvetica, sans-serif"> <center> <font color="#CCCCCC">Plays</font> </center> </font></th> <th ><font face="Verdana, Arial, Helvetica, sans-serif"> <center> <font color="#CCCCCC">Music Video</font> </center> </font></th> <th ><font face="Verdana, Arial, Helvetica, sans-serif"> <center> <font color="#CCCCCC">Uploaded By</font> </center> </font></th> </tr> <?php $hostname_l = "db stuff"; $username_l = "db stuff"; $password_l = "db stuff"; $dbname_l = "db stuff"; $conn_l = mysqli_connect($hostname_l, $username_l, $password_l, $dbname_l); if(!$conn_l){ echo "Database connection error".mysqli_connect_error(); } $user_id = 151; $sql_l = "SELECT * FROM audio"; $result = $conn_l->query($sql_l); if ($result->num_rows > 0) { // output data of each row while($row = $result->fetch_assoc()) { echo '<tr background="faded.png"> <td><center><font face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF"><b>'.$row["band"].'</b></font></center></td> <td><center><img src="img/'.$row["cover"].'" style="width:75px"></center></td> <td><center><font face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF"><b>'.$row["audio_title"].'</b></font></center></td> <td><center><audio controls controlsList="nodownload"> <source src="audio/'.$row["audio"].'" type="audio/mpeg"> Your browser does not support the audio element. </audio> </center></td> <td><center><font face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF"><b>'.$row["pcount"].'</b></font></center></td> <td><center><font face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF"><b><a href="'.$row['video'].'" target="_blank">'.$row["video"].'</a></b></font></center></td> <td><center><font face="Verdana, Arial, Helvetica, sans-serif" color="#FFFFFF"><b>'.$row["fname"].'</b></font></center></td> </tr>'; } } else { echo "0 results"; } ?> </table> <font face="Verdana, Arial, Helvetica, sans-serif"> <script> function searchTable() { var input, filter, found, table, tr, td, i, j; input = document.getElementById("myInput"); filter = input.value.toUpperCase(); table = document.getElementById("myTable"); tr = table.getElementsByTagName("tr"); for (i = 0; i < tr.length; i++) { td = tr[i].getElementsByTagName("td"); for (j = 0; j < td.length; j++) { if (td[j].innerHTML.toUpperCase().indexOf(filter) > -1) { found = true; } } if (found) { tr[i].style.display = ""; found = false; } else { tr[i].style.display = "none"; } } } </script> </font> </body> </html> Quote Link to comment Share on other sites More sharing options...
Barand Posted February 13, 2023 Share Posted February 13, 2023 Not directly related to your problem, but your opening and closing HTML tags should match The opening tag should be <audio ... An audio element has a "play" event which you could use to trigger updates to your count via AJAX. Your html markup is antiquated. You should use a learining resource that was written this century and not in the days of Netscape Navigator. Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 13, 2023 Author Share Posted February 13, 2023 @Barand Thank you. I didn't even notice that the tags weren't matched haha. I wonder how it's working like that then. Good learning point, thank you. So I should look for an ajax script that I can edit and learn how to use it to add the count to the database column? Quote Link to comment Share on other sites More sharing options...
Barand Posted February 13, 2023 Share Posted February 13, 2023 13 minutes ago, PNewCode said: I wonder how it's working like that then I had another look at the code. You have an <audio opening tag. "<source" shouldn't be there. Your ajax needs to use the player's "onplay" event. Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 13, 2023 Author Share Posted February 13, 2023 (edited) @Barand Thank you. I searched every way of phrasing a counter for this I can think of, and putting "ajax" in the search string and I cannot find a single thing that shows anything like it with the type of media play that I'm using. Is there a term or something I'm missing for my searches? Or rather, since you don't know all of what I've tried, is there something more specific I can look for? I'm not asking anyone to write it for me, but I can't find anything even remotely suitable that will work with my player. Many thanks Edit: Even more so, how to get it to save in the database or anything to save in the database with a click and ajax or javascript Edit 2: Side note... the amount of results about Ajax liquid dish soap is hilarious Edited February 13, 2023 by PNewCode Quote Link to comment Share on other sites More sharing options...
ginerjm Posted February 13, 2023 Share Posted February 13, 2023 YOu didn't mention that you were using Ajax for this. Why not just let the script upload the data, do the db update and then return to the player or whatever started this? And then show us the Code that is not working , not all the html and css stuff. Just the php. And maybe the form so we can confirm that you are using named fields properly. Quote Link to comment Share on other sites More sharing options...
gizmola Posted February 14, 2023 Share Posted February 14, 2023 Hopefully this will simplify the whole ajax thing for you. 1st create a php script that will accept a POST request with a method to identify which row in the audio table needs to be updated This script needs to make a database connection and then issue an UPDATE query for that row The query is simple enough, something like: 'UPDATE audio SET pcount = pcount + 1 WHERE id = ?' You should use a prepared statement for this, where you bind the id parameter After the update, you can add a query to get the new count if you would like, and return that in a json form using json_encode. You will pass/get the ID parameter from the $_POST You can test this script independently using any testing tool that can generate a post. Postman is a good one, but if you use VSCode, then Thunderclient is an extension that will do the same thing, only integrated into VSCode, which is a nice option. Once you've tested your update script (which you might want to call something like "update_audio_playcount.php") you are ready to call this script via "ajax". Ajax is simply some javascript code you can call when your play button is pushed. You will need to populate your markup in some way so that you can get the audio row id value. Typically people use 'data-*' attributes to do this, so a good way of handling this would be to make sure the play button has an attribute like "data-song-id=x" which should make it very easy from javascript to get the id of the row in the audio table you need to update. Just to keep this simple, you can use the javascript fetch api to make your POST request to the update script. This example is from the MDN page I linked with a couple of modification hints you need for a form POST: // Example POST method implementation: async function postData(url = '', data = {}) { // Default options are marked with * const formData = new FormData(); formData.append('id', ... get the audio table id here and pass it); const response = await fetch(url, { method: 'POST', // *GET, POST, PUT, DELETE, etc. mode: 'cors', // no-cors, *cors, same-origin cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached credentials: 'same-origin', // include, *same-origin, omit headers: { 'Content-Type': 'application/json' // 'Content-Type': 'application/x-www-form-urlencoded', }, redirect: 'follow', // manual, *follow, error referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url body: formData }); return response.json(); // parses JSON response into native JavaScript objects } postData('https://example.com/answer', {}) .then((data) => { console.log(data); // JSON data parsed by `data.json()` call }); The important detail we don't have is the structure of the audio table. Hopefully each row has an auto_increment id value, so that you can use that to identify the row that's needed. Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 15, 2023 Author Share Posted February 15, 2023 @gizmola and @Barand Thank you both for that useful bunch of information. I was able to learn from that. And this is what I ended up with that works near-perfect. The only problem I have now, that I didn't anticipate being an issue, is that when the player his PAUSE and then PLAY again, it adds to the play count. So if someone wanted to make it look like they are getting a bunch of plays, they can just hit PAUSE - PLAY - PAUSE - PLAY over and over again. I wonder could there be a way around that? $('audio').on("play", function(id,data, status){ var a_id = $(this).attr("id"); $.ajax({url: "count-play.php?id=" + a_id, success: function(result){ $("."+ a_id + "count").html(result); }}); Quote Link to comment Share on other sites More sharing options...
kicken Posted February 15, 2023 Share Posted February 15, 2023 1 hour ago, PNewCode said: I wonder could there be a way around that? Keep track of whether or not you've sent the count request, and only send it if you haven't. $('audio').on("play", function(){ let requestSent = false return function(){ if (requestSent){ return; } requestSent = true; let a_id = $(this).attr("id"); $.ajax({ url: "count-play.php?id=" + a_id , success: function(result){ $("."+ a_id + "count").html(result); } }); }; }()); The first time the event fires, requestSent will be false so the ajax call will run and record the play event and requestSent will be set to true. Later events will see that requestSent is true and immediately return thus doing nothing. 2 Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 15, 2023 Author Share Posted February 15, 2023 (edited) @kicken Thank you, however that seems to be preventing any count at all. What you have certainly stops the count every time play/pause is clicked. But it also doesn't allow new plays to be entered either. Any thoughts? Edit: I made a test account and opened it in a new browser (one is chrome, the other is edge) to test it Edited February 15, 2023 by PNewCode Quote Link to comment Share on other sites More sharing options...
kicken Posted February 15, 2023 Share Posted February 15, 2023 (edited) If you have multiple audio tags or you're re-using the same tag for different tracks then you'll need to extend the code from using a simple true/false variable to something that can differentiate between the tags/tracks (ie, by the ID for example). An example would be to change requestSent into an array and store the ID of the played tracks into that array. To test if the track has been played, search that array for the ID. Edited February 15, 2023 by kicken Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 15, 2023 Author Share Posted February 15, 2023 @kicken Yes the tracks are pulled from a mysql database and listed in php. There are multiple id's for this page with all of the songs. They are all stored in the DB and also the number of plays are as well Quote Link to comment Share on other sites More sharing options...
Solution gizmola Posted February 15, 2023 Solution Share Posted February 15, 2023 Yes, so implementing kicken's suggestion, this should work probably: $('audio').on("play", function(){ let requestSent = [] return function(){ let a_id = $(this).attr("id") if (requestSent.includes(a_id)) { return; } requestSent.push(a_id) $.ajax({ url: "count-play.php?id=" + a_id , success: function(result){ $("."+ a_id + "count").html(result) } }); }; }()); Hopefully you understand what this code does, which makes use of some tricky javascript concepts, namely: Javascript closure An IFFE 1 Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 16, 2023 Author Share Posted February 16, 2023 (edited) @gizmola You nailed it. I love this site. So much help and great learning experiences too. This is doing exactly as I needed. So, just so I understand it (correct me if I'm wrong please, I want to learn from what is shown to me), for my education... Like what @kicken said, the "let requestSent" tracks if the count request was sent, and if it has then it sends it in the "return" function for the player when PLAY button is clicked. And then... it looks for the id of the strack to include it in the functions request. AND THEN WHAT YOU SAID.... Sends the ajax functions to complete the requests Right? I may be talking in circles and not using the correct words or phrases in my understanding, so I'm kind of saying this in lamens terms haha. I'm excited now. I also have (on the same page) a like / unlike feature that sends a number to add to the database and displays the number. But it's annoying right now because it refreshes the whole page to show it. So with a longer list, it just goes to the top of the page when the "heart" is clicked on. So I'm going to try to use this to do the same for that too and see if I can pull it off. YOU ALL ROCK SO MUCH! Thank you! Edited February 16, 2023 by PNewCode Quote Link to comment Share on other sites More sharing options...
gizmola Posted February 16, 2023 Share Posted February 16, 2023 So just to say it, the on event handler is accepting a callback function to run when there is a "play" event. A simpler solution would be to just have a function defined there, that the callback would run, or to define a function globally and pass the name of the function. However, @Kicken coded this function to return an anonymous function. It helps to focus in on return statements in code like this. If you notice the requestSent variable is declared outside the function declaration that does the work. This creates a "closure" (or takes advantage of javascript closure) depending on how you want to think about it. It makes the variable requestSent available to the inner function that is being returned, and this variable will continue to exist in the browser's memory associated with the window/page, until such a time as a new request is made that causes new html/javascript/css to be loaded. An alternative would be to declare requestSent globally and use that, but he gave you something more sophisticated -- a function that returns a function and takes advantage of a variable that is only visible to the anonymous function, and yet is available to the anonymous function across executions. Each time the callback is run, this could be either for the same song or a different song, so inside the function, there is a jQuery call to find the id of the button. let a_id = $(this).attr("id") It's good to think about why this is declared inside the function and how that works. Since this handler can be called for any song, the $(this) resolves in this situation as the song that is being played. Thus the a_id gets set each time there's a play event, and then gets the html id attribute. I added code to push the value onto the requestSent array, which again, since it's part of the closure for the anonymous function, survives across plays. I used Array.includes() to check if the song id already exists in requestSent. If not, I update requestSent with requestSent.push(a_id) and the ajax runs, passing a_id. The ajax is also being done using the jQuery library. The final question you should probably be asking is: if this is a function that returns a function, then how is it, that the callback, which requires a function to run, gets the actual function it needs. A function that returns a function is not a callback. The answer is that again Kicken used an IFFE here. What is actually being passed is a function that is immediately executed. You can see this because after the function definition function () { ... } It is immediately followed by the parens ie. () which causes javascript to execute the function. function () { ... }() So this code works because the function that returns a function, is run immediately, giving the callback parameter what it wants ... a function to run when a play event occurs. The function is anonymous and only bound to the event handler for play events, which also keeps global scope from being cluttered with a symbol table entry for a function that is only needed for the callback. The benefit of doing it this way is that he did not need to utilize a global variable, since closure takes care of this for you. This type of code is favored in many situations, since you don't have a slew of global variables floating around. Nothing outside the callback function can see or modify the requestSent array -- yet it is essentially a private environment that the callback uses. As I said previously -- advanced javascript stuff, that can be confusing if you are still learning javascript. Hope this helps -- using those terms (IFFE, javascript closure, js anonymous function, js callbacks, js this) will lead you to an enormous amount of additional material if you need to explore them further. 1 1 Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 17, 2023 Author Share Posted February 17, 2023 @gizmola Wow thank you very much! I opened the working script and was following it down the line while reading your reply. It's all making more sense to me. And I was able to use that to create the other task that I was working on to add a count to something else on my page too. Also, I'm going to use this same lesson to make a voting up and down for some fun polls on songs too! Thank you again! Quote Link to comment Share on other sites More sharing options...
Barand Posted February 18, 2023 Share Posted February 18, 2023 I took an alternative approach, using two data attributes for each audio player element... data-id : as above contains the track id data-play : initially 0, set to 1 when play is clicked, reset to 0 when track has ended. The play count can only be incremented if play is clicked when data-play is 0. This prevents play...pause...play...pause from boosting the count. The AJAX processing receives the id, increments pcount for that track, retrieves the new pcount, returns that in the response <?php include 'db_inc.php'; // use your own $pdo = pdoConnect('db2'); // db connection code ################################################################################ # Handle ajax requests # ################################################################################ if (isset($_POST['ajax'])) { if ($_POST['ajax']=='addPlay') { // increment the count for the selected player file $stmt = $pdo->prepare("UPDATE audio SET pcount = pcount + 1 WHERE id = ? "); $stmt->execute([ $_POST['id'] ]); // get the new count $res = $pdo->prepare("SELECT pcount FROM audio WHERE id = ? "); $res->execute([ $_POST['id'] ]); $pc = $res->fetchColumn(); exit("$pc"); // send the new count as the reponse } } ################################################################################ # get available audio files and build page output # ################################################################################ $res = $pdo->query("SELECT id , audio , pcount FROM audio "); $players = ''; foreach ($res as $row) { $players .= "<tr> <td>" . substr(basename($row['audio']), 0, -4) . "</td> <td> <audio class='player' data-id='{$row['id']}' data-play='0' controls controlsList='nodownload' src='{$row['audio']}' preload> Your browser does not support the audio element. </audio> </td> <td data-id='{$row['id']}'>{$row['pcount']} </tr> "; } ?> <!DOCTYPE html> <html lang="en"> <head> <title>sample</title> <meta charset="utf-8"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script type='text/javascript'> $().ready(function() { $(".player").on("play", function() { let id = $(this).data("id") if ($(this).attr("data-play")=='0') { // only allow increment when data-play is 0 $.post( "", {"ajax":"addPlay", "id":id}, function(resp) { if (resp > '0') { // update the page with new play count $("td[data-id="+id+"]").html(resp) } }, "TEXT" ) $(this).attr("data-play", '1') // set data-play to 1 to prevent increments } }) $(".player").on("ended", function() { $(this).attr("data-play", '0') // now track has ended, reset data-play to 0 }) }) </script> </head> <body> <table border='1'> <tr><th>File name</th> <th>Player</th> <th>Plays to<br>date</th> </tr> <?= $players ?> </table> </body> </html> 1 Quote Link to comment Share on other sites More sharing options...
PNewCode Posted February 18, 2023 Author Share Posted February 18, 2023 (edited) @kicken @gizmola @Barand and others,EDIT!!! I solved it. I'm adding the solution to the bottom in case anyone else can use this for help. I am still crediting you all for this because like I said, it stems from what I learned on here on this topic. So I took what I learned from this and made a like / dislike feature. I am stuck again (of course) but I NEARLY have it. I'm adding it to this because it stems from this topic (if this should be a new question let me know and my apologies) So the below script is including a calandar bit that I have. There's no issues with that. Also, it has a thumbs up bit that is working perfectly too. Adding the functions for the thumbs down isn't agreeing with me though. WHAT I HAVE DONE: Lots of stuff haha. But The part at the bottom I tried duplicating the whole thing and changing the parts to reflect the php pages and database (heart2, addheartno.php, count2) When I have that duplicated in a whole part of it's own on a DIFFERENT page, it works to duplicate it, however the calandar parts aren't on that different page. So I'm guessing that I'm missing something(Please see the second script to see what I tried and got closest but no cigar) This is what I have that works with just the thumbs up <script> $( function() { $( "#datepicker" ).datepicker({changeMonth: true, changeYear: true}); } ); init(); function init() { var today = new Date(); var date = (today.getMonth()+1)+'/'+today.getDate() +'/'+ today.getFullYear(); $.get("getplaylist-playhouse.php", { cur_date: date }, function(data, status){ let element = document.getElementById("play_list"); while (element.firstChild) { element.removeChild(element.firstChild); } document.getElementById("play_list").innerHTML = data; }); } $(document).on("change", "#datepicker", function () { cur_date =$(this).val(); $.get("getplaylist-playhouse.php", { cur_date: cur_date }, function(data, status){ let element = document.getElementById("play_list"); while (element.firstChild) { element.removeChild(element.firstChild); } /////////// THIS IS THE SECTION THAT HANDLES THE THUMBS UP ////////// document.getElementById("play_list").innerHTML = data; $('.heart').on("click", function(){ var a_id = $(this).attr("id"); $.ajax({url: "addheart.php?id=" + a_id, success: function(result){ $("#count"+ a_id).html(result); }}); }) }); }) </script> And this is what I tried to add the thumbs down, and doesn't work (only showing what I added to it) And by the way, what I mean by it working on the other page, I'll show the complete script below this one document.getElementById("play_list").innerHTML = data; $('.heart').on("click", function(){ var a_id = $(this).attr("id"); $.ajax({url: "addheart.php?id=" + a_id, success: function(result){ $("#count"+ a_id).html(result); }}); }) document.getElementById("play_list").innerHTML = data; $('.heartno').on("click", function(){ var a_id = $(this).attr("id"); $.ajax({url: "addheartno.php?id=" + a_id, success: function(result){ $("#count2"+ a_id).html(result); }}); }) And this is what I have that is working on the other page, but doesn't work when I do this with the page that also has the calandar bit to it <script> $('.heart').on("click", function(){ var a_id = $(this).attr("id"); $.ajax({url: "addheart.php?id=" + a_id, success: function(result){ $("#count"+ a_id).html(result); }}); }) </script> <script> $('.heartno').on("click", function(){ var a_id = $(this).attr("id"); $.ajax({url: "addheartno.php?id=" + a_id, success: function(result){ $("#count2"+ a_id).html(result); }}); }) </script> Any thoughts on what I'm missing? SOLUTION! document.getElementById("play_list").innerHTML = data; $('.heart').on("click", function(){ var a_id = $(this).attr("id"); $.ajax({url: "addheart.php?id=" + a_id, success: function(result){ $("#count"+ a_id).html(result); }}); }) $('.heartno').on("click", function(){ var a_id = $(this).attr("id"); $.ajax({url: "addheartno.php?id=" + a_id, success: function(result){ $("#count2"+ a_id).html(result); }}); }) }); }) </script> Edited February 18, 2023 by PNewCode Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.