Telegram Web, preconfigured for usage in I2P.
http://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
539 lines
12 KiB
539 lines
12 KiB
/*! |
|
* Webogram v0.5.1 - messaging web application for MTProto |
|
* https://github.com/zhukov/webogram |
|
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> |
|
* https://github.com/zhukov/webogram/blob/master/LICENSE |
|
*/ |
|
|
|
var _logTimer = (new Date()).getTime(); |
|
function dT () { |
|
return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']'; |
|
} |
|
|
|
function checkClick (e, noprevent) { |
|
if (e.which == 1 && (e.ctrlKey || e.metaKey) || e.which == 2) { |
|
return true; |
|
} |
|
|
|
if (!noprevent) { |
|
e.preventDefault(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
function isInDOM (element, parentNode) { |
|
if (!element) { |
|
return false; |
|
} |
|
parentNode = parentNode || document.body; |
|
if (element == parentNode) { |
|
return true; |
|
} |
|
return isInDOM(element.parentNode, parentNode) |
|
} |
|
|
|
function checkDragEvent(e) { |
|
if (!e || e.target && (e.target.tagName == 'IMG' || e.target.tagName == 'A')) return false; |
|
if (e.dataTransfer && e.dataTransfer.types) { |
|
for (var i = 0; i < e.dataTransfer.types.length; i++) { |
|
if (e.dataTransfer.types[i] == 'Files') { |
|
return true; |
|
} |
|
} |
|
} else { |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
function cancelEvent (event) { |
|
event = event || window.event; |
|
if (event) { |
|
event = event.originalEvent || event; |
|
|
|
if (event.stopPropagation) event.stopPropagation(); |
|
if (event.preventDefault) event.preventDefault(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
function getScrollWidth() { |
|
var outer = $('<div>').css({ |
|
position: 'absolute', |
|
width: 100, |
|
height: 100, |
|
overflow: 'scroll', |
|
top: -9999 |
|
}).appendTo($(document.body)); |
|
|
|
var scrollbarWidth = outer[0].offsetWidth - outer[0].clientWidth; |
|
outer.remove(); |
|
|
|
return scrollbarWidth; |
|
}; |
|
|
|
function onCtrlEnter (textarea, cb) { |
|
$(textarea).on('keydown', function (e) { |
|
if (e.keyCode == 13 && (e.ctrlKey || e.metaKey)) { |
|
cb(); |
|
return cancelEvent(e); |
|
} |
|
}); |
|
} |
|
|
|
function setFieldSelection(field, from, to) { |
|
field = $(field)[0]; |
|
try { |
|
field.focus(); |
|
if (from === undefined || from === false) { |
|
from = field.value.length; |
|
} |
|
if (to === undefined || to === false) { |
|
to = from; |
|
} |
|
if (field.createTextRange) { |
|
var range = field.createTextRange(); |
|
range.collapse(true); |
|
range.moveEnd('character', to); |
|
range.moveStart('character', from); |
|
range.select(); |
|
} |
|
else if (field.setSelectionRange) { |
|
field.setSelectionRange(from, to); |
|
} |
|
} catch(e) {} |
|
} |
|
|
|
function getFieldSelection (field) { |
|
if (field.selectionStart) { |
|
return field.selectionStart; |
|
} |
|
else if (!document.selection) { |
|
return 0; |
|
} |
|
|
|
var c = "\001", |
|
sel = document.selection.createRange(), |
|
txt = sel.text, |
|
dup = sel.duplicate(), |
|
len = 0; |
|
|
|
try { |
|
dup.moveToElementText(field); |
|
} catch(e) { |
|
return 0; |
|
} |
|
|
|
sel.text = txt + c; |
|
len = dup.text.indexOf(c); |
|
sel.moveStart('character', -1); |
|
sel.text = ''; |
|
|
|
// if (browser.msie && len == -1) { |
|
// return field.value.length; |
|
// } |
|
return len; |
|
} |
|
|
|
function getRichValue(field) { |
|
if (!field) { |
|
return ''; |
|
} |
|
var lines = []; |
|
var line = []; |
|
|
|
getRichElementValue(field, lines, line); |
|
if (line.length) { |
|
lines.push(line.join('')); |
|
} |
|
|
|
return lines.join('\n'); |
|
} |
|
|
|
function getRichValueWithCaret(field) { |
|
if (!field) { |
|
return []; |
|
} |
|
var lines = []; |
|
var line = []; |
|
|
|
var sel = window.getSelection ? window.getSelection() : false; |
|
var selNode, selOffset; |
|
if (sel && sel.rangeCount) { |
|
var range = sel.getRangeAt(0); |
|
if (range.startContainer && |
|
range.startContainer == range.endContainer && |
|
range.startOffset == range.endOffset) { |
|
selNode = range.startContainer; |
|
selOffset = range.startOffset; |
|
} |
|
} |
|
|
|
getRichElementValue(field, lines, line, selNode, selOffset); |
|
|
|
if (line.length) { |
|
lines.push(line.join('')); |
|
} |
|
|
|
var value = lines.join('\n'); |
|
var caretPos = value.indexOf('\001'); |
|
if (caretPos != -1) { |
|
value = value.substr(0, caretPos) + value.substr(caretPos + 1); |
|
} |
|
|
|
return [value, caretPos]; |
|
} |
|
|
|
function getRichElementValue(node, lines, line, selNode, selOffset) { |
|
if (node.nodeType == 3) { // TEXT |
|
if (selNode === node) { |
|
var value = node.nodeValue; |
|
line.push(value.substr(0, selOffset) + '\001' + value.substr(selOffset)); |
|
} else { |
|
line.push(node.nodeValue); |
|
} |
|
return; |
|
} |
|
if (node.nodeType != 1) { // NON-ELEMENT |
|
return; |
|
} |
|
var isSelected = (selNode === node); |
|
var isBlock = node.tagName == 'DIV' || node.tagName == 'P'; |
|
var curChild; |
|
if (isBlock && line.length || node.tagName == 'BR') { |
|
lines.push(line.join('')); |
|
line.splice(0, line.length); |
|
} |
|
else if (node.tagName == 'IMG') { |
|
if (node.alt) { |
|
line.push(node.alt); |
|
} |
|
} |
|
if (isSelected && !selOffset) { |
|
line.push('\001'); |
|
} |
|
var curChild = node.firstChild; |
|
while (curChild) { |
|
getRichElementValue(curChild, lines, line, selNode, selOffset); |
|
curChild = curChild.nextSibling; |
|
} |
|
if (isSelected && selOffset) { |
|
line.push('\001'); |
|
} |
|
if (isBlock && line.length) { |
|
lines.push(line.join('')); |
|
line.splice(0, line.length); |
|
} |
|
} |
|
|
|
function setRichFocus(field, selectNode, noCollapse) { |
|
field.focus(); |
|
if (selectNode && |
|
selectNode.parentNode == field && |
|
!selectNode.nextSibling && |
|
!noCollapse) { |
|
field.removeChild(selectNode); |
|
selectNode = null; |
|
} |
|
if (window.getSelection && document.createRange) { |
|
var range = document.createRange(); |
|
if (selectNode) { |
|
range.selectNode(selectNode); |
|
} else { |
|
range.selectNodeContents(field); |
|
} |
|
if (!noCollapse) { |
|
range.collapse(false); |
|
} |
|
|
|
var sel = window.getSelection(); |
|
sel.removeAllRanges(); |
|
sel.addRange(range); |
|
} |
|
else if (document.body.createTextRange !== undefined) { |
|
var textRange = document.body.createTextRange(); |
|
textRange.moveToElementText(selectNode || field); |
|
if (!noCollapse) { |
|
textRange.collapse(false); |
|
} |
|
textRange.select(); |
|
} |
|
} |
|
|
|
function getSelectedText () { |
|
var sel = ( |
|
window.getSelection && window.getSelection() || |
|
document.getSelection && document.getSelection() || |
|
document.selection && document.selection.createRange().text || '' |
|
).toString().replace(/^\s+|\s+$/g, ''); |
|
|
|
return sel; |
|
} |
|
|
|
function scrollToNode (scrollable, node, scroller) { |
|
var elTop = node.offsetTop - 15, |
|
elHeight = node.offsetHeight + 30, |
|
scrollTop = scrollable.scrollTop, |
|
viewportHeight = scrollable.clientHeight; |
|
|
|
if (scrollTop > elTop) { // we are below the node to scroll |
|
scrollable.scrollTop = elTop; |
|
$(scroller).nanoScroller({flash: true}); |
|
} |
|
else if (scrollTop < elTop + elHeight - viewportHeight) { // we are over the node to scroll |
|
scrollable.scrollTop = elTop + elHeight - viewportHeight; |
|
$(scroller).nanoScroller({flash: true}); |
|
} |
|
} |
|
|
|
function onContentLoaded (cb) { |
|
setZeroTimeout(cb); |
|
} |
|
|
|
function tsNow (seconds) { |
|
var t = +new Date() + (window.tsOffset || 0); |
|
return seconds ? Math.floor(t / 1000) : t; |
|
} |
|
|
|
function safeReplaceObject (wasObject, newObject) { |
|
for (var key in wasObject) { |
|
if (!newObject.hasOwnProperty(key) && key.charAt(0) != '$') { |
|
delete wasObject[key]; |
|
} |
|
} |
|
for (var key in newObject) { |
|
if (newObject.hasOwnProperty(key)) { |
|
wasObject[key] = newObject[key]; |
|
} |
|
} |
|
} |
|
|
|
function listMergeSorted (list1, list2) { |
|
list1 = list1 || []; |
|
list2 = list2 || []; |
|
|
|
var result = angular.copy(list1); |
|
|
|
var minID = list1.length ? list1[list1.length - 1] : 0xFFFFFFFF; |
|
for (var i = 0; i < list2.length; i++) { |
|
if (list2[i] < minID) { |
|
result.push(list2[i]); |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
function listUniqSorted (list) { |
|
list = list || []; |
|
var resultList = [], |
|
prev = false; |
|
for (var i = 0; i < list.length; i++) { |
|
if (list[i] !== prev) { |
|
resultList.push(list[i]) |
|
} |
|
prev = list[i]; |
|
} |
|
|
|
return resultList; |
|
} |
|
|
|
function templateUrl (tplName) { |
|
var forceLayout = { |
|
confirm_modal: 'desktop', |
|
error_modal: 'desktop', |
|
media_modal_layout: 'desktop', |
|
slider: 'desktop', |
|
reply_message: 'desktop', |
|
forwarded_messages: 'desktop', |
|
chat_invite_link_modal: 'desktop', |
|
reply_markup: 'desktop', |
|
dialog_service: 'desktop', |
|
channel_edit_modal: 'desktop', |
|
megagroup_edit_modal: 'desktop' |
|
}; |
|
var layout = forceLayout[tplName] || (Config.Mobile ? 'mobile' : 'desktop'); |
|
return 'partials/' + layout + '/' + tplName + '.html'; |
|
} |
|
|
|
function encodeEntities(value) { |
|
return value. |
|
replace(/&/g, '&'). |
|
replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, function (value) { |
|
var hi = value.charCodeAt(0); |
|
var low = value.charCodeAt(1); |
|
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; |
|
}). |
|
replace(/([^\#-~| |!])/g, function (value) { // non-alphanumeric |
|
return '&#' + value.charCodeAt(0) + ';'; |
|
}). |
|
replace(/</g, '<'). |
|
replace(/>/g, '>'); |
|
} |
|
|
|
function calcImageInBox(imageW, imageH, boxW, boxH, noZooom) { |
|
var boxedImageW = boxW; |
|
var boxedImageH = boxH; |
|
|
|
if ((imageW / imageH) > (boxW / boxH)) { |
|
boxedImageH = parseInt(imageH * boxW / imageW); |
|
} |
|
else { |
|
boxedImageW = parseInt(imageW * boxH / imageH); |
|
if (boxedImageW > boxW) { |
|
boxedImageH = parseInt(boxedImageH * boxW / boxedImageW); |
|
boxedImageW = boxW; |
|
} |
|
} |
|
|
|
// if (Config.Navigator.retina) { |
|
// imageW = Math.floor(imageW / 2); |
|
// imageH = Math.floor(imageH / 2); |
|
// } |
|
|
|
if (noZooom && boxedImageW >= imageW && boxedImageH >= imageH) { |
|
boxedImageW = imageW; |
|
boxedImageH = imageH; |
|
} |
|
|
|
return {w: boxedImageW, h: boxedImageH}; |
|
} |
|
|
|
function versionCompare (ver1, ver2) { |
|
if (typeof ver1 !== 'string') { |
|
ver1 = ''; |
|
} |
|
if (typeof ver2 !== 'string') { |
|
ver2 = ''; |
|
} |
|
ver1 = ver1.replace(/^\s+|\s+$/g, '').split('.'); |
|
ver2 = ver2.replace(/^\s+|\s+$/g, '').split('.'); |
|
|
|
var a = Math.max(ver1.length, ver2.length), i; |
|
|
|
for (i = 0; i < a; i++) { |
|
if (ver1[i] == ver2[i]) { |
|
continue; |
|
} |
|
if (ver1[i] > ver2[i]) { |
|
return 1; |
|
} else { |
|
return -1; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
(function (global) { |
|
|
|
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g, |
|
trimRe = /^\s+|\s$/g; |
|
|
|
function createIndex () { |
|
return { |
|
shortIndexes: {}, |
|
fullTexts: {} |
|
} |
|
} |
|
|
|
function cleanSearchText (text) { |
|
var hasTag = text.charAt(0) == '%'; |
|
text = text.replace(badCharsRe, ' ').replace(trimRe, ''); |
|
text = text.replace(/[^A-Za-z0-9]/g, function (ch) { |
|
return Config.LatinizeMap[ch] || ch; |
|
}); |
|
text = text.toLowerCase(); |
|
if (hasTag) { |
|
text = '%' + text; |
|
} |
|
|
|
return text; |
|
} |
|
|
|
function cleanUsername (username) { |
|
return username && username.toLowerCase() || ''; |
|
} |
|
|
|
function indexObject (id, searchText, searchIndex) { |
|
if (searchIndex.fullTexts[id] !== undefined) { |
|
return false; |
|
} |
|
|
|
searchText = cleanSearchText(searchText); |
|
|
|
if (!searchText.length) { |
|
return false; |
|
} |
|
|
|
var shortIndexes = searchIndex.shortIndexes; |
|
|
|
searchIndex.fullTexts[id] = searchText; |
|
|
|
angular.forEach(searchText.split(' '), function(searchWord) { |
|
var len = Math.min(searchWord.length, 3), |
|
wordPart, i; |
|
for (i = 1; i <= len; i++) { |
|
wordPart = searchWord.substr(0, i); |
|
if (shortIndexes[wordPart] === undefined) { |
|
shortIndexes[wordPart] = [id]; |
|
} else { |
|
shortIndexes[wordPart].push(id); |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function search (query, searchIndex) { |
|
var shortIndexes = searchIndex.shortIndexes, |
|
fullTexts = searchIndex.fullTexts; |
|
|
|
query = cleanSearchText(query); |
|
|
|
var queryWords = query.split(' '), |
|
foundObjs = false, |
|
newFoundObjs, i, j, searchText, found; |
|
|
|
for (i = 0; i < queryWords.length; i++) { |
|
newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)]; |
|
if (!newFoundObjs) { |
|
foundObjs = []; |
|
break; |
|
} |
|
if (foundObjs === false || foundObjs.length > newFoundObjs.length) { |
|
foundObjs = newFoundObjs; |
|
} |
|
} |
|
|
|
newFoundObjs = {}; |
|
|
|
for (j = 0; j < foundObjs.length; j++) { |
|
found = true; |
|
searchText = fullTexts[foundObjs[j]]; |
|
for (i = 0; i < queryWords.length; i++) { |
|
if (searchText.indexOf(queryWords[i]) == -1) { |
|
found = false; |
|
break; |
|
} |
|
} |
|
if (found) { |
|
newFoundObjs[foundObjs[j]] = true; |
|
} |
|
} |
|
|
|
return newFoundObjs; |
|
} |
|
|
|
global.SearchIndexManager = { |
|
createIndex: createIndex, |
|
indexObject: indexObject, |
|
cleanSearchText: cleanSearchText, |
|
cleanUsername: cleanUsername, |
|
search: search |
|
}; |
|
|
|
})(window);
|
|
|