PageSourceSearch

https://psychology-relation.press/.s/src/telegram/auth.js

js psychology-relation.press collected 2026-10-04 20:25:16 UTC 84,154 bytes, 2,458 lines download raw bytes

1// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
2// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
3
4// requestAnimationFrame polyfill by Erik MГ¶ller. fixes from Paul Irish and Tino Zijdel
5
6// MIT license
7
8(function() {
9    var lastTime = 0;
10    var vendors = ['ms', 'moz', 'webkit', 'o'];
11    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
12        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
13        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
14                                   || window[vendors[x]+'CancelRequestAnimationFrame'];
15    }
16
17    if (!window.requestAnimationFrame)
18        window.requestAnimationFrame = function(callback, element) {
19            var currTime = new Date().getTime();
20            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
21            var id = window.setTimeout(function() { callback(currTime + timeToCall); },
22              timeToCall);
23            lastTime = currTime + timeToCall;
24            return id;
25        };
26
27    if (!window.cancelAnimationFrame)
28        window.cancelAnimationFrame = function(id) {
29            clearTimeout(id);
30        };
31}());
32
33window.__parseFunction = function(__func, __attrs) {
34      __attrs = __attrs || [];
35      __func = '(function(' + __attrs.join(',') + '){' + __func + '})';
36      return window.execScript ? window.execScript(__func) : eval(__func);
37}
38
39/*! npm.im/iphone-inline-video 2.0.2 */
40
41var enableInlineVideo=function(){"use strict";/*! npm.im/intervalometer */
42function e(e,i,n,r){function t(n){d=i(t,r),e(n-(a||n)),a=n}var d,a;return{start:function(){d||t(0)},stop:function(){n(d),d=null,a=0}}}function i(i){return e(i,requestAnimationFrame,cancelAnimationFrame)}function n(e,i,n,r){function t(i){Boolean(e[n])===Boolean(r)&&i.stopImmediatePropagation(),delete e[n]}return e.addEventListener(i,t,!1),t}function r(e,i,n,r){function t(){return n[i]}function d(e){n[i]=e}r&&d(e[i]),Object.defineProperty(e,i,{get:t,set:d})}function t(e,i,n){n.addEventListener(i,function(){return e.dispatchEvent(new Event(i))})}function d(e,i){Promise.resolve().then(function(){e.dispatchEvent(new Event(i))})}function a(e){var i=new Audio;return t(e,"play",i),t(e,"playing",i),t(e,"pause",i),i.crossOrigin=e.crossOrigin,i.src=e.src||e.currentSrc||"data:",i}function o(e,i,n){(m||0)+200<Date.now()&&(e[b]=!0,m=Date.now()),n||(e.currentTime=i),w[++T%3]=100*i|0}function u(e){return e.driver.currentTime>=e.video.duration}function s(e){var i=this;i.video.readyState>=i.video.HAVE_FUTURE_DATA?(i.hasAudio||(i.driver.currentTime=i.video.currentTime+e*i.video.playbackRate/1e3,i.video.loop&&u(i)&&(i.driver.currentTime=0)),o(i.video,i.driver.currentTime)):i.video.networkState===i.video.NETWORK_IDLE&&0===i.video.buffered.length&&i.video.load(),i.video.ended&&(delete i.video[b],i.video.pause(!0))}function c(){var e=this,i=e[h];return e.webkitDisplayingFullscreen?void e[g]():("data:"!==i.driver.src&&i.driver.src!==e.src&&(o(e,0,!0),i.driver.src=e.src),void(e.paused&&(i.paused=!1,0===e.buffered.length&&e.load(),i.driver.play(),i.updater.start(),i.hasAudio||(d(e,"play"),i.video.readyState>=i.video.HAVE_ENOUGH_DATA&&d(e,"playing")))))}function v(e){var i=this,n=i[h];n.driver.pause(),n.updater.stop(),i.webkitDisplayingFullscreen&&i[E](),n.paused&&!e||(n.paused=!0,n.hasAudio||d(i,"pause"),i.ended&&(i[b]=!0,d(i,"ended")))}function p(e,n){var r=e[h]={};r.paused=!0,r.hasAudio=n,r.video=e,r.updater=i(s.bind(r)),n?r.driver=a(e):(e.addEventListener("canplay",function(){e.paused||d(e,"playing")}),r.driver={src:e.src||e.currentSrc||"data:",muted:!0,paused:!0,pause:function(){r.driver.paused=!0},play:function(){r.driver.paused=!1,u(r)&&o(e,0)},get ended(){return u(r)}}),e.addEventListener("emptied",function(){var i=!r.driver.src||"data:"===r.driver.src;r.driver.src&&r.driver.src!==e.src&&(o(e,0,!0),r.driver.src=e.src,i?r.driver.play():r.updater.stop())},!1),e.addEventListener("webkitbeginfullscreen",function(){e.paused?n&&0===r.driver.buffered.length&&r.driver.load():(e.pause(),e[g]())}),n&&(e.addEventListener("webkitendfullscreen",function(){r.driver.currentTime=e.currentTime}),e.addEventListener("seeking",function(){w.indexOf(100*e.currentTime|0)<0&&(r.driver.currentTime=e.currentTime)}))}function l(e){var i=e[h];e[g]=e.play,e[E]=e.pause,e.play=c,e.pause=v,r(e,"paused",i.driver),r(e,"muted",i.driver,!0),r(e,"playbackRate",i.driver,!0),r(e,"ended",i.driver),r(e,"loop",i.driver,!0),n(e,"seeking"),n(e,"seeked"),n(e,"timeupdate",b,!1),n(e,"ended",b,!1)}function f(e,i){if(void 0===i&&(i={}),!e[h]){if(!i.everywhere){if(!y)return;if(!(i.iPad||i.ipad?/iPhone|iPod|iPad/:/iPhone|iPod/).test(navigator.userAgent))return}!e.paused&&e.webkitDisplayingFullscreen&&e.pause(),p(e,!e.muted),l(e),e.classList.add("IIV"),e.muted&&e.autoplay&&e.play(),/iPhone|iPod|iPad/.test(navigator.platform)||console.warn("iphone-inline-video is not guaranteed to work in emulated environments")}}var m,y="object"==typeof document&&"object-fit"in document.head.style&&!matchMedia("(-webkit-video-playable-inline)").matches,h="bfred-it:iphone-inline-video",b="bfred-it:iphone-inline-video:event",g="bfred-it:iphone-inline-video:nativeplay",E="bfred-it:iphone-inline-video:nativepause",w=[],T=0;return f}();
43
44if (!Array.isArray) {
45  Array.isArray = function(arg) {
46    return Object.prototype.toString.call(arg) === '[object Array]';
47  };
48}
49
50(!this.CustomEvent || typeof this.CustomEvent === "object") && (function() {
51  this.CustomEvent = function CustomEvent(type, eventInitDict) {
52    var event;
53    eventInitDict = eventInitDict || {bubbles: false, cancelable: false, detail: undefined};
54
55    try {
56      event = document.createEvent('CustomEvent');
57      event.initCustomEvent(type, eventInitDict.bubbles, eventInitDict.cancelable, eventInitDict.detail);
58    } catch (error) {
59      event = document.createEvent('Event');
60      event.initEvent(type, eventInitDict.bubbles, eventInitDict.cancelable);
61      event.detail = eventInitDict.detail;
62    }
63
64    return event;
65  };
66})();
67
68var Keys = {
69  BACKSPACE: 8,
70  ESC: 27,
71  TAB: 9,
72  RETURN: 13,
73  LEFT: 37,
74  UP: 38,
75  RIGHT: 39,
76  DOWN: 40
77};
78
79var TWidget = {
80  options: {},
81  isFocused: false
82};
83function inFrame() {
84  return (window.parent != null && window != window.parent);
85}
86function inFullFrame() {
87  return inFrame() && TWidget.options.auto_height;
88}
89function isFocused() {
90  return inFrame() ? TWidget.isFocused : document.hasFocus();
91}
92
93function l(lang_key, params, def_value) {
94  if (typeof params === 'string') {
95    def_value = params;
96    params = {};
97  }
98  params = params || {};
99  var value = l._keys[lang_key] || def_value || lang_key;
100  value = value.replace(/\{\{([A-Za-z_\-\d]{1,32}):(.+?)\}\}/g, function(lang_value, token, options) {
101    var number = +params[token] || 0;
102    var numeric_options = options.split('|');
103    var i;
104    if (number == 1) i = 0;
105    else i = 1;
106    if (typeof numeric_options[i] === 'undefined') {
107      i = 1;
108    }
109    var numeric_option = numeric_options[i] || '#';
110    return numeric_option.replace(/#/g, number);
111  });
112  value = value.replace(/\{([A-Za-z_\-\d]{1,32}):(.{1,256}?)\}/g, function(lang_value, token, options) {
113    var number = +params[token] || 0;
114    var numeric_options = options.split('|');
115    var i;
116    if (!number) i = 0;
117    else if (number == 1) i = 1;
118    else i = 2;
119    if (typeof numeric_options[i] === 'undefined') {
120      i = 0;
121    }
122    var numeric_option = numeric_options[i] || '#';
123    return numeric_option.replace(/#/g, number);
124  });
125  for (var param in params) {
126    value = value.split('{' + param + '}').join(params[param]);
127  }
128  return value;
129}
130l._keys = {};
131l.add = function(lang_values) {
132  for (var lang_key in lang_values) {
133    l._keys[lang_key] = lang_values[lang_key];
134  }
135}
136
137var PostMessage = {
138  _callbacks: {},
139  _lastId: 0,
140  send: function(data, origin, callback) {
141    if (typeof origin === 'function') {
142      callback = origin; origin = null;
143    }
144    try {
145      if (callback) {
146        data._cb = ++PostMessage._lastId;
147        PostMessage._callbacks[data._cb] = callback;
148
149      }
150      	if (data.event == 'auth_user'){
151      		onauth_f = __parseFunction(document.getElementById('tgauth').getAttribute('data-onauth'), ['user']);
152      		onauth_f(data['auth_data']);
153      	}
154      window.parent.postMessage(JSON.stringify(data), origin || '*');
155    } catch(e) {
156      if (origin) alert('Bot domain invalid');
157    }
158  },
159  onMessage: function(event) {
160    if (event.source !== window.parent) return;
161    try {
162      var data = JSON.parse(event.data);
163    } catch(e) {
164      var data = {};
165    }
166    if (data.event == 'visible') {
167      if (!frameWasVisible) {
168        frameWasVisible = true;
169        TWidget.options.onVisible && TWidget.options.onVisible();
170      }
171      PostMessage.send({event: 'visible_off'});
172    }
173    else if (data.event == 'focus') {
174      TWidget.isFocused = data.has_focus;
175      triggerEvent(window, 'tg:focuschange');
176    }
177    else if (data.event == 'set_options') {
178      triggerEvent(window, 'tg:optionschange', {detail: data.options});
179    }
180    else if (data.event == 'get_info') {
181      triggerEvent(window, 'tg:inforequest', {detail: {
182        callback: function(value) {
183          PostMessage.send({event: 'callback', _cb: data._cb, value: value});
184        }
185      }});
186    }
187    else if (data.event == 'visible') {
188      if (!frameWasVisible) {
189        frameWasVisible = true;
190        TWidget.options.onVisible && TWidget.options.onVisible();
191      }
192      PostMessage.send({event: 'visible_off'});
193    }
194    else if (data.event == 'callback') {
195      if (PostMessage._callbacks[data._cb]) {
196        PostMessage._callbacks[data._cb](data.value);
197        delete PostMessage._callbacks[data._cb];
198      } else {
199        console.warn('Callback #' + data._cb + ' not found');
200      }
201    }
202  }
203};
204
205var TPopups = {
206  _list: [],
207  _lastId: 0,
208  _inited: false,
209  init: function() {
210    if (!TPopups._inited) {
211      TPopups._inited = true;
212      addEvent(document, 'keydown', function(e) {
213        if (e.keyCode == Keys.ESC && TPopups._list.length > 0) {
214          e.stopImmediatePropagation();
215          e.preventDefault();
216          TPopups.close();
217        }
218      });
219    }
220  },
221  open: function(popup_el, options) {
222    TPopups.init();
223    popup_el = ge1(popup_el);
224    if (!popup_el) {
225      return popup_el;
226    }
227    options = options || {};
228    var popup_id = popup_el.__puid;
229    if (!popup_id) {
230      popup_id = ++TPopups._lastId;
231      popup_el.__puid = popup_id;
232    }
233    popup_el.__options = options;
234    var index = TPopups._list.indexOf(popup_id);
235    if (index >= 0) {
236      TPopups._list.splice(index, 1);
237    }
238    TPopups._list.push(popup_id);
239    document.body.style.overflow = 'hidden';
240    document.body.appendChild(popup_el);
241    removeClass(popup_el, 'hide');
242    TPopups.setPosition(popup_el);
243    if (document.activeElement) {
244      document.activeElement.blur();
245    }
246    if (ge1('.js-popup_box', popup_el)) {
247      addEvent(popup_el, 'click', function(e) {
248        if (elInBody(e.target) &&
249            !hasClass(e.target, 'js-popup_box') &&
250            !gpeByClass(e.target, 'js-popup_box')) {
251          TPopups.close(popup_el);
252        }
253      });
254    }
255    gec('.js-popup_close', function() {
256      addEvent(this, 'click', function() {
257        TPopups.close(popup_el);
258      });
259    }, popup_el);
260    triggerEvent(popup_el, 'tg:popupopen');
261    return popup_el;
262  },
263  close: function(popup_el) {
264    if (!TPopups._list.length) return false;
265    var popup_id;
266    if (popup_el) {
267      popup_id = popup_el.__puid;
268    } else {
269      popup_id = TPopups._list.pop();
270      gec('.js-popup_container', function() {
271        if (popup_id == this.__puid) {
272          popup_el = this;
273          return false;
274        }
275      });
276    }
277    if (!popup_el) {
278      return false;
279    }
280    var options = popup_el.__options;
281    var index = TPopups._list.indexOf(popup_id);
282    if (index >= 0) {
283      TPopups._list.splice(index, 1);
284    }
285    if (!TPopups._list.length) {
286      document.body.style.overflow = '';
287    }
288    removeEvent(popup_el, 'click');
289    gec('.js-popup_close', function() {
290      removeEvent(this, 'click');
291    }, popup_el);
292    addClass(popup_el, 'hide');
293    triggerEvent(popup_el, 'tg:popupclose');
294  },
295  closeAll: function() {
296    while (TPopups._list.length) {
297      TPopups.close();
298    }
299  },
300  setPosition: function(popul_el) {
301    var popup_box = ge1('.js-popup_box', popul_el);
302    if (!popup_box) return;
303    getCoords(function(coords) {
304      var style      = window.getComputedStyle(popul_el);
305      var contTop    = parseInt(style.paddingTop);
306      var contBottom = parseInt(style.paddingBottom);
307      var marginMax  = popul_el.offsetHeight - contTop - contBottom - coords.elHeight;
308      var frameTop   = coords.frameTop || 0;
309      var deltaY     = (coords.clientHeight - coords.elHeight) / 2;
310      var marginTop  = deltaY - contTop - frameTop;
311      marginTop = Math.max(0, Math.min(marginMax, marginTop));
312      popup_box.style.marginTop = marginTop + 'px';
313    }, popup_box);
314  },
315  show: function(html, buttons, options) {
316    options = options || {};
317    var popup_el = newEl('div', 'tgme_popup_container js-popup_container tgme_popup_alert hide', '<div class="tgme_popup js-popup_box"><div class="tgme_popup_body"><div class="tgme_popup_text js-popup_text"></div><div class="tgme_popup_buttons js-popup_buttons"></div></div></div></div>');
318    var text_el = ge1('.js-popup_text', popup_el);
319    var buttons_el = ge1('.js-popup_buttons', popup_el);
320    setHtml(text_el, html);
321    var enterBtn = null, onEnterPress = null;
322    for (var i = 0; i < buttons.length; i++) {
323      var btn = buttons[i];
324      var button_el = newEl('div', 'tgme_popup_button' + (btn.close ? ' js-popup_close' : ''), btn.label);
325      btn.el = button_el;
326      buttons_el.appendChild(button_el);
327      if (btn.enter) {
328        enterBtn = button_el;
329      }
330      if (btn.onPress) {
331        addEvent(button_el, 'click', btn.onPress);
332      }
333    }
334    if (enterBtn) {
335      onEnterPress = function(e) {
336        if (e.keyCode == Keys.RETURN) {
337          e.stopImmediatePropagation();
338          e.preventDefault();
339          triggerEvent(enterBtn, 'click');
340        }
341      };
342      addEvent(document, 'keydown', onEnterPress);
343    }
344    var onPopupClose = function(e) {
345      if (enterBtn && onEnterPress) {
346        removeEvent(document, 'keydown', onEnterPress);
347      }
348      for (var i = 0; i < buttons.length; i++) {
349        var btn = buttons[i];
350        if (btn.onPress) {
351          removeEvent(btn.el, 'click', btn.onPress);
352        }
353      }
354      removeEvent(popup_el, 'tg:popupclose', onPopupClose);
355      removeEl(popup_el);
356    };
357    addEvent(popup_el, 'tg:popupclose', onPopupClose);
358    return TPopups.open(popup_el);
359  }
360};
361
362function showAlert(html, onClose) {
363  return TPopups.show(html, [{
364    label: l('WEB_CLOSE', 'Close'),
365    enter: true,
366    close: true
367  }]);
368}
369
370function showConfirm(html, onConfirm, confirm_btn, onCancel, cancel_btn) {
371  var popup_el = TPopups.show(html, [{
372    label: cancel_btn || l('WEB_CANCEL', 'Cancel'),
373    onPress: function() {
374      onCancel && onCancel(popup_el);
375    },
376    close: true
377  }, {
378    label: confirm_btn || l('WEB_OK', 'OK'),
379    onPress: function() {
380      onConfirm && onConfirm(popup_el);
381      TPopups.close(popup_el);
382    },
383    enter: true
384  }]);
385  return popup_el;
386}
387
388function addEvent(el, event, handler) {
389  gec(el, function() {
390    var events = event.split(/\s+/);
391    for (var i = 0; i < events.length; i++) {
392      if (this.addEventListener) {
393        this.addEventListener(events[i], handler, false);
394      } else {
395        this.attachEvent('on' + events[i], handler);
396      }
397    }
398  });
399}
400function removeEvent(el, event, handler) {
401  gec(el, function() {
402    var events = event.split(/\s+/);
403    for (var i = 0; i < events.length; i++) {
404      if (this.removeEventListener) {
405        this.removeEventListener(events[i], handler);
406      } else {
407        this.detachEvent('on' + events[i], handler);
408      }
409    }
410  });
411}
412function triggerEvent(el, event_type, init_dict) {
413  gec(el, function() {
414    var event = new CustomEvent(event_type, init_dict);
415    this.dispatchEvent(event);
416  });
417}
418
419function geById(el_or_id) {
420  if (typeof el_or_id == 'string' || el_or_id instanceof String) {
421    return document.getElementById(el_or_id);
422  } else if (el_or_id instanceof HTMLElement) {
423    return el_or_id;
424  }
425  return null;
426}
427
428function gec(el, callback, context) {
429  var list = ge(el, context);
430  for (var i = 0, l = list.length; i < l; i++) {
431    var result = callback.call(list[i], list[i], i, list);
432    if (result === false) {
433      break;
434    }
435  }
436}
437function ge(el, context) {
438  var list = [];
439  if (typeof el === 'string') {
440    list = (ge1(context) || document).querySelectorAll(el);
441  } else if (el instanceof Node || el instanceof Window) {
442    list = [el];
443  } else if (Array.isArray(el)) {
444    list = el;
445  } else if (el) {
446    console.warn('unknown type of el', el);
447    return [el];
448  }
449  if (list instanceof NodeList) {
450    list = Array.prototype.slice.call(list);
451  }
452  return list;
453}
454function ge1(el, context) {
455  if (typeof el === 'string') {
456    return (ge1(context) || document).querySelector(el);
457  } else if (el instanceof Node || el instanceof Window) {
458    return el;
459  } else if (el instanceof NodeList) {
460    return el.item(0);
461  } else if (el instanceof Array) {
462    return el[0];
463  } else if (el) {
464    console.warn('unknown type of el', el);
465    return el;
466  }
467  return null;
468}
469function newEl(tag, cl, html, styles) {
470  var el = document.createElement((tag || 'DIV').toUpperCase());
471  if (cl) el.className = cl;
472  if (styles) {
473    for (var k in styles) {
474      el.style[k] = styles[k];
475    }
476  }
477  if (html) {
478    el.innerHTML = html;
479  }
480  return el;
481}
482function gpeByClass(el, cl) {
483  if (!el) return null;
484  while (el = el.parentNode) {
485    if (hasClass(el, cl)) break;
486  }
487  return el || null;
488}
489function elInBody(el) {
490  if (!el) return false;
491  while (el = el.parentNode) {
492    if (el === document.body) return true;
493  }
494  return false;
495}
496
497function getCoords(callback, el) {
498  var rect = {};
499  if (el = ge1(el)) {
500    rect = el.getBoundingClientRect();
501  }
502  var docEl = document.documentElement;
503  var coords = {};
504  if (inFullFrame()) {
505    PostMessage.send({event: 'get_coords'}, function(coords) {
506      coords.inFrame = true;
507      if (el) {
508        coords.elTop = rect.top + coords.frameTop;
509        coords.elBottom = rect.bottom + coords.frameTop;
510        coords.elLeft = rect.left + coords.frameLeft;
511        coords.elRight = rect.right + coords.frameLeft;
512        coords.elWidth = rect.width;
513        coords.elHeight = rect.height;
514      }
515      callback && callback(coords);
516    });
517    return;
518  } else {
519    if (el) {
520      coords.elTop = rect.top;
521      coords.elBottom = rect.bottom;
522      coords.elLeft = rect.left;
523      coords.elRight = rect.right;
524      coords.elWidth = rect.width;
525      coords.elHeight = rect.height;
526    }
527    coords.scrollTop = window.pageYOffset;
528    coords.scrollLeft = window.pageXOffset;
529    coords.clientWidth = docEl.clientWidth;
530    coords.clientHeight = docEl.clientHeight;
531    callback && callback(coords);
532  }
533}
534function scrollToY(y) {
535  if (inFullFrame()) {
536    PostMessage.send({event: 'scroll_to', y: y});
537  } else {
538    window.scrollTo(0, y);
539  }
540}
541
542function addClass(el, cl) {
543  gec(el, function() {
544    var cls = cl.split(/\s+/);
545    for (var i = 0; i < cls.length; i++) {
546      this.classList.add(cls[i]);
547    }
548  });
549}
550function removeClass(el, cl) {
551  gec(el, function() {
552    var cls = cl.split(/\s+/);
553    for (var i = 0; i < cls.length; i++) {
554      this.classList.remove(cls[i]);
555    }
556  });
557}
558function toggleClass(el, cl, add) {
559  gec(el, function() {
560    var cls = cl.split(/\s+/);
561    for (var i = 0; i < cls.length; i++) {
562      cl = cls[i];
563      var add_cl = (typeof add !== 'undefined') ? add : !hasClass(this, cl);
564      add_cl ? this.classList.add(cl) : this.classList.remove(cl);
565    }
566  });
567}
568function hasClass(el, cl) {
569  var item = ge1(el);
570  return (item && item.classList && item.classList.contains(cl));
571}
572function removeEl(el) {
573  gec(el, function() {
574    if (this && this.parentNode) {
575      this.parentNode.removeChild(this);
576    }
577  });
578}
579function getHtml(el, context) {
580  var item = ge1(el, context);
581  return item ? item.innerHTML : null;
582}
583function setHtml(el, html) {
584  gec(el, function() {
585    if (this) {
586      this.innerHTML = html;
587    }
588  });
589}
590function getAttr(el, attr) {
591  var item = ge1(el);
592  return item ? item.getAttribute(attr) : null;
593}
594function setAttr(el, attr, value) {
595  gec(el, function() {
596    if (this) {
597      this.setAttribute(attr, value);
598    }
599  });
600}
601
602function isLSEnabled() {
603  try {
604    return window.localStorage ? true : false;
605  } catch (e) {
606    return false;
607  }
608}
609function parseHeaders(headers) {
610  var headers_strs = headers.replace(/^\s+|\s+$/g, '').split(/[\r\n]+/);
611  var headers_arr = [];
612  for (var i = 0; i < headers_strs.length; i++) {
613    var header_str = headers_strs[i];
614    var parts = header_str.split(': ');
615    var name = parts.shift().toLowerCase();
616    var value = parts.join(': ');
617    headers_arr.push({name: name, value: value});
618  }
619  return headers_arr;
620}
621function setLS(xhr) {
622  if (!isLSEnabled()) return;
623  try {
624    var headers = parseHeaders(xhr.getAllResponseHeaders());
625    for (var i = 0; i < headers.length; i++) {
626      var header = headers[i];
627      if (header.name == 'x-set-local-storage') {
628        var arr = header.value.split('=');
629        var key = decodeURIComponent(arr[0]);
630        var val = decodeURIComponent(arr[1]);
631        if (val.length) {
632          localStorage.setItem(key, val);
633        } else {
634          localStorage.removeItem(key);
635        }
636      }
637    }
638  } catch (e) {}
639}
640function getLSString() {
641  if (!isLSEnabled()) return false;
642  var arr = [];
643  for (var i = 0; i < localStorage.length; i++) {
644    var key = localStorage.key(i);
645    arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(localStorage[key]));
646  }
647  return arr.join('; ');
648}
649
650function getXHR() {
651  if (navigator.appName == "Microsoft Internet Explorer"){
652    return new ActiveXObject("Microsoft.XMLHTTP");
653  } else {
654    return new XMLHttpRequest();
655  }
656}
657
658function xhrRequest(href, postdata, onCallback, retry_delay) {
659  var xhr = getXHR(), type = 'GET', data = null, ls_header;
660  if (postdata !== false) {
661    type = 'POST';
662    var data_arr = [];
663    for (var field in postdata) {
664      data_arr.push(encodeURIComponent(field) + '=' + encodeURIComponent(postdata[field]));
665    }
666    data = data_arr.join('&');
667  }
668  xhr.open(type, href);
669  if (postdata !== false) {
670    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
671  }
672  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
673  if (ls_header = getLSString()) {
674    xhr.setRequestHeader('X-Local-Storage', ls_header);
675  }
676  xhr.onerror = function() {
677    if (xhr._retried) return;
678    xhr._retried = true;
679    retry_delay = retry_delay || 1000;
680    if (retry_delay > 60000) {
681      onCallback && onCallback('Server unavailable');
682      return;
683    }
684    setTimeout(function() {
685      xhrRequest(href, postdata, onCallback, retry_delay * 2);
686    }, retry_delay);
687  };
688  xhr.onreadystatechange = function() {
689    if (xhr.readyState == 4) {
690      setLS(xhr);
691      if (typeof xhr.responseBody == 'undefined' && xhr.responseText) {
692        try {
693          var result = JSON.parse(xhr.responseText);
694        } catch(e) {
695          var result = {};
696        }
697        if (xhr.status == 401) { // Unauthorized
698          TWidgetAuth.reload();
699          return;
700        }
701        if (result.error && result.flood_wait) {
702          console.log('flood_wait', result.flood_wait);
703          setTimeout(function() {
704            xhrRequest(href, postdata, onCallback);
705          }, result.flood_wait * 1000);
706          return;
707        }
708        onCallback && onCallback(null, result);
709      } else {
710        xhr.onerror();
711      }
712    }
713  };
714  xhr.withCredentials = true;
715  xhr.send(data);
716  return xhr;
717}
718
719function xhrUploadRequest(href, params, onCallback, onProgress) {
720  var xhr = getXHR(), data = new FormData(), ls_header;
721  xhr.open('POST', href, true);
722  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
723  if (ls_header = getLSString()) {
724    xhr.setRequestHeader('X-Local-Storage', ls_header);
725  }
726  xhr.upload.addEventListener('progress', function(event) {
727    if (event.lengthComputable) {
728      onProgress && onProgress(event.loaded, event.total);
729    }
730  });
731  xhr.onerror = function() {
732    onCallback && onCallback('Server unavailable');
733  };
734  xhr.onreadystatechange = function() {
735    if (xhr.readyState == 4) {
736      setLS(xhr);
737      if (typeof xhr.responseBody == 'undefined' && xhr.responseText) {
738        try {
739          var result = JSON.parse(xhr.responseText);
740        } catch(e) {
741          var result = {};
742        }
743        if (result.error && result.flood_wait) {
744          console.log('flood_wait', result.flood_wait);
745          setTimeout(function() {
746            xhrUploadRequest(href, params, onCallback, onProgress);
747          }, result.flood_wait * 1000);
748          return;
749        }
750        onCallback && onCallback(null, result);
751      } else {
752        xhr.onerror();
753      }
754    }
755  };
756  for (var k in params) {
757    var value = params[k];
758    if (value instanceof File) {
759      data.append(k, value, value.name);
760    } else {
761      data.append(k, value);
762    }
763  }
764  xhr.withCredentials = true;
765  xhr.send(data);
766  return xhr;
767}
768
769window.TWidgetAuth = {
770  init: function(options) {
771    options = options || {};
772    TWidgetAuth.options = options;
773  },
774  apiRequest: function(method, params, callback) {
775    var options = TWidgetAuth.options || {};
776    if (!options.api_url) {
777      console.warn('API url not found');
778      return null;
779    }
780    params.method = method;
781    return xhrRequest(options.api_url, params, callback);
782  },
783  uploadRequest: function(params, onCallback, onProgress) {
784    var options = TWidgetAuth.options || {};
785    if (!options.upload_url) {
786      console.warn('Upload url not found');
787      return null;
788    }
789    return xhrUploadRequest(options.upload_url, params, onCallback, onProgress);
790  },
791  logIn: function() {
792    var options = TWidgetAuth.options || {};
793    if (!options.bot_id) {
794      console.warn('Bot id not found');
795      return;
796    }
797    if (TWidgetAuth.isLoggedIn()) {
798      return;
799    }
800    Telegram.Login.auth({bot_id: options.bot_id, lang: 'en'}, function(user) {
801      if (user) {
802        xhrRequest('https://oauth.telegram.org/auth', user, function(err, result) {
803          if (result.ok) {
804            TWidgetAuth.reload(result.host);
805          } else {
806            location.reload();
807          }
808        });
809      }
810    });
811  },
812  reload: function(host, callback) {
813    var xhr = getXHR(), data = null, ls_header;
814    var url = location.href;
815    if (host) {
816      var a = newEl('a');
817      a.href = url; a.hostname = host; url = a.href;
818    }
819    xhr.open('GET', url);
820    xhr.setRequestHeader('X-Requested-With', 'relogin');
821    if (ls_header = getLSString()) {
822      xhr.setRequestHeader('X-Local-Storage', ls_header);
823    }
824    xhr.onreadystatechange = function() {
825      if (xhr.readyState == 4) {
826        setLS(xhr);
827        if (typeof xhr.responseBody == 'undefined' && xhr.responseText) {
828          document.open();
829          document.write(xhr.responseText);
830          document.close();
831          callback && callback();
832        } else {
833          location.reload();
834        }
835      }
836    };
837    xhr.onerror = function() {
838      location.reload();
839    };
840    xhr.withCredentials = true;
841    xhr.send();
842  },
843  isLoggedIn: function() {
844    var options = TWidgetAuth.options || {};
845    return options && !options.unauth;
846  }
847};
848window.apiRequest = TWidgetAuth.apiRequest;
849window.uploadRequest = TWidgetAuth.uploadRequest;
850
851function loadImage(file, callback) {
852  var image = new Image();
853  image.onload = function() {
854    var w = image.naturalWidth;
855    var h = image.naturalHeight;
856    callback && callback(null, {
857      url:    image.src,
858      width:  w,
859      height: h,
860      image:  image
861    });
862  };
863  image.onerror = function() {
864    callback && callback('LOAD_FAILED');
865  };
866  image.src = URL.createObjectURL(file);
867}
868
869function initWidgetFrame(options) {
870  TWidget.options = options || {};
871  if (window.devicePixelRatio >= 2) {
872    addClass(document.body, 'r2x');
873  }
874  if (TWidget.options.auto_height ||
875      TWidget.options.auto_width) {
876    addEvent(window, 'resize', checkFrameSize);
877    checkFrameSize();
878  }
879  addEvent(window, 'message', PostMessage.onMessage);
880  addEvent(window, 'focus blur', function() {
881    triggerEvent(window, 'tg:focuschange');
882  });
883  PostMessage.send({event: 'ready'});
884}
885
886var frameLastHeight = null,
887    frameLastWidth = null,
888    frameWasVisible = false;
889function checkFrameSize() {
890  var height, width, style;
891  if (document.body) {
892    if (window.getComputedStyle) {
893      style = window.getComputedStyle(document.body);
894      height = style.height;
895      if (height.substr(-2) == 'px') {
896        height = height.slice(0, -2);
897      }
898      width = style.width;
899      if (width.substr(-2) == 'px') {
900        width = width.slice(0, -2);
901      }
902    } else {
903      height = document.body.offsetHeight;
904      width = document.body.offsetWidth;
905    }
906    var data = {event: 'resize'}, resized = false;
907    if (TWidget.options.auto_height) {
908      height = Math.ceil(height);
909      if (height != frameLastHeight) {
910        frameLastHeight = height;
911        data.height = height;
912        resized = true;
913      }
914    }
915    if (TWidget.options.auto_width) {
916      width = Math.ceil(width);
917      if (width != frameLastWidth) {
918        frameLastWidth = width;
919        data.width = width;
920        resized = true;
921      }
922    }
923    if (resized) {
924      PostMessage.send(data);
925    }
926  }
927  requestAnimationFrame(checkFrameSize);
928}
929
930
931(function() {
932  var ua = navigator.userAgent.toLowerCase();
933  var browser = {
934    opera: (/opera/i.test(ua) || /opr/i.test(ua)),
935    msie: (/msie/i.test(ua) && !/opera/i.test(ua) || /trident\//i.test(ua)) || /edge/i.test(ua),
936    msie_edge: (/edge/i.test(ua) && !/opera/i.test(ua)),
937    mozilla: /firefox/i.test(ua),
938    chrome: /chrome/i.test(ua) && !/edge/i.test(ua),
939    safari: (!(/chrome/i.test(ua)) && /webkit|safari|khtml/i.test(ua)),
940    iphone: /iphone/i.test(ua),
941    ipod: /ipod/i.test(ua),
942    ipad: /ipad/i.test(ua),
943    android: /android/i.test(ua),
944    mobile: /iphone|ipod|ipad|opera mini|opera mobi|iemobile|android/i.test(ua),
945    safari_mobile: /iphone|ipod|ipad/i.test(ua),
946    opera_mobile: /opera mini|opera mobi/i.test(ua),
947    opera_mini: /opera mini/i.test(ua),
948    mac: /mac/i.test(ua),
949  };
950  var TBaseUrl = window.TBaseUrl || '//telegram.org/';
951
952  function formatDateTime(datetime) {
953    var date = new Date(datetime);
954    var cur_date = new Date();
955    if (cur_date.getFullYear() == date.getFullYear() &&
956        cur_date.getMonth() == date.getMonth() &&
957        cur_date.getDate() == date.getDate()) {
958      return formatTime(datetime);
959    }
960    return formatDate(datetime);
961  }
962
963  function formatDate(datetime) {
964    var date = new Date(datetime);
965    var cur_date = new Date();
966    var j = date.getDate();
967    var M = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][date.getMonth()];
968    var Y = date.getFullYear();
969    if (cur_date.getFullYear() == date.getFullYear()) {
970      return M + ' ' + j;
971    }
972    return M + ' ' + j + ', ' + Y;
973  }
974
975  function formatTime(datetime) {
976    var date = new Date(datetime);
977    var H = date.getHours();
978    if (H < 10) H = '0' + H;
979    var i = date.getMinutes();
980    if (i < 10) i = '0' + i;
981    return H + ':' + i;
982  }
983
984  function formatDuration(duration) {
985    duration = Math.floor(duration);
986    duration = Math.max(0, duration);
987    var duration_str = '';
988    if (duration >= 3600) {
989      var hours = Math.floor(duration / 3600);
990      duration_str += hours + ':';
991      var minutes = Math.floor((duration % 3600) / 60);
992      if (minutes < 10) minutes = '0' + minutes;
993    } else {
994      var minutes = Math.floor(duration / 60);
995    }
996    duration_str += minutes + ':';
997    var seconds = duration % 60;
998    if (seconds < 10) seconds = '0' + seconds;
999    duration_str += seconds;
1000    return duration_str;
1001  }
1002
1003  function doesSupportEmoji() {
1004    var context, smile, canvas = document.createElement('canvas');
1005    if (!canvas.getContext) return false;
1006    context = canvas.getContext('2d');
1007    if (typeof context.fillText != 'function') return false;
1008    smile = '#' + String.fromCharCode(65039) + String.fromCharCode(8419);
1009    context.textBaseline = 'top';
1010    context.font = '32px Arial';
1011    context.fillText(smile, 0, 0);
1012    if (context.getImageData(16, 16, 1, 1).data[0] === 0) return false;
1013    var div = document.createElement('div');
1014    div.style.position = 'absolute';
1015    div.style.overflow = 'hidden';
1016    div.style.top = '-1000px';
1017    var span = document.createElement('span');
1018    div.style.fontSize = '16px';
1019    span.innerHTML = smile;
1020    div.appendChild(span);
1021    document.body.insertBefore(div, document.body.firstChild);
1022    var width = span.offsetWidth;
1023    document.body.removeChild(div);
1024    if (width < 18) return false;
1025    return true;
1026  }
1027
1028  function cloneArr(arrLike) {
1029    return Array.prototype.slice.apply(arrLike);
1030  }
1031
1032  var loadedLibs = {};
1033  function loadLib(file, callback) {
1034    if (!loadedLibs[file]) {
1035      loadedLibs[file] = {
1036        loaded: null,
1037        callbacks: [callback]
1038      };
1039      var script = document.createElement('script');
1040      script.type = 'text/javascript';
1041      script.async = true;
1042      script.src = file;
1043      script.onerror = function() {
1044        loadedLibs[file].loaded = false;
1045        applyCallbacks(loadedLibs[file].callbacks, loadedLibs[file].loaded);
1046      }
1047      script.onload = function() {
1048        loadedLibs[file].loaded = true;
1049        applyCallbacks(loadedLibs[file].callbacks, loadedLibs[file].loaded);
1050      }
1051      var head = document.getElementsByTagName('head')[0];
1052      head.appendChild(script);
1053      return script;
1054    } else if (loadedLibs[file].loaded === null) {
1055      loadedLibs[file].callbacks.push(callback);
1056    } else {
1057      callback(loadedLibs[file].loaded);
1058    }
1059  };
1060
1061  var webpNativeSupport = null, webpFallbackSupport = null, webpImage = null, webpCallbacks = [];
1062  function applyCallbacks(callbacks) {
1063    var args = cloneArr(arguments); args.shift();
1064    for (var i = 0; i < callbacks.length; i++) {
1065      callbacks[i].apply(null, args);
1066    }
1067  }
1068  function doesSupportWebp(callback) {
1069    if (webpFallbackSupport !== null) {
1070      callback(webpNativeSupport, webpFallbackSupport);
1071    } else {
1072      webpCallbacks.push(callback);
1073      if (!webpImage) {
1074        webpImage = new Image();
1075        webpImage.onerror = webpImage.onload = function() {
1076          if (this.width === 2 && this.height === 1) {
1077            webpNativeSupport = true;
1078            webpFallbackSupport = false;
1079            applyCallbacks(webpCallbacks, webpNativeSupport, webpFallbackSupport);
1080          } else {
1081            webpNativeSupport = false;
1082            var script = document.createElement('script');
1083            script.type = 'text/javascript';
1084            script.async = true;
1085            script.src = TBaseUrl + 'js/libwebp-0.2.0.js';
1086            script.onerror = function() {
1087              webpFallbackSupport = false;
1088              applyCallbacks(webpCallbacks, webpNativeSupport, webpFallbackSupport);
1089            }
1090            script.onload = function() {
1091              webpFallbackSupport = true;
1092              applyCallbacks(webpCallbacks, webpNativeSupport, webpFallbackSupport);
1093            }
1094            var head = document.getElementsByTagName('head')[0];
1095            head.appendChild(script);
1096          }
1097        }
1098        webpImage.src = 'data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==';
1099      }
1100    }
1101  }
1102
1103  function getPngDataUrlFromWebp(data) {
1104    var decoder = new WebPDecoder();
1105    var config = decoder.WebPDecoderConfig;
1106    var buffer = config.j || config.output;
1107    var bitstream = config.input;
1108    if (!decoder.WebPInitDecoderConfig(config)) {
1109      throw new Error('[webpjs] Library version mismatch!');
1110    }
1111    var StatusCode = decoder.VP8StatusCode;
1112    status = decoder.WebPGetFeatures(data, data.length, bitstream);
1113    if (status != (StatusCode.VP8_STATUS_OK || 0)) {
1114      throw new Error('[webpjs] status error');
1115    }
1116    var mode = decoder.WEBP_CSP_MODE;
1117    buffer.colorspace = mode.MODE_RGBA;
1118    buffer.J = 4;
1119    try {
1120      status = decoder.WebPDecode(data, data.length, config);
1121    } catch (e) {
1122      status = e
1123    }
1124    var ok = (status == 0);
1125    if (!ok) {
1126      throw new Error('[webpjs] decoding failed');
1127    }
1128    var bitmap = buffer.c.RGBA.ma;
1129    if (!bitmap) {
1130      throw new Error('[webpjs] bitmap error');
1131    }
1132    var biHeight = buffer.height;
1133    var biWidth = buffer.width;
1134    var canvas = document.createElement('canvas');
1135    var context = canvas.getContext('2d');
1136    canvas.height = biHeight;
1137    canvas.width = biWidth;
1138    var output = context.createImageData(canvas.width, canvas.height);
1139    var outputData = output.data;
1140    for (var h = 0; h < biHeight; h++) {
1141      for (var w = 0; w < biWidth; w++) {
1142        outputData[0 + w * 4 + (biWidth * 4) * h] = bitmap[1 + w * 4 + (biWidth * 4) * h];
1143        outputData[1 + w * 4 + (biWidth * 4) * h] = bitmap[2 + w * 4 + (biWidth * 4) * h];
1144        outputData[2 + w * 4 + (biWidth * 4) * h] = bitmap[3 + w * 4 + (biWidth * 4) * h];
1145        outputData[3 + w * 4 + (biWidth * 4) * h] = bitmap[0 + w * 4 + (biWidth * 4) * h];
1146      }
1147    }
1148    context.putImageData(output, 0, 0);
1149    return canvas.toDataURL('image/png');
1150  }
1151
1152  function proccessWebpImage(imgEl, failed_callback, success_callback) {
1153    var imgEl = geById(imgEl);
1154    if (!imgEl || imgEl.__inited) return;
1155    imgEl.__inited = true;
1156    failed_callback = failed_callback || function(){};
1157    success_callback = success_callback || function(){};
1158    doesSupportWebp(function(nativeSupport, fallbackSupport) {
1159      var isImage, src;
1160      var webpSrc = imgEl.getAttribute('data-webp');
1161      if (imgEl.tagName && imgEl.tagName.toUpperCase() == 'IMG' && imgEl.src) {
1162        isImage = true;
1163        src = imgEl.src;
1164      } else {
1165        isImage = false;
1166        var bgImage;
1167        if (window.getComputedStyle) {
1168          bgImage = window.getComputedStyle(imgEl).backgroundImage;
1169        } else {
1170          bgImage = imgEl.style && imgEl.style.backgroundImage;
1171        }
1172        src = bgImage.slice(4, -1).replace(/["|']/g, '');
1173      }
1174      var setImgSrc = function(src) {
1175        if (isImage) {
1176          imgEl.src = src;
1177        } else {
1178          imgEl.style.backgroundImage = "url('" + src + "')";
1179        }
1180        addClass(imgEl, 'webp_sticker_done');
1181      };
1182      if (nativeSupport) {
1183        if (webpSrc) {
1184          var img = new Image();
1185          img.onload = function() {
1186            setImgSrc(webpSrc);
1187            success_callback();
1188          }
1189          img.onerror = function() {
1190            failed_callback();
1191          }
1192          img.src = webpSrc;
1193        } else {
1194          success_callback();
1195        }
1196        return;
1197      } else if (!fallbackSupport) {
1198        failed_callback();
1199        return;
1200      }
1201      if (hasClass(imgEl, 'webp_sticker_done')) {
1202        success_callback();
1203        return;
1204      }
1205      if (!src) {
1206        failed_callback();
1207        return;
1208      }
1209      if (webpSrc) {
1210        src = webpSrc;
1211      }
1212      var xhr = getXHR();
1213      xhr.open('get', src);
1214      if (xhr.overrideMimeType) {
1215        xhr.overrideMimeType('text/plain;
1215 charset=x-user-defined');
1216      } else {
1217        xhr.setRequestHeader('Accept-Charset', 'x-user-defined');
1218      }
1219      xhr.onreadystatechange = function() {
1220        if (xhr.readyState == 4) {
1221          if (typeof xhr.responseBody == 'undefined' && xhr.responseText) {
1222            var rlen = xhr.responseText.length, uarr = new Uint8Array(rlen);
1223            for (var i = 0; i < rlen; i++) {
1224              uarr[i] = xhr.responseText.charCodeAt(i);
1225            }
1226            try {
1227              var src = getPngDataUrlFromWebp(uarr);
1228              if (isImage) {
1229                imgEl.src = src;
1230              } else {
1231                imgEl.style.backgroundImage = "url('" + src + "')";
1232              }
1233              addClass(imgEl, 'webp_sticker_done');
1234              success_callback();
1235            } catch(e) {
1236              failed_callback();
1237            }
1238          } else {
1239            failed_callback();
1240          }
1241        }
1242      };
1243      xhr.send(null);
1244    });
1245  }
1246
1247  function checkVideo(el, error_callback) {
1248    var timeout, eventAdded;
1249    if (!eventAdded) {
1250      function destroyCheck() {
1251        clearTimeout(timeout);
1252        removeEvent(el, 'canplay', onCanPlay);
1253        removeEvent(el, 'error', onError);
1254      }
1255      function onCanPlay() {
1256        destroyCheck();
1257      }
1258      function onError() {
1259        destroyCheck();
1260        error_callback();
1261      }
1262      eventAdded = true;
1263      addEvent(el, 'canplay', onCanPlay);
1264      addEvent(el, 'error', onError);
1265    }
1266    if (el.readyState >= 2) {
1267      destroyCheck();
1268    } else {
1269      timeout = setTimeout(function() {
1270        checkVideo(el, error_callback);
1271      }, 50);
1272    }
1273  }
1274
1275  window.TPost = {
1276    init: function(postEl, options) {
1277      postEl = geById(postEl);
1278      options = options || {};
1279      if (!postEl || postEl.__inited) return;
1280      postEl.__inited = true;
1281      gec('time[datetime]', function() {
1282        var datetime = this.getAttribute('datetime');
1283        if (datetime) {
1284          if (hasClass(this, 'datetime')) {
1285            this.innerHTML = formatDate(datetime) + ' at ' + formatTime(datetime);
1286          } else if (hasClass(this, 'time')) {
1287            this.innerHTML = formatTime(datetime);
1288          } else {
1289            this.innerHTML = formatDateTime(datetime);
1290          }
1291        }
1292      }, postEl);
1293      gec('.js-message_footer.compact', function() {
1294        var timeEl = ge1('time[datetime]', this)
1295          , textEl = this.previousElementSibling;
1296        if (textEl && !textEl.__inited && hasClass(textEl, 'js-message_text')) {
1297          var text_rect = textEl.getBoundingClientRect();
1298          var tnode = textEl.firstChild;
1299          while (tnode && tnode.nodeType == tnode.ELEMENT_NODE) {
1300            tnode = tnode.firstChild;
1301          }
1302          if (tnode) {
1303            var r = document.createRange();
1304            r.setStart(tnode, 0);
1305            r.setEnd(tnode, 1);
1306            var char_rect = r.getBoundingClientRect();
1307            textEl.__inited = true;
1308            if (Math.abs(char_rect.right - text_rect.right) > 3) {
1309              var infoEl = ge1('.js-message_info', this);
1310              if (infoEl) {
1311                var shadowEl = document.createElement('span');
1312                shadowEl.style.display = 'inline-block';
1313                shadowEl.style.width = infoEl.offsetWidth + 'px';
1314                textEl.appendChild(shadowEl);
1315                addClass(textEl, 'before_footer');
1316              }
1317            }
1318          }
1319        }
1320      }, postEl);
1321      gec('.js-message_video_player', function() {
1322        TVideo.init(this);
1323      }, postEl);
1324      gec('.js-message_photo', function() {},);
1325        //TPhoto.init(this);
1326      //}, postEl);
1327      gec('.js-message_grouped_wrap', function() {
1328        TGrouped.init(this);
1329      }, postEl);
1330      gec('.js-message_roundvideo_player', function() {
1331        TRoundVideo.init(this);
1332      }, postEl);
1333      gec('.js-message_voice_player', function() {
1334        TVoice.init(this);
1335      }, postEl);
1336      gec('.js-sticker_image', function() {
1337        TSticker.init(this, function() {
1338          addClass(postEl, 'media_not_supported');
1339          removeClass(postEl, 'no_bubble');
1340        });
1341      }, postEl);
1342      gec('.js-sticker_thumb', function() {
1343        TSticker.init(this);
1344      }, postEl);
1345      gec('.js-tgsticker_image', function() {
1346        if (options.tgs_workers_limit) {
1347          RLottie.WORKERS_LIMIT = options.tgs_workers_limit;
1348        } else if (options.frame) {
1349          RLottie.WORKERS_LIMIT = 1;
1350        }
1351        RLottie.init(this, {
1352          playOnce: this.hasAttribute('data-play-once')
1353        });
1354      }, postEl);
1355    },
1356    view: function(postEl) {
1357      postEl = geById(postEl);
1358      if (!postEl) return;
1359      var view = postEl.getAttribute('data-view');
1360      if (view) {
1361        var xhr = getXHR();
1362        xhr.open('get', '/v/?views=' + encodeURIComponent(view));
1363        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
1364        xhr.send(null);
1365      }
1366    }
1367  };
1368
1369  var TPhoto = window.TPhoto = {
1370    init: function(photoEl) {
1371      photoEl = geById(photoEl);
1372      if (!photoEl || photoEl.__inited) return;
1373      photoEl.__inited = true;
1374      var inGroup = hasClass(photoEl, 'grouped_media_wrap')
1375        , opened
1376        , overTo;
1377      if (inGroup) {
1378        addEvent(photoEl, 'click', function togglePhoto(e) {
1379          if (e.metaKey || e.ctrlKey) return true;
1380          e.stopPropagation();
1381          if (!photoEl) return true;
1382          if (opened) {
1383            opened = false;
1384            removeClass(photoEl, 'active');
1385            overTo = setTimeout(function() {
1386              removeClass(photoEl, 'over');
1387            }, 200);
1388          } else {
1389            opened = true;
1390            clearTimeout(overTo);
1391            addClass(photoEl, 'over active');
1392          }
1393          e.preventDefault();
1394          return false;
1395        });
1396      }
1397    }
1398  };
1399
1400  var TVideo = window.TVideo = {
1401    init: function(playerEl) {
1402      playerEl = geById(playerEl);
1403      if (!playerEl || playerEl.__inited) return;
1404      playerEl.__inited = true;
1405      var videoEl = ge1('.js-message_video', playerEl)
1406        , videoBluredEl = ge1('.js-message_video_blured', playerEl)
1407        , playEl = ge1('.js-message_video_play', playerEl)
1408        , durationEl = ge1('.js-message_video_duration', playerEl)
1409        , inGroup = hasClass(playerEl, 'grouped_media_wrap')
1410        , looped
1411        , overTo;
1412      if (!videoEl) return;
1413
1414      looped = videoEl.hasAttribute('loop');
1415      if (inGroup) {
1416        addEvent(playerEl, 'click', function videoToggleInGroup(e) {
1417          if (e.metaKey || e.ctrlKey) return true;
1418          if (hasClass(e.target, 'message_media_view_in_telegram')) return true;
1419          e.stopPropagation();
1420          if (!playerEl) return true;
1421          if (hasClass(playerEl, 'active')) {
1422            removeClass(playerEl, 'active');
1423            overTo = setTimeout(function() {
1424              removeClass(playerEl, 'over');
1425            }, 200);
1426          } else {
1427            clearTimeout(overTo);
1428            addClass(playerEl, 'over active');
1429          }
1430          e.preventDefault();
1431          return false;
1432        });
1433      }
1434      if (videoEl.hasAttribute('playsinline')) {
1435        enableInlineVideo(videoEl);
1436        if (videoBluredEl) {
1437          enableInlineVideo(videoBluredEl);
1438        }
1439      }
1440      function fixControls() {
1441        if (videoEl.controls) videoEl.controls = false;
1442      }
1443      var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
1444      if (MutationObserver) {
1445        (new MutationObserver(fixControls)).observe(videoEl, {attributes: true});
1446      }
1447
1448      checkVideo(videoEl, function() {
1449        addClass(playerEl, 'not_supported');
1450      });
1451      addEvent(videoEl, 'play', function() {
1452        fixControls();
1453        addClass(playerEl, 'playing');
1454        if (inGroup) {
1455          clearTimeout(overTo);
1456          addClass(playerEl, 'over active');
1457        }
1458        if (videoBluredEl) {
1459          videoBluredEl.currentTime = videoEl.currentTime;
1460          if (!browser.mobile) {
1461            videoBluredEl.play();
1462          }
1463        }
1464        if (!looped) {
1465          triggerEvent(videoEl, 'tg:play', {bubbles: true});
1466        }
1467      });
1468      addEvent(document, 'tg:play', function(e) {
1469        if (e.target === videoEl || looped) return true;
1470        if (!videoEl.paused) {
1471          videoEl.pause();
1472          if (videoBluredEl) {
1473            videoBluredEl.pause();
1474          }
1475        }
1476      });
1477      addEvent(videoEl, 'pause', function() {
1478        fixControls();
1479        removeClass(playerEl, 'playing');
1480        if (inGroup) {
1481          removeClass(playerEl, 'active');
1482          overTo = setTimeout(function() {
1483            removeClass(playerEl, 'over');
1484          }, 200);
1485        }
1486        if (videoBluredEl) {
1487          videoBluredEl.currentTime = videoEl.currentTime;
1488          videoBluredEl.pause();
1489        }
1490        if (looped) {
1491          videoEl.play();
1492        }
1493      });
1494      addEvent(videoEl, 'timeupdate', function(e) {
1495        fixControls();
1496        if (videoBluredEl && videoBluredEl.currentTime != videoEl.currentTime) {
1497          videoBluredEl.currentTime = videoEl.currentTime;
1498        }
1499        if (durationEl && videoEl.duration) {
1500          var duration = Math.floor(videoEl.duration);
1501          durationEl.innerHTML = formatDuration(duration - videoEl.currentTime);
1502        }
1503      });
1504      addEvent(videoEl, 'ended load', function(e) {
1505        fixControls();
1506        if (durationEl && videoEl.duration) {
1507          var duration = Math.floor(videoEl.duration);
1508          durationEl.innerHTML = formatDuration(duration);
1509        }
1510      });
1511      if (looped) {
1512        addEvent(document, 'touchstart', function(){ videoEl.play(); });
1513      } else {
1514        addClass(playerEl, 'ready');
1515      }
1516      if (playEl) {
1517        addEvent(playEl, 'click', function toggleVideo(e) {
1518          if (e.metaKey || e.ctrlKey) return true;
1519          e.stopPropagation();
1520          if (!videoEl) return true;
1521          if (videoEl.paused) {
1522            videoEl.play();
1523            if (videoBluredEl) {
1524              videoBluredEl.play();
1525            }
1526          } else {
1527            videoEl.pause();
1528            if (videoBluredEl) {
1529              videoBluredEl.pause();
1530            }
1531          }
1532          e.preventDefault();
1533          return false;
1534        });
1535      }
1536    }
1537  };
1538
1539  var TGrouped = window.TGrouped = {
1540    init: function(groupedWrapEl) {
1541      groupedWrapEl = geById(groupedWrapEl);
1542      if (!groupedWrapEl || groupedWrapEl.__inited) return;
1543      groupedWrapEl.__inited = true;
1544      var groupedEl = ge1('.js-message_grouped', groupedWrapEl)
1545        , groupedLayerEl = ge1('.js-message_grouped_layer', groupedEl)
1546        , thumbsEl = groupedLayerEl.children
1547        , margin_w = +groupedWrapEl.getAttribute('data-margin-w') || 2
1548        , margin_h = +groupedWrapEl.getAttribute('data-margin-h') || 2;
1549      if (!thumbsEl.length) {
1550        return false;
1551      }
1552
1553      addEvent(window, 'resize', function() {
1554        if (groupedLayerEl.offsetWidth != groupedEl.offsetWidth) {
1555          recalcGrouped(groupedWrapEl.offsetWidth);
1556        }
1557      });
1558      recalcGrouped(groupedWrapEl.offsetWidth);
1559
1560      function updateThumb(thumbEl, x, y, width, height, th_width, th_height, position) {
1561        if (!thumbEl) return;
1562        var t = false, r = false, b = false, l = false;
1563        for (var i = 0; i < position.length; i++) {
1564          if      (position[i] == 't') t = true;
1565          else if (position[i] == 'r') r = true;
1566          else if (position[i] == 'b') b = true;
1567          else if (position[i] == 'l') l = true;
1568        }
1569        thumbEl.style.left = x + 'px';
1570        thumbEl.style.top = y + 'px';
1571        thumbEl.style.width = width + 'px';
1572        thumbEl.style.height = height + 'px';
1573        thumbEl.style.marginRight = (!r ? margin_w : 0) + 'px';
1574        thumbEl.style.marginBottom = (!b ? margin_h : 0) + 'px';
1575
1576        var th_ratio = th_width / th_height;
1577        var ratio    = +thumbEl.getAttribute('data-ratio') || 1.0;
1578        var mediaEl  = ge1('.grouped_media', thumbEl);
1579        var helperEl = ge1('.grouped_media_helper', thumbEl);
1580
1581        if (mediaEl) {
1582          var media_height = Math.ceil(width / ratio);
1583          var media_tb = height - media_height;
1584          if (media_tb < 0) {
1585            var media_t = Math.floor(media_tb / 2);
1586            var media_b = media_tb - media_t;
1587            mediaEl.style.left = 0;
1588            mediaEl.style.right = 0;
1589            mediaEl.style.top = media_t + 'px';
1590            mediaEl.style.bottom = media_b + 'px';
1591          } else {
1592            var media_width = Math.ceil(height * ratio);
1593            var media_lr = width - media_width;
1594            var media_l = Math.floor(media_lr / 2);
1595            var media_r = media_lr - media_l;
1596            mediaEl.style.top = 0;
1597            mediaEl.style.bottom = 0;
1598            mediaEl.style.left = media_l + 'px';
1599            mediaEl.style.right = media_r + 'px';
1600          }
1601        }
1602        if (helperEl) {
1603          var helper_height = Math.floor(th_width / ratio);
1604          var helper_tb = th_height - helper_height;
1605          if (helper_tb > 0) {
1606            var helper_t = Math.floor(helper_tb / 2);
1607            var helper_b = helper_tb - helper_t;
1608            helperEl.style.left = 0;
1609            helperEl.style.right = 0;
1610            helperEl.style.top = helper_t + 'px';
1611            helperEl.style.bottom = helper_b + 'px';
1612          } else {
1613            var helper_width = Math.ceil(th_height * ratio);
1614            var helper_lr = th_width - helper_width;
1615            var helper_l = Math.floor(helper_lr / 2);
1616            var helper_r = helper_lr - helper_l;
1617            helperEl.style.top = 0;
1618            helperEl.style.bottom = 0;
1619            helperEl.style.left = helper_l + 'px';
1620            helperEl.style.right = helper_r + 'px';
1621          }
1622        }
1623      }
1624
1625      function recalcGrouped(max_w) {
1626        var orients = '';
1627        var ratios = [];
1628        var cnt = thumbsEl.length;
1629
1630        var ratios_sum = 0;
1631        for (var i = 0; i < thumbsEl.length; i++) {
1632          var thumbEl = thumbsEl[i];
1633          var ratio = +thumbEl.getAttribute('data-ratio') || 1.0;
1634          orients += ratio > 1.2 ? 'w' : (ratio < 0.8 ? 'n' : 'q');
1635          ratios_sum += ratio;
1636          ratios.push(ratio);
1637        }
1638
1639        var avg_ratio = ratios.length ? ratios_sum / ratios.length : 1.0;
1640        var max_ratio = 0.75;
1641        var min_w     = 75;
1642        var max_h     = max_w / max_ratio;
1643
1644        var w, h, w0, w1, w2, h0, h1, h2, x, y, x1, x2, y1, y2,
1645            th_width, th_height;
1646
1647        if (cnt == 2) {
1648          if (orients == 'ww' &&
1649              avg_ratio > 1.4 * max_ratio &&
1650              (ratios[1] - ratios[0]) < 0.2) {
1651            // 2 wide pics are one below the other
1652            w = max_w;
1653            h = Math.min(w / ratios[0], Math.min(w / ratios[1], (max_h - margin_h) / 2.0));
1654            th_width  = max_w;
1655            th_height = 2 * h + margin_h;
1656            h0 = Math.floor(h);
1657            h1 = th_height - h0 - margin_h;
1658            y = h0 + margin_h;
1659            updateThumb(thumbsEl[0], 0, 0, w, h0, th_width, th_height, 'trl');
1660            updateThumb(thumbsEl[1], 0, y, w, h1, th_width, th_height, 'rbl');
1661          }
1662          else if (orients == 'ww' || orients == 'qq') {
1663            // 2 equal width pics
1664            w = (max_w - margin_w) / 2;
1665            h = Math.floor(Math.min(max_h, Math.min(w / ratios[0], w / ratios[1])));
1666            th_width  = max_w;
1667            th_height = h;
1668            w0 = Math.floor(w);
1669            w1 = max_w - w0 - margin_w;
1670            x = w0 + margin_w;
1671            updateThumb(thumbsEl[0], 0, 0, w0, h, th_width, th_height, 'tbl');
1672            updateThumb(thumbsEl[1], x, 0, w1, h, th_width, th_height, 'trb');
1673          }
1674          else {
1675            // so, we have one wide and one not wide (square or narrow)
1676            w0 = Math.floor((max_w - margin_w) / ratios[1] / (1 / ratios[0] + 1 / ratios[1]));
1677            w1 = max_w - w0 - margin_w;
1678            h = Math.floor(Math.min(max_h, Math.min(w0 / ratios[0], w1 / ratios[1])));
1679            th_width  = max_w;
1680            th_height = h;
1681            x = w0 + margin_w;
1682            updateThumb(thumbsEl[0], 0, 0, w0, h, th_width, th_height, 'tbl');
1683            updateThumb(thumbsEl[1], x, 0, w1, h, th_width, th_height, 'trb');
1684          }
1685        }
1686        else if (cnt == 3) {
1687          if (orients[0] == 'n') {
1688            // 2nd and 3rd photos are on the right part
1689            h0 = max_h;
1690            h2 = Math.floor(Math.min((max_h - margin_h) * 0.5, ratios[1] * (max_h - margin_h) / (ratios[2] + ratios[1])));
1691            h1 = max_h - h2 - margin_h;
1692            w1 = Math.floor(Math.max(min_w, Math.min((max_w - margin_w) * 0.5, Math.min(h2 * ratios[2], h1 * ratios[1]))));
1693            w0 = Math.min(Math.floor(h0 * ratios[0]), (max_w - w1 - margin_w));
1694            th_width  = w0 + w1 + margin_w;
1695            th_height = max_h;
1696            x = w0 + margin_w;
1697            y = h1 + margin_h;
1698            updateThumb(thumbsEl[0], 0, 0, w0, h0, th_width, th_height, 'tbl');
1699            updateThumb(thumbsEl[1], x, 0, w1, h1, th_width, th_height, 'tr');
1700            updateThumb(thumbsEl[2], x, y, w1, h2, th_width, th_height, 'rb');
1701          }
1702          else {
1703            // 2nd and 3rd photos are on the next line
1704            w0 = max_w;
1705            h0 = Math.floor(Math.min(w0 / ratios[0], (max_h - margin_h) * 0.66));
1706            w = (max_w - margin_w) / 2;
1707            h = Math.floor(Math.min(max_h - h0 - margin_h, Math.min(w / ratios[1], w / ratios[2])));
1708            th_width  = max_w;
1709            th_height = h0 + h + margin_h;
1710            w1 = Math.floor(w);
1711            w2 = max_w - w1 - margin_w;
1712            x = w1 + margin_w;
1713            y = h0 + margin_h;
1714            updateThumb(thumbsEl[0], 0, 0, w0, h0, th_width, th_height, 'tlr');
1715            updateThumb(thumbsEl[1], 0, y, w1, h, th_width, th_height, 'bl');
1716            updateThumb(thumbsEl[2], x, y, w2, h, th_width, th_height, 'rb');
1717          }
1718        }
1719        else if (cnt == 4) {
1720          if (orients == 'wwww' || orients[0] == 'w') {
1721            // 2nd, 3rd and 4th photos are on the next line
1722            w = max_w;
1723            h0 = Math.floor(Math.min(w / ratios[0], (max_h - margin_h) * 0.66));
1724            h = (max_w - 2 * margin_w) / (ratios[1] + ratios[2] + ratios[3]);
1725            w0 = Math.floor(Math.max(min_w, Math.min((max_w - 2 * margin_w) * 0.4, h * ratios[1])));
1726            w2 = Math.floor(Math.max(min_w, Math.min((max_w - 2 * margin_w) * 0.33, h * ratios[3])));
1727            w1 = w - w0 - w2 - 2 * margin_w;
1728            h = Math.floor(Math.min(max_h - h0 - margin_h, h));
1729            th_width  = max_w;
1730            th_height = h0 + h + margin_h;
1731            y = h0 + margin_h;
1732            x1 = w0 + margin_w;
1733            x2 = x1 + w1 + margin_w;
1734            updateThumb(thumbsEl[0], 0, 0, w, h0, th_width, th_height, 'tlr');
1735            updateThumb(thumbsEl[1], 0, y, w0, h, th_width, th_height, 'bl');
1736            updateThumb(thumbsEl[2], x1, y, w1, h, th_width, th_height, 'b');
1737            updateThumb(thumbsEl[3], x2, y, w2, h, th_width, th_height, 'rb');
1738          }
1739          else {
1740            // 2nd, 3rd and 4th photos are on the right part
1741            h = max_h;
1742            w0 = Math.floor(Math.min(h * ratios[0], (max_w - margin_w) * 0.66));
1743            w = Math.floor((max_h - 2 * margin_h) / (1 / ratios[1] + 1 / ratios[2] + 1 / ratios[3]));
1744            h0 = Math.floor(w / ratios[1]);
1745            h2 = Math.floor(w / ratios[3]);
1746            h1 = h - h0 - h2 - 2 * margin_h;
1747            w = Math.max(min_w, Math.min(max_w - w0 - margin_w, w));
1748            th_width  = w0 + w + margin_w;
1749            th_height = max_h;
1750            x = w0 + margin_w;
1751            y1 = h0 + margin_h;
1752            y2 = y1 + h1 + margin_h;
1753            updateThumb(thumbsEl[0], 0, 0, w0, h, th_width, th_height, 'tbl');
1754            updateThumb(thumbsEl[1], x, 0, w, h0, th_width, th_height, 'tr');
1755            updateThumb(thumbsEl[2], x, y1, w, h1, th_width, th_height, 'r');
1756            updateThumb(thumbsEl[3], x, y2, w, h2, th_width, th_height, 'rb');
1757          }
1758        }
1759        else {
1760          var ratios_cropped = [];
1761          for (var i = 0; i < ratios.length; i++) {
1762            var ratio = ratios[i];
1763            if (avg_ratio > 1.1) {
1764              ratio_cropped = Math.max(1.0, ratio);
1765            } else {
1766              ratio_cropped = Math.min(1.0, ratio);
1767            }
1768            ratio_cropped = Math.max(0.66667, Math.min(1.7, ratio_cropped));
1769            ratios_cropped.push(ratio_cropped);
1770          }
1771
1772          var multiHeight = function(ratios) {
1773            var ratios_sum = 0;
1774            for (var i = 0; i < ratios.length; i++) {
1775              var ratio = ratios[i];
1776              ratios_sum += ratio;
1777            }
1778            return (max_w - (ratios.length - 1) * margin_w) / ratios_sum;
1779          };
1780
1781          var tries = [];
1782          var first_line, second_line, third_line, fourth_line;
1783          // Two lines
1784          for (first_line = 1; first_line <= cnt - 1; first_line++) {
1785            second_line = cnt - first_line;
1786            if (first_line  > 3 ||
1787                second_line > 3) {
1788              continue;
1789            }
1790            tries.push([[first_line, second_line], [
1791              multiHeight(ratios_cropped.slice(0, first_line)),
1792              multiHeight(ratios_cropped.slice(first_line)),
1793            ]]);
1794          }
1795
1796          // Three lines
1797          for (first_line = 1; first_line <= cnt - 2; first_line++) {
1798            for (second_line = 1; second_line <= cnt - first_line - 1; second_line++) {
1799              third_line = cnt - first_line - second_line;
1800              if (first_line  > 3 ||
1801                  second_line > (avg_ratio < 0.85 ? 4 : 3) ||
1802                  third_line  > 3) {
1803                continue;
1804              }
1805              tries.push([[first_line, second_line, third_line], [
1806                multiHeight(ratios_cropped.slice(0, first_line)),
1807                multiHeight(ratios_cropped.slice(first_line, first_line + second_line)),
1808                multiHeight(ratios_cropped.slice(first_line + second_line)),
1809              ]]);
1810            }
1811          }
1812
1813          // Four lines
1814          for (first_line = 1; first_line <= cnt - 3; first_line++) {
1815            for (second_line = 1; second_line <= cnt - first_line - 2; second_line++) {
1816              for (third_line = 1; third_line <= cnt - first_line - second_line - 1; third_line++) {
1817                fourth_line = cnt - first_line - second_line - third_line;
1818                if (first_line  > 3 ||
1819                    second_line > 3 ||
1820                    third_line  > 3 ||
1821                    fourth_line > 3) {
1822                  continue;
1823                }
1824                tries.push([[first_line, second_line, third_line, fourth_line], [
1825                  multiHeight(ratios_cropped.slice(0, first_line)),
1826                  multiHeight(ratios_cropped.slice(first_line, first_line + second_line)),
1827                  multiHeight(ratios_cropped.slice(first_line + second_line, first_line + second_line + third_line)),
1828                  multiHeight(ratios_cropped.slice(first_line + second_line + third_line)),
1829                ]]);
1830              }
1831            }
1832          }
1833
1834          // Looking for minimum difference between thumbs block height and max_h (may probably be little over)
1835          var opt_i = false;
1836          var opt_conf = false;
1837          var opt_diff = false;
1838          var opt_h = false;
1839          for (var i = 0; i < tries.length; i++) {
1840            var conf_nums = tries[i][0];
1841            var heights = tries[i][1];
1842            var heights_sum = 0;
1843            var heights_min = Infinity;
1844            for (var j = 0; j < heights.length; j++) {
1845              heights_sum += heights[j];
1846              if (heights_min > heights[j]) {
1847                heights_min = heights[j];
1848              }
1849            }
1850            var conf_h = Math.floor(heights_sum + margin_h * (heights.length - 1));
1851            var conf_diff = Math.abs(conf_h - max_h);
1852            if (conf_nums.length > 1) {
1853              if (conf_nums[0] > conf_nums[1] ||
1854                  conf_nums[2] && conf_nums[1] > conf_nums[2] ||
1855                  conf_nums[3] && conf_nums[2] > conf_nums[3]) {
1856                conf_diff *= 1.5;
1857              }
1858            }
1859            if (heights_min < min_w) {
1860              conf_diff *= 1.5;
1861            }
1862            if (opt_conf === false || conf_diff < opt_diff) {
1863              opt_i = i;
1864              opt_conf = cloneArr(conf_nums);
1865              opt_diff = conf_diff;
1866              opt_h = conf_h;
1867            }
1868          }
1869
1870          // Generating optimal thumbs
1871          th_width  = max_w;
1872          th_height = opt_h;
1873          var thumbs_remain = cloneArr(thumbsEl);
1874          var ratios_remain = cloneArr(ratios_cropped);
1875          var chunks = cloneArr(opt_conf);
1876          var opt_heights = cloneArr(tries[opt_i][1]);
1877          var chunks_num = chunks.length;
1878          var last_row = chunks_num - 1;
1879          var sy = 0;
1880          for (var i = 0; i < chunks.length; i++) {
1881            var line_chunks_num = chunks[i];
1882            var line_thumbs = thumbs_remain.splice(0, line_chunks_num);
1883            var line_height = opt_heights.shift();
1884            var last_column = line_thumbs.length - 1;
1885            var h = Math.floor(line_height);
1886            var sx = 0;
1887            var t = '', r = '', b = '', l = '';
1888            if (i == 0) {
1889              t = 't';
1890            }
1891            if (i == last_row) {
1892              b = 'b';
1893              h = th_height - sy;
1894            }
1895            for (var j = 0; j < line_thumbs.length; j++) {
1896              var thumbEl = line_thumbs[j];
1897              var thumb_ratio = ratios_remain.shift();
1898              var w = Math.floor(thumb_ratio * h);
1899              if (j == 0) {
1900                l = 'l';
1901              }
1902              if (j == last_column) {
1903                r = 'r';
1904                w = th_width - sx;
1905              }
1906              updateThumb(thumbEl, sx, sy, w, h, th_width, th_height, t+r+b+l);
1907              sx += w + margin_w;
1908            }
1909            sy += h + margin_h;
1910          }
1911        }
1912
1913        groupedEl.style.paddingTop  = (th_height / th_width * 100) + '%';
1914        groupedLayerEl.style.width  = th_width + 'px';
1915        groupedLayerEl.style.height = th_height + 'px';
1916      }
1917    }
1918  };
1919
1920  var TRoundVideo = window.TRoundVideo = {
1921    init: function(playerEl) {
1922      playerEl = geById(playerEl);
1923      if (!playerEl || playerEl.__inited) return;
1924      playerEl.__inited = true;
1925      var videoEl = ge1('.js-message_roundvideo', playerEl)
1926        , playEl = ge1('.js-message_roundvideo_play', playerEl)
1927        , progressEl = ge1('.js-message_roundvideo_progress', playerEl)
1928        , durationEl = ge1('.js-message_roundvideo_duration', playerEl)
1929        , playing = false;
1930      if (!videoEl) return;
1931
1932      function autoplay() {
1933        if (!videoEl) return;
1934        removeEvent(document, 'touchstart', autoplay);
1935        removeClass(playerEl, 'playing');
1936        playing = false;
1937        videoEl.muted = true;
1938        videoEl.loop = true;
1939        videoEl.currentTime = 0;
1940        play();
1941        showProgress();
1942      }
1943      function showProgress() {
1944        if (!videoEl) return;
1945        if (playing && !videoEl.paused) {
1946          requestAnimationFrame(function(){ showProgress(); });
1947        }
1948        redrawProgress();
1949        if (videoEl.duration) {
1950          var duration = Math.floor(videoEl.duration);
1951          durationEl.innerHTML = formatDuration(duration - videoEl.currentTime);
1952        }
1953      }
1954      function redrawProgress(updateSVG) {
1955        if (!videoEl) return;
1956        var progress;
1957        if (playing) {
1958          progress = videoEl.currentTime / videoEl.duration;
1959        } else {
1960          progress = 0;
1961        }
1962        progress = Math.max(0, Math.min(progress, 1));
1963        var wrapWidth = playerEl.offsetWidth;
1964        if (wrapWidth) {
1965          var rd = progressEl.getAttribute('data-rd') || 3;
1966          var d = (wrapWidth - rd);
1967          var l = (d * Math.PI);
1968          progressEl.setAttribute('r', (d / 2));
1969          progressEl.setAttribute('stroke-dasharray', l);
1970          progressEl.setAttribute('stroke-dashoffset', l * (1 - progress));
1971          if (updateSVG) {
1972            progressEl.style.transform = !progressEl.style.transform ? 'rotateZ(270deg)' : '';
1973          }
1974        }
1975      }
1976      function play() {
1977        if (!videoEl) return;
1978        var video = videoEl;
1979        var isPlaying = (video.currentTime > 0) &&
1980                        !video.paused &&
1981                        !video.ended &&
1982                        (video.readyState > 2);
1983        if (!isPlaying) {
1984          video.play();
1985          if (playing) {
1986            triggerEvent(videoEl, 'tg:play', {bubbles: true});
1987          }
1988        }
1989      }
1990      addEvent(document, 'tg:play', function(e) {
1991        if (e.target === videoEl || !playing) return true;
1992        if (videoEl.paused) {
1993          play();
1994          showProgress();
1995        } else {
1996          autoplay();
1997        }
1998      });
1999      function pause() {
2000        if (!videoEl) return;
2001        videoEl.pause();
2002      }
2003      function toggle(e) {
2004        e.stopPropagation();
2005        if (!playing) {
2006          redrawProgress();
2007          addClass(playerEl, 'playing');
2008          playing = true;
2009          videoEl.muted = false;
2010          videoEl.loop = false;
2011          videoEl.currentTime = 0;
2012          play();
2013          showProgress();
2014        } else {
2015          if (videoEl.paused) {
2016            play();
2017            showProgress();
2018          } else {
2019            pause();
2020          }
2021        }
2022      }
2023
2024      enableInlineVideo(videoEl);
2025
2026      checkVideo(videoEl, function() {
2027        addClass(playerEl, 'not_supported');
2028      });
2029      autoplay();
2030      addEvent(document, 'touchstart', autoplay);
2031      addEvent(videoEl, 'ended', function() {
2032        autoplay();
2033      });
2034      addEvent(window, 'resize', function() {
2035        redrawProgress(true)
2036      });
2037      if (playEl) {
2038        addEvent(playEl, 'click', toggle);
2039      }
2040    }
2041  };
2042
2043  var TVoice = window.TVoice = {
2044    init: function(playerEl) {
2045      playerEl = geById(playerEl);
2046      if (!playerEl || playerEl.__inited) return;
2047      playerEl.__inited = true;
2048      var audioEl = ge1('.js-message_voice', playerEl)
2049        , durationEl = ge1('.js-message_voice_duration', playerEl)
2050        , progressEl = ge1('.js-message_voice_progress', playerEl)
2051        , progressWrapEl = ge1('.js-message_voice_progress_wrap', playerEl)
2052        , player = null
2053        , isOGG = audioEl.hasAttribute('data-ogg')
2054        , seekTo = null
2055        , seeking = false
2056        , disableClick = false;
2057      if (!audioEl) return;
2058
2059      function initPlayer() {
2060        addClass(playerEl, 'ready');
2061        addEvent(player, 'play', function() {
2062          addClass(playerEl, 'playing');
2063          triggerEvent(playerEl, 'tg:play', {bubbles: true});
2064        });
2065        addEvent(player, 'pause', function() {
2066          removeClass(playerEl, 'playing');
2067        });
2068        addEvent(player, 'ended', function() {
2069          player.currentTime = 0;
2070          showProgress();
2071        });
2072        addEvent(player, 'loadedmetadata', function() {
2073          showProgress();
2074        });
2075        addEvent(playerEl, 'click', toggle);
2076        addEvent(progressWrapEl, 'mousedown touchstart', seekStart);
2077        addEvent(document, 'tg:play', stop);
2078      }
2079      function toggle(e) {
2080        e.stopPropagation();
2081        if (!disableClick) {
2082          if (!player) return true;
2083          if (player.paused) {
2084            player.play();
2085            showProgress();
2086          } else {
2087            player.pause();
2088            showProgress();
2089          }
2090        } else {
2091          disableClick = false;
2092        }
2093        e.preventDefault();
2094        return false;
2095      }
2096      function stop(e) {
2097        if (e.target === playerEl || !player) return true;
2098        if (!player.paused) {
2099          player.pause();
2100          showProgress();
2101        }
2102        return false;
2103      }
2104      function seekStart(e) {
2105        if (player &&
2106            player.duration !== Infinity &&
2107            (!player.paused ||
2108             player.currentTime > 0 && player.currentTime < player.duration)) {
2109          e.preventDefault();
2110          seeking = true;
2111          disableClick = false;
2112          player.pause();
2113          addEvent(document, 'mousemove touchmove', seek);
2114          addEvent(document, 'mouseup mouseleave touchend touchcancel', seekEnd);
2115          seek(e);
2116        }
2117      }
2118      function seek(e) {
2119        if (!seeking) return;
2120        var px = e.pageX;
2121        var op = progressWrapEl;
2122        var x = op.offsetLeft;
2123        var w = op.offsetWidth;
2124        while (op = op.offsetParent) {
2125          x += op.offsetLeft;
2126        }
2127        var ct = Math.max(0, Math.min(px - x, w)) / w;
2128        seekTo = ct;
2129        showProgress();
2130      }
2131      function seekEnd(e) {
2132        if (!seeking) return;
2133        seek(e);
2134        var duration = Math.floor(player.duration);
2135        player.currentTime = seekTo * duration;
2136        seekTo = null;
2137        seeking = false;
2138        if (e.type.substr(0, 5) == 'mouse') {
2139          disableClick = true;
2140        }
2141        player.play();
2142        showProgress();
2143        removeEvent(document, 'mousemove touchmove', seek);
2144        removeEvent(document, 'mouseup touchend', seekEnd);
2145      }
2146      function showProgress() {
2147        if (!player) return;
2148        if (!player.paused) {
2149          requestAnimationFrame(function(){ showProgress(); });
2150        }
2151        if (player.duration && player.duration !== Infinity) {
2152          var duration = Math.floor(player.duration);
2153          if (seeking) {
2154            var currentTime = seekTo * duration;
2155          } else {
2156            var currentTime = Math.max(0, player.currentTime);
2157          }
2158          if (progressEl) {
2159            progressEl.style.width = Math.min(100, currentTime / duration * 100) + '%';
2160          }
2161          if (durationEl) {
2162            durationEl.innerHTML = formatDuration(duration - currentTime);
2163          }
2164        }
2165      }
2166      function redrawProgress() {
2167        if (!audioEl) return;
2168        var ss = progressWrapEl.getElementsByTagName('S');
2169        var ss_count = ss.length / 2;
2170        var waveform_str = audioEl.getAttribute('data-waveform') || '';
2171        var waveform = waveform_str.split(',');
2172        var lines_cnt = Math.floor((progressWrapEl.offsetWidth + 2) / 6);
2173        var p = waveform.length / lines_cnt;
2174        var values = [];
2175        var max_value = 0;
2176        for (var i = 0; i < lines_cnt; i++) {
2177          var ws = i * p;
2178          var we = ws + p;
2179          var wsi = Math.floor(ws);
2180          var wei = Math.floor(we);
2181          if (wsi == wei) {
2182            var value = waveform[wsi] * (we - ws);
2183          } else {
2184            var value = 0;
2185            for (var j = wsi; j <= wei; j++) {
2186              var wv = +waveform[j] || 0;
2187              if (j == wsi) {
2188                value += wv * (j + 1 - ws);
2189              } else if (j == wei) {
2190                value += wv * (we - j);
2191              } else {
2192                value += wv;
2193              }
2194            }
2195          }
2196          value = value / p;
2197          max_value = Math.max(value, max_value);
2198          values.push(value);
2199        }
2200        for (var i = 0; i < ss.length; i++) {
2201          var li = i % ss_count;
2202          if (li < lines_cnt) {
2203            var height = (values[li] / max_value) * 100;
2204            ss[i].style.height = height + '%';
2205            ss[i].style.display = '';
2206          } else {
2207            ss[i].style.display = 'none';
2208          }
2209        }
2210      }
2211
2212      loadLib(TBaseUrl + 'js/ogvjs/ogv-support.js', function(success) {
2213        if (!success) return;
2214        if (isOGG &&
2215            OGVCompat.hasWebAudio() &&
2216            OGVCompat.supported('OGVPlayer')) {
2217          loadLib(TBaseUrl + 'js/ogvjs/ogv.js', function(success) {
2218            if (!success) return;
2219            player = new OGVPlayer();
2220            player.src = audioEl.src;
2221            initPlayer();
2222          });
2223        } else {
2224          player = audioEl;
2225          initPlayer();
2226        }
2227      });
2228      addEvent(window, 'resize', function(){ redrawProgress() });
2229      redrawProgress();
2230    }
2231  };
2232
2233  var TSticker = window.TSticker = {
2234    init: proccessWebpImage
2235  };
2236
2237  window.TWidgetPost = {
2238    init: function(options) {
2239      if (!doesSupportEmoji()) {
2240        removeClass(document.body, 'emoji_default');
2241        addClass(document.body, 'emoji_image');
2242      }
2243      options = options || {};
2244      TWidgetPost.options = options;
2245
2246      gec('.js-widget_message', function() {
2247        TPost.init(this, {tgs_workers_limit: 1});
2248        addEvent(ge('.js-poll_option', this), 'click', TWidgetPost.eSelectPollOption);
2249        addEvent(ge('.js-poll_vote_btn', this), 'click', TWidgetPost.eSendVotes);
2250      });
2251      initWidgetFrame({
2252        auto_height: true,
2253        onVisible: function() {
2254          gec('.js-widget_message', function() {
2255            TPost.view(this);
2256          });
2257        }
2258      });
2259      addEvent(window, 'tg:optionschange', TWidgetPost.onOptionsChange);
2260    },
2261    onOptionsChange: function(e) {
2262      var new_options = e.detail, transition_off = false;
2263      if (typeof new_options.dark !== undefined) {
2264        transition_off = true;
2265        addClass(document.body, 'no_transitions');
2266        toggleClass(document.body, 'dark', !!new_options.dark);
2267        toggleClass(document.body, 'nodark', !new_options.dark);
2268      }
2269      if (transition_off) {
2270        setTimeout(function() {
2271          removeClass(document.body, 'no_transitions');
2272        }, 100);
2273      }
2274    },
2275    eSelectPollOption: function(e) {
2276      e.preventDefault();
2277      if (!TWidgetAuth.isLoggedIn()) {
2278        return TWidgetAuth.logIn();
2279      }
2280      var poll_el = gpeByClass(this, 'js-poll');
2281      if (!poll_el) {
2282        return false;
2283      }
2284      toggleClass(this, 'selected');
2285      toggleClass(poll_el, 'selected', ge('.js-poll_option.selected', poll_el).length > 0);
2286      if (!hasClass(poll_el, 'multiple')) {
2287        TWidgetPost.sendPollVote(poll_el, this);
2288      }
2289    },
2290    eSendVotes: function(e) {
2291      e.preventDefault();
2292      if (!TWidgetAuth.isLoggedIn()) {
2293        return TWidgetAuth.logIn();
2294      }
2295      var poll_el = gpeByClass(this, 'js-poll');
2296      if (!poll_el) {
2297        return false;
2298      }
2299      TWidgetPost.sendPollVote(poll_el, this);
2300    },
2301    sendPollVote: function(poll_el, option_el) {
2302      if (!poll_el || hasClass(poll_el, 'sending')) {
2303        return false;
2304      }
2305      var option_els = ge('.js-poll_option.selected', poll_el);
2306      var options = TWidgetPost.options || {};
2307      var post_el = gpeByClass(option_el, 'js-widget_message');
2308      if (!post_el) {
2309        return false;
2310      }
2311      var peer = getAttr(post_el, 'data-peer');
2312      var peer_hash = getAttr(post_el, 'data-peer-hash');
2313      var post_id = getAttr(post_el, 'data-post-id');
2314      if (!peer || !peer_hash || !post_id) {
2315        return false;
2316      }
2317      var poll_options = [];
2318      gec(option_els, function() {
2319        poll_options.push(getAttr(this, 'data-value'));
2320      });
2321      removeClass(poll_el, 'selected');
2322      addClass(poll_el, 'sending');
2323      apiRequest('sendVote', {
2324        peer: peer,
2325        peer_hash: peer_hash,
2326        post_id: post_id,
2327        options: poll_options.join(';')
2328      }, function(err, result) {
2329        removeClass(poll_el, 'sending');
2330        if (result.media_html) {
2331          var media_wrap = newEl('div', '', result.media_html);
2332          var media_html = getHtml('.js-poll', media_wrap);
2333          setHtml(poll_el, media_html);
2334          addEvent(ge('.js-poll_option', poll_el), 'click', TWidgetPost.eSelectPollOption);
2335          addEvent(ge('.js-poll_vote_btn', poll_el), 'click', TWidgetPost.eSendVotes);
2336        }
2337        toggleClass(poll_el, 'selected', ge('.js-poll_option.selected', poll_el).length > 0);
2338      });
2339    }
2340  };
2341
2342  var TWidgetLogin = {
2343    init: function(id, bot_id, params, init_auth, lang) {
2344      initWidgetFrame({
2345        auto_height: true,
2346        auto_width: true
2347      });
2348      TWidgetLogin.widgetEl = document.getElementById(id);
2349      TWidgetLogin.botId = bot_id;
2350      TWidgetLogin.params = params;
2351      TWidgetLogin.lang = lang;
2352      var params_encoded = '', params_arr = [];
2353      for (var k in params) {
2354        params_arr.push(encodeURIComponent(k) + '=' + encodeURIComponent(params[k]));
2355      }
2356      TWidgetLogin.paramsEncoded = params_arr.join('&');
2357      if (init_auth) {
2358        TWidgetLogin.getAuth(true);
2359      }
2360      addEvent(window, 'message', function (event) {
2361        try {
2362          var data = JSON.parse(event.data);
2363        } catch(e) {
2364          var data = {};
2365        }
2366        if (event.source !== TWidgetLogin.activePopup) return;
2367        if (data.event == 'auth_result') {
2368          TWidgetLogin.onAuth(data.origin, data.result);
2369        }
2370      });
2371    },
2372    auth: function() {
2373      var width = 550;
2374      var height = 470;
2375      var left = Math.max(0, (screen.width - width) / 2) + (screen.availLeft | 0),
2376          top = Math.max(0, (screen.height - height) / 2) + (screen.availTop | 0);
2377      function checkClose() {
2378        if (!TWidgetLogin.activePopup || TWidgetLogin.activePopup.closed) {
2379          return TWidgetLogin.onClose();
2380        }
2381        setTimeout(checkClose, 100);
2382      }
2383      TWidgetLogin.activePopup = window.open('https://oauth.telegram.org/auth?bot_id=' + TWidgetLogin.botId + (TWidgetLogin.paramsEncoded ? '&' + TWidgetLogin.paramsEncoded : ''), 'telegram_oauth', 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',status=0,location=0,menubar=0,toolbar=0');
2384      TWidgetLogin.authFinished = false;
2385      if (TWidgetLogin.activePopup) {
2386        TWidgetLogin.activePopup.focus();
2387        checkClose();
2388      }
2389    },
2390    getAuth: function(init) {
2391      var xhr = getXHR();
2392      xhr.open('POST', 'https://oauth.telegram.org/auth/get?bot_id=' + TWidgetLogin.botId + (TWidgetLogin.lang ? '&lang=' + encodeURIComponent(TWidgetLogin.lang) : ''));
2393      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
2394      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
2395      xhr.onreadystatechange = function() {
2396        if (xhr.readyState == 4) {
2397          if (typeof xhr.responseBody == 'undefined' && xhr.responseText) {
2398            try {
2399              var result = JSON.parse(xhr.responseText);
2400            } catch(e) {
2401              var result = {};
2402            }
2403            //if (result.html && TWidgetLogin.widgetEl.innerHTML != result.html) {
2404              //TWidgetLogin.widgetEl.innerHTML = result.html;
2405            //}
2406            if (result.user) {
2407              TWidgetLogin.onAuth(result.origin, result.user, init);
2408            } else {
2409              TWidgetLogin.onAuth(result.origin, false, init);
2410            }
2411          } else {
2412            TWidgetLogin.onAuth('*', false, init);
2413          }
2414        }
2415      };
2416      xhr.onerror = function() {
2417        TWidgetLogin.onAuth('*', false, init);
2418      };
2419      xhr.withCredentials = true;
2420      xhr.send(TWidgetLogin.paramsEncoded);
2421    },
2422    onAuth: function(origin, authData, init) {
2423      if (TWidgetLogin.authFinished) return;
2424      if (authData) {
2425        var data = {event: 'auth_user', auth_data: authData};
2426      } else {
2427        var data = {event: 'unauthorized'};
2428      }
2429      if (init) {
2430        data.init = true;
2431      }
2432      PostMessage.send(data, origin);
2433      TWidgetLogin.authFinished = true;
2434    },
2435    onClose: function() {
2436      TWidgetLogin.getAuth();
2437    }
2438  };
2439  window.TWidgetLogin = TWidgetLogin;
2440
2441  var TStats = {
2442    init: function () {
2443      if (!doesSupportEmoji()) {
2444        removeClass(document.body, 'emoji_default');
2445        addClass(document.body, 'emoji_image');
2446      }
2447      gec('.js-sticker_thumb', function() {
2448        TSticker.init(this);
2449      });
2450    }
2451  }
2452  window.TStats = TStats;
2453
2454  window.TWidget = {
2455    initFrame: initWidgetFrame
2456  };
2457
2458})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.