There are 24 previous versions of this script.
the source is over 100KB, syntax highlighting in the browser is too slow
// ==UserScript==
// @name Avanturist.org.PATCH
// @description Avanturist.org forum PATCH
// @version 0.11.3
// @include http://avanturist.org*
// @include http://www.avanturist.org*
// @include https://avanturist.org*
// @include https://www.avanturist.org*
// ==/UserScript==
var scriptVersion = "0.11.3";
var scriptDate = "17.08.2009";
var app;
var wnd = typeof unsafeWindow != "undefined" ? unsafeWindow : window;
var userAgent = navigator.userAgent.toLowerCase();
var isOpera = /opera/.test(userAgent);
var url = location.href;
var clearUrl = url.replace(/#.*/, "");
// XPath до тела сообщения. При этом в качестве начала пути надо использовать document
// TODO Перенести в какой-нибудь класс, когда закончу рефакторинг
var xpathMsgTableBody = "//div[@class='divMain']/div[@class='divContainer']/" +
"div[@class='content']/div[@class='text']/div[not(@class)]/" +
"table[@class='table']/tbody[1]/tr/td/table[@class='table']/tbody[1]";
var xpathPostBody = xpathMsgTableBody + "/tr[2]/td[2]/div[@class='post']";
var debug = /^.*#enable_log$/.test(url);
var start;
var log;
if (debug) {
if (isOpera) {
log = function(msg) {
opera.postError("AVANTURIST.ORG: " + msg);
}
} else {
log = function(msg) {
GM_log("AVANTURIST.ORG: " + msg);
}
}
} else {
log = function(msg) {
}
}
var logError;
if (isOpera) {
logError = function(msg) {
opera.postError("AVANTURIST.ORG ERROR: " + msg);
}
} else {
logError = function(msg) {
GM_log("AVANTURIST.ORG ERROR: " + msg);
}
}
log("User-Agent: " + userAgent);
log("Is Opera? " + isOpera);
log("url = " + url);
// ****************************************************************************
// ********************************* HTML *************************************
// ****************************************************************************
// TODO: Здесь одна проблема, форма вставляется на страницу, и в одном случае оказывается вложенной.
// По-моему, вложенные формы запрещены, но в данный момент работает, благодаря div
var formHtml =
"<div id='york_div%idx%' style='visibility: hidden; z-index: 10; position: absolute; background-color: rgb(245, 245, 250); border-style: solid; border-width: 1px; border-color: rgb(60, 97, 164); padding: 3px'>" +
" <form id='york_form%idx%' method='get' action='/forum/index.php'>" +
" <input type='hidden' id='york_topic%idx%' name='topic' value=''/>" +
" <input type='hidden' id='york_start%idx%' name='start' value=''/>" +
// Номер страницы:
" \u041d\u043e\u043c\u0435\u0440 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b:" +
" <input type='text' id='york_page%idx%' value='' maxlength='4' size='4'/>" +
// Перейти
" <input type='submit' value='\u041f\u0435\u0440\u0435\u0439\u0442\u0438'/>" +
" </form>" +
"</div>";
// ****************************************************************************
// ******************************** HELPERS ***********************************
// ****************************************************************************
function $id(id) {
return document.getElementById(id);
}
function $x(xpath, context) {
context = context || document.body;
return document.evaluate(xpath, context, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}
function $x1(xpath, context) {
var snapshot = $x(xpath, context);
if (snapshot.snapshotLength > 0) {
return snapshot.snapshotItem(0);
} else {
return null;
}
}
// TODO переделать, надо бы topic_id где-нибудь хранить, а не вычислять каждый раз
function getTopicId() {
var elements = document.getElementsByName("topic_id");
if (0 < elements.length) {
return elements[0].value;
} else {
return null;
}
}
function showHideForm(e) {
log("showHideForm()");
e = e || window.event;
var src = e.srcelement ? e.srcelement : e.target;
var divId = src.getAttribute("div_id");
var div = $id(divId);
if (div.style.visibility == "visible") {
div.style.visibility = "hidden";
log("Form " + divId + " was hidden");
} else {
div.style.visibility = "visible";
var inputId = src.getAttribute("input_id");
if (inputId != null) {
var input = $id(inputId);
input.focus();
}
log("Form " + divId + " was showed");
}
e.returnValue = false;
if (typeof e.preventDefault != "undefined") {
e.preventDefault();
}
return false;
}
// ****************************************************************************
// ***************************** CTopicWrapper ********************************
// ****************************************************************************
var topicWrapper;
function CTopicWrapper() {
log("CTopicWrapper()");
this.boards = new Array();
// TODO дублирование кода
var xpathDivMain = "div[@class='divMain']";
var xpathDivContainer = xpathDivMain + "/div[@class='divContainer']";
var xpathDivContent = xpathDivContainer + "/div[@class='content']";
var xpathDivText = xpathDivContent + "/div[@class='text']";
// TODO собрать работу с xpath в одном месте
var xpath = xpathDivText + "/div[not(@class)]/table[contains(@class, boardTable)]/tbody/tr[th[@colspan='7']/table[@class='boardTable']]";
var snapshot = $x(xpath);
log("Number of found boards: " + snapshot.snapshotLength);
for (var i = 0; i < snapshot.snapshotLength; i++) {
var boardTr = snapshot.snapshotItem(i);
var boardIdx = this.boards.length;
var board = new CBoard(boardTr, boardIdx);
this.boards[boardIdx] = board;
}
};
CTopicWrapper.onClick = function(e) {
log("CTopicWrapper.onClick()");
e = e || window.event;
var src = e.srcelement ? e.srcelement : e.target;
var boardIdx = src.getAttribute("board_idx");
board = topicWrapper.boards[boardIdx];
board.onClick();
e.returnValue = false;
if (typeof e.preventDefault != "undefined") {
e.preventDefault();
}
return false;
};
CTopicWrapper.prototype = {
collapse: function() {
log("CTopicWrapper.collapse()");
for (var i = 0; i < this.boards.length; i++) {
this.boards[i].collapse();
}
},
expand: function() {
log("CTopicWrapper.expand()");
for (var i = 0; i < this.boards.length; i++) {
this.boards[i].expand();
}
},
boards: []
};
// ****************************************************************************
// ******************************** CBoard ************************************
// ****************************************************************************
function CBoard(boardTr, boardIdx) {
log("CBoard(): boardIdx = " + boardIdx + ", row index = " + boardTr.rowIndex);
this.board = boardTr;
this.isCollapsed = false;
this.href = null;
this.hiddenTopics = [];
// TODO переписать
// TODO добавить настройку
// Всегда сворачиваю раздел "Модерация"
var collapseAll = 7 == boardIdx;
var table = boardTr.parentNode;
var rows = table.rows;
var hasHidden = false;
for (var i = boardTr.rowIndex + 1; i < rows.length; i++) {
var topicTr = rows[i];
if (topicTr.cells.length != 7) {
break;
}
var topic = new CTopic(this, topicTr);
if (topic.isRead || collapseAll) {
this.hiddenTopics.push(topic);
}
}
var xpath = "th/table[@class='boardTable']/tbody/tr[1]";
var tr = $x1(xpath, boardTr);
if (tr) {
this.href = document.createElement("A");
this.href.href = "#";
this.href.innerHTML = this.collapseText;
this.href.setAttribute("board_idx", boardIdx);
this.href.addEventListener("click", CTopicWrapper.onClick, false);
if (this.hiddenTopics.length == 0) {
this.href.style.visibility = "hidden";
}
var th = document.createElement("TH");
th.width = "20%";
th.style.textAlign = "center";
th.appendChild(this.href);
tr.insertBefore(th, tr.cells[1]);
tr.cells[0].width = "40%";
tr.cells[2].width = "40%";
tr.cells[2].style.textAlign = "right";
// Выравниваю название раздела влево
tr.cells[0].align = "left";
// TODO переписать
// Если есть список модераторов, то помещаю его в новую строку таблицы
var small = $x1("th[1]/small", tr);
if (null != small) {
var tr2 = document.createElement("TR");
th = document.createElement("TH");
th.colSpan = 3;
th.align = "left";
th.appendChild(small);
tr2.appendChild(th);
var table = tr.parentNode;
table.appendChild(tr2);
}
} else {
logError("TR wasn't found");
}
};
CBoard.prototype = {
collapse: function() {
log("CBoard.collapse(): row index = " + this.board.rowIndex);
for (var i = 0; i < this.hiddenTopics.length; i++) {
this.hiddenTopics[i].collapse();
}
if (this.href != null) {
this.href.innerHTML = this.expandText;
} else {
logError("this.href == null. It isn't expected");
}
this.isCollapsed = true;
},
expand: function() {
log("CBoard.expand()");
for (var i = 0; i < this.hiddenTopics.length; i++) {
this.hiddenTopics[i].expand();
}
this.href.innerHTML = this.collapseText;
this.isCollapsed = false;
},
onClick: function() {
log("CBoard.onClick()");
if (this.isCollapsed) {
this.expand();
} else {
this.collapse();
}
},
// "СВЕРНУТЬ"
collapseText: "\u0421\u0412\u0415\u0420\u041d\u0423\u0422\u042c",
// "РАЗВЕРНУТЬ"
expandText: "\u0420\u0410\u0417\u0412\u0415\u0420\u041d\u0423\u0422\u042c"
};
// ****************************************************************************
// ******************************** CTopic ************************************
// ****************************************************************************
function CTopic(board, topicTr) {
log("CTopic(): row index = " + topicTr.rowIndex);
CTopic.nextId++;
topicTr.setAttribute("topicId", CTopic.nextId);
CTopic.topics[CTopic.nextId] = this;
this.board = board;
this.topic = topicTr;
this.isRead = topicTr.cells[2].innerHTML.indexOf("new_post.gif") == -1;
var xpath = "td[3]/a[1]";
var topicHref = $x1(xpath, topicTr);
if (topicHref != null) {
var regexp = /^.*[\/?&]topic[=,](\d+).*$/;
var matches = regexp.exec(topicHref.href);
if (matches != null && matches.length == 2) {
this.topicId = matches[1];
} else {
logError("Topic ID wasn't found. URL: " + topicHref.href + ", matches = " + matches);
}
} else {
logError("Topic HREF wasn't found");
}
};
CTopic.find = function(topicTr) {
var topicId = topicTr.getAttribute("topicId");
if (topicId == null) {
return null;
}
return CTopic.topics[topicId];
}
CTopic.topics = new Array();
CTopic.nextId = 0;
CTopic.prototype = {
collapse: function() {
log("CTopic.collapse()");
this.topic.style.display = "none";
},
expand: function() {
log("CTopic.expand()");
this.topic.style.display = "";
}
};
// ****************************************************************************
// ********************************* OTHER ************************************
// ****************************************************************************
function createGotoPageForms(topicId) {
log("createGotoPageForms(" + topicId + ")");
var tables = document.body.getElementsByTagName("TABLE");
var found = 0;
for (var i = 0; i < tables.length && found < 2; i++) {
var table = tables[i];
var innerTables = table.getElementsByTagName("TABLE");
if (innerTables != null && innerTables.length > 0) {
continue;
}
// "Страниц:"
var pages = "\u0421\u0442\u0440\u0430\u043d\u0438\u0446:";
if (table.innerHTML.indexOf(pages) != -1) {
log("Page navigator #" + found + " is found");
// Нашли таблицу, включающую список страниц
// Ищем элемент с текстом "Страниц:"
var cell = table.rows[0].cells[0];
// Это нужный элемент!
var text = cell.childNodes[0];
// Убеждаемся, что это он
if (text.nodeType == 3 && text.nodeValue.indexOf(pages) == 0) {
var href = document.createElement("A");
href.appendChild(text);
href.href = "#";
href.setAttribute("div_id", "york_div" + found);
href.setAttribute("input_id", "york_page" + found);
href.addEventListener("click", showHideForm, false);
href.style.fontWeight = "bold";
cell.insertBefore(href, cell.childNodes[0]);
var span = document.createElement("SPAN");
span.id = "york_span" + found;
span.innerHTML = formHtml.replace(/%idx%/g, found);
cell.insertBefore(span, cell.childNodes[1]);
var form = $id("york_form" + found);
form.setAttribute("idx", found);
form.addEventListener("submit", submitGotoPageForm, false);
var topic = $id("york_topic" + found);
topic.value = topicId;
found++;
}
}
}
}
function submitGotoPageForm(e) {
log("submitGotoPageForm()");
e = e || window.event;
var src = e.srcelement ? e.srcelement : e.target;
var idx = src.getAttribute("idx");
var inputStart = $id("york_start" + idx);
var inputPage = $id("york_page" + idx);
inputStart.value = (inputPage.value - 1) * 20;
}
// Почему-то не работают всякие хитрые jQuery запросы, поэтому просо перебираю элементы
function getArchiveSourceId(href) {
var childrens = $(href).parent().children();
for (var i = 0; i < childrens.length; i++) {
var node = childrens.get(i);
if (node.name == "source_id") {
return node.value;
}
}
log("source_id wasn't found! target = " + href);
return null;
}
function archiveSeen() {
log("archiveSeen()");
var source_id = getArchiveSourceId(this);
if (!source_id || source_id == 'undefined') {
// "Ошибка подтверждения нового поступления в ваш личный архив!"
alert("\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u0432 \u0432\u0430\u0448 \u043b\u0438\u0447\u043d\u044b\u0439 \u0430\u0440\u0445\u0438\u0432!");
logError("Can't approve the archive card: source_id is undefined");
return;
}
// отправляем запрос
$.getJSON("/myarchive/seen/" + source_id, {source_id: source_id}, function(data) {
if (data.result == 1) {
$('#ga_myarchive_card_' + source_id).slideUp("slow");
} else {
// "Ошибка: "
alert("\u041e\u0448\u0438\u0431\u043a\u0430: " + data.result_status);
logError("The archive card with source_id=" + source_id + " wasn't confirmed. " +
"The server returns an error. data.result_status = " + data.result_status);
}
});
return false;
}
function journalSeen() {
log("journalSeen()");
var source_id = getArchiveSourceId(this);
if (!source_id || source_id == 'undefined') {
// "Ошибка подтверждения нового поступления в ваш личный журнал!"
alert("\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u0432 \u0432\u0430\u0448 \u043b\u0438\u0447\u043d\u044b\u0439 \u0436\u0443\u0440\u043d\u0430\u043b!");
logError("Can't approve the journal card: source_id is undefined");
return;
}
// отправляем запрос
$.getJSON(
"/myjournal/seen/" + source_id,
{source_id: source_id},
function(data) {
if (data.result == 1) {
$('#ga_myjournal_card_' + source_id).slideUp("slow");
} else {
// "Ошибка: "
alert("\u041e\u0448\u0438\u0431\u043a\u0430: " + data.result_status);
logError("The jorunal card with source_id=" + source_id + " wasn't confirmed. " +
"The server returns an error. data.result_status = " + data.result_status);
}
}
);
return false;
}
function archiveDelete() {
log("archiveDelete()");
// "Вы действительно хотите удалить данный материал?"
if (!confirm("\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b?")) {
return false;
}
var source_id = getArchiveSourceId(this);
if (!source_id || source_id == 'undefined') {
// "Ошибка удаления нового поступления в ваш личный архив!"
alert("\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u0432 \u0432\u0430\u0448 \u043b\u0438\u0447\u043d\u044b\u0439 \u0430\u0440\u0445\u0438\u0432!");
logError("Can't delete the archive card: source_id is undefined");
return;
}
// отправляем запрос
$.getJSON("/myarchive/delete/" + source_id, {source_id: source_id}, function(data) {
if (data.result == 1) {
$('#ga_myarchive_card_' + source_id).slideUp("slow");
} else {
// "Ошибка: "
alert("\u041e\u0448\u0438\u0431\u043a\u0430: " + data.result_status);
logError("The archive card with source_id=" + source_id + " wasn't deleted. " +
"The server returns an error. data.result_status = " + data.result_status);
}
});
return false;
}
function journalDelete() {
log("journalDelete()");
// "Вы действительно хотите удалить данный материал?"
if (!confirm("\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b?")) {
return false;
}
var source_id = getArchiveSourceId(this);
if (!source_id || source_id == 'undefined') {
// "Ошибка удаления нового поступления в ваш личный журнал!"
alert("\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u0432 \u0432\u0430\u0448 \u043b\u0438\u0447\u043d\u044b\u0439 \u0436\u0443\u0440\u043d\u0430\u043b!");
logError("Can't delete the archive card: source_id is undefined");
return;
}
// отправляем запрос
$.getJSON(
"/myjournal/delete/" + source_id,
{source_id: source_id},
function(data) {
if (data.result == 1) {
$('#ga_myjournal_card_' + source_id).slideUp("slow");
} else {
// "Ошибка: "
alert("\u041e\u0448\u0438\u0431\u043a\u0430: " + data.result_status);
logError("The journal card with source_id=" + source_id + " wasn't deleted. " +
"The server returns an error. data.result_status = " + data.result_status);
}
}
);
return false;
}
/*
* Если ссылка длинная, то обрезает её, чтобы она не портила форматирование
*/
function cutLongLink(link, redirectUrl) {
log("cutLongLink()");
var href = link.href;
var html = link.innerHTML;
if (href.indexOf(redirectUrl) == 0) {
href = href.substr(redirectUrl.length);
}
if (href.indexOf("http://") == 0 && html.indexOf("http://") == 0 && html.length > 80) {
var found = href == html;
if (!found) {
var htmlAmp = html.replace(/&/g, "&");
found = href == htmlAmp;
if (!found) {
var decodedHref;
try {
decodedHref = decodeURI(href);
} catch (err) {
decodedHref = href;
logError("Can't decode href: " + href);
}
var decodedHtml;
try {
decodedHtml = decodeURI(htmlAmp);
} catch (err) {
decodedHtml = htmlAmp;
logError("Can't decode htmlAmp: " + htmlAmp);
}
found = decodedHref == decodedHtml;
} else {
log("href == htmlAmp");
}
} else {
log("href == html");
}
if (found) {
log("Found long link: " + href);
link.innerHTML = html.substr(0, 77) + "...";
link.style.fontStyle = "italic";
}
}
}
function processMessageLinks() {
log("processMessageLinks()");
var redirectUrl = "http://www.avanturist.org/redirect.php?url=";
var xpath = xpathPostBody + "//a";
var snapshot = $x(xpath);
log("processMessageLinks(): number of found links: " + snapshot.snapshotLength);
for (var i = 0; i < snapshot.snapshotLength; i++) {
var link = snapshot.snapshotItem(i);
cutLongLink(link, redirectUrl);
if (link.href.indexOf(redirectUrl) == 0) {
// Во всех ссылках надо заменить & на его URL encode представление
link.href = link.href.replace(/&/g, "%26");
}
}
}
// ****************************************************************************
// ************************** Обработчики страниц *****************************
// ****************************************************************************
function handleArchivePage() {
log("handleArchivePage()");
var seenHrefs = $(".ga_myarchive_seen");
if (seenHrefs != null && seenHrefs.length > 0) {
seenHrefs.unbind("click");
seenHrefs.attr("class", "");
seenHrefs.click(archiveSeen);
} else {
// "\"Подтвердить\" links weren't found!"
log("\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\" links weren't found!");
}
var deleteHrefs = $(".ga_myarchive_delete");
if (deleteHrefs != null && deleteHrefs.length > 0) {
deleteHrefs.unbind("click");
deleteHrefs.attr("class", "");
deleteHrefs.click(archiveDelete);
} else {
// "\"Удалить\" links weren't found!"
log("\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c\" links weren't found!");
}
}
function handleJournalPage() {
log("handleJournalPage()");
// TODO: дублирование кода с handleArchivePage(), и не в рамках MVC
var seenHrefs = $(".ga_myjournal_seen");
if (seenHrefs != null && seenHrefs.length > 0) {
seenHrefs.unbind("click");
seenHrefs.attr("class", "");
seenHrefs.click(journalSeen);
} else {
// "\"Подтвердить\" links weren't found!"
log("\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\" links weren't found!");
}
var deleteHrefs = $(".ga_myjournal_delete");
if (deleteHrefs != null && deleteHrefs.length > 0) {
deleteHrefs.unbind("click");
deleteHrefs.attr("class", "");
deleteHrefs.click(journalDelete);
} else {
// "\"Удалить\" links weren't found!"
log("\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c\" links weren't found!");
}
}
function handleTopicPage() {
log("handleTopicPage()");
var topicId = getTopicId();
log("topic_id = " + topicId);
if (topicId != null) {
createGotoPageForms(topicId);
}
processMessageLinks();
}
function handleBoardPage() {
log("handleBoardPage()");
}
function handleForumMainPage() {
log("handleForumMainPage()");
// TODO workaround
if (app.controllers[0].settings.sett_collapseTopics) {
topicWrapper = new CTopicWrapper();
topicWrapper.collapse();
} else {
log("Don't collapse the read topics");
}
}
function handleOtherForumPages() {
log("handleOtherForumPages()");
// TODO: IMPLEMENT
}
function handleOtherSitePages() {
log("handleOtherSitePages()");
// TODO: IMPLEMENT
}
// ****************************************************************************
// ****************************** Точка входа *********************************
// ****************************************************************************
function onLoad(e) {
log("onLoad()");
start = new Date();
log("START: " + start);
if (!document.body) {
logError("document.body hasn't been loaded");
return;
}
if (typeof $ == "undefined") {
$ = wnd.jQuery;
}
main();
}
function main(e) {
var jQueryInstalled = true;
if (typeof $ == "undefined") {
logError("jQuery hasn't installed. URL: " + url);
$ = function() {};
jQueryInstalled = false;
}
// ************************************************************************
// **************************** Settings Model ****************************
// ************************************************************************
// Вид и контроллер находятся ниже, т.к. может оказаться, что нет необходимости
// создавать
function CSettingsModel() {
log("CSettingsModel()");
this.isVisible = false;
this.sett_collapseTopics = false;
this.label_sett_collapseTopics =
// Сворачивать
"\u0421\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c " +
// прочитанные
"\u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 " +
// темы после загрузки
"\u0442\u0435\u043c\u044b \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 " +
// главной страницы
"\u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b";
this.sett_searchPosts = false;
this.label_sett_searchPosts =
// Автоматически
"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 " +
// искать
"\u0438\u0441\u043a\u0430\u0442\u044c " +
// сообщения
"\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f";
this.sett_confirmSearchPosts = false;
this.label_sett_confirmSearchPosts =
// Выдавать запрос
"\u0412\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 " +
// перед началом
"\u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c " +
// поиска сообщения
"\u043f\u043e\u0438\u0441\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f";
this.sett_borderTables = false;
this.label_sett_borderTables =
// Рисовать
"\u0420\u0438\u0441\u043e\u0432\u0430\u0442\u044c " +
// границы у
"\u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0443 " +
// таблиц в
"\u0442\u0430\u0431\u043b\u0438\u0446 \u0432 " +
// сообщениях
"\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u0445";
this.sett_addImageLinks = false;
this.label_sett_addImageLinks =
// Добавлять
"\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c " +
// ссылки к
"\u0441\u0441\u044b\u043b\u043a\u0438 \u043a " +
// уменьшенным
"\u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u043d\u044b\u043c " +
// изображениям
"\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c";
this.userIgnoreType = CSettingsModel.BLACK_LIST;
this.userIgnoreListBlack = new Array();
this.userIgnoreListWhite = new Array();
this.load();
}
CSettingsModel.BLACK_LIST = 0;
CSettingsModel.WHITE_LIST = 1;
CSettingsModel.prototype = {
store: function(name, value) {
log("CSettingsModel.store()");
if (isOpera) {
var yearInSec = 365 * 24 * 60 * 60;
var now = (new Date()).getTime();
var fiveYearsLater = new Date(now + 5 * 1000 * yearInSec);
document.cookie = escape(name) + "=" + escape(value) +
";expires=" + fiveYearsLater.toGMTString() +
";path=/";
} else {
GM_setValue(name, value);
}
},
restore: function(name) {
log("CSettingsModel.restore()");
if (isOpera) {
var value = null;
var cookies = document.cookie.split("; ");
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].split("=");
if (cookie[0] == escape(name)) {
try {
value = unescape(cookie[1]);
} catch(e) {
logError("Can't unescape the value of the cookie: '" + cookie[0] + "=" + cookie[1] + "'");
}
break;
}
}
return value;
} else {
return GM_getValue(name);
}
},
save: function() {
log("CSettingsModel.save()");
var value = "";
for (var key in this) {
if (key.indexOf("sett_") == 0) {
if (value.length > 0) {
value += ",";
}
value += key + '=' + this[key];
}
}
if (value.length > 0) {
value += ",";
}
value += "userIgnoreType=";
value += this.userIgnoreType;
var listBlack = this.userIgnoreListToString(this.userIgnoreListBlack);
value += ",userIgnoreListBlack=";
value += listBlack;
var listWhite = this.userIgnoreListToString(this.userIgnoreListWhite);
value += ",userIgnoreListWhite=";
value += listWhite;
this.store("york_settings", value);
},
load: function() {
log("CSettingsModel.load()");
var value = this.restore("york_settings");
if (value == null) {
logError("Stored settings weren't found");
return;
}
var values = value.split(",");
for (var i = 0; i < values.length; i++) {
var setting = values[i].split("=");
if (setting[0].indexOf("sett_") == 0) {
this[setting[0]] = eval(setting[1]);
} else if (setting[0] == "userIgnoreType") {
this.userIgnoreType = parseInt(setting[1]);
} else if (setting[0] == "userIgnoreList") {
// Обрабатываю эту настройку для совместимостью с версиями до 0.08.x включительно
if (this.userIgnoreType == CSettingsModel.BLACK_LIST) {
this.userIgnoreListFromString(this.userIgnoreListBlack, setting[1]);
} else {
this.userIgnoreListFromString(this.userIgnoreListWhite, setting[1]);
}
} else if (setting[0] == "userIgnoreListBlack") {
this.userIgnoreListFromString(this.userIgnoreListBlack, setting[1]);
} else if (setting[0] == "userIgnoreListWhite") {
this.userIgnoreListFromString(this.userIgnoreListWhite, setting[1]);
} else {
logError("CSettingsModel.load(): Unknown setting: " + values[i]);
}
}
},
userIgnoreListFromString: function(list, str) {
var idNamePairs = str.split("|");
var len = idNamePairs.length;
for (var i = 0; i < len; i++) {
if (idNamePairs[i] != null && idNamePairs[i].length > 0) {
var pair = idNamePairs[i].split(":");
var id = NaN;
try {
id = parseInt(pair[0]);
} catch (e) {
id = NaN;
}
if (!isNaN(id)) {
list[pair[0]] = pair[1] == null ? "" : pair[1];
}
}
}
},
userIgnoreListToString: function(list) {
var users = "";
if (list.length > 0) {
var regexp = /[,:|]/g;
for (var id in list) {
if (users.length > 0) {
users += "|";
}
var name = list[id];
users += id;
users += ":";
if (!isOpera) {
users += name.replace(regexp, "_");
}
}
}
return users;
}
};
// Конец CSettingsModel
// ************************************************************************
var settingsModel = new CSettingsModel();
if (settingsModel.sett_searchPosts) {
// В целях оптимизации объявляю класс CPostSearcher только если задана
// соответсвующая настройка. После объявления класса идёт использующий
// его код
// ********************************************************************
// ************************** CPostSearcher ***************************
// ********************************************************************
function CPostSearcher(msdId) {
log("CPostSearcher(" + msgId + ")");
this.msgId = msgId;
this.topicId = getTopicId();
if (null == this.topicId) {
throw "CPostSearcher(): topic_id wasn't found";
}
// TODO дублирование кода
// Нахожу навигатор по страницам и определяю текущую страницу (находится в теге <b>)
// Ищем тэг B, после которого идёт текст, содержащий "]"
var xpath = "//body/div[@class='divMain']/div[@class='divContainer']/" +
"div[@class='content']/div[@class='text']/div[not(@class)]/" +
"table[not(@id)]/tbody[1]/tr[1]/td[@class='middletext']/" +
"b[following-sibling::node()[position()=1 and contains(self::text(), ']')]]";
// TODO $id("content")
var b = $x1(xpath, $id("content"));
if (b == null) {
throw "CPostSearcher(): Navigator wasn't found";
}
this.currentPage = parseInt(b.innerHTML);
log("currentPage = " + this.currentPage);
// TODO дублирование кода
// Определяю номер последней страницы темы (последняя ссылка в навигаторе)
xpath = "//body/div[@class='divMain']/div[@class='divContainer']/" +
"div[@class='content']/div[@class='text']/div[not(@class)]/" +
"table[not(@id)]/tbody[1]/tr[1]/td[@class='middletext']/" +
"a[@class='navPages' and position()=last()]";
// xpath = "tbody[1]/tr[1]/td[1]/table[not(@id)]/tbody[1]/tr[1]/td[@class='middletext']/a[@class='navPages' and position()=last()]";
// TODO $id("content")
// var href = $x1(xpath, $id("content"));
var href = $x1(xpath);
this.lastPage = href != null ? parseInt(href.innerHTML) : 1;
this.lastPage = Math.max(this.lastPage, this.currentPage);
log("lastPage = " + this.lastPage);
var params = CPostSearcher.parseUrlParameters(url);
this.searchFrom = parseInt(params["search_from"]);
this.searchTo = parseInt(params["search_to"]);
this.searchMid = parseInt(params["search_mid"]);
// Ниже mode может быть изменён!
this.mode = 1;
if (!this.initPageMessageIds()) {
throw "CPostSearcher(): Can't initialize page message ids";
}
if (this.firstPageMsgId > this.lastPageMsgId) {
// У пользователя сообщения в обратном порядке! Поэтому все идентификаторы
// сообщений буду брать со знаком минус, в этом случае алгоритм почти
// не надо менять!
this.mode = -1;
this.msgId *= -1;
this.firstPageMsgId *= -1;
this.lastPageMsgId *= -1;
}
};
CPostSearcher.prototype = {
searchMessage: function() {
log("CPostSearcher.searchMessage()");
if (isNaN(this.searchFrom) || isNaN(this.searchTo)) {
// Этап I. Первая загрузка страницы
return this.firstPageLoad();
} else if (isNaN(this.searchMid)) {
// Этап II. Поиск диапазона страниц, содержащих сообщение
return this.searchPageInterval();
} else {
// Этап III. Поиск сообщения где-то между двумя страницами
return this.binaryPageSearch();
}
},
// Этап I. Первая загрузка страницы
// Проверяем, есть ли искомое сообщение на странице, и если нет,
// то определяем в какую сторону двигаться чтобы его найти
firstPageLoad: function() {
log("CPostSearcher.firstPageLoad()");
var msg =
// "Вы пытаетесь
"\u0412\u044b \u043f\u044b\u0442\u0430\u0435\u0442\u0435\u0441\u044c " +
// перейти к
"\u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a " +
// сообщению № "
"\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044e \u2116 " + this.msgId +
// .\nЭто сообщение
".\n\u042d\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 " +
// не найдено на
"\u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u043d\u0430 " +
// текущей
"\u0442\u0435\u043a\u0443\u0449\u0435\u0439 " +
// странице №
"\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u2116 " + this.currentPage +
// .\nНачать поиск
".\n\u041d\u0430\u0447\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a " +
// сообщения?\n
"\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f?\n" +
// (В процессе
"(\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 " +
// поиска
"\u043f\u043e\u0438\u0441\u043a\u0430 " +
// страница будет
"\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0431\u0443\u0434\u0435\u0442 " +
// неоднократно
"\u043d\u0435\u043e\u0434\u043d\u043e\u043a\u0440\u0430\u0442\u043d\u043e " +
// перегружена.
"\u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0435\u043d\u0430.)";
var step = 3;
this.searchFrom = this.currentPage;
if (this.msgId < this.firstPageMsgId) {
if (this.currentPage == 1) {
// Встаём на первое сообщение на странице
return this.jumpToMessage(1, this.firstPageMsgId);
} else {
step = Math.floor(this.currentPage / step);
step = Math.max(1, step);
this.searchTo = this.searchFrom - step;
this.searchTo = Math.max(1, this.searchTo);
if (!settingsModel.sett_confirmSearchPosts || confirm(msg)) {
return this.jumpToSearchToPage();
} else {
return false;
}
}
} else if (this.lastPageMsgId < this.msgId) {
if (this.currentPage == this.lastPage) {
// Встаём на последнее сообщение на странице
return this.jumpToMessage(this.lastPage, this.lastPageMsgId);
} else {
step = Math.floor((this.lastPage - this.currentPage) / step);
step = Math.max(1, step);
this.searchTo = this.searchFrom + step;
this.searchTo = Math.min(this.lastPage, this.searchTo);
if (!settingsModel.sett_confirmSearchPosts || confirm(msg)) {
return this.jumpToSearchToPage();
} else {
return false;
}
}
} else {
log("Message " + this.msgId + " is on the current page");
// Ищем сообщение на странице, если не находим, то ищем сообщение,
// которое было непосредственно перед ним
var targetMsgId = this.msgId;
for (var i = 0; i < this.pageMessagesSnapshot.snapshotLength; i++) {
var id = this.extractMessageId(i);
if (id < this.msgId) {
targetMsgId = id;
} else if (id == this.msgId) {
targetMsgId = id;
break;
} else {
break;
}
}
if (targetMsgId != this.msgId) {
return this.jumpToMessage(this.currentPage, targetMsgId);
}
}
return false;
},
// Этап II. Поиск диапазона страниц, содержащих сообщение
// При этом если searchFrom < searchTo, то движемся справа налево, иначе слева направо
searchPageInterval: function() {
log("CPostSearcher.searchPageInterval()");
var step = Math.abs(this.searchTo - this.searchFrom);
if (this.msgId < this.firstPageMsgId) {
if (this.searchFrom < this.searchTo) {
// Диапазон найден! Переходим к этапу III
if (step == 1) {
// Сообщения не существует ни на одной из соседних
// страниц. Значит надо загрузить страницу с минимальным
// номером и перейти на ней к последнему сообщению
// В данном случае надо загрузить страницу searchFrom.
// Для этого делаем переход к ней, она при загрузке обнаружит,
// что сообщение на ней отсутствует и перейдёт к последнему
// сообщению на странице
this.swapFromAndTo();
return this.jumpToSearchToPage();
} else {
step = Math.floor(step / 2);
this.searchMid = this.searchFrom + step;
return this.jumpToSearchMidPage();
}
} else if (this.searchTo < this.searchFrom) {
// Недолёт. Надо дальше двигаться в сторону уменьшения номеров страниц
if (this.searchTo == 1) {
// Встаём на первое сообщение на странице
return this.jumpToMessage(1, this.firstPageMsgId);
} else {
this.searchFrom = this.searchTo;
this.searchTo = this.searchFrom - step;
this.searchTo = Math.max(1, this.searchTo);
return this.jumpToSearchToPage();
}
} else {
// Такого быть не может
logError("CPostSearcher.searchPageInterval(): Unexpected error");
}
} else if (this.lastPageMsgId < this.msgId) {
if (this.searchFrom < this.searchTo) {
// Недолёт. Надо дальше двигаться в сторону увеличения номеров страниц
if (this.searchTo == this.lastPage) {
// Встаём на последнее сообщение на странице
return this.jumpToMessage(this.lastPage, this.lastPageMsgId);
} else {
this.searchFrom = this.searchTo;
this.searchTo = this.searchFrom + step;
this.searchTo = Math.min(this.lastPage, this.searchTo);
return this.jumpToSearchToPage();
}
} else if (this.searchTo < this.searchFrom) {
// Диапазон найден! Ищем сообщение в этом диапазоне
if (step == 1) {
// Сообщения не существует ни на одной из соседних
// страниц. Значит надо загрузить страницу с минимальным
// номером и перейти на ней к последнему сообщению
// В данном случае надо перейти к последнему сообщению на текущей странице
return this.jumpToMessage(this.currentPage, this.lastPageMsgId);
} else {
// Теперь можно сделать так, чтобы searchFrom всегда было меньше searchTo
this.swapFromAndTo();
step = Math.floor(step / 2);
this.searchMid = this.searchFrom + step;
return this.jumpToSearchMidPage();
}
} else {
// Такого быть не может
logError("CPostSearcher.searchPageInterval(): Unexpected error");
}
} else {
log("Message " + this.msgId + " is found");
return this.jumpToMessage(this.currentPage, this.msgId);
}
return false;
},
// Этап III. Поиск сообщения где-то между двумя страницами
// Всегда выполняется условие: searchFrom < searchMid < searchTo
binaryPageSearch: function() {
log("CPostSearcher.binaryPageSearch()");
if (this.msgId < this.firstPageMsgId) {
// Сообщение находится между searchFrom и searchMid
this.searchTo = this.searchMid;
var step = this.searchTo - this.searchFrom;
if (step == 1) {
// Сообщения не существует ни на одной из соседних
// страниц. Значит надо загрузить страницу с минимальным
// номером и перейти на ней к последнему сообщению
// В данном случае надо загрузить страницу searchFrom
this.swapFromAndTo();
return this.jumpToSearchToPage();
} else {
step = Math.floor(step / 2);
this.searchMid = this.searchFrom + step;
return this.jumpToSearchMidPage();
}
} else if (this.lastPageMsgId < this.msgId) {
// Сообщение находится между searchMid и searchTo
this.searchFrom = this.searchMid;
var step = this.searchTo - this.searchFrom;
if (step == 1) {
// Сообщения не существует ни на одной из соседних
// страниц. Значит надо загрузить страницу с минимальным
// номером и перейти на ней к последнему сообщению
// В данном случае надо перейти к последнему сообщению на текущей странице
return this.jumpToMessage(this.searchFrom, this.lastPageMsgId);
} else {
step = Math.floor(step / 2);
this.searchMid = this.searchFrom + step;
return this.jumpToSearchMidPage();
}
} else {
log("Message " + msgId + " is found");
return this.jumpToMessage(this.searchMid, this.msgId);
}
return false;
},
initPageMessageIds: function() {
log("CPostSearcher.getPageMessageIds()");
// TODO Можно оптимизировать, где-нибудь сохранив ссылку на таблицу сообщений
// var snapshot = $x(xpathMsgTableBody);
var snapshot = $x("/html/body/div[@class='divMain']/div[@class='divContainer']/div[@class='content']/div[@class='text']/div[not(@class)]/table[@class='table']/tbody[1]/tr/td/table[@class='table']/tbody[1]");
if (snapshot.snapshotLength > 0) {
this.pageMessagesSnapshot = snapshot;
this.firstPageMsgId = this.extractMessageId(0);
this.lastPageMsgId = this.extractMessageId(snapshot.snapshotLength - 1);
log("firstPageMsgId = " + this.firstPageMsgId);
log("lastPageMsgId = " + this.lastPageMsgId);
return !isNaN(this.firstPageMsgId) && !isNaN(this.lastPageMsgId);
} else {
logError("CPostSearcher.initPageMessageIds(). The message table wasn't found");
return false;
}
},
extractMessageId: function(idx) {
var msgTableBody = this.pageMessagesSnapshot.snapshotItem(idx);
var id = parseInt(msgTableBody.rows[1].cells[1].id.substr(13));
if (!isNaN(id)) {
// Зачем это нужно см. в конструкторе
id *= this.mode;
}
return id;
},
swapFromAndTo: function() {
var temp = this.searchFrom;
this.searchFrom = this.searchTo;
this.searchTo = temp;
},
jumpToMessage: function(page, msg) {
var start = 20 * (page - 1);
// Про необходимость Math.abs см. в конструкторе
wnd.location.replace(wnd.location.protocol + "//www.avanturist.org/forum/index.php/" +
"topic," + this.topicId +
"." + start +
".html#msg" + Math.abs(msg));
},
jumpToSearchToPage: function() {
var start = 20 * (this.searchTo - 1);
// Про необходимость Math.abs см. в конструкторе
wnd.location.replace(wnd.location.protocol + "//www.avanturist.org/forum/index.php" +
"?topic=" + this.topicId +
"&start=" + start +
"&search_from=" + this.searchFrom +
"&search_to=" + this.searchTo +
"#msg" + Math.abs(this.msgId));
return true;
},
jumpToSearchMidPage: function() {
var start = 20 * (this.searchMid - 1);
// Про необходимость Math.abs см. в конструкторе
wnd.location.replace(wnd.location.protocol + "//www.avanturist.org/forum/index.php" +
"?topic=" + this.topicId +
"&start=" + start +
"&search_from=" + this.searchFrom +
"&search_to=" + this.searchTo +
"&search_mid=" + this.searchMid +
"#msg" + Math.abs(this.msgId));
return true;
}
};
CPostSearcher.parseUrlParameters = function(url) {
log("CPostSearcher.parseUrlParameters(" + url + ")");
var res = new Array();
var idxQuery = url.indexOf("?") + 1;
if (idxQuery == 0) { // Выше была прибавлена 1, поэтому сравнение не с -1
log("Question mark wasn't found");
return res;
}
var idxHash = url.indexOf("#");
idxHash = idxHash == -1 ? url.length() : idxHash;
if (idxHash <= idxQuery) {
logError("invalid URL");
return res;
}
var paramStr = url.substr(idxQuery, idxHash - idxQuery);
log("paramStr = " + paramStr);
var pairs = paramStr.split("&");
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split("=");
res[pair[0]] = pair[1];
}
return res;
}
// Конец CPostSearcher
// ************************************************************************
// Выполняем поиск нужного сообщения
var regexp = /^.*#msg(\d+)$/;
var matches = regexp.exec(url);
if (matches != null && matches.length == 2) {
// URL страницы создан для перехода к конкректному сообщению
var msgId = parseInt(matches[1]);
log("msgId = " + msgId);
try {
var postSearcher = new CPostSearcher(msgId);
if (postSearcher.searchMessage()) {
// Необходимо перегрузить страницу, поэтому дальше выполнять скрипт не надо
return;
}
} catch (e) {
logError("onLoad(): error in CPostSearcher initialization. " + e);
}
} else {
log("There is not a search message URL");
}
} // if (settingsModel.sett_searchPosts)
// NOTE: Изменение свойства visibility работает быстрее, чем изменение display!!!
var DIV_STYLE =
"visibility: hidden;" +
"z-index: 2;" +
"position: absolute;" +
"background-color: rgb(245, 245, 250);" +
"border: 1px solid rgb(60, 97, 164);" +
"padding: 3px;" +
"color: black;";
// ****************************************************************************
// ****************************** User ignore *********************************
// ****************************************************************************
function CDisplayedMessageModel(userIgnoreModel, originalMsg, userId, userName) {
log("CDisplayedMessageModel()");
this.userIgnoreModel = userIgnoreModel;
this.originalMsg = originalMsg;
this.userId = userId;
this.userName = userName;
}
CDisplayedMessageModel.prototype = {
removeFromList: function() {
this.userIgnoreModel.removeFromList(this.userId);
},
addToList: function() {
this.userIgnoreModel.addToList(this.userId, this.userName);
}
}
function CDisplayedMessageController(userIgnoreController, model, view) {
log("CDisplayedMessageController()");
app.addController(this);
this.userIgnoreController = userIgnoreController;
this.model = model;
this.view = view;
}
CDisplayedMessageController.prototype = {
handle: function(src, e) {
log("CDisplayedMessageController.handle(" + src + ", " + e.type + ")");
var view = this.view;
var model = this.model;
if (src == view.listCtrlHref) {
if (model.userIgnoreModel.isBlackList) {
var msg =
// Вы действительно
"\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e " +
// хотите поместить \"
"\u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \"" +
this.model.userName +
// \" в \"чёрный\"
"\" \u0432 \"\u0447\u0451\u0440\u043d\u044b\u0439\" " +
// список?
"\u0441\u043f\u0438\u0441\u043e\u043a?";
if (!confirm(msg)) {
return false;
}
this.model.addToList();
} else {
var msg =
// Вы действительно
"\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e " +
// хотите удалить \"
"\u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \"" +
this.model.userName +
// \" из \"белого\"
"\" \u0438\u0437 \"\u0431\u0435\u043b\u043e\u0433\u043e\" " +
// списка?
"\u0441\u043f\u0438\u0441\u043a\u0430?";
if (!confirm(msg)) {
return false;
}
this.model.removeFromList();
}
this.userIgnoreController.updateSettings();
wnd.location.reload();
return false;
} else {
logError("CDisplayedMessageController.handle(): An unexpected event. src.id = " + src.id + ", e.type = " + e.type);
return true;
}
}
}
function CDisplayedMessageView(model, userIgnoreController) {
log("CDisplayedMessageView()");
this.model = model;
this.controller = new CDisplayedMessageController(userIgnoreController, model, this);
xpath = "tbody/tr[1]/td[1]/b";
this.profileHrefNode = $x1(xpath, this.model.originalMsg);
if (this.profileHrefNode == null) {
logError("CDisplayedMessageView(): Profile URL wasn't found");
return;
}
var cellUser = this.profileHrefNode.parentNode;
this.createUserCellContentNode();;
cellUser.appendChild(this.userCellContentNode);
}
CDisplayedMessageView.prototype = {
createListCtrlHref: function() {
log("CDisplayedMessageView.createListCtrlHref()");
var href = document.createElement("A");
href.href = "#";
href.innerHTML = "x";
href.setAttribute("style", "font-weight: bold; font-size: 16px; color: red;");
if (this.model.userIgnoreModel.isBlackList) {
href.title =
// Поместить
"\u041f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c " +
// пользователя в
"\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 " +
// \"чёрный\" список
"\"\u0447\u0451\u0440\u043d\u044b\u0439\" \u0441\u043f\u0438\u0441\u043e\u043a";
} else {
href.title =
// Удалить
"\u0423\u0434\u0430\u043b\u0438\u0442\u044c " +
// пользователя из
"\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 " +
// \"белого\" списка
"\"\u0431\u0435\u043b\u043e\u0433\u043e\" \u0441\u043f\u0438\u0441\u043a\u0430";
}
app.addEventListener(href, "click", this.controller);
this.listCtrlHref = href;
},
createUserCellContentNode: function() {
log("C...MessageView.createCollapsedUserCell()");
this.createListCtrlHref();
var table = document.createElement("TABLE");
table.width = "100%";
var row = table.insertRow(-1);
var userCell = row.insertCell(-1);
userCell.width = "95%";
userCell.appendChild(this.profileHrefNode);
var hrefCell = row.insertCell(-1);
hrefCell.width = "5%";
hrefCell.align = "right";
hrefCell.appendChild(this.listCtrlHref);
this.userCellContentNode = table;
}
}
function CHiddenMessageModel(userIgnoreModel, originalMsg, userId, userName) {
log("CHiddenMessageModel()");
this.userIgnoreModel = userIgnoreModel;
this.originalMsg = originalMsg;
this.userId = userId;
this.userName = userName;
this.isVisible = true;
}
CHiddenMessageModel.prototype = {
removeFromList: function() {
this.userIgnoreModel.removeFromList(this.userId);
},
addToList: function() {
this.userIgnoreModel.addToList(this.userId, this.userName);
}
}
function CHiddenMessageController(userIgnoreController, model, view) {
log("CHiddenMessageController()");
app.addController(this);
this.userIgnoreController = userIgnoreController;
this.model = model;
this.view = view;
}
CHiddenMessageController.prototype = {
handle: function(src, e) {
log("CHiddenMessageController.handle(" + src + ", " + e.type + ")");
if (src == this.view.expandHref) {
this.view.expand();
return false;
} else if (src == this.view.collapseHref) {
this.view.collapse();
return false;
} else if (src == this.view.listCtrlHref) {
if (this.model.userIgnoreModel.isBlackList) {
var msg =
// Вы действительно
"\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e " +
// хотите удалить \"
"\u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \"" +
this.model.userName +
// \" из \"чёрного\"
"\" \u0438\u0437 \"\u0447\u0451\u0440\u043d\u043e\u0433\u043e\" " +
// списка?
"\u0441\u043f\u0438\u0441\u043a\u0430?";
if (!confirm(msg)) {
return false;
}
this.model.removeFromList();
} else {
var msg =
// Вы действительно
"\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e " +
// хотите добавить \"
"\u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \"" +
this.model.userName +
// \" в \"белый\"
"\" \u0432 \"\u0431\u0435\u043b\u044b\u0439\" " +
// список?
"\u0441\u043f\u0438\u0441\u043e\u043a?";
if (!confirm(msg)) {
return false;
}
this.model.addToList();
}
this.userIgnoreController.updateSettings();
wnd.location.reload();
return false;
} else {
logError("An unexpected event. src.id = " + src.id + ", e.type = " + e.type);
return true;
}
}
}
function CHiddenMessageView(model, userIgnoreController) {
log("CHiddenMessageView()");
this.model = model;
this.controller = new CHiddenMessageController(userIgnoreController, model, this);
this.expanded = model.originalMsg;
this.parseExpandedView();
this.createUserCellContentNode();
this.createCollapsedView();
this.expanded.parentNode.insertBefore(this.collapsed, this.expanded);
}
CHiddenMessageView.prototype = {
createListCtrlHref: function() {
log("CHiddenMessageView.createListCtrlHref()");
var href = document.createElement("A");
href.href = "#";
href.innerHTML = "+";
href.setAttribute("style", "font-weight: bold; font-size: 16px; color: blue;");
if (this.model.userIgnoreModel.isBlackList) {
href.title =
// Удалить
"\u0423\u0434\u0430\u043b\u0438\u0442\u044c " +
// пользователя из
"\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 " +
// \"чёрного\" списка
"\"\u0447\u0451\u0440\u043d\u043e\u0433\u043e\" \u0441\u043f\u0438\u0441\u043a\u0430";
} else {
href.title =
// Добавить
"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c " +
// пользователя в
"\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 " +
// \"белый\" список
"\"\u0431\u0435\u043b\u044b\u0439\" \u0441\u043f\u0438\u0441\u043e\u043a";
}
app.addEventListener(href, "click", this.controller);
this.listCtrlHref = href;
},
createUserCellContentNode: CDisplayedMessageView.prototype.createUserCellContentNode,
createCollapsedView: function() {
log("CHiddenMessageView.createCollapsedView()");
var msgTable = document.createElement("TABLE");
// Стиль message_hidden_york описан ниже в CPageView.addCustomCSS()
msgTable.className = "message_hidden_york";
msgTable.cellSpacing = "1";
msgTable.cellPadding = "5";
msgTable.width = "100%";
msgTable.style.display = "none";
var row = msgTable.insertRow(-1);
var cellUser = row.insertCell(-1);
cellUser.width = 200;
var cellInfo = row.insertCell(-1);
var cellInfoTable = document.createElement("TABLE");
cellInfoTable.width = "100%";
row = cellInfoTable.insertRow(-1);
var cellInfoCell1 = row.insertCell(-1);
cellInfoCell1.width = "40%";
var cellInfoCell2 = row.insertCell(-1);
cellInfoCell2.width = "10%";
cellInfoCell2.align = "center";
var cellInfoCell3 = row.insertCell(-1);
cellInfoCell3.width = "40%";
cellInfoCell3.align = "right";
cellInfoCell3.id = "XXX";
var href = document.createElement("A");
href.href = "#";
href.style.fontWeight = "bold";
// "РАЗВЕРНУТЬ"
href.innerHTML = "\u0420\u0410\u0417\u0412\u0415\u0420\u041d\u0423\u0422\u042c";
app.addEventListener(href, "click", this.controller);
cellInfoCell2.appendChild(href);
cellInfo.appendChild(cellInfoTable);
this.collapsed = msgTable;
this.collapsedCellUser = cellUser;
this.collapsedCellInfo = cellInfoCell1;
this.collapsedCellCtrl = cellInfoCell2;
this.collapsedCellRating = cellInfoCell3;
this.expandHref = href;
},
parseExpandedView: function() {
log("CHiddenMessageView.parseExpandedView()");
xpath = "tbody/tr[1]/td[1]/b";
this.profileHrefNode = $x1(xpath, this.expanded);
if (this.profileHrefNode == null) {
logError("CHiddenMessageView.parseExpandedView(): Profile URL wasn't found");
return;
}
this.expandedCellUser = this.profileHrefNode.parentNode;
// Ссылки из первой ячейка заголовка сообщения: якорь и дата
xpath = "tbody/tr[1]/td[2]/table/tbody/tr[1]/td[1]/a";
var snapshot = $x(xpath, this.expanded);
if (snapshot.snapshotLength == 0) {
logError("CHiddenMessageView.parseExpandedView(): Anchor and date weren't found");
return;
}
// Добавляю все ссылки из 1-й ячейки заголовка сообщения в массив
this.cellAnchorContent = new Array();
for (var i = 0; i < snapshot.snapshotLength; i++) {
var children = snapshot.snapshotItem(i);
this.cellAnchorContent[i] = children;
}
this.expandedCellAnchor = this.cellAnchorContent[0].parentNode;
// Создаю ссылку "СВЕРНУТЬ"
var href = document.createElement("A");
href.href = "#";
href.style.fontWeight = "bold";
// "СВЕРНУТЬ"
href.innerHTML = "\u0421\u0412\u0415\u0420\u041d\u0423\u0422\u042c";
app.addEventListener(href, "click", this.controller);
// Строка - заголовок сообщения
var tr = this.expandedCellAnchor.parentNode;
// Добавляю ячейку в середину, было 2 ячейки стало 3
var td = tr.insertCell(1);
td.align = "center";
td.width = "10%";
td.appendChild(href);
tr.cells[0].width = "40%";
tr.cells[2].width = "40%";
this.collapseHref = href;
// Ссылка на div с рейтингом
xpath = "tbody/tr[3]/td[2]/table/tbody/tr[1]/td[1]/div";
this.ratingDiv = $x1(xpath, this.expanded);
if (this.ratingDiv == null) {
logError("CHiddenMessageView.parseExpandedView(): Message rating wasn't found");
return;
}
this.expandedCellRating = this.ratingDiv.parentNode;
},
createCollapsedUserCell: function() {
log("CHiddenMessageView.createCollapsedUserCell()");
var table = document.createElement("TABLE");
table.width = "100%";
var row = userTable.insertRow(-1);
var userCell = userTableRow.insertCell(-1);
var hrefCell = userTableRow.insertCell(-1);
hrefCell.align = "right";
this.collapsedCellUser = cellUser;
this.collapsedCellListCtrl = hrefCell;
},
collapse: function() {
log("CHiddenMessageView.collapse()");
if (!this.model.isVisible) {
return;
}
this.expanded.style.display = "none";
this.collapsedCellUser.appendChild(this.userCellContentNode);
var len = this.cellAnchorContent.length;
for (var i = 0; i < len; i++) {
this.collapsedCellInfo.appendChild(this.cellAnchorContent[i]);
}
this.collapsedCellRating.appendChild(this.ratingDiv);
this.collapsed.style.display = "table";
this.model.isVisible = false;
},
expand: function() {
log("CHiddenMessageView.expand()");
if (this.model.isVisible) {
return;
}
this.collapsed.style.display = "none";
this.expandedCellUser.appendChild(this.userCellContentNode);
var len = this.cellAnchorContent.length;
for (var i = 0; i < len; i++) {
this.expandedCellAnchor.appendChild(this.cellAnchorContent[i]);
}
this.expandedCellRating.insertBefore(this.ratingDiv, this.expandedCellRating.childNodes[0]);
this.expanded.style.display = "table";
this.model.isVisible = true;
}
}
function CUserIgnoreModel(isBlackList, users, scriptUserId) {
log("CUserIgnoreModel()");
this.isBlackList = isBlackList;
this.users = users;
this.hiddenMessages = new Array();
this.displayedMessages = new Array();
var xpath = xpathMsgTableBody + "/tr[1]/td[1]/b/a[contains(@href,'profile')]";
var snapshot = $x(xpath, $id("topic_posts"));
// b td tr
xpath = "../../../td[2]/table/tbody/tr[1]/td[2]/a[@class='all_save_to_journal']";
for (var i = 0; i < snapshot.snapshotLength; i++) {
var profileHref = snapshot.snapshotItem(i);
var saveToJournal = $x1(xpath, profileHref);
if (saveToJournal != null) {
log("There is script user's message");
continue;
}
var href = profileHref.href;
var idx = href.indexOf(";u=");
if (idx == -1) {
logError("CUserIgnoreModel(). Can't find user ID. href = " + href);
continue;
}
var userId = href.substr(idx + 3);
var userName = profileHref.innerHTML;
// a b td tr tbody table
var msgTable = profileHref.parentNode.parentNode.parentNode.parentNode.parentNode;
var contains = users[userId] != null;
if (contains && isBlackList || !contains && !isBlackList) {
log("Found hidden message: idx = " + i);
var len = this.hiddenMessages.length;
this.hiddenMessages[len] = new CHiddenMessageModel(this, msgTable, userId, userName);
} else {
var len = this.displayedMessages.length;
this.displayedMessages[len] = new CDisplayedMessageModel(this, msgTable, userId, userName);
}
}
}
CUserIgnoreModel.prototype = {
removeFromList: function(userId) {
delete this.users[userId];
},
addToList: function(userId, userName) {
this.users[userId] = userName;
}
}
function CUserIgnoreController(settings) {
log("CUserIgnoreController()");
app.addController(this);
this.settings = settings;
if (settings.userIgnoreType == CSettingsModel.BLACK_LIST) {
this.model = new CUserIgnoreModel(true, settings.userIgnoreListBlack);
} else {
this.model = new CUserIgnoreModel(false, settings.userIgnoreListWhite);
}
this.view = new CUserIgnoreView(this.model, this);
var len = this.model.hiddenMessages.length;
for (var i = 0; i < len; i++) {
var msgModel = this.model.hiddenMessages[i];
this.view.addHiddenMsgView(new CHiddenMessageView(msgModel, this));
}
len = this.model.displayedMessages.length;
for (var i = 0; i < len; i++) {
var msgModel = this.model.displayedMessages[i];
this.view.addDisplayedMsgView(new CDisplayedMessageView(msgModel, this));
}
// alt + ctrl + ↑
app.addHotKey("alt+ctrl+38", this);
// ctrl + shift + ↑
app.addHotKey("ctrl+shift+38", this);
// alt + ctrl + ↓
app.addHotKey("alt+ctrl+40", this);
// ctrl + shift + ↓
app.addHotKey("ctrl+shift+40", this);
}
CUserIgnoreController.prototype = {
handleHotKey: function(hotKey) {
log("CUserIgnoreController.handleHotKey(" + hotKey + ")");
if ("alt+ctrl+38" == hotKey || "ctrl+shift+38" == hotKey) {
// alt + ctrl + ↑ ИЛИ ctrl + shift + ↑
this.collapse();
} else if ("alt+ctrl+40" == hotKey || "ctrl+shift+40" == hotKey) {
// alt + ctrl + ↓ ИЛИ alt + shift + ↓
this.expand();
}
},
collapse: function() {
this.view.collapse();
},
expand: function() {
this.view.expand();
},
updateSettings: function() {
if (this.model.isBlackList) {
this.settings.userIgnoreType = CSettingsModel.BLACK_LIST;
this.settings.userIgnoreListBlack = this.model.users;
} else {
this.settings.userIgnoreType = CSettingsModel.WHITE_LIST;
this.settings.userIgnoreListWhite = this.model.users;
}
this.settings.save();
}
}
function CUserIgnoreView(model, controller) {
log("CUserIgnoreView()");
this.model = model;
this.controller = controller;
this.hiddenMsgViews = new Array();
this.displayedMsgViews = new Array();
}
CUserIgnoreView.prototype = {
addHiddenMsgView: function(msgView) {
this.hiddenMsgViews[this.hiddenMsgViews.length] = msgView;
},
addDisplayedMsgView: function(msgView) {
this.displayedMsgViews[this.displayedMsgViews.length] = msgView;
},
collapse: function() {
log("CUserIgnoreView.collapse()");
var views = this.hiddenMsgViews;
var len = views.length;
for (var i = 0; i < len; i++) {
views[i].collapse();
}
},
expand: function() {
log("CUserIgnoreView.expand()");
var views = this.hiddenMsgViews;
var len = views.length;
for (var i = 0; i < len; i++) {
views[i].expand();
}
}
}
// ****************************************************************************
// ************************ Settings View & Controller ************************
// ****************************************************************************
// Здесь находится всё, кроме модели. Модель создаётся в самом начале метода
// onLoad, т.к. она используется ещё до создания других объектов
function CSettingsController(model, menuCtrl) {
log("CSettingsController()");
app.addController(this);
this.model = model;
this.view = new CSettingsView(this, menuCtrl);
}
CSettingsController.prototype = {
handle: function(src, e) {
log("CSettingsController.handle(" + src + ", " + e.type + ")");
var view = this.view;
if (src == view.btnSave) {
this.showHideForm();
this.synchronizeModel();
this.model.save();
wnd.location.reload();
return false;
} else if (src == view.href || src == view.btnCancel) {
this.showHideForm();
return false;
} else if (src == view.userIgnoreRadioBlack || src == view.userIgnoreRadioWhite) {
this.synchronizeSelect();
return true;
} else if (src == view.userIgnoreBtnDelete) {
var select =
view.userIgnoreRadioBlack.checked ?
view.userIgnoreSelectBlack :
view.userIgnoreSelectWhite;
var options = select.options;
var len = options.length;
var idx = -1;
for (var i = 0; i < len; i++) {
if (options[i].selected) {
idx = i;
break;
}
}
if (idx != -1) {
var option = options[idx];
select.removeChild(option);
len--;
if (idx < len) {
options[idx].selected = true;
} else if (len > 0) {
options[len - 1].selected = true;
}
} else {
logError("CSettingsController.handle(): selectedIndex = " + idx);
}
return false;
} else if (src == view.userIgnoreBtnImport) {
var useBlackList = view.userIgnoreRadioBlack.checked;
var importStr = prompt(
// Импорт \"
"\u0418\u043c\u043f\u043e\u0440\u0442 \"" +
// БЕЛОГО : ЧЁРНОГО
(useBlackList ? "\u0427\u0401\u0420\u041d\u041e\u0413\u041e" : "\u0411\u0415\u041b\u041e\u0413\u041e") +
// \" списка.\n"
"\" \u0441\u043f\u0438\u0441\u043a\u0430.\n" +
// Вставьте
"\u0412\u0441\u0442\u0430\u0432\u044c\u0442\u0435 " +
// строку,
"\u0441\u0442\u0440\u043e\u043a\u0443, " +
// полученную
"\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u0443\u044e " +
// при экспорте
"\u043f\u0440\u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0435 " +
// списка
"\u0441\u043f\u0438\u0441\u043a\u0430");
if (importStr != null && importStr.length > 0) {
var arr = new Array();
this.model.userIgnoreListFromString(arr, importStr);
this.createUserIgnoreListOptionsFromArray(useBlackList, arr);
}
return false;
} else if (src == view.userIgnoreBtnExport) {
var useBlackList = view.userIgnoreRadioBlack.checked;
var arr = this.userIgnoreListOptionsToArray(useBlackList);
var exportStr = this.model.userIgnoreListToString(arr);
prompt(
// Экспорт \"
"\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \"" +
// БЕЛОГО : ЧЁРНОГО
(useBlackList ? "\u0427\u0401\u0420\u041d\u041e\u0413\u041e" : "\u0411\u0415\u041b\u041e\u0413\u041e") +
// \" списка.\n"
"\" \u0441\u043f\u0438\u0441\u043a\u0430.\n" +
// Сохраните
"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 " +
// куда-нибудь
"\u043a\u0443\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c " +
// эту строку.\n
"\u044d\u0442\u0443 \u0441\u0442\u0440\u043e\u043a\u0443.\n" +
// В любой момент
"\u0412 \u043b\u044e\u0431\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 " +
// Вы можете
"\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 " +
// восстановить
"\u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c " +
// сохранённый\n
"\u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u0439\n" +
// список
"\u0441\u043f\u0438\u0441\u043e\u043a " +
// воспользовавшись
"\u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0432\u0448\u0438\u0441\u044c " +
// кнопкой
"\u043a\u043d\u043e\u043f\u043a\u043e\u0439 " +
// \"Импорт\"
"\"\u0418\u043c\u043f\u043e\u0440\u0442\"",
exportStr);
return false;
} else {
logError("CSettingsController.handle(): An unexpected event. src.id = " + src.id + ", e.type = " + e.type);
return true;
}
},
synchronizeForm: function() {
log("CSettingsController.synchronizeForm()");
var model = this.model;
var view = this.view;
for (var key in model) {
if (key.indexOf("sett_") == 0) {
var checkbox = view[key];
if (checkbox != null) {
checkbox.checked = model[key];
}
}
}
if (model.userIgnoreType == CSettingsModel.BLACK_LIST) {
view.userIgnoreRadioBlack.checked = true;
view.userIgnoreRadioWhite.checked = false;
} else {
view.userIgnoreRadioBlack.checked = false;
view.userIgnoreRadioWhite.checked = true;
}
this.createUserIgnoreListOptionsFromArray(true, model.userIgnoreListBlack);
this.createUserIgnoreListOptionsFromArray(false, model.userIgnoreListWhite);
this.synchronizeSelect();
},
synchronizeModel: function() {
log("CSettingsController.synchronizeModel()");
var model = this.model;
var view = this.view;
for (var key in view) {
if (key.indexOf("sett_") == 0) {
var checkbox = view[key];
if (checkbox != null) {
model[key] = checkbox.checked;
}
}
}
model.userIgnoreType =
view.userIgnoreRadioBlack.checked ?
CSettingsModel.BLACK_LIST :
CSettingsModel.WHITE_LIST;
model.userIgnoreListBlack = this.userIgnoreListOptionsToArray(true);
model.userIgnoreListWhite = this.userIgnoreListOptionsToArray(false);
},
createUserIgnoreListOptionsFromArray: function(useBlackList, userIgnoreList) {
// Вывожу элементы в порядке их идентификаторов
var sortedIds = new Array();
for (var id in userIgnoreList) {
sortedIds[sortedIds.length] = parseInt(id);
}
// Почему-то sort() без параметра не работала, пришлось написать функцию
sortedIds.sort(function(a, b) {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
var select =
useBlackList ?
this.view.userIgnoreSelectBlack :
this.view.userIgnoreSelectWhite;
// Удаляю все существующие элементы
var len = select.options.length;
for (i = len - 1; i >= 0; i--) {
select.removeChild(select.options[i]);
}
len = sortedIds.length;
for (var i = 0; i < len; i++) {
var option = document.createElement("OPTION");
var id = sortedIds[i];
var name = userIgnoreList[new String(id)];
option.value = id;
option.setAttribute("userName", name);
if (name.length > 0) {
option.innerHTML = id + " (" + name + ")";
} else {
option.innerHTML = id;
}
select.options[select.options.length] = option;
}
if (select.options.length > 0) {
select.options[0].selected = true;
}
},
userIgnoreListOptionsToArray: function(useBlackList) {
var select =
useBlackList ?
this.view.userIgnoreSelectBlack :
this.view.userIgnoreSelectWhite;
var options = select.options;
var arr = new Array();
var len = options.length;
for (var i = 0; i < len; i++) {
var userId = options[i].value;
var userName = options[i].getAttribute("userName");
arr[userId] = userName;
}
return arr;
},
showHideForm: function() {
if (this.model.isVisible) {
this.view.div.style.visibility = "hidden";
this.model.isVisible = false;
} else {
this.synchronizeForm();
this.view.div.style.visibility = "visible";
this.model.isVisible = true;
}
},
synchronizeSelect: function() {
log("CSettingsController.synchronizeSelect()");
var view = this.view;
if (view.userIgnoreRadioBlack.checked) {
view.userIgnoreSelectBlack.style.visibility = "inherit";
view.userIgnoreSelectBlack.style.zOrder = "20";
view.userIgnoreSelectWhite.style.visibility = "hidden";
view.userIgnoreSelectWhite.style.zOrder = "10";
} else {
view.userIgnoreSelectWhite.style.visibility = "inherit";
view.userIgnoreSelectWhite.style.zOrder = "20";
view.userIgnoreSelectBlack.style.visibility = "hidden";
view.userIgnoreSelectBlack.style.zOrder = "10";
}
}
}
function CSettingsView(controller, menuCtrl) {
this.model = controller.model;
this.controller = controller;
this.href = document.createElement("A");
this.href.href = "#";
// Настройки
this.href.innerHTML = "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438";
app.addEventListener(this.href, "click", this.controller);
this.form = document.createElement("FORM");
var form = this.form;
var model = controller.model;
for (var key in model) {
if (key.indexOf("sett_") == 0) {
var input = document.createElement("INPUT");
input.type = "checkbox";
this[key] = input;
var label = document.createElement("LABEL");
label.appendChild(input);
label.appendChild(document.createTextNode(model["label_" + key]));
form.appendChild(label);
form.appendChild(document.createElement("BR"));
}
}
this.createUserIgnoreSettingsView();
var button = document.createElement("INPUT");
button.type = "button";
// Сохранить
button.value = "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c";
app.addEventListener(button, "click", this.controller);
this.btnSave = button;
form.appendChild(button);
form.appendChild(document.createTextNode(" "));
button = document.createElement("INPUT");
button.type = "button";
// Отменить
button.value = "\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c";
app.addEventListener(button, "click", this.controller);
this.btnCancel = button;
form.appendChild(button);
this.div = document.createElement("DIV");
this.div.setAttribute("style", DIV_STYLE);
this.div.appendChild(form);
// Добавляю пункт в меню
var span = document.createElement("SPAN");
span.style.display = "inline-block";
span.appendChild(this.href);
span.appendChild(this.div);
var item = menuCtrl.createItem("");
item.appendChild(span);
menuCtrl.insertScriptMenuItem(item);
// Продолжаем обработку настроек игнорирования пользователей
this.userIgnoreRadioBlack = $id("york_user_ignore_type_black");
this.userIgnoreRadioWhite = $id("york_user_ignore_type_white");
this.userIgnoreSelectBlack = $id("york_user_ignore_select_black");
this.userIgnoreSelectWhite = $id("york_user_ignore_select_white");
this.userIgnoreBtnDelete = $id("york_user_ignore_btn_delete");
this.userIgnoreBtnImport = $id("york_user_ignore_btn_import");
this.userIgnoreBtnExport = $id("york_user_ignore_btn_export");
app.addEventListener(this.userIgnoreRadioBlack, "click", this.controller);
app.addEventListener(this.userIgnoreRadioWhite, "click", this.controller);
app.addEventListener(this.userIgnoreBtnDelete, "click", this.controller);
app.addEventListener(this.userIgnoreBtnImport, "click", this.controller);
app.addEventListener(this.userIgnoreBtnExport, "click", this.controller);
}
CSettingsView.prototype = {
createUserIgnoreSettingsView: function() {
var div = document.createElement("DIV");
div.setAttribute("style", "border: 1px solid black; margin-top: 10px; margin-bottom: 10px; padding: 5px; width: 300px;");
div.innerHTML =
// Автоскрытие сообщений
"<b>\u0410\u0432\u0442\u043e\u0441\u043a\u0440\u044b\u0442\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439</b>" +
"<br/>" +
"<br/>" +
// Тип списка:
"\u0422\u0438\u043f \u0441\u043f\u0438\u0441\u043a\u0430: " +
"<label>" +
"<input type='radio' name='york_user_ignore_type' id='york_user_ignore_type_black'/> " +
// "Чёрный"
"\"\u0427\u0451\u0440\u043d\u044b\u0439\"" +
"</label>" +
" " +
"<label>" +
"<input type='radio' name='york_user_ignore_type' id='york_user_ignore_type_white'/> " +
// "Белый"
"\"\u0411\u0435\u043b\u044b\u0439\"" +
"</label>" +
"<br/>" +
"<br/>" +
// Список пользователей:
"\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439:" +
"<table width='100%' cellpadding='0' cellspacint='0'>" +
"<tr>" +
"<td width='90%' rowspan='2'>" +
"<div style='position: relative; top: 0px; left: 0px;'>" +
"<select id='york_user_ignore_select_black' size='10' style='z-index: 10; width: 100%; visibility: inherit;'>" +
"</select>" +
"<select id='york_user_ignore_select_white' size='10' style='z-index: 20; position: absolute; top: 0px; left: 0px; width: 100%; visibility: hidden;'>" +
"</select>" +
"</div>" +
"</td>" +
"<td width='10%' valign='top'>" +
// Удалить
"<input type='button' id='york_user_ignore_btn_delete' value='\u0423\u0434\u0430\u043b\u0438\u0442\u044c'/> " +
"</td>" +
"</tr>" +
"<tr>" +
"<td width='10%' valign='bottom'>" +
// Импорт
"<input type='button' id='york_user_ignore_btn_import' value='\u0418\u043c\u043f\u043e\u0440\u0442'/> " +
// Экспорт
"<input type='button' id='york_user_ignore_btn_export' value='\u042d\u043a\u0441\u043f\u043e\u0440\u0442'/> " +
"</td>" +
"</tr>" +
"</table>" +
"";
this.form.appendChild(div);
}
}
// ****************************************************************************
// ******************************** Search ************************************
// ****************************************************************************
function CSearchModel(topicId) {
log("CSearchModel(" + topicId + ")");
this.isVisible = false;
this.isTopic = topicId != null;
if (this.isTopic) {
this.topicId = topicId;
this.topicTitle = document.title;
}
}
function CSearchController(pageCtrl, menuCtrl) {
log("CSearchController()");
app.addController(this);
this.pageCtrl = pageCtrl;
this.model = new CSearchModel(pageCtrl.model.topicId);
this.view = new CSearchView(this.model, this, menuCtrl);
}
CSearchController.prototype = {
handle: function(src, e) {
log("CSearchController.handle(" + src + ", " + e.type + ")");
if (src == this.view.form) {
if (this.model.isTopic) {
var view = this.view;
var form = view.form;
if (view.getCheckbox().checked) {
form.appendChild(view.getInputTopicId());
form.appendChild(view.getInputTopicTitle());
} else {
form.removeChild(view.getInputTopicId());
form.removeChild(view.getInputTopicTitle());
}
}
this.showHideForm();
return true;
} else if (src == this.view.href) {
this.showHideForm();
return false;
} else {
logError("An unexpected event. src.id = " + src.id + ", e.type = " + e.type);
return true;
}
},
showHideForm: function() {
if (this.model.isVisible) {
this.view.div.style.visibility = "hidden";
this.model.isVisible = false;
} else {
this.view.div.style.visibility = "visible";
this.view.getInputText().focus();
this.model.isVisible = true;
}
}
};
function CSearchView(model, controller, menuCtrl) {
log("CSearchView()");
this.model = model;
this.controller = controller;
this.href = document.createElement("A");
this.href.href = "#";
this.href.innerHTML =
"<img src='http://www.google.com/favicon.ico' alt='g' " +
"style='vertical-align: middle; height: 15px;'/> " +
// Поиск с Google
"\u041f\u043e\u0438\u0441\u043a \u0441 Google";
app.addEventListener(this.href, "click", this.controller);
this.form = document.createElement("FORM");
var form = this.form;
form.method = "GET";
form.action = "http://www.google.com/search";
form.target = "_blank";
form.innerHTML =
"<input type='text' id='york_search_text' name='q' value='' size='30'/>" +
// Поиск
"<input type='submit' value='\u041f\u043e\u0438\u0441\u043a'/>";
if (model.isTopic) {
form.innerHTML +=
"<br/>" +
"<input type='checkbox' id='york_checkbox_in_topic' checked/>" +
"<label for='york_checkbox_in_topic'>" +
// Только в
"\u0422\u043e\u043b\u044c\u043a\u043e \u0432 " +
// текущей теме
"\u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0442\u0435\u043c\u0435" +
"</label>" +
"<input type='hidden' id='york_search_topic_id' name='q' value='inurl:" + model.topicId + "'/>" +
"<input type='hidden' id='york_search_topic_title' name='q' value='intitle:\"" + model.topicTitle + "\"'/>";
}
form.innerHTML +=
"<input type='hidden' name='q' value='site:avanturist.org'/>" +
"<input type='hidden' name='q' value='inurl:forum'/>" +
"<input type='hidden' name='q' value='inurl:topic'/>";
app.addEventListener(this.form, "submit", this.controller);
this.div = document.createElement("DIV");
this.div.align = "left";
this.div.setAttribute("style", DIV_STYLE);
this.div.appendChild(this.form);
var span = document.createElement("SPAN");
span.style.display = "inline-block";
span.appendChild(this.href);
span.appendChild(this.div);
var item = menuCtrl.createItem("");
item.appendChild(span);
menuCtrl.insertTopUserItem(item, 0);
}
CSearchView.prototype = {
getInputText: function() {
if (this.inputText == null) {
this.inputText = $id("york_search_text");
}
return this.inputText;
},
getInputTopicId: function() {
if (this.inputTopicId == null) {
this.inputTopicId = $id("york_search_topic_id");
}
return this.inputTopicId;
},
getInputTopicTitle: function() {
if (this.inputTopicTitle == null) {
this.inputTopicTitle = $id("york_search_topic_title");
}
return this.inputTopicTitle;
},
getCheckbox: function() {
if (this.checkbox == null) {
this.checkbox = $id("york_checkbox_in_topic");
}
return this.checkbox;
}
}
// ****************************************************************************
// ********************************* Menu *************************************
// ****************************************************************************
// TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
function throwError(msg) {
logError(msg);
throw msg;
}
function CMenuControl() {
log("CMenuControl()");
var xpathDivMain = "div[@class='divMain']";
var xpathDivTopMenu = xpathDivMain + "/div[@class='topMenu']";
var xpathDivContainer = xpathDivMain + "/div[@class='divContainer']";
var xpathDivContent = xpathDivContainer + "/div[@class='content']";
var xpathDivText = xpathDivContent + "/div[@class='text']";
var topMenu = $x1(xpathDivTopMenu);
if (null == topMenu) {
throwError("CMenuControl(): Top menu wasn't found");
}
this.topMenuLinks = $x1(xpathDivTopMenu + "/ul[@class='links']");
if (null == this.topMenuLinks) {
throwError("CMenuControl(): Top links menu wasn't found");
}
this.topMenuUser = $x1(xpathDivTopMenu + "/ul[@class='userLinks']");
if (null == this.topMenuUser) {
throwError("CMenuControl(): Top user menu wasn't found");
}
// Создаю div для меню скрипта
var divScriptMenu = document.createElement("DIV");
divScriptMenu.className = "topMenu";
this.scriptMenu = document.createElement("UL");
this.scriptMenu.className = "links";
this.scriptMenu.style.width = "600px";
this.scriptLinks = document.createElement("UL");
this.scriptLinks.className = "userLinks";
// Добавляю меню скрипта на страницу
divScriptMenu.appendChild(this.scriptMenu);
divScriptMenu.appendChild(this.scriptLinks);
topMenu.parentNode.insertBefore(divScriptMenu, topMenu.nextSibling);
// Удаляю два пустых div'а
var xpathVSpacer = xpathDivText + "/div[position()=1 and count(child::*)=0]";
for (var i = 0; i < 2; ++i) {
var divRemovedSpacer = $x1(xpathVSpacer);
if (null == divRemovedSpacer) {
// Если не удалось удалить элемент, то продолжаем работу
logError("CMenuControl(): Vertical spacer wasn't found");
break;
}
divRemovedSpacer.parentNode.removeChild(divRemovedSpacer);
}
// Уменьшаю высоту div'а clear
var divClear = $x1(xpathDivText + "/div[@class='clear']");
if (null == divClear) {
// Если не удалось удалить элемент, то продолжаем работу
logError("CMenuControl(): Vertical spacer wasn't found");
} else {
divClear.style.height = "4px";
}
// Добавляю текст "Скрипт:"
// <b>Скрипт:</b>
var item = this.createItem("<b>\u0421\u043a\u0440\u0438\u043f\u0442:</b>");
this.insertScriptMenuItem(item);
// Делаю отступ перед именем пользователя
item = this.createItem(" ");
this.insertTopUserItem(item, 0);
}
CMenuControl.prototype = {
insertTopLinksItem: function(item, idx) {
this.insertItem(this.topMenuLinks, item, idx);
},
insertTopUserItem: function(item, idx) {
this.insertItem(this.topMenuUser, item, idx);
},
insertScriptMenuItem: function(item, idx) {
this.insertItem(this.scriptMenu, item, idx);
},
insertScriptLinksItem: function(item, idx) {
this.insertItem(this.scriptLinks, item, idx);
},
insertItem: function(menu, item, idx) {
if (null == idx) {
menu.appendChild(item);
} else {
menu.insertBefore(item, menu.childNodes[idx]);
}
},
createItem: function(text, url) {
var textNode;
if (null != url) {
textNode = document.createElement("A");
textNode.href = url;
} else {
textNode = document.createElement("SPAN");
}
textNode.innerHTML = text;
var item = document.createElement("LI");
item.appendChild(textNode);
return item;
}
}
// ****************************************************************************
// ********************************* Page *************************************
// ****************************************************************************
function CPageModel() {
log("CPageModel()");
if (jQueryInstalled && /myarchive/.test(url)) {
this.pageType = CPageModel.PAGE_TYPE_ARCHIVE;
} else if (jQueryInstalled && /myjournal/.test(url)) {
this.pageType = CPageModel.PAGE_TYPE_JOURNAL;
} else if (jQueryInstalled && /index\.php.+action=post/.test(url)) {
this.pageType = CPageModel.PAGE_TYPE_MESSAGE;
} else if (jQueryInstalled && /index\.php.+topic[=,]/.test(url)) {
this.pageType = CPageModel.PAGE_TYPE_TOPIC;
} else if (jQueryInstalled && /index\.php.+board[=,]/.test(url)) {
this.pageType = CPageModel.PAGE_TYPE_BOARD;
} else if (jQueryInstalled && /\/forum\//.test(url)) {
// TODO дублирование кода, где-то уже это есть
// TODO здесь было $id("category")
if (null != $x1("div[@class='divMain']/div[@class='divContainer']/div[@class='content']/div[@class='text']/div[not(@class)]/table[contains(@class, 'boardTable')]")) {
this.pageType = CPageModel.PAGE_TYPE_MAIN_FORUM_PAGE;
} else {
this.pageType = CPageModel.PAGE_TYPE_OTHER_FORUM_PAGE;
}
} else {
this.pageType = CPageModel.PAGE_TYPE_OTHER_SITE_PAGE;
}
log("Page type = " + this.pageType);
if (CPageModel.PAGE_TYPE_TOPIC == this.pageType) {
// TODO дублирование кода, где-то уже это есть
this.topicId = getTopicId();
log("topic_id = " + this.topicId);
// if (null != this.topicId) {
// this.topicId = input.value;
// }
// TODO: Дублирование кода, см. CPostSearcher
// Нахожу навигатор по страницам и определяю текущую страницу (находится в теге <b>)
// Ищем тэг B, после которого идёт текст, содержащий "]"
// var xpath = "tbody[1]/tr[1]/td[1]/table[not(@id)]/tbody[1]/tr[1]/td[@class='middletext']/b[following-sibling::node()[position()=1 and contains(self::text(), ']')]]";
var xpath = "//body/div[@class='divMain']/div[@class='divContainer']/" +
"div[@class='content']/div[@class='text']/div[not(@class)]/" +
"table[not(@id)]/tbody[1]/tr[1]/td[@class='middletext']/" +
"b[following-sibling::node()[position()=1 and contains(self::text(), ']')]]";
// TODO $id("content")
var b = $x1(xpath, $id("content"));
if (b != null) {
this.currentPage = parseInt(b.innerHTML);
log("currentPage = " + this.currentPage);
} else {
thtowError("CPostSearcher(): Navigator wasn't found");
}
// TODO: Дублирование кода, см. CPostSearcher
// Определяю номер последней страницы темы (последняя ссылка в навигаторе)
// xpath = "tbody[1]/tr[1]/td[1]/table[not(@id)]/tbody[1]/tr[1]/td[@class='middletext']/a[@class='navPages' and position()=last()]";
xpath = "//body/div[@class='divMain']/div[@class='divContainer']/" +
"div[@class='content']/div[@class='text']/div[not(@class)]/" +
"table[not(@id)]/tbody[1]/tr[1]/td[@class='middletext']/" +
"a[@class='navPages' and position()=last()]";
// TODO $id("content")
var href = $x1(xpath, $id("content"));
this.lastPage = href != null ? parseInt(href.innerHTML) : 1;
this.lastPage = Math.max(this.lastPage, this.currentPage);
log("lastPage = " + this.lastPage);
if (this.currentPage != 1) {
this.firstPageUrl = this.buildPageUrl(1);
this.prevPageUrl = this.buildPageUrl(this.currentPage - 1);
}
if (this.currentPage != this.lastPage) {
this.nextPageUrl = this.buildPageUrl(this.currentPage + 1);
this.lastPageUrl = this.buildPageUrl(this.lastPage);
}
}
}
CPageModel.PAGE_TYPE_ARCHIVE = 0x0201;
CPageModel.PAGE_TYPE_JOURNAL = 0x0202;
CPageModel.PAGE_TYPE_MAIN_FORUM_PAGE = 0x0104;
CPageModel.PAGE_TYPE_BOARD = 0x0108;
CPageModel.PAGE_TYPE_TOPIC = 0x0510;
CPageModel.PAGE_TYPE_MESSAGE = 0x0420; // Форма отправки/редактирования сообщения
CPageModel.PAGE_TYPE_OTHER_FORUM_PAGE = 0x0140;
CPageModel.PAGE_TYPE_OTHER_SITE_PAGE = 0x0280;
// Страницы форума, на которых надо показывать меню, поиск и настройки
CPageModel.PAGE_TYPE_FORUM_PAGE_GROUP = 0x0100;
// Прочие страницы сайта
CPageModel.PAGE_TYPE_SITE_GROUP = 0x0200;
// Страницы форума отображающие содержиое темы: просмотр темы и отправка сообщения
CPageModel.PAGE_TYPE_FORUM_TOPIC_GROUP = 0x0400;
CPageModel.prototype = {
isClosedTopic: function() {
if (this.pageType != CPageModel.PAGE_TYPE_TOPIC)
return false;
if (typeof this.isClosed == "undefined") {
// TODO дублирование кода
var xpathDivMain = "div[@class='divMain']";
var xpathDivContainer = xpathDivMain + "/div[@class='divContainer']";
var xpathDivContent = xpathDivContainer + "/div[@class='content']";
var xpathDivText = xpathDivContent + "/div[@class='text']";
var xpathDivTopMenu = xpathDivMain + "/div[@class='topMenu']";
var xpathULUserLinks = xpathDivTopMenu + "/ul[@class='userLinks']";
// Проверяю, вошёл ли пользователь
var xpath = xpathULUserLinks + "/li/a[contains(@href, '/user/signup')]";
var userSignedup = null == $x1(xpath);
if (userSignedup) {
// Проверяю, есть ли кнопка "Ответить"
// TODO как-нибудь надо упростить обращение с xpath
var xpath = xpathDivText + "/div[not(@class)]/table[not(@class)]/tbody[1]/tr[1]/td[2]/a[contains(@href, 'action=post')]";
this.isClosed = null == $x1(xpath);
} else {
this.isClosed = false;
}
}
return this.isClosed;
},
buildPageUrl: function(page) {
var start = (page - 1) * 20;
return "/forum/index.php/topic," + this.topicId + "." + start + ".html";
}
};
function CPageController(isForumPage, isTopic) {
log("CPageController()");
app.addController(this);
this.settings = new CSettingsModel();
this.model = new CPageModel();
this.view = new CPageView(this, this.model);
if ((this.model.pageType & CPageModel.PAGE_TYPE_FORUM_PAGE_GROUP) != 0) {
this.searchCtrl = new CSearchController(this, this.view.menuCtrl);
// Настройка меню скрипта
this.settingsCtrl = new CSettingsController(this.settings, this.view.menuCtrl);
this.view.createUserMenuItems();
}
if (CPageModel.PAGE_TYPE_TOPIC == this.model.pageType) {
this.userIgnoreController = new CUserIgnoreController(this.settings);
this.userIgnoreController.collapse();
// Горячие клавиши для навигации по теме
// ctrl + ←
app.addHotKey("ctrl+37", this);
// ctrl + →
app.addHotKey("ctrl+39", this);
// ctrl + Home
app.addHotKey("ctrl+36", this);
// ctrl + End
app.addHotKey("ctrl+35", this);
}
if (CPageModel.PAGE_TYPE_MAIN_FORUM_PAGE == this.model.pageType) {
// Горячие клавиши для сворачивания/разворачивания тем на первой странице
// alt + ctrl + ↑
app.addHotKey("alt+ctrl+38", this);
// ctrl + shift + ↑
app.addHotKey("ctrl+shift+38", this);
// alt + ctrl + ↓
app.addHotKey("alt+ctrl+40", this);
// ctrl + shift + ↓
app.addHotKey("ctrl+shift+40", this);
}
}
CPageController.prototype = {
handle: function(src, e) {
log("CPageController.handle(" + src + ", " + e.type + ")");
if (e.type == "click") {
if (src == this.view.collapseAllBoards) {
// TODO: переделать
topicWrapper.collapse();
return false;
} else if (src == this.view.expandAllBoards) {
// TODO: переделать
topicWrapper.expand();
return false;
} else if (src == this.view.collapseIgnoredMsgs) {
this.userIgnoreController.collapse();
return false;
} else if (src == this.view.expandIgnoredMsgs) {
this.userIgnoreController.expand();
return false;
} else if (src == this.view.btnCode || src.parentNode == this.view.btnCode) {
wnd.surroundText("[code]", "[/code]", this.view.textArea);
return false;
} else if (src == this.view.btnUrl || src.parentNode == this.view.btnUrl) {
// Введите URL. Например, http://yandex.ru
var href = wnd.prompt("\u0412\u0432\u0435\u0434\u0438\u0442\u0435 URL. " +
"\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, http://yandex.ru", "");
wnd.surroundText("[url=" + href + "]", "[/url]", this.view.textArea);
return false;
} else if (src == this.view.btnQuote || src.parentNode == this.view.btnQuote) {
// Укажите автора цитаты или её источник. Например, avanturist.
// Можете ничего не указывать.
// Кнопка "Отменить" отменяет создание цитаты.
var source = wnd.prompt("\u0412\u0432\u0435\u0434\u0438\u0442\u0435 " +
"\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a " +
"\u0446\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. " +
"\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, avanturist.\n" +
"\u041c\u043e\u0436\u0435\u0442\u0435 " +
"\u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 " +
"\u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c.\n" +
"\u041a\u043d\u043e\u043f\u043a\u0430 " +
"\"\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c\" " +
"\u043e\u0442\u043c\u0435\u043d\u044f\u0435\u0442 " +
"\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 " +
"\u0446\u0438\u0442\u0430\u0442\u044b.", "");
if (source != null) {
var openQuote = "[quote" + (source != "" ? "=" + source : "") + "]";
wnd.surroundText(openQuote, "[/quote]", this.view.textArea);
}
return false;
} else if (src == this.view.btnFormatTable || src.parentNode == this.view.btnFormatTable) {
var textArea = this.view.textArea;
// Can a text range be created?
if (typeof(textArea.caretPos) != "undefined" && textArea.createTextRange) {
var caretPos = textArea.caretPos;
var len = caretPos.text.length;
var formatted = this.formatTable(caretPos.text);
if (formatted.length > 0) {
caretPos.text = formatted;
textArea.focus(caretPos);
}
} else if (typeof(textArea.selectionStart) != "undefined") {
// Mozilla text range wrap
var begin = textArea.value.substr(0, textArea.selectionStart);
var selection = textArea.value.substr(textArea.selectionStart,
textArea.selectionEnd - textArea.selectionStart);
var end = textArea.value.substr(textArea.selectionEnd);
var newCursorPos = textArea.selectionStart;
var scrollPos = textArea.scrollTop;
var formatted = this.formatTable(selection);
if (formatted.length > 0) {
textArea.value = begin + formatted + end;
if (textArea.setSelectionRange) {
textArea.setSelectionRange(newCursorPos, newCursorPos + formatted.length);
textArea.focus();
}
textArea.scrollTop = scrollPos;
}
} else {
// Извините, функция не поддерживается вашим браузером
alert("\u0418\u0437\u0432\u0438\u043d\u0438\u0442\u0435, " +
"\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 " +
"\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f " +
"\u0432\u0430\u0448\u0438\u043c " +
"\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c");
}
return false;
} else {
logError("An unexpected event. src.id = " + src.id + ", e.type = " + e.type);
return true;
}
} else if (e.type == "mouseover") {
if (wnd.bbc_highlight) {
wnd.bbc_highlight(src, true);
}
return false;
} else if (e.type == "mouseout") {
if (wnd.bbc_highlight) {
wnd.bbc_highlight(src, false);
}
return false;
} else {
logError("An unexpected event. src.id = " + src.id + ", e.type = " + e.type);
return true;
}
},
handleHotKey: function(hotKey) {
log("CPageController.handleHotKey(" + hotKey + ")");
if ("alt+ctrl+38" == hotKey || "ctrl+shift+38" == hotKey) {
// alt + ctrl + ↑ ИЛИ ctrl + shift + ↑
// TODO: переделать
topicWrapper.collapse();
} else if ("alt+ctrl+40" == hotKey || "ctrl+shift+40" == hotKey) {
// alt + ctrl + ↓ ИЛИ alt + shift + ↓
// TODO: переделать
topicWrapper.expand();
} else if ("ctrl+37" == hotKey) {
// ctrl + ←
if (null != this.model.prevPageUrl) {
window.location.replace(this.model.prevPageUrl);
};
} else if ("ctrl+39" == hotKey) {
// ctrl + →
if (null != this.model.nextPageUrl) {
window.location.replace(this.model.nextPageUrl);
};
} else if ("ctrl+36" == hotKey) {
// ctrl + Home
if (null != this.model.firstPageUrl) {
window.location.replace(this.model.firstPageUrl);
};
} else if ("ctrl+35" == hotKey) {
// ctrl + End
if (null != this.model.lastPageUrl) {
window.location.replace(this.model.lastPageUrl);
};
}
},
handleSaveToArchive: function(src, e) {
log("CPageController.handleSaveToArchive(" + src + ", " + e.type + ")");
var topic_id = $(src).parent().parent().find("input[name='topic_id']").val();
var message_id = $(src).parent().parent().find("input[name='message_id']").val();
// Проверяем входные параметры
if (topic_id == 'undefined' || message_id == 'undefined') {
// Ошибка сохранения материала в ваш личный архив!
alert("\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u0432 \u0432\u0430\u0448 \u043b\u0438\u0447\u043d\u044b\u0439 \u0430\u0440\u0445\u0438\u0432!");
return;
}
// отправляем запрос
$.getJSON("/forum/index.php?action=all_ajax_save_to_archive",
{topic_id: topic_id, message_id: message_id},
function(data) {
if (data.result == 1) {
$(src).hide();
} else {
alert(data.result_status);
}
}
);
return false;
},
formatTable: function(text) {
// Сначала удаляем все лишние символы сначала и конца строки
var start = text.length;
for (var i = 0; i < text.length; i++) {
var ch = text[i];
if (ch != " " && ch != "\r" && ch != "\n") {
start = i;
break;
}
}
if (start == text.length) {
logError("CPageController.formatTable(): the text is empty");
return "";
}
var end = 0;
for (var i = text.length - 1; i > start; i--) {
var ch = text[i];
if (ch != " " && ch != "\r" && ch != "\n") {
end = i;
break;
}
}
var res = "[table]\n[tr][td]";
var len = text.length;
for (var i = start; i <= end; i++) {
var ch = text[i];
if (ch == "\t") {
res += "[/td][td]";
} else if (ch == "\n") {
res += "[/td][/tr]\n[tr][td]";
} else if (ch == "\r") {
// Ignore
} else {
res += ch;
}
}
res += "[/td][/tr]\n[/table]\n";
return res;
}
};
function CPageView(controller, model) {
log("CPageView()");
this.controller = controller;
this.model = model;
if ((model.pageType & CPageModel.PAGE_TYPE_FORUM_PAGE_GROUP) != 0) {
this.menuCtrl = new CMenuControl();
var item = this.menuCtrl.createItem(
// СТАТИСТИКА
"\u0421\u0422\u0410\u0422\u0418\u0421\u0422\u0418\u041a\u0410",
"/forum/index.php?action=stats");
this.menuCtrl.insertTopLinksItem(item, 3);
item = this.menuCtrl.createItem(
"<a>" +
"<img src='/forum/Themes/default/images/ga_ico_search.gif' " +
"style='vertical-align:middle; margin-bottom:1px;'/>" +
// Поиск
" \u041f\u043e\u0438\u0441\u043a" +
"</a>",
"/forum/index.php?action=search");
this.menuCtrl.insertTopUserItem(item, 0);
item = this.menuCtrl.createItem(
// Непрочитанные
"\u041d\u0435\u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 " +
// темы
"\u0442\u0435\u043c\u044b",
"/forum/index.php?action=unread");
this.menuCtrl.insertScriptLinksItem(item);
item = this.menuCtrl.createItem(
// Непрочитанные
"\u041d\u0435\u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 " +
// ответы
"\u043e\u0442\u0432\u0435\u0442\u044b",
"/forum/index.php?action=unreadreplies");
this.menuCtrl.insertScriptLinksItem(item);
item = this.menuCtrl.createItem(
// Последние
"\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 " +
// сообщения
"\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f",
"/forum/index.php?action=recent");
this.menuCtrl.insertScriptLinksItem(item);
}
if (model.pageType == CPageModel.PAGE_TYPE_MESSAGE) {
var xpath = "table/tbody[1]/tr[2]/td[1]/table[1]/tbody[1]/tr/td[2]/textarea[@name='message']";
this.textArea = $x1(xpath, $id("postmodify"));
if (this.textArea == null) {
logError("CPageView(): the text area wasn't found");
return;
}
xpath = "table/tbody[1]/tr[2]/td[1]/table[1]/tbody[1]/tr/td[2]/br[preceding-sibling::img[position()=1 and @alt='|']]";
var br = $x1(xpath, $id("postmodify"));
if (br == null) {
logError("CPageView(): <br> in the buttons cell wasn't found");
return;
}
var td = br.parentNode;
td.removeChild(br);
// Ищем картинку разделитель, если находим, то добавляем разделитель
for (var i = 0; i < td.childNodes.length; i++) {
var child = td.childNodes[i];
if (child.tagName != null && child.tagName == "IMG") {
td.appendChild(child.cloneNode(true));
td.appendChild(document.createTextNode(" "));
break;
}
}
// Форматировать таблицу
this.btnFormatTable = this.createButton("/forum/Themes/default/images/bbc/flash.gif",
"\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c " +
"\u0442\u0430\u0431\u043b\u0438\u0446\u0443");
td.appendChild(this.btnFormatTable);
// Код
this.btnCode = this.createButton("/forum/Themes/default/images/bbc/code.gif",
"\u041a\u043e\u0434");
td.appendChild(this.btnCode);
// Вставить URL
this.btnUrl = this.createButton("/forum/Themes/default/images/bbc/url.gif",
"\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c URL");
td.appendChild(this.btnUrl);
// Цитата
this.btnQuote = this.createButton("/forum/Themes/default/images/bbc/quote.gif",
"\u0426\u0438\u0442\u0430\u0442\u0430");
td.appendChild(this.btnQuote);
}
if (model.pageType == CPageModel.PAGE_TYPE_TOPIC) {
if (model.isClosedTopic()) {
this.addSaveToArchiveButtons();
}
if (controller.settings.sett_addImageLinks) {
this.addImageLinks();
}
// TODO: добавить настройку
this.updateHeadLinks();
}
if ((model.pageType & CPageModel.PAGE_TYPE_FORUM_TOPIC_GROUP) != 0) {
if (controller.settings.sett_borderTables) {
this.borderTables();
}
}
if ((model.pageType & CPageModel.PAGE_TYPE_FORUM_PAGE_GROUP) != 0 ||
(model.pageType & CPageModel.PAGE_TYPE_FORUM_TOPIC_GROUP) != 0) {
this.addScriptVersion();
}
this.addImageAlt();
this.addFavicon();
this.addCustomCSS();
}
CPageView.prototype = {
createUserMenuItems: function(parentNode) {
if (CPageModel.PAGE_TYPE_MAIN_FORUM_PAGE == this.model.pageType) {
// Свернуть разделы
this.collapseAllBoards = this.appendScriptMenuItem(
"\u0421\u0432\u0435\u0440\u043d\u0443\u0442\u044c " +
"\u0440\u0430\u0437\u0434\u0435\u043b\u044b");
// Развернуть разделы
this.expandAllBoards = this.appendScriptMenuItem(
"\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c " +
"\u0440\u0430\u0437\u0434\u0435\u043b\u044b");
} else if (CPageModel.PAGE_TYPE_TOPIC == this.model.pageType) {
// Свернуть игнорируемые сообщения
this.collapseIgnoredMsgs = this.appendScriptMenuItem(
"\u0421\u0432\u0435\u0440\u043d\u0443\u0442\u044c " +
"\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0435 " +
"\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f");
// Развернуть игнорируемые сообщения
this.expandIgnoredMsgs = this.appendScriptMenuItem(
"\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c " +
"\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0435 " +
"\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f");
}
},
appendScriptMenuItem: function(title) {
var href = document.createElement("A");
href.href = "#";
href.innerHTML = title;
app.addEventListener(href, "click", this.controller);
var item = this.menuCtrl.createItem("");
item.appendChild(href);
this.menuCtrl.insertScriptMenuItem(item);
return href;
},
createButton: function(imgUrl, title) {
var img = document.createElement("IMG");
img.src = imgUrl;
img.title = title;
img.alt = title;
img.height = 22;
img.width = 23;
img.align = "bottom";
img.style.margin = "0px 2px 0px 1px";
img.style.backgroundImage = "url(/forum/Themes/default/images/bbc/bbc_bg.gif)";
img.style.verticalAlign = "middle";
app.addEventListener(img, "mouseout", this.controller);
app.addEventListener(img, "mouseover", this.controller);
var href = document.createElement("A");
href.href = "#";
app.addEventListener(href, "click", this.controller);
href.appendChild(img);
return href;
},
borderTables: function() {
var cssCode =
".text .table .table .post table, #preview_section .post table {" +
"border: 2px solid rgb(213, 213, 234);" +
"border-spacing: 0px;" +
"border-collapse: collapse;" +
"margin-top: 5px; } " +
".text .table .table .post tr:first-child, #preview_section .post tr:first-child {" +
"font-weight: bold; }" +
".text .table .table .post td, #preview_section .post td {" +
"border: 1px solid rgb(213, 213, 234);" +
"padding: 2px;" +
"}";
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = cssCode;
} else {
styleElement.appendChild(document.createTextNode(cssCode));
}
document.getElementsByTagName("head")[0].appendChild(styleElement);
},
// Окружаю рисунки без ссылок на источник, такими ссылками
addImageLinks: function() {
log("CPageView.addImageLinks()");
// Выбираю все рисунки из текстов сообщений, которые:
// 1. не содержат в пути "avanturist.org/",
// 2. путь к нем начинается с "http://",
// 3. не окружены ссылками
// 4. имеют теги height и width
var xpath = xpathPostBody + "//img[" +
"not(contains(@src, 'avanturist.org/')) and " +
"starts-with(@src, 'http://') and " +
"string-length(@width) > 0 and string-length(@height) > 0 and " +
"count(ancestor::a)=0]";
var snapshot = $x(xpath, $id("topic_posts"));
log("Found images: " + snapshot.snapshotLength);
for (var i = 0; i < snapshot.snapshotLength; i++) {
var img = snapshot.snapshotItem(i);
var href = document.createElement("A");
href.href = img.src;
href.target = "_blank";
img.parentNode.insertBefore(href, img);
href.appendChild(img);
}
},
// Добавляю атрибут alt ко всем рисункам без этого атрибута
addImageAlt: function() {
log("CPageView.addImageAlt()");
// Выбираю все рисунки без атрибута alt
var xpath = "//img[@alt='']";
var snapshot = $x(xpath);
log("Found images without alt: " + snapshot.snapshotLength);
for (var i = 0; i < snapshot.snapshotLength; i++) {
var img = snapshot.snapshotItem(i);
img.alt = "IMG";
}
},
addSaveToArchiveButtons: function() {
log("CPageView.addSaveToArchiveButtons()");
// Получаю последние ячейки в таблице, которая находится в правой верхней ячейки таблицы сообщения
var xpath = xpathMsgTableBody + "/tr[1]/td[2]/table/tbody/tr[1]/td[count(../td)]";
var snapshot = $x(xpath, $id("topic_posts"));
for (var i = 0; i < snapshot.snapshotLength; i++) {
var td = snapshot.snapshotItem(i);
var img = document.createElement("IMG");
img.src = "/forum/Themes/default/images/btn_save_to_archive.gif";
img.style.verticalAlign = "middle";
// Сохранить в архив
img.alt = "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0430\u0440\u0445\u0438\u0432";
img.title = img.alt;
var href = document.createElement("A");
href.href = "#";
href.appendChild(img);
app.addEventListener(href, "click", this.controller, "handleSaveToArchive");
td.appendChild(href);
}
},
updateHeadLinks: function() {
log("CPageView.updateHeadLinks()");
var head = document.getElementsByTagName("HEAD")[0];
// Только (?) для Opera
var link = document.createElement("LINK");
link.href = "#";
link.rel = "up";
head.appendChild(link);
link = document.createElement("LINK");
link.href = "/forum/index.php?action=search";
link.rel = "search";
head.appendChild(link);
link = document.createElement("LINK");
link.href = "/forum/index.php";
link.rel = "Index";
head.appendChild(link);
link = document.createElement("LINK");
link.href = "/forum/index.php";
link.rel = "Contents";
head.appendChild(link);
link = document.createElement("LINK");
link.href = "/forum/index.php?action=help";
link.rel = "Help";
head.appendChild(link);
if (null != this.model.firstPageUrl) {
link = link.cloneNode(true);
link.href = this.model.firstPageUrl;
link.rel = "start";
head.appendChild(link);
// Только (?) для Opera
link = link.cloneNode(true);
link.rel = "first";
head.appendChild(link);
}
if (null != this.model.lastPageUrl) {
link = link.cloneNode(true);
link.href = this.model.lastPageUrl;
link.rel = "last";
head.appendChild(link);
}
var xpath = "/html/head/link[@rel='prev']";
link = $x1(xpath);
if (null != this.model.prevPageUrl) {
if (null == link) {
log("CPageView.updateHeadLinks(): <link rel='prev'> wasn't found");
link = document.createElement("LINK");
link.rel = "prev";
head.appendChild(link);
}
link.href = this.model.prevPageUrl;
} else if (null != link) {
link.parentNode.removeChild(link);
}
xpath = "/html/head/link[@rel='next']";
link = $x1(xpath);
if (null != this.model.nextPageUrl) {
if (null == link) {
log("CPageView.updateHeadLinks(): <link rel='next'> wasn't found");
link = document.createElement("LINK");
link.rel = "next";
head.appendChild(link);
}
link.href = this.model.nextPageUrl;
} else if (null != link) {
link.parentNode.removeChild(link);
}
},
addFavicon: function() {
var head = document.getElementsByTagName("HEAD")[0];
var link = document.createElement("LINK");
link.href = "data:image/x-icon;base64," +
"AAABAAIAEBAAAAEACABoBQAAJgAAABAQAAABACAAaAQAAI4FAAAoAAAAEAAA" +
"ACAAAAABAAgAAAAAAEABAAAAAAAAAAAAAAABAAAAAAAAAAAAAP///wBENwQA" +
"nq+kAKOwpACcppcAoK+jAJekmQCerp4AmaGOAJOdgQCNmHgAmKiOAJiihgCb" +
"n38AkZiAAJGcfQCWnIIAl56CAJ2nlACgqZwApK6fAKe0qgCxwLEAcndBAEtM" +
"EwA8KAAAKRUAACELAACanZsAoaqXAI2ZfQCMl3YAjpRyAI+cfgCVpIsAlaGL" +
"AJ+rmwCgq5YAhY9wAEdFEwA0GwwAJQQAACYAAAAkAAAANB0jAKawkQCPm4AA" +
"k5+AAJWbfwCao4sAl6GIAJKZegCcoYYAnaWKAGJfQQAjAAAALgIIAMnNwACf" +
"qJQAo6qgAKKvmACepIkAmaGDAJadfQCSmoEAqK2OAGBZQQBHoT8AOkkQAFA8" +
"NwBARB0ANZI6ACeJIgC4zMIAt8OwAKiyqQCpuasAprKkAI6TbwCTmXYAn6J+" +
"AFRGMgB2bU8AanVLADtwIgBgiFsASoRSAFOYVABWOzwAIAUAAHFtbACkr54A" +
"lqaRAKezqQCWnHIAmp15AJedfgCosIgAWGKHAGCKzwAtdhEAUIJ3AEZdPwBN" +
"fyYAMFxKAKizuACbp4EAlZ16AJejgwCmrpUAmJt4AJCZbgCSmXQAmJ1qAE9s" +
"wwBxqYgAMYw1ADmMRABQpzIARViTAC8xlQB1enIAn6N2AJ6fdQCboYAAoauR" +
"AKGkjwCcoH0AkZd2AJ2feABkfrEAbaulAFrAqwAio1QAKqNCAEaBHQAgK6YA" +
"tbuSAJyeewCgpogAn6SCAKWvlQClr54AsbmuALG/sgC6xbgAeo+vAHuprQAe" +
"nSUAK58zACyXFQBFei4AOD6YAMPJrQCutqIArbWkAKatmQCot6UArrWfAK20" +
"rQCxu7EArL62ALK/qwBfh9oAVIZbADp5AAA6eBAAQ1umAHlxkwC7xakAt8Cy" +
"ALO6pwCxvrAAtryqAJWhhwCmr5wAs7qqAKu0pACjr6MAXXKaAFB51ACBqPAA" +
"QWXQAB44jQCCfIAAsrifAKitlgCjsJcAqK+ZAKu0mwCeqI8AnKmTAKOtoQCq" +
"tacAiJaAAIGBbQCAlckALk6iABcddQCJg6IAW19AAIuLdQClrJQAkpl5AKGn" +
"iwChpYYAusKvALLArQDAzsYAm6iZAEtICQBkYlcA6P/vALS8zQDR7e8AxdnU" +
"AExLLQAnEAAAdHNlALW/sACus5gAsLaZAKy7sQCDgWkAUU8iADkqAgBRURoA" +
"QTQbANv38wBudb0Ag5CzAMvm8AAqHgAARjcTADUjAAA/NQ8Af3xrALvGtQBJ" +
"RR0AWlgmAEU4FQBQTCEAPCwAALzP0wCBiscAkqS/ALPNyQAvFgAASUYVAEEs" +
"EwBSSxoAUk0TADo1AgBwjIQAEEREAALv8PHy8/T19vf4+fr7/P3f4OHi4+Tl" +
"5ufo6err7O3uz9DR0tPU1dbX2Nna29zd3r/AwcLDxMXGx8jJysvMzc6vsLGy" +
"s7S1tre4ubq7vL2+n6ChoqOkpaanqKmqq6ytro+QkZKTlJWWl5iZmpucnZ5/" +
"gIGCg4SFhoeIiYqLjI2Ob3BxcnN0dXZ3eHl6e3x9fl9gYWJjZGVmZ2hpamts" +
"bW5PUFFSU1RVVldYWVpbXF1eP0BBQkNERUZHSElKS0xNTjM0NTY3OCssLCw5" +
"Ojs8PT4jJCUmJygpKissLS4vMDEyExQVFhcYGRobHB0eHyAhIgMEBQYHCAkK" +
"CwwNDg8QERIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAABAAAAAgAAAAAQAgAAAA" +
"AABABAAAAAAAAAAAAAAAAAAAAAAAAEQ3BP9JRR3/Wlgm/0U4Ff9QTCH/PCwA" +
"/7zP0/+Bisf/kqS//7PNyf8vFgD/SUYV/0EsE/9SSxr/Uk0T/zo1Av+su7H/" +
"g4Fp/1FPIv85KgL/UVEa/0E0G//b9/P/bnW9/4OQs//L5vD/Kh4A/0Y3E/81" +
"IwD/PzUP/398a/+7xrX/usKv/7LArf/Azsb/m6iZ/0tICf9kYlf/6P/v/7S8" +
"zf/R7e//xdnU/0xLLf8nEAD/dHNl/7W/sP+us5j/sLaZ/56oj/+cqZP/o62h" +
"/6q1p/+IloD/gYFt/4CVyf8uTqL/Fx11/4mDov9bX0D/i4t1/6WslP+SmXn/" +
"oaeL/6Glhv+VoYf/pq+c/7O6qv+rtKT/o6+j/11ymv9QedT/gajw/0Fl0P8e" +
"OI3/gnyA/7K4n/+orZb/o7CX/6ivmf+rtJv/rrWf/620rf+xu7H/rL62/7K/" +
"q/9fh9r/VIZb/zp5AP86eBD/Q1um/3lxk/+7xan/t8Cy/7O6p/+xvrD/tryq" +
"/6Wvnv+xua7/sb+y/7rFuP96j6//e6mt/x6dJf8rnzP/LJcV/0V6Lv84Ppj/" +
"w8mt/662ov+ttaT/pq2Z/6i3pf+hpI//nKB9/5GXdv+dn3j/ZH6x/22rpf9a" +
"wKv/IqNU/yqjQv9GgR3/ICum/7W7kv+cnnv/oKaI/5+kgv+lr5X/mJt4/5CZ" +
"bv+SmXT/mJ1q/09sw/9xqYj/MYw1/zmMRP9QpzL/RViT/y8xlf91enL/n6N2" +
"/56fdf+boYD/oauR/5accv+anXn/l51+/6iwiP9YYof/YIrP/y12Ef9Qgnf/" +
"Rl0//01/Jv8wXEr/qLO4/5ungf+VnXr/l6OD/6aulf+Ok2//k5l2/5+ifv9U" +
"RjL/dm1P/2p1S/87cCL/YIhb/0qEUv9TmFT/Vjs8/yAFAP9xbWz/pK+e/5am" +
"kf+ns6n/maGD/5adff+SmoH/qK2O/2BZQf9HoT//OkkQ/1A8N/9ARB3/NZI6" +
"/yeJIv+4zML/t8Ow/6iyqf+puav/prKk/5ehiP+SmXr/nKGG/52liv9iX0H/" +
"IwAA/yYAAP8kAAD/JAAA/yQAAP8uAgj/yc3A/5+olP+jqqD/oq+Y/56kif+V" +
"pIv/laGL/5+rm/+gq5b/hY9w/0dFE/80Gwz/JQQA/yYAAP8kAAD/NB0j/6aw" +
"kf+Pm4D/k5+A/5Wbf/+ao4v/naeU/6CpnP+krp//p7Sq/7HAsf9yd0H/S0wT" +
"/zwoAP8pFQD/IQsA/5qdm/+hqpf/jZl9/4yXdv+OlHL/j5x+/56vpP+jsKT/" +
"nKaX/6Cvo/+XpJn/nq6e/5mhjv+TnYH/jZh4/5iojv+Yoob/m59//5GYgP+R" +
"nH3/lpyC/5eegv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
link.rel = "shortcut icon";
link.type = "image/x-icon";
head.appendChild(link);
},
addCustomCSS: function() {
log("CPageView.addCustomCSS()");
var cssCode =
// Подчёрквает синим пунктиром ссылки в цитатах
".signature a {" +
"text-decoration: none !important;" +
"border-bottom: 1px dashed blue;}" +
// Отменяет подчёркивание и пр. в ссылках в цитатах
".signature a * {" +
"text-decoration: none !important;}" +
// Подчёрквает ссылки в цитатах
".quote a {" +
"text-decoration: underline !important;" +
"color: blue !important;}" +
// Запрет подчёркивания ссылок в заголовках вложенных цитат
".quoteheader a {" +
"text-decoration: none !important;" +
"color: black !important;}" +
// Подчёркивание ссылок в заголовках цитат при наведении на них курсора мыши
".quoteheader a:hover {" +
"text-decoration: underline !important;" +
"color: blue !important;}" +
// Набор стилей для свёрнутых сообщений
".message_hidden_york {" +
"border: 1px solid #3C61A4;" +
"background-color: #d5d5ea; }" +
".message_hidden_york td {" +
"background-color: #e8e8ff; }" +
".message_hidden_york .ga_message_expert_vote_rate {" +
"float: right; }" +
"";
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = cssCode;
} else {
styleElement.appendChild(document.createTextNode(cssCode));
}
document.getElementsByTagName("head")[0].appendChild(styleElement);
},
addScriptVersion: function() {
log("CPageView.addScriptVersion()");
// Нахожу BR после строки "Все права защищены..."
// TODO дублирование кода!!!
var xpathDivMain = "div[@class='divMain']";
var xpathDivBottom = xpathDivMain + "/div[@class='bottom']";
var xpath = xpathDivBottom + "/div[not(@class)]/br[1]";
var br = $x1(xpath);
if (null == br) {
logError("CPageView.addScriptVersion(): BR wasn't found");
return;
}
var span = document.createElement("SPAN");
var href = document.createElement("A");
href.href = "/forum/index.php/topic,196.html";
href.innerHTML =
// Патч к
"\u041f\u0430\u0442\u0447 \u043a " +
// форуму
"\u0444\u043e\u0440\u0443\u043c\u0443 v" + scriptVersion +
// от
" \u043e\u0442 " + scriptDate;
// Отменяю все выделения для ссылок внизу страницы, так они будут
// выглядеть как обычный текст
href.style.fontWeight = "normal";
href.style.color = "#666666";
// Чтобы всё же хоть как-то выделить ссылку делаю её подчёркнутой
href.style.textDecoration = "underline";
span.appendChild(document.createTextNode(" "));
span.appendChild(href);
br.parentNode.insertBefore(span, br);
}
}
// ****************************************************************************
// ********************************** App *************************************
// ****************************************************************************
app = {
controllers: new Array(),
run: function() {
log("app.run()");
this.hotKeys = new Array();
document.addEventListener("keydown", app.keyPressed, false);
this.pageController = new CPageController();
var isForumPage = false;
var isTopic = false;
if (jQueryInstalled && /myarchive/.test(url)) {
handleArchivePage();
} else if (jQueryInstalled && /myjournal/.test(url)) {
handleJournalPage();
} else if (jQueryInstalled && /index\.php.+topic[=,]/.test(url)) {
isForumPage = true;
isTopic = true;
handleTopicPage();
} else if (jQueryInstalled && /index\.php.+board[=,]/.test(url)) {
isForumPage = true;
handleBoardPage();
} else if (jQueryInstalled && /\/forum\//.test(url)) {
// TODO здесь было $id("category")
if (null != $x1("div[@class='divMain']/div[@class='divContainer']/div[@class='content']/div[@class='text']/div[not(@class)]/table[contains(@class, 'boardTable')]")) {
isForumPage = true;
handleForumMainPage();
} else {
isForumPage = true;
handleOtherForumPages();
}
} else {
handleOtherSitePages();
}
},
addController: function(controller) {
controller.idx = app.controllers.length;
app.controllers[controller.idx] = controller;
},
actionHandler: function(e) {
log("actionHandler()");
e = e || window.event;
var src = e.srcelement ? e.srcelement : e.target;
var controllerIdx = src.getAttribute("ctrl_idx");
while (controllerIdx == null) {
src = src.parentNode;
if (src != null && typeof src.getAttribute == "function") {
controllerIdx = src.getAttribute("ctrl_idx");
} else {
break;
}
}
if (controllerIdx == null) {
src = e.srcelement ? e.srcelement : e.target;
logError("Controller index wasn't found. Event: type = " + e.type + ", src = " + src);
return true;
}
var controller = app.controllers[parseInt(controllerIdx)];
if (controller != null) {
var res;
var handler = src.getAttribute("ctrl_handler");
if (handler) {
res = controller[handler](src, e);
} else {
res = controller.handle(src, e);
}
if (!res) {
e.returnValue = false;
if (typeof e.preventDefault != "undefined") {
e.preventDefault();
}
}
return res;
} else {
logError("Controller wasn't found. Event: type = " + e.type + ", src = " + src + ", controllerIdx = " + controllerIdx);
return true;
}
},
addEventListener: function(element, type, controller, handler) {
element.setAttribute("ctrl_idx", controller.idx);
if (handler) {
element.setAttribute("ctrl_handler", handler);
}
element.addEventListener(type, app.actionHandler, false);
},
keyPressed: function(e) {
log("keyPressed()");
e = e || window.event;
var keyCode = e.which || e.keyCode;
var hotKey = "";
hotKey += e.altKey ? "alt+" : "";
hotKey += e.ctrlKey ? "ctrl+" : "";
hotKey += e.shiftKey ? "shift+" : "";
hotKey += keyCode;
log("Hot key: " + hotKey);
var ctrls = app.hotKeys[hotKey];
if (null != ctrls) {
log("Controllers count: " + ctrls.length);
for (var i = 0; i < ctrls.length; i++) {
ctrls[i].handleHotKey(hotKey);
}
return false;
}
return true;
},
addHotKey: function(hotKey, controller) {
var hotKeyLower = hotKey.toLowerCase();
var ctrls = this.hotKeys[hotKeyLower];
if (ctrls == null) {
ctrls = new Array();
this.hotKeys[hotKeyLower] = ctrls;
}
ctrls.push(controller);
}
};
// ****************************************************************************
// ********************************* START ************************************
// ****************************************************************************
app.run();
var end = new Date();
log("END: " + end);
log("SCRIPT TOOK " + (end.getTime() - start.getTime()) + " ms");
}
if (/^http:\/\/[^\/]*avanturist.org(\/|\?|$)/.test(url)) {
if (isOpera) {
// TODO оптимизация загрузки страниц архива, улучшить код, написать такую же для журнала
if (/myarchive/.test(url)) {
window.opera.addEventListener(
"BeforeExternalScript",
function (e) {
var src = e.element.getAttribute('src');
if (src.match(/ga_mycolumns\.js$|ga_myjournal\.js$/)) {
e.preventDefault();
}
},
false
);
}
if(typeof(opera.version) == "function" && opera.version() >= 9) {
log("Opera 9.x. Subscribing on the DOMContentLoaded event");
document.addEventListener("DOMContentLoaded", onLoad, false);
} else {
log("Opera older then 9.0. Subscribing on the load event");
document.addEventListener('load', onLoad, false);
}
} else {
log("Firefox. Calling onLoad()");
onLoad();
}
} else {
logError(url + " - isn't avanturist.org. Do nothing.");
}
