var watchedMenu; var isInThread = document.getElementById('threadIdentifier') ? true : false; var watcherAlertCounter = 0; var elementRelation = {}; var watcherDragInfo = {} function stopMovingWatched() { if (!watcherDragInfo.shouldMove) { return; } watcherDragInfo.shouldMove = false lockedDrag = false var body = document.getElementsByTagName('body')[0]; body.onmouseup = watcherDragInfo.originalMouseUp; } function startMovingWatched(evt) { if (watcherDragInfo.shouldMove || (typeof (lockedDrag) != 'undefined') && lockedDrag) { return; } evt.preventDefault(); lockedDrag = true; var body = document.getElementsByTagName('body')[0]; watcherDragInfo.originalMouseUp = body.onmouseup; body.onmouseup = function() { stopMovingWatched(); }; watcherDragInfo.shouldMove = true; evt = evt || window.event; var rect = watchedMenu.getBoundingClientRect(); watcherDragInfo.diffX = evt.clientX - rect.right; watcherDragInfo.diffY = evt.clientY - rect.top; } var moveWatched = function(evt) { if (!watcherDragInfo.shouldMove) { return; } evt = evt || window.event; var newX = (window.innerWidth - evt.clientX) + watcherDragInfo.diffX; var newY = evt.clientY - watcherDragInfo.diffY; if (newX < 0) { newX = 0; } if (newY < 0) { newY = 0; } var watchedPanel = document.getElementById('watchedMenu'); var upperXLimit = document.body.clientWidth - watchedPanel.offsetWidth; if (newX > upperXLimit) { newX = upperXLimit; } var upperYLimit = window.innerHeight - watchedPanel.offsetHeight; if (newY > upperYLimit) { newY = upperYLimit; } watchedPanel.style.right = newX + 'px'; watchedPanel.style.top = newY + 'px'; }; if (!DISABLE_JS) { var homepageLi = document.getElementById('navPosting2'); var postingLink, referenceNode if (homepageLi) { postingLink = homepageLi referenceNode = document.createElement('li'); if (homepageLi) { postingLink.parentNode.insertBefore(referenceNode, postingLink.nextSibling); } else { console.warn('no navPosting?') } } else { postingLink = document.getElementById('navPosting'); //var referenceNode = postingLink.nextSibling; // homepage wants an li but not the board pages referenceNode = document.createElement('span'); if (postingLink) { postingLink.parentNode.insertBefore(referenceNode, postingLink.nextSibling); } else { console.warn('no navPosting?') } postingLink.parentNode.insertBefore(document.createTextNode(' '), referenceNode); var divider = document.createElement('span'); divider.innerHTML = '/'; postingLink.parentNode.insertBefore(divider, referenceNode); postingLink.parentNode.insertBefore(document.createTextNode(' '), referenceNode); } var watcherButton = document.createElement('a'); watcherButton.innerHTML = ' watched threads'; watcherButton.id = 'watcherButton'; watcherButton.setAttribute('class', 'coloredIcon'); var watcherCounter = document.createElement('span'); watcherCounter.style.float = 'none'; watcherButton.appendChild(watcherCounter); postingLink.parentNode.insertBefore(watcherButton, referenceNode); watchedMenu = document.createElement('div'); var watchedMenuLabel = document.createElement('label'); watchedMenuLabel.innerHTML = ' Watched thread list'; watchedMenuLabel.onmousedown = function(event) { startMovingWatched(event); }; var showingWatched = false; var closeWatcherMenuButton = document.createElement('span'); closeWatcherMenuButton.id = 'closeWatcherMenuButton'; closeWatcherMenuButton.setAttribute('class', 'coloredIcon'); closeWatcherMenuButton.innerHTML = ''; closeWatcherMenuButton.onclick = function() { if (!showingWatched) { return; } var body = document.getElementsByTagName('body')[0]; body.removeEventListener('mousemove', moveWatched); showingWatched = false; watchedMenu.style.display = 'none'; }; watchedMenu.appendChild(closeWatcherMenuButton); // flip order for the float right watchedMenu.appendChild(watchedMenuLabel); watchedMenu.appendChild(document.createElement('hr')); watchedMenu.id = 'watchedMenu'; watchedMenu.setAttribute('class', 'floatingMenu'); watchedMenu.style.display = 'none'; document.body.appendChild(watchedMenu); watcherButton.onclick = function() { if (showingWatched) { return; } var body = document.getElementsByTagName('body')[0]; body.addEventListener('mousemove', moveWatched); showingWatched = true; watchedMenu.style.display = 'block'; } var ops = document.getElementsByClassName('innerOP'); for (var i = 0; i < ops.length; i++) { processOP(ops[i]); } var storedWatchedData = getStoredWatchedData(); for ( var currentBoard in storedWatchedData) { if (storedWatchedData.hasOwnProperty(currentBoard)) { var threads = storedWatchedData[currentBoard]; for ( var thread in threads) { if (threads.hasOwnProperty(thread)) { if (isInThread && currentBoard == boardUri && thread == threadId) { threads[thread].lastSeen = new Date().getTime(); localStorage.watchedData = JSON.stringify(storedWatchedData); } addWatchedCell(currentBoard, thread, threads[thread]); } } } } updateWatcherCounter(); scheduleWatchedThreadsCheck(); } function updateWatcherCounter() { watcherCounter.innerHTML = watcherAlertCounter ? '(' + watcherAlertCounter + ')' : ''; } function getStoredWatchedData() { var storedWatchedData = localStorage.watchedData; if (storedWatchedData) { storedWatchedData = JSON.parse(storedWatchedData); } else { storedWatchedData = {}; } return storedWatchedData; } function iterateWatchedThreads(urls, index) { index = index || 0; if (index >= urls.length) { updateWatcherCounter(); scheduleWatchedThreadsCheck(); return; } var url = urls[index]; localRequest('/' + url.board + '/res/' + url.thread + '.json', function gotThreadInfo(error, data) { if (error) { iterateWatchedThreads(urls, ++index); return; } data = JSON.parse(data); var posts = data.posts; if (posts && posts.length) { var lastPost = posts[posts.length - 1]; var parsedCreation = new Date(lastPost.creation); var storedWatchedData = getStoredWatchedData(); var watchData = storedWatchedData[url.board][url.thread]; if (parsedCreation.getTime() > watchData.lastReplied) { watchData.lastReplied = parsedCreation.getTime(); localStorage.watchedData = JSON.stringify(storedWatchedData); } if (!elementRelation[url.board] || !elementRelation[url.board][url.thread]) { addWatchedCell(url.board, url.thread, watchData); } else if (watchData.lastSeen >= watchData.lastReplied) { elementRelation[url.board][url.thread].style.display = 'none'; } else { watcherAlertCounter++; elementRelation[url.board][url.thread].style.display = 'inline'; } } iterateWatchedThreads(urls, ++index); }); } function runWatchedThreadsCheck() { watcherAlertCounter = 0; localStorage.lastWatchCheck = new Date().getTime(); var urls = []; var storedWatchedData = getStoredWatchedData(); for ( var board in storedWatchedData) { if (storedWatchedData.hasOwnProperty(board)) { var threads = storedWatchedData[board]; for ( var thread in threads) { if (threads.hasOwnProperty(thread)) { if (isInThread && board == boardUri && thread == threadId) { threads[thread].lastSeen = new Date().getTime(); localStorage.watchedData = JSON.stringify(storedWatchedData); } urls.push({ board : board, thread : thread }); } } } } iterateWatchedThreads(urls); } function scheduleWatchedThreadsCheck() { var lastCheck = localStorage.lastWatchCheck; if (!lastCheck) { runWatchedThreadsCheck(); return; } lastCheck = new Date(+lastCheck); lastCheck.setUTCMinutes(lastCheck.getUTCMinutes() + 5); setTimeout(function() { runWatchedThreadsCheck(); }, lastCheck.getTime() - new Date().getTime()); } function addWatchedCell(board, thread, watchData) { var cellWrapper = document.createElement('div'); var cell = document.createElement('div'); cell.setAttribute('class', 'watchedCell'); var labelWrapper = document.createElement('label'); labelWrapper.setAttribute('class', 'watchedCellLabel'); var label = document.createElement('a'); label.innerHTML = watchData.label || (board + '/' + thread); label.href = '/' + board + '/res/' + thread + '.html'; labelWrapper.appendChild(label); var notification = document.createElement('span'); notification.innerHTML = ''; notification.setAttribute('class', 'watchedNotification'); if (!elementRelation[board]) { elementRelation[board] = {}; } elementRelation[board][thread] = notification; if (watchData.lastSeen >= watchData.lastReplied) { notification.style.display = 'none'; } else { watcherAlertCounter++; } labelWrapper.appendChild(notification); cell.appendChild(labelWrapper); var button = document.createElement('span'); button.setAttribute('class', 'watchedCellCloseButton coloredIcon'); button.innerHTML = ''; cell.appendChild(button); button.onclick = function() { watchedMenu.removeChild(cellWrapper); var storedWatchedData = getStoredWatchedData(); var boardThreads = storedWatchedData[board]; if (!boardThreads || !boardThreads[thread]) { return; } delete boardThreads[thread]; localStorage.watchedData = JSON.stringify(storedWatchedData); } cellWrapper.appendChild(cell); cellWrapper.appendChild(document.createElement('hr')); watchedMenu.appendChild(cellWrapper); } function processOP(op) { var checkBox = op.getElementsByClassName('deletionCheckBox')[0]; var nameParts = checkBox.name.split('-'); var board = nameParts[0]; var thread = nameParts[1]; var watchButton = document.createElement('span'); watchButton.setAttribute('class', 'watchButton coloredIcon'); watchButton.title = "Watch Thread"; watchButton.innerHTML = ''; checkBox.parentNode.insertBefore(watchButton, checkBox.nextSibling.nextSibling); watchButton.onclick = function() { var storedWatchedData = getStoredWatchedData(); var boardThreads = storedWatchedData[board] || {}; if (boardThreads[thread]) { return; } var subject = op.getElementsByClassName('labelSubject'); var message = op.getElementsByClassName('divMessage')[0]; var label = (subject.length ? subject[0].innerHTML : null) || message.innerHTML.substr(0, 16).trim(); if (!label.length) { label = null; } boardThreads[thread] = { lastSeen : new Date().getTime(), lastReplied : new Date().getTime(), label : label }; storedWatchedData[board] = boardThreads; localStorage.watchedData = JSON.stringify(storedWatchedData); addWatchedCell(board, thread, boardThreads[thread]); }; }