PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.7
Backup Migration v2.1.7
2.1.7 2.1.6 2.1.5.2 trunk 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.6.1 1.4.7 1.4.8 1.4.9 1.4.9.1 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.5.1
backup-backup / admin / js / backup-migration.min.js
backup-backup / admin / js Last commit date
backup-migration.min.js 2 weeks ago
backup-migration.min.js
13334 lines
1 jQuery(document).ready(function($) {
2 (function(root, factory) {
3 if (typeof define === 'function' && define.amd) {
4 define(["jquery"], function(a0) {
5 return (factory(a0));
6 });
7 } else if (typeof exports === 'object') {
8 module.exports = factory(require("jquery"));
9 } else {
10 factory(jQuery);
11 }
12 }(this, function($) {
13
14 // This file will be UMDified by a build task.
15
16 var defaults = {
17 animation: 'fade',
18 animationDuration: 350,
19 content: null,
20 contentAsHTML: false,
21 contentCloning: false,
22 debug: true,
23 delay: 300,
24 delayTouch: [300, 500],
25 functionInit: null,
26 functionBefore: null,
27 functionReady: null,
28 functionAfter: null,
29 functionFormat: null,
30 IEmin: 6,
31 interactive: false,
32 multiple: false,
33 // will default to document.body, or must be an element positioned at (0, 0)
34 // in the document, typically like the very top views of an app.
35 parent: null,
36 plugins: ['sideTip'],
37 repositionOnScroll: false,
38 restoration: 'none',
39 selfDestruction: true,
40 theme: [],
41 timer: 0,
42 trackerInterval: 500,
43 trackOrigin: false,
44 trackTooltip: false,
45 trigger: 'hover',
46 triggerClose: {
47 click: false,
48 mouseleave: false,
49 originClick: false,
50 scroll: false,
51 tap: false,
52 touchleave: false
53 },
54 triggerOpen: {
55 click: false,
56 mouseenter: false,
57 tap: false,
58 touchstart: false
59 },
60 updateAnimation: 'rotate',
61 zIndex: 9999999
62 },
63 // we'll avoid using the 'window' global as a good practice but npm's
64 // jquery@<2.1.0 package actually requires a 'window' global, so not sure
65 // it's useful at all
66 win = (typeof window != 'undefined') ? window : null,
67 // env will be proxied by the core for plugins to have access its properties
68 env = {
69 // detect if this device can trigger touch events. Better have a false
70 // positive (unused listeners, that's ok) than a false negative.
71 // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
72 // http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
73 hasTouchCapability: !!(
74 win &&
75 ('ontouchstart' in win ||
76 (win.DocumentTouch && win.document instanceof win.DocumentTouch) ||
77 win.navigator.maxTouchPoints
78 )
79 ),
80 hasTransitions: transitionSupport(),
81 IE: false,
82 // don't set manually, it will be updated by a build task after the manifest
83 semVer: '4.2.8',
84 window: win
85 },
86 core = function() {
87
88 // core variables
89
90 // the core emitters
91 this.__$emitterPrivate = $({});
92 this.__$emitterPublic = $({});
93 this.__instancesLatestArr = [];
94 // collects plugin constructors
95 this.__plugins = {};
96 // proxy env variables for plugins who might use them
97 this._env = env;
98 };
99
100 // core methods
101 core.prototype = {
102
103 /**
104 * A function to proxy the public methods of an object onto another
105 *
106 * @param {object} constructor The constructor to bridge
107 * @param {object} obj The object that will get new methods (an instance or the core)
108 * @param {string} pluginName A plugin name for the console log message
109 * @return {core}
110 * @private
111 */
112 __bridge: function(constructor, obj, pluginName) {
113
114 // if it's not already bridged
115 if (!obj[pluginName]) {
116
117 var fn = function() {};
118 fn.prototype = constructor;
119
120 var pluginInstance = new fn();
121
122 // the _init method has to exist in instance constructors but might be missing
123 // in core constructors
124 if (pluginInstance.__init) {
125 pluginInstance.__init(obj);
126 }
127
128 $.each(constructor, function(methodName, fn) {
129
130 // don't proxy "private" methods, only "protected" and public ones
131 if (methodName.indexOf('__') != 0) {
132
133 // if the method does not exist yet
134 if (!obj[methodName]) {
135
136 obj[methodName] = function() {
137 return pluginInstance[methodName].apply(pluginInstance, Array.prototype.slice.apply(arguments));
138 };
139
140 // remember to which plugin this method corresponds (several plugins may
141 // have methods of the same name, we need to be sure)
142 obj[methodName].bridged = pluginInstance;
143 } else if (defaults.debug) {}
144 }
145 });
146
147 obj[pluginName] = pluginInstance;
148 }
149
150 return this;
151 },
152
153 /**
154 * For mockup in Node env if need be, for testing purposes
155 *
156 * @return {core}
157 * @private
158 */
159 __setWindow: function(window) {
160 env.window = window;
161 return this;
162 },
163
164 /**
165 * Returns a ruler, a tool to help measure the size of a tooltip under
166 * various settings. Meant for plugins
167 *
168 * @see Ruler
169 * @return {object} A Ruler instance
170 * @protected
171 */
172 _getRuler: function($tooltip) {
173 return new Ruler($tooltip);
174 },
175
176 /**
177 * For internal use by plugins, if needed
178 *
179 * @return {core}
180 * @protected
181 */
182 _off: function() {
183 this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
184 return this;
185 },
186
187 /**
188 * For internal use by plugins, if needed
189 *
190 * @return {core}
191 * @protected
192 */
193 _on: function() {
194 this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
195 return this;
196 },
197
198 /**
199 * For internal use by plugins, if needed
200 *
201 * @return {core}
202 * @protected
203 */
204 _one: function() {
205 this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
206 return this;
207 },
208
209 /**
210 * Returns (getter) or adds (setter) a plugin
211 *
212 * @param {string|object} plugin Provide a string (in the full form
213 * "namespace.name") to use as as getter, an object to use as a setter
214 * @return {object|core}
215 * @protected
216 */
217 _plugin: function(plugin) {
218
219 var self = this;
220
221 // getter
222 if (typeof plugin == 'string') {
223
224 var pluginName = plugin,
225 p = null;
226
227 // if the namespace is provided, it's easy to search
228 if (pluginName.indexOf('.') > 0) {
229 p = self.__plugins[pluginName];
230 }
231 // otherwise, return the first name that matches
232 else {
233 $.each(self.__plugins, function(i, plugin) {
234
235 if (plugin.name.substring(plugin.name.length - pluginName.length - 1) == '.' + pluginName) {
236 p = plugin;
237 return false;
238 }
239 });
240 }
241
242 return p;
243 }
244 // setter
245 else {
246
247 // force namespaces
248 if (plugin.name.indexOf('.') < 0) {
249 throw new Error('Plugins must be namespaced');
250 }
251
252 self.__plugins[plugin.name] = plugin;
253
254 // if the plugin has core features
255 if (plugin.core) {
256
257 // bridge non-private methods onto the core to allow new core methods
258 self.__bridge(plugin.core, self, plugin.name);
259 }
260
261 return this;
262 }
263 },
264
265 /**
266 * Trigger events on the core emitters
267 *
268 * @returns {core}
269 * @protected
270 */
271 _trigger: function() {
272
273 var args = Array.prototype.slice.apply(arguments);
274
275 if (typeof args[0] == 'string') {
276 args[0] = {
277 type: args[0]
278 };
279 }
280
281 // note: the order of emitters matters
282 this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
283 this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
284
285 return this;
286 },
287
288 /**
289 * Returns instances of all tooltips in the page or an a given element
290 *
291 * @param {string|HTML object collection} selector optional Use this
292 * parameter to restrict the set of objects that will be inspected
293 * for the retrieval of instances. By default, all instances in the
294 * page are returned.
295 * @return {array} An array of instance objects
296 * @public
297 */
298 instances: function(selector) {
299
300 var instances = [],
301 sel = selector || '.tooltipstered';
302
303 $(sel).each(function() {
304
305 var $this = $(this),
306 ns = $this.data('tooltipster-ns');
307
308 if (ns) {
309
310 $.each(ns, function(i, namespace) {
311 instances.push($this.data(namespace));
312 });
313 }
314 });
315
316 return instances;
317 },
318
319 /**
320 * Returns the Tooltipster objects generated by the last initializing call
321 *
322 * @return {array} An array of instance objects
323 * @public
324 */
325 instancesLatest: function() {
326 return this.__instancesLatestArr;
327 },
328
329 /**
330 * For public use only, not to be used by plugins (use ::_off() instead)
331 *
332 * @return {core}
333 * @public
334 */
335 off: function() {
336 this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
337 return this;
338 },
339
340 /**
341 * For public use only, not to be used by plugins (use ::_on() instead)
342 *
343 * @return {core}
344 * @public
345 */
346 on: function() {
347 this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
348 return this;
349 },
350
351 /**
352 * For public use only, not to be used by plugins (use ::_one() instead)
353 *
354 * @return {core}
355 * @public
356 */
357 one: function() {
358 this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
359 return this;
360 },
361
362 /**
363 * Returns all HTML elements which have one or more tooltips
364 *
365 * @param {string} selector optional Use this to restrict the results
366 * to the descendants of an element
367 * @return {array} An array of HTML elements
368 * @public
369 */
370 origins: function(selector) {
371
372 var sel = selector ?
373 selector + ' ' :
374 '';
375
376 return $(sel + '.tooltipstered').toArray();
377 },
378
379 /**
380 * Change default options for all future instances
381 *
382 * @param {object} d The options that should be made defaults
383 * @return {core}
384 * @public
385 */
386 setDefaults: function(d) {
387 $.extend(defaults, d);
388 return this;
389 },
390
391 /**
392 * For users to trigger their handlers on the public emitter
393 *
394 * @returns {core}
395 * @public
396 */
397 triggerHandler: function() {
398 this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
399 return this;
400 }
401 };
402
403 // $.tooltipster will be used to call core methods
404 $.tooltipster = new core();
405
406 // the Tooltipster instance class (mind the capital T)
407 $.Tooltipster = function(element, options) {
408
409 // list of instance variables
410
411 // stack of custom callbacks provided as parameters to API methods
412 this.__callbacks = {
413 close: [],
414 open: []
415 };
416 // the schedule time of DOM removal
417 this.__closingTime;
418 // this will be the user content shown in the tooltip. A capital "C" is used
419 // because there is also a method called content()
420 this.__Content;
421 // for the size tracker
422 this.__contentBcr;
423 // to disable the tooltip after destruction
424 this.__destroyed = false;
425 // we can't emit directly on the instance because if a method with the same
426 // name as the event exists, it will be called by jQuery. Se we use a plain
427 // object as emitter. This emitter is for internal use by plugins,
428 // if needed.
429 this.__$emitterPrivate = $({});
430 // this emitter is for the user to listen to events without risking to mess
431 // with our internal listeners
432 this.__$emitterPublic = $({});
433 this.__enabled = true;
434 // the reference to the gc interval
435 this.__garbageCollector;
436 // various position and size data recomputed before each repositioning
437 this.__Geometry;
438 // the tooltip position, saved after each repositioning by a plugin
439 this.__lastPosition;
440 // a unique namespace per instance
441 this.__namespace = 'tooltipster-' + Math.round(Math.random() * 1000000);
442 this.__options;
443 // will be used to support origins in scrollable areas
444 this.__$originParents;
445 this.__pointerIsOverOrigin = false;
446 // to remove themes if needed
447 this.__previousThemes = [];
448 // the state can be either: appearing, stable, disappearing, closed
449 this.__state = 'closed';
450 // timeout references
451 this.__timeouts = {
452 close: [],
453 open: null
454 };
455 // store touch events to be able to detect emulated mouse events
456 this.__touchEvents = [];
457 // the reference to the tracker interval
458 this.__tracker = null;
459 // the element to which this tooltip is associated
460 this._$origin;
461 // this will be the tooltip element (jQuery wrapped HTML element).
462 // It's the job of a plugin to create it and append it to the DOM
463 this._$tooltip;
464
465 // launch
466 this.__init(element, options);
467 };
468
469 $.Tooltipster.prototype = {
470
471 /**
472 * @param origin
473 * @param options
474 * @private
475 */
476 __init: function(origin, options) {
477
478 var self = this;
479
480 self._$origin = $(origin);
481 self.__options = $.extend(true, {}, defaults, options);
482
483 // some options may need to be reformatted
484 self.__optionsFormat();
485
486 // don't run on old IE if asked no to
487 if (!env.IE ||
488 env.IE >= self.__options.IEmin
489 ) {
490
491 // note: the content is null (empty) by default and can stay that
492 // way if the plugin remains initialized but not fed any content. The
493 // tooltip will just not appear.
494
495 // let's save the initial value of the title attribute for later
496 // restoration if need be.
497 var initialTitle = null;
498
499 // it will already have been saved in case of multiple tooltips
500 if (self._$origin.data('tooltipster-initialTitle') === undefined) {
501
502 initialTitle = self._$origin.attr('title');
503
504 // we do not want initialTitle to be "undefined" because
505 // of how jQuery's .data() method works
506 if (initialTitle === undefined) initialTitle = null;
507
508 self._$origin.data('tooltipster-initialTitle', initialTitle);
509 }
510
511 // If content is provided in the options, it has precedence over the
512 // title attribute.
513 // Note: an empty string is considered content, only 'null' represents
514 // the absence of content.
515 // Also, an existing title="" attribute will result in an empty string
516 // content
517 if (self.__options.content !== null) {
518 self.__contentSet(self.__options.content);
519 } else {
520
521 var selector = self._$origin.attr('data-tooltip-content'),
522 $el;
523
524 if (selector) {
525 $el = $(selector);
526 }
527
528 if ($el && $el[0]) {
529 self.__contentSet($el.first());
530 } else {
531 self.__contentSet(initialTitle);
532 }
533 }
534
535 self._$origin
536 // strip the title off of the element to prevent the default tooltips
537 // from popping up
538 .removeAttr('title')
539 // to be able to find all instances on the page later (upon window
540 // events in particular)
541 .addClass('tooltipstered');
542
543 // set listeners on the origin
544 self.__prepareOrigin();
545
546 // set the garbage collector
547 self.__prepareGC();
548
549 // init plugins
550 $.each(self.__options.plugins, function(i, pluginName) {
551 self._plug(pluginName);
552 });
553
554 // to detect swiping
555 if (env.hasTouchCapability) {
556 $(env.window.document.body).on('touchmove.' + self.__namespace + '-triggerOpen', function(event) {
557 self._touchRecordEvent(event);
558 });
559 }
560
561 self
562 // prepare the tooltip when it gets created. This event must
563 // be fired by a plugin
564 ._on('created', function() {
565 self.__prepareTooltip();
566 })
567 // save position information when it's sent by a plugin
568 ._on('repositioned', function(e) {
569 self.__lastPosition = e.position;
570 });
571 } else {
572 self.__options.disabled = true;
573 }
574 },
575
576 /**
577 * Insert the content into the appropriate HTML element of the tooltip
578 *
579 * @returns {self}
580 * @private
581 */
582 __contentInsert: function() {
583
584 var self = this,
585 $el = self._$tooltip.find('.tooltipster-content'),
586 formattedContent = self.__Content,
587 format = function(content) {
588 formattedContent = content;
589 };
590
591 self._trigger({
592 type: 'format',
593 content: self.__Content,
594 format: format
595 });
596
597 if (self.__options.functionFormat) {
598
599 formattedContent = self.__options.functionFormat.call(
600 self,
601 self, {
602 origin: self._$origin[0]
603 },
604 self.__Content
605 );
606 }
607
608 if (typeof formattedContent === 'string' && !self.__options.contentAsHTML) {
609 $el.text(formattedContent);
610 } else {
611 $el
612 .empty()
613 .append(formattedContent);
614 }
615
616 return self;
617 },
618
619 /**
620 * Save the content, cloning it beforehand if need be
621 *
622 * @param content
623 * @returns {self}
624 * @private
625 */
626 __contentSet: function(content) {
627
628 // clone if asked. Cloning the object makes sure that each instance has its
629 // own version of the content (in case a same object were provided for several
630 // instances)
631 // reminder: typeof null === object
632 if (content instanceof $ && this.__options.contentCloning) {
633 content = content.clone(true);
634 }
635
636 this.__Content = content;
637
638 this._trigger({
639 type: 'updated',
640 content: content
641 });
642
643 return this;
644 },
645
646 /**
647 * Error message about a method call made after destruction
648 *
649 * @private
650 */
651 __destroyError: function() {
652 throw new Error('This tooltip has been destroyed and cannot execute your method call.');
653 },
654
655 /**
656 * Gather all information about dimensions and available space,
657 * called before every repositioning
658 *
659 * @private
660 * @returns {object}
661 */
662 __geometry: function() {
663
664 var self = this,
665 $target = self._$origin,
666 originIsArea = self._$origin.is('area');
667
668 // if this._$origin is a map area, the target we'll need
669 // the dimensions of is actually the image using the map,
670 // not the area itself
671 if (originIsArea) {
672
673 var mapName = self._$origin.parent().attr('name');
674
675 $target = $('img[usemap="#' + mapName + '"]');
676 }
677
678 var bcr = $target[0].getBoundingClientRect(),
679 $document = $(env.window.document),
680 $window = $(env.window),
681 $parent = $target,
682 // some useful properties of important elements
683 geo = {
684 // available space for the tooltip, see down below
685 available: {
686 document: null,
687 window: null
688 },
689 document: {
690 size: {
691 height: $document.height(),
692 width: $document.width()
693 }
694 },
695 window: {
696 scroll: {
697 // the second ones are for IE compatibility
698 left: env.window.scrollX || env.window.document.documentElement.scrollLeft,
699 top: env.window.scrollY || env.window.document.documentElement.scrollTop
700 },
701 size: {
702 height: $window.height(),
703 width: $window.width()
704 }
705 },
706 origin: {
707 // the origin has a fixed lineage if itself or one of its
708 // ancestors has a fixed position
709 fixedLineage: false,
710 // relative to the document
711 offset: {},
712 size: {
713 height: bcr.bottom - bcr.top,
714 width: bcr.right - bcr.left
715 },
716 usemapImage: originIsArea ? $target[0] : null,
717 // relative to the window
718 windowOffset: {
719 bottom: bcr.bottom,
720 left: bcr.left,
721 right: bcr.right,
722 top: bcr.top
723 }
724 }
725 },
726 geoFixed = false;
727
728 // if the element is a map area, some properties may need
729 // to be recalculated
730 if (originIsArea) {
731
732 var shape = self._$origin.attr('shape'),
733 coords = self._$origin.attr('coords');
734
735 if (coords) {
736
737 coords = coords.split(',');
738
739 $.map(coords, function(val, i) {
740 coords[i] = parseInt(val);
741 });
742 }
743
744 // if the image itself is the area, nothing more to do
745 if (shape != 'default') {
746
747 switch (shape) {
748
749 case 'circle':
750
751 var circleCenterLeft = coords[0],
752 circleCenterTop = coords[1],
753 circleRadius = coords[2],
754 areaTopOffset = circleCenterTop - circleRadius,
755 areaLeftOffset = circleCenterLeft - circleRadius;
756
757 geo.origin.size.height = circleRadius * 2;
758 geo.origin.size.width = geo.origin.size.height;
759
760 geo.origin.windowOffset.left += areaLeftOffset;
761 geo.origin.windowOffset.top += areaTopOffset;
762
763 break;
764
765 case 'rect':
766
767 var areaLeft = coords[0],
768 areaTop = coords[1],
769 areaRight = coords[2],
770 areaBottom = coords[3];
771
772 geo.origin.size.height = areaBottom - areaTop;
773 geo.origin.size.width = areaRight - areaLeft;
774
775 geo.origin.windowOffset.left += areaLeft;
776 geo.origin.windowOffset.top += areaTop;
777
778 break;
779
780 case 'poly':
781
782 var areaSmallestX = 0,
783 areaSmallestY = 0,
784 areaGreatestX = 0,
785 areaGreatestY = 0,
786 arrayAlternate = 'even';
787
788 for (var i = 0; i < coords.length; i++) {
789
790 var areaNumber = coords[i];
791
792 if (arrayAlternate == 'even') {
793
794 if (areaNumber > areaGreatestX) {
795
796 areaGreatestX = areaNumber;
797
798 if (i === 0) {
799 areaSmallestX = areaGreatestX;
800 }
801 }
802
803 if (areaNumber < areaSmallestX) {
804 areaSmallestX = areaNumber;
805 }
806
807 arrayAlternate = 'odd';
808 } else {
809 if (areaNumber > areaGreatestY) {
810
811 areaGreatestY = areaNumber;
812
813 if (i == 1) {
814 areaSmallestY = areaGreatestY;
815 }
816 }
817
818 if (areaNumber < areaSmallestY) {
819 areaSmallestY = areaNumber;
820 }
821
822 arrayAlternate = 'even';
823 }
824 }
825
826 geo.origin.size.height = areaGreatestY - areaSmallestY;
827 geo.origin.size.width = areaGreatestX - areaSmallestX;
828
829 geo.origin.windowOffset.left += areaSmallestX;
830 geo.origin.windowOffset.top += areaSmallestY;
831
832 break;
833 }
834 }
835 }
836
837 // user callback through an event
838 var edit = function(r) {
839 geo.origin.size.height = r.height,
840 geo.origin.windowOffset.left = r.left,
841 geo.origin.windowOffset.top = r.top,
842 geo.origin.size.width = r.width
843 };
844
845 self._trigger({
846 type: 'geometry',
847 edit: edit,
848 geometry: {
849 height: geo.origin.size.height,
850 left: geo.origin.windowOffset.left,
851 top: geo.origin.windowOffset.top,
852 width: geo.origin.size.width
853 }
854 });
855
856 // calculate the remaining properties with what we got
857
858 geo.origin.windowOffset.right = geo.origin.windowOffset.left + geo.origin.size.width;
859 geo.origin.windowOffset.bottom = geo.origin.windowOffset.top + geo.origin.size.height;
860
861 geo.origin.offset.left = geo.origin.windowOffset.left + geo.window.scroll.left;
862 geo.origin.offset.top = geo.origin.windowOffset.top + geo.window.scroll.top;
863 geo.origin.offset.bottom = geo.origin.offset.top + geo.origin.size.height;
864 geo.origin.offset.right = geo.origin.offset.left + geo.origin.size.width;
865
866 // the space that is available to display the tooltip relatively to the document
867 geo.available.document = {
868 bottom: {
869 height: geo.document.size.height - geo.origin.offset.bottom,
870 width: geo.document.size.width
871 },
872 left: {
873 height: geo.document.size.height,
874 width: geo.origin.offset.left
875 },
876 right: {
877 height: geo.document.size.height,
878 width: geo.document.size.width - geo.origin.offset.right
879 },
880 top: {
881 height: geo.origin.offset.top,
882 width: geo.document.size.width
883 }
884 };
885
886 // the space that is available to display the tooltip relatively to the viewport
887 // (the resulting values may be negative if the origin overflows the viewport)
888 geo.available.window = {
889 bottom: {
890 // the inner max is here to make sure the available height is no bigger
891 // than the viewport height (when the origin is off screen at the top).
892 // The outer max just makes sure that the height is not negative (when
893 // the origin overflows at the bottom).
894 height: Math.max(geo.window.size.height - Math.max(geo.origin.windowOffset.bottom, 0), 0),
895 width: geo.window.size.width
896 },
897 left: {
898 height: geo.window.size.height,
899 width: Math.max(geo.origin.windowOffset.left, 0)
900 },
901 right: {
902 height: geo.window.size.height,
903 width: Math.max(geo.window.size.width - Math.max(geo.origin.windowOffset.right, 0), 0)
904 },
905 top: {
906 height: Math.max(geo.origin.windowOffset.top, 0),
907 width: geo.window.size.width
908 }
909 };
910
911 while ($parent[0].tagName.toLowerCase() != 'html') {
912
913 if ($parent.css('position') == 'fixed') {
914 geo.origin.fixedLineage = true;
915 break;
916 }
917
918 $parent = $parent.parent();
919 }
920
921 return geo;
922 },
923
924 /**
925 * Some options may need to be formated before being used
926 *
927 * @returns {self}
928 * @private
929 */
930 __optionsFormat: function() {
931
932 if (typeof this.__options.animationDuration == 'number') {
933 this.__options.animationDuration = [this.__options.animationDuration, this.__options.animationDuration];
934 }
935
936 if (typeof this.__options.delay == 'number') {
937 this.__options.delay = [this.__options.delay, this.__options.delay];
938 }
939
940 if (typeof this.__options.delayTouch == 'number') {
941 this.__options.delayTouch = [this.__options.delayTouch, this.__options.delayTouch];
942 }
943
944 if (typeof this.__options.theme == 'string') {
945 this.__options.theme = [this.__options.theme];
946 }
947
948 // determine the future parent
949 if (this.__options.parent === null) {
950 this.__options.parent = $(env.window.document.body);
951 } else if (typeof this.__options.parent == 'string') {
952 this.__options.parent = $(this.__options.parent);
953 }
954
955 if (this.__options.trigger == 'hover') {
956
957 this.__options.triggerOpen = {
958 mouseenter: true,
959 touchstart: true
960 };
961
962 this.__options.triggerClose = {
963 mouseleave: true,
964 originClick: true,
965 touchleave: true
966 };
967 } else if (this.__options.trigger == 'click') {
968
969 this.__options.triggerOpen = {
970 click: true,
971 tap: true
972 };
973
974 this.__options.triggerClose = {
975 click: true,
976 tap: true
977 };
978 }
979
980 // for the plugins
981 this._trigger('options');
982
983 return this;
984 },
985
986 /**
987 * Schedules or cancels the garbage collector task
988 *
989 * @returns {self}
990 * @private
991 */
992 __prepareGC: function() {
993
994 var self = this;
995
996 // in case the selfDestruction option has been changed by a method call
997 if (self.__options.selfDestruction) {
998
999 // the GC task
1000 self.__garbageCollector = setInterval(function() {
1001
1002 var now = new Date().getTime();
1003
1004 // forget the old events
1005 self.__touchEvents = $.grep(self.__touchEvents, function(event, i) {
1006 // 1 minute
1007 return now - event.time > 60000;
1008 });
1009
1010 // auto-destruct if the origin is gone
1011 if (!bodyContains(self._$origin)) {
1012
1013 self.close(function() {
1014 self.destroy();
1015 });
1016 }
1017 }, 20000);
1018 } else {
1019 clearInterval(self.__garbageCollector);
1020 }
1021
1022 return self;
1023 },
1024
1025 /**
1026 * Sets listeners on the origin if the open triggers require them.
1027 * Unlike the listeners set at opening time, these ones
1028 * remain even when the tooltip is closed. It has been made a
1029 * separate method so it can be called when the triggers are
1030 * changed in the options. Closing is handled in _open()
1031 * because of the bindings that may be needed on the tooltip
1032 * itself
1033 *
1034 * @returns {self}
1035 * @private
1036 */
1037 __prepareOrigin: function() {
1038
1039 var self = this;
1040
1041 // in case we're resetting the triggers
1042 self._$origin.off('.' + self.__namespace + '-triggerOpen');
1043
1044 // if the device is touch capable, even if only mouse triggers
1045 // are asked, we need to listen to touch events to know if the mouse
1046 // events are actually emulated (so we can ignore them)
1047 if (env.hasTouchCapability) {
1048
1049 self._$origin.on(
1050 'touchstart.' + self.__namespace + '-triggerOpen ' +
1051 'touchend.' + self.__namespace + '-triggerOpen ' +
1052 'touchcancel.' + self.__namespace + '-triggerOpen',
1053 function(event) {
1054 self._touchRecordEvent(event);
1055 }
1056 );
1057 }
1058
1059 // mouse click and touch tap work the same way
1060 if (self.__options.triggerOpen.click ||
1061 (self.__options.triggerOpen.tap && env.hasTouchCapability)
1062 ) {
1063
1064 var eventNames = '';
1065 if (self.__options.triggerOpen.click) {
1066 eventNames += 'click.' + self.__namespace + '-triggerOpen ';
1067 }
1068 if (self.__options.triggerOpen.tap && env.hasTouchCapability) {
1069 eventNames += 'touchend.' + self.__namespace + '-triggerOpen';
1070 }
1071
1072 self._$origin.on(eventNames, function(event) {
1073 if (self._touchIsMeaningfulEvent(event)) {
1074 self._open(event);
1075 }
1076 });
1077 }
1078
1079 // mouseenter and touch start work the same way
1080 if (self.__options.triggerOpen.mouseenter ||
1081 (self.__options.triggerOpen.touchstart && env.hasTouchCapability)
1082 ) {
1083
1084 var eventNames = '';
1085 if (self.__options.triggerOpen.mouseenter) {
1086 eventNames += 'mouseenter.' + self.__namespace + '-triggerOpen ';
1087 }
1088 if (self.__options.triggerOpen.touchstart && env.hasTouchCapability) {
1089 eventNames += 'touchstart.' + self.__namespace + '-triggerOpen';
1090 }
1091
1092 self._$origin.on(eventNames, function(event) {
1093 if (self._touchIsTouchEvent(event) ||
1094 !self._touchIsEmulatedEvent(event)
1095 ) {
1096 self.__pointerIsOverOrigin = true;
1097 self._openShortly(event);
1098 }
1099 });
1100 }
1101
1102 // info for the mouseleave/touchleave close triggers when they use a delay
1103 if (self.__options.triggerClose.mouseleave ||
1104 (self.__options.triggerClose.touchleave && env.hasTouchCapability)
1105 ) {
1106
1107 var eventNames = '';
1108 if (self.__options.triggerClose.mouseleave) {
1109 eventNames += 'mouseleave.' + self.__namespace + '-triggerOpen ';
1110 }
1111 if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
1112 eventNames += 'touchend.' + self.__namespace + '-triggerOpen touchcancel.' + self.__namespace + '-triggerOpen';
1113 }
1114
1115 self._$origin.on(eventNames, function(event) {
1116
1117 if (self._touchIsMeaningfulEvent(event)) {
1118 self.__pointerIsOverOrigin = false;
1119 }
1120 });
1121 }
1122
1123 return self;
1124 },
1125
1126 /**
1127 * Do the things that need to be done only once after the tooltip
1128 * HTML element it has been created. It has been made a separate
1129 * method so it can be called when options are changed. Remember
1130 * that the tooltip may actually exist in the DOM before it is
1131 * opened, and present after it has been closed: it's the display
1132 * plugin that takes care of handling it.
1133 *
1134 * @returns {self}
1135 * @private
1136 */
1137 __prepareTooltip: function() {
1138
1139 var self = this,
1140 p = self.__options.interactive ? 'auto' : '';
1141
1142 // this will be useful to know quickly if the tooltip is in
1143 // the DOM or not
1144 self._$tooltip
1145 .attr('id', self.__namespace)
1146 .css({
1147 // pointer events
1148 'pointer-events': p,
1149 zIndex: self.__options.zIndex
1150 });
1151
1152 // themes
1153 // remove the old ones and add the new ones
1154 $.each(self.__previousThemes, function(i, theme) {
1155 self._$tooltip.removeClass(theme);
1156 });
1157 $.each(self.__options.theme, function(i, theme) {
1158 self._$tooltip.addClass(theme);
1159 });
1160
1161 self.__previousThemes = $.merge([], self.__options.theme);
1162
1163 return self;
1164 },
1165
1166 /**
1167 * Handles the scroll on any of the parents of the origin (when the
1168 * tooltip is open)
1169 *
1170 * @param {object} event
1171 * @returns {self}
1172 * @private
1173 */
1174 __scrollHandler: function(event) {
1175
1176 var self = this;
1177
1178 if (self.__options.triggerClose.scroll) {
1179 self._close(event);
1180 } else {
1181
1182 // if the origin or tooltip have been removed: do nothing, the tracker will
1183 // take care of it later
1184 if (bodyContains(self._$origin) && bodyContains(self._$tooltip)) {
1185
1186 var geo = null;
1187
1188 // if the scroll happened on the window
1189 if (event.target === env.window.document) {
1190
1191 // if the origin has a fixed lineage, window scroll will have no
1192 // effect on its position nor on the position of the tooltip
1193 if (!self.__Geometry.origin.fixedLineage) {
1194
1195 // we don't need to do anything unless repositionOnScroll is true
1196 // because the tooltip will already have moved with the window
1197 // (and of course with the origin)
1198 if (self.__options.repositionOnScroll) {
1199 self.reposition(event);
1200 }
1201 }
1202 }
1203 // if the scroll happened on another parent of the tooltip, it means
1204 // that it's in a scrollable area and now needs to have its position
1205 // adjusted or recomputed, depending ont the repositionOnScroll
1206 // option. Also, if the origin is partly hidden due to a parent that
1207 // hides its overflow, we'll just hide (not close) the tooltip.
1208 else {
1209
1210 geo = self.__geometry();
1211
1212 var overflows = false;
1213
1214 // a fixed position origin is not affected by the overflow hiding
1215 // of a parent
1216 if (self._$origin.css('position') != 'fixed') {
1217
1218 self.__$originParents.each(function(i, el) {
1219
1220 var $el = $(el),
1221 overflowX = $el.css('overflow-x'),
1222 overflowY = $el.css('overflow-y');
1223
1224 if (overflowX != 'visible' || overflowY != 'visible') {
1225
1226 var bcr = el.getBoundingClientRect();
1227
1228 if (overflowX != 'visible') {
1229
1230 if (geo.origin.windowOffset.left < bcr.left ||
1231 geo.origin.windowOffset.right > bcr.right
1232 ) {
1233 overflows = true;
1234 return false;
1235 }
1236 }
1237
1238 if (overflowY != 'visible') {
1239
1240 if (geo.origin.windowOffset.top < bcr.top ||
1241 geo.origin.windowOffset.bottom > bcr.bottom
1242 ) {
1243 overflows = true;
1244 return false;
1245 }
1246 }
1247 }
1248
1249 // no need to go further if fixed, for the same reason as above
1250 if ($el.css('position') == 'fixed') {
1251 return false;
1252 }
1253 });
1254 }
1255
1256 if (overflows) {
1257 self._$tooltip.css('visibility', 'hidden');
1258 } else {
1259
1260 self._$tooltip.css('visibility', 'visible');
1261
1262 // reposition
1263 if (self.__options.repositionOnScroll) {
1264 self.reposition(event);
1265 }
1266 // or just adjust offset
1267 else {
1268
1269 // we have to use offset and not windowOffset because this way,
1270 // only the scroll distance of the scrollable areas are taken into
1271 // account (the scrolltop value of the main window must be
1272 // ignored since the tooltip already moves with it)
1273 var offsetLeft = geo.origin.offset.left - self.__Geometry.origin.offset.left,
1274 offsetTop = geo.origin.offset.top - self.__Geometry.origin.offset.top;
1275
1276 // add the offset to the position initially computed by the display plugin
1277 self._$tooltip.css({
1278 left: self.__lastPosition.coord.left + offsetLeft,
1279 top: self.__lastPosition.coord.top + offsetTop
1280 });
1281 }
1282 }
1283 }
1284
1285 self._trigger({
1286 type: 'scroll',
1287 event: event,
1288 geo: geo
1289 });
1290 }
1291 }
1292
1293 return self;
1294 },
1295
1296 /**
1297 * Changes the state of the tooltip
1298 *
1299 * @param {string} state
1300 * @returns {self}
1301 * @private
1302 */
1303 __stateSet: function(state) {
1304
1305 this.__state = state;
1306
1307 this._trigger({
1308 type: 'state',
1309 state: state
1310 });
1311
1312 return this;
1313 },
1314
1315 /**
1316 * Clear appearance timeouts
1317 *
1318 * @returns {self}
1319 * @private
1320 */
1321 __timeoutsClear: function() {
1322
1323 // there is only one possible open timeout: the delayed opening
1324 // when the mouseenter/touchstart open triggers are used
1325 clearTimeout(this.__timeouts.open);
1326 this.__timeouts.open = null;
1327
1328 // ... but several close timeouts: the delayed closing when the
1329 // mouseleave close trigger is used and the timer option
1330 $.each(this.__timeouts.close, function(i, timeout) {
1331 clearTimeout(timeout);
1332 });
1333 this.__timeouts.close = [];
1334
1335 return this;
1336 },
1337
1338 /**
1339 * Start the tracker that will make checks at regular intervals
1340 *
1341 * @returns {self}
1342 * @private
1343 */
1344 __trackerStart: function() {
1345
1346 var self = this,
1347 $content = self._$tooltip.find('.tooltipster-content');
1348
1349 // get the initial content size
1350 if (self.__options.trackTooltip) {
1351 self.__contentBcr = $content[0].getBoundingClientRect();
1352 }
1353
1354 self.__tracker = setInterval(function() {
1355
1356 // if the origin or tooltip elements have been removed.
1357 // Note: we could destroy the instance now if the origin has
1358 // been removed but we'll leave that task to our garbage collector
1359 if (!bodyContains(self._$origin) || !bodyContains(self._$tooltip)) {
1360 self._close();
1361 }
1362 // if everything is alright
1363 else {
1364
1365 // compare the former and current positions of the origin to reposition
1366 // the tooltip if need be
1367 if (self.__options.trackOrigin) {
1368
1369 var g = self.__geometry(),
1370 identical = false;
1371
1372 // compare size first (a change requires repositioning too)
1373 if (areEqual(g.origin.size, self.__Geometry.origin.size)) {
1374
1375 // for elements that have a fixed lineage (see __geometry()), we track the
1376 // top and left properties (relative to window)
1377 if (self.__Geometry.origin.fixedLineage) {
1378 if (areEqual(g.origin.windowOffset, self.__Geometry.origin.windowOffset)) {
1379 identical = true;
1380 }
1381 }
1382 // otherwise, track total offset (relative to document)
1383 else {
1384 if (areEqual(g.origin.offset, self.__Geometry.origin.offset)) {
1385 identical = true;
1386 }
1387 }
1388 }
1389
1390 if (!identical) {
1391
1392 // close the tooltip when using the mouseleave close trigger
1393 // (see https://github.com/calebjacob/tooltipster/pull/253)
1394 if (self.__options.triggerClose.mouseleave) {
1395 self._close();
1396 } else {
1397 self.reposition();
1398 }
1399 }
1400 }
1401
1402 if (self.__options.trackTooltip) {
1403
1404 var currentBcr = $content[0].getBoundingClientRect();
1405
1406 if (currentBcr.height !== self.__contentBcr.height ||
1407 currentBcr.width !== self.__contentBcr.width
1408 ) {
1409 self.reposition();
1410 self.__contentBcr = currentBcr;
1411 }
1412 }
1413 }
1414 }, self.__options.trackerInterval);
1415
1416 return self;
1417 },
1418
1419 /**
1420 * Closes the tooltip (after the closing delay)
1421 *
1422 * @param event
1423 * @param callback
1424 * @param force Set to true to override a potential refusal of the user's function
1425 * @returns {self}
1426 * @protected
1427 */
1428 _close: function(event, callback, force) {
1429
1430 var self = this,
1431 ok = true;
1432
1433 self._trigger({
1434 type: 'close',
1435 event: event,
1436 stop: function() {
1437 ok = false;
1438 }
1439 });
1440
1441 // a destroying tooltip (force == true) may not refuse to close
1442 if (ok || force) {
1443
1444 // save the method custom callback and cancel any open method custom callbacks
1445 if (callback) self.__callbacks.close.push(callback);
1446 self.__callbacks.open = [];
1447
1448 // clear open/close timeouts
1449 self.__timeoutsClear();
1450
1451 var finishCallbacks = function() {
1452
1453 // trigger any close method custom callbacks and reset them
1454 $.each(self.__callbacks.close, function(i, c) {
1455 c.call(self, self, {
1456 event: event,
1457 origin: self._$origin[0]
1458 });
1459 });
1460
1461 self.__callbacks.close = [];
1462 };
1463
1464 if (self.__state != 'closed') {
1465
1466 var necessary = true,
1467 d = new Date(),
1468 now = d.getTime(),
1469 newClosingTime = now + self.__options.animationDuration[1];
1470
1471 // the tooltip may already already be disappearing, but if a new
1472 // call to close() is made after the animationDuration was changed
1473 // to 0 (for example), we ought to actually close it sooner than
1474 // previously scheduled. In that case it should be noted that the
1475 // browser will not adapt the animation duration to the new
1476 // animationDuration that was set after the start of the closing
1477 // animation.
1478 // Note: the same thing could be considered at opening, but is not
1479 // really useful since the tooltip is actually opened immediately
1480 // upon a call to _open(). Since it would not make the opening
1481 // animation finish sooner, its sole impact would be to trigger the
1482 // state event and the open callbacks sooner than the actual end of
1483 // the opening animation, which is not great.
1484 if (self.__state == 'disappearing') {
1485
1486 if (newClosingTime > self.__closingTime
1487 // in case closing is actually overdue because the script
1488 // execution was suspended. See #679
1489 &&
1490 self.__options.animationDuration[1] > 0
1491 ) {
1492 necessary = false;
1493 }
1494 }
1495
1496 if (necessary) {
1497
1498 self.__closingTime = newClosingTime;
1499
1500 if (self.__state != 'disappearing') {
1501 self.__stateSet('disappearing');
1502 }
1503
1504 var finish = function() {
1505
1506 // stop the tracker
1507 clearInterval(self.__tracker);
1508
1509 // a "beforeClose" option has been asked several times but would
1510 // probably useless since the content element is still accessible
1511 // via ::content(), and because people can always use listeners
1512 // inside their content to track what's going on. For the sake of
1513 // simplicity, this has been denied. Bur for the rare people who
1514 // really need the option (for old browsers or for the case where
1515 // detaching the content is actually destructive, for file or
1516 // password inputs for example), this event will do the work.
1517 self._trigger({
1518 type: 'closing',
1519 event: event
1520 });
1521
1522 // unbind listeners which are no longer needed
1523
1524 self._$tooltip
1525 .off('.' + self.__namespace + '-triggerClose')
1526 .removeClass('tooltipster-dying');
1527
1528 // orientationchange, scroll and resize listeners
1529 $(env.window).off('.' + self.__namespace + '-triggerClose');
1530
1531 // scroll listeners
1532 self.__$originParents.each(function(i, el) {
1533 $(el).off('scroll.' + self.__namespace + '-triggerClose');
1534 });
1535 // clear the array to prevent memory leaks
1536 self.__$originParents = null;
1537
1538 $(env.window.document.body).off('.' + self.__namespace + '-triggerClose');
1539
1540 self._$origin.off('.' + self.__namespace + '-triggerClose');
1541
1542 self._off('dismissable');
1543
1544 // a plugin that would like to remove the tooltip from the
1545 // DOM when closed should bind on this
1546 self.__stateSet('closed');
1547
1548 // trigger event
1549 self._trigger({
1550 type: 'after',
1551 event: event
1552 });
1553
1554 // call our constructor custom callback function
1555 if (self.__options.functionAfter) {
1556 self.__options.functionAfter.call(self, self, {
1557 event: event,
1558 origin: self._$origin[0]
1559 });
1560 }
1561
1562 // call our method custom callbacks functions
1563 finishCallbacks();
1564 };
1565
1566 if (env.hasTransitions) {
1567
1568 self._$tooltip.css({
1569 '-moz-animation-duration': self.__options.animationDuration[1] + 'ms',
1570 '-ms-animation-duration': self.__options.animationDuration[1] + 'ms',
1571 '-o-animation-duration': self.__options.animationDuration[1] + 'ms',
1572 '-webkit-animation-duration': self.__options.animationDuration[1] + 'ms',
1573 'animation-duration': self.__options.animationDuration[1] + 'ms',
1574 'transition-duration': self.__options.animationDuration[1] + 'ms'
1575 });
1576
1577 self._$tooltip
1578 // clear both potential open and close tasks
1579 .clearQueue()
1580 .removeClass('tooltipster-show')
1581 // for transitions only
1582 .addClass('tooltipster-dying');
1583
1584 if (self.__options.animationDuration[1] > 0) {
1585 self._$tooltip.delay(self.__options.animationDuration[1]);
1586 }
1587
1588 self._$tooltip.queue(finish);
1589 } else {
1590
1591 self._$tooltip
1592 .stop()
1593 .fadeOut(self.__options.animationDuration[1], finish);
1594 }
1595 }
1596 }
1597 // if the tooltip is already closed, we still need to trigger
1598 // the method custom callbacks
1599 else {
1600 finishCallbacks();
1601 }
1602 }
1603
1604 return self;
1605 },
1606
1607 /**
1608 * For internal use by plugins, if needed
1609 *
1610 * @returns {self}
1611 * @protected
1612 */
1613 _off: function() {
1614 this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
1615 return this;
1616 },
1617
1618 /**
1619 * For internal use by plugins, if needed
1620 *
1621 * @returns {self}
1622 * @protected
1623 */
1624 _on: function() {
1625 this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
1626 return this;
1627 },
1628
1629 /**
1630 * For internal use by plugins, if needed
1631 *
1632 * @returns {self}
1633 * @protected
1634 */
1635 _one: function() {
1636 this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
1637 return this;
1638 },
1639
1640 /**
1641 * Opens the tooltip right away.
1642 *
1643 * @param event
1644 * @param callback Will be called when the opening animation is over
1645 * @returns {self}
1646 * @protected
1647 */
1648 _open: function(event, callback) {
1649
1650 var self = this;
1651
1652 // if the destruction process has not begun and if this was not
1653 // triggered by an unwanted emulated click event
1654 if (!self.__destroying) {
1655
1656 // check that the origin is still in the DOM
1657 if (bodyContains(self._$origin)
1658 // if the tooltip is enabled
1659 &&
1660 self.__enabled
1661 ) {
1662
1663 var ok = true;
1664
1665 // if the tooltip is not open yet, we need to call functionBefore.
1666 // otherwise we can jst go on
1667 if (self.__state == 'closed') {
1668
1669 // trigger an event. The event.stop function allows the callback
1670 // to prevent the opening of the tooltip
1671 self._trigger({
1672 type: 'before',
1673 event: event,
1674 stop: function() {
1675 ok = false;
1676 }
1677 });
1678
1679 if (ok && self.__options.functionBefore) {
1680
1681 // call our custom function before continuing
1682 ok = self.__options.functionBefore.call(self, self, {
1683 event: event,
1684 origin: self._$origin[0]
1685 });
1686 }
1687 }
1688
1689 if (ok !== false) {
1690
1691 // if there is some content
1692 if (self.__Content !== null) {
1693
1694 // save the method callback and cancel close method callbacks
1695 if (callback) {
1696 self.__callbacks.open.push(callback);
1697 }
1698 self.__callbacks.close = [];
1699
1700 // get rid of any appearance timeouts
1701 self.__timeoutsClear();
1702
1703 var extraTime,
1704 finish = function() {
1705
1706 if (self.__state != 'stable') {
1707 self.__stateSet('stable');
1708 }
1709
1710 // trigger any open method custom callbacks and reset them
1711 $.each(self.__callbacks.open, function(i, c) {
1712 c.call(self, self, {
1713 origin: self._$origin[0],
1714 tooltip: self._$tooltip[0]
1715 });
1716 });
1717
1718 self.__callbacks.open = [];
1719 };
1720
1721 // if the tooltip is already open
1722 if (self.__state !== 'closed') {
1723
1724 // the timer (if any) will start (or restart) right now
1725 extraTime = 0;
1726
1727 // if it was disappearing, cancel that
1728 if (self.__state === 'disappearing') {
1729
1730 self.__stateSet('appearing');
1731
1732 if (env.hasTransitions) {
1733
1734 self._$tooltip
1735 .clearQueue()
1736 .removeClass('tooltipster-dying')
1737 .addClass('tooltipster-show');
1738
1739 if (self.__options.animationDuration[0] > 0) {
1740 self._$tooltip.delay(self.__options.animationDuration[0]);
1741 }
1742
1743 self._$tooltip.queue(finish);
1744 } else {
1745 // in case the tooltip was currently fading out, bring it back
1746 // to life
1747 self._$tooltip
1748 .stop()
1749 .fadeIn(finish);
1750 }
1751 }
1752 // if the tooltip is already open, we still need to trigger the method
1753 // custom callback
1754 else if (self.__state == 'stable') {
1755 finish();
1756 }
1757 }
1758 // if the tooltip isn't already open, open it
1759 else {
1760
1761 // a plugin must bind on this and store the tooltip in this._$tooltip
1762 self.__stateSet('appearing');
1763
1764 // the timer (if any) will start when the tooltip has fully appeared
1765 // after its transition
1766 extraTime = self.__options.animationDuration[0];
1767
1768 // insert the content inside the tooltip
1769 self.__contentInsert();
1770
1771 // reposition the tooltip and attach to the DOM
1772 self.reposition(event, true);
1773
1774 // animate in the tooltip. If the display plugin wants no css
1775 // animations, it may override the animation option with a
1776 // dummy value that will produce no effect
1777 if (env.hasTransitions) {
1778
1779 // note: there seems to be an issue with start animations which
1780 // are randomly not played on fast devices in both Chrome and FF,
1781 // couldn't find a way to solve it yet. It seems that applying
1782 // the classes before appending to the DOM helps a little, but
1783 // it messes up some CSS transitions. The issue almost never
1784 // happens when delay[0]==0 though
1785 self._$tooltip
1786 .addClass('tooltipster-' + self.__options.animation)
1787 .addClass('tooltipster-initial')
1788 .css({
1789 '-moz-animation-duration': self.__options.animationDuration[0] + 'ms',
1790 '-ms-animation-duration': self.__options.animationDuration[0] + 'ms',
1791 '-o-animation-duration': self.__options.animationDuration[0] + 'ms',
1792 '-webkit-animation-duration': self.__options.animationDuration[0] + 'ms',
1793 'animation-duration': self.__options.animationDuration[0] + 'ms',
1794 'transition-duration': self.__options.animationDuration[0] + 'ms'
1795 });
1796
1797 setTimeout(
1798 function() {
1799
1800 // a quick hover may have already triggered a mouseleave
1801 if (self.__state != 'closed') {
1802
1803 self._$tooltip
1804 .addClass('tooltipster-show')
1805 .removeClass('tooltipster-initial');
1806
1807 if (self.__options.animationDuration[0] > 0) {
1808 self._$tooltip.delay(self.__options.animationDuration[0]);
1809 }
1810
1811 self._$tooltip.queue(finish);
1812 }
1813 },
1814 0
1815 );
1816 } else {
1817
1818 // old browsers will have to live with this
1819 self._$tooltip
1820 .css('display', 'none')
1821 .fadeIn(self.__options.animationDuration[0], finish);
1822 }
1823
1824 // checks if the origin is removed while the tooltip is open
1825 self.__trackerStart();
1826
1827 // NOTE: the listeners below have a '-triggerClose' namespace
1828 // because we'll remove them when the tooltip closes (unlike
1829 // the '-triggerOpen' listeners). So some of them are actually
1830 // not about close triggers, rather about positioning.
1831
1832 $(env.window)
1833 // reposition on resize
1834 .on('resize.' + self.__namespace + '-triggerClose', function(e) {
1835
1836 var $ae = $(document.activeElement);
1837
1838 // reposition only if the resize event was not triggered upon the opening
1839 // of a virtual keyboard due to an input field being focused within the tooltip
1840 // (otherwise the repositioning would lose the focus)
1841 if ((!$ae.is('input') && !$ae.is('textarea')) ||
1842 !$.contains(self._$tooltip[0], $ae[0])
1843 ) {
1844 self.reposition(e);
1845 }
1846 })
1847 // same as below for parents
1848 .on('scroll.' + self.__namespace + '-triggerClose', function(e) {
1849 self.__scrollHandler(e);
1850 });
1851
1852 self.__$originParents = self._$origin.parents();
1853
1854 // scrolling may require the tooltip to be moved or even
1855 // repositioned in some cases
1856 self.__$originParents.each(function(i, parent) {
1857
1858 $(parent).on('scroll.' + self.__namespace + '-triggerClose', function(e) {
1859 self.__scrollHandler(e);
1860 });
1861 });
1862
1863 if (self.__options.triggerClose.mouseleave ||
1864 (self.__options.triggerClose.touchleave && env.hasTouchCapability)
1865 ) {
1866
1867 // we use an event to allow users/plugins to control when the mouseleave/touchleave
1868 // close triggers will come to action. It allows to have more triggering elements
1869 // than just the origin and the tooltip for example, or to cancel/delay the closing,
1870 // or to make the tooltip interactive even if it wasn't when it was open, etc.
1871 self._on('dismissable', function(event) {
1872
1873 if (event.dismissable) {
1874
1875 if (event.delay) {
1876
1877 timeout = setTimeout(function() {
1878 // event.event may be undefined
1879 self._close(event.event);
1880 }, event.delay);
1881
1882 self.__timeouts.close.push(timeout);
1883 } else {
1884 self._close(event);
1885 }
1886 } else {
1887 clearTimeout(timeout);
1888 }
1889 });
1890
1891 // now set the listeners that will trigger 'dismissable' events
1892 var $elements = self._$origin,
1893 eventNamesIn = '',
1894 eventNamesOut = '',
1895 timeout = null;
1896
1897 // if we have to allow interaction, bind on the tooltip too
1898 if (self.__options.interactive) {
1899 $elements = $elements.add(self._$tooltip);
1900 }
1901
1902 if (self.__options.triggerClose.mouseleave) {
1903 eventNamesIn += 'mouseenter.' + self.__namespace + '-triggerClose ';
1904 eventNamesOut += 'mouseleave.' + self.__namespace + '-triggerClose ';
1905 }
1906 if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
1907 eventNamesIn += 'touchstart.' + self.__namespace + '-triggerClose';
1908 eventNamesOut += 'touchend.' + self.__namespace + '-triggerClose touchcancel.' + self.__namespace + '-triggerClose';
1909 }
1910
1911 $elements
1912 // close after some time spent outside of the elements
1913 .on(eventNamesOut, function(event) {
1914
1915 // it's ok if the touch gesture ended up to be a swipe,
1916 // it's still a "touch leave" situation
1917 if (self._touchIsTouchEvent(event) ||
1918 !self._touchIsEmulatedEvent(event)
1919 ) {
1920
1921 var delay = (event.type == 'mouseleave') ?
1922 self.__options.delay :
1923 self.__options.delayTouch;
1924
1925 self._trigger({
1926 delay: delay[1],
1927 dismissable: true,
1928 event: event,
1929 type: 'dismissable'
1930 });
1931 }
1932 })
1933 // suspend the mouseleave timeout when the pointer comes back
1934 // over the elements
1935 .on(eventNamesIn, function(event) {
1936
1937 // it's also ok if the touch event is a swipe gesture
1938 if (self._touchIsTouchEvent(event) ||
1939 !self._touchIsEmulatedEvent(event)
1940 ) {
1941 self._trigger({
1942 dismissable: false,
1943 event: event,
1944 type: 'dismissable'
1945 });
1946 }
1947 });
1948 }
1949
1950 // close the tooltip when the origin gets a mouse click (common behavior of
1951 // native tooltips)
1952 if (self.__options.triggerClose.originClick) {
1953
1954 self._$origin.on('click.' + self.__namespace + '-triggerClose', function(event) {
1955
1956 // we could actually let a tap trigger this but this feature just
1957 // does not make sense on touch devices
1958 if (!self._touchIsTouchEvent(event) &&
1959 !self._touchIsEmulatedEvent(event)
1960 ) {
1961 self._close(event);
1962 }
1963 });
1964 }
1965
1966 // set the same bindings for click and touch on the body to close the tooltip
1967 if (self.__options.triggerClose.click ||
1968 (self.__options.triggerClose.tap && env.hasTouchCapability)
1969 ) {
1970
1971 // don't set right away since the click/tap event which triggered this method
1972 // (if it was a click/tap) is going to bubble up to the body, we don't want it
1973 // to close the tooltip immediately after it opened
1974 setTimeout(function() {
1975
1976 if (self.__state != 'closed') {
1977
1978 var eventNames = '',
1979 $body = $(env.window.document.body);
1980
1981 if (self.__options.triggerClose.click) {
1982 eventNames += 'click.' + self.__namespace + '-triggerClose ';
1983 }
1984 if (self.__options.triggerClose.tap && env.hasTouchCapability) {
1985 eventNames += 'touchend.' + self.__namespace + '-triggerClose';
1986 }
1987
1988 $body.on(eventNames, function(event) {
1989
1990 if (self._touchIsMeaningfulEvent(event)) {
1991
1992 self._touchRecordEvent(event);
1993
1994 if (!self.__options.interactive || !$.contains(self._$tooltip[0], event.target)) {
1995 self._close(event);
1996 }
1997 }
1998 });
1999
2000 // needed to detect and ignore swiping
2001 if (self.__options.triggerClose.tap && env.hasTouchCapability) {
2002
2003 $body.on('touchstart.' + self.__namespace + '-triggerClose', function(event) {
2004 self._touchRecordEvent(event);
2005 });
2006 }
2007 }
2008 }, 0);
2009 }
2010
2011 self._trigger('ready');
2012
2013 // call our custom callback
2014 if (self.__options.functionReady) {
2015 self.__options.functionReady.call(self, self, {
2016 origin: self._$origin[0],
2017 tooltip: self._$tooltip[0]
2018 });
2019 }
2020 }
2021
2022 // if we have a timer set, let the countdown begin
2023 if (self.__options.timer > 0) {
2024
2025 var timeout = setTimeout(function() {
2026 self._close();
2027 }, self.__options.timer + extraTime);
2028
2029 self.__timeouts.close.push(timeout);
2030 }
2031 }
2032 }
2033 }
2034 }
2035
2036 return self;
2037 },
2038
2039 /**
2040 * When using the mouseenter/touchstart open triggers, this function will
2041 * schedule the opening of the tooltip after the delay, if there is one
2042 *
2043 * @param event
2044 * @returns {self}
2045 * @protected
2046 */
2047 _openShortly: function(event) {
2048
2049 var self = this,
2050 ok = true;
2051
2052 if (self.__state != 'stable' && self.__state != 'appearing') {
2053
2054 // if a timeout is not already running
2055 if (!self.__timeouts.open) {
2056
2057 self._trigger({
2058 type: 'start',
2059 event: event,
2060 stop: function() {
2061 ok = false;
2062 }
2063 });
2064
2065 if (ok) {
2066
2067 var delay = (event.type.indexOf('touch') == 0) ?
2068 self.__options.delayTouch :
2069 self.__options.delay;
2070
2071 if (delay[0]) {
2072
2073 self.__timeouts.open = setTimeout(function() {
2074
2075 self.__timeouts.open = null;
2076
2077 // open only if the pointer (mouse or touch) is still over the origin.
2078 // The check on the "meaningful event" can only be made here, after some
2079 // time has passed (to know if the touch was a swipe or not)
2080 if (self.__pointerIsOverOrigin && self._touchIsMeaningfulEvent(event)) {
2081
2082 // signal that we go on
2083 self._trigger('startend');
2084
2085 self._open(event);
2086 } else {
2087 // signal that we cancel
2088 self._trigger('startcancel');
2089 }
2090 }, delay[0]);
2091 } else {
2092 // signal that we go on
2093 self._trigger('startend');
2094
2095 self._open(event);
2096 }
2097 }
2098 }
2099 }
2100
2101 return self;
2102 },
2103
2104 /**
2105 * Meant for plugins to get their options
2106 *
2107 * @param {string} pluginName The name of the plugin that asks for its options
2108 * @param {object} defaultOptions The default options of the plugin
2109 * @returns {object} The options
2110 * @protected
2111 */
2112 _optionsExtract: function(pluginName, defaultOptions) {
2113
2114 var self = this,
2115 options = $.extend(true, {}, defaultOptions);
2116
2117 // if the plugin options were isolated in a property named after the
2118 // plugin, use them (prevents conflicts with other plugins)
2119 var pluginOptions = self.__options[pluginName];
2120
2121 // if not, try to get them as regular options
2122 if (!pluginOptions) {
2123
2124 pluginOptions = {};
2125
2126 $.each(defaultOptions, function(optionName, value) {
2127
2128 var o = self.__options[optionName];
2129
2130 if (o !== undefined) {
2131 pluginOptions[optionName] = o;
2132 }
2133 });
2134 }
2135
2136 // let's merge the default options and the ones that were provided. We'd want
2137 // to do a deep copy but not let jQuery merge arrays, so we'll do a shallow
2138 // extend on two levels, that will be enough if options are not more than 1
2139 // level deep
2140 $.each(options, function(optionName, value) {
2141
2142 if (pluginOptions[optionName] !== undefined) {
2143
2144 if ((typeof value == 'object' &&
2145 !(value instanceof Array) &&
2146 value != null
2147 ) &&
2148 (typeof pluginOptions[optionName] == 'object' &&
2149 !(pluginOptions[optionName] instanceof Array) &&
2150 pluginOptions[optionName] != null
2151 )
2152 ) {
2153 $.extend(options[optionName], pluginOptions[optionName]);
2154 } else {
2155 options[optionName] = pluginOptions[optionName];
2156 }
2157 }
2158 });
2159
2160 return options;
2161 },
2162
2163 /**
2164 * Used at instantiation of the plugin, or afterwards by plugins that activate themselves
2165 * on existing instances
2166 *
2167 * @param {object} pluginName
2168 * @returns {self}
2169 * @protected
2170 */
2171 _plug: function(pluginName) {
2172
2173 var plugin = $.tooltipster._plugin(pluginName);
2174
2175 if (plugin) {
2176
2177 // if there is a constructor for instances
2178 if (plugin.instance) {
2179
2180 // proxy non-private methods on the instance to allow new instance methods
2181 $.tooltipster.__bridge(plugin.instance, this, plugin.name);
2182 }
2183 } else {
2184 throw new Error('The "' + pluginName + '" plugin is not defined');
2185 }
2186
2187 return this;
2188 },
2189
2190 /**
2191 * This will return true if the event is a mouse event which was
2192 * emulated by the browser after a touch event. This allows us to
2193 * really dissociate mouse and touch triggers.
2194 *
2195 * There is a margin of error if a real mouse event is fired right
2196 * after (within the delay shown below) a touch event on the same
2197 * element, but hopefully it should not happen often.
2198 *
2199 * @returns {boolean}
2200 * @protected
2201 */
2202 _touchIsEmulatedEvent: function(event) {
2203
2204 var isEmulated = false,
2205 now = new Date().getTime();
2206
2207 for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
2208
2209 var e = this.__touchEvents[i];
2210
2211 // delay, in milliseconds. It's supposed to be 300ms in
2212 // most browsers (350ms on iOS) to allow a double tap but
2213 // can be less (check out FastClick for more info)
2214 if (now - e.time < 500) {
2215
2216 if (e.target === event.target) {
2217 isEmulated = true;
2218 }
2219 } else {
2220 break;
2221 }
2222 }
2223
2224 return isEmulated;
2225 },
2226
2227 /**
2228 * Returns false if the event was an emulated mouse event or
2229 * a touch event involved in a swipe gesture.
2230 *
2231 * @param {object} event
2232 * @returns {boolean}
2233 * @protected
2234 */
2235 _touchIsMeaningfulEvent: function(event) {
2236 return (
2237 (this._touchIsTouchEvent(event) && !this._touchSwiped(event.target)) ||
2238 (!this._touchIsTouchEvent(event) && !this._touchIsEmulatedEvent(event))
2239 );
2240 },
2241
2242 /**
2243 * Checks if an event is a touch event
2244 *
2245 * @param {object} event
2246 * @returns {boolean}
2247 * @protected
2248 */
2249 _touchIsTouchEvent: function(event) {
2250 return event.type.indexOf('touch') == 0;
2251 },
2252
2253 /**
2254 * Store touch events for a while to detect swiping and emulated mouse events
2255 *
2256 * @param {object} event
2257 * @returns {self}
2258 * @protected
2259 */
2260 _touchRecordEvent: function(event) {
2261
2262 if (this._touchIsTouchEvent(event)) {
2263 event.time = new Date().getTime();
2264 this.__touchEvents.push(event);
2265 }
2266
2267 return this;
2268 },
2269
2270 /**
2271 * Returns true if a swipe happened after the last touchstart event fired on
2272 * event.target.
2273 *
2274 * We need to differentiate a swipe from a tap before we let the event open
2275 * or close the tooltip. A swipe is when a touchmove (scroll) event happens
2276 * on the body between the touchstart and the touchend events of an element.
2277 *
2278 * @param {object} target The HTML element that may have triggered the swipe
2279 * @returns {boolean}
2280 * @protected
2281 */
2282 _touchSwiped: function(target) {
2283
2284 var swiped = false;
2285
2286 for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
2287
2288 var e = this.__touchEvents[i];
2289
2290 if (e.type == 'touchmove') {
2291 swiped = true;
2292 break;
2293 } else if (
2294 e.type == 'touchstart' &&
2295 target === e.target
2296 ) {
2297 break;
2298 }
2299 }
2300
2301 return swiped;
2302 },
2303
2304 /**
2305 * Triggers an event on the instance emitters
2306 *
2307 * @returns {self}
2308 * @protected
2309 */
2310 _trigger: function() {
2311
2312 var args = Array.prototype.slice.apply(arguments);
2313
2314 if (typeof args[0] == 'string') {
2315 args[0] = {
2316 type: args[0]
2317 };
2318 }
2319
2320 // add properties to the event
2321 args[0].instance = this;
2322 args[0].origin = this._$origin ? this._$origin[0] : null;
2323 args[0].tooltip = this._$tooltip ? this._$tooltip[0] : null;
2324
2325 // note: the order of emitters matters
2326 this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
2327 $.tooltipster._trigger.apply($.tooltipster, args);
2328 this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
2329
2330 return this;
2331 },
2332
2333 /**
2334 * Deactivate a plugin on this instance
2335 *
2336 * @returns {self}
2337 * @protected
2338 */
2339 _unplug: function(pluginName) {
2340
2341 var self = this;
2342
2343 // if the plugin has been activated on this instance
2344 if (self[pluginName]) {
2345
2346 var plugin = $.tooltipster._plugin(pluginName);
2347
2348 // if there is a constructor for instances
2349 if (plugin.instance) {
2350
2351 // unbridge
2352 $.each(plugin.instance, function(methodName, fn) {
2353
2354 // if the method exists (privates methods do not) and comes indeed from
2355 // this plugin (may be missing or come from a conflicting plugin).
2356 if (self[methodName] &&
2357 self[methodName].bridged === self[pluginName]
2358 ) {
2359 delete self[methodName];
2360 }
2361 });
2362 }
2363
2364 // destroy the plugin
2365 if (self[pluginName].__destroy) {
2366 self[pluginName].__destroy();
2367 }
2368
2369 // remove the reference to the plugin instance
2370 delete self[pluginName];
2371 }
2372
2373 return self;
2374 },
2375
2376 /**
2377 * @see self::_close
2378 * @returns {self}
2379 * @public
2380 */
2381 close: function(callback) {
2382
2383 if (!this.__destroyed) {
2384 this._close(null, callback);
2385 } else {
2386 this.__destroyError();
2387 }
2388
2389 return this;
2390 },
2391
2392 /**
2393 * Sets or gets the content of the tooltip
2394 *
2395 * @returns {mixed|self}
2396 * @public
2397 */
2398 content: function(content) {
2399
2400 var self = this;
2401
2402 // getter method
2403 if (content === undefined) {
2404 return self.__Content;
2405 }
2406 // setter method
2407 else {
2408
2409 if (!self.__destroyed) {
2410
2411 // change the content
2412 self.__contentSet(content);
2413
2414 if (self.__Content !== null) {
2415
2416 // update the tooltip if it is open
2417 if (self.__state !== 'closed') {
2418
2419 // reset the content in the tooltip
2420 self.__contentInsert();
2421
2422 // reposition and resize the tooltip
2423 self.reposition();
2424
2425 // if we want to play a little animation showing the content changed
2426 if (self.__options.updateAnimation) {
2427
2428 if (env.hasTransitions) {
2429
2430 // keep the reference in the local scope
2431 var animation = self.__options.updateAnimation;
2432
2433 self._$tooltip.addClass('tooltipster-update-' + animation);
2434
2435 // remove the class after a while. The actual duration of the
2436 // update animation may be shorter, it's set in the CSS rules
2437 setTimeout(function() {
2438
2439 if (self.__state != 'closed') {
2440
2441 self._$tooltip.removeClass('tooltipster-update-' + animation);
2442 }
2443 }, 1000);
2444 } else {
2445 self._$tooltip.fadeTo(200, 0.5, function() {
2446 if (self.__state != 'closed') {
2447 self._$tooltip.fadeTo(200, 1);
2448 }
2449 });
2450 }
2451 }
2452 }
2453 } else {
2454 self._close();
2455 }
2456 } else {
2457 self.__destroyError();
2458 }
2459
2460 return self;
2461 }
2462 },
2463
2464 /**
2465 * Destroys the tooltip
2466 *
2467 * @returns {self}
2468 * @public
2469 */
2470 destroy: function() {
2471
2472 var self = this;
2473
2474 if (!self.__destroyed) {
2475
2476 if (self.__state != 'closed') {
2477
2478 // no closing delay
2479 self.option('animationDuration', 0)
2480 // force closing
2481 ._close(null, null, true);
2482 } else {
2483 // there might be an open timeout still running
2484 self.__timeoutsClear();
2485 }
2486
2487 // send event
2488 self._trigger('destroy');
2489
2490 self.__destroyed = true;
2491
2492 self._$origin
2493 .removeData(self.__namespace)
2494 // remove the open trigger listeners
2495 .off('.' + self.__namespace + '-triggerOpen');
2496
2497 // remove the touch listener
2498 $(env.window.document.body).off('.' + self.__namespace + '-triggerOpen');
2499
2500 var ns = self._$origin.data('tooltipster-ns');
2501
2502 // if the origin has been removed from DOM, its data may
2503 // well have been destroyed in the process and there would
2504 // be nothing to clean up or restore
2505 if (ns) {
2506
2507 // if there are no more tooltips on this element
2508 if (ns.length === 1) {
2509
2510 // optional restoration of a title attribute
2511 var title = null;
2512 if (self.__options.restoration == 'previous') {
2513 title = self._$origin.data('tooltipster-initialTitle');
2514 } else if (self.__options.restoration == 'current') {
2515
2516 // old school technique to stringify when outerHTML is not supported
2517 title = (typeof self.__Content == 'string') ?
2518 self.__Content :
2519 $('<div></div>').append(self.__Content).html();
2520 }
2521
2522 if (title) {
2523 self._$origin.attr('title', title);
2524 }
2525
2526 // final cleaning
2527
2528 self._$origin.removeClass('tooltipstered');
2529
2530 self._$origin
2531 .removeData('tooltipster-ns')
2532 .removeData('tooltipster-initialTitle');
2533 } else {
2534 // remove the instance namespace from the list of namespaces of
2535 // tooltips present on the element
2536 ns = $.grep(ns, function(el, i) {
2537 return el !== self.__namespace;
2538 });
2539 self._$origin.data('tooltipster-ns', ns);
2540 }
2541 }
2542
2543 // last event
2544 self._trigger('destroyed');
2545
2546 // unbind private and public event listeners
2547 self._off();
2548 self.off();
2549
2550 // remove external references, just in case
2551 self.__Content = null;
2552 self.__$emitterPrivate = null;
2553 self.__$emitterPublic = null;
2554 self.__options.parent = null;
2555 self._$origin = null;
2556 self._$tooltip = null;
2557
2558 // make sure the object is no longer referenced in there to prevent
2559 // memory leaks
2560 $.tooltipster.__instancesLatestArr = $.grep($.tooltipster.__instancesLatestArr, function(el, i) {
2561 return self !== el;
2562 });
2563
2564 clearInterval(self.__garbageCollector);
2565 } else {
2566 self.__destroyError();
2567 }
2568
2569 // we return the scope rather than true so that the call to
2570 // .tooltipster('destroy') actually returns the matched elements
2571 // and applies to all of them
2572 return self;
2573 },
2574
2575 /**
2576 * Disables the tooltip
2577 *
2578 * @returns {self}
2579 * @public
2580 */
2581 disable: function() {
2582
2583 if (!this.__destroyed) {
2584
2585 // close first, in case the tooltip would not disappear on
2586 // its own (no close trigger)
2587 this._close();
2588 this.__enabled = false;
2589
2590 return this;
2591 } else {
2592 this.__destroyError();
2593 }
2594
2595 return this;
2596 },
2597
2598 /**
2599 * Returns the HTML element of the origin
2600 *
2601 * @returns {self}
2602 * @public
2603 */
2604 elementOrigin: function() {
2605
2606 if (!this.__destroyed) {
2607 return this._$origin[0];
2608 } else {
2609 this.__destroyError();
2610 }
2611 },
2612
2613 /**
2614 * Returns the HTML element of the tooltip
2615 *
2616 * @returns {self}
2617 * @public
2618 */
2619 elementTooltip: function() {
2620 return this._$tooltip ? this._$tooltip[0] : null;
2621 },
2622
2623 /**
2624 * Enables the tooltip
2625 *
2626 * @returns {self}
2627 * @public
2628 */
2629 enable: function() {
2630 this.__enabled = true;
2631 return this;
2632 },
2633
2634 /**
2635 * Alias, deprecated in 4.0.0
2636 *
2637 * @param {function} callback
2638 * @returns {self}
2639 * @public
2640 */
2641 hide: function(callback) {
2642 return this.close(callback);
2643 },
2644
2645 /**
2646 * Returns the instance
2647 *
2648 * @returns {self}
2649 * @public
2650 */
2651 instance: function() {
2652 return this;
2653 },
2654
2655 /**
2656 * For public use only, not to be used by plugins (use ::_off() instead)
2657 *
2658 * @returns {self}
2659 * @public
2660 */
2661 off: function() {
2662
2663 if (!this.__destroyed) {
2664 this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2665 }
2666
2667 return this;
2668 },
2669
2670 /**
2671 * For public use only, not to be used by plugins (use ::_on() instead)
2672 *
2673 * @returns {self}
2674 * @public
2675 */
2676 on: function() {
2677
2678 if (!this.__destroyed) {
2679 this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2680 } else {
2681 this.__destroyError();
2682 }
2683
2684 return this;
2685 },
2686
2687 /**
2688 * For public use only, not to be used by plugins
2689 *
2690 * @returns {self}
2691 * @public
2692 */
2693 one: function() {
2694
2695 if (!this.__destroyed) {
2696 this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2697 } else {
2698 this.__destroyError();
2699 }
2700
2701 return this;
2702 },
2703
2704 /**
2705 * @see self::_open
2706 * @returns {self}
2707 * @public
2708 */
2709 open: function(callback) {
2710
2711 if (!this.__destroyed) {
2712 this._open(null, callback);
2713 } else {
2714 this.__destroyError();
2715 }
2716
2717 return this;
2718 },
2719
2720 /**
2721 * Get or set options. For internal use and advanced users only.
2722 *
2723 * @param {string} o Option name
2724 * @param {mixed} val optional A new value for the option
2725 * @return {mixed|self} If val is omitted, the value of the option
2726 * is returned, otherwise the instance itself is returned
2727 * @public
2728 */
2729 option: function(o, val) {
2730
2731 // getter
2732 if (val === undefined) {
2733 return this.__options[o];
2734 }
2735 // setter
2736 else {
2737
2738 if (!this.__destroyed) {
2739
2740 // change value
2741 this.__options[o] = val;
2742
2743 // format
2744 this.__optionsFormat();
2745
2746 // re-prepare the triggers if needed
2747 if ($.inArray(o, ['trigger', 'triggerClose', 'triggerOpen']) >= 0) {
2748 this.__prepareOrigin();
2749 }
2750
2751 if (o === 'selfDestruction') {
2752 this.__prepareGC();
2753 }
2754 } else {
2755 this.__destroyError();
2756 }
2757
2758 return this;
2759 }
2760 },
2761
2762 /**
2763 * This method is in charge of setting the position and size properties of the tooltip.
2764 * All the hard work is delegated to the display plugin.
2765 * Note: The tooltip may be detached from the DOM at the moment the method is called
2766 * but must be attached by the end of the method call.
2767 *
2768 * @param {object} event For internal use only. Defined if an event such as
2769 * window resizing triggered the repositioning
2770 * @param {boolean} tooltipIsDetached For internal use only. Set this to true if you
2771 * know that the tooltip not being in the DOM is not an issue (typically when the
2772 * tooltip element has just been created but has not been added to the DOM yet).
2773 * @returns {self}
2774 * @public
2775 */
2776 reposition: function(event, tooltipIsDetached) {
2777
2778 var self = this;
2779
2780 if (!self.__destroyed) {
2781
2782 // if the tooltip is still open and the origin is still in the DOM
2783 if (self.__state != 'closed' && bodyContains(self._$origin)) {
2784
2785 // if the tooltip has not been removed from DOM manually (or if it
2786 // has been detached on purpose)
2787 if (tooltipIsDetached || bodyContains(self._$tooltip)) {
2788
2789 if (!tooltipIsDetached) {
2790 // detach in case the tooltip overflows the window and adds
2791 // scrollbars to it, so __geometry can be accurate
2792 self._$tooltip.detach();
2793 }
2794
2795 // refresh the geometry object before passing it as a helper
2796 self.__Geometry = self.__geometry();
2797
2798 // let a plugin fo the rest
2799 self._trigger({
2800 type: 'reposition',
2801 event: event,
2802 helper: {
2803 geo: self.__Geometry
2804 }
2805 });
2806 }
2807 }
2808 } else {
2809 self.__destroyError();
2810 }
2811
2812 return self;
2813 },
2814
2815 /**
2816 * Alias, deprecated in 4.0.0
2817 *
2818 * @param callback
2819 * @returns {self}
2820 * @public
2821 */
2822 show: function(callback) {
2823 return this.open(callback);
2824 },
2825
2826 /**
2827 * Returns some properties about the instance
2828 *
2829 * @returns {object}
2830 * @public
2831 */
2832 status: function() {
2833
2834 return {
2835 destroyed: this.__destroyed,
2836 enabled: this.__enabled,
2837 open: this.__state !== 'closed',
2838 state: this.__state
2839 };
2840 },
2841
2842 /**
2843 * For public use only, not to be used by plugins
2844 *
2845 * @returns {self}
2846 * @public
2847 */
2848 triggerHandler: function() {
2849
2850 if (!this.__destroyed) {
2851 this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2852 } else {
2853 this.__destroyError();
2854 }
2855
2856 return this;
2857 }
2858 };
2859
2860 $.fn.tooltipster = function() {
2861
2862 // for using in closures
2863 var args = Array.prototype.slice.apply(arguments),
2864 // common mistake: an HTML element can't be in several tooltips at the same time
2865 contentCloningWarning = 'You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.';
2866
2867 // this happens with $(sel).tooltipster(...) when $(sel) does not match anything
2868 if (this.length === 0) {
2869
2870 // still chainable
2871 return this;
2872 }
2873 // this happens when calling $(sel).tooltipster('methodName or options')
2874 // where $(sel) matches one or more elements
2875 else {
2876
2877 // method calls
2878 if (typeof args[0] === 'string') {
2879
2880 var v = '#*$~&';
2881
2882 this.each(function() {
2883
2884 // retrieve the namepaces of the tooltip(s) that exist on that element.
2885 // We will interact with the first tooltip only.
2886 var ns = $(this).data('tooltipster-ns'),
2887 // self represents the instance of the first tooltipster plugin
2888 // associated to the current HTML object of the loop
2889 self = ns ? $(this).data(ns[0]) : null;
2890
2891 // if the current element holds a tooltipster instance
2892 if (self) {
2893
2894 if (typeof self[args[0]] === 'function') {
2895
2896 if (this.length > 1 &&
2897 args[0] == 'content' &&
2898 (args[1] instanceof $ ||
2899 (typeof args[1] == 'object' && args[1] != null && args[1].tagName)
2900 ) &&
2901 !self.__options.contentCloning &&
2902 self.__options.debug
2903 ) {
2904
2905 }
2906
2907 // note : args[1] and args[2] may not be defined
2908 var resp = self[args[0]](args[1], args[2]);
2909 } else {
2910 throw new Error('Unknown method "' + args[0] + '"');
2911 }
2912
2913 // if the function returned anything other than the instance
2914 // itself (which implies chaining, except for the `instance` method)
2915 if (resp !== self || args[0] === 'instance') {
2916
2917 v = resp;
2918
2919 // return false to stop .each iteration on the first element
2920 // matched by the selector
2921 return false;
2922 }
2923 } else {
2924 throw new Error('You called Tooltipster\'s "' + args[0] + '" method on an uninitialized element');
2925 }
2926 });
2927
2928 return (v !== '#*$~&') ? v : this;
2929 }
2930 // first argument is undefined or an object: the tooltip is initializing
2931 else {
2932
2933 // reset the array of last initialized objects
2934 $.tooltipster.__instancesLatestArr = [];
2935
2936 // is there a defined value for the multiple option in the options object ?
2937 var multipleIsSet = args[0] && args[0].multiple !== undefined,
2938 // if the multiple option is set to true, or if it's not defined but
2939 // set to true in the defaults
2940 multiple = (multipleIsSet && args[0].multiple) || (!multipleIsSet && defaults.multiple),
2941 // same for content
2942 contentIsSet = args[0] && args[0].content !== undefined,
2943 content = (contentIsSet && args[0].content) || (!contentIsSet && defaults.content),
2944 // same for contentCloning
2945 contentCloningIsSet = args[0] && args[0].contentCloning !== undefined,
2946 contentCloning =
2947 (contentCloningIsSet && args[0].contentCloning) ||
2948 (!contentCloningIsSet && defaults.contentCloning),
2949 // same for debug
2950 debugIsSet = args[0] && args[0].debug !== undefined,
2951 debug = (debugIsSet && args[0].debug) || (!debugIsSet && defaults.debug);
2952
2953 if (this.length > 1 &&
2954 (content instanceof $ ||
2955 (typeof content == 'object' && content != null && content.tagName)
2956 ) &&
2957 !contentCloning &&
2958 debug
2959 ) {
2960
2961 }
2962
2963 // create a tooltipster instance for each element if it doesn't
2964 // already have one or if the multiple option is set, and attach the
2965 // object to it
2966 this.each(function() {
2967
2968 var go = false,
2969 $this = $(this),
2970 ns = $this.data('tooltipster-ns'),
2971 obj = null;
2972
2973 if (!ns) {
2974 go = true;
2975 } else if (multiple) {
2976 go = true;
2977 } else if (debug) {}
2978
2979 if (go) {
2980 obj = new $.Tooltipster(this, args[0]);
2981
2982 // save the reference of the new instance
2983 if (!ns) ns = [];
2984 ns.push(obj.__namespace);
2985 $this.data('tooltipster-ns', ns);
2986
2987 // save the instance itself
2988 $this.data(obj.__namespace, obj);
2989
2990 // call our constructor custom function.
2991 // we do this here and not in ::init() because we wanted
2992 // the object to be saved in $this.data before triggering
2993 // it
2994 if (obj.__options.functionInit) {
2995 obj.__options.functionInit.call(obj, obj, {
2996 origin: this
2997 });
2998 }
2999
3000 // and now the event, for the plugins and core emitter
3001 obj._trigger('init');
3002 }
3003
3004 $.tooltipster.__instancesLatestArr.push(obj);
3005 });
3006
3007 return this;
3008 }
3009 }
3010 };
3011
3012 // Utilities
3013
3014 /**
3015 * A class to check if a tooltip can fit in given dimensions
3016 *
3017 * @param {object} $tooltip The jQuery wrapped tooltip element, or a clone of it
3018 */
3019 function Ruler($tooltip) {
3020
3021 // list of instance variables
3022
3023 this.$container;
3024 this.constraints = null;
3025 this.__$tooltip;
3026
3027 this.__init($tooltip);
3028 }
3029
3030 Ruler.prototype = {
3031
3032 /**
3033 * Move the tooltip into an invisible div that does not allow overflow to make
3034 * size tests. Note: the tooltip may or may not be attached to the DOM at the
3035 * moment this method is called, it does not matter.
3036 *
3037 * @param {object} $tooltip The object to test. May be just a clone of the
3038 * actual tooltip.
3039 * @private
3040 */
3041 __init: function($tooltip) {
3042
3043 this.__$tooltip = $tooltip;
3044
3045 this.__$tooltip
3046 .css({
3047 // for some reason we have to specify top and left 0
3048 left: 0,
3049 // any overflow will be ignored while measuring
3050 overflow: 'hidden',
3051 // positions at (0,0) without the div using 100% of the available width
3052 position: 'absolute',
3053 top: 0
3054 })
3055 // overflow must be auto during the test. We re-set this in case
3056 // it were modified by the user
3057 .find('.tooltipster-content')
3058 .css('overflow', 'auto');
3059
3060 this.$container = $('<div class="tooltipster-ruler"></div>')
3061 .append(this.__$tooltip)
3062 .appendTo(env.window.document.body);
3063 },
3064
3065 /**
3066 * Force the browser to redraw (re-render) the tooltip immediately. This is required
3067 * when you changed some CSS properties and need to make something with it
3068 * immediately, without waiting for the browser to redraw at the end of instructions.
3069 *
3070 * @see http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes
3071 * @private
3072 */
3073 __forceRedraw: function() {
3074
3075 // note: this would work but for Webkit only
3076 //this.__$tooltip.close();
3077 //this.__$tooltip[0].offsetHeight;
3078 //this.__$tooltip.open();
3079
3080 // works in FF too
3081 var $p = this.__$tooltip.parent();
3082 this.__$tooltip.detach();
3083 this.__$tooltip.appendTo($p);
3084 },
3085
3086 /**
3087 * Set maximum dimensions for the tooltip. A call to ::measure afterwards
3088 * will tell us if the content overflows or if it's ok
3089 *
3090 * @param {int} width
3091 * @param {int} height
3092 * @return {Ruler}
3093 * @public
3094 */
3095 constrain: function(width, height) {
3096
3097 this.constraints = {
3098 width: width,
3099 height: height
3100 };
3101
3102 this.__$tooltip.css({
3103 // we disable display:flex, otherwise the content would overflow without
3104 // creating horizontal scrolling (which we need to detect).
3105 display: 'block',
3106 // reset any previous height
3107 height: '',
3108 // we'll check if horizontal scrolling occurs
3109 overflow: 'auto',
3110 // we'll set the width and see what height is generated and if there
3111 // is horizontal overflow
3112 width: width
3113 });
3114
3115 return this;
3116 },
3117
3118 /**
3119 * Reset the tooltip content overflow and remove the test container
3120 *
3121 * @returns {Ruler}
3122 * @public
3123 */
3124 destroy: function() {
3125
3126 // in case the element was not a clone
3127 this.__$tooltip
3128 .detach()
3129 .find('.tooltipster-content')
3130 .css({
3131 // reset to CSS value
3132 display: '',
3133 overflow: ''
3134 });
3135
3136 this.$container.remove();
3137 },
3138
3139 /**
3140 * Removes any constraints
3141 *
3142 * @returns {Ruler}
3143 * @public
3144 */
3145 free: function() {
3146
3147 this.constraints = null;
3148
3149 // reset to natural size
3150 this.__$tooltip.css({
3151 display: '',
3152 height: '',
3153 overflow: 'visible',
3154 width: ''
3155 });
3156
3157 return this;
3158 },
3159
3160 /**
3161 * Returns the size of the tooltip. When constraints are applied, also returns
3162 * whether the tooltip fits in the provided dimensions.
3163 * The idea is to see if the new height is small enough and if the content does
3164 * not overflow horizontally.
3165 *
3166 * @param {int} width
3167 * @param {int} height
3168 * @returns {object} An object with a bool `fits` property and a `size` property
3169 * @public
3170 */
3171 measure: function() {
3172
3173 this.__forceRedraw();
3174
3175 var tooltipBcr = this.__$tooltip[0].getBoundingClientRect(),
3176 result = {
3177 size: {
3178 // bcr.width/height are not defined in IE8- but in this
3179 // case, bcr.right/bottom will have the same value
3180 // except in iOS 8+ where tooltipBcr.bottom/right are wrong
3181 // after scrolling for reasons yet to be determined.
3182 // tooltipBcr.top/left might not be 0, see issue #514
3183 height: tooltipBcr.height || (tooltipBcr.bottom - tooltipBcr.top),
3184 width: tooltipBcr.width || (tooltipBcr.right - tooltipBcr.left)
3185 }
3186 };
3187
3188 if (this.constraints) {
3189
3190 // note: we used to use offsetWidth instead of boundingRectClient but
3191 // it returned rounded values, causing issues with sub-pixel layouts.
3192
3193 // note2: noticed that the bcrWidth of text content of a div was once
3194 // greater than the bcrWidth of its container by 1px, causing the final
3195 // tooltip box to be too small for its content. However, evaluating
3196 // their widths one against the other (below) surprisingly returned
3197 // equality. Happened only once in Chrome 48, was not able to reproduce
3198 // => just having fun with float position values...
3199
3200 var $content = this.__$tooltip.find('.tooltipster-content'),
3201 height = this.__$tooltip.outerHeight(),
3202 contentBcr = $content[0].getBoundingClientRect(),
3203 fits = {
3204 height: height <= this.constraints.height,
3205 width: (
3206 // this condition accounts for min-width property that
3207 // may apply
3208 tooltipBcr.width <= this.constraints.width
3209 // the -1 is here because scrollWidth actually returns
3210 // a rounded value, and may be greater than bcr.width if
3211 // it was rounded up. This may cause an issue for contents
3212 // which actually really overflow by 1px or so, but that
3213 // should be rare. Not sure how to solve this efficiently.
3214 // See http://blogs.msdn.com/b/ie/archive/2012/02/17/sub-pixel-rendering-and-the-css-object-model.aspx
3215 &&
3216 contentBcr.width >= $content[0].scrollWidth - 1
3217 )
3218 };
3219
3220 result.fits = fits.height && fits.width;
3221 }
3222
3223 // old versions of IE get the width wrong for some reason and it causes
3224 // the text to be broken to a new line, so we round it up. If the width
3225 // is the width of the screen though, we can assume it is accurate.
3226 if (env.IE &&
3227 env.IE <= 11 &&
3228 result.size.width !== env.window.document.documentElement.clientWidth
3229 ) {
3230 result.size.width = Math.ceil(result.size.width) + 1;
3231 }
3232
3233 return result;
3234 }
3235 };
3236
3237 // quick & dirty compare function, not bijective nor multidimensional
3238 function areEqual(a, b) {
3239 var same = true;
3240 $.each(a, function(i, _) {
3241 if (b[i] === undefined || a[i] !== b[i]) {
3242 same = false;
3243 return false;
3244 }
3245 });
3246 return same;
3247 }
3248
3249 /**
3250 * A fast function to check if an element is still in the DOM. It
3251 * tries to use an id as ids are indexed by the browser, or falls
3252 * back to jQuery's `contains` method. May fail if two elements
3253 * have the same id, but so be it
3254 *
3255 * @param {object} $obj A jQuery-wrapped HTML element
3256 * @return {boolean}
3257 */
3258 function bodyContains($obj) {
3259 var id = $obj.attr('id'),
3260 el = id ? env.window.document.getElementById(id) : null;
3261 // must also check that the element with the id is the one we want
3262 return el ? el === $obj[0] : $.contains(env.window.document.body, $obj[0]);
3263 }
3264
3265 // detect IE versions for dirty fixes
3266 var uA = navigator.userAgent.toLowerCase();
3267 if (uA.indexOf('msie') != -1) env.IE = parseInt(uA.split('msie')[1]);
3268 else if (uA.toLowerCase().indexOf('trident') !== -1 && uA.indexOf(' rv:11') !== -1) env.IE = 11;
3269 else if (uA.toLowerCase().indexOf('edge/') != -1) env.IE = parseInt(uA.toLowerCase().split('edge/')[1]);
3270
3271 // detecting support for CSS transitions
3272 function transitionSupport() {
3273
3274 // env.window is not defined yet when this is called
3275 if (!win) return false;
3276
3277 var b = win.document.body || win.document.documentElement,
3278 s = b.style,
3279 p = 'transition',
3280 v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
3281
3282 if (typeof s[p] == 'string') {
3283 return true;
3284 }
3285
3286 p = p.charAt(0).toUpperCase() + p.substr(1);
3287 for (var i = 0; i < v.length; i++) {
3288 if (typeof s[v[i] + p] == 'string') {
3289 return true;
3290 }
3291 }
3292 return false;
3293 }
3294
3295 // we'll return jQuery for plugins not to have to declare it as a dependency,
3296 // but it's done by a build task since it should be included only once at the
3297 // end when we concatenate the main file with a pluginreturn $;
3298
3299 // sideTip is Tooltipster's default plugin.
3300 // This file will be UMDified by a build task.
3301 $.tooltipster._plugin({
3302 name: 'tooltipster.sideTip',
3303 instance: {
3304 /**
3305 * Defaults are provided as a function for an easy override by inheritance
3306 *
3307 * @return {object} An object with the defaults options
3308 * @private
3309 */
3310 __defaults: function() {
3311
3312 return {
3313 // if the tooltip should display an arrow that points to the origin
3314 arrow: true,
3315 // the distance in pixels between the tooltip and the origin
3316 distance: 6,
3317 // allows to easily change the position of the tooltip
3318 functionPosition: null,
3319 maxWidth: null,
3320 // used to accomodate the arrow of tooltip if there is one.
3321 // First to make sure that the arrow target is not too close
3322 // to the edge of the tooltip, so the arrow does not overflow
3323 // the tooltip. Secondly when we reposition the tooltip to
3324 // make sure that it's positioned in such a way that the arrow is
3325 // still pointing at the target (and not a few pixels beyond it).
3326 // It should be equal to or greater than half the width of
3327 // the arrow (by width we mean the size of the side which touches
3328 // the side of the tooltip).
3329 minIntersection: 16,
3330 minWidth: 0,
3331 // deprecated in 4.0.0. Listed for _optionsExtract to pick it up
3332 position: null,
3333 side: 'top',
3334 // set to false to position the tooltip relatively to the document rather
3335 // than the window when we open it
3336 viewportAware: true
3337 };
3338 },
3339
3340 /**
3341 * Run once: at instantiation of the plugin
3342 *
3343 * @param {object} instance The tooltipster object that instantiated this plugin
3344 * @private
3345 */
3346 __init: function(instance) {
3347
3348 var self = this;
3349
3350 // list of instance variables
3351
3352 self.__instance = instance;
3353 self.__namespace = 'tooltipster-sideTip-' + Math.round(Math.random() * 1000000);
3354 self.__previousState = 'closed';
3355 self.__options;
3356
3357 // initial formatting
3358 self.__optionsFormat();
3359
3360 self.__instance._on('state.' + self.__namespace, function(event) {
3361
3362 if (event.state == 'closed') {
3363 self.__close();
3364 } else if (event.state == 'appearing' && self.__previousState == 'closed') {
3365 self.__create();
3366 }
3367
3368 self.__previousState = event.state;
3369 });
3370
3371 // reformat every time the options are changed
3372 self.__instance._on('options.' + self.__namespace, function() {
3373 self.__optionsFormat();
3374 });
3375
3376 self.__instance._on('reposition.' + self.__namespace, function(e) {
3377 self.__reposition(e.event, e.helper);
3378 });
3379 },
3380
3381 /**
3382 * Called when the tooltip has closed
3383 *
3384 * @private
3385 */
3386 __close: function() {
3387
3388 // detach our content object first, so the next jQuery's remove()
3389 // call does not unbind its event handlers
3390 if (this.__instance.content() instanceof $) {
3391 this.__instance.content().detach();
3392 }
3393
3394 // remove the tooltip from the DOM
3395 this.__instance._$tooltip.remove();
3396 this.__instance._$tooltip = null;
3397 },
3398
3399 /**
3400 * Creates the HTML element of the tooltip.
3401 *
3402 * @private
3403 */
3404 __create: function() {
3405
3406 // note: we wrap with a .tooltipster-box div to be able to set a margin on it
3407 // (.tooltipster-base must not have one)
3408 var $html = $(
3409 '<div class="tooltipster-base tooltipster-sidetip">' +
3410 '<div class="tooltipster-box">' +
3411 '<div class="tooltipster-content"></div>' +
3412 '</div>' +
3413 '<div class="tooltipster-arrow">' +
3414 '<div class="tooltipster-arrow-uncropped">' +
3415 '<div class="tooltipster-arrow-border"></div>' +
3416 '<div class="tooltipster-arrow-background"></div>' +
3417 '</div>' +
3418 '</div>' +
3419 '</div>'
3420 );
3421
3422 // hide arrow if asked
3423 if (!this.__options.arrow) {
3424 $html
3425 .find('.tooltipster-box')
3426 .css('margin', 0)
3427 .end()
3428 .find('.tooltipster-arrow')
3429 .hide();
3430 }
3431
3432 // apply min/max width if asked
3433 if (this.__options.minWidth) {
3434 $html.css('min-width', this.__options.minWidth + 'px');
3435 }
3436 if (this.__options.maxWidth) {
3437 $html.css('max-width', this.__options.maxWidth + 'px');
3438 }
3439
3440 this.__instance._$tooltip = $html;
3441
3442 // tell the instance that the tooltip element has been created
3443 this.__instance._trigger('created');
3444 },
3445
3446 /**
3447 * Used when the plugin is to be unplugged
3448 *
3449 * @private
3450 */
3451 __destroy: function() {
3452 this.__instance._off('.' + self.__namespace);
3453 },
3454
3455 /**
3456 * (Re)compute this.__options from the options declared to the instance
3457 *
3458 * @private
3459 */
3460 __optionsFormat: function() {
3461
3462 var self = this;
3463
3464 // get the options
3465 self.__options = self.__instance._optionsExtract('tooltipster.sideTip', self.__defaults());
3466
3467 // for backward compatibility, deprecated in v4.0.0
3468 if (self.__options.position) {
3469 self.__options.side = self.__options.position;
3470 }
3471
3472 // options formatting
3473
3474 // format distance as a four-cell array if it ain't one yet and then make
3475 // it an object with top/bottom/left/right properties
3476 if (typeof self.__options.distance != 'object') {
3477 self.__options.distance = [self.__options.distance];
3478 }
3479 if (self.__options.distance.length < 4) {
3480 if (self.__options.distance[1] === undefined) self.__options.distance[1] = self.__options.distance[0];
3481 if (self.__options.distance[2] === undefined) self.__options.distance[2] = self.__options.distance[0];
3482 if (self.__options.distance[3] === undefined) self.__options.distance[3] = self.__options.distance[1];
3483 }
3484
3485 self.__options.distance = {
3486 top: self.__options.distance[0],
3487 right: self.__options.distance[1],
3488 bottom: self.__options.distance[2],
3489 left: self.__options.distance[3]
3490 };
3491
3492 // let's transform:
3493 // 'top' into ['top', 'bottom', 'right', 'left']
3494 // 'right' into ['right', 'left', 'top', 'bottom']
3495 // 'bottom' into ['bottom', 'top', 'right', 'left']
3496 // 'left' into ['left', 'right', 'top', 'bottom']
3497 if (typeof self.__options.side == 'string') {
3498
3499 var opposites = {
3500 'top': 'bottom',
3501 'right': 'left',
3502 'bottom': 'top',
3503 'left': 'right'
3504 };
3505
3506 self.__options.side = [self.__options.side, opposites[self.__options.side]];
3507
3508 if (self.__options.side[0] == 'left' || self.__options.side[0] == 'right') {
3509 self.__options.side.push('top', 'bottom');
3510 } else {
3511 self.__options.side.push('right', 'left');
3512 }
3513 }
3514
3515 // misc
3516 // disable the arrow in IE6 unless the arrow option was explicitly set to true
3517 if ($.tooltipster._env.IE === 6 &&
3518 self.__options.arrow !== true
3519 ) {
3520 self.__options.arrow = false;
3521 }
3522 },
3523
3524 /**
3525 * This method must compute and set the positioning properties of the
3526 * tooltip (left, top, width, height, etc.). It must also make sure the
3527 * tooltip is eventually appended to its parent (since the element may be
3528 * detached from the DOM at the moment the method is called).
3529 *
3530 * We'll evaluate positioning scenarios to find which side can contain the
3531 * tooltip in the best way. We'll consider things relatively to the window
3532 * (unless the user asks not to), then to the document (if need be, or if the
3533 * user explicitly requires the tests to run on the document). For each
3534 * scenario, measures are taken, allowing us to know how well the tooltip
3535 * is going to fit. After that, a sorting function will let us know what
3536 * the best scenario is (we also allow the user to choose his favorite
3537 * scenario by using an event).
3538 *
3539 * @param {object} helper An object that contains variables that plugin
3540 * creators may find useful (see below)
3541 * @param {object} helper.geo An object with many layout properties
3542 * about objects of interest (window, document, origin). This should help
3543 * plugin users compute the optimal position of the tooltip
3544 * @private
3545 */
3546 __reposition: function(event, helper) {
3547
3548 var self = this,
3549 finalResult,
3550 // to know where to put the tooltip, we need to know on which point
3551 // of the x or y axis we should center it. That coordinate is the target
3552 targets = self.__targetFind(helper),
3553 testResults = [];
3554
3555 // make sure the tooltip is detached while we make tests on a clone
3556 self.__instance._$tooltip.detach();
3557
3558 // we could actually provide the original element to the Ruler and
3559 // not a clone, but it just feels right to keep it out of the
3560 // machinery.
3561 var $clone = self.__instance._$tooltip.clone(),
3562 // start position tests session
3563 ruler = $.tooltipster._getRuler($clone),
3564 satisfied = false,
3565 animation = self.__instance.option('animation');
3566
3567 // an animation class could contain properties that distort the size
3568 if (animation) {
3569 $clone.removeClass('tooltipster-' + animation);
3570 }
3571
3572 // start evaluating scenarios
3573 $.each(['window', 'document'], function(i, container) {
3574
3575 var takeTest = null;
3576
3577 // let the user decide to keep on testing or not
3578 self.__instance._trigger({
3579 container: container,
3580 helper: helper,
3581 satisfied: satisfied,
3582 takeTest: function(bool) {
3583 takeTest = bool;
3584 },
3585 results: testResults,
3586 type: 'positionTest'
3587 });
3588
3589 if (takeTest == true ||
3590 (takeTest != false &&
3591 satisfied == false
3592 // skip the window scenarios if asked. If they are reintegrated by
3593 // the callback of the positionTest event, they will have to be
3594 // excluded using the callback of positionTested
3595 &&
3596 (container != 'window' || self.__options.viewportAware)
3597 )
3598 ) {
3599
3600 // for each allowed side
3601 for (var i = 0; i < self.__options.side.length; i++) {
3602
3603 var distance = {
3604 horizontal: 0,
3605 vertical: 0
3606 },
3607 side = self.__options.side[i];
3608
3609 if (side == 'top' || side == 'bottom') {
3610 distance.vertical = self.__options.distance[side];
3611 } else {
3612 distance.horizontal = self.__options.distance[side];
3613 }
3614
3615 // this may have an effect on the size of the tooltip if there are css
3616 // rules for the arrow or something else
3617 self.__sideChange($clone, side);
3618
3619 $.each(['natural', 'constrained'], function(i, mode) {
3620
3621 takeTest = null;
3622
3623 // emit an event on the instance
3624 self.__instance._trigger({
3625 container: container,
3626 event: event,
3627 helper: helper,
3628 mode: mode,
3629 results: testResults,
3630 satisfied: satisfied,
3631 side: side,
3632 takeTest: function(bool) {
3633 takeTest = bool;
3634 },
3635 type: 'positionTest'
3636 });
3637
3638 if (takeTest == true ||
3639 (takeTest != false &&
3640 satisfied == false
3641 )
3642 ) {
3643
3644 var testResult = {
3645 container: container,
3646 // we let the distance as an object here, it can make things a little easier
3647 // during the user's calculations at positionTest/positionTested
3648 distance: distance,
3649 // whether the tooltip can fit in the size of the viewport (does not mean
3650 // that we'll be able to make it initially entirely visible, see 'whole')
3651 fits: null,
3652 mode: mode,
3653 outerSize: null,
3654 side: side,
3655 size: null,
3656 target: targets[side],
3657 // check if the origin has enough surface on screen for the tooltip to
3658 // aim at it without overflowing the viewport (this is due to the thickness
3659 // of the arrow represented by the minIntersection length).
3660 // If not, the tooltip will have to be partly or entirely off screen in
3661 // order to stay docked to the origin. This value will stay null when the
3662 // container is the document, as it is not relevant
3663 whole: null
3664 };
3665
3666 // get the size of the tooltip with or without size constraints
3667 var rulerConfigured = (mode == 'natural') ?
3668 ruler.free() :
3669 ruler.constrain(
3670 helper.geo.available[container][side].width - distance.horizontal,
3671 helper.geo.available[container][side].height - distance.vertical
3672 ),
3673 rulerResults = rulerConfigured.measure();
3674
3675 testResult.size = rulerResults.size;
3676 testResult.outerSize = {
3677 height: rulerResults.size.height + distance.vertical,
3678 width: rulerResults.size.width + distance.horizontal
3679 };
3680
3681 if (mode == 'natural') {
3682
3683 if (helper.geo.available[container][side].width >= testResult.outerSize.width &&
3684 helper.geo.available[container][side].height >= testResult.outerSize.height
3685 ) {
3686 testResult.fits = true;
3687 } else {
3688 testResult.fits = false;
3689 }
3690 } else {
3691 testResult.fits = rulerResults.fits;
3692 }
3693
3694 if (container == 'window') {
3695
3696 if (!testResult.fits) {
3697 testResult.whole = false;
3698 } else {
3699 if (side == 'top' || side == 'bottom') {
3700
3701 testResult.whole = (
3702 helper.geo.origin.windowOffset.right >= self.__options.minIntersection &&
3703 helper.geo.window.size.width - helper.geo.origin.windowOffset.left >= self.__options.minIntersection
3704 );
3705 } else {
3706 testResult.whole = (
3707 helper.geo.origin.windowOffset.bottom >= self.__options.minIntersection &&
3708 helper.geo.window.size.height - helper.geo.origin.windowOffset.top >= self.__options.minIntersection
3709 );
3710 }
3711 }
3712 }
3713
3714 testResults.push(testResult);
3715
3716 // we don't need to compute more positions if we have one fully on screen
3717 if (testResult.whole) {
3718 satisfied = true;
3719 } else {
3720 // don't run the constrained test unless the natural width was greater
3721 // than the available width, otherwise it's pointless as we know it
3722 // wouldn't fit either
3723 if (testResult.mode == 'natural' &&
3724 (testResult.fits ||
3725 testResult.size.width <= helper.geo.available[container][side].width
3726 )
3727 ) {
3728 return false;
3729 }
3730 }
3731 }
3732 });
3733 }
3734 }
3735 });
3736
3737 // the user may eliminate the unwanted scenarios from testResults, but he's
3738 // not supposed to alter them at this point. functionPosition and the
3739 // position event serve that purpose.
3740 self.__instance._trigger({
3741 edit: function(r) {
3742 testResults = r;
3743 },
3744 event: event,
3745 helper: helper,
3746 results: testResults,
3747 type: 'positionTested'
3748 });
3749
3750 /**
3751 * Sort the scenarios to find the favorite one.
3752 *
3753 * The favorite scenario is when we can fully display the tooltip on screen,
3754 * even if it means that the middle of the tooltip is no longer centered on
3755 * the middle of the origin (when the origin is near the edge of the screen
3756 * or even partly off screen). We want the tooltip on the preferred side,
3757 * even if it means that we have to use a constrained size rather than a
3758 * natural one (as long as it fits). When the origin is off screen at the top
3759 * the tooltip will be positioned at the bottom (if allowed), if the origin
3760 * is off screen on the right, it will be positioned on the left, etc.
3761 * If there are no scenarios where the tooltip can fit on screen, or if the
3762 * user does not want the tooltip to fit on screen (viewportAware == false),
3763 * we fall back to the scenarios relative to the document.
3764 *
3765 * When the tooltip is bigger than the viewport in either dimension, we stop
3766 * looking at the window scenarios and consider the document scenarios only,
3767 * with the same logic to find on which side it would fit best.
3768 *
3769 * If the tooltip cannot fit the document on any side, we force it at the
3770 * bottom, so at least the user can scroll to see it.
3771 */
3772 testResults.sort(function(a, b) {
3773
3774 // best if it's whole (the tooltip fits and adapts to the viewport)
3775 if (a.whole && !b.whole) {
3776 return -1;
3777 } else if (!a.whole && b.whole) {
3778 return 1;
3779 } else if (a.whole && b.whole) {
3780
3781 var ai = self.__options.side.indexOf(a.side),
3782 bi = self.__options.side.indexOf(b.side);
3783
3784 // use the user's sides fallback array
3785 if (ai < bi) {
3786 return -1;
3787 } else if (ai > bi) {
3788 return 1;
3789 } else {
3790 // will be used if the user forced the tests to continue
3791 return a.mode == 'natural' ? -1 : 1;
3792 }
3793 } else {
3794
3795 // better if it fits
3796 if (a.fits && !b.fits) {
3797 return -1;
3798 } else if (!a.fits && b.fits) {
3799 return 1;
3800 } else if (a.fits && b.fits) {
3801
3802 var ai = self.__options.side.indexOf(a.side),
3803 bi = self.__options.side.indexOf(b.side);
3804
3805 // use the user's sides fallback array
3806 if (ai < bi) {
3807 return -1;
3808 } else if (ai > bi) {
3809 return 1;
3810 } else {
3811 // will be used if the user forced the tests to continue
3812 return a.mode == 'natural' ? -1 : 1;
3813 }
3814 } else {
3815
3816 // if everything failed, this will give a preference to the case where
3817 // the tooltip overflows the document at the bottom
3818 if (a.container == 'document' &&
3819 a.side == 'bottom' &&
3820 a.mode == 'natural'
3821 ) {
3822 return -1;
3823 } else {
3824 return 1;
3825 }
3826 }
3827 }
3828 });
3829
3830 finalResult = testResults[0];
3831
3832
3833 // now let's find the coordinates of the tooltip relatively to the window
3834 finalResult.coord = {};
3835
3836 switch (finalResult.side) {
3837
3838 case 'left':
3839 case 'right':
3840 finalResult.coord.top = Math.floor(finalResult.target - finalResult.size.height / 2);
3841 break;
3842
3843 case 'bottom':
3844 case 'top':
3845 finalResult.coord.left = Math.floor(finalResult.target - finalResult.size.width / 2);
3846 break;
3847 }
3848
3849 switch (finalResult.side) {
3850
3851 case 'left':
3852 finalResult.coord.left = helper.geo.origin.windowOffset.left - finalResult.outerSize.width;
3853 break;
3854
3855 case 'right':
3856 finalResult.coord.left = helper.geo.origin.windowOffset.right + finalResult.distance.horizontal;
3857 break;
3858
3859 case 'top':
3860 finalResult.coord.top = helper.geo.origin.windowOffset.top - finalResult.outerSize.height;
3861 break;
3862
3863 case 'bottom':
3864 finalResult.coord.top = helper.geo.origin.windowOffset.bottom + finalResult.distance.vertical;
3865 break;
3866 }
3867
3868 // if the tooltip can potentially be contained within the viewport dimensions
3869 // and that we are asked to make it fit on screen
3870 if (finalResult.container == 'window') {
3871
3872 // if the tooltip overflows the viewport, we'll move it accordingly (then it will
3873 // not be centered on the middle of the origin anymore). We only move horizontally
3874 // for top and bottom tooltips and vice versa.
3875 if (finalResult.side == 'top' || finalResult.side == 'bottom') {
3876
3877 // if there is an overflow on the left
3878 if (finalResult.coord.left < 0) {
3879
3880 // prevent the overflow unless the origin itself gets off screen (minus the
3881 // margin needed to keep the arrow pointing at the target)
3882 if (helper.geo.origin.windowOffset.right - this.__options.minIntersection >= 0) {
3883 finalResult.coord.left = 0;
3884 } else {
3885 finalResult.coord.left = helper.geo.origin.windowOffset.right - this.__options.minIntersection - 1;
3886 }
3887 }
3888 // or an overflow on the right
3889 else if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
3890
3891 if (helper.geo.origin.windowOffset.left + this.__options.minIntersection <= helper.geo.window.size.width) {
3892 finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
3893 } else {
3894 finalResult.coord.left = helper.geo.origin.windowOffset.left + this.__options.minIntersection + 1 - finalResult.size.width;
3895 }
3896 }
3897 } else {
3898
3899 // overflow at the top
3900 if (finalResult.coord.top < 0) {
3901
3902 if (helper.geo.origin.windowOffset.bottom - this.__options.minIntersection >= 0) {
3903 finalResult.coord.top = 0;
3904 } else {
3905 finalResult.coord.top = helper.geo.origin.windowOffset.bottom - this.__options.minIntersection - 1;
3906 }
3907 }
3908 // or at the bottom
3909 else if (finalResult.coord.top > helper.geo.window.size.height - finalResult.size.height) {
3910
3911 if (helper.geo.origin.windowOffset.top + this.__options.minIntersection <= helper.geo.window.size.height) {
3912 finalResult.coord.top = helper.geo.window.size.height - finalResult.size.height;
3913 } else {
3914 finalResult.coord.top = helper.geo.origin.windowOffset.top + this.__options.minIntersection + 1 - finalResult.size.height;
3915 }
3916 }
3917 }
3918 } else {
3919
3920 // there might be overflow here too but it's easier to handle. If there has
3921 // to be an overflow, we'll make sure it's on the right side of the screen
3922 // (because the browser will extend the document size if there is an overflow
3923 // on the right, but not on the left). The sort function above has already
3924 // made sure that a bottom document overflow is preferred to a top overflow,
3925 // so we don't have to care about it.
3926
3927 // if there is an overflow on the right
3928 if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
3929
3930 // this may actually create on overflow on the left but we'll fix it in a sec
3931 finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
3932 }
3933
3934 // if there is an overflow on the left
3935 if (finalResult.coord.left < 0) {
3936
3937 // don't care if it overflows the right after that, we made our best
3938 finalResult.coord.left = 0;
3939 }
3940 }
3941
3942
3943 // submit the positioning proposal to the user function which may choose to change
3944 // the side, size and/or the coordinates
3945
3946 // first, set the rules that corresponds to the proposed side: it may change
3947 // the size of the tooltip, and the custom functionPosition may want to detect the
3948 // size of something before making a decision. So let's make things easier for the
3949 // implementor
3950 self.__sideChange($clone, finalResult.side);
3951
3952 // add some variables to the helper
3953 helper.tooltipClone = $clone[0];
3954 helper.tooltipParent = self.__instance.option('parent').parent[0];
3955 // move informative values to the helper
3956 helper.mode = finalResult.mode;
3957 helper.whole = finalResult.whole;
3958 // add some variables to the helper for the functionPosition callback (these
3959 // will also be added to the event fired by self.__instance._trigger but that's
3960 // ok, we're just being consistent)
3961 helper.origin = self.__instance._$origin[0];
3962 helper.tooltip = self.__instance._$tooltip[0];
3963
3964 // leave only the actionable values in there for functionPosition
3965 delete finalResult.container;
3966 delete finalResult.fits;
3967 delete finalResult.mode;
3968 delete finalResult.outerSize;
3969 delete finalResult.whole;
3970
3971 // keep only the distance on the relevant side, for clarity
3972 finalResult.distance = finalResult.distance.horizontal || finalResult.distance.vertical;
3973
3974 // beginners may not be comfortable with the concept of editing the object
3975 // passed by reference, so we provide an edit function and pass a clone
3976 var finalResultClone = $.extend(true, {}, finalResult);
3977
3978 // emit an event on the instance
3979 self.__instance._trigger({
3980 edit: function(result) {
3981 finalResult = result;
3982 },
3983 event: event,
3984 helper: helper,
3985 position: finalResultClone,
3986 type: 'position'
3987 });
3988
3989 if (self.__options.functionPosition) {
3990
3991 var result = self.__options.functionPosition.call(self, self.__instance, helper, finalResultClone);
3992
3993 if (result) finalResult = result;
3994 }
3995
3996 // end the positioning tests session (the user might have had a
3997 // use for it during the position event, now it's over)
3998 ruler.destroy();
3999
4000 // compute the position of the target relatively to the tooltip root
4001 // element so we can place the arrow and make the needed adjustments
4002 var arrowCoord,
4003 maxVal;
4004
4005 if (finalResult.side == 'top' || finalResult.side == 'bottom') {
4006
4007 arrowCoord = {
4008 prop: 'left',
4009 val: finalResult.target - finalResult.coord.left
4010 };
4011 maxVal = finalResult.size.width - this.__options.minIntersection;
4012 } else {
4013
4014 arrowCoord = {
4015 prop: 'top',
4016 val: finalResult.target - finalResult.coord.top
4017 };
4018 maxVal = finalResult.size.height - this.__options.minIntersection;
4019 }
4020
4021 // cannot lie beyond the boundaries of the tooltip, minus the
4022 // arrow margin
4023 if (arrowCoord.val < this.__options.minIntersection) {
4024 arrowCoord.val = this.__options.minIntersection;
4025 } else if (arrowCoord.val > maxVal) {
4026 arrowCoord.val = maxVal;
4027 }
4028
4029 var originParentOffset;
4030
4031 // let's convert the window-relative coordinates into coordinates relative to the
4032 // future positioned parent that the tooltip will be appended to
4033 if (helper.geo.origin.fixedLineage) {
4034
4035 // same as windowOffset when the position is fixed
4036 originParentOffset = helper.geo.origin.windowOffset;
4037 } else {
4038
4039 // this assumes that the parent of the tooltip is located at
4040 // (0, 0) in the document, typically like when the parent is
4041 // <body>.
4042 // If we ever allow other types of parent, .tooltipster-ruler
4043 // will have to be appended to the parent to inherit css style
4044 // values that affect the display of the text and such.
4045 originParentOffset = {
4046 left: helper.geo.origin.windowOffset.left + helper.geo.window.scroll.left,
4047 top: helper.geo.origin.windowOffset.top + helper.geo.window.scroll.top
4048 };
4049 }
4050
4051 finalResult.coord = {
4052 left: originParentOffset.left + (finalResult.coord.left - helper.geo.origin.windowOffset.left),
4053 top: originParentOffset.top + (finalResult.coord.top - helper.geo.origin.windowOffset.top)
4054 };
4055
4056 // set position values on the original tooltip element
4057
4058 self.__sideChange(self.__instance._$tooltip, finalResult.side);
4059
4060 if (helper.geo.origin.fixedLineage) {
4061 self.__instance._$tooltip
4062 .css('position', 'fixed');
4063 } else {
4064 // CSS default
4065 self.__instance._$tooltip
4066 .css('position', '');
4067 }
4068
4069 self.__instance._$tooltip
4070 .css({
4071 left: finalResult.coord.left,
4072 top: finalResult.coord.top,
4073 // we need to set a size even if the tooltip is in its natural size
4074 // because when the tooltip is positioned beyond the width of the body
4075 // (which is by default the width of the window; it will happen when
4076 // you scroll the window horizontally to get to the origin), its text
4077 // content will otherwise break lines at each word to keep up with the
4078 // body overflow strategy.
4079 height: finalResult.size.height,
4080 width: finalResult.size.width
4081 })
4082 .find('.tooltipster-arrow')
4083 .css({
4084 'left': '',
4085 'top': ''
4086 })
4087 .css(arrowCoord.prop, arrowCoord.val);
4088
4089 // append the tooltip HTML element to its parent
4090 self.__instance._$tooltip.appendTo(self.__instance.option('parent'));
4091
4092 self.__instance._trigger({
4093 type: 'repositioned',
4094 event: event,
4095 position: finalResult
4096 });
4097 },
4098
4099 /**
4100 * Make whatever modifications are needed when the side is changed. This has
4101 * been made an independant method for easy inheritance in custom plugins based
4102 * on this default plugin.
4103 *
4104 * @param {object} $obj
4105 * @param {string} side
4106 * @private
4107 */
4108 __sideChange: function($obj, side) {
4109
4110 $obj
4111 .removeClass('tooltipster-bottom')
4112 .removeClass('tooltipster-left')
4113 .removeClass('tooltipster-right')
4114 .removeClass('tooltipster-top')
4115 .addClass('tooltipster-' + side);
4116 },
4117
4118 /**
4119 * Returns the target that the tooltip should aim at for a given side.
4120 * The calculated value is a distance from the edge of the window
4121 * (left edge for top/bottom sides, top edge for left/right side). The
4122 * tooltip will be centered on that position and the arrow will be
4123 * positioned there (as much as possible).
4124 *
4125 * @param {object} helper
4126 * @return {integer}
4127 * @private
4128 */
4129 __targetFind: function(helper) {
4130
4131 var target = {},
4132 rects = this.__instance._$origin[0].getClientRects();
4133
4134 // these lines fix a Chrome bug (issue #491)
4135 if (rects.length > 1) {
4136 var opacity = this.__instance._$origin.css('opacity');
4137 if (opacity == 1) {
4138 this.__instance._$origin.css('opacity', 0.99);
4139 rects = this.__instance._$origin[0].getClientRects();
4140 this.__instance._$origin.css('opacity', 1);
4141 }
4142 }
4143
4144 // by default, the target will be the middle of the origin
4145 if (rects.length < 2) {
4146
4147 target.top = Math.floor(helper.geo.origin.windowOffset.left + (helper.geo.origin.size.width / 2));
4148 target.bottom = target.top;
4149
4150 target.left = Math.floor(helper.geo.origin.windowOffset.top + (helper.geo.origin.size.height / 2));
4151 target.right = target.left;
4152 }
4153 // if multiple client rects exist, the element may be text split
4154 // up into multiple lines and the middle of the origin may not be
4155 // best option anymore. We need to choose the best target client rect
4156 else {
4157
4158 // top: the first
4159 var targetRect = rects[0];
4160 target.top = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
4161
4162 // right: the middle line, rounded down in case there is an even
4163 // number of lines (looks more centered => check out the
4164 // demo with 4 split lines)
4165 if (rects.length > 2) {
4166 targetRect = rects[Math.ceil(rects.length / 2) - 1];
4167 } else {
4168 targetRect = rects[0];
4169 }
4170 target.right = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
4171
4172 // bottom: the last
4173 targetRect = rects[rects.length - 1];
4174 target.bottom = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
4175
4176 // left: the middle line, rounded up
4177 if (rects.length > 2) {
4178 targetRect = rects[Math.ceil((rects.length + 1) / 2) - 1];
4179 } else {
4180 targetRect = rects[rects.length - 1];
4181 }
4182
4183 target.left = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
4184 }
4185
4186 return target;
4187 }
4188 }
4189 });
4190
4191 }));
4192 });
4193 jQuery(document).ready(function($) {
4194 var fchunker_upload = {
4195 fchunker: function(config) {
4196 $.extend(config);
4197 if ($.upId && $.upUrl) {
4198 $.domHtml = $('#' + $.upId).html();
4199 $.upInputId = $.upId + '_input';
4200 }
4201 },
4202 fchunker_limitFileSize: function(file, limitSize) {
4203 var arr = ["KB", "MB", "GB"],
4204 limit = limitSize.toUpperCase(),
4205 limitNum = 0;
4206 for (var i = 0; i < arr.length; i++) {
4207 var leval = limit.indexOf(arr[i]);
4208 if (leval > -1) {
4209 limitNum = parseInt(limit.substr(0, leval)) * Math.pow(1024, (i + 1));
4210 break;
4211 }
4212 }
4213 if (file.size > limitNum) {
4214 return false;
4215 }
4216 return true;
4217 },
4218 upErrorMsg: function(err) {
4219 $.upError = err;
4220 },
4221 upStop: function(err) {
4222 $.upError = err;
4223 },
4224 upStatus: function() {
4225 if ($.upError) {
4226 if (typeof $.upStop == 'function') {
4227 $.upStop($.upError);
4228 }
4229 return false;
4230 }
4231 return true;
4232 },
4233 fchunker_getPercent: function(num, total) {
4234 num = parseInt(num);
4235 total = parseInt(total);
4236 if (isNaN(num) || isNaN(total)) {
4237 return "-";
4238 }
4239
4240 let sum = total <= 0 ? 0 : (Math.round(num / total * 100));
4241 return sum;
4242 },
4243 fchunker_upload: function(x, xfile) {
4244
4245 $.upError = '';
4246 $.tempFile = $('#' + $.upInputId)[0].files[0];
4247 if (x == 'file') $.tempFile = xfile;
4248 var file = $.tempFile;
4249 if (!file) {
4250 return false;
4251 }
4252 if (typeof $.upStart == 'function') {
4253 $.upStart();
4254 }
4255 var filename = file.name,
4256 index1 = filename.lastIndexOf("."),
4257 index2 = filename.length,
4258 suffix = filename.substring(index1 + 1, index2);
4259 if ($.upType) {
4260 uptype = $.upType.split(",");
4261 if ($.inArray(suffix, uptype) == -1) {
4262 $.upError = 'Type error: Error-' + suffix;
4263 }
4264 }
4265 // if ($.upMaxSize) {
4266 // if (!$.fchunker_limitFileSize(file, $.upMaxSize + 'MB')) {
4267 // $.upError = 'Error';
4268 // }
4269 // }
4270 if ($.upStatus() == false) {
4271 return false;
4272 }
4273 $.taskStart = +new Date();
4274 setTimeout("jQuery.fchunker_upload_core()", "100");
4275 },
4276 fchunker_upload_core: function() {
4277 var file = $.tempFile;
4278 if (!file) {
4279 return false;
4280 }
4281 if (!$.upShardSize) {
4282 $.upShardSize = 2;
4283 }
4284
4285 $.upShardSize = $.upShardSize * 0.8;
4286
4287 var filename = file.name,
4288 size = file.size,
4289 index1 = filename.lastIndexOf("."),
4290 index2 = filename.length,
4291 suffix = filename.substring(index1 + 1, index2),
4292 shardSize = $.upShardSize * 1024 * 1024,
4293 succeed = 0,
4294 shardCount = Math.ceil(size / shardSize);
4295
4296 var re = [];
4297 var start, end = 0;
4298 for (var i = 0; i < shardCount; ++i) {
4299 re[i] = [];
4300 start = i * shardSize,
4301 end = Math.min(size, start + shardSize);
4302 re[i]["file_data"] = file.slice(start, end);
4303 re[i]["file_name"] = filename;
4304 re[i]["file_size"] = size;
4305 }
4306 const URL = $.upUrl;
4307 var i2 = 0,
4308 i3 = 1,
4309 fcs = Array();
4310 var xhr = new XMLHttpRequest();
4311
4312 function ajaxStack(stack) {
4313 if ($.upStatus() == false) {
4314 return;
4315 }
4316 var form = new FormData();
4317 if (stack[i2]) {
4318 fcs = stack[i2];
4319 form.append("file_data", fcs['file_data']);
4320 form.append("file_name", fcs['file_name']);
4321 form.append("file_size", fcs['file_size']);
4322 form.append("file_total", shardCount);
4323 form.append("file_index", i3);
4324 form.append("taskStart", $.taskStart);
4325 form.append("action", "backup_migration");
4326 form.append("token", "bmi");
4327 form.append("f", "upload-backup");
4328 form.append("nonce", $.bmiNonce);
4329 xhr.open('POST', URL, true);
4330 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
4331 xhr.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest");
4332 xhr.onload = function() {
4333 ajaxStack(stack);
4334 }
4335 xhr.onreadystatechange = function() {
4336 if ($.upStatus() == false) {
4337 return;
4338 }
4339 if (xhr.readyState == 4 && xhr.status == 200) {
4340 var data = xhr.responseText ? eval('(' + xhr.responseText + ')') : '';
4341 ++succeed;
4342 var cent = $.fchunker_getPercent(succeed, shardCount);
4343 if (typeof $.upEvent == 'function') {
4344 $.upEvent(cent);
4345 }
4346 if (cent == 100) {
4347 setTimeout(function() {
4348 if (typeof $.upCallBack == 'function') {
4349 $.upCallBack(data);
4350 }
4351 }, 500);
4352 } else {
4353 if (typeof $.upCallBack == 'function') {
4354 $.upCallBack(data);
4355 }
4356 }
4357 }
4358 }
4359 xhr.send(form);
4360 i2++;
4361 i3++;
4362 form.delete('file_data');
4363 form.delete('file_name');
4364 form.delete('file_size');
4365 form.delete('file_total');
4366 form.delete('taskStart');
4367 form.delete('file_index');
4368 form.delete('action');
4369 form.delete('token');
4370 form.delete('nonce');
4371 form.delete('f');
4372 }
4373 }
4374 ajaxStack(re);
4375 re = null,
4376 file = null;
4377 }
4378 };
4379
4380 $.extend(fchunker_upload);
4381 });
4382 // Preloader
4383 jQuery(window).on('load', function() {
4384 if (pagenow !== 'toplevel_page_backup-migration' && pagenow !== 'toplevel_page_backup-migration-network') return;
4385 setTimeout(function() {
4386 jQuery('#bmi').css({
4387 opacity: 0
4388 });
4389 jQuery('#bmi-preload').css({
4390 opacity: 1
4391 });
4392 jQuery('#bmi-preload').animate({
4393 opacity: 0
4394 }, 150, function() {
4395 jQuery('#bmi-preload').remove();
4396 jQuery('#bmi').css({
4397 display: 'block'
4398 });
4399 jQuery.bmi.dropdowns.init();
4400 setTimeout(function() {
4401 jQuery('#bmi').animate({ opacity: 1 }, 350, function () {
4402 jQuery(window).trigger("bmi-preload-collapsed");
4403 });
4404 jQuery('#bmi_carrousel').show(200);
4405 }, 100);
4406 });
4407 }, 50);
4408 });
4409
4410 // Plugin for jQuery - Handler of BMI
4411 jQuery(document).ready(function($) {
4412 let collapsing = false;
4413 let ongoing_latest_token = false;
4414 let currentUploadType = null;
4415 wakeLock = null;
4416
4417
4418 function escapeHtml(t){return(t=""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}
4419
4420 $.extend({
4421 bmi: {
4422 requestWakeLock: async () => {
4423 try {
4424 if ( wakeLock == null && 'wakeLock' in navigator) {
4425 wakeLock = await navigator.wakeLock.request('screen');
4426
4427 // Listen for release (e.g., if user switches tabs)
4428 wakeLock.addEventListener('release', () => {
4429 wakeLock = null;
4430 });
4431 }
4432 } catch (err) {
4433 console.error(`${err.name}, ${err.message}`);
4434 }
4435 },
4436
4437 releaseWakeLock: () => {
4438 if (typeof wakeLock !== 'undefined' && wakeLock !== null) {
4439 wakeLock.release();
4440 wakeLock = null;
4441 }
4442 },
4443
4444 // STATIC: Copy to clipboard
4445 clipboard: function(str, text = false) {
4446
4447 try {
4448
4449 const el = document.createElement('textarea');
4450 el.value = str;
4451 el.setAttribute('readonly', '');
4452 el.style.position = 'absolute';
4453 el.style.left = '-9999px';
4454 document.body.appendChild(el);
4455 el.select();
4456 document.execCommand('copy');
4457 document.body.removeChild(el);
4458
4459 let def = $('#bmi-success-copy').text();
4460 if (text != false) def = text;
4461 $.bmi.alert('success', def, 3000);
4462
4463 return true;
4464
4465 } catch (e) {
4466
4467 console.log(e);
4468 $.bmi.alert('warning', $('#bmi-failed-copy').text(), 3000);
4469 return false;
4470
4471 }
4472
4473 },
4474
4475 // Alert
4476 alert: function(type = 'default', msg = '---', timeout = 7000) {
4477
4478 if ($('.bmi-notification-box').length <= 0)
4479 $('body').find('#bmi').prepend($('<div class="bmi-notification-box"></div>'));
4480
4481 if (type == 'default') type = '';
4482 else if (type == 'success') type = ' is-success';
4483 else if (type == 'warning') type = ' is-warning';
4484 else if (type == 'error') type = ' is-danger';
4485 else if (type == 'info') type = ' is-info';
4486 else type = ' is-info';
4487 let elid = Math.floor(Math.random() * Math.floor(64000));
4488
4489 let $html = `<div style="display: none;" id="ntf-${elid}" class="bmi-notification${type}">
4490 <button class="bmi-times-button" onclick="jQuery.bmi.hideAlert(this)">&times;</button>
4491 <div class="bmi-cf">
4492 <div class="bmi-left bmi-alert-icon"><div class="bmi-icon-bg"></div></div>
4493 <div class="bmi-left bmi-alert-msg-title">
4494 <div class="bmi-title${type}">Backup Migration</div>
4495 <div>${msg}</div>
4496 </div>
4497 </div>
4498 </div>`;
4499
4500
4501 $('.bmi-notification-box').prepend($html);
4502 let ntf = $(`#ntf-${elid}`);
4503 ntf.css({
4504 opacity: 0,
4505 display: 'block'
4506 });
4507 let width = ntf.outerWidth(),
4508 height = ntf.outerHeight();
4509
4510 ntf.css({
4511 right: '-35vw',
4512 'font-size': '0px',
4513 width: 0,
4514 padding: 0,
4515 opacity: '0'
4516 });
4517 ntf.animate({
4518 right: '15px',
4519 width: width,
4520 padding: '1rem 2rem 1rem 1.5rem',
4521 opacity: '1'
4522 }, {
4523 duration: 200,
4524 queue: false
4525 });
4526 ntf.animate({
4527 'font-size': '16px'
4528 }, {
4529 duration: 300,
4530 queue: false
4531 });
4532
4533 setTimeout(() => {
4534 $(`#ntf-${elid}`).css({
4535 width: ''
4536 });
4537 }, 250);
4538
4539 setTimeout(() => {
4540 ntf.animate({
4541 'font-size': '0px'
4542 }, {
4543 duration: 200,
4544 queue: false
4545 });
4546 ntf.animate({
4547 right: '-35vw',
4548 height: 0,
4549 width: 0,
4550 margin: 0,
4551 padding: 0,
4552 opacity: '0'
4553 }, 300, function() {
4554 ntf.remove();
4555 });
4556 }, timeout);
4557
4558 },
4559
4560 // Response message
4561 _msg: function(res) {
4562
4563 if (res.status != 'msg') return;
4564
4565 if (typeof res.level == 'undefined') res.level = 'info';
4566 $.bmi.alert(res.level, res.why, 3000);
4567
4568 console.log('Backup-migration: ', '[' + res.level.toUpperCase() + ']', res.why);
4569
4570 },
4571
4572 // Hide alert
4573 hideAlert: function(self) {
4574
4575 let ntf = $(self).parents('.bmi-notification');
4576 ntf.animate({
4577 'font-size': '0px'
4578 }, {
4579 duration: 200,
4580 queue: false
4581 });
4582 ntf.animate({
4583 right: '-35vw',
4584 height: 0,
4585 width: 0,
4586 margin: 0,
4587 padding: 0,
4588 opacity: '0'
4589 }, 300, function() {
4590 ntf.remove();
4591 });
4592
4593 },
4594
4595 // OBJECT: Modal
4596 modal: function(id = false) {
4597
4598 let mod = false;
4599 if (id != false) mod = document.getElementById(id);
4600
4601 return {
4602 clearModal: function() {
4603
4604 mod.querySelectorAll('.customselect').forEach(function(dropdown) {
4605 dropdown.classList.remove('active');
4606 });
4607
4608 mod.querySelectorAll('input[type="text"]').forEach(function(input) {
4609 input.value = '';
4610 input.setAttribute('value', '');
4611 });
4612
4613 },
4614 open: function(cb = function() {}) {
4615
4616 mod.classList.add('before-open');
4617 setTimeout(function() {
4618 mod.classList.add('open');
4619 $('html')[0].style.overflowY = 'hidden';
4620 setTimeout(cb, 410);
4621 }, 10);
4622
4623 },
4624 close: function(cb = function() {}) {
4625
4626 if (mod.offsetWidth > 0 && mod.offsetHeight > 0) {
4627 mod.classList.add('before-close');
4628 setTimeout(function() {
4629 mod.classList.add('closed');
4630 setTimeout(function() {
4631 mod.classList.remove('before-open');
4632 mod.classList.remove('open');
4633 mod.classList.remove('before-close');
4634 mod.classList.remove('closed');
4635 $.bmi.modal(mod.id).clearModal();
4636 cb();
4637 }, 410);
4638 }, 10);
4639 } else {
4640 mod.classList.remove('before-open');
4641 mod.classList.remove('open');
4642 mod.classList.remove('before-close');
4643 mod.classList.remove('closed');
4644 cb();
4645 }
4646
4647 $('html')[0].style.overflowY = 'auto';
4648
4649 },
4650 closeAll: function() {
4651
4652 let modals = document.querySelectorAll('.modal');
4653 modals.forEach(function(mod) {
4654
4655 $.bmi.modal(mod.id).close();
4656
4657 });
4658
4659 $('html')[0].style.overflowY = 'auto';
4660
4661 },
4662 setParent: function(parentId) {
4663
4664 mod.setAttribute('data-parent-id',parentId);
4665
4666 },
4667 getParent: function() {
4668
4669 return mod.getAttribute('data-parent-id');
4670 }
4671 }
4672
4673 },
4674
4675 // PROMISE: Return JSON (from string) or FAIL
4676 json: function(str) {
4677
4678 let originalString = str;
4679 return new Promise(function(resolve, reject) {
4680
4681 try {
4682
4683 let json = JSON.parse(str);
4684 return resolve(json);
4685
4686 } catch (e) {
4687
4688 if (typeof str === 'string') {
4689
4690 let reversed = $.bmi.reverse(str);
4691 let lastcorrect = reversed.indexOf('}');
4692 if (lastcorrect == 0) lastcorrect = str.length;
4693 else lastcorrect = -lastcorrect;
4694
4695 str = str.slice(str.indexOf('{'), lastcorrect);
4696
4697 try {
4698
4699 let json = JSON.parse(str);
4700 return resolve(json);
4701
4702 } catch (e) {
4703
4704 return resolve(originalString);
4705
4706 }
4707
4708 } else return reject(false);
4709
4710 }
4711
4712 });
4713
4714 },
4715
4716 // STATIC: Returns reversed string
4717 reverse: function(str) {
4718
4719 if (typeof str === 'string') {
4720
4721 return (str === '') ? '' : $.bmi.reverse(str.substr(1)) + str.charAt(0);
4722
4723 } else {
4724
4725 return str;
4726
4727 }
4728
4729 },
4730
4731 // AJAX: Logger of JSON
4732 logJsonError: function(errorLogObj, func) {
4733
4734 data = {};
4735 data.action = 'backup_migration';
4736 data.token = 'bmi';
4737 data.nonce = bmiVariables.nonce;
4738 data.f = 'front-end-ajax-error';
4739 data.call = func;
4740 data.error = errorLogObj;
4741
4742 $.post(ajaxurl, data).done((res) => {
4743
4744 $.bmi.json(res).then(function(res) {
4745
4746 return;
4747
4748 }).catch(function(error) {
4749
4750 console.log(error);
4751
4752 });
4753
4754 }).fail((error) => {
4755
4756 console.error(error);
4757
4758 });
4759
4760 },
4761
4762 objectToFormData: (obj, form = null, namespace = '') => {
4763 const formData = form || new FormData();
4764 const isArr = Array.isArray(obj);
4765
4766 for (const key in obj) {
4767 if (!obj.hasOwnProperty(key) || obj[key] === null || obj[key] === undefined) {
4768 continue;
4769 }
4770
4771 const formKey = namespace ? (isArr ? `${namespace}[]` : `${namespace}[${key}]`) : key;
4772
4773 if (typeof obj[key] === 'string') {
4774 formData.append(formKey, obj[key]);
4775 } else if (obj[key] instanceof Date) {
4776 formData.append(formKey, obj[key].toISOString());
4777 } else if (obj[key] instanceof File) {
4778 formData.append(formKey, obj[key]);
4779 } else if (typeof obj[key] !== 'object') {
4780 formData.append(formKey, obj[key].toString());
4781 } else if (Array.isArray(obj[key])) {
4782 obj[key].forEach((item, index) => {
4783 const arrayKey = `${formKey}[${index}]`;
4784 if (typeof item === 'string')
4785 formData.append(arrayKey, item);
4786
4787 else
4788 jQuery.bmi.objectToFormData(item, formData, arrayKey);
4789 });
4790 } else {
4791 jQuery.bmi.objectToFormData(obj[key], formData, formKey);
4792 }
4793 }
4794
4795 return formData;
4796 },
4797
4798 // PROMISE: BMI POST Requests
4799 ajax: function(func, data = {}) {
4800
4801 return new Promise(async function(resolve, reject) {
4802
4803 function _error(error) {
4804
4805 let ajaxErrorObj = {};
4806
4807 console.log('------- BACKUP MIGRATION ERROR START -------');
4808 console.log('The error:', error);
4809 console.log('Call: ', func);
4810 ajaxErrorObj['call'] = func;
4811
4812 if (typeof error == 'object') {
4813 for (let i = 0; i < error.length; ++i) {
4814 if (typeof error[i] == 'object') {
4815 if (typeof error[i]['message'] != 'undefined') {
4816 console.log(i, error[i]['message']);
4817 ajaxErrorObj[`${i}_x`] = error[i]['message'];
4818 }
4819
4820 for (let k in error[i]) {
4821 if (typeof error[i][k] != 'function') {
4822 if (typeof error[i][k] == 'string' && error[i][k].length > 2000) {
4823 ajaxErrorObj[`${i}_${k}`] = error[i][k].slice(0, 2000);
4824 console.log(i, k);
4825 console.warn(error[i][k]);
4826 } else {
4827 console.log(i, k, error[i][k]);
4828 ajaxErrorObj[`${i}_${k}`] = error[i][k];
4829 }
4830 }
4831 }
4832 } else {
4833 console.log(i, error[i]);
4834 ajaxErrorObj[`${i}_x`] = error[i];
4835 }
4836 }
4837 } else {
4838 console.log(error);
4839
4840 if (typeof error == 'string' && error.length > 2000) {
4841 ajaxErrorObj[`single_error_txt`] = error.slice(0, 2000);
4842 } else {
4843 ajaxErrorObj[`single_error_txt`] = error;
4844 }
4845 }
4846 console.log('-------- BACKUP MIGRATION ERROR END --------\n\n');
4847
4848 $.bmi.logJsonError(ajaxErrorObj, func);
4849
4850 reject(error);
4851
4852 }
4853
4854 data.action = 'backup_migration';
4855 data.token = 'bmi';
4856 data.nonce = bmiVariables.nonce;
4857 data.f = func;
4858
4859 try {
4860 let res = await fetch(ajaxurl + '?cache=false', {
4861 "body": new URLSearchParams($.bmi.objectToFormData(data)).toString(),
4862 "cache": "default",
4863 "credentials": "include",
4864 "headers": {
4865 "Accept": "*/*",
4866 "Accept-Language": navigator.language + ";q=0.9",
4867 "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
4868 "User-Agent": navigator.userAgent,
4869 "X-Requested-With": "XMLHttpRequest"
4870 },
4871 "method": "POST",
4872 "mode": "cors",
4873 "redirect": "follow",
4874 "referrer": window.location.href,
4875 "referrerPolicy": "strict-origin-when-cross-origin"
4876 });
4877
4878 let response = null;
4879 if (res.ok === true) {
4880
4881 response = await res.text();
4882 $.bmi.json(response).then(function(parsedResponse) {
4883
4884 resolve(parsedResponse);
4885
4886 }).catch(function(error) {
4887
4888 console.error(error);
4889 _error(['json', error, res, func, data, (response || false)]);
4890
4891 });
4892
4893 } else {
4894
4895 response = await res.text().catch(() => '');
4896 throw new Error('Response is not OK (' + res.status + '): ' + (response || 'No response body'));
4897
4898 }
4899
4900 } catch (error) {
4901
4902 _error([error, func, data]);
4903
4904 }
4905
4906 // $.post(ajaxurl + '?cache=false', data).done((res) => {
4907
4908 // $.bmi.json(res).then(function(res) {
4909
4910 // resolve(res);
4911
4912 // }).catch(function(error) {
4913
4914 // console.error(error);
4915 // _error(['json', error, res, func, data, (res.responseText || false)]);
4916
4917 // });
4918
4919 // }).fail((error) => {
4920
4921 // _error([error, func, data]);
4922
4923 // });
4924
4925 });
4926
4927 },
4928
4929 // STATIC: Tooltips
4930 tooltips: {
4931
4932 init: function() {
4933
4934 function tooltipReposition() {
4935 setTimeout(() => {
4936 let instances = $.tooltipster.instances();
4937 for (instance in instances)
4938 instances[instance].reposition();
4939 }, 10);
4940 }
4941
4942 let settings = {
4943 delay: 200,
4944 debug: false,
4945 delayTouch: [100, 200],
4946 interactive: false,
4947 distance: 0,
4948 side: 'top',
4949 contentAsHTML: false,
4950 maxWidth: 460,
4951 triggerOpen: {
4952 mouseenter: true,
4953 touchstart: true
4954 },
4955 triggerClose: {
4956 mouseleave: true,
4957 click: true,
4958 tap: true
4959 },
4960 theme: ['bmi-tooltip', 'bmi-tt-default', 'bmi-tt-default-customized'],
4961 repositionOnScroll: true,
4962 functionReady: tooltipReposition
4963 }
4964
4965 function extractThemesFromElement(el) {
4966 try {
4967 const base = settings.theme;
4968 const classList = (el && el.className ? el.className : '')
4969 .split(/\s+/)
4970 .filter(Boolean);
4971 const extracted = classList.filter(c => c.indexOf('bmi-tt-') === 0);
4972 if (extracted.length) return base.concat(extracted);
4973 else return base;
4974 } catch (e) {
4975 return settings.theme;
4976 }
4977 }
4978
4979 let tts = $('.tooltip');
4980 let tts_html = $('.tooltip-html');
4981 let premiums = $('.premium-wrapper');
4982 let settings_html = JSON.parse(JSON.stringify(settings));
4983 settings_html.contentAsHTML = true;
4984 settings_html.interactive = true;
4985
4986 for (let i = 0; i < tts.length; ++i) {
4987
4988 let tooltip = tts[i];
4989 let top = tooltip.getAttribute('data-top');
4990 let side = tooltip.getAttribute('side');
4991 let s = JSON.parse(JSON.stringify(settings));
4992 s.content = tooltip.getAttribute('tooltip');
4993 s.theme = extractThemesFromElement(tooltip);
4994 if (top) s.distance = parseInt(top);
4995 if (side && side.trim().length > 0) s.side = side;
4996
4997 $(tooltip).tooltipster(s);
4998
4999 }
5000
5001 for (let i = 0; i < tts_html.length; ++i) {
5002
5003 let tooltip = tts_html[i];
5004 let side = tooltip.getAttribute('side');
5005 let s = JSON.parse(JSON.stringify(settings_html));
5006 s.content = tooltip.getAttribute('tooltip');
5007 s.theme = extractThemesFromElement(tooltip);
5008 if (side && side.trim().length > 0) s.side = side;
5009
5010 $(tooltip).tooltipster(s);
5011
5012 }
5013
5014 for (let i = 0; i < premiums.length; ++i) {
5015
5016 let premium = premiums[i];
5017 let semiums = JSON.parse(JSON.stringify(settings));
5018 semiums.contentAsHTML = true;
5019 semiums.interactive = true;
5020 semiums.maxWidth = 500;
5021 semiums.theme = extractThemesFromElement(premium);
5022
5023 if (premium.getAttribute('tooltip')) semiums.content = premium.getAttribute('tooltip');
5024 else if (premium.getAttribute('data-ready')) {
5025 semiums.content = $('#premium-tooltip-pre')[0].innerHTML.trim() + ' ' + premium.getAttribute('data-ready').trim() + ' ' + $('#premium-tooltip-r')[0].innerHTML.trim();
5026 } else semiums.content = $('#premium-tooltip')[0].innerHTML;
5027
5028 if (premium.getAttribute('side')) semiums.side = premium.getAttribute('side');
5029
5030 $(premium).tooltipster(semiums);
5031
5032 }
5033
5034 let isHidingTooltips = false;
5035 const hideTooltips = function() {
5036 if (!isHidingTooltips && document.querySelector('.tooltipster-base')) {
5037 isHidingTooltips = true;
5038 $.bmi.tooltips.hideAll(true);
5039 setTimeout(() => { isHidingTooltips = false; }, 500);
5040 }
5041 };
5042 window.addEventListener('scroll', hideTooltips, true);
5043 window.addEventListener('resize', hideTooltips, true);
5044
5045 },
5046
5047 hideAll: function(rightnow = false) {
5048
5049 function _hide() {
5050 let instances = $.tooltipster.instances();
5051 for (instance in instances) instances[instance].close();
5052 }
5053
5054 if (rightnow) _hide();
5055 else setTimeout(_hide, 10);
5056
5057 }
5058
5059 },
5060
5061 // STATIC: Collapsers
5062 collapsers: {
5063
5064 toggle: function(self) {
5065
5066 if (collapsing === true) return;
5067 else collapsing = true;
5068
5069 let group = self.getAttribute('group');
5070 if (self.classList.contains('active')) $.bmi.collapsers.close(self);
5071 else $.bmi.collapsers.open(self, group);
5072
5073 },
5074
5075 open: function(el, group) {
5076
5077 $.bmi.collapsers.closeGroup(group);
5078 $(el).addClass('active');
5079 $(el).find('.content').show(300);
5080 setTimeout(function() {
5081 collapsing = false;
5082 }, 300);
5083
5084 },
5085
5086 close: function(el) {
5087
5088 $(el).removeClass('active');
5089 $(el).find('.content').hide(300);
5090 setTimeout(function() {
5091 collapsing = false;
5092 }, 300);
5093
5094 },
5095
5096 closeGroup: function(group) {
5097
5098 $('.collapser[group="' + group + '"]').removeClass('active');
5099 $('.collapser[group="' + group + '"]').find('.content').hide(300);
5100 setTimeout(function() {
5101 collapsing = false;
5102 }, 300);
5103
5104 },
5105
5106 closeAll: function() {
5107
5108 $('.collapser').removeClass('active');
5109 $('.collapser').find('.content').hide(300);
5110 setTimeout(function() {
5111 collapsing = false;
5112 }, 300);
5113
5114 }
5115
5116 },
5117
5118 // STATIC: URL Validation
5119 isUrlValid: function(url) {
5120 var re_weburl = new RegExp(
5121 "^" +
5122 "(?:(?:(?:https?|ftp):)?\\/\\/)" +
5123 "(?:\\S+(?::\\S*)?@)?" +
5124 "(?:" +
5125 "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
5126 "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
5127 "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
5128 "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
5129 "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
5130 "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
5131 "|" +
5132 "(?:" +
5133 "(?:" +
5134 "[a-z0-9\\u00a1-\\uffff]" +
5135 "[a-z0-9\\u00a1-\\uffff_-]{0,62}" +
5136 ")?" +
5137 "[a-z0-9\\u00a1-\\uffff]\\." +
5138 ")+" +
5139 "(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" +
5140 ")" +
5141 "(?::\\d{2,5})?" +
5142 "(?:[/?#]\\S*)?" +
5143 "$", "i"
5144 );
5145
5146 if (re_weburl.test(url)) return true;
5147 else return false;
5148 },
5149
5150 // STATIC: Human Readable
5151 bytesToHuman: function(a, b, c, d, e) {
5152 return (b = Math, c = b.log, d = 1024, e = c(a) / c(d) | 0, a / b.pow(d, e))
5153 .toFixed(2) + ' ' + (e ? 'KMGTPEZY' [--e] + 'B' : 'Bytes');
5154 },
5155
5156 // STATIC: Human Readable Cut Decimal
5157 bytesToHumanCut: function(a, b, c, d, e) {
5158 return Math.ceil((b = Math, c = b.log, d = 1024, e = c(a) / c(d) | 0, a / b.pow(d, e)))
5159 + ' ' + (e ? 'KMGTPEZY' [--e] + 'B' : 'Bytes');
5160 },
5161
5162 // STATIC: Getting backups
5163 getCurrentBackups: function(done = function() {}, tries = 0, q = '') {
5164
5165 if ($('#reloading-bm-list').length > 0 && $('#reloading-bm-list')[0].style.display == 'none') {
5166 $('#reloading-bm-list').show();
5167 $.bmi.ajax('get-current-backups', {
5168 q: q
5169 }).then(function(res) {
5170
5171 // console.warn('Backup list loaded...');
5172 $.bmi.ajax('check-not-uploaded-backups').then(function(res) {
5173 // console.warn('Checking for not uploaded backups...');
5174 // console.log(res);
5175 });
5176 $('#reloading-bm-list').hide();
5177 done(res);
5178
5179
5180 }).catch(function(error) {
5181
5182 $('#reloading-bm-list').hide();
5183
5184 if (tries > 5) return;
5185
5186 setTimeout(() => {
5187 $.bmi.getCurrentBackups(done, (tries+1));
5188 }, 1000);
5189
5190 });
5191 }
5192
5193 },
5194
5195 getCurrentStaging: function(done = function() {}) {
5196
5197 if ($('#reloading-bm-stg-list').length > 0 && $('#reloading-bm-stg-list')[0].style.display == 'none') {
5198 $('#reloading-bm-stg-list').show();
5199 $.bmi.ajax('staging-get-updated-list', {}).then(function(res) {
5200
5201 $('#reloading-bm-stg-list').hide();
5202 done(res);
5203
5204 }).catch(function(error) {
5205
5206 //
5207
5208 });
5209 }
5210
5211 },
5212
5213 getExpirationTime: function(time) {
5214
5215 let $tbody = $('#stg-tbody-table');
5216 let expiresNever = $tbody.data('never');
5217
5218 if (isNaN(parseInt(time)) || time == expiresNever) {
5219 return time;
5220 }
5221
5222 let now = +new Date();
5223 let destination = time * 1000;
5224 let diff = (destination - now);
5225
5226 let days = Math.floor(diff / 24 / 60 / 60 / 1000);
5227 let hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
5228 let minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
5229 let seconds = Math.floor((diff % (1000 * 60)) / 1000);
5230
5231 if (destination < now) {
5232 days = 0;
5233 hours = 0;
5234 minutes = 0;
5235 seconds = 0;
5236 }
5237
5238 if (days == 0 && hours == 0 && minutes == 0 && (seconds == 2 || seconds == 1)) {
5239 clearTimeout(window.bmiRefreshExpired);
5240 window.bmiRefreshExpired = setTimeout(function () {
5241 $.bmi.reloadStaging();
5242 }, 3000);
5243 }
5244
5245 return `${('0' + days).slice(-2)}:${('0' + hours).slice(-2)}:${('0' + minutes).slice(-2)}:${('0' + seconds).slice(-2)}`;
5246
5247 },
5248
5249 fillWithNewStagings: function(sites, cb = function() {}) {
5250
5251 let stagingSiteNames = [];
5252 let amountOfSites = 0;
5253 let $tbody = $('#stg-tbody-table');
5254 let $templateOrigin = $('.br_stg_tr_template');
5255 let emptyText = $tbody.data('empty');
5256 let localText = $tbody.data('local');
5257 let prefixText = $tbody.data('prefix');
5258 let expiresNever = $tbody.data('never');
5259 let displayText = $tbody.data('display');
5260 let originalText = $tbody.data('original');
5261 let emptyElementTR = document.createElement('TR');
5262 let emptyElementTD = document.createElement('TD');
5263 emptyElementTD.setAttribute('colspan', '100%');
5264 emptyElementTD.classList.add('center');
5265 emptyElementTD.innerText = emptyText;
5266 emptyElementTR.append(emptyElementTD);
5267
5268 $tbody.html('');
5269 for (let site in sites) {
5270
5271 site = sites[site];
5272 if (typeof site.name == 'undefined') continue;
5273 if (typeof site.url == 'undefined') continue;
5274 if (typeof site.db_prefix == 'undefined') continue;
5275
5276 stagingSiteNames.push(escapeHtml(site.name));
5277
5278 let $template = $templateOrigin.clone();
5279 $template.removeClass('br_stg_tr_template');
5280 $template.attr('name', escapeHtml(site.name));
5281 $template.find('.stg-tr-name').text(escapeHtml(site.display_name));
5282 $template.find('.stg-tr-name').attr('tooltip', `<b>${originalText}</b>: ${escapeHtml(site.name)}<br><b>${displayText}</b>: ${escapeHtml(site.display_name)}`);
5283 if (escapeHtml(site.name) == escapeHtml(site.display_name)) {
5284 $template.find('.stg-tr-name').attr('tooltip', `<b>${displayText}</b>: ${escapeHtml(site.display_name)}`);
5285 }
5286 $template.find('.stg-tr-url-el').attr('href', escapeHtml(site.url));
5287 $template.find('.stg-tr-url-el').attr('tooltip', escapeHtml(site.url));
5288 $template.find('.stg-tr-url-el').text(escapeHtml(site.url));
5289 $template.find('.stg-tr-size').text(escapeHtml(site.total_size) + ' (' + escapeHtml(site.total_files) + ')');
5290 $template.find('.stg-tr-server').text((site.communication_secret == 'local') ? escapeHtml(localText) : 'TasteWP');
5291 $template.find('.stg-tr-server').attr('tooltip', prefixText + ': ' + escapeHtml(site.db_prefix));
5292 $template.find('.stg-tr-creation').text(escapeHtml(site.creation_date));
5293 $template.find('.stg-tr-expiration span').text(escapeHtml(site.expiration_time));
5294
5295 if (site.communication_secret != 'local') {
5296 $template.addClass('bmi-tastewp-staging-row');
5297 $template.attr('server', 'tastewp');
5298 $template.attr('token', escapeHtml(site.communication_secret));
5299 if (isNaN(parseInt(site.expiration_time)) || site.expiration_time == expiresNever) {
5300 $template.attr('expiration', escapeHtml(expiresNever));
5301 $template.find('.stg-tr-expiration span').text(escapeHtml(expiresNever));
5302 if (site.is_premium == '1') $template.find('.stg-tr-expiration span').addClass('stg-premium-stars');
5303 } else {
5304 $template.attr('expiration', parseInt(site.expiration_time));
5305 $template.find('.stg-tr-expiration span').text($.bmi.getExpirationTime(parseInt(site.expiration_time)));
5306 }
5307 } else {
5308 $template.attr('server', 'local');
5309 $template.find('.stg-tr-expiration span').text(escapeHtml(expiresNever));
5310 }
5311
5312 $template.hide();
5313
5314 $template.prependTo($tbody);
5315 amountOfSites++;
5316 }
5317
5318 if (amountOfSites == 0) {
5319 $tbody.html(emptyElementTR);
5320 }
5321
5322 if ($('#bmi-stg-subname-input').val() == '') {
5323 let j = 1;
5324 let defaultName = bmiVariables.stgStagingDefaultName;
5325 for (let i = 0; i < stagingSiteNames.length; ++i) {
5326 let name = stagingSiteNames[i];
5327 if (stagingSiteNames.includes(defaultName)) {
5328 defaultName = bmiVariables.stgStagingDefaultName + j;
5329 j++;
5330 } else break;
5331 }
5332 $('#bmi-stg-subname-input').val(defaultName);
5333 }
5334
5335 $.bmi.tooltips.init();
5336 $.bmi.showStagings();
5337
5338 },
5339
5340 // STATIC: Filling backups
5341 fillWithNewBackups: function(res, ongoing, cb = function() {}, search = '') {
5342 // let backupsList = {};
5343 let external = res.external;
5344 let local = res.local;
5345 let backups = {};
5346
5347 // Local backups
5348 for (let name in local) {
5349 if (search !== '' && local[name][0].toLowerCase().indexOf(search.toLowerCase()) === -1) continue;
5350 let backup = local[name];
5351 backups[backup[7]] = backup;
5352 backups[backup[7]][10] = backup[9];
5353 backups[backup[7]][9] = backup[8];
5354 backups[backup[7]][8] = { local: true };
5355 }
5356 // Google Drive and FTP backups
5357 // for (let cloudName in external) {
5358 // for (let md5 in external[cloudName]) {
5359 // let backup = external[cloudName][md5];
5360 // for (let md51 in external[cloudName]) {
5361 // if (typeof backups[md51] == 'undefined') {
5362 // if (cloudName === 'FTP'){
5363 // backups[md51] = backup;
5364 // backups[md51][9] = backup[9];
5365 // if (typeof backups[md51][8] == 'undefined' || typeof backups[md51][8] != 'object') {
5366 // backups[md51][8] = {local: false, ftp: backup[8]}
5367 // } else if (typeof backups[md51][8] == 'object') {
5368 // backups[md51][8] = backup[8];
5369 // }
5370 // }else if(cloudName === 'gdrive'){
5371 // backups[md51] = backup;
5372 // backups[md51][9] = backup[9];
5373 // if (typeof backups[md51][8] == 'undefined' || typeof backups[md51][8] != 'object') {
5374 // backups[md51][8] = {local: false, gdrive: backup[8]}
5375 // } else if (typeof backups[md51][8] == 'object') {
5376 // backups[md51][8].gdrive = backup[8];
5377 // }
5378 // }
5379 // } else {
5380 // if (cloudName === 'FTP'){
5381 // backups[md51][8].ftp = backup[8];
5382 // }else if(cloudName === 'gdrive'){
5383 // backups[md51][8].gdrive = backup[8];
5384 // }
5385 // }
5386 // }
5387 // }
5388 // }
5389
5390 // Google Drive backups
5391 for (let drive in external) {
5392 for (let backupKey in external[drive]) {
5393 if (search !== '' && external[drive][backupKey][0].toLowerCase().indexOf(search.toLowerCase()) === -1) continue;
5394 let backup = external[drive][backupKey];
5395 let md5 = backup[7];
5396 if (typeof backups[md5] == 'undefined') {
5397 backups[md5] = {...backup};
5398 backups[md5][9] = backup[9];
5399 if (typeof backups[md5][8] == 'undefined' || typeof backups[md5][8] != 'object') {
5400 backups[md5][8] = { local: false };
5401 if (typeof backups[md5][8][drive] == 'undefined') {
5402 backups[md5][8][drive] = backup[8];
5403 }
5404 } else if (typeof backups[md5][8] == 'object') {
5405 if (typeof backups[md5][8][drive] == 'undefined') {
5406 backups[md5][8][drive] = backup[8];
5407 }
5408 }
5409 } else {
5410 if (typeof backups[md5][8][drive] == 'undefined') {
5411 backups[md5][8][drive] = backup[8];
5412 }
5413 }
5414 }
5415 }
5416
5417 $('#bmi_restore_tbody').html('');
5418 $('.bmi-stg-dropdown-area-inner-scroll').html('');
5419
5420 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(bmiVariables.stgLoading);
5421 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text('---');
5422 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text('---');
5423
5424 let totalStagingPossibleBackups = 0;
5425 let lockedtxt = $('#bmi-manual-locked').text().trim();
5426 let backupsSorted = Object.keys(backups).sort(function(a, b) {
5427 let x = +new Date(backups[a][1].replace(/\-/g, '/'));
5428 let y = +new Date(backups[b][1].replace(/\-/g, '/'));
5429 return x - y;
5430 });
5431
5432 let i = 0;
5433 for (; i < backupsSorted.length; ++i) {
5434
5435 let storages = [];
5436 let index = backupsSorted[i];
5437 let md5 = backups[index][7];
5438 let domain = backups[index][9];
5439 let locked = backups[index][5] === 'locked' ? true : false;
5440
5441 let name = backups[index][0];
5442 let originalname = backups[index][0];
5443
5444 if (backups[index][0].includes('#%&')) {
5445 name = backups[index][0].split('#%&')[1];
5446 originalname = backups[index][0].split('#%&')[0];
5447 }
5448
5449 name = name.replace(/ /g, '');
5450 name = name.trim();
5451
5452 originalname = originalname.replace(/ /g, '');
5453 originalname = originalname.trim();
5454
5455 let isLocalBackup = true;
5456 let isGoogleDrive = false;
5457 let isFtp = false;
5458 let isDropbox = false;
5459 let isCloud = false;
5460 let isOneDrive = false;
5461 let isPCloud = false;
5462 let isAWS = false;
5463 let isWasabi = false;
5464 let isBackupBliss = false;
5465 let isSftp = false;
5466 if (typeof backups[index][8] != 'undefined') {
5467 if (typeof backups[index][8] == 'object') {
5468 for (let storage in backups[index][8]) {
5469 if (storage == 'gdrive') {
5470 isGoogleDrive = backups[index][8][storage];
5471 } else if (storage == 'FTP') {
5472 isFtp = backups[index][8][storage];
5473 } else if (storage == 'dropbox') {
5474 isDropbox = backups[index][8][storage];
5475 } else if (storage == 'local') {
5476 isLocalBackup = backups[index][8][storage];
5477 } else if (storage == 'onedrive') {
5478 isOneDrive = backups[index][8][storage];
5479 } else if (storage == 'pcloud') {
5480 isPCloud = backups[index][8][storage];
5481 } else if (storage == 'aws') {
5482 isAWS = backups[index][8][storage];
5483 } else if (storage == 'wasabi') {
5484 isWasabi = backups[index][8][storage];
5485 } else if (storage == 'backupbliss') {
5486 isBackupBliss = backups[index][8][storage];
5487 } else if (storage == 'sftp') {
5488 isSftp = backups[index][8][storage];
5489 }
5490 }
5491 }
5492 }
5493
5494 isCloud = isGoogleDrive || isFtp || isDropbox || isOneDrive || isBackupBliss || isSftp || isAWS || isWasabi || isPCloud;
5495
5496 let bwsc = '<b>' + $('#bmi-backup-created-on').text().trim() + '</b>';
5497 let bonc = '<b>' + $('#bmi-backup-original-name').text().trim() + '</b>';
5498 let bfnc = '<b>' + $('#bmi-backup-file-name').text().trim() + '</b>';
5499
5500 let id = 'bmi_br_backup_' + i;
5501 let $template = $('.br_tr_template').clone();
5502 $template.removeClass('br_tr_template');
5503 $template[0].style.display = 'none';
5504 $template.attr('md5', md5);
5505 if (isGoogleDrive) $template.attr('gdrive-id', isGoogleDrive);
5506
5507 if (isFtp) $template.attr('ftp-id', isFtp);
5508
5509 if (isDropbox) $template.attr('dropbox-id', isDropbox);
5510
5511 if (!isLocalBackup && isCloud) $template.attr('data-is-local', 'no');
5512 else $template.attr('data-is-local', 'yes');
5513
5514 if (isOneDrive) $template.attr('onedrive-id', isOneDrive);
5515
5516 if (isPCloud) $template.attr('pcloud-id', isPCloud);
5517
5518 if (isAWS) $template.attr('aws-id', isAWS);
5519
5520 if (isWasabi) $template.attr('wasabi-id', isWasabi);
5521 if (isBackupBliss) $template.attr('backupbliss-id', isBackupBliss);
5522
5523 if (isSftp) $template.attr('sftp-id', isSftp);
5524
5525 $template.find('.br_label').attr('for', id);
5526 $template.find('.br_checkbox').attr('id', id);
5527 $template.find('.bc-download-btn').attr('href', $('#BMI_BLOG_URL').text().trim() + '/?backup-migration=BMI_BACKUP&bmi-id=' + name + '&sk=' + $('#BMI_DOWNLOAD_TOKEN').text().trim());
5528 $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());
5529
5530 $template.find('.br_date').text(backups[index][1]);
5531 $template.find('.br_name').text(name);
5532 if (originalname == name) {
5533 $template.find('.br_name').attr('tooltip', (bonc + ' ' + escapeHtml(originalname) + '<br><br>' + bwsc + ' ' + escapeHtml(domain)));
5534 } else {
5535 $template.find('.br_name').attr('tooltip', (bonc + ' ' + escapeHtml(originalname) + '<br><br>' + bfnc + ' ' + escapeHtml(name) + '<br><br>' + bwsc + ' ' + escapeHtml(domain)));
5536 }
5537
5538 let readableBytesNotCut = $.bmi.bytesToHuman(backups[index][4]);
5539 let readableBytes = $.bmi.bytesToHuman(backups[index][4]);
5540 if (readableBytes.includes('MB') || readableBytes.includes('KB')) {
5541 readableBytes = $.bmi.bytesToHumanCut(backups[index][4]);
5542 }
5543
5544 $template.find('.br_size').text(readableBytes + ' (' + backups[index][2] + ')');
5545
5546 if (isGoogleDrive) {
5547 $template.find('.br_stroage').find('.strg-gdrive').addClass('img-green');
5548 storages.push('Google Drive');
5549 } else {
5550 $template.find('.br_stroage').find('.strg-gdrive').addClass('img-red');
5551 }
5552
5553 // if (isLocalBackup && isCloud) {
5554 // $template.find('.br_stroage').find('.strg-suc').addClass('img-green');
5555 // } else {
5556 // $template.find('.br_stroage').find('.strg-suc').addClass('img-orange');
5557 // }
5558
5559 if (isFtp) {
5560 $template.find('.br_stroage').find('.strg-ftp').addClass('img-green');
5561 storages.push('FTP');
5562 } else {
5563 $template.find('.br_stroage').find('.strg-ftp').addClass('img-red');
5564 }
5565
5566 if (isDropbox) {
5567 $template.find('.br_stroage').find('.strg-dropbox').addClass('img-green');
5568 storages.push('Dropbox');
5569 } else {
5570 $template.find('.br_stroage').find('.strg-dropbox').addClass('img-red');
5571 }
5572
5573 if (isOneDrive) {
5574 $template.find('.br_stroage').find('.strg-onedrive').addClass('img-green');
5575 storages.push('OneDrive');
5576 } else {
5577 $template.find('.br_stroage').find('.strg-onedrive').addClass('img-red');
5578 }
5579
5580 if (isPCloud) {
5581 $template.find('.br_stroage').find('.strg-pcloud').addClass('img-green');
5582 storages.push('pCloud');
5583 } else {
5584 $template.find('.br_stroage').find('.strg-pcloud').addClass('img-red');
5585 }
5586
5587 if (isAWS) {
5588 $template.find('.br_stroage').find('.strg-aws').addClass('img-green');
5589 storages.push('AWS S3');
5590 } else {
5591 $template.find('.br_stroage').find('.strg-aws').addClass('img-red');
5592 }
5593
5594 if (isWasabi) {
5595 $template.find('.br_stroage').find('.strg-wasabi').addClass('img-green');
5596 storages.push('Wasabi');
5597 } else {
5598 $template.find('.br_stroage').find('.strg-wasabi').addClass('img-red');
5599 }
5600 if (isBackupBliss) {
5601 $template.find('.br_stroage').find('.strg-backupbliss').addClass('img-green');
5602 storages.push('BackupBliss');
5603 } else {
5604 $template.find('.br_stroage').find('.strg-backupbliss').addClass('img-red');
5605 }
5606
5607 if (isSftp) {
5608 $template.find('.br_stroage').find('.strg-sftp').addClass('img-green');
5609 storages.push('SFTP');
5610 } else {
5611 $template.find('.br_stroage').find('.strg-sftp').addClass('img-red');
5612 }
5613
5614 if (isLocalBackup) {
5615 $template.find('.br_stroage').find('.strg-local').addClass('img-green');
5616 storages.push('Local Storage');
5617 for (let key in backups[index][10]) {
5618 if (backups[index][10].hasOwnProperty(key)) {
5619 if ($template.find('.br_stroage').find('.strg-' + key).length > 0 && $template.find('.br_stroage').find('.strg-' + key).hasClass('img-red')) {
5620 let storageElement = $template.find('.br_stroage').find('.strg-' + key);
5621 let storageElementTooltip = storageElement.attr('tooltip');
5622
5623 storageElement.addClass('can-be-manually-uploaded');
5624 storageElement.attr('tooltip', 'Upload To ' + storageElementTooltip);
5625 }
5626 }
5627 }
5628 } else {
5629 $template.find('.br_stroage').find('.strg-local').addClass('img-red');
5630 $template.find('.brow_subactions').hide();
5631 $template.find('.brow_lock').hide();
5632 }
5633
5634
5635 //$template.find('.br_stroage').find('.strg-suc').attr('tooltip', 'Backup is stored on: ' + storages.join(', ') + '.');
5636
5637 if (!$('#bmi-pro-storage-gdrive-toggle').is(':checked')) {
5638 $template.find('.br_stroage').addClass('bmi-gdrive-disabled');
5639 }
5640
5641 if (!$('#bmi-pro-storage-ftp-toggle').is(':checked')) {
5642 $template.find('.br_stroage').addClass('bmi-ftp-disabled');
5643 }
5644
5645 if (!$('#bmi-pro-storage-onedrive-toggle').is(':checked')) {
5646 $template.find('.br_stroage').addClass('bmi-onedrive-disabled');
5647 }
5648
5649 if ((''+backups[index][6]).trim().length == 0 || (backups[index][6]+'') == 'false') {
5650 $template.find('.bc-locked-btn').addClass('forever');
5651 $template.find('.bc-locked-btn').attr('tooltip', lockedtxt);
5652 locked = true;
5653 }
5654
5655 if (locked) {
5656 $template.find('.bc-unlocked-btn').hide();
5657 } else {
5658 $template.find('.bc-locked-btn').hide();
5659 }
5660
5661 if ($('#BMI_BACKUP_PRO').val() == 1) {
5662
5663 $template.attr('data-backup-name', name);
5664 console.log("backups[index]", backups[index])
5665 let isEncrypted = (backups[index].is_encrypted === true || backups[index].is_encrypted === "true" || backups[index].is_encrypted == 1);
5666 if (isEncrypted) {
5667 $template.find('.bc-encrypt-btn').hide();
5668 $template.find('.bc-decrypt-btn').show();
5669 } else {
5670 $template.find('.bc-encrypt-btn').show();
5671 $template.find('.bc-decrypt-btn').hide();
5672 }
5673 }
5674
5675 isDirectLinkEnabled = $('[name="radioAccessViaLink"]:checked').val() === "true";
5676
5677 if (!isDirectLinkEnabled) {
5678 $template.find('.bc-url-btn svg')
5679 .css({
5680 cursor: "not-allowed",
5681 color: "red"
5682 })
5683 .addClass('disabled');
5684 $template.find('.bc-url-btn')
5685 .attr('tooltip', $('#direct-link-disabled-tooltip').text().trim());
5686 } else {
5687 $template.find('.bc-url-btn svg')
5688 .css({
5689 cursor: "pointer",
5690 color: "#B6B6B6"
5691 })
5692 .removeClass('disabled');
5693 $template.find('.bc-url-btn')
5694 .attr('tooltip', $('#direct-link-enabled-tooltip').text().trim());
5695 }
5696
5697 $template.prependTo('#bmi_restore_tbody');
5698
5699 $('.bmi-stg-drop-option.active').removeClass('active');
5700
5701 if (isLocalBackup) {
5702 let $stgOptionTemplate = $('.bmi-stg-option-template').clone();
5703 $stgOptionTemplate.removeClass('bmi-stg-option-template');
5704 $stgOptionTemplate[0].style.display = '';
5705 $stgOptionTemplate.attr('backup-name', name);
5706 $stgOptionTemplate.addClass('active');
5707
5708 $stgOptionTemplate.find('.bmi-stg-option-name').text(name);
5709 $stgOptionTemplate.find('.bmi-stg-option-date i').text(backups[index][1]);
5710 $stgOptionTemplate.find('.bmi-stg-option-size i').text(readableBytesNotCut);
5711
5712 $stgOptionTemplate.prependTo('.bmi-stg-dropdown-area-inner-scroll');
5713
5714 // if (!isLocalBackup && isGoogleDrive) { // Unreachable code
5715 // $stgOptionTemplate.attr('gdrive-id', isGoogleDrive);
5716 // $stgOptionTemplate.attr('data-is-local', 'no');
5717 // } else if (isLocalBackup) {
5718 // $stgOptionTemplate.attr('data-is-local', 'yes');
5719 // }
5720
5721 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(name);
5722 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text(backups[index][1]);
5723 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text(readableBytesNotCut);
5724
5725 $('#bmi-stg-current-backup-selected').val(name);
5726 totalStagingPossibleBackups++;
5727 }
5728
5729 // backups[name].push(name);
5730 // backupsList[id] = backups[name];
5731
5732 }
5733
5734 if ($('.bmi-stg-sel-box.bmi-active').attr('data-mode') == 'tastewp') {
5735 $('.bmi-stg-creation-box-local').hide(300);
5736 $('.bmi-stg-creation-box-tastewp').hide(300);
5737 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
5738 if ($('.bmi-stg-drop-option:not(.bmi-stg-option-template)').length > 0) {
5739 $('.bmi-stg-creation-box-tastewp').show(300);
5740 } else {
5741 $('.bmi-stg-creation-box-tastewp-empty').show(300);
5742 }
5743 } else {
5744 $('.bmi-stg-creation-box-local').show(300);
5745 $('.bmi-stg-creation-box-tastewp').hide(300);
5746 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
5747 }
5748
5749 if (i == 0) {
5750
5751 let empty = $('#bmi_restore_tbody').data('empty');
5752 $('#bmi_restore_tbody').html('<tr class="bmi-empty-text"><td class="center text-muted" colspan="100%">' + empty + '</td></tr>');
5753
5754 }
5755
5756 $.bmi.tooltips.init();
5757 $.bmi.showMoreBackups();
5758
5759 if ($('.bmi-stg-sel-box.bmi-active').data('mode') == 'tastewp') {
5760 $('.bmi-stg-creation-box-local').hide(300);
5761 $('.bmi-stg-creation-box-tastewp').hide(300);
5762 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
5763 if ($('#bmi_restore_tbody').find('tr:not(.bmi-empty-text)').length > 0) {
5764 $('.bmi-stg-creation-box-tastewp').show(300);
5765 } else {
5766 $('.bmi-stg-creation-box-tastewp-empty').show(300);
5767 }
5768 }
5769
5770 if (typeof ongoing != 'undefined') $.bmi.fillOnGoing(ongoing);
5771 cb();
5772
5773 },
5774
5775 // STATIC: Loading 10 more backups
5776 showMoreBackups: function(res) {
5777
5778 backups = {};
5779 let trs = $('#bmi_restore_tbody').find('tr:hidden').not('.bmi-empty-text').not('.bmi-empty-text-search').not('.search-hidden');
5780
5781 for (let i = 0;
5782 (i < trs.length && i < 10); ++i) {
5783 setTimeout(function() {
5784
5785 $(trs[i]).show(300);
5786
5787 }, (i * 50));
5788 }
5789
5790 if ((trs.length - 10) <= 0) {
5791 $('#load-more-backups-wrp').hide(300);
5792 } else {
5793 $('#load-more-backups-wrp').show(300);
5794 }
5795
5796 },
5797
5798 // STATIC: Animate staging list show
5799 showStagings: function() {
5800
5801 backups = {};
5802 let trs = $('#stg-tbody-table').find('tr:hidden');
5803
5804 for (let i = 0; i < trs.length; ++i) {
5805 setTimeout(function() {
5806 $(trs[i]).show(300);
5807 }, (i * 50));
5808 }
5809
5810 },
5811
5812 setBackupProgressList: function(md5, type, tooltip = false) {
5813
5814 //let suc = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-suc');
5815 let warn = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-warn');
5816 let ong = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-ong');
5817 let wait = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-wait');
5818
5819 //suc.hide();
5820 warn.hide();
5821 ong.hide();
5822 wait.hide();
5823
5824 // if (type == 'suc') {
5825 // suc.show();
5826 // if (tooltip) suc.tooltipster('content', tooltip);
5827 // }
5828 if (type == 'warn') {
5829 warn.show();
5830 if (tooltip) warn.tooltipster('content', tooltip);
5831 }
5832 if (type == 'ong') {
5833 ong.show();
5834 if (tooltip) ong.tooltipster('content', tooltip);
5835 }
5836 if (type == 'wait') {
5837 wait.show();
5838 if (tooltip) wait.tooltipster('content', tooltip);
5839 }
5840
5841 },
5842
5843 fillOnGoing: function(ongoing = false) {
5844
5845 if (ongoing && typeof ongoing.queue != 'undefined' && typeof ongoing.current_upload != 'undefined') {
5846
5847 for (let task in ongoing.queue) {
5848 let data = ongoing.queue[task];
5849 let md5 = data.md5;
5850 uploadType = task.split('_')[0];
5851 //Replaces existing img- class or add it with new
5852 $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-' + uploadType).attr('class', function(i, cls) {
5853 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-purple') : cls + ' img-purple';
5854 });
5855 $.bmi.setBackupProgressList(md5, 'wait');
5856 }
5857
5858 if (typeof ongoing.current_upload == 'object') {
5859 if (ongoing.current_upload.length == 0) {
5860
5861 if (ongoing_latest_token) {
5862 //$.bmi.reloadBackups();
5863 ongoing_failed = false;
5864 if (typeof ongoing.failed != 'undefined') {
5865 ongoing_failed = currentUploadType + "_" + ongoing_latest_token in ongoing.failed;
5866 }
5867
5868 $('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
5869 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, ongoing_failed ? 'img-red' : 'img-green') : cls + ongoing_failed ? ' img-red' : ' img-green';
5870 });
5871 $.bmi.setBackupProgressList(ongoing_latest_token, 'done');
5872 ongoing_latest_token = false;
5873 }
5874
5875 } else {
5876
5877 if (!ongoing_latest_token) {
5878 ongoing_latest_token = ongoing.current_upload.md5;
5879 }
5880
5881 if (ongoing_latest_token && ongoing_latest_token != ongoing.current_upload.md5) {
5882 //$.bmi.reloadBackups();
5883 ongoing_failed = false;
5884 if (typeof ongoing.failed != 'undefined') {
5885 ongoing_failed = currentUploadType + "_" + ongoing_latest_token in ongoing.failed;
5886 }
5887
5888 $('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
5889 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, ongoing_failed ? 'img-red' : 'img-green') : cls + ongoing_failed ? ' img-red' : ' img-green';
5890 });
5891 $.bmi.setBackupProgressList(ongoing_latest_token, 'done');
5892 ongoing_latest_token = ongoing.current_upload.md5;
5893 }
5894
5895 let progress = '0%';
5896 if (typeof ongoing.current_upload.progress != 'undefined') progress = ongoing.current_upload.progress;
5897
5898 //Detect type
5899 currentUploadType = ongoing.current_upload.task.split('_')[0];
5900 let message = 'Upload to ';
5901 let externalStorageType = 'Google Drive';
5902 switch (currentUploadType) {
5903 case 'ftp':
5904 externalStorageType = 'FTP';
5905 break;
5906 case 'dropbox':
5907 externalStorageType = 'Dropbox';
5908 break;
5909 case 'onedrive':
5910 externalStorageType = 'OneDrive';
5911 break;
5912 case 'aws':
5913 externalStorageType = 'Amazon S3';
5914 break;
5915 case 'wasabi':
5916 externalStorageType = 'Wasabi';
5917 break;
5918 case 'pcloud':
5919 externalStorageType = 'pCloud';
5920 case 'backupbliss':
5921 externalStorageType = 'BackupBliss';
5922 break;
5923 case 'sftp':
5924 externalStorageType = 'SFTP';
5925 break;
5926 }
5927
5928 //Replaces existing img- class or add it with new
5929 $('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
5930 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-orange') : cls + ' img-orange';
5931 });
5932 message += externalStorageType;
5933 $.bmi.setBackupProgressList(ongoing.current_upload.md5, 'ong', message + ' in progress: ' + progress);
5934 }
5935 }
5936
5937 }
5938
5939 if (typeof ongoing.failed != 'undefined') {
5940
5941 for (let token in ongoing.failed) {
5942 data = token.split("_");
5943 md5 = data[1];
5944 uploadType = data[0];
5945
5946 if (!(ongoing.current_upload instanceof Array))
5947 if (ongoing.current_upload.md5 == md5) // Checks if the current upload task is of the same backup the failed task is
5948 return; //There's an ongoing upload for the same backup, so will not show any failures to show the upload status.
5949
5950 // if (typeof ongoing.queue[token] != 'undefined') {
5951 // md5 = ongoing.queue[token].md5;
5952 // }
5953
5954 // if (typeof ongoing.current_upload.task != 'undefined' && ongoing.current_upload.task == token) {
5955 // md5 = ongoing.current_upload.md5;
5956 // }
5957
5958
5959
5960 if (md5) {
5961 //Replaces existing img- class or add it with new
5962 $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-' + uploadType).attr('class', function(i, cls) {
5963 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-red') : cls + ' img-red';
5964 });
5965 let message = 'There was an error during upload to: ';
5966 if (uploadType == null || uploadType == 'gdrive') externalStorageType = 'Google Drive';
5967 if (uploadType == 'ftp') externalStorageType = 'FTP';
5968 if (uploadType == 'dropbox') externalStorageType = 'Dropbox';
5969 if (uploadType == 'pcloud') externalStorageType = 'pCloud';
5970 if (uploadType == 'onedrive') externalStorageType = 'OneDrive';
5971 if (uploadType == 'aws') externalStorageType = 'AWS';
5972 if (uploadType == 'wasabi') externalStorageType = 'Wasabi';
5973 if (uploadType == 'backupbliss') externalStorageType = 'BackupBliss';
5974 if (uploadType == 'sftp') externalStorageType = 'SFTP';
5975 message += externalStorageType;
5976
5977 $.bmi.setBackupProgressList(md5, 'warn', message);
5978 }
5979 }
5980
5981 }
5982
5983
5984 // Update tooltips of external storage icons with upload status
5985 const tooltips = {
5986 'img-green': 'Backup is available on %s.',
5987 'img-orange': 'Upload to %s is in progress.',
5988 'img-purple': 'Upload to %s is queued.',
5989 'img-red': 'Backup is not available on %s.',
5990 };
5991
5992 const $tbody = $('#bmi_restore_tbody');
5993 let storagesToStr = {
5994 'strg-local': 'Local Storage',
5995 'strg-ftp': 'FTP',
5996 'strg-aws': 'AWS S3',
5997 'strg-backupbliss': 'BackupBliss',
5998 'strg-sftp': 'SFTP',
5999 'strg-gdrive': 'Google Drive',
6000 'strg-onedrive': 'OneDrive',
6001 'strg-pcloud': 'pCloud',
6002 'strg-wasabi': 'Wasabi',
6003 'strg-dropbox': 'Dropbox',
6004 }
6005
6006 Object.entries(tooltips).forEach(([imgClass, template]) => {
6007 $tbody.find(`.${imgClass}`).each(function () {
6008 const $el = $(this);
6009 const classes = $el.attr('class').split(/\s+/);
6010 if (classes.includes('can-be-manually-uploaded')) return;
6011 const strgClass = classes.find(c => c.startsWith('strg-'));
6012
6013 if (!strgClass || ['strg-suc', 'strg-warn', 'strg-ong', 'strg-wait'].includes(strgClass)) return;
6014
6015 let cap = '';
6016 cap = storagesToStr[strgClass] || strgClass.replace('strg-', '').charAt(0).toUpperCase() + strgClass.replace('strg-', '').slice(1);
6017 const tooltip = template.replace('%s', cap);
6018 $el.tooltipster('content', tooltip);
6019 });
6020 });
6021 },
6022
6023 adjustStorageIcons: function() {
6024 let es = ['gdrive', 'onedrive', 'sftp', 'ftp', 'dropbox', 'aws', 'wasabi', 'pcloud'];
6025
6026 let activeEsCount = 2;
6027 for (let i = 0; i < es.length; ++i) {
6028 if ($('#bmi-pro-storage-' + es[i] + '-toggle').is(':checked')) {
6029 $('.strg-' + es[i]).show();
6030 activeEsCount++;
6031 } else {
6032 $('.strg-' + es[i]).hide();
6033 }
6034 }
6035 if ($('.storage-icons-container').length == 0) return;
6036 if (activeEsCount > 5 ) {
6037 $('.storage-icons-container').css('grid-template-rows', 'repeat(2, auto)');
6038 } else {
6039 $('.storage-icons-container').css('grid-template-rows', 'none');
6040 }
6041 },
6042
6043 // STATIC: Reload backups list
6044 reloadBackups: function(callagain = function() {}) {
6045
6046 $.bmi.getCurrentBackups(function(res) {
6047 $.bmi.fillWithNewBackups(res.backups, res.backups.ongoing, function() {
6048 callagain();
6049
6050 $('#backups-select-all').prop('checked', false);
6051 $('.del-all-btn-wrp').hide(300);
6052 });
6053 });
6054
6055 },
6056
6057 // STATIC: Reload staging list
6058 reloadStaging: function(callagain = function() {}) {
6059
6060 $.bmi.getCurrentStaging(function(res) {
6061 $.bmi.fillWithNewStagings(res.sites, function() {
6062 callagain();
6063 });
6064 });
6065
6066 },
6067
6068 searchInBackups: function(keyword, cb = function() {}) {
6069 let keywordLower = keyword.toLowerCase().trim();
6070 let $rows = $('#bmi_restore_tbody').find('tr').not('.bmi-empty-text').not('.bmi-empty-text-search');
6071 let matchCount = 0;
6072
6073 $rows.each(function() {
6074 let name = $(this).find('.br_name').text().toLowerCase();
6075 $(this).hide(); // Reset to hidden for pagination
6076 if (keywordLower === '' || name.indexOf(keywordLower) !== -1) {
6077 $(this).removeClass('search-hidden');
6078 matchCount++;
6079 } else {
6080 $(this).addClass('search-hidden');
6081 }
6082 });
6083
6084 if (matchCount === 0) {
6085 if ($('#bmi_restore_tbody').find('tr.bmi-empty-text-search').length === 0) {
6086 let empty = $('#bmi_restore_tbody').data('empty') || 'No backups found matching your search.';
6087 $('#bmi_restore_tbody').append('<tr class="bmi-empty-text-search"><td class="center text-muted" colspan="100%">' + empty + '</td></tr>');
6088 } else {
6089 $('#bmi_restore_tbody').find('tr.bmi-empty-text-search').show();
6090 }
6091 $('#load-more-backups-wrp').hide();
6092 } else {
6093 $('#bmi_restore_tbody').find('tr.bmi-empty-text-search').hide();
6094 $.bmi.showMoreBackups();
6095 }
6096
6097 cb();
6098 },
6099
6100 // STATIC: Hides all dropdowns
6101 hideAllLists: function() {
6102
6103 let opens = $('.dropdown-open');
6104 if (opens.length <= 0) return;
6105
6106 for (let i = 0; i < opens.length; ++i) {
6107
6108 let open = $(opens[i]);
6109 if (open.hasClass('ignored-open')) {
6110
6111 open.removeClass('ignored-open');
6112
6113 } else {
6114
6115 open.hide(300);
6116 open.removeClass('dropdown-open');
6117 $(open[0].closest('.bmi-dropdown')).removeClass('active');
6118
6119 }
6120
6121 }
6122
6123 },
6124
6125 // Sets and option
6126 setOption: function($dropdown, $option = null, value = false) {
6127
6128 let $optlist = $dropdown.find('.dropdown-options');
6129 if ($dropdown.attr('data-optioner')) {
6130 $optlist = $('.optioner-' + $dropdown.attr('data-optioner'));
6131 }
6132
6133 $optlist.find('.active-option').removeClass('active-option');
6134 if (value !== false) {
6135 $option = $optlist.find('.dropdown-option[data-value="' + value + '"]');
6136 }
6137
6138 if ($($option).hasClass('active-option')) return;
6139
6140 $dropdown.find('.dropdown-title').text($option.text());
6141 $dropdown.attr('data-selected', $option.data('value'));
6142 $option.addClass('active-option');
6143
6144 if (value === false) {
6145 $dropdown.change();
6146 }
6147
6148 $.bmi.hideAllLists();
6149
6150 },
6151
6152 // STATIC: Prepare file frmo text
6153 prepareFile: function(filename, text) {
6154
6155 let element = document.createElement('a');
6156 element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
6157 element.setAttribute('download', filename);
6158
6159 element.style.display = 'none';
6160 document.body.appendChild(element);
6161
6162 element.click();
6163 document.body.removeChild(element);
6164
6165 $.bmi.alert('success', $('#bmi-download-should-start').text(), 3000);
6166
6167 },
6168
6169 // Utility to escape html
6170 escapeHtml: function(text)
6171 {
6172 return escapeHtml(text)
6173 }
6174
6175 }
6176 });
6177 });
6178 jQuery(document).ready(function($) {
6179
6180 $('.collapser .header').on('click', function(e) {
6181
6182 e.preventDefault();
6183 let el = $(this).parent('.collapser')[0];
6184 $.bmi.collapsers.toggle(el);
6185
6186 });
6187
6188 $('.bmi_will_collapse').on('change', function(e) {
6189
6190 e.preventDefault();
6191 let doNotShow = false;
6192 if (this.getAttribute('type') == 'radio') {
6193 if (this.getAttribute('value') == 'false') {
6194 doNotShow = true;
6195 }
6196 }
6197
6198 $child = $(this).data('if-checked');
6199 if (this.checked === true && !doNotShow) {
6200 $('#' + $child).show(300);
6201 } else $('#' + $child).hide(300);
6202
6203 });
6204
6205 $('.collapser-openner').on('click', function(e) {
6206
6207 e.preventDefault();
6208 let group = 'configuration';
6209 let el = $(this).data('el');
6210 if ($(this).data('group')) {
6211 group = $(this).data('group');
6212 }
6213
6214 $.bmi.collapsers.open(el, group);
6215
6216 setTimeout(function() {
6217 $([document.documentElement, document.body]).animate({
6218 scrollTop: $(el).offset().top - 50 + 'px'
6219 }, 300);
6220 }, 300);
6221
6222 });
6223
6224 (function() {
6225
6226 let $triggers = $('.bmi_will_collapse');
6227 for (let i = 0; i < $triggers.length; ++i) {
6228
6229 let doNotShow = false;
6230 $trigger = $triggers[i];
6231 $child = $($trigger).data('if-checked');
6232
6233 if ($trigger.getAttribute('type') == 'radio') {
6234 if ($trigger.getAttribute('value') == 'false') {
6235 doNotShow = true;
6236 }
6237 }
6238
6239 if ($trigger.checked === true) {
6240 $('#' + $child).show();
6241 } else $('#' + $child).hide();
6242
6243 }
6244
6245 })();
6246
6247 });jQuery(document).ready(function($) {
6248
6249 var firsttime = true;
6250 var initialized = false;
6251 $.bmi.crons = function() {
6252
6253 if (initialized === true) return;
6254 else initialized = true;
6255
6256 if (location.href.includes("crons=true") && location.href.includes("page=backup-migration")){
6257 setTimeout(function(){
6258 $('#i-backup-cron').click();
6259 },300);
6260 }
6261
6262 function getCronResult(settings, done) {
6263 $.bmi.ajax('calculate-cron', settings).then(function(res) {
6264
6265 if (res.status == 'success') {
6266 done(res);
6267 } else {
6268 done(false);
6269 $.bmi._msg(res);
6270 }
6271
6272 }).catch(function(error) {
6273
6274 done(false);
6275 console.log(error);
6276
6277 });
6278 }
6279
6280 function settings_changed() {
6281
6282 let settings = {
6283 type: $('[data-id="cron-period"]').attr('data-selected'),
6284 day: $('[data-id="cron-day"]').attr('data-selected'),
6285 week: $('[data-id="cron-week"]').attr('data-selected'),
6286 hour: $('[data-id="cron-hour"]').attr('data-selected'),
6287 minute: $('[data-id="cron-minute"]').attr('data-selected'),
6288 keep: $('[data-id="cron-keep-backups"]').attr('data-selected'),
6289 enabled: ((!$('#cron-btn-toggle')[0].checked === true) ? true : false),
6290 remote_ping: ($('#remote-ping').prop('checked') ? 'true' : 'false')
6291 }
6292
6293 getCronResult(settings, function(res) {
6294
6295 if (res.status === 'success' && res !== false) {
6296
6297 $('.cron-time-server').tooltipster('option', 'interactive', false);
6298 $('.cron-time-server').tooltipster('option', 'contentAsHTML', true);
6299
6300 if ($('#cron-btn-toggle')[0].checked === true) res.data = '---';
6301 $('.cron-time-server').tooltipster('content', '<b>' + $('#bmi-next-cron').text() + '</b>' + res.data + '<br>' + '<b>' + $('#bmi-current-time').text() + '</b>' + res.currdata);
6302
6303 if (!firsttime){
6304 if (res.local_site) {
6305 $.bmi.alert('warning', $('#bmi-cron-local-site').text(), 30000);
6306 } else if (!res.ping_working) {
6307 $.bmi.alert('warning', $('#bmi-cron-ping-server').text(), 30000);
6308 } else {
6309 $.bmi.alert('success', $('#bmi-cron-updated').text(), 1500);
6310 }
6311 }
6312 else firsttime = false;
6313
6314 } else {
6315
6316 if (!firsttime)
6317 $.bmi.alert('error', $('#bmi-cron-updated-fail').text(), 2500);
6318 else firsttime = false;
6319
6320 }
6321
6322 });
6323
6324 }
6325
6326 $('#bmi').on('change', '[data-id="cron-period"]', function(e) {
6327
6328 settings_changed();
6329 adjust_text(e.target.getAttribute('data-selected'));
6330
6331 });
6332
6333 $('#bmi').on('change', '[data-id="cron-day"]', function(e) {
6334
6335 let val = e.target.getAttribute('data-selected');
6336 settings_changed();
6337
6338 });
6339 $('#bmi').on('change', '[data-id="cron-week"]', function(e) {
6340
6341 let val = e.target.getAttribute('data-selected');
6342 settings_changed();
6343
6344 });
6345
6346 $('#bmi').on('change', '[data-id="cron-hour"]', function(e) {
6347
6348 let val = e.target.getAttribute('data-selected');
6349 settings_changed();
6350
6351 });
6352
6353 $('#bmi').on('change', '[data-id="cron-minute"]', function(e) {
6354
6355 let val = e.target.getAttribute('data-selected');
6356 settings_changed();
6357
6358 });
6359
6360 $('#bmi').on('change', '[data-id="cron-keep-backups"]', function(e) {
6361
6362 let val = e.target.getAttribute('data-selected');
6363 settings_changed();
6364
6365 });
6366
6367 $('#bmi').on('change', '#remote-ping', function(e) {
6368
6369 settings_changed();
6370
6371 });
6372
6373 $('#i-backup-cron').on('click', function() {
6374
6375 if ($('.cron-backups').find('.turned-off').is(':visible')) {
6376
6377 $('#cron-btn-toggle').prop('checked', false);
6378 $('.cron-backups').removeClass('disabled');
6379 settings_changed();
6380
6381 $('.cron-backups').find('.turned-on').css({
6382 opacity: 0
6383 });
6384 $('.cron-backups').find('.turned-off').css({
6385 opacity: 0
6386 });
6387 $('.cron-backups').find('.turned-on').show();
6388 $('.cron-backups').find('.turned-on').css({
6389 opacity: 1
6390 });
6391 setTimeout(function() {
6392 $('.cron-backups').find('.turned-off').hide();
6393 }, 300);
6394 }
6395
6396 });
6397
6398 $('#cron-btn-toggle').on('change', function() {
6399
6400 if (!this.checked) {
6401 $('.cron-backups').removeClass('disabled');
6402 } else {
6403 $('.cron-backups').addClass('disabled');
6404 }
6405
6406 settings_changed();
6407
6408 });
6409
6410 function adjust_text(val) {
6411 if (val == 'month') {
6412
6413 $('.cron-the').show();
6414 $('[data-id="cron-day"]').show();
6415 $('[data-id="cron-week"]').hide();
6416 $('#cron-on-word').show();
6417
6418 } else if (val == 'week') {
6419
6420 $('.cron-the').hide();
6421 $('[data-id="cron-day"]').hide();
6422 $('[data-id="cron-week"]').show();
6423 $('#cron-on-word').show();
6424
6425 } else {
6426
6427 $('.cron-the').hide();
6428 $('[data-id="cron-day"]').hide();
6429 $('[data-id="cron-week"]').hide();
6430 $('#cron-on-word').hide();
6431
6432 }
6433 }
6434
6435 (function() {
6436
6437 adjust_text($('[data-id="cron-period"]')[0].getAttribute('data-selected'));
6438 settings_changed();
6439
6440 })();
6441
6442 }
6443
6444 });jQuery(document).ready(function($) {
6445
6446 $.bmi.dropdowns = {
6447 init: function() {
6448 let $r = $('#bmi');
6449
6450 function scanSelects() {
6451
6452 let selects = $r.find('select');
6453 for (let i = 0; i < selects.length; ++i) {
6454
6455 let select = selects[i];
6456 handleSelect(select);
6457
6458 }
6459
6460 }
6461
6462 function createDropdown(title = '---') {
6463
6464 let dropdown = $('.dropdown-template').clone();
6465 dropdown.removeClass('dropdown-template');
6466 dropdown.find('.dropdown-title').text(title);
6467
6468 return dropdown;
6469
6470 }
6471
6472 function createOption(value = 'null', text = '---') {
6473
6474 let option = $('.option-template').clone();
6475 option.removeClass('option-template');
6476 option.attr('data-value', value);
6477 option.text(text);
6478
6479 return option;
6480
6481 }
6482
6483 function longestOption(options) {
6484
6485 let longest = '';
6486 for (let i = 0; i < options.length; ++i) {
6487
6488 let len = options[i].innerText;
6489 if (longest.length < len.length) longest = len;
6490
6491 }
6492
6493 return longest;
6494
6495 }
6496
6497 function handleSelect(select) {
6498
6499 if (select.style.display != 'none') {
6500
6501 select.style.display = 'none';
6502 let $select = $(select);
6503 let options = $select.find('option');
6504 let longest = longestOption(options);
6505 let dropdown = createDropdown(longest);
6506 dropdown = handleOptions(dropdown, options);
6507
6508 let def = options[0].value;
6509 let parent = select.getAttribute('data-parent');
6510 let classes = select.getAttribute('data-classes');
6511 let isHidden = select.getAttribute('data-hide') === 'true' ? true : false;
6512 if ($select.attr('data-def')) def = $select.attr('data-def');
6513
6514 $(dropdown).attr('data-id', select.id);
6515 $(dropdown).attr('class', ((classes != null ? classes : '') + ' bmi-dropdown').trim());
6516 $(dropdown).insertBefore($select);
6517
6518 if (parent !== null) {
6519 let num = parseInt(Math.random() * (987654321 - 123456789) + 123456789);
6520 $(dropdown).attr('data-optioner', num);
6521 $(dropdown).find('.dropdown-options').attr('data-oparent', num);
6522 $(dropdown).find('.dropdown-options').addClass('optioner-' + num);
6523 $(dropdown).find('.dropdown-options').appendTo(parent);
6524 $(parent).css({ position: 'relative' });
6525 }
6526
6527 let clone = $(dropdown).clone();
6528 if (!clone) return;
6529
6530 clone[0].style.visibility = 'hidden';
6531 $r.append(clone);
6532 let width = clone.width();
6533 clone.remove();
6534
6535 $.bmi.setOption($(dropdown), null, def);
6536 let fixedWidth = $select.attr('data-width');
6537 if (fixedWidth) width = parseInt(fixedWidth);
6538 $(dropdown).find('.dropdown-title')[0].style.minWidth = (width + 10) + 'px';
6539 let readonly = $select.attr('data-readonly');
6540 if (readonly == 'true') {
6541 $(dropdown).find('.dropdown-title')[0].style.pointerEvents = 'none';
6542 $(dropdown).closest('.bmi-dropdown')[0].style.background = '#f1f1f1';
6543 }
6544 if (isHidden) $(dropdown).hide();
6545
6546 }
6547
6548 }
6549
6550 function handleOptions(dropdown, options) {
6551
6552 let list = $(dropdown).find('.dropdown-options');
6553 if ($(dropdown).attr('data-optioner')) {
6554 list = $('.optioner-' + $(dropdown).attr('data-optioner'));
6555 }
6556
6557 for (let i = 0; i < options.length; ++i) {
6558
6559 let option = options[i];
6560 list.append(createOption(option.value, option.innerText));
6561
6562 }
6563
6564 return dropdown;
6565
6566 }
6567
6568 function toggleDropdown($dropdown) {
6569
6570 let $list = $dropdown.find('.dropdown-options');
6571 if ($dropdown.attr('data-optioner')) {
6572 $list = $('.optioner-' + $dropdown.attr('data-optioner'));
6573 $list.css({
6574 position: 'absolute'
6575 });
6576
6577 let width = $dropdown[0].offsetWidth;
6578 let left = $dropdown[0].offsetLeft + 240;
6579 let top = $dropdown[0].offsetTop + $dropdown[0].offsetHeight + 5;
6580 $list.css({
6581 maxWidth: width + 'px',
6582 minWidth: width + 'px',
6583 top: top + 'px',
6584 left: left + 'px'
6585 });
6586 }
6587
6588 if ($list.hasClass('dropdown-open')) {
6589
6590 $dropdown.removeClass('active');
6591 $list.removeClass('dropdown-open');
6592 $list.hide(300);
6593
6594 } else {
6595
6596 $dropdown.addClass('active');
6597 $list.addClass('dropdown-open');
6598 $list.addClass('ignored-open');
6599 $list.show(300);
6600
6601 }
6602
6603 }
6604
6605 // Initlaization
6606 scanSelects();
6607
6608 // Init crons after init of drop
6609 $.bmi.crons();
6610
6611 // Events
6612 $r.on('click', function(e) {
6613
6614 if (!($(e.target).hasClass('bmi-dropdown') || e.target.closest('.bmi-dropdown'))) $.bmi.hideAllLists();
6615
6616 });
6617
6618 $r.on('click', '.dropdown-title', function(e) {
6619
6620 $dropdown = $(e.target.closest('.bmi-dropdown'));
6621 toggleDropdown($dropdown);
6622 $.bmi.hideAllLists();
6623
6624 });
6625
6626 $r.on('click', '.dropdown-option', function(e) {
6627
6628 if ($(e.target.closest('.dropdown-options')).attr('data-oparent')) {
6629 $dropdown = $('.bmi-dropdown[data-optioner="' + $(e.target.closest('.dropdown-options')).attr('data-oparent') + '"]');
6630 $option = $(e.target);
6631 $.bmi.setOption($dropdown, $option);
6632 } else {
6633 $dropdown = $(e.target.closest('.bmi-dropdown'));
6634 $option = $(e.target);
6635 $.bmi.setOption($dropdown, $option);
6636 }
6637
6638 });
6639 }
6640 }
6641
6642 });
6643 jQuery(document).ready(function ($) {
6644 var backups = {};
6645 var bacupisDone = false,
6646 progressIsDone = false;
6647 var timeouter,
6648 curdivs = 0,
6649 iprogres;
6650 var stopBackgroundBackup = false;
6651 let tryingToAbortBackup = false;
6652 let totalSize = 0,
6653 totalExcluded = 0,
6654 totalSizesI = 0;
6655 let current_restore = null;
6656 let current_last = null;
6657 let latest_delete = {};
6658 let backupOnGoing = false;
6659 let restoreOnGoing = false;
6660 let ongoing_interval = 10000;
6661 let triggeredByIntv = false;
6662 let backgroundBackup = false;
6663 let backgroundName = null;
6664 let headers_for_middleware = false;
6665 let cli_quickmigration = false;
6666 let restoreCLI = false;
6667 let autoLog = {};
6668 let latestLines = [];
6669 let lineTimeoutAppend = null;
6670 let isLineAppendTimeoutRunning = false;
6671 let latestStep = "";
6672 let currentGDriveIdRestoration = null;
6673 let restoreStartTime = null;
6674 let downloadBackupFinished = false;
6675 let restoreLogs = null;
6676 window.bmi = {};
6677
6678 function isChecked(id) {
6679 $cb = $("#" + id).is(":checked");
6680 if ($cb === true) return "true";
6681 else return "false";
6682 }
6683
6684 function getSelectedSize() {
6685 size = 0;
6686
6687 if ($("#files-group-backup").is(":checked")) {
6688 if ($("#files-group-plugins").is(":checked")) {
6689 size += parseInt(
6690 $('label[for="files-group-plugins"]').find(".value").attr("bytes"),
6691 );
6692 }
6693
6694 if ($("#files-group-uploads").is(":checked")) {
6695 size += parseInt(
6696 $('label[for="files-group-uploads"]').find(".value").attr("bytes"),
6697 );
6698 }
6699
6700 if ($("#files-group-themes").is(":checked")) {
6701 size += parseInt(
6702 $('label[for="files-group-themes"]').find(".value").attr("bytes"),
6703 );
6704 }
6705
6706 if ($("#files-group-other-contents").is(":checked")) {
6707 size += parseInt(
6708 $('label[for="files-group-other-contents"]')
6709 .find(".value")
6710 .attr("bytes"),
6711 );
6712 }
6713
6714 if ($("#files-group-wp-install").is(":checked")) {
6715 size += parseInt(
6716 $('label[for="files-group-wp-install"]').find(".value").attr("bytes"),
6717 );
6718 }
6719 }
6720
6721 if ($("#database-group-backup").is(":checked")) {
6722 size += parseInt(
6723 $('label[for="database-group-backup"]').find(".value").attr("bytes"),
6724 );
6725 }
6726
6727 return size;
6728 }
6729
6730 function locationSet(location, res) {
6731 totalSizesI += 1;
6732 if (location != "database") totalSize += parseInt(res["bytes"]);
6733 totalExcluded += parseInt(res["excluded"]);
6734 if (res["bytes"] <= 10000) {
6735 $("#bmi-scan-" + location)
6736 .find(".value")
6737 .text("(0 MB)");
6738 } else {
6739 let megas = (parseInt(res["bytes"]) / 1024 / 1024).toFixed(2);
6740 $("#bmi-scan-" + location)
6741 .find(".value")
6742 .text("(" + megas + " MB)");
6743 }
6744 $("#bmi-scan-" + location)
6745 .find(".value")
6746 .attr("bytes", parseInt(res["bytes"]));
6747 $("#bmi-scan-" + location)
6748 .find(".value")
6749 .attr("excluded", parseInt(res["excluded"]));
6750 if (totalSizesI == 6) doSomethingWithTotal();
6751 }
6752
6753 function scanAndSet(location) {
6754 return new Promise(function (resolve) {
6755 $.bmi
6756 .ajax("scan-directory", {
6757 folder: location,
6758 })
6759 .then(function (res) {
6760 setTimeout(resolve, 150);
6761 locationSet(location, res);
6762 })
6763 .catch(function (error) {
6764 setTimeout(resolve, 150);
6765 locationSet(location, {
6766 bytes: "0",
6767 excluded: "0",
6768 readable: "0 B",
6769 });
6770 });
6771 });
6772 }
6773
6774 function resetSpinners(resources, withTotal = false) {
6775 if (withTotal) {
6776 $("#bmi-scan-total")
6777 .find(".value")
6778 .html('(<div class="spinner-loader"></div>)');
6779 $("#bmi-scan-total").find(".value").removeAttr("bytes");
6780 }
6781
6782 for (let i = 0; i < resources.length; i++) {
6783 $("#bmi-scan-" + resources[i])
6784 .find(".value")
6785 .html('(<div class="spinner-loader"></div>)');
6786 $("#bmi-scan-" + resources[i])
6787 .find(".value")
6788 .removeAttr("bytes");
6789 }
6790
6791 $("#esta-exclude").html('(<div class="spinner-loader"></div>)');
6792 $("#esta-exclude-total").html('(<div class="spinner-loader"></div>)');
6793 $("#esta-size-for").html('(<div class="spinner-loader"></div>)');
6794
6795 let preloader_divs = "";
6796 for (let i = 0; i < 12; ++i) preloader_divs += "<div></div>";
6797 $(".spinner-loader").html(preloader_divs).addClass("lds-spinner");
6798 }
6799
6800 async function scanDirectories() {
6801 totalSizesI = 0;
6802 totalSize = 0;
6803 totalExcluded = 0;
6804
6805 let resources = [
6806 "plugins",
6807 "uploads",
6808 "themes",
6809 "contents_others",
6810 "wordpress",
6811 "database",
6812 ];
6813
6814 await resetSpinners(resources, true);
6815 for (let i = 0; i < resources.length; i++) {
6816 await scanAndSet(resources[i]);
6817 }
6818 }
6819
6820 function doSomethingWithTotal() {
6821 $("#bmi-scan-total")
6822 .find(".value")
6823 .text("(" + $.bmi.bytesToHuman(totalSize) + ")");
6824 $("#bmi-scan-total").find(".value").attr("bytes", parseInt(totalSize));
6825 updateEsta();
6826 }
6827
6828 // Live log
6829 $("#live-log-toggle").on("click", function () {
6830 if ($(".expanded-logs").length > 0) {
6831 $(this).text($(this).data("hide"));
6832 } else $(this).text($(this).data("show"));
6833
6834 $("#live-log-wrapper").toggleClass("expanded-logs");
6835 });
6836
6837 async function stopBackgroundProcess() {
6838 await forceBackupToStop(true);
6839 window.onbeforeunload = null;
6840 window.location.reload();
6841 }
6842
6843 // Stop backup
6844 $("#backup-stop").on("click", function () {
6845 $.bmi.modal("backup-progress-modal").close();
6846 $.bmi.modal("freeze-loading-modal").open();
6847 tryingToAbortBackup = true;
6848 $.bmi
6849 .ajax("stop-backup", {})
6850 .then(function (res) {
6851 if (res.status == "success") {
6852 $.bmi.modal("freeze-loading-modal").close();
6853 $.bmi.alert("success", $("#bmi-abort-soon").text(), 3000);
6854 } else $.bmi._msg(res);
6855 })
6856 .catch(function (error) {
6857 //
6858 });
6859 });
6860
6861 // Is backup running
6862 function isRunning(mute = false, cb = function () { }) {
6863 $.bmi
6864 .ajax("is-running-backup", {})
6865 .then(function (res) {
6866 if (typeof res.ongoing != "undefined") {
6867 $.bmi.fillOnGoing(res.ongoing);
6868 if (typeof res.ongoing.queue == 'object' || typeof res.ongoing.current_upload == 'object') {
6869 if (!(res.ongoing.queue instanceof Array) || !(res.ongoing.current_upload instanceof Array)) {
6870 if (typeof (globalBMIKeepAlive) == "function") {
6871 globalBMIKeepAlive();
6872 }
6873 }
6874 else {
6875 $.bmi.ajax('check-not-uploaded-backups').then(function(res) {
6876 // console.warn('Checking for not uploaded backups...');
6877 // console.log(res);
6878 });
6879 }
6880 }
6881 }
6882
6883 if (res.status == "success") cb(false);
6884 else {
6885 if (mute == true && res.status == "msg") cb(true);
6886 else {
6887 $.bmi.modal("freeze-loading-modal").close();
6888 $.bmi._msg(res);
6889 }
6890 }
6891 })
6892 .catch(function (error) {
6893 cb(false);
6894 $.bmi.modal("freeze-loading-modal").close();
6895 //
6896 });
6897 }
6898
6899 function forceBackupToStop(withNotice = false) {
6900 return new Promise((resolve) => {
6901 $.bmi
6902 .ajax("force-backup-to-stop")
6903 .then(function (res) {
6904 if (withNotice) {
6905 $.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
6906 }
6907
6908 return resolve(true);
6909 })
6910 .catch(function (error) {
6911 if (withNotice) {
6912 $.bmi.alert("error", $("#failed-to-stop").text(), 6000);
6913 }
6914
6915 return resolve(false);
6916 });
6917 });
6918 }
6919
6920 $("#bmi-force-backup-to-stop").on("click", function (e) {
6921 e.preventDefault();
6922 $.bmi.alert("info", $("#bmi-force-stop-in-progress").text(), 3000);
6923 $.bmi
6924 .ajax("force-backup-to-stop")
6925 .then(function (res) {
6926 $.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
6927 })
6928 .catch(function (error) {
6929 $.bmi.alert("error", $("#failed-to-stop").text(), 6000);
6930 });
6931 });
6932
6933 $("#bmi-force-restore-to-stop").on("click", function (e) {
6934 e.preventDefault();
6935 $.bmi.alert("info", $("#bmi-force-stop-in-progress").text(), 3000);
6936 $.bmi
6937 .ajax("force-restore-to-stop")
6938 .then(function (res) {
6939 $.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
6940 })
6941 .catch(function (error) {
6942 $.bmi.alert("error", $("#failed-to-stop").text(), 6000);
6943 });
6944 });
6945
6946 $("#bmi_restore_tbody").on("click", ".bc-unlocked-btn", function (e) {
6947 e.preventDefault();
6948 let $el = e.target;
6949 let name = $el.closest("tr").querySelector(".br_name").innerText.trim();
6950
6951 $.bmi
6952 .ajax("lock-backup", {
6953 filename: name,
6954 })
6955 .then(function (res) {
6956 if (res.status == "success") {
6957 $.bmi.alert("success", $("#bmi-lock-success").text(), 6000);
6958 $($el.closest("tr").querySelector(".bc-unlocked-btn")).hide();
6959 $($el.closest("tr").querySelector(".bc-locked-btn")).show();
6960 } else {
6961 $.bmi.alert("error", $("#bmi-lock-error").text(), 8000);
6962 console.error("BMI Backend error: ", res);
6963 }
6964 })
6965 .catch(function (error) {
6966 //
6967 });
6968 });
6969
6970 function updateEsta() {
6971 let pluginsSize = parseInt(
6972 $('label[for="files-group-plugins"]').find(".value").attr("bytes"),
6973 );
6974 let uploadsSize = parseInt(
6975 $('label[for="files-group-uploads"]').find(".value").attr("bytes"),
6976 );
6977 let themesSize = parseInt(
6978 $('label[for="files-group-themes"]').find(".value").attr("bytes"),
6979 );
6980 let otherSize = parseInt(
6981 $('label[for="files-group-other-contents"]').find(".value").attr("bytes"),
6982 );
6983 let wpSize = parseInt(
6984 $('label[for="files-group-wp-install"]').find(".value").attr("bytes"),
6985 );
6986 let dbSize = parseInt(
6987 $('label[for="database-group-backup"]').find(".value").attr("bytes"),
6988 );
6989
6990 let pluginsExcluded = parseInt(
6991 $('label[for="files-group-plugins"]').find(".value").attr("excluded"),
6992 );
6993 let uploadsExcluded = parseInt(
6994 $('label[for="files-group-uploads"]').find(".value").attr("excluded"),
6995 );
6996 let themesExcluded = parseInt(
6997 $('label[for="files-group-themes"]').find(".value").attr("excluded"),
6998 );
6999 let otherExcluded = parseInt(
7000 $('label[for="files-group-other-contents"]')
7001 .find(".value")
7002 .attr("excluded"),
7003 );
7004 let wpExcluded = parseInt(
7005 $('label[for="files-group-wp-install"]').find(".value").attr("excluded"),
7006 );
7007 let dbExcluded = parseInt(
7008 $('label[for="database-group-backup"]').find(".value").attr("excluded"),
7009 );
7010
7011 let databaseExcludedSizeInBytes = 0;
7012 let excludedSize =
7013 pluginsExcluded +
7014 uploadsExcluded +
7015 themesExcluded +
7016 otherExcluded +
7017 wpExcluded +
7018 dbExcluded;
7019 let totalSize =
7020 excludedSize +
7021 pluginsSize +
7022 uploadsSize +
7023 themesSize +
7024 otherSize +
7025 wpSize +
7026 dbSize;
7027
7028 if ($("#files-group-backup").is(":checked")) {
7029 if (!$("#files-group-plugins").is(":checked"))
7030 excludedSize += pluginsSize;
7031 if (!$("#files-group-uploads").is(":checked"))
7032 excludedSize += uploadsSize;
7033 if (!$("#files-group-themes").is(":checked")) excludedSize += themesSize;
7034 if (!$("#files-group-other-contents").is(":checked"))
7035 excludedSize += otherSize;
7036 if (!$("#files-group-wp-install").is(":checked")) excludedSize += wpSize;
7037 } else {
7038 excludedSize +=
7039 pluginsSize + uploadsSize + themesSize + otherSize + wpSize;
7040 }
7041
7042 if ($("#database-group-backup").is(":checked")) {
7043 if (
7044 $("#bmi_total_size_excluded").length > 0 &&
7045 $("#bmi-pro-db-tables-exclusion").is(":checked")
7046 ) {
7047 databaseExcludedSizeInBytes =
7048 parseFloat($("#bmi_total_size_excluded").text().split("/")[0]) *
7049 1024 *
7050 1024;
7051 if (!isNaN(databaseExcludedSizeInBytes))
7052 excludedSize += databaseExcludedSizeInBytes;
7053 }
7054 } else excludedSize += dbSize;
7055
7056 $("#esta-exclude").text($.bmi.bytesToHuman(excludedSize));
7057 $("#esta-exclude-total").text($.bmi.bytesToHuman(totalSize));
7058 $("#esta-size-for").text($.bmi.bytesToHuman(totalSize - excludedSize));
7059 }
7060
7061 $("#files-group-backup").on("change", updateEsta);
7062 $("#database-group-backup").on("change", updateEsta);
7063 $(".basic-file-exlusion").on("change", 'input[type="checkbox"]', updateEsta);
7064
7065 $("#bmi_restore_tbody").on("click", ".bc-locked-btn", function (e) {
7066 e.preventDefault();
7067 if ($(e.target).hasClass("forever")) return;
7068 let $el = e.target;
7069 let name = $el.closest("tr").querySelector(".br_name").innerText.trim();
7070
7071 $.bmi
7072 .ajax("unlock-backup", {
7073 filename: name,
7074 })
7075 .then(function (res) {
7076 if (res.status == "success") {
7077 $.bmi.alert("success", $("#bmi-unlock-success").text(), 3000);
7078 $($el.closest("tr").querySelector(".bc-locked-btn")).hide();
7079 $($el.closest("tr").querySelector(".bc-unlocked-btn")).show();
7080 } else {
7081 $.bmi.alert("error", $("#bmi-unlock-error").text(), 3000);
7082 console.error("BMI Backend error: ", res);
7083 }
7084 })
7085 .catch(function (error) {
7086 //
7087 });
7088 });
7089
7090 $("#bmi_restore_tbody").on("click", ".bc-url-btn", function (e) {
7091 e.preventDefault();
7092 let $el = e.target;
7093 if ($el.closest("svg").classList.contains("disabled")) return;
7094
7095 let url = $el
7096 .closest("tr")
7097 .querySelector(".bc-download-btn")
7098 .getAttribute("href");
7099 $.bmi.clipboard(url, bmiVariables.urlCopies);
7100 });
7101
7102 $("#stg-tbody-table").on("click", ".bc-stg-url-btn", function (e) {
7103 let $el = e.target;
7104
7105 let url = $el
7106 .closest("tr")
7107 .querySelector(".stg-tr-url-el")
7108 .getAttribute("href");
7109 $.bmi.clipboard(url, bmiVariables.urlCopies);
7110 });
7111
7112 // delete single backup
7113 $("#bmi_restore_tbody").on("click", ".bc-remove-btn", function (e) {
7114
7115 let $el = e.target;
7116 let name = $el.closest('tr').querySelector('.br_name').innerText.trim();
7117 let isCloud = Array.from($el.closest('tr').querySelectorAll('[class*="strg-"]')).filter(el => !el.classList.contains('strg-local')).some(el => el.classList.contains('img-green'));
7118 let notOnLocalTr = $($el.closest('tr')).data('is-local') == 'no' ? true : false;
7119
7120 latest_delete[name] = {
7121 hash: $el.closest("tr").getAttribute("md5"),
7122 isCloud: isCloud
7123 };
7124
7125 $("#delete-confirm-modal").find(".text2").hide();
7126 $("#delete-confirm-modal").find(".text3").hide();
7127 $("#delete-confirm-modal").find(".text4").hide();
7128 $("#delete-confirm-modal").find(".text1").show();
7129
7130 if (isCloud) $(".bmi-cloud-removal").show();
7131 else $(".bmi-cloud-removal").hide();
7132
7133 $("#remove-cloud-backup-as-well")[0].checked = false;
7134
7135 if (isCloud && notOnLocalTr) {
7136 $("#remove-cloud-backup-as-well")[0].checked = true;
7137 $(".bmi-cloud-removal").hide();
7138 $("#delete-confirm-modal").find(".text1").hide();
7139 $("#delete-confirm-modal").find(".text2").hide();
7140 $("#delete-confirm-modal").find(".text3").hide();
7141 $("#delete-confirm-modal").find(".text4").show();
7142 $(".del-more-than-one").hide();
7143 $(".del-only-one").show();
7144 }
7145
7146 $.bmi.modal("delete-confirm-modal").open();
7147 });
7148
7149 $("#sure_delete").on("click", function (e) {
7150 e.preventDefault();
7151
7152 $.bmi.modal("delete-confirm-modal").close();
7153
7154 // Reset/Setup delete progress modal
7155 $("#delete-progress-modal .progress-active-bar").css("width", "0%");
7156 $("#delete-progress-modal .progress-percentage").css("left", "0%").text("0%");
7157 $("#delete_current_step").text("Preparing deletion...");
7158 $("#cancel_delete").removeClass("disabled").removeAttr("disabled").css("pointer-events", "").css("opacity", "");
7159
7160 $.bmi.modal("delete-progress-modal").open();
7161
7162 let totalBackups = Object.keys(latest_delete).length;
7163 let deletedBackups = 0;
7164 let isCancelled = false;
7165
7166 // Handle Cancel button click
7167 $("#cancel_delete").off("click").on("click", function(event) {
7168 event.preventDefault();
7169 if ($(this).hasClass("disabled")) return;
7170 isCancelled = true;
7171 $.bmi.modal("delete-progress-modal").close();
7172 $.bmi.reloadBackups();
7173 $.bmi.alert("warning", "Backup deletion was cancelled.", 3000);
7174 });
7175
7176 let processDeletions = function(backupsList) {
7177 if (isCancelled) return;
7178
7179 // Show which file is being deleted
7180 let currentFile = Object.keys(backupsList)[0];
7181 if (currentFile) {
7182 $("#delete_current_step").text("Deleting " + currentFile + "...");
7183 }
7184
7185 // If it's the last batch, disable Cancel
7186 if (Object.keys(backupsList).length <= 5) {
7187 $("#cancel_delete").addClass("disabled").attr("disabled", true).css("pointer-events", "none").css("opacity", "0.5");
7188 }
7189
7190 $.bmi
7191 .ajax("delete-backup", {
7192 backups: backupsList,
7193 deleteCloud: $("#remove-cloud-backup-as-well")[0].checked
7194 ? "yes"
7195 : "no"
7196 })
7197 .then(function (res) {
7198 if (isCancelled) return;
7199
7200 if (res.status == "continue" && res.remaining) {
7201 let remainingCount = Object.keys(res.remaining).length;
7202 deletedBackups = totalBackups - remainingCount;
7203 let percent = Math.round((deletedBackups / totalBackups) * 100);
7204
7205 $("#delete-progress-modal .progress-active-bar").css("width", percent + "%");
7206 $("#delete-progress-modal .progress-percentage").css("left", percent + "%").text(percent + "%");
7207
7208 processDeletions(res.remaining);
7209 } else {
7210 // Completed
7211 $("#delete-progress-modal .progress-active-bar").css("width", "100%");
7212 $("#delete-progress-modal .progress-percentage").css("left", "100%").text("100%");
7213 $("#delete_current_step").text("Finished!");
7214
7215 latest_delete = {};
7216 $("#remove-cloud-backup-as-well")[0].checked = false;
7217
7218 setTimeout(function() {
7219 $.bmi.modal("delete-progress-modal").close();
7220 if (res.status == "success") {
7221 $.bmi.reloadBackups();
7222 $.bmi.alert("success", $("#bmi-remove-success").text(), 3000);
7223 } else {
7224 $.bmi.alert("warning", $("#bmi-remove-error").text(), 3000);
7225 console.error("BMI Backend error: ", res);
7226 }
7227 }, 500);
7228 }
7229 })
7230 .catch(function (error) {
7231 if (isCancelled) return;
7232 $.bmi.modal("delete-progress-modal").close();
7233 });
7234 };
7235
7236 processDeletions(latest_delete);
7237 });
7238
7239 $(".bmi-send-troubleshooting-logs").on("click", function (e) {
7240 e.preventDefault();
7241
7242 $.bmi.alert("info", $("#bmi-support-send-start").text(), 6000);
7243 $(".bmi-send-troubleshooting-logs").addClass("disabled");
7244
7245 let errorVisible = false;
7246 let stagingErrorVisible = false;
7247
7248 if ($("#error-modal").is(":visible")) {
7249 $.bmi.modal("error-modal").close();
7250 errorVisible = true;
7251 stagingErrorVisible = false;
7252 }
7253
7254 if ($("#staging-error-modal").is(":visible")) {
7255 $.bmi.modal("staging-error-modal").close();
7256 errorVisible = false;
7257 stagingErrorVisible = true;
7258 }
7259
7260 $.bmi.modal("freeze-loading-modal").open();
7261
7262 let logsSource = $("#after-logs-sent-modal").attr("data-error-source");
7263 $.bmi
7264 .ajax("send-troubleshooting-logs", { source: logsSource })
7265 .then(function (res) {
7266 $.bmi.modal("freeze-loading-modal").close();
7267 if (res.status == "success") {
7268 $("#bmi-support-code-generated").text(res.code);
7269 setTimeout(function () {
7270 $.bmi.alert("success", $("#bmi-support-send-success").text(), 4000);
7271 $.bmi.modal("after-logs-sent-modal").open();
7272 }, 300);
7273 } else {
7274 $.bmi.alert("error", $("#bmi-support-send-fail").text(), 6000);
7275 if (errorVisible) $.bmi.modal("error-modal").open();
7276 if (stagingErrorVisible) $.bmi.modal("staging-error-modal").open();
7277 }
7278
7279 $(".bmi-send-troubleshooting-logs").removeClass("disabled");
7280 })
7281 .catch(function (error) {
7282 $.bmi.modal("freeze-loading-modal").close();
7283 $.bmi.alert("success", $("#bmi-support-send-fail").text(), 6000);
7284 alert(
7285 "Something went wrong on your browser side and we could not send your logs to support team.",
7286 );
7287 $(".bmi-send-troubleshooting-logs").removeClass("disabled");
7288 if (errorVisible) $.bmi.modal("error-modal").open();
7289 if (stagingErrorVisible) $.bmi.modal("staging-error-modal").open();
7290 });
7291 });
7292
7293 $("#share-logs-allowed").on("click", function (e) {
7294 e.preventDefault();
7295 let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
7296 $.bmi.alert("success", $("#bmi-share-logs-thank-you").text(), 3000);
7297 $.bmi.modal("logs-sharing-ask-modal").close();
7298 $.bmi.modal("freeze-loading-modal").open();
7299
7300 shareLogsStatus("set_yes", function () {
7301 setTimeout(function () {
7302 handleAfterDesicionMadeSharing(newModalName);
7303 }, 300);
7304 });
7305 });
7306
7307 $("#share-logs-not-allowed").on("click", function (e) {
7308 e.preventDefault();
7309 let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
7310
7311 $.bmi.modal("logs-sharing-ask-modal").close();
7312 $.bmi.modal("freeze-loading-modal").open();
7313
7314 shareLogsStatus("set_no", function () {
7315 setTimeout(function () {
7316 handleAfterDesicionMadeSharing(newModalName);
7317 }, 300);
7318 });
7319 });
7320
7321 $("#ignore-share-log-request-for-now").on("click", function (e) {
7322 e.preventDefault();
7323 $.bmi.modal("logs-sharing-ask-modal").close();
7324 let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
7325 handleAfterDesicionMadeSharing(newModalName);
7326 });
7327
7328 function handleAfterDesicionMadeSharing(modalName) {
7329 if (modalName == "backup-prenotice") {
7330 runPrenoticeBeforeBackup();
7331 } else {
7332 $.bmi.modal("freeze-loading-modal").close();
7333 setTimeout(function () {
7334 $.bmi.modal(modalName).open();
7335 }, 300);
7336 }
7337 }
7338
7339 function isAllowedToShareLogs(cb = function () { }) {
7340 shareLogsStatus("is_allowed", function (res) {
7341 cb(res);
7342 });
7343 }
7344
7345 function shareLogsStatus(type, cb = function () { }) {
7346 // Res to: is_allowed (res.result)
7347 // allowed
7348 // not-allowed
7349 // ask
7350 //
7351 // For set_yes, set_no there is no response only status success or fail
7352
7353 if (type == "is_allowed") cb("not-allowed");
7354 else cb();
7355
7356 // DISABLED:
7357 // $.bmi.ajax('log-sharing-details', { question: type }).then(function(res) {
7358 //
7359 // if (res.status == 'success') {
7360 //
7361 // if (type == 'is_allowed') {
7362 //
7363 // if (typeof res.result != 'undefined') {
7364 // if (['allowed', 'not-allowed', 'ask'].includes(res.result)) {
7365 // cb(res.result);
7366 // } else {
7367 // cb('not-allowed')
7368 // }
7369 // }
7370 //
7371 // } else {
7372 //
7373 // cb();
7374 //
7375 // }
7376 //
7377 // } else {
7378 //
7379 // cb('error');
7380 //
7381 // }
7382 //
7383 // }).catch(function(error) {
7384 //
7385 // 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.');
7386 // cb('error');
7387 //
7388 // });
7389 }
7390
7391 $("#add-exclusion-rule").on("click", function (e) {
7392 e.preventDefault();
7393 let $template = $(".exclusion_template").clone();
7394 $template[0].classList.remove("exclusion_template");
7395 $template[0].style.display = "none";
7396
7397 $("#bmi_exclusion_rules").append($template);
7398 $template.show(300);
7399 });
7400
7401 $("#bmi_exclusion_rules").on("click", ".kill-exclusion-rule", function (e) {
7402 e.preventDefault();
7403 $el = e.target;
7404 $parent = $el.closest(".exclude-row");
7405
7406 if ($parent) {
7407 $($parent).hide(300);
7408 setTimeout(function () {
7409 $parent.remove();
7410 }, 320);
7411 }
7412 });
7413
7414 function isOnlyDB() {
7415 if (!(isChecked("database-group-backup") === "true")) {
7416 return false;
7417 } else {
7418 if (isChecked("database-group-backup") === "true") {
7419 if (!(isChecked("files-group-backup") === "true")) {
7420 return true;
7421 } else {
7422 if (
7423 isChecked("files-group-plugins") === "true" ||
7424 isChecked("files-group-themes") === "true" ||
7425 isChecked("files-group-uploads") === "true" ||
7426 isChecked("files-group-wp-install") === "true" ||
7427 isChecked("files-group-other-contents") === "true"
7428 ) {
7429 return false;
7430 }
7431 }
7432 } else return false;
7433 }
7434 }
7435
7436 function isOnlyFiles() {
7437 if (isChecked("database-group-backup") === "true") {
7438 return false;
7439 } else {
7440 if (!(isChecked("database-group-backup") === "true")) {
7441 if (
7442 isChecked("files-group-backup") === "true" &&
7443 (isChecked("files-group-plugins") === "true" ||
7444 isChecked("files-group-themes") === "true" ||
7445 isChecked("files-group-uploads") === "true" ||
7446 isChecked("files-group-wp-install") === "true" ||
7447 isChecked("files-group-other-contents") === "true")
7448 ) {
7449 return true;
7450 } else return false;
7451 } else return false;
7452 }
7453 }
7454
7455 function isOnlyPart() {
7456 if (
7457 isChecked("database-group-backup") === "true" &&
7458 isChecked("files-group-backup") === "true" &&
7459 isChecked("files-group-plugins") === "true" &&
7460 isChecked("files-group-themes") === "true" &&
7461 isChecked("files-group-uploads") === "true" &&
7462 isChecked("files-group-other-contents") === "true"
7463 ) {
7464 return false;
7465 }
7466
7467 return true;
7468 }
7469
7470 function isFilesOrDb() {
7471 if (isChecked("database-group-backup") === "true") {
7472 return true;
7473 } else {
7474 if (isChecked("files-group-backup") === "true") {
7475 if (
7476 isChecked("files-group-plugins") === "true" ||
7477 isChecked("files-group-themes") === "true" ||
7478 isChecked("files-group-uploads") === "true" ||
7479 isChecked("files-group-wp-install") === "true" ||
7480 isChecked("files-group-other-contents") === "true"
7481 ) {
7482 return true;
7483 }
7484 }
7485 }
7486
7487 return false;
7488 }
7489
7490 function setupPrenotice() {
7491 $("#prenotice-modal .prenotice").hide();
7492
7493 $(".prenotic-3").show();
7494
7495 if (isOnlyDB()) {
7496 $(".prenotic-6").show();
7497 }
7498
7499 if (isOnlyFiles()) {
7500 $(".prenotic-5").show();
7501 }
7502
7503 if (isOnlyPart()) {
7504 $(".prenotic-4").show();
7505 }
7506
7507 $("#prenotice-modal .prenotice:visible").css({
7508 background: "",
7509 });
7510 let prenotices = $("#prenotice-modal .prenotice"),
7511 sorted = [];
7512 for (let i = 0; i < prenotices.length; ++i) {
7513 if (!(prenotices[i].style.display === "none")) {
7514 sorted.push(prenotices[i]);
7515 }
7516 }
7517 for (let i = 0; i < sorted.length; i += 2) {
7518 sorted[i].style.background = "#f8f8f8";
7519 }
7520 }
7521
7522 // Backup creation
7523 $("#i-backup-creator, .i-backup-creator-trigger").on("click", function () {
7524
7525 if (!isFilesOrDb()) {
7526 $.bmi.alert("warning", $("#bmi-no-selected").text(), 3000);
7527 return;
7528 }
7529
7530 $.bmi.modal("freeze-loading-modal").open();
7531 setTimeout(function () {
7532 shareLogsStatus("is_allowed", function (res) {
7533 if (res === "ask") {
7534 $.bmi.modal("freeze-loading-modal").close();
7535 $("#logs-sharing-ask-modal").attr(
7536 "data-destination",
7537 "backup-prenotice",
7538 );
7539 setTimeout(function () {
7540 $.bmi.modal("logs-sharing-ask-modal").open();
7541 }, 300);
7542 } else {
7543 runPrenoticeBeforeBackup();
7544 }
7545 });
7546 }, 300);
7547 });
7548
7549 function runPrenoticeBeforeBackup() {
7550 $.bmi.modal("freeze-loading-modal").open();
7551
7552 setupPrenotice();
7553 isRunning(false, function () {
7554 setTimeout(function () {
7555 $.bmi.modal("freeze-loading-modal").close();
7556
7557 setTimeout(function () {
7558 $.bmi.modal("prenotice-modal").open();
7559 }, 300);
7560 }, 300);
7561 });
7562 }
7563
7564 $("#BFFSIN").on("change", function (e) {
7565 let i = parseInt(this.value);
7566 if (isNaN(i)) {
7567 this.value = 1;
7568 return;
7569 } else {
7570 if (i > 9999) {
7571 this.value = 9999;
7572 return;
7573 }
7574
7575 if (i <= 0) {
7576 this.value = 1;
7577 return;
7578 }
7579
7580 this.value = i;
7581 }
7582 });
7583
7584 function saveBtnEventHandler(e = false, type = false, cb = () => { }) {
7585 if (e) e.preventDefault();
7586
7587 let data = {};
7588 let save = type;
7589 if (e != false && type == false)
7590 save = $(this.closest(".save-action")).data("save");
7591
7592 if (!save) return;
7593
7594 if (save == "save-storage") {
7595 data["directory"] = $("#bmi_path_storage_default").val();
7596 data["access"] =
7597 $('[name="radioAccessViaLink"]:checked').val() === "true"
7598 ? "true"
7599 : "false";
7600 data["gdrive"] =
7601 $("#bmi-pro-storage-gdrive-toggle").is(":checked") === true
7602 ? "true"
7603 : "false";
7604
7605 if ($("#bmip-googledrive-path").length > 0) {
7606 data["gdrivedirname"] = $("#bmip-googledrive-path").val().trim();
7607 } else {
7608 data["gdrivedirname"] = "BACKUP_MIGRATION_BACKUPS";
7609 }
7610
7611 // onedrive
7612 data["onedrive"] =
7613 $("#bmi-pro-storage-onedrive-toggle").is(":checked") === true
7614 ? "true"
7615 : "false";
7616
7617 data["sftp"] = $("#bmi-pro-storage-sftp-toggle").is(":checked") === true ? 'true' : 'false';
7618
7619
7620 // storage ftp
7621 data['ftp'] = $('#bmi-pro-storage-ftp-toggle').is(':checked') === true ? 'true' : 'false';
7622 if ($('#bmip-ftp-host-ip').length > 0) {
7623 data['ftphostip'] = $('#bmip-ftp-host-ip').val().trim();
7624 } else {
7625 data['ftphostip'] = '';
7626 }
7627
7628 data['dropbox'] = $('#bmi-pro-storage-dropbox-toggle').is(':checked') === true ? 'true' : 'false';
7629
7630 data['pcloud'] = $('#bmi-pro-storage-pcloud-toggle').is(':checked') === true ? 'true' : 'false';
7631
7632 if ($('#bmip-ftp-user-name').length > 0) {
7633 data['ftphostusername'] = $('#bmip-ftp-user-name').val().trim();
7634 } else {
7635 data['ftphostusername'] = '';
7636 }
7637
7638 if ($('#bmip-ftp-password').length > 0) {
7639 data['ftppassword'] = $('#bmip-ftp-password').val().trim();
7640 } else {
7641 data['ftppassword'] = '';
7642 }
7643 if ($('#bmip-ftp-backup-dir').length > 0) {
7644 data['ftpdir'] = $('#bmip-ftp-backup-dir').val().trim();
7645 } else {
7646 data['ftpdir'] = '';
7647 }
7648
7649 if ($('#bmip-ftp-host-port').length > 0) {
7650 data['ftpport'] = $('#bmip-ftp-host-port').val().trim();
7651 } else {
7652 data['ftpport'] = '';
7653 }
7654
7655 data['aws'] = $('#bmi-pro-storage-aws-toggle').is(':checked') === true ? 'true' : 'false';
7656 data['wasabi'] = $('#bmi-pro-storage-wasabi-toggle').is(':checked') === true ? 'true' : 'false';
7657
7658
7659 } else if (save == 'save-file-config') {
7660
7661
7662 // Main groups
7663 data["database_group"] = isChecked("database-group-backup");
7664 data["files_group"] = isChecked("files-group-backup");
7665
7666 // Additional database exclusion rules
7667 if ($("#bmi-pro-db-tables-exclusion").length > 0) {
7668 data["db-exclude-tables-group"] = isChecked(
7669 "bmi-pro-db-tables-exclusion",
7670 );
7671 data["db-excluded-tables"] = ["empty"];
7672
7673 let excludedTables = $(".bmi_pro_tables_display").find("input:checked");
7674 if (excludedTables.length > 0) {
7675 data["db-excluded-tables"] = [];
7676 for (let i = 0; i < excludedTables.length; ++i) {
7677 data["db-excluded-tables"].push(excludedTables[i].value);
7678 }
7679 } else {
7680 data["db-exclude-tables-group"] = "false";
7681 }
7682 } else {
7683 data["db-exclude-tables-group"] = "false";
7684 data["db-excluded-tables"] = ["empty"];
7685 }
7686
7687 // Subgroup of files
7688 data["files-group-plugins"] = isChecked("files-group-plugins");
7689 data["files-group-uploads"] = isChecked("files-group-uploads");
7690 data["files-group-themes"] = isChecked("files-group-themes");
7691 data["files-group-other-contents"] = isChecked(
7692 "files-group-other-contents",
7693 );
7694 data["files-group-wp-install"] = isChecked("files-group-wp-install");
7695
7696 // Subgroup of filters of files
7697 data["files_by_filters"] = isChecked("files_by_filters");
7698 data["ex_b_fs"] = isChecked("ex_b_fs");
7699 data["BFFSIN"] = $("#BFFSIN").val() ? $("#BFFSIN").val() : "1";
7700 data["ex_b_names"] = isChecked("ex_b_names");
7701 data["ex_b_fpaths"] = isChecked("ex_b_fpaths");
7702 data["ex_b_dpaths"] = isChecked("ex_b_dpaths");
7703
7704 // Make dynamic
7705 let rules = [];
7706 let rows = $("#bmi_exclusion_rules").find(".exclude-row");
7707 for (let i = 0; i < rows.length; ++i) {
7708 let row = $(rows[i]);
7709 let txt = row.find(".exclusion_txt").val();
7710 let pos =
7711 row
7712 .find(".exclusion_position")
7713 .find(".bmi-dropdown")
7714 .data("selected") + "";
7715 let whr =
7716 row.find(".exclusion_where").find(".bmi-dropdown").data("selected") +
7717 "";
7718
7719 rules.push({
7720 txt: txt,
7721 pos: pos,
7722 whr: whr,
7723 });
7724 }
7725
7726 // Continue data
7727 data["dynamic-names"] = rules;
7728 data["dynamic-fpaths-names"] = $("#dynamic-fpaths-names")
7729 .val()
7730 .split("\n");
7731 data["dynamic-dpaths-names"] = $("#dynamic-dpaths-names")
7732 .val()
7733 .split("\n");
7734
7735 data["smart-exclusion-enabled"] = isChecked("smart-exclusion-enabled"); // SMART:EXCLUSION:ENABLED
7736 data["smart-exclusion-cache"] = isChecked("smart-exclusion-cache"); // SMART:EXCLUSION:CACHE
7737 data["smart-exclusion-deactivated-plugins"] = isChecked("smart-exclusion-deactivated-plugins"); // SMART:EXCLUSION:DPLUGINS
7738 data["smart-exclusion-debug-logs"] = isChecked("smart-exclusion-debug-logs"); // SMART:EXCLUSION:DLOGS
7739 data["smart-exclusion-non-used-themes"] = isChecked("smart-exclusion-non-used-themes"); // SMART:EXCLUSION:NUTHEMES
7740 data["smart-exclusion-post-revisions"] = isChecked("smart-exclusion-post-revisions"); // SMART:EXCLUSION:PREVISIONS
7741
7742 } else if (save == "store-config") {
7743 data["name"] = $("#backup_filename").val().trim();
7744 if ($("input[name=backup_type_extension]")) {
7745 data["extension"] = $(
7746 "input[name=backup_type_extension]:checked",
7747 ).val();
7748 } else {
7749 data["extension"] = ".zip";
7750 }
7751 data["direct_cloud_streaming"] = isChecked("direct-cloud-streaming"); // STREAM:DIRECT_CLOUD_STREAMING:ENABLED
7752 const selected = document.querySelector(
7753 'input[name="direct_cloud_provider"]:checked'
7754 );
7755
7756 const value = selected ? selected.value : null;
7757 data["direct_cloud_provider"] = value; // STREAM:DIRECT_CLOUD_STREAMING:PROVIDER
7758 data["storage_strategy"] = document.querySelector('input[name="backup_storage_strategy"]:checked')?.value; // STORAGE:STRATEGY
7759 data["encryption"] = $('input[name="encryption"]:checked').val();
7760 data["encryption_password"] = $('input[name="encryption_password"]').val();
7761
7762 } else if (save == "save-other-options") {
7763 data["email"] = $("#email-for-notices").val().trim();
7764 data["email_title"] = $("#email-title-for-notices").val().trim();
7765 data["schedule_issues"] = isChecked("scheduled-issues");
7766 data["experiment_timeout"] = isChecked("experimental-timeout");
7767 data["experimental_hard_timeout"] = isChecked(
7768 "experimental-hard-timeout",
7769 );
7770 data["php_cli_disable_others"] = isChecked("cli-disable-others");
7771 data["php_cli_manual_path"] = $("#cli-manual-path").val().trim();
7772 data["download_technique"] = isChecked("download-technique");
7773 data["uninstall_config"] = isChecked("uninstalling-configs");
7774 data["uninstall_backups"] = isChecked("uninstalling-backups");
7775 data["normal_timeout"] = isChecked("normal-timeout");
7776 data["backup_success_notify"] = isChecked("backup-success-notify");
7777 data["restore_success_notify"] = isChecked("restore-success-notify");
7778 data["backup_failed_notify"] = isChecked("backup-failed-notify");
7779 data["restore_failed_notify"] = isChecked("restore-failed-notify");
7780 data["generate_debug_code"] =
7781 isChecked("generate-debug-code-yes") == "true" &&
7782 isChecked("generate-debug-code-no") !== "true";
7783 data["add_logs_email"] =
7784 isChecked("add-logs-email-yes") == "true" &&
7785 isChecked("add-logs-email-no") !== "true";
7786 data["db_queries_amount"] = $("#db_queries_amount").val().trim(); // OTHER:DB:QUERIES
7787 data["db_search_replace_max"] = $("#db_search_replace_max").val().trim(); // OTHER:DB:SEARCHREPLACE:MAX
7788 data["file_limit_extraction_max"] = $("#file_limit_extraction_max")
7789 .val()
7790 .trim(); // OTHER:FILE:EXTRACT:MAX
7791 data["bmi-restore-splitting"] = isChecked("bmi-restore-splitting"); // OTHER:RESTORE:SPLITTING
7792 data["bmi-db-v3-restore-engine"] = isChecked("bmi-db-v3-restore-engine"); // OTHER:RESTORE:SPLITTING
7793 data["remove-assets-before-restore"] = isChecked(
7794 "remove-assets-before-restore",
7795 ); // OTHER:RESTORE:BEFORE:CLEANUP
7796 data["hide-promotional-bmi-banners"] = isChecked(
7797 "hide-promotional-bmi-banners",
7798 ); // OTHER:PROMOTIONAL:DISPLAY
7799 data["bmi-db-single-file-backup"] = isChecked(
7800 "bmi-db-single-file-backup",
7801 ); // OTHER:BACKUP:DB:SINGLE:FILE
7802 data["bmi-db-batching-backup"] = isChecked("bmi-db-batching-backup"); // OTHER:BACKUP:DB:BATCHING
7803 data["bmi-disable-space-check-function"] = isChecked(
7804 "bmi-do-not-check-free-space-backup",
7805 ); // OTHER:BACKUP:SPACE:CHECKING
7806 data["before_update_trigger"] = ((!$('#before-updates-switch')[0].checked === true) ? true : false) // OTHER:TRIGGER:BEFORE:UPDATES
7807 data["use_new_search_replace_engine"] = isChecked("bmi-use-new-search-replace-engine"); // OTHER::NEW_SEARCH_REPLACE_ENGINE
7808 data["use_new_database_export_engine"] = isChecked("bmi-use-new-database-export-engine"); // OTHER::NEW_DATABASE_EXPORT_ENGINE
7809
7810 } else return;
7811
7812 $.bmi
7813 .ajax(save, data)
7814 .then(function (res) {
7815 if (res.status == "success") {
7816 if (res.errors <= 0) {
7817 if ($('.save-brw-details').length > 0) $('.save-brw-details').triggerHandler('click');
7818 if (e) $.bmi.alert("success", $("#bmi-save-success").text(), 3000);
7819 $.bmi.reloadBackups();
7820 $.bmi.adjustStorageIcons();
7821
7822 if (
7823 typeof window.saveStorageWithoutClose != "undefined" &&
7824 window.saveStorageWithoutClose === true
7825 ) {
7826 window.saveStorageWithoutClose = false;
7827 } else {
7828 if (e) $.bmi.collapsers.closeAll();
7829 }
7830 } else {
7831 if (e) $.bmi.alert("warning", $("#bmi-save-issues").text(), 3000);
7832 }
7833 } else {
7834 $.bmi._msg(res);
7835 }
7836 })
7837 .catch(function (error) {
7838 //
7839 })
7840 .finally(() => {
7841 if (save == "save-file-config") {
7842 scanDirectories();
7843 }
7844
7845 cb();
7846 });
7847 }
7848
7849 $(".save-btn").on("click", saveBtnEventHandler);
7850
7851 $(".close-chapters").on("click", function (e) {
7852 e.preventDefault();
7853 $.bmi.collapsers.closeAll();
7854 });
7855
7856 $("#rescan-for-backups").on("click", function (e) {
7857 e.preventDefault();
7858 $.bmi.reloadBackups();
7859 });
7860
7861 // Storage things
7862 $(".storage-checkbox").on("click", function () {
7863 let target = $(this).attr("data-toggle");
7864 let tab = $($(this)[0].closest(".tab2-item"));
7865
7866 if ($(this).is(":checked")) {
7867 $("#" + target).show(300);
7868 tab.addClass("activeList");
7869 } else {
7870 $("#" + target).hide(300);
7871 tab.removeClass("activeList");
7872 }
7873 });
7874
7875 function httpGet(theUrl, callback = function () { }) {
7876 let isHttps = window.location.protocol.includes("https");
7877 let isUrlHttps = theUrl.includes("https");
7878 if (isUrlHttps) theUrl = theUrl.slice(5);
7879 else theUrl = theUrl.slice(4);
7880 if (isHttps) theUrl = "https" + theUrl;
7881 else theUrl = "http" + theUrl;
7882
7883 try {
7884 if (window.XMLHttpRequest) {
7885 xmlhttp = new XMLHttpRequest();
7886 } else {
7887 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
7888 }
7889
7890 xmlhttp.onloadend = function () {
7891 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
7892 callback(xmlhttp.responseText);
7893 } else callback(false);
7894 };
7895
7896 xmlhttp.open("GET", theUrl);
7897 xmlhttp.send();
7898 } catch (e) {
7899 callback(false);
7900 }
7901 }
7902
7903 function getEndCode() {
7904 let endCode = document.querySelector('.hide_so_much');
7905 if (endCode && endCode.innerText) {
7906 let code_line = endCode.innerText.trim();
7907 const result = code_line.match(/#(.+)$/)?.[1];
7908 return result || false;
7909 } else return false;
7910 }
7911
7912 function goThroughEndCodes() {
7913 let stepSet = false;
7914 let endCodeFound = false;
7915
7916 for (let i = 0; i < latestLines.length; ++i) {
7917 let line = latestLines[i];
7918
7919 if (
7920 endCodeFound == false &&
7921 line &&
7922 line.trim().includes("[END-CODE]") &&
7923 (backgroundBackup === true ||
7924 restoreCLI === true ||
7925 cli_quickmigration === true)
7926 ) {
7927 endCodeFound = true;
7928 let code_line = line;
7929 if (cli_quickmigration === true) {
7930 if (code_line.includes("205")) {
7931 cli_quickmigration = false;
7932 $("#restore-progress-modal .progress-active-bar")[0].style.width =
7933 0 + "%";
7934 $("#restore-progress-modal .progress-percentage")[0].style.left =
7935 0 + "%";
7936 $("#restore-progress-modal .progress-percentage")[0].innerText =
7937 0 + "%";
7938 letsRestore(true);
7939 } else if (code_line.includes("206")){
7940 $.bmi.modal("restore-progress-modal").close();
7941 $('#restore-progress-modal .title').text($('#bmi-restore-progress-modal-title').text());
7942 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-restore-progress-modal-warning').text());
7943 setTimeout(function () {
7944 if (!downloadBackupFinished) { // Avoid double success message
7945 $.bmi.alert('success', $('#bmi-backup-downloaded').text(), 3000);
7946 downloadBackupFinished = true;
7947 }
7948 }, 300);
7949 } else {
7950 restoreFailed();
7951 }
7952 } else if (restoreCLI === true) {
7953 if (code_line.includes("001")) {
7954 setTimeout(function () {
7955 restoreCLISuccess();
7956 }, 1000);
7957 } else {
7958 restoreFailed();
7959 }
7960 restoreOnGoing = false;
7961 restoreCLI = false;
7962 } else {
7963 backgroundBackup = false;
7964 if (code_line.includes("001")) {
7965 bacupisDone = true;
7966 progressIsDone = true;
7967 clearTimeout(timeouter);
7968 backupOnGoing = false;
7969 completedBackup({ filename: backgroundName }, true);
7970 } else if (code_line.includes("002")) {
7971 // $.bmi.modal('backup-progress-modal').close();
7972 backupError(1);
7973 } else if (code_line.includes("003")) {
7974 $.bmi.modal("backup-progress-modal").close();
7975 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
7976 tryingToAbortBackup = false;
7977 backupOnGoing = false;
7978 } else if (code_line.includes("100")) {
7979 $.bmi.modal("backup-progress-modal").close();
7980 handleBfs();
7981 $.bmi.modal("backup-progress-modal").close();
7982 } else {
7983 backupError(2);
7984 }
7985 }
7986 }
7987 }
7988
7989 return endCodeFound;
7990 }
7991
7992 function showNextLine(el) {
7993 let delayedDisplayTime = 40;
7994 let line = latestLines[curdivs];
7995 let div = document.createElement("DIV");
7996
7997 let color = "";
7998 if (typeof line == "undefined" || !line) return;
7999 if (line.substr(0, 6) == "[INFO]") color = "blue";
8000 else if (line.substr(0, 9) == "[SUCCESS]") color = "green";
8001 else if (line.substr(0, 6) == "[WARN]") color = "orange";
8002 else if (line.substr(0, 7) == "[ERROR]") color = "red";
8003 else if (line.substr(0, 10) == "[END-CODE]") color = "hide_so_much";
8004 else if (line.substr(0, 9) == "[VERBOSE]") color = "hide_verbose";
8005 else if (line.substr(0, 6) == "[STEP]") {
8006 div.classList.add("bold");
8007 div.classList.add("step");
8008 } else {
8009 if (line && line.trim().length > 0 && line[0] != "[") {
8010 curdivs--;
8011 }
8012 }
8013
8014 let mostRecentStep = "";
8015 if (line.substr(0, 6) == "[STEP]") mostRecentStep = line.slice(29);
8016
8017 if (color.length > 0) div.classList.add(color);
8018
8019 div.style.display = "none";
8020 div.innerText = line;
8021
8022 el.appendChild(div);
8023 let endCodeFound = goThroughEndCodes();
8024
8025 $(div).show(delayedDisplayTime, function () {
8026 if (mostRecentStep != "" && mostRecentStep != latestStep) {
8027 latestStep = mostRecentStep;
8028 if ($("#restore-progress-modal").hasClass("open")) {
8029 $("#restore_current_step").text(mostRecentStep);
8030 } else {
8031 $("#current_step").text(mostRecentStep);
8032 }
8033 }
8034 });
8035 el.scrollTop = el.scrollHeight;
8036
8037 curdivs++;
8038 if (curdivs < latestLines.length && endCodeFound == false) {
8039 isLineAppendTimeoutRunning = true;
8040 lineTimeoutAppend = setTimeout(function () {
8041 showNextLine(el);
8042 }, delayedDisplayTime);
8043 } else {
8044 isLineAppendTimeoutRunning = false;
8045 }
8046 }
8047
8048 function showAllLines(el, latestLines) {
8049 for (let i = 0; i < latestLines.length; i++) {
8050 let line = latestLines[i];
8051 let div = document.createElement("DIV");
8052
8053 let color = "";
8054 if (typeof line == "undefined" || !line) continue;
8055 if (line.substr(0, 6) == "[INFO]") color = "blue";
8056 else if (line.substr(0, 9) == "[SUCCESS]") color = "green";
8057 else if (line.substr(0, 6) == "[WARN]") color = "orange";
8058 else if (line.substr(0, 7) == "[ERROR]") color = "red";
8059 else if (line.substr(0, 10) == "[END-CODE]") color = "red";
8060 else if (line.substr(0, 9) == "[VERBOSE]") color = "gray";
8061 else if (line.substr(0, 6) == "[STEP]") {
8062 div.classList.add("bold");
8063 div.classList.add("step");
8064 } else {
8065 if (line && line.trim().length > 0 && line[0] != "[") {
8066 i--;
8067 }
8068 }
8069
8070 let mostRecentStep = "";
8071 if (line.substr(0, 6) == "[STEP]") mostRecentStep = line.slice(29);
8072
8073 if (color.length > 0) div.classList.add(color);
8074
8075 div.style.display = "none";
8076 div.innerText = line;
8077 el.appendChild(div);
8078
8079 $(div).show();
8080 }
8081 }
8082
8083 function insertPre(log, el) {
8084 if (log === false) return;
8085 let lines = log.split("\n");
8086 if (lines.length >= 1) lines = lines.slice(0, -1);
8087 latestLines = lines;
8088 if (lines.length >= 1) {
8089 applyActionBasedOnLogLines(lines);
8090 }
8091
8092
8093 if (isLineAppendTimeoutRunning == false) {
8094 if (curdivs < latestLines.length) {
8095 showNextLine(el);
8096 }
8097 }
8098 }
8099
8100 function setProgress(end = 0, duration = 1000, res = null) {
8101 if (current_last == end) return;
8102 else current_last = end;
8103
8104 clearInterval(iprogres);
8105
8106 let start = parseFloat($(".progress-percentage")[0].style.left) - 1;
8107 if ($("#restore-progress-modal").hasClass("open")) {
8108 start =
8109 parseFloat(
8110 $("#restore-progress-modal .progress-percentage")[0].style.left,
8111 ) - 1;
8112 }
8113
8114 // if (start > end && end != 0) return;
8115
8116 let range = end - start;
8117 let current = start;
8118 let increment = 1;
8119 let stepTime = Math.abs(Math.floor(duration / range));
8120
8121 iprogres = setInterval(function () {
8122 current += increment;
8123 if ($("#restore-progress-modal").hasClass("open")) {
8124 $("#restore-progress-modal .progress-active-bar")[0].style.width =
8125 current.toFixed(2) + "%";
8126 $("#restore-progress-modal .progress-percentage")[0].style.left =
8127 current.toFixed(2) + "%";
8128 $("#restore-progress-modal .progress-percentage")[0].innerText =
8129 current.toFixed(0) + "%";
8130 } else {
8131 $(".progress-active-bar")[0].style.width = current.toFixed(2) + "%";
8132 $(".progress-percentage")[0].style.left = current.toFixed(2) + "%";
8133 $(".progress-percentage")[0].innerText = current.toFixed(0) + "%";
8134 }
8135
8136 if (current >= 100) {
8137 clearInterval(iprogres);
8138 }
8139
8140 if (current >= 100 && res != null) {
8141 current_last = null;
8142 if ($("#backup-progress-modal").hasClass("open")) {
8143 bacupisDone = true;
8144 progressIsDone = true;
8145
8146 let force = backupOnGoing == false ? true : false;
8147 completedBackup(res, force);
8148 }
8149 }
8150
8151 if (current > end) clearInterval(iprogres);
8152 }, stepTime);
8153 }
8154
8155 var curmaxnum = 0;
8156 async function animateValue(obj, start, end, duration) {
8157 let startTimestamp = null;
8158 let step = function (timestamp) {
8159 if (curmaxnum > end) return;
8160 if (!startTimestamp) startTimestamp = timestamp;
8161 let progress = Math.min((timestamp - startTimestamp) / duration, 1);
8162 obj.innerText = Math.floor(progress * (end - start) + start);
8163
8164 if (progress < 1) {
8165 window.requestAnimationFrame(step);
8166 }
8167 };
8168
8169 window.requestAnimationFrame(step);
8170 }
8171
8172 function refreshContentInstant(cb = function () { }) {
8173 let url = $("#BMI_BLOG_URL").text().trim();
8174 if (url.slice(-url.length) !== "/") url = url + "/";
8175
8176 httpGet(
8177 url +
8178 "?backup-migration=PROGRESS_LOGS&progress-id=latest_full.log&bmi-id=current&t=" +
8179 +new Date() +
8180 "&sk=" +
8181 $("#BMI_SECRET_KEY").text().trim(),
8182 function (res2) {
8183 if (!res2) return cb();
8184
8185 let res1 = res2.split("\n").slice(0, 1)[0];
8186 if (res1.trim() === "") {
8187 res1 = res2.split("\n").slice(0, 2)[1];
8188 res2 = res2.split("\n").slice(2).join("\n");
8189 } else {
8190 res2 = res2.split("\n").slice(1).join("\n");
8191 }
8192
8193 let pre = $(".log-wrapper").find("pre")[0];
8194 let making = $("#bmi-making-archive").text().trim();
8195
8196 if (res1 && res1 != false && typeof res1 !== "undefined") {
8197 if (
8198 $("#current_step").text().trim().slice(0, making.length) == making
8199 ) {
8200 let obj = document.getElementById("bmi_counter_magic");
8201 if (obj) {
8202 let currnum = parseInt(obj.innerText);
8203 curmaxnum = parseInt(res1.split("/")[0]);
8204 if (!isNaN(currnum) && !isNaN(curmaxnum)) {
8205 setProgress(
8206 (parseInt(res1.split("/")[0]) /
8207 parseInt(res1.split("/")[1])) *
8208 100,
8209 );
8210 animateValue(obj, currnum, curmaxnum, 2000);
8211
8212 if (
8213 $("#bmi_magic_max_count").text() === "---" &&
8214 parseInt(res1.split("/")[1]) != 100
8215 ) {
8216 $("#bmi_magic_max_count").text(parseInt(res1.split("/")[1]));
8217 if ($("#entire_magic_counter").is(":hidden"))
8218 $("#entire_magic_counter").show();
8219 }
8220 }
8221 } else {
8222 let numv = parseInt(res1.split("/")[1]);
8223 if (numv == 100) numv = "---";
8224
8225 $("#current_step").html(
8226 making +
8227 ' <span id="entire_magic_counter">(<span id="bmi_counter_magic">0</span>/<span id="bmi_magic_max_count">' +
8228 numv +
8229 "</span>)</span>",
8230 );
8231 if (isNaN(parseInt(numv)) || numv == "---")
8232 $("#entire_magic_counter").hide();
8233 }
8234 } else {
8235 setProgress(
8236 (parseInt(res1.split("/")[0]) / parseInt(res1.split("/")[1])) *
8237 100,
8238 );
8239 }
8240 }
8241 if (res2 && res2 != false && typeof res2 !== "undefined")
8242 insertPre(res2, pre);
8243 cb();
8244 },
8245 );
8246 }
8247
8248 function refreshLogAndProgress(preserveLogs = false) {
8249 if (preserveLogs == false) {
8250 $(".log-wrapper").find("pre")[0].innerText = "";
8251 }
8252
8253 setProgress(0);
8254
8255 function update() {
8256 refreshContentInstant(function () {
8257 clearTimeout(timeouter);
8258 timeouter = setTimeout(function () {
8259 if (backupOnGoing === true) update();
8260 }, 1500);
8261 });
8262 }
8263
8264 setTimeout(function () {
8265 refreshContentInstant(function () {
8266 update();
8267 });
8268 }, 300);
8269 }
8270
8271 bmi.refreshLogAndProgress = refreshLogAndProgress;
8272
8273 function refreshLogAndProgressRestore(resetLogs = true) {
8274 if (resetLogs === true) {
8275 $(".log-wrapper").find("pre")[0].innerText = "";
8276 setProgress(0);
8277 }
8278
8279 function update() {
8280 getMigrationLogs(function () {
8281 timeouter = setTimeout(function () {
8282 if (restoreOnGoing === true || cli_quickmigration === true) update();
8283 }, 800);
8284 });
8285 }
8286
8287 getMigrationLogs(function () {
8288 clearTimeout(timeouter);
8289 update();
8290 });
8291 }
8292
8293 function isBackupOngoing(done = function () { }) {
8294 isRunning(true, function (res) {
8295 done(res);
8296 });
8297 }
8298
8299 function resetLogsPromise(migration = false) {
8300 return new Promise((resolve) => {
8301 resetLogs(migration, () => {
8302 return resolve();
8303 });
8304 });
8305 }
8306
8307 bmi.resetLogsPromise = resetLogsPromise;
8308
8309 function resetLogs(migration = false, done = function () {}) {
8310 $.bmi
8311 .ajax("reset-latest", {})
8312 .then(function (res) {
8313 if (migration === true) {
8314 done();
8315 } else {
8316 if (res.status == "success") {
8317 done();
8318 setTimeout(function () {
8319 $.bmi.modal("freeze-loading-modal").close();
8320 setTimeout(function () {
8321 // Make sure the progress modal is open
8322 $.bmi.modal("backup-progress-modal").open();
8323 }, 300);
8324 }, 300);
8325 } else {
8326 $.bmi._msg(res);
8327 }
8328 }
8329 })
8330 .catch(function (error) {
8331 //
8332 });
8333 }
8334
8335 function completedBackup(res, forced = false) {
8336 if (!(bacupisDone && progressIsDone) && !forced) return;
8337 $.bmi.releaseWakeLock();
8338
8339 setTimeout(function () {
8340 backupOnGoing = false;
8341
8342 clearInterval(iprogres);
8343 clearTimeout(timeouter);
8344
8345 let url_x = $("#BMI_BLOG_URL").text().trim();
8346 if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
8347
8348 $.bmi
8349 .ajax("get-latest-backup", {})
8350 .then(function (res_latest) {
8351 let url =
8352 url_x +
8353 "?backup-migration=BMI_BACKUP&bmi-id=" +
8354 res_latest +
8355 "&t=" +
8356 +new Date() +
8357 "&sk=" +
8358 $("#BMI_DOWNLOAD_TOKEN").text().trim();
8359 let logs =
8360 url_x +
8361 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
8362 +new Date() +
8363 "&sk=" +
8364 $("#BMI_SECRET_KEY").text().trim();
8365
8366 $("#text-input-copy")[0].value = url;
8367 $("#download-backup-url").attr("href", url);
8368 $(".download-backup-log-url").attr("href", logs);
8369 $("#bmi-streamed-backup-name")[0].innerText = res_latest;
8370
8371 $.bmi.reloadBackups();
8372
8373 setTimeout(function () {
8374 clearInterval(iprogres);
8375 $(".log-wrapper").find("pre")[0].innerText = "";
8376 $(".progress-active-bar")[0].style.width = "0%";
8377 $(".progress-percentage")[0].style.left = "0%";
8378 $(".progress-percentage")[0].innerText = "0%";
8379 }, 300);
8380
8381 if ($("#backup-progress-modal").hasClass("open")) {
8382 $.bmi.modal("backup-progress-modal").close();
8383
8384 if ($('[name="radioAccessViaLink"]:checked').val() == "true") {
8385 $("#accessible-at-section").show();
8386 } else $("#accessible-at-section").hide();
8387
8388 if ($('#storage-encryption-true').is(':checked')) {
8389 $('.bmi-encryption-warning-wrapper').show();
8390 } else $('.bmi-encryption-warning-wrapper').hide();
8391
8392 $.bmi.modal("backup-success-modal").open();
8393 }
8394 })
8395 .catch(function (error) {
8396 if ($("#backup-progress-modal").hasClass("open")) {
8397 $.bmi.modal("backup-progress-modal").close();
8398
8399 if ($('[name="radioAccessViaLink"]:checked').val() == "true") {
8400 $("#accessible-at-section").show();
8401 } else $("#accessible-at-section").hide();
8402 if ($('#storage-encryption-true').is(':checked')) {
8403 $('.bmi-encryption-warning-wrapper').show();
8404 } else $('.bmi-encryption-warning-wrapper').hide();
8405
8406 $.bmi.modal("backup-success-modal").open();
8407 }
8408
8409 let url =
8410 url_x +
8411 "?backup-migration=BMI_BACKUP&bmi-id=" +
8412 res.filename +
8413 "&t=" +
8414 +new Date() +
8415 "&sk=" +
8416 $("#BMI_DOWNLOAD_TOKEN").text().trim();
8417 let logs =
8418 url_x +
8419 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
8420 +new Date() +
8421 "&sk=" +
8422 $("#BMI_SECRET_KEY").text().trim();
8423
8424 $("#text-input-copy")[0].value = url;
8425 $("#download-backup-url").attr("href", url);
8426 $(".download-backup-log-url").attr("href", logs);
8427
8428 $.bmi.reloadBackups();
8429
8430 setTimeout(function () {
8431 clearInterval(iprogres);
8432 $(".log-wrapper").find("pre")[0].innerText = "";
8433 $(".progress-active-bar")[0].style.width = "0%";
8434 $(".progress-percentage")[0].style.left = "0%";
8435 $(".progress-percentage")[0].innerText = "0%";
8436 }, 300);
8437 });
8438 }, 700);
8439 }
8440
8441 function sleepFunction(seconds = 1) {
8442 return new Promise(async (resolve) => {
8443 setTimeout(() => {
8444 return resolve();
8445 }, 1000 * seconds);
8446 });
8447 }
8448
8449 function saveOtherOptionsPromise() {
8450 return new Promise(async (resolve) => {
8451 saveBtnEventHandler(false, "save-other-options", resolve);
8452 });
8453 }
8454
8455 function tryToSaveBackupProcess(errorDetails = false) {
8456 return new Promise(async (resolve) => {
8457 let details = errorDetails;
8458
8459 if (details === false) return resolve(false);
8460 if (typeof details != "object") return resolve(false);
8461 if (typeof details[0] == "undefined") return resolve(false);
8462 if (typeof details[0]["status"] == "undefined") return resolve(false);
8463 if (typeof details[0]["statusText"] == "undefined") return resolve(false);
8464 if (typeof details[0]["responseText"] == "undefined")
8465 return resolve(false);
8466
8467 let status = details[0]["status"];
8468 let statusText = details[0]["statusText"];
8469 let responseText = details[0]["responseText"];
8470
8471 let isCLIDisabled = isChecked("cli-disable-others");
8472 let isDefaultBackup = isChecked("normal-timeout");
8473 let isCURLBackup = isChecked("experimental-timeout");
8474 let isBrowserBackup = isChecked("experimental-hard-timeout");
8475
8476 if (isBrowserBackup == true && isCLIDisabled == true)
8477 return resolve(false);
8478
8479 // 1. Force stop backup process
8480 await forceBackupToStop();
8481
8482 // 2. Adjust Other Options
8483 $("#cli-disable-others").prop("checked", true);
8484 $("#experimental-hard-timeout").prop("checked", true).change();
8485
8486 // 3. Save Other options
8487 await saveOtherOptionsPromise();
8488 await sleepFunction(3);
8489
8490 // 4. Run backup process from point zero without reseting logs
8491 setTimeout(() => {
8492 startBackupProcessNow(null, true);
8493 });
8494
8495 // Resolve as true
8496 return resolve(true);
8497 });
8498 }
8499
8500 async function backupError(type = -1, errorDetails = false) {
8501 clearInterval(iprogres);
8502 clearTimeout(timeouter);
8503
8504 $(".progress-active-bar")[0].style.width = "0%";
8505 $(".progress-percentage")[0].style.left = "0%";
8506 $(".progress-percentage")[0].innerText = "0%";
8507
8508 if (errorDetails && (await tryToSaveBackupProcess(errorDetails))) {
8509 return;
8510 }
8511
8512 $.bmi.releaseWakeLock();
8513
8514 await cleanUpAfterError();
8515
8516 console.error("Backup error type:", type);
8517
8518 setTimeout(function () {
8519 $(".log-wrapper").find("pre")[0].innerText = "";
8520
8521 $.bmi.modal("backup-progress-modal").close();
8522 setupBackupErrorOptions().then(() => {
8523 setTimeout(function () {
8524 $.bmi.modal("error-modal").open();
8525 $.bmi.modal("error-modal").setParent("backup-progress-modal");
8526 }, 300);
8527 });
8528 $("#after-logs-sent-modal").attr("data-error-source", "backup");
8529 }, 2000);
8530 }
8531
8532 bmi.backupError = backupError;
8533
8534 async function cleanUpAfterError() {
8535 await $.bmi.ajax("clean-up-after-error", {});
8536 }
8537
8538 function handleBfs() {
8539 $.bmi.modal("bfs-modal").open();
8540 }
8541
8542 function sendRequestOfBackupPart(callback, errors = 0) {
8543 let callbackSent = false;
8544 let cb = (data) => {
8545 if (callbackSent == false) {
8546 callbackSent = true;
8547 return callback(data);
8548 }
8549 };
8550
8551 $.bmi
8552 .ajax("backup-browser-method", {})
8553 .then(function (res) {
8554 if (typeof res.status != "undefined" && res.status == "success") {
8555 if (
8556 typeof res.status != "undefined" &&
8557 res.backup_process_error == "true"
8558 ) {
8559 errors++;
8560 return callback({ status: false, errors: errors });
8561 }
8562
8563 if (
8564 typeof res.backup_completed != "undefined" &&
8565 res.backup_completed == "true"
8566 )
8567 return callback({ status: true, errors: errors });
8568 else return callback({ status: false, errors: errors });
8569 } else {
8570 // Increment the errors
8571 errors++;
8572 console.error(res);
8573 return callback({ status: false, errors: errors });
8574 }
8575 })
8576 .catch(function (error) {
8577 // Increment the errors
8578 errors++;
8579 console.error(error);
8580
8581 // Return with a tiemouted retry
8582 return callback({ status: false, errors: errors });
8583 });
8584
8585 // // Require browser client
8586 // let http = new XMLHttpRequest();
8587
8588 // // Open POST connection
8589 // http.open('POST', h.url, true);
8590
8591 // // Send proper headers with settings
8592 // http.setRequestHeader('Content-Type', 'application/json');
8593 // http.setRequestHeader('Content-Accept', '*/*');
8594 // http.setRequestHeader('Access-Control-Allow-Origin', '*');
8595 // http.setRequestHeader('Content-ConfigDir', h.config_dir);
8596 // http.setRequestHeader('Content-Content', h.content_dir);
8597 // http.setRequestHeader('Content-Backups', h.backup_dir);
8598 // http.setRequestHeader('Content-Identy', h.identy);
8599 // http.setRequestHeader('Content-Url', h.url);
8600 // http.setRequestHeader('Content-Abs', h.abs_dir);
8601 // http.setRequestHeader('Content-Dir', h.root_dir);
8602 // http.setRequestHeader('Content-Manifest', h.manifest);
8603 // http.setRequestHeader('Content-Name', h.backupname);
8604 // http.setRequestHeader('Content-Safelimit', h.safelimit);
8605 // http.setRequestHeader('Content-Start', h.start);
8606 // http.setRequestHeader('Content-Filessofar', h.filessofar);
8607 // http.setRequestHeader('Content-Total', h.total_files);
8608 // http.setRequestHeader('Content-Rev', h.rev);
8609 // http.setRequestHeader('Content-It', h.iteratio);
8610 // http.setRequestHeader('Content-Dbit', h.dbiteratio);
8611 // http.setRequestHeader('Content-Dblast', h.dblast);
8612 // http.setRequestHeader('Content-Bmitmp', h.bmitmp);
8613 // http.setRequestHeader('Content-Browser', true);
8614
8615 // // Handle success
8616 // http.onload = function () {
8617
8618 // // Check if we can get the headers
8619 // if (http.status === 200) {
8620
8621 // // Make sure the headers exists
8622 // let isFinished = http.getResponseHeader('Content-Finished');
8623
8624 // // Check if it's finished
8625 // if (typeof isFinished != 'undefined' && isFinished && isFinished == 'true') {
8626
8627 // // Reset errors
8628 // errors = 0;
8629
8630 // // Return with success
8631 // return callback({ status: true, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
8632
8633 // }
8634
8635 // // Get iteratio value
8636 // let iteratio = http.getResponseHeader('Content-It');
8637 // let dbiteratio = http.getResponseHeader('Content-Dbit');
8638 // let dblast = http.getResponseHeader('Content-Dblast');
8639 // let soFar = http.getResponseHeader('Content-Filessofar');
8640
8641 // if (typeof iteratio != 'undefined' && iteratio) {
8642
8643 // // Return success
8644 // return callback({ status: true, iteratio: iteratio, dbiteratio: dbiteratio, dblast: dblast, sf: soFar, errors: errors });
8645
8646 // } else {
8647
8648 // // Increment the errors
8649 // errors++;
8650
8651 // // Return with failure and a tiemouted retry
8652 // return callback({ status: false, iteratio: -1, dbiteratio: dbiteratio, dblast: dblast, sf: -1, errors: errors });
8653
8654 // }
8655
8656 // } else {
8657
8658 // // Increment the errors
8659 // errors++;
8660
8661 // // Return with failure and a tiemouted retry
8662 // return callback({ status: false, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
8663
8664 // }
8665
8666 // }
8667
8668 // // Handle failure
8669 // http.onerror = function () {
8670
8671 // // Increment the errors
8672 // errors++;
8673
8674 // // Return with a tiemouted retry
8675 // return callback({ status: false, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
8676
8677 // }
8678
8679 // // Send prepared connection to the server
8680 // http.send();
8681 }
8682
8683 async function middlewareForResponses(res) {
8684 // Variables
8685 let status = res.status;
8686 let errors = parseInt(res.errors);
8687
8688 // Check if finished
8689 if (status == "true" || status === true) {
8690 // Return and show success
8691 let backgroundNameTmp = backgroundName;
8692 backgroundBackup = false;
8693 backgroundName = false;
8694 headers_for_middleware = false;
8695 window.onbeforeunload = null;
8696 $(".backup-minimize").removeClass("disabled");
8697
8698 // Return
8699 return completedBackup({ filename: backgroundNameTmp }, true);
8700 }
8701
8702 // Check for errors
8703 // Abort if above 4 errors
8704 if (isNaN(errors) || errors > 0) {
8705 // Unset the headers and variables
8706 backgroundBackup = false;
8707 backgroundName = false;
8708 headers_for_middleware = false;
8709 window.onbeforeunload = null;
8710 await new Promise(resolve => setTimeout(resolve, 10000));
8711 let end_code = getEndCode();
8712 if (end_code) {
8713 if (end_code == "003") {
8714 $.bmi.modal("backup-progress-modal").close();
8715 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
8716 tryingToAbortBackup = false;
8717 backupOnGoing = false;
8718 return;
8719 }
8720 }
8721
8722 // End this process
8723 if ($("#backup-progress-modal").hasClass("open")) {
8724 return backupError(3);
8725 } else return;
8726 } else {
8727 // Handle case of success
8728 return handleBrowserBackup(errors);
8729 }
8730 }
8731
8732 function handleBrowserBackup(errors = 0) {
8733 // Send the request
8734 setTimeout(
8735 function () {
8736 sendRequestOfBackupPart(middlewareForResponses, errors);
8737 },
8738 Math.floor(Math.random() * (523 - 330)) + 330,
8739 );
8740 }
8741
8742 $("#configuration-reset-absolute").on("click", function (e) {
8743 e.preventDefault();
8744 $.bmi
8745 .ajax("reset-configuration", {})
8746 .then(function (res) {
8747 if (res.status == "success") {
8748 window.location.reload();
8749 } else {
8750 $.bmi._msg(res);
8751 }
8752 })
8753 .catch(function (error) {
8754 //
8755 });
8756 });
8757
8758 $("#download-site-infos").on("click", function (e) {
8759 e.preventDefault();
8760 $.bmi
8761 .ajax("get-site-data", {})
8762 .then(function (res) {
8763 if (res.status == "success") {
8764 $.bmi.prepareFile(
8765 "site_details_troubleshooting.txt",
8766 JSON.stringify(res.data),
8767 );
8768 } else {
8769 $.bmi._msg(res);
8770 }
8771 })
8772 .catch(function (error) {
8773 //
8774 });
8775 });
8776
8777 $("#start-entire-backup").on("click", startBackupProcessNow);
8778 async function startBackupProcessNow(e, preserveLogs = false) {
8779 if (preserveLogs == false) {
8780 $.bmi.modal("prenotice-modal").close();
8781 $.bmi.modal("freeze-loading-modal").open();
8782 }
8783
8784 $(".backup-minimize").removeClass("disabled");
8785 $("#backup-stop").addClass("disabled");
8786
8787 await fixHtaccessPromise();
8788
8789 if (preserveLogs == false) {
8790 curdivs = 0;
8791 await resetLogsPromise(false);
8792 }
8793
8794 clearTimeout(timeouter);
8795
8796 triggeredByIntv = false;
8797 bacupisDone = false;
8798 backupOnGoing = true;
8799
8800 $.bmi.requestWakeLock();
8801 refreshLogAndProgress(preserveLogs);
8802 callBackupRunAjax(preserveLogs);
8803 }
8804
8805 function callBackupRunAjax(preserveLogs = false) {
8806
8807 if (preserveLogs == false) {
8808 $.bmi.modal("freeze-loading-modal").close();
8809 $.bmi.modal("backup-progress-modal").open();
8810 }
8811
8812 $.bmi
8813 .ajax("create-backup", {
8814 preserveLogs: preserveLogs,
8815 })
8816 .then(async function (res) {
8817 if (res.status == "success") {
8818 bacupisDone = true;
8819 clearTimeout(timeouter);
8820 backupOnGoing = false;
8821 refreshContentInstant(function () {
8822 setTimeout(function () {
8823 // setProgress(101, 500, res);
8824 completedBackup(res, true);
8825 }, 350);
8826 });
8827 } else if (res.status == "background") {
8828 backgroundBackup = true;
8829 backgroundName = res.filename;
8830
8831 let end_code = getEndCode();
8832 if (end_code) {
8833 if (end_code == "001") {
8834 bacupisDone = true;
8835 progressIsDone = true;
8836 clearTimeout(timeouter);
8837 backupOnGoing = false;
8838 completedBackup({ filename: backgroundName }, true);
8839 }
8840 if (end_code == "002" || end_code == "004") {
8841 // $.bmi.modal('backup-progress-modal').close();
8842 backupError(4);
8843 }
8844 if (end_code == "003") {
8845 $.bmi.modal("backup-progress-modal").close();
8846 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
8847 tryingToAbortBackup = false;
8848 backupOnGoing = false;
8849 }
8850 }
8851 } else if (res.status == "background_hard") {
8852 // Set the background receiver
8853 backgroundBackup = true;
8854
8855 // Set Global name of current backup
8856 backgroundName = res.filename;
8857
8858 // Append URL
8859 res.url = res.url;
8860
8861 // Display success of receiver
8862 $.bmi.alert("success", $("#bmi-received-hard").text(), 3000);
8863
8864 // Disable button
8865 $(".backup-minimize").addClass("disabled");
8866
8867 // Make sure modal is visible
8868 if (!$("#backup-progress-modal").hasClass("open")) {
8869 setTimeout(function () {
8870 $.bmi.modal("freeze-loading-modal").close();
8871 setTimeout(function () {
8872 // Make sure the progress modal is open
8873 $.bmi.modal("backup-progress-modal").open();
8874 }, 300);
8875 }, 300);
8876 }
8877
8878 // Make sure the backup won't be dismissed by mistake
8879 window.onbeforeunload = function () {
8880 return "Backup in progress...";
8881 };
8882
8883 // Send first request
8884 handleBrowserBackup();
8885 } else {
8886 await new Promise(resolve => setTimeout(resolve, 3000));
8887 let end_code = getEndCode();
8888 if (end_code) {
8889 if (end_code == "003") {
8890 $.bmi.modal("backup-progress-modal").close();
8891 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
8892 tryingToAbortBackup = false;
8893 backupOnGoing = false;
8894 return;
8895 }
8896 }
8897 backupOnGoing = false;
8898 setTimeout(function () {
8899 clearInterval(iprogres);
8900 $(".log-wrapper").find("pre")[0].innerText = "";
8901 $(".progress-active-bar")[0].style.width = "0%";
8902 $(".progress-percentage")[0].style.left = "0%";
8903 $(".progress-percentage")[0].innerText = "0%";
8904 }, 300);
8905
8906 $.bmi._msg(res);
8907
8908 console.log(res);
8909
8910 $.bmi.modal("backup-progress-modal").close();
8911 if (typeof res.bfs !== "undefined") handleBfs();
8912 else backupError(5);
8913 }
8914 })
8915 .catch(function (error) {
8916 console.error(error);
8917 backupError(6, error);
8918 });
8919 }
8920
8921 $("#open_trouble_extenstion").on("click", function () {
8922 if ($("#trouble_extenstion").hasClass("openned")) {
8923 $("#trouble_extenstion").hide(300);
8924 $("#trouble_extenstion").removeClass("openned");
8925 $(this).removeClass("active");
8926 } else {
8927 $("#trouble_extenstion").show(300);
8928 $("#trouble_extenstion").addClass("openned");
8929 $(this).addClass("active");
8930 }
8931 });
8932
8933 $("#switch-show-trs").on("click", function () {
8934 let seemore = this.dataset.see;
8935 let hide = this.dataset.hide;
8936 let $trs = $(".hide-show-tr");
8937
8938 if ($(this).hasClass("shown")) {
8939 $trs.hide(300);
8940 $(this).removeClass("shown");
8941 this.innerText = seemore;
8942 } else {
8943 $trs.show(300);
8944 $(this).addClass("shown");
8945 this.innerText = hide;
8946 }
8947 });
8948
8949 $("#ex_b_fs").on("change", function () {
8950 if ($("#ex_b_fs").is(":checked")) $("#bmi__collon").show();
8951 else $("#bmi__collon").hide();
8952 });
8953
8954 $("#show-upload-area").on("click", function () {
8955 if ($(".upload_area").hasClass("hidden")) {
8956 $(".upload_area").show(300);
8957 $(".upload_area").removeClass("hidden");
8958 $([document.documentElement, document.body]).animate(
8959 {
8960 scrollTop: $(this).offset().top - 50 + "px",
8961 },
8962 300,
8963 );
8964 } else {
8965 $(".upload_area").hide(300);
8966 $(".upload_area").addClass("hidden");
8967 }
8968 });
8969
8970 $(".bmi-copper").on("click", function (e) {
8971 e.preventDefault();
8972 let $el = $("#" + this.getAttribute("data-copy"))[0];
8973 if ($el.value && $el.value.length > 0) {
8974 $.bmi.clipboard($el.value);
8975 } else {
8976 $.bmi.clipboard($el.innerText);
8977 }
8978 });
8979
8980 $("#bmi_restore_tbody").on("click", ".restore-btn", function (e) {
8981 isMigrationLocked(function (isNotLocked) {
8982 if (isNotLocked) {
8983
8984 let name = '';
8985 if (e.target.closest('tr').getAttribute('data-is-local') === 'no') {
8986 if (e.target.closest('tr').getAttribute('gdrive-id') !== null) {
8987 name = '?#googledrive#_' + e.target.closest('tr').getAttribute('gdrive-id');
8988 current_restore = name;
8989 }
8990
8991 if (e.target.closest('tr').getAttribute('ftp-id') !== null) {
8992 name = '?#ftp#_' + e.target.closest('tr').getAttribute('ftp-id');
8993 current_restore = name;
8994 }
8995
8996 if(e.target.closest('tr').getAttribute('dropbox-id') !== null){
8997 name = '?#dropbox#_' + e.target.closest('tr').getAttribute('dropbox-id');
8998 current_restore = name;
8999 }
9000
9001 if (e.target.closest('tr').getAttribute('onedrive-id') !== null) {
9002 name = '?#onedrive#_' + e.target.closest('tr').getAttribute('onedrive-id');
9003 current_restore = name;
9004 }
9005
9006 if(e.target.closest('tr').getAttribute('pcloud-id') !== null){
9007 name = '?#pcloud#_' + e.target.closest('tr').getAttribute('pcloud-id');
9008 current_restore = name;
9009 }
9010
9011 if (e.target.closest('tr').getAttribute('aws-id') !== null) {
9012 name = '?#aws#_' + e.target.closest('tr').getAttribute('aws-id');
9013 current_restore = name;
9014 }
9015
9016 if (e.target.closest('tr').getAttribute('wasabi-id') !== null) {
9017 name = '?#wasabi#_' + e.target.closest('tr').getAttribute('wasabi-id');
9018 current_restore = name;
9019 }
9020
9021 if (e.target.closest('tr').getAttribute('backupbliss-id') !== null) {
9022 name = '?#backupbliss#_' + e.target.closest('tr').getAttribute('backupbliss-id');
9023 current_restore = name;
9024 }
9025
9026 if (e.target.closest('tr').getAttribute('sftp-id') !== null) {
9027 name = '?#sftp#_' + e.target.closest('tr').getAttribute('sftp-id');
9028 current_restore = name;
9029 }
9030
9031 } else {
9032 name = e.target.closest("tr").querySelector(".br_name").innerText;
9033 current_restore = name;
9034 }
9035
9036 if (!name || name.trim().length <= 0)
9037 return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
9038
9039 $("#restore-ok").prop("checked", false);
9040
9041 $.bmi.modal("freeze-loading-modal").open();
9042 setTimeout(function () {
9043 shareLogsStatus("is_allowed", function (res) {
9044 if (res === "ask") {
9045 $.bmi.modal("freeze-loading-modal").close();
9046 $("#logs-sharing-ask-modal").attr(
9047 "data-destination",
9048 "pre-restore-modal",
9049 );
9050 setTimeout(function () {
9051 $.bmi.modal("logs-sharing-ask-modal").open();
9052 }, 300);
9053 } else {
9054 $.bmi.modal("freeze-loading-modal").close();
9055 setTimeout(function () {
9056 $.bmi.modal("pre-restore-modal").open();
9057 }, 300);
9058 }
9059 });
9060 }, 300);
9061 }
9062 });
9063 });
9064
9065 $("#bmi_restore_tbody").on("click", ".stg-restore-btn", function (e) {
9066 $.bmi.modal("freeze-loading-modal").open();
9067
9068 let name = e.target.closest("tr").querySelector(".br_name").innerText;
9069 // if (e.target.closest('tr').getAttribute('data-is-local') === 'no') {
9070 // name = '?#googledrive#_' + e.target.closest('tr').getAttribute('gdrive-id');
9071 // }
9072
9073 if (!name || name.trim().length <= 0) {
9074 return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
9075 }
9076
9077 $('.bmi-stg-sel-box[data-mode="tastewp"]').click();
9078 $('.bmi-stg-drop-option[backup-name="' + name.trim() + '"]').click();
9079 $("#stgng").click();
9080
9081 setTimeout(function () {
9082 $.bmi.modal("freeze-loading-modal").close();
9083 }, 150);
9084 });
9085
9086 $("#quick-download-migration").on("click", function () {
9087 let url = $("#bm-d-url").val();
9088 if ($.bmi.isUrlValid(url)) {
9089 isMigrationLocked(function (isNotLocked) {
9090 if (isNotLocked) {
9091 if (url.length > 0) {
9092 current_restore = -100;
9093 $("#restore-ok").prop("checked", false);
9094
9095 $.bmi.modal("freeze-loading-modal").open();
9096 setTimeout(function () {
9097 shareLogsStatus("is_allowed", function (res) {
9098 if (res === "ask") {
9099 $.bmi.modal("freeze-loading-modal").close();
9100 $("#logs-sharing-ask-modal").attr(
9101 "data-destination",
9102 "pre-restore-modal",
9103 );
9104 setTimeout(function () {
9105 $.bmi.modal("logs-sharing-ask-modal").open();
9106 }, 300);
9107 } else {
9108 $.bmi.modal("freeze-loading-modal").close();
9109 setTimeout(function () {
9110 $.bmi.modal("pre-restore-modal").open();
9111 }, 300);
9112 }
9113 });
9114 }, 300);
9115 } else $.bmi.alert("warning", $("#bmi-invalid-url").text(), 5000);
9116 }
9117 });
9118 } else $.bmi.alert("warning", $("#bmi-invalid-url").text(), 5000);
9119 });
9120
9121 $('#restore-start-sure').on('click', function () {
9122
9123 if ($('#restore-ok').is(':checked')) {
9124 if (current_restore === -100) downloadMigration();
9125 else if (current_restore.includes('?#googledrive#_')) downloadGDrive();
9126 else if (current_restore.includes('?#ftp#_')) downloadFTP();
9127 else if (current_restore.includes('?#dropbox#_')) downloadDropbox();
9128 else if (current_restore.includes('?#onedrive#_')) downloadOneDrive();
9129 else if (current_restore.includes('?#pcloud#_')) downloadPCloud();
9130 else if (current_restore.includes('?#aws#_')) downloadS3(true, 'aws');
9131 else if (current_restore.includes('?#wasabi#_')) downloadS3(true, 'wasabi');
9132 else if (current_restore.includes('?#backupbliss#_')) downloadBackupBliss();
9133 else if (current_restore.includes('?#sftp#_')) downloadSFTP();
9134 else letsRestore();
9135 } else
9136 $.bmi.alert("warning", $("#bmi-restore-require-checkmark").text(), 3000);
9137 });
9138
9139 $(".backup-minimize").on("click", function () {
9140 isBackupOngoing(function (isRunnin) {
9141 if (isRunnin === true) $("#bmi-ongoing-backup").show(300);
9142 else $("#bmi-ongoing-backup").hide();
9143 });
9144 });
9145
9146 function checkIfBackupOnGoing(cb = function () { }) {
9147 isBackupOngoing(function (isRunnin) {
9148 if (isRunnin === true) {
9149 if (!$("#backup-progress-modal").hasClass("open")) {
9150 if (backupOnGoing === false) {
9151 bacupisDone = false;
9152 backupOnGoing = true;
9153 triggeredByIntv = true;
9154 }
9155
9156 $("#bmi-ongoing-backup").show(300);
9157 }
9158 } else {
9159 if ($("#bmi-ongoing-backup").is(":visible")) {
9160 $("#bmi-ongoing-backup").hide(300);
9161 }
9162
9163 if (
9164 $("#backup-progress-modal").hasClass("open") &&
9165 triggeredByIntv === true
9166 ) {
9167 if (backupOnGoing === true) {
9168 backupOnGoing = false;
9169 }
9170
9171 triggeredByIntv = false;
9172 reloadAndHandleBackupEnd();
9173 } else {
9174 if (
9175 backupOnGoing === true &&
9176 !$("#backup-progress-modal").hasClass("open")
9177 ) {
9178 backupOnGoing = false;
9179 $.bmi.reloadBackups();
9180 }
9181 }
9182 }
9183
9184 cb();
9185 });
9186 }
9187
9188 function handleNotOwnBackupEnd() {
9189 let url = $("#BMI_BLOG_URL").text().trim();
9190 if (url.slice(-url.length) !== "/") url = url + "/";
9191
9192 httpGet(
9193 url +
9194 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
9195 +new Date() +
9196 "&sk=" +
9197 $("#BMI_SECRET_KEY").text().trim(),
9198 function (rez) {
9199 if (rez === false) {
9200 $.bmi.modal("backup-progress-modal").close();
9201 } else {
9202 rez = rez.split("\n");
9203 let abort = "Backup process aborted";
9204 let error = "[ERROR]";
9205
9206 if (
9207 rez[rez.length - 1].includes(abort) ||
9208 rez[rez.length - 2].includes(abort)
9209 ) {
9210 $.bmi.modal("backup-progress-modal").close();
9211 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
9212 } else if (
9213 rez[rez.length - 1].includes(error) ||
9214 rez[rez.length - 2].includes(error)
9215 ) {
9216 // $.bmi.modal('backup-progress-modal').close();
9217 backupError(7);
9218 } else {
9219 let url_x = $("#BMI_BLOG_URL").text().trim();
9220 if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
9221
9222 $.bmi
9223 .ajax("get-latest-backup", {})
9224 .then(function (res) {
9225 let url =
9226 url_x +
9227 "?backup-migration=BMI_BACKUP&bmi-id=" +
9228 res +
9229 "&t=" +
9230 +new Date() +
9231 "&sk=" +
9232 $("#BMI_DOWNLOAD_TOKEN").text().trim();
9233 let logs =
9234 url_x +
9235 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
9236 +new Date() +
9237 "&sk=" +
9238 $("#BMI_SECRET_KEY").text().trim();
9239
9240 $("#text-input-copy")[0].value = url;
9241 $("#download-backup-url").attr("href", url);
9242 $(".download-backup-log-url").attr("href", logs);
9243
9244 $.bmi.modal("backup-progress-modal").close();
9245 if ($('#storage-encryption-true').is(':checked')) {
9246 $('.bmi-encryption-warning-wrapper').show();
9247 } else $('.bmi-encryption-warning-wrapper').hide();
9248
9249 $.bmi.modal("backup-success-modal").open();
9250 })
9251 .catch(function (error) {
9252 $.bmi.modal("backup-progress-modal").close();
9253 if ($('#storage-encryption-true').is(':checked')) {
9254 $('.bmi-encryption-warning-wrapper').show();
9255 } else $('.bmi-encryption-warning-wrapper').hide();
9256
9257 $.bmi.modal("backup-success-modal").open();
9258
9259 let url = $($("#bmi_restore_tbody").find("tr")[0])
9260 .find(".bc-download-btn")
9261 .attr("href");
9262 $("#download-backup-url").attr({ href: url });
9263 $("#text-input-copy").val(url);
9264 });
9265 }
9266 }
9267 },
9268 );
9269 }
9270
9271 function reloadAndHandleBackupEnd() {
9272 $.bmi.getCurrentBackups(function (res) {
9273 $.bmi.fillWithNewBackups(res.backups, res.backups.ongoing, function () {
9274 handleNotOwnBackupEnd();
9275 });
9276 });
9277 }
9278
9279 // ONGOING BACKUP
9280 function runCheckOnGoing() {
9281 checkIfBackupOnGoing(function () {
9282 setTimeout(function () {
9283 if (!$("#freeze-loading-modal").hasClass("open") && !(tryingToAbortBackup)){
9284 runCheckOnGoing();
9285 }
9286 }, ongoing_interval);
9287 });
9288 }
9289 runCheckOnGoing();
9290
9291 $("#bmi-ongoing-backup").on("click", function () {
9292 isBackupOngoing(function (isRunnin) {
9293 if (isRunnin === true) {
9294 curdivs = 0;
9295 refreshLogAndProgress();
9296 setTimeout(function () {
9297 $.bmi.modal("freeze-loading-modal").close();
9298 setTimeout(function () {
9299 // Make sure the progress modal is open
9300 $.bmi.modal("backup-progress-modal").open();
9301 }, 300);
9302 }, 300);
9303 } else {
9304 $.bmi.alert("info", $("#bmi-bc-ended").text(), 3000);
9305 $.bmi.reloadBackups();
9306 }
9307
9308 $("#bmi-ongoing-backup").hide(300);
9309 });
9310 });
9311
9312 function getMigrationLogs(cb = function () { }) {
9313 let url = $("#BMI_BLOG_URL").text().trim();
9314 if (url.slice(-url.length) !== "/") url = url + "/";
9315
9316 httpGet(
9317 url +
9318 "?backup-migration=PROGRESS_LOGS&progress-id=latest_migration_full.log&bmi-id=current&t=" +
9319 +new Date() +
9320 "&sk=" +
9321 $("#BMI_SECRET_KEY").text().trim(),
9322 function (res1) {
9323 if (!res1) {
9324 return cb();
9325 }
9326
9327 let res2 = res1.split("\n").slice(0, 1)[0];
9328 res1 = res1.split("\n").slice(1).join("\n");
9329
9330 if (res2 === false || isNaN(parseFloat(res2))) {
9331 return cb();
9332 }
9333 restoreLogs = res1;
9334 let pre = $("#restore-live-log-wrapper").find("pre")[0];
9335
9336 if (!res1.includes("<") && !res1.includes(">")) {
9337 if (res1 && res1 != false && typeof res1 !== "undefined") {
9338 insertPre(res1, pre);
9339 }
9340 }
9341
9342 setProgress(res2);
9343 cb();
9344 },
9345 );
9346 }
9347
9348 function fixHtaccessPromise() {
9349 return new Promise((resolve) => {
9350 fixHtaccess(() => {
9351 return resolve();
9352 });
9353 });
9354 }
9355
9356 bmi.fixHtaccessPromise = fixHtaccessPromise;
9357
9358 function fixHtaccess(cb = function () {}) {
9359 $.bmi
9360 .ajax("htaccess-litespeed", {})
9361 .then(function (res) {
9362 setTimeout(function () {
9363 cb(true);
9364 }, 400);
9365 })
9366 .catch(function (error) {
9367 cb(false);
9368 });
9369 }
9370
9371 function isMigrationLocked(
9372 cb = function () { },
9373 mute = false,
9374 clearLogs = true,
9375 ) {
9376 $.bmi
9377 .ajax("migration-locked", { clearLogs: clearLogs })
9378 .then(function (res) {
9379 if (res.status == "success") cb(true);
9380 else {
9381 if (!mute) $.bmi._msg(res);
9382 cb(false);
9383 }
9384 })
9385 .catch(function (error) {
9386 //
9387 });
9388 }
9389
9390 function downloadGDrive(startRestoreProcess = true) {
9391 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9392 let currentGDriveIdRestoration =
9393 current_restore.split("?#googledrive#_")[1];
9394 current_restore = null;
9395
9396 if (startRestoreProcess == false) {
9397 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9398 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9399 }
9400
9401 isMigrationLocked(function (isNotLocked) {
9402 resetLogs(true, function () {
9403 fixHtaccess(function () {
9404 if (isNotLocked) {
9405 curdivs = 0;
9406 refreshLogAndProgressRestore();
9407 restoreOnGoing = true;
9408 cli_quickmigration = true;
9409
9410 $.bmi.modal("pre-restore-modal").close();
9411 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9412 $.bmi.modal("restore-progress-modal").open();
9413
9414 performGDriveDownload(startRestoreProcess, currentGDriveIdRestoration);
9415 }
9416 });
9417 });
9418 });
9419 }
9420
9421 $.bmi.downloadGDrive = downloadGDrive;
9422
9423 function downloadFTP(startRestoreProcess = true) {
9424
9425 $('#restore-live-log-wrapper').find('pre')[0].innerText = '';
9426 let currentFtpIdRestoration = current_restore.split('?#ftp#_')[1];
9427 let md5 = $("#bmi_restore_tbody").find("tr[ftp-id='" + currentFtpIdRestoration + "']").attr("md5");
9428 current_restore = null;
9429
9430 if (startRestoreProcess == false) {
9431 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9432 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9433 }
9434
9435 isMigrationLocked(function (isNotLocked) {
9436 resetLogs(true, function () {
9437 fixHtaccess(function () {
9438 if (isNotLocked) {
9439
9440 curdivs = 0;
9441 refreshLogAndProgressRestore();
9442 restoreOnGoing = true;
9443 cli_quickmigration = true;
9444
9445 $.bmi.modal('pre-restore-modal').close();
9446 $('#restore-live-log-wrapper').find('pre')[0].innerText = '';
9447 $.bmi.modal('restore-progress-modal').open();
9448
9449 performFtpDownload(startRestoreProcess, currentFtpIdRestoration, 0, false, md5);
9450
9451 }
9452 });
9453 });
9454 });
9455
9456 }
9457
9458 $.bmi.downloadFTP = downloadFTP;
9459
9460 function downloadOneDrive(startRestoreProcess = true) {
9461 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9462 let currentOneDriveIdRestoration =
9463 current_restore.split("?#onedrive#_")[1];
9464
9465 let md5 = $("#bmi_restore_tbody").find("tr[onedrive-id='" + currentOneDriveIdRestoration + "']").attr("md5");
9466 current_restore = null;
9467
9468 if (startRestoreProcess == false) {
9469 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9470 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9471 }
9472
9473 isMigrationLocked(function (isNotLocked) {
9474 resetLogs(true, function () {
9475 fixHtaccess(function () {
9476 if (isNotLocked) {
9477 curdivs = 0;
9478 refreshLogAndProgressRestore();
9479 restoreOnGoing = true;
9480 cli_quickmigration = true;
9481
9482 $.bmi.modal("pre-restore-modal").close();
9483 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9484 $.bmi.modal("restore-progress-modal").open();
9485
9486 performOneDriveDownload(startRestoreProcess, currentOneDriveIdRestoration, 0, false, md5);
9487 }
9488 });
9489 });
9490 });
9491 }
9492
9493 $.bmi.downloadOneDrive = downloadOneDrive;
9494
9495 function downloadBackupBliss(startRestoreProcess = true) {
9496 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9497 let currentBackupBlissIdRestoration =
9498 current_restore.split("?#backupbliss#_")[1];
9499
9500 let md5 = $("#bmi_restore_tbody").find("tr[backupbliss-id='" + currentBackupBlissIdRestoration + "']").attr("md5");
9501 current_restore = null;
9502
9503 isMigrationLocked(function (isNotLocked) {
9504 resetLogs(true, function () {
9505 fixHtaccess(function () {
9506 if (isNotLocked) {
9507 curdivs = 0;
9508 refreshLogAndProgressRestore();
9509 restoreOnGoing = true;
9510 cli_quickmigration = true;
9511
9512 $.bmi.modal("pre-restore-modal").close();
9513 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9514 $.bmi.modal("restore-progress-modal").open();
9515
9516 performBackupBlissDownload(startRestoreProcess, currentBackupBlissIdRestoration, 0, false, md5);
9517 }
9518 });
9519 });
9520 });
9521 }
9522
9523 $.bmi.downloadBackupBliss = downloadBackupBliss;
9524
9525 function performFtpDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9526
9527 $.bmi.ajax('download-cloud-backup', {
9528
9529 storage: 'ftp',
9530 startRestoreProcess: startRestoreProcess,
9531 fileId: fileId,
9532 step: step,
9533 size: size,
9534 md5: md5,
9535 filename: originalFilename,
9536 writepath: writepath,
9537 chunksize: chunksize,
9538 secret: secret
9539
9540 }).then(function (res) {
9541
9542 if (res.status == 'success') {
9543
9544 if (typeof res.size != 'undefined') size = res.size;
9545 if (typeof res.md5 != 'undefined') md5 = res.md5;
9546 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9547 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9548 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9549 if (typeof res.secret != 'undefined') secret = res.secret;
9550
9551 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9552
9553 step++;
9554 performFtpDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9555
9556 } else {
9557
9558 cli_quickmigration = true;
9559 current_restore = res.filename;
9560 refreshLogAndProgressRestore(false);
9561
9562 $.bmi.reloadBackups(function () {
9563
9564 setTimeout(function () {
9565
9566 clearInterval(iprogres);
9567 $('#restore-progress-modal .progress-active-bar')[0].style.width = 0 + '%';
9568 $('#restore-progress-modal .progress-percentage')[0].style.left = 0 + '%';
9569 $('#restore-progress-modal .progress-percentage')[0].innerText = 0 + '%';
9570 $('#restore_current_step').text($('#bmi-restoring-prepare').text());
9571
9572 }, 600);
9573 });
9574
9575 }
9576
9577 } else {
9578
9579 $.bmi._msg(res);
9580 restoreFailed();
9581 console.error(res);
9582
9583 }
9584
9585 }).catch(function (error) {
9586
9587 restoreFailed(error);
9588
9589 });
9590
9591 }
9592
9593 function performGDriveDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9594
9595 $.bmi.ajax('download-cloud-backup', {
9596
9597 storage: 'googledrive',
9598 startRestoreProcess: startRestoreProcess,
9599 fileId: fileId,
9600 step: step,
9601 size: size,
9602 md5: md5,
9603 filename: originalFilename,
9604 writepath: writepath,
9605 chunksize: chunksize,
9606 secret: secret
9607
9608 }).then(function (res) {
9609
9610 if (res.status == 'success') {
9611
9612 if (typeof res.size != 'undefined') size = res.size;
9613 if (typeof res.md5 != 'undefined') md5 = res.md5;
9614 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9615 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9616 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9617 if (typeof res.secret != 'undefined') secret = res.secret;
9618
9619 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9620
9621 step++;
9622 performGDriveDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9623
9624 } else if (res.finished == 'true') {
9625 cli_quickmigration = true;
9626 current_restore = res.filename;
9627 refreshLogAndProgressRestore(false);
9628
9629 $.bmi.reloadBackups(function () {
9630 setTimeout(function () {
9631 clearInterval(iprogres);
9632 $(
9633 "#restore-progress-modal .progress-active-bar",
9634 )[0].style.width = 0 + "%";
9635 $(
9636 "#restore-progress-modal .progress-percentage",
9637 )[0].style.left = 0 + "%";
9638 $("#restore-progress-modal .progress-percentage")[0].innerText =
9639 0 + "%";
9640 $("#restore_current_step").text(
9641 $("#bmi-restoring-prepare").text(),
9642 );
9643 }, 600);
9644 });
9645
9646 }
9647 } else {
9648 $.bmi._msg(res);
9649 restoreFailed();
9650 console.error(res);
9651 }
9652 })
9653 .catch(function (error) {
9654 restoreFailed(error);
9655 });
9656 }
9657
9658 function downloadDropbox(startRestoreProcess = true) {
9659 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9660 let currentDropboxIdRestoration = current_restore.split("?#dropbox#_")[1];
9661 let md5 = $("#bmi_restore_tbody").find("tr[dropbox-id='" + currentDropboxIdRestoration + "']").attr("md5");
9662 current_restore = null;
9663
9664 if (startRestoreProcess == false) {
9665 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9666 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9667 }
9668
9669 isMigrationLocked(function (isNotLocked) {
9670 resetLogs(true, function () {
9671 fixHtaccess(function () {
9672 if (isNotLocked) {
9673 curdivs = 0;
9674 refreshLogAndProgressRestore();
9675 restoreOnGoing = true;
9676 cli_quickmigration = true;
9677
9678 $.bmi.modal("pre-restore-modal").close();
9679 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9680 $.bmi.modal("restore-progress-modal").open();
9681
9682
9683 performDropboxDownload(startRestoreProcess, currentDropboxIdRestoration, 0, false, md5);
9684 }
9685 });
9686 });
9687 });
9688 }
9689
9690 function performDropboxDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = false, secret = false) {
9691
9692 $.bmi.ajax('download-dropbox-backup', {
9693
9694 fileId: fileId,
9695 step: step,
9696 size: size,
9697 md5: md5,
9698 filename: originalFilename,
9699 writepath: writepath,
9700 chunksize: chunksize,
9701 startRestoreProcess: startRestoreProcess,
9702 secret: secret
9703
9704 }).then(function(res) {
9705
9706 if (res.status == 'success') {
9707
9708 if (typeof res.size != 'undefined') size = res.size;
9709 if (typeof res.md5 != 'undefined') md5 = res.md5;
9710 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9711 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9712 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9713 if (typeof res.secret != 'undefined') secret = res.secret;
9714
9715 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9716
9717 step++;
9718 performDropboxDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9719
9720
9721 } else if (res.finished == 'true') {
9722
9723 cli_quickmigration = true;
9724 current_restore = res.filename;
9725 refreshLogAndProgressRestore(false);
9726
9727 $.bmi.reloadBackups(function () {
9728 setTimeout(function () {
9729 clearInterval(iprogres);
9730 $(
9731 "#restore-progress-modal .progress-active-bar",
9732 )[0].style.width = 0 + "%";
9733 $(
9734 "#restore-progress-modal .progress-percentage",
9735 )[0].style.left = 0 + "%";
9736 $("#restore-progress-modal .progress-percentage")[0].innerText =
9737 0 + "%";
9738 $("#restore_current_step").text(
9739 $("#bmi-restoring-prepare").text(),
9740 );
9741 }, 600);
9742 });
9743
9744 }
9745
9746 } else {
9747
9748 $.bmi._msg(res);
9749 restoreFailed();
9750 console.error(res);
9751
9752 }
9753
9754 }).catch(function(error) {
9755
9756 restoreFailed(error);
9757
9758 });
9759
9760 }
9761
9762
9763 function downloadS3(startRestoreProcess = true, provider = 'aws') {
9764 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9765 let currentS3IdRestoration = current_restore.split("?#" + provider + "#_")[1];
9766 let md5 = $("#bmi_restore_tbody").find("tr[" + provider + "-id='" + currentS3IdRestoration + "']").attr("md5");
9767 current_restore = null;
9768
9769 isMigrationLocked(function (isNotLocked) {
9770 resetLogs(true, function () {
9771 fixHtaccess(function () {
9772 if (isNotLocked) {
9773 curdivs = 0;
9774 refreshLogAndProgressRestore();
9775 restoreOnGoing = true;
9776 cli_quickmigration = true;
9777
9778 $.bmi.modal("pre-restore-modal").close();
9779 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9780 $.bmi.modal("restore-progress-modal").open();
9781
9782 performS3Download(startRestoreProcess, provider, currentS3IdRestoration, 0, false, md5);
9783 }
9784 });
9785 });
9786 });
9787 }
9788
9789 function performS3Download(startRestoreProcess = true, provider = 'aws', fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9790 $.bmi.ajax('download-cloud-backup', {
9791 storage: 's3',
9792 provider: provider,
9793 fileId: fileId,
9794 step: step,
9795 size: size,
9796 md5: md5,
9797 filename: originalFilename,
9798 writepath: writepath,
9799 chunksize: chunksize,
9800 startRestoreProcess: startRestoreProcess,
9801 secret: secret
9802 }).then(function (res) {
9803 if (res.status == 'success') {
9804 if (typeof res.size != 'undefined') size = res.size;
9805 if (typeof res.md5 != 'undefined') md5 = res.md5;
9806 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9807 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9808 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9809 if (typeof res.secret != 'undefined') secret = res.secret;
9810 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9811 step++;
9812 performS3Download(startRestoreProcess, provider, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9813 } else if (res.finished == 'true') {
9814 cli_quickmigration = true;
9815 current_restore = res.filename;
9816 refreshLogAndProgressRestore(false);
9817 $.bmi.reloadBackups(function () {
9818 setTimeout(function () {
9819 clearInterval(iprogres);
9820 $(
9821 "#restore-progress-modal .progress-active-bar",
9822 )[0].style.width = 0 + "%";
9823 $(
9824 "#restore-progress-modal .progress-percentage",
9825 )[0].style.left = 0 + "%";
9826 $("#restore-progress-modal .progress-percentage")[0].innerText =
9827 0 + "%";
9828 $("#restore_current_step").text(
9829 $("#bmi-restoring-prepare").text(),
9830 );
9831 }, 600);
9832 });
9833 }
9834 } else {
9835 $.bmi._msg(res);
9836 restoreFailed();
9837 console.error(res);
9838 }
9839 }).catch(function (error) {
9840 restoreFailed(error);
9841 });
9842 }
9843
9844 $.bmi.downloadS3 = downloadS3;
9845
9846 function performOneDriveDownload(startRestoreProcess = true,fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9847
9848 $.bmi.ajax('download-cloud-backup', {
9849
9850 storage: 'onedrive',
9851 fileId: fileId,
9852 step: step,
9853 size: size,
9854 md5: md5,
9855 filename: originalFilename,
9856 writepath: writepath,
9857 chunksize: chunksize,
9858 startRestoreProcess: startRestoreProcess,
9859 secret: secret
9860
9861 }).then(function (res) {
9862
9863 if (res.status == 'success') {
9864
9865 if (typeof res.size != 'undefined') size = res.size;
9866 if (typeof res.md5 != 'undefined') md5 = res.md5;
9867 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9868 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9869 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9870 if (typeof res.secret != 'undefined') secret = res.secret;
9871
9872 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9873
9874 step++;
9875 performOneDriveDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9876
9877 } else if (res.finished == 'true') {
9878 cli_quickmigration = true;
9879 current_restore = res.filename;
9880 refreshLogAndProgressRestore(false);
9881
9882 $.bmi.reloadBackups(function () {
9883 setTimeout(function () {
9884 clearInterval(iprogres);
9885 $(
9886 "#restore-progress-modal .progress-active-bar",
9887 )[0].style.width = 0 + "%";
9888 $(
9889 "#restore-progress-modal .progress-percentage",
9890 )[0].style.left = 0 + "%";
9891 $("#restore-progress-modal .progress-percentage")[0].innerText =
9892 0 + "%";
9893 $("#restore_current_step").text(
9894 $("#bmi-restoring-prepare").text(),
9895 );
9896 }, 600);
9897 });
9898
9899 }
9900 } else {
9901 $.bmi._msg(res);
9902 restoreFailed();
9903 console.error(res);
9904 }
9905 })
9906 .catch(function (error) {
9907 restoreFailed(error);
9908 });
9909 }
9910
9911 function performBackupBlissDownload(startRestoreProcess = true,fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9912
9913 $.bmi.ajax('download-cloud-backup', {
9914
9915 storage: 'backupbliss',
9916 fileId: fileId,
9917 step: step,
9918 size: size,
9919 md5: md5,
9920 filename: originalFilename,
9921 writepath: writepath,
9922 chunksize: chunksize,
9923 startRestoreProcess: startRestoreProcess,
9924 secret: secret
9925
9926 }).then(function (res) {
9927
9928 if (res.status == 'success') {
9929
9930 if (typeof res.size != 'undefined') size = res.size;
9931 if (typeof res.md5 != 'undefined') md5 = res.md5;
9932 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9933 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9934 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9935 if (typeof res.secret != 'undefined') secret = res.secret;
9936
9937 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9938
9939 step++;
9940 performBackupBlissDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9941
9942 } else if (res.finished == 'true') {
9943 cli_quickmigration = true;
9944 current_restore = res.filename;
9945 refreshLogAndProgressRestore(false);
9946
9947 $.bmi.reloadBackups(function () {
9948 setTimeout(function () {
9949 clearInterval(iprogres);
9950 $(
9951 "#restore-progress-modal .progress-active-bar",
9952 )[0].style.width = 0 + "%";
9953 $(
9954 "#restore-progress-modal .progress-percentage",
9955 )[0].style.left = 0 + "%";
9956 $("#restore-progress-modal .progress-percentage")[0].innerText =
9957 0 + "%";
9958 $("#restore_current_step").text(
9959 $("#bmi-restoring-prepare").text(),
9960 );
9961 }, 600);
9962 });
9963
9964 }
9965 } else {
9966 $.bmi._msg(res);
9967 restoreFailed();
9968 console.error(res);
9969 }
9970 })
9971 .catch(function (error) {
9972 restoreFailed(error);
9973 });
9974 }
9975
9976 function downloadSFTP(startRestoreProcess = true) {
9977 console.log("downloadSFTP");
9978 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9979 let currentSFTPIdRestoration = current_restore.split("?#sftp#_")[1];
9980 let md5 = $("#bmi_restore_tbody").find("tr[sftp-id='" + currentSFTPIdRestoration + "']").attr("md5");
9981 current_restore = null;
9982
9983 isMigrationLocked(function (isNotLocked) {
9984 resetLogs(true, function () {
9985 fixHtaccess(function () {
9986 if (isNotLocked) {
9987 curdivs = 0;
9988 refreshLogAndProgressRestore();
9989 restoreOnGoing = true;
9990 cli_quickmigration = true;
9991
9992 $.bmi.modal("pre-restore-modal").close();
9993 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9994 $.bmi.modal("restore-progress-modal").open();
9995
9996 performSFTPDownload(startRestoreProcess, currentSFTPIdRestoration, 0, false, md5);
9997 }
9998 });
9999 });
10000 });
10001 }
10002
10003 function performSFTPDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
10004
10005 $.bmi.ajax('download-sftp-backup', {
10006
10007 fileId: fileId,
10008 step: step,
10009 size: size,
10010 md5: md5,
10011 filename: originalFilename,
10012 writepath: writepath,
10013 chunksize: chunksize,
10014 startRestoreProcess: startRestoreProcess,
10015 secret: secret
10016
10017 }).then(function (res) {
10018
10019 if (res.status == 'success') {
10020
10021 if (typeof res.size != 'undefined') size = res.size;
10022 if (typeof res.md5 != 'undefined') md5 = res.md5;
10023 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
10024 if (typeof res.writepath != 'undefined') writepath = res.writepath;
10025 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
10026 if (typeof res.secret != 'undefined') secret = res.secret;
10027
10028 if (typeof res.finished != 'undefined' && res.finished != 'true') {
10029
10030 step++;
10031 performSFTPDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
10032
10033 } else if (res.finished == 'true') {
10034 cli_quickmigration = true;
10035 current_restore = res.filename;
10036 refreshLogAndProgressRestore(false);
10037
10038 $.bmi.reloadBackups(function () {
10039 setTimeout(function () {
10040 clearInterval(iprogres);
10041 $("#restore-progress-modal .progress-active-bar")[0].style.width = 0 + "%";
10042 $("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
10043 $("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
10044 $("#restore_current_step").text($("#bmi-restoring-prepare").text());
10045 }, 600);
10046 });
10047
10048 }
10049 } else {
10050 $.bmi._msg(res);
10051 restoreFailed();
10052 console.error(res);
10053 }
10054 })
10055 .catch(function (error) {
10056 restoreFailed(error);
10057 });
10058 }
10059
10060
10061 $.bmi.downloadSFTP = downloadSFTP;
10062
10063
10064
10065 function downloadPCloud(startRestoreProcess = true) {
10066 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
10067 let currentPCloudIdRestoration = current_restore.split("?#pcloud#_")[1];
10068 let md5 = $("#bmi_restore_tbody").find("tr[pcloud-id='" + currentPCloudIdRestoration + "']").attr("md5");
10069 current_restore = null;
10070
10071 if (startRestoreProcess == false) {
10072 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
10073 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
10074 }
10075
10076 isMigrationLocked(function (isNotLocked) {
10077 resetLogs(true, function () {
10078 fixHtaccess(function () {
10079 if (isNotLocked) {
10080 curdivs = 0;
10081 refreshLogAndProgressRestore();
10082 restoreOnGoing = true;
10083 cli_quickmigration = true;
10084
10085 $.bmi.modal("pre-restore-modal").close();
10086 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
10087 $.bmi.modal("restore-progress-modal").open();
10088
10089 performPCloudDownload(startRestoreProcess, currentPCloudIdRestoration, 0, false, md5);
10090 }
10091 });
10092 });
10093 });
10094 }
10095
10096 $.bmi.downloadPCloud = downloadPCloud;
10097
10098 function performPCloudDownload(
10099 startRestoreProcess = true,
10100 fileId = false,
10101 step = 0,
10102 size = false,
10103 md5 = false,
10104 originalFilename = false,
10105 writepath = false,
10106 chunksize = false,
10107 secret = false
10108 ) {
10109 $.bmi
10110 .ajax("download-cloud-backup", {
10111 storage: "pcloud",
10112 fileId: fileId,
10113 step: step,
10114 size: size,
10115 md5: md5,
10116 filename: originalFilename,
10117 writepath: writepath,
10118 chunksize: chunksize,
10119 startRestoreProcess: startRestoreProcess,
10120 secret: secret,
10121 })
10122 .then(function (res) {
10123 if (res.status == "success") {
10124 if (typeof res.size != "undefined") size = res.size;
10125 if (typeof res.md5 != "undefined") md5 = res.md5;
10126 if (typeof res.originalFilename != "undefined")
10127 originalFilename = res.originalFilename;
10128 if (typeof res.writepath != "undefined") writepath = res.writepath;
10129 if (typeof res.chunksize != "undefined") chunksize = res.chunksize;
10130 if (typeof res.secret != "undefined") secret = res.secret;
10131
10132 if (typeof res.finished != "undefined" && res.finished != "true") {
10133 step++;
10134 performPCloudDownload(
10135 startRestoreProcess,
10136 fileId,
10137 step,
10138 size,
10139 md5,
10140 originalFilename,
10141 writepath,
10142 chunksize,
10143 secret,
10144 );
10145 } else if (res.finished == "true") {
10146 cli_quickmigration = true;
10147 current_restore = res.filename;
10148 refreshLogAndProgressRestore(false);
10149
10150 $.bmi.reloadBackups(function () {
10151 setTimeout(function () {
10152 clearInterval(iprogres);
10153 $(
10154 "#restore-progress-modal .progress-active-bar",
10155 )[0].style.width = 0 + "%";
10156 $(
10157 "#restore-progress-modal .progress-percentage",
10158 )[0].style.left = 0 + "%";
10159 $("#restore-progress-modal .progress-percentage")[0].innerText =
10160 0 + "%";
10161 $("#restore_current_step").text(
10162 $("#bmi-restoring-prepare").text(),
10163 );
10164 }, 600);
10165 });
10166 }
10167 } else {
10168 $.bmi._msg(res);
10169 restoreFailed();
10170 console.error(res);
10171 }
10172 })
10173 .catch(function (error) {
10174 restoreFailed(error);
10175 });
10176 }
10177
10178 $.bmi.downloadDropbox = downloadDropbox;
10179
10180
10181 function downloadMigration( startRestoreProcess = true) {
10182 current_restore = null;
10183 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
10184
10185 if (startRestoreProcess == false) {
10186 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
10187 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
10188 }
10189
10190 isMigrationLocked(function (isNotLocked) {
10191 resetLogs(true, function () {
10192 fixHtaccess(function () {
10193 if (isNotLocked) {
10194 let url = $("#bm-d-url").val();
10195 $("#restore_current_step").text(
10196 $("#bmi-downloading-remote").text(),
10197 );
10198 $.bmi
10199 .ajax("download-backup", {
10200 url: url,
10201 startRestoreProcess: startRestoreProcess
10202 })
10203 .then(function (res) {
10204 clearInterval(iprogres);
10205 clearTimeout(timeouter);
10206
10207 if (res.status === "success") {
10208 // Set backup to restore name
10209 current_restore = res.name;
10210
10211 // Refresh logs in such case
10212 cli_quickmigration = true;
10213 refreshLogAndProgressRestore(false);
10214
10215 // Reload backup list
10216 $.bmi.reloadBackups();
10217
10218 // setProgress(100, 300);
10219 setTimeout(function () {
10220 clearInterval(iprogres);
10221 $(
10222 "#restore-progress-modal .progress-active-bar",
10223 )[0].style.width = 0 + "%";
10224 $(
10225 "#restore-progress-modal .progress-percentage",
10226 )[0].style.left = 0 + "%";
10227 $(
10228 "#restore-progress-modal .progress-percentage",
10229 )[0].innerText = 0 + "%";
10230 $("#restore_current_step").text(
10231 $("#bmi-restoring-prepare").text(),
10232 );
10233 }, 600);
10234 } else if (res.status == "cli_download") {
10235 current_restore = ".cli_download";
10236 cli_quickmigration = true;
10237 refreshLogAndProgressRestore(false);
10238 } else if (res.status === "error") {
10239 restoreFailed();
10240 } else {
10241 $.bmi._msg(res);
10242 restoreFailed();
10243 }
10244 })
10245 .catch(function (error) {
10246 restoreFailed(error);
10247 });
10248
10249 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
10250
10251 curdivs = 0;
10252 restoreOnGoing = true;
10253 cli_quickmigration = true;
10254 refreshLogAndProgressRestore();
10255 $.bmi.modal("pre-restore-modal").close();
10256 $.bmi.modal("restore-progress-modal").open();
10257 }
10258 });
10259 });
10260 });
10261 }
10262
10263 $.bmi.downloadMigration = downloadMigration;
10264
10265 function restoreFailed(text = "") {
10266 $('#restore-progress-modal .title').text($('#bmi-restore-progress-modal-title').text());
10267 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-restore-progress-modal-warning').text());
10268 setTimeout(function () {
10269 $("#restore-progress-modal .progress-active-bar")[0].style.width =
10270 0 + "%";
10271 $("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
10272 $("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
10273 }, 500);
10274
10275 $.bmi.releaseWakeLock();
10276
10277 isMigrationLocked(
10278 function (isNotLocked) {
10279 if (isNotLocked) {
10280 setTimeout(function () {
10281 if (!$("#restore-progress-modal").hasClass("open")) return;
10282
10283 restoreOnGoing = false;
10284 cli_quickmigration = false;
10285 $("#restore-error-pre").text(
10286 $("#bmi-loading-translation").text().trim(),
10287 );
10288 $("#after-logs-sent-modal").attr("data-error-source", "migration");
10289
10290 let url = $("#BMI_BLOG_URL").text().trim();
10291 if (url.slice(-url.length) !== "/") url = url + "/";
10292
10293 // httpGet(url + '?backup-migration=PROGRESS_LOGS&progress-id=latest_migration.log&bmi-id=current&t=' + +new Date(), function(res) {
10294 //
10295 // if (res == false) {
10296 // setTimeout(function () {
10297 // restoreFailed();
10298 // }, 1500);
10299 // } else {
10300 // let pre = $('#restore-error-pre')[0];
10301 // $('#restore-error-pre').text('');
10302 // curdivs = 0;
10303 // insertPre(res, pre);
10304 // }
10305 //
10306 // });
10307
10308 $.bmi.modal("restore-progress-modal").close();
10309 setupRestoreErrorOptions().then(function () {
10310 $.bmi.modal("error-modal").open();
10311 $.bmi.modal("error-modal").setParent("restore-progress-modal");
10312 });
10313 }, 1000);
10314 } else {
10315 setTimeout(function () {
10316 $.bmi.modal("restore-progress-modal").close();
10317 setupRestoreErrorOptions().then(function () {
10318 $.bmi.modal("error-modal").open();
10319 $.bmi.modal("error-modal").setParent("restore-progress-modal");
10320 });
10321 });
10322 }
10323 },
10324 true,
10325 false,
10326 );
10327 }
10328
10329 function restoreCLISuccess() {
10330 $.bmi.modal("restore-progress-modal").close();
10331 restoreExecutionTime = (new Date().getTime() - restoreStartTime) / 1000;
10332 $.bmi.modal("restore-success-modal").open();
10333
10334 setTimeout(function () {
10335 $("#restore-progress-modal .progress-active-bar")[0].style.width =
10336 0 + "%";
10337 $("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
10338 $("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
10339 }, 500);
10340
10341 restoreCLI = false;
10342 }
10343
10344 function letsRestore(remote = false, secret = null) {
10345 let name = current_restore;
10346 if (!name || name.trim().length <= 0)
10347 return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
10348
10349 if ($("#pre-restore-modal").hasClass("open"))
10350 $.bmi.modal("pre-restore-modal").close();
10351
10352 if (!$("#restore-progress-modal").hasClass("open"))
10353 $.bmi.modal("restore-progress-modal").open();
10354
10355 if (!remote) $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
10356
10357 if (secret == null) {
10358 if (!remote) curdivs = 0;
10359 restoreOnGoing = true;
10360 clearTimeout(timeouter);
10361
10362 restoreStartTime = new Date().getTime();
10363 function initializeRestoration() {
10364 fixHtaccess(function () {
10365 getMigrationLogs(function () {
10366 runRestoreProcess(name, remote, secret);
10367 });
10368 });
10369 }
10370
10371 $.bmi.requestWakeLock();
10372 if (!remote) resetLogs(true, initializeRestoration);
10373 else initializeRestoration();
10374 }
10375 }
10376
10377 $.bmi.letsRestore = letsRestore;
10378
10379 function runRestoreProcess(
10380 name,
10381 remote,
10382 secret,
10383 tmpname = false,
10384 ignoreRunning = "false",
10385 options = {},
10386 ) {
10387 if (secret == null && remote !== true) {
10388 refreshLogAndProgressRestore(true);
10389 }
10390
10391 if (typeof options.storage != "undefined") {
10392 options.storage = options.storage.replace(/[\\\\/]+/g, "/");
10393 }
10394
10395 $.bmi
10396 .ajax("restore-backup", {
10397 file: name,
10398 remote: remote,
10399 secret: secret,
10400 ignoreRunning: ignoreRunning,
10401 tmpname: tmpname,
10402 options: options,
10403 })
10404 .then(function (res) {
10405 if (res.status === "cli") {
10406 autoLog = { l: res.login, u: res.url };
10407 restoreCLI = true;
10408
10409 let end_code = getEndCode();
10410 if (end_code ) {
10411 if (end_code == "001") {
10412 $.bmi.releaseWakeLock();
10413 setTimeout(function () {
10414 restoreCLISuccess();
10415 restoreOnGoing = false;
10416 restoreCLI = false;
10417 }, 1000);
10418 } else {
10419 restoreFailed();
10420 }
10421 }
10422 } else if (res.status === "success") {
10423 autoLog = { l: res.login, u: res.url };
10424 $.bmi.releaseWakeLock();
10425
10426 setTimeout(function () {
10427 clearInterval(iprogres);
10428 clearTimeout(timeouter);
10429 restoreOnGoing = false;
10430
10431 $.bmi.modal("restore-progress-modal").close();
10432 restoreExecutionTime = (new Date().getTime() - restoreStartTime) / 1000;
10433 $.bmi.modal("restore-success-modal").open();
10434
10435 setTimeout(function () {
10436 $("#restore-progress-modal .progress-active-bar")[0].style.width =
10437 0 + "%";
10438 $("#restore-progress-modal .progress-percentage")[0].style.left =
10439 0 + "%";
10440 $("#restore-progress-modal .progress-percentage")[0].innerText =
10441 0 + "%";
10442 }, 500);
10443 }, 1500);
10444 } else if (res.status === "password") {
10445 console.log("Password required to proceed with restore. Prompting user for password.");
10446 promptAndValidatePassword(name)
10447 .then((validSecret) => {
10448 console.log("Password validated successfully. Resuming restore process.");
10449 res.options.step = parseInt(res.options.step) + 1;
10450 res.options.password = validSecret;
10451
10452 runRestoreProcess(
10453 name,
10454 remote,
10455 "secret", // Bypass reset of logs and progress since we're just resuming after password entry
10456 res.tmp,
10457 "true",
10458 res.options
10459 );
10460 })
10461 .catch((err) => {
10462
10463 // Handled if the user clicks cancel
10464 console.warn(err.message);
10465 $.bmi.modal("restore-progress-modal").close();
10466 $("#bmi-force-restore-to-stop").click(); // Force stop the restore process on the backend as well
10467 setTimeout(function () { // Reset progress bar after a short delay to ensure the modal has closed first
10468 $("#restore-progress-modal .progress-active-bar")[0].style.width =
10469 0 + "%";
10470 $("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
10471 $("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
10472 }, 500);
10473
10474 $.bmi.releaseWakeLock();
10475 restoreOnGoing = false;
10476 cli_quickmigration = false;
10477 });
10478 } else if (res.status === "secret") {
10479 res.options.step = parseInt(res.options.step) + 1;
10480 runRestoreProcess(
10481 name,
10482 remote,
10483 res.secret,
10484 res.tmp,
10485 "true",
10486 res.options,
10487 );
10488 } else if (res.status === "restore_ongoing") {
10489 if (typeof res.options.firstDB != "undefined") {
10490 res.options.firstDB = false;
10491 }
10492
10493 if (typeof res.options.dbFinished != "undefined") {
10494 if (
10495 res.options.dbFinished === true ||
10496 res.options.dbFinished === "true" ||
10497 res.options.dbFinished === "1"
10498 ) {
10499 res.options.step = parseInt(res.options.step) + 1;
10500 }
10501 } else if (typeof res.options.dbConvertionFinished != "undefined") {
10502 if (
10503 res.options.dbConvertionFinished === "true" ||
10504 res.options.dbConvertionFinished === true ||
10505 res.options.dbConvertionFinished === "1"
10506 ) {
10507 res.options.step = parseInt(res.options.step) + 1;
10508 }
10509 } else if (typeof res.options.replaceFinished != "undefined") {
10510 if (
10511 res.options.replaceFinished === "true" ||
10512 res.options.replaceFinished === true ||
10513 res.options.replaceFinished === "1"
10514 ) {
10515 res.options.step = parseInt(res.options.step) + 1;
10516 }
10517 } else {
10518 res.options.step = parseInt(res.options.step) + 1;
10519
10520 if (
10521 (res.options.step == 4 || res.options.step == "4") &&
10522 typeof res.options.repeat_export != "undefined"
10523 ) {
10524 if (
10525 res.options.repeat_export === true ||
10526 res.options.repeat_export === "true" ||
10527 res.options.repeat_export === "1"
10528 ) {
10529 res.options.step = 3;
10530 res.options.firstExtract = "false";
10531 }
10532 }
10533
10534 if (
10535 (res.options.step == 6 || res.options.step == "6") &&
10536 typeof res.options.repeat_restore != "undefined"
10537 ) {
10538 if (
10539 res.options.repeat_restore === true ||
10540 res.options.repeat_restore === "true" ||
10541 res.options.repeat_restore === "1"
10542 ) {
10543 res.options.step = 5;
10544 res.options.firstFileRestore = "false";
10545 }
10546 }
10547 }
10548
10549 setTimeout(
10550 function () {
10551 runRestoreProcess(
10552 name,
10553 remote,
10554 res.secret,
10555 res.tmp,
10556 "true",
10557 res.options,
10558 );
10559 },
10560 Math.floor(Math.random() * (523 - 330)) + 330,
10561 );
10562 } else if (res.status === "error") {
10563 setTimeout(function () {
10564 clearInterval(iprogres);
10565 clearTimeout(timeouter);
10566 console.error(res);
10567
10568 restoreFailed();
10569 }, 1000);
10570 } else {
10571 $.bmi.modal("pre-restore-modal").close();
10572 $.bmi.modal("restore-progress-modal").close();
10573 $.bmi._msg(res);
10574 }
10575 })
10576 .catch(function (error) {
10577 console.error(error);
10578 restoreFailed(error);
10579 });
10580
10581 }
10582 function promptAndValidatePassword(backupName) {
10583 console.log("Prompting user for backup password for backup:", backupName);
10584 return new Promise((resolve, reject) => {
10585 $("#bmi-password-modal-title").text($("#bmi-pwd-modal-restore-title").text().trim() || "Enter Backup Password");
10586 $("#bmi-password-modal-desc").text($("#bmi-pwd-modal-restore-desc").text().trim() || "Please provide the password for this backup to proceed with the restoration.");
10587 $("#backup-password-verify-confirm").text($("#bmi-pwd-modal-restore-btn").text().trim() || "Verify & Restore");
10588 $("#bmi-password-modal-warning-wrapper").hide();
10589 $("#bmi-password-modal-icon-box").html('<svg class="bmi-restore-password-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>');
10590
10591 $.bmi.modal("password-request-modal").open();
10592
10593 $("#bmi-backup-password-input").val("");
10594
10595 $("#backup-password-verify-confirm").off("click").on("click", function (e) {
10596 e.preventDefault();
10597
10598 const enteredPassword = $("#bmi-backup-password-input").val();
10599
10600 $.bmi.modal("freeze-loading-modal").open();
10601
10602 $.bmi
10603 .ajax("validate-backup-password", {
10604 backupName: backupName,
10605 password: enteredPassword
10606 })
10607 .then(function (valRes) {
10608 $.bmi.modal("freeze-loading-modal").close();
10609
10610 if (valRes.status === "success") {
10611 $.bmi.alert("success", valRes.message || "Password correct! Resuming restore...", 3000);
10612 $.bmi.modal("password-request-modal").close();
10613
10614 resolve(enteredPassword);
10615 } else {
10616 $.bmi.alert(valRes.level || "error", valRes.why || "Incorrect password, please try again.", 3000);
10617 }
10618 })
10619 .catch(function (error) {
10620 console.error("Password validation error:", error);
10621 $.bmi.modal("freeze-loading-modal").close();
10622 $.bmi.alert("error", "An error occurred while validating the password. Please try again.", 3000);
10623 });
10624 });
10625
10626 $("#password-request-modal .bmi-modal-close, #password-request-modal .bmi-modal-closer").off("click").on("click", function() {
10627 $.bmi.modal("password-request-modal").close();
10628 reject(new Error("User cancelled password entry."));
10629 });
10630 });
10631 }
10632
10633 function getAndInsertDynamicNames() {
10634 $.bmi
10635 .ajax("get-dynamic-names", {})
10636 .then(function (res) {
10637 let rules = res.data;
10638
10639 $("#bmi_exclusion_rules").text("");
10640 if (typeof rules === "undefined") return;
10641 if (typeof rules.length === "undefined") return;
10642 for (let i = 0; i < rules.length; ++i) {
10643 let row = $(".exclusion_template").clone();
10644 row.removeClass("exclusion_template");
10645
10646 row.find(".exclusion_txt").val(rules[i].txt);
10647
10648 let posdd = row.find(".exclusion_position").find("select");
10649 let whrdd = row.find(".exclusion_where").find("select");
10650
10651 if (!posdd) continue;
10652 else posdd[0].setAttribute("data-def", rules[i].pos);
10653
10654 if (!whrdd) continue;
10655 else whrdd[0].setAttribute("data-def", rules[i].whr);
10656
10657 $("#bmi_exclusion_rules").append(row);
10658
10659 if (
10660 row.find(".exclusion_position").find(".bmi-dropdown").length > 0
10661 ) {
10662 $.bmi.setOption(
10663 row.find(".exclusion_position").find(".bmi-dropdown"),
10664 null,
10665 rules[i].pos,
10666 );
10667 }
10668
10669 if (row.find(".exclusion_where").find(".bmi-dropdown").length > 0) {
10670 $.bmi.setOption(
10671 row.find(".exclusion_where").find(".bmi-dropdown"),
10672 null,
10673 rules[i].whr,
10674 );
10675 }
10676 }
10677
10678 $("#dynamic-fpaths-names").text(res["dynamic-fpaths-names"].join("\n"));
10679 $("#dynamic-dpaths-names").text(res["dynamic-dpaths-names"].join("\n"));
10680 })
10681 .catch(function (error) {
10682 //
10683 });
10684 }
10685
10686 $("#bmi_support_chat").on("click", function () {
10687 if ($("#support-bmi").length === 0) {
10688 $("#bmi").append(
10689 '<script id="support-bmi" src="' +
10690 $("#bmi-support-url-translation").val() +
10691 '" async></script>',
10692 );
10693 setTimeout(function () {
10694 $("#bmi_support_chat").hide();
10695 }, 100);
10696 var loaded = false;
10697 let loadinter = setInterval(function () {
10698 if (loaded == true) clearInterval(loadinter);
10699 if (typeof window.jivo_api !== "undefined") {
10700 window.jivo_api.open();
10701 loaded = true;
10702 }
10703 }, 30);
10704 }
10705 });
10706
10707 function getAllSelectedBackups() {
10708 return $("#bmi_restore_tbody").find('input[type="checkbox"]:checked');
10709 }
10710
10711 // del-all-btn-wrp
10712 $("#bmi_restore_tbody").on("change", 'input[type="checkbox"]', function (e) {
10713 let $selected = getAllSelectedBackups().length;
10714 if ($selected > 0) {
10715 $(".del-all-btn-wrp").show(300);
10716 } else $(".del-all-btn-wrp").hide(300);
10717
10718 if (
10719 $selected == $("#bmi_restore_tbody").find('input[type="checkbox"]').length
10720 ) {
10721 $("#backups-select-all").prop("checked", true);
10722 } else {
10723 $("#backups-select-all").prop("checked", false);
10724 }
10725 });
10726
10727 $("#fix-uname-issues").on("click", function (e) {
10728 e.preventDefault();
10729 $.bmi
10730 .ajax("fix_uname_issues", {})
10731 .then(function (res) {
10732 $.bmi.alert("success", $("#bmi-default-success").text(), 3000);
10733 })
10734 .catch(function (error) {
10735 $.bmi.alert("error", $("#bmi-default-fail").text(), 3000);
10736 });
10737 });
10738
10739 $("#revert-uname-issues").on("click", function (e) {
10740 e.preventDefault();
10741 $.bmi
10742 .ajax("revert_uname_issues", {})
10743 .then(function (res) {
10744 $.bmi.alert("success", $("#bmi-default-success").text(), 3000);
10745 })
10746 .catch(function (error) {
10747 $.bmi.alert("error", $("#bmi-default-fail").text(), 3000);
10748 });
10749 });
10750
10751 $("#backups-select-all").on("change", function () {
10752 if (this.checked === true) {
10753 $("#bmi_restore_tbody")
10754 .find('input[type="checkbox"]')
10755 .prop("checked", true);
10756 } else {
10757 $("#bmi_restore_tbody")
10758 .find('input[type="checkbox"]')
10759 .prop("checked", false);
10760 }
10761
10762 let $selected = getAllSelectedBackups().length;
10763 if ($selected > 0) {
10764 $(".del-all-btn-wrp").show(300);
10765 } else $(".del-all-btn-wrp").hide(300);
10766 });
10767
10768 $(".lrn-mr-btn, .closer-learn-more").on("click", function () {
10769 if ($(".learn_more_about_cron").hasClass("open")) {
10770 $(".learn_more_about_cron").removeClass("open");
10771 $(".learn_more_about_cron").hide(300);
10772 $(".lrn-mr-btn").show();
10773 $(".lrn-mr-btn").css({
10774 opacity: 0,
10775 });
10776 $(".lrn-mr-btn").animate(
10777 {
10778 opacity: 1,
10779 },
10780 300,
10781 );
10782 } else {
10783 $(".learn_more_about_cron").addClass("open");
10784 $(".learn_more_about_cron").show(300);
10785 $(".lrn-mr-btn").css({
10786 opacity: 1,
10787 });
10788 $(".lrn-mr-btn").animate(
10789 {
10790 opacity: 0,
10791 },
10792 300,
10793 function () {
10794 $(".lrn-mr-btn").hide();
10795 },
10796 );
10797 }
10798 });
10799
10800 $(".bmi-error-toggle").on("click", function () {
10801 let parent = $(this).closest(".error-noticer");
10802 let parentId = parent.attr("id");
10803 let errorBody = $("#" + parentId + " .error-body");
10804 if ($(errorBody).hasClass("open")) {
10805 $(errorBody).hide(300);
10806 $(errorBody).removeClass("open");
10807 $("#" + parentId + " .bmi-error-toggle").text($("#" + parentId + " .bmi-error-toggle").data("expand"));
10808 } else {
10809 $(errorBody).show(300);
10810 $(errorBody).addClass("open");
10811 $("#" + parentId + " .bmi-error-toggle").text($("#" + parentId + " .bmi-error-toggle").data("collapse"));
10812 }
10813 });
10814
10815 function runTimerST() {
10816 let time = parseInt($("#server-time-auto").attr("data-time")) * 1000;
10817 let date = new Date(time);
10818 $("#server-time-auto").text(date.toUTCString());
10819 setInterval(function () {
10820 time += 1000;
10821 date = new Date(time);
10822 $("#server-time-auto").text(date.toUTCString());
10823 }, 1000);
10824
10825 if ($("#ex_b_fs").is(":checked")) $("#bmi__collon").show();
10826 else $("#bmi__collon").hide();
10827 }
10828
10829 $("#bmi_send_test_mail").on("click", function (e) {
10830 e.preventDefault();
10831 $.bmi
10832 .ajax("send-test-mail", {})
10833 .then(function (res) {
10834 $.bmi.alert("success", $("#bmi-email-success").text(), 3000);
10835 })
10836 .catch(function (error) {
10837 $.bmi.alert("error", $("#bmi-email-fail").text(), 3000);
10838 });
10839 });
10840
10841 $(".bmi-error-dismiss").on("click", function () {
10842 let parent = $(this).closest(".error-noticer");
10843 let parentId = parent.attr("id");
10844 $("#" + parentId).hide(300);
10845 setTimeout(function () {
10846 $("#" + parentId).remove();
10847 }, 330);
10848
10849 $.bmi
10850 .ajax("dismiss-error-notice", {
10851 option_id: parentId,
10852 })
10853 .then(function (res) {})
10854 .catch(function (error) {});
10855 });
10856
10857 // delete multiple backups
10858 $(".deleteAllSelected").on("click", function () {
10859 let anyInCloud = false;
10860 let $selected = getAllSelectedBackups(),
10861 names = [];
10862 let notOnLocal = [];
10863 latest_delete = {};
10864
10865
10866 for (let i = 0; i < $selected.length; ++i) {
10867 let tr = $selected[i].closest("tr");
10868 let notOnLocalTr = $(tr).data("is-local") == "no" ? true : false;
10869 let name = tr.querySelector(".br_name").innerText;
10870 let isCloud = Array.from(tr.querySelectorAll('[class*="strg-"]')).filter(el => !el.classList.contains('strg-local')).some(el => el.classList.contains('img-green'));
10871 if (isCloud) anyInCloud = true;
10872 names.push(name);
10873
10874 if (notOnLocalTr) {
10875 notOnLocal.push(name);
10876 }
10877
10878 latest_delete[name] = {
10879 hash: tr.getAttribute("md5"),
10880 isCloud: isCloud
10881 };
10882
10883 }
10884
10885 $("#delete-confirm-modal").find(".text1").hide();
10886 $("#delete-confirm-modal").find(".text4").hide();
10887 $("#delete-confirm-modal").find(".text3").hide();
10888 $("#delete-confirm-modal").find(".text2").show();
10889
10890 let count = names.length;
10891
10892 if (count <= 0) return;
10893 else {
10894 $(".backup-multiple-del-count").text(count);
10895
10896 if (count > 1) {
10897 $(".del-only-one").hide();
10898 $(".del-more-than-one").show();
10899 } else {
10900 $(".del-more-than-one").hide();
10901 $(".del-only-one").show();
10902 }
10903 }
10904
10905 if (anyInCloud) $(".bmi-cloud-removal").show();
10906 else $(".bmi-cloud-removal").hide();
10907 $("#remove-cloud-backup-as-well")[0].checked = false;
10908
10909 if (anyInCloud && notOnLocal.length === $selected.length) {
10910 $("#remove-cloud-backup-as-well")[0].checked = true;
10911 $(".bmi-cloud-removal").hide();
10912 $("#delete-confirm-modal").find(".text1").hide();
10913 $("#delete-confirm-modal").find(".text2").hide();
10914 $("#delete-confirm-modal").find(".text4").hide();
10915 $("#delete-confirm-modal").find(".text3").show();
10916 }
10917
10918 $.bmi.modal("delete-confirm-modal").open();
10919 });
10920
10921 $("#load-more-backups").on("click", function (e) {
10922 e.preventDefault();
10923 $.bmi.showMoreBackups();
10924 });
10925
10926 function toggleFormatTip(e) {
10927 e.preventDefault();
10928 $("#format-tip-wrp")[0].style.minWidth = "calc(100% - 120px)";
10929
10930 if ($("#format-tip-wrp")[0].style.display === "none") {
10931 $("#format-tip-wrp").show(300);
10932 } else {
10933 $("#format-tip-wrp").hide(300);
10934 }
10935 }
10936
10937 $("#show-format-tip").on("click", toggleFormatTip);
10938 $("#hide-format-tip").on("click", toggleFormatTip);
10939
10940 $(".bmi-review-btn").on("click", function (e) {
10941 e.preventDefault();
10942
10943 var url = $(this).attr("href");
10944
10945 $.bmi.ajax("clicked-on-plugin-review", {}).then(function (res) {
10946 $(".bmi-ask-for-review").hide(300);
10947 window.open(url, "_blank");
10948 });
10949 });
10950
10951 $(".go-to-marbs").on("click", function (e) {
10952 e.preventDefault();
10953 document.getElementById("marbs").click();
10954 $.bmi.modal().closeAll();
10955 });
10956
10957 $(".go-to-stgng").on("click", function (e) {
10958 e.preventDefault();
10959 document.getElementById("stgng").click();
10960 $.bmi.modal().closeAll();
10961 });
10962
10963 $(".site-reloader").on("click", function () {
10964 element_enable_crons = [
10965 "choose-auto-backup-interval",
10966 "weekly-auto-backup-switch",
10967 ];
10968 let element_id = $(this).attr("id");
10969 let crons_enabled = element_enable_crons.includes(element_id)
10970 ? "&crons=true"
10971 : null;
10972 let url = autoLog.u;
10973 if (url.slice(-url.length) !== "/") url = url + "/";
10974
10975 let url_final =
10976 url +
10977 "?backup-migration=AFTER_RESTORE&bmi-id=" +
10978 autoLog.l +
10979 "&progress-id=4u70L051n&t=" +
10980 crons_enabled +
10981 +new Date() +
10982 "&sk=" +
10983 $("#BMI_SECRET_KEY").text().trim();
10984 window.location = url_final;
10985 });
10986
10987 $(".get-file-database-sizes").on("click", function (e) {
10988 e.preventDefault();
10989
10990 let resources = [
10991 "plugins",
10992 "uploads",
10993 "themes",
10994 "contents_others",
10995 "wordpress",
10996 "database",
10997 ];
10998 resetSpinners(resources, true);
10999
11000 saveBtnEventHandler(false, "save-file-config");
11001 });
11002
11003 $("#bmi-pro-storage-gdrive-toggle").on("change", function (e) {
11004 if (!$("#bmi-pro-storage-gdrive-toggle").is(":checked")) {
11005 $("td.br_stroage.center").addClass("bmi-gdrive-disabled");
11006 } else {
11007 $("td.br_stroage.center").removeClass("bmi-gdrive-disabled");
11008 }
11009 });
11010
11011 $(".open-logs-modal-url").on("click", function () {
11012 let parentId = $(this).closest(".bmi-modal").attr("id");
11013 $.bmi.modal("logs-modal").setParent(parentId);
11014 $.bmi.modal(parentId).close();
11015 $.bmi.modal("freeze-loading-modal").open();
11016 setupLogsModal();
11017 });
11018
11019 $(".skip-share-logs-after-restore, .shared-log-after-restore").on("click", function () {
11020 $.bmi.modal("supportive-restore-success-modal").close();
11021 $.bmi.modal("supportive-restore-success-cont-modal").open();
11022 });
11023
11024
11025 $(".try-in-different-way").on("click", tryInDifferentWay);
11026
11027 function refreshBBStorageInfo(silent=false) {
11028 $('.refresh-img').addClass("loading");
11029 $.bmi.ajax('bb-storage-info').then(function (res) {
11030
11031 if (res.status == 'success') {
11032 $(".bb-storage-amount").text(res.data.storage_info.total_space_humanized);
11033 $(".bb-storage-used").text(res.data.storage_info.used_space_humanized);
11034 $(".bb-storage-used-percent").text("("+res.data.storage_info.used_space_percent+"%)");
11035 } else {
11036 if (!silent)
11037 $.bmi.alert('error', res.message);
11038
11039 }
11040
11041 $('.refresh-img').removeClass("loading");
11042
11043 }).catch(function (error) {
11044
11045 $('.refresh-img').removeClass("loading");
11046 if (!silent)
11047 $.bmi.alert('error', 'There was an error contacting the plugin backend.');
11048
11049 });
11050 }
11051
11052 refreshBBStorageInfo(true);
11053
11054 $(".refresh-bb-storage").on("click", function(e){
11055 e.preventDefault();
11056 refreshBBStorageInfo();
11057 });
11058
11059 $("#bb-upload-fail-dismiss").on("click", function(e){
11060 e.preventDefault();
11061 $.bmi
11062 .ajax("dismiss-error-notice", {
11063 option_id: "backupbliss-dismiss-upload-issue",
11064 })
11065 .then(function (res) {})
11066 .catch(function (error) {});
11067
11068 $("#bb-warning-notice").hide(300);
11069 setTimeout(function () {
11070 $("#bb-warning-notice").remove();
11071 }, 330);
11072 });
11073
11074 $(".bb-disconnect").on("click", function (e) {
11075 e.preventDefault();
11076 $.bmi.modal("bb-disconnect-modal").open();
11077 });
11078
11079 $(".bb-disconnect-cancel").on("click", function (e) {
11080 e.preventDefault();
11081 $.bmi.modal("bb-disconnect-modal").close();
11082 });
11083
11084 $(".bb-disconnect-btn").on("click", function(e){
11085 e.preventDefault();
11086 $.bmi.modal("bb-disconnect-modal").close();
11087 $.bmi.modal("freeze-loading-modal").open();
11088 $.bmi.ajax('bb-disconnect').then(function (res) {
11089
11090 if (res.status == 'success') {
11091 $(".how-it-works").show();
11092 $(".youre-connected").hide();
11093 } else {
11094
11095 $.bmi.alert('error', res.message);
11096
11097 }
11098
11099 $.bmi.modal('freeze-loading-modal').close();
11100
11101 }).catch(function (error) {
11102
11103 $.bmi.modal('freeze-loading-modal').close();
11104 $.bmi.alert('error', 'There was an error contacting the plugin backend.');
11105
11106 });
11107 });
11108
11109 $(".bb-connect").on("click", function(e){
11110 e.preventDefault();
11111 $.bmi.modal("freeze-loading-modal").open();
11112 $.bmi.ajax('bb-connect', {
11113 api_key: $(".api-key-input").val()
11114 }).then(function (res) {
11115
11116 if (res.status == 'success') {
11117 refreshBBStorageInfo();
11118 $(".how-it-works").hide();
11119 $(".youre-connected").show();
11120 $.bmi.reloadBackups();
11121 } else {
11122
11123 $.bmi.alert('error', res.message);
11124
11125 }
11126
11127 $.bmi.modal('freeze-loading-modal').close();
11128
11129 }).catch(function (error) {
11130
11131 $.bmi.modal('freeze-loading-modal').close();
11132 $.bmi.alert('error', 'There was an error contacting the plugin backend.');
11133
11134 });
11135 });
11136
11137 $('#dropbox-connect-btn').on('click', function (e) {
11138
11139 e.preventDefault();
11140 $.bmi.modal('freeze-loading-modal').open();
11141 $.bmi.ajax('get-dropbox-token').then(function (res) {
11142
11143 $.bmi.modal('freeze-loading-modal').close();
11144 if (typeof res.token != 'undefined') {
11145
11146 let url = 'https://authentication.backupbliss.com/v1/dropbox/connect';
11147 url += '?token=' + encodeURIComponent(res.token);
11148 url += '&redirect=' + encodeURIComponent(window.location.origin + window.location.pathname);
11149
11150 window.location.href = url;
11151
11152 } else {
11153
11154 $.bmi.alert('error', 'We could not generate individual token at this moment, please refresh page and try again [#Dropbox-02].');
11155
11156 }
11157
11158 }).catch(function (error) {
11159
11160 $.bmi.modal('freeze-loading-modal').close();
11161 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#Dropbox-01].');
11162
11163 });
11164
11165 });
11166
11167 $('#dropbox-disconnect-btn').on('click', function (e) {
11168
11169 e.preventDefault();
11170 $.bmi.modal('freeze-loading-modal').open();
11171 $.bmi.ajax('disconnect-dropbox').then(function (res) {
11172
11173 $.bmi.modal('freeze-loading-modal').close();
11174 if (typeof res.status != 'undefined' && res.status == 'success') {
11175
11176 window.location.reload();
11177
11178 } else {
11179
11180 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
11181
11182 }
11183
11184 }).catch(function (error) {
11185
11186 $.bmi.modal('freeze-loading-modal').close();
11187 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11188
11189 });
11190
11191 });
11192
11193 function verifyDropboxConnection(silent = false) {
11194
11195 $.bmi.ajax('verify-dropbox-connection', { uri: window.location.host }).then(function (res) {
11196
11197 $('#dropbox-unauthenticated-box').show();
11198 $('#dropbox-authenticated-box').hide();
11199
11200 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
11201
11202 if (res.result == 'connected') {
11203
11204 $('#dropbox-unauthenticated-box').hide();
11205 $('#dropbox-authenticated-box').show();
11206
11207 }
11208
11209 } else if (typeof res.status != 'undefined' && res.status == 'error') {
11210
11211 if (!silent) {
11212 $.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].');
11213 }
11214
11215 } else {
11216 if (!silent) {
11217 $.bmi.alert('error', 'We could not verify Dropbox connection, please refresh page and try again [#DB-05].');
11218 }
11219
11220 }
11221
11222 }).catch(function (error) {
11223 if (!silent) {
11224 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#DB-08].');
11225 }
11226
11227 });
11228 }
11229
11230 $('#bmi-pro-storage-dropbox-toggle').on('change', function (e) {
11231
11232 e.preventDefault();
11233
11234 $('#dropbox-authenticated-box').hide();
11235 $('#dropbox-unauthenticated-box').show();
11236
11237 if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === true) {
11238 verifyDropboxConnection();
11239 }
11240
11241 });
11242
11243
11244 $('#bmip-dropbox-issues-dismiss').on("click", function () {
11245 $('#dropbox-issues').hide(300);
11246 setTimeout(function () {
11247 $('#dropbox-issues').remove();
11248 }, 330);
11249 $.bmi.ajax('dismiss-dropbox-notice', {}).then(function (res) { }).catch(function (error) { });
11250 });
11251
11252 $('#gdrive-connect-btn').on('click', function (e) {
11253
11254 e.preventDefault();
11255 $.bmi.modal('freeze-loading-modal').open();
11256 let backupDirectoryPath = $('#bmip-googledrive-path').val();
11257 $.bmi.ajax('get-gdrive-token',
11258 { backupDirectoryPath: backupDirectoryPath }
11259 ).then(function (res) {
11260
11261 $.bmi.modal('freeze-loading-modal').close();
11262 if (typeof res.token != 'undefined') {
11263
11264 let url = 'https://authentication.backupbliss.com/v1/gdrive/connect';
11265 url += '?token=' + encodeURIComponent(res.token);
11266 url += '&redirect=' + encodeURIComponent(window.location.origin + window.location.pathname);
11267
11268 window.location.href = url;
11269
11270 } else {
11271 if (typeof res.status != 'undefined' && res.status === 'msg') {
11272 $.bmi.alert('warning', res.why);
11273 } else {
11274 $.bmi.alert('error', 'We could not generate individual token at this moment, please refresh page and try again [#GD-02].');
11275 }
11276
11277 }
11278
11279 }).catch(function (error) {
11280
11281 $.bmi.modal('freeze-loading-modal').close();
11282 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-01].');
11283
11284 });
11285
11286 });
11287
11288 $('#gdrive-disconnect-btn').on('click', function (e) {
11289
11290 e.preventDefault();
11291 $.bmi.modal('freeze-loading-modal').open();
11292 $.bmi.ajax('disconnect-gdrive').then(function (res) {
11293
11294 $.bmi.modal('freeze-loading-modal').close();
11295 if (typeof res.status != 'undefined' && res.status == 'success') {
11296
11297 window.location.reload();
11298
11299 } else {
11300
11301 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again [#GD-03].');
11302
11303 }
11304
11305 }).catch(function (error) {
11306
11307 $.bmi.modal('freeze-loading-modal').close();
11308 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-04].');
11309
11310 });
11311
11312 });
11313
11314 function verifyGDriveConnection(silent = false) {
11315
11316 $.bmi.ajax('verify-gdrive-connection', { uri: window.location.host }).then(function (res) {
11317
11318 $('#gdrive-unauthenticated-box').show();
11319 $('#gdrive-authenticated-box').hide();
11320
11321 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
11322
11323 if (res.result == 'connected') {
11324
11325 $('#gdrive-unauthenticated-box').hide();
11326 $('#gdrive-authenticated-box').show();
11327
11328 }
11329
11330 } else if (typeof res.status != 'undefined' && res.status == 'error') {
11331
11332 if (!silent) {
11333 $.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].');
11334 }
11335
11336 } else {
11337 if (!silent) {
11338 $.bmi.alert('error', 'We could not verify Google Drive connection, please refresh page and try again [#GD-05].');
11339 }
11340
11341 }
11342
11343 }).catch(function (error) {
11344 if (!silent) {
11345 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-08].');
11346 }
11347
11348 });
11349
11350 }
11351
11352 $('#bmi-pro-storage-gdrive-toggle').on('change', function (e) {
11353
11354 e.preventDefault();
11355
11356 $('#gdrive-authenticated-box').hide();
11357 $('#gdrive-unauthenticated-box').show();
11358
11359 if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === true) {
11360 verifyGDriveConnection();
11361 }
11362
11363 });
11364
11365
11366 $('#ftp-connect-btn').on('click', function (e) {
11367 e.preventDefault();
11368 $.bmi.modal('freeze-loading-modal').open();
11369 let data = {};
11370 data['bmip-ftp-host'] = $('#bmip-ftp-host-ip').val();
11371 data['bmip-ftp-backup-dir'] = $('#bmip-ftp-backup-dir').val();
11372 data['bmip-ftp-host-port'] = $('#bmip-ftp-host-port').val();
11373 data['bmip-ftp-username'] = $('#bmip-ftp-user-name').val();
11374 data['bmip-ftp-password'] = $('#bmip-ftp-password').val();
11375
11376 $.bmi.ajax('get-ftp-config', data).then(function (res) {
11377
11378 $.bmi.modal('freeze-loading-modal').close();
11379
11380 if (typeof res.status != 'undefined' && res.status === 'success') {
11381 if (e) $.bmi.alert('success', $('#bmi-save-connect-ftp').text(), 3000);
11382
11383 setTimeout(function () {
11384 window.location.reload();
11385 }, 300);
11386
11387 } else {
11388 $.bmi.alert('error', res.msg);
11389 }
11390 }).catch(function (error) {
11391 $.bmi.modal('freeze-loading-modal').close();
11392 $.bmi.alert('error', '');
11393 });
11394 });
11395
11396 $('#ftp-disconnect-btn').on('click', function (e) {
11397
11398 e.preventDefault();
11399 $.bmi.modal('freeze-loading-modal').open();
11400 $.bmi.ajax('disconnect-ftp').then(function (res) {
11401
11402 $.bmi.modal('freeze-loading-modal').close();
11403 if (typeof res.status != 'undefined' && res.status == 'success') {
11404
11405 window.location.reload();
11406
11407 } else {
11408
11409 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again [#GD-03].');
11410
11411 }
11412
11413 }).catch(function (error) {
11414
11415 $.bmi.modal('freeze-loading-modal').close();
11416 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-04].');
11417
11418 });
11419
11420 });
11421
11422
11423 $('#aws-connect-btn').on('click', function (e) {
11424 e.preventDefault();
11425 $.bmi.modal('freeze-loading-modal').open();
11426 let data = {};
11427 data['access-key'] = $('#bmip-aws-access-key').val();
11428 data['secret-key'] = $('#bmip-aws-secret-key').val();
11429 data['bucket'] = $('#bmip-aws-bucket').val();
11430 data['sse'] = $('#bmip-aws-sse').is(':checked') === true ? $('#bmip-aws-sse').val().trim() : '';
11431 data['path'] = $('#bmip-aws-path').val();
11432 data['storage-class'] = $('[data-id="bmip-aws-storage-class"]').attr('data-selected');
11433 data['region'] = $('[data-id="bmip-aws-region"]').attr('data-selected');
11434
11435 $.bmi.ajax('save-aws-config', data).then(function (res) {
11436
11437 $.bmi.modal('freeze-loading-modal').close();
11438
11439 if (typeof res.status != 'undefined' && res.status === 'success') {
11440 if (e) $.bmi.alert('success', $('#bmi-save-connect-s3-success').text(), 3000);
11441 window.location.reload();
11442 } else {
11443 $.bmi.alert('error', res.msg);
11444 }
11445 }).catch(function (error) {
11446 $.bmi.modal('freeze-loading-modal').close();
11447 $.bmi.alert('error', 'There was an error while trying to connect to Amazon S3, please try again.');
11448 });
11449 });
11450
11451
11452 $('#aws-disconnect-btn').on('click', function (e) {
11453
11454 e.preventDefault();
11455 $.bmi.modal('freeze-loading-modal').open();
11456 $.bmi.ajax('disconnect-aws').then(function (res) {
11457
11458 $.bmi.modal('freeze-loading-modal').close();
11459 if (typeof res.status != 'undefined' && res.status == 'success') {
11460 window.location.reload();
11461 } else {
11462 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
11463 }
11464
11465 }).catch(function (error) {
11466
11467 $.bmi.modal('freeze-loading-modal').close();
11468 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11469
11470 });
11471
11472 });
11473
11474 function verifyAWSConnection(silent = false) {
11475 $.bmi.ajax('verify-aws-connection', { uri: window.location.host}).then(function (res) {
11476 $('#aws-unauthenticated-box').show();
11477 $('#aws-authenticated-box').hide();
11478
11479
11480 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
11481 if (res.result == 'connected') {
11482 $('#aws-unauthenticated-box').hide();
11483 $('#aws-authenticated-box').show();
11484 $('#bmip-aws-path').val(res.configs['path']).attr('readonly', true);
11485 $('#bmip-aws-bucket').val(res.configs['bucket']).attr('readonly', true);
11486 $('.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');
11487 $('#storage-s3-row .region-container').hide();
11488 $('#storage-s3-row .aws-access-key-container').hide();
11489 $('#storage-s3-row .aws-secret-key-container').hide();
11490 $('#bmip-aws-sse').prop('checked', res.configs['sse'] === 'AES256').attr('disabled', true).css('cursor', 'not-allowed');
11491 $('#bmip-aws-sse').closest('.checkbox-container').css('cursor', 'not-allowed');
11492 } else {
11493 $('#aws-unauthenticated-box').show();
11494 $('#aws-authenticated-box').hide();
11495 }
11496 } else if (typeof res.status != 'undefined' && res.status == 'error') {
11497 if (!silent) {
11498 $.bmi.alert('error', 'We could not verify AWS S3 connection, some error happened during communication with API.');
11499 }
11500 } else {
11501 if (!silent) {
11502 $.bmi.alert('error', 'We could not verify AWS S3 connection, please refresh page and try again.');
11503 }
11504 }
11505 }).catch(function (error) {
11506 if (!silent) {
11507 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11508 }
11509 });
11510 }
11511
11512 $('#bmi-pro-storage-aws-toggle').on('change', function (e) {
11513 e.preventDefault();
11514 $('#aws-authenticated-box').hide();
11515 $('#aws-unauthenticated-box').show();
11516 if ($('#bmi-pro-storage-aws-toggle').is(':checked') === true) {
11517 verifyAWSConnection();
11518 }
11519 });
11520
11521 $('#wasabi-connect-btn').on('click', function (e) {
11522 e.preventDefault();
11523 $.bmi.modal('freeze-loading-modal').open();
11524 let data = {};
11525 data['access-key'] = $('#bmip-wasabi-access-key').val();
11526 data['secret-key'] = $('#bmip-wasabi-secret-key').val();
11527 data['bucket'] = $('#bmip-wasabi-bucket').val();
11528 data['path'] = $('#bmip-wasabi-path').val();
11529 data['region'] = $('[data-id="bmip-wasabi-region"]').attr('data-selected');
11530
11531 $.bmi.ajax('save-wasabi-config', data).then(function (res) {
11532
11533 $.bmi.modal('freeze-loading-modal').close();
11534
11535 if (typeof res.status != 'undefined' && res.status === 'success') {
11536 if (e) $.bmi.alert('success', $('#bmi-save-connect-s3-success').text(), 3000);
11537 window.location.reload();
11538 } else {
11539 $.bmi.alert('error', res.msg);
11540 }
11541 }).catch(function (error) {
11542 $.bmi.modal('freeze-loading-modal').close();
11543 $.bmi.alert('error', 'There was an error while trying to connect to Wasabi, please try again.');
11544 });
11545 });
11546
11547 $('#wasabi-disconnect-btn').on('click', function (e) {
11548
11549 e.preventDefault();
11550 $.bmi.modal('freeze-loading-modal').open();
11551 $.bmi.ajax('disconnect-wasabi').then(function (res) {
11552
11553 $.bmi.modal('freeze-loading-modal').close();
11554 if (typeof res.status != 'undefined' && res.status == 'success') {
11555 window.location.reload();
11556 } else {
11557 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
11558 }
11559
11560 }).catch(function (error) {
11561
11562 $.bmi.modal('freeze-loading-modal').close();
11563 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11564
11565 });
11566
11567 });
11568
11569 function verifyWasabiConnection(silent = false) {
11570 $.bmi.ajax('verify-wasabi-connection', { uri: window.location.host}).then(function (res) {
11571 $('#wasabi-unauthenticated-box').show();
11572 $('#wasabi-authenticated-box').hide();
11573
11574
11575 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
11576 if (res.result == 'connected') {
11577 $('#wasabi-unauthenticated-box').hide();
11578 $('#wasabi-authenticated-box').show();
11579 $('#bmip-wasabi-path').val(res.configs['path']).attr('readonly', true);
11580 $('#bmip-wasabi-bucket').val(res.configs['bucket']).attr('readonly', true);
11581 $('#storage-wasabi-row .region-container').hide();
11582 $('#storage-wasabi-row .wasabi-access-key-container').hide();
11583 $('#storage-wasabi-row .wasabi-secret-key-container').hide();
11584 } else {
11585 $('#wasabi-unauthenticated-box').show();
11586 $('#wasabi-authenticated-box').hide();
11587 }
11588 } else if (typeof res.status != 'undefined' && res.status == 'error') {
11589 if (!silent) {
11590 $.bmi.alert('error', 'We could not verify Wasabi connection, some error happened during communication with API.');
11591 }
11592 } else {
11593 if (!silent) {
11594 $.bmi.alert('error', 'We could not verify Wasabi connection, please refresh page and try again.');
11595 }
11596 }
11597 }).catch(function (error) {
11598 if (!silent) {
11599 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11600 }
11601 });
11602 }
11603
11604 $('#bmi-pro-storage-wasabi-toggle').on('change', function (e) {
11605 e.preventDefault();
11606 $('#wasabi-authenticated-box').hide();
11607 $('#wasabi-unauthenticated-box').show();
11608 if ($('#bmi-pro-storage-wasabi-toggle').is(':checked') === true) {
11609 verifyWasabiConnection();
11610 }
11611 });
11612
11613 $('#bmi_restore_tbody').on('click', '.can-be-manually-uploaded', function (e) {
11614
11615 e.preventDefault();
11616 const classList = $(this).attr('class').split(/\s+/);
11617 let type = false;
11618 let md5 = $(this).closest('tr').attr('md5');
11619
11620 for (let cls of classList) {
11621 if (cls.startsWith('strg-')) {
11622 type = cls.replace('strg-', ''); // get the part after 'strg-'
11623 break;
11624 }
11625 }
11626
11627 $.bmi.ajax('manually-enqueue-upload', {
11628 type: type,
11629 md5: md5
11630 }).then(function (res) {
11631 if (res.status == 'success') {
11632 $(this).removeClass('can-be-manually-uploaded');
11633 $.bmi.alert('success', 'Backup will be added to the upload queue shortly.');
11634 } else {
11635 $.bmi.alert('error', res.msg || 'There was an error while trying to enqueue the backup for upload.');
11636 }
11637 });
11638
11639 });
11640
11641 $(window).on("bmi-preload-collapsed", function (e) {
11642
11643 const urlSearchParams = new URLSearchParams(window.location.search);
11644 const params = Object.fromEntries(urlSearchParams.entries());
11645
11646 if (typeof params.bmi_drive != 'undefined') {
11647 window.history.pushState('', window.title, window.location.pathname + '?page=backup-migration');
11648
11649
11650 if (typeof params.bmi_auth_status != 'undefined' && params.bmi_auth_status == 'error') {
11651 if (typeof params.bmi_error_reason != 'undefined') {
11652 if (params.bmi_drive == 'gdrive') {
11653 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].');
11654 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].');
11655 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].');
11656 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].');
11657 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].');
11658 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].');
11659 } else if (params.bmi_drive == 'dropbox') {
11660 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].');
11661 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].');
11662 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].');
11663 if (params.bmi_error_reason == '4') return $.bmi.alert('error', 'Error during Dropbox connection: Access has been denied by you during authorization. [#DB-14].');
11664 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].');
11665 if (params.bmi_error_reason == '6') return $.bmi.alert('error', 'Error during Dropbox connection: Access token and refresh token has been revoked. [#DB-16].');
11666 }
11667 }
11668 }
11669
11670 if (typeof params.bmi_token == 'undefined' || typeof params.bmi_secret == 'undefined' || typeof params.bmi_auth_status == 'undefined') {
11671 if (params.bmi_drive == 'gdrive') {
11672 return $.bmi.alert('error', 'Cannot save connection with Google Drive, due to insufficient details [#GD-09].');
11673 } else if (params.bmi_drive == 'dropbox') {
11674 return $.bmi.alert('error', 'Cannot save connection with Dropbox, due to insufficient details [#DB-09].');
11675 }
11676 }
11677
11678 if (params.bmi_auth_status != 'success') {
11679 if (params.bmi_drive == 'gdrive') {
11680 return $.bmi.alert('error', 'Cannot save connection with Google Drive, due to unknown response from Google API [#GD-10].');
11681 } else if (params.bmi_drive == 'dropbox') {
11682 return $.bmi.alert('error', 'Cannot save connection with Dropbox, due to unknown response from Dropbox API [#DB-10].');
11683 }
11684 }
11685
11686 $.bmi.modal('freeze-loading-modal').open();
11687 $('#bmi-external-storage-options').click();
11688
11689 if (params.bmi_drive == 'gdrive') {
11690 if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === false) {
11691 $('#bmi-pro-storage-gdrive-toggle').click();
11692 }
11693 } else if (params.bmi_drive == 'dropbox') {
11694 if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === false) {
11695 $('#bmi-pro-storage-dropbox-toggle').click();
11696 }
11697 }
11698
11699 if (params.bmi_drive == 'gdrive') {
11700 $.bmi.ajax('keep-gdrive-connection', { receivedToken: params.bmi_token, receivedClientID: params.bmi_secret }).then(function (res) {
11701
11702 $.bmi.modal('freeze-loading-modal').close();
11703
11704 setTimeout(function () {
11705 $('#bmi-pro-storage-gdrive-toggle')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11706
11707 setTimeout(function () {
11708 $('#gdrive-unauthenticated-box').fadeOut();
11709 $('#gdrive-authenticated-box').fadeIn();
11710 setTimeout(function () {
11711 $('#gdrive-authed-content-box')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11712 window.saveStorageWithoutClose = true;
11713 $('#storage-options').find('.save-btn').click();
11714 }, 300);
11715 }, 300);
11716 }, 300);
11717
11718 }).catch(function (error) {
11719
11720 $.bmi.modal('freeze-loading-modal').close();
11721 $.bmi.alert('error', 'Cannot save connection with Google Drive, please try again [#GD-06].');
11722
11723 });
11724 } else if (params.bmi_drive == 'dropbox') {
11725 $.bmi.ajax('keep-dropbox-connection', { receivedToken: params.bmi_token, receivedClientID: params.bmi_secret }).then(function (res) {
11726
11727 $.bmi.modal('freeze-loading-modal').close();
11728
11729 setTimeout(function () {
11730 $('#bmi-pro-storage-dropbox-toggle')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11731
11732 setTimeout(function () {
11733 $('#dropbox-unauthenticated-box').fadeOut();
11734 $('#dropbox-authenticated-box').fadeIn();
11735 setTimeout(function () {
11736 $('#dropbox-authed-content-box')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11737 window.saveStorageWithoutClose = true;
11738 $('#storage-options').find('.save-btn').click();
11739 }, 300);
11740 }, 300);
11741 }, 300);
11742
11743 }).catch(function (error) {
11744
11745 $.bmi.modal('freeze-loading-modal').close();
11746 $.bmi.alert('error', 'Cannot save connection with Dropbox, please try again [#DB-06].');
11747
11748 });
11749 } else {
11750 jQuery(window).trigger("bmi-preload-collapsed-pro", [params]);
11751 }
11752 }
11753
11754
11755 if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === true) {
11756 verifyDropboxConnection(true);
11757 }
11758
11759 if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === true) {
11760 verifyGDriveConnection(true);
11761 }
11762
11763 if ($('#bmi-pro-storage-aws-toggle').is(':checked') === true) {
11764 verifyAWSConnection(true);
11765 }
11766
11767 if ($('#bmi-pro-storage-wasabi-toggle').is(':checked') === true) {
11768 verifyWasabiConnection(true);
11769 }
11770
11771 });
11772
11773 // Currently not used
11774 $('.bmi-check-disk-space').on('click', function (e) {
11775 e.preventDefault();
11776
11777 // Prevent multiple clicks
11778 if (!$('.space-checking .loading').hasClass('hide_verbose')) return;
11779
11780 $('.space-checking .loading').removeClass('hide_verbose');
11781 // Hide all verbose messages if any shown
11782 if (!$('.checking-result .not-enough-space').hasClass('hide_verbose')) $('.checking-result .not-enough-space').addClass('hide_verbose');
11783 if (!$('.checking-result .enough-space').hasClass('hide_verbose')) $('.checking-result .enough-space').addClass('hide_verbose');
11784 if (!$('.checking-result .failed').hasClass('hide_verbose')) $('.checking-result .failed').addClass('hide_verbose');
11785
11786
11787 $.bmi.ajax('check-disk-space').then(function (res) {
11788 if (res.status == 'enough-space') {
11789 $('.space-checking .loading').addClass('hide_verbose');
11790 $('.checking-result .enough-space').removeClass('hide_verbose');
11791 } else if (res.status == 'not-enough-space') {
11792 $('.space-checking .loading').addClass('hide_verbose');
11793 $('.checking-result .not-enough-space').removeClass('hide_verbose');
11794 $('.checking-result .not-enough-space').find('.required-space').text(res.data['required']);
11795 $('.checking-result .not-enough-space').find('.available-space').text(res.data['available']);
11796 }
11797 }).catch(function (error) {
11798 console.error(error);
11799 $('.space-checking .loading').addClass('hide_verbose');
11800 $('.checking-result .failed').removeClass('hide_verbose');
11801 });
11802 });
11803
11804 async function tryInDifferentWay() {
11805 let errorParent = $.bmi.modal("error-modal").getParent();
11806 if (errorParent == "backup-progress-modal") {
11807 if (isNaN(getSelectedSize())) {
11808 setTimeout(tryInDifferentWay, 1000);
11809 }
11810 $("#cli-disable-others").prop("checked", true);
11811 $("#experimental-hard-timeout").prop("checked", true);
11812 $("#download-technique").prop("checked", true);
11813 $("#bmi-db-batching-backup").prop("checked", true);
11814 $("#bmi-use-new-database-export-engine").prop("checked", true);
11815 await saveOtherOptionsPromise();
11816
11817 $.bmi.modal($(this).closest(".bmi-modal").attr("id")).close();
11818 $("#start-entire-backup").click();
11819 } else if (errorParent == "restore-progress-modal") {
11820 $("#cli-disable-others").prop("checked", true);
11821 $("#file_limit_extraction_max").val("300");
11822 await saveOtherOptionsPromise();
11823
11824 $.bmi.modal($(this).closest(".bmi-modal").attr("id")).close();
11825 $("#restore-start-sure").click();
11826
11827 }
11828 }
11829
11830 function bmiSetFailureReasonsState(reasonCount) {
11831 const $plural = $(".there-are-reasons");
11832 const $single = $(".there-is-a-reason");
11833 const $none = $(".there-are-no-reasons");
11834 const $box = $(".debug-it-yourself");
11835 const $content = $box.find(".content");
11836
11837 // Hide all first
11838 $plural.hide();
11839 $single.hide();
11840 $none.hide();
11841
11842 if (reasonCount <= 0) {
11843 $none.show();
11844 $box.removeClass("active");
11845 $content.css("display", "none");
11846 } else if (reasonCount === 1) {
11847 $single.show();
11848 $box.addClass("active");
11849 $content.css("display", "block");
11850 } else {
11851 $plural.show();
11852 $box.addClass("active");
11853 $content.css("display", "block");
11854 }
11855 }
11856
11857 async function setupRestoreErrorOptions() {
11858 $.bmi.modal("freeze-loading-modal").open();
11859 $("#error-modal").find(".modal-title").text($("#bmi-restore-error-modal-title").text());
11860
11861
11862 const tryInDifferentWayBtn = $(".try-in-different-way");
11863 const tryInDifferentWayOption = tryInDifferentWayBtn.closest(".failure-option");
11864 const failureReasons = $(".failure-reasons");
11865
11866 if (
11867 !$("#cli-disable-others").is(":checked") ||
11868 $("#file_limit_extraction_max").val() != "300"
11869 ) {
11870 tryInDifferentWayOption.show();
11871 } else {
11872 tryInDifferentWayOption.hide();
11873 }
11874
11875 try {
11876 const res = await $.bmi.ajax("check-comptability", {
11877 for: "restore"
11878 });
11879 $.bmi.modal("freeze-loading-modal").close();
11880
11881 failureReasons.empty();
11882
11883 let reasonCount = 0;
11884 if (res.status === "success" ) {
11885 if (Array.isArray(res.data) && res.data.length > 0) {
11886 res.data.forEach(r => failureReasons.append("<li>" + r + "</li>"));
11887 reasonCount = failureReasons.find("li").length;
11888 }
11889 if (res.mainReasonFound) {
11890 tryInDifferentWayOption.hide();
11891 }
11892 }
11893 bmiSetFailureReasonsState(reasonCount);
11894 setupDownloadLogsHref("restore");
11895 } catch (error) {
11896 console.log(error);
11897 $.bmi.modal("freeze-loading-modal").close();
11898 }
11899 }
11900
11901 async function setupBackupErrorOptions() {
11902 $.bmi.modal("freeze-loading-modal").open();
11903 $("#error-modal").find(".modal-title").text($("#bmi-error-modal-title").text());
11904
11905 const tryInDifferentWayBtn = $(".try-in-different-way");
11906 const tryInDifferentWayOption = tryInDifferentWayBtn.closest(".failure-option");
11907 const failureReasons = $(".failure-reasons");
11908
11909 if (
11910 !$("#cli-disable-others").is(":checked") ||
11911 !$("#experimental-hard-timeout").is(":checked") ||
11912 !$("#download-technique").is(":checked") ||
11913 !$("#bmi-db-batching-backup").is(":checked") ||
11914 !$("#bmi-use-new-database-export-engine").is(":checked")
11915 ) {
11916 if (!tryInDifferentWayOption.hasClass("force_hide")) {
11917 tryInDifferentWayOption.show();
11918 }
11919 } else {
11920 tryInDifferentWayOption.hide();
11921 }
11922
11923 try {
11924 const res = await $.bmi.ajax("check-comptability", {
11925 for: "backup"
11926 });
11927 $.bmi.modal("freeze-loading-modal").close();
11928
11929 failureReasons.empty();
11930
11931 let reasonCount = 0;
11932 if (res.status === "success" && Array.isArray(res.data) && res.data.length > 0) {
11933 res.data.forEach(r => failureReasons.append("<li>" + r + "</li>"));
11934 reasonCount = failureReasons.find("li").length;
11935 }
11936
11937 bmiSetFailureReasonsState(reasonCount);
11938 setupDownloadLogsHref("backup");
11939 } catch (error) {
11940 console.log(error);
11941 $.bmi.modal("freeze-loading-modal").close();
11942 }
11943 }
11944
11945 async function fillLogs(
11946 type = "backup",
11947 preElement = $(".log-wrapper").find("pre")[0],
11948 logPath = "?backup-migration=PROGRESS_LOGS&progress-id=latest_full.log&uncensored=true&bmi-id=current&t=" +
11949 +new Date() +
11950 "&sk=" +
11951 $("#BMI_SECRET_KEY").text().trim(),
11952 ) {
11953 let url = $("#BMI_BLOG_URL").text().trim();
11954 if (url.slice(-url.length) !== "/") url = url + "/";
11955
11956 if (type === "backup") logPath += "&progress-id=latest_full.log";
11957 else if (type === "restore") logPath += "&progress-id=latest_migration_full.log";
11958
11959 return new Promise((resolve, reject) => {
11960 httpGet(url + logPath, function (log) {
11961 let res1 = log.split("\n").slice(0, 1)[0];
11962
11963 if (res1.trim() === "") {
11964 res1 = log.split("\n").slice(0, 2)[1];
11965 log = log.split("\n").slice(2).join("\n");
11966 } else {
11967 log = log.split("\n").slice(1).join("\n");
11968 }
11969 if (log && log != false && typeof log !== "undefined") {
11970 if (log === false) {
11971 reject("Error: Failed to retrieve backup logs");
11972 return;
11973 }
11974 let lines = log.split("\n");
11975 if (lines.length >= 1) lines = lines.slice(0, -1);
11976 preElement.innerText = "";
11977 showAllLines(preElement, lines);
11978 resolve();
11979 } else {
11980 reject("Error: Failed to retrieve backup logs");
11981 }
11982 });
11983 });
11984 }
11985
11986 function runStagingTimers() {
11987 setInterval(function () {
11988 $trs = $(".bmi-tastewp-staging-row");
11989 $trs.each(function (i) {
11990 let $tr = $($trs[i]);
11991 let expiration = $tr.attr("expiration");
11992 if (expiration && !isNaN(parseInt(expiration))) {
11993 expiration = parseInt(expiration);
11994 $tr
11995 .find(".stg-tr-expiration span")
11996 .text($.bmi.getExpirationTime(expiration));
11997 }
11998 });
11999 }, 1000);
12000 }
12001
12002 function setupLogsModal() {
12003 let errorParent = $.bmi.modal("error-modal").getParent();
12004 let type = "";
12005 if (errorParent == "backup-progress-modal") {
12006 $("#logs-modal").find(".modal-title").text($('#bmi-backup-logs-modal-title').text());
12007 type = "backup";
12008 } else if (errorParent == "restore-progress-modal") {
12009 $("#logs-modal").find(".modal-title").text($('#bmi-restore-logs-modal-title').text());
12010 type = "restore";
12011 }
12012 let preElement = $("#logs-modal").find("pre")[0];
12013 fillLogs(type, preElement).then(function () {
12014 $.bmi.modal("freeze-loading-modal").close();
12015 setTimeout(function () {
12016 $.bmi.modal("logs-modal").open();
12017 preElement.scroll({ top: preElement.scrollHeight });
12018 }, 300);
12019 });
12020 }
12021
12022 function fillRestoreLogs() {
12023 let textarea = $('#restore-log')[0];
12024 textarea.value = restoreLogs;
12025 }
12026
12027 function setupDownloadLogsHref(type = "backup") {
12028 let url = $("#BMI_BLOG_URL").text().trim();
12029 if (url.slice(-url.length) !== "/") url = url + "/";
12030 $progressId = type == "backup" ? "latest_full.log" : "latest_migration_full.log";
12031 let logs_url = url + "?backup-migration=PROGRESS_LOGS&progress-id=" + $progressId + "&bmi-id=current&t=" + +new Date() + "&sk=" + $("#BMI_SECRET_KEY").text().trim();
12032 $(".download-log-url.censored").attr("download", "secure-" + (type == "backup" ? "backup" : "restore") + "-logs.txt")
12033 $(".download-log-url.uncensored").attr("download", (type == "backup" ? "backup" : "restore") + "-logs.txt")
12034 $(".download-log-url.censored").attr("href", logs_url);
12035 $(".download-log-url.uncensored").attr("href", logs_url + "&uncensored=true");
12036 }
12037
12038 function getCurrentRestore() {
12039 return current_restore;
12040 }
12041 function getIsDownloadBackupFinished() {
12042 return downloadBackupFinished;
12043 }
12044 $.bmi.getCurrentRestore = getCurrentRestore;
12045 $.bmi.getIsDownloadBackupFinished = getIsDownloadBackupFinished;
12046
12047 function updateSuffixPosition() {
12048 let localBackupsPath = document.getElementById('bmi_path_storage_default');
12049 let localBackupsPathSuffix = document.getElementById('local-backups-suffix');
12050 let textWidth = getTextWidth(localBackupsPath.value, window.getComputedStyle(localBackupsPath).font);
12051 let suffixWidth = getTextWidth(localBackupsPathSuffix.innerText, window.getComputedStyle(localBackupsPathSuffix).font);
12052
12053 localBackupsPath.style.paddingRight = suffixWidth + 22 + 'px';
12054 if (localBackupsPath.value === '') {
12055 localBackupsPathSuffix.style.display = 'none';
12056 return;
12057 } else {
12058 localBackupsPathSuffix.style.display = 'inline-block';
12059 }
12060 localBackupsPathSuffix.style.left = Math.min(textWidth + 20, localBackupsPath.clientWidth - suffixWidth - 20) + 'px';
12061 }
12062
12063 function getTextWidth(text, font) {
12064 const canvas = document.createElement('canvas');
12065 const ctx = canvas.getContext('2d');
12066 ctx.font = font;
12067 return ctx.measureText(text).width;
12068 }
12069 $('#bmi_path_storage_default').on('input', updateSuffixPosition);
12070 $('#storage-options').on('click', function() {
12071 setTimeout(() => {
12072 updateSuffixPosition();
12073 }, 100);
12074 setTimeout(() => {
12075 updateSuffixPosition();
12076 }, 300);
12077 });
12078
12079 $('#bmi_backup_search_input').on('input', function() {
12080 if ($(this).hasClass('processing')) return;
12081 $(this).addClass('processing');
12082
12083 let searchTerm = $(this).val().toLowerCase();
12084 $.bmi.searchInBackups(searchTerm);
12085 setTimeout(function () {
12086 $(this).removeClass('processing');
12087 }, 100)
12088
12089 });
12090
12091 $('#bmi_backup_search_input').on('keypress', function(e) {
12092 if (e.which === 13) {
12093 e.preventDefault();
12094 let searchTerm = $(this).val().toLowerCase();
12095 $.bmi.searchInBackups(searchTerm);
12096 }
12097 });
12098
12099 $(window).on('resize', updateSuffixPosition);
12100
12101 $(document).on('visibilitychange', async function() {
12102 if ((backupOnGoing || restoreOnGoing) && document.visibilityState === 'visible') {
12103 await $.bmi.requestWakeLock();
12104 }
12105 });
12106
12107 $('.resync-with-ping-server').on('click', function(e) {
12108 e.preventDefault();
12109 $.bmi.ajax('resync-with-ping-server').then(function (res) {
12110 if (res.status == 'success') {
12111 $.bmi.alert('success', 'Your site has been registered successfully with our ping server.');
12112 } else {
12113 $.bmi.alert('error', res.msg || 'There was an error while trying to resync with ping server.');
12114 }
12115 }).catch(function (error) {
12116 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
12117 });
12118 });
12119
12120
12121 function applyActionBasedOnLogLines(lines) {
12122 if ($("#backup-stop").hasClass("disabled")) {
12123 let backupInitializedLine = lines.find(line => line.includes("backup_initialized"));
12124 if (backupInitializedLine) {
12125 $("#backup-stop").removeClass("disabled");
12126 }
12127 }
12128 }
12129
12130 // Init
12131 (function () {
12132 if (pagenow !== 'toplevel_page_backup-migration' && pagenow !== 'toplevel_page_backup-migration-network') return;
12133 scanDirectories();
12134 getAndInsertDynamicNames();
12135 $.bmi.reloadBackups();
12136 $.bmi.adjustStorageIcons();
12137 $.bmi.reloadStaging();
12138 runTimerST();
12139 runStagingTimers();
12140
12141 let url_x = $("#BMI_BLOG_URL").text().trim();
12142 if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
12143 let logs_backup =
12144 url_x +
12145 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&bmi-id=current&t=" +
12146 +new Date() +
12147 "&sk=" +
12148 $("#BMI_SECRET_KEY").text().trim();
12149 let logs_backup_uncensored =
12150 url_x +
12151 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&uncensored=true&bmi-id=current&t=" +
12152 +new Date() +
12153 "&sk=" +
12154 $("#BMI_SECRET_KEY").text().trim();
12155 let logs_restore =
12156 url_x +
12157 "?backup-migration=PROGRESS_LOGS&progress-id=latest_migration.log&bmi-id=current&t=" +
12158 +new Date() +
12159 "&sk=" +
12160 $("#BMI_SECRET_KEY").text().trim();
12161 let logs_staging =
12162 url_x +
12163 "?backup-migration=PROGRESS_LOGS&progress-id=latest_staging.log&bmi-id=current&t=" +
12164 +new Date() +
12165 "&sk=" +
12166 $("#BMI_SECRET_KEY").text().trim();
12167
12168 $(".download-backup-log-url").attr("href", logs_backup);
12169 $(".download-backup-log-url.uncensored").attr(
12170 "href",
12171 logs_backup_uncensored,
12172 );
12173 $(".download-restore-log-url").attr("href", logs_restore);
12174 $(".download-staging-log-url").attr("href", logs_staging);
12175 })();
12176 });
12177 jQuery(document).ready(function($) {
12178
12179 $('.bmi-modal-opener').on('click', function(e) {
12180
12181 if (this.getAttribute('data-modal') && this.getAttribute('data-modal') != '') {
12182 e.preventDefault();
12183 if (this.getAttribute('data-close'))
12184 $.bmi.modal(this.getAttribute('data-close')).close();
12185
12186 $.bmi.modal(this.getAttribute('data-modal')).open();
12187 }
12188
12189 });
12190
12191 $('.bmi-modal-closer').on('click', function(e) {
12192
12193 if (this.getAttribute('data-close') && this.getAttribute('data-close') != '') {
12194 e.preventDefault();
12195 $.bmi.modal(this.getAttribute('data-close')).close();
12196 } else {
12197 if (this.closest('.bmi-modal')) {
12198 e.preventDefault();
12199 if ($('#' + this.closest('.bmi-modal').getAttribute('id')).length > 0)
12200 $.bmi.modal(this.closest('.bmi-modal').getAttribute('id')).close();
12201 }
12202 }
12203
12204 });
12205
12206 $('.bmi-modal-close').on('click', function(e) {
12207
12208 if (this.closest('.bmi-modal')) {
12209 e.preventDefault();
12210 $.bmi.modal(this.closest('.bmi-modal').id).close();
12211 }
12212
12213 });
12214
12215 $('.bmi-modal').on('click', function(e) {
12216
12217 if (e.target == this && !$(e.target).hasClass('bmi-modal-no-close')) {
12218 $.bmi.modal(this.id).close();
12219 }
12220
12221 });
12222
12223 $('.bmi-modal-back').on('click', function(e) {
12224
12225 if (this.closest('.bmi-modal')){
12226 e.preventDefault();
12227 let modal = $.bmi.modal(this.closest('.bmi-modal').id);
12228 let modalParent = modal.getParent();
12229 if ( modalParent ){
12230 modal.close();
12231 $.bmi.modal(modalParent).open();
12232 }
12233 }
12234
12235 });
12236
12237 });jQuery(document).ready(function($) {
12238
12239 // Init tooltips
12240 if(pagenow === 'toplevel_page_backup-migration' || pagenow === 'toplevel_page_backup-migration-network')
12241 $.bmi.tooltips.init();
12242
12243 // Progress interval
12244 let upload_progress, current_last = -1,
12245 startmsg = false;
12246
12247 // Replace pleaceholders with real preloaders
12248 let preloader_divs = '';
12249 for (let i = 0; i < 12; ++i) preloader_divs += '<div></div>';
12250 $('.spinner-loader').html(preloader_divs).addClass('lds-spinner');
12251
12252 function setProgress(end = 0, duration = 1000) {
12253
12254 if (current_last == end) return;
12255 else current_last = end;
12256 clearInterval(upload_progress);
12257
12258 let start = parseInt($('.upload-percentage').text()) - 1;
12259 if (start > end && end != 0) return;
12260
12261 let range = end - start;
12262 let current = start;
12263 let increment = 1;
12264 let stepTime = Math.abs(Math.floor(duration / range));
12265
12266 upload_progress = setInterval(function() {
12267
12268 current += increment;
12269 $('.upload-progress-bar').find('span')[0].style.width = current + '%';
12270 $('.upload-percentage').text(current + '%');
12271
12272 if (current >= 100) {
12273 clearInterval(upload_progress);
12274 current_last = null;
12275 }
12276
12277 }, stepTime);
12278
12279 }
12280
12281 function Progress(value) {
12282
12283 clearInterval(upload_progress);
12284 if (value == 0) {
12285 $('.upload-progress-bar').find('span')[0].style.width = value + '%';
12286 $('.upload-percentage').text(value + '%');
12287 } else if (value == 100) {
12288 $('.upload-progress-bar').find('span')[0].style.width = value + '%';
12289 $('.upload-percentage').text(value + '%');
12290 } else {
12291 $('.upload-progress-bar').find('span')[0].style.width = value + '%';
12292 $('.upload-percentage').text(value + '%');
12293 // setProgress(value, 300);
12294 }
12295
12296 }
12297
12298 $.fchunker({
12299
12300 upId: 'upid',
12301 upShardSize: bmiVariables.maxUploadSize,
12302 upMaxSize: '2000',
12303 upUrl: ajaxurl + '?cache=false',
12304 upType: 'zip,tar,gz',
12305 bmiNonce: bmiVariables.nonce,
12306
12307 upCallBack: function(res) {
12308
12309 var status = res.status;
12310 var msg = res.message;
12311 var url = res.url + "?" + Math.random();
12312
12313 if (status == 2) {
12314
12315 setTimeout(function() {
12316 $('#drop-area').show(300);
12317 $('.upload-progress').hide(300);
12318 }, 100);
12319
12320 $.bmi.alert('success', $('#bmi-upload-end').text(), 3000);
12321 $.bmi.modal('upload-success-modal').open();
12322 $.bmi.reloadBackups();
12323
12324 }
12325
12326 if (status == 1) {
12327
12328 // console.log(msg);
12329 if (!startmsg) {
12330 $.bmi.alert('success', $('#bmi-upload-start').text(), 3000);
12331 startmsg = true;
12332 }
12333
12334 }
12335
12336 if (status == 0) {
12337
12338 $.upErrorMsg(msg);
12339 $('#drop-area').show(300);
12340 $('.upload-progress').hide(300);
12341
12342 }
12343
12344 if (status == 5) {
12345
12346 $.bmi.modal('upload-invalid-manifest-modal').open();
12347 $('#drop-area').show(300);
12348 $('.upload-progress').hide(300);
12349
12350 }
12351
12352 if (status == 3) {
12353
12354 Progress(100);
12355 $.upErrorMsg(msg);
12356 $('#drop-area').show(300);
12357 $('.upload-progress').hide(300);
12358
12359 }
12360
12361 },
12362
12363 upEvent: function(num) {
12364
12365 Progress(num);
12366
12367 },
12368
12369 upStop: function(errmsg) {
12370
12371 Progress(0);
12372
12373 setTimeout(function() {
12374 $('#drop-area').show(300);
12375 $('.upload-progress').hide(300);
12376
12377 }, 100);
12378
12379 if (errmsg.includes('Type error')) {
12380
12381 $.bmi.modal('upload-wrong-file-modal').open();
12382 $.bmi.alert('warning', $('#bmi-upload-wrong').text(), 3000);
12383
12384 } else if (errmsg.includes('File already exists')) {
12385
12386 $.bmi.modal('upload-exist-file-modal').open();
12387 $.bmi.alert('warning', $('#bmi-upload-exists').text(), 3000);
12388
12389 } else {
12390
12391 $.bmi.alert('error', $('#bmi-upload-error').text(), 3000);
12392 console.error(errmsg);
12393
12394 }
12395
12396 },
12397
12398 upStart: function() {
12399
12400 startmsg = false;
12401 current_last = -1;
12402 Progress(0);
12403
12404 setTimeout(function() {
12405 $('#drop-area').hide(300);
12406 $('.upload-progress').show(300);
12407 }, 100);
12408
12409 }
12410
12411 });
12412
12413 });
12414
12415 // function bmi_debug_function(data = {}) {
12416 //
12417 // jQuery.bmi.ajax('debugging', data).then(function(res) {
12418 //
12419 // console.log(res);
12420 //
12421 // }).catch(function(error) {
12422 //
12423 // console.log(error);
12424 //
12425 // });
12426 //
12427 // }
12428 jQuery(document).ready(function($) {
12429
12430 function setRadios(radios) {
12431 for (let i = 0; i < radios.length; ++i) {
12432
12433 let c = radios[i].closest('.container-radio');
12434 if (c && typeof c.classList != undefined) {
12435
12436 c.classList.remove('active');
12437
12438 if (radios[i].checked === true) {
12439 c.classList.add('active');
12440 }
12441
12442 }
12443
12444 }
12445 }
12446
12447 $('input[type="radio"]').on('change', function() {
12448
12449 let name = this.getAttribute('name');
12450 let container = this.closest('.container-radio');
12451 let radios = document.querySelectorAll('[name="' + name + '"]');
12452 setRadios(radios);
12453
12454 });
12455
12456 (function() {
12457 if (pagenow !== 'toplevel_page_backup-migration' && pagenow !== 'toplevel_page_backup-migration-network') return;
12458 let radios = document.getElementById('bmi').querySelectorAll('input[type="radio"]');
12459 setRadios(radios);
12460
12461 })();
12462
12463 });jQuery(document).ready(function($) {
12464
12465 let current_last = null;
12466 let logs_progress = null;
12467 let timeouter = null;
12468 let isLineAppendTimeoutRunning = false;
12469 let latestLines = [];
12470 let curmaxnum = 0;
12471 let curdivs = 0;
12472 let latestStep = '';
12473 let lineAppender = null;
12474 let endCodeCall = null;
12475 let nextBatchStop = false;
12476
12477 async function repeatLogUpdate() {
12478
12479 clearTimeout(timeouter);
12480 updateStagingLogs();
12481 initializeStagingLogsUpdater(true);
12482
12483 }
12484
12485 async function initializeStagingLogsUpdater(selfcall = false) {
12486
12487 if (selfcall == false) {
12488
12489 $('#staging-live-log-wrapper .log-wrapper').find('pre')[0].innerText = '';
12490 $('#staging-progress-modal .progress-active-bar')[0].style.width = '0%';
12491 $('#staging-progress-modal .progress-percentage')[0].style.left = '0%';
12492 $('#staging-progress-modal .progress-percentage')[0].innerText = '0%';
12493
12494 setProgressStaging(0);
12495 repeatLogUpdate();
12496
12497 } else {
12498
12499 timeouter = setTimeout(repeatLogUpdate, 1500);
12500
12501 }
12502
12503 }
12504
12505 function endStagingLogerUpdater() {
12506
12507 clearInterval(logs_progress);
12508 clearTimeout(lineAppender);
12509 clearTimeout(endCodeCall);
12510 clearTimeout(timeouter);
12511
12512 latestStep = '';
12513 latestLines = [];
12514 curmaxnum = 0;
12515 curdivs = 0;
12516 isLineAppendTimeoutRunning = false;
12517
12518 }
12519
12520 function insertPre(log, el) {
12521
12522 if (log === false) return;
12523 let lines = log.split('\n');
12524 if (lines.length >= 1) lines = lines.slice(0, -1);
12525 latestLines = lines;
12526
12527 if (isLineAppendTimeoutRunning == false) {
12528 if (curdivs < latestLines.length) {
12529 showNextLine(el);
12530 }
12531 }
12532
12533 }
12534
12535 function httpGet(theUrl) {
12536
12537 return new Promise(function(resolve) {
12538
12539 let isHttps = window.location.protocol.includes('https');
12540 let isUrlHttps = theUrl.includes('https');
12541 if (isUrlHttps) theUrl = theUrl.slice(5);
12542 else theUrl = theUrl.slice(4);
12543 if (isHttps) theUrl = 'https' + theUrl;
12544 else theUrl = 'http' + theUrl;
12545
12546 try {
12547
12548 if (window.XMLHttpRequest) {
12549 xmlhttp = new XMLHttpRequest();
12550 } else {
12551 xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
12552 }
12553
12554 xmlhttp.onloadend = function() {
12555
12556 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
12557
12558 return resolve(xmlhttp.responseText);
12559
12560 } else return resolve(false);
12561
12562 }
12563
12564 xmlhttp.open('GET', theUrl);
12565 xmlhttp.send();
12566
12567 } catch (e) {
12568
12569 return resolve(false);
12570
12571 }
12572
12573 });
12574
12575 }
12576
12577 function setProgressStaging(end = 0, duration = 1500) {
12578
12579 if (current_last == end) return;
12580 else current_last = end;
12581
12582 clearInterval(logs_progress);
12583
12584 let start = parseFloat($('#staging-progress-modal .progress-percentage')[0].style.left) - 1;
12585
12586 let range = end - start;
12587 let current = start;
12588 let increment = 1;
12589 let stepTime = Math.abs(Math.floor(duration / range));
12590
12591 logs_progress = setInterval(function() {
12592
12593 current += increment;
12594
12595 $('#staging-progress-modal .progress-active-bar')[0].style.width = (current).toFixed(2) + '%';
12596 $('#staging-progress-modal .progress-percentage')[0].style.left = (current).toFixed(2) + '%';
12597 $('#staging-progress-modal .progress-percentage')[0].innerText = (current).toFixed(0) + '%';
12598
12599 if (current >= 100) {
12600 current_last = null;
12601 clearInterval(logs_progress);
12602 }
12603
12604 if (current > end) clearInterval(logs_progress);
12605
12606 }, stepTime);
12607
12608 }
12609
12610 async function updateStagingLogs() {
12611
12612 return new Promise(async function (resolve) {
12613
12614 let url = $('#BMI_BLOG_URL').text().trim();
12615 if (url.slice(-url.length) !== '/') url = url + '/';
12616
12617 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());
12618 if (!res) return resolve();
12619
12620 let progress = res.split('\n').slice(0, 1)[0];
12621 res = res.split('\n').slice(1).join('\n');
12622
12623 if (progress === false || isNaN(parseFloat(progress))) return resolve();
12624 let pre = $('#staging-live-log-wrapper').find('pre')[0];
12625
12626 if (!res.includes('<') && !res.includes('>')) {
12627 if (res && res != false && typeof res !== 'undefined') {
12628 insertPre(res, pre);
12629 }
12630 }
12631
12632 setProgressStaging(progress);
12633 return resolve();
12634
12635 });
12636
12637 }
12638
12639 function showNextLine(el) {
12640
12641 let mostRecentStep = '';
12642 let delayedDisplayTime = 40;
12643 let line = latestLines[curdivs];
12644 let div = document.createElement('DIV');
12645
12646 let color = '';
12647 if (typeof line == 'undefined' || !line) return;
12648 if (line.substr(0, 6) == '[INFO]') color = 'blue';
12649 else if (line.substr(0, 9) == '[SUCCESS]') color = 'green';
12650 else if (line.substr(0, 6) == '[WARN]') color = 'orange';
12651 else if (line.substr(0, 7) == '[ERROR]') color = 'red';
12652 else if (line.substr(0, 10) == '[END-CODE]') color = 'hide_so_much';
12653 else if (line.substr(0, 9) == '[VERBOSE]') color = 'hide_verbose';
12654 else if (line.substr(0, 6) == '[STEP]') {
12655
12656 div.classList.add('bold');
12657 div.classList.add('step');
12658
12659 } else {
12660
12661 if (line && line.trim().length > 0 && line[0] != '[') {
12662 curdivs--;
12663 }
12664
12665 }
12666
12667 if (line.substr(0, 6) == '[STEP]') mostRecentStep = line.slice(29);
12668 if (color.length > 0) div.classList.add(color);
12669
12670 div.style.display = 'none';
12671 div.innerText = line;
12672
12673 el.appendChild(div);
12674 let endCodeFound = goThroughEndCodes();
12675
12676 $(div).show(delayedDisplayTime, function () {
12677 if (mostRecentStep != '' && mostRecentStep != latestStep) {
12678 latestStep = mostRecentStep;
12679 $('#staging_current_step').text(mostRecentStep);
12680 }
12681 });
12682 el.scrollTop = el.scrollHeight;
12683
12684 curdivs++;
12685 if (curdivs < latestLines.length && endCodeFound == false) {
12686 isLineAppendTimeoutRunning = true;
12687 lineAppender = setTimeout(function () {
12688 showNextLine(el);
12689 }, delayedDisplayTime);
12690 } else {
12691 isLineAppendTimeoutRunning = false;
12692 }
12693
12694 }
12695
12696 function goThroughEndCodes() {
12697
12698 let mostRecentStep = '';
12699 let endCodeFound = false;
12700
12701 for (let i = 0; i < latestLines.length; ++i) {
12702
12703 let line = latestLines[i];
12704 if (line.substr(0, 6) == '[STEP]') mostRecentStep = line.slice(29);
12705 else if (!endCodeFound && line && line.trim().includes('[END-CODE]')) {
12706
12707 endCodeFound = true;
12708 clearTimeout(endCodeCall);
12709
12710 if (line.includes('001')) endCodeCall = setTimeout(stagingCreationSuccess, 2500);
12711 else endCodeCall = setTimeout(stagingCreationFailed, 2500);
12712
12713 }
12714
12715 }
12716
12717 return endCodeFound;
12718
12719 }
12720
12721 function stagingCreationSuccess() {
12722
12723 endStagingLogerUpdater();
12724 $.bmi.modal('freeze-loading-modal').close();
12725 $.bmi.modal('staging-progress-modal').close();
12726 $.bmi.modal('staging-error-modal').close();
12727 $.bmi.modal('staging-success-modal').open();
12728
12729 }
12730
12731 function stagingCreationFailed(hideDefault = false) {
12732
12733 $('#after-logs-sent-modal').attr('data-error-source', 'staging');
12734
12735 endStagingLogerUpdater();
12736 $.bmi.modal('freeze-loading-modal').close();
12737 $.bmi.modal('staging-progress-modal').close();
12738 $.bmi.modal('staging-success-modal').close();
12739 $.bmi.modal('staging-error-modal').open();
12740
12741 if (!hideDefault) {
12742 let errorNotice = $('#stg-notices-button').attr('c-errorProcess');
12743 $.bmi.alert('error', errorNotice, 5000);
12744 }
12745
12746 }
12747
12748 function abortCreationProcess(name) {
12749
12750 endStagingLogerUpdater();
12751
12752 $.bmi.modal('freeze-loading-modal').close();
12753 $.bmi.modal('staging-progress-modal').close();
12754 $.bmi.modal('staging-success-modal').close();
12755 $.bmi.modal('staging-error-modal').close();
12756
12757 $.bmi.alert('info', $('#bmi-stg-aborted-al').text(), 3000);
12758
12759 $.bmi.reloadStaging();
12760
12761 }
12762
12763 function callLocalStagingCreationProcess(data, continuation = false) {
12764
12765 return new Promise(function(resolve) {
12766
12767 let endpoint = 'staging-start-local-creation';
12768 if (!continuation) {
12769 $.bmi.modal('freeze-loading-modal').open();
12770 } else {
12771 endpoint = 'staging-local-creation-process';
12772 }
12773
12774 $.bmi.ajax(endpoint, data).then(function(res) {
12775
12776 if (!continuation) {
12777 $.bmi.modal('freeze-loading-modal').close();
12778 $.bmi.modal('staging-progress-modal').open();
12779 }
12780
12781 if (res.status == 'success') {
12782
12783 $('#bmi-tastewp-staging-success-text').hide();
12784 $('#bmi-local-staging-success-text').show();
12785 $('.bmi-staging-success-img-normal').addClass('bmi-image-hide');
12786 $('.bmi-staging-success-img-tastewp').addClass('bmi-image-hide');
12787 $('.bmi-staging-success-img-normal').removeClass('bmi-image-hide');
12788
12789 $('#bmi-stg-subname-input').val('');
12790 $.bmi.reloadStaging();
12791
12792 let loginURL = `${res.url}/wp-login.php?autologin=true&user=${res.userid}&secret=${res.password}`;
12793 $('#bmi-visit-latest-staging').attr('href', loginURL);
12794 $('#bmi-staging-latest-url').attr('href', loginURL);
12795
12796 endCodeCall = setTimeout(stagingCreationSuccess, 500);
12797
12798 } else if (res.status == 'continue') {
12799
12800 if (!continuation) initializeStagingLogsUpdater();
12801 if (nextBatchStop) res['data']['delete'] = true;
12802 callLocalStagingCreationProcess(res['data'], true);
12803
12804 } else if (res.status == 'fail') {
12805
12806 stagingCreationFailed(true);
12807 return $.bmi.alert('warning', res.message, 5000);
12808
12809 } else if (res.status == 'deleted') {
12810
12811 abortCreationProcess(res['name']);
12812
12813 } else {
12814
12815 stagingCreationFailed();
12816
12817 }
12818
12819 }).catch(function(error) {
12820
12821 stagingCreationFailed();
12822 console.error(error);
12823
12824 });
12825
12826 });
12827
12828 }
12829
12830 function callTasteWPStagingCreationProcess(data, initialize = false) {
12831
12832 return new Promise(function(resolve) {
12833
12834 let endpoint = 'staging-tastewp-creation-process';
12835 if (initialize) {
12836 $.bmi.modal('freeze-loading-modal').open();
12837 data['initialize'] = true;
12838 }
12839
12840 $.bmi.ajax(endpoint, data).then(function(res) {
12841
12842 if (res.status == 'success') {
12843
12844 $.bmi.reloadStaging();
12845
12846 let accessURL = `${res.url}`;
12847 $('#bmi-visit-latest-staging').attr('href', accessURL);
12848 $('#bmi-staging-latest-url').attr('href', accessURL);
12849 $('#bmi-staging-latest-url').text(accessURL);
12850
12851 $('#bmi-tastewp-staging-success-text').show();
12852 $('#bmi-local-staging-success-text').hide();
12853 $('.bmi-staging-success-img-normal').addClass('bmi-image-hide');
12854 $('.bmi-staging-success-img-tastewp').addClass('bmi-image-hide');
12855 $('.bmi-staging-success-img-tastewp').removeClass('bmi-image-hide');
12856
12857 endCodeCall = setTimeout(stagingCreationSuccess, 500);
12858
12859 } else if (res.status == 'continue') {
12860
12861 if (initialize) {
12862 initializeStagingLogsUpdater();
12863 $.bmi.modal('freeze-loading-modal').close();
12864 $.bmi.modal('staging-progress-modal').open();
12865 }
12866
12867 if (nextBatchStop) res['data']['delete'] = true;
12868 callTasteWPStagingCreationProcess(res['data']);
12869
12870 } else if (res.status == 'fail') {
12871
12872 stagingCreationFailed(true);
12873 return $.bmi.alert('warning', res.message, 5000);
12874
12875 } else if (res.status == 'deleted') {
12876
12877 abortCreationProcess(res['name']);
12878
12879 } else {
12880
12881 stagingCreationFailed();
12882
12883 }
12884
12885 }).catch(function(error) {
12886
12887 stagingCreationFailed();
12888 console.error(error);
12889
12890 });
12891
12892 });
12893
12894 }
12895
12896 $('#bmi').on('click', '.bmi-stg-sel-box', function (e) {
12897 if ($(this)[0].classList.contains('bmi-active')) return;
12898 if (e.target.tagName == 'A') return;
12899 $('.bmi-stg-sel-box.bmi-active').removeClass('bmi-active');
12900 $(this).addClass('bmi-active');
12901
12902 if ($(this).data('mode') == 'tastewp') {
12903 $('.bmi-stg-creation-box-local').hide(300);
12904 $('.bmi-stg-creation-box-tastewp').hide(300);
12905 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
12906 if ($('.bmi-stg-drop-option:not(.bmi-stg-option-template)').length > 0) {
12907 $('.bmi-stg-creation-box-tastewp').show(300);
12908 } else {
12909 $('.bmi-stg-creation-box-tastewp-empty').show(300);
12910 }
12911 } else {
12912 $('.bmi-stg-creation-box-local').show(300);
12913 $('.bmi-stg-creation-box-tastewp').hide(300);
12914 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
12915 }
12916 });
12917
12918 $('.bmi-stg-dropdown-area-selector').on('click', function () {
12919 $('.bmi-stg-dropdown-area').toggleClass('bmi-active');
12920 });
12921
12922 $('body').on('click', ':not(.bmi-stg-dropdown-area)', function (e) {
12923 if (!e.target.closest('.bmi-stg-dropdown-area')) {
12924 $('.bmi-stg-dropdown-area').removeClass('bmi-active');
12925 }
12926 });
12927
12928 $('.bmi-stg-dropdown-area-inner-scroll').on('click', '.bmi-stg-drop-option', function (e) {
12929 let name = $(this).attr('backup-name');
12930 let date = $(this).find('.bmi-stg-option-date i').text();
12931 let size = $(this).find('.bmi-stg-option-size i').text();
12932
12933 $('.bmi-stg-drop-option.active').removeClass('active');
12934 $(this).addClass('active');
12935
12936 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(name);
12937 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text(date);
12938 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text(size);
12939
12940 $('#bmi-stg-current-backup-selected').val(name);
12941
12942 $('.bmi-stg-dropdown-area').removeClass('bmi-active');
12943 });
12944
12945 $('.i-staging-creator-tastewp').on('click', function () {
12946
12947 let backupName = $('.bmi-stg-dropdown-area-selector .bmi-stg-option-name').text();
12948
12949 $('#stg-prenotice-mode-local').hide();
12950 $('#stg-prenotice-mode-tastewp').show();
12951 $('#start-entire-staging').attr('mode', 'tastewp');
12952 $('#bmi-staging-local-current-backup').text(backupName);
12953
12954 $.bmi.modal('staging-prenotice-modal').open();
12955
12956 });
12957
12958 $('.i-staging-creator-local').on('click', function () {
12959
12960 let name = $('#bmi-stg-subname-input').val().trim();
12961
12962 let empty = $(this).attr('c-empty');
12963 let long = $(this).attr('c-long');
12964 let errorNotice = $(this).attr('c-error');
12965 let invalid = $(this).attr('c-invalid');
12966
12967 let validRegex = /^[a-zA-Z0-9-_]+$/;
12968
12969 if (name.length <= 0) {
12970 return $.bmi.alert('warning', empty, 5000);
12971 }
12972
12973 if (!validRegex.test(name)) {
12974 return $.bmi.alert('warning', invalid, 5000);
12975 }
12976
12977 if (name.length >= 24) {
12978 return $.bmi.alert('warning', long, 5000);
12979 }
12980
12981 $.bmi.modal('freeze-loading-modal').open();
12982
12983 $.bmi.ajax('staging-local-name', { name: name }).then(function(res) {
12984
12985 $.bmi.modal('freeze-loading-modal').close();
12986
12987 if (res.status == 'success') {
12988
12989 let url = $('#bmi-stg-homeurl').text() + $('#bmi-stg-subname-input').val();
12990 $('#stg-prenotice-mode-local').show();
12991 $('#stg-prenotice-mode-tastewp').hide();
12992 $('#bmi-staging-local-current-url').text(url);
12993 $('#bmi-staging-latest-url').text(url);
12994 $('#bmi-staging-latest-url, #bmi-visit-latest-staging').attr('href', url);
12995 $('#start-entire-staging').attr('mode', 'local'); // tastewp for TWP
12996
12997 $.bmi.modal('staging-prenotice-modal').open();
12998
12999 } else if (res.status == 'fail') {
13000
13001 return $.bmi.alert('warning', res.message, 5000);
13002
13003 } else {
13004
13005 return $.bmi.alert('error', errorNotice, 5000);
13006
13007 }
13008
13009 }).catch(function(error) {
13010
13011 $.bmi.modal('freeze-loading-modal').close();
13012 return $.bmi.alert('error', errorNotice, 5000);
13013 console.error(error);
13014
13015 });
13016
13017 });
13018
13019 $('#start-entire-staging').on('click', function (e) {
13020
13021 e.preventDefault();
13022 $.bmi.modal('staging-prenotice-modal').close();
13023 nextBatchStop = false;
13024
13025 if ($(this).attr('mode') == 'local') {
13026
13027 let name = $('#bmi-stg-subname-input').val().trim();
13028 callLocalStagingCreationProcess({ name: name });
13029
13030 } else if ($(this).attr('mode') == 'tastewp') {
13031
13032 let currMills = +new Date() + '';
13033 let name = Math.random().toString(36).substr(2, 16) + currMills.slice(-4);
13034 let backupName = $('#bmi-stg-current-backup-selected').val();
13035 callTasteWPStagingCreationProcess({ name: name, backupName: backupName }, true);
13036
13037 } else {
13038
13039 $.bmi.modal('freeze-loading-modal').close();
13040
13041 }
13042
13043 });
13044
13045 $('#rescan-for-staging').on('click', function(e) {
13046
13047 e.preventDefault();
13048 $.bmi.reloadStaging();
13049
13050 });
13051
13052 $('#stg-tbody-table').on('click', '.bc-stg-edit-btn', function(e) {
13053
13054 e.preventDefault();
13055 let name = $(this).closest('tr').attr('name');
13056 let displayName = $(this).closest('tr').find('.stg-tr-name').text();
13057
13058 $('#bmi-stg-rename-input').val(displayName);
13059 $('#stg-display-name-edit-confirm').attr('data-name', name);
13060
13061 $.bmi.modal('staging-rename-modal').open();
13062
13063 });
13064
13065 $('#stg-display-name-edit-confirm').on('click', function(e) {
13066
13067 e.preventDefault();
13068
13069 let empty = $('.i-staging-creator-local').attr('c-empty');
13070 let long = $('.i-staging-creator-local').attr('c-long');
13071 let invalid = $('.i-staging-creator-local').attr('c-invalid');
13072 let errorNotice = $('.i-staging-creator-local').attr('c-error');
13073
13074 let newName = $('#bmi-stg-rename-input').val();
13075 let oldName = $(this).attr('data-name');
13076
13077 let validRegex = /^[a-zA-Z0-9-_]+$/;
13078 if (newName.length <= 0) return $.bmi.alert('warning', empty, 5000);
13079 if (!validRegex.test(newName)) return $.bmi.alert('warning', invalid, 5000);
13080 if (newName.length >= 24) return $.bmi.alert('warning', long, 5000);
13081
13082 $.bmi.modal('freeze-loading-modal').open();
13083 $.bmi.ajax('staging-rename-display', { name: oldName, new: newName }).then(function(res) {
13084
13085 $.bmi.modal('freeze-loading-modal').close();
13086 if (res.status == 'success') {
13087
13088 $.bmi.modal('staging-rename-modal').close();
13089 $.bmi.reloadStaging();
13090
13091 } else if (res.status == 'fail') {
13092
13093 return $.bmi.alert('warning', res.message, 5000);
13094
13095 } else {
13096
13097 return $.bmi.alert('error', errorNotice, 5000);
13098
13099 }
13100
13101 }).catch(function(error) {
13102
13103 $.bmi.modal('freeze-loading-modal').close();
13104 return $.bmi.alert('error', errorNotice, 5000);
13105 console.error(error);
13106
13107 });
13108
13109 });
13110
13111 $('#bmi-visit-latest-staging').on('click', function (e) {
13112 if ($('#bmi-staging-latest-url').text().includes('tastewp')) {
13113 setTimeout(function () {
13114 $.bmi.reloadStaging();
13115 }, 8000);
13116 }
13117 });
13118
13119 $('#bmi-staging-stop').on('click', function () {
13120 $.bmi.modal('freeze-loading-modal').open();
13121 nextBatchStop = true;
13122 });
13123
13124 $('#stg-tbody-table').on('click', '.stg-login-btn', function(e) {
13125
13126 if ($(this).closest('tr').attr('server') == 'tastewp') {
13127 let url = $(this).closest('tr').find('.stg-tr-url-el').text();
13128 if (url.includes('/stg/')) $(this).attr('href', url);
13129 else {
13130 setTimeout(function () {
13131 $.bmi.reloadStaging();
13132 }, 8000);
13133 $(this).attr('href', 'https://tastewp.com/stg/access/' + $(this).closest('tr').attr('token'));
13134 }
13135 return true;
13136 }
13137
13138 e.preventDefault();
13139 let name = $(this).closest('tr').attr('name');
13140 let errorNotice = $('.i-staging-creator-local').attr('c-error');
13141 let self = this;
13142
13143 $.bmi.modal('freeze-loading-modal').open();
13144 $.bmi.ajax('staging-prepare-login', { name: name }).then(function(res) {
13145
13146 $.bmi.modal('freeze-loading-modal').close();
13147 if (res.status == 'success') {
13148
13149 $(self).attr('href', res.url);
13150 window.open(res.url, '_blank');
13151
13152 } else if (res.status == 'fail') {
13153
13154 return $.bmi.alert('warning', res.message, 5000);
13155
13156 } else {
13157
13158 return $.bmi.alert('error', errorNotice, 5000);
13159
13160 }
13161
13162 }).catch(function(error) {
13163
13164 $.bmi.modal('freeze-loading-modal').close();
13165 return $.bmi.alert('error', errorNotice, 5000);
13166 console.error(error);
13167
13168 });
13169
13170 });
13171
13172 $('#stg-tbody-table').on('click', '.bc-stg-remove-btn', function(e) {
13173
13174 e.preventDefault();
13175 let name = $(this).closest('tr').attr('name');
13176 let url = $(this).closest('tr').find('.stg-tr-url-el').text();
13177
13178 $('#bmi-staging-removal-url').attr('href', url);
13179 $('#bmi-staging-removal-url').text(url);
13180 $('#stg-removal-confirm').attr('data-name', name);
13181
13182 $.bmi.modal('staging-delete-confirm-modal').open();
13183
13184 });
13185
13186 $('#stg-removal-confirm').on('click', function(e) {
13187
13188 e.preventDefault();
13189
13190 let name = $(this).attr('data-name');
13191 let errorNotice = $('.i-staging-creator-local').attr('c-error');
13192
13193 $.bmi.modal('freeze-loading-modal').open();
13194 $.bmi.ajax('staging-delete-permanently', { name: name }).then(function(res) {
13195
13196 $.bmi.modal('freeze-loading-modal').close();
13197 if (res.status == 'success') {
13198
13199 $.bmi.modal('staging-delete-confirm-modal').close();
13200 $.bmi.reloadStaging();
13201
13202 } else if (res.status == 'fail') {
13203
13204 return $.bmi.alert('warning', res.message, 5000);
13205
13206 } else {
13207
13208 return $.bmi.alert('error', errorNotice, 5000);
13209
13210 }
13211
13212 }).catch(function(error) {
13213
13214 $.bmi.modal('freeze-loading-modal').close();
13215 return $.bmi.alert('error', errorNotice, 5000);
13216 console.error(error);
13217
13218 });
13219
13220 });
13221
13222 let wrapper = document.querySelector('.bmi-stg-dropdown-area-inner-scroll');
13223 if (wrapper) {
13224 wrapper.addEventListener('wheel', function (e) {
13225 let space = (wrapper.scrollHeight - wrapper.offsetHeight) - wrapper.scrollTop;
13226 if (e.deltaY < 0) {
13227 if (wrapper.scrollTop == 0) {
13228 e.preventDefault();
13229 e.stopPropagation();
13230 }
13231 } else if (space <= 0) {
13232 e.preventDefault();
13233 e.stopPropagation();
13234 }
13235 });
13236 }
13237
13238 });
13239 jQuery(document).ready(function($) {
13240
13241 let transition = false;
13242
13243 $('.bmi-tabs').on('click', '.bmi-tab', function(e) {
13244
13245 if (this.classList.contains('active')) return;
13246 if (transition === true) return;
13247 else transition = true;
13248
13249 $.bmi.collapsers.closeAll();
13250 let id = this.getAttribute('data-point');
13251
13252 if (id == 'manage-restore-wrapper') {
13253
13254 $('#create-backup-wrapper').hide(300);
13255 $('#staging-sites-wrapper').hide(300);
13256 $('[data-point="create-backup-wrapper"]').removeClass('active');
13257 $('[data-point="staging-sites-wrapper"]').removeClass('active');
13258
13259 } else if (id == 'staging-sites-wrapper') {
13260
13261 $('#create-backup-wrapper').hide(300);
13262 $('#manage-restore-wrapper').hide(300);
13263 $('[data-point="create-backup-wrapper"]').removeClass('active');
13264 $('[data-point="manage-restore-wrapper"]').removeClass('active');
13265
13266 } else if (id == 'create-backup-wrapper') {
13267
13268 $('#manage-restore-wrapper').hide(300);
13269 $('#staging-sites-wrapper').hide(300);
13270 $('[data-point="manage-restore-wrapper"]').removeClass('active');
13271 $('[data-point="staging-sites-wrapper"]').removeClass('active');
13272
13273 }
13274
13275 $('#' + id).show(300);
13276 $(this).addClass('active');
13277
13278 setTimeout(function() {
13279 transition = false;
13280 }, 320);
13281
13282 });
13283
13284 });
13285 jQuery(document).ready(function($) {
13286
13287 let dropArea = document.getElementById("drop-area");
13288 if (!dropArea) return;
13289
13290 ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
13291 dropArea.addEventListener(eventName, preventDefaults, false)
13292 });
13293
13294 ['dragenter', 'dragover'].forEach(eventName => {
13295 dropArea.addEventListener(eventName, highlight, false);
13296 document.querySelector('body').addEventListener(eventName, highlight, false);
13297 });
13298
13299 ['dragleave', 'drop', 'mouseleave'].forEach(eventName => {
13300 dropArea.addEventListener(eventName, unhighlight, false);
13301 });
13302
13303 $('body, #drop-area').on('mouseleave dragleave drop', function() {
13304 unhighlight();
13305 });
13306
13307 dropArea.addEventListener('drop', handleDrop, false);
13308
13309 function preventDefaults(e) {
13310 e.preventDefault()
13311 e.stopPropagation()
13312 }
13313
13314 function highlight(e) {
13315 dropArea.classList.add('highlight')
13316 }
13317
13318 function unhighlight(e) {
13319 dropArea.classList.remove('highlight')
13320 }
13321
13322 function handleDrop(e) {
13323 var dt = e.dataTransfer
13324 var files = dt.files
13325
13326 handleFiles(files)
13327 }
13328
13329 function handleFiles(files) {
13330 files = [...files];
13331 jQuery.fchunker_upload('file', files[0]);
13332 }
13333
13334 });