Skip to content

Instantly share code, notes, and snippets.

@lubieowoce
Last active February 25, 2020 14:34
Show Gist options
  • Save lubieowoce/ec398061fbaa6e5bc7ed99b04546fa06 to your computer and use it in GitHub Desktop.
Save lubieowoce/ec398061fbaa6e5bc7ed99b04546fa06 to your computer and use it in GitHub Desktop.
// 2020.02.25-r1
// url: .*reservise\.com/(?:calendar|clients).*
// F1 - add_benefit_card()
// F2 - edit_popup_price()
// F4 - click_popup_present()
// ShortKeys config
/*
[
{"key":"F1","action":"javascript","blacklist":"whitelist","sites":"*reservise.com/calendar*","open":false,"customName":"add-benefit-card","code":"add_benefit_card()","exported":true,"sitesArray":["*reservise.com/calendar*"],"activeInInputs":true},
{"key":"F2","action":"javascript","blacklist":"whitelist","sites":"*reservise.com/calendar*","open":false,"customName":"edit-popup-price","code":"edit_popup_price()","exported":true,"sitesArray":["*reservise.com/calendar*"],"activeInInputs":true},
{"key":"F4","action":"javascript","blacklist":"whitelist","sites":"*reservise.com/calendar*","open":false,"customName":"click-popup-present","code":"click_popup_present()","exported":true,"sitesArray":["*reservise.com/calendar*"],"activeInInputs":true}
]
*/
const PRICE = {
do_rozliczenia: 22,
cennink_trenerski: 19
}
const BENEFIT = "15.00;1646"
const KEYCODE_ESCAPE = $.ui.keyCode.ESCAPE
// document.removeEventListener('focusin', watch_price)
// document.addEventListener('focusin', watch_price)
// function watch_price(e) {
// let target = e.target
// if (target.id != 'id_price_list') {
// return
// }
// console.log("watching", target, "existing listeners:", $(target).data("events"))
// const event_types = ['keydown', 'keypress', 'keyup', 'input']
// for (event_type of event_types) {
// document.addEventListener(event_type, listen_price_keys, {capture: true})
// }
// target.addEventListener("focusout", (_) => {
// console.log("stopping watching", target)
// for (event_type of event_types) {
// document.removeEventListener(event_type, listen_price_keys)
// }
// }, {once: true, passive: true})
// }
// PRICE_INPUTTING_SPECIAL = false
// function listen_price_keys(e) {
// console.log(e)
// if (e.type == 'input') {
// return
// }
// if (!PRICE_INPUTTING_SPECIAL && e.key == ";") {
// PRICE_INPUTTING_SPECIAL = true
// return
// }
// else if (PRICE_INPUTTING_SPECIAL && e.key == "Enter") {
// PRICE_INPUTTING_SPECIAL = false
// return
// }
// else if (PRICE_INPUTTING_SPECIAL) {
// console.log("special", e.key)
// return
// }
// else if (!PRICE_INPUTTING_SPECIAL) {
// console.log(e.type, "watching, got key", e.key)
// }
// }
install_escape()
function install_escape() {
$(document).keyup(handle_escape);
}
function handle_escape(e) {
if (e.which === KEYCODE_ESCAPE) {
console.log('escape')
let _active = document.activeElement
let active = $(_active)
let is_focused_input = (active.is(':input') && active.is(':focus'))
if (!is_focused_input) {
close_current_popup()
}
console.log("blurring", active[0])
active.blur();
// if (active !== null && active !== document.body && is_visible(active) ) {
// let popup = find_enclosing_popup(active)
// if (popup !== null) {
// console.log("focusing parent:", popup)
// $(popup).focus()
// }
// } else {
// close_current_popup()
// }
}
}
async function edit_popup_price() {
let popup = find_current_popup()
if (popup === null || !is_reservation_popover(popup)) { return null }
return click(await edit_price(popup))
}
function click_popup_present() {
let popup = find_current_popup()
console.log('press_popup_present :: popup', popup)
if (popup === null || !is_reservation_popover(popup)) { return null }
let present = popup.querySelector('.checkPresence')
console.log('press_popup_present :: present', present)
if (present === null) { return null }
click(present)
return present
}
async function add_benefit_card() {
console.log('add_benefit_card')
if (typeof calendar === 'undefined') {
console.log("no access to `calendar` global var in window", window)
return
}
if (calendar.HAS_ADD_CLIENTS === true) {
let popup = find_current_popup()
if (popup === null || !is_reservation_popover(popup)) { return }
let custom_user_search = popup.querySelector('.custom-wrapper .custom-add-user-search')
if (custom_user_search === null) { return }
custom_user_search.focus()
} else {
let benefit_reservation = Object.entries(calendar.reservationsById)
.filter(([id, r]) => (
getp(r, ['event', 'className'], []).includes('class-event') &&
getp(r, ['event', 'title'], '').search(/karty zniżkowe/i) !== -1 &&
document.contains(getp(r, ['element', 0]))
))
.map(([_, r]) => r) [0]
let benefit_box = benefit_reservation.element[0]
// let benefit_box = [...document.querySelectorAll('.fc-event-title')].filter((n) => n.textContent.search('Karty zniżkowe') !== -1)[0].parentNode.parentNode
let btn = benefit_box.querySelector('.add-client-to-class-event-icon')
click(btn)
// await sleep(500)
// let _input = document.activeElement
// console.log('blurring', _input)
// $(_input).blur()
// await sleep(500)
// let popover = await retryUntil((x) => notNull(x) && no_spinner(x), find_reservation_popover, "popover search")
// let price = popover.querySelector('select#id_price_list')
// let price = await waitSelector(document, 'select#id_price_list', "price list search")
// $(price).val(BENEFIT)
// let input = document.querySelector('#add-client-form input.userprofile-autocomplete')
// input.focus()
// return price
}
}
function click(node) { console.log('click::', node); $(node).trigger('click') }
function hover(node) { $(node).trigger('mouseenter') }
function sleep(t) {
return new Promise(resolve => {
setTimeout(()=>resolve(null), t);
});
}
async function soon(f) {
await sleep(5000)
let x = f()
if (typeof x === 'Promise') {
x = await x
}
return x
}
function is_hidden(el) { return window.getComputedStyle(el).display === 'none' }
// function is_hidden(el) { return el.offsetParent === null }
function is_visible(el) { return window.getComputedStyle(el).display !== 'none' }
function no_add_missing_phone_number() {
function DummyFillPhoneNumberForm(url, onSuccess) {
this.url = url;
this.onSuccess = onSuccess;
console.log("fill number", this)
}
_FillPhoneNumberForm = FillPhoneNumberForm
FillPhoneNumberForm = DummyFillPhoneNumberForm
}
function search_selector(search_start, selector, search_direction) {
console.log('search_selector', search_start, selector, search_direction)
if (!is_visible(search_start)) { return null }
if (search_direction === 'up') {
return search_start.closest(selector)
} else if (search_direction === 'down') {
return getp([...search_start.querySelectorAll(selector)].filter(is_visible), 0, null)
}
else {
throw "invalid search direction: " + search_direction
}
}
function isEmpty(obj) { return Object.entries(obj).length === 0 }
function getp(obj, path, _default=undefined) {
if (typeof path !== 'object') { path = [path] }
for (let prop of path) {
// console.log("getp :: ", prop)
if (typeof obj[prop] !== 'undefined') {
obj = obj[prop]
} else {
return _default
}
}
return obj
}
function nice_modals() {
let style = `
.modal-backdrop {
display: none;
}
.modal {
overflow: unset;
overflow-y: unset;
bottom: unset;
left: unset;
scale: 0.7;
}
`
}
// function close_all_popups() {
// console.log('close_all_popups')
// // cleanPopovers() // Reservise - doesn't close [ Reject | Confirm ] prompts
// let btns = [...document.querySelectorAll('.popover .popover-close-button'), ...document.querySelectorAll('.modal-dialog [data-dismiss=modal]')]
// for (let x of btns) {
// if (x !== null && window.getComputedStyle(x).display !== 'none') {
// click(x)
// }
// }
// }
function close_current_popup() {
console.log('close_current_popup')
let popup = find_current_popup()
if (popup !== null) { console.log('close_current_popup :: closing'); return close_popup(popup) }
return null
}
function find_current_popup() {
console.log('find_current_popup')
let active = document.activeElement
console.log('find_current_popup :: active', active)
if (active !== null && is_visible(active)) {
let popup = find_enclosing_popup(active)
if (popup !== null) { console.log('find_current_popup :: found parent'); return popup }
}
let popup = find_popup(document.body)
if (popup !== null) { console.log('find_current_popup :: found child'); return popup }
return null
}
function close_popup(node) {
if(is_modal(node)) {
return close_modal(node)
} else if (is_popover(node)) {
return close_popover(node)
}
return null
}
function find_enclosing_popup(node) {
let modal = is_modal(node) ? node : find_modal(node, 'up')
if (modal !== null) { return modal }
let popover = is_popover(node) ? node : find_popover(node, 'up')
if (popover !== null) { return popover }
return null
}
function find_popup(node) {
let modal = find_modal(node, 'down')
if (modal !== null) { return modal }
let popover = find_popover(node, 'down')
if (popover !== null) { return popover }
return null
}
function is_modal(node) { return node.classList.contains('modal') }
function find_modal(search_start, search_direction) {
return search_selector(search_start, '.modal', search_direction)
}
function close_modal(node) {
console.log('close_modal', node)
let close = node.querySelector('[data-dismiss]')
console.log('close_modal :: close', close)
if (close === null) { return null }
// console.log('close_modal :: click', close)
click(close)
return close
}
function is_reservation_popover(node) { return is_popover(node) && node.classList.contains('reservationPopover') }
function is_popover(node) { return node.classList.contains('popover') }
function find_popover(search_start, search_direction) {
return search_selector(search_start, '.popover', search_direction)
}
function close_popover(node) {
console.log('close_popover', node)
let close = node.querySelector('.popover-close-button')
console.log('close_popover :: close', close)
if (close === null) { return null }
// console.log('close_popover :: click', close)
click(close)
return close
}
// soon(close_current_popup)
function find_reservation_popover() { return document.querySelector('.reservationPopover')}
function find_spinner(root) { return root.querySelector('.spinner')}
function no_spinner(root) { s = find_spinner(root); return isNull(s) || is_hidden(s) }
function switch_to_price_tab(node=null) {
console.log('switch_to_price_tab')
if (node === null) { node = document.querySelector('.reservationPopover') }
let btn = node.querySelector('a[href="#tab-payments"]')
if (btn !== null) {
click(btn)
}
}
function find_price_input(node=null) {
if (node === null) { node = document.querySelector('.reservationPopover') }
let price = node.querySelector('#id_price_list')
console.log('find_price_input ::', price)
return price
}
async function edit_price(node=null) {
console.log('edit_price')
if (node === null) { node = document.querySelector('.reservationPopover') }
let btn = await waitSelector(node, 'a.edit-prices', "edit btn search")
click(btn)
let price = await retryUntil(notNull, (()=>find_price_input(node)), "input search")
// if (price !== null) {
$(price).focus()
// }
return price
}
function input_price(ix, node=null) {
console.log('input_price')
if (node === null) { node = document.querySelector('.reservationPopover') }
let price = find_price_input()
if (price !== null) {
price.options.selectedIndex = ix
$(price).trigger('change')
}
// save_price()
}
function save_price(node=null) {
console.log('save_price')
if (node === null) { node = document.querySelector('.reservationPopover') }
let submit_btn = node.querySelector('form.price_form button[type="submit"]')
if (submit_btn !== null) {
click(submit_btn)
}
return submit_btn
}
function mark(node) {
if (!node.classList.contains('fc-event')) {
node = node.closest('.fc-event')
}
node.style.backgroundColor = 'tomato'
}
// calendar.reservationsById
// calendar.nonGhostsCache
// async function get_event_details(reservation_node) {
// click(reservation_node)
// await sleep(1000)
// let node = document.querySelector('.reservationPopover')
// let details = JSON.parse(node.querySelector('.event-details').getAttribute('data-event'))
// close_popup(node)
// node.style.backgroundColor = "tomato"
// return details
// }
// function event_details(node=null) {
// if (node === null) { node = document.querySelector('.reservationPopover') }
// return JSON.parse(node.querySelector('.event-details').getAttribute('data-event'))
// }
async function retryUntil(pred, f, label=null) {
let attempts = 20
while (attempts > 0) {
let x = f()
if (pred(x)) {
let msg = 'retryUntil :: done with'
if (label !== null) { msg += " " + label }
console.log(msg, x)
return x
} else {
await sleep(500)
let msg = 'retryUntil :: retrying'
if (label !== null) { msg += " " + label }
msg += " " + attempts + " attempts left"
console.log(msg)
}
attempts -= 1
}
throw ("retryUntil :: waited too long for " + label)
}
function notNull(x) { return x !== null }
function isNull(x) { return x === null }
function defined(x, path=null) {
if (path===null || path.length===0) {
return typeof x !== 'undefined'
} else {
return typeof getp(x, path) !== 'undefined'
}
}
async function waitSelector(root, selector, label=null) {
return await retryUntil(notNull, (() => root.querySelector(selector)), label!==null?label:selector)
}
function expect_defined(x, label) { if (typeof x === 'undefined') { throw "expected " + label + " to be defined"} }
function is_liga(reservation) {
return getp(reservation, ['event', 'clients', 0, 'annotation'], '') === 'LIGA ŚRODA';
}
async function set_liga_dorozliczenia() {
let ligaIds = (
Object.entries(calendar.reservationsById)
.filter(([id, r]) => is_liga(r))
.map(([id, _]) => id)
)
let i = 0
for (let ligaId of ligaIds) {
// if (i > 6) { break }
let eventNode = calendar.reservationsById[ligaId].element[0]
console.log(ligaId, eventNode)
hover(eventNode)
await sleep(500)
click(eventNode)
$(eventNode).trigger('mouseleave')
await sleep(800)
await retryUntil((x) => notNull(x) && no_spinner(x), find_reservation_popover, "popover search")
let details = await retryUntil((x) => defined(x) && defined(x, 'classList') && !(x.classList.contains('spinner')),
() => getp(calendar.reservationsById[ligaId], ['detailsPopover', 'popoverContent', 0])
)
await edit_price(details)
await sleep(500)
input_price(PRICE.do_rozliczenia, details)
await sleep(500)
save_price(details)
// await retryUntil(isNull, find_price_input, "price input disappear")
await sleep(100)
await retryUntil(no_spinner,
() => getp(calendar.reservationsById[ligaId], ['detailsPopover', 'popoverContent', 0])
)
await sleep(500)
// i+=1
// await sleep(2000)
let close_btn = await retryUntil(defined, () => getp(calendar.reservationsById[ligaId], ['detailsPopover', 'popoverContent', 2]))
// expect_defined(close_btn, 'close_btn')
click(close_btn)
await retryUntil(isNull, find_reservation_popover, "popover disappear")
// await sleep(2000)
// i += 1
}
}
// set_liga_dorozliczenia()
// soon(add_benefit_card)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment