1856 lines
40 KiB
JavaScript
1856 lines
40 KiB
JavaScript
(function() {
|
|
"use strict";
|
|
|
|
BX.namespace("BX.Landing");
|
|
|
|
/**
|
|
* Landing utils.
|
|
*/
|
|
BX.Landing.Utils = function() {};
|
|
|
|
/**
|
|
* Shows element
|
|
* @param {Element|HTMLElement} element
|
|
* @return {Promise}
|
|
* @constructor
|
|
*/
|
|
BX.Landing.Utils.Show = function(element)
|
|
{
|
|
return new Promise(function(resolve) {
|
|
if (!!element && !BX.Landing.Utils.isShown(element))
|
|
{
|
|
BX.Landing.Utils.onAnimationEnd(element)
|
|
.then(function(event) {
|
|
element.dataset.isShown = true;
|
|
resolve(event);
|
|
});
|
|
|
|
element.hidden = false;
|
|
element.classList.remove("landing-ui-hide");
|
|
element.classList.add("landing-ui-show");
|
|
}
|
|
else
|
|
{
|
|
resolve();
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks that element is shown
|
|
* @param {HTMLElement} element
|
|
* @return {boolean}
|
|
*/
|
|
BX.Landing.Utils.isShown = function(element)
|
|
{
|
|
return element.dataset.isShown === "true";
|
|
};
|
|
|
|
|
|
/**
|
|
* Hides element
|
|
* @param {HTMLElement} element
|
|
* @return {Promise}
|
|
* @constructor
|
|
*/
|
|
BX.Landing.Utils.Hide = function(element)
|
|
{
|
|
return new Promise(function(resolve) {
|
|
if (!!element && BX.Landing.Utils.isShown(element))
|
|
{
|
|
BX.Landing.Utils.onAnimationEnd(element)
|
|
.then(function(event) {
|
|
element.hidden = true;
|
|
element.dataset.isShown = false;
|
|
resolve(event);
|
|
});
|
|
|
|
element.classList.remove("landing-ui-show");
|
|
element.classList.add("landing-ui-hide");
|
|
}
|
|
else
|
|
{
|
|
resolve();
|
|
}
|
|
});
|
|
};
|
|
|
|
BX.Landing.Utils.isValidElementId = function(id)
|
|
{
|
|
var re = new RegExp('^[A-Za-z]+[\\w\\-\\:\\.]*$');
|
|
return re.test(id)
|
|
};
|
|
|
|
BX.Landing.Utils.ignorePromiseDecorator = function(fn)
|
|
{
|
|
var nothing = function() {};
|
|
|
|
return function() {
|
|
fn.apply(null, arguments).then(nothing);
|
|
}
|
|
};
|
|
|
|
|
|
BX.Landing.Utils.appendHTML = function(element, html)
|
|
{
|
|
element.innerHTML = element.innerHTML + html;
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets CSS unique selector of element
|
|
* @param {HTMLElement} element
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.getCSSSelector = function(element)
|
|
{
|
|
var names = [];
|
|
|
|
while (element.parentNode)
|
|
{
|
|
if (element.id)
|
|
{
|
|
names.unshift('#'+element.id);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (element === element.ownerDocument.documentElement)
|
|
{
|
|
names.unshift(element.tagName.toLowerCase());
|
|
}
|
|
else
|
|
{
|
|
for (var c=1, e=element; e.previousElementSibling; e=e.previousElementSibling, c++)
|
|
{
|
|
}
|
|
names.unshift(element.tagName.toLowerCase()+":nth-child("+c+")");
|
|
}
|
|
|
|
element = element.parentNode;
|
|
}
|
|
}
|
|
return names.join(" > ");
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles transition end event
|
|
* @param {HTMLElement|HTMLElement[]} elements
|
|
*/
|
|
BX.Landing.Utils.onTransitionEnd = function(elements)
|
|
{
|
|
elements = BX.type.isArray(elements) ? elements : [elements];
|
|
|
|
return Promise.all(elements.map(function(element) {
|
|
return new Promise(function(resolve) {
|
|
element.addEventListener("webkitTransitionEnd", resolve);
|
|
element.addEventListener("transitionend", resolve);
|
|
element.addEventListener("msTransitionEnd", resolve);
|
|
element.addEventListener("oTransitionEnd", resolve);
|
|
return resolve;
|
|
}).then(function(resolver) {
|
|
element.removeEventListener("webkitTransitionEnd", resolver);
|
|
element.removeEventListener("transitionend", resolver);
|
|
element.removeEventListener("msTransitionEnd", resolver);
|
|
element.removeEventListener("oTransitionEnd", resolver);
|
|
})
|
|
}));
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles animationend event
|
|
* @param {Element} element
|
|
* @param {string} [animationName]
|
|
* @return {Promise<AnimationEvent>}
|
|
*/
|
|
BX.Landing.Utils.onAnimationEnd = function(element, animationName)
|
|
{
|
|
return new Promise(function(resolve) {
|
|
var onAnimationEnd = function(event)
|
|
{
|
|
if (!animationName || (event.animationName === animationName))
|
|
{
|
|
resolve(event);
|
|
element.removeEventListener("animationend", onAnimationEnd);
|
|
element.removeEventListener("oAnimationEnd", onAnimationEnd);
|
|
element.removeEventListener("webkitAnimationEnd", onAnimationEnd);
|
|
}
|
|
};
|
|
|
|
element.addEventListener("animationend", onAnimationEnd);
|
|
element.addEventListener("oAnimationEnd", onAnimationEnd);
|
|
element.addEventListener("webkitAnimationEnd", onAnimationEnd);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Converts html to Element
|
|
* @param {string} html
|
|
* @return {?HTMLElement}
|
|
*/
|
|
BX.Landing.Utils.htmlToElement = function(html)
|
|
{
|
|
return BX.create("div", {html: html}).firstElementChild;
|
|
};
|
|
|
|
|
|
/**
|
|
* Converts html to DocumentFragment
|
|
* @param {string} html
|
|
* @return {DocumentFragment}
|
|
*/
|
|
BX.Landing.Utils.htmlToFragment = function(html)
|
|
{
|
|
var tmpElement = BX.create("div", {html: html});
|
|
var fragment = document.createDocumentFragment();
|
|
|
|
[].slice.call(tmpElement.children).forEach(function(element) {
|
|
fragment.appendChild(element);
|
|
});
|
|
|
|
return fragment;
|
|
};
|
|
|
|
|
|
/**
|
|
* Freezes object
|
|
* @param {object} object
|
|
* @return {object}
|
|
*/
|
|
BX.Landing.Utils.deepFreeze = function(object)
|
|
{
|
|
Object.freeze(object);
|
|
|
|
Object.keys(object).forEach(function(prop) {
|
|
if (!!object[prop] && (typeof object[prop] === "object" || typeof object[prop] === "function"))
|
|
{
|
|
BX.Landing.Utils.deepFreeze(object[prop]);
|
|
}
|
|
});
|
|
|
|
return object;
|
|
};
|
|
|
|
|
|
/**
|
|
* Inserts element with a specified position
|
|
* @param {HTMLElement} container
|
|
* @param {HTMLElement} element
|
|
* @param {int} position
|
|
* @static
|
|
*/
|
|
BX.Landing.Utils.insert = function(container, element, position)
|
|
{
|
|
if (position === 0)
|
|
{
|
|
BX.prepend(element, container);
|
|
}
|
|
else if (position > 0 && position <= container.children.length-1)
|
|
{
|
|
container.insertBefore(element, container.children[position]);
|
|
}
|
|
else
|
|
{
|
|
container.appendChild(element);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Contains RegExp's form media services
|
|
* @type {{
|
|
* youtube: RegExp,
|
|
* vimeo: RegExp,
|
|
* vine: RegExp,
|
|
* instagram: RegExp,
|
|
* googleMapsSearch: RegExp,
|
|
* googleMapsPlace: RegExp
|
|
* }}
|
|
*/
|
|
BX.Landing.Utils.Matchers = {
|
|
youtube: new RegExp("(youtube\\.com|youtu\\.be|youtube\\-nocookie\\.com)\\/((watch\\?(.*&)?v=|v\\/|u\\/|embed\\/?)|(shorts\\/))?(videoseries\\?list=(.*)|[\\w-]{11}|\\?listType=(.*)&list=(.*))(.*)"),
|
|
vimeo: new RegExp("^.+vimeo.com\\/(.*\\/)?([\\d]+)(.*)?"),
|
|
vine: new RegExp("vine.co\\/v\\/([a-zA-Z0-9\\?\\=\\-]+)"),
|
|
instagram: new RegExp("(instagr\\.am|instagram\\.com)\\/p\\/([a-zA-Z0-9_\\-]+)\\/?"),
|
|
rutube: new RegExp("rutube\\.ru\\/video\\/(private\\/)?([a-zA-Z0-9]+)\\/?"),
|
|
|
|
// Examples:
|
|
// https://vkvideo.ru/video-123456789_123456789
|
|
// https://vk.com/video-123456789_123456789
|
|
// https://vk.ru/video-123456789_123456789
|
|
// https://vkvideo.ru/clip-123456789_123456789
|
|
// https://vk.com/clip-123456789_123456789
|
|
// https://vk.ru/clip-123456789_123456789
|
|
vk: new RegExp("(vk\\.com|vk\\.ru|vkvideo\\.ru)\\/.*(video|clip)(-?\\d+_\\d+)\\/?"),
|
|
|
|
// Examples:
|
|
// https://www.google.com/maps/search/Bitrix24+office/
|
|
// https://www.google.com/maps/search/?api=1&query=Bitrix24+office
|
|
// https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393
|
|
googleMapsSearch: new RegExp("(maps\\.)?google\\.([a-z]{2,3}(\\.[a-z]{2})?)\\/(maps\\/search\\/)(.*)", "i"),
|
|
|
|
// Examples:
|
|
// http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
|
|
// https://www.google.com/maps/@37.7852006,-122.4146355,14.65z
|
|
// https://www.google.com/maps/place/Bitrix24+office/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572
|
|
googleMapsPlace: new RegExp("(maps\\.)?google\\.([a-z]{2,3}(\\.[a-z]{2})?)\\/(((maps\\/(place\\/(.*)\\/)?\\@(.*),(\\d+.?\\d+?)z))|(\\?ll=))(.*)?", "i"),
|
|
headerTag: new RegExp("^H[1-6]$"),
|
|
russianText: new RegExp("[\u0400-\u04FF]"),
|
|
facebookPages: new RegExp("(?:http:\/\/)?(?:www\.)?facebook\.com\/(?:(?:\w)*#!\/)?(?:pages\/)?(?:[\w\-]*\/)*([\w\-]*)"),
|
|
facebookPosts: new RegExp("^https:\/\/www\.facebook\.com\/(photo(\.php|s)|permalink\.php|media|questions|notes|[^\/]+\/(activity|posts))[\/?].*$"),
|
|
facebookVideos: new RegExp("^(?:(?:https?:)?\/\/)?(?:www\.)?facebook\.com\/[a-z0-9\.]+\/videos\/(?:[a-z0-9\.]+\/)?([0-9]+)\/?(?:\\?.*)?$")
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets URL preview object
|
|
* @param {string} url
|
|
* @return {Promise<Object, Object>}
|
|
*/
|
|
BX.Landing.Utils.getURLPreview = function(url)
|
|
{
|
|
return BX.Landing.Backend.getInstance()
|
|
.action("Utils::getUrlPreview", {url: url});
|
|
};
|
|
|
|
|
|
/**
|
|
* Converts HTML string to HTMLElement
|
|
* @param {string} html
|
|
* @return {HTMLElement}
|
|
* @static
|
|
*/
|
|
BX.Landing.Utils.HTMLToElement = function(html)
|
|
{
|
|
return BX.create("div", {html: html}).firstElementChild;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Gets all URL params with value
|
|
* @param {string} url
|
|
* @return {object.<string, string>}
|
|
*/
|
|
BX.Landing.Utils.getQueryParams = function(url)
|
|
{
|
|
var result = {};
|
|
|
|
if (typeof url === "string")
|
|
{
|
|
var queryString = url.split("?")[1];
|
|
|
|
if (queryString)
|
|
{
|
|
var vars = queryString.split("&");
|
|
|
|
for (var i = 0; i < vars.length; i++)
|
|
{
|
|
var pair = vars[i].split("=");
|
|
|
|
if (typeof result[pair[0]] === "undefined")
|
|
{
|
|
result[pair[0]] = decodeURIComponent(pair[1]);
|
|
}
|
|
else if (typeof result[pair[0]] === "string")
|
|
{
|
|
result[pair[0]] = [result[pair[0]], decodeURIComponent(pair[1])];
|
|
}
|
|
else
|
|
{
|
|
result[pair[0]].push(decodeURIComponent(pair[1]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
|
|
/**
|
|
* Escapes html
|
|
* @param {string} html
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.escapeHtml = function(html)
|
|
{
|
|
return BX.util.htmlspecialchars(
|
|
BX.util.htmlspecialcharsback("" + html)
|
|
);
|
|
};
|
|
|
|
|
|
/**
|
|
* Escapes text
|
|
* @param {*} text
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.escapeText = function(text)
|
|
{
|
|
var result = text;
|
|
|
|
if (typeof text === "number" || typeof text === "boolean")
|
|
{
|
|
result = "" + text;
|
|
}
|
|
else if (!!text && typeof text === "object")
|
|
{
|
|
result = JSON.stringify(text);
|
|
}
|
|
|
|
return BX.Landing.Utils.escapeHtml(result);
|
|
};
|
|
|
|
|
|
/**
|
|
* Escapes attribute value
|
|
* @param {*} value
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.escapeAttributeValue = function(value)
|
|
{
|
|
if (BX.Landing.Utils.isPlainObject(value) || BX.Landing.Utils.isArray(value))
|
|
{
|
|
value = JSON.stringify(value);
|
|
}
|
|
|
|
return BX.util.jsencode("" + value);
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets text content to node
|
|
* @param {HTMLElement} element
|
|
* @param {string} text
|
|
* @param {string} classForTextNode
|
|
*/
|
|
BX.Landing.Utils.setTextContent = function(element, text, classForTextNode = '')
|
|
{
|
|
var addClass = BX.Landing.Utils.addClass;
|
|
if (typeof text === "string")
|
|
{
|
|
var firstNode = element.firstChild;
|
|
var textNode = document.createElement('div');
|
|
|
|
if (firstNode &&
|
|
firstNode === element.lastChild &&
|
|
firstNode.nodeType === Node.TEXT_NODE)
|
|
{
|
|
firstNode.nodeValue = text;
|
|
if (classForTextNode.length > 0)
|
|
{
|
|
addClass(textNode, classForTextNode);
|
|
textNode.textContent = element.innerText;
|
|
element.innerText = '';
|
|
BX.Dom.append(textNode, element);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
element.textContent = text;
|
|
if (classForTextNode.length > 0)
|
|
{
|
|
addClass(textNode, classForTextNode);
|
|
textNode.textContent = element.innerText;
|
|
element.innerText = '';
|
|
BX.Dom.append(textNode, element);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Encodes data attribute value
|
|
* @param {*} value
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.encodeDataValue = function(value)
|
|
{
|
|
if (BX.Landing.Utils.isPlainObject(value) || BX.Landing.Utils.isArray(value))
|
|
{
|
|
value = JSON.stringify(value);
|
|
}
|
|
else
|
|
{
|
|
if (BX.Landing.Utils.isString(value))
|
|
{
|
|
value = BX.Landing.Utils.escapeHtml(value);
|
|
}
|
|
}
|
|
|
|
return "" + value;
|
|
};
|
|
|
|
|
|
/**
|
|
* Decodes data attribute value
|
|
* @param {string} value
|
|
* @return {*}
|
|
*/
|
|
BX.Landing.Utils.decodeDataValue = function(value)
|
|
{
|
|
var result = value;
|
|
|
|
try
|
|
{
|
|
result = JSON.parse(value);
|
|
}
|
|
catch(e)
|
|
{
|
|
result = value;
|
|
}
|
|
|
|
if (BX.Landing.Utils.isString(result))
|
|
{
|
|
result = BX.util.htmlspecialcharsback(result);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
|
|
/**
|
|
* Works with data-attributes.
|
|
* @param {HTMLElement} element
|
|
* @param {string|object} [name]
|
|
* @param {*} [value]
|
|
* @return {*}
|
|
*/
|
|
BX.Landing.Utils.data = function(element, name, value)
|
|
{
|
|
var decodeDataValue = BX.Landing.Utils.decodeDataValue;
|
|
var encodeDataValue = BX.Landing.Utils.encodeDataValue;
|
|
var isPlainObject = BX.Landing.Utils.isPlainObject;
|
|
var isString = BX.Landing.Utils.isString;
|
|
var dataRegExp = new RegExp("^data-");
|
|
|
|
if (!element)
|
|
{
|
|
throw new TypeError("Element is required");
|
|
}
|
|
|
|
// Get all data attributes if name not set
|
|
if (!name)
|
|
{
|
|
var result = {};
|
|
|
|
[].forEach.call(element.attributes, function(attr) {
|
|
if (dataRegExp.test(attr.name))
|
|
{
|
|
result[attr.name] = decodeDataValue(attr.value);
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
if (isString(name))
|
|
{
|
|
name = !dataRegExp.test(name) ? "data-" + name : name;
|
|
|
|
// Get single value
|
|
if (value === undefined)
|
|
{
|
|
return decodeDataValue(element.getAttribute(name));
|
|
}
|
|
|
|
// Remove attribute
|
|
if (value === null)
|
|
{
|
|
return element.removeAttribute(name);
|
|
}
|
|
|
|
// Sets single value
|
|
return element.setAttribute(name, encodeDataValue(value));
|
|
}
|
|
|
|
if (isPlainObject(name))
|
|
{
|
|
// Sets attributes values from object
|
|
Object.keys(name).forEach(function(attr) {
|
|
BX.Landing.Utils.data(element, attr, name[attr]);
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
function getTextNodes(el)
|
|
{
|
|
el = el || document.body;
|
|
|
|
var doc = el.ownerDocument || document;
|
|
var walker = doc.createTreeWalker(el, NodeFilter.SHOW_TEXT, null, false);
|
|
var textNodes = [];
|
|
var node;
|
|
|
|
while (node = walker.nextNode())
|
|
{
|
|
textNodes.push(node)
|
|
}
|
|
|
|
return textNodes
|
|
}
|
|
|
|
function rangesIntersect(rangeA, rangeB)
|
|
{
|
|
return rangeA.compareBoundaryPoints(Range.END_TO_START, rangeB) === -1 &&
|
|
rangeA.compareBoundaryPoints(Range.START_TO_END, rangeB) === 1
|
|
}
|
|
|
|
function createRangeFromNode(node)
|
|
{
|
|
var range = node.ownerDocument.createRange();
|
|
|
|
try {
|
|
range.selectNode(node)
|
|
} catch (e) {
|
|
range.selectNodeContents(node)
|
|
}
|
|
|
|
return range
|
|
}
|
|
|
|
function rangeIntersectsNode(range, node)
|
|
{
|
|
if (range.intersectsNode)
|
|
{
|
|
return range.intersectsNode(node)
|
|
}
|
|
else
|
|
{
|
|
return rangesIntersect(range, createRangeFromNode(node))
|
|
}
|
|
}
|
|
|
|
function getRangeTextNodes(range)
|
|
{
|
|
var container = range.commonAncestorContainer;
|
|
var nodes = getTextNodes(container.parentNode || container);
|
|
|
|
return nodes.filter(function (node) {
|
|
return rangeIntersectsNode(range, node) && isNonEmptyTextNode(node);
|
|
})
|
|
}
|
|
|
|
function isNonEmptyTextNode(node)
|
|
{
|
|
return node.textContent.length > 0;
|
|
}
|
|
|
|
function remove(el)
|
|
{
|
|
if (el.parentNode)
|
|
{
|
|
el.parentNode.removeChild(el);
|
|
}
|
|
}
|
|
|
|
function replaceNode(replacementNode, node)
|
|
{
|
|
remove(replacementNode);
|
|
node.parentNode.insertBefore(replacementNode, node);
|
|
remove(node)
|
|
}
|
|
|
|
function unwrap(el)
|
|
{
|
|
var range = document.createRange();
|
|
range.selectNodeContents(el);
|
|
replaceNode(range.extractContents(), el);
|
|
}
|
|
|
|
function undo(nodes)
|
|
{
|
|
nodes.forEach(function (node) {
|
|
var parent = node.parentNode;
|
|
unwrap(node);
|
|
parent.normalize();
|
|
})
|
|
}
|
|
|
|
function createWrapperFunction(wrapperEl, range)
|
|
{
|
|
var startNode = range.startContainer;
|
|
var endNode = range.endContainer;
|
|
var startOffset = range.startOffset;
|
|
var endOffset = range.endOffset;
|
|
|
|
return function wrapNode(node)
|
|
{
|
|
var currentRange = document.createRange();
|
|
var currentWrapper = wrapperEl;
|
|
|
|
currentRange.selectNodeContents(node);
|
|
|
|
if (node === startNode && startNode.nodeType === 3)
|
|
{
|
|
currentRange.setStart(node, startOffset);
|
|
startNode = currentWrapper;
|
|
startOffset = 0;
|
|
}
|
|
|
|
if (node === endNode && endNode.nodeType === 3)
|
|
{
|
|
currentRange.setEnd(node, endOffset);
|
|
endNode = currentWrapper;
|
|
endOffset = 1;
|
|
}
|
|
|
|
currentRange.surroundContents(currentWrapper);
|
|
return currentWrapper;
|
|
}
|
|
}
|
|
|
|
BX.Landing.Utils.wrapSelection = function(wrapperEl, range)
|
|
{
|
|
var nodes;
|
|
var wrapNode;
|
|
var wrapperObj = {};
|
|
|
|
if (typeof range === 'undefined')
|
|
{
|
|
range = window.getSelection().getRangeAt(0);
|
|
}
|
|
|
|
if (range.isCollapsed)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
if (typeof wrapperEl === 'undefined')
|
|
{
|
|
wrapperEl = 'span';
|
|
}
|
|
|
|
if (typeof wrapperEl === 'string')
|
|
{
|
|
wrapperEl = document.createElement(wrapperEl);
|
|
}
|
|
|
|
wrapNode = createWrapperFunction(wrapperEl, range);
|
|
|
|
nodes = getRangeTextNodes(range);
|
|
nodes = nodes.map(wrapNode);
|
|
|
|
wrapperObj.nodes = nodes;
|
|
wrapperObj.unwrap = function() {
|
|
if (this.nodes.length)
|
|
{
|
|
undo(this.nodes);
|
|
this.nodes = [];
|
|
}
|
|
};
|
|
|
|
return wrapperObj;
|
|
};
|
|
|
|
|
|
BX.Landing.Utils.createRangeFromNode = createRangeFromNode;
|
|
|
|
|
|
/**
|
|
* Creates selection range for node, from start and end points
|
|
* @param {HTMLElement} el
|
|
* @param {int} start
|
|
* @param {int} end
|
|
* @return {Range}
|
|
*/
|
|
BX.Landing.Utils.createSelectionRange = function(el, start, end)
|
|
{
|
|
var range;
|
|
|
|
if (document.createRange && window.getSelection)
|
|
{
|
|
range = document.createRange();
|
|
range.selectNodeContents(el);
|
|
var textNodes = getTextNodes(el);
|
|
var foundStart = false;
|
|
var charCount = 0, endCharCount;
|
|
|
|
for (var i = 0, textNode; textNode = textNodes[i++]; )
|
|
{
|
|
endCharCount = charCount + textNode.length;
|
|
if (!foundStart && start >= charCount && (start < endCharCount || (start === endCharCount && i <= textNodes.length)))
|
|
{
|
|
range.setStart(textNode, start - charCount);
|
|
foundStart = true;
|
|
}
|
|
|
|
if (foundStart && end <= endCharCount)
|
|
{
|
|
range.setEnd(textNode, end - charCount);
|
|
break;
|
|
}
|
|
|
|
charCount = endCharCount;
|
|
}
|
|
}
|
|
else if (document.selection && document.body.createTextRange)
|
|
{
|
|
range = document.body.createTextRange();
|
|
range.moveToElementText(el);
|
|
range.collapse(true);
|
|
range.moveEnd("character", end);
|
|
range.moveStart("character", start);
|
|
}
|
|
|
|
return range;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets element styles
|
|
*
|
|
* @param {HTMLElement} element
|
|
* @param {?object} styles - Null removes all styles
|
|
*
|
|
* @return {Promise} - Resolves when styles applied
|
|
*/
|
|
BX.Landing.Utils.style = function(element, styles)
|
|
{
|
|
return new Promise(function(resolve) {
|
|
if (styles === null)
|
|
{
|
|
requestAnimationFrame(function() {
|
|
element.style = null;
|
|
resolve();
|
|
});
|
|
}
|
|
|
|
if (!!styles && typeof styles === "object")
|
|
{
|
|
requestAnimationFrame(function() {
|
|
Object.keys(styles).forEach(function(style) {
|
|
element.style.setProperty(style, styles[style]);
|
|
});
|
|
resolve();
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Translates element by y axis
|
|
* @param {HTMLElement} element
|
|
* @param {Number} translateLength
|
|
* @return {Promise}
|
|
*/
|
|
BX.Landing.Utils.translateY = function(element, translateLength)
|
|
{
|
|
return BX.Landing.Utils.translate("y", element, translateLength);
|
|
};
|
|
|
|
|
|
/**
|
|
* Translates element by x axis
|
|
* @param {HTMLElement} element
|
|
* @param {Number} translateLength
|
|
* @return {Promise}
|
|
*/
|
|
BX.Landing.Utils.translateX = function(element, translateLength)
|
|
{
|
|
return BX.Landing.Utils.translate("x", element, translateLength);
|
|
};
|
|
|
|
|
|
/**
|
|
* Translates element by axis
|
|
* @param {string} axis - x|y
|
|
* @param {HTMLElement} element
|
|
* @param {Number} translateLength
|
|
* @return {Promise}
|
|
*/
|
|
BX.Landing.Utils.translate = function(axis, element, translateLength)
|
|
{
|
|
void BX.Landing.Utils.style(element, {
|
|
"transition": "transform 200ms ease",
|
|
"transform": "translate"+axis.toUpperCase()+"("+translateLength+"px) translateZ(0)"
|
|
});
|
|
return BX.Landing.Utils.onTransitionEnd(element);
|
|
};
|
|
|
|
|
|
/**
|
|
* Inserts element before target element
|
|
* @param {HTMLElement} element
|
|
* @param {HTMLElement} targetElement
|
|
*/
|
|
BX.Landing.Utils.insertBefore = function(element, targetElement)
|
|
{
|
|
targetElement.parentElement.insertBefore(element, targetElement);
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets bounding client rect of element or range
|
|
* @param {Element|Range} element
|
|
* @return {ClientRect | {left, top, width, height}}
|
|
*/
|
|
BX.Landing.Utils.rect = function(element)
|
|
{
|
|
return element.getBoundingClientRect();
|
|
};
|
|
|
|
|
|
/**
|
|
* Finds next element sibling
|
|
* @param {HTMLElement} element
|
|
* @param {String} [className]
|
|
* @return {HTMLElement}
|
|
*/
|
|
BX.Landing.Utils.nextSibling = function(element, className)
|
|
{
|
|
return className ? BX.findNextSibling(element, {className: className}) : element.nextElementSibling;
|
|
};
|
|
|
|
|
|
/**
|
|
* Finds previous element sibling
|
|
* @param {HTMLElement} element
|
|
* @param {String} className
|
|
* @return {HTMLElement}
|
|
*/
|
|
BX.Landing.Utils.prevSibling = function(element, className)
|
|
{
|
|
return className ? BX.findPreviousSibling(element, {className: className}) : element.previousElementSibling;
|
|
};
|
|
|
|
|
|
/**
|
|
* Joins arguments
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.join = function()
|
|
{
|
|
return [].slice.call(arguments).join("");
|
|
};
|
|
|
|
|
|
/**
|
|
* @param item
|
|
* @return {*[]}
|
|
*/
|
|
BX.Landing.Utils.slice = function(item)
|
|
{
|
|
return [].slice.call(item);
|
|
};
|
|
|
|
|
|
/**
|
|
* Set element attributes
|
|
* @param {HTMLElement|Element} element
|
|
* @param {Object.<String, ?String>|String} attrs
|
|
* @param {?String} [value]
|
|
* @return {String|void}
|
|
*/
|
|
BX.Landing.Utils.attr = function(element, attrs, value)
|
|
{
|
|
if (BX.Landing.Utils.isString(attrs))
|
|
{
|
|
if (typeof value === "undefined")
|
|
{
|
|
return element.getAttribute(attrs);
|
|
}
|
|
|
|
element.setAttribute(attrs, BX.Landing.Utils.encodeDataValue(value));
|
|
}
|
|
|
|
if (BX.Landing.Utils.isPlainObject(attrs))
|
|
{
|
|
Object.keys(attrs).forEach(function(key) {
|
|
if (attrs[key] === null)
|
|
{
|
|
element.removeAttribute(key);
|
|
}
|
|
else
|
|
{
|
|
element.setAttribute(key, BX.Landing.Utils.encodeDataValue(attrs[key]));
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Removes all panels from element
|
|
* @param {HTMLElement} element
|
|
* @return {?HTMLElement}
|
|
*/
|
|
BX.Landing.Utils.removePanels = function(element)
|
|
{
|
|
[].slice.call(element.querySelectorAll(".landing-ui-panel"))
|
|
.forEach(function(panel) {
|
|
BX.remove(panel);
|
|
});
|
|
return element;
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets file extension
|
|
* @param {string} filename
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.getFileExtension = function(filename)
|
|
{
|
|
var name = "fm";
|
|
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)");
|
|
var results = regex.exec(filename);
|
|
|
|
if (!results || !results[2])
|
|
{
|
|
return '';
|
|
}
|
|
|
|
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks pressed key
|
|
* @type {{
|
|
* isUp: BX.Landing.Utils.key.isUp,
|
|
* isDown: BX.Landing.Utils.key.isDown,
|
|
* isRight: BX.Landing.Utils.key.isRight,
|
|
* isLeft: BX.Landing.Utils.key.isLeft,
|
|
* isEnter: BX.Landing.Utils.key.isEnter,
|
|
* isEscape: BX.Landing.Utils.key.isEscape
|
|
* }}
|
|
*/
|
|
BX.Landing.Utils.key = {
|
|
isUp: function(event) {
|
|
return event.keyCode === 38;
|
|
},
|
|
|
|
isDown: function(event) {
|
|
return event.keyCode === 40;
|
|
},
|
|
|
|
isRight: function(event) {
|
|
return event.keyCode === 39;
|
|
},
|
|
|
|
isLeft: function(event) {
|
|
return event.keyCode === 37;
|
|
},
|
|
|
|
isEnter: function(event) {
|
|
return event.keyCode === 13;
|
|
},
|
|
|
|
isEscape: function(event)
|
|
{
|
|
return event.keyCode === 27;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Extends BX.PopupMenuWindow. Adds menu items filter/search
|
|
* @param {BX.PopupMenuWindow} menu
|
|
*/
|
|
BX.Landing.Utils.makeFilterablePopupMenu = function(menu)
|
|
{
|
|
var append = BX.Landing.Utils.append;
|
|
var prepend = BX.Landing.Utils.prepend;
|
|
var onCustomEvent = BX.Landing.Utils.onCustomEvent;
|
|
var create = BX.Landing.Utils.create;
|
|
var addClass = BX.Landing.Utils.addClass;
|
|
|
|
|
|
/**
|
|
* Checks that menuItems list contains visible item
|
|
* @param {BX.PopupMenuItem[]} menuItems
|
|
* @return {boolean}
|
|
*/
|
|
function containsVisibleItems(menuItems)
|
|
{
|
|
return menuItems.some(function(menuItem) {
|
|
return !menuItem.layout.item.hidden;
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles filter input event
|
|
* @param event
|
|
*/
|
|
function onInput(event)
|
|
{
|
|
var search = event.currentTarget.value.toLowerCase();
|
|
menu.menuItems.forEach(function(item) {
|
|
item.layout.item.hidden = !item.text.toLowerCase().includes(search);
|
|
});
|
|
|
|
emptyResult.hidden = containsVisibleItems(menu.menuItems);
|
|
}
|
|
|
|
var filter = create("div", {props: {className: "landing-ui-popup-filter"}});
|
|
var filterInput = create("input", {
|
|
props: {className: "landing-ui-popup-filter-input"},
|
|
attrs: {placeholder: BX.Landing.Loc.getMessage("LANDING_MENU_ITEM_FILTER")},
|
|
events: {"input": onInput}
|
|
});
|
|
var emptyResult = create("div", {
|
|
props: {className: "landing-ui-popup-filter-empty"},
|
|
children: [create("span", {
|
|
props: {className: "landing-ui-popup-filter-empty-text"},
|
|
html: BX.Landing.Loc.getMessage("LANDING_MENU_ITEM_FILTER_EMPTY")
|
|
})],
|
|
attrs: {hidden: true}
|
|
});
|
|
|
|
append(filterInput, filter);
|
|
prepend(filter, menu.popupWindow.contentContainer);
|
|
append(emptyResult, menu.popupWindow.contentContainer);
|
|
|
|
addClass(menu.popupWindow.popupContainer, "landing-ui-popup-filterable");
|
|
|
|
filterInput.focus();
|
|
|
|
onCustomEvent(menu.popupWindow, "onAfterPopupShow", function() {
|
|
requestAnimationFrame(function() {
|
|
filterInput.focus()
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Extends BX.PopupMenuWindow. Makes navigation by arrows keys
|
|
* @param {BX.PopupMenuWindow} menu
|
|
*/
|
|
BX.Landing.Utils.makeSelectablePopupMenu = function(menu)
|
|
{
|
|
var addClass = BX.Landing.Utils.addClass;
|
|
var removeClass = BX.Landing.Utils.removeClass;
|
|
var hasClass = BX.Landing.Utils.hasClass;
|
|
var bind = BX.Landing.Utils.bind;
|
|
var key = BX.Landing.Utils.key;
|
|
|
|
var currentItem = null;
|
|
var currentIndex = -1;
|
|
|
|
/**
|
|
* Selects menu items
|
|
* @param {BX.PopupMenuItem} menuItem
|
|
*/
|
|
function selectItem(menuItem)
|
|
{
|
|
addClass(menuItem.layout.item, "landing-ui-select");
|
|
}
|
|
|
|
|
|
/**
|
|
* Removes selection from menu item
|
|
* @param {BX.PopupMenuItem} menuItem
|
|
*/
|
|
function unselectMenuItem(menuItem)
|
|
{
|
|
removeClass(menuItem.layout.item, "landing-ui-select");
|
|
}
|
|
|
|
|
|
/**
|
|
* Removes selection from all menu items
|
|
* @param {BX.PopupMenuItem[]} menuItems
|
|
*/
|
|
function unselectItems(menuItems)
|
|
{
|
|
menuItems.forEach(unselectMenuItem);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get selected item
|
|
* @param {BX.PopupMenuItem[]} menuItems
|
|
*/
|
|
function getSelected(menuItems)
|
|
{
|
|
return menuItems.find(function(item) {
|
|
return hasClass(item.layout.item, "landing-ui-select");
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets menu item index
|
|
* @param {BX.PopupMenuItem[]} menuItems
|
|
* @param {BX.PopupMenuItem} menuItem
|
|
* @return {number|*}
|
|
*/
|
|
function getItemIndex(menuItems, menuItem)
|
|
{
|
|
return menuItems.findIndex(function(item) {
|
|
return menuItem === item;
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets first item
|
|
* @param {BX.PopupMenuItem[]} menuItems
|
|
* @return {?BX.PopupMenuItem}
|
|
*/
|
|
function getFirstItem(menuItems)
|
|
{
|
|
return menuItems.find(function(item) {
|
|
return !item.layout.item.hidden;
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets net item
|
|
* @param {BX.PopupMenuItem[]} menuItems
|
|
* @return {BX.PopupMenuItem}
|
|
*/
|
|
function getNextItem(menuItems)
|
|
{
|
|
if (currentItem)
|
|
{
|
|
currentIndex = getItemIndex(menuItems, currentItem);
|
|
}
|
|
|
|
var nextItem = menuItems.find(function(item, index) {
|
|
return index > currentIndex && !item.layout.item.hidden;
|
|
});
|
|
|
|
if (nextItem)
|
|
{
|
|
currentItem = nextItem;
|
|
return nextItem;
|
|
}
|
|
|
|
nextItem = getFirstItem(menuItems);
|
|
currentItem = nextItem;
|
|
|
|
return nextItem;
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks that pressed key from allowed list
|
|
* @param {KeyboardEvent} event
|
|
* @return {boolean}
|
|
*/
|
|
function isAllowedKeyPress(event)
|
|
{
|
|
var key = BX.Landing.Utils.key;
|
|
|
|
return (
|
|
key.isLeft(event) ||
|
|
key.isRight(event) ||
|
|
key.isUp(event) ||
|
|
key.isDown(event) ||
|
|
key.isEnter(event)
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Closes all sub menu
|
|
* @param {BX.PopupMenuItem[]} menuItems
|
|
*/
|
|
function closeAllSubMenu(menuItems)
|
|
{
|
|
menuItems.forEach(function(item) {
|
|
item.closeSubMenu();
|
|
|
|
var subMenu = item.getSubMenu();
|
|
|
|
if (subMenu)
|
|
{
|
|
unselectItems(subMenu.menuItems);
|
|
}
|
|
});
|
|
}
|
|
|
|
var isRevert = false;
|
|
|
|
bind(menu.popupWindow.popupContainer, "keydown", function(event) {
|
|
var currentMenu = menu;
|
|
if (currentItem && currentItem.menuWindow.popupWindow.isShown())
|
|
{
|
|
currentMenu = currentItem.menuWindow;
|
|
}
|
|
|
|
if (isAllowedKeyPress(event))
|
|
{
|
|
var selectedItem = getSelected(currentMenu.menuItems);
|
|
|
|
if (key.isDown(event) && isRevert && currentMenu === menu)
|
|
{
|
|
isRevert = false;
|
|
currentMenu.menuItems = currentMenu.menuItems.reverse();
|
|
}
|
|
|
|
if (key.isUp(event) && !isRevert && currentMenu === menu)
|
|
{
|
|
isRevert = true;
|
|
currentMenu.menuItems = currentMenu.menuItems.reverse();
|
|
}
|
|
|
|
if (key.isRight(event))
|
|
{
|
|
if (selectedItem)
|
|
{
|
|
selectedItem.showSubMenu();
|
|
|
|
if (selectedItem.hasSubMenu())
|
|
{
|
|
var submenu = selectedItem.getSubMenu();
|
|
unselectItems(submenu.menuItems);
|
|
selectItem(submenu.menuItems[0]);
|
|
currentItem = submenu.menuItems[0];
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (key.isLeft(event))
|
|
{
|
|
closeAllSubMenu(menu.menuItems);
|
|
currentItem = getSelected(menu.menuItems);
|
|
return;
|
|
}
|
|
|
|
if (key.isEnter(event))
|
|
{
|
|
if (selectedItem)
|
|
{
|
|
BX.fireEvent(selectedItem.layout.item, "click");
|
|
return;
|
|
}
|
|
}
|
|
|
|
unselectItems(currentMenu.menuItems);
|
|
|
|
var nextItem = getNextItem(currentMenu.menuItems);
|
|
|
|
if (nextItem)
|
|
{
|
|
selectItem(nextItem);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (key.isEscape(event))
|
|
{
|
|
currentMenu.close();
|
|
}
|
|
|
|
closeAllSubMenu(menu.menuItems);
|
|
});
|
|
};
|
|
|
|
|
|
BX.Landing.Utils.delay = function(delay, data)
|
|
{
|
|
return new Promise(function(resolve) {
|
|
setTimeout(resolve.bind(null, data), delay);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Highlights node
|
|
* @param {HTMLElement} node
|
|
* @param {?boolean} [useRangeRect = false]
|
|
* @param {?boolean} [highlightBottom = false]
|
|
* @return {Promise}
|
|
*/
|
|
BX.Landing.Utils.highlight = function(node, useRangeRect, highlightBottom)
|
|
{
|
|
var rect;
|
|
|
|
if (useRangeRect)
|
|
{
|
|
var range = document.createRange();
|
|
range.selectNodeContents(node);
|
|
rect = range.getBoundingClientRect();
|
|
}
|
|
else
|
|
{
|
|
rect = node.getBoundingClientRect();
|
|
}
|
|
|
|
if (highlightBottom)
|
|
{
|
|
rect = {
|
|
top: rect.bottom,
|
|
left: rect.left,
|
|
right: rect.right,
|
|
bottom: rect.bottom+1,
|
|
height: 20,
|
|
width: rect.width
|
|
};
|
|
}
|
|
|
|
return BX.Landing.History.Highlight.getInstance().show(node, rect);
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Scrolls to node
|
|
* @param {HTMLElement} node
|
|
* @return {Promise}
|
|
*/
|
|
BX.Landing.Utils.scrollTo = function(node)
|
|
{
|
|
return BX.Landing.PageObject.getInstance().view().then(function(iframe) {
|
|
return BX.Landing.UI.Panel.Content.scrollTo(iframe, node)
|
|
.then(function() {
|
|
return new Promise(function(resolve) {
|
|
setTimeout(resolve, 50);
|
|
})
|
|
})
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* @todo refactoring
|
|
* @param element
|
|
* @param offsetParent
|
|
* @return {number}
|
|
*/
|
|
BX.Landing.Utils.offsetTop = function(element, offsetParent)
|
|
{
|
|
var elementRect = element.getBoundingClientRect();
|
|
var parentRect = offsetParent.getBoundingClientRect();
|
|
var scrollTop = offsetParent.scrollTop;
|
|
var borderTopWidth = parseInt(BX.style(offsetParent, "border-top-width"));
|
|
borderTopWidth = borderTopWidth === borderTopWidth ? borderTopWidth : 0;
|
|
|
|
return (elementRect.top + scrollTop) - parentRect.top - borderTopWidth;
|
|
};
|
|
|
|
|
|
/**
|
|
* @todo refactoring
|
|
* @param element
|
|
* @param offsetParent
|
|
* @return {number}
|
|
*/
|
|
BX.Landing.Utils.offsetLeft = function(element, offsetParent)
|
|
{
|
|
var elementRect = element.getBoundingClientRect();
|
|
var parentRect = offsetParent.getBoundingClientRect();
|
|
var scrollLeft = offsetParent.scrollLeft;
|
|
|
|
return (elementRect.left + scrollLeft) - parentRect.left;
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks that value is array-like object
|
|
* @param value
|
|
* @return {boolean}
|
|
*/
|
|
BX.Landing.Utils.isArrayLike = function(value)
|
|
{
|
|
var isBoolean = BX.Landing.Utils.isBoolean;
|
|
var isNumber = BX.Landing.Utils.isNumber;
|
|
var isFunction = BX.Landing.Utils.isFunction;
|
|
|
|
return (
|
|
value !== null &&
|
|
!isFunction(value) &&
|
|
!isBoolean(value) &&
|
|
!isNumber(value) &&
|
|
value.length > 0 &&
|
|
value.length <= Number.MAX_SAFE_INTEGER
|
|
);
|
|
};
|
|
|
|
|
|
/**
|
|
* Check that value is arguments
|
|
* @param value
|
|
* @return {boolean}
|
|
*/
|
|
BX.Landing.Utils.isArguments = function(value)
|
|
{
|
|
var isArrayLike = BX.Landing.Utils.isArrayLike;
|
|
return isArrayLike(value) && value.toString() === "[object Arguments]";
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks that value is empty
|
|
* @param value
|
|
* @return {boolean}
|
|
*/
|
|
BX.Landing.Utils.isEmpty = function(value)
|
|
{
|
|
var isArrayLike = BX.Landing.Utils.isArrayLike;
|
|
|
|
if (value == null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (isArrayLike(value))
|
|
{
|
|
return !value.length;
|
|
}
|
|
|
|
for (var key in value)
|
|
{
|
|
if (value.hasOwnProperty(key))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets random integer
|
|
* @param {int} min
|
|
* @param {int} max
|
|
* @return {int}
|
|
*/
|
|
BX.Landing.Utils.randomInt = function(min, max)
|
|
{
|
|
max += 1;
|
|
return Math.floor(Math.random() * (max - min)) + min;
|
|
};
|
|
|
|
|
|
/**
|
|
* Produces an array that contains every
|
|
* item shared between all the passed-in arrays.
|
|
* @param {...}
|
|
* @return {*[]}
|
|
*/
|
|
BX.Landing.Utils.intersection = function()
|
|
{
|
|
var slice = BX.Landing.Utils.slice;
|
|
|
|
return slice(arguments).reduce(function(previous, current){
|
|
return previous.filter(function(element){
|
|
return current.includes(element);
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Takes the difference between one array
|
|
* and a number of other arrays.
|
|
* @param {...}
|
|
* @return {*[]}
|
|
*/
|
|
BX.Landing.Utils.difference = function()
|
|
{
|
|
var slice = BX.Landing.Utils.slice;
|
|
|
|
return slice(arguments).reduce(function(previous, current){
|
|
return previous.filter(function(element){
|
|
return !current.includes(element);
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
BX.Landing.Utils.changeTagName = function(element, tagName)
|
|
{
|
|
if (!element || !tagName)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var slice = BX.Landing.Utils.slice;
|
|
var create = BX.Landing.Utils.create;
|
|
var attributes = slice(element.attributes);
|
|
var newElement = create(tagName);
|
|
var innerHTML = element.innerHTML;
|
|
|
|
attributes.forEach(function(attribute) {
|
|
newElement.setAttribute(attribute.nodeName, attribute.nodeValue);
|
|
});
|
|
|
|
newElement.innerHTML = innerHTML;
|
|
|
|
element.parentElement.replaceChild(newElement, element);
|
|
|
|
return newElement;
|
|
};
|
|
|
|
|
|
/**
|
|
* Creates hash from array like objects
|
|
* @param value
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.hash = function(value)
|
|
{
|
|
if (BX.Landing.Utils.isArray(value) || BX.Landing.Utils.isPlainObject(value))
|
|
{
|
|
value = JSON.stringify(BX.Landing.Utils.sortObject(value));
|
|
}
|
|
|
|
return "" + BX.util.hashCode(value);
|
|
};
|
|
|
|
|
|
/**
|
|
* Sort object by keys
|
|
* @param unordered
|
|
* @return {{}}
|
|
*/
|
|
BX.Landing.Utils.sortObject = function(unordered)
|
|
{
|
|
return Object.keys(unordered).sort().reduce(function(ordered, key) {
|
|
return ordered[key] = unordered[key], ordered;
|
|
}, {});
|
|
};
|
|
|
|
|
|
/**
|
|
* Capitalizes string
|
|
* @param {string} str
|
|
* @return {string}
|
|
*/
|
|
BX.Landing.Utils.capitalize = function(str)
|
|
{
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
};
|
|
|
|
BX.Landing.Utils.textToPlaceholders = function(str)
|
|
{
|
|
var matcher = new RegExp("<span[^>]*data-placeholder=\"(\\w+)\"[^>]*>(.+?)<\\/span>", 'gm');
|
|
var segments = matcher.exec(str);
|
|
|
|
if (segments)
|
|
{
|
|
return str.replace(matcher, "{{"+segments[1]+"}}");
|
|
}
|
|
|
|
return str;
|
|
};
|
|
|
|
/**
|
|
* Changes file path extension
|
|
* @param {string} path
|
|
* @param {string} newExtension
|
|
* @return {*}
|
|
*/
|
|
BX.Landing.Utils.changeExtension = function(path, newExtension)
|
|
{
|
|
return !!path ? path.replace(/\.[^\.]+$/, "." + newExtension) : path;
|
|
};
|
|
|
|
/**
|
|
* @param path
|
|
* @return {*}
|
|
*/
|
|
BX.Landing.Utils.rename2x = function(path)
|
|
{
|
|
path = path.replace(/@2x/, "");
|
|
let extension = BX.util.getExtension(path);
|
|
if (extension.length > 4)
|
|
{
|
|
extension = extension.split('_').pop();
|
|
}
|
|
|
|
return !!path ? path.replace(/\.[^\.]+$/, "@2x." + extension) : path;
|
|
};
|
|
|
|
/**
|
|
* Gets delta from event
|
|
* @param event
|
|
* @return {{x, y: number}}
|
|
*/
|
|
BX.Landing.Utils.getDeltaFromEvent = function(event)
|
|
{
|
|
var deltaX = event.deltaX;
|
|
var deltaY = -1 * event.deltaY;
|
|
|
|
if (typeof deltaX === "undefined" || typeof deltaY === "undefined")
|
|
{
|
|
deltaX = -1 * event.wheelDeltaX / 6;
|
|
deltaY = event.wheelDeltaY / 6;
|
|
}
|
|
|
|
if (event.deltaMode && event.deltaMode === 1)
|
|
{
|
|
deltaX *= 10;
|
|
deltaY *= 10;
|
|
}
|
|
|
|
if (event.deltaMode && event.deltaMode === 1)
|
|
{
|
|
deltaX *= 10;
|
|
deltaY *= 10;
|
|
}
|
|
|
|
/** NaN checks */
|
|
if (deltaX !== deltaX && deltaY !== deltaY)
|
|
{
|
|
deltaX = 0;
|
|
deltaY = event.wheelDelta;
|
|
}
|
|
|
|
return {x: deltaX, y: deltaY};
|
|
};
|
|
|
|
/**
|
|
* Loads file as blob
|
|
* @param url
|
|
* @return {Promise<Blob, string>}
|
|
*/
|
|
BX.Landing.Utils.urlToBlob = function(url)
|
|
{
|
|
if (!BX.type.isString(url))
|
|
{
|
|
return Promise.resolve(url);
|
|
}
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
try {
|
|
var xhr = BX.ajax.xhr();
|
|
xhr.open("GET", url);
|
|
xhr.responseType = "blob";
|
|
xhr.onerror = function()
|
|
{
|
|
reject("Network error.")
|
|
};
|
|
xhr.onload = function()
|
|
{
|
|
if (xhr.status === 200)
|
|
{
|
|
resolve(xhr.response);
|
|
}
|
|
else
|
|
{
|
|
reject("Loading error:" + xhr.statusText);
|
|
}
|
|
};
|
|
xhr.send();
|
|
}
|
|
catch(err)
|
|
{
|
|
reject(err.message);
|
|
}
|
|
});
|
|
};
|
|
|
|
BX.Landing.Utils.getFileName = function(path)
|
|
{
|
|
return path.split('\\').pop().split('/').pop();
|
|
};
|
|
|
|
BX.Landing.Utils.getSelectedElement = function(contextDocument)
|
|
{
|
|
const currentDocument =
|
|
(typeof contextDocument !== 'undefined' && contextDocument.nodeType === Node.DOCUMENT_NODE)
|
|
? contextDocument
|
|
: document
|
|
;
|
|
|
|
let range;
|
|
let sel;
|
|
let container;
|
|
if (currentDocument.selection)
|
|
{
|
|
range = currentDocument.selection.createRange();
|
|
return range.parentElement();
|
|
}
|
|
else
|
|
{
|
|
sel = currentDocument.defaultView.getSelection();
|
|
if (sel.getRangeAt)
|
|
{
|
|
if (sel.rangeCount > 0)
|
|
{
|
|
range = sel.getRangeAt(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Old WebKit
|
|
range = currentDocument.createRange();
|
|
range.setStart(sel.anchorNode, sel.anchorOffset);
|
|
range.setEnd(sel.focusNode, sel.focusOffset);
|
|
|
|
if (range.collapsed !== sel.isCollapsed) {
|
|
range.setStart(sel.focusNode, sel.focusOffset);
|
|
range.setEnd(sel.anchorNode, sel.anchorOffset);
|
|
}
|
|
}
|
|
|
|
if (range)
|
|
{
|
|
container = range["endContainer"];
|
|
|
|
return container.nodeType === 3 ? container.parentNode : container;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fires custom event
|
|
* @param {Object} [target = window]
|
|
* @param {string} eventName
|
|
* @param {*[]} [params]
|
|
*/
|
|
BX.Landing.Utils.fireCustomEvent = function(target, eventName, params)
|
|
{
|
|
try
|
|
{
|
|
BX.onCustomEvent(target, eventName, params);
|
|
}
|
|
catch (err)
|
|
{
|
|
console.error(eventName, err);
|
|
}
|
|
};
|
|
|
|
BX.Landing.Utils.onCustomEvent = BX.addCustomEvent;
|
|
BX.Landing.Utils.removeCustomEvent = BX.removeCustomEvent;
|
|
|
|
|
|
BX.Landing.Utils.insertAfter = BX.insertAfter;
|
|
BX.Landing.Utils.isPlainObject = BX.type.isPlainObject;
|
|
BX.Landing.Utils.append = BX.append;
|
|
BX.Landing.Utils.prepend = BX.prepend;
|
|
BX.Landing.Utils.isBoolean = BX.type.isBoolean;
|
|
BX.Landing.Utils.isNumber = BX.type.isNumber;
|
|
BX.Landing.Utils.isString = BX.type.isString;
|
|
BX.Landing.Utils.isArray = BX.type.isArray;
|
|
BX.Landing.Utils.isFunction = BX.type.isFunction;
|
|
BX.Landing.Utils.addClass = BX.addClass;
|
|
BX.Landing.Utils.removeClass = BX.removeClass;
|
|
BX.Landing.Utils.toggleClass = BX.toggleClass;
|
|
BX.Landing.Utils.hasClass = BX.hasClass;
|
|
BX.Landing.Utils.debounce = BX.debounce;
|
|
BX.Landing.Utils.throttle = BX.throttle;
|
|
BX.Landing.Utils.bind = BX.bind;
|
|
BX.Landing.Utils.unbind = BX.unbind;
|
|
BX.Landing.Utils.getClass = BX.getClass;
|
|
BX.Landing.Utils.pos = BX.pos;
|
|
BX.Landing.Utils.assign = Object.assign || BX.util.objectMerge;
|
|
BX.Landing.Utils.clone = BX.clone;
|
|
BX.Landing.Utils.create = BX.create;
|
|
BX.Landing.Utils.remove = BX.remove;
|
|
BX.Landing.Utils.trim = BX.util.trim;
|
|
BX.Landing.Utils.random = BX.util.getRandomString;
|
|
BX.Landing.Utils.findParent = BX.findParent;
|
|
BX.Landing.Utils.proxy = BX.proxy;
|
|
BX.Landing.Utils.arrayUnique = BX.util.array_unique;
|
|
BX.Landing.Utils.keys = Object.keys;
|
|
BX.Landing.Utils.fireEvent = BX.fireEvent;
|
|
BX.Landing.Utils.addQueryParams = BX.util.add_url_param.bind(BX.util);
|
|
})(); |