I'm annoyingly back again with a problem with my admin page. I pull data from the database and generate an html table with the data. I haven't decided what columns i'll display in the final draft but nonetheless, the last column is a button to give/remove admin status.
The basic idea is that when you click the add/remove buttons it changes the boolean value in the database and refreshes that TD, I was thinking this could be done through ajax but I haven't been able to figure out how to target the dynamically generated ID's or maybe I should use the parent of the click target? I'm afraid I'm in the proverbial tutorial hell so I'm unsure of everything anymore.
In the mysql, the is_admin column is a boolean and to generate the button I did
for(let i = 0; i < data.length; i++) {
let tr = document.createElement("tr");
let row = `
<td name="id" id=id-${data[i].id}>${data[i].id}</td>
<td name="name" id=name-${data[i].username}>${data[i].username}</td>
<td name="password" id=pass-${data[i].password}>${data[i].password}</td>
<input type="hidden" name="is_admin" value=${data[i].is_admin} id=${data[i].is_admin}>
<td>${data[i].is_admin == 1 ? "<button class='remove'>Remove Admin</button>" : "<button class='add' >Add Admin</button>"}</td>
`;
tr.innerHTML = row;
tr.classList.add("local");
table.appendChild(tr);
}
I'm trying to make it so if you're an admin, there's a "Remove Admin" button and if you're not, an "Add Admin" button displays, which works. But when you click the Remove admin button, It needs to update the is_admin for that user, which I assume I can do with with an sql UPDATE. Currently my function to give admin is
function addAdmin() {
let xhr = new XMLHttpRequest();
xhr.open("post", "add-admin.php", true);
xhr.setRequestHeader('Content-type', 'application/x-www.form.urlencoded');
xhr.onload = function() {
if(this.status == 200) {
console.log(this.responseText);
}
}
xhr.send();
}
As you can see, there's nothing going on yet because I'm not sure how to get the data to the php to update the database. Also, I'm using xhr because I haven't learned any jquery. Currently the php just echo's some text, and it does log it to the console. I'm lost past that though. I don't know how I'm supposed to pass the javascript variables, in this case ${data[i].is_admin}, to the php. I think I should be able to wrap the TR in a form tag that might let me pass it via POST?
Not that it's doing anything but the php currently is
<?php
session_start();
include("../../connection.php");
include("../../functions.php");
$user_data = check_login($conn);
?>
<script src="users-test.js"></script>
<?php
if(!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
else {
echo "Admin Added";
}
?>
I added the script tags to the php because I read online that you could do that but even if you can, I'm still lost on how to access the variables through php. I had seen somewhere you could so something like
let variable = <?php $phpvariable ?>;
to bring a php variable into javascript but i can't seem to figure out how to go the other way with it.
document.addEventListener('click',function(e){
if(e.target && e.target.className == 'add'){
console.log("add clicked");
addAdmin(e);
} else if (e.target && e.target.className == 'remove'){
console.log("remove clicked");
removeAdmin(e);
}
});
Edit to add event listener code
This project has definitely pushed me out of my comfort zone which I suppose is a good thing, I just hate feeling like I need to ask questions with nearly every other line of code for it. Sorry for the noob questions, I'm just a noob trying to not be a noob anymore.
Learn javascript they said. It'll be fun they said...
Anyway, thank you for your time. Sorry if some of the text overlaps, This was partially written last night and finished this morning when I woke up. Thank you again.
how is it possible to achieve this:
http://jennamolby.com/how-to-display-dynamic-content-on-a-page-using-url-parameters/
using php?
let's say that I have the following a url:
http://localhost:8888/index.php?page=pages-folder/works-folder/content-manager?article=my-article
to get there I have a link in pages-folder/works.php :
link
which should open content-manager.php in which inside a div I should load my-article.php
EDITED:
I have an index file in which a load into the div.container all the pages I need, so in this case my works.php file is loaded int the div.container using using:
<?php
$page = $_GET['page'];
if(!empty($page)){
$page .= '.php';
include($page);
}
else {
include('pages/home.php');
}
since I also needed to update the url without reloading the page I use this script:
function ChangeUrl(page, url) {
if (typeof (history.pushState) != "undefined") {
var obj = { Page: page, Url: url };
history.pushState(obj, obj.Page, obj.Url);
}
}
$('ul.menu li a').on('click', function(){
var page = $(this).attr('href');
var pageUrl = page.split("/");
pageUrl = pageUrl[1];
$('.container').load(page + '.php', function(){
//fadeout old content
//fadein new content
});
ChangeUrl('Page1', '?page=' + page);
return false;
})
once I have my works.php loaded into the div.container I have the above mentioned link which should lead me to: pages-folder/works-folder/content-manager.php
it is in this page where I'd like to load my-article.php inside the main div of content-manager.php
I thought that adding the ?article= variable would have worked using the same system as above:
$article = $_GET['article'];
if(!empty($article)){
$article .= '.php';
include($article);
}
else {
...
}
but it doesn't...
how can I achieve this?
Why you don't just add you article as a query param ?
http://localhost:8888/index.php?page=pages-folder/works-folder/content-manager&article=my-article
and make a link like this
link
This is just an exemple to understand what you want to do, don't use this kind of code in production, he is vulnerably to CSRF attack
EDIT: with echo it's better sorry
I haven't answered your question per se but this is the sort of code you are looking for:
<?php if (isset($_GET["page"]) && strtolower($_GET["page"]) == "1") { ?>
<p>You are on page one</p>
Back
<?php } elseif (isset($_GET["page"]) && strtolower($_GET["page"]) == "2") { ?>
<p>You are on page two</p>
Back
<?php } else { ?>
<p>You have not selected a page. Click one of the links:</p>
Page one
Page two
<?php } ?>
Explanation
How does $_GET work?
$_GET is a super global variable - meaning it can be accessed from anywhere.
It is a an associative array of variables passed to the current script via the URL parameters.
These are specified following a question mark (?) in the URL. To specify multiple parameters you must use the ampersand (&) character between each one.
$_GET must be specified at the end of the URL after everything else.
http://www.example.com/thisPage.php?page=a
http://www.example.com/thisPage.php?page=a&theme=light
The first URL will produce a $_GET with one element which can be accessed as: $_GET["page"] and would return a string of one character a.
The second will produce:
$_GET["page"]; // returns "a"
$_GET["theme"]; // returns "light"
Notice that for each parameter a new key-value pair is created.
I wrote a comprehensive explanation of superglobals on SO Documentation, but that has since been deprecated. RIP my hard work :P
Showing differing content
As you can see from my answer above. You can use simple if statements to check what the value is.
Firstly, ensure that $_GET isset and then check the value.
I have converted the value of the array to lowercase since "A" is not the same as "a".
The example you linked to really over-complicates things. There is honestly no need for all that regular expressions, and it also relies on JavaScript which is not necessarily a good idea.
With my example at the top, there is no difference between user experience as PHP is server sided thus all the content is worked out and then served to the user.
One step further
Using this you can go that extra step and have an event listener and combine it with AJAX.
Altering my initial example you can have the following.
I have used the jQuery library as it is a lot easier to implement.
<div id="test">
<?php if (isset($_GET["page"]) && strtolower($_GET["page"]) == "1") { ?>
<p>You are on page one</p>
Back
<?php } elseif (isset($_GET["page"]) && strtolower($_GET["page"]) == "2") { ?>
<p>You are on page two</p>
Back
<?php } else { ?>
<p>You have not selected a page. Click one of the links:</p>
Page one
Page two
<?php } ?>
</div>
function myAJAX() {
$("a").on("click", function(e) {
e.preventDefault();
// get the clicked page number
if (this.href.indexOf("&") > -1) {
var d = this.href.substring(this.href.indexOf("page=") + "page=".length, this.href.indexOf("&"))
} else {
var d = this.href.substr(this.href.indexOf("page=") + "page=".length)
}
$.ajax({
method: "GET",
url: "t.php",
data: "page=" + d,
success: function(data, textStatus, jqXHR) {
// change the content of the #test div
$("#test").html($($.parseHTML(data)).filter("#test")[0]);
myAJAX();
}
});
});
}
myAJAX();
Notice that the HTML is not being wrapped in <div id="test"> which is so that the JavaScript can find that element and change it in the function.
$("#test").html($($.parseHTML(data)).filter("#test")[0]); is the line that is fetching the HTML and changing it with the data from the page you tried to click on.
I also call the function inside itself so that it will reattach on the anchor links. If you remove this line then the page will redirect as normal.
The good thing about this implementation is that if your user does not have JavaScript then the page will act as normal and there will be a normal reload of the site.
No need for any extra work on your part.
I'm trying to create a generic "soft-search" menu dropdown for my bootstrap text-boxes, so that when a user types something on the keyboard, the app should dynamically lookup the back-end rest api through ajax and populate the results in a dynamically generated bootstrap menu just below it:
I use the jquery.bind to bind the keyup event of the input to read the input and dynamically create this dropdown. Now, the dropdown is generated fine, however, I want that when the user presses the "up/down" key, the focus should automatically shift to the dropdown menu, and then the user should select their choice using up/down keys again to select that particular option.
Now, the first part of this is achieved - the focus shifts to the first <li> element on the dropdown. However, the up/down keys aren't working on the dropdown. For example, in above case, when I click down key, the focus doesn't change, it stays stuck on the first item - "test--Foo Bar Limited". What can I do in order to make the dropdown "navigable" using the arrow keys once I shift focus to it? I've scavenged a lot of Internet, but unable to find the solution for this yet.
Below is my code for the keyup event where this dropdown is generated:
$("#searchbox").bind("keyup", function(e){
console.log(e.type, e.which, e.keyCode);
console.log('keycode:',e.key);
var text = $(this).val(); //+(e.key=='Backspace' ? '': e.key);
console.log('text:',text);
if (e.key=='ArrowUp' || e.key=='ArrowDown') {
//FOCUS ON THE DROPDOWN AND RETURN
e.preventDefault();
$('#thedrop li:first-child a').focus();
// setTimeout(function() {
// }, 700);
return;
}
$("#spinner").addClass('glyphicon-refresh glyphicon-spin');
//REST API CALL:
$.get("/client/search/" + text, function(data){
$("#spinner").removeClass('glyphicon-refresh glyphicon-spin');
//CREATE THE DROPDOWN
var darray = JSON.parse(data);
var drop = '<ul tabindex="1" id="thedrop" class="dropdown-menu" role="menu" aria-expanded="true" >';
for(var i=0;i<darray.length;i++){
console.log(darray[i]['ein'],'::',darray[i]['company_name']);
drop += '<li><a tabindex="-1" href="#">' + darray[i]['ein'] + '--' + darray[i]['company_name'] + '</a></li>';
}
drop += '</ul>';
var top = $('#searchbox').offset().top;
if (darray.length>0) {
$('#thedrop').remove();
$('body').append(drop);
$('#thedrop').css({
'top':top + $('#searchbox').outerHeight() ,
'left':$('#searchbox').offset().left,
});
$('#thedrop').show();
}
});
});
edit
I've looked up this and this questions, but none of them helps me. They are both proposing that bootstrap already supports up/down keys as standard in a dropdown, then what am I doing wrong?
Bootstrap's JS checks for and generates its JS components on page init. As such, and new items added to the DOM after init will need to be manually instantiated.
To do this you will need to call $('#thedrop').dropdown() after appending it to the body, like so:
...
if (darray.length>0) {
$('#thedrop').remove();
$('body').append(drop);
$('#thedrop').dropdown(); // <-- Inserted here
$('#thedrop').css({
'top':top + $('#searchbox').outerHeight() ,
'left':$('#searchbox').offset().left,
});
$('#thedrop').show();
}
...
I'm systematically building jQuery functions such that the css classes of various inputs in a web form have dependencies on other inputs (i.e. when a given input has a given value, the "hide" class is removed from the appropriate subsequent input etc.)
A specific (working) example of the jQuery I am using is:
$(document).ready(function(){
$("input[name$='q_4']").change(function(){
if(this.value == 'Yes') {
$('#qu_5').removeClass('hide');
} else {
$('#qu_5').addClass('hide');
}
});
});
In this example, the dependent question div (#qu_5) depends on the value entered via radio button for (name=q_4) to be "Yes".
Because I am building these functions dynamically (users can edit properties of questions such that they have these kinds of display dependencies) via a database, I end up with multiple chunks of this code on a page with several interdependent inputs. Each chunk of code has the name of the master question, the id of the slave question and the value that the slave relies on to be revealed. This also works as intended.
Sometimes however, one input should reveal multiple other questions so I end up with code something like:
$(document).ready(function(){
$("input[name$='q_87']").change(function(){
if(this.value == 'yes') {
$('#qu_88').removeClass('hide');
} else {
$('#qu_88').addClass('hide');
}
});
$("input[name$='q_87']").change(function(){
if(this.value == 'yes') {
$('#qu_89').removeClass('hide');
} else {
$('#qu_89').addClass('hide');
}
});
});
This does not work. (and indeed stops all the reveal / hide functions working on that page)
I presume it is because jQuery/javascript isn't happy with the same event input[name$='q_87']").change firing two different functions? This is the only thing I can think of.
Does anyone have any advice as to how I could achieve what I want in a way that works? Thanks! :)
If you need a var and an array you can write it like this
var questions = {
"q_87":["qu_88","qu_89"],
"q_96":["qu_95","qu_99"]
}
$.each(questions,function(q,arr) {
$("input[name$='"+q+"']").change(function(){
$("'#"+arr.join(",#")+"'").toggleClass('hide',this.value == 'yes');
});
});
So I have 2 html pages. 1 that functions as container and 1 that functions as content.
When I load the content page with a table I'm able to use drag and drop.
But when I go to my container page and load the content page into a div with ajax, the drag and drop stops working. All other javascript functionalities inside the content page still work. How can I bind the jquery dnd plugin to the table loaded with ajax?
I'm using drag & drop with this as tutorial http://isocra.com/2008/02/table-drag-and-drop-jquery-plugin/
my code looks like this:
$(window).load(function()
{ if(temp == 0)
{
DP("eerste keer")
load_table();
temp = 1;
}
} );
function load_table()
{
DP('load_table');
$.ajax({
//async: false,
type: "POST",
url: "/diagnose_hoofdpagina/table_diagnose/" + DosierID, // <== loads requested page
success: function (data) {
$("#diagnoses_zelf").html(''); //<== clears current content of div
$("#diagnoses_zelf").append(data).trigger('create'); // <== appends requested page
},
error: function(){
alert('error');
}
}).done(function() {
update_table();
initialize_table(); // <== calls jquery plug in
});
return false;
}
function initialize_table()
{
var tableid = $('#diagnoses_zelf table').attr('id'); //< this finds the correct table thanks to Fábio Batista => this option worked, rest didn't
alert(tableid);
$(tableid).tableDnD({
onDrop: function(table, row) {
alert(table + " " + row);
},
onDragStart: function(table,row){
var tette = $(row).index;
alert(tette);
},
dragHandle: ".dragHandle"
});
}
How is this possible and what can I do about it?
Can anyone help me with this please.
Very short:
I want access to the ID of the table I load into my container page with ajax and use the jquery drag and drop plug in on it.
EDIT
Findings:
Somehow my table in the container page got renamed to pSqlaTable instead of the id I gave to it in the controller page which is.
<table id="tableDiagnose" class="table table-hover">
Thats why the code couldn't find the table annymore Got fixed by this code thanks to Fábio Batista:
$('#diagnoses_zelf table').tableDnD( ... );
, but how can I use the dnd plugin now ?
It finds the table now, but I'm still not able to bind the dnd plugin to it, Am I able to bind a jquery plug in to ajax loaded tables ?
EDIT
//drag & drop http://isocra.com/2008/02/table-drag-and-drop-jquery-plugin/
function initialize_table()
{
var tableid = $('#diagnoses_zelf table').attr('id');
alert(tableid);
$('#' + tableid).tableDnD({
onDrop: function(table, row) {
alert(table + " " + row);
},
onDragStart: function(table,row){
alert('issemer?');
},
dragHandle: ".dragHandle"
});
}
This is the code i'm still stuck with. tableid is correct but the initialisation of the jquery isn't. I can't drag the drows in the table. Is my syntax wrong ?
EDIT
Could it be that I can't bind the jquery to the table because I dynamicaly generate the table on the other page with ZPT (or javascript) ?
The issue with plugins.
You're mixing lots of external libraries and code. This results in possible mis-matches between versions, and a lot of black boxes in your code.
As a developer, this should make you feel very uneasy. Having code you do not fully understand in your code base can get really frustrating really fast.
The alternative.
Often, these sort of plugins provide functionality we, as JavaScript developers can accomplish just as easily without them. This development process, in simple enough scenarios, lets us create code we understand and have an easier time maintaining. Not only do we learn from this process, but we also create smaller bits of specific code. Community driven solutions are very good in general, but it's important to remember they're not a silver bullet. Often you're stuck using a not-so-active project which has a bug for your specific case and you have to dig through a large, unfamiliar code base.
Your code
So what does this drag and drop plugin do?
Well, I'd break it down as the following:
Listens to the mousedown event on table rows
When such an event fires, start moving the table row to match the mouse position
When mouseup occurs, detect that, and finalize the position.
Let us see how we can do something similar.
Let's assume the table's HTML is something like:
<table>
<tbody>
<tr>
<td> Hello 1</td>
</tr><tr>
<td> Hello 2</td>
</tr>
</tbody>
</table>
Here is a fiddle with the table with some basic styling applied
Next, we'll listen to the selection events. We'll add an event to the table rows for selection and to the document to when the mouse is up. jQuery has event listeners for such events. Since we want these events to stick even after AJAX, we'll use .on which lets us use delegated events. .on means that even if we add content to the table later, it won't matter.
var selected; // currently selected row
$(document).on("mousedown","#MySpecialTable tr",function(){
$("#textDiv").text(this.textContent);
selected = this;
});
$(document).on("mouseup",function(){
$("#textDiv").text("Left "+selected.textContent);
selected = null;
});
Here is a working fiddle of such code.
Now, we'll want to actually change the drag&drop to work when, that is, update the current position to the one reflecting the mouse position. We can listen to mousemove events, and detect the element we're currently on. Something like
$(document).on("mousemove",function(e){
$("#textDiv").text($(e.target).html());
});
You can see a working fiddle here
That's nice, but we want to actually change the element position. So we'll need to change the table structure to allow that. We can remove the element, and append it at the correct position. We'll check if we have a selected element, and if we do, we can track it compared to the current element in the mousemove event. We can for starters detect if we should drag with something like:
$(document).on("mousemove",function(e){
if(selected !=null){// got an element selected
if($("#MySpecialTable").has(e.target).length > 0){ //in the table
$("#mousePos").text("DRAGGING");
}
}else{
$("#mousePos").text("NOT SELECTED");
}
});
(Fiddle)
Now, we'll add actual selection, we'll replace the elements when the target is not our element and we're in the table. Our full code should be something like:
var selected;
$(document).on("mousedown","#MySpecialTable tr",function(e){
e.preventDefault();//stop the text selection;
$("#textDiv").text(this.textContent);
selected = $(this);
selected.find("td").css("background-color","#999");
});
$(document).on("mouseup",function(){
$("#textDiv").text("Left "+selected.text());
selected.find("td").css("background-color","");
selected = null;
});
$(document).on("mousemove",function(e){
if(selected !=null){// got an element selected
if($("#MySpecialTable").has(e.target).length > 0){ //in the table
var el = $(e.target).closest("tr");//the tr element we're on
el.before(selected);// replace the elements
}
}else{
$("#mousePos").text("NOT SELECTED");
}
});
$("#MySpecialTable").on('selectstart', false);//Don't let the user select the table
(Fiddle)
Now, so far we only have a few lines of code, which is nice since we know exactly what's going on and didn't need to use lots of lines of external code we don't fully understand.
But will it AJAX?
Let's load the data into the table with AJAX and see! We'll simulate an AJAX response using a setTimeout which would allow us to simulate an asynchronous request. We'll use
setTimeout(function(){
$("#MySpecialTable").html("<tr><td> Hello 1</td></tr><tr><td> Hello 2</td></tr><tr><td> Hello 3</td></tr><tr><td> Hello 4</td></tr><tr><td> Hello 5</td></tr><tr><td> Hello 6</td></tr>");
},1000);
This means, update the HTML of #MySpecialTable after one second. Let's see if it works shall we?
So why does it work? well, we used delegated events which means we don't care if the elements we're loading are in the screen right now or not. We had the insight to do this since we built our code ourselves and knew what our final goal was. The only thing left to do is clean the code a little.
We'll wrap our code in the following, to prevent $ from being an issue in non-conflict mode (that is, $ is already taken in the page:
(function($){
})(jQuery);
Next we'll add a binding for our table event:
$.GertVDragTable = function(elementSelector){ // rest of code.
Eventually, our code might look something like this.
Using it, would be a simple $.GertVDragTable("#MySpecialTable"); alternatively, we can put it on $.fn and allow every function to call it. Which is a matter of taste.
No copy-pasta please :) I'd appreciate it if you stop on every stage and think why the next step was taken.
You don't need to use the ID as a selector, you can use any expression that can find your table.
If there's only one table on the resulting $.ajax call, you can search for "a table inside the container", using the container ID, which won't change:
$('#diagnoses_zelf table').tableDnD( ... );
If there's more than one table, use a different kind of selector, instead of the ID. A CSS class works fine:
$('table.table-diagnose').tableDnD( ... );
So does a data- attribute:
$("table[data-diagnose]").tableDnD( ... );
Try adding a title to your table, like so:
<table id = "tableDiagnose" class = "table table-hover" title = "table-content">
Then use the jQuery attribute selector to find this table instead of finding it by id.
$('table[title="table-content"]').tableDnD({
// the rest of your code
If your id is changing you should not use an ID then:
<table class="tableDiagnose table table-hover">
Plugin
function initialize_table()
{
$('.tableDiagnose.table').tableDnD({
onDrop: function(table, row) {
alert(table + " " + row);
},
dragHandle: ".dragHandle"
});
DP('nee');
}
EDIT: ajax is asynchronous :
function load_table()
{
DP('load_table');
$.ajax({
//async: false,
type: "POST",
url: "/diagnose_hoofdpagina/table_diagnose/" + DosierID, // <== loads requested page
success: function (data) {
$("#diagnoses_zelf").html(''); //<== clears current content of div
$("#diagnoses_zelf").append(data).trigger('create'); // <== appends requested page
update_table();
initialize_table(); // <== calls jquery plug in
},
error: function(){
alert('error');
}
});
//removed .done as you already have a success option in ajax
return false;
}
EDIT: found your bug........
you retrieve the table id then select it in $(tableid) but you missed the #
function initialize_table()
{
/*
var tableid = $('#diagnoses_zelf table').attr('id'); //< this finds the correct table thanks to Fábio Batista => this option worked, rest didn't
alert(tableid);
// but you really should limit the use of variables when you don't need them*/
//$('#'+tableid).tableDnD({
//like this directly
$('#diagnoses_zelf table').tableDnD({
onDrop: function(table, row) {
alert(table + " " + row);
},
onDragStart: function(table,row){
var tette = $(row).index;
//alert(tette);
},
dragHandle: ".dragHandle"
});
}
See the demo here
EDIT
Do you include the script file in the container page or in the content page? I guess you might want to try to load it when calling the dnd plugin with getScript:
...
$.getScript('pathTotableDnDlib').done(function(){
$(tableid).tableDnD({
onDrop: function(table, row) {
alert(table + " " + row);
},
onDragStart: function(table,row){
var tette = $(row).index;
alert(tette);
},
dragHandle: ".dragHandle"
});});
...
more on getscript: here
#BenjaminGruenbaum Hi thx a lot for the tutorial, i modified a bit the code to block the drag n'drop on table headers and to improve the drag fluidity tracking the mouse direction.
var old_y = 0;
(function ($) {
$.GertVDragTable = function (tableName) {
var selected;
$(document).on("mousedown", tableName+" tr",function (e) {
e.preventDefault(); //stop the text selection;
if (($(this).find('th').length)== 0){ //prevent dragging on tr containing th
selected = $(this);
selected.find("td").css("background-color", "black");
selected.find("td").css("color", "white");
}
});
$(document).on("mouseup", function () {
selected.find("td").css("background-color", "");
selected.find("td").css("color", "");
selected = null;
});
$(document).on("mousemove", function (e) {
if (selected != null ) { // got an element selected
if ($(tableName).has(e.target).length > 0) { //in the table
var el = $(e.target).closest("tr"); //the tr element we're on
if (el.find('th').length==0){ //prevent dropping on headers row
if (e.pageY > old_y){ //**
el.after(selected);}else{ //**-->more fluid dragging based on mouse direction
el.before(selected); //**
}
}
}
old_y = e.pageY;
}
});
$(tableName).on('selectstart', false); //Don't let the user select the table
}
})(jQuery);
here's the fiddle http://jsfiddle.net/59rdq/
I hope it will be useful for someone.