diff --git a/app/Models/Page.php b/app/Models/Page.php index d1fb1e57..fa50ed79 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -114,6 +114,12 @@ abstract class Page extends Model 'src' => $this->publicLink('/js/common.js'), ]); + if ($this->useMediaJS) { + $this->pageHeader('mediaJS', 'script', 10000, [ + 'src' => $this->publicLink('/js/media.js'), + ]); + } + $this->boardNavigation(); $this->iswevMessages(); } diff --git a/app/Models/Pages/Edit.php b/app/Models/Pages/Edit.php index f59c8022..c8c88a2d 100644 --- a/app/Models/Pages/Edit.php +++ b/app/Models/Pages/Edit.php @@ -68,6 +68,7 @@ class Edit extends Page $this->previewHtml = $this->c->censorship->censor( $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies) ); + $this->useMediaJS = true; if ( $firstPost diff --git a/app/Models/Pages/Post.php b/app/Models/Pages/Post.php index 8fe2c48e..079f3d07 100644 --- a/app/Models/Pages/Post.php +++ b/app/Models/Pages/Post.php @@ -72,6 +72,7 @@ class Post extends Page $this->previewHtml = $this->c->censorship->censor( $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies) ); + $this->useMediaJS = true; if ( $this->userRules->usePoll @@ -139,6 +140,7 @@ class Post extends Page && ! $v->getErrors() ) { $this->previewHtml = $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies); + $this->useMediaJS = true; } } elseif (isset($args['quote'])) { $post = $this->c->posts->load($args['quote'], $topic->id); diff --git a/app/Models/Pages/Search.php b/app/Models/Pages/Search.php index 914bdc5b..748bd423 100644 --- a/app/Models/Pages/Search.php +++ b/app/Models/Pages/Search.php @@ -542,8 +542,9 @@ class Search extends Page } else { $this->c->Lang->load('topic'); - $this->nameTpl = 'topic_in_search'; - $this->posts = $list; + $this->nameTpl = 'topic_in_search'; + $this->posts = $list; + $this->useMediaJS = true; } $this->fIndex = self::FI_SRCH; diff --git a/app/Models/Pages/Topic.php b/app/Models/Pages/Topic.php index 70510e64..d33b072b 100644 --- a/app/Models/Pages/Topic.php +++ b/app/Models/Pages/Topic.php @@ -148,6 +148,7 @@ class Topic extends Page $this->crumbs = $this->crumbs($topic); $this->online = $this->c->Online->calc($this)->info(); $this->stats = null; + $this->useMediaJS = true; if ( $topic->canReply diff --git a/public/js/media.js b/public/js/media.js new file mode 100644 index 00000000..b001d0ed --- /dev/null +++ b/public/js/media.js @@ -0,0 +1,467 @@ +/** + * This file is part of the ForkBB . + * + * @copyright (c) Visman + * @license The MIT License (MIT) + */ + +if (typeof ForkBB === "undefined" || !ForkBB) { + var ForkBB = {}; +} + +ForkBB.media = (function (doc, win, nav) { + 'use strict'; + + var id = 0, + html5mime = {}, + html5aRegx = getHTML5("audio", ["mp3", "m4a", "ogg", "oga", "webma", "wav", "flac"], ["mpeg", "mp4", "ogg", "ogg", "webm", "wav", "flac"]), + html5vRegx = getHTML5("video", ["mp4", "m4v", "ogv", "webm", "webmv"], ["mp4", "mp4", "ogg", "webm", "webm"]), + lazyFlag = false, + lazyClass = "mediajslazy", + lazyData = [], + lazyObserver, + searchClass = "formedia", + addClass = "mediajslink", + fb = false; + + function getHTML5(type, exts, mimes) { + var i, + r = "", + a = doc.createElement(type); + + if (!!a.canPlayType) { + for (i = 0; i"\'&]+\\.)(' + r + ')$', 'i') : false; + } + + return false; + } + + function getOembedSize(data) { + if (data.width && data.height) { + var e = 1; + + if (data.width < data.height) { + if (data.height < 480) { + e = 480 / data.height; + } else if (data.height > 640) { + e = 640 / data.height; + } + } else { + if (data.width > 640) { + e = 640 / data.width; + } else if (data.height < 480) { + e = Math.min(640 / data.width, 480 / data.height); + } + } + + return [Math.floor(data.width * e), Math.floor(data.height * e)]; + } + } + + function getOembed(url, successHandler, errorHandler) { + var xhr = new XMLHttpRequest(); + + xhr.open("GET", url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + var data = xhr.responseText; + + if (typeof data === "string") { + try { + data = JSON.parse(data); + } catch (e) { + errorHandler && errorHandler(e); + return; + } + } + + if ("width" in data && "height" in data && "html" in data) { + successHandler && successHandler(data); + } else { + errorHandler && errorHandler(data); + } + } else { + errorHandler && errorHandler(xhr); + } + } + }; + xhr.send(); + } + + function createEl(node, type, attr, param, after) { + attr = attr || {}; + param = param || {}; + + var child, + flag = type === "video" || type === "audio", + obj = doc.createElement(type); + + for (var i in attr) { + if (attr.hasOwnProperty(i)) { + if (i == "style") { + obj.style.cssText = attr[i]; + } else { + obj.setAttribute(i, attr[i]); + } + } + } + + if (flag) { + child = doc.createElement("source"); + } + + for (var i in param) { + if (param.hasOwnProperty(i)) { + if (flag) { + child.setAttribute(i, param[i]); + } else { + child = doc.createElement("param"); + child.setAttribute("name", i); + child.setAttribute("value", param[i]); + obj.appendChild(child); + } + } + } + + if (flag) { + obj.appendChild(child); + } + + if (!after) { + return node.appendChild(obj); + } + + return node.parentNode.insertBefore(obj, node.nextSibling); + } + + function createMedia(node, playAttr, size, type, playParam) { + var style1 = "padding-top:5px;", + style2 = "overflow:hidden;position:relative;", + attrDiv1 = {style: style1 + style2}, + attrDiv2, tw, th; + + type = type || "iframe"; + playParam = playParam || {}; + + if ("iframe" === type) { + playAttr.frameborder = "0"; + playAttr.scrolling = "no"; + + if (! playAttr.allowfullscreen) { + playAttr.allowfullscreen = "allowfullscreen"; + } + } + + size = size || [640, 360]; + // 0 - ошибка, 1 - размер не указывать, 2 - в пикселах, 3 - в процентах + tw = typeSize(size[0]); + th = typeSize(size[1]); + + if (!tw || !th) { + return; + } + + if (tw > 1) { + attrDiv1.style = attrDiv1.style + (3 === tw ? "width:" + size[0] + ";" : "max-width:" + size[0] + "px;"); + } + + if (3 === tw && 2 === th) { + attrDiv2 = {style: style2 + "height:" + size[1] + "px;"}; + playAttr.style = "width:100%;height:" + size[1] + "px;"; + } else if (tw > 1 && th > 1) { + attrDiv2 = {style: style2 + "height:0;padding-bottom:" + (3 === th ? size[1] : (100 * size[1] / size[0]) + "%;")}; + playAttr.style = "position:absolute;left:0;top:0;width:100%;height:100%;"; + } else if (tw > 1) { + playAttr.style = "width:100%;"; + } + + ++id; + playAttr.id = "mediajs" + id; + + node.classList.replace(searchClass, addClass); + + var div = createEl(node, "div", attrDiv1, {}, true); + + if (attrDiv2) { + div = createEl(div, "div", attrDiv2); + } + + if (lazyFlag && "div" !== type) { + div.classList.add(lazyClass); + div.setAttribute("data-lazyid", id); + + lazyData[id] = { + type: type, + attr: playAttr, + param: playParam + }; + + if (! lazyObserver) { + lazyObserver = new IntersectionObserver(function(entries, observer) { + entries.forEach(function(entry) { + if (entry.isIntersecting) { + var lazyObj = entry.target, + id = lazyObj.dataset.lazyid, + set = lazyData[id]; + + createEl(lazyObj, set.type, set.attr, set.param); + lazyObj.classList.remove(lazyClass); + observer.unobserve(lazyObj); + } + }); + }); + } + + lazyObserver.observe(div); + } else { + createEl(div, type, playAttr, playParam); + } + + return true; + } + + function convertRangeToZoom(range, type) { + if (type != "z") { + range = Math.log(70400000 / range) / Math.log(2); + } + + return Math.min(18, Math.max(Math.round(range), 3)); + } + + function hmsToSec(str) { + if (/^\d+$/.test(str)) { + return str; + } + + if (str = str.match(/^((\d{1,2})h)?((\b\d{1,3}|\d{1,2})m)?((\b\d{1,5}|\d{1,2})s)?$/)) { + return 3600 * (str[2] || 0) + 60 * (str[4] || 0) + 1 * (str[6] || 0); + } + + return ""; + } + + // 0 - ошибка, 1 - размер не указывать, 2 - в пикселах, 3 - в процентах + function typeSize(size) { + if ("string" === typeof size && /^(?:100|[1-9]\d(?:\.\d{1,2})?)%$/.test(size)) { + return 3; + } else if (0 !== size % 1) { + return 0; + } else if (size > 15) { + return 2; + } else if (size < 1) { + return 1; + } else { + return 0; + } + } + + function test(node) + { + var url = node.href, + m = url.match(/^(.+)(?:#|%23)(\d+(?:\.\d{1,2})?)(%|%25)?(?::|%3A)(\d+(?:\.\d{1,2})?)(%|%25)?$/), + arr, size; + + if (m) { + node.herf = url = m[1]; + m[2] += (m[3] ? "%" : ""); + m[4] += (m[5] ? "%" : ""); + + if (typeSize(m[2]) > 0 && typeSize(m[4]) > 0) { + size = [m[2], m[4]]; + } + } + + if (!(m = url.replace(/&/g, "&").match(/^(https?:\/\/)(www\.)?(.+)$/))) { + return; + } + + url = m[3]; + + if (!!html5aRegx && (arr = url.match(html5aRegx))) { + createMedia(node, {controls: true, preload: "metadata", controlsList: "nodownload"}, size || ["100%", 0], "audio", {src: m[1] + (m[2] ? m[2] : "") + arr[1] + arr[2], type: html5mime[arr[2].toLowerCase()]}); + + } else if (!!html5vRegx && (arr = url.match(html5vRegx))) { + createMedia(node, {controls: true, preload: "metadata", controlsList: "nodownload"}, size, "video", {src: m[1] + (m[2] ? m[2] : "") + arr[1] + arr[2], type: html5mime[arr[2].toLowerCase()]}); + + } else if (arr = url.match(/^(?:[a-z\d-]+\.)?youtu(?:be(?:-nocookie)?\.com|\.be)\/(?:playlist|(?:(?:.*?[\?&]v=)?([\w-]{11})(?:\?|&|#|$)))/)) { + var b = "https://www.youtube.com/embed", + param = ["wmode=transparent"], // , "controls=2" + need = [], + c, d, e, only; + + if (arr[1]) { + b += "/" + arr[1]; + only = ["start", "t", "end", "list", "rel", "loop"]; + } else { + param.push("listType=playlist"); + only = ["list", "index", "loop"]; + need = ["list"] + } + + e = url.split(/[\?&#]/); + + while (c = e.shift()) { + d = c.split("="); + + if (d[0] && d[1] && only.indexOf(d[0]) > -1 && /^[\w-]+$/.test(d[1])) { + if (d[0] == "start" || d[0] == "t" || d[0] == "end") { + param.push((d[0] == "end" ? "end" : "start") + "=" + hmsToSec(d[1])); + } else { + param.push(d[0] + "=" + d[1]); + + if (d[0] == "loop" && d[1] == "1" && arr[1]) { + param.push("playlist=" + arr[1]); + } + } + + d = need.indexOf(d[0]); + + if (d > -1) { + need.splice(d, 1); + } + } + } + + if (!need.length) { + createMedia(node, {src: b + "?" + param.join("&")}, size); + } + + } else if (arr = url.match(/^(?:player\.)?vimeo\.com\/(?:[^\s\/<>'"]+\/){0,3}(?:\d+)(?=\?|#|$)/)) { + getOembed("https://vimeo.com/api/oembed.json?url=https%3A//" + arr[0], function(data) { + if (data.m = data.html.match(/