806 lines
31 KiB
JavaScript
806 lines
31 KiB
JavaScript
/*jshint sub:true, esversion: 6, -W083 */
|
|
|
|
const debug = false;
|
|
|
|
// Default timer for jquery slide, except set to 0 if the user prefers reduced motion.
|
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
const slideTimer = prefersReducedMotion?0:400;
|
|
class Store {
|
|
constructor(title, storeID, state) {
|
|
this.itemsObj = {};
|
|
this.state = state || 'planning'; // states: planning/shopping/closed
|
|
|
|
let storeNum = $(".store").length+1;
|
|
this.title = title || "Store "+storeNum;
|
|
this.storeID = storeID || null;
|
|
|
|
let html = "";
|
|
html += "<div class='card store' style='width: 25rem; max-width: 90vw;' id='store"+storeNum+"'>";
|
|
html += " <div class='iconWrapper' style='position: absolute; top: 1px; left: 1px;'>";
|
|
html += " <nav aria-label='State changer'><ul class='pagination'>";
|
|
html += " <li class='page-item planningState "+(this.state === 'planning'?"active":"")+"'><span class='page-link'><img src='../icon/list-check.svg' class='ariaButton' alt='Planning state' title='Planning state' data-toggle='tooltip' tabindex=0 role='button' /></span></li>";
|
|
html += " <li class='page-item shoppingState "+(this.state === 'shopping'?"active":"")+"'><span class='page-link'><img src='../icon/basket2.svg' class='ariaButton' alt='Shopping state' title='Shopping state' data-toggle='tooltip' tabindex=0 role='button' /></span></li>";
|
|
html += " <li class='page-item closedState "+(this.state === 'closed'?"active":"")+"'><span class='page-link'><img src='../icon/check-all.svg' class='ariaButton' alt='Closed state' title='Closed state' data-toggle='tooltip' tabindex=0 role='button' /></span></li>";
|
|
html += " </ul></nav>";
|
|
html += " </div>";
|
|
html += " <div class='iconWrapper' style='position: absolute; top: 6px; right: 6px;'>";
|
|
html += " <img src='../icon/pencil-square.svg' class='editStoreName ariaButton' alt='edit name' data-toggle='tooltip' title='Edit store name' tabindex=0 role='button' />";
|
|
// html += " <img src='../icon/filter-square.svg' class='sortItems ariaButton' alt='sort items' data-toggle='tooltip' title='Sort items by appearance in store' tabindex=0 role='button' />";
|
|
html += " <img src='../icon/x-circle.svg' class='removeStore ariaButton' alt='remove store' data-toggle='tooltip' title='Remove store' tabindex=0 role='button' />";
|
|
html += " </div>";
|
|
html += " <div class='card-header'>"+this.title+"</div>";
|
|
html += " <div class='card-body' tabindex='-1'>";
|
|
|
|
html += HtmlElements.store.editStoreSection(this.title);
|
|
|
|
//
|
|
// html += " <div class='archiveSection' style='"+(this.state === 'closed'?'':'display: none; ')+"padding: 10px;'>";
|
|
// html += " <button class='btn btn-primary mb-1'>Archive all checked items</button>";
|
|
// html += " <button class='btn btn-secondary mb-1'>Archive all items</button>";
|
|
// html += " <button class='btn btn-secondary'>Take a picture of the receipt</button>";
|
|
// html += " </div>";
|
|
|
|
html += " <ul class='list-group list-group-flush storeItems dragHolder' data-storeid='"+this.storeID+"'>";
|
|
html += " <li class='list-group-item emptyList'>No items added</li>";
|
|
// html += " <li class='list-group-item draggable' data-itemid='0' data-storeid='"+this.storeID+"' style='height: 10px; width: 100%;'></li>";
|
|
html += " </ul>";
|
|
|
|
html += HtmlElements.store.addItemSection;
|
|
|
|
html += " </div>";
|
|
html += " <div class='card-footer subtotal'>Subtotal: <span class='priceWrapper price'>0.00</span></div>";
|
|
html += "</div>";
|
|
|
|
this.selector = $(html).appendTo("#stores");
|
|
|
|
HtmlElements.store.editStoreSectionEvents();
|
|
|
|
this.selector.find(".newItemName").on('keyup', productSearchEvent);
|
|
|
|
this.selector.find(".addItemForm").on('submit', ev => {
|
|
ev.preventDefault();
|
|
|
|
this.addItem($(ev.target).find('.newItemName').val(), $(ev.target).find('.newItemPrice').val()).done(json => {
|
|
this.selector.find('.newItemPrice').val(0);
|
|
this.selector.find('.newItemName').val("").focus();
|
|
});
|
|
});
|
|
|
|
|
|
/** CHANGE STATE **/
|
|
this.selector.find('.iconWrapper .page-item').on('click', ev=>{
|
|
this.selector.find('.page-item.active').removeClass('active');
|
|
|
|
$(ev.currentTarget).addClass('active');
|
|
|
|
if($(ev.currentTarget).hasClass('planningState')){
|
|
this.setState('planning');
|
|
this.selector.find(".card-body").focus();
|
|
}
|
|
else if($(ev.currentTarget).hasClass('shoppingState')){
|
|
this.setState('shopping');
|
|
this.selector.find(".card-body").focus();
|
|
}
|
|
else if($(ev.currentTarget).hasClass('closedState')){
|
|
this.setState('closed');
|
|
this.selector.find(".card-body").focus();
|
|
}
|
|
});
|
|
|
|
this.selector.find('.editStoreName').one('click', ev => { this.editNameFn(ev); });
|
|
// this.selector.find('.editStoreName').on('click', ev => { this.toggleEditStore(); });
|
|
|
|
this.selector.find('.removeStore').on('click', ev => {
|
|
if(confirm("Are you sure you want to remove this store?")){ this.removeStore(); }
|
|
});
|
|
|
|
$(function () {
|
|
$('[data-toggle="tooltip"]').tooltip();
|
|
});
|
|
|
|
// INIT DRAGGING
|
|
this.selector.find('.dragHolder').sortable({
|
|
handle: '.draggable',
|
|
filter: '.priceWrapper, .itemButtons',
|
|
group: 'shared',
|
|
ghostClass: 'bg-secondary',
|
|
delay: 100,
|
|
delayOnTouchOnly: true,
|
|
onEnd: (evt) => {
|
|
let newStore;
|
|
if(evt.from !== evt.to){
|
|
console.info("Item is moved to a new list.");
|
|
|
|
let fromStoreElem= $(evt.from);
|
|
if(fromStoreElem.find("li").length <= 1){
|
|
fromStoreElem.find('.emptyList').show();
|
|
}
|
|
|
|
let toStoreElem= $(evt.to);
|
|
newStore = toStoreElem.attr('data-storeid');
|
|
toStoreElem.find('.emptyList').hide();
|
|
}
|
|
else if(evt.oldIndex === evt.newIndex){
|
|
console.warn("Item wasn't moved! (?)");
|
|
return;
|
|
}
|
|
|
|
let itemElem = $(evt.item);
|
|
let itemId = itemElem.attr('data-itemid');
|
|
|
|
let afterId = itemElem.prev().attr('data-itemid');
|
|
|
|
if(typeof afterId === "undefined"){
|
|
afterId = 0;
|
|
}
|
|
|
|
this.setItemPosition(itemId, afterId, newStore);
|
|
}
|
|
});
|
|
|
|
/*this.draggingClass = new Draggable("draggable", 'dragHolder', 'drag-over', "#store"+storeNum,(moved, after)=> {
|
|
// console.log(moved, after);
|
|
|
|
let movedID = moved.attr('data-itemid');
|
|
let afterID = after.attr('data-itemid');
|
|
if(typeof movedID !== "undefined" && typeof afterID !== "undefined"){
|
|
this.setItemPosition(movedID, afterID);
|
|
|
|
if(moved.parent().parent().parent().attr('id') !== after.parent().parent().parent().attr('id')){
|
|
console.info("Moved item to different store, so refreshing all");
|
|
$("#refreshAll").trigger('click');
|
|
}
|
|
}
|
|
});*/
|
|
}
|
|
|
|
toggleEditStore(set){
|
|
set = set || "toggle";
|
|
|
|
if(set === "toggle"){
|
|
if(typeof this.editStoreState === "undefined" || this.editStoreState === "closed"){
|
|
set = "open";
|
|
}
|
|
else {
|
|
set = "close";
|
|
}
|
|
}
|
|
|
|
let cardBody = this.selector.find(".card-body");
|
|
let editStoreSection = this.selector.find(".editStoreSection");
|
|
let timer = slideTimer;
|
|
if(set === "open"){
|
|
cardBody.css({'height': cardBody.css('height'), 'min-height': '220px', "overflow": "hidden" });
|
|
editStoreSection.slideDown(timer);
|
|
|
|
$(document).one('click', () => { this.toggleEditStore('close'); });
|
|
this.selector.one('click', ev => { ev.stopPropagation(); });
|
|
this.selector.find('.editStoreForm').on('click', ev => { ev.stopPropagation(); });
|
|
this.editStoreState = "opened";
|
|
}
|
|
else {
|
|
editStoreSection.slideUp(timer, () => {
|
|
cardBody.css({'height': '', 'min-height': '', "overflow": '' });
|
|
});
|
|
this.editStoreState = "closed";
|
|
}
|
|
}
|
|
|
|
setState(state, animTime){
|
|
animTime = prefersReducedMotion?0:(animTime || 200);
|
|
let prevState = this.state;
|
|
|
|
if(state === "planning"){
|
|
this.state = "planning";
|
|
this.selector.find('ul').addClass('storePlanState').find('li:not(.emptyList)').addClass('draggable');
|
|
this.selector.find('li:not(.checkedItem) .itemAmountButtons').slideDown(animTime);
|
|
// this.selector.find('li:not(.checkedItem) .itemAmountText').slideUp(animTime);
|
|
// this.selector.find('.checkedItem .itemAmountText:not(.oneItem)').slideDown(animTime);
|
|
|
|
this.selector.find('.addItemFormWrapper').slideDown(animTime);
|
|
this.selector.find('.remItem').show();
|
|
// this.draggingClass.unpause();
|
|
}
|
|
if(state !== "planning"){
|
|
this.selector.find('ul').removeClass('storePlanState').find('li').removeClass('draggable');
|
|
this.selector.find('.itemAmountButtons').slideUp(animTime);
|
|
// this.selector.find('.itemAmountText:not(.oneItem)').slideDown(animTime);
|
|
this.selector.find('.addItemFormWrapper').slideUp(animTime);
|
|
this.selector.find('.remItem').hide();
|
|
// this.draggingClass.pause();
|
|
}
|
|
|
|
if(state === "shopping"){
|
|
this.state = "shopping";
|
|
this.selector.find('.checkItems').show().animate({ width: '14px', 'margin-right': '5px' }, animTime);
|
|
}
|
|
if(state !== "shopping"){
|
|
this.selector.find('.checkItems').animate({ width: '0', 'margin-right': '0' }, animTime, function(){ $(this).hide(); });
|
|
}
|
|
|
|
if(state === "closed"){
|
|
this.state = "closed";
|
|
|
|
}
|
|
if(state !== "closed"){
|
|
this.selector.find('.archiveSection').slideUp(animTime);
|
|
}
|
|
|
|
if(prevState !== state){
|
|
ajaxReq({ plan: 'setState', storeID: this.storeID, state: this.state });
|
|
}
|
|
}
|
|
|
|
editNameFn(ev){
|
|
if(ev.type === 'click'){
|
|
|
|
this.selector.find('.iconWrapper').hide();
|
|
// $(ev.target).parent().hide();
|
|
|
|
let headerElem = $(ev.target).parent().parent().find(".card-header");
|
|
let orgHtml = headerElem.html();
|
|
|
|
headerElem.html("<span style='display: none;'>"+orgHtml+"</span><form action='#' class='changeNameForm'><input type='text' class='newName' value='"+orgHtml+"'><input type='image' src='../icon/pencil-square.svg'></form>");
|
|
headerElem.find(".changeNameForm").on('submit keyup', ev2 => {
|
|
if(ev2.type === "submit"){
|
|
|
|
ev2.preventDefault();
|
|
|
|
let newName = $(ev2.target).find(".newName").val();
|
|
|
|
let that = this;
|
|
this.rename(newName).done(json => {
|
|
|
|
// success
|
|
headerElem.html(newName);
|
|
that.selector.find('.iconWrapper').show();
|
|
that.selector.find('.editStoreName').one('click keyup', ev3 => { this.editNameFn(ev3); });
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
headerElem.find(".newName").on('keyup', ev3 => { this.resetEditNameFn(ev3); });
|
|
headerElem.find('input').first().focus();
|
|
// $("body").one('click', ev3 => { this.resetEditNameFn(ev3); });
|
|
}
|
|
}
|
|
|
|
resetEditNameFn(ev){
|
|
if(
|
|
(ev.type==="keyup" && ev.keyCode === 27) ||
|
|
ev.type !== "keyup"
|
|
){
|
|
// cancel
|
|
let orgHtml = this.selector.find('.card-header span').html();
|
|
this.selector.find('.card-header').html(orgHtml);
|
|
|
|
this.selector.find('.iconWrapper').show();
|
|
this.selector.find('.editStoreName').one('click keyup', ev3 => { this.editNameFn(ev3); });
|
|
|
|
}
|
|
}
|
|
|
|
addItem(text, price, amount){
|
|
amount = amount || 1;
|
|
if(text.length > 0){
|
|
|
|
if(this.storeID === null){
|
|
this.getStoreID().done(json => { this.addItem(text, price, amount); });
|
|
|
|
return $.ajax();
|
|
}
|
|
|
|
let that = this;
|
|
return ajaxReq({ plan: 'addItem', storeID: this.storeID, name: text, price: price, amount: amount })
|
|
.done(json => {
|
|
return that.addItemHtml(text, price, json['data'], amount);
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
addItemHtml(text, price, itemID, amount, checked){
|
|
amount = amount || 1;
|
|
checked = Number(checked) || 0;
|
|
|
|
text = insertLinks(text);
|
|
|
|
try {
|
|
price = Number(price);
|
|
this.itemsObj[itemID] = { text: text, price: price, itemID: itemID, amount: amount, checked: checked };
|
|
|
|
let html =
|
|
"<li tabindex='0' class='list-group-item draggable"+(checked?' checkedItem':'')+"' id='item_"+itemID+"' data-itemid='"+itemID+"' style='height: 100%; min-width: 200px;'>" +
|
|
" <div style='display: flex;'>" + // draggable='true'
|
|
//" <span style='float: left; margin-right: 0px; width: 32px; margin-left: -22px;'><svg xmlns='http://www.w3.org/2000/svg' fill='gray' class='bi bi-grip-vertical' viewBox='0 0 16 16' height='32' width='32'> <path d='M7 2a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM7 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM7 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0z'></path></svg></span>" +
|
|
" <div style='flex: 10px; display: none; margin-right: 5px;' class='checkItems'><input type='checkbox'"+(checked?" checked":"")+"></div>" +
|
|
" <div style='flex: 55%;'>"+text+"</div>" +
|
|
|
|
" <div style='flex: 35%;' class='priceWrapper'>" +
|
|
" <span class='price'>"+(price*amount).toFixed(2)+"</span>" +
|
|
" <img src='../icon/dash-circle.svg' alt='Remove item' class='remItem ariaButton' data-itemID='"+itemID+"' data-price='"+price+"' style='margin-right: 3px;' tabindex='0' role='button'>" +
|
|
" </div>" +
|
|
" </div>" +
|
|
|
|
" <span class='itemAmountWrapper' style=''>" +
|
|
" <div class='itemButtons' style=''>" +
|
|
" <div class='input-group itemAmountButtons' style='width: fit-content; max-width: 80%; float: left; padding: 5px 10px;"+(this.state !== "planning" || checked?"display: none;":'')+"'>" +
|
|
// " <div class='' style='float: left;'>" +
|
|
" <button class='btn btn-outline-danger itemAmountBtn' type='button'>-</button>" +
|
|
" <input class='form-control itemAmount itemAmountBtn' type='number' value='"+amount+"' min='0' max='99' aria-label='Amount of item' >" +
|
|
(price !== 0?" <span class='input-group-text' style='padding-left: 3px; padding-right: 0; font-size: 12px;text-align: right;'>x<span class='price'>" + price.toFixed(2) + "</span></span>":"") +
|
|
" <button class='btn btn-outline-success itemAmountBtn' type='button'>+</button>" +
|
|
" </div>" +
|
|
// " <button class='btn btn-secondary' style='float: right;'>Save</button>" +
|
|
" </div>" +
|
|
|
|
|
|
// " <div class='itemAmountText "+(amount <= 1?"oneItem":"")+"' "+(this.state !== "shopping" || !checked?"style='display: none;'":'')+">" +
|
|
" <div class='itemAmountText "+(amount <= 1?"oneItem":"")+"' "+(amount <= 1?"style='display: none;'":'')+">" +
|
|
" Amount: <span class='itemAmount' style='padding-left: 10px;'>"+amount+"</span>x " +
|
|
(price !== 0?"<span class='price'>" + price.toFixed(2) + "</span>":"") +
|
|
" </div>" +
|
|
" </span>" +
|
|
|
|
"</li>\n";
|
|
|
|
this.selector.find("ul.storeItems").append(html);
|
|
this.selector.find(".emptyList").hide();
|
|
this.selector.find('#item_'+itemID+' .checkItems input').off().on('click', ev => {
|
|
if(this.checkItem(itemID)){
|
|
$(ev.currentTarget).prop('checked', true);
|
|
} else {
|
|
$(ev.currentTarget).prop('checked', false);
|
|
}
|
|
});
|
|
this.verify();
|
|
return true;
|
|
}
|
|
catch(e){
|
|
alert("Something failed. Try again.");
|
|
console.error(e, e.stack);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
checkItem(itemID){
|
|
let newChecked = true;
|
|
if(this.itemsObj[itemID].checked){
|
|
newChecked = false;
|
|
}
|
|
ajaxReq({ plan: 'checkItem', storeID: this.storeID, itemID: itemID, checked: (newChecked?"1":"0") })
|
|
.done(json => {
|
|
console.log("Successfully "+(newChecked?"checked":"unchecked")+" item.");
|
|
this.itemsObj[itemID].checked = newChecked;
|
|
|
|
if(newChecked === true){
|
|
this.selector.find('#item_'+itemID).addClass("checkedItem");
|
|
// this.draggingClass.moveToBottomAnimated('#item_'+itemID);
|
|
}
|
|
else {
|
|
this.selector.find('#item_'+itemID).removeClass("checkedItem");
|
|
}
|
|
});
|
|
return newChecked;
|
|
}
|
|
|
|
remItem(itemID, price){
|
|
let that = this;
|
|
|
|
if(this.state === "planning"){
|
|
|
|
return ajaxReq({ plan: 'remItem', storeID: this.storeID, itemID: itemID, price: price })
|
|
.done(json => {
|
|
// console.log("remItem return:", json);
|
|
return that.remItemHtml(itemID);
|
|
});
|
|
}
|
|
|
|
}
|
|
remItemHtml(itemID){
|
|
// for(let i = 0; i < this.items.length; i++){
|
|
// if(this.items[i].itemID === itemID){
|
|
// this.items.splice(i,1);
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
if(delete this.itemsObj[itemID]){
|
|
this.selector.find('#item_'+itemID).remove();
|
|
}
|
|
|
|
this.verify();
|
|
return true;
|
|
}
|
|
|
|
setItemAmount(itemID, amount){
|
|
// console.log(itemID, amount, this.items);
|
|
// this.items.forEach((item, key) => {
|
|
// if(item.itemID === itemID){
|
|
// this.items[key].amount = amount;
|
|
// this.selector.find('#item_'+itemID+" .priceWrapper .price").html(Number(amount*this.items[key].price).toFixed(2));
|
|
// }
|
|
// });
|
|
|
|
this.itemsObj[itemID].amount = amount;
|
|
this.selector.find('#item_'+itemID+" .priceWrapper .price").html(Number(amount*this.itemsObj[itemID].price).toFixed(2));
|
|
|
|
if(typeof this.itemAmountDelay === "undefined"){
|
|
this.itemAmountDelay = {};
|
|
}
|
|
|
|
if(typeof this.itemAmountDelay[itemID] !== "undefined"){ clearTimeout(this.itemAmountDelay[itemID]); }
|
|
|
|
this.itemAmountDelay[itemID] = setTimeout(() => {
|
|
return ajaxReq({ plan: 'updateItemAmount', storeID: this.storeID, itemID: itemID, newAmount: amount })
|
|
.done(()=>{ this.verify(); });
|
|
}, 500);
|
|
}
|
|
|
|
setItemPosition(itemID, afterID, newStore){
|
|
afterID = afterID || 0;
|
|
newStore = newStore || this.storeID;
|
|
|
|
if(itemID !== afterID){
|
|
return ajaxReq({ plan: 'moveItem', storeID: this.storeID, itemID: itemID, afterID: afterID, newStoreId: newStore });
|
|
}
|
|
return false;
|
|
}
|
|
|
|
verify(){
|
|
// UPDATE TOTAL PRICE
|
|
let total = 0;
|
|
// this.items.forEach(item => {
|
|
// total += item.price*item.amount;
|
|
// console.log("verify - item:' "+item.price+"*"+item.amount);
|
|
// });
|
|
|
|
for(const itemId in this.itemsObj){
|
|
let item = this.itemsObj[itemId];
|
|
total += item.price*item.amount;
|
|
}
|
|
|
|
$(this.selector).find(".subtotal .price").html(total.toFixed(2));
|
|
|
|
// SHOW if-empty MESSAGE
|
|
if(this.selector.find(".storeItems li").length <= 1){
|
|
this.selector.find(".emptyList").show();
|
|
}
|
|
|
|
// BIND add/remove item amount
|
|
let that = this;
|
|
this.selector.find('.itemAmountBtn').off().on('click change', function(e){
|
|
let itemid = $(this).parent().parent().parent().parent().attr('data-itemid');
|
|
let amountElem = $(this).parent().find('.itemAmount');
|
|
let newValue = Number(amountElem.val());
|
|
|
|
if($(this).html() === "-"){
|
|
newValue--;
|
|
}
|
|
else if($(this).html() === "+"){
|
|
newValue++;
|
|
}
|
|
|
|
if(newValue > 0 && newValue < 100){
|
|
amountElem.val(newValue);
|
|
that.setItemAmount(itemid, newValue);
|
|
|
|
let textAmountElem = $(this).parent().parent().parent().find('.itemAmountText');
|
|
textAmountElem.find('.itemAmount').html(newValue);
|
|
|
|
if(newValue === 1){
|
|
textAmountElem.addClass('oneItem');
|
|
textAmountElem.hide();
|
|
}
|
|
else {
|
|
textAmountElem.removeClass('oneItem');
|
|
textAmountElem.show();
|
|
}
|
|
}
|
|
});
|
|
|
|
//
|
|
this.selector.find('.editItemBtn').off().on('click', async ev => {
|
|
const clickedElem = $(ev.currentTarget);
|
|
|
|
let editItemSectionHtml = await HtmlElements.store.editItemSection();
|
|
let editItemSection = $(editItemSectionHtml);
|
|
|
|
editItemSection.insertAfter(clickedElem);
|
|
|
|
// Keep element open
|
|
clickedElem.parent().css('height', editItemSection.css('height'));
|
|
clickedElem.parent().parent().find('.itemAmountText').hide();
|
|
});
|
|
|
|
// DRAGGABLE
|
|
// this.draggingClass.update();
|
|
/*let draggingObj = null, dragPos = null;
|
|
this.selector.find('.draggable').unbind().on('dragstart dragover dragenter dragleave dragend', ev => {
|
|
// this.selector.find('.draggable').hammer().bind('swipe', ev => {
|
|
if(ev.type === 'dragover' || ev.type === 'dragenter'){
|
|
ev.preventDefault();
|
|
// console.log(ev);
|
|
$(ev.currentTarget).addClass('drag-over');
|
|
dragPos = ev.currentTarget;
|
|
}
|
|
else if(ev.type === 'dragstart'){
|
|
// console.log('drag start');
|
|
draggingObj = $(ev.target);
|
|
draggingObj.css('opacity', 0.2);
|
|
}
|
|
else if(ev.type === 'dragleave'){
|
|
$('.drag-over').removeClass('drag-over');
|
|
}
|
|
else if(ev.type === 'dragend'){
|
|
// console.log(draggingObj);
|
|
if(draggingObj === null){
|
|
console.warn('no object to move');
|
|
return;
|
|
}
|
|
|
|
// console.log("move object", draggingObj, "to", dragPos);
|
|
|
|
draggingObj.css('opacity', 1);
|
|
|
|
if( this.setItemPosition(draggingObj.attr('data-itemid'), $(dragPos).attr('data-itemid')) !== false){
|
|
|
|
if($(dragPos).attr('data-itemID') == null){
|
|
draggingObj.detach().insertAfter('.emptyList');
|
|
}
|
|
else {
|
|
draggingObj.detach().insertAfter(dragPos);
|
|
}
|
|
draggingObj = null;
|
|
}
|
|
|
|
$('.drag-over').removeClass('drag-over');
|
|
}
|
|
});
|
|
*/
|
|
|
|
// BIND remove-buttons
|
|
$(this.selector).find(".remItem").each((key, val) => {
|
|
let that = this;
|
|
$(val).off().on('click', function(){
|
|
// console.log("remItem", $(this).hasClass("confirm"), $(this));
|
|
if($(this).hasClass("confirm")){
|
|
|
|
that.remItem($(this).attr('data-itemid'), $(this).attr("data-price"));
|
|
try {
|
|
$(this).tooltip('dispose');
|
|
}
|
|
catch(ignore){}
|
|
}
|
|
else {
|
|
// console.log("remItem addClass");
|
|
$(".confirm").removeClass("confirm");
|
|
|
|
setTimeout(() => { $(this).addClass("confirm"); }, 10);
|
|
let newThat = this;
|
|
setTimeout(function(){ $(newThat).removeClass("confirm"); }, 5000);
|
|
}
|
|
});
|
|
});
|
|
|
|
$(this.selector).find(".ariaButton").off('keydown').on('keydown', function(e){
|
|
if(e.code === "Space" || e.code === "Enter"){
|
|
e.preventDefault();
|
|
$(this).trigger('click');
|
|
}
|
|
});
|
|
|
|
// Update the total-price
|
|
updateTotalPrice();
|
|
}
|
|
|
|
// STORE MANIPULATION
|
|
removeStore(){
|
|
this.selector.remove();
|
|
if(this.storeID != null){
|
|
ajaxReq({ plan: 'deleteStore', storeID: this.storeID, storeName: this.title, itemsLength: Object.keys(this.itemsObj).length })
|
|
.done(json => {
|
|
// console.log("Delete store response:",json);
|
|
});
|
|
}
|
|
}
|
|
|
|
getStoreID(){
|
|
let that = this;
|
|
|
|
return ajaxReq({ plan: 'saveStore', storeName: this.title })
|
|
.done(json => {
|
|
console.log("getStore:", json);
|
|
that.storeID = json['data'];
|
|
return that.storeID;
|
|
});
|
|
}
|
|
|
|
rename(newName){
|
|
this.title = newName;
|
|
|
|
if(this.storeID !== null){
|
|
return ajaxReq({ plan: 'renameStore', storeID: this.storeID, newName: newName });
|
|
}
|
|
|
|
// Return blank ajax as a false
|
|
return $.ajax();
|
|
}
|
|
}
|
|
|
|
let stores = [];
|
|
$("#stores").html("");
|
|
|
|
$("#refreshAll").on('click', ev => {
|
|
let storesElem = $("#stores");
|
|
storesElem.css('height', storesElem.height());
|
|
$(".tooltip").remove();
|
|
$(".elemHolder").remove();
|
|
getStores(spaceID).done(json => { $("#stores").css('height', ''); });
|
|
});
|
|
|
|
$("body").on('click', function(ev){
|
|
if( $(".price").find( ev.target ).length === 0){
|
|
$(".confirm").removeClass("confirm");
|
|
}
|
|
});
|
|
|
|
let totalPrice = 0;
|
|
function updateTotalPrice(){
|
|
totalPrice = 0;
|
|
for(const storeKey in stores){
|
|
const store = stores[storeKey];
|
|
|
|
for(const itemID in store.itemsObj){
|
|
totalPrice += store.itemsObj[itemID].amount*store.itemsObj[itemID].price;
|
|
}
|
|
}
|
|
$("#totalPrice").html(totalPrice.toFixed(2));
|
|
}
|
|
|
|
let spaceID = 0;
|
|
function getSpaces(){
|
|
$("#stores").html("Loading...");
|
|
return ajaxReq({plan: 'spaces'}).done(json => {
|
|
let optionsHtml = "";
|
|
|
|
// console.log(json);
|
|
|
|
let spaceNum = 1;
|
|
for(const spaceI in json.data.spaces){
|
|
const space = json.data.spaces[spaceI];
|
|
if(spaceID === 0){
|
|
spaceID = space['space_id'];
|
|
}
|
|
let spaceName = space['space_name'] || "Space "+spaceNum;
|
|
optionsHtml += "<option value='"+space['space_id']+"'>"+spaceName+"</option>\n";
|
|
spaceNum++;
|
|
}
|
|
|
|
let spaceSelectElem = $("#spaceSelect");
|
|
spaceSelectElem
|
|
.html(optionsHtml)
|
|
.on('change', ev => {
|
|
spaceID = $(ev.target).val();
|
|
getStores(spaceID);
|
|
});
|
|
|
|
if(typeof json.data.lastSpace !== "undefined"){
|
|
spaceID = Number(json.data.lastSpace);
|
|
spaceSelectElem.val(spaceID);
|
|
}
|
|
});
|
|
|
|
|
|
}
|
|
getSpaces().done(() => { getStores(spaceID); });
|
|
|
|
// GET STORES
|
|
function getStores(spaceID){
|
|
spaceID = spaceID || 0;
|
|
|
|
// $("#stores").html("");
|
|
stores = [];
|
|
|
|
let options = { plan: '' };
|
|
if(spaceID !== 0){
|
|
options = { plan: '', space: spaceID }
|
|
}
|
|
|
|
return $.getJSON('do.php', options)
|
|
.done(json => {
|
|
if(handleJsonErrors(json)){
|
|
return;
|
|
}
|
|
|
|
$("#stores").html("");
|
|
|
|
// console.log(json);
|
|
for(const store in json.data){
|
|
let storeKey = stores.length;
|
|
|
|
stores.push(new Store(json.data[store].name, json.data[store].plan_store_id, json.data[store].state));
|
|
for(const item in json.data[store].items){
|
|
stores[storeKey].addItemHtml(
|
|
json.data[store].items[item].name,
|
|
json.data[store].items[item].price,
|
|
json.data[store].items[item].plan_item_id,
|
|
json.data[store].items[item].amount,
|
|
json.data[store].items[item].checked);
|
|
}
|
|
stores[storeKey].setState(json.data[store].state, 1);
|
|
}
|
|
updateTotalPrice();
|
|
})
|
|
.fail(handleAjaxErrors);
|
|
}
|
|
|
|
|
|
function ajaxReq( data ){
|
|
if(typeof spaceID !== "undefined" && spaceID !== 0){
|
|
data.space = spaceID;
|
|
}
|
|
return $.ajax({
|
|
method: "POST",
|
|
url: "do.php",
|
|
data: data,
|
|
dataType: 'JSON'
|
|
})
|
|
.done(json => {
|
|
return !handleJsonErrors(json);
|
|
})
|
|
.fail(handleAjaxErrors);
|
|
}
|
|
|
|
function handleJsonErrors(json){
|
|
if(typeof json.status != "undefined" && json.status !== 0){
|
|
if(json.message === "Not logged in"){
|
|
location.href = '../login.php';
|
|
}
|
|
else {
|
|
alert(json.message);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
function handleAjaxErrors(jqxhr, textStatus, error){
|
|
if(textStatus === "parsererror" && jqxhr.responseText !== ""){
|
|
alert("An error occured:\n"+jqxhr.responseText);
|
|
}
|
|
else {
|
|
alert("An error occured:\n"+error);
|
|
}
|
|
}
|
|
|
|
function insertLinks(text){
|
|
let markdownLink = /\[([a-zA-ZæøåÆØÅ0-9 &+-]+)\]\(((?:|http(?:|s):\/\/)[a-zA-Z0-9\/.\-+?=&]+)\)/gm;
|
|
let m;
|
|
|
|
while((m = markdownLink.exec(text)) !== null){
|
|
// This is necessary to avoid infinite loops with zero-width matches
|
|
if(m.index === markdownLink.lastIndex){
|
|
markdownLink.lastIndex++;
|
|
}
|
|
|
|
text = text.replace(m[0], "<a href='"+m[2]+"' target='_blank'>"+m[1]+"</a>");
|
|
}
|
|
|
|
let linkRegex = /(?:([("']|)((?:[A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)(?:(?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/gm;
|
|
let n;
|
|
let inputText = text;
|
|
while((n = linkRegex.exec(inputText)) !== null){
|
|
// This is necessary to avoid infinite loops with zero-width matches
|
|
if(n.index === linkRegex.lastIndex){
|
|
linkRegex.lastIndex++;
|
|
}
|
|
|
|
if(n[1] === ""){
|
|
text = text.replace(n[0], "<a href='"+(n[0][0] !== "h"?"//":'')+n[0]+"' target='_blank'>"+(n[2].replace('https://','').replace('http://', ''))+(n[2].length<n[0].length?"/...":"")+"</a>");
|
|
}
|
|
}
|
|
|
|
return text;
|
|
} |