jQuery(document).ready(function($) {
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(["jquery"], function(a0) {
return (factory(a0));
});
} else if (typeof exports === 'object') {
module.exports = factory(require("jquery"));
} else {
factory(jQuery);
}
}(this, function($) {
// This file will be UMDified by a build task.
var defaults = {
animation: 'fade',
animationDuration: 350,
content: null,
contentAsHTML: false,
contentCloning: false,
debug: true,
delay: 300,
delayTouch: [300, 500],
functionInit: null,
functionBefore: null,
functionReady: null,
functionAfter: null,
functionFormat: null,
IEmin: 6,
interactive: false,
multiple: false,
// will default to document.body, or must be an element positioned at (0, 0)
// in the document, typically like the very top views of an app.
parent: null,
plugins: ['sideTip'],
repositionOnScroll: false,
restoration: 'none',
selfDestruction: true,
theme: [],
timer: 0,
trackerInterval: 500,
trackOrigin: false,
trackTooltip: false,
trigger: 'hover',
triggerClose: {
click: false,
mouseleave: false,
originClick: false,
scroll: false,
tap: false,
touchleave: false
},
triggerOpen: {
click: false,
mouseenter: false,
tap: false,
touchstart: false
},
updateAnimation: 'rotate',
zIndex: 9999999
},
// we'll avoid using the 'window' global as a good practice but npm's
// jquery@<2.1.0 package actually requires a 'window' global, so not sure
// it's useful at all
win = (typeof window != 'undefined') ? window : null,
// env will be proxied by the core for plugins to have access its properties
env = {
// detect if this device can trigger touch events. Better have a false
// positive (unused listeners, that's ok) than a false negative.
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
// http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
hasTouchCapability: !!(
win &&
('ontouchstart' in win ||
(win.DocumentTouch && win.document instanceof win.DocumentTouch) ||
win.navigator.maxTouchPoints
)
),
hasTransitions: transitionSupport(),
IE: false,
// don't set manually, it will be updated by a build task after the manifest
semVer: '4.2.8',
window: win
},
core = function() {
// core variables
// the core emitters
this.__$emitterPrivate = $({});
this.__$emitterPublic = $({});
this.__instancesLatestArr = [];
// collects plugin constructors
this.__plugins = {};
// proxy env variables for plugins who might use them
this._env = env;
};
// core methods
core.prototype = {
/**
* A function to proxy the public methods of an object onto another
*
* @param {object} constructor The constructor to bridge
* @param {object} obj The object that will get new methods (an instance or the core)
* @param {string} pluginName A plugin name for the console log message
* @return {core}
* @private
*/
__bridge: function(constructor, obj, pluginName) {
// if it's not already bridged
if (!obj[pluginName]) {
var fn = function() {};
fn.prototype = constructor;
var pluginInstance = new fn();
// the _init method has to exist in instance constructors but might be missing
// in core constructors
if (pluginInstance.__init) {
pluginInstance.__init(obj);
}
$.each(constructor, function(methodName, fn) {
// don't proxy "private" methods, only "protected" and public ones
if (methodName.indexOf('__') != 0) {
// if the method does not exist yet
if (!obj[methodName]) {
obj[methodName] = function() {
return pluginInstance[methodName].apply(pluginInstance, Array.prototype.slice.apply(arguments));
};
// remember to which plugin this method corresponds (several plugins may
// have methods of the same name, we need to be sure)
obj[methodName].bridged = pluginInstance;
} else if (defaults.debug) {}
}
});
obj[pluginName] = pluginInstance;
}
return this;
},
/**
* For mockup in Node env if need be, for testing purposes
*
* @return {core}
* @private
*/
__setWindow: function(window) {
env.window = window;
return this;
},
/**
* Returns a ruler, a tool to help measure the size of a tooltip under
* various settings. Meant for plugins
*
* @see Ruler
* @return {object} A Ruler instance
* @protected
*/
_getRuler: function($tooltip) {
return new Ruler($tooltip);
},
/**
* For internal use by plugins, if needed
*
* @return {core}
* @protected
*/
_off: function() {
this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
return this;
},
/**
* For internal use by plugins, if needed
*
* @return {core}
* @protected
*/
_on: function() {
this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
return this;
},
/**
* For internal use by plugins, if needed
*
* @return {core}
* @protected
*/
_one: function() {
this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
return this;
},
/**
* Returns (getter) or adds (setter) a plugin
*
* @param {string|object} plugin Provide a string (in the full form
* "namespace.name") to use as as getter, an object to use as a setter
* @return {object|core}
* @protected
*/
_plugin: function(plugin) {
var self = this;
// getter
if (typeof plugin == 'string') {
var pluginName = plugin,
p = null;
// if the namespace is provided, it's easy to search
if (pluginName.indexOf('.') > 0) {
p = self.__plugins[pluginName];
}
// otherwise, return the first name that matches
else {
$.each(self.__plugins, function(i, plugin) {
if (plugin.name.substring(plugin.name.length - pluginName.length - 1) == '.' + pluginName) {
p = plugin;
return false;
}
});
}
return p;
}
// setter
else {
// force namespaces
if (plugin.name.indexOf('.') < 0) {
throw new Error('Plugins must be namespaced');
}
self.__plugins[plugin.name] = plugin;
// if the plugin has core features
if (plugin.core) {
// bridge non-private methods onto the core to allow new core methods
self.__bridge(plugin.core, self, plugin.name);
}
return this;
}
},
/**
* Trigger events on the core emitters
*
* @returns {core}
* @protected
*/
_trigger: function() {
var args = Array.prototype.slice.apply(arguments);
if (typeof args[0] == 'string') {
args[0] = {
type: args[0]
};
}
// note: the order of emitters matters
this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
return this;
},
/**
* Returns instances of all tooltips in the page or an a given element
*
* @param {string|HTML object collection} selector optional Use this
* parameter to restrict the set of objects that will be inspected
* for the retrieval of instances. By default, all instances in the
* page are returned.
* @return {array} An array of instance objects
* @public
*/
instances: function(selector) {
var instances = [],
sel = selector || '.tooltipstered';
$(sel).each(function() {
var $this = $(this),
ns = $this.data('tooltipster-ns');
if (ns) {
$.each(ns, function(i, namespace) {
instances.push($this.data(namespace));
});
}
});
return instances;
},
/**
* Returns the Tooltipster objects generated by the last initializing call
*
* @return {array} An array of instance objects
* @public
*/
instancesLatest: function() {
return this.__instancesLatestArr;
},
/**
* For public use only, not to be used by plugins (use ::_off() instead)
*
* @return {core}
* @public
*/
off: function() {
this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
return this;
},
/**
* For public use only, not to be used by plugins (use ::_on() instead)
*
* @return {core}
* @public
*/
on: function() {
this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
return this;
},
/**
* For public use only, not to be used by plugins (use ::_one() instead)
*
* @return {core}
* @public
*/
one: function() {
this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
return this;
},
/**
* Returns all HTML elements which have one or more tooltips
*
* @param {string} selector optional Use this to restrict the results
* to the descendants of an element
* @return {array} An array of HTML elements
* @public
*/
origins: function(selector) {
var sel = selector ?
selector + ' ' :
'';
return $(sel + '.tooltipstered').toArray();
},
/**
* Change default options for all future instances
*
* @param {object} d The options that should be made defaults
* @return {core}
* @public
*/
setDefaults: function(d) {
$.extend(defaults, d);
return this;
},
/**
* For users to trigger their handlers on the public emitter
*
* @returns {core}
* @public
*/
triggerHandler: function() {
this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
return this;
}
};
// $.tooltipster will be used to call core methods
$.tooltipster = new core();
// the Tooltipster instance class (mind the capital T)
$.Tooltipster = function(element, options) {
// list of instance variables
// stack of custom callbacks provided as parameters to API methods
this.__callbacks = {
close: [],
open: []
};
// the schedule time of DOM removal
this.__closingTime;
// this will be the user content shown in the tooltip. A capital "C" is used
// because there is also a method called content()
this.__Content;
// for the size tracker
this.__contentBcr;
// to disable the tooltip after destruction
this.__destroyed = false;
// we can't emit directly on the instance because if a method with the same
// name as the event exists, it will be called by jQuery. Se we use a plain
// object as emitter. This emitter is for internal use by plugins,
// if needed.
this.__$emitterPrivate = $({});
// this emitter is for the user to listen to events without risking to mess
// with our internal listeners
this.__$emitterPublic = $({});
this.__enabled = true;
// the reference to the gc interval
this.__garbageCollector;
// various position and size data recomputed before each repositioning
this.__Geometry;
// the tooltip position, saved after each repositioning by a plugin
this.__lastPosition;
// a unique namespace per instance
this.__namespace = 'tooltipster-' + Math.round(Math.random() * 1000000);
this.__options;
// will be used to support origins in scrollable areas
this.__$originParents;
this.__pointerIsOverOrigin = false;
// to remove themes if needed
this.__previousThemes = [];
// the state can be either: appearing, stable, disappearing, closed
this.__state = 'closed';
// timeout references
this.__timeouts = {
close: [],
open: null
};
// store touch events to be able to detect emulated mouse events
this.__touchEvents = [];
// the reference to the tracker interval
this.__tracker = null;
// the element to which this tooltip is associated
this._$origin;
// this will be the tooltip element (jQuery wrapped HTML element).
// It's the job of a plugin to create it and append it to the DOM
this._$tooltip;
// launch
this.__init(element, options);
};
$.Tooltipster.prototype = {
/**
* @param origin
* @param options
* @private
*/
__init: function(origin, options) {
var self = this;
self._$origin = $(origin);
self.__options = $.extend(true, {}, defaults, options);
// some options may need to be reformatted
self.__optionsFormat();
// don't run on old IE if asked no to
if (!env.IE ||
env.IE >= self.__options.IEmin
) {
// note: the content is null (empty) by default and can stay that
// way if the plugin remains initialized but not fed any content. The
// tooltip will just not appear.
// let's save the initial value of the title attribute for later
// restoration if need be.
var initialTitle = null;
// it will already have been saved in case of multiple tooltips
if (self._$origin.data('tooltipster-initialTitle') === undefined) {
initialTitle = self._$origin.attr('title');
// we do not want initialTitle to be "undefined" because
// of how jQuery's .data() method works
if (initialTitle === undefined) initialTitle = null;
self._$origin.data('tooltipster-initialTitle', initialTitle);
}
// If content is provided in the options, it has precedence over the
// title attribute.
// Note: an empty string is considered content, only 'null' represents
// the absence of content.
// Also, an existing title="" attribute will result in an empty string
// content
if (self.__options.content !== null) {
self.__contentSet(self.__options.content);
} else {
var selector = self._$origin.attr('data-tooltip-content'),
$el;
if (selector) {
$el = $(selector);
}
if ($el && $el[0]) {
self.__contentSet($el.first());
} else {
self.__contentSet(initialTitle);
}
}
self._$origin
// strip the title off of the element to prevent the default tooltips
// from popping up
.removeAttr('title')
// to be able to find all instances on the page later (upon window
// events in particular)
.addClass('tooltipstered');
// set listeners on the origin
self.__prepareOrigin();
// set the garbage collector
self.__prepareGC();
// init plugins
$.each(self.__options.plugins, function(i, pluginName) {
self._plug(pluginName);
});
// to detect swiping
if (env.hasTouchCapability) {
$(env.window.document.body).on('touchmove.' + self.__namespace + '-triggerOpen', function(event) {
self._touchRecordEvent(event);
});
}
self
// prepare the tooltip when it gets created. This event must
// be fired by a plugin
._on('created', function() {
self.__prepareTooltip();
})
// save position information when it's sent by a plugin
._on('repositioned', function(e) {
self.__lastPosition = e.position;
});
} else {
self.__options.disabled = true;
}
},
/**
* Insert the content into the appropriate HTML element of the tooltip
*
* @returns {self}
* @private
*/
__contentInsert: function() {
var self = this,
$el = self._$tooltip.find('.tooltipster-content'),
formattedContent = self.__Content,
format = function(content) {
formattedContent = content;
};
self._trigger({
type: 'format',
content: self.__Content,
format: format
});
if (self.__options.functionFormat) {
formattedContent = self.__options.functionFormat.call(
self,
self, {
origin: self._$origin[0]
},
self.__Content
);
}
if (typeof formattedContent === 'string' && !self.__options.contentAsHTML) {
$el.text(formattedContent);
} else {
$el
.empty()
.append(formattedContent);
}
return self;
},
/**
* Save the content, cloning it beforehand if need be
*
* @param content
* @returns {self}
* @private
*/
__contentSet: function(content) {
// clone if asked. Cloning the object makes sure that each instance has its
// own version of the content (in case a same object were provided for several
// instances)
// reminder: typeof null === object
if (content instanceof $ && this.__options.contentCloning) {
content = content.clone(true);
}
this.__Content = content;
this._trigger({
type: 'updated',
content: content
});
return this;
},
/**
* Error message about a method call made after destruction
*
* @private
*/
__destroyError: function() {
throw new Error('This tooltip has been destroyed and cannot execute your method call.');
},
/**
* Gather all information about dimensions and available space,
* called before every repositioning
*
* @private
* @returns {object}
*/
__geometry: function() {
var self = this,
$target = self._$origin,
originIsArea = self._$origin.is('area');
// if this._$origin is a map area, the target we'll need
// the dimensions of is actually the image using the map,
// not the area itself
if (originIsArea) {
var mapName = self._$origin.parent().attr('name');
$target = $('img[usemap="#' + mapName + '"]');
}
var bcr = $target[0].getBoundingClientRect(),
$document = $(env.window.document),
$window = $(env.window),
$parent = $target,
// some useful properties of important elements
geo = {
// available space for the tooltip, see down below
available: {
document: null,
window: null
},
document: {
size: {
height: $document.height(),
width: $document.width()
}
},
window: {
scroll: {
// the second ones are for IE compatibility
left: env.window.scrollX || env.window.document.documentElement.scrollLeft,
top: env.window.scrollY || env.window.document.documentElement.scrollTop
},
size: {
height: $window.height(),
width: $window.width()
}
},
origin: {
// the origin has a fixed lineage if itself or one of its
// ancestors has a fixed position
fixedLineage: false,
// relative to the document
offset: {},
size: {
height: bcr.bottom - bcr.top,
width: bcr.right - bcr.left
},
usemapImage: originIsArea ? $target[0] : null,
// relative to the window
windowOffset: {
bottom: bcr.bottom,
left: bcr.left,
right: bcr.right,
top: bcr.top
}
}
},
geoFixed = false;
// if the element is a map area, some properties may need
// to be recalculated
if (originIsArea) {
var shape = self._$origin.attr('shape'),
coords = self._$origin.attr('coords');
if (coords) {
coords = coords.split(',');
$.map(coords, function(val, i) {
coords[i] = parseInt(val);
});
}
// if the image itself is the area, nothing more to do
if (shape != 'default') {
switch (shape) {
case 'circle':
var circleCenterLeft = coords[0],
circleCenterTop = coords[1],
circleRadius = coords[2],
areaTopOffset = circleCenterTop - circleRadius,
areaLeftOffset = circleCenterLeft - circleRadius;
geo.origin.size.height = circleRadius * 2;
geo.origin.size.width = geo.origin.size.height;
geo.origin.windowOffset.left += areaLeftOffset;
geo.origin.windowOffset.top += areaTopOffset;
break;
case 'rect':
var areaLeft = coords[0],
areaTop = coords[1],
areaRight = coords[2],
areaBottom = coords[3];
geo.origin.size.height = areaBottom - areaTop;
geo.origin.size.width = areaRight - areaLeft;
geo.origin.windowOffset.left += areaLeft;
geo.origin.windowOffset.top += areaTop;
break;
case 'poly':
var areaSmallestX = 0,
areaSmallestY = 0,
areaGreatestX = 0,
areaGreatestY = 0,
arrayAlternate = 'even';
for (var i = 0; i < coords.length; i++) {
var areaNumber = coords[i];
if (arrayAlternate == 'even') {
if (areaNumber > areaGreatestX) {
areaGreatestX = areaNumber;
if (i === 0) {
areaSmallestX = areaGreatestX;
}
}
if (areaNumber < areaSmallestX) {
areaSmallestX = areaNumber;
}
arrayAlternate = 'odd';
} else {
if (areaNumber > areaGreatestY) {
areaGreatestY = areaNumber;
if (i == 1) {
areaSmallestY = areaGreatestY;
}
}
if (areaNumber < areaSmallestY) {
areaSmallestY = areaNumber;
}
arrayAlternate = 'even';
}
}
geo.origin.size.height = areaGreatestY - areaSmallestY;
geo.origin.size.width = areaGreatestX - areaSmallestX;
geo.origin.windowOffset.left += areaSmallestX;
geo.origin.windowOffset.top += areaSmallestY;
break;
}
}
}
// user callback through an event
var edit = function(r) {
geo.origin.size.height = r.height,
geo.origin.windowOffset.left = r.left,
geo.origin.windowOffset.top = r.top,
geo.origin.size.width = r.width
};
self._trigger({
type: 'geometry',
edit: edit,
geometry: {
height: geo.origin.size.height,
left: geo.origin.windowOffset.left,
top: geo.origin.windowOffset.top,
width: geo.origin.size.width
}
});
// calculate the remaining properties with what we got
geo.origin.windowOffset.right = geo.origin.windowOffset.left + geo.origin.size.width;
geo.origin.windowOffset.bottom = geo.origin.windowOffset.top + geo.origin.size.height;
geo.origin.offset.left = geo.origin.windowOffset.left + geo.window.scroll.left;
geo.origin.offset.top = geo.origin.windowOffset.top + geo.window.scroll.top;
geo.origin.offset.bottom = geo.origin.offset.top + geo.origin.size.height;
geo.origin.offset.right = geo.origin.offset.left + geo.origin.size.width;
// the space that is available to display the tooltip relatively to the document
geo.available.document = {
bottom: {
height: geo.document.size.height - geo.origin.offset.bottom,
width: geo.document.size.width
},
left: {
height: geo.document.size.height,
width: geo.origin.offset.left
},
right: {
height: geo.document.size.height,
width: geo.document.size.width - geo.origin.offset.right
},
top: {
height: geo.origin.offset.top,
width: geo.document.size.width
}
};
// the space that is available to display the tooltip relatively to the viewport
// (the resulting values may be negative if the origin overflows the viewport)
geo.available.window = {
bottom: {
// the inner max is here to make sure the available height is no bigger
// than the viewport height (when the origin is off screen at the top).
// The outer max just makes sure that the height is not negative (when
// the origin overflows at the bottom).
height: Math.max(geo.window.size.height - Math.max(geo.origin.windowOffset.bottom, 0), 0),
width: geo.window.size.width
},
left: {
height: geo.window.size.height,
width: Math.max(geo.origin.windowOffset.left, 0)
},
right: {
height: geo.window.size.height,
width: Math.max(geo.window.size.width - Math.max(geo.origin.windowOffset.right, 0), 0)
},
top: {
height: Math.max(geo.origin.windowOffset.top, 0),
width: geo.window.size.width
}
};
while ($parent[0].tagName.toLowerCase() != 'html') {
if ($parent.css('position') == 'fixed') {
geo.origin.fixedLineage = true;
break;
}
$parent = $parent.parent();
}
return geo;
},
/**
* Some options may need to be formated before being used
*
* @returns {self}
* @private
*/
__optionsFormat: function() {
if (typeof this.__options.animationDuration == 'number') {
this.__options.animationDuration = [this.__options.animationDuration, this.__options.animationDuration];
}
if (typeof this.__options.delay == 'number') {
this.__options.delay = [this.__options.delay, this.__options.delay];
}
if (typeof this.__options.delayTouch == 'number') {
this.__options.delayTouch = [this.__options.delayTouch, this.__options.delayTouch];
}
if (typeof this.__options.theme == 'string') {
this.__options.theme = [this.__options.theme];
}
// determine the future parent
if (this.__options.parent === null) {
this.__options.parent = $(env.window.document.body);
} else if (typeof this.__options.parent == 'string') {
this.__options.parent = $(this.__options.parent);
}
if (this.__options.trigger == 'hover') {
this.__options.triggerOpen = {
mouseenter: true,
touchstart: true
};
this.__options.triggerClose = {
mouseleave: true,
originClick: true,
touchleave: true
};
} else if (this.__options.trigger == 'click') {
this.__options.triggerOpen = {
click: true,
tap: true
};
this.__options.triggerClose = {
click: true,
tap: true
};
}
// for the plugins
this._trigger('options');
return this;
},
/**
* Schedules or cancels the garbage collector task
*
* @returns {self}
* @private
*/
__prepareGC: function() {
var self = this;
// in case the selfDestruction option has been changed by a method call
if (self.__options.selfDestruction) {
// the GC task
self.__garbageCollector = setInterval(function() {
var now = new Date().getTime();
// forget the old events
self.__touchEvents = $.grep(self.__touchEvents, function(event, i) {
// 1 minute
return now - event.time > 60000;
});
// auto-destruct if the origin is gone
if (!bodyContains(self._$origin)) {
self.close(function() {
self.destroy();
});
}
}, 20000);
} else {
clearInterval(self.__garbageCollector);
}
return self;
},
/**
* Sets listeners on the origin if the open triggers require them.
* Unlike the listeners set at opening time, these ones
* remain even when the tooltip is closed. It has been made a
* separate method so it can be called when the triggers are
* changed in the options. Closing is handled in _open()
* because of the bindings that may be needed on the tooltip
* itself
*
* @returns {self}
* @private
*/
__prepareOrigin: function() {
var self = this;
// in case we're resetting the triggers
self._$origin.off('.' + self.__namespace + '-triggerOpen');
// if the device is touch capable, even if only mouse triggers
// are asked, we need to listen to touch events to know if the mouse
// events are actually emulated (so we can ignore them)
if (env.hasTouchCapability) {
self._$origin.on(
'touchstart.' + self.__namespace + '-triggerOpen ' +
'touchend.' + self.__namespace + '-triggerOpen ' +
'touchcancel.' + self.__namespace + '-triggerOpen',
function(event) {
self._touchRecordEvent(event);
}
);
}
// mouse click and touch tap work the same way
if (self.__options.triggerOpen.click ||
(self.__options.triggerOpen.tap && env.hasTouchCapability)
) {
var eventNames = '';
if (self.__options.triggerOpen.click) {
eventNames += 'click.' + self.__namespace + '-triggerOpen ';
}
if (self.__options.triggerOpen.tap && env.hasTouchCapability) {
eventNames += 'touchend.' + self.__namespace + '-triggerOpen';
}
self._$origin.on(eventNames, function(event) {
if (self._touchIsMeaningfulEvent(event)) {
self._open(event);
}
});
}
// mouseenter and touch start work the same way
if (self.__options.triggerOpen.mouseenter ||
(self.__options.triggerOpen.touchstart && env.hasTouchCapability)
) {
var eventNames = '';
if (self.__options.triggerOpen.mouseenter) {
eventNames += 'mouseenter.' + self.__namespace + '-triggerOpen ';
}
if (self.__options.triggerOpen.touchstart && env.hasTouchCapability) {
eventNames += 'touchstart.' + self.__namespace + '-triggerOpen';
}
self._$origin.on(eventNames, function(event) {
if (self._touchIsTouchEvent(event) ||
!self._touchIsEmulatedEvent(event)
) {
self.__pointerIsOverOrigin = true;
self._openShortly(event);
}
});
}
// info for the mouseleave/touchleave close triggers when they use a delay
if (self.__options.triggerClose.mouseleave ||
(self.__options.triggerClose.touchleave && env.hasTouchCapability)
) {
var eventNames = '';
if (self.__options.triggerClose.mouseleave) {
eventNames += 'mouseleave.' + self.__namespace + '-triggerOpen ';
}
if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
eventNames += 'touchend.' + self.__namespace + '-triggerOpen touchcancel.' + self.__namespace + '-triggerOpen';
}
self._$origin.on(eventNames, function(event) {
if (self._touchIsMeaningfulEvent(event)) {
self.__pointerIsOverOrigin = false;
}
});
}
return self;
},
/**
* Do the things that need to be done only once after the tooltip
* HTML element it has been created. It has been made a separate
* method so it can be called when options are changed. Remember
* that the tooltip may actually exist in the DOM before it is
* opened, and present after it has been closed: it's the display
* plugin that takes care of handling it.
*
* @returns {self}
* @private
*/
__prepareTooltip: function() {
var self = this,
p = self.__options.interactive ? 'auto' : '';
// this will be useful to know quickly if the tooltip is in
// the DOM or not
self._$tooltip
.attr('id', self.__namespace)
.css({
// pointer events
'pointer-events': p,
zIndex: self.__options.zIndex
});
// themes
// remove the old ones and add the new ones
$.each(self.__previousThemes, function(i, theme) {
self._$tooltip.removeClass(theme);
});
$.each(self.__options.theme, function(i, theme) {
self._$tooltip.addClass(theme);
});
self.__previousThemes = $.merge([], self.__options.theme);
return self;
},
/**
* Handles the scroll on any of the parents of the origin (when the
* tooltip is open)
*
* @param {object} event
* @returns {self}
* @private
*/
__scrollHandler: function(event) {
var self = this;
if (self.__options.triggerClose.scroll) {
self._close(event);
} else {
// if the origin or tooltip have been removed: do nothing, the tracker will
// take care of it later
if (bodyContains(self._$origin) && bodyContains(self._$tooltip)) {
var geo = null;
// if the scroll happened on the window
if (event.target === env.window.document) {
// if the origin has a fixed lineage, window scroll will have no
// effect on its position nor on the position of the tooltip
if (!self.__Geometry.origin.fixedLineage) {
// we don't need to do anything unless repositionOnScroll is true
// because the tooltip will already have moved with the window
// (and of course with the origin)
if (self.__options.repositionOnScroll) {
self.reposition(event);
}
}
}
// if the scroll happened on another parent of the tooltip, it means
// that it's in a scrollable area and now needs to have its position
// adjusted or recomputed, depending ont the repositionOnScroll
// option. Also, if the origin is partly hidden due to a parent that
// hides its overflow, we'll just hide (not close) the tooltip.
else {
geo = self.__geometry();
var overflows = false;
// a fixed position origin is not affected by the overflow hiding
// of a parent
if (self._$origin.css('position') != 'fixed') {
self.__$originParents.each(function(i, el) {
var $el = $(el),
overflowX = $el.css('overflow-x'),
overflowY = $el.css('overflow-y');
if (overflowX != 'visible' || overflowY != 'visible') {
var bcr = el.getBoundingClientRect();
if (overflowX != 'visible') {
if (geo.origin.windowOffset.left < bcr.left ||
geo.origin.windowOffset.right > bcr.right
) {
overflows = true;
return false;
}
}
if (overflowY != 'visible') {
if (geo.origin.windowOffset.top < bcr.top ||
geo.origin.windowOffset.bottom > bcr.bottom
) {
overflows = true;
return false;
}
}
}
// no need to go further if fixed, for the same reason as above
if ($el.css('position') == 'fixed') {
return false;
}
});
}
if (overflows) {
self._$tooltip.css('visibility', 'hidden');
} else {
self._$tooltip.css('visibility', 'visible');
// reposition
if (self.__options.repositionOnScroll) {
self.reposition(event);
}
// or just adjust offset
else {
// we have to use offset and not windowOffset because this way,
// only the scroll distance of the scrollable areas are taken into
// account (the scrolltop value of the main window must be
// ignored since the tooltip already moves with it)
var offsetLeft = geo.origin.offset.left - self.__Geometry.origin.offset.left,
offsetTop = geo.origin.offset.top - self.__Geometry.origin.offset.top;
// add the offset to the position initially computed by the display plugin
self._$tooltip.css({
left: self.__lastPosition.coord.left + offsetLeft,
top: self.__lastPosition.coord.top + offsetTop
});
}
}
}
self._trigger({
type: 'scroll',
event: event,
geo: geo
});
}
}
return self;
},
/**
* Changes the state of the tooltip
*
* @param {string} state
* @returns {self}
* @private
*/
__stateSet: function(state) {
this.__state = state;
this._trigger({
type: 'state',
state: state
});
return this;
},
/**
* Clear appearance timeouts
*
* @returns {self}
* @private
*/
__timeoutsClear: function() {
// there is only one possible open timeout: the delayed opening
// when the mouseenter/touchstart open triggers are used
clearTimeout(this.__timeouts.open);
this.__timeouts.open = null;
// ... but several close timeouts: the delayed closing when the
// mouseleave close trigger is used and the timer option
$.each(this.__timeouts.close, function(i, timeout) {
clearTimeout(timeout);
});
this.__timeouts.close = [];
return this;
},
/**
* Start the tracker that will make checks at regular intervals
*
* @returns {self}
* @private
*/
__trackerStart: function() {
var self = this,
$content = self._$tooltip.find('.tooltipster-content');
// get the initial content size
if (self.__options.trackTooltip) {
self.__contentBcr = $content[0].getBoundingClientRect();
}
self.__tracker = setInterval(function() {
// if the origin or tooltip elements have been removed.
// Note: we could destroy the instance now if the origin has
// been removed but we'll leave that task to our garbage collector
if (!bodyContains(self._$origin) || !bodyContains(self._$tooltip)) {
self._close();
}
// if everything is alright
else {
// compare the former and current positions of the origin to reposition
// the tooltip if need be
if (self.__options.trackOrigin) {
var g = self.__geometry(),
identical = false;
// compare size first (a change requires repositioning too)
if (areEqual(g.origin.size, self.__Geometry.origin.size)) {
// for elements that have a fixed lineage (see __geometry()), we track the
// top and left properties (relative to window)
if (self.__Geometry.origin.fixedLineage) {
if (areEqual(g.origin.windowOffset, self.__Geometry.origin.windowOffset)) {
identical = true;
}
}
// otherwise, track total offset (relative to document)
else {
if (areEqual(g.origin.offset, self.__Geometry.origin.offset)) {
identical = true;
}
}
}
if (!identical) {
// close the tooltip when using the mouseleave close trigger
// (see https://github.com/calebjacob/tooltipster/pull/253)
if (self.__options.triggerClose.mouseleave) {
self._close();
} else {
self.reposition();
}
}
}
if (self.__options.trackTooltip) {
var currentBcr = $content[0].getBoundingClientRect();
if (currentBcr.height !== self.__contentBcr.height ||
currentBcr.width !== self.__contentBcr.width
) {
self.reposition();
self.__contentBcr = currentBcr;
}
}
}
}, self.__options.trackerInterval);
return self;
},
/**
* Closes the tooltip (after the closing delay)
*
* @param event
* @param callback
* @param force Set to true to override a potential refusal of the user's function
* @returns {self}
* @protected
*/
_close: function(event, callback, force) {
var self = this,
ok = true;
self._trigger({
type: 'close',
event: event,
stop: function() {
ok = false;
}
});
// a destroying tooltip (force == true) may not refuse to close
if (ok || force) {
// save the method custom callback and cancel any open method custom callbacks
if (callback) self.__callbacks.close.push(callback);
self.__callbacks.open = [];
// clear open/close timeouts
self.__timeoutsClear();
var finishCallbacks = function() {
// trigger any close method custom callbacks and reset them
$.each(self.__callbacks.close, function(i, c) {
c.call(self, self, {
event: event,
origin: self._$origin[0]
});
});
self.__callbacks.close = [];
};
if (self.__state != 'closed') {
var necessary = true,
d = new Date(),
now = d.getTime(),
newClosingTime = now + self.__options.animationDuration[1];
// the tooltip may already already be disappearing, but if a new
// call to close() is made after the animationDuration was changed
// to 0 (for example), we ought to actually close it sooner than
// previously scheduled. In that case it should be noted that the
// browser will not adapt the animation duration to the new
// animationDuration that was set after the start of the closing
// animation.
// Note: the same thing could be considered at opening, but is not
// really useful since the tooltip is actually opened immediately
// upon a call to _open(). Since it would not make the opening
// animation finish sooner, its sole impact would be to trigger the
// state event and the open callbacks sooner than the actual end of
// the opening animation, which is not great.
if (self.__state == 'disappearing') {
if (newClosingTime > self.__closingTime
// in case closing is actually overdue because the script
// execution was suspended. See #679
&&
self.__options.animationDuration[1] > 0
) {
necessary = false;
}
}
if (necessary) {
self.__closingTime = newClosingTime;
if (self.__state != 'disappearing') {
self.__stateSet('disappearing');
}
var finish = function() {
// stop the tracker
clearInterval(self.__tracker);
// a "beforeClose" option has been asked several times but would
// probably useless since the content element is still accessible
// via ::content(), and because people can always use listeners
// inside their content to track what's going on. For the sake of
// simplicity, this has been denied. Bur for the rare people who
// really need the option (for old browsers or for the case where
// detaching the content is actually destructive, for file or
// password inputs for example), this event will do the work.
self._trigger({
type: 'closing',
event: event
});
// unbind listeners which are no longer needed
self._$tooltip
.off('.' + self.__namespace + '-triggerClose')
.removeClass('tooltipster-dying');
// orientationchange, scroll and resize listeners
$(env.window).off('.' + self.__namespace + '-triggerClose');
// scroll listeners
self.__$originParents.each(function(i, el) {
$(el).off('scroll.' + self.__namespace + '-triggerClose');
});
// clear the array to prevent memory leaks
self.__$originParents = null;
$(env.window.document.body).off('.' + self.__namespace + '-triggerClose');
self._$origin.off('.' + self.__namespace + '-triggerClose');
self._off('dismissable');
// a plugin that would like to remove the tooltip from the
// DOM when closed should bind on this
self.__stateSet('closed');
// trigger event
self._trigger({
type: 'after',
event: event
});
// call our constructor custom callback function
if (self.__options.functionAfter) {
self.__options.functionAfter.call(self, self, {
event: event,
origin: self._$origin[0]
});
}
// call our method custom callbacks functions
finishCallbacks();
};
if (env.hasTransitions) {
self._$tooltip.css({
'-moz-animation-duration': self.__options.animationDuration[1] + 'ms',
'-ms-animation-duration': self.__options.animationDuration[1] + 'ms',
'-o-animation-duration': self.__options.animationDuration[1] + 'ms',
'-webkit-animation-duration': self.__options.animationDuration[1] + 'ms',
'animation-duration': self.__options.animationDuration[1] + 'ms',
'transition-duration': self.__options.animationDuration[1] + 'ms'
});
self._$tooltip
// clear both potential open and close tasks
.clearQueue()
.removeClass('tooltipster-show')
// for transitions only
.addClass('tooltipster-dying');
if (self.__options.animationDuration[1] > 0) {
self._$tooltip.delay(self.__options.animationDuration[1]);
}
self._$tooltip.queue(finish);
} else {
self._$tooltip
.stop()
.fadeOut(self.__options.animationDuration[1], finish);
}
}
}
// if the tooltip is already closed, we still need to trigger
// the method custom callbacks
else {
finishCallbacks();
}
}
return self;
},
/**
* For internal use by plugins, if needed
*
* @returns {self}
* @protected
*/
_off: function() {
this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
return this;
},
/**
* For internal use by plugins, if needed
*
* @returns {self}
* @protected
*/
_on: function() {
this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
return this;
},
/**
* For internal use by plugins, if needed
*
* @returns {self}
* @protected
*/
_one: function() {
this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
return this;
},
/**
* Opens the tooltip right away.
*
* @param event
* @param callback Will be called when the opening animation is over
* @returns {self}
* @protected
*/
_open: function(event, callback) {
var self = this;
// if the destruction process has not begun and if this was not
// triggered by an unwanted emulated click event
if (!self.__destroying) {
// check that the origin is still in the DOM
if (bodyContains(self._$origin)
// if the tooltip is enabled
&&
self.__enabled
) {
var ok = true;
// if the tooltip is not open yet, we need to call functionBefore.
// otherwise we can jst go on
if (self.__state == 'closed') {
// trigger an event. The event.stop function allows the callback
// to prevent the opening of the tooltip
self._trigger({
type: 'before',
event: event,
stop: function() {
ok = false;
}
});
if (ok && self.__options.functionBefore) {
// call our custom function before continuing
ok = self.__options.functionBefore.call(self, self, {
event: event,
origin: self._$origin[0]
});
}
}
if (ok !== false) {
// if there is some content
if (self.__Content !== null) {
// save the method callback and cancel close method callbacks
if (callback) {
self.__callbacks.open.push(callback);
}
self.__callbacks.close = [];
// get rid of any appearance timeouts
self.__timeoutsClear();
var extraTime,
finish = function() {
if (self.__state != 'stable') {
self.__stateSet('stable');
}
// trigger any open method custom callbacks and reset them
$.each(self.__callbacks.open, function(i, c) {
c.call(self, self, {
origin: self._$origin[0],
tooltip: self._$tooltip[0]
});
});
self.__callbacks.open = [];
};
// if the tooltip is already open
if (self.__state !== 'closed') {
// the timer (if any) will start (or restart) right now
extraTime = 0;
// if it was disappearing, cancel that
if (self.__state === 'disappearing') {
self.__stateSet('appearing');
if (env.hasTransitions) {
self._$tooltip
.clearQueue()
.removeClass('tooltipster-dying')
.addClass('tooltipster-show');
if (self.__options.animationDuration[0] > 0) {
self._$tooltip.delay(self.__options.animationDuration[0]);
}
self._$tooltip.queue(finish);
} else {
// in case the tooltip was currently fading out, bring it back
// to life
self._$tooltip
.stop()
.fadeIn(finish);
}
}
// if the tooltip is already open, we still need to trigger the method
// custom callback
else if (self.__state == 'stable') {
finish();
}
}
// if the tooltip isn't already open, open it
else {
// a plugin must bind on this and store the tooltip in this._$tooltip
self.__stateSet('appearing');
// the timer (if any) will start when the tooltip has fully appeared
// after its transition
extraTime = self.__options.animationDuration[0];
// insert the content inside the tooltip
self.__contentInsert();
// reposition the tooltip and attach to the DOM
self.reposition(event, true);
// animate in the tooltip. If the display plugin wants no css
// animations, it may override the animation option with a
// dummy value that will produce no effect
if (env.hasTransitions) {
// note: there seems to be an issue with start animations which
// are randomly not played on fast devices in both Chrome and FF,
// couldn't find a way to solve it yet. It seems that applying
// the classes before appending to the DOM helps a little, but
// it messes up some CSS transitions. The issue almost never
// happens when delay[0]==0 though
self._$tooltip
.addClass('tooltipster-' + self.__options.animation)
.addClass('tooltipster-initial')
.css({
'-moz-animation-duration': self.__options.animationDuration[0] + 'ms',
'-ms-animation-duration': self.__options.animationDuration[0] + 'ms',
'-o-animation-duration': self.__options.animationDuration[0] + 'ms',
'-webkit-animation-duration': self.__options.animationDuration[0] + 'ms',
'animation-duration': self.__options.animationDuration[0] + 'ms',
'transition-duration': self.__options.animationDuration[0] + 'ms'
});
setTimeout(
function() {
// a quick hover may have already triggered a mouseleave
if (self.__state != 'closed') {
self._$tooltip
.addClass('tooltipster-show')
.removeClass('tooltipster-initial');
if (self.__options.animationDuration[0] > 0) {
self._$tooltip.delay(self.__options.animationDuration[0]);
}
self._$tooltip.queue(finish);
}
},
0
);
} else {
// old browsers will have to live with this
self._$tooltip
.css('display', 'none')
.fadeIn(self.__options.animationDuration[0], finish);
}
// checks if the origin is removed while the tooltip is open
self.__trackerStart();
// NOTE: the listeners below have a '-triggerClose' namespace
// because we'll remove them when the tooltip closes (unlike
// the '-triggerOpen' listeners). So some of them are actually
// not about close triggers, rather about positioning.
$(env.window)
// reposition on resize
.on('resize.' + self.__namespace + '-triggerClose', function(e) {
var $ae = $(document.activeElement);
// reposition only if the resize event was not triggered upon the opening
// of a virtual keyboard due to an input field being focused within the tooltip
// (otherwise the repositioning would lose the focus)
if ((!$ae.is('input') && !$ae.is('textarea')) ||
!$.contains(self._$tooltip[0], $ae[0])
) {
self.reposition(e);
}
})
// same as below for parents
.on('scroll.' + self.__namespace + '-triggerClose', function(e) {
self.__scrollHandler(e);
});
self.__$originParents = self._$origin.parents();
// scrolling may require the tooltip to be moved or even
// repositioned in some cases
self.__$originParents.each(function(i, parent) {
$(parent).on('scroll.' + self.__namespace + '-triggerClose', function(e) {
self.__scrollHandler(e);
});
});
if (self.__options.triggerClose.mouseleave ||
(self.__options.triggerClose.touchleave && env.hasTouchCapability)
) {
// we use an event to allow users/plugins to control when the mouseleave/touchleave
// close triggers will come to action. It allows to have more triggering elements
// than just the origin and the tooltip for example, or to cancel/delay the closing,
// or to make the tooltip interactive even if it wasn't when it was open, etc.
self._on('dismissable', function(event) {
if (event.dismissable) {
if (event.delay) {
timeout = setTimeout(function() {
// event.event may be undefined
self._close(event.event);
}, event.delay);
self.__timeouts.close.push(timeout);
} else {
self._close(event);
}
} else {
clearTimeout(timeout);
}
});
// now set the listeners that will trigger 'dismissable' events
var $elements = self._$origin,
eventNamesIn = '',
eventNamesOut = '',
timeout = null;
// if we have to allow interaction, bind on the tooltip too
if (self.__options.interactive) {
$elements = $elements.add(self._$tooltip);
}
if (self.__options.triggerClose.mouseleave) {
eventNamesIn += 'mouseenter.' + self.__namespace + '-triggerClose ';
eventNamesOut += 'mouseleave.' + self.__namespace + '-triggerClose ';
}
if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
eventNamesIn += 'touchstart.' + self.__namespace + '-triggerClose';
eventNamesOut += 'touchend.' + self.__namespace + '-triggerClose touchcancel.' + self.__namespace + '-triggerClose';
}
$elements
// close after some time spent outside of the elements
.on(eventNamesOut, function(event) {
// it's ok if the touch gesture ended up to be a swipe,
// it's still a "touch leave" situation
if (self._touchIsTouchEvent(event) ||
!self._touchIsEmulatedEvent(event)
) {
var delay = (event.type == 'mouseleave') ?
self.__options.delay :
self.__options.delayTouch;
self._trigger({
delay: delay[1],
dismissable: true,
event: event,
type: 'dismissable'
});
}
})
// suspend the mouseleave timeout when the pointer comes back
// over the elements
.on(eventNamesIn, function(event) {
// it's also ok if the touch event is a swipe gesture
if (self._touchIsTouchEvent(event) ||
!self._touchIsEmulatedEvent(event)
) {
self._trigger({
dismissable: false,
event: event,
type: 'dismissable'
});
}
});
}
// close the tooltip when the origin gets a mouse click (common behavior of
// native tooltips)
if (self.__options.triggerClose.originClick) {
self._$origin.on('click.' + self.__namespace + '-triggerClose', function(event) {
// we could actually let a tap trigger this but this feature just
// does not make sense on touch devices
if (!self._touchIsTouchEvent(event) &&
!self._touchIsEmulatedEvent(event)
) {
self._close(event);
}
});
}
// set the same bindings for click and touch on the body to close the tooltip
if (self.__options.triggerClose.click ||
(self.__options.triggerClose.tap && env.hasTouchCapability)
) {
// don't set right away since the click/tap event which triggered this method
// (if it was a click/tap) is going to bubble up to the body, we don't want it
// to close the tooltip immediately after it opened
setTimeout(function() {
if (self.__state != 'closed') {
var eventNames = '',
$body = $(env.window.document.body);
if (self.__options.triggerClose.click) {
eventNames += 'click.' + self.__namespace + '-triggerClose ';
}
if (self.__options.triggerClose.tap && env.hasTouchCapability) {
eventNames += 'touchend.' + self.__namespace + '-triggerClose';
}
$body.on(eventNames, function(event) {
if (self._touchIsMeaningfulEvent(event)) {
self._touchRecordEvent(event);
if (!self.__options.interactive || !$.contains(self._$tooltip[0], event.target)) {
self._close(event);
}
}
});
// needed to detect and ignore swiping
if (self.__options.triggerClose.tap && env.hasTouchCapability) {
$body.on('touchstart.' + self.__namespace + '-triggerClose', function(event) {
self._touchRecordEvent(event);
});
}
}
}, 0);
}
self._trigger('ready');
// call our custom callback
if (self.__options.functionReady) {
self.__options.functionReady.call(self, self, {
origin: self._$origin[0],
tooltip: self._$tooltip[0]
});
}
}
// if we have a timer set, let the countdown begin
if (self.__options.timer > 0) {
var timeout = setTimeout(function() {
self._close();
}, self.__options.timer + extraTime);
self.__timeouts.close.push(timeout);
}
}
}
}
}
return self;
},
/**
* When using the mouseenter/touchstart open triggers, this function will
* schedule the opening of the tooltip after the delay, if there is one
*
* @param event
* @returns {self}
* @protected
*/
_openShortly: function(event) {
var self = this,
ok = true;
if (self.__state != 'stable' && self.__state != 'appearing') {
// if a timeout is not already running
if (!self.__timeouts.open) {
self._trigger({
type: 'start',
event: event,
stop: function() {
ok = false;
}
});
if (ok) {
var delay = (event.type.indexOf('touch') == 0) ?
self.__options.delayTouch :
self.__options.delay;
if (delay[0]) {
self.__timeouts.open = setTimeout(function() {
self.__timeouts.open = null;
// open only if the pointer (mouse or touch) is still over the origin.
// The check on the "meaningful event" can only be made here, after some
// time has passed (to know if the touch was a swipe or not)
if (self.__pointerIsOverOrigin && self._touchIsMeaningfulEvent(event)) {
// signal that we go on
self._trigger('startend');
self._open(event);
} else {
// signal that we cancel
self._trigger('startcancel');
}
}, delay[0]);
} else {
// signal that we go on
self._trigger('startend');
self._open(event);
}
}
}
}
return self;
},
/**
* Meant for plugins to get their options
*
* @param {string} pluginName The name of the plugin that asks for its options
* @param {object} defaultOptions The default options of the plugin
* @returns {object} The options
* @protected
*/
_optionsExtract: function(pluginName, defaultOptions) {
var self = this,
options = $.extend(true, {}, defaultOptions);
// if the plugin options were isolated in a property named after the
// plugin, use them (prevents conflicts with other plugins)
var pluginOptions = self.__options[pluginName];
// if not, try to get them as regular options
if (!pluginOptions) {
pluginOptions = {};
$.each(defaultOptions, function(optionName, value) {
var o = self.__options[optionName];
if (o !== undefined) {
pluginOptions[optionName] = o;
}
});
}
// let's merge the default options and the ones that were provided. We'd want
// to do a deep copy but not let jQuery merge arrays, so we'll do a shallow
// extend on two levels, that will be enough if options are not more than 1
// level deep
$.each(options, function(optionName, value) {
if (pluginOptions[optionName] !== undefined) {
if ((typeof value == 'object' &&
!(value instanceof Array) &&
value != null
) &&
(typeof pluginOptions[optionName] == 'object' &&
!(pluginOptions[optionName] instanceof Array) &&
pluginOptions[optionName] != null
)
) {
$.extend(options[optionName], pluginOptions[optionName]);
} else {
options[optionName] = pluginOptions[optionName];
}
}
});
return options;
},
/**
* Used at instantiation of the plugin, or afterwards by plugins that activate themselves
* on existing instances
*
* @param {object} pluginName
* @returns {self}
* @protected
*/
_plug: function(pluginName) {
var plugin = $.tooltipster._plugin(pluginName);
if (plugin) {
// if there is a constructor for instances
if (plugin.instance) {
// proxy non-private methods on the instance to allow new instance methods
$.tooltipster.__bridge(plugin.instance, this, plugin.name);
}
} else {
throw new Error('The "' + pluginName + '" plugin is not defined');
}
return this;
},
/**
* This will return true if the event is a mouse event which was
* emulated by the browser after a touch event. This allows us to
* really dissociate mouse and touch triggers.
*
* There is a margin of error if a real mouse event is fired right
* after (within the delay shown below) a touch event on the same
* element, but hopefully it should not happen often.
*
* @returns {boolean}
* @protected
*/
_touchIsEmulatedEvent: function(event) {
var isEmulated = false,
now = new Date().getTime();
for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
var e = this.__touchEvents[i];
// delay, in milliseconds. It's supposed to be 300ms in
// most browsers (350ms on iOS) to allow a double tap but
// can be less (check out FastClick for more info)
if (now - e.time < 500) {
if (e.target === event.target) {
isEmulated = true;
}
} else {
break;
}
}
return isEmulated;
},
/**
* Returns false if the event was an emulated mouse event or
* a touch event involved in a swipe gesture.
*
* @param {object} event
* @returns {boolean}
* @protected
*/
_touchIsMeaningfulEvent: function(event) {
return (
(this._touchIsTouchEvent(event) && !this._touchSwiped(event.target)) ||
(!this._touchIsTouchEvent(event) && !this._touchIsEmulatedEvent(event))
);
},
/**
* Checks if an event is a touch event
*
* @param {object} event
* @returns {boolean}
* @protected
*/
_touchIsTouchEvent: function(event) {
return event.type.indexOf('touch') == 0;
},
/**
* Store touch events for a while to detect swiping and emulated mouse events
*
* @param {object} event
* @returns {self}
* @protected
*/
_touchRecordEvent: function(event) {
if (this._touchIsTouchEvent(event)) {
event.time = new Date().getTime();
this.__touchEvents.push(event);
}
return this;
},
/**
* Returns true if a swipe happened after the last touchstart event fired on
* event.target.
*
* We need to differentiate a swipe from a tap before we let the event open
* or close the tooltip. A swipe is when a touchmove (scroll) event happens
* on the body between the touchstart and the touchend events of an element.
*
* @param {object} target The HTML element that may have triggered the swipe
* @returns {boolean}
* @protected
*/
_touchSwiped: function(target) {
var swiped = false;
for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
var e = this.__touchEvents[i];
if (e.type == 'touchmove') {
swiped = true;
break;
} else if (
e.type == 'touchstart' &&
target === e.target
) {
break;
}
}
return swiped;
},
/**
* Triggers an event on the instance emitters
*
* @returns {self}
* @protected
*/
_trigger: function() {
var args = Array.prototype.slice.apply(arguments);
if (typeof args[0] == 'string') {
args[0] = {
type: args[0]
};
}
// add properties to the event
args[0].instance = this;
args[0].origin = this._$origin ? this._$origin[0] : null;
args[0].tooltip = this._$tooltip ? this._$tooltip[0] : null;
// note: the order of emitters matters
this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
$.tooltipster._trigger.apply($.tooltipster, args);
this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
return this;
},
/**
* Deactivate a plugin on this instance
*
* @returns {self}
* @protected
*/
_unplug: function(pluginName) {
var self = this;
// if the plugin has been activated on this instance
if (self[pluginName]) {
var plugin = $.tooltipster._plugin(pluginName);
// if there is a constructor for instances
if (plugin.instance) {
// unbridge
$.each(plugin.instance, function(methodName, fn) {
// if the method exists (privates methods do not) and comes indeed from
// this plugin (may be missing or come from a conflicting plugin).
if (self[methodName] &&
self[methodName].bridged === self[pluginName]
) {
delete self[methodName];
}
});
}
// destroy the plugin
if (self[pluginName].__destroy) {
self[pluginName].__destroy();
}
// remove the reference to the plugin instance
delete self[pluginName];
}
return self;
},
/**
* @see self::_close
* @returns {self}
* @public
*/
close: function(callback) {
if (!this.__destroyed) {
this._close(null, callback);
} else {
this.__destroyError();
}
return this;
},
/**
* Sets or gets the content of the tooltip
*
* @returns {mixed|self}
* @public
*/
content: function(content) {
var self = this;
// getter method
if (content === undefined) {
return self.__Content;
}
// setter method
else {
if (!self.__destroyed) {
// change the content
self.__contentSet(content);
if (self.__Content !== null) {
// update the tooltip if it is open
if (self.__state !== 'closed') {
// reset the content in the tooltip
self.__contentInsert();
// reposition and resize the tooltip
self.reposition();
// if we want to play a little animation showing the content changed
if (self.__options.updateAnimation) {
if (env.hasTransitions) {
// keep the reference in the local scope
var animation = self.__options.updateAnimation;
self._$tooltip.addClass('tooltipster-update-' + animation);
// remove the class after a while. The actual duration of the
// update animation may be shorter, it's set in the CSS rules
setTimeout(function() {
if (self.__state != 'closed') {
self._$tooltip.removeClass('tooltipster-update-' + animation);
}
}, 1000);
} else {
self._$tooltip.fadeTo(200, 0.5, function() {
if (self.__state != 'closed') {
self._$tooltip.fadeTo(200, 1);
}
});
}
}
}
} else {
self._close();
}
} else {
self.__destroyError();
}
return self;
}
},
/**
* Destroys the tooltip
*
* @returns {self}
* @public
*/
destroy: function() {
var self = this;
if (!self.__destroyed) {
if (self.__state != 'closed') {
// no closing delay
self.option('animationDuration', 0)
// force closing
._close(null, null, true);
} else {
// there might be an open timeout still running
self.__timeoutsClear();
}
// send event
self._trigger('destroy');
self.__destroyed = true;
self._$origin
.removeData(self.__namespace)
// remove the open trigger listeners
.off('.' + self.__namespace + '-triggerOpen');
// remove the touch listener
$(env.window.document.body).off('.' + self.__namespace + '-triggerOpen');
var ns = self._$origin.data('tooltipster-ns');
// if the origin has been removed from DOM, its data may
// well have been destroyed in the process and there would
// be nothing to clean up or restore
if (ns) {
// if there are no more tooltips on this element
if (ns.length === 1) {
// optional restoration of a title attribute
var title = null;
if (self.__options.restoration == 'previous') {
title = self._$origin.data('tooltipster-initialTitle');
} else if (self.__options.restoration == 'current') {
// old school technique to stringify when outerHTML is not supported
title = (typeof self.__Content == 'string') ?
self.__Content :
$('
').append(self.__Content).html();
}
if (title) {
self._$origin.attr('title', title);
}
// final cleaning
self._$origin.removeClass('tooltipstered');
self._$origin
.removeData('tooltipster-ns')
.removeData('tooltipster-initialTitle');
} else {
// remove the instance namespace from the list of namespaces of
// tooltips present on the element
ns = $.grep(ns, function(el, i) {
return el !== self.__namespace;
});
self._$origin.data('tooltipster-ns', ns);
}
}
// last event
self._trigger('destroyed');
// unbind private and public event listeners
self._off();
self.off();
// remove external references, just in case
self.__Content = null;
self.__$emitterPrivate = null;
self.__$emitterPublic = null;
self.__options.parent = null;
self._$origin = null;
self._$tooltip = null;
// make sure the object is no longer referenced in there to prevent
// memory leaks
$.tooltipster.__instancesLatestArr = $.grep($.tooltipster.__instancesLatestArr, function(el, i) {
return self !== el;
});
clearInterval(self.__garbageCollector);
} else {
self.__destroyError();
}
// we return the scope rather than true so that the call to
// .tooltipster('destroy') actually returns the matched elements
// and applies to all of them
return self;
},
/**
* Disables the tooltip
*
* @returns {self}
* @public
*/
disable: function() {
if (!this.__destroyed) {
// close first, in case the tooltip would not disappear on
// its own (no close trigger)
this._close();
this.__enabled = false;
return this;
} else {
this.__destroyError();
}
return this;
},
/**
* Returns the HTML element of the origin
*
* @returns {self}
* @public
*/
elementOrigin: function() {
if (!this.__destroyed) {
return this._$origin[0];
} else {
this.__destroyError();
}
},
/**
* Returns the HTML element of the tooltip
*
* @returns {self}
* @public
*/
elementTooltip: function() {
return this._$tooltip ? this._$tooltip[0] : null;
},
/**
* Enables the tooltip
*
* @returns {self}
* @public
*/
enable: function() {
this.__enabled = true;
return this;
},
/**
* Alias, deprecated in 4.0.0
*
* @param {function} callback
* @returns {self}
* @public
*/
hide: function(callback) {
return this.close(callback);
},
/**
* Returns the instance
*
* @returns {self}
* @public
*/
instance: function() {
return this;
},
/**
* For public use only, not to be used by plugins (use ::_off() instead)
*
* @returns {self}
* @public
*/
off: function() {
if (!this.__destroyed) {
this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
}
return this;
},
/**
* For public use only, not to be used by plugins (use ::_on() instead)
*
* @returns {self}
* @public
*/
on: function() {
if (!this.__destroyed) {
this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
} else {
this.__destroyError();
}
return this;
},
/**
* For public use only, not to be used by plugins
*
* @returns {self}
* @public
*/
one: function() {
if (!this.__destroyed) {
this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
} else {
this.__destroyError();
}
return this;
},
/**
* @see self::_open
* @returns {self}
* @public
*/
open: function(callback) {
if (!this.__destroyed) {
this._open(null, callback);
} else {
this.__destroyError();
}
return this;
},
/**
* Get or set options. For internal use and advanced users only.
*
* @param {string} o Option name
* @param {mixed} val optional A new value for the option
* @return {mixed|self} If val is omitted, the value of the option
* is returned, otherwise the instance itself is returned
* @public
*/
option: function(o, val) {
// getter
if (val === undefined) {
return this.__options[o];
}
// setter
else {
if (!this.__destroyed) {
// change value
this.__options[o] = val;
// format
this.__optionsFormat();
// re-prepare the triggers if needed
if ($.inArray(o, ['trigger', 'triggerClose', 'triggerOpen']) >= 0) {
this.__prepareOrigin();
}
if (o === 'selfDestruction') {
this.__prepareGC();
}
} else {
this.__destroyError();
}
return this;
}
},
/**
* This method is in charge of setting the position and size properties of the tooltip.
* All the hard work is delegated to the display plugin.
* Note: The tooltip may be detached from the DOM at the moment the method is called
* but must be attached by the end of the method call.
*
* @param {object} event For internal use only. Defined if an event such as
* window resizing triggered the repositioning
* @param {boolean} tooltipIsDetached For internal use only. Set this to true if you
* know that the tooltip not being in the DOM is not an issue (typically when the
* tooltip element has just been created but has not been added to the DOM yet).
* @returns {self}
* @public
*/
reposition: function(event, tooltipIsDetached) {
var self = this;
if (!self.__destroyed) {
// if the tooltip is still open and the origin is still in the DOM
if (self.__state != 'closed' && bodyContains(self._$origin)) {
// if the tooltip has not been removed from DOM manually (or if it
// has been detached on purpose)
if (tooltipIsDetached || bodyContains(self._$tooltip)) {
if (!tooltipIsDetached) {
// detach in case the tooltip overflows the window and adds
// scrollbars to it, so __geometry can be accurate
self._$tooltip.detach();
}
// refresh the geometry object before passing it as a helper
self.__Geometry = self.__geometry();
// let a plugin fo the rest
self._trigger({
type: 'reposition',
event: event,
helper: {
geo: self.__Geometry
}
});
}
}
} else {
self.__destroyError();
}
return self;
},
/**
* Alias, deprecated in 4.0.0
*
* @param callback
* @returns {self}
* @public
*/
show: function(callback) {
return this.open(callback);
},
/**
* Returns some properties about the instance
*
* @returns {object}
* @public
*/
status: function() {
return {
destroyed: this.__destroyed,
enabled: this.__enabled,
open: this.__state !== 'closed',
state: this.__state
};
},
/**
* For public use only, not to be used by plugins
*
* @returns {self}
* @public
*/
triggerHandler: function() {
if (!this.__destroyed) {
this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
} else {
this.__destroyError();
}
return this;
}
};
$.fn.tooltipster = function() {
// for using in closures
var args = Array.prototype.slice.apply(arguments),
// common mistake: an HTML element can't be in several tooltips at the same time
contentCloningWarning = 'You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.';
// this happens with $(sel).tooltipster(...) when $(sel) does not match anything
if (this.length === 0) {
// still chainable
return this;
}
// this happens when calling $(sel).tooltipster('methodName or options')
// where $(sel) matches one or more elements
else {
// method calls
if (typeof args[0] === 'string') {
var v = '#*$~&';
this.each(function() {
// retrieve the namepaces of the tooltip(s) that exist on that element.
// We will interact with the first tooltip only.
var ns = $(this).data('tooltipster-ns'),
// self represents the instance of the first tooltipster plugin
// associated to the current HTML object of the loop
self = ns ? $(this).data(ns[0]) : null;
// if the current element holds a tooltipster instance
if (self) {
if (typeof self[args[0]] === 'function') {
if (this.length > 1 &&
args[0] == 'content' &&
(args[1] instanceof $ ||
(typeof args[1] == 'object' && args[1] != null && args[1].tagName)
) &&
!self.__options.contentCloning &&
self.__options.debug
) {
}
// note : args[1] and args[2] may not be defined
var resp = self[args[0]](args[1], args[2]);
} else {
throw new Error('Unknown method "' + args[0] + '"');
}
// if the function returned anything other than the instance
// itself (which implies chaining, except for the `instance` method)
if (resp !== self || args[0] === 'instance') {
v = resp;
// return false to stop .each iteration on the first element
// matched by the selector
return false;
}
} else {
throw new Error('You called Tooltipster\'s "' + args[0] + '" method on an uninitialized element');
}
});
return (v !== '#*$~&') ? v : this;
}
// first argument is undefined or an object: the tooltip is initializing
else {
// reset the array of last initialized objects
$.tooltipster.__instancesLatestArr = [];
// is there a defined value for the multiple option in the options object ?
var multipleIsSet = args[0] && args[0].multiple !== undefined,
// if the multiple option is set to true, or if it's not defined but
// set to true in the defaults
multiple = (multipleIsSet && args[0].multiple) || (!multipleIsSet && defaults.multiple),
// same for content
contentIsSet = args[0] && args[0].content !== undefined,
content = (contentIsSet && args[0].content) || (!contentIsSet && defaults.content),
// same for contentCloning
contentCloningIsSet = args[0] && args[0].contentCloning !== undefined,
contentCloning =
(contentCloningIsSet && args[0].contentCloning) ||
(!contentCloningIsSet && defaults.contentCloning),
// same for debug
debugIsSet = args[0] && args[0].debug !== undefined,
debug = (debugIsSet && args[0].debug) || (!debugIsSet && defaults.debug);
if (this.length > 1 &&
(content instanceof $ ||
(typeof content == 'object' && content != null && content.tagName)
) &&
!contentCloning &&
debug
) {
}
// create a tooltipster instance for each element if it doesn't
// already have one or if the multiple option is set, and attach the
// object to it
this.each(function() {
var go = false,
$this = $(this),
ns = $this.data('tooltipster-ns'),
obj = null;
if (!ns) {
go = true;
} else if (multiple) {
go = true;
} else if (debug) {}
if (go) {
obj = new $.Tooltipster(this, args[0]);
// save the reference of the new instance
if (!ns) ns = [];
ns.push(obj.__namespace);
$this.data('tooltipster-ns', ns);
// save the instance itself
$this.data(obj.__namespace, obj);
// call our constructor custom function.
// we do this here and not in ::init() because we wanted
// the object to be saved in $this.data before triggering
// it
if (obj.__options.functionInit) {
obj.__options.functionInit.call(obj, obj, {
origin: this
});
}
// and now the event, for the plugins and core emitter
obj._trigger('init');
}
$.tooltipster.__instancesLatestArr.push(obj);
});
return this;
}
}
};
// Utilities
/**
* A class to check if a tooltip can fit in given dimensions
*
* @param {object} $tooltip The jQuery wrapped tooltip element, or a clone of it
*/
function Ruler($tooltip) {
// list of instance variables
this.$container;
this.constraints = null;
this.__$tooltip;
this.__init($tooltip);
}
Ruler.prototype = {
/**
* Move the tooltip into an invisible div that does not allow overflow to make
* size tests. Note: the tooltip may or may not be attached to the DOM at the
* moment this method is called, it does not matter.
*
* @param {object} $tooltip The object to test. May be just a clone of the
* actual tooltip.
* @private
*/
__init: function($tooltip) {
this.__$tooltip = $tooltip;
this.__$tooltip
.css({
// for some reason we have to specify top and left 0
left: 0,
// any overflow will be ignored while measuring
overflow: 'hidden',
// positions at (0,0) without the div using 100% of the available width
position: 'absolute',
top: 0
})
// overflow must be auto during the test. We re-set this in case
// it were modified by the user
.find('.tooltipster-content')
.css('overflow', 'auto');
this.$container = $('')
.append(this.__$tooltip)
.appendTo(env.window.document.body);
},
/**
* Force the browser to redraw (re-render) the tooltip immediately. This is required
* when you changed some CSS properties and need to make something with it
* immediately, without waiting for the browser to redraw at the end of instructions.
*
* @see http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes
* @private
*/
__forceRedraw: function() {
// note: this would work but for Webkit only
//this.__$tooltip.close();
//this.__$tooltip[0].offsetHeight;
//this.__$tooltip.open();
// works in FF too
var $p = this.__$tooltip.parent();
this.__$tooltip.detach();
this.__$tooltip.appendTo($p);
},
/**
* Set maximum dimensions for the tooltip. A call to ::measure afterwards
* will tell us if the content overflows or if it's ok
*
* @param {int} width
* @param {int} height
* @return {Ruler}
* @public
*/
constrain: function(width, height) {
this.constraints = {
width: width,
height: height
};
this.__$tooltip.css({
// we disable display:flex, otherwise the content would overflow without
// creating horizontal scrolling (which we need to detect).
display: 'block',
// reset any previous height
height: '',
// we'll check if horizontal scrolling occurs
overflow: 'auto',
// we'll set the width and see what height is generated and if there
// is horizontal overflow
width: width
});
return this;
},
/**
* Reset the tooltip content overflow and remove the test container
*
* @returns {Ruler}
* @public
*/
destroy: function() {
// in case the element was not a clone
this.__$tooltip
.detach()
.find('.tooltipster-content')
.css({
// reset to CSS value
display: '',
overflow: ''
});
this.$container.remove();
},
/**
* Removes any constraints
*
* @returns {Ruler}
* @public
*/
free: function() {
this.constraints = null;
// reset to natural size
this.__$tooltip.css({
display: '',
height: '',
overflow: 'visible',
width: ''
});
return this;
},
/**
* Returns the size of the tooltip. When constraints are applied, also returns
* whether the tooltip fits in the provided dimensions.
* The idea is to see if the new height is small enough and if the content does
* not overflow horizontally.
*
* @param {int} width
* @param {int} height
* @returns {object} An object with a bool `fits` property and a `size` property
* @public
*/
measure: function() {
this.__forceRedraw();
var tooltipBcr = this.__$tooltip[0].getBoundingClientRect(),
result = {
size: {
// bcr.width/height are not defined in IE8- but in this
// case, bcr.right/bottom will have the same value
// except in iOS 8+ where tooltipBcr.bottom/right are wrong
// after scrolling for reasons yet to be determined.
// tooltipBcr.top/left might not be 0, see issue #514
height: tooltipBcr.height || (tooltipBcr.bottom - tooltipBcr.top),
width: tooltipBcr.width || (tooltipBcr.right - tooltipBcr.left)
}
};
if (this.constraints) {
// note: we used to use offsetWidth instead of boundingRectClient but
// it returned rounded values, causing issues with sub-pixel layouts.
// note2: noticed that the bcrWidth of text content of a div was once
// greater than the bcrWidth of its container by 1px, causing the final
// tooltip box to be too small for its content. However, evaluating
// their widths one against the other (below) surprisingly returned
// equality. Happened only once in Chrome 48, was not able to reproduce
// => just having fun with float position values...
var $content = this.__$tooltip.find('.tooltipster-content'),
height = this.__$tooltip.outerHeight(),
contentBcr = $content[0].getBoundingClientRect(),
fits = {
height: height <= this.constraints.height,
width: (
// this condition accounts for min-width property that
// may apply
tooltipBcr.width <= this.constraints.width
// the -1 is here because scrollWidth actually returns
// a rounded value, and may be greater than bcr.width if
// it was rounded up. This may cause an issue for contents
// which actually really overflow by 1px or so, but that
// should be rare. Not sure how to solve this efficiently.
// See http://blogs.msdn.com/b/ie/archive/2012/02/17/sub-pixel-rendering-and-the-css-object-model.aspx
&&
contentBcr.width >= $content[0].scrollWidth - 1
)
};
result.fits = fits.height && fits.width;
}
// old versions of IE get the width wrong for some reason and it causes
// the text to be broken to a new line, so we round it up. If the width
// is the width of the screen though, we can assume it is accurate.
if (env.IE &&
env.IE <= 11 &&
result.size.width !== env.window.document.documentElement.clientWidth
) {
result.size.width = Math.ceil(result.size.width) + 1;
}
return result;
}
};
// quick & dirty compare function, not bijective nor multidimensional
function areEqual(a, b) {
var same = true;
$.each(a, function(i, _) {
if (b[i] === undefined || a[i] !== b[i]) {
same = false;
return false;
}
});
return same;
}
/**
* A fast function to check if an element is still in the DOM. It
* tries to use an id as ids are indexed by the browser, or falls
* back to jQuery's `contains` method. May fail if two elements
* have the same id, but so be it
*
* @param {object} $obj A jQuery-wrapped HTML element
* @return {boolean}
*/
function bodyContains($obj) {
var id = $obj.attr('id'),
el = id ? env.window.document.getElementById(id) : null;
// must also check that the element with the id is the one we want
return el ? el === $obj[0] : $.contains(env.window.document.body, $obj[0]);
}
// detect IE versions for dirty fixes
var uA = navigator.userAgent.toLowerCase();
if (uA.indexOf('msie') != -1) env.IE = parseInt(uA.split('msie')[1]);
else if (uA.toLowerCase().indexOf('trident') !== -1 && uA.indexOf(' rv:11') !== -1) env.IE = 11;
else if (uA.toLowerCase().indexOf('edge/') != -1) env.IE = parseInt(uA.toLowerCase().split('edge/')[1]);
// detecting support for CSS transitions
function transitionSupport() {
// env.window is not defined yet when this is called
if (!win) return false;
var b = win.document.body || win.document.documentElement,
s = b.style,
p = 'transition',
v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
if (typeof s[p] == 'string') {
return true;
}
p = p.charAt(0).toUpperCase() + p.substr(1);
for (var i = 0; i < v.length; i++) {
if (typeof s[v[i] + p] == 'string') {
return true;
}
}
return false;
}
// we'll return jQuery for plugins not to have to declare it as a dependency,
// but it's done by a build task since it should be included only once at the
// end when we concatenate the main file with a pluginreturn $;
// sideTip is Tooltipster's default plugin.
// This file will be UMDified by a build task.
$.tooltipster._plugin({
name: 'tooltipster.sideTip',
instance: {
/**
* Defaults are provided as a function for an easy override by inheritance
*
* @return {object} An object with the defaults options
* @private
*/
__defaults: function() {
return {
// if the tooltip should display an arrow that points to the origin
arrow: true,
// the distance in pixels between the tooltip and the origin
distance: 6,
// allows to easily change the position of the tooltip
functionPosition: null,
maxWidth: null,
// used to accomodate the arrow of tooltip if there is one.
// First to make sure that the arrow target is not too close
// to the edge of the tooltip, so the arrow does not overflow
// the tooltip. Secondly when we reposition the tooltip to
// make sure that it's positioned in such a way that the arrow is
// still pointing at the target (and not a few pixels beyond it).
// It should be equal to or greater than half the width of
// the arrow (by width we mean the size of the side which touches
// the side of the tooltip).
minIntersection: 16,
minWidth: 0,
// deprecated in 4.0.0. Listed for _optionsExtract to pick it up
position: null,
side: 'top',
// set to false to position the tooltip relatively to the document rather
// than the window when we open it
viewportAware: true
};
},
/**
* Run once: at instantiation of the plugin
*
* @param {object} instance The tooltipster object that instantiated this plugin
* @private
*/
__init: function(instance) {
var self = this;
// list of instance variables
self.__instance = instance;
self.__namespace = 'tooltipster-sideTip-' + Math.round(Math.random() * 1000000);
self.__previousState = 'closed';
self.__options;
// initial formatting
self.__optionsFormat();
self.__instance._on('state.' + self.__namespace, function(event) {
if (event.state == 'closed') {
self.__close();
} else if (event.state == 'appearing' && self.__previousState == 'closed') {
self.__create();
}
self.__previousState = event.state;
});
// reformat every time the options are changed
self.__instance._on('options.' + self.__namespace, function() {
self.__optionsFormat();
});
self.__instance._on('reposition.' + self.__namespace, function(e) {
self.__reposition(e.event, e.helper);
});
},
/**
* Called when the tooltip has closed
*
* @private
*/
__close: function() {
// detach our content object first, so the next jQuery's remove()
// call does not unbind its event handlers
if (this.__instance.content() instanceof $) {
this.__instance.content().detach();
}
// remove the tooltip from the DOM
this.__instance._$tooltip.remove();
this.__instance._$tooltip = null;
},
/**
* Creates the HTML element of the tooltip.
*
* @private
*/
__create: function() {
// note: we wrap with a .tooltipster-box div to be able to set a margin on it
// (.tooltipster-base must not have one)
var $html = $(
''
);
// hide arrow if asked
if (!this.__options.arrow) {
$html
.find('.tooltipster-box')
.css('margin', 0)
.end()
.find('.tooltipster-arrow')
.hide();
}
// apply min/max width if asked
if (this.__options.minWidth) {
$html.css('min-width', this.__options.minWidth + 'px');
}
if (this.__options.maxWidth) {
$html.css('max-width', this.__options.maxWidth + 'px');
}
this.__instance._$tooltip = $html;
// tell the instance that the tooltip element has been created
this.__instance._trigger('created');
},
/**
* Used when the plugin is to be unplugged
*
* @private
*/
__destroy: function() {
this.__instance._off('.' + self.__namespace);
},
/**
* (Re)compute this.__options from the options declared to the instance
*
* @private
*/
__optionsFormat: function() {
var self = this;
// get the options
self.__options = self.__instance._optionsExtract('tooltipster.sideTip', self.__defaults());
// for backward compatibility, deprecated in v4.0.0
if (self.__options.position) {
self.__options.side = self.__options.position;
}
// options formatting
// format distance as a four-cell array if it ain't one yet and then make
// it an object with top/bottom/left/right properties
if (typeof self.__options.distance != 'object') {
self.__options.distance = [self.__options.distance];
}
if (self.__options.distance.length < 4) {
if (self.__options.distance[1] === undefined) self.__options.distance[1] = self.__options.distance[0];
if (self.__options.distance[2] === undefined) self.__options.distance[2] = self.__options.distance[0];
if (self.__options.distance[3] === undefined) self.__options.distance[3] = self.__options.distance[1];
}
self.__options.distance = {
top: self.__options.distance[0],
right: self.__options.distance[1],
bottom: self.__options.distance[2],
left: self.__options.distance[3]
};
// let's transform:
// 'top' into ['top', 'bottom', 'right', 'left']
// 'right' into ['right', 'left', 'top', 'bottom']
// 'bottom' into ['bottom', 'top', 'right', 'left']
// 'left' into ['left', 'right', 'top', 'bottom']
if (typeof self.__options.side == 'string') {
var opposites = {
'top': 'bottom',
'right': 'left',
'bottom': 'top',
'left': 'right'
};
self.__options.side = [self.__options.side, opposites[self.__options.side]];
if (self.__options.side[0] == 'left' || self.__options.side[0] == 'right') {
self.__options.side.push('top', 'bottom');
} else {
self.__options.side.push('right', 'left');
}
}
// misc
// disable the arrow in IE6 unless the arrow option was explicitly set to true
if ($.tooltipster._env.IE === 6 &&
self.__options.arrow !== true
) {
self.__options.arrow = false;
}
},
/**
* This method must compute and set the positioning properties of the
* tooltip (left, top, width, height, etc.). It must also make sure the
* tooltip is eventually appended to its parent (since the element may be
* detached from the DOM at the moment the method is called).
*
* We'll evaluate positioning scenarios to find which side can contain the
* tooltip in the best way. We'll consider things relatively to the window
* (unless the user asks not to), then to the document (if need be, or if the
* user explicitly requires the tests to run on the document). For each
* scenario, measures are taken, allowing us to know how well the tooltip
* is going to fit. After that, a sorting function will let us know what
* the best scenario is (we also allow the user to choose his favorite
* scenario by using an event).
*
* @param {object} helper An object that contains variables that plugin
* creators may find useful (see below)
* @param {object} helper.geo An object with many layout properties
* about objects of interest (window, document, origin). This should help
* plugin users compute the optimal position of the tooltip
* @private
*/
__reposition: function(event, helper) {
var self = this,
finalResult,
// to know where to put the tooltip, we need to know on which point
// of the x or y axis we should center it. That coordinate is the target
targets = self.__targetFind(helper),
testResults = [];
// make sure the tooltip is detached while we make tests on a clone
self.__instance._$tooltip.detach();
// we could actually provide the original element to the Ruler and
// not a clone, but it just feels right to keep it out of the
// machinery.
var $clone = self.__instance._$tooltip.clone(),
// start position tests session
ruler = $.tooltipster._getRuler($clone),
satisfied = false,
animation = self.__instance.option('animation');
// an animation class could contain properties that distort the size
if (animation) {
$clone.removeClass('tooltipster-' + animation);
}
// start evaluating scenarios
$.each(['window', 'document'], function(i, container) {
var takeTest = null;
// let the user decide to keep on testing or not
self.__instance._trigger({
container: container,
helper: helper,
satisfied: satisfied,
takeTest: function(bool) {
takeTest = bool;
},
results: testResults,
type: 'positionTest'
});
if (takeTest == true ||
(takeTest != false &&
satisfied == false
// skip the window scenarios if asked. If they are reintegrated by
// the callback of the positionTest event, they will have to be
// excluded using the callback of positionTested
&&
(container != 'window' || self.__options.viewportAware)
)
) {
// for each allowed side
for (var i = 0; i < self.__options.side.length; i++) {
var distance = {
horizontal: 0,
vertical: 0
},
side = self.__options.side[i];
if (side == 'top' || side == 'bottom') {
distance.vertical = self.__options.distance[side];
} else {
distance.horizontal = self.__options.distance[side];
}
// this may have an effect on the size of the tooltip if there are css
// rules for the arrow or something else
self.__sideChange($clone, side);
$.each(['natural', 'constrained'], function(i, mode) {
takeTest = null;
// emit an event on the instance
self.__instance._trigger({
container: container,
event: event,
helper: helper,
mode: mode,
results: testResults,
satisfied: satisfied,
side: side,
takeTest: function(bool) {
takeTest = bool;
},
type: 'positionTest'
});
if (takeTest == true ||
(takeTest != false &&
satisfied == false
)
) {
var testResult = {
container: container,
// we let the distance as an object here, it can make things a little easier
// during the user's calculations at positionTest/positionTested
distance: distance,
// whether the tooltip can fit in the size of the viewport (does not mean
// that we'll be able to make it initially entirely visible, see 'whole')
fits: null,
mode: mode,
outerSize: null,
side: side,
size: null,
target: targets[side],
// check if the origin has enough surface on screen for the tooltip to
// aim at it without overflowing the viewport (this is due to the thickness
// of the arrow represented by the minIntersection length).
// If not, the tooltip will have to be partly or entirely off screen in
// order to stay docked to the origin. This value will stay null when the
// container is the document, as it is not relevant
whole: null
};
// get the size of the tooltip with or without size constraints
var rulerConfigured = (mode == 'natural') ?
ruler.free() :
ruler.constrain(
helper.geo.available[container][side].width - distance.horizontal,
helper.geo.available[container][side].height - distance.vertical
),
rulerResults = rulerConfigured.measure();
testResult.size = rulerResults.size;
testResult.outerSize = {
height: rulerResults.size.height + distance.vertical,
width: rulerResults.size.width + distance.horizontal
};
if (mode == 'natural') {
if (helper.geo.available[container][side].width >= testResult.outerSize.width &&
helper.geo.available[container][side].height >= testResult.outerSize.height
) {
testResult.fits = true;
} else {
testResult.fits = false;
}
} else {
testResult.fits = rulerResults.fits;
}
if (container == 'window') {
if (!testResult.fits) {
testResult.whole = false;
} else {
if (side == 'top' || side == 'bottom') {
testResult.whole = (
helper.geo.origin.windowOffset.right >= self.__options.minIntersection &&
helper.geo.window.size.width - helper.geo.origin.windowOffset.left >= self.__options.minIntersection
);
} else {
testResult.whole = (
helper.geo.origin.windowOffset.bottom >= self.__options.minIntersection &&
helper.geo.window.size.height - helper.geo.origin.windowOffset.top >= self.__options.minIntersection
);
}
}
}
testResults.push(testResult);
// we don't need to compute more positions if we have one fully on screen
if (testResult.whole) {
satisfied = true;
} else {
// don't run the constrained test unless the natural width was greater
// than the available width, otherwise it's pointless as we know it
// wouldn't fit either
if (testResult.mode == 'natural' &&
(testResult.fits ||
testResult.size.width <= helper.geo.available[container][side].width
)
) {
return false;
}
}
}
});
}
}
});
// the user may eliminate the unwanted scenarios from testResults, but he's
// not supposed to alter them at this point. functionPosition and the
// position event serve that purpose.
self.__instance._trigger({
edit: function(r) {
testResults = r;
},
event: event,
helper: helper,
results: testResults,
type: 'positionTested'
});
/**
* Sort the scenarios to find the favorite one.
*
* The favorite scenario is when we can fully display the tooltip on screen,
* even if it means that the middle of the tooltip is no longer centered on
* the middle of the origin (when the origin is near the edge of the screen
* or even partly off screen). We want the tooltip on the preferred side,
* even if it means that we have to use a constrained size rather than a
* natural one (as long as it fits). When the origin is off screen at the top
* the tooltip will be positioned at the bottom (if allowed), if the origin
* is off screen on the right, it will be positioned on the left, etc.
* If there are no scenarios where the tooltip can fit on screen, or if the
* user does not want the tooltip to fit on screen (viewportAware == false),
* we fall back to the scenarios relative to the document.
*
* When the tooltip is bigger than the viewport in either dimension, we stop
* looking at the window scenarios and consider the document scenarios only,
* with the same logic to find on which side it would fit best.
*
* If the tooltip cannot fit the document on any side, we force it at the
* bottom, so at least the user can scroll to see it.
*/
testResults.sort(function(a, b) {
// best if it's whole (the tooltip fits and adapts to the viewport)
if (a.whole && !b.whole) {
return -1;
} else if (!a.whole && b.whole) {
return 1;
} else if (a.whole && b.whole) {
var ai = self.__options.side.indexOf(a.side),
bi = self.__options.side.indexOf(b.side);
// use the user's sides fallback array
if (ai < bi) {
return -1;
} else if (ai > bi) {
return 1;
} else {
// will be used if the user forced the tests to continue
return a.mode == 'natural' ? -1 : 1;
}
} else {
// better if it fits
if (a.fits && !b.fits) {
return -1;
} else if (!a.fits && b.fits) {
return 1;
} else if (a.fits && b.fits) {
var ai = self.__options.side.indexOf(a.side),
bi = self.__options.side.indexOf(b.side);
// use the user's sides fallback array
if (ai < bi) {
return -1;
} else if (ai > bi) {
return 1;
} else {
// will be used if the user forced the tests to continue
return a.mode == 'natural' ? -1 : 1;
}
} else {
// if everything failed, this will give a preference to the case where
// the tooltip overflows the document at the bottom
if (a.container == 'document' &&
a.side == 'bottom' &&
a.mode == 'natural'
) {
return -1;
} else {
return 1;
}
}
}
});
finalResult = testResults[0];
// now let's find the coordinates of the tooltip relatively to the window
finalResult.coord = {};
switch (finalResult.side) {
case 'left':
case 'right':
finalResult.coord.top = Math.floor(finalResult.target - finalResult.size.height / 2);
break;
case 'bottom':
case 'top':
finalResult.coord.left = Math.floor(finalResult.target - finalResult.size.width / 2);
break;
}
switch (finalResult.side) {
case 'left':
finalResult.coord.left = helper.geo.origin.windowOffset.left - finalResult.outerSize.width;
break;
case 'right':
finalResult.coord.left = helper.geo.origin.windowOffset.right + finalResult.distance.horizontal;
break;
case 'top':
finalResult.coord.top = helper.geo.origin.windowOffset.top - finalResult.outerSize.height;
break;
case 'bottom':
finalResult.coord.top = helper.geo.origin.windowOffset.bottom + finalResult.distance.vertical;
break;
}
// if the tooltip can potentially be contained within the viewport dimensions
// and that we are asked to make it fit on screen
if (finalResult.container == 'window') {
// if the tooltip overflows the viewport, we'll move it accordingly (then it will
// not be centered on the middle of the origin anymore). We only move horizontally
// for top and bottom tooltips and vice versa.
if (finalResult.side == 'top' || finalResult.side == 'bottom') {
// if there is an overflow on the left
if (finalResult.coord.left < 0) {
// prevent the overflow unless the origin itself gets off screen (minus the
// margin needed to keep the arrow pointing at the target)
if (helper.geo.origin.windowOffset.right - this.__options.minIntersection >= 0) {
finalResult.coord.left = 0;
} else {
finalResult.coord.left = helper.geo.origin.windowOffset.right - this.__options.minIntersection - 1;
}
}
// or an overflow on the right
else if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
if (helper.geo.origin.windowOffset.left + this.__options.minIntersection <= helper.geo.window.size.width) {
finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
} else {
finalResult.coord.left = helper.geo.origin.windowOffset.left + this.__options.minIntersection + 1 - finalResult.size.width;
}
}
} else {
// overflow at the top
if (finalResult.coord.top < 0) {
if (helper.geo.origin.windowOffset.bottom - this.__options.minIntersection >= 0) {
finalResult.coord.top = 0;
} else {
finalResult.coord.top = helper.geo.origin.windowOffset.bottom - this.__options.minIntersection - 1;
}
}
// or at the bottom
else if (finalResult.coord.top > helper.geo.window.size.height - finalResult.size.height) {
if (helper.geo.origin.windowOffset.top + this.__options.minIntersection <= helper.geo.window.size.height) {
finalResult.coord.top = helper.geo.window.size.height - finalResult.size.height;
} else {
finalResult.coord.top = helper.geo.origin.windowOffset.top + this.__options.minIntersection + 1 - finalResult.size.height;
}
}
}
} else {
// there might be overflow here too but it's easier to handle. If there has
// to be an overflow, we'll make sure it's on the right side of the screen
// (because the browser will extend the document size if there is an overflow
// on the right, but not on the left). The sort function above has already
// made sure that a bottom document overflow is preferred to a top overflow,
// so we don't have to care about it.
// if there is an overflow on the right
if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
// this may actually create on overflow on the left but we'll fix it in a sec
finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
}
// if there is an overflow on the left
if (finalResult.coord.left < 0) {
// don't care if it overflows the right after that, we made our best
finalResult.coord.left = 0;
}
}
// submit the positioning proposal to the user function which may choose to change
// the side, size and/or the coordinates
// first, set the rules that corresponds to the proposed side: it may change
// the size of the tooltip, and the custom functionPosition may want to detect the
// size of something before making a decision. So let's make things easier for the
// implementor
self.__sideChange($clone, finalResult.side);
// add some variables to the helper
helper.tooltipClone = $clone[0];
helper.tooltipParent = self.__instance.option('parent').parent[0];
// move informative values to the helper
helper.mode = finalResult.mode;
helper.whole = finalResult.whole;
// add some variables to the helper for the functionPosition callback (these
// will also be added to the event fired by self.__instance._trigger but that's
// ok, we're just being consistent)
helper.origin = self.__instance._$origin[0];
helper.tooltip = self.__instance._$tooltip[0];
// leave only the actionable values in there for functionPosition
delete finalResult.container;
delete finalResult.fits;
delete finalResult.mode;
delete finalResult.outerSize;
delete finalResult.whole;
// keep only the distance on the relevant side, for clarity
finalResult.distance = finalResult.distance.horizontal || finalResult.distance.vertical;
// beginners may not be comfortable with the concept of editing the object
// passed by reference, so we provide an edit function and pass a clone
var finalResultClone = $.extend(true, {}, finalResult);
// emit an event on the instance
self.__instance._trigger({
edit: function(result) {
finalResult = result;
},
event: event,
helper: helper,
position: finalResultClone,
type: 'position'
});
if (self.__options.functionPosition) {
var result = self.__options.functionPosition.call(self, self.__instance, helper, finalResultClone);
if (result) finalResult = result;
}
// end the positioning tests session (the user might have had a
// use for it during the position event, now it's over)
ruler.destroy();
// compute the position of the target relatively to the tooltip root
// element so we can place the arrow and make the needed adjustments
var arrowCoord,
maxVal;
if (finalResult.side == 'top' || finalResult.side == 'bottom') {
arrowCoord = {
prop: 'left',
val: finalResult.target - finalResult.coord.left
};
maxVal = finalResult.size.width - this.__options.minIntersection;
} else {
arrowCoord = {
prop: 'top',
val: finalResult.target - finalResult.coord.top
};
maxVal = finalResult.size.height - this.__options.minIntersection;
}
// cannot lie beyond the boundaries of the tooltip, minus the
// arrow margin
if (arrowCoord.val < this.__options.minIntersection) {
arrowCoord.val = this.__options.minIntersection;
} else if (arrowCoord.val > maxVal) {
arrowCoord.val = maxVal;
}
var originParentOffset;
// let's convert the window-relative coordinates into coordinates relative to the
// future positioned parent that the tooltip will be appended to
if (helper.geo.origin.fixedLineage) {
// same as windowOffset when the position is fixed
originParentOffset = helper.geo.origin.windowOffset;
} else {
// this assumes that the parent of the tooltip is located at
// (0, 0) in the document, typically like when the parent is
// .
// If we ever allow other types of parent, .tooltipster-ruler
// will have to be appended to the parent to inherit css style
// values that affect the display of the text and such.
originParentOffset = {
left: helper.geo.origin.windowOffset.left + helper.geo.window.scroll.left,
top: helper.geo.origin.windowOffset.top + helper.geo.window.scroll.top
};
}
finalResult.coord = {
left: originParentOffset.left + (finalResult.coord.left - helper.geo.origin.windowOffset.left),
top: originParentOffset.top + (finalResult.coord.top - helper.geo.origin.windowOffset.top)
};
// set position values on the original tooltip element
self.__sideChange(self.__instance._$tooltip, finalResult.side);
if (helper.geo.origin.fixedLineage) {
self.__instance._$tooltip
.css('position', 'fixed');
} else {
// CSS default
self.__instance._$tooltip
.css('position', '');
}
self.__instance._$tooltip
.css({
left: finalResult.coord.left,
top: finalResult.coord.top,
// we need to set a size even if the tooltip is in its natural size
// because when the tooltip is positioned beyond the width of the body
// (which is by default the width of the window; it will happen when
// you scroll the window horizontally to get to the origin), its text
// content will otherwise break lines at each word to keep up with the
// body overflow strategy.
height: finalResult.size.height,
width: finalResult.size.width
})
.find('.tooltipster-arrow')
.css({
'left': '',
'top': ''
})
.css(arrowCoord.prop, arrowCoord.val);
// append the tooltip HTML element to its parent
self.__instance._$tooltip.appendTo(self.__instance.option('parent'));
self.__instance._trigger({
type: 'repositioned',
event: event,
position: finalResult
});
},
/**
* Make whatever modifications are needed when the side is changed. This has
* been made an independant method for easy inheritance in custom plugins based
* on this default plugin.
*
* @param {object} $obj
* @param {string} side
* @private
*/
__sideChange: function($obj, side) {
$obj
.removeClass('tooltipster-bottom')
.removeClass('tooltipster-left')
.removeClass('tooltipster-right')
.removeClass('tooltipster-top')
.addClass('tooltipster-' + side);
},
/**
* Returns the target that the tooltip should aim at for a given side.
* The calculated value is a distance from the edge of the window
* (left edge for top/bottom sides, top edge for left/right side). The
* tooltip will be centered on that position and the arrow will be
* positioned there (as much as possible).
*
* @param {object} helper
* @return {integer}
* @private
*/
__targetFind: function(helper) {
var target = {},
rects = this.__instance._$origin[0].getClientRects();
// these lines fix a Chrome bug (issue #491)
if (rects.length > 1) {
var opacity = this.__instance._$origin.css('opacity');
if (opacity == 1) {
this.__instance._$origin.css('opacity', 0.99);
rects = this.__instance._$origin[0].getClientRects();
this.__instance._$origin.css('opacity', 1);
}
}
// by default, the target will be the middle of the origin
if (rects.length < 2) {
target.top = Math.floor(helper.geo.origin.windowOffset.left + (helper.geo.origin.size.width / 2));
target.bottom = target.top;
target.left = Math.floor(helper.geo.origin.windowOffset.top + (helper.geo.origin.size.height / 2));
target.right = target.left;
}
// if multiple client rects exist, the element may be text split
// up into multiple lines and the middle of the origin may not be
// best option anymore. We need to choose the best target client rect
else {
// top: the first
var targetRect = rects[0];
target.top = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
// right: the middle line, rounded down in case there is an even
// number of lines (looks more centered => check out the
// demo with 4 split lines)
if (rects.length > 2) {
targetRect = rects[Math.ceil(rects.length / 2) - 1];
} else {
targetRect = rects[0];
}
target.right = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
// bottom: the last
targetRect = rects[rects.length - 1];
target.bottom = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
// left: the middle line, rounded up
if (rects.length > 2) {
targetRect = rects[Math.ceil((rects.length + 1) / 2) - 1];
} else {
targetRect = rects[rects.length - 1];
}
target.left = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
}
return target;
}
}
});
}));
});
jQuery(document).ready(function($) {
var fchunker_upload = {
fchunker: function(config) {
$.extend(config);
if ($.upId && $.upUrl) {
$.domHtml = $('#' + $.upId).html();
$.upInputId = $.upId + '_input';
}
},
fchunker_limitFileSize: function(file, limitSize) {
var arr = ["KB", "MB", "GB"],
limit = limitSize.toUpperCase(),
limitNum = 0;
for (var i = 0; i < arr.length; i++) {
var leval = limit.indexOf(arr[i]);
if (leval > -1) {
limitNum = parseInt(limit.substr(0, leval)) * Math.pow(1024, (i + 1));
break;
}
}
if (file.size > limitNum) {
return false;
}
return true;
},
upErrorMsg: function(err) {
$.upError = err;
},
upStop: function(err) {
$.upError = err;
},
upStatus: function() {
if ($.upError) {
if (typeof $.upStop == 'function') {
$.upStop($.upError);
}
return false;
}
return true;
},
fchunker_getPercent: function(num, total) {
num = parseInt(num);
total = parseInt(total);
if (isNaN(num) || isNaN(total)) {
return "-";
}
let sum = total <= 0 ? 0 : (Math.round(num / total * 100));
return sum;
},
fchunker_upload: function(x, xfile) {
$.upError = '';
$.tempFile = $('#' + $.upInputId)[0].files[0];
if (x == 'file') $.tempFile = xfile;
var file = $.tempFile;
if (!file) {
return false;
}
if (typeof $.upStart == 'function') {
$.upStart();
}
var filename = file.name,
index1 = filename.lastIndexOf("."),
index2 = filename.length,
suffix = filename.substring(index1 + 1, index2);
if ($.upType) {
uptype = $.upType.split(",");
if ($.inArray(suffix, uptype) == -1) {
$.upError = 'Type error: Error-' + suffix;
}
}
// if ($.upMaxSize) {
// if (!$.fchunker_limitFileSize(file, $.upMaxSize + 'MB')) {
// $.upError = 'Error';
// }
// }
if ($.upStatus() == false) {
return false;
}
$.taskStart = +new Date();
setTimeout("jQuery.fchunker_upload_core()", "100");
},
fchunker_upload_core: function() {
var file = $.tempFile;
if (!file) {
return false;
}
if (!$.upShardSize) {
$.upShardSize = 2;
}
$.upShardSize = $.upShardSize * 0.8;
var filename = file.name,
size = file.size,
index1 = filename.lastIndexOf("."),
index2 = filename.length,
suffix = filename.substring(index1 + 1, index2),
shardSize = $.upShardSize * 1024 * 1024,
succeed = 0,
shardCount = Math.ceil(size / shardSize);
var re = [];
var start, end = 0;
for (var i = 0; i < shardCount; ++i) {
re[i] = [];
start = i * shardSize,
end = Math.min(size, start + shardSize);
re[i]["file_data"] = file.slice(start, end);
re[i]["file_name"] = filename;
re[i]["file_size"] = size;
}
const URL = $.upUrl;
var i2 = 0,
i3 = 1,
fcs = Array();
var xhr = new XMLHttpRequest();
function ajaxStack(stack) {
if ($.upStatus() == false) {
return;
}
var form = new FormData();
if (stack[i2]) {
fcs = stack[i2];
form.append("file_data", fcs['file_data']);
form.append("file_name", fcs['file_name']);
form.append("file_size", fcs['file_size']);
form.append("file_total", shardCount);
form.append("file_index", i3);
form.append("taskStart", $.taskStart);
form.append("action", "backup_migration");
form.append("token", "bmi");
form.append("f", "upload-backup");
form.append("nonce", $.bmiNonce);
xhr.open('POST', URL, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest");
xhr.onload = function() {
ajaxStack(stack);
}
xhr.onreadystatechange = function() {
if ($.upStatus() == false) {
return;
}
if (xhr.readyState == 4 && xhr.status == 200) {
var data = xhr.responseText ? eval('(' + xhr.responseText + ')') : '';
++succeed;
var cent = $.fchunker_getPercent(succeed, shardCount);
if (typeof $.upEvent == 'function') {
$.upEvent(cent);
}
if (cent == 100) {
setTimeout(function() {
if (typeof $.upCallBack == 'function') {
$.upCallBack(data);
}
}, 500);
} else {
if (typeof $.upCallBack == 'function') {
$.upCallBack(data);
}
}
}
}
xhr.send(form);
i2++;
i3++;
form.delete('file_data');
form.delete('file_name');
form.delete('file_size');
form.delete('file_total');
form.delete('taskStart');
form.delete('file_index');
form.delete('action');
form.delete('token');
form.delete('nonce');
form.delete('f');
}
}
ajaxStack(re);
re = null,
file = null;
}
};
$.extend(fchunker_upload);
});
// Preloader
jQuery(window).on('load', function() {
if (pagenow !== 'toplevel_page_backup-migration') return;
setTimeout(function() {
jQuery('#bmi').css({
opacity: 0
});
jQuery('#bmi-preload').css({
opacity: 1
});
jQuery('#bmi-preload').animate({
opacity: 0
}, 150, function() {
jQuery('#bmi-preload').remove();
jQuery('#bmi').css({
display: 'block'
});
jQuery.bmi.dropdowns.init();
setTimeout(function() {
jQuery('#bmi').animate({ opacity: 1 }, 350, function () {
jQuery(window).trigger("bmi-preload-collapsed");
});
jQuery('#bmi_carrousel').show(200);
}, 100);
});
}, 50);
});
// Plugin for jQuery - Handler of BMI
jQuery(document).ready(function($) {
let collapsing = false;
let ongoing_latest_token = false;
let currentUploadType = null;
wakeLock = null;
function escapeHtml(t){return(t=""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}
$.extend({
bmi: {
requestWakeLock: async () => {
try {
if ( wakeLock == null && 'wakeLock' in navigator) {
wakeLock = await navigator.wakeLock.request('screen');
// Listen for release (e.g., if user switches tabs)
wakeLock.addEventListener('release', () => {
wakeLock = null;
});
}
} catch (err) {
console.error(`${err.name}, ${err.message}`);
}
},
releaseWakeLock: () => {
if (typeof wakeLock !== 'undefined' && wakeLock !== null) {
wakeLock.release();
wakeLock = null;
}
},
// STATIC: Copy to clipboard
clipboard: function(str, text = false) {
try {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
let def = $('#bmi-success-copy').text();
if (text != false) def = text;
$.bmi.alert('success', def, 3000);
return true;
} catch (e) {
console.log(e);
$.bmi.alert('warning', $('#bmi-failed-copy').text(), 3000);
return false;
}
},
// Alert
alert: function(type = 'default', msg = '---', timeout = 7000) {
if ($('.bmi-notification-box').length <= 0)
$('body').find('#bmi').prepend($(''));
if (type == 'default') type = '';
else if (type == 'success') type = ' is-success';
else if (type == 'warning') type = ' is-warning';
else if (type == 'error') type = ' is-danger';
else if (type == 'info') type = ' is-info';
else type = ' is-info';
let elid = Math.floor(Math.random() * Math.floor(64000));
let $html = ``;
$('.bmi-notification-box').prepend($html);
let ntf = $(`#ntf-${elid}`);
ntf.css({
opacity: 0,
display: 'block'
});
let width = ntf.outerWidth(),
height = ntf.outerHeight();
ntf.css({
right: '-35vw',
'font-size': '0px',
width: 0,
padding: 0,
opacity: '0'
});
ntf.animate({
right: '15px',
width: width,
padding: '1rem 2rem 1rem 1.5rem',
opacity: '1'
}, {
duration: 200,
queue: false
});
ntf.animate({
'font-size': '16px'
}, {
duration: 300,
queue: false
});
setTimeout(() => {
$(`#ntf-${elid}`).css({
width: ''
});
}, 250);
setTimeout(() => {
ntf.animate({
'font-size': '0px'
}, {
duration: 200,
queue: false
});
ntf.animate({
right: '-35vw',
height: 0,
width: 0,
margin: 0,
padding: 0,
opacity: '0'
}, 300, function() {
ntf.remove();
});
}, timeout);
},
// Response message
_msg: function(res) {
if (res.status != 'msg') return;
if (typeof res.level == 'undefined') res.level = 'info';
$.bmi.alert(res.level, res.why, 3000);
console.log('Backup-migration: ', '[' + res.level.toUpperCase() + ']', res.why);
},
// Hide alert
hideAlert: function(self) {
let ntf = $(self).parents('.bmi-notification');
ntf.animate({
'font-size': '0px'
}, {
duration: 200,
queue: false
});
ntf.animate({
right: '-35vw',
height: 0,
width: 0,
margin: 0,
padding: 0,
opacity: '0'
}, 300, function() {
ntf.remove();
});
},
// OBJECT: Modal
modal: function(id = false) {
let mod = false;
if (id != false) mod = document.getElementById(id);
return {
clearModal: function() {
mod.querySelectorAll('.customselect').forEach(function(dropdown) {
dropdown.classList.remove('active');
});
mod.querySelectorAll('input[type="text"]').forEach(function(input) {
input.value = '';
input.setAttribute('value', '');
});
},
open: function(cb = function() {}) {
mod.classList.add('before-open');
setTimeout(function() {
mod.classList.add('open');
$('html')[0].style.overflowY = 'hidden';
setTimeout(cb, 410);
}, 10);
},
close: function(cb = function() {}) {
if (mod.offsetWidth > 0 && mod.offsetHeight > 0) {
mod.classList.add('before-close');
setTimeout(function() {
mod.classList.add('closed');
setTimeout(function() {
mod.classList.remove('before-open');
mod.classList.remove('open');
mod.classList.remove('before-close');
mod.classList.remove('closed');
$.bmi.modal(mod.id).clearModal();
cb();
}, 410);
}, 10);
} else {
mod.classList.remove('before-open');
mod.classList.remove('open');
mod.classList.remove('before-close');
mod.classList.remove('closed');
cb();
}
$('html')[0].style.overflowY = 'auto';
},
closeAll: function() {
let modals = document.querySelectorAll('.modal');
modals.forEach(function(mod) {
$.bmi.modal(mod.id).close();
});
$('html')[0].style.overflowY = 'auto';
},
setParent: function(parentId) {
mod.setAttribute('data-parent-id',parentId);
},
getParent: function() {
return mod.getAttribute('data-parent-id');
}
}
},
// PROMISE: Return JSON (from string) or FAIL
json: function(str) {
let originalString = str;
return new Promise(function(resolve, reject) {
try {
let json = JSON.parse(str);
return resolve(json);
} catch (e) {
if (typeof str === 'string') {
let reversed = $.bmi.reverse(str);
let lastcorrect = reversed.indexOf('}');
if (lastcorrect == 0) lastcorrect = str.length;
else lastcorrect = -lastcorrect;
str = str.slice(str.indexOf('{'), lastcorrect);
try {
let json = JSON.parse(str);
return resolve(json);
} catch (e) {
return resolve(originalString);
}
} else return reject(false);
}
});
},
// STATIC: Returns reversed string
reverse: function(str) {
if (typeof str === 'string') {
return (str === '') ? '' : $.bmi.reverse(str.substr(1)) + str.charAt(0);
} else {
return str;
}
},
// AJAX: Logger of JSON
logJsonError: function(errorLogObj, func) {
data = {};
data.action = 'backup_migration';
data.token = 'bmi';
data.nonce = bmiVariables.nonce;
data.f = 'front-end-ajax-error';
data.call = func;
data.error = errorLogObj;
$.post(ajaxurl, data).done((res) => {
$.bmi.json(res).then(function(res) {
return;
}).catch(function(error) {
console.log(error);
});
}).fail((error) => {
console.error(error);
});
},
objectToFormData: (obj, form = null, namespace = '') => {
const formData = form || new FormData();
const isArr = Array.isArray(obj);
for (const key in obj) {
if (!obj.hasOwnProperty(key) || obj[key] === null || obj[key] === undefined) {
continue;
}
const formKey = namespace ? (isArr ? `${namespace}[]` : `${namespace}[${key}]`) : key;
if (typeof obj[key] === 'string') {
formData.append(formKey, obj[key]);
} else if (obj[key] instanceof Date) {
formData.append(formKey, obj[key].toISOString());
} else if (obj[key] instanceof File) {
formData.append(formKey, obj[key]);
} else if (typeof obj[key] !== 'object') {
formData.append(formKey, obj[key].toString());
} else if (Array.isArray(obj[key])) {
obj[key].forEach((item, index) => {
const arrayKey = `${formKey}[${index}]`;
if (typeof item === 'string')
formData.append(arrayKey, item);
else
jQuery.bmi.objectToFormData(item, formData, arrayKey);
});
} else {
jQuery.bmi.objectToFormData(obj[key], formData, formKey);
}
}
return formData;
},
// PROMISE: BMI POST Requests
ajax: function(func, data = {}) {
return new Promise(async function(resolve, reject) {
function _error(error) {
let ajaxErrorObj = {};
console.log('------- BACKUP MIGRATION ERROR START -------');
console.log('The error:', error);
console.log('Call: ', func);
ajaxErrorObj['call'] = func;
if (typeof error == 'object') {
for (let i = 0; i < error.length; ++i) {
if (typeof error[i] == 'object') {
if (typeof error[i]['message'] != 'undefined') {
console.log(i, error[i]['message']);
ajaxErrorObj[`${i}_x`] = error[i]['message'];
}
for (let k in error[i]) {
if (typeof error[i][k] != 'function') {
if (typeof error[i][k] == 'string' && error[i][k].length > 2000) {
ajaxErrorObj[`${i}_${k}`] = error[i][k].slice(0, 2000);
console.log(i, k);
console.warn(error[i][k]);
} else {
console.log(i, k, error[i][k]);
ajaxErrorObj[`${i}_${k}`] = error[i][k];
}
}
}
} else {
console.log(i, error[i]);
ajaxErrorObj[`${i}_x`] = error[i];
}
}
} else {
console.log(error);
if (typeof error == 'string' && error.length > 2000) {
ajaxErrorObj[`single_error_txt`] = error.slice(0, 2000);
} else {
ajaxErrorObj[`single_error_txt`] = error;
}
}
console.log('-------- BACKUP MIGRATION ERROR END --------\n\n');
$.bmi.logJsonError(ajaxErrorObj, func);
reject(error);
}
data.action = 'backup_migration';
data.token = 'bmi';
data.nonce = bmiVariables.nonce;
data.f = func;
try {
let res = await fetch(ajaxurl + '?cache=false', {
"body": new URLSearchParams($.bmi.objectToFormData(data)).toString(),
"cache": "default",
"credentials": "include",
"headers": {
"Accept": "*/*",
"Accept-Language": navigator.language + ";q=0.9",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"User-Agent": navigator.userAgent,
"X-Requested-With": "XMLHttpRequest"
},
"method": "POST",
"mode": "cors",
"redirect": "follow",
"referrer": window.location.href,
"referrerPolicy": "strict-origin-when-cross-origin"
});
let response = null;
if (res.ok === true) {
response = await res.text();
$.bmi.json(response).then(function(parsedResponse) {
resolve(parsedResponse);
}).catch(function(error) {
console.error(error);
_error(['json', error, res, func, data, (response || false)]);
});
} else {
response = await res.text().catch(() => '');
throw new Error('Response is not OK (' + res.status + '): ' + (response || 'No response body'));
}
} catch (error) {
_error([error, func, data]);
}
// $.post(ajaxurl + '?cache=false', data).done((res) => {
// $.bmi.json(res).then(function(res) {
// resolve(res);
// }).catch(function(error) {
// console.error(error);
// _error(['json', error, res, func, data, (res.responseText || false)]);
// });
// }).fail((error) => {
// _error([error, func, data]);
// });
});
},
// STATIC: Tooltips
tooltips: {
init: function() {
function tooltipReposition() {
setTimeout(() => {
let instances = $.tooltipster.instances();
for (instance in instances)
instances[instance].reposition();
}, 10);
}
let settings = {
delay: 200,
debug: false,
delayTouch: [100, 200],
interactive: false,
distance: 0,
side: 'top',
contentAsHTML: false,
maxWidth: 460,
triggerOpen: {
mouseenter: true,
touchstart: true
},
triggerClose: {
mouseleave: true,
click: true,
tap: true
},
theme: ['bmi-tooltip', 'bmi-tt-default', 'bmi-tt-default-customized'],
repositionOnScroll: true,
functionReady: tooltipReposition
}
function extractThemesFromElement(el) {
try {
const base = settings.theme;
const classList = (el && el.className ? el.className : '')
.split(/\s+/)
.filter(Boolean);
const extracted = classList.filter(c => c.indexOf('bmi-tt-') === 0);
if (extracted.length) return base.concat(extracted);
else return base;
} catch (e) {
return settings.theme;
}
}
let tts = $('.tooltip');
let tts_html = $('.tooltip-html');
let premiums = $('.premium-wrapper');
let settings_html = JSON.parse(JSON.stringify(settings));
settings_html.contentAsHTML = true;
settings_html.interactive = true;
for (let i = 0; i < tts.length; ++i) {
let tooltip = tts[i];
let top = tooltip.getAttribute('data-top');
let side = tooltip.getAttribute('side');
let s = JSON.parse(JSON.stringify(settings));
s.content = tooltip.getAttribute('tooltip');
s.theme = extractThemesFromElement(tooltip);
if (top) s.distance = parseInt(top);
if (side && side.trim().length > 0) s.side = side;
$(tooltip).tooltipster(s);
}
for (let i = 0; i < tts_html.length; ++i) {
let tooltip = tts_html[i];
let side = tooltip.getAttribute('side');
let s = JSON.parse(JSON.stringify(settings_html));
s.content = tooltip.getAttribute('tooltip');
s.theme = extractThemesFromElement(tooltip);
if (side && side.trim().length > 0) s.side = side;
$(tooltip).tooltipster(s);
}
for (let i = 0; i < premiums.length; ++i) {
let premium = premiums[i];
let semiums = JSON.parse(JSON.stringify(settings));
semiums.contentAsHTML = true;
semiums.interactive = true;
semiums.maxWidth = 500;
semiums.theme = extractThemesFromElement(premium);
if (premium.getAttribute('tooltip')) semiums.content = premium.getAttribute('tooltip');
else if (premium.getAttribute('data-ready')) {
semiums.content = $('#premium-tooltip-pre')[0].innerHTML.trim() + ' ' + premium.getAttribute('data-ready').trim() + ' ' + $('#premium-tooltip-r')[0].innerHTML.trim();
} else semiums.content = $('#premium-tooltip')[0].innerHTML;
if (premium.getAttribute('side')) semiums.side = premium.getAttribute('side');
$(premium).tooltipster(semiums);
}
},
hideAll: function(rightnow = false) {
function _hide() {
let instances = $.tooltipster.instances();
for (instance in instances) instances[instance].close();
}
if (rightnow) _hide();
else setTimeout(_hide, 10);
}
},
// STATIC: Collapsers
collapsers: {
toggle: function(self) {
if (collapsing === true) return;
else collapsing = true;
let group = self.getAttribute('group');
if (self.classList.contains('active')) $.bmi.collapsers.close(self);
else $.bmi.collapsers.open(self, group);
},
open: function(el, group) {
$.bmi.collapsers.closeGroup(group);
$(el).addClass('active');
$(el).find('.content').show(300);
setTimeout(function() {
collapsing = false;
}, 300);
},
close: function(el) {
$(el).removeClass('active');
$(el).find('.content').hide(300);
setTimeout(function() {
collapsing = false;
}, 300);
},
closeGroup: function(group) {
$('.collapser[group="' + group + '"]').removeClass('active');
$('.collapser[group="' + group + '"]').find('.content').hide(300);
setTimeout(function() {
collapsing = false;
}, 300);
},
closeAll: function() {
$('.collapser').removeClass('active');
$('.collapser').find('.content').hide(300);
setTimeout(function() {
collapsing = false;
}, 300);
}
},
// STATIC: URL Validation
isUrlValid: function(url) {
var re_weburl = new RegExp(
"^" +
"(?:(?:(?:https?|ftp):)?\\/\\/)" +
"(?:\\S+(?::\\S*)?@)?" +
"(?:" +
"(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
"(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
"(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
"|" +
"(?:" +
"(?:" +
"[a-z0-9\\u00a1-\\uffff]" +
"[a-z0-9\\u00a1-\\uffff_-]{0,62}" +
")?" +
"[a-z0-9\\u00a1-\\uffff]\\." +
")+" +
"(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" +
")" +
"(?::\\d{2,5})?" +
"(?:[/?#]\\S*)?" +
"$", "i"
);
if (re_weburl.test(url)) return true;
else return false;
},
// STATIC: Human Readable
bytesToHuman: function(a, b, c, d, e) {
return (b = Math, c = b.log, d = 1024, e = c(a) / c(d) | 0, a / b.pow(d, e))
.toFixed(2) + ' ' + (e ? 'KMGTPEZY' [--e] + 'B' : 'Bytes');
},
// STATIC: Human Readable Cut Decimal
bytesToHumanCut: function(a, b, c, d, e) {
return Math.ceil((b = Math, c = b.log, d = 1024, e = c(a) / c(d) | 0, a / b.pow(d, e)))
+ ' ' + (e ? 'KMGTPEZY' [--e] + 'B' : 'Bytes');
},
// STATIC: Getting backups
getCurrentBackups: function(done = function() {}, tries = 0, q = '') {
if ($('#reloading-bm-list').length > 0 && $('#reloading-bm-list')[0].style.display == 'none') {
$('#reloading-bm-list').show();
$.bmi.ajax('get-current-backups', {
q: q
}).then(function(res) {
// console.warn('Backup list loaded...');
$.bmi.ajax('check-not-uploaded-backups').then(function(res) {
// console.warn('Checking for not uploaded backups...');
// console.log(res);
});
$('#reloading-bm-list').hide();
done(res);
}).catch(function(error) {
$('#reloading-bm-list').hide();
if (tries > 5) return;
setTimeout(() => {
$.bmi.getCurrentBackups(done, (tries+1));
}, 1000);
});
}
},
getCurrentStaging: function(done = function() {}) {
if ($('#reloading-bm-stg-list').length > 0 && $('#reloading-bm-stg-list')[0].style.display == 'none') {
$('#reloading-bm-stg-list').show();
$.bmi.ajax('staging-get-updated-list', {}).then(function(res) {
$('#reloading-bm-stg-list').hide();
done(res);
}).catch(function(error) {
//
});
}
},
getExpirationTime: function(time) {
let $tbody = $('#stg-tbody-table');
let expiresNever = $tbody.data('never');
if (isNaN(parseInt(time)) || time == expiresNever) {
return time;
}
let now = +new Date();
let destination = time * 1000;
let diff = (destination - now);
let days = Math.floor(diff / 24 / 60 / 60 / 1000);
let hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
let minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
let seconds = Math.floor((diff % (1000 * 60)) / 1000);
if (destination < now) {
days = 0;
hours = 0;
minutes = 0;
seconds = 0;
}
if (days == 0 && hours == 0 && minutes == 0 && (seconds == 2 || seconds == 1)) {
clearTimeout(window.bmiRefreshExpired);
window.bmiRefreshExpired = setTimeout(function () {
$.bmi.reloadStaging();
}, 3000);
}
return `${('0' + days).slice(-2)}:${('0' + hours).slice(-2)}:${('0' + minutes).slice(-2)}:${('0' + seconds).slice(-2)}`;
},
fillWithNewStagings: function(sites, cb = function() {}) {
let stagingSiteNames = [];
let amountOfSites = 0;
let $tbody = $('#stg-tbody-table');
let $templateOrigin = $('.br_stg_tr_template');
let emptyText = $tbody.data('empty');
let localText = $tbody.data('local');
let prefixText = $tbody.data('prefix');
let expiresNever = $tbody.data('never');
let displayText = $tbody.data('display');
let originalText = $tbody.data('original');
let emptyElementTR = document.createElement('TR');
let emptyElementTD = document.createElement('TD');
emptyElementTD.setAttribute('colspan', '100%');
emptyElementTD.classList.add('center');
emptyElementTD.innerText = emptyText;
emptyElementTR.append(emptyElementTD);
$tbody.html('');
for (let site in sites) {
site = sites[site];
if (typeof site.name == 'undefined') continue;
if (typeof site.url == 'undefined') continue;
if (typeof site.db_prefix == 'undefined') continue;
stagingSiteNames.push(escapeHtml(site.name));
let $template = $templateOrigin.clone();
$template.removeClass('br_stg_tr_template');
$template.attr('name', escapeHtml(site.name));
$template.find('.stg-tr-name').text(escapeHtml(site.display_name));
$template.find('.stg-tr-name').attr('tooltip', `${originalText}: ${escapeHtml(site.name)}
${displayText}: ${escapeHtml(site.display_name)}`);
if (escapeHtml(site.name) == escapeHtml(site.display_name)) {
$template.find('.stg-tr-name').attr('tooltip', `${displayText}: ${escapeHtml(site.display_name)}`);
}
$template.find('.stg-tr-url-el').attr('href', escapeHtml(site.url));
$template.find('.stg-tr-url-el').attr('tooltip', escapeHtml(site.url));
$template.find('.stg-tr-url-el').text(escapeHtml(site.url));
$template.find('.stg-tr-size').text(escapeHtml(site.total_size) + ' (' + escapeHtml(site.total_files) + ')');
$template.find('.stg-tr-server').text((site.communication_secret == 'local') ? escapeHtml(localText) : 'TasteWP');
$template.find('.stg-tr-server').attr('tooltip', prefixText + ': ' + escapeHtml(site.db_prefix));
$template.find('.stg-tr-creation').text(escapeHtml(site.creation_date));
$template.find('.stg-tr-expiration span').text(escapeHtml(site.expiration_time));
if (site.communication_secret != 'local') {
$template.addClass('bmi-tastewp-staging-row');
$template.attr('server', 'tastewp');
$template.attr('token', escapeHtml(site.communication_secret));
if (isNaN(parseInt(site.expiration_time)) || site.expiration_time == expiresNever) {
$template.attr('expiration', escapeHtml(expiresNever));
$template.find('.stg-tr-expiration span').text(escapeHtml(expiresNever));
if (site.is_premium == '1') $template.find('.stg-tr-expiration span').addClass('stg-premium-stars');
} else {
$template.attr('expiration', parseInt(site.expiration_time));
$template.find('.stg-tr-expiration span').text($.bmi.getExpirationTime(parseInt(site.expiration_time)));
}
} else {
$template.attr('server', 'local');
$template.find('.stg-tr-expiration span').text(escapeHtml(expiresNever));
}
$template.hide();
$template.prependTo($tbody);
amountOfSites++;
}
if (amountOfSites == 0) {
$tbody.html(emptyElementTR);
}
if ($('#bmi-stg-subname-input').val() == '') {
let j = 1;
let defaultName = bmiVariables.stgStagingDefaultName;
for (let i = 0; i < stagingSiteNames.length; ++i) {
let name = stagingSiteNames[i];
if (stagingSiteNames.includes(defaultName)) {
defaultName = bmiVariables.stgStagingDefaultName + j;
j++;
} else break;
}
$('#bmi-stg-subname-input').val(defaultName);
}
$.bmi.tooltips.init();
$.bmi.showStagings();
},
// STATIC: Filling backups
fillWithNewBackups: function(res, ongoing, cb = function() {}, search = '') {
// let backupsList = {};
let external = res.external;
let local = res.local;
let backups = {};
// Local backups
for (let name in local) {
if (search !== '' && local[name][0].toLowerCase().indexOf(search.toLowerCase()) === -1) continue;
let backup = local[name];
backups[backup[7]] = backup;
backups[backup[7]][10] = backup[9];
backups[backup[7]][9] = backup[8];
backups[backup[7]][8] = { local: true };
}
// Google Drive and FTP backups
// for (let cloudName in external) {
// for (let md5 in external[cloudName]) {
// let backup = external[cloudName][md5];
// for (let md51 in external[cloudName]) {
// if (typeof backups[md51] == 'undefined') {
// if (cloudName === 'FTP'){
// backups[md51] = backup;
// backups[md51][9] = backup[9];
// if (typeof backups[md51][8] == 'undefined' || typeof backups[md51][8] != 'object') {
// backups[md51][8] = {local: false, ftp: backup[8]}
// } else if (typeof backups[md51][8] == 'object') {
// backups[md51][8] = backup[8];
// }
// }else if(cloudName === 'gdrive'){
// backups[md51] = backup;
// backups[md51][9] = backup[9];
// if (typeof backups[md51][8] == 'undefined' || typeof backups[md51][8] != 'object') {
// backups[md51][8] = {local: false, gdrive: backup[8]}
// } else if (typeof backups[md51][8] == 'object') {
// backups[md51][8].gdrive = backup[8];
// }
// }
// } else {
// if (cloudName === 'FTP'){
// backups[md51][8].ftp = backup[8];
// }else if(cloudName === 'gdrive'){
// backups[md51][8].gdrive = backup[8];
// }
// }
// }
// }
// }
// Google Drive backups
for (let drive in external) {
for (let backupKey in external[drive]) {
if (search !== '' && external[drive][backupKey][0].toLowerCase().indexOf(search.toLowerCase()) === -1) continue;
let backup = external[drive][backupKey];
let md5 = backup[7];
if (typeof backups[md5] == 'undefined') {
backups[md5] = {...backup};
backups[md5][9] = backup[9];
if (typeof backups[md5][8] == 'undefined' || typeof backups[md5][8] != 'object') {
backups[md5][8] = { local: false };
if (typeof backups[md5][8][drive] == 'undefined') {
backups[md5][8][drive] = backup[8];
}
} else if (typeof backups[md5][8] == 'object') {
if (typeof backups[md5][8][drive] == 'undefined') {
backups[md5][8][drive] = backup[8];
}
}
} else {
if (typeof backups[md5][8][drive] == 'undefined') {
backups[md5][8][drive] = backup[8];
}
}
}
}
$('#bmi_restore_tbody').html('');
$('.bmi-stg-dropdown-area-inner-scroll').html('');
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(bmiVariables.stgLoading);
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text('---');
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text('---');
let totalStagingPossibleBackups = 0;
let lockedtxt = $('#bmi-manual-locked').text().trim();
let backupsSorted = Object.keys(backups).sort(function(a, b) {
let x = +new Date(backups[a][1].replace(/\-/g, '/'));
let y = +new Date(backups[b][1].replace(/\-/g, '/'));
return x - y;
});
let i = 0;
for (; i < backupsSorted.length; ++i) {
let storages = [];
let index = backupsSorted[i];
let md5 = backups[index][7];
let domain = backups[index][9];
let locked = backups[index][5] === 'locked' ? true : false;
let name = backups[index][0];
let originalname = backups[index][0];
if (backups[index][0].includes('#%&')) {
name = backups[index][0].split('#%&')[1];
originalname = backups[index][0].split('#%&')[0];
}
name = name.replace(/ /g, '');
name = name.trim();
originalname = originalname.replace(/ /g, '');
originalname = originalname.trim();
let isLocalBackup = true;
let isGoogleDrive = false;
let isFtp = false;
let isDropbox = false;
let isCloud = false;
let isOneDrive = false;
let isPCloud = false;
let isAWS = false;
let isWasabi = false;
let isBackupBliss = false;
let isSftp = false;
if (typeof backups[index][8] != 'undefined') {
if (typeof backups[index][8] == 'object') {
for (let storage in backups[index][8]) {
if (storage == 'gdrive') {
isGoogleDrive = backups[index][8][storage];
} else if (storage == 'FTP') {
isFtp = backups[index][8][storage];
} else if (storage == 'dropbox') {
isDropbox = backups[index][8][storage];
} else if (storage == 'local') {
isLocalBackup = backups[index][8][storage];
} else if (storage == 'onedrive') {
isOneDrive = backups[index][8][storage];
} else if (storage == 'pcloud') {
isPCloud = backups[index][8][storage];
} else if (storage == 'aws') {
isAWS = backups[index][8][storage];
} else if (storage == 'wasabi') {
isWasabi = backups[index][8][storage];
} else if (storage == 'backupbliss') {
isBackupBliss = backups[index][8][storage];
} else if (storage == 'sftp') {
isSftp = backups[index][8][storage];
}
}
}
}
isCloud = isGoogleDrive || isFtp || isDropbox || isOneDrive || isBackupBliss || isSftp || isAWS || isWasabi || isPCloud;
let bwsc = '' + $('#bmi-backup-created-on').text().trim() + '';
let bonc = '' + $('#bmi-backup-original-name').text().trim() + '';
let bfnc = '' + $('#bmi-backup-file-name').text().trim() + '';
let id = 'bmi_br_backup_' + i;
let $template = $('.br_tr_template').clone();
$template.removeClass('br_tr_template');
$template[0].style.display = 'none';
$template.attr('md5', md5);
if (isGoogleDrive) $template.attr('gdrive-id', isGoogleDrive);
if (isFtp) $template.attr('ftp-id', isFtp);
if (isDropbox) $template.attr('dropbox-id', isDropbox);
if (!isLocalBackup && isCloud) $template.attr('data-is-local', 'no');
else $template.attr('data-is-local', 'yes');
if (isOneDrive) $template.attr('onedrive-id', isOneDrive);
if (isPCloud) $template.attr('pcloud-id', isPCloud);
if (isAWS) $template.attr('aws-id', isAWS);
if (isWasabi) $template.attr('wasabi-id', isWasabi);
if (isBackupBliss) $template.attr('backupbliss-id', isBackupBliss);
if (isSftp) $template.attr('sftp-id', isSftp);
$template.find('.br_label').attr('for', id);
$template.find('.br_checkbox').attr('id', id);
$template.find('.bc-download-btn').attr('href', $('#BMI_BLOG_URL').text().trim() + '/?backup-migration=BMI_BACKUP&bmi-id=' + name + '&sk=' + $('#BMI_SECRET_KEY').text().trim());
$template.find('.bc-logs-btn').attr('href', $('#BMI_BLOG_URL').text().trim() + '/?backup-migration=BMI_BACKUP_LOGS&bmi-id=' + name + '&sk=' + $('#BMI_SECRET_KEY').text().trim());
$template.find('.br_date').text(backups[index][1]);
$template.find('.br_name').text(name);
if (originalname == name) {
$template.find('.br_name').attr('tooltip', (bonc + ' ' + escapeHtml(originalname) + '
' + bwsc + ' ' + escapeHtml(domain)));
} else {
$template.find('.br_name').attr('tooltip', (bonc + ' ' + escapeHtml(originalname) + '
' + bfnc + ' ' + escapeHtml(name) + '
' + bwsc + ' ' + escapeHtml(domain)));
}
let readableBytesNotCut = $.bmi.bytesToHuman(backups[index][4]);
let readableBytes = $.bmi.bytesToHuman(backups[index][4]);
if (readableBytes.includes('MB') || readableBytes.includes('KB')) {
readableBytes = $.bmi.bytesToHumanCut(backups[index][4]);
}
$template.find('.br_size').text(readableBytes + ' (' + backups[index][2] + ')');
if (isGoogleDrive) {
$template.find('.br_stroage').find('.strg-gdrive').addClass('img-green');
storages.push('Google Drive');
} else {
$template.find('.br_stroage').find('.strg-gdrive').addClass('img-red');
}
// if (isLocalBackup && isCloud) {
// $template.find('.br_stroage').find('.strg-suc').addClass('img-green');
// } else {
// $template.find('.br_stroage').find('.strg-suc').addClass('img-orange');
// }
if (isFtp) {
$template.find('.br_stroage').find('.strg-ftp').addClass('img-green');
storages.push('FTP');
} else {
$template.find('.br_stroage').find('.strg-ftp').addClass('img-red');
}
if (isDropbox) {
$template.find('.br_stroage').find('.strg-dropbox').addClass('img-green');
storages.push('Dropbox');
} else {
$template.find('.br_stroage').find('.strg-dropbox').addClass('img-red');
}
if (isOneDrive) {
$template.find('.br_stroage').find('.strg-onedrive').addClass('img-green');
storages.push('OneDrive');
} else {
$template.find('.br_stroage').find('.strg-onedrive').addClass('img-red');
}
if (isPCloud) {
$template.find('.br_stroage').find('.strg-pcloud').addClass('img-green');
storages.push('pCloud');
} else {
$template.find('.br_stroage').find('.strg-pcloud').addClass('img-red');
}
if (isAWS) {
$template.find('.br_stroage').find('.strg-aws').addClass('img-green');
storages.push('AWS S3');
} else {
$template.find('.br_stroage').find('.strg-aws').addClass('img-red');
}
if (isWasabi) {
$template.find('.br_stroage').find('.strg-wasabi').addClass('img-green');
storages.push('Wasabi');
} else {
$template.find('.br_stroage').find('.strg-wasabi').addClass('img-red');
}
if (isBackupBliss) {
$template.find('.br_stroage').find('.strg-backupbliss').addClass('img-green');
storages.push('BackupBliss');
} else {
$template.find('.br_stroage').find('.strg-backupbliss').addClass('img-red');
}
if (isSftp) {
$template.find('.br_stroage').find('.strg-sftp').addClass('img-green');
storages.push('SFTP');
} else {
$template.find('.br_stroage').find('.strg-sftp').addClass('img-red');
}
if (isLocalBackup) {
$template.find('.br_stroage').find('.strg-local').addClass('img-green');
storages.push('Local Storage');
for (let key in backups[index][10]) {
if (backups[index][10].hasOwnProperty(key)) {
if ($template.find('.br_stroage').find('.strg-' + key).length > 0 && $template.find('.br_stroage').find('.strg-' + key).hasClass('img-red')) {
let storageElement = $template.find('.br_stroage').find('.strg-' + key);
let storageElementTooltip = storageElement.attr('tooltip');
storageElement.addClass('can-be-manually-uploaded');
storageElement.attr('tooltip', 'Upload To ' + storageElementTooltip);
}
}
}
} else {
$template.find('.br_stroage').find('.strg-local').addClass('img-red');
$template.find('.brow_subactions').hide();
$template.find('.brow_lock').hide();
}
//$template.find('.br_stroage').find('.strg-suc').attr('tooltip', 'Backup is stored on: ' + storages.join(', ') + '.');
if (!$('#bmi-pro-storage-gdrive-toggle').is(':checked')) {
$template.find('.br_stroage').addClass('bmi-gdrive-disabled');
}
if (!$('#bmi-pro-storage-ftp-toggle').is(':checked')) {
$template.find('.br_stroage').addClass('bmi-ftp-disabled');
}
if (!$('#bmi-pro-storage-onedrive-toggle').is(':checked')) {
$template.find('.br_stroage').addClass('bmi-onedrive-disabled');
}
if ((''+backups[index][6]).trim().length == 0 || (backups[index][6]+'') == 'false') {
$template.find('.bc-locked-btn').addClass('forever');
$template.find('.bc-locked-btn').attr('tooltip', lockedtxt);
locked = true;
}
if (locked) {
$template.find('.bc-unlocked-btn').hide();
} else {
$template.find('.bc-locked-btn').hide();
}
isDirectLinkEnabled = $('[name="radioAccessViaLink"]:checked').val() === "true";
if (!isDirectLinkEnabled) {
$template.find('.bc-url-btn svg')
.css({
cursor: "not-allowed",
color: "red"
})
.addClass('disabled');
$template.find('.bc-url-btn')
.attr('tooltip', $('#direct-link-disabled-tooltip').text().trim());
} else {
$template.find('.bc-url-btn svg')
.css({
cursor: "pointer",
color: "#B6B6B6"
})
.removeClass('disabled');
$template.find('.bc-url-btn')
.attr('tooltip', $('#direct-link-enabled-tooltip').text().trim());
}
$template.prependTo('#bmi_restore_tbody');
$('.bmi-stg-drop-option.active').removeClass('active');
if (isLocalBackup) {
let $stgOptionTemplate = $('.bmi-stg-option-template').clone();
$stgOptionTemplate.removeClass('bmi-stg-option-template');
$stgOptionTemplate[0].style.display = '';
$stgOptionTemplate.attr('backup-name', name);
$stgOptionTemplate.addClass('active');
$stgOptionTemplate.find('.bmi-stg-option-name').text(name);
$stgOptionTemplate.find('.bmi-stg-option-date i').text(backups[index][1]);
$stgOptionTemplate.find('.bmi-stg-option-size i').text(readableBytesNotCut);
$stgOptionTemplate.prependTo('.bmi-stg-dropdown-area-inner-scroll');
// if (!isLocalBackup && isGoogleDrive) { // Unreachable code
// $stgOptionTemplate.attr('gdrive-id', isGoogleDrive);
// $stgOptionTemplate.attr('data-is-local', 'no');
// } else if (isLocalBackup) {
// $stgOptionTemplate.attr('data-is-local', 'yes');
// }
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(name);
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text(backups[index][1]);
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text(readableBytesNotCut);
$('#bmi-stg-current-backup-selected').val(name);
totalStagingPossibleBackups++;
}
// backups[name].push(name);
// backupsList[id] = backups[name];
}
if ($('.bmi-stg-sel-box.bmi-active').attr('data-mode') == 'tastewp') {
$('.bmi-stg-creation-box-local').hide(300);
$('.bmi-stg-creation-box-tastewp').hide(300);
$('.bmi-stg-creation-box-tastewp-empty').hide(300);
if ($('.bmi-stg-drop-option:not(.bmi-stg-option-template)').length > 0) {
$('.bmi-stg-creation-box-tastewp').show(300);
} else {
$('.bmi-stg-creation-box-tastewp-empty').show(300);
}
} else {
$('.bmi-stg-creation-box-local').show(300);
$('.bmi-stg-creation-box-tastewp').hide(300);
$('.bmi-stg-creation-box-tastewp-empty').hide(300);
}
if (i == 0) {
let empty = $('#bmi_restore_tbody').data('empty');
$('#bmi_restore_tbody').html('| ' + empty + ' |
');
}
$.bmi.tooltips.init();
$.bmi.showMoreBackups();
if ($('.bmi-stg-sel-box.bmi-active').data('mode') == 'tastewp') {
$('.bmi-stg-creation-box-local').hide(300);
$('.bmi-stg-creation-box-tastewp').hide(300);
$('.bmi-stg-creation-box-tastewp-empty').hide(300);
if ($('#bmi_restore_tbody').find('tr:not(.bmi-empty-text)').length > 0) {
$('.bmi-stg-creation-box-tastewp').show(300);
} else {
$('.bmi-stg-creation-box-tastewp-empty').show(300);
}
}
if (typeof ongoing != 'undefined') $.bmi.fillOnGoing(ongoing);
cb();
},
// STATIC: Loading 10 more backups
showMoreBackups: function(res) {
backups = {};
let trs = $('#bmi_restore_tbody').find('tr:hidden');
for (let i = 0;
(i < trs.length && i < 10); ++i) {
setTimeout(function() {
$(trs[i]).show(300);
}, (i * 50));
}
if ((trs.length - 10) <= 0) {
$('#load-more-backups-wrp').hide(300);
} else {
$('#load-more-backups-wrp').show(300);
}
},
// STATIC: Animate staging list show
showStagings: function() {
backups = {};
let trs = $('#stg-tbody-table').find('tr:hidden');
for (let i = 0; i < trs.length; ++i) {
setTimeout(function() {
$(trs[i]).show(300);
}, (i * 50));
}
},
setBackupProgressList: function(md5, type, tooltip = false) {
//let suc = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-suc');
let warn = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-warn');
let ong = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-ong');
let wait = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-wait');
//suc.hide();
warn.hide();
ong.hide();
wait.hide();
// if (type == 'suc') {
// suc.show();
// if (tooltip) suc.tooltipster('content', tooltip);
// }
if (type == 'warn') {
warn.show();
if (tooltip) warn.tooltipster('content', tooltip);
}
if (type == 'ong') {
ong.show();
if (tooltip) ong.tooltipster('content', tooltip);
}
if (type == 'wait') {
wait.show();
if (tooltip) wait.tooltipster('content', tooltip);
}
},
fillOnGoing: function(ongoing = false) {
if (ongoing && typeof ongoing.queue != 'undefined' && typeof ongoing.current_upload != 'undefined') {
for (let task in ongoing.queue) {
let data = ongoing.queue[task];
let md5 = data.md5;
uploadType = task.split('_')[0];
//Replaces existing img- class or add it with new
$('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-' + uploadType).attr('class', function(i, cls) {
return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-purple') : cls + ' img-purple';
});
$.bmi.setBackupProgressList(md5, 'wait');
}
if (typeof ongoing.current_upload == 'object') {
if (ongoing.current_upload.length == 0) {
if (ongoing_latest_token) {
//$.bmi.reloadBackups();
ongoing_failed = false;
if (typeof ongoing.failed != 'undefined') {
ongoing_failed = currentUploadType + "_" + ongoing_latest_token in ongoing.failed;
}
$('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, ongoing_failed ? 'img-red' : 'img-green') : cls + ongoing_failed ? ' img-red' : ' img-green';
});
$.bmi.setBackupProgressList(ongoing_latest_token, 'done');
ongoing_latest_token = false;
}
} else {
if (!ongoing_latest_token) {
ongoing_latest_token = ongoing.current_upload.md5;
}
if (ongoing_latest_token && ongoing_latest_token != ongoing.current_upload.md5) {
//$.bmi.reloadBackups();
ongoing_failed = false;
if (typeof ongoing.failed != 'undefined') {
ongoing_failed = currentUploadType + "_" + ongoing_latest_token in ongoing.failed;
}
$('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, ongoing_failed ? 'img-red' : 'img-green') : cls + ongoing_failed ? ' img-red' : ' img-green';
});
$.bmi.setBackupProgressList(ongoing_latest_token, 'done');
ongoing_latest_token = ongoing.current_upload.md5;
}
let progress = '0%';
if (typeof ongoing.current_upload.progress != 'undefined') progress = ongoing.current_upload.progress;
//Detect type
currentUploadType = ongoing.current_upload.task.split('_')[0];
let message = 'Upload to ';
let externalStorageType = 'Google Drive';
switch (currentUploadType) {
case 'ftp':
externalStorageType = 'FTP';
break;
case 'dropbox':
externalStorageType = 'Dropbox';
break;
case 'onedrive':
externalStorageType = 'OneDrive';
break;
case 'aws':
externalStorageType = 'Amazon S3';
break;
case 'wasabi':
externalStorageType = 'Wasabi';
break;
case 'pcloud':
externalStorageType = 'pCloud';
case 'backupbliss':
externalStorageType = 'BackupBliss';
break;
case 'sftp':
externalStorageType = 'SFTP';
break;
}
//Replaces existing img- class or add it with new
$('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-orange') : cls + ' img-orange';
});
message += externalStorageType;
$.bmi.setBackupProgressList(ongoing.current_upload.md5, 'ong', message + ' in progress: ' + progress);
}
}
}
if (typeof ongoing.failed != 'undefined') {
for (let token in ongoing.failed) {
data = token.split("_");
md5 = data[1];
uploadType = data[0];
if (!(ongoing.current_upload instanceof Array))
if (ongoing.current_upload.md5 == md5) // Checks if the current upload task is of the same backup the failed task is
return; //There's an ongoing upload for the same backup, so will not show any failures to show the upload status.
// if (typeof ongoing.queue[token] != 'undefined') {
// md5 = ongoing.queue[token].md5;
// }
// if (typeof ongoing.current_upload.task != 'undefined' && ongoing.current_upload.task == token) {
// md5 = ongoing.current_upload.md5;
// }
if (md5) {
//Replaces existing img- class or add it with new
$('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-' + uploadType).attr('class', function(i, cls) {
return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-red') : cls + ' img-red';
});
let message = 'There was an error during upload to: ';
if (uploadType == null || uploadType == 'gdrive') externalStorageType = 'Google Drive';
if (uploadType == 'ftp') externalStorageType = 'FTP';
if (uploadType == 'dropbox') externalStorageType = 'Dropbox';
if (uploadType == 'pcloud') externalStorageType = 'pCloud';
if (uploadType == 'onedrive') externalStorageType = 'OneDrive';
if (uploadType == 'aws') externalStorageType = 'AWS';
if (uploadType == 'wasabi') externalStorageType = 'Wasabi';
if (uploadType == 'backupbliss') externalStorageType = 'BackupBliss';
if (uploadType == 'sftp') externalStorageType = 'SFTP';
message += externalStorageType;
$.bmi.setBackupProgressList(md5, 'warn', message);
}
}
}
// Update tooltips of external storage icons with upload status
const tooltips = {
'img-green': 'Backup is available on %s.',
'img-orange': 'Upload to %s is in progress.',
'img-purple': 'Upload to %s is queued.',
'img-red': 'Backup is not available on %s.',
};
const $tbody = $('#bmi_restore_tbody');
let storagesToStr = {
'strg-local': 'Local Storage',
'strg-ftp': 'FTP',
'strg-aws': 'AWS S3',
'strg-backupbliss': 'BackupBliss',
'strg-sftp': 'SFTP',
'strg-gdrive': 'Google Drive',
'strg-onedrive': 'OneDrive',
'strg-pcloud': 'pCloud',
'strg-wasabi': 'Wasabi',
'strg-dropbox': 'Dropbox',
}
Object.entries(tooltips).forEach(([imgClass, template]) => {
$tbody.find(`.${imgClass}`).each(function () {
const $el = $(this);
const classes = $el.attr('class').split(/\s+/);
if (classes.includes('can-be-manually-uploaded')) return;
const strgClass = classes.find(c => c.startsWith('strg-'));
if (!strgClass || ['strg-suc', 'strg-warn', 'strg-ong', 'strg-wait'].includes(strgClass)) return;
let cap = '';
cap = storagesToStr[strgClass] || strgClass.replace('strg-', '').charAt(0).toUpperCase() + strgClass.replace('strg-', '').slice(1);
const tooltip = template.replace('%s', cap);
$el.tooltipster('content', tooltip);
});
});
},
adjustStorageIcons: function() {
let es = ['gdrive', 'onedrive', 'sftp', 'ftp', 'dropbox', 'aws', 'wasabi', 'pcloud'];
let activeEsCount = 2;
for (let i = 0; i < es.length; ++i) {
if ($('#bmi-pro-storage-' + es[i] + '-toggle').is(':checked')) {
$('.strg-' + es[i]).show();
activeEsCount++;
} else {
$('.strg-' + es[i]).hide();
}
}
if ($('.storage-icons-container').length == 0) return;
if (activeEsCount > 5 ) {
$('.storage-icons-container').css('grid-template-rows', 'repeat(2, auto)');
} else {
$('.storage-icons-container').css('grid-template-rows', 'none');
}
},
// STATIC: Reload backups list
reloadBackups: function(callagain = function() {}) {
$.bmi.getCurrentBackups(function(res) {
$.bmi.fillWithNewBackups(res.backups, res.backups.ongoing, function() {
callagain();
$('#backups-select-all').prop('checked', false);
$('.del-all-btn-wrp').hide(300);
});
});
},
// STATIC: Reload staging list
reloadStaging: function(callagain = function() {}) {
$.bmi.getCurrentStaging(function(res) {
$.bmi.fillWithNewStagings(res.sites, function() {
callagain();
});
});
},
searchInBackups: function(keyword, callagain = function() {}) {
$.bmi.getCurrentBackups(function(res) {
$.bmi.fillWithNewBackups(res.backups, res.backups.ongoing, function() {
callagain();
}, keyword );
});
},
// STATIC: Hides all dropdowns
hideAllLists: function() {
let opens = $('.dropdown-open');
if (opens.length <= 0) return;
for (let i = 0; i < opens.length; ++i) {
let open = $(opens[i]);
if (open.hasClass('ignored-open')) {
open.removeClass('ignored-open');
} else {
open.hide(300);
open.removeClass('dropdown-open');
$(open[0].closest('.bmi-dropdown')).removeClass('active');
}
}
},
// Sets and option
setOption: function($dropdown, $option = null, value = false) {
let $optlist = $dropdown.find('.dropdown-options');
if ($dropdown.attr('data-optioner')) {
$optlist = $('.optioner-' + $dropdown.attr('data-optioner'));
}
$optlist.find('.active-option').removeClass('active-option');
if (value !== false) {
$option = $optlist.find('.dropdown-option[data-value="' + value + '"]');
}
if ($($option).hasClass('active-option')) return;
$dropdown.find('.dropdown-title').text($option.text());
$dropdown.attr('data-selected', $option.data('value'));
$option.addClass('active-option');
if (value === false) {
$dropdown.change();
}
$.bmi.hideAllLists();
},
// STATIC: Prepare file frmo text
prepareFile: function(filename, text) {
let element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
$.bmi.alert('success', $('#bmi-download-should-start').text(), 3000);
},
// Utility to escape html
escapeHtml: function(text)
{
return escapeHtml(text)
}
}
});
});
jQuery(document).ready(function($) {
$('.collapser .header').on('click', function(e) {
e.preventDefault();
let el = $(this).parent('.collapser')[0];
$.bmi.collapsers.toggle(el);
});
$('.bmi_will_collapse').on('change', function(e) {
e.preventDefault();
let doNotShow = false;
if (this.getAttribute('type') == 'radio') {
if (this.getAttribute('value') == 'false') {
doNotShow = true;
}
}
$child = $(this).data('if-checked');
if (this.checked === true && !doNotShow) {
$('#' + $child).show(300);
} else $('#' + $child).hide(300);
});
$('.collapser-openner').on('click', function(e) {
e.preventDefault();
let group = 'configuration';
let el = $(this).data('el');
if ($(this).data('group')) {
group = $(this).data('group');
}
$.bmi.collapsers.open(el, group);
setTimeout(function() {
$([document.documentElement, document.body]).animate({
scrollTop: $(el).offset().top - 50 + 'px'
}, 300);
}, 300);
});
(function() {
let $triggers = $('.bmi_will_collapse');
for (let i = 0; i < $triggers.length; ++i) {
let doNotShow = false;
$trigger = $triggers[i];
$child = $($trigger).data('if-checked');
if ($trigger.getAttribute('type') == 'radio') {
if ($trigger.getAttribute('value') == 'false') {
doNotShow = true;
}
}
if ($trigger.checked === true) {
$('#' + $child).show();
} else $('#' + $child).hide();
}
})();
});jQuery(document).ready(function($) {
var firsttime = true;
var initialized = false;
$.bmi.crons = function() {
if (initialized === true) return;
else initialized = true;
if (location.href.includes("crons=true") && location.href.includes("page=backup-migration")){
setTimeout(function(){
$('#i-backup-cron').click();
},300);
}
function getCronResult(settings, done) {
$.bmi.ajax('calculate-cron', settings).then(function(res) {
if (res.status == 'success') {
done(res);
} else {
done(false);
$.bmi._msg(res);
}
}).catch(function(error) {
done(false);
console.log(error);
});
}
function settings_changed() {
let settings = {
type: $('[data-id="cron-period"]').attr('data-selected'),
day: $('[data-id="cron-day"]').attr('data-selected'),
week: $('[data-id="cron-week"]').attr('data-selected'),
hour: $('[data-id="cron-hour"]').attr('data-selected'),
minute: $('[data-id="cron-minute"]').attr('data-selected'),
keep: $('[data-id="cron-keep-backups"]').attr('data-selected'),
enabled: ((!$('#cron-btn-toggle')[0].checked === true) ? true : false)
}
getCronResult(settings, function(res) {
if (res.status === 'success' && res !== false) {
$('.cron-time-server').tooltipster('option', 'interactive', false);
$('.cron-time-server').tooltipster('option', 'contentAsHTML', true);
if ($('#cron-btn-toggle')[0].checked === true) res.data = '---';
$('.cron-time-server').tooltipster('content', '' + $('#bmi-next-cron').text() + '' + res.data + '
' + '' + $('#bmi-current-time').text() + '' + res.currdata);
if (!firsttime){
if (res.local_site) {
$.bmi.alert('warning', $('#bmi-cron-local-site').text(), 30000);
} else if (!res.ping_working) {
$.bmi.alert('warning', $('#bmi-cron-ping-server').text(), 30000);
} else {
$.bmi.alert('success', $('#bmi-cron-updated').text(), 1500);
}
}
else firsttime = false;
} else {
if (!firsttime)
$.bmi.alert('error', $('#bmi-cron-updated-fail').text(), 2500);
else firsttime = false;
}
});
}
$('#bmi').on('change', '[data-id="cron-period"]', function(e) {
settings_changed();
adjust_text(e.target.getAttribute('data-selected'));
});
$('#bmi').on('change', '[data-id="cron-day"]', function(e) {
let val = e.target.getAttribute('data-selected');
settings_changed();
});
$('#bmi').on('change', '[data-id="cron-week"]', function(e) {
let val = e.target.getAttribute('data-selected');
settings_changed();
});
$('#bmi').on('change', '[data-id="cron-hour"]', function(e) {
let val = e.target.getAttribute('data-selected');
settings_changed();
});
$('#bmi').on('change', '[data-id="cron-minute"]', function(e) {
let val = e.target.getAttribute('data-selected');
settings_changed();
});
$('#bmi').on('change', '[data-id="cron-keep-backups"]', function(e) {
let val = e.target.getAttribute('data-selected');
settings_changed();
});
$('#i-backup-cron').on('click', function() {
if ($('.cron-backups').find('.turned-off').is(':visible')) {
$('#cron-btn-toggle').prop('checked', false);
$('.cron-backups').removeClass('disabled');
settings_changed();
$('.cron-backups').find('.turned-on').css({
opacity: 0
});
$('.cron-backups').find('.turned-off').css({
opacity: 0
});
$('.cron-backups').find('.turned-on').show();
$('.cron-backups').find('.turned-on').css({
opacity: 1
});
setTimeout(function() {
$('.cron-backups').find('.turned-off').hide();
}, 300);
}
});
$('#cron-btn-toggle').on('change', function() {
if (!this.checked) {
$('.cron-backups').removeClass('disabled');
} else {
$('.cron-backups').addClass('disabled');
}
settings_changed();
});
function adjust_text(val) {
if (val == 'month') {
$('.cron-the').show();
$('[data-id="cron-day"]').show();
$('[data-id="cron-week"]').hide();
$('#cron-on-word').show();
} else if (val == 'week') {
$('.cron-the').hide();
$('[data-id="cron-day"]').hide();
$('[data-id="cron-week"]').show();
$('#cron-on-word').show();
} else {
$('.cron-the').hide();
$('[data-id="cron-day"]').hide();
$('[data-id="cron-week"]').hide();
$('#cron-on-word').hide();
}
}
(function() {
adjust_text($('[data-id="cron-period"]')[0].getAttribute('data-selected'));
settings_changed();
})();
}
});jQuery(document).ready(function($) {
$.bmi.dropdowns = {
init: function() {
let $r = $('#bmi');
function scanSelects() {
let selects = $r.find('select');
for (let i = 0; i < selects.length; ++i) {
let select = selects[i];
handleSelect(select);
}
}
function createDropdown(title = '---') {
let dropdown = $('.dropdown-template').clone();
dropdown.removeClass('dropdown-template');
dropdown.find('.dropdown-title').text(title);
return dropdown;
}
function createOption(value = 'null', text = '---') {
let option = $('.option-template').clone();
option.removeClass('option-template');
option.attr('data-value', value);
option.text(text);
return option;
}
function longestOption(options) {
let longest = '';
for (let i = 0; i < options.length; ++i) {
let len = options[i].innerText;
if (longest.length < len.length) longest = len;
}
return longest;
}
function handleSelect(select) {
if (select.style.display != 'none') {
select.style.display = 'none';
let $select = $(select);
let options = $select.find('option');
let longest = longestOption(options);
let dropdown = createDropdown(longest);
dropdown = handleOptions(dropdown, options);
let def = options[0].value;
let parent = select.getAttribute('data-parent');
let classes = select.getAttribute('data-classes');
let isHidden = select.getAttribute('data-hide') === 'true' ? true : false;
if ($select.attr('data-def')) def = $select.attr('data-def');
$(dropdown).attr('data-id', select.id);
$(dropdown).attr('class', ((classes != null ? classes : '') + ' bmi-dropdown').trim());
$(dropdown).insertBefore($select);
if (parent !== null) {
let num = parseInt(Math.random() * (987654321 - 123456789) + 123456789);
$(dropdown).attr('data-optioner', num);
$(dropdown).find('.dropdown-options').attr('data-oparent', num);
$(dropdown).find('.dropdown-options').addClass('optioner-' + num);
$(dropdown).find('.dropdown-options').appendTo(parent);
$(parent).css({ position: 'relative' });
}
let clone = $(dropdown).clone();
if (!clone) return;
clone[0].style.visibility = 'hidden';
$r.append(clone);
let width = clone.width();
clone.remove();
$.bmi.setOption($(dropdown), null, def);
let fixedWidth = $select.attr('data-width');
if (fixedWidth) width = parseInt(fixedWidth);
$(dropdown).find('.dropdown-title')[0].style.minWidth = (width + 10) + 'px';
let readonly = $select.attr('data-readonly');
if (readonly == 'true') {
$(dropdown).find('.dropdown-title')[0].style.pointerEvents = 'none';
$(dropdown).closest('.bmi-dropdown')[0].style.background = '#f1f1f1';
}
if (isHidden) $(dropdown).hide();
}
}
function handleOptions(dropdown, options) {
let list = $(dropdown).find('.dropdown-options');
if ($(dropdown).attr('data-optioner')) {
list = $('.optioner-' + $(dropdown).attr('data-optioner'));
}
for (let i = 0; i < options.length; ++i) {
let option = options[i];
list.append(createOption(option.value, option.innerText));
}
return dropdown;
}
function toggleDropdown($dropdown) {
let $list = $dropdown.find('.dropdown-options');
if ($dropdown.attr('data-optioner')) {
$list = $('.optioner-' + $dropdown.attr('data-optioner'));
$list.css({
position: 'absolute'
});
let width = $dropdown[0].offsetWidth;
let left = $dropdown[0].offsetLeft + 240;
let top = $dropdown[0].offsetTop + $dropdown[0].offsetHeight + 5;
$list.css({
maxWidth: width + 'px',
minWidth: width + 'px',
top: top + 'px',
left: left + 'px'
});
}
if ($list.hasClass('dropdown-open')) {
$dropdown.removeClass('active');
$list.removeClass('dropdown-open');
$list.hide(300);
} else {
$dropdown.addClass('active');
$list.addClass('dropdown-open');
$list.addClass('ignored-open');
$list.show(300);
}
}
// Initlaization
scanSelects();
// Init crons after init of drop
$.bmi.crons();
// Events
$r.on('click', function(e) {
if (!($(e.target).hasClass('bmi-dropdown') || e.target.closest('.bmi-dropdown'))) $.bmi.hideAllLists();
});
$r.on('click', '.dropdown-title', function(e) {
$dropdown = $(e.target.closest('.bmi-dropdown'));
toggleDropdown($dropdown);
$.bmi.hideAllLists();
});
$r.on('click', '.dropdown-option', function(e) {
if ($(e.target.closest('.dropdown-options')).attr('data-oparent')) {
$dropdown = $('.bmi-dropdown[data-optioner="' + $(e.target.closest('.dropdown-options')).attr('data-oparent') + '"]');
$option = $(e.target);
$.bmi.setOption($dropdown, $option);
} else {
$dropdown = $(e.target.closest('.bmi-dropdown'));
$option = $(e.target);
$.bmi.setOption($dropdown, $option);
}
});
}
}
});
jQuery(document).ready(function ($) {
var backups = {};
var bacupisDone = false,
progressIsDone = false;
var timeouter,
curdivs = 0,
iprogres;
var stopBackgroundBackup = false;
let tryingToAbortBackup = false;
let totalSize = 0,
totalExcluded = 0,
totalSizesI = 0;
let current_restore = null;
let current_last = null;
let latest_delete = null;
let latest_delete_cloud = null;
let backupOnGoing = false;
let restoreOnGoing = false;
let ongoing_interval = 10000;
let triggeredByIntv = false;
let backgroundBackup = false;
let backgroundName = null;
let headers_for_middleware = false;
let cli_quickmigration = false;
let restoreCLI = false;
let autoLog = {};
let latestLines = [];
let lineTimeoutAppend = null;
let isLineAppendTimeoutRunning = false;
let latestStep = "";
let currentGDriveIdRestoration = null;
let restoreStartTime = null;
let downloadBackupFinished = false;
let restoreLogs = null;
window.bmi = {};
function isChecked(id) {
$cb = $("#" + id).is(":checked");
if ($cb === true) return "true";
else return "false";
}
function getSelectedSize() {
size = 0;
if ($("#files-group-backup").is(":checked")) {
if ($("#files-group-plugins").is(":checked")) {
size += parseInt(
$('label[for="files-group-plugins"]').find(".value").attr("bytes"),
);
}
if ($("#files-group-uploads").is(":checked")) {
size += parseInt(
$('label[for="files-group-uploads"]').find(".value").attr("bytes"),
);
}
if ($("#files-group-themes").is(":checked")) {
size += parseInt(
$('label[for="files-group-themes"]').find(".value").attr("bytes"),
);
}
if ($("#files-group-other-contents").is(":checked")) {
size += parseInt(
$('label[for="files-group-other-contents"]')
.find(".value")
.attr("bytes"),
);
}
if ($("#files-group-wp-install").is(":checked")) {
size += parseInt(
$('label[for="files-group-wp-install"]').find(".value").attr("bytes"),
);
}
}
if ($("#database-group-backup").is(":checked")) {
size += parseInt(
$('label[for="database-group-backup"]').find(".value").attr("bytes"),
);
}
return size;
}
function locationSet(location, res) {
totalSizesI += 1;
if (location != "database") totalSize += parseInt(res["bytes"]);
totalExcluded += parseInt(res["excluded"]);
if (res["bytes"] <= 10000) {
$("#bmi-scan-" + location)
.find(".value")
.text("(0 MB)");
} else {
let megas = (parseInt(res["bytes"]) / 1024 / 1024).toFixed(2);
$("#bmi-scan-" + location)
.find(".value")
.text("(" + megas + " MB)");
}
$("#bmi-scan-" + location)
.find(".value")
.attr("bytes", parseInt(res["bytes"]));
$("#bmi-scan-" + location)
.find(".value")
.attr("excluded", parseInt(res["excluded"]));
if (totalSizesI == 6) doSomethingWithTotal();
}
function scanAndSet(location) {
return new Promise(function (resolve) {
$.bmi
.ajax("scan-directory", {
folder: location,
})
.then(function (res) {
setTimeout(resolve, 150);
locationSet(location, res);
})
.catch(function (error) {
setTimeout(resolve, 150);
locationSet(location, {
bytes: "0",
excluded: "0",
readable: "0 B",
});
});
});
}
function resetSpinners(resources, withTotal = false) {
if (withTotal) {
$("#bmi-scan-total")
.find(".value")
.html('()');
$("#bmi-scan-total").find(".value").removeAttr("bytes");
}
for (let i = 0; i < resources.length; i++) {
$("#bmi-scan-" + resources[i])
.find(".value")
.html('()');
$("#bmi-scan-" + resources[i])
.find(".value")
.removeAttr("bytes");
}
$("#esta-exclude").html('()');
$("#esta-exclude-total").html('()');
$("#esta-size-for").html('()');
let preloader_divs = "";
for (let i = 0; i < 12; ++i) preloader_divs += "";
$(".spinner-loader").html(preloader_divs).addClass("lds-spinner");
}
async function scanDirectories() {
totalSizesI = 0;
totalSize = 0;
totalExcluded = 0;
let resources = [
"plugins",
"uploads",
"themes",
"contents_others",
"wordpress",
"database",
];
await resetSpinners(resources, true);
for (let i = 0; i < resources.length; i++) {
await scanAndSet(resources[i]);
}
}
function doSomethingWithTotal() {
$("#bmi-scan-total")
.find(".value")
.text("(" + $.bmi.bytesToHuman(totalSize) + ")");
$("#bmi-scan-total").find(".value").attr("bytes", parseInt(totalSize));
updateEsta();
}
// Live log
$("#live-log-toggle").on("click", function () {
if ($(".expanded-logs").length > 0) {
$(this).text($(this).data("hide"));
} else $(this).text($(this).data("show"));
$("#live-log-wrapper").toggleClass("expanded-logs");
});
async function stopBackgroundProcess() {
await forceBackupToStop(true);
window.onbeforeunload = null;
window.location.reload();
}
// Stop backup
$("#backup-stop").on("click", function () {
$.bmi.modal("backup-progress-modal").close();
$.bmi.modal("freeze-loading-modal").open();
tryingToAbortBackup = true;
$.bmi
.ajax("stop-backup", {})
.then(function (res) {
if (res.status == "success") {
$.bmi.modal("freeze-loading-modal").close();
$.bmi.alert("success", $("#bmi-abort-soon").text(), 3000);
} else $.bmi._msg(res);
})
.catch(function (error) {
//
});
});
// Is backup running
function isRunning(mute = false, cb = function () { }) {
$.bmi
.ajax("is-running-backup", {})
.then(function (res) {
if (typeof res.ongoing != "undefined") {
$.bmi.fillOnGoing(res.ongoing);
if (typeof res.ongoing.queue == 'object' || typeof res.ongoing.current_upload == 'object') {
if (!(res.ongoing.queue instanceof Array) || !(res.ongoing.current_upload instanceof Array)) {
if (typeof (globalBMIKeepAlive) == "function") {
globalBMIKeepAlive();
}
}
else {
$.bmi.ajax('check-not-uploaded-backups').then(function(res) {
// console.warn('Checking for not uploaded backups...');
// console.log(res);
});
}
}
}
if (res.status == "success") cb(false);
else {
if (mute == true && res.status == "msg") cb(true);
else {
$.bmi.modal("freeze-loading-modal").close();
$.bmi._msg(res);
}
}
})
.catch(function (error) {
cb(false);
$.bmi.modal("freeze-loading-modal").close();
//
});
}
function forceBackupToStop(withNotice = false) {
return new Promise((resolve) => {
$.bmi
.ajax("force-backup-to-stop")
.then(function (res) {
if (withNotice) {
$.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
}
return resolve(true);
})
.catch(function (error) {
if (withNotice) {
$.bmi.alert("error", $("#failed-to-stop").text(), 6000);
}
return resolve(false);
});
});
}
$("#bmi-force-backup-to-stop").on("click", function (e) {
e.preventDefault();
$.bmi.alert("info", $("#bmi-force-stop-in-progress").text(), 3000);
$.bmi
.ajax("force-backup-to-stop")
.then(function (res) {
$.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
})
.catch(function (error) {
$.bmi.alert("error", $("#failed-to-stop").text(), 6000);
});
});
$("#bmi-force-restore-to-stop").on("click", function (e) {
e.preventDefault();
$.bmi.alert("info", $("#bmi-force-stop-in-progress").text(), 3000);
$.bmi
.ajax("force-restore-to-stop")
.then(function (res) {
$.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
})
.catch(function (error) {
$.bmi.alert("error", $("#failed-to-stop").text(), 6000);
});
});
$("#bmi_restore_tbody").on("click", ".bc-unlocked-btn", function (e) {
e.preventDefault();
let $el = e.target;
let name = $el.closest("tr").querySelector(".br_name").innerText.trim();
$.bmi
.ajax("lock-backup", {
filename: name,
})
.then(function (res) {
if (res.status == "success") {
$.bmi.alert("success", $("#bmi-lock-success").text(), 6000);
$($el.closest("tr").querySelector(".bc-unlocked-btn")).hide();
$($el.closest("tr").querySelector(".bc-locked-btn")).show();
} else {
$.bmi.alert("error", $("#bmi-lock-error").text(), 8000);
console.error("BMI Backend error: ", res);
}
})
.catch(function (error) {
//
});
});
function updateEsta() {
let pluginsSize = parseInt(
$('label[for="files-group-plugins"]').find(".value").attr("bytes"),
);
let uploadsSize = parseInt(
$('label[for="files-group-uploads"]').find(".value").attr("bytes"),
);
let themesSize = parseInt(
$('label[for="files-group-themes"]').find(".value").attr("bytes"),
);
let otherSize = parseInt(
$('label[for="files-group-other-contents"]').find(".value").attr("bytes"),
);
let wpSize = parseInt(
$('label[for="files-group-wp-install"]').find(".value").attr("bytes"),
);
let dbSize = parseInt(
$('label[for="database-group-backup"]').find(".value").attr("bytes"),
);
let pluginsExcluded = parseInt(
$('label[for="files-group-plugins"]').find(".value").attr("excluded"),
);
let uploadsExcluded = parseInt(
$('label[for="files-group-uploads"]').find(".value").attr("excluded"),
);
let themesExcluded = parseInt(
$('label[for="files-group-themes"]').find(".value").attr("excluded"),
);
let otherExcluded = parseInt(
$('label[for="files-group-other-contents"]')
.find(".value")
.attr("excluded"),
);
let wpExcluded = parseInt(
$('label[for="files-group-wp-install"]').find(".value").attr("excluded"),
);
let dbExcluded = parseInt(
$('label[for="database-group-backup"]').find(".value").attr("excluded"),
);
let databaseExcludedSizeInBytes = 0;
let excludedSize =
pluginsExcluded +
uploadsExcluded +
themesExcluded +
otherExcluded +
wpExcluded +
dbExcluded;
let totalSize =
excludedSize +
pluginsSize +
uploadsSize +
themesSize +
otherSize +
wpSize +
dbSize;
if ($("#files-group-backup").is(":checked")) {
if (!$("#files-group-plugins").is(":checked"))
excludedSize += pluginsSize;
if (!$("#files-group-uploads").is(":checked"))
excludedSize += uploadsSize;
if (!$("#files-group-themes").is(":checked")) excludedSize += themesSize;
if (!$("#files-group-other-contents").is(":checked"))
excludedSize += otherSize;
if (!$("#files-group-wp-install").is(":checked")) excludedSize += wpSize;
} else {
excludedSize +=
pluginsSize + uploadsSize + themesSize + otherSize + wpSize;
}
if ($("#database-group-backup").is(":checked")) {
if (
$("#bmi_total_size_excluded").length > 0 &&
$("#bmi-pro-db-tables-exclusion").is(":checked")
) {
databaseExcludedSizeInBytes =
parseFloat($("#bmi_total_size_excluded").text().split("/")[0]) *
1024 *
1024;
if (!isNaN(databaseExcludedSizeInBytes))
excludedSize += databaseExcludedSizeInBytes;
}
} else excludedSize += dbSize;
$("#esta-exclude").text($.bmi.bytesToHuman(excludedSize));
$("#esta-exclude-total").text($.bmi.bytesToHuman(totalSize));
$("#esta-size-for").text($.bmi.bytesToHuman(totalSize - excludedSize));
}
$("#files-group-backup").on("change", updateEsta);
$("#database-group-backup").on("change", updateEsta);
$(".basic-file-exlusion").on("change", 'input[type="checkbox"]', updateEsta);
$("#bmi_restore_tbody").on("click", ".bc-locked-btn", function (e) {
e.preventDefault();
if ($(e.target).hasClass("forever")) return;
let $el = e.target;
let name = $el.closest("tr").querySelector(".br_name").innerText.trim();
$.bmi
.ajax("unlock-backup", {
filename: name,
})
.then(function (res) {
if (res.status == "success") {
$.bmi.alert("success", $("#bmi-unlock-success").text(), 3000);
$($el.closest("tr").querySelector(".bc-locked-btn")).hide();
$($el.closest("tr").querySelector(".bc-unlocked-btn")).show();
} else {
$.bmi.alert("error", $("#bmi-unlock-error").text(), 3000);
console.error("BMI Backend error: ", res);
}
})
.catch(function (error) {
//
});
});
$("#bmi_restore_tbody").on("click", ".bc-url-btn", function (e) {
e.preventDefault();
let $el = e.target;
if ($el.closest("svg").classList.contains("disabled")) return;
let url = $el
.closest("tr")
.querySelector(".bc-download-btn")
.getAttribute("href");
$.bmi.clipboard(url, bmiVariables.urlCopies);
});
$("#stg-tbody-table").on("click", ".bc-stg-url-btn", function (e) {
let $el = e.target;
let url = $el
.closest("tr")
.querySelector(".stg-tr-url-el")
.getAttribute("href");
$.bmi.clipboard(url, bmiVariables.urlCopies);
});
$("#bmi_restore_tbody").on("click", ".bc-remove-btn", function (e) {
let $el = e.target;
let name = $el.closest('tr').querySelector('.br_name').innerText.trim();
let isCloud = Array.from($el.closest('tr').querySelectorAll('[class*="strg-"]')).filter(el => !el.classList.contains('strg-local')).some(el => el.classList.contains('img-green'));
let notOnLocalTr = $($el.closest('tr')).data('is-local') == 'no' ? true : false;
latest_delete = [name];
latest_delete_cloud = {};
latest_delete_cloud[name] = {
md5: $el.closest("tr").getAttribute("md5"),
gid: $el.closest("tr").getAttribute("gdrive-id"),
};
$("#delete-confirm-modal").find(".text2").hide();
$("#delete-confirm-modal").find(".text3").hide();
$("#delete-confirm-modal").find(".text4").hide();
$("#delete-confirm-modal").find(".text1").show();
if (isCloud) $(".bmi-cloud-removal").show();
else $(".bmi-cloud-removal").hide();
$("#remove-cloud-backup-as-well")[0].checked = false;
if (isCloud && notOnLocalTr) {
$("#remove-cloud-backup-as-well")[0].checked = true;
$(".bmi-cloud-removal").hide();
$("#delete-confirm-modal").find(".text1").hide();
$("#delete-confirm-modal").find(".text2").hide();
$("#delete-confirm-modal").find(".text3").hide();
$("#delete-confirm-modal").find(".text4").show();
$(".del-more-than-one").hide();
$(".del-only-one").show();
}
$.bmi.modal("delete-confirm-modal").open();
});
$("#sure_delete").on("click", function (e) {
e.preventDefault();
$.bmi.modal("delete-confirm-modal").close();
$.bmi.modal("freeze-loading-modal").open();
$.bmi
.ajax("delete-backup", {
filenames: latest_delete,
deleteCloud: $("#remove-cloud-backup-as-well")[0].checked
? "yes"
: "no",
cloudDetails: latest_delete_cloud,
})
.then(function (res) {
latest_delete = null;
latest_delete_cloud = null;
$("#remove-cloud-backup-as-well")[0].checked = false;
$.bmi.modal("freeze-loading-modal").close();
if (res.status == "success") {
$.bmi.reloadBackups(() => {
$.bmi.reloadBackups(); // twice on purpose
});
$.bmi.alert("success", $("#bmi-remove-success").text(), 3000);
} else {
$.bmi.alert("warning", $("#bmi-remove-error").text(), 3000);
console.error("BMI Backend error: ", res);
}
})
.catch(function (error) {
$.bmi.modal("freeze-loading-modal").close();
});
});
$(".bmi-send-troubleshooting-logs").on("click", function (e) {
e.preventDefault();
$.bmi.alert("info", $("#bmi-support-send-start").text(), 6000);
$(".bmi-send-troubleshooting-logs").addClass("disabled");
let errorVisible = false;
let stagingErrorVisible = false;
if ($("#error-modal").is(":visible")) {
$.bmi.modal("error-modal").close();
errorVisible = true;
stagingErrorVisible = false;
}
if ($("#staging-error-modal").is(":visible")) {
$.bmi.modal("staging-error-modal").close();
errorVisible = false;
stagingErrorVisible = true;
}
$.bmi.modal("freeze-loading-modal").open();
let logsSource = $("#after-logs-sent-modal").attr("data-error-source");
$.bmi
.ajax("send-troubleshooting-logs", { source: logsSource })
.then(function (res) {
$.bmi.modal("freeze-loading-modal").close();
if (res.status == "success") {
$("#bmi-support-code-generated").text(res.code);
setTimeout(function () {
$.bmi.alert("success", $("#bmi-support-send-success").text(), 4000);
$.bmi.modal("after-logs-sent-modal").open();
}, 300);
} else {
$.bmi.alert("error", $("#bmi-support-send-fail").text(), 6000);
if (errorVisible) $.bmi.modal("error-modal").open();
if (stagingErrorVisible) $.bmi.modal("staging-error-modal").open();
}
$(".bmi-send-troubleshooting-logs").removeClass("disabled");
})
.catch(function (error) {
$.bmi.modal("freeze-loading-modal").close();
$.bmi.alert("success", $("#bmi-support-send-fail").text(), 6000);
alert(
"Something went wrong on your browser side and we could not send your logs to support team.",
);
$(".bmi-send-troubleshooting-logs").removeClass("disabled");
if (errorVisible) $.bmi.modal("error-modal").open();
if (stagingErrorVisible) $.bmi.modal("staging-error-modal").open();
});
});
$("#share-logs-allowed").on("click", function (e) {
e.preventDefault();
let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
$.bmi.alert("success", $("#bmi-share-logs-thank-you").text(), 3000);
$.bmi.modal("logs-sharing-ask-modal").close();
$.bmi.modal("freeze-loading-modal").open();
shareLogsStatus("set_yes", function () {
setTimeout(function () {
handleAfterDesicionMadeSharing(newModalName);
}, 300);
});
});
$("#share-logs-not-allowed").on("click", function (e) {
e.preventDefault();
let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
$.bmi.modal("logs-sharing-ask-modal").close();
$.bmi.modal("freeze-loading-modal").open();
shareLogsStatus("set_no", function () {
setTimeout(function () {
handleAfterDesicionMadeSharing(newModalName);
}, 300);
});
});
$("#ignore-share-log-request-for-now").on("click", function (e) {
e.preventDefault();
$.bmi.modal("logs-sharing-ask-modal").close();
let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
handleAfterDesicionMadeSharing(newModalName);
});
function handleAfterDesicionMadeSharing(modalName) {
if (modalName == "backup-prenotice") {
runPrenoticeBeforeBackup();
} else {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
$.bmi.modal(modalName).open();
}, 300);
}
}
function isAllowedToShareLogs(cb = function () { }) {
shareLogsStatus("is_allowed", function (res) {
cb(res);
});
}
function shareLogsStatus(type, cb = function () { }) {
// Res to: is_allowed (res.result)
// allowed
// not-allowed
// ask
//
// For set_yes, set_no there is no response only status success or fail
if (type == "is_allowed") cb("not-allowed");
else cb();
// DISABLED:
// $.bmi.ajax('log-sharing-details', { question: type }).then(function(res) {
//
// if (res.status == 'success') {
//
// if (type == 'is_allowed') {
//
// if (typeof res.result != 'undefined') {
// if (['allowed', 'not-allowed', 'ask'].includes(res.result)) {
// cb(res.result);
// } else {
// cb('not-allowed')
// }
// }
//
// } else {
//
// cb();
//
// }
//
// } else {
//
// cb('error');
//
// }
//
// }).catch(function(error) {
//
// alert('Something went wrong on browser side and we could not send your decision, for now we will assume that you have not agreed, more details in developer console.');
// cb('error');
//
// });
}
$("#add-exclusion-rule").on("click", function (e) {
e.preventDefault();
let $template = $(".exclusion_template").clone();
$template[0].classList.remove("exclusion_template");
$template[0].style.display = "none";
$("#bmi_exclusion_rules").append($template);
$template.show(300);
});
$("#bmi_exclusion_rules").on("click", ".kill-exclusion-rule", function (e) {
e.preventDefault();
$el = e.target;
$parent = $el.closest(".exclude-row");
if ($parent) {
$($parent).hide(300);
setTimeout(function () {
$parent.remove();
}, 320);
}
});
function isOnlyDB() {
if (!(isChecked("database-group-backup") === "true")) {
return false;
} else {
if (isChecked("database-group-backup") === "true") {
if (!(isChecked("files-group-backup") === "true")) {
return true;
} else {
if (
isChecked("files-group-plugins") === "true" ||
isChecked("files-group-themes") === "true" ||
isChecked("files-group-uploads") === "true" ||
isChecked("files-group-wp-install") === "true" ||
isChecked("files-group-other-contents") === "true"
) {
return false;
}
}
} else return false;
}
}
function isOnlyFiles() {
if (isChecked("database-group-backup") === "true") {
return false;
} else {
if (!(isChecked("database-group-backup") === "true")) {
if (
isChecked("files-group-backup") === "true" &&
(isChecked("files-group-plugins") === "true" ||
isChecked("files-group-themes") === "true" ||
isChecked("files-group-uploads") === "true" ||
isChecked("files-group-wp-install") === "true" ||
isChecked("files-group-other-contents") === "true")
) {
return true;
} else return false;
} else return false;
}
}
function isOnlyPart() {
if (
isChecked("database-group-backup") === "true" &&
isChecked("files-group-backup") === "true" &&
isChecked("files-group-plugins") === "true" &&
isChecked("files-group-themes") === "true" &&
isChecked("files-group-uploads") === "true" &&
isChecked("files-group-other-contents") === "true"
) {
return false;
}
return true;
}
function isFilesOrDb() {
if (isChecked("database-group-backup") === "true") {
return true;
} else {
if (isChecked("files-group-backup") === "true") {
if (
isChecked("files-group-plugins") === "true" ||
isChecked("files-group-themes") === "true" ||
isChecked("files-group-uploads") === "true" ||
isChecked("files-group-wp-install") === "true" ||
isChecked("files-group-other-contents") === "true"
) {
return true;
}
}
}
return false;
}
function setupPrenotice() {
$("#prenotice-modal .prenotice").hide();
$(".prenotic-3").show();
if (isOnlyDB()) {
$(".prenotic-6").show();
}
if (isOnlyFiles()) {
$(".prenotic-5").show();
}
if (isOnlyPart()) {
$(".prenotic-4").show();
}
$("#prenotice-modal .prenotice:visible").css({
background: "",
});
let prenotices = $("#prenotice-modal .prenotice"),
sorted = [];
for (let i = 0; i < prenotices.length; ++i) {
if (!(prenotices[i].style.display === "none")) {
sorted.push(prenotices[i]);
}
}
for (let i = 0; i < sorted.length; i += 2) {
sorted[i].style.background = "#f8f8f8";
}
}
// Backup creation
$("#i-backup-creator, .i-backup-creator-trigger").on("click", function () {
if (!isFilesOrDb()) {
$.bmi.alert("warning", $("#bmi-no-selected").text(), 3000);
return;
}
$.bmi.modal("freeze-loading-modal").open();
setTimeout(function () {
shareLogsStatus("is_allowed", function (res) {
if (res === "ask") {
$.bmi.modal("freeze-loading-modal").close();
$("#logs-sharing-ask-modal").attr(
"data-destination",
"backup-prenotice",
);
setTimeout(function () {
$.bmi.modal("logs-sharing-ask-modal").open();
}, 300);
} else {
runPrenoticeBeforeBackup();
}
});
}, 300);
});
function runPrenoticeBeforeBackup() {
$.bmi.modal("freeze-loading-modal").open();
setupPrenotice();
isRunning(false, function () {
setTimeout(function () {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
$.bmi.modal("prenotice-modal").open();
}, 300);
}, 300);
});
}
$("#BFFSIN").on("change", function (e) {
let i = parseInt(this.value);
if (isNaN(i)) {
this.value = 1;
return;
} else {
if (i > 9999) {
this.value = 9999;
return;
}
if (i <= 0) {
this.value = 1;
return;
}
this.value = i;
}
});
function saveBtnEventHandler(e = false, type = false, cb = () => { }) {
if (e) e.preventDefault();
let data = {};
let save = type;
if (e != false && type == false)
save = $(this.closest(".save-action")).data("save");
if (!save) return;
if (save == "save-storage") {
data["directory"] = $("#bmi_path_storage_default").val();
data["access"] =
$('[name="radioAccessViaLink"]:checked').val() === "true"
? "true"
: "false";
data["gdrive"] =
$("#bmi-pro-storage-gdrive-toggle").is(":checked") === true
? "true"
: "false";
if ($("#bmip-googledrive-path").length > 0) {
data["gdrivedirname"] = $("#bmip-googledrive-path").val().trim();
} else {
data["gdrivedirname"] = "BACKUP_MIGRATION_BACKUPS";
}
// onedrive
data["onedrive"] =
$("#bmi-pro-storage-onedrive-toggle").is(":checked") === true
? "true"
: "false";
data["sftp"] = $("#bmi-pro-storage-sftp-toggle").is(":checked") === true ? 'true' : 'false';
// storage ftp
data['ftp'] = $('#bmi-pro-storage-ftp-toggle').is(':checked') === true ? 'true' : 'false';
if ($('#bmip-ftp-host-ip').length > 0) {
data['ftphostip'] = $('#bmip-ftp-host-ip').val().trim();
} else {
data['ftphostip'] = '';
}
data['dropbox'] = $('#bmi-pro-storage-dropbox-toggle').is(':checked') === true ? 'true' : 'false';
data['pcloud'] = $('#bmi-pro-storage-pcloud-toggle').is(':checked') === true ? 'true' : 'false';
if ($('#bmip-ftp-user-name').length > 0) {
data['ftphostusername'] = $('#bmip-ftp-user-name').val().trim();
} else {
data['ftphostusername'] = '';
}
if ($('#bmip-ftp-password').length > 0) {
data['ftppassword'] = $('#bmip-ftp-password').val().trim();
} else {
data['ftppassword'] = '';
}
if ($('#bmip-ftp-backup-dir').length > 0) {
data['ftpdir'] = $('#bmip-ftp-backup-dir').val().trim();
} else {
data['ftpdir'] = '';
}
if ($('#bmip-ftp-host-port').length > 0) {
data['ftpport'] = $('#bmip-ftp-host-port').val().trim();
} else {
data['ftpport'] = '';
}
data['aws'] = $('#bmi-pro-storage-aws-toggle').is(':checked') === true ? 'true' : 'false';
data['wasabi'] = $('#bmi-pro-storage-wasabi-toggle').is(':checked') === true ? 'true' : 'false';
} else if (save == 'save-file-config') {
// Main groups
data["database_group"] = isChecked("database-group-backup");
data["files_group"] = isChecked("files-group-backup");
// Additional database exclusion rules
if ($("#bmi-pro-db-tables-exclusion").length > 0) {
data["db-exclude-tables-group"] = isChecked(
"bmi-pro-db-tables-exclusion",
);
data["db-excluded-tables"] = ["empty"];
let excludedTables = $(".bmi_pro_tables_display").find("input:checked");
if (excludedTables.length > 0) {
data["db-excluded-tables"] = [];
for (let i = 0; i < excludedTables.length; ++i) {
data["db-excluded-tables"].push(excludedTables[i].value);
}
} else {
data["db-exclude-tables-group"] = "false";
}
} else {
data["db-exclude-tables-group"] = "false";
data["db-excluded-tables"] = ["empty"];
}
// Subgroup of files
data["files-group-plugins"] = isChecked("files-group-plugins");
data["files-group-uploads"] = isChecked("files-group-uploads");
data["files-group-themes"] = isChecked("files-group-themes");
data["files-group-other-contents"] = isChecked(
"files-group-other-contents",
);
data["files-group-wp-install"] = isChecked("files-group-wp-install");
// Subgroup of filters of files
data["files_by_filters"] = isChecked("files_by_filters");
data["ex_b_fs"] = isChecked("ex_b_fs");
data["BFFSIN"] = $("#BFFSIN").val() ? $("#BFFSIN").val() : "1";
data["ex_b_names"] = isChecked("ex_b_names");
data["ex_b_fpaths"] = isChecked("ex_b_fpaths");
data["ex_b_dpaths"] = isChecked("ex_b_dpaths");
// Make dynamic
let rules = [];
let rows = $("#bmi_exclusion_rules").find(".exclude-row");
for (let i = 0; i < rows.length; ++i) {
let row = $(rows[i]);
let txt = row.find(".exclusion_txt").val();
let pos =
row
.find(".exclusion_position")
.find(".bmi-dropdown")
.data("selected") + "";
let whr =
row.find(".exclusion_where").find(".bmi-dropdown").data("selected") +
"";
rules.push({
txt: txt,
pos: pos,
whr: whr,
});
}
// Continue data
data["dynamic-names"] = rules;
data["dynamic-fpaths-names"] = $("#dynamic-fpaths-names")
.val()
.split("\n");
data["dynamic-dpaths-names"] = $("#dynamic-dpaths-names")
.val()
.split("\n");
data["smart-exclusion-enabled"] = isChecked("smart-exclusion-enabled"); // SMART:EXCLUSION:ENABLED
data["smart-exclusion-cache"] = isChecked("smart-exclusion-cache"); // SMART:EXCLUSION:CACHE
data["smart-exclusion-deactivated-plugins"] = isChecked("smart-exclusion-deactivated-plugins"); // SMART:EXCLUSION:DPLUGINS
data["smart-exclusion-debug-logs"] = isChecked("smart-exclusion-debug-logs"); // SMART:EXCLUSION:DLOGS
data["smart-exclusion-non-used-themes"] = isChecked("smart-exclusion-non-used-themes"); // SMART:EXCLUSION:NUTHEMES
data["smart-exclusion-post-revisions"] = isChecked("smart-exclusion-post-revisions"); // SMART:EXCLUSION:PREVISIONS
} else if (save == "store-config") {
data["name"] = $("#backup_filename").val().trim();
if ($("input[name=backup_type_extension]")) {
data["extension"] = $(
"input[name=backup_type_extension]:checked",
).val();
} else {
data["extension"] = ".zip";
}
data["direct_cloud_streaming"] = isChecked("direct-cloud-streaming"); // STREAM:DIRECT_CLOUD_STREAMING:ENABLED
const selected = document.querySelector(
'input[name="direct_cloud_provider"]:checked'
);
const value = selected ? selected.value : null;
data["direct_cloud_provider"] = value; // STREAM:DIRECT_CLOUD_STREAMING:PROVIDER
data["storage_strategy"] = document.querySelector('input[name="backup_storage_strategy"]:checked')?.value; // STORAGE:STRATEGY
} else if (save == "save-other-options") {
data["email"] = $("#email-for-notices").val().trim();
data["email_title"] = $("#email-title-for-notices").val().trim();
data["schedule_issues"] = isChecked("scheduled-issues");
data["experiment_timeout"] = isChecked("experimental-timeout");
data["experimental_hard_timeout"] = isChecked(
"experimental-hard-timeout",
);
data["php_cli_disable_others"] = isChecked("cli-disable-others");
data["php_cli_manual_path"] = $("#cli-manual-path").val().trim();
data["download_technique"] = isChecked("download-technique");
data["uninstall_config"] = isChecked("uninstalling-configs");
data["uninstall_backups"] = isChecked("uninstalling-backups");
data["normal_timeout"] = isChecked("normal-timeout");
data["backup_success_notify"] = isChecked("backup-success-notify");
data["restore_success_notify"] = isChecked("restore-success-notify");
data["backup_failed_notify"] = isChecked("backup-failed-notify");
data["restore_failed_notify"] = isChecked("restore-failed-notify");
data["generate_debug_code"] =
isChecked("generate-debug-code-yes") == "true" &&
isChecked("generate-debug-code-no") !== "true";
data["add_logs_email"] =
isChecked("add-logs-email-yes") == "true" &&
isChecked("add-logs-email-no") !== "true";
data["db_queries_amount"] = $("#db_queries_amount").val().trim(); // OTHER:DB:QUERIES
data["db_search_replace_max"] = $("#db_search_replace_max").val().trim(); // OTHER:DB:SEARCHREPLACE:MAX
data["file_limit_extraction_max"] = $("#file_limit_extraction_max")
.val()
.trim(); // OTHER:FILE:EXTRACT:MAX
data["bmi-restore-splitting"] = isChecked("bmi-restore-splitting"); // OTHER:RESTORE:SPLITTING
data["bmi-db-v3-restore-engine"] = isChecked("bmi-db-v3-restore-engine"); // OTHER:RESTORE:SPLITTING
data["remove-assets-before-restore"] = isChecked(
"remove-assets-before-restore",
); // OTHER:RESTORE:BEFORE:CLEANUP
data["hide-promotional-bmi-banners"] = isChecked(
"hide-promotional-bmi-banners",
); // OTHER:PROMOTIONAL:DISPLAY
data["bmi-db-single-file-backup"] = isChecked(
"bmi-db-single-file-backup",
); // OTHER:BACKUP:DB:SINGLE:FILE
data["bmi-db-batching-backup"] = isChecked("bmi-db-batching-backup"); // OTHER:BACKUP:DB:BATCHING
data["bmi-disable-space-check-function"] = isChecked(
"bmi-do-not-check-free-space-backup",
); // OTHER:BACKUP:SPACE:CHECKING
data["before_update_trigger"] = ((!$('#before-updates-switch')[0].checked === true) ? true : false) // OTHER:TRIGGER:BEFORE:UPDATES
data["use_new_search_replace_engine"] = isChecked("bmi-use-new-search-replace-engine"); // OTHER::NEW_SEARCH_REPLACE_ENGINE
data["use_new_database_export_engine"] = isChecked("bmi-use-new-database-export-engine"); // OTHER::NEW_DATABASE_EXPORT_ENGINE
} else return;
$.bmi
.ajax(save, data)
.then(function (res) {
if (res.status == "success") {
if (res.errors <= 0) {
if ($('.save-brw-details').length > 0) $('.save-brw-details').triggerHandler('click');
if (e) $.bmi.alert("success", $("#bmi-save-success").text(), 3000);
$.bmi.reloadBackups();
$.bmi.adjustStorageIcons();
if (
typeof window.saveStorageWithoutClose != "undefined" &&
window.saveStorageWithoutClose === true
) {
window.saveStorageWithoutClose = false;
} else {
if (e) $.bmi.collapsers.closeAll();
}
} else {
if (e) $.bmi.alert("warning", $("#bmi-save-issues").text(), 3000);
}
} else {
$.bmi._msg(res);
}
})
.catch(function (error) {
//
})
.finally(() => {
if (save == "save-file-config") {
scanDirectories();
}
cb();
});
}
$(".save-btn").on("click", saveBtnEventHandler);
$(".close-chapters").on("click", function (e) {
e.preventDefault();
$.bmi.collapsers.closeAll();
});
$("#rescan-for-backups").on("click", function (e) {
e.preventDefault();
$.bmi.reloadBackups();
});
// Storage things
$(".storage-checkbox").on("click", function () {
let target = $(this).attr("data-toggle");
let tab = $($(this)[0].closest(".tab2-item"));
if ($(this).is(":checked")) {
$("#" + target).show(300);
tab.addClass("activeList");
} else {
$("#" + target).hide(300);
tab.removeClass("activeList");
}
});
function httpGet(theUrl, callback = function () { }) {
let isHttps = window.location.protocol.includes("https");
let isUrlHttps = theUrl.includes("https");
if (isUrlHttps) theUrl = theUrl.slice(5);
else theUrl = theUrl.slice(4);
if (isHttps) theUrl = "https" + theUrl;
else theUrl = "http" + theUrl;
try {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onloadend = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback(xmlhttp.responseText);
} else callback(false);
};
xmlhttp.open("GET", theUrl);
xmlhttp.send();
} catch (e) {
callback(false);
}
}
function getEndCode() {
let endCode = document.querySelector('.hide_so_much');
if (endCode && endCode.innerText) {
let code_line = endCode.innerText.trim();
const result = code_line.match(/#(.+)$/)?.[1];
return result || false;
} else return false;
}
function goThroughEndCodes() {
let stepSet = false;
let endCodeFound = false;
for (let i = 0; i < latestLines.length; ++i) {
let line = latestLines[i];
if (
endCodeFound == false &&
line &&
line.trim().includes("[END-CODE]") &&
(backgroundBackup === true ||
restoreCLI === true ||
cli_quickmigration === true)
) {
endCodeFound = true;
let code_line = line;
if (cli_quickmigration === true) {
if (code_line.includes("205")) {
cli_quickmigration = false;
$("#restore-progress-modal .progress-active-bar")[0].style.width =
0 + "%";
$("#restore-progress-modal .progress-percentage")[0].style.left =
0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
letsRestore(true);
} else if (code_line.includes("206")){
$.bmi.modal("restore-progress-modal").close();
$('#restore-progress-modal .title').text($('#bmi-restore-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-restore-progress-modal-warning').text());
setTimeout(function () {
if (!downloadBackupFinished) { // Avoid double success message
$.bmi.alert('success', $('#bmi-backup-downloaded').text(), 3000);
downloadBackupFinished = true;
}
}, 300);
} else {
restoreFailed();
}
} else if (restoreCLI === true) {
if (code_line.includes("001")) {
setTimeout(function () {
restoreCLISuccess();
}, 1000);
} else {
restoreFailed();
}
restoreOnGoing = false;
restoreCLI = false;
} else {
backgroundBackup = false;
if (code_line.includes("001")) {
bacupisDone = true;
progressIsDone = true;
clearTimeout(timeouter);
backupOnGoing = false;
completedBackup({ filename: backgroundName }, true);
} else if (code_line.includes("002")) {
// $.bmi.modal('backup-progress-modal').close();
backupError(1);
} else if (code_line.includes("003")) {
$.bmi.modal("backup-progress-modal").close();
$.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
tryingToAbortBackup = false;
backupOnGoing = false;
} else if (code_line.includes("100")) {
$.bmi.modal("backup-progress-modal").close();
handleBfs();
$.bmi.modal("backup-progress-modal").close();
} else {
backupError(2);
}
}
}
}
return endCodeFound;
}
function showNextLine(el) {
let delayedDisplayTime = 40;
let line = latestLines[curdivs];
let div = document.createElement("DIV");
let color = "";
if (typeof line == "undefined" || !line) return;
if (line.substr(0, 6) == "[INFO]") color = "blue";
else if (line.substr(0, 9) == "[SUCCESS]") color = "green";
else if (line.substr(0, 6) == "[WARN]") color = "orange";
else if (line.substr(0, 7) == "[ERROR]") color = "red";
else if (line.substr(0, 10) == "[END-CODE]") color = "hide_so_much";
else if (line.substr(0, 9) == "[VERBOSE]") color = "hide_verbose";
else if (line.substr(0, 6) == "[STEP]") {
div.classList.add("bold");
div.classList.add("step");
} else {
if (line && line.trim().length > 0 && line[0] != "[") {
curdivs--;
}
}
let mostRecentStep = "";
if (line.substr(0, 6) == "[STEP]") mostRecentStep = line.slice(29);
if (color.length > 0) div.classList.add(color);
div.style.display = "none";
div.innerText = line;
el.appendChild(div);
let endCodeFound = goThroughEndCodes();
$(div).show(delayedDisplayTime, function () {
if (mostRecentStep != "" && mostRecentStep != latestStep) {
latestStep = mostRecentStep;
if ($("#restore-progress-modal").hasClass("open")) {
$("#restore_current_step").text(mostRecentStep);
} else {
$("#current_step").text(mostRecentStep);
}
}
});
el.scrollTop = el.scrollHeight;
curdivs++;
if (curdivs < latestLines.length && endCodeFound == false) {
isLineAppendTimeoutRunning = true;
lineTimeoutAppend = setTimeout(function () {
showNextLine(el);
}, delayedDisplayTime);
} else {
isLineAppendTimeoutRunning = false;
}
}
function showAllLines(el, latestLines) {
for (let i = 0; i < latestLines.length; i++) {
let line = latestLines[i];
let div = document.createElement("DIV");
let color = "";
if (typeof line == "undefined" || !line) continue;
if (line.substr(0, 6) == "[INFO]") color = "blue";
else if (line.substr(0, 9) == "[SUCCESS]") color = "green";
else if (line.substr(0, 6) == "[WARN]") color = "orange";
else if (line.substr(0, 7) == "[ERROR]") color = "red";
else if (line.substr(0, 10) == "[END-CODE]") color = "red";
else if (line.substr(0, 9) == "[VERBOSE]") color = "gray";
else if (line.substr(0, 6) == "[STEP]") {
div.classList.add("bold");
div.classList.add("step");
} else {
if (line && line.trim().length > 0 && line[0] != "[") {
i--;
}
}
let mostRecentStep = "";
if (line.substr(0, 6) == "[STEP]") mostRecentStep = line.slice(29);
if (color.length > 0) div.classList.add(color);
div.style.display = "none";
div.innerText = line;
el.appendChild(div);
$(div).show();
}
}
function insertPre(log, el) {
if (log === false) return;
let lines = log.split("\n");
if (lines.length >= 1) lines = lines.slice(0, -1);
latestLines = lines;
if (lines.length >= 1) {
applyActionBasedOnLogLines(lines);
}
if (isLineAppendTimeoutRunning == false) {
if (curdivs < latestLines.length) {
showNextLine(el);
}
}
}
function setProgress(end = 0, duration = 1000, res = null) {
if (current_last == end) return;
else current_last = end;
clearInterval(iprogres);
let start = parseFloat($(".progress-percentage")[0].style.left) - 1;
if ($("#restore-progress-modal").hasClass("open")) {
start =
parseFloat(
$("#restore-progress-modal .progress-percentage")[0].style.left,
) - 1;
}
// if (start > end && end != 0) return;
let range = end - start;
let current = start;
let increment = 1;
let stepTime = Math.abs(Math.floor(duration / range));
iprogres = setInterval(function () {
current += increment;
if ($("#restore-progress-modal").hasClass("open")) {
$("#restore-progress-modal .progress-active-bar")[0].style.width =
current.toFixed(2) + "%";
$("#restore-progress-modal .progress-percentage")[0].style.left =
current.toFixed(2) + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
current.toFixed(0) + "%";
} else {
$(".progress-active-bar")[0].style.width = current.toFixed(2) + "%";
$(".progress-percentage")[0].style.left = current.toFixed(2) + "%";
$(".progress-percentage")[0].innerText = current.toFixed(0) + "%";
}
if (current >= 100) {
clearInterval(iprogres);
}
if (current >= 100 && res != null) {
current_last = null;
if ($("#backup-progress-modal").hasClass("open")) {
bacupisDone = true;
progressIsDone = true;
let force = backupOnGoing == false ? true : false;
completedBackup(res, force);
}
}
if (current > end) clearInterval(iprogres);
}, stepTime);
}
var curmaxnum = 0;
async function animateValue(obj, start, end, duration) {
let startTimestamp = null;
let step = function (timestamp) {
if (curmaxnum > end) return;
if (!startTimestamp) startTimestamp = timestamp;
let progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerText = Math.floor(progress * (end - start) + start);
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
function refreshContentInstant(cb = function () { }) {
let url = $("#BMI_BLOG_URL").text().trim();
if (url.slice(-url.length) !== "/") url = url + "/";
httpGet(
url +
"?backup-migration=PROGRESS_LOGS&progress-id=latest_full.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim(),
function (res2) {
if (!res2) return cb();
let res1 = res2.split("\n").slice(0, 1)[0];
if (res1.trim() === "") {
res1 = res2.split("\n").slice(0, 2)[1];
res2 = res2.split("\n").slice(2).join("\n");
} else {
res2 = res2.split("\n").slice(1).join("\n");
}
let pre = $(".log-wrapper").find("pre")[0];
let making = $("#bmi-making-archive").text().trim();
if (res1 && res1 != false && typeof res1 !== "undefined") {
if (
$("#current_step").text().trim().slice(0, making.length) == making
) {
let obj = document.getElementById("bmi_counter_magic");
if (obj) {
let currnum = parseInt(obj.innerText);
curmaxnum = parseInt(res1.split("/")[0]);
if (!isNaN(currnum) && !isNaN(curmaxnum)) {
setProgress(
(parseInt(res1.split("/")[0]) /
parseInt(res1.split("/")[1])) *
100,
);
animateValue(obj, currnum, curmaxnum, 2000);
if (
$("#bmi_magic_max_count").text() === "---" &&
parseInt(res1.split("/")[1]) != 100
) {
$("#bmi_magic_max_count").text(parseInt(res1.split("/")[1]));
if ($("#entire_magic_counter").is(":hidden"))
$("#entire_magic_counter").show();
}
}
} else {
let numv = parseInt(res1.split("/")[1]);
if (numv == 100) numv = "---";
$("#current_step").html(
making +
' (0/' +
numv +
")",
);
if (isNaN(parseInt(numv)) || numv == "---")
$("#entire_magic_counter").hide();
}
} else {
setProgress(
(parseInt(res1.split("/")[0]) / parseInt(res1.split("/")[1])) *
100,
);
}
}
if (res2 && res2 != false && typeof res2 !== "undefined")
insertPre(res2, pre);
cb();
},
);
}
function refreshLogAndProgress(preserveLogs = false) {
if (preserveLogs == false) {
$(".log-wrapper").find("pre")[0].innerText = "";
}
setProgress(0);
function update() {
refreshContentInstant(function () {
clearTimeout(timeouter);
timeouter = setTimeout(function () {
if (backupOnGoing === true) update();
}, 1500);
});
}
setTimeout(function () {
refreshContentInstant(function () {
update();
});
}, 300);
}
bmi.refreshLogAndProgress = refreshLogAndProgress;
function refreshLogAndProgressRestore(resetLogs = true) {
if (resetLogs === true) {
$(".log-wrapper").find("pre")[0].innerText = "";
setProgress(0);
}
function update() {
getMigrationLogs(function () {
timeouter = setTimeout(function () {
if (restoreOnGoing === true || cli_quickmigration === true) update();
}, 800);
});
}
getMigrationLogs(function () {
clearTimeout(timeouter);
update();
});
}
function isBackupOngoing(done = function () { }) {
isRunning(true, function (res) {
done(res);
});
}
function resetLogsPromise(migration = false) {
return new Promise((resolve) => {
resetLogs(migration, () => {
return resolve();
});
});
}
bmi.resetLogsPromise = resetLogsPromise;
function resetLogs(migration = false, done = function () {}) {
$.bmi
.ajax("reset-latest", {})
.then(function (res) {
if (migration === true) {
done();
} else {
if (res.status == "success") {
done();
setTimeout(function () {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
// Make sure the progress modal is open
$.bmi.modal("backup-progress-modal").open();
}, 300);
}, 300);
} else {
$.bmi._msg(res);
}
}
})
.catch(function (error) {
//
});
}
function completedBackup(res, forced = false) {
if (!(bacupisDone && progressIsDone) && !forced) return;
$.bmi.releaseWakeLock();
setTimeout(function () {
backupOnGoing = false;
clearInterval(iprogres);
clearTimeout(timeouter);
let url_x = $("#BMI_BLOG_URL").text().trim();
if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
$.bmi
.ajax("get-latest-backup", {})
.then(function (res_latest) {
let url =
url_x +
"?backup-migration=BMI_BACKUP&bmi-id=" +
res_latest +
"&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
let logs =
url_x +
"?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
$("#text-input-copy")[0].value = url;
$("#download-backup-url").attr("href", url);
$(".download-backup-log-url").attr("href", logs);
$("#bmi-streamed-backup-name")[0].innerText = res_latest;
$.bmi.reloadBackups();
setTimeout(function () {
clearInterval(iprogres);
$(".log-wrapper").find("pre")[0].innerText = "";
$(".progress-active-bar")[0].style.width = "0%";
$(".progress-percentage")[0].style.left = "0%";
$(".progress-percentage")[0].innerText = "0%";
}, 300);
if ($("#backup-progress-modal").hasClass("open")) {
$.bmi.modal("backup-progress-modal").close();
if ($('[name="radioAccessViaLink"]:checked').val() == "true") {
$("#accessible-at-section").show();
} else $("#accessible-at-section").hide();
$.bmi.modal("backup-success-modal").open();
}
})
.catch(function (error) {
if ($("#backup-progress-modal").hasClass("open")) {
$.bmi.modal("backup-progress-modal").close();
if ($('[name="radioAccessViaLink"]:checked').val() == "true") {
$("#accessible-at-section").show();
} else $("#accessible-at-section").hide();
$.bmi.modal("backup-success-modal").open();
}
let url =
url_x +
"?backup-migration=BMI_BACKUP&bmi-id=" +
res.filename +
"&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
let logs =
url_x +
"?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
$("#text-input-copy")[0].value = url;
$("#download-backup-url").attr("href", url);
$(".download-backup-log-url").attr("href", logs);
$.bmi.reloadBackups();
setTimeout(function () {
clearInterval(iprogres);
$(".log-wrapper").find("pre")[0].innerText = "";
$(".progress-active-bar")[0].style.width = "0%";
$(".progress-percentage")[0].style.left = "0%";
$(".progress-percentage")[0].innerText = "0%";
}, 300);
});
}, 700);
}
function sleepFunction(seconds = 1) {
return new Promise(async (resolve) => {
setTimeout(() => {
return resolve();
}, 1000 * seconds);
});
}
function saveOtherOptionsPromise() {
return new Promise(async (resolve) => {
saveBtnEventHandler(false, "save-other-options", resolve);
});
}
function tryToSaveBackupProcess(errorDetails = false) {
return new Promise(async (resolve) => {
let details = errorDetails;
if (details === false) return resolve(false);
if (typeof details != "object") return resolve(false);
if (typeof details[0] == "undefined") return resolve(false);
if (typeof details[0]["status"] == "undefined") return resolve(false);
if (typeof details[0]["statusText"] == "undefined") return resolve(false);
if (typeof details[0]["responseText"] == "undefined")
return resolve(false);
let status = details[0]["status"];
let statusText = details[0]["statusText"];
let responseText = details[0]["responseText"];
let isCLIDisabled = isChecked("cli-disable-others");
let isDefaultBackup = isChecked("normal-timeout");
let isCURLBackup = isChecked("experimental-timeout");
let isBrowserBackup = isChecked("experimental-hard-timeout");
if (isBrowserBackup == true && isCLIDisabled == true)
return resolve(false);
// 1. Force stop backup process
await forceBackupToStop();
// 2. Adjust Other Options
$("#cli-disable-others").prop("checked", true);
$("#experimental-hard-timeout").prop("checked", true).change();
// 3. Save Other options
await saveOtherOptionsPromise();
await sleepFunction(3);
// 4. Run backup process from point zero without reseting logs
setTimeout(() => {
startBackupProcessNow(null, true);
});
// Resolve as true
return resolve(true);
});
}
async function backupError(type = -1, errorDetails = false) {
clearInterval(iprogres);
clearTimeout(timeouter);
$(".progress-active-bar")[0].style.width = "0%";
$(".progress-percentage")[0].style.left = "0%";
$(".progress-percentage")[0].innerText = "0%";
if (errorDetails && (await tryToSaveBackupProcess(errorDetails))) {
return;
}
$.bmi.releaseWakeLock();
await cleanUpAfterError();
console.error("Backup error type:", type);
setTimeout(function () {
$(".log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("backup-progress-modal").close();
setupBackupErrorOptions().then(() => {
setTimeout(function () {
$.bmi.modal("error-modal").open();
$.bmi.modal("error-modal").setParent("backup-progress-modal");
}, 300);
});
$("#after-logs-sent-modal").attr("data-error-source", "backup");
}, 2000);
}
bmi.backupError = backupError;
async function cleanUpAfterError() {
await $.bmi.ajax("clean-up-after-error", {});
}
function handleBfs() {
$.bmi.modal("bfs-modal").open();
}
function sendRequestOfBackupPart(callback, errors = 0) {
let callbackSent = false;
let cb = (data) => {
if (callbackSent == false) {
callbackSent = true;
return callback(data);
}
};
$.bmi
.ajax("backup-browser-method", {})
.then(function (res) {
if (typeof res.status != "undefined" && res.status == "success") {
if (
typeof res.status != "undefined" &&
res.backup_process_error == "true"
) {
errors++;
return callback({ status: false, errors: errors });
}
if (
typeof res.backup_completed != "undefined" &&
res.backup_completed == "true"
)
return callback({ status: true, errors: errors });
else return callback({ status: false, errors: errors });
} else {
// Increment the errors
errors++;
console.error(res);
return callback({ status: false, errors: errors });
}
})
.catch(function (error) {
// Increment the errors
errors++;
console.error(error);
// Return with a tiemouted retry
return callback({ status: false, errors: errors });
});
// // Require browser client
// let http = new XMLHttpRequest();
// // Open POST connection
// http.open('POST', h.url, true);
// // Send proper headers with settings
// http.setRequestHeader('Content-Type', 'application/json');
// http.setRequestHeader('Content-Accept', '*/*');
// http.setRequestHeader('Access-Control-Allow-Origin', '*');
// http.setRequestHeader('Content-ConfigDir', h.config_dir);
// http.setRequestHeader('Content-Content', h.content_dir);
// http.setRequestHeader('Content-Backups', h.backup_dir);
// http.setRequestHeader('Content-Identy', h.identy);
// http.setRequestHeader('Content-Url', h.url);
// http.setRequestHeader('Content-Abs', h.abs_dir);
// http.setRequestHeader('Content-Dir', h.root_dir);
// http.setRequestHeader('Content-Manifest', h.manifest);
// http.setRequestHeader('Content-Name', h.backupname);
// http.setRequestHeader('Content-Safelimit', h.safelimit);
// http.setRequestHeader('Content-Start', h.start);
// http.setRequestHeader('Content-Filessofar', h.filessofar);
// http.setRequestHeader('Content-Total', h.total_files);
// http.setRequestHeader('Content-Rev', h.rev);
// http.setRequestHeader('Content-It', h.iteratio);
// http.setRequestHeader('Content-Dbit', h.dbiteratio);
// http.setRequestHeader('Content-Dblast', h.dblast);
// http.setRequestHeader('Content-Bmitmp', h.bmitmp);
// http.setRequestHeader('Content-Browser', true);
// // Handle success
// http.onload = function () {
// // Check if we can get the headers
// if (http.status === 200) {
// // Make sure the headers exists
// let isFinished = http.getResponseHeader('Content-Finished');
// // Check if it's finished
// if (typeof isFinished != 'undefined' && isFinished && isFinished == 'true') {
// // Reset errors
// errors = 0;
// // Return with success
// return callback({ status: true, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
// }
// // Get iteratio value
// let iteratio = http.getResponseHeader('Content-It');
// let dbiteratio = http.getResponseHeader('Content-Dbit');
// let dblast = http.getResponseHeader('Content-Dblast');
// let soFar = http.getResponseHeader('Content-Filessofar');
// if (typeof iteratio != 'undefined' && iteratio) {
// // Return success
// return callback({ status: true, iteratio: iteratio, dbiteratio: dbiteratio, dblast: dblast, sf: soFar, errors: errors });
// } else {
// // Increment the errors
// errors++;
// // Return with failure and a tiemouted retry
// return callback({ status: false, iteratio: -1, dbiteratio: dbiteratio, dblast: dblast, sf: -1, errors: errors });
// }
// } else {
// // Increment the errors
// errors++;
// // Return with failure and a tiemouted retry
// return callback({ status: false, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
// }
// }
// // Handle failure
// http.onerror = function () {
// // Increment the errors
// errors++;
// // Return with a tiemouted retry
// return callback({ status: false, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
// }
// // Send prepared connection to the server
// http.send();
}
async function middlewareForResponses(res) {
// Variables
let status = res.status;
let errors = parseInt(res.errors);
// Check if finished
if (status == "true" || status === true) {
// Return and show success
let backgroundNameTmp = backgroundName;
backgroundBackup = false;
backgroundName = false;
headers_for_middleware = false;
window.onbeforeunload = null;
$(".backup-minimize").removeClass("disabled");
// Return
return completedBackup({ filename: backgroundNameTmp }, true);
}
// Check for errors
// Abort if above 4 errors
if (isNaN(errors) || errors > 0) {
// Unset the headers and variables
backgroundBackup = false;
backgroundName = false;
headers_for_middleware = false;
window.onbeforeunload = null;
await new Promise(resolve => setTimeout(resolve, 10000));
let end_code = getEndCode();
if (end_code) {
if (end_code == "003") {
$.bmi.modal("backup-progress-modal").close();
$.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
tryingToAbortBackup = false;
backupOnGoing = false;
return;
}
}
// End this process
if ($("#backup-progress-modal").hasClass("open")) {
return backupError(3);
} else return;
} else {
// Handle case of success
return handleBrowserBackup(errors);
}
}
function handleBrowserBackup(errors = 0) {
// Send the request
setTimeout(
function () {
sendRequestOfBackupPart(middlewareForResponses, errors);
},
Math.floor(Math.random() * (523 - 330)) + 330,
);
}
$("#configuration-reset-absolute").on("click", function (e) {
e.preventDefault();
$.bmi
.ajax("reset-configuration", {})
.then(function (res) {
if (res.status == "success") {
window.location.reload();
} else {
$.bmi._msg(res);
}
})
.catch(function (error) {
//
});
});
$("#download-site-infos").on("click", function (e) {
e.preventDefault();
$.bmi
.ajax("get-site-data", {})
.then(function (res) {
if (res.status == "success") {
$.bmi.prepareFile(
"site_details_troubleshooting.txt",
JSON.stringify(res.data),
);
} else {
$.bmi._msg(res);
}
})
.catch(function (error) {
//
});
});
$("#start-entire-backup").on("click", startBackupProcessNow);
async function startBackupProcessNow(e, preserveLogs = false) {
if (preserveLogs == false) {
$.bmi.modal("prenotice-modal").close();
$.bmi.modal("freeze-loading-modal").open();
}
$(".backup-minimize").removeClass("disabled");
$("#backup-stop").addClass("disabled");
await fixHtaccessPromise();
if (preserveLogs == false) {
curdivs = 0;
await resetLogsPromise(false);
}
clearTimeout(timeouter);
triggeredByIntv = false;
bacupisDone = false;
backupOnGoing = true;
$.bmi.requestWakeLock();
refreshLogAndProgress(preserveLogs);
callBackupRunAjax(preserveLogs);
}
function callBackupRunAjax(preserveLogs = false) {
if (preserveLogs == false) {
$.bmi.modal("freeze-loading-modal").close();
$.bmi.modal("backup-progress-modal").open();
}
$.bmi
.ajax("create-backup", {
preserveLogs: preserveLogs,
})
.then(async function (res) {
if (res.status == "success") {
bacupisDone = true;
clearTimeout(timeouter);
backupOnGoing = false;
refreshContentInstant(function () {
setTimeout(function () {
// setProgress(101, 500, res);
completedBackup(res, true);
}, 350);
});
} else if (res.status == "background") {
backgroundBackup = true;
backgroundName = res.filename;
let end_code = getEndCode();
if (end_code) {
if (end_code == "001") {
bacupisDone = true;
progressIsDone = true;
clearTimeout(timeouter);
backupOnGoing = false;
completedBackup({ filename: backgroundName }, true);
}
if (end_code == "002" || end_code == "004") {
// $.bmi.modal('backup-progress-modal').close();
backupError(4);
}
if (end_code == "003") {
$.bmi.modal("backup-progress-modal").close();
$.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
tryingToAbortBackup = false;
backupOnGoing = false;
}
}
} else if (res.status == "background_hard") {
// Set the background receiver
backgroundBackup = true;
// Set Global name of current backup
backgroundName = res.filename;
// Append URL
res.url = res.url;
// Display success of receiver
$.bmi.alert("success", $("#bmi-received-hard").text(), 3000);
// Disable button
$(".backup-minimize").addClass("disabled");
// Make sure modal is visible
if (!$("#backup-progress-modal").hasClass("open")) {
setTimeout(function () {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
// Make sure the progress modal is open
$.bmi.modal("backup-progress-modal").open();
}, 300);
}, 300);
}
// Make sure the backup won't be dismissed by mistake
window.onbeforeunload = function () {
return "Backup in progress...";
};
// Send first request
handleBrowserBackup();
} else {
await new Promise(resolve => setTimeout(resolve, 3000));
let end_code = getEndCode();
if (end_code) {
if (end_code == "003") {
$.bmi.modal("backup-progress-modal").close();
$.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
tryingToAbortBackup = false;
backupOnGoing = false;
return;
}
}
backupOnGoing = false;
setTimeout(function () {
clearInterval(iprogres);
$(".log-wrapper").find("pre")[0].innerText = "";
$(".progress-active-bar")[0].style.width = "0%";
$(".progress-percentage")[0].style.left = "0%";
$(".progress-percentage")[0].innerText = "0%";
}, 300);
$.bmi._msg(res);
console.log(res);
$.bmi.modal("backup-progress-modal").close();
if (typeof res.bfs !== "undefined") handleBfs();
else backupError(5);
}
})
.catch(function (error) {
console.error(error);
backupError(6, error);
});
}
$("#open_trouble_extenstion").on("click", function () {
if ($("#trouble_extenstion").hasClass("openned")) {
$("#trouble_extenstion").hide(300);
$("#trouble_extenstion").removeClass("openned");
$(this).removeClass("active");
} else {
$("#trouble_extenstion").show(300);
$("#trouble_extenstion").addClass("openned");
$(this).addClass("active");
}
});
$("#switch-show-trs").on("click", function () {
let seemore = this.dataset.see;
let hide = this.dataset.hide;
let $trs = $(".hide-show-tr");
if ($(this).hasClass("shown")) {
$trs.hide(300);
$(this).removeClass("shown");
this.innerText = seemore;
} else {
$trs.show(300);
$(this).addClass("shown");
this.innerText = hide;
}
});
$("#ex_b_fs").on("change", function () {
if ($("#ex_b_fs").is(":checked")) $("#bmi__collon").show();
else $("#bmi__collon").hide();
});
$("#show-upload-area").on("click", function () {
if ($(".upload_area").hasClass("hidden")) {
$(".upload_area").show(300);
$(".upload_area").removeClass("hidden");
$([document.documentElement, document.body]).animate(
{
scrollTop: $(this).offset().top - 50 + "px",
},
300,
);
} else {
$(".upload_area").hide(300);
$(".upload_area").addClass("hidden");
}
});
$(".bmi-copper").on("click", function (e) {
e.preventDefault();
let $el = $("#" + this.getAttribute("data-copy"))[0];
if ($el.value && $el.value.length > 0) {
$.bmi.clipboard($el.value);
} else {
$.bmi.clipboard($el.innerText);
}
});
$("#bmi_restore_tbody").on("click", ".restore-btn", function (e) {
isMigrationLocked(function (isNotLocked) {
if (isNotLocked) {
let name = '';
if (e.target.closest('tr').getAttribute('data-is-local') === 'no') {
if (e.target.closest('tr').getAttribute('gdrive-id') !== null) {
name = '?#googledrive#_' + e.target.closest('tr').getAttribute('gdrive-id');
current_restore = name;
}
if (e.target.closest('tr').getAttribute('ftp-id') !== null) {
name = '?#ftp#_' + e.target.closest('tr').getAttribute('ftp-id');
current_restore = name;
}
if(e.target.closest('tr').getAttribute('dropbox-id') !== null){
name = '?#dropbox#_' + e.target.closest('tr').getAttribute('dropbox-id');
current_restore = name;
}
if (e.target.closest('tr').getAttribute('onedrive-id') !== null) {
name = '?#onedrive#_' + e.target.closest('tr').getAttribute('onedrive-id');
current_restore = name;
}
if(e.target.closest('tr').getAttribute('pcloud-id') !== null){
name = '?#pcloud#_' + e.target.closest('tr').getAttribute('pcloud-id');
current_restore = name;
}
if (e.target.closest('tr').getAttribute('aws-id') !== null) {
name = '?#aws#_' + e.target.closest('tr').getAttribute('aws-id');
current_restore = name;
}
if (e.target.closest('tr').getAttribute('wasabi-id') !== null) {
name = '?#wasabi#_' + e.target.closest('tr').getAttribute('wasabi-id');
current_restore = name;
}
if (e.target.closest('tr').getAttribute('backupbliss-id') !== null) {
name = '?#backupbliss#_' + e.target.closest('tr').getAttribute('backupbliss-id');
current_restore = name;
}
if (e.target.closest('tr').getAttribute('sftp-id') !== null) {
name = '?#sftp#_' + e.target.closest('tr').getAttribute('sftp-id');
current_restore = name;
}
} else {
name = e.target.closest("tr").querySelector(".br_name").innerText;
current_restore = name;
}
if (!name || name.trim().length <= 0)
return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
$("#restore-ok").prop("checked", false);
$.bmi.modal("freeze-loading-modal").open();
setTimeout(function () {
shareLogsStatus("is_allowed", function (res) {
if (res === "ask") {
$.bmi.modal("freeze-loading-modal").close();
$("#logs-sharing-ask-modal").attr(
"data-destination",
"pre-restore-modal",
);
setTimeout(function () {
$.bmi.modal("logs-sharing-ask-modal").open();
}, 300);
} else {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
$.bmi.modal("pre-restore-modal").open();
}, 300);
}
});
}, 300);
}
});
});
$("#bmi_restore_tbody").on("click", ".stg-restore-btn", function (e) {
$.bmi.modal("freeze-loading-modal").open();
let name = e.target.closest("tr").querySelector(".br_name").innerText;
// if (e.target.closest('tr').getAttribute('data-is-local') === 'no') {
// name = '?#googledrive#_' + e.target.closest('tr').getAttribute('gdrive-id');
// }
if (!name || name.trim().length <= 0) {
return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
}
$('.bmi-stg-sel-box[data-mode="tastewp"]').click();
$('.bmi-stg-drop-option[backup-name="' + name.trim() + '"]').click();
$("#stgng").click();
setTimeout(function () {
$.bmi.modal("freeze-loading-modal").close();
}, 150);
});
$("#quick-download-migration").on("click", function () {
let url = $("#bm-d-url").val();
if ($.bmi.isUrlValid(url)) {
isMigrationLocked(function (isNotLocked) {
if (isNotLocked) {
if (url.length > 0) {
current_restore = -100;
$("#restore-ok").prop("checked", false);
$.bmi.modal("freeze-loading-modal").open();
setTimeout(function () {
shareLogsStatus("is_allowed", function (res) {
if (res === "ask") {
$.bmi.modal("freeze-loading-modal").close();
$("#logs-sharing-ask-modal").attr(
"data-destination",
"pre-restore-modal",
);
setTimeout(function () {
$.bmi.modal("logs-sharing-ask-modal").open();
}, 300);
} else {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
$.bmi.modal("pre-restore-modal").open();
}, 300);
}
});
}, 300);
} else $.bmi.alert("warning", $("#bmi-invalid-url").text(), 5000);
}
});
} else $.bmi.alert("warning", $("#bmi-invalid-url").text(), 5000);
});
$('#restore-start-sure').on('click', function () {
if ($('#restore-ok').is(':checked')) {
if (current_restore === -100) downloadMigration();
else if (current_restore.includes('?#googledrive#_')) downloadGDrive();
else if (current_restore.includes('?#ftp#_')) downloadFTP();
else if (current_restore.includes('?#dropbox#_')) downloadDropbox();
else if (current_restore.includes('?#onedrive#_')) downloadOneDrive();
else if (current_restore.includes('?#pcloud#_')) downloadPCloud();
else if (current_restore.includes('?#aws#_')) downloadS3(true, 'aws');
else if (current_restore.includes('?#wasabi#_')) downloadS3(true, 'wasabi');
else if (current_restore.includes('?#backupbliss#_')) downloadBackupBliss();
else if (current_restore.includes('?#sftp#_')) downloadSFTP();
else letsRestore();
} else
$.bmi.alert("warning", $("#bmi-restore-require-checkmark").text(), 3000);
});
$(".backup-minimize").on("click", function () {
isBackupOngoing(function (isRunnin) {
if (isRunnin === true) $("#bmi-ongoing-backup").show(300);
else $("#bmi-ongoing-backup").hide();
});
});
function checkIfBackupOnGoing(cb = function () { }) {
isBackupOngoing(function (isRunnin) {
if (isRunnin === true) {
if (!$("#backup-progress-modal").hasClass("open")) {
if (backupOnGoing === false) {
bacupisDone = false;
backupOnGoing = true;
triggeredByIntv = true;
}
$("#bmi-ongoing-backup").show(300);
}
} else {
if ($("#bmi-ongoing-backup").is(":visible")) {
$("#bmi-ongoing-backup").hide(300);
}
if (
$("#backup-progress-modal").hasClass("open") &&
triggeredByIntv === true
) {
if (backupOnGoing === true) {
backupOnGoing = false;
}
triggeredByIntv = false;
reloadAndHandleBackupEnd();
} else {
if (
backupOnGoing === true &&
!$("#backup-progress-modal").hasClass("open")
) {
backupOnGoing = false;
$.bmi.reloadBackups();
}
}
}
cb();
});
}
function handleNotOwnBackupEnd() {
let url = $("#BMI_BLOG_URL").text().trim();
if (url.slice(-url.length) !== "/") url = url + "/";
httpGet(
url +
"?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim(),
function (rez) {
if (rez === false) {
$.bmi.modal("backup-progress-modal").close();
} else {
rez = rez.split("\n");
let abort = "Backup process aborted";
let error = "[ERROR]";
if (
rez[rez.length - 1].includes(abort) ||
rez[rez.length - 2].includes(abort)
) {
$.bmi.modal("backup-progress-modal").close();
$.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
} else if (
rez[rez.length - 1].includes(error) ||
rez[rez.length - 2].includes(error)
) {
// $.bmi.modal('backup-progress-modal').close();
backupError(7);
} else {
let url_x = $("#BMI_BLOG_URL").text().trim();
if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
$.bmi
.ajax("get-latest-backup", {})
.then(function (res) {
let url =
url_x +
"?backup-migration=BMI_BACKUP&bmi-id=" +
res +
"&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
let logs =
url_x +
"?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
$("#text-input-copy")[0].value = url;
$("#download-backup-url").attr("href", url);
$(".download-backup-log-url").attr("href", logs);
$.bmi.modal("backup-progress-modal").close();
$.bmi.modal("backup-success-modal").open();
})
.catch(function (error) {
$.bmi.modal("backup-progress-modal").close();
$.bmi.modal("backup-success-modal").open();
let url = $($("#bmi_restore_tbody").find("tr")[0])
.find(".bc-download-btn")
.attr("href");
$("#download-backup-url").attr({ href: url });
$("#text-input-copy").val(url);
});
}
}
},
);
}
function reloadAndHandleBackupEnd() {
$.bmi.getCurrentBackups(function (res) {
$.bmi.fillWithNewBackups(res.backups, res.backups.ongoing, function () {
handleNotOwnBackupEnd();
});
});
}
// ONGOING BACKUP
function runCheckOnGoing() {
checkIfBackupOnGoing(function () {
setTimeout(function () {
if (!$("#freeze-loading-modal").hasClass("open") && !(tryingToAbortBackup)){
runCheckOnGoing();
}
}, ongoing_interval);
});
}
runCheckOnGoing();
$("#bmi-ongoing-backup").on("click", function () {
isBackupOngoing(function (isRunnin) {
if (isRunnin === true) {
curdivs = 0;
refreshLogAndProgress();
setTimeout(function () {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
// Make sure the progress modal is open
$.bmi.modal("backup-progress-modal").open();
}, 300);
}, 300);
} else {
$.bmi.alert("info", $("#bmi-bc-ended").text(), 3000);
$.bmi.reloadBackups();
}
$("#bmi-ongoing-backup").hide(300);
});
});
function getMigrationLogs(cb = function () { }) {
let url = $("#BMI_BLOG_URL").text().trim();
if (url.slice(-url.length) !== "/") url = url + "/";
httpGet(
url +
"?backup-migration=PROGRESS_LOGS&progress-id=latest_migration_full.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim(),
function (res1) {
if (!res1) {
return cb();
}
let res2 = res1.split("\n").slice(0, 1)[0];
res1 = res1.split("\n").slice(1).join("\n");
if (res2 === false || isNaN(parseFloat(res2))) {
return cb();
}
restoreLogs = res1;
let pre = $("#restore-live-log-wrapper").find("pre")[0];
if (!res1.includes("<") && !res1.includes(">")) {
if (res1 && res1 != false && typeof res1 !== "undefined") {
insertPre(res1, pre);
}
}
setProgress(res2);
cb();
},
);
}
function fixHtaccessPromise() {
return new Promise((resolve) => {
fixHtaccess(() => {
return resolve();
});
});
}
bmi.fixHtaccessPromise = fixHtaccessPromise;
function fixHtaccess(cb = function () {}) {
$.bmi
.ajax("htaccess-litespeed", {})
.then(function (res) {
setTimeout(function () {
cb(true);
}, 400);
})
.catch(function (error) {
cb(false);
});
}
function isMigrationLocked(
cb = function () { },
mute = false,
clearLogs = true,
) {
$.bmi
.ajax("migration-locked", { clearLogs: clearLogs })
.then(function (res) {
if (res.status == "success") cb(true);
else {
if (!mute) $.bmi._msg(res);
cb(false);
}
})
.catch(function (error) {
//
});
}
function downloadGDrive(startRestoreProcess = true) {
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
let currentGDriveIdRestoration =
current_restore.split("?#googledrive#_")[1];
current_restore = null;
if (startRestoreProcess == false) {
$('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
}
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal("pre-restore-modal").close();
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("restore-progress-modal").open();
performGDriveDownload(startRestoreProcess, currentGDriveIdRestoration);
}
});
});
});
}
$.bmi.downloadGDrive = downloadGDrive;
function downloadFTP(startRestoreProcess = true) {
$('#restore-live-log-wrapper').find('pre')[0].innerText = '';
let currentFtpIdRestoration = current_restore.split('?#ftp#_')[1];
let md5 = $("#bmi_restore_tbody").find("tr[ftp-id='" + currentFtpIdRestoration + "']").attr("md5");
current_restore = null;
if (startRestoreProcess == false) {
$('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
}
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal('pre-restore-modal').close();
$('#restore-live-log-wrapper').find('pre')[0].innerText = '';
$.bmi.modal('restore-progress-modal').open();
performFtpDownload(startRestoreProcess, currentFtpIdRestoration, 0, false, md5);
}
});
});
});
}
$.bmi.downloadFTP = downloadFTP;
function downloadOneDrive(startRestoreProcess = true) {
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
let currentOneDriveIdRestoration =
current_restore.split("?#onedrive#_")[1];
let md5 = $("#bmi_restore_tbody").find("tr[onedrive-id='" + currentOneDriveIdRestoration + "']").attr("md5");
current_restore = null;
if (startRestoreProcess == false) {
$('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
}
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal("pre-restore-modal").close();
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("restore-progress-modal").open();
performOneDriveDownload(startRestoreProcess, currentOneDriveIdRestoration, 0, false, md5);
}
});
});
});
}
$.bmi.downloadOneDrive = downloadOneDrive;
function downloadBackupBliss(startRestoreProcess = true) {
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
let currentBackupBlissIdRestoration =
current_restore.split("?#backupbliss#_")[1];
let md5 = $("#bmi_restore_tbody").find("tr[backupbliss-id='" + currentBackupBlissIdRestoration + "']").attr("md5");
current_restore = null;
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal("pre-restore-modal").close();
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("restore-progress-modal").open();
performBackupBlissDownload(startRestoreProcess, currentBackupBlissIdRestoration, 0, false, md5);
}
});
});
});
}
$.bmi.downloadBackupBliss = downloadBackupBliss;
function performFtpDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
$.bmi.ajax('download-cloud-backup', {
storage: 'ftp',
startRestoreProcess: startRestoreProcess,
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
secret: secret
}).then(function (res) {
if (res.status == 'success') {
if (typeof res.size != 'undefined') size = res.size;
if (typeof res.md5 != 'undefined') md5 = res.md5;
if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
if (typeof res.writepath != 'undefined') writepath = res.writepath;
if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
if (typeof res.secret != 'undefined') secret = res.secret;
if (typeof res.finished != 'undefined' && res.finished != 'true') {
step++;
performFtpDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
} else {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$('#restore-progress-modal .progress-active-bar')[0].style.width = 0 + '%';
$('#restore-progress-modal .progress-percentage')[0].style.left = 0 + '%';
$('#restore-progress-modal .progress-percentage')[0].innerText = 0 + '%';
$('#restore_current_step').text($('#bmi-restoring-prepare').text());
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
}).catch(function (error) {
restoreFailed(error);
});
}
function performGDriveDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
$.bmi.ajax('download-cloud-backup', {
storage: 'googledrive',
startRestoreProcess: startRestoreProcess,
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
secret: secret
}).then(function (res) {
if (res.status == 'success') {
if (typeof res.size != 'undefined') size = res.size;
if (typeof res.md5 != 'undefined') md5 = res.md5;
if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
if (typeof res.writepath != 'undefined') writepath = res.writepath;
if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
if (typeof res.secret != 'undefined') secret = res.secret;
if (typeof res.finished != 'undefined' && res.finished != 'true') {
step++;
performGDriveDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
} else if (res.finished == 'true') {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$(
"#restore-progress-modal .progress-active-bar",
)[0].style.width = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
$("#restore_current_step").text(
$("#bmi-restoring-prepare").text(),
);
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
})
.catch(function (error) {
restoreFailed(error);
});
}
function downloadDropbox(startRestoreProcess = true) {
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
let currentDropboxIdRestoration = current_restore.split("?#dropbox#_")[1];
let md5 = $("#bmi_restore_tbody").find("tr[dropbox-id='" + currentDropboxIdRestoration + "']").attr("md5");
current_restore = null;
if (startRestoreProcess == false) {
$('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
}
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal("pre-restore-modal").close();
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("restore-progress-modal").open();
performDropboxDownload(startRestoreProcess, currentDropboxIdRestoration, 0, false, md5);
}
});
});
});
}
function performDropboxDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = false, secret = false) {
$.bmi.ajax('download-dropbox-backup', {
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
startRestoreProcess: startRestoreProcess,
secret: secret
}).then(function(res) {
if (res.status == 'success') {
if (typeof res.size != 'undefined') size = res.size;
if (typeof res.md5 != 'undefined') md5 = res.md5;
if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
if (typeof res.writepath != 'undefined') writepath = res.writepath;
if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
if (typeof res.secret != 'undefined') secret = res.secret;
if (typeof res.finished != 'undefined' && res.finished != 'true') {
step++;
performDropboxDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
} else if (res.finished == 'true') {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$(
"#restore-progress-modal .progress-active-bar",
)[0].style.width = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
$("#restore_current_step").text(
$("#bmi-restoring-prepare").text(),
);
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
}).catch(function(error) {
restoreFailed(error);
});
}
function downloadS3(startRestoreProcess = true, provider = 'aws') {
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
let currentS3IdRestoration = current_restore.split("?#" + provider + "#_")[1];
let md5 = $("#bmi_restore_tbody").find("tr[" + provider + "-id='" + currentS3IdRestoration + "']").attr("md5");
current_restore = null;
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal("pre-restore-modal").close();
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("restore-progress-modal").open();
performS3Download(startRestoreProcess, provider, currentS3IdRestoration, 0, false, md5);
}
});
});
});
}
function performS3Download(startRestoreProcess = true, provider = 'aws', fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
$.bmi.ajax('download-cloud-backup', {
storage: 's3',
provider: provider,
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
startRestoreProcess: startRestoreProcess,
secret: secret
}).then(function (res) {
if (res.status == 'success') {
if (typeof res.size != 'undefined') size = res.size;
if (typeof res.md5 != 'undefined') md5 = res.md5;
if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
if (typeof res.writepath != 'undefined') writepath = res.writepath;
if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
if (typeof res.secret != 'undefined') secret = res.secret;
if (typeof res.finished != 'undefined' && res.finished != 'true') {
step++;
performS3Download(startRestoreProcess, provider, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
} else if (res.finished == 'true') {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$(
"#restore-progress-modal .progress-active-bar",
)[0].style.width = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
$("#restore_current_step").text(
$("#bmi-restoring-prepare").text(),
);
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
}).catch(function (error) {
restoreFailed(error);
});
}
$.bmi.downloadS3 = downloadS3;
function performOneDriveDownload(startRestoreProcess = true,fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
$.bmi.ajax('download-cloud-backup', {
storage: 'onedrive',
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
startRestoreProcess: startRestoreProcess,
secret: secret
}).then(function (res) {
if (res.status == 'success') {
if (typeof res.size != 'undefined') size = res.size;
if (typeof res.md5 != 'undefined') md5 = res.md5;
if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
if (typeof res.writepath != 'undefined') writepath = res.writepath;
if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
if (typeof res.secret != 'undefined') secret = res.secret;
if (typeof res.finished != 'undefined' && res.finished != 'true') {
step++;
performOneDriveDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
} else if (res.finished == 'true') {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$(
"#restore-progress-modal .progress-active-bar",
)[0].style.width = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
$("#restore_current_step").text(
$("#bmi-restoring-prepare").text(),
);
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
})
.catch(function (error) {
restoreFailed(error);
});
}
function performBackupBlissDownload(startRestoreProcess = true,fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
$.bmi.ajax('download-cloud-backup', {
storage: 'backupbliss',
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
startRestoreProcess: startRestoreProcess,
secret: secret
}).then(function (res) {
if (res.status == 'success') {
if (typeof res.size != 'undefined') size = res.size;
if (typeof res.md5 != 'undefined') md5 = res.md5;
if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
if (typeof res.writepath != 'undefined') writepath = res.writepath;
if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
if (typeof res.secret != 'undefined') secret = res.secret;
if (typeof res.finished != 'undefined' && res.finished != 'true') {
step++;
performBackupBlissDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
} else if (res.finished == 'true') {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$(
"#restore-progress-modal .progress-active-bar",
)[0].style.width = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
$("#restore_current_step").text(
$("#bmi-restoring-prepare").text(),
);
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
})
.catch(function (error) {
restoreFailed(error);
});
}
function downloadSFTP(startRestoreProcess = true) {
console.log("downloadSFTP");
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
let currentSFTPIdRestoration = current_restore.split("?#sftp#_")[1];
let md5 = $("#bmi_restore_tbody").find("tr[sftp-id='" + currentSFTPIdRestoration + "']").attr("md5");
current_restore = null;
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal("pre-restore-modal").close();
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("restore-progress-modal").open();
performSFTPDownload(startRestoreProcess, currentSFTPIdRestoration, 0, false, md5);
}
});
});
});
}
function performSFTPDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
$.bmi.ajax('download-sftp-backup', {
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
startRestoreProcess: startRestoreProcess,
secret: secret
}).then(function (res) {
if (res.status == 'success') {
if (typeof res.size != 'undefined') size = res.size;
if (typeof res.md5 != 'undefined') md5 = res.md5;
if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
if (typeof res.writepath != 'undefined') writepath = res.writepath;
if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
if (typeof res.secret != 'undefined') secret = res.secret;
if (typeof res.finished != 'undefined' && res.finished != 'true') {
step++;
performSFTPDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
} else if (res.finished == 'true') {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$("#restore-progress-modal .progress-active-bar")[0].style.width = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
$("#restore_current_step").text($("#bmi-restoring-prepare").text());
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
})
.catch(function (error) {
restoreFailed(error);
});
}
$.bmi.downloadSFTP = downloadSFTP;
function downloadPCloud(startRestoreProcess = true) {
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
let currentPCloudIdRestoration = current_restore.split("?#pcloud#_")[1];
let md5 = $("#bmi_restore_tbody").find("tr[pcloud-id='" + currentPCloudIdRestoration + "']").attr("md5");
current_restore = null;
if (startRestoreProcess == false) {
$('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
}
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
curdivs = 0;
refreshLogAndProgressRestore();
restoreOnGoing = true;
cli_quickmigration = true;
$.bmi.modal("pre-restore-modal").close();
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
$.bmi.modal("restore-progress-modal").open();
performPCloudDownload(startRestoreProcess, currentPCloudIdRestoration, 0, false, md5);
}
});
});
});
}
$.bmi.downloadPCloud = downloadPCloud;
function performPCloudDownload(
startRestoreProcess = true,
fileId = false,
step = 0,
size = false,
md5 = false,
originalFilename = false,
writepath = false,
chunksize = false,
secret = false
) {
$.bmi
.ajax("download-cloud-backup", {
storage: "pcloud",
fileId: fileId,
step: step,
size: size,
md5: md5,
filename: originalFilename,
writepath: writepath,
chunksize: chunksize,
startRestoreProcess: startRestoreProcess,
secret: secret,
})
.then(function (res) {
if (res.status == "success") {
if (typeof res.size != "undefined") size = res.size;
if (typeof res.md5 != "undefined") md5 = res.md5;
if (typeof res.originalFilename != "undefined")
originalFilename = res.originalFilename;
if (typeof res.writepath != "undefined") writepath = res.writepath;
if (typeof res.chunksize != "undefined") chunksize = res.chunksize;
if (typeof res.secret != "undefined") secret = res.secret;
if (typeof res.finished != "undefined" && res.finished != "true") {
step++;
performPCloudDownload(
startRestoreProcess,
fileId,
step,
size,
md5,
originalFilename,
writepath,
chunksize,
secret,
);
} else if (res.finished == "true") {
cli_quickmigration = true;
current_restore = res.filename;
refreshLogAndProgressRestore(false);
$.bmi.reloadBackups(function () {
setTimeout(function () {
clearInterval(iprogres);
$(
"#restore-progress-modal .progress-active-bar",
)[0].style.width = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
$("#restore_current_step").text(
$("#bmi-restoring-prepare").text(),
);
}, 600);
});
}
} else {
$.bmi._msg(res);
restoreFailed();
console.error(res);
}
})
.catch(function (error) {
restoreFailed(error);
});
}
$.bmi.downloadDropbox = downloadDropbox;
function downloadMigration( startRestoreProcess = true) {
current_restore = null;
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
if (startRestoreProcess == false) {
$('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
}
isMigrationLocked(function (isNotLocked) {
resetLogs(true, function () {
fixHtaccess(function () {
if (isNotLocked) {
let url = $("#bm-d-url").val();
$("#restore_current_step").text(
$("#bmi-downloading-remote").text(),
);
$.bmi
.ajax("download-backup", {
url: url,
startRestoreProcess: startRestoreProcess
})
.then(function (res) {
clearInterval(iprogres);
clearTimeout(timeouter);
if (res.status === "success") {
// Set backup to restore name
current_restore = res.name;
// Refresh logs in such case
cli_quickmigration = true;
refreshLogAndProgressRestore(false);
// Reload backup list
$.bmi.reloadBackups();
// setProgress(100, 300);
setTimeout(function () {
clearInterval(iprogres);
$(
"#restore-progress-modal .progress-active-bar",
)[0].style.width = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].style.left = 0 + "%";
$(
"#restore-progress-modal .progress-percentage",
)[0].innerText = 0 + "%";
$("#restore_current_step").text(
$("#bmi-restoring-prepare").text(),
);
}, 600);
} else if (res.status == "cli_download") {
current_restore = ".cli_download";
cli_quickmigration = true;
refreshLogAndProgressRestore(false);
} else if (res.status === "error") {
restoreFailed();
} else {
$.bmi._msg(res);
restoreFailed();
}
})
.catch(function (error) {
restoreFailed(error);
});
$("#restore-live-log-wrapper").find("pre")[0].innerText = "";
curdivs = 0;
restoreOnGoing = true;
cli_quickmigration = true;
refreshLogAndProgressRestore();
$.bmi.modal("pre-restore-modal").close();
$.bmi.modal("restore-progress-modal").open();
}
});
});
});
}
$.bmi.downloadMigration = downloadMigration;
function restoreFailed(text = "") {
$('#restore-progress-modal .title').text($('#bmi-restore-progress-modal-title').text());
$('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-restore-progress-modal-warning').text());
setTimeout(function () {
$("#restore-progress-modal .progress-active-bar")[0].style.width =
0 + "%";
$("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
}, 500);
$.bmi.releaseWakeLock();
isMigrationLocked(
function (isNotLocked) {
if (isNotLocked) {
setTimeout(function () {
if (!$("#restore-progress-modal").hasClass("open")) return;
restoreOnGoing = false;
cli_quickmigration = false;
$("#restore-error-pre").text(
$("#bmi-loading-translation").text().trim(),
);
$("#after-logs-sent-modal").attr("data-error-source", "migration");
let url = $("#BMI_BLOG_URL").text().trim();
if (url.slice(-url.length) !== "/") url = url + "/";
// httpGet(url + '?backup-migration=PROGRESS_LOGS&progress-id=latest_migration.log&bmi-id=current&t=' + +new Date(), function(res) {
//
// if (res == false) {
// setTimeout(function () {
// restoreFailed();
// }, 1500);
// } else {
// let pre = $('#restore-error-pre')[0];
// $('#restore-error-pre').text('');
// curdivs = 0;
// insertPre(res, pre);
// }
//
// });
$.bmi.modal("restore-progress-modal").close();
setupRestoreErrorOptions().then(function () {
$.bmi.modal("error-modal").open();
$.bmi.modal("error-modal").setParent("restore-progress-modal");
});
}, 1000);
} else {
setTimeout(function () {
$.bmi.modal("restore-progress-modal").close();
setupRestoreErrorOptions().then(function () {
$.bmi.modal("error-modal").open();
$.bmi.modal("error-modal").setParent("restore-progress-modal");
});
});
}
},
true,
false,
);
}
function restoreCLISuccess() {
$.bmi.modal("restore-progress-modal").close();
restoreExecutionTime = (new Date().getTime() - restoreStartTime) / 1000;
$.bmi.modal("restore-success-modal").open();
setTimeout(function () {
$("#restore-progress-modal .progress-active-bar")[0].style.width =
0 + "%";
$("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
}, 500);
restoreCLI = false;
}
function letsRestore(remote = false, secret = null) {
let name = current_restore;
if (!name || name.trim().length <= 0)
return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
if ($("#pre-restore-modal").hasClass("open"))
$.bmi.modal("pre-restore-modal").close();
if (!$("#restore-progress-modal").hasClass("open"))
$.bmi.modal("restore-progress-modal").open();
if (!remote) $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
if (secret == null) {
if (!remote) curdivs = 0;
restoreOnGoing = true;
clearTimeout(timeouter);
restoreStartTime = new Date().getTime();
function initializeRestoration() {
fixHtaccess(function () {
getMigrationLogs(function () {
runRestoreProcess(name, remote, secret);
});
});
}
$.bmi.requestWakeLock();
if (!remote) resetLogs(true, initializeRestoration);
else initializeRestoration();
}
}
$.bmi.letsRestore = letsRestore;
function runRestoreProcess(
name,
remote,
secret,
tmpname = false,
ignoreRunning = "false",
options = {},
) {
if (secret == null && remote !== true) {
refreshLogAndProgressRestore(true);
}
if (typeof options.storage != "undefined") {
options.storage = options.storage.replace(/[\\\\/]+/g, "/");
}
$.bmi
.ajax("restore-backup", {
file: name,
remote: remote,
secret: secret,
ignoreRunning: ignoreRunning,
tmpname: tmpname,
options: options,
})
.then(function (res) {
if (res.status === "cli") {
autoLog = { l: res.login, u: res.url };
restoreCLI = true;
let end_code = getEndCode();
if (end_code ) {
if (end_code == "001") {
$.bmi.releaseWakeLock();
setTimeout(function () {
restoreCLISuccess();
restoreOnGoing = false;
restoreCLI = false;
}, 1000);
} else {
restoreFailed();
}
}
} else if (res.status === "success") {
autoLog = { l: res.login, u: res.url };
$.bmi.releaseWakeLock();
setTimeout(function () {
clearInterval(iprogres);
clearTimeout(timeouter);
restoreOnGoing = false;
$.bmi.modal("restore-progress-modal").close();
restoreExecutionTime = (new Date().getTime() - restoreStartTime) / 1000;
$.bmi.modal("restore-success-modal").open();
setTimeout(function () {
$("#restore-progress-modal .progress-active-bar")[0].style.width =
0 + "%";
$("#restore-progress-modal .progress-percentage")[0].style.left =
0 + "%";
$("#restore-progress-modal .progress-percentage")[0].innerText =
0 + "%";
}, 500);
}, 1500);
} else if (res.status === "secret") {
res.options.step = parseInt(res.options.step) + 1;
runRestoreProcess(
name,
remote,
res.secret,
res.tmp,
"true",
res.options,
);
} else if (res.status === "restore_ongoing") {
if (typeof res.options.firstDB != "undefined") {
res.options.firstDB = false;
}
if (typeof res.options.dbFinished != "undefined") {
if (
res.options.dbFinished === true ||
res.options.dbFinished === "true" ||
res.options.dbFinished === "1"
) {
res.options.step = parseInt(res.options.step) + 1;
}
} else if (typeof res.options.dbConvertionFinished != "undefined") {
if (
res.options.dbConvertionFinished === "true" ||
res.options.dbConvertionFinished === true ||
res.options.dbConvertionFinished === "1"
) {
res.options.step = parseInt(res.options.step) + 1;
}
} else if (typeof res.options.replaceFinished != "undefined") {
if (
res.options.replaceFinished === "true" ||
res.options.replaceFinished === true ||
res.options.replaceFinished === "1"
) {
res.options.step = parseInt(res.options.step) + 1;
}
} else {
res.options.step = parseInt(res.options.step) + 1;
if (
(res.options.step == 4 || res.options.step == "4") &&
typeof res.options.repeat_export != "undefined"
) {
if (
res.options.repeat_export === true ||
res.options.repeat_export === "true" ||
res.options.repeat_export === "1"
) {
res.options.step = 3;
res.options.firstExtract = "false";
}
}
if (
(res.options.step == 6 || res.options.step == "6") &&
typeof res.options.repeat_restore != "undefined"
) {
if (
res.options.repeat_restore === true ||
res.options.repeat_restore === "true" ||
res.options.repeat_restore === "1"
) {
res.options.step = 5;
res.options.firstFileRestore = "false";
}
}
}
setTimeout(
function () {
runRestoreProcess(
name,
remote,
res.secret,
res.tmp,
"true",
res.options,
);
},
Math.floor(Math.random() * (523 - 330)) + 330,
);
} else if (res.status === "error") {
setTimeout(function () {
clearInterval(iprogres);
clearTimeout(timeouter);
console.error(res);
restoreFailed();
}, 1000);
} else {
$.bmi.modal("pre-restore-modal").close();
$.bmi.modal("restore-progress-modal").close();
$.bmi._msg(res);
}
})
.catch(function (error) {
console.error(error);
restoreFailed(error);
});
}
function getAndInsertDynamicNames() {
$.bmi
.ajax("get-dynamic-names", {})
.then(function (res) {
let rules = res.data;
$("#bmi_exclusion_rules").text("");
if (typeof rules === "undefined") return;
if (typeof rules.length === "undefined") return;
for (let i = 0; i < rules.length; ++i) {
let row = $(".exclusion_template").clone();
row.removeClass("exclusion_template");
row.find(".exclusion_txt").val(rules[i].txt);
let posdd = row.find(".exclusion_position").find("select");
let whrdd = row.find(".exclusion_where").find("select");
if (!posdd) continue;
else posdd[0].setAttribute("data-def", rules[i].pos);
if (!whrdd) continue;
else whrdd[0].setAttribute("data-def", rules[i].whr);
$("#bmi_exclusion_rules").append(row);
if (
row.find(".exclusion_position").find(".bmi-dropdown").length > 0
) {
$.bmi.setOption(
row.find(".exclusion_position").find(".bmi-dropdown"),
null,
rules[i].pos,
);
}
if (row.find(".exclusion_where").find(".bmi-dropdown").length > 0) {
$.bmi.setOption(
row.find(".exclusion_where").find(".bmi-dropdown"),
null,
rules[i].whr,
);
}
}
$("#dynamic-fpaths-names").text(res["dynamic-fpaths-names"].join("\n"));
$("#dynamic-dpaths-names").text(res["dynamic-dpaths-names"].join("\n"));
})
.catch(function (error) {
//
});
}
$("#bmi_support_chat").on("click", function () {
if ($("#support-bmi").length === 0) {
$("#bmi").append(
'',
);
setTimeout(function () {
$("#bmi_support_chat").hide();
}, 100);
var loaded = false;
let loadinter = setInterval(function () {
if (loaded == true) clearInterval(loadinter);
if (typeof window.jivo_api !== "undefined") {
window.jivo_api.open();
loaded = true;
}
}, 30);
}
});
function getAllSelectedBackups() {
return $("#bmi_restore_tbody").find('input[type="checkbox"]:checked');
}
// del-all-btn-wrp
$("#bmi_restore_tbody").on("change", 'input[type="checkbox"]', function (e) {
let $selected = getAllSelectedBackups().length;
if ($selected > 0) {
$(".del-all-btn-wrp").show(300);
} else $(".del-all-btn-wrp").hide(300);
if (
$selected == $("#bmi_restore_tbody").find('input[type="checkbox"]').length
) {
$("#backups-select-all").prop("checked", true);
} else {
$("#backups-select-all").prop("checked", false);
}
});
$("#fix-uname-issues").on("click", function (e) {
e.preventDefault();
$.bmi
.ajax("fix_uname_issues", {})
.then(function (res) {
$.bmi.alert("success", $("#bmi-default-success").text(), 3000);
})
.catch(function (error) {
$.bmi.alert("error", $("#bmi-default-fail").text(), 3000);
});
});
$("#revert-uname-issues").on("click", function (e) {
e.preventDefault();
$.bmi
.ajax("revert_uname_issues", {})
.then(function (res) {
$.bmi.alert("success", $("#bmi-default-success").text(), 3000);
})
.catch(function (error) {
$.bmi.alert("error", $("#bmi-default-fail").text(), 3000);
});
});
$("#backups-select-all").on("change", function () {
if (this.checked === true) {
$("#bmi_restore_tbody")
.find('input[type="checkbox"]')
.prop("checked", true);
} else {
$("#bmi_restore_tbody")
.find('input[type="checkbox"]')
.prop("checked", false);
}
let $selected = getAllSelectedBackups().length;
if ($selected > 0) {
$(".del-all-btn-wrp").show(300);
} else $(".del-all-btn-wrp").hide(300);
});
$(".lrn-mr-btn, .closer-learn-more").on("click", function () {
if ($(".learn_more_about_cron").hasClass("open")) {
$(".learn_more_about_cron").removeClass("open");
$(".learn_more_about_cron").hide(300);
$(".lrn-mr-btn").show();
$(".lrn-mr-btn").css({
opacity: 0,
});
$(".lrn-mr-btn").animate(
{
opacity: 1,
},
300,
);
} else {
$(".learn_more_about_cron").addClass("open");
$(".learn_more_about_cron").show(300);
$(".lrn-mr-btn").css({
opacity: 1,
});
$(".lrn-mr-btn").animate(
{
opacity: 0,
},
300,
function () {
$(".lrn-mr-btn").hide();
},
);
}
});
$(".bmi-error-toggle").on("click", function () {
let parent = $(this).closest(".error-noticer");
let parentId = parent.attr("id");
let errorBody = $("#" + parentId + " .error-body");
if ($(errorBody).hasClass("open")) {
$(errorBody).hide(300);
$(errorBody).removeClass("open");
$("#" + parentId + " .bmi-error-toggle").text($("#" + parentId + " .bmi-error-toggle").data("expand"));
} else {
$(errorBody).show(300);
$(errorBody).addClass("open");
$("#" + parentId + " .bmi-error-toggle").text($("#" + parentId + " .bmi-error-toggle").data("collapse"));
}
});
function runTimerST() {
let time = parseInt($("#server-time-auto").attr("data-time")) * 1000;
let date = new Date(time);
$("#server-time-auto").text(date.toUTCString());
setInterval(function () {
time += 1000;
date = new Date(time);
$("#server-time-auto").text(date.toUTCString());
}, 1000);
if ($("#ex_b_fs").is(":checked")) $("#bmi__collon").show();
else $("#bmi__collon").hide();
}
$("#bmi_send_test_mail").on("click", function (e) {
e.preventDefault();
$.bmi
.ajax("send-test-mail", {})
.then(function (res) {
$.bmi.alert("success", $("#bmi-email-success").text(), 3000);
})
.catch(function (error) {
$.bmi.alert("error", $("#bmi-email-fail").text(), 3000);
});
});
$(".bmi-error-dismiss").on("click", function () {
let parent = $(this).closest(".error-noticer");
let parentId = parent.attr("id");
$("#" + parentId).hide(300);
setTimeout(function () {
$("#" + parentId).remove();
}, 330);
$.bmi
.ajax("dismiss-error-notice", {
option_id: parentId,
})
.then(function (res) {})
.catch(function (error) {});
});
$(".deleteAllSelected").on("click", function () {
let isCloud = false;
let $selected = getAllSelectedBackups(),
names = [];
latest_delete_cloud = {};
let notOnLocal = [];
for (let i = 0; i < $selected.length; ++i) {
let tr = $selected[i].closest("tr");
let notOnLocalTr = $(tr).data("is-local") == "no" ? true : false;
let name = tr.querySelector(".br_name").innerText;
names.push(name);
if (notOnLocalTr) {
notOnLocal.push(name);
}
latest_delete_cloud[name] = {
md5: tr.getAttribute("md5"),
gid: tr.getAttribute("gdrive-id"),
};
isCloud = Array.from(tr.querySelectorAll('[class*="strg-"]')).filter(el => !el.classList.contains('strg-local')).some(el => el.classList.contains('img-green'));
}
$("#delete-confirm-modal").find(".text1").hide();
$("#delete-confirm-modal").find(".text4").hide();
$("#delete-confirm-modal").find(".text3").hide();
$("#delete-confirm-modal").find(".text2").show();
let count = names.length;
if (count <= 0) return;
else {
$(".backup-multiple-del-count").text(count);
if (count > 1) {
$(".del-only-one").hide();
$(".del-more-than-one").show();
} else {
$(".del-more-than-one").hide();
$(".del-only-one").show();
}
}
latest_delete = names;
if (isCloud) $(".bmi-cloud-removal").show();
else $(".bmi-cloud-removal").hide();
$("#remove-cloud-backup-as-well")[0].checked = false;
if (isCloud && notOnLocal.length === $selected.length) {
$("#remove-cloud-backup-as-well")[0].checked = true;
$(".bmi-cloud-removal").hide();
$("#delete-confirm-modal").find(".text1").hide();
$("#delete-confirm-modal").find(".text2").hide();
$("#delete-confirm-modal").find(".text4").hide();
$("#delete-confirm-modal").find(".text3").show();
}
$.bmi.modal("delete-confirm-modal").open();
});
$("#load-more-backups").on("click", function (e) {
e.preventDefault();
$.bmi.showMoreBackups();
});
function toggleFormatTip(e) {
e.preventDefault();
$("#format-tip-wrp")[0].style.minWidth = "calc(100% - 120px)";
if ($("#format-tip-wrp")[0].style.display === "none") {
$("#format-tip-wrp").show(300);
} else {
$("#format-tip-wrp").hide(300);
}
}
$("#show-format-tip").on("click", toggleFormatTip);
$("#hide-format-tip").on("click", toggleFormatTip);
$(".bmi-review-btn").on("click", function (e) {
e.preventDefault();
var url = $(this).attr("href");
$.bmi.ajax("clicked-on-plugin-review", {}).then(function (res) {
$(".bmi-ask-for-review").hide(300);
window.open(url, "_blank");
});
});
$(".go-to-marbs").on("click", function (e) {
e.preventDefault();
document.getElementById("marbs").click();
$.bmi.modal().closeAll();
});
$(".go-to-stgng").on("click", function (e) {
e.preventDefault();
document.getElementById("stgng").click();
$.bmi.modal().closeAll();
});
$(".site-reloader").on("click", function () {
element_enable_crons = [
"choose-auto-backup-interval",
"weekly-auto-backup-switch",
];
let element_id = $(this).attr("id");
let crons_enabled = element_enable_crons.includes(element_id)
? "&crons=true"
: null;
let url = autoLog.u;
if (url.slice(-url.length) !== "/") url = url + "/";
let url_final =
url +
"?backup-migration=AFTER_RESTORE&bmi-id=" +
autoLog.l +
"&progress-id=4u70L051n&t=" +
crons_enabled +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
window.location = url_final;
});
$(".get-file-database-sizes").on("click", function (e) {
e.preventDefault();
let resources = [
"plugins",
"uploads",
"themes",
"contents_others",
"wordpress",
"database",
];
resetSpinners(resources, true);
saveBtnEventHandler(false, "save-file-config");
});
$("#bmi-pro-storage-gdrive-toggle").on("change", function (e) {
if (!$("#bmi-pro-storage-gdrive-toggle").is(":checked")) {
$("td.br_stroage.center").addClass("bmi-gdrive-disabled");
} else {
$("td.br_stroage.center").removeClass("bmi-gdrive-disabled");
}
});
$(".open-logs-modal-url").on("click", function () {
let parentId = $(this).closest(".bmi-modal").attr("id");
$.bmi.modal("logs-modal").setParent(parentId);
$.bmi.modal(parentId).close();
$.bmi.modal("freeze-loading-modal").open();
setupLogsModal();
});
$(".skip-share-logs-after-restore, .shared-log-after-restore").on("click", function () {
$.bmi.modal("supportive-restore-success-modal").close();
$.bmi.modal("supportive-restore-success-cont-modal").open();
});
$(".try-in-different-way").on("click", tryInDifferentWay);
function refreshBBStorageInfo(silent=false) {
$('.refresh-img').addClass("loading");
$.bmi.ajax('bb-storage-info').then(function (res) {
if (res.status == 'success') {
$(".bb-storage-amount").text(res.data.storage_info.total_space_humanized);
$(".bb-storage-used").text(res.data.storage_info.used_space_humanized);
$(".bb-storage-used-percent").text("("+res.data.storage_info.used_space_percent+"%)");
} else {
if (!silent)
$.bmi.alert('error', res.message);
}
$('.refresh-img').removeClass("loading");
}).catch(function (error) {
$('.refresh-img').removeClass("loading");
if (!silent)
$.bmi.alert('error', 'There was an error contacting the plugin backend.');
});
}
refreshBBStorageInfo(true);
$(".refresh-bb-storage").on("click", function(e){
e.preventDefault();
refreshBBStorageInfo();
});
$("#bb-upload-fail-dismiss").on("click", function(e){
e.preventDefault();
$.bmi
.ajax("dismiss-error-notice", {
option_id: "backupbliss-dismiss-upload-issue",
})
.then(function (res) {})
.catch(function (error) {});
$("#bb-warning-notice").hide(300);
setTimeout(function () {
$("#bb-warning-notice").remove();
}, 330);
});
$(".bb-disconnect").on("click", function (e) {
e.preventDefault();
$.bmi.modal("bb-disconnect-modal").open();
});
$(".bb-disconnect-cancel").on("click", function (e) {
e.preventDefault();
$.bmi.modal("bb-disconnect-modal").close();
});
$(".bb-disconnect-btn").on("click", function(e){
e.preventDefault();
$.bmi.modal("bb-disconnect-modal").close();
$.bmi.modal("freeze-loading-modal").open();
$.bmi.ajax('bb-disconnect').then(function (res) {
if (res.status == 'success') {
$(".how-it-works").show();
$(".youre-connected").hide();
} else {
$.bmi.alert('error', res.message);
}
$.bmi.modal('freeze-loading-modal').close();
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'There was an error contacting the plugin backend.');
});
});
$(".bb-connect").on("click", function(e){
e.preventDefault();
$.bmi.modal("freeze-loading-modal").open();
$.bmi.ajax('bb-connect', {
api_key: $(".api-key-input").val()
}).then(function (res) {
if (res.status == 'success') {
refreshBBStorageInfo();
$(".how-it-works").hide();
$(".youre-connected").show();
$.bmi.reloadBackups();
} else {
$.bmi.alert('error', res.message);
}
$.bmi.modal('freeze-loading-modal').close();
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'There was an error contacting the plugin backend.');
});
});
$('#dropbox-connect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('get-dropbox-token').then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.token != 'undefined') {
let url = 'https://authentication.backupbliss.com/v1/dropbox/connect';
url += '?token=' + encodeURIComponent(res.token);
url += '&redirect=' + encodeURIComponent(window.location.origin + window.location.pathname);
window.location.href = url;
} else {
$.bmi.alert('error', 'We could not generate individual token at this moment, please refresh page and try again [#Dropbox-02].');
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#Dropbox-01].');
});
});
$('#dropbox-disconnect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('disconnect-dropbox').then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status == 'success') {
window.location.reload();
} else {
$.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
});
});
function verifyDropboxConnection(silent = false) {
$.bmi.ajax('verify-dropbox-connection', { uri: window.location.host }).then(function (res) {
$('#dropbox-unauthenticated-box').show();
$('#dropbox-authenticated-box').hide();
if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
if (res.result == 'connected') {
$('#dropbox-unauthenticated-box').hide();
$('#dropbox-authenticated-box').show();
}
} else if (typeof res.status != 'undefined' && res.status == 'error') {
if (!silent) {
$.bmi.alert('error', 'We could not verify Dropbox connection, some error happened during communication with API, look for more details in global logs [#DB-07].');
}
} else {
if (!silent) {
$.bmi.alert('error', 'We could not verify Dropbox connection, please refresh page and try again [#DB-05].');
}
}
}).catch(function (error) {
if (!silent) {
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#DB-08].');
}
});
}
$('#bmi-pro-storage-dropbox-toggle').on('change', function (e) {
e.preventDefault();
$('#dropbox-authenticated-box').hide();
$('#dropbox-unauthenticated-box').show();
if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === true) {
verifyDropboxConnection();
}
});
$('#bmip-dropbox-issues-dismiss').on("click", function () {
$('#dropbox-issues').hide(300);
setTimeout(function () {
$('#dropbox-issues').remove();
}, 330);
$.bmi.ajax('dismiss-dropbox-notice', {}).then(function (res) { }).catch(function (error) { });
});
$('#gdrive-connect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
let backupDirectoryPath = $('#bmip-googledrive-path').val();
$.bmi.ajax('get-gdrive-token',
{ backupDirectoryPath: backupDirectoryPath }
).then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.token != 'undefined') {
let url = 'https://authentication.backupbliss.com/v1/gdrive/connect';
url += '?token=' + encodeURIComponent(res.token);
url += '&redirect=' + encodeURIComponent(window.location.origin + window.location.pathname);
window.location.href = url;
} else {
if (typeof res.status != 'undefined' && res.status === 'msg') {
$.bmi.alert('warning', res.why);
} else {
$.bmi.alert('error', 'We could not generate individual token at this moment, please refresh page and try again [#GD-02].');
}
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-01].');
});
});
$('#gdrive-disconnect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('disconnect-gdrive').then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status == 'success') {
window.location.reload();
} else {
$.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again [#GD-03].');
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-04].');
});
});
function verifyGDriveConnection(silent = false) {
$.bmi.ajax('verify-gdrive-connection', { uri: window.location.host }).then(function (res) {
$('#gdrive-unauthenticated-box').show();
$('#gdrive-authenticated-box').hide();
if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
if (res.result == 'connected') {
$('#gdrive-unauthenticated-box').hide();
$('#gdrive-authenticated-box').show();
}
} else if (typeof res.status != 'undefined' && res.status == 'error') {
if (!silent) {
$.bmi.alert('error', 'We could not verify Google Drive connection, some error happened during communication with API, look for more details in global logs [#GD-07].');
}
} else {
if (!silent) {
$.bmi.alert('error', 'We could not verify Google Drive connection, please refresh page and try again [#GD-05].');
}
}
}).catch(function (error) {
if (!silent) {
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-08].');
}
});
}
$('#bmi-pro-storage-gdrive-toggle').on('change', function (e) {
e.preventDefault();
$('#gdrive-authenticated-box').hide();
$('#gdrive-unauthenticated-box').show();
if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === true) {
verifyGDriveConnection();
}
});
$('#ftp-connect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
let data = {};
data['bmip-ftp-host'] = $('#bmip-ftp-host-ip').val();
data['bmip-ftp-backup-dir'] = $('#bmip-ftp-backup-dir').val();
data['bmip-ftp-host-port'] = $('#bmip-ftp-host-port').val();
data['bmip-ftp-username'] = $('#bmip-ftp-user-name').val();
data['bmip-ftp-password'] = $('#bmip-ftp-password').val();
$.bmi.ajax('get-ftp-config', data).then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status === 'success') {
if (e) $.bmi.alert('success', $('#bmi-save-connect-ftp').text(), 3000);
setTimeout(function () {
window.location.reload();
}, 300);
} else {
$.bmi.alert('error', res.msg);
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', '');
});
});
$('#ftp-disconnect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('disconnect-ftp').then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status == 'success') {
window.location.reload();
} else {
$.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again [#GD-03].');
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-04].');
});
});
$('#aws-connect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
let data = {};
data['access-key'] = $('#bmip-aws-access-key').val();
data['secret-key'] = $('#bmip-aws-secret-key').val();
data['bucket'] = $('#bmip-aws-bucket').val();
data['sse'] = $('#bmip-aws-sse').is(':checked') === true ? $('#bmip-aws-sse').val().trim() : '';
data['path'] = $('#bmip-aws-path').val();
data['storage-class'] = $('[data-id="bmip-aws-storage-class"]').attr('data-selected');
data['region'] = $('[data-id="bmip-aws-region"]').attr('data-selected');
$.bmi.ajax('save-aws-config', data).then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status === 'success') {
if (e) $.bmi.alert('success', $('#bmi-save-connect-s3-success').text(), 3000);
window.location.reload();
} else {
$.bmi.alert('error', res.msg);
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'There was an error while trying to connect to Amazon S3, please try again.');
});
});
$('#aws-disconnect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('disconnect-aws').then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status == 'success') {
window.location.reload();
} else {
$.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
});
});
function verifyAWSConnection(silent = false) {
$.bmi.ajax('verify-aws-connection', { uri: window.location.host}).then(function (res) {
$('#aws-unauthenticated-box').show();
$('#aws-authenticated-box').hide();
if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
if (res.result == 'connected') {
$('#aws-unauthenticated-box').hide();
$('#aws-authenticated-box').show();
$('#bmip-aws-path').val(res.configs['path']).attr('readonly', true);
$('#bmip-aws-bucket').val(res.configs['bucket']).attr('readonly', true);
$('.bmi-dropdown[data-id="bmip-aws-storage-class"]').attr('data-selected', res.configs['storage-class']).val(res.configs['storage-class']).css('background', '#f1f1f1').css('pointerEvents', 'none');
$('#storage-s3-row .region-container').hide();
$('#storage-s3-row .aws-access-key-container').hide();
$('#storage-s3-row .aws-secret-key-container').hide();
$('#bmip-aws-sse').prop('checked', res.configs['sse'] === 'AES256').attr('disabled', true).css('cursor', 'not-allowed');
$('#bmip-aws-sse').closest('.checkbox-container').css('cursor', 'not-allowed');
} else {
$('#aws-unauthenticated-box').show();
$('#aws-authenticated-box').hide();
}
} else if (typeof res.status != 'undefined' && res.status == 'error') {
if (!silent) {
$.bmi.alert('error', 'We could not verify AWS S3 connection, some error happened during communication with API.');
}
} else {
if (!silent) {
$.bmi.alert('error', 'We could not verify AWS S3 connection, please refresh page and try again.');
}
}
}).catch(function (error) {
if (!silent) {
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
}
});
}
$('#bmi-pro-storage-aws-toggle').on('change', function (e) {
e.preventDefault();
$('#aws-authenticated-box').hide();
$('#aws-unauthenticated-box').show();
if ($('#bmi-pro-storage-aws-toggle').is(':checked') === true) {
verifyAWSConnection();
}
});
$('#wasabi-connect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
let data = {};
data['access-key'] = $('#bmip-wasabi-access-key').val();
data['secret-key'] = $('#bmip-wasabi-secret-key').val();
data['bucket'] = $('#bmip-wasabi-bucket').val();
data['path'] = $('#bmip-wasabi-path').val();
data['region'] = $('[data-id="bmip-wasabi-region"]').attr('data-selected');
$.bmi.ajax('save-wasabi-config', data).then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status === 'success') {
if (e) $.bmi.alert('success', $('#bmi-save-connect-s3-success').text(), 3000);
window.location.reload();
} else {
$.bmi.alert('error', res.msg);
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'There was an error while trying to connect to Wasabi, please try again.');
});
});
$('#wasabi-disconnect-btn').on('click', function (e) {
e.preventDefault();
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('disconnect-wasabi').then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
if (typeof res.status != 'undefined' && res.status == 'success') {
window.location.reload();
} else {
$.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
}
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
});
});
function verifyWasabiConnection(silent = false) {
$.bmi.ajax('verify-wasabi-connection', { uri: window.location.host}).then(function (res) {
$('#wasabi-unauthenticated-box').show();
$('#wasabi-authenticated-box').hide();
if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
if (res.result == 'connected') {
$('#wasabi-unauthenticated-box').hide();
$('#wasabi-authenticated-box').show();
$('#bmip-wasabi-path').val(res.configs['path']).attr('readonly', true);
$('#bmip-wasabi-bucket').val(res.configs['bucket']).attr('readonly', true);
$('#storage-wasabi-row .region-container').hide();
$('#storage-wasabi-row .wasabi-access-key-container').hide();
$('#storage-wasabi-row .wasabi-secret-key-container').hide();
} else {
$('#wasabi-unauthenticated-box').show();
$('#wasabi-authenticated-box').hide();
}
} else if (typeof res.status != 'undefined' && res.status == 'error') {
if (!silent) {
$.bmi.alert('error', 'We could not verify Wasabi connection, some error happened during communication with API.');
}
} else {
if (!silent) {
$.bmi.alert('error', 'We could not verify Wasabi connection, please refresh page and try again.');
}
}
}).catch(function (error) {
if (!silent) {
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
}
});
}
$('#bmi-pro-storage-wasabi-toggle').on('change', function (e) {
e.preventDefault();
$('#wasabi-authenticated-box').hide();
$('#wasabi-unauthenticated-box').show();
if ($('#bmi-pro-storage-wasabi-toggle').is(':checked') === true) {
verifyWasabiConnection();
}
});
$('#bmi_restore_tbody').on('click', '.can-be-manually-uploaded', function (e) {
e.preventDefault();
const classList = $(this).attr('class').split(/\s+/);
let type = false;
let md5 = $(this).closest('tr').attr('md5');
for (let cls of classList) {
if (cls.startsWith('strg-')) {
type = cls.replace('strg-', ''); // get the part after 'strg-'
break;
}
}
$.bmi.ajax('manually-enqueue-upload', {
type: type,
md5: md5
}).then(function (res) {
if (res.status == 'success') {
$(this).removeClass('can-be-manually-uploaded');
$.bmi.alert('success', 'Backup will be added to the upload queue shortly.');
} else {
$.bmi.alert('error', res.msg || 'There was an error while trying to enqueue the backup for upload.');
}
});
});
$(window).on("bmi-preload-collapsed", function (e) {
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
if (typeof params.bmi_drive != 'undefined') {
window.history.pushState('', window.title, window.location.pathname + '?page=backup-migration');
if (typeof params.bmi_auth_status != 'undefined' && params.bmi_auth_status == 'error') {
if (typeof params.bmi_error_reason != 'undefined') {
if (params.bmi_drive == 'gdrive') {
if (params.bmi_error_reason == '1') return $.bmi.alert('error', 'Error during Google Drive connection: We could not setup auth space for you at this moment. [#GD-11].');
if (params.bmi_error_reason == '2') return $.bmi.alert('error', 'Error during Google Drive connection: We could not check for existing authentication, please try again. [#GD-12].');
if (params.bmi_error_reason == '3') return $.bmi.alert('error', 'Error during Google Drive connection: We could not update your your auth token, please try again. [#GD-13].');
if (params.bmi_error_reason == '4') return $.bmi.alert('error', 'Error during Google Drive connection: Access has been denied by you during authorization. [#GD-14].');
if (params.bmi_error_reason == '5') return $.bmi.alert('error', 'Error during Google Drive connection: Some unexpected error happened (google side), please try again. [#GD-15].');
if (params.bmi_error_reason == '6') return $.bmi.alert('error', 'Error during Google Drive connection: Access token and refresh token has been revoked. [#GD-16].');
} else if (params.bmi_drive == 'dropbox') {
if (params.bmi_error_reason == '1') return $.bmi.alert('error', 'Error during Dropbox connection: We could not setup auth space for you at this moment. [#DB-11].');
if (params.bmi_error_reason == '2') return $.bmi.alert('error', 'Error during Dropbox connection: We could not check for existing authentication, please try again. [#DB-12].');
if (params.bmi_error_reason == '3') return $.bmi.alert('error', 'Error during Dropbox connection: We could not update your your auth token, please try again. [#DB-13].');
if (params.bmi_error_reason == '4') return $.bmi.alert('error', 'Error during Dropbox connection: Access has been denied by you during authorization. [#DB-14].');
if (params.bmi_error_reason == '5') return $.bmi.alert('error', 'Error during Dropbox connection: Some unexpected error happened (dropbox side), please try again. [#DB-15].');
if (params.bmi_error_reason == '6') return $.bmi.alert('error', 'Error during Dropbox connection: Access token and refresh token has been revoked. [#DB-16].');
}
}
}
if (typeof params.bmi_token == 'undefined' || typeof params.bmi_secret == 'undefined' || typeof params.bmi_auth_status == 'undefined') {
if (params.bmi_drive == 'gdrive') {
return $.bmi.alert('error', 'Cannot save connection with Google Drive, due to insufficient details [#GD-09].');
} else if (params.bmi_drive == 'dropbox') {
return $.bmi.alert('error', 'Cannot save connection with Dropbox, due to insufficient details [#DB-09].');
}
}
if (params.bmi_auth_status != 'success') {
if (params.bmi_drive == 'gdrive') {
return $.bmi.alert('error', 'Cannot save connection with Google Drive, due to unknown response from Google API [#GD-10].');
} else if (params.bmi_drive == 'dropbox') {
return $.bmi.alert('error', 'Cannot save connection with Dropbox, due to unknown response from Dropbox API [#DB-10].');
}
}
$.bmi.modal('freeze-loading-modal').open();
$('#bmi-external-storage-options').click();
if (params.bmi_drive == 'gdrive') {
if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === false) {
$('#bmi-pro-storage-gdrive-toggle').click();
}
} else if (params.bmi_drive == 'dropbox') {
if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === false) {
$('#bmi-pro-storage-dropbox-toggle').click();
}
}
if (params.bmi_drive == 'gdrive') {
$.bmi.ajax('keep-gdrive-connection', { receivedToken: params.bmi_token, receivedClientID: params.bmi_secret }).then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
setTimeout(function () {
$('#bmi-pro-storage-gdrive-toggle')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
setTimeout(function () {
$('#gdrive-unauthenticated-box').fadeOut();
$('#gdrive-authenticated-box').fadeIn();
setTimeout(function () {
$('#gdrive-authed-content-box')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
window.saveStorageWithoutClose = true;
$('#storage-options').find('.save-btn').click();
}, 300);
}, 300);
}, 300);
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot save connection with Google Drive, please try again [#GD-06].');
});
} else if (params.bmi_drive == 'dropbox') {
$.bmi.ajax('keep-dropbox-connection', { receivedToken: params.bmi_token, receivedClientID: params.bmi_secret }).then(function (res) {
$.bmi.modal('freeze-loading-modal').close();
setTimeout(function () {
$('#bmi-pro-storage-dropbox-toggle')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
setTimeout(function () {
$('#dropbox-unauthenticated-box').fadeOut();
$('#dropbox-authenticated-box').fadeIn();
setTimeout(function () {
$('#dropbox-authed-content-box')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
window.saveStorageWithoutClose = true;
$('#storage-options').find('.save-btn').click();
}, 300);
}, 300);
}, 300);
}).catch(function (error) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.alert('error', 'Cannot save connection with Dropbox, please try again [#DB-06].');
});
} else {
jQuery(window).trigger("bmi-preload-collapsed-pro", [params]);
}
}
if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === true) {
verifyDropboxConnection(true);
}
if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === true) {
verifyGDriveConnection(true);
}
if ($('#bmi-pro-storage-aws-toggle').is(':checked') === true) {
verifyAWSConnection(true);
}
if ($('#bmi-pro-storage-wasabi-toggle').is(':checked') === true) {
verifyWasabiConnection(true);
}
});
// Currently not used
$('.bmi-check-disk-space').on('click', function (e) {
e.preventDefault();
// Prevent multiple clicks
if (!$('.space-checking .loading').hasClass('hide_verbose')) return;
$('.space-checking .loading').removeClass('hide_verbose');
// Hide all verbose messages if any shown
if (!$('.checking-result .not-enough-space').hasClass('hide_verbose')) $('.checking-result .not-enough-space').addClass('hide_verbose');
if (!$('.checking-result .enough-space').hasClass('hide_verbose')) $('.checking-result .enough-space').addClass('hide_verbose');
if (!$('.checking-result .failed').hasClass('hide_verbose')) $('.checking-result .failed').addClass('hide_verbose');
$.bmi.ajax('check-disk-space').then(function (res) {
if (res.status == 'enough-space') {
$('.space-checking .loading').addClass('hide_verbose');
$('.checking-result .enough-space').removeClass('hide_verbose');
} else if (res.status == 'not-enough-space') {
$('.space-checking .loading').addClass('hide_verbose');
$('.checking-result .not-enough-space').removeClass('hide_verbose');
$('.checking-result .not-enough-space').find('.required-space').text(res.data['required']);
$('.checking-result .not-enough-space').find('.available-space').text(res.data['available']);
}
}).catch(function (error) {
console.error(error);
$('.space-checking .loading').addClass('hide_verbose');
$('.checking-result .failed').removeClass('hide_verbose');
});
});
async function tryInDifferentWay() {
let errorParent = $.bmi.modal("error-modal").getParent();
if (errorParent == "backup-progress-modal") {
if (isNaN(getSelectedSize())) {
setTimeout(tryInDifferentWay, 1000);
}
$("#cli-disable-others").prop("checked", true);
$("#experimental-hard-timeout").prop("checked", true);
$("#download-technique").prop("checked", true);
$("#bmi-db-batching-backup").prop("checked", true);
$("#bmi-use-new-database-export-engine").prop("checked", true);
await saveOtherOptionsPromise();
$.bmi.modal($(this).closest(".bmi-modal").attr("id")).close();
$("#start-entire-backup").click();
} else if (errorParent == "restore-progress-modal") {
$("#cli-disable-others").prop("checked", true);
$("#file_limit_extraction_max").val("300");
await saveOtherOptionsPromise();
$.bmi.modal($(this).closest(".bmi-modal").attr("id")).close();
$("#restore-start-sure").click();
}
}
function bmiSetFailureReasonsState(reasonCount) {
const $plural = $(".there-are-reasons");
const $single = $(".there-is-a-reason");
const $none = $(".there-are-no-reasons");
const $box = $(".debug-it-yourself");
const $content = $box.find(".content");
// Hide all first
$plural.hide();
$single.hide();
$none.hide();
if (reasonCount <= 0) {
$none.show();
$box.removeClass("active");
$content.css("display", "none");
} else if (reasonCount === 1) {
$single.show();
$box.addClass("active");
$content.css("display", "block");
} else {
$plural.show();
$box.addClass("active");
$content.css("display", "block");
}
}
async function setupRestoreErrorOptions() {
$.bmi.modal("freeze-loading-modal").open();
$("#error-modal").find(".modal-title").text($("#bmi-restore-error-modal-title").text());
const tryInDifferentWayBtn = $(".try-in-different-way");
const tryInDifferentWayOption = tryInDifferentWayBtn.closest(".failure-option");
const failureReasons = $(".failure-reasons");
if (
!$("#cli-disable-others").is(":checked") ||
$("#file_limit_extraction_max").val() != "300"
) {
tryInDifferentWayOption.show();
} else {
tryInDifferentWayOption.hide();
}
try {
const res = await $.bmi.ajax("check-comptability", {
for: "restore"
});
$.bmi.modal("freeze-loading-modal").close();
failureReasons.empty();
let reasonCount = 0;
if (res.status === "success" ) {
if (Array.isArray(res.data) && res.data.length > 0) {
res.data.forEach(r => failureReasons.append("" + r + ""));
reasonCount = failureReasons.find("li").length;
}
if (res.mainReasonFound) {
tryInDifferentWayOption.hide();
}
}
bmiSetFailureReasonsState(reasonCount);
setupDownloadLogsHref("restore");
} catch (error) {
console.log(error);
$.bmi.modal("freeze-loading-modal").close();
}
}
async function setupBackupErrorOptions() {
$.bmi.modal("freeze-loading-modal").open();
$("#error-modal").find(".modal-title").text($("#bmi-error-modal-title").text());
const tryInDifferentWayBtn = $(".try-in-different-way");
const tryInDifferentWayOption = tryInDifferentWayBtn.closest(".failure-option");
const failureReasons = $(".failure-reasons");
if (
!$("#cli-disable-others").is(":checked") ||
!$("#experimental-hard-timeout").is(":checked") ||
!$("#download-technique").is(":checked") ||
!$("#bmi-db-batching-backup").is(":checked") ||
!$("#bmi-use-new-database-export-engine").is(":checked")
) {
if (!tryInDifferentWayOption.hasClass("force_hide")) {
tryInDifferentWayOption.show();
}
} else {
tryInDifferentWayOption.hide();
}
try {
const res = await $.bmi.ajax("check-comptability", {
for: "backup"
});
$.bmi.modal("freeze-loading-modal").close();
failureReasons.empty();
let reasonCount = 0;
if (res.status === "success" && Array.isArray(res.data) && res.data.length > 0) {
res.data.forEach(r => failureReasons.append("" + r + ""));
reasonCount = failureReasons.find("li").length;
}
bmiSetFailureReasonsState(reasonCount);
setupDownloadLogsHref("backup");
} catch (error) {
console.log(error);
$.bmi.modal("freeze-loading-modal").close();
}
}
async function fillLogs(
type = "backup",
preElement = $(".log-wrapper").find("pre")[0],
logPath = "?backup-migration=PROGRESS_LOGS&progress-id=latest_full.log&uncensored=true&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim(),
) {
let url = $("#BMI_BLOG_URL").text().trim();
if (url.slice(-url.length) !== "/") url = url + "/";
if (type === "backup") logPath += "&progress-id=latest_full.log";
else if (type === "restore") logPath += "&progress-id=latest_migration_full.log";
return new Promise((resolve, reject) => {
httpGet(url + logPath, function (log) {
let res1 = log.split("\n").slice(0, 1)[0];
if (res1.trim() === "") {
res1 = log.split("\n").slice(0, 2)[1];
log = log.split("\n").slice(2).join("\n");
} else {
log = log.split("\n").slice(1).join("\n");
}
if (log && log != false && typeof log !== "undefined") {
if (log === false) {
reject("Error: Failed to retrieve backup logs");
return;
}
let lines = log.split("\n");
if (lines.length >= 1) lines = lines.slice(0, -1);
preElement.innerText = "";
showAllLines(preElement, lines);
resolve();
} else {
reject("Error: Failed to retrieve backup logs");
}
});
});
}
function runStagingTimers() {
setInterval(function () {
$trs = $(".bmi-tastewp-staging-row");
$trs.each(function (i) {
let $tr = $($trs[i]);
let expiration = $tr.attr("expiration");
if (expiration && !isNaN(parseInt(expiration))) {
expiration = parseInt(expiration);
$tr
.find(".stg-tr-expiration span")
.text($.bmi.getExpirationTime(expiration));
}
});
}, 1000);
}
function setupLogsModal() {
let errorParent = $.bmi.modal("error-modal").getParent();
let type = "";
if (errorParent == "backup-progress-modal") {
$("#logs-modal").find(".modal-title").text($('#bmi-backup-logs-modal-title').text());
type = "backup";
} else if (errorParent == "restore-progress-modal") {
$("#logs-modal").find(".modal-title").text($('#bmi-restore-logs-modal-title').text());
type = "restore";
}
let preElement = $("#logs-modal").find("pre")[0];
fillLogs(type, preElement).then(function () {
$.bmi.modal("freeze-loading-modal").close();
setTimeout(function () {
$.bmi.modal("logs-modal").open();
preElement.scroll({ top: preElement.scrollHeight });
}, 300);
});
}
function fillRestoreLogs() {
let textarea = $('#restore-log')[0];
textarea.value = restoreLogs;
}
function setupDownloadLogsHref(type = "backup") {
let url = $("#BMI_BLOG_URL").text().trim();
if (url.slice(-url.length) !== "/") url = url + "/";
$progressId = type == "backup" ? "latest_full.log" : "latest_migration_full.log";
let logs_url = url + "?backup-migration=PROGRESS_LOGS&progress-id=" + $progressId + "&bmi-id=current&t=" + +new Date() + "&sk=" + $("#BMI_SECRET_KEY").text().trim();
$(".download-log-url.censored").attr("download", "secure-" + (type == "backup" ? "backup" : "restore") + "-logs.txt")
$(".download-log-url.uncensored").attr("download", (type == "backup" ? "backup" : "restore") + "-logs.txt")
$(".download-log-url.censored").attr("href", logs_url);
$(".download-log-url.uncensored").attr("href", logs_url + "&uncensored=true");
}
function getCurrentRestore() {
return current_restore;
}
function getIsDownloadBackupFinished() {
return downloadBackupFinished;
}
$.bmi.getCurrentRestore = getCurrentRestore;
$.bmi.getIsDownloadBackupFinished = getIsDownloadBackupFinished;
function updateSuffixPosition() {
let localBackupsPath = document.getElementById('bmi_path_storage_default');
let localBackupsPathSuffix = document.getElementById('local-backups-suffix');
let textWidth = getTextWidth(localBackupsPath.value, window.getComputedStyle(localBackupsPath).font);
let suffixWidth = getTextWidth(localBackupsPathSuffix.innerText, window.getComputedStyle(localBackupsPathSuffix).font);
localBackupsPath.style.paddingRight = suffixWidth + 22 + 'px';
if (localBackupsPath.value === '') {
localBackupsPathSuffix.style.display = 'none';
return;
} else {
localBackupsPathSuffix.style.display = 'inline-block';
}
localBackupsPathSuffix.style.left = Math.min(textWidth + 20, localBackupsPath.clientWidth - suffixWidth - 20) + 'px';
}
function getTextWidth(text, font) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.font = font;
return ctx.measureText(text).width;
}
$('#bmi_path_storage_default').on('input', updateSuffixPosition);
$('#storage-options').on('click', function() {
setTimeout(() => {
updateSuffixPosition();
}, 100);
setTimeout(() => {
updateSuffixPosition();
}, 300);
});
$('#bmi_backup_search_input').on('input', function() {
if ($(this).hasClass('processing')) return;
$(this).addClass('processing');
let searchTerm = $(this).val().toLowerCase();
if (searchTerm.trim() === '') {
$.bmi.reloadBackups();
$(this).removeClass('processing');
return;
}
$.bmi.searchInBackups(searchTerm);
setTimeout(() => {
$(this).removeClass('processing');
}, 500);
});
$('#bmi_backup_search_input').on('keypress', function(e) {
if (e.which === 13) {
e.preventDefault();
let searchTerm = $(this).val().toLowerCase();
if (searchTerm.trim() === ''){
$.bmi.reloadBackups();
return;
}
$.bmi.searchInBackups(searchTerm);
}
});
$(window).on('resize', updateSuffixPosition);
$(document).on('visibilitychange', async function() {
if ((backupOnGoing || restoreOnGoing) && document.visibilityState === 'visible') {
await $.bmi.requestWakeLock();
}
});
$('.resync-with-ping-server').on('click', function(e) {
e.preventDefault();
$.bmi.ajax('resync-with-ping-server').then(function (res) {
if (res.status == 'success') {
$.bmi.alert('success', 'Your site has been registered successfully with our ping server.');
} else {
$.bmi.alert('error', res.msg || 'There was an error while trying to resync with ping server.');
}
}).catch(function (error) {
$.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
});
});
function applyActionBasedOnLogLines(lines) {
if ($("#backup-stop").hasClass("disabled")) {
let backupInitializedLine = lines.find(line => line.includes("backup_initialized"));
if (backupInitializedLine) {
$("#backup-stop").removeClass("disabled");
}
}
}
// Init
(function () {
if (pagenow !== 'toplevel_page_backup-migration') return;
scanDirectories();
getAndInsertDynamicNames();
$.bmi.reloadBackups();
$.bmi.adjustStorageIcons();
$.bmi.reloadStaging();
runTimerST();
runStagingTimers();
let url_x = $("#BMI_BLOG_URL").text().trim();
if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
let logs_backup =
url_x +
"?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
let logs_backup_uncensored =
url_x +
"?backup-migration=PROGRESS_LOGS&progress-id=latest.log&uncensored=true&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
let logs_restore =
url_x +
"?backup-migration=PROGRESS_LOGS&progress-id=latest_migration.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
let logs_staging =
url_x +
"?backup-migration=PROGRESS_LOGS&progress-id=latest_staging.log&bmi-id=current&t=" +
+new Date() +
"&sk=" +
$("#BMI_SECRET_KEY").text().trim();
$(".download-backup-log-url").attr("href", logs_backup);
$(".download-backup-log-url.uncensored").attr(
"href",
logs_backup_uncensored,
);
$(".download-restore-log-url").attr("href", logs_restore);
$(".download-staging-log-url").attr("href", logs_staging);
})();
});
jQuery(document).ready(function($) {
$('.bmi-modal-opener').on('click', function(e) {
if (this.getAttribute('data-modal') && this.getAttribute('data-modal') != '') {
e.preventDefault();
if (this.getAttribute('data-close'))
$.bmi.modal(this.getAttribute('data-close')).close();
$.bmi.modal(this.getAttribute('data-modal')).open();
}
});
$('.bmi-modal-closer').on('click', function(e) {
if (this.getAttribute('data-close') && this.getAttribute('data-close') != '') {
e.preventDefault();
$.bmi.modal(this.getAttribute('data-close')).close();
} else {
if (this.closest('.bmi-modal')) {
e.preventDefault();
if ($('#' + this.closest('.bmi-modal').getAttribute('id')).length > 0)
$.bmi.modal(this.closest('.bmi-modal').getAttribute('id')).close();
}
}
});
$('.bmi-modal-close').on('click', function(e) {
if (this.closest('.bmi-modal')) {
e.preventDefault();
$.bmi.modal(this.closest('.bmi-modal').id).close();
}
});
$('.bmi-modal').on('click', function(e) {
if (e.target == this && !$(e.target).hasClass('bmi-modal-no-close')) {
$.bmi.modal(this.id).close();
}
});
$('.bmi-modal-back').on('click', function(e) {
if (this.closest('.bmi-modal')){
e.preventDefault();
let modal = $.bmi.modal(this.closest('.bmi-modal').id);
let modalParent = modal.getParent();
if ( modalParent ){
modal.close();
$.bmi.modal(modalParent).open();
}
}
});
});jQuery(document).ready(function($) {
// Init tooltips
if(pagenow === 'toplevel_page_backup-migration')
$.bmi.tooltips.init();
// Progress interval
let upload_progress, current_last = -1,
startmsg = false;
// Replace pleaceholders with real preloaders
let preloader_divs = '';
for (let i = 0; i < 12; ++i) preloader_divs += '';
$('.spinner-loader').html(preloader_divs).addClass('lds-spinner');
function setProgress(end = 0, duration = 1000) {
if (current_last == end) return;
else current_last = end;
clearInterval(upload_progress);
let start = parseInt($('.upload-percentage').text()) - 1;
if (start > end && end != 0) return;
let range = end - start;
let current = start;
let increment = 1;
let stepTime = Math.abs(Math.floor(duration / range));
upload_progress = setInterval(function() {
current += increment;
$('.upload-progress-bar').find('span')[0].style.width = current + '%';
$('.upload-percentage').text(current + '%');
if (current >= 100) {
clearInterval(upload_progress);
current_last = null;
}
}, stepTime);
}
function Progress(value) {
clearInterval(upload_progress);
if (value == 0) {
$('.upload-progress-bar').find('span')[0].style.width = value + '%';
$('.upload-percentage').text(value + '%');
} else if (value == 100) {
$('.upload-progress-bar').find('span')[0].style.width = value + '%';
$('.upload-percentage').text(value + '%');
} else {
$('.upload-progress-bar').find('span')[0].style.width = value + '%';
$('.upload-percentage').text(value + '%');
// setProgress(value, 300);
}
}
$.fchunker({
upId: 'upid',
upShardSize: bmiVariables.maxUploadSize,
upMaxSize: '2000',
upUrl: ajaxurl + '?cache=false',
upType: 'zip,tar,gz',
bmiNonce: bmiVariables.nonce,
upCallBack: function(res) {
var status = res.status;
var msg = res.message;
var url = res.url + "?" + Math.random();
if (status == 2) {
setTimeout(function() {
$('#drop-area').show(300);
$('.upload-progress').hide(300);
}, 100);
$.bmi.alert('success', $('#bmi-upload-end').text(), 3000);
$.bmi.modal('upload-success-modal').open();
$.bmi.reloadBackups();
}
if (status == 1) {
// console.log(msg);
if (!startmsg) {
$.bmi.alert('success', $('#bmi-upload-start').text(), 3000);
startmsg = true;
}
}
if (status == 0) {
$.upErrorMsg(msg);
$('#drop-area').show(300);
$('.upload-progress').hide(300);
}
if (status == 5) {
$.bmi.modal('upload-invalid-manifest-modal').open();
$('#drop-area').show(300);
$('.upload-progress').hide(300);
}
if (status == 3) {
Progress(100);
$.upErrorMsg(msg);
$('#drop-area').show(300);
$('.upload-progress').hide(300);
}
},
upEvent: function(num) {
Progress(num);
},
upStop: function(errmsg) {
Progress(0);
setTimeout(function() {
$('#drop-area').show(300);
$('.upload-progress').hide(300);
}, 100);
if (errmsg.includes('Type error')) {
$.bmi.modal('upload-wrong-file-modal').open();
$.bmi.alert('warning', $('#bmi-upload-wrong').text(), 3000);
} else if (errmsg.includes('File already exists')) {
$.bmi.modal('upload-exist-file-modal').open();
$.bmi.alert('warning', $('#bmi-upload-exists').text(), 3000);
} else {
$.bmi.alert('error', $('#bmi-upload-error').text(), 3000);
console.error(errmsg);
}
},
upStart: function() {
startmsg = false;
current_last = -1;
Progress(0);
setTimeout(function() {
$('#drop-area').hide(300);
$('.upload-progress').show(300);
}, 100);
}
});
});
// function bmi_debug_function(data = {}) {
//
// jQuery.bmi.ajax('debugging', data).then(function(res) {
//
// console.log(res);
//
// }).catch(function(error) {
//
// console.log(error);
//
// });
//
// }
jQuery(document).ready(function($) {
function setRadios(radios) {
for (let i = 0; i < radios.length; ++i) {
let c = radios[i].closest('.container-radio');
if (c && typeof c.classList != undefined) {
c.classList.remove('active');
if (radios[i].checked === true) {
c.classList.add('active');
}
}
}
}
$('input[type="radio"]').on('change', function() {
let name = this.getAttribute('name');
let container = this.closest('.container-radio');
let radios = document.querySelectorAll('[name="' + name + '"]');
setRadios(radios);
});
(function() {
if (pagenow !== 'toplevel_page_backup-migration') return;
let radios = document.getElementById('bmi').querySelectorAll('input[type="radio"]');
setRadios(radios);
})();
});jQuery(document).ready(function($) {
let current_last = null;
let logs_progress = null;
let timeouter = null;
let isLineAppendTimeoutRunning = false;
let latestLines = [];
let curmaxnum = 0;
let curdivs = 0;
let latestStep = '';
let lineAppender = null;
let endCodeCall = null;
let nextBatchStop = false;
async function repeatLogUpdate() {
clearTimeout(timeouter);
updateStagingLogs();
initializeStagingLogsUpdater(true);
}
async function initializeStagingLogsUpdater(selfcall = false) {
if (selfcall == false) {
$('#staging-live-log-wrapper .log-wrapper').find('pre')[0].innerText = '';
$('#staging-progress-modal .progress-active-bar')[0].style.width = '0%';
$('#staging-progress-modal .progress-percentage')[0].style.left = '0%';
$('#staging-progress-modal .progress-percentage')[0].innerText = '0%';
setProgressStaging(0);
repeatLogUpdate();
} else {
timeouter = setTimeout(repeatLogUpdate, 1500);
}
}
function endStagingLogerUpdater() {
clearInterval(logs_progress);
clearTimeout(lineAppender);
clearTimeout(endCodeCall);
clearTimeout(timeouter);
latestStep = '';
latestLines = [];
curmaxnum = 0;
curdivs = 0;
isLineAppendTimeoutRunning = false;
}
function insertPre(log, el) {
if (log === false) return;
let lines = log.split('\n');
if (lines.length >= 1) lines = lines.slice(0, -1);
latestLines = lines;
if (isLineAppendTimeoutRunning == false) {
if (curdivs < latestLines.length) {
showNextLine(el);
}
}
}
function httpGet(theUrl) {
return new Promise(function(resolve) {
let isHttps = window.location.protocol.includes('https');
let isUrlHttps = theUrl.includes('https');
if (isUrlHttps) theUrl = theUrl.slice(5);
else theUrl = theUrl.slice(4);
if (isHttps) theUrl = 'https' + theUrl;
else theUrl = 'http' + theUrl;
try {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
xmlhttp.onloadend = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
return resolve(xmlhttp.responseText);
} else return resolve(false);
}
xmlhttp.open('GET', theUrl);
xmlhttp.send();
} catch (e) {
return resolve(false);
}
});
}
function setProgressStaging(end = 0, duration = 1500) {
if (current_last == end) return;
else current_last = end;
clearInterval(logs_progress);
let start = parseFloat($('#staging-progress-modal .progress-percentage')[0].style.left) - 1;
let range = end - start;
let current = start;
let increment = 1;
let stepTime = Math.abs(Math.floor(duration / range));
logs_progress = setInterval(function() {
current += increment;
$('#staging-progress-modal .progress-active-bar')[0].style.width = (current).toFixed(2) + '%';
$('#staging-progress-modal .progress-percentage')[0].style.left = (current).toFixed(2) + '%';
$('#staging-progress-modal .progress-percentage')[0].innerText = (current).toFixed(0) + '%';
if (current >= 100) {
current_last = null;
clearInterval(logs_progress);
}
if (current > end) clearInterval(logs_progress);
}, stepTime);
}
async function updateStagingLogs() {
return new Promise(async function (resolve) {
let url = $('#BMI_BLOG_URL').text().trim();
if (url.slice(-url.length) !== '/') url = url + '/';
let res = await httpGet(url + '?backup-migration=PROGRESS_LOGS&progress-id=latest_staging_full.log&bmi-id=current&t=' + +new Date() + '&sk=' + $('#BMI_SECRET_KEY').text().trim());
if (!res) return resolve();
let progress = res.split('\n').slice(0, 1)[0];
res = res.split('\n').slice(1).join('\n');
if (progress === false || isNaN(parseFloat(progress))) return resolve();
let pre = $('#staging-live-log-wrapper').find('pre')[0];
if (!res.includes('<') && !res.includes('>')) {
if (res && res != false && typeof res !== 'undefined') {
insertPre(res, pre);
}
}
setProgressStaging(progress);
return resolve();
});
}
function showNextLine(el) {
let mostRecentStep = '';
let delayedDisplayTime = 40;
let line = latestLines[curdivs];
let div = document.createElement('DIV');
let color = '';
if (typeof line == 'undefined' || !line) return;
if (line.substr(0, 6) == '[INFO]') color = 'blue';
else if (line.substr(0, 9) == '[SUCCESS]') color = 'green';
else if (line.substr(0, 6) == '[WARN]') color = 'orange';
else if (line.substr(0, 7) == '[ERROR]') color = 'red';
else if (line.substr(0, 10) == '[END-CODE]') color = 'hide_so_much';
else if (line.substr(0, 9) == '[VERBOSE]') color = 'hide_verbose';
else if (line.substr(0, 6) == '[STEP]') {
div.classList.add('bold');
div.classList.add('step');
} else {
if (line && line.trim().length > 0 && line[0] != '[') {
curdivs--;
}
}
if (line.substr(0, 6) == '[STEP]') mostRecentStep = line.slice(29);
if (color.length > 0) div.classList.add(color);
div.style.display = 'none';
div.innerText = line;
el.appendChild(div);
let endCodeFound = goThroughEndCodes();
$(div).show(delayedDisplayTime, function () {
if (mostRecentStep != '' && mostRecentStep != latestStep) {
latestStep = mostRecentStep;
$('#staging_current_step').text(mostRecentStep);
}
});
el.scrollTop = el.scrollHeight;
curdivs++;
if (curdivs < latestLines.length && endCodeFound == false) {
isLineAppendTimeoutRunning = true;
lineAppender = setTimeout(function () {
showNextLine(el);
}, delayedDisplayTime);
} else {
isLineAppendTimeoutRunning = false;
}
}
function goThroughEndCodes() {
let mostRecentStep = '';
let endCodeFound = false;
for (let i = 0; i < latestLines.length; ++i) {
let line = latestLines[i];
if (line.substr(0, 6) == '[STEP]') mostRecentStep = line.slice(29);
else if (!endCodeFound && line && line.trim().includes('[END-CODE]')) {
endCodeFound = true;
clearTimeout(endCodeCall);
if (line.includes('001')) endCodeCall = setTimeout(stagingCreationSuccess, 2500);
else endCodeCall = setTimeout(stagingCreationFailed, 2500);
}
}
return endCodeFound;
}
function stagingCreationSuccess() {
endStagingLogerUpdater();
$.bmi.modal('freeze-loading-modal').close();
$.bmi.modal('staging-progress-modal').close();
$.bmi.modal('staging-error-modal').close();
$.bmi.modal('staging-success-modal').open();
}
function stagingCreationFailed(hideDefault = false) {
$('#after-logs-sent-modal').attr('data-error-source', 'staging');
endStagingLogerUpdater();
$.bmi.modal('freeze-loading-modal').close();
$.bmi.modal('staging-progress-modal').close();
$.bmi.modal('staging-success-modal').close();
$.bmi.modal('staging-error-modal').open();
if (!hideDefault) {
let errorNotice = $('#stg-notices-button').attr('c-errorProcess');
$.bmi.alert('error', errorNotice, 5000);
}
}
function abortCreationProcess(name) {
endStagingLogerUpdater();
$.bmi.modal('freeze-loading-modal').close();
$.bmi.modal('staging-progress-modal').close();
$.bmi.modal('staging-success-modal').close();
$.bmi.modal('staging-error-modal').close();
$.bmi.alert('info', $('#bmi-stg-aborted-al').text(), 3000);
$.bmi.reloadStaging();
}
function callLocalStagingCreationProcess(data, continuation = false) {
return new Promise(function(resolve) {
let endpoint = 'staging-start-local-creation';
if (!continuation) {
$.bmi.modal('freeze-loading-modal').open();
} else {
endpoint = 'staging-local-creation-process';
}
$.bmi.ajax(endpoint, data).then(function(res) {
if (!continuation) {
$.bmi.modal('freeze-loading-modal').close();
$.bmi.modal('staging-progress-modal').open();
}
if (res.status == 'success') {
$('#bmi-tastewp-staging-success-text').hide();
$('#bmi-local-staging-success-text').show();
$('.bmi-staging-success-img-normal').addClass('bmi-image-hide');
$('.bmi-staging-success-img-tastewp').addClass('bmi-image-hide');
$('.bmi-staging-success-img-normal').removeClass('bmi-image-hide');
$('#bmi-stg-subname-input').val('');
$.bmi.reloadStaging();
let loginURL = `${res.url}/wp-login.php?autologin=true&user=${res.userid}&secret=${res.password}`;
$('#bmi-visit-latest-staging').attr('href', loginURL);
$('#bmi-staging-latest-url').attr('href', loginURL);
endCodeCall = setTimeout(stagingCreationSuccess, 500);
} else if (res.status == 'continue') {
if (!continuation) initializeStagingLogsUpdater();
if (nextBatchStop) res['data']['delete'] = true;
callLocalStagingCreationProcess(res['data'], true);
} else if (res.status == 'fail') {
stagingCreationFailed(true);
return $.bmi.alert('warning', res.message, 5000);
} else if (res.status == 'deleted') {
abortCreationProcess(res['name']);
} else {
stagingCreationFailed();
}
}).catch(function(error) {
stagingCreationFailed();
console.error(error);
});
});
}
function callTasteWPStagingCreationProcess(data, initialize = false) {
return new Promise(function(resolve) {
let endpoint = 'staging-tastewp-creation-process';
if (initialize) {
$.bmi.modal('freeze-loading-modal').open();
data['initialize'] = true;
}
$.bmi.ajax(endpoint, data).then(function(res) {
if (res.status == 'success') {
$.bmi.reloadStaging();
let accessURL = `${res.url}`;
$('#bmi-visit-latest-staging').attr('href', accessURL);
$('#bmi-staging-latest-url').attr('href', accessURL);
$('#bmi-staging-latest-url').text(accessURL);
$('#bmi-tastewp-staging-success-text').show();
$('#bmi-local-staging-success-text').hide();
$('.bmi-staging-success-img-normal').addClass('bmi-image-hide');
$('.bmi-staging-success-img-tastewp').addClass('bmi-image-hide');
$('.bmi-staging-success-img-tastewp').removeClass('bmi-image-hide');
endCodeCall = setTimeout(stagingCreationSuccess, 500);
} else if (res.status == 'continue') {
if (initialize) {
initializeStagingLogsUpdater();
$.bmi.modal('freeze-loading-modal').close();
$.bmi.modal('staging-progress-modal').open();
}
if (nextBatchStop) res['data']['delete'] = true;
callTasteWPStagingCreationProcess(res['data']);
} else if (res.status == 'fail') {
stagingCreationFailed(true);
return $.bmi.alert('warning', res.message, 5000);
} else if (res.status == 'deleted') {
abortCreationProcess(res['name']);
} else {
stagingCreationFailed();
}
}).catch(function(error) {
stagingCreationFailed();
console.error(error);
});
});
}
$('#bmi').on('click', '.bmi-stg-sel-box', function (e) {
if ($(this)[0].classList.contains('bmi-active')) return;
if (e.target.tagName == 'A') return;
$('.bmi-stg-sel-box.bmi-active').removeClass('bmi-active');
$(this).addClass('bmi-active');
if ($(this).data('mode') == 'tastewp') {
$('.bmi-stg-creation-box-local').hide(300);
$('.bmi-stg-creation-box-tastewp').hide(300);
$('.bmi-stg-creation-box-tastewp-empty').hide(300);
if ($('.bmi-stg-drop-option:not(.bmi-stg-option-template)').length > 0) {
$('.bmi-stg-creation-box-tastewp').show(300);
} else {
$('.bmi-stg-creation-box-tastewp-empty').show(300);
}
} else {
$('.bmi-stg-creation-box-local').show(300);
$('.bmi-stg-creation-box-tastewp').hide(300);
$('.bmi-stg-creation-box-tastewp-empty').hide(300);
}
});
$('.bmi-stg-dropdown-area-selector').on('click', function () {
$('.bmi-stg-dropdown-area').toggleClass('bmi-active');
});
$('body').on('click', ':not(.bmi-stg-dropdown-area)', function (e) {
if (!e.target.closest('.bmi-stg-dropdown-area')) {
$('.bmi-stg-dropdown-area').removeClass('bmi-active');
}
});
$('.bmi-stg-dropdown-area-inner-scroll').on('click', '.bmi-stg-drop-option', function (e) {
let name = $(this).attr('backup-name');
let date = $(this).find('.bmi-stg-option-date i').text();
let size = $(this).find('.bmi-stg-option-size i').text();
$('.bmi-stg-drop-option.active').removeClass('active');
$(this).addClass('active');
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(name);
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text(date);
$('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text(size);
$('#bmi-stg-current-backup-selected').val(name);
$('.bmi-stg-dropdown-area').removeClass('bmi-active');
});
$('.i-staging-creator-tastewp').on('click', function () {
let backupName = $('.bmi-stg-dropdown-area-selector .bmi-stg-option-name').text();
$('#stg-prenotice-mode-local').hide();
$('#stg-prenotice-mode-tastewp').show();
$('#start-entire-staging').attr('mode', 'tastewp');
$('#bmi-staging-local-current-backup').text(backupName);
$.bmi.modal('staging-prenotice-modal').open();
});
$('.i-staging-creator-local').on('click', function () {
let name = $('#bmi-stg-subname-input').val().trim();
let empty = $(this).attr('c-empty');
let long = $(this).attr('c-long');
let errorNotice = $(this).attr('c-error');
let invalid = $(this).attr('c-invalid');
let validRegex = /^[a-zA-Z0-9-_]+$/;
if (name.length <= 0) {
return $.bmi.alert('warning', empty, 5000);
}
if (!validRegex.test(name)) {
return $.bmi.alert('warning', invalid, 5000);
}
if (name.length >= 24) {
return $.bmi.alert('warning', long, 5000);
}
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('staging-local-name', { name: name }).then(function(res) {
$.bmi.modal('freeze-loading-modal').close();
if (res.status == 'success') {
let url = $('#bmi-stg-homeurl').text() + $('#bmi-stg-subname-input').val();
$('#stg-prenotice-mode-local').show();
$('#stg-prenotice-mode-tastewp').hide();
$('#bmi-staging-local-current-url').text(url);
$('#bmi-staging-latest-url').text(url);
$('#bmi-staging-latest-url, #bmi-visit-latest-staging').attr('href', url);
$('#start-entire-staging').attr('mode', 'local'); // tastewp for TWP
$.bmi.modal('staging-prenotice-modal').open();
} else if (res.status == 'fail') {
return $.bmi.alert('warning', res.message, 5000);
} else {
return $.bmi.alert('error', errorNotice, 5000);
}
}).catch(function(error) {
$.bmi.modal('freeze-loading-modal').close();
return $.bmi.alert('error', errorNotice, 5000);
console.error(error);
});
});
$('#start-entire-staging').on('click', function (e) {
e.preventDefault();
$.bmi.modal('staging-prenotice-modal').close();
nextBatchStop = false;
if ($(this).attr('mode') == 'local') {
let name = $('#bmi-stg-subname-input').val().trim();
callLocalStagingCreationProcess({ name: name });
} else if ($(this).attr('mode') == 'tastewp') {
let currMills = +new Date() + '';
let name = Math.random().toString(36).substr(2, 16) + currMills.slice(-4);
let backupName = $('#bmi-stg-current-backup-selected').val();
callTasteWPStagingCreationProcess({ name: name, backupName: backupName }, true);
} else {
$.bmi.modal('freeze-loading-modal').close();
}
});
$('#rescan-for-staging').on('click', function(e) {
e.preventDefault();
$.bmi.reloadStaging();
});
$('#stg-tbody-table').on('click', '.bc-stg-edit-btn', function(e) {
e.preventDefault();
let name = $(this).closest('tr').attr('name');
let displayName = $(this).closest('tr').find('.stg-tr-name').text();
$('#bmi-stg-rename-input').val(displayName);
$('#stg-display-name-edit-confirm').attr('data-name', name);
$.bmi.modal('staging-rename-modal').open();
});
$('#stg-display-name-edit-confirm').on('click', function(e) {
e.preventDefault();
let empty = $('.i-staging-creator-local').attr('c-empty');
let long = $('.i-staging-creator-local').attr('c-long');
let invalid = $('.i-staging-creator-local').attr('c-invalid');
let errorNotice = $('.i-staging-creator-local').attr('c-error');
let newName = $('#bmi-stg-rename-input').val();
let oldName = $(this).attr('data-name');
let validRegex = /^[a-zA-Z0-9-_]+$/;
if (newName.length <= 0) return $.bmi.alert('warning', empty, 5000);
if (!validRegex.test(newName)) return $.bmi.alert('warning', invalid, 5000);
if (newName.length >= 24) return $.bmi.alert('warning', long, 5000);
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('staging-rename-display', { name: oldName, new: newName }).then(function(res) {
$.bmi.modal('freeze-loading-modal').close();
if (res.status == 'success') {
$.bmi.modal('staging-rename-modal').close();
$.bmi.reloadStaging();
} else if (res.status == 'fail') {
return $.bmi.alert('warning', res.message, 5000);
} else {
return $.bmi.alert('error', errorNotice, 5000);
}
}).catch(function(error) {
$.bmi.modal('freeze-loading-modal').close();
return $.bmi.alert('error', errorNotice, 5000);
console.error(error);
});
});
$('#bmi-visit-latest-staging').on('click', function (e) {
if ($('#bmi-staging-latest-url').text().includes('tastewp')) {
setTimeout(function () {
$.bmi.reloadStaging();
}, 8000);
}
});
$('#bmi-staging-stop').on('click', function () {
$.bmi.modal('freeze-loading-modal').open();
nextBatchStop = true;
});
$('#stg-tbody-table').on('click', '.stg-login-btn', function(e) {
if ($(this).closest('tr').attr('server') == 'tastewp') {
let url = $(this).closest('tr').find('.stg-tr-url-el').text();
if (url.includes('/stg/')) $(this).attr('href', url);
else {
setTimeout(function () {
$.bmi.reloadStaging();
}, 8000);
$(this).attr('href', 'https://tastewp.com/stg/access/' + $(this).closest('tr').attr('token'));
}
return true;
}
e.preventDefault();
let name = $(this).closest('tr').attr('name');
let errorNotice = $('.i-staging-creator-local').attr('c-error');
let self = this;
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('staging-prepare-login', { name: name }).then(function(res) {
$.bmi.modal('freeze-loading-modal').close();
if (res.status == 'success') {
$(self).attr('href', res.url);
window.open(res.url, '_blank');
} else if (res.status == 'fail') {
return $.bmi.alert('warning', res.message, 5000);
} else {
return $.bmi.alert('error', errorNotice, 5000);
}
}).catch(function(error) {
$.bmi.modal('freeze-loading-modal').close();
return $.bmi.alert('error', errorNotice, 5000);
console.error(error);
});
});
$('#stg-tbody-table').on('click', '.bc-stg-remove-btn', function(e) {
e.preventDefault();
let name = $(this).closest('tr').attr('name');
let url = $(this).closest('tr').find('.stg-tr-url-el').text();
$('#bmi-staging-removal-url').attr('href', url);
$('#bmi-staging-removal-url').text(url);
$('#stg-removal-confirm').attr('data-name', name);
$.bmi.modal('staging-delete-confirm-modal').open();
});
$('#stg-removal-confirm').on('click', function(e) {
e.preventDefault();
let name = $(this).attr('data-name');
let errorNotice = $('.i-staging-creator-local').attr('c-error');
$.bmi.modal('freeze-loading-modal').open();
$.bmi.ajax('staging-delete-permanently', { name: name }).then(function(res) {
$.bmi.modal('freeze-loading-modal').close();
if (res.status == 'success') {
$.bmi.modal('staging-delete-confirm-modal').close();
$.bmi.reloadStaging();
} else if (res.status == 'fail') {
return $.bmi.alert('warning', res.message, 5000);
} else {
return $.bmi.alert('error', errorNotice, 5000);
}
}).catch(function(error) {
$.bmi.modal('freeze-loading-modal').close();
return $.bmi.alert('error', errorNotice, 5000);
console.error(error);
});
});
let wrapper = document.querySelector('.bmi-stg-dropdown-area-inner-scroll');
if (wrapper) {
wrapper.addEventListener('wheel', function (e) {
let space = (wrapper.scrollHeight - wrapper.offsetHeight) - wrapper.scrollTop;
if (e.deltaY < 0) {
if (wrapper.scrollTop == 0) {
e.preventDefault();
e.stopPropagation();
}
} else if (space <= 0) {
e.preventDefault();
e.stopPropagation();
}
});
}
});
jQuery(document).ready(function($) {
let transition = false;
$('.bmi-tabs').on('click', '.bmi-tab', function(e) {
if (this.classList.contains('active')) return;
if (transition === true) return;
else transition = true;
$.bmi.collapsers.closeAll();
let id = this.getAttribute('data-point');
if (id == 'manage-restore-wrapper') {
$('#create-backup-wrapper').hide(300);
$('#staging-sites-wrapper').hide(300);
$('[data-point="create-backup-wrapper"]').removeClass('active');
$('[data-point="staging-sites-wrapper"]').removeClass('active');
} else if (id == 'staging-sites-wrapper') {
$('#create-backup-wrapper').hide(300);
$('#manage-restore-wrapper').hide(300);
$('[data-point="create-backup-wrapper"]').removeClass('active');
$('[data-point="manage-restore-wrapper"]').removeClass('active');
} else if (id == 'create-backup-wrapper') {
$('#manage-restore-wrapper').hide(300);
$('#staging-sites-wrapper').hide(300);
$('[data-point="manage-restore-wrapper"]').removeClass('active');
$('[data-point="staging-sites-wrapper"]').removeClass('active');
}
$('#' + id).show(300);
$(this).addClass('active');
setTimeout(function() {
transition = false;
}, 320);
});
});
jQuery(document).ready(function($) {
let dropArea = document.getElementById("drop-area");
if (!dropArea) return;
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
});
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false);
document.querySelector('body').addEventListener(eventName, highlight, false);
});
['dragleave', 'drop', 'mouseleave'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false);
});
$('body, #drop-area').on('mouseleave dragleave drop', function() {
unhighlight();
});
dropArea.addEventListener('drop', handleDrop, false);
function preventDefaults(e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('highlight')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
function handleFiles(files) {
files = [...files];
jQuery.fchunker_upload('file', files[0]);
}
});