NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Klanowicze online
// @author Reskiezis
// @version 1.0
// @match http://*.margonem.pl/
// @match http://*.margonem.com/
// @grant none
// @license MIT
// ==/UserScript==
((Engine, hero, $) => {
'use strict'
const createStorage = () => {
let storage = {}
try {
storage = JSON.parse(
localStorage.getItem('klanowicze_online')
)
} catch(error) {}
//assigns object to storage
const setStorage = object => {
const modifiedStorage = {
...storage,
...object
}
storage = modifiedStorage
localStorage.setItem(
'klanowicze_online',
JSON.stringify(modifiedStorage)
)
}
const getStorage = () => ({
...storage
})
return { setStorage, getStorage }
}
const {
setStorage,
getStorage
} = createStorage()
let state = {
wasMembersHidden: false,
top: '10px',
left: '200px',
...getStorage()
}
//assigns object to state
const setState = object => {
state = {
...state,
...object
}
}
//assigns object to state and storage
const updateMemory = object => {
setState(object)
setStorage(object)
}
//Start UI
const appendCorners = element => {
const cor1 = document.createElement('div')
Object.assign(cor1.style, { background: 'url(img/tip-cor.png) no-repeat 0px 0px', position: 'absolute', top: '-6px', left: '-6px', width: '35px', height: '23px' })
const cor2 = document.createElement('div')
Object.assign(cor2.style, { background: 'url(img/tip-cor.png) no-repeat -35px 0px', position: 'absolute', bottom: '-6px', right: '-6px', width: '35px', height: '23px' })
element.appendChild(cor1)
element.appendChild(cor2)
}
const container = document.createElement('div')
container.id = 'klanowicze-online'
container.classList.add('default-cursor')
Object.assign(container.style, {
boxSizing: 'border-box',
position: 'absolute',
left: state.left,
top: state.top,
border: '3px gold double',
color: '#eeeeee',
width: '210px',
backgroundColor: 'black',
fontSize: '16px',
zIndex: 500
})
const title = document.createElement('div')
title.textContent = 'Klanowicze online'
Object.assign(title.style, {
fontSize: '1.1em',
textAlign: 'center',
fontWeight: 'bold',
borderBottom: '1px solid gold',
padding: '2px'
})
container.appendChild(title)
const membersTable = document.createElement('table')
Object.assign(membersTable.style, {
fontSize: '0.7em',
width: '100%',
borderCollapse: 'collapse',
tableLayout: 'fixed'
})
const membersTableWrapper = document.createElement('div')
membersTableWrapper.hidden = state.areMembersHidden
membersTableWrapper.style.padding = '1px'
membersTableWrapper.appendChild(membersTable)
container.appendChild(membersTableWrapper)
const hideMembersButton = document.createElement('div')
hideMembersButton.classList.add('do-action-cursor')
hideMembersButton.textContent = state.areMembersHidden
? 'Rozwiń'
: 'Zwiń'
Object.assign(hideMembersButton.style, {
fontSize: '0.8rem',
textAlign: 'center',
borderTop: '1px solid gold'
})
container.appendChild(hideMembersButton)
appendCorners(container)
$(container).draggable({
cancel: 'table', //can't drag membersTable
stop: function(){
const { top, left } = container.style
updateMemory({
top,
left
})
}
})
//style table rows and cells
const tableStyle = document.createElement('style')
tableStyle.textContent = `
.ko-row {
border: solid;
border-width: 1px 0;
border-color: #5d5006;
height: 1.6em;
}
.ko-row:hover {
background: #3c3c16;
}
.ko-row:first-child {
border-top: none;
}
.ko-row:last-child {
border-bottom: none;
}
.ko-row > * {
vertical-align: middle;
}
.ko-add-to-group-cell, .ko-nick-cell {
user-select: none;
}
.ko-add-to-group-cell:hover, .ko-nick-cell:hover {
color: #eaeb74;
}
.ko-add-to-group-cell {
text-align: center;
width: 12px;
}
.ko-map-cell {
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`
document.body.appendChild(tableStyle)
//End UI
const startWritingMessageTo = nick => {
const chatInput = document.querySelector(
'div[data-section="chat"] .input-wrapper input'
)
chatInput.value = `@${nick} `
chatInput.focus()
}
const chatWith = nick =>
() =>
startWritingMessageTo(nick)
const addToGroup = id =>
() =>
window._g(`party&a=inv&id=${id}`)
const fetchMembers = () =>
new Promise(resolve => {
//super API bulwo
const clan = Engine.clan
? { ...Engine.clan } //shallow copy
: Engine.clan
if(!clan)
Engine.clan = {
updateMembers(){}
}
_g(`clan&a=members`, ({ members2 }) => {
Engine.clan = clan
if(!members2){
resolve(null)
return
}
resolve(members2)
})
})
// [ id, nick, lvl, prof, map, x, y, ?, onlineStatus, icon ]
const MEMBER_TUPLE_LENGTH = 10
const renderOnlineMembers = members => {
if(members === null)
return
const membersTableBody = document.createElement('tbody')
let onlineMembersCount = 0
let wasHeroCounted = false
for(let i = 0; i <= members.length; i += MEMBER_TUPLE_LENGTH){
const onlineStatus = members[i+8]
if(onlineStatus !== 'online')
continue
onlineMembersCount++
const nick = members[i+1]
if(nick === hero.nick){
wasHeroCounted = true
continue
}
const id = members[i]
const lvl = members[i+2]
const prof = members[i+3]
const map = members[i+4]
const x = members[i+5]
const y = members[i+6]
const row = membersTableBody.insertRow()
row.classList.add('ko-row')
const addToGroupCell = row.insertCell()
addToGroupCell.textContent = '+'
addToGroupCell.dataset.tip = 'Dodaj do grupy'
addToGroupCell.classList.add('ko-add-to-group-cell', 'do-action-cursor')
addToGroupCell.addEventListener('click', addToGroup(id))
const nickCell = row.insertCell()
nickCell.textContent = `${nick} (${lvl}${prof})`
nickCell.classList.add('ko-nick-cell', 'do-action-cursor')
nickCell.addEventListener('click', chatWith(nick))
const mapCell = row.insertCell()
mapCell.textContent = map
mapCell.dataset.tip = `${map} (${x},${y})`
mapCell.classList.add('ko-map-cell')
}
if(!wasHeroCounted)
onlineMembersCount++
title.dataset.tip = onlineMembersCount === 1
? 'Jesteś tylko ty'
: `${onlineMembersCount} klanowiczów (łącznie z tobą)`
if(membersTable.tBodies.length === 0){
membersTable.appendChild(membersTableBody)
return
}
membersTable.replaceChild(membersTableBody, membersTable.tBodies[0])
}
const fetchAndRenderOnlineMembers = () =>
fetchMembers().then(renderOnlineMembers)
let fetchMembersInterval = null
const startMembersFetching = () => {
fetchAndRenderOnlineMembers()
fetchMembersInterval = setInterval(fetchAndRenderOnlineMembers, 10000)
}
const stopMembersFetching = () => {
clearInterval(fetchMembersInterval)
}
const handleHideMembersButtonClick = () => {
const modifiedAreMembersHidden = !state.areMembersHidden
membersTableWrapper.hidden = modifiedAreMembersHidden
updateMemory({
areMembersHidden: modifiedAreMembersHidden
})
if(modifiedAreMembersHidden){
stopMembersFetching()
hideMembersButton.textContent = 'Rozwiń'
return
}
startMembersFetching()
hideMembersButton.textContent = 'Zwiń'
}
hideMembersButton.addEventListener('click', handleHideMembersButtonClick)
const getDoggoImage = () => {
const doggo = new Image
doggo.src = 'https://i.imgur.com/jti8ksH.png'
doggo.style.width = '100%'
return doggo
}
const loadQueuePush = fn => {
if(Engine.allInit){
fn()
return
}
setTimeout(loadQueuePush, 2000, fn)
}
loadQueuePush(() => {
if(!hero.clan)
return
if(state.areMembersHidden || startMembersFetching(),
hero.clan.id === 13757 &&
Engine.worldName === 'hutena')
container.replaceChild(getDoggoImage(), title)
document.body.appendChild(container)
})
})(window.Engine, window.Engine.hero.d, window.$)