PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.0.0
Backup Migration v2.0.0
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 8 months ago
backup-migration.min.js
12816 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') 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
4416
4417 function escapeHtml(t){return(t=""+t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}
4418
4419 $.extend({
4420 bmi: {
4421
4422 // STATIC: Copy to clipboard
4423 clipboard: function(str, text = false) {
4424
4425 try {
4426
4427 const el = document.createElement('textarea');
4428 el.value = str;
4429 el.setAttribute('readonly', '');
4430 el.style.position = 'absolute';
4431 el.style.left = '-9999px';
4432 document.body.appendChild(el);
4433 el.select();
4434 document.execCommand('copy');
4435 document.body.removeChild(el);
4436
4437 let def = $('#bmi-success-copy').text();
4438 if (text != false) def = text;
4439 $.bmi.alert('success', def, 3000);
4440
4441 return true;
4442
4443 } catch (e) {
4444
4445 console.log(e);
4446 $.bmi.alert('warning', $('#bmi-failed-copy').text(), 3000);
4447 return false;
4448
4449 }
4450
4451 },
4452
4453 // Alert
4454 alert: function(type = 'default', msg = '---', timeout = 7000) {
4455
4456 if ($('.bmi-notification-box').length <= 0)
4457 $('body').find('#bmi').prepend($('<div class="bmi-notification-box"></div>'));
4458
4459 if (type == 'default') type = '';
4460 else if (type == 'success') type = ' is-success';
4461 else if (type == 'warning') type = ' is-warning';
4462 else if (type == 'error') type = ' is-danger';
4463 else if (type == 'info') type = ' is-info';
4464 else type = ' is-info';
4465 let elid = Math.floor(Math.random() * Math.floor(64000));
4466
4467 let $html = `<div style="display: none;" id="ntf-${elid}" class="bmi-notification${type}">
4468 <button class="bmi-times-button" onclick="jQuery.bmi.hideAlert(this)">&times;</button>
4469 <div class="bmi-cf">
4470 <div class="bmi-left bmi-alert-icon"><div class="bmi-icon-bg"></div></div>
4471 <div class="bmi-left bmi-alert-msg-title">
4472 <div class="bmi-title${type}">Backup Migration</div>
4473 <div>${msg}</div>
4474 </div>
4475 </div>
4476 </div>`;
4477
4478
4479 $('.bmi-notification-box').prepend($html);
4480 let ntf = $(`#ntf-${elid}`);
4481 ntf.css({
4482 opacity: 0,
4483 display: 'block'
4484 });
4485 let width = ntf.outerWidth(),
4486 height = ntf.outerHeight();
4487
4488 ntf.css({
4489 right: '-35vw',
4490 'font-size': '0px',
4491 width: 0,
4492 padding: 0,
4493 opacity: '0'
4494 });
4495 ntf.animate({
4496 right: '15px',
4497 width: width,
4498 padding: '1rem 2rem 1rem 1.5rem',
4499 opacity: '1'
4500 }, {
4501 duration: 200,
4502 queue: false
4503 });
4504 ntf.animate({
4505 'font-size': '16px'
4506 }, {
4507 duration: 300,
4508 queue: false
4509 });
4510
4511 setTimeout(() => {
4512 $(`#ntf-${elid}`).css({
4513 width: ''
4514 });
4515 }, 250);
4516
4517 setTimeout(() => {
4518 ntf.animate({
4519 'font-size': '0px'
4520 }, {
4521 duration: 200,
4522 queue: false
4523 });
4524 ntf.animate({
4525 right: '-35vw',
4526 height: 0,
4527 width: 0,
4528 margin: 0,
4529 padding: 0,
4530 opacity: '0'
4531 }, 300, function() {
4532 ntf.remove();
4533 });
4534 }, timeout);
4535
4536 },
4537
4538 // Response message
4539 _msg: function(res) {
4540
4541 if (res.status != 'msg') return;
4542
4543 if (typeof res.level == 'undefined') res.level = 'info';
4544 $.bmi.alert(res.level, res.why, 3000);
4545
4546 console.log('Backup-migration: ', '[' + res.level.toUpperCase() + ']', res.why);
4547
4548 },
4549
4550 // Hide alert
4551 hideAlert: function(self) {
4552
4553 let ntf = $(self).parents('.bmi-notification');
4554 ntf.animate({
4555 'font-size': '0px'
4556 }, {
4557 duration: 200,
4558 queue: false
4559 });
4560 ntf.animate({
4561 right: '-35vw',
4562 height: 0,
4563 width: 0,
4564 margin: 0,
4565 padding: 0,
4566 opacity: '0'
4567 }, 300, function() {
4568 ntf.remove();
4569 });
4570
4571 },
4572
4573 // OBJECT: Modal
4574 modal: function(id = false) {
4575
4576 let mod = false;
4577 if (id != false) mod = document.getElementById(id);
4578
4579 return {
4580 clearModal: function() {
4581
4582 mod.querySelectorAll('.customselect').forEach(function(dropdown) {
4583 dropdown.classList.remove('active');
4584 });
4585
4586 mod.querySelectorAll('input[type="text"]').forEach(function(input) {
4587 input.value = '';
4588 input.setAttribute('value', '');
4589 });
4590
4591 },
4592 open: function(cb = function() {}) {
4593
4594 mod.classList.add('before-open');
4595 setTimeout(function() {
4596 mod.classList.add('open');
4597 $('html')[0].style.overflowY = 'hidden';
4598 setTimeout(cb, 410);
4599 }, 10);
4600
4601 },
4602 close: function(cb = function() {}) {
4603
4604 if (mod.offsetWidth > 0 && mod.offsetHeight > 0) {
4605 mod.classList.add('before-close');
4606 setTimeout(function() {
4607 mod.classList.add('closed');
4608 setTimeout(function() {
4609 mod.classList.remove('before-open');
4610 mod.classList.remove('open');
4611 mod.classList.remove('before-close');
4612 mod.classList.remove('closed');
4613 $.bmi.modal(mod.id).clearModal();
4614 cb();
4615 }, 410);
4616 }, 10);
4617 } else {
4618 mod.classList.remove('before-open');
4619 mod.classList.remove('open');
4620 mod.classList.remove('before-close');
4621 mod.classList.remove('closed');
4622 cb();
4623 }
4624
4625 $('html')[0].style.overflowY = 'auto';
4626
4627 },
4628 closeAll: function() {
4629
4630 let modals = document.querySelectorAll('.modal');
4631 modals.forEach(function(mod) {
4632
4633 $.bmi.modal(mod.id).close();
4634
4635 });
4636
4637 $('html')[0].style.overflowY = 'auto';
4638
4639 },
4640 setParent: function(parentId) {
4641
4642 mod.setAttribute('data-parent-id',parentId);
4643
4644 },
4645 getParent: function() {
4646
4647 return mod.getAttribute('data-parent-id');
4648 }
4649 }
4650
4651 },
4652
4653 // PROMISE: Return JSON (from string) or FAIL
4654 json: function(str) {
4655
4656 let originalString = str;
4657 return new Promise(function(resolve, reject) {
4658
4659 try {
4660
4661 let json = JSON.parse(str);
4662 return resolve(json);
4663
4664 } catch (e) {
4665
4666 if (typeof str === 'string') {
4667
4668 let reversed = $.bmi.reverse(str);
4669 let lastcorrect = reversed.indexOf('}');
4670 if (lastcorrect == 0) lastcorrect = str.length;
4671 else lastcorrect = -lastcorrect;
4672
4673 str = str.slice(str.indexOf('{'), lastcorrect);
4674
4675 try {
4676
4677 let json = JSON.parse(str);
4678 return resolve(json);
4679
4680 } catch (e) {
4681
4682 return resolve(originalString);
4683
4684 }
4685
4686 } else return reject(false);
4687
4688 }
4689
4690 });
4691
4692 },
4693
4694 // STATIC: Returns reversed string
4695 reverse: function(str) {
4696
4697 if (typeof str === 'string') {
4698
4699 return (str === '') ? '' : $.bmi.reverse(str.substr(1)) + str.charAt(0);
4700
4701 } else {
4702
4703 return str;
4704
4705 }
4706
4707 },
4708
4709 // AJAX: Logger of JSON
4710 logJsonError: function(errorLogObj, func) {
4711
4712 data = {};
4713 data.action = 'backup_migration';
4714 data.token = 'bmi';
4715 data.nonce = bmiVariables.nonce;
4716 data.f = 'front-end-ajax-error';
4717 data.call = func;
4718 data.error = errorLogObj;
4719
4720 $.post(ajaxurl, data).done((res) => {
4721
4722 $.bmi.json(res).then(function(res) {
4723
4724 return;
4725
4726 }).catch(function(error) {
4727
4728 console.log(error);
4729
4730 });
4731
4732 }).fail((error) => {
4733
4734 console.error(error);
4735
4736 });
4737
4738 },
4739
4740 objectToFormData: (obj, form = null, namespace = '') => {
4741 const formData = form || new FormData();
4742 const isArr = Array.isArray(obj);
4743
4744 for (const key in obj) {
4745 if (!obj.hasOwnProperty(key) || obj[key] === null || obj[key] === undefined) {
4746 continue;
4747 }
4748
4749 const formKey = namespace ? (isArr ? `${namespace}[]` : `${namespace}[${key}]`) : key;
4750
4751 if (typeof obj[key] === 'string') {
4752 formData.append(formKey, obj[key]);
4753 } else if (obj[key] instanceof Date) {
4754 formData.append(formKey, obj[key].toISOString());
4755 } else if (obj[key] instanceof File) {
4756 formData.append(formKey, obj[key]);
4757 } else if (typeof obj[key] !== 'object') {
4758 formData.append(formKey, obj[key].toString());
4759 } else if (Array.isArray(obj[key])) {
4760 obj[key].forEach((item, index) => {
4761 const arrayKey = `${formKey}[${index}]`;
4762 if (typeof item === 'string')
4763 formData.append(arrayKey, item);
4764
4765 else
4766 jQuery.bmi.objectToFormData(item, formData, arrayKey);
4767 });
4768 } else {
4769 jQuery.bmi.objectToFormData(obj[key], formData, formKey);
4770 }
4771 }
4772
4773 return formData;
4774 },
4775
4776 // PROMISE: BMI POST Requests
4777 ajax: function(func, data = {}) {
4778
4779 return new Promise(async function(resolve, reject) {
4780
4781 function _error(error) {
4782
4783 let ajaxErrorObj = {};
4784
4785 console.log('------- BACKUP MIGRATION ERROR START -------');
4786 console.log('The error:', error);
4787 console.log('Call: ', func);
4788 ajaxErrorObj['call'] = func;
4789
4790 if (typeof error == 'object') {
4791 for (let i = 0; i < error.length; ++i) {
4792 if (typeof error[i] == 'object') {
4793 if (typeof error[i]['message'] != 'undefined') {
4794 console.log(i, error[i]['message']);
4795 ajaxErrorObj[`${i}_x`] = error[i]['message'];
4796 }
4797
4798 for (let k in error[i]) {
4799 if (typeof error[i][k] != 'function') {
4800 if (typeof error[i][k] == 'string' && error[i][k].length > 2000) {
4801 ajaxErrorObj[`${i}_${k}`] = error[i][k].slice(0, 2000);
4802 console.log(i, k);
4803 console.warn(error[i][k]);
4804 } else {
4805 console.log(i, k, error[i][k]);
4806 ajaxErrorObj[`${i}_${k}`] = error[i][k];
4807 }
4808 }
4809 }
4810 } else {
4811 console.log(i, error[i]);
4812 ajaxErrorObj[`${i}_x`] = error[i];
4813 }
4814 }
4815 } else {
4816 console.log(error);
4817
4818 if (typeof error == 'string' && error.length > 2000) {
4819 ajaxErrorObj[`single_error_txt`] = error.slice(0, 2000);
4820 } else {
4821 ajaxErrorObj[`single_error_txt`] = error;
4822 }
4823 }
4824 console.log('-------- BACKUP MIGRATION ERROR END --------\n\n');
4825
4826 $.bmi.logJsonError(ajaxErrorObj, func);
4827
4828 reject(error);
4829
4830 }
4831
4832 data.action = 'backup_migration';
4833 data.token = 'bmi';
4834 data.nonce = bmiVariables.nonce;
4835 data.f = func;
4836
4837 try {
4838 let res = await fetch(ajaxurl + '?cache=false', {
4839 "body": new URLSearchParams($.bmi.objectToFormData(data)).toString(),
4840 "cache": "default",
4841 "credentials": "include",
4842 "headers": {
4843 "Accept": "*/*",
4844 "Accept-Language": navigator.language + ";q=0.9",
4845 "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
4846 "User-Agent": navigator.userAgent,
4847 "X-Requested-With": "XMLHttpRequest"
4848 },
4849 "method": "POST",
4850 "mode": "cors",
4851 "redirect": "follow",
4852 "referrer": window.location.href,
4853 "referrerPolicy": "strict-origin-when-cross-origin"
4854 });
4855
4856 if (res.ok === true) {
4857
4858 let response = await res.text();
4859 $.bmi.json(response).then(function(parsedResponse) {
4860
4861 resolve(parsedResponse);
4862
4863 }).catch(function(error) {
4864
4865 console.error(error);
4866 _error(['json', error, res, func, data, (response || false)]);
4867
4868 });
4869
4870 } else {
4871
4872 throw new Error('Response is not OK: ' + response);
4873
4874 }
4875
4876 } catch (error) {
4877
4878 _error([error, func, data]);
4879
4880 }
4881
4882 // $.post(ajaxurl + '?cache=false', data).done((res) => {
4883
4884 // $.bmi.json(res).then(function(res) {
4885
4886 // resolve(res);
4887
4888 // }).catch(function(error) {
4889
4890 // console.error(error);
4891 // _error(['json', error, res, func, data, (res.responseText || false)]);
4892
4893 // });
4894
4895 // }).fail((error) => {
4896
4897 // _error([error, func, data]);
4898
4899 // });
4900
4901 });
4902
4903 },
4904
4905 // STATIC: Tooltips
4906 tooltips: {
4907
4908 init: function() {
4909
4910 function tooltipReposition() {
4911 setTimeout(() => {
4912 let instances = $.tooltipster.instances();
4913 for (instance in instances)
4914 instances[instance].reposition();
4915 }, 10);
4916 }
4917
4918 let settings = {
4919 delay: 200,
4920 debug: false,
4921 delayTouch: [100, 200],
4922 interactive: false,
4923 distance: 0,
4924 side: 'top',
4925 contentAsHTML: false,
4926 maxWidth: 460,
4927 triggerOpen: {
4928 mouseenter: true,
4929 touchstart: true
4930 },
4931 triggerClose: {
4932 mouseleave: true,
4933 click: true,
4934 tap: true
4935 },
4936 theme: ['bmi-tooltip', 'bmi-tt-default', 'bmi-tt-default-customized'],
4937 repositionOnScroll: true,
4938 functionReady: tooltipReposition
4939 }
4940
4941 function extractThemesFromElement(el) {
4942 try {
4943 const base = settings.theme;
4944 const classList = (el && el.className ? el.className : '')
4945 .split(/\s+/)
4946 .filter(Boolean);
4947 const extracted = classList.filter(c => c.indexOf('bmi-tt-') === 0);
4948 if (extracted.length) return base.concat(extracted);
4949 else return base;
4950 } catch (e) {
4951 return settings.theme;
4952 }
4953 }
4954
4955 let tts = $('.tooltip');
4956 let tts_html = $('.tooltip-html');
4957 let premiums = $('.premium-wrapper');
4958 let settings_html = JSON.parse(JSON.stringify(settings));
4959 settings_html.contentAsHTML = true;
4960 settings_html.interactive = true;
4961
4962 for (let i = 0; i < tts.length; ++i) {
4963
4964 let tooltip = tts[i];
4965 let top = tooltip.getAttribute('data-top');
4966 let side = tooltip.getAttribute('side');
4967 let s = JSON.parse(JSON.stringify(settings));
4968 s.content = tooltip.getAttribute('tooltip');
4969 s.theme = extractThemesFromElement(tooltip);
4970 if (top) s.distance = parseInt(top);
4971 if (side && side.trim().length > 0) s.side = side;
4972
4973 $(tooltip).tooltipster(s);
4974
4975 }
4976
4977 for (let i = 0; i < tts_html.length; ++i) {
4978
4979 let tooltip = tts_html[i];
4980 let side = tooltip.getAttribute('side');
4981 let s = JSON.parse(JSON.stringify(settings_html));
4982 s.content = tooltip.getAttribute('tooltip');
4983 s.theme = extractThemesFromElement(tooltip);
4984 if (side && side.trim().length > 0) s.side = side;
4985
4986 $(tooltip).tooltipster(s);
4987
4988 }
4989
4990 for (let i = 0; i < premiums.length; ++i) {
4991
4992 let premium = premiums[i];
4993 let semiums = JSON.parse(JSON.stringify(settings));
4994 semiums.contentAsHTML = true;
4995 semiums.interactive = true;
4996 semiums.maxWidth = 500;
4997 semiums.theme = extractThemesFromElement(premium);
4998
4999 if (premium.getAttribute('tooltip')) semiums.content = premium.getAttribute('tooltip');
5000 else if (premium.getAttribute('data-ready')) {
5001 semiums.content = $('#premium-tooltip-pre')[0].innerHTML.trim() + ' ' + premium.getAttribute('data-ready').trim() + ' ' + $('#premium-tooltip-r')[0].innerHTML.trim();
5002 } else semiums.content = $('#premium-tooltip')[0].innerHTML;
5003
5004 if (premium.getAttribute('side')) semiums.side = premium.getAttribute('side');
5005
5006 $(premium).tooltipster(semiums);
5007
5008 }
5009
5010 },
5011
5012 hideAll: function(rightnow = false) {
5013
5014 function _hide() {
5015 let instances = $.tooltipster.instances();
5016 for (instance in instances) instances[instance].close();
5017 }
5018
5019 if (rightnow) _hide();
5020 else setTimeout(_hide, 10);
5021
5022 }
5023
5024 },
5025
5026 // STATIC: Collapsers
5027 collapsers: {
5028
5029 toggle: function(self) {
5030
5031 if (collapsing === true) return;
5032 else collapsing = true;
5033
5034 let group = self.getAttribute('group');
5035 if (self.classList.contains('active')) $.bmi.collapsers.close(self);
5036 else $.bmi.collapsers.open(self, group);
5037
5038 },
5039
5040 open: function(el, group) {
5041
5042 $.bmi.collapsers.closeGroup(group);
5043 $(el).addClass('active');
5044 $(el).find('.content').show(300);
5045 setTimeout(function() {
5046 collapsing = false;
5047 }, 300);
5048
5049 },
5050
5051 close: function(el) {
5052
5053 $(el).removeClass('active');
5054 $(el).find('.content').hide(300);
5055 setTimeout(function() {
5056 collapsing = false;
5057 }, 300);
5058
5059 },
5060
5061 closeGroup: function(group) {
5062
5063 $('.collapser[group="' + group + '"]').removeClass('active');
5064 $('.collapser[group="' + group + '"]').find('.content').hide(300);
5065 setTimeout(function() {
5066 collapsing = false;
5067 }, 300);
5068
5069 },
5070
5071 closeAll: function() {
5072
5073 $('.collapser').removeClass('active');
5074 $('.collapser').find('.content').hide(300);
5075 setTimeout(function() {
5076 collapsing = false;
5077 }, 300);
5078
5079 }
5080
5081 },
5082
5083 // STATIC: URL Validation
5084 isUrlValid: function(url) {
5085 var re_weburl = new RegExp(
5086 "^" +
5087 "(?:(?:(?:https?|ftp):)?\\/\\/)" +
5088 "(?:\\S+(?::\\S*)?@)?" +
5089 "(?:" +
5090 "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
5091 "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
5092 "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
5093 "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
5094 "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
5095 "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
5096 "|" +
5097 "(?:" +
5098 "(?:" +
5099 "[a-z0-9\\u00a1-\\uffff]" +
5100 "[a-z0-9\\u00a1-\\uffff_-]{0,62}" +
5101 ")?" +
5102 "[a-z0-9\\u00a1-\\uffff]\\." +
5103 ")+" +
5104 "(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" +
5105 ")" +
5106 "(?::\\d{2,5})?" +
5107 "(?:[/?#]\\S*)?" +
5108 "$", "i"
5109 );
5110
5111 if (re_weburl.test(url)) return true;
5112 else return false;
5113 },
5114
5115 // STATIC: Human Readable
5116 bytesToHuman: function(a, b, c, d, e) {
5117 return (b = Math, c = b.log, d = 1024, e = c(a) / c(d) | 0, a / b.pow(d, e))
5118 .toFixed(2) + ' ' + (e ? 'KMGTPEZY' [--e] + 'B' : 'Bytes');
5119 },
5120
5121 // STATIC: Human Readable Cut Decimal
5122 bytesToHumanCut: function(a, b, c, d, e) {
5123 return Math.ceil((b = Math, c = b.log, d = 1024, e = c(a) / c(d) | 0, a / b.pow(d, e)))
5124 + ' ' + (e ? 'KMGTPEZY' [--e] + 'B' : 'Bytes');
5125 },
5126
5127 // STATIC: Getting backups
5128 getCurrentBackups: function(done = function() {}, tries = 0) {
5129
5130 if ($('#reloading-bm-list').length > 0 && $('#reloading-bm-list')[0].style.display == 'none') {
5131 $('#reloading-bm-list').show();
5132 $.bmi.ajax('get-current-backups', {}).then(function(res) {
5133
5134 // console.warn('Backup list loaded...');
5135 $.bmi.ajax('check-not-uploaded-backups').then(function(res) {
5136 // console.warn('Checking for not uploaded backups...');
5137 // console.log(res);
5138 });
5139 $('#reloading-bm-list').hide();
5140 done(res);
5141
5142
5143 }).catch(function(error) {
5144
5145 $('#reloading-bm-list').hide();
5146
5147 if (tries > 5) return;
5148
5149 setTimeout(() => {
5150 $.bmi.getCurrentBackups(done, (tries+1));
5151 }, 1000);
5152
5153 });
5154 }
5155
5156 },
5157
5158 getCurrentStaging: function(done = function() {}) {
5159
5160 if ($('#reloading-bm-stg-list').length > 0 && $('#reloading-bm-stg-list')[0].style.display == 'none') {
5161 $('#reloading-bm-stg-list').show();
5162 $.bmi.ajax('staging-get-updated-list', {}).then(function(res) {
5163
5164 $('#reloading-bm-stg-list').hide();
5165 done(res);
5166
5167 }).catch(function(error) {
5168
5169 //
5170
5171 });
5172 }
5173
5174 },
5175
5176 getExpirationTime: function(time) {
5177
5178 let $tbody = $('#stg-tbody-table');
5179 let expiresNever = $tbody.data('never');
5180
5181 if (isNaN(parseInt(time)) || time == expiresNever) {
5182 return time;
5183 }
5184
5185 let now = +new Date();
5186 let destination = time * 1000;
5187 let diff = (destination - now);
5188
5189 let days = Math.floor(diff / 24 / 60 / 60 / 1000);
5190 let hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
5191 let minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
5192 let seconds = Math.floor((diff % (1000 * 60)) / 1000);
5193
5194 if (destination < now) {
5195 days = 0;
5196 hours = 0;
5197 minutes = 0;
5198 seconds = 0;
5199 }
5200
5201 if (days == 0 && hours == 0 && minutes == 0 && (seconds == 2 || seconds == 1)) {
5202 clearTimeout(window.bmiRefreshExpired);
5203 window.bmiRefreshExpired = setTimeout(function () {
5204 $.bmi.reloadStaging();
5205 }, 3000);
5206 }
5207
5208 return `${('0' + days).slice(-2)}:${('0' + hours).slice(-2)}:${('0' + minutes).slice(-2)}:${('0' + seconds).slice(-2)}`;
5209
5210 },
5211
5212 fillWithNewStagings: function(sites, cb = function() {}) {
5213
5214 let stagingSiteNames = [];
5215 let amountOfSites = 0;
5216 let $tbody = $('#stg-tbody-table');
5217 let $templateOrigin = $('.br_stg_tr_template');
5218 let emptyText = $tbody.data('empty');
5219 let localText = $tbody.data('local');
5220 let prefixText = $tbody.data('prefix');
5221 let expiresNever = $tbody.data('never');
5222 let displayText = $tbody.data('display');
5223 let originalText = $tbody.data('original');
5224 let emptyElementTR = document.createElement('TR');
5225 let emptyElementTD = document.createElement('TD');
5226 emptyElementTD.setAttribute('colspan', '100%');
5227 emptyElementTD.classList.add('center');
5228 emptyElementTD.innerText = emptyText;
5229 emptyElementTR.append(emptyElementTD);
5230
5231 $tbody.html('');
5232 for (let site in sites) {
5233
5234 site = sites[site];
5235 if (typeof site.name == 'undefined') continue;
5236 if (typeof site.url == 'undefined') continue;
5237 if (typeof site.db_prefix == 'undefined') continue;
5238
5239 stagingSiteNames.push(escapeHtml(site.name));
5240
5241 let $template = $templateOrigin.clone();
5242 $template.removeClass('br_stg_tr_template');
5243 $template.attr('name', escapeHtml(site.name));
5244 $template.find('.stg-tr-name').text(escapeHtml(site.display_name));
5245 $template.find('.stg-tr-name').attr('tooltip', `<b>${originalText}</b>: ${escapeHtml(site.name)}<br><b>${displayText}</b>: ${escapeHtml(site.display_name)}`);
5246 if (escapeHtml(site.name) == escapeHtml(site.display_name)) {
5247 $template.find('.stg-tr-name').attr('tooltip', `<b>${displayText}</b>: ${escapeHtml(site.display_name)}`);
5248 }
5249 $template.find('.stg-tr-url-el').attr('href', escapeHtml(site.url));
5250 $template.find('.stg-tr-url-el').attr('tooltip', escapeHtml(site.url));
5251 $template.find('.stg-tr-url-el').text(escapeHtml(site.url));
5252 $template.find('.stg-tr-size').text(escapeHtml(site.total_size) + ' (' + escapeHtml(site.total_files) + ')');
5253 $template.find('.stg-tr-server').text((site.communication_secret == 'local') ? escapeHtml(localText) : 'TasteWP');
5254 $template.find('.stg-tr-server').attr('tooltip', prefixText + ': ' + escapeHtml(site.db_prefix));
5255 $template.find('.stg-tr-creation').text(escapeHtml(site.creation_date));
5256 $template.find('.stg-tr-expiration span').text(escapeHtml(site.expiration_time));
5257
5258 if (site.communication_secret != 'local') {
5259 $template.addClass('bmi-tastewp-staging-row');
5260 $template.attr('server', 'tastewp');
5261 $template.attr('token', escapeHtml(site.communication_secret));
5262 if (isNaN(parseInt(site.expiration_time)) || site.expiration_time == expiresNever) {
5263 $template.attr('expiration', escapeHtml(expiresNever));
5264 $template.find('.stg-tr-expiration span').text(escapeHtml(expiresNever));
5265 if (site.is_premium == '1') $template.find('.stg-tr-expiration span').addClass('stg-premium-stars');
5266 } else {
5267 $template.attr('expiration', parseInt(site.expiration_time));
5268 $template.find('.stg-tr-expiration span').text($.bmi.getExpirationTime(parseInt(site.expiration_time)));
5269 }
5270 } else {
5271 $template.attr('server', 'local');
5272 $template.find('.stg-tr-expiration span').text(escapeHtml(expiresNever));
5273 }
5274
5275 $template.hide();
5276
5277 $template.prependTo($tbody);
5278 amountOfSites++;
5279 }
5280
5281 if (amountOfSites == 0) {
5282 $tbody.html(emptyElementTR);
5283 }
5284
5285 if ($('#bmi-stg-subname-input').val() == '') {
5286 let j = 1;
5287 let defaultName = bmiVariables.stgStagingDefaultName;
5288 for (let i = 0; i < stagingSiteNames.length; ++i) {
5289 let name = stagingSiteNames[i];
5290 if (stagingSiteNames.includes(defaultName)) {
5291 defaultName = bmiVariables.stgStagingDefaultName + j;
5292 j++;
5293 } else break;
5294 }
5295 $('#bmi-stg-subname-input').val(defaultName);
5296 }
5297
5298 $.bmi.tooltips.init();
5299 $.bmi.showStagings();
5300
5301 },
5302
5303 // STATIC: Filling backups
5304 fillWithNewBackups: function(res, ongoing, cb = function() {}) {
5305 // let backupsList = {};
5306 let external = res.external;
5307 let local = res.local;
5308 let backups = {};
5309
5310 // Local backups
5311 for (let name in local) {
5312 let backup = local[name];
5313 backups[backup[7]] = backup;
5314 backups[backup[7]][10] = backup[9];
5315 backups[backup[7]][9] = backup[8];
5316 backups[backup[7]][8] = { local: true };
5317 }
5318 // Google Drive and FTP backups
5319 // for (let cloudName in external) {
5320 // for (let md5 in external[cloudName]) {
5321 // let backup = external[cloudName][md5];
5322 // for (let md51 in external[cloudName]) {
5323 // if (typeof backups[md51] == 'undefined') {
5324 // if (cloudName === 'FTP'){
5325 // backups[md51] = backup;
5326 // backups[md51][9] = backup[9];
5327 // if (typeof backups[md51][8] == 'undefined' || typeof backups[md51][8] != 'object') {
5328 // backups[md51][8] = {local: false, ftp: backup[8]}
5329 // } else if (typeof backups[md51][8] == 'object') {
5330 // backups[md51][8] = backup[8];
5331 // }
5332 // }else if(cloudName === 'gdrive'){
5333 // backups[md51] = backup;
5334 // backups[md51][9] = backup[9];
5335 // if (typeof backups[md51][8] == 'undefined' || typeof backups[md51][8] != 'object') {
5336 // backups[md51][8] = {local: false, gdrive: backup[8]}
5337 // } else if (typeof backups[md51][8] == 'object') {
5338 // backups[md51][8].gdrive = backup[8];
5339 // }
5340 // }
5341 // } else {
5342 // if (cloudName === 'FTP'){
5343 // backups[md51][8].ftp = backup[8];
5344 // }else if(cloudName === 'gdrive'){
5345 // backups[md51][8].gdrive = backup[8];
5346 // }
5347 // }
5348 // }
5349 // }
5350 // }
5351
5352 // Google Drive backups
5353 for (let drive in external) {
5354 for (let backupKey in external[drive]) {
5355 let backup = external[drive][backupKey];
5356 let md5 = backup[7];
5357 if (typeof backups[md5] == 'undefined') {
5358 backups[md5] = {...backup};
5359 backups[md5][9] = backup[9];
5360 if (typeof backups[md5][8] == 'undefined' || typeof backups[md5][8] != 'object') {
5361 backups[md5][8] = { local: false };
5362 if (typeof backups[md5][8][drive] == 'undefined') {
5363 backups[md5][8][drive] = backup[8];
5364 }
5365 } else if (typeof backups[md5][8] == 'object') {
5366 if (typeof backups[md5][8][drive] == 'undefined') {
5367 backups[md5][8][drive] = backup[8];
5368 }
5369 }
5370 } else {
5371 if (typeof backups[md5][8][drive] == 'undefined') {
5372 backups[md5][8][drive] = backup[8];
5373 }
5374 }
5375 }
5376 }
5377
5378 $('#bmi_restore_tbody').html('');
5379 $('.bmi-stg-dropdown-area-inner-scroll').html('');
5380
5381 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(bmiVariables.stgLoading);
5382 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text('---');
5383 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text('---');
5384
5385 let totalStagingPossibleBackups = 0;
5386 let lockedtxt = $('#bmi-manual-locked').text().trim();
5387 let backupsSorted = Object.keys(backups).sort(function(a, b) {
5388 let x = +new Date(backups[a][1].replace(/\-/g, '/'));
5389 let y = +new Date(backups[b][1].replace(/\-/g, '/'));
5390 return x - y;
5391 });
5392
5393 let i = 0;
5394 for (; i < backupsSorted.length; ++i) {
5395
5396 let storages = [];
5397 let index = backupsSorted[i];
5398 let md5 = backups[index][7];
5399 let domain = backups[index][9];
5400 let locked = backups[index][5] === 'locked' ? true : false;
5401
5402 let name = backups[index][0];
5403 let originalname = backups[index][0];
5404
5405 if (backups[index][0].includes('#%&')) {
5406 name = backups[index][0].split('#%&')[1];
5407 originalname = backups[index][0].split('#%&')[0];
5408 }
5409
5410 name = name.replace(/ /g, '');
5411 name = name.trim();
5412
5413 originalname = originalname.replace(/ /g, '');
5414 originalname = originalname.trim();
5415
5416 let isLocalBackup = true;
5417 let isGoogleDrive = false;
5418 let isFtp = false;
5419 let isDropbox = false;
5420 let isCloud = false;
5421 let isOneDrive = false;
5422 let isAWS = false;
5423 let isWasabi = false;
5424 let isBackupBliss = false;
5425 let isSftp = false;
5426 if (typeof backups[index][8] != 'undefined') {
5427 if (typeof backups[index][8] == 'object') {
5428 for (let storage in backups[index][8]) {
5429 if (storage == 'gdrive') {
5430 isGoogleDrive = backups[index][8][storage];
5431 } else if (storage == 'FTP') {
5432 isFtp = backups[index][8][storage];
5433 } else if (storage == 'dropbox') {
5434 isDropbox = backups[index][8][storage];
5435 } else if (storage == 'local') {
5436 isLocalBackup = backups[index][8][storage];
5437 } else if (storage == 'onedrive') {
5438 isOneDrive = backups[index][8][storage];
5439 } else if (storage == 'aws') {
5440 isAWS = backups[index][8][storage];
5441 } else if (storage == 'wasabi') {
5442 isWasabi = backups[index][8][storage];
5443 } else if (storage == 'backupbliss') {
5444 isBackupBliss = backups[index][8][storage];
5445 } else if (storage == 'sftp') {
5446 isSftp = backups[index][8][storage];
5447 }
5448 }
5449 }
5450 }
5451
5452 isCloud = isGoogleDrive || isFtp || isDropbox || isOneDrive || isBackupBliss || isSftp || isAWS || isWasabi;
5453
5454 let bwsc = '<b>' + $('#bmi-backup-created-on').text().trim() + '</b>';
5455 let bonc = '<b>' + $('#bmi-backup-original-name').text().trim() + '</b>';
5456 let bfnc = '<b>' + $('#bmi-backup-file-name').text().trim() + '</b>';
5457
5458 let id = 'bmi_br_backup_' + i;
5459 let $template = $('.br_tr_template').clone();
5460 $template.removeClass('br_tr_template');
5461 $template[0].style.display = 'none';
5462 $template.attr('md5', md5);
5463 if (isGoogleDrive) $template.attr('gdrive-id', isGoogleDrive);
5464
5465 if (isFtp) $template.attr('ftp-id', isFtp);
5466
5467 if (isDropbox) $template.attr('dropbox-id', isDropbox);
5468
5469 if (!isLocalBackup && isCloud) $template.attr('data-is-local', 'no');
5470 else $template.attr('data-is-local', 'yes');
5471
5472 if (isOneDrive) $template.attr('onedrive-id', isOneDrive);
5473
5474 if (isAWS) $template.attr('aws-id', isAWS);
5475
5476 if (isWasabi) $template.attr('wasabi-id', isWasabi);
5477 if (isBackupBliss) $template.attr('backupbliss-id', isBackupBliss);
5478
5479 if (isSftp) $template.attr('sftp-id', isSftp);
5480
5481 $template.find('.br_label').attr('for', id);
5482 $template.find('.br_checkbox').attr('id', id);
5483 $template.find('.bc-download-btn').attr('href', $('#BMI_BLOG_URL').text().trim() + '/?backup-migration=BMI_BACKUP&backup-id=' + name + '&sk=' + $('#BMI_SECRET_KEY').text().trim());
5484 $template.find('.bc-logs-btn').attr('href', $('#BMI_BLOG_URL').text().trim() + '/?backup-migration=BMI_BACKUP_LOGS&backup-id=' + name + '&sk=' + $('#BMI_SECRET_KEY').text().trim());
5485
5486 $template.find('.br_date').text(backups[index][1]);
5487 $template.find('.br_name').text(name);
5488 if (originalname == name) {
5489 $template.find('.br_name').attr('tooltip', (bonc + ' ' + escapeHtml(originalname) + '<br><br>' + bwsc + ' ' + escapeHtml(domain)));
5490 } else {
5491 $template.find('.br_name').attr('tooltip', (bonc + ' ' + escapeHtml(originalname) + '<br><br>' + bfnc + ' ' + escapeHtml(name) + '<br><br>' + bwsc + ' ' + escapeHtml(domain)));
5492 }
5493
5494 let readableBytesNotCut = $.bmi.bytesToHuman(backups[index][4]);
5495 let readableBytes = $.bmi.bytesToHuman(backups[index][4]);
5496 if (readableBytes.includes('MB') || readableBytes.includes('KB')) {
5497 readableBytes = $.bmi.bytesToHumanCut(backups[index][4]);
5498 }
5499
5500 $template.find('.br_size').text(readableBytes + ' (' + backups[index][2] + ')');
5501
5502 if (isGoogleDrive) {
5503 $template.find('.br_stroage').find('.strg-gdrive').addClass('img-green');
5504 storages.push('Google Drive');
5505 } else {
5506 $template.find('.br_stroage').find('.strg-gdrive').addClass('img-red');
5507 }
5508
5509 // if (isLocalBackup && isCloud) {
5510 // $template.find('.br_stroage').find('.strg-suc').addClass('img-green');
5511 // } else {
5512 // $template.find('.br_stroage').find('.strg-suc').addClass('img-orange');
5513 // }
5514
5515 if (isFtp) {
5516 $template.find('.br_stroage').find('.strg-ftp').addClass('img-green');
5517 storages.push('FTP');
5518 } else {
5519 $template.find('.br_stroage').find('.strg-ftp').addClass('img-red');
5520 }
5521
5522 if (isDropbox) {
5523 $template.find('.br_stroage').find('.strg-dropbox').addClass('img-green');
5524 storages.push('Dropbox');
5525 } else {
5526 $template.find('.br_stroage').find('.strg-dropbox').addClass('img-red');
5527 }
5528
5529 if (isOneDrive) {
5530 $template.find('.br_stroage').find('.strg-onedrive').addClass('img-green');
5531 storages.push('OneDrive');
5532 } else {
5533 $template.find('.br_stroage').find('.strg-onedrive').addClass('img-red');
5534 }
5535
5536 if (isAWS) {
5537 $template.find('.br_stroage').find('.strg-aws').addClass('img-green');
5538 storages.push('AWS S3');
5539 } else {
5540 $template.find('.br_stroage').find('.strg-aws').addClass('img-red');
5541 }
5542
5543 if (isWasabi) {
5544 $template.find('.br_stroage').find('.strg-wasabi').addClass('img-green');
5545 storages.push('Wasabi');
5546 } else {
5547 $template.find('.br_stroage').find('.strg-wasabi').addClass('img-red');
5548 }
5549 if (isBackupBliss) {
5550 $template.find('.br_stroage').find('.strg-backupbliss').addClass('img-green');
5551 storages.push('BackupBliss');
5552 } else {
5553 $template.find('.br_stroage').find('.strg-backupbliss').addClass('img-red');
5554 }
5555
5556 if (isSftp) {
5557 $template.find('.br_stroage').find('.strg-sftp').addClass('img-green');
5558 storages.push('SFTP');
5559 } else {
5560 $template.find('.br_stroage').find('.strg-sftp').addClass('img-red');
5561 }
5562
5563 if (isLocalBackup) {
5564 $template.find('.br_stroage').find('.strg-local').addClass('img-green');
5565 storages.push('Local Storage');
5566 for (let key in backups[index][10]) {
5567 if (backups[index][10].hasOwnProperty(key)) {
5568 if ($template.find('.br_stroage').find('.strg-' + key).length > 0 && $template.find('.br_stroage').find('.strg-' + key).hasClass('img-red')) {
5569 let storageElement = $template.find('.br_stroage').find('.strg-' + key);
5570 let storageElementTooltip = storageElement.attr('tooltip');
5571
5572 storageElement.addClass('can-be-manually-uploaded');
5573 storageElement.attr('tooltip', 'Upload To ' + storageElementTooltip);
5574 }
5575 }
5576 }
5577 } else {
5578 $template.find('.br_stroage').find('.strg-local').addClass('img-red');
5579 $template.find('.brow_subactions').hide();
5580 $template.find('.brow_lock').hide();
5581 }
5582
5583
5584 //$template.find('.br_stroage').find('.strg-suc').attr('tooltip', 'Backup is stored on: ' + storages.join(', ') + '.');
5585
5586 if (!$('#bmi-pro-storage-gdrive-toggle').is(':checked')) {
5587 $template.find('.br_stroage').addClass('bmi-gdrive-disabled');
5588 }
5589
5590 if (!$('#bmi-pro-storage-ftp-toggle').is(':checked')) {
5591 $template.find('.br_stroage').addClass('bmi-ftp-disabled');
5592 }
5593
5594 if (!$('#bmi-pro-storage-onedrive-toggle').is(':checked')) {
5595 $template.find('.br_stroage').addClass('bmi-onedrive-disabled');
5596 }
5597
5598 if ((''+backups[index][6]).trim().length == 0 || (backups[index][6]+'') == 'false') {
5599 $template.find('.bc-locked-btn').addClass('forever');
5600 $template.find('.bc-locked-btn').attr('tooltip', lockedtxt);
5601 locked = true;
5602 }
5603
5604 if (locked) {
5605 $template.find('.bc-unlocked-btn').hide();
5606 } else {
5607 $template.find('.bc-locked-btn').hide();
5608 }
5609
5610 isDirectLinkEnabled = $('[name="radioAccessViaLink"]:checked').val() === "true";
5611
5612 if (!isDirectLinkEnabled) {
5613 $template.find('.bc-url-btn svg')
5614 .css({
5615 cursor: "not-allowed",
5616 color: "red"
5617 })
5618 .addClass('disabled')
5619 .attr('tooltip', $('#direct-link-disabled-tooltip').text().trim());
5620 } else {
5621 $template.find('.bc-url-btn svg')
5622 .css({
5623 cursor: "pointer",
5624 color: "#B6B6B6"
5625 })
5626 .removeClass('disabled')
5627 .attr('tooltip', $('#direct-link-enabled-tooltip').text().trim());
5628 }
5629
5630 $template.prependTo('#bmi_restore_tbody');
5631
5632 $('.bmi-stg-drop-option.active').removeClass('active');
5633
5634 if (isLocalBackup) {
5635 let $stgOptionTemplate = $('.bmi-stg-option-template').clone();
5636 $stgOptionTemplate.removeClass('bmi-stg-option-template');
5637 $stgOptionTemplate[0].style.display = '';
5638 $stgOptionTemplate.attr('backup-name', name);
5639 $stgOptionTemplate.addClass('active');
5640
5641 $stgOptionTemplate.find('.bmi-stg-option-name').text(name);
5642 $stgOptionTemplate.find('.bmi-stg-option-date i').text(backups[index][1]);
5643 $stgOptionTemplate.find('.bmi-stg-option-size i').text(readableBytesNotCut);
5644
5645 $stgOptionTemplate.prependTo('.bmi-stg-dropdown-area-inner-scroll');
5646
5647 // if (!isLocalBackup && isGoogleDrive) { // Unreachable code
5648 // $stgOptionTemplate.attr('gdrive-id', isGoogleDrive);
5649 // $stgOptionTemplate.attr('data-is-local', 'no');
5650 // } else if (isLocalBackup) {
5651 // $stgOptionTemplate.attr('data-is-local', 'yes');
5652 // }
5653
5654 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(name);
5655 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text(backups[index][1]);
5656 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text(readableBytesNotCut);
5657
5658 $('#bmi-stg-current-backup-selected').val(name);
5659 totalStagingPossibleBackups++;
5660 }
5661
5662 // backups[name].push(name);
5663 // backupsList[id] = backups[name];
5664
5665 }
5666
5667 if ($('.bmi-stg-sel-box.bmi-active').attr('data-mode') == 'tastewp') {
5668 $('.bmi-stg-creation-box-local').hide(300);
5669 $('.bmi-stg-creation-box-tastewp').hide(300);
5670 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
5671 if ($('.bmi-stg-drop-option:not(.bmi-stg-option-template)').length > 0) {
5672 $('.bmi-stg-creation-box-tastewp').show(300);
5673 } else {
5674 $('.bmi-stg-creation-box-tastewp-empty').show(300);
5675 }
5676 } else {
5677 $('.bmi-stg-creation-box-local').show(300);
5678 $('.bmi-stg-creation-box-tastewp').hide(300);
5679 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
5680 }
5681
5682 if (i == 0) {
5683
5684 let empty = $('#bmi_restore_tbody').data('empty');
5685 $('#bmi_restore_tbody').html('<tr class="bmi-empty-text"><td class="center text-muted" colspan="100%">' + empty + '</td></tr>');
5686
5687 }
5688
5689 $.bmi.tooltips.init();
5690 $.bmi.showMoreBackups();
5691
5692 if ($('.bmi-stg-sel-box.bmi-active').data('mode') == 'tastewp') {
5693 $('.bmi-stg-creation-box-local').hide(300);
5694 $('.bmi-stg-creation-box-tastewp').hide(300);
5695 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
5696 if ($('#bmi_restore_tbody').find('tr:not(.bmi-empty-text)').length > 0) {
5697 $('.bmi-stg-creation-box-tastewp').show(300);
5698 } else {
5699 $('.bmi-stg-creation-box-tastewp-empty').show(300);
5700 }
5701 }
5702
5703 if (typeof ongoing != 'undefined') $.bmi.fillOnGoing(ongoing);
5704 cb();
5705
5706 },
5707
5708 // STATIC: Loading 10 more backups
5709 showMoreBackups: function(res) {
5710
5711 backups = {};
5712 let trs = $('#bmi_restore_tbody').find('tr:hidden');
5713
5714 for (let i = 0;
5715 (i < trs.length && i < 10); ++i) {
5716 setTimeout(function() {
5717
5718 $(trs[i]).show(300);
5719
5720 }, (i * 50));
5721 }
5722
5723 if ((trs.length - 10) <= 0) {
5724 $('#load-more-backups-wrp').hide(300);
5725 } else {
5726 $('#load-more-backups-wrp').show(300);
5727 }
5728
5729 },
5730
5731 // STATIC: Animate staging list show
5732 showStagings: function() {
5733
5734 backups = {};
5735 let trs = $('#stg-tbody-table').find('tr:hidden');
5736
5737 for (let i = 0; i < trs.length; ++i) {
5738 setTimeout(function() {
5739 $(trs[i]).show(300);
5740 }, (i * 50));
5741 }
5742
5743 },
5744
5745 setBackupProgressList: function(md5, type, tooltip = false) {
5746
5747 //let suc = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-suc');
5748 let warn = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-warn');
5749 let ong = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-ong');
5750 let wait = $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-wait');
5751
5752 //suc.hide();
5753 warn.hide();
5754 ong.hide();
5755 wait.hide();
5756
5757 // if (type == 'suc') {
5758 // suc.show();
5759 // if (tooltip) suc.tooltipster('content', tooltip);
5760 // }
5761 if (type == 'warn') {
5762 warn.show();
5763 if (tooltip) warn.tooltipster('content', tooltip);
5764 }
5765 if (type == 'ong') {
5766 ong.show();
5767 if (tooltip) ong.tooltipster('content', tooltip);
5768 }
5769 if (type == 'wait') {
5770 wait.show();
5771 if (tooltip) wait.tooltipster('content', tooltip);
5772 }
5773
5774 },
5775
5776 fillOnGoing: function(ongoing = false) {
5777
5778 if (ongoing && typeof ongoing.queue != 'undefined' && typeof ongoing.current_upload != 'undefined') {
5779
5780 for (let task in ongoing.queue) {
5781 let data = ongoing.queue[task];
5782 let md5 = data.md5;
5783 uploadType = task.split('_')[0];
5784 //Replaces existing img- class or add it with new
5785 $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-' + uploadType).attr('class', function(i, cls) {
5786 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-purple') : cls + ' img-purple';
5787 });
5788 $.bmi.setBackupProgressList(md5, 'wait');
5789 }
5790
5791 if (typeof ongoing.current_upload == 'object') {
5792 if (ongoing.current_upload.length == 0) {
5793
5794 if (ongoing_latest_token) {
5795 //$.bmi.reloadBackups();
5796 ongoing_failed = false;
5797 if (typeof ongoing.failed != 'undefined') {
5798 ongoing_failed = currentUploadType + "_" + ongoing_latest_token in ongoing.failed;
5799 }
5800
5801 $('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
5802 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, ongoing_failed ? 'img-red' : 'img-green') : cls + ongoing_failed ? ' img-red' : ' img-green';
5803 });
5804 $.bmi.setBackupProgressList(ongoing_latest_token, 'done');
5805 ongoing_latest_token = false;
5806 }
5807
5808 } else {
5809
5810 if (!ongoing_latest_token) {
5811 ongoing_latest_token = ongoing.current_upload.md5;
5812 }
5813
5814 if (ongoing_latest_token && ongoing_latest_token != ongoing.current_upload.md5) {
5815 //$.bmi.reloadBackups();
5816 ongoing_failed = false;
5817 if (typeof ongoing.failed != 'undefined') {
5818 ongoing_failed = currentUploadType + "_" + ongoing_latest_token in ongoing.failed;
5819 }
5820
5821 $('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
5822 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, ongoing_failed ? 'img-red' : 'img-green') : cls + ongoing_failed ? ' img-red' : ' img-green';
5823 });
5824 $.bmi.setBackupProgressList(ongoing_latest_token, 'done');
5825 ongoing_latest_token = ongoing.current_upload.md5;
5826 }
5827
5828 let progress = '0%';
5829 if (typeof ongoing.current_upload.progress != 'undefined') progress = ongoing.current_upload.progress;
5830
5831 //Detect type
5832 currentUploadType = ongoing.current_upload.task.split('_')[0];
5833 let message = 'Upload to ';
5834 let externalStorageType = 'Google Drive';
5835 switch (currentUploadType) {
5836 case 'ftp':
5837 externalStorageType = 'FTP';
5838 break;
5839 case 'dropbox':
5840 externalStorageType = 'Dropbox';
5841 break;
5842 case 'onedrive':
5843 externalStorageType = 'OneDrive';
5844 break;
5845 case 'aws':
5846 externalStorageType = 'Amazon S3';
5847 break;
5848 case 'wasabi':
5849 externalStorageType = 'Wasabi';
5850 break;
5851 case 'backupbliss':
5852 externalStorageType = 'BackupBliss';
5853 break;
5854 case 'sftp':
5855 externalStorageType = 'SFTP';
5856 break;
5857 }
5858
5859 //Replaces existing img- class or add it with new
5860 $('#bmi_restore_tbody').find('tr[md5="' + ongoing_latest_token + '"]').find('.strg-' + currentUploadType).attr('class', function(i, cls) {
5861 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-orange') : cls + ' img-orange';
5862 });
5863 message += externalStorageType;
5864 $.bmi.setBackupProgressList(ongoing.current_upload.md5, 'ong', message + ' in progress: ' + progress);
5865 }
5866 }
5867
5868 }
5869
5870 if (typeof ongoing.failed != 'undefined') {
5871
5872 for (let token in ongoing.failed) {
5873 data = token.split("_");
5874 md5 = data[1];
5875 uploadType = data[0];
5876
5877 if (!(ongoing.current_upload instanceof Array))
5878 if (ongoing.current_upload.md5 == md5) // Checks if the current upload task is of the same backup the failed task is
5879 return; //There's an ongoing upload for the same backup, so will not show any failures to show the upload status.
5880
5881 // if (typeof ongoing.queue[token] != 'undefined') {
5882 // md5 = ongoing.queue[token].md5;
5883 // }
5884
5885 // if (typeof ongoing.current_upload.task != 'undefined' && ongoing.current_upload.task == token) {
5886 // md5 = ongoing.current_upload.md5;
5887 // }
5888
5889
5890
5891 if (md5) {
5892 //Replaces existing img- class or add it with new
5893 $('#bmi_restore_tbody').find('tr[md5="' + md5 + '"]').find('.strg-' + uploadType).attr('class', function(i, cls) {
5894 return /\bimg-[^\s]+/.test(cls) ? cls.replace(/\bimg-[^\s]+/g, 'img-red') : cls + ' img-red';
5895 });
5896 let message = 'There was an error during upload to: ';
5897 if (uploadType == null || uploadType == 'gdrive') externalStorageType = 'Google Drive';
5898 if (uploadType == 'ftp') externalStorageType = 'FTP';
5899 if (uploadType == 'dropbox') externalStorageType = 'Dropbox';
5900 if (uploadType == 'onedrive') externalStorageType = 'OneDrive';
5901 if (uploadType == 'aws') externalStorageType = 'AWS';
5902 if (uploadType == 'wasabi') externalStorageType = 'Wasabi';
5903 if (uploadType == 'backupbliss') externalStorageType = 'BackupBliss';
5904 if (uploadType == 'sftp') externalStorageType = 'SFTP';
5905 message += externalStorageType;
5906
5907 $.bmi.setBackupProgressList(md5, 'warn', message);
5908 }
5909 }
5910
5911 }
5912
5913 },
5914
5915 adjustStorageIcons: function() {
5916 let es = ['gdrive', 'onedrive', 'sftp', 'ftp', 'dropbox', 'aws', 'wasabi'];
5917
5918 let activeEsCount = 2;
5919 for (let i = 0; i < es.length; ++i) {
5920 if ($('#bmi-pro-storage-' + es[i] + '-toggle').is(':checked')) {
5921 $('.strg-' + es[i]).show();
5922 activeEsCount++;
5923 } else {
5924 $('.strg-' + es[i]).hide();
5925 }
5926 }
5927 if ($('.storage-icons-container').length == 0) return;
5928 if (activeEsCount > 5 ) {
5929 $('.storage-icons-container').css('grid-template-rows', 'repeat(2, auto)');
5930 } else {
5931 $('.storage-icons-container').css('grid-template-rows', 'none');
5932 }
5933 },
5934
5935 // STATIC: Reload backups list
5936 reloadBackups: function(callagain = function() {}) {
5937
5938 $.bmi.getCurrentBackups(function(res) {
5939 $.bmi.fillWithNewBackups(res.backups, res.backups.ongoing, function() {
5940 callagain();
5941
5942 $('#backups-select-all').prop('checked', false);
5943 $('.del-all-btn-wrp').hide(300);
5944 });
5945 });
5946
5947 },
5948
5949 // STATIC: Reload staging list
5950 reloadStaging: function(callagain = function() {}) {
5951
5952 $.bmi.getCurrentStaging(function(res) {
5953 $.bmi.fillWithNewStagings(res.sites, function() {
5954 callagain();
5955 });
5956 });
5957
5958 },
5959
5960 // STATIC: Hides all dropdowns
5961 hideAllLists: function() {
5962
5963 let opens = $('.dropdown-open');
5964 if (opens.length <= 0) return;
5965
5966 for (let i = 0; i < opens.length; ++i) {
5967
5968 let open = $(opens[i]);
5969 if (open.hasClass('ignored-open')) {
5970
5971 open.removeClass('ignored-open');
5972
5973 } else {
5974
5975 open.hide(300);
5976 open.removeClass('dropdown-open');
5977 $(open[0].closest('.bmi-dropdown')).removeClass('active');
5978
5979 }
5980
5981 }
5982
5983 },
5984
5985 // Sets and option
5986 setOption: function($dropdown, $option = null, value = false) {
5987
5988 let $optlist = $dropdown.find('.dropdown-options');
5989 if ($dropdown.attr('data-optioner')) {
5990 $optlist = $('.optioner-' + $dropdown.attr('data-optioner'));
5991 }
5992
5993 $optlist.find('.active-option').removeClass('active-option');
5994 if (value !== false) {
5995 $option = $optlist.find('.dropdown-option[data-value="' + value + '"]');
5996 }
5997
5998 if ($($option).hasClass('active-option')) return;
5999
6000 $dropdown.find('.dropdown-title').text($option.text());
6001 $dropdown.attr('data-selected', $option.data('value'));
6002 $option.addClass('active-option');
6003
6004 if (value === false) {
6005 $dropdown.change();
6006 }
6007
6008 $.bmi.hideAllLists();
6009
6010 },
6011
6012 // STATIC: Prepare file frmo text
6013 prepareFile: function(filename, text) {
6014
6015 let element = document.createElement('a');
6016 element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
6017 element.setAttribute('download', filename);
6018
6019 element.style.display = 'none';
6020 document.body.appendChild(element);
6021
6022 element.click();
6023 document.body.removeChild(element);
6024
6025 $.bmi.alert('success', $('#bmi-download-should-start').text(), 3000);
6026
6027 },
6028
6029 // Utility to escape html
6030 escapeHtml: function(text)
6031 {
6032 return escapeHtml(text)
6033 }
6034
6035 }
6036 });
6037 });
6038 jQuery(document).ready(function($) {
6039
6040 $('.collapser .header').on('click', function(e) {
6041
6042 e.preventDefault();
6043 let el = $(this).parent('.collapser')[0];
6044 $.bmi.collapsers.toggle(el);
6045
6046 });
6047
6048 $('.bmi_will_collapse').on('change', function(e) {
6049
6050 e.preventDefault();
6051 let doNotShow = false;
6052 if (this.getAttribute('type') == 'radio') {
6053 if (this.getAttribute('value') == 'false') {
6054 doNotShow = true;
6055 }
6056 }
6057
6058 $child = $(this).data('if-checked');
6059 if (this.checked === true && !doNotShow) {
6060 $('#' + $child).show(300);
6061 } else $('#' + $child).hide(300);
6062
6063 });
6064
6065 $('.collapser-openner').on('click', function(e) {
6066
6067 e.preventDefault();
6068 let group = 'configuration';
6069 let el = $(this).data('el');
6070 if ($(this).data('group')) {
6071 group = $(this).data('group');
6072 }
6073
6074 $.bmi.collapsers.open(el, group);
6075
6076 setTimeout(function() {
6077 $([document.documentElement, document.body]).animate({
6078 scrollTop: $(el).offset().top - 50 + 'px'
6079 }, 300);
6080 }, 300);
6081
6082 });
6083
6084 (function() {
6085
6086 let $triggers = $('.bmi_will_collapse');
6087 for (let i = 0; i < $triggers.length; ++i) {
6088
6089 let doNotShow = false;
6090 $trigger = $triggers[i];
6091 $child = $($trigger).data('if-checked');
6092
6093 if ($trigger.getAttribute('type') == 'radio') {
6094 if ($trigger.getAttribute('value') == 'false') {
6095 doNotShow = true;
6096 }
6097 }
6098
6099 if ($trigger.checked === true) {
6100 $('#' + $child).show();
6101 } else $('#' + $child).hide();
6102
6103 }
6104
6105 })();
6106
6107 });jQuery(document).ready(function($) {
6108
6109 var firsttime = true;
6110 var initialized = false;
6111 $.bmi.crons = function() {
6112
6113 if (initialized === true) return;
6114 else initialized = true;
6115
6116 if (location.href.includes("crons=true") && location.href.includes("page=backup-migration")){
6117 setTimeout(function(){
6118 $('#i-backup-cron').click();
6119 },300);
6120 }
6121
6122 function getCronResult(settings, done) {
6123 $.bmi.ajax('calculate-cron', settings).then(function(res) {
6124
6125 if (res.status == 'success') {
6126 done(res);
6127 } else {
6128 done(false);
6129 $.bmi._msg(res);
6130 }
6131
6132 }).catch(function(error) {
6133
6134 done(false);
6135 console.log(error);
6136
6137 });
6138 }
6139
6140 function settings_changed() {
6141
6142 let settings = {
6143 type: $('[data-id="cron-period"]').attr('data-selected'),
6144 day: $('[data-id="cron-day"]').attr('data-selected'),
6145 week: $('[data-id="cron-week"]').attr('data-selected'),
6146 hour: $('[data-id="cron-hour"]').attr('data-selected'),
6147 minute: $('[data-id="cron-minute"]').attr('data-selected'),
6148 keep: $('[data-id="cron-keep-backups"]').attr('data-selected'),
6149 enabled: ((!$('#cron-btn-toggle')[0].checked === true) ? true : false)
6150 }
6151
6152 getCronResult(settings, function(res) {
6153
6154 if (res.status === 'success' && res !== false) {
6155
6156 $('.cron-time-server').tooltipster('option', 'interactive', false);
6157 $('.cron-time-server').tooltipster('option', 'contentAsHTML', true);
6158
6159 if ($('#cron-btn-toggle')[0].checked === true) res.data = '---';
6160 $('.cron-time-server').tooltipster('content', '<b>' + $('#bmi-next-cron').text() + '</b>' + res.data + '<br>' + '<b>' + $('#bmi-current-time').text() + '</b>' + res.currdata);
6161
6162 if (!firsttime)
6163 $.bmi.alert('success', $('#bmi-cron-updated').text(), 1500);
6164 else firsttime = false;
6165
6166 } else {
6167
6168 if (!firsttime)
6169 $.bmi.alert('error', $('#bmi-cron-updated-fail').text(), 2500);
6170 else firsttime = false;
6171
6172 }
6173
6174 });
6175
6176 }
6177
6178 $('#bmi').on('change', '[data-id="cron-period"]', function(e) {
6179
6180 settings_changed();
6181 adjust_text(e.target.getAttribute('data-selected'));
6182
6183 });
6184
6185 $('#bmi').on('change', '[data-id="cron-day"]', function(e) {
6186
6187 let val = e.target.getAttribute('data-selected');
6188 settings_changed();
6189
6190 });
6191 $('#bmi').on('change', '[data-id="cron-week"]', function(e) {
6192
6193 let val = e.target.getAttribute('data-selected');
6194 settings_changed();
6195
6196 });
6197
6198 $('#bmi').on('change', '[data-id="cron-hour"]', function(e) {
6199
6200 let val = e.target.getAttribute('data-selected');
6201 settings_changed();
6202
6203 });
6204
6205 $('#bmi').on('change', '[data-id="cron-minute"]', function(e) {
6206
6207 let val = e.target.getAttribute('data-selected');
6208 settings_changed();
6209
6210 });
6211
6212 $('#bmi').on('change', '[data-id="cron-keep-backups"]', function(e) {
6213
6214 let val = e.target.getAttribute('data-selected');
6215 settings_changed();
6216
6217 });
6218
6219 $('#i-backup-cron').on('click', function() {
6220
6221 if ($('.cron-backups').find('.turned-off').is(':visible')) {
6222
6223 $('#cron-btn-toggle').prop('checked', false);
6224 $('.cron-backups').removeClass('disabled');
6225 settings_changed();
6226
6227 $('.cron-backups').find('.turned-on').css({
6228 opacity: 0
6229 });
6230 $('.cron-backups').find('.turned-off').css({
6231 opacity: 0
6232 });
6233 $('.cron-backups').find('.turned-on').show();
6234 $('.cron-backups').find('.turned-on').css({
6235 opacity: 1
6236 });
6237 setTimeout(function() {
6238 $('.cron-backups').find('.turned-off').hide();
6239 }, 300);
6240 }
6241
6242 });
6243
6244 $('#cron-btn-toggle').on('change', function() {
6245
6246 if (!this.checked) {
6247 $('.cron-backups').removeClass('disabled');
6248 } else {
6249 $('.cron-backups').addClass('disabled');
6250 }
6251
6252 settings_changed();
6253
6254 });
6255
6256 function adjust_text(val) {
6257 if (val == 'month') {
6258
6259 $('.cron-the').show();
6260 $('[data-id="cron-day"]').show();
6261 $('[data-id="cron-week"]').hide();
6262 $('#cron-on-word').show();
6263
6264 } else if (val == 'week') {
6265
6266 $('.cron-the').hide();
6267 $('[data-id="cron-day"]').hide();
6268 $('[data-id="cron-week"]').show();
6269 $('#cron-on-word').show();
6270
6271 } else {
6272
6273 $('.cron-the').hide();
6274 $('[data-id="cron-day"]').hide();
6275 $('[data-id="cron-week"]').hide();
6276 $('#cron-on-word').hide();
6277
6278 }
6279 }
6280
6281 (function() {
6282
6283 adjust_text($('[data-id="cron-period"]')[0].getAttribute('data-selected'));
6284 settings_changed();
6285
6286 })();
6287
6288 }
6289
6290 });jQuery(document).ready(function($) {
6291
6292 $.bmi.dropdowns = {
6293 init: function() {
6294 let $r = $('#bmi');
6295
6296 function scanSelects() {
6297
6298 let selects = $r.find('select');
6299 for (let i = 0; i < selects.length; ++i) {
6300
6301 let select = selects[i];
6302 handleSelect(select);
6303
6304 }
6305
6306 }
6307
6308 function createDropdown(title = '---') {
6309
6310 let dropdown = $('.dropdown-template').clone();
6311 dropdown.removeClass('dropdown-template');
6312 dropdown.find('.dropdown-title').text(title);
6313
6314 return dropdown;
6315
6316 }
6317
6318 function createOption(value = 'null', text = '---') {
6319
6320 let option = $('.option-template').clone();
6321 option.removeClass('option-template');
6322 option.attr('data-value', value);
6323 option.text(text);
6324
6325 return option;
6326
6327 }
6328
6329 function longestOption(options) {
6330
6331 let longest = '';
6332 for (let i = 0; i < options.length; ++i) {
6333
6334 let len = options[i].innerText;
6335 if (longest.length < len.length) longest = len;
6336
6337 }
6338
6339 return longest;
6340
6341 }
6342
6343 function handleSelect(select) {
6344
6345 if (select.style.display != 'none') {
6346
6347 select.style.display = 'none';
6348 let $select = $(select);
6349 let options = $select.find('option');
6350 let longest = longestOption(options);
6351 let dropdown = createDropdown(longest);
6352 dropdown = handleOptions(dropdown, options);
6353
6354 let def = options[0].value;
6355 let parent = select.getAttribute('data-parent');
6356 let classes = select.getAttribute('data-classes');
6357 let isHidden = select.getAttribute('data-hide') === 'true' ? true : false;
6358 if ($select.attr('data-def')) def = $select.attr('data-def');
6359
6360 $(dropdown).attr('data-id', select.id);
6361 $(dropdown).attr('class', ((classes != null ? classes : '') + ' bmi-dropdown').trim());
6362 $(dropdown).insertBefore($select);
6363
6364 if (parent !== null) {
6365 let num = parseInt(Math.random() * (987654321 - 123456789) + 123456789);
6366 $(dropdown).attr('data-optioner', num);
6367 $(dropdown).find('.dropdown-options').attr('data-oparent', num);
6368 $(dropdown).find('.dropdown-options').addClass('optioner-' + num);
6369 $(dropdown).find('.dropdown-options').appendTo(parent);
6370 $(parent).css({ position: 'relative' });
6371 }
6372
6373 let clone = $(dropdown).clone();
6374 if (!clone) return;
6375
6376 clone[0].style.visibility = 'hidden';
6377 $r.append(clone);
6378 let width = clone.width();
6379 clone.remove();
6380
6381 $.bmi.setOption($(dropdown), null, def);
6382 let fixedWidth = $select.attr('data-width');
6383 if (fixedWidth) width = parseInt(fixedWidth);
6384 $(dropdown).find('.dropdown-title')[0].style.minWidth = (width + 10) + 'px';
6385 let readonly = $select.attr('data-readonly');
6386 if (readonly == 'true') {
6387 $(dropdown).find('.dropdown-title')[0].style.pointerEvents = 'none';
6388 $(dropdown).closest('.bmi-dropdown')[0].style.background = '#f1f1f1';
6389 }
6390 if (isHidden) $(dropdown).hide();
6391
6392 }
6393
6394 }
6395
6396 function handleOptions(dropdown, options) {
6397
6398 let list = $(dropdown).find('.dropdown-options');
6399 if ($(dropdown).attr('data-optioner')) {
6400 list = $('.optioner-' + $(dropdown).attr('data-optioner'));
6401 }
6402
6403 for (let i = 0; i < options.length; ++i) {
6404
6405 let option = options[i];
6406 list.append(createOption(option.value, option.innerText));
6407
6408 }
6409
6410 return dropdown;
6411
6412 }
6413
6414 function toggleDropdown($dropdown) {
6415
6416 let $list = $dropdown.find('.dropdown-options');
6417 if ($dropdown.attr('data-optioner')) {
6418 $list = $('.optioner-' + $dropdown.attr('data-optioner'));
6419 $list.css({
6420 position: 'absolute'
6421 });
6422
6423 let width = $dropdown[0].offsetWidth;
6424 let left = $dropdown[0].offsetLeft + 240;
6425 let top = $dropdown[0].offsetTop + $dropdown[0].offsetHeight + 5;
6426 $list.css({
6427 maxWidth: width + 'px',
6428 minWidth: width + 'px',
6429 top: top + 'px',
6430 left: left + 'px'
6431 });
6432 }
6433
6434 if ($list.hasClass('dropdown-open')) {
6435
6436 $dropdown.removeClass('active');
6437 $list.removeClass('dropdown-open');
6438 $list.hide(300);
6439
6440 } else {
6441
6442 $dropdown.addClass('active');
6443 $list.addClass('dropdown-open');
6444 $list.addClass('ignored-open');
6445 $list.show(300);
6446
6447 }
6448
6449 }
6450
6451 // Initlaization
6452 scanSelects();
6453
6454 // Init crons after init of drop
6455 $.bmi.crons();
6456
6457 // Events
6458 $r.on('click', function(e) {
6459
6460 if (!($(e.target).hasClass('bmi-dropdown') || e.target.closest('.bmi-dropdown'))) $.bmi.hideAllLists();
6461
6462 });
6463
6464 $r.on('click', '.dropdown-title', function(e) {
6465
6466 $dropdown = $(e.target.closest('.bmi-dropdown'));
6467 toggleDropdown($dropdown);
6468 $.bmi.hideAllLists();
6469
6470 });
6471
6472 $r.on('click', '.dropdown-option', function(e) {
6473
6474 if ($(e.target.closest('.dropdown-options')).attr('data-oparent')) {
6475 $dropdown = $('.bmi-dropdown[data-optioner="' + $(e.target.closest('.dropdown-options')).attr('data-oparent') + '"]');
6476 $option = $(e.target);
6477 $.bmi.setOption($dropdown, $option);
6478 } else {
6479 $dropdown = $(e.target.closest('.bmi-dropdown'));
6480 $option = $(e.target);
6481 $.bmi.setOption($dropdown, $option);
6482 }
6483
6484 });
6485 }
6486 }
6487
6488 });
6489 jQuery(document).ready(function ($) {
6490 var backups = {};
6491 var bacupisDone = false,
6492 progressIsDone = false;
6493 var timeouter,
6494 curdivs = 0,
6495 iprogres;
6496 var stopBackgroundBackup = false;
6497 let totalSize = 0,
6498 totalExcluded = 0,
6499 totalSizesI = 0;
6500 let current_restore = null;
6501 let current_last = null;
6502 let latest_delete = null;
6503 let latest_delete_cloud = null;
6504 let backupOnGoing = false;
6505 let restoreOnGoing = false;
6506 let ongoing_interval = 10000;
6507 let triggeredByIntv = false;
6508 let backgroundBackup = false;
6509 let backgroundName = null;
6510 let headers_for_middleware = false;
6511 let cli_quickmigration = false;
6512 let restoreCLI = false;
6513 let autoLog = {};
6514 let latestLines = [];
6515 let lineTimeoutAppend = null;
6516 let isLineAppendTimeoutRunning = false;
6517 let latestStep = "";
6518 let currentGDriveIdRestoration = null;
6519 let restoreStartTime = null;
6520 let downloadBackupFinished = false;
6521 window.bmi = {};
6522
6523 function isChecked(id) {
6524 $cb = $("#" + id).is(":checked");
6525 if ($cb === true) return "true";
6526 else return "false";
6527 }
6528
6529 function getSelectedSize() {
6530 size = 0;
6531
6532 if ($("#files-group-backup").is(":checked")) {
6533 if ($("#files-group-plugins").is(":checked")) {
6534 size += parseInt(
6535 $('label[for="files-group-plugins"]').find(".value").attr("bytes"),
6536 );
6537 }
6538
6539 if ($("#files-group-uploads").is(":checked")) {
6540 size += parseInt(
6541 $('label[for="files-group-uploads"]').find(".value").attr("bytes"),
6542 );
6543 }
6544
6545 if ($("#files-group-themes").is(":checked")) {
6546 size += parseInt(
6547 $('label[for="files-group-themes"]').find(".value").attr("bytes"),
6548 );
6549 }
6550
6551 if ($("#files-group-other-contents").is(":checked")) {
6552 size += parseInt(
6553 $('label[for="files-group-other-contents"]')
6554 .find(".value")
6555 .attr("bytes"),
6556 );
6557 }
6558
6559 if ($("#files-group-wp-install").is(":checked")) {
6560 size += parseInt(
6561 $('label[for="files-group-wp-install"]').find(".value").attr("bytes"),
6562 );
6563 }
6564 }
6565
6566 if ($("#database-group-backup").is(":checked")) {
6567 size += parseInt(
6568 $('label[for="database-group-backup"]').find(".value").attr("bytes"),
6569 );
6570 }
6571
6572 return size;
6573 }
6574
6575 function locationSet(location, res) {
6576 totalSizesI += 1;
6577 if (location != "database") totalSize += parseInt(res["bytes"]);
6578 totalExcluded += parseInt(res["excluded"]);
6579 if (res["bytes"] <= 10000) {
6580 $("#bmi-scan-" + location)
6581 .find(".value")
6582 .text("(0 MB)");
6583 } else {
6584 let megas = (parseInt(res["bytes"]) / 1024 / 1024).toFixed(2);
6585 $("#bmi-scan-" + location)
6586 .find(".value")
6587 .text("(" + megas + " MB)");
6588 }
6589 $("#bmi-scan-" + location)
6590 .find(".value")
6591 .attr("bytes", parseInt(res["bytes"]));
6592 $("#bmi-scan-" + location)
6593 .find(".value")
6594 .attr("excluded", parseInt(res["excluded"]));
6595 if (totalSizesI == 6) doSomethingWithTotal();
6596 }
6597
6598 function scanAndSet(location) {
6599 return new Promise(function (resolve) {
6600 $.bmi
6601 .ajax("scan-directory", {
6602 folder: location,
6603 })
6604 .then(function (res) {
6605 setTimeout(resolve, 150);
6606 locationSet(location, res);
6607 })
6608 .catch(function (error) {
6609 setTimeout(resolve, 150);
6610 locationSet(location, {
6611 bytes: "0",
6612 excluded: "0",
6613 readable: "0 B",
6614 });
6615 });
6616 });
6617 }
6618
6619 function resetSpinners(resources, withTotal = false) {
6620 if (withTotal) {
6621 $("#bmi-scan-total")
6622 .find(".value")
6623 .html('(<div class="spinner-loader"></div>)');
6624 $("#bmi-scan-total").find(".value").removeAttr("bytes");
6625 }
6626
6627 for (let i = 0; i < resources.length; i++) {
6628 $("#bmi-scan-" + resources[i])
6629 .find(".value")
6630 .html('(<div class="spinner-loader"></div>)');
6631 $("#bmi-scan-" + resources[i])
6632 .find(".value")
6633 .removeAttr("bytes");
6634 }
6635
6636 $("#esta-exclude").html('(<div class="spinner-loader"></div>)');
6637 $("#esta-exclude-total").html('(<div class="spinner-loader"></div>)');
6638 $("#esta-size-for").html('(<div class="spinner-loader"></div>)');
6639
6640 let preloader_divs = "";
6641 for (let i = 0; i < 12; ++i) preloader_divs += "<div></div>";
6642 $(".spinner-loader").html(preloader_divs).addClass("lds-spinner");
6643 }
6644
6645 async function scanDirectories() {
6646 totalSizesI = 0;
6647 totalSize = 0;
6648 totalExcluded = 0;
6649
6650 let resources = [
6651 "plugins",
6652 "uploads",
6653 "themes",
6654 "contents_others",
6655 "wordpress",
6656 "database",
6657 ];
6658
6659 await resetSpinners(resources, true);
6660 for (let i = 0; i < resources.length; i++) {
6661 await scanAndSet(resources[i]);
6662 }
6663 }
6664
6665 function doSomethingWithTotal() {
6666 $("#bmi-scan-total")
6667 .find(".value")
6668 .text("(" + $.bmi.bytesToHuman(totalSize) + ")");
6669 $("#bmi-scan-total").find(".value").attr("bytes", parseInt(totalSize));
6670 updateEsta();
6671 }
6672
6673 // Live log
6674 $("#live-log-toggle").on("click", function () {
6675 if ($(".expanded-logs").length > 0) {
6676 $(this).text($(this).data("hide"));
6677 } else $(this).text($(this).data("show"));
6678
6679 $("#live-log-wrapper").toggleClass("expanded-logs");
6680 });
6681
6682 async function stopBackgroundProcess() {
6683 await forceBackupToStop(true);
6684 window.onbeforeunload = null;
6685 window.location.reload();
6686 }
6687
6688 // Stop backup
6689 $("#backup-stop").on("click", function () {
6690 if (isChecked("experimental-hard-timeout") === "true") {
6691 $.bmi.modal("freeze-loading-modal").open();
6692
6693 stopBackgroundBackup = true;
6694 return;
6695 }
6696
6697 $.bmi
6698 .ajax("stop-backup", {})
6699 .then(function (res) {
6700 if (res.status == "success")
6701 $.bmi.alert("success", $("#bmi-abort-soon").text(), 3000);
6702 else $.bmi._msg(res);
6703 })
6704 .catch(function (error) {
6705 //
6706 });
6707 });
6708
6709 // Is backup running
6710 function isRunning(mute = false, cb = function () { }) {
6711 $.bmi
6712 .ajax("is-running-backup", {})
6713 .then(function (res) {
6714 if (typeof res.ongoing != "undefined") {
6715 $.bmi.fillOnGoing(res.ongoing);
6716 if (typeof res.ongoing.queue == 'object' || typeof res.ongoing.current_upload == 'object') {
6717 if (!(res.ongoing.queue instanceof Array) || !(res.ongoing.current_upload instanceof Array)) {
6718 if (typeof (globalBMIKeepAlive) == "function") {
6719 globalBMIKeepAlive();
6720 }
6721 }
6722 else {
6723 $.bmi.ajax('check-not-uploaded-backups').then(function(res) {
6724 // console.warn('Checking for not uploaded backups...');
6725 // console.log(res);
6726 });
6727 }
6728 }
6729 }
6730
6731 if (res.status == "success") cb(false);
6732 else {
6733 if (mute == true) cb(true);
6734 else {
6735 $.bmi.modal("freeze-loading-modal").close();
6736 $.bmi._msg(res);
6737 }
6738 }
6739 })
6740 .catch(function (error) {
6741 cb(false);
6742 $.bmi.modal("freeze-loading-modal").close();
6743 //
6744 });
6745 }
6746
6747 function forceBackupToStop(withNotice = false) {
6748 return new Promise((resolve) => {
6749 $.bmi
6750 .ajax("force-backup-to-stop")
6751 .then(function (res) {
6752 if (withNotice) {
6753 $.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
6754 }
6755
6756 return resolve(true);
6757 })
6758 .catch(function (error) {
6759 if (withNotice) {
6760 $.bmi.alert("error", $("#failed-to-stop").text(), 6000);
6761 }
6762
6763 return resolve(false);
6764 });
6765 });
6766 }
6767
6768 $("#bmi-force-backup-to-stop").on("click", function (e) {
6769 e.preventDefault();
6770 $.bmi.alert("info", $("#bmi-force-stop-in-progress").text(), 3000);
6771 $.bmi
6772 .ajax("force-backup-to-stop")
6773 .then(function (res) {
6774 $.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
6775 })
6776 .catch(function (error) {
6777 $.bmi.alert("error", $("#failed-to-stop").text(), 6000);
6778 });
6779 });
6780
6781 $("#bmi-force-restore-to-stop").on("click", function (e) {
6782 e.preventDefault();
6783 $.bmi.alert("info", $("#bmi-force-stop-in-progress").text(), 3000);
6784 $.bmi
6785 .ajax("force-restore-to-stop")
6786 .then(function (res) {
6787 $.bmi.alert("success", $("#bmi-force-stop-success").text(), 6000);
6788 })
6789 .catch(function (error) {
6790 $.bmi.alert("error", $("#failed-to-stop").text(), 6000);
6791 });
6792 });
6793
6794 $("#bmi_restore_tbody").on("click", ".bc-unlocked-btn", function (e) {
6795 e.preventDefault();
6796 let $el = e.target;
6797 let name = $el.closest("tr").querySelector(".br_name").innerText.trim();
6798
6799 $.bmi
6800 .ajax("lock-backup", {
6801 filename: name,
6802 })
6803 .then(function (res) {
6804 if (res.status == "success") {
6805 $.bmi.alert("success", $("#bmi-lock-success").text(), 6000);
6806 $($el.closest("tr").querySelector(".bc-unlocked-btn")).hide();
6807 $($el.closest("tr").querySelector(".bc-locked-btn")).show();
6808 } else {
6809 $.bmi.alert("error", $("#bmi-lock-error").text(), 8000);
6810 console.error("BMI Backend error: ", res);
6811 }
6812 })
6813 .catch(function (error) {
6814 //
6815 });
6816 });
6817
6818 function updateEsta() {
6819 let pluginsSize = parseInt(
6820 $('label[for="files-group-plugins"]').find(".value").attr("bytes"),
6821 );
6822 let uploadsSize = parseInt(
6823 $('label[for="files-group-uploads"]').find(".value").attr("bytes"),
6824 );
6825 let themesSize = parseInt(
6826 $('label[for="files-group-themes"]').find(".value").attr("bytes"),
6827 );
6828 let otherSize = parseInt(
6829 $('label[for="files-group-other-contents"]').find(".value").attr("bytes"),
6830 );
6831 let wpSize = parseInt(
6832 $('label[for="files-group-wp-install"]').find(".value").attr("bytes"),
6833 );
6834 let dbSize = parseInt(
6835 $('label[for="database-group-backup"]').find(".value").attr("bytes"),
6836 );
6837
6838 let pluginsExcluded = parseInt(
6839 $('label[for="files-group-plugins"]').find(".value").attr("excluded"),
6840 );
6841 let uploadsExcluded = parseInt(
6842 $('label[for="files-group-uploads"]').find(".value").attr("excluded"),
6843 );
6844 let themesExcluded = parseInt(
6845 $('label[for="files-group-themes"]').find(".value").attr("excluded"),
6846 );
6847 let otherExcluded = parseInt(
6848 $('label[for="files-group-other-contents"]')
6849 .find(".value")
6850 .attr("excluded"),
6851 );
6852 let wpExcluded = parseInt(
6853 $('label[for="files-group-wp-install"]').find(".value").attr("excluded"),
6854 );
6855 let dbExcluded = parseInt(
6856 $('label[for="database-group-backup"]').find(".value").attr("excluded"),
6857 );
6858
6859 let databaseExcludedSizeInBytes = 0;
6860 let excludedSize =
6861 pluginsExcluded +
6862 uploadsExcluded +
6863 themesExcluded +
6864 otherExcluded +
6865 wpExcluded +
6866 dbExcluded;
6867 let totalSize =
6868 excludedSize +
6869 pluginsSize +
6870 uploadsSize +
6871 themesSize +
6872 otherSize +
6873 wpSize +
6874 dbSize;
6875
6876 if ($("#files-group-backup").is(":checked")) {
6877 if (!$("#files-group-plugins").is(":checked"))
6878 excludedSize += pluginsSize;
6879 if (!$("#files-group-uploads").is(":checked"))
6880 excludedSize += uploadsSize;
6881 if (!$("#files-group-themes").is(":checked")) excludedSize += themesSize;
6882 if (!$("#files-group-other-contents").is(":checked"))
6883 excludedSize += otherSize;
6884 if (!$("#files-group-wp-install").is(":checked")) excludedSize += wpSize;
6885 } else {
6886 excludedSize +=
6887 pluginsSize + uploadsSize + themesSize + otherSize + wpSize;
6888 }
6889
6890 if ($("#database-group-backup").is(":checked")) {
6891 if (
6892 $("#bmi_total_size_excluded").length > 0 &&
6893 $("#bmi-pro-db-tables-exclusion").is(":checked")
6894 ) {
6895 databaseExcludedSizeInBytes =
6896 parseFloat($("#bmi_total_size_excluded").text().split("/")[0]) *
6897 1024 *
6898 1024;
6899 if (!isNaN(databaseExcludedSizeInBytes))
6900 excludedSize += databaseExcludedSizeInBytes;
6901 }
6902 } else excludedSize += dbSize;
6903
6904 $("#esta-exclude").text($.bmi.bytesToHuman(excludedSize));
6905 $("#esta-exclude-total").text($.bmi.bytesToHuman(totalSize));
6906 $("#esta-size-for").text($.bmi.bytesToHuman(totalSize - excludedSize));
6907 }
6908
6909 $("#files-group-backup").on("change", updateEsta);
6910 $("#database-group-backup").on("change", updateEsta);
6911 $(".basic-file-exlusion").on("change", 'input[type="checkbox"]', updateEsta);
6912
6913 $("#bmi_restore_tbody").on("click", ".bc-locked-btn", function (e) {
6914 e.preventDefault();
6915 if ($(e.target).hasClass("forever")) return;
6916 let $el = e.target;
6917 let name = $el.closest("tr").querySelector(".br_name").innerText.trim();
6918
6919 $.bmi
6920 .ajax("unlock-backup", {
6921 filename: name,
6922 })
6923 .then(function (res) {
6924 if (res.status == "success") {
6925 $.bmi.alert("success", $("#bmi-unlock-success").text(), 3000);
6926 $($el.closest("tr").querySelector(".bc-locked-btn")).hide();
6927 $($el.closest("tr").querySelector(".bc-unlocked-btn")).show();
6928 } else {
6929 $.bmi.alert("error", $("#bmi-unlock-error").text(), 3000);
6930 console.error("BMI Backend error: ", res);
6931 }
6932 })
6933 .catch(function (error) {
6934 //
6935 });
6936 });
6937
6938 $("#bmi_restore_tbody").on("click", ".bc-url-btn", function (e) {
6939 e.preventDefault();
6940 let $el = e.target;
6941 if ($el.closest("svg").classList.contains("disabled")) return;
6942
6943 let url = $el
6944 .closest("tr")
6945 .querySelector(".bc-download-btn")
6946 .getAttribute("href");
6947 $.bmi.clipboard(url, bmiVariables.urlCopies);
6948 });
6949
6950 $("#stg-tbody-table").on("click", ".bc-stg-url-btn", function (e) {
6951 let $el = e.target;
6952
6953 let url = $el
6954 .closest("tr")
6955 .querySelector(".stg-tr-url-el")
6956 .getAttribute("href");
6957 $.bmi.clipboard(url, bmiVariables.urlCopies);
6958 });
6959
6960 $("#bmi_restore_tbody").on("click", ".bc-remove-btn", function (e) {
6961
6962 let $el = e.target;
6963 let name = $el.closest('tr').querySelector('.br_name').innerText.trim();
6964 let isCloud = Array.from($el.closest('tr').querySelectorAll('[class*="strg-"]')).filter(el => !el.classList.contains('strg-local')).some(el => el.classList.contains('img-green'));
6965
6966 let notOnLocalTr = $($el.closest('tr')).data('is-local') == 'no' ? true : false;
6967
6968 latest_delete = [name];
6969
6970
6971 latest_delete_cloud = {};
6972 latest_delete_cloud[name] = {
6973 md5: $el.closest("tr").getAttribute("md5"),
6974 gid: $el.closest("tr").getAttribute("gdrive-id"),
6975 };
6976
6977 $("#delete-confirm-modal").find(".text2").hide();
6978 $("#delete-confirm-modal").find(".text3").hide();
6979 $("#delete-confirm-modal").find(".text4").hide();
6980 $("#delete-confirm-modal").find(".text1").show();
6981
6982 if (isCloud) $(".bmi-cloud-removal").show();
6983 else $(".bmi-cloud-removal").hide();
6984
6985 $("#remove-cloud-backup-as-well")[0].checked = false;
6986
6987 if (isCloud && notOnLocalTr) {
6988 $("#remove-cloud-backup-as-well")[0].checked = true;
6989 $(".bmi-cloud-removal").hide();
6990 $("#delete-confirm-modal").find(".text1").hide();
6991 $("#delete-confirm-modal").find(".text2").hide();
6992 $("#delete-confirm-modal").find(".text3").hide();
6993 $("#delete-confirm-modal").find(".text4").show();
6994 $(".del-more-than-one").hide();
6995 $(".del-only-one").show();
6996 }
6997
6998 $.bmi.modal("delete-confirm-modal").open();
6999 });
7000
7001 $("#sure_delete").on("click", function (e) {
7002 e.preventDefault();
7003
7004 $.bmi.modal("delete-confirm-modal").close();
7005 $.bmi.modal("freeze-loading-modal").open();
7006
7007 $.bmi
7008 .ajax("delete-backup", {
7009 filenames: latest_delete,
7010 deleteCloud: $("#remove-cloud-backup-as-well")[0].checked
7011 ? "yes"
7012 : "no",
7013 cloudDetails: latest_delete_cloud,
7014 })
7015 .then(function (res) {
7016 latest_delete = null;
7017 latest_delete_cloud = null;
7018 $("#remove-cloud-backup-as-well")[0].checked = false;
7019 $.bmi.modal("freeze-loading-modal").close();
7020 if (res.status == "success") {
7021 $.bmi.reloadBackups(() => {
7022 $.bmi.reloadBackups(); // twice on purpose
7023 });
7024 $.bmi.alert("success", $("#bmi-remove-success").text(), 3000);
7025 } else {
7026 $.bmi.alert("warning", $("#bmi-remove-error").text(), 3000);
7027 console.error("BMI Backend error: ", res);
7028 }
7029 })
7030 .catch(function (error) {
7031 $.bmi.modal("freeze-loading-modal").close();
7032 });
7033 });
7034
7035 $(".bmi-send-troubleshooting-logs").on("click", function (e) {
7036 e.preventDefault();
7037
7038 $.bmi.alert("info", $("#bmi-support-send-start").text(), 6000);
7039 $(".bmi-send-troubleshooting-logs").addClass("disabled");
7040
7041 let errorVisible = false;
7042 let stagingErrorVisible = false;
7043
7044 if ($("#error-modal").is(":visible")) {
7045 $.bmi.modal("error-modal").close();
7046 errorVisible = true;
7047 stagingErrorVisible = false;
7048 }
7049
7050 if ($("#staging-error-modal").is(":visible")) {
7051 $.bmi.modal("staging-error-modal").close();
7052 errorVisible = false;
7053 stagingErrorVisible = true;
7054 }
7055
7056 $.bmi.modal("freeze-loading-modal").open();
7057
7058 let logsSource = $("#after-logs-sent-modal").attr("data-error-source");
7059 $.bmi
7060 .ajax("send-troubleshooting-logs", { source: logsSource })
7061 .then(function (res) {
7062 $.bmi.modal("freeze-loading-modal").close();
7063 if (res.status == "success") {
7064 $("#bmi-support-code-generated").text(res.code);
7065 setTimeout(function () {
7066 $.bmi.alert("success", $("#bmi-support-send-success").text(), 4000);
7067 $.bmi.modal("after-logs-sent-modal").open();
7068 }, 300);
7069 } else {
7070 $.bmi.alert("error", $("#bmi-support-send-fail").text(), 6000);
7071 if (errorVisible) $.bmi.modal("error-modal").open();
7072 if (stagingErrorVisible) $.bmi.modal("staging-error-modal").open();
7073 }
7074
7075 $(".bmi-send-troubleshooting-logs").removeClass("disabled");
7076 })
7077 .catch(function (error) {
7078 $.bmi.modal("freeze-loading-modal").close();
7079 $.bmi.alert("success", $("#bmi-support-send-fail").text(), 6000);
7080 alert(
7081 "Something went wrong on your browser side and we could not send your logs to support team.",
7082 );
7083 $(".bmi-send-troubleshooting-logs").removeClass("disabled");
7084 if (errorVisible) $.bmi.modal("error-modal").open();
7085 if (stagingErrorVisible) $.bmi.modal("staging-error-modal").open();
7086 });
7087 });
7088
7089 $("#share-logs-allowed").on("click", function (e) {
7090 e.preventDefault();
7091 let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
7092 $.bmi.alert("success", $("#bmi-share-logs-thank-you").text(), 3000);
7093 $.bmi.modal("logs-sharing-ask-modal").close();
7094 $.bmi.modal("freeze-loading-modal").open();
7095
7096 shareLogsStatus("set_yes", function () {
7097 setTimeout(function () {
7098 handleAfterDesicionMadeSharing(newModalName);
7099 }, 300);
7100 });
7101 });
7102
7103 $("#share-logs-not-allowed").on("click", function (e) {
7104 e.preventDefault();
7105 let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
7106
7107 $.bmi.modal("logs-sharing-ask-modal").close();
7108 $.bmi.modal("freeze-loading-modal").open();
7109
7110 shareLogsStatus("set_no", function () {
7111 setTimeout(function () {
7112 handleAfterDesicionMadeSharing(newModalName);
7113 }, 300);
7114 });
7115 });
7116
7117 $("#ignore-share-log-request-for-now").on("click", function (e) {
7118 e.preventDefault();
7119 $.bmi.modal("logs-sharing-ask-modal").close();
7120 let newModalName = $("#logs-sharing-ask-modal").attr("data-destination");
7121 handleAfterDesicionMadeSharing(newModalName);
7122 });
7123
7124 function handleAfterDesicionMadeSharing(modalName) {
7125 if (modalName == "backup-prenotice") {
7126 runPrenoticeBeforeBackup();
7127 } else {
7128 $.bmi.modal("freeze-loading-modal").close();
7129 setTimeout(function () {
7130 $.bmi.modal(modalName).open();
7131 }, 300);
7132 }
7133 }
7134
7135 function isAllowedToShareLogs(cb = function () { }) {
7136 shareLogsStatus("is_allowed", function (res) {
7137 cb(res);
7138 });
7139 }
7140
7141 function shareLogsStatus(type, cb = function () { }) {
7142 // Res to: is_allowed (res.result)
7143 // allowed
7144 // not-allowed
7145 // ask
7146 //
7147 // For set_yes, set_no there is no response only status success or fail
7148
7149 if (type == "is_allowed") cb("not-allowed");
7150 else cb();
7151
7152 // DISABLED:
7153 // $.bmi.ajax('log-sharing-details', { question: type }).then(function(res) {
7154 //
7155 // if (res.status == 'success') {
7156 //
7157 // if (type == 'is_allowed') {
7158 //
7159 // if (typeof res.result != 'undefined') {
7160 // if (['allowed', 'not-allowed', 'ask'].includes(res.result)) {
7161 // cb(res.result);
7162 // } else {
7163 // cb('not-allowed')
7164 // }
7165 // }
7166 //
7167 // } else {
7168 //
7169 // cb();
7170 //
7171 // }
7172 //
7173 // } else {
7174 //
7175 // cb('error');
7176 //
7177 // }
7178 //
7179 // }).catch(function(error) {
7180 //
7181 // 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.');
7182 // cb('error');
7183 //
7184 // });
7185 }
7186
7187 $("#add-exclusion-rule").on("click", function (e) {
7188 e.preventDefault();
7189 let $template = $(".exclusion_template").clone();
7190 $template[0].classList.remove("exclusion_template");
7191 $template[0].style.display = "none";
7192
7193 $("#bmi_exclusion_rules").append($template);
7194 $template.show(300);
7195 });
7196
7197 $("#bmi_exclusion_rules").on("click", ".kill-exclusion-rule", function (e) {
7198 e.preventDefault();
7199 $el = e.target;
7200 $parent = $el.closest(".exclude-row");
7201
7202 if ($parent) {
7203 $($parent).hide(300);
7204 setTimeout(function () {
7205 $parent.remove();
7206 }, 320);
7207 }
7208 });
7209
7210 function isOnlyDB() {
7211 if (!(isChecked("database-group-backup") === "true")) {
7212 return false;
7213 } else {
7214 if (isChecked("database-group-backup") === "true") {
7215 if (!(isChecked("files-group-backup") === "true")) {
7216 return true;
7217 } else {
7218 if (
7219 isChecked("files-group-plugins") === "true" ||
7220 isChecked("files-group-themes") === "true" ||
7221 isChecked("files-group-uploads") === "true" ||
7222 isChecked("files-group-wp-install") === "true" ||
7223 isChecked("files-group-other-contents") === "true"
7224 ) {
7225 return false;
7226 }
7227 }
7228 } else return false;
7229 }
7230 }
7231
7232 function isOnlyFiles() {
7233 if (isChecked("database-group-backup") === "true") {
7234 return false;
7235 } else {
7236 if (!(isChecked("database-group-backup") === "true")) {
7237 if (
7238 isChecked("files-group-backup") === "true" &&
7239 (isChecked("files-group-plugins") === "true" ||
7240 isChecked("files-group-themes") === "true" ||
7241 isChecked("files-group-uploads") === "true" ||
7242 isChecked("files-group-wp-install") === "true" ||
7243 isChecked("files-group-other-contents") === "true")
7244 ) {
7245 return true;
7246 } else return false;
7247 } else return false;
7248 }
7249 }
7250
7251 function isOnlyPart() {
7252 if (
7253 isChecked("database-group-backup") === "true" &&
7254 isChecked("files-group-backup") === "true" &&
7255 isChecked("files-group-plugins") === "true" &&
7256 isChecked("files-group-themes") === "true" &&
7257 isChecked("files-group-uploads") === "true" &&
7258 isChecked("files-group-other-contents") === "true"
7259 ) {
7260 return false;
7261 }
7262
7263 return true;
7264 }
7265
7266 function isFilesOrDb() {
7267 if (isChecked("database-group-backup") === "true") {
7268 return true;
7269 } else {
7270 if (isChecked("files-group-backup") === "true") {
7271 if (
7272 isChecked("files-group-plugins") === "true" ||
7273 isChecked("files-group-themes") === "true" ||
7274 isChecked("files-group-uploads") === "true" ||
7275 isChecked("files-group-wp-install") === "true" ||
7276 isChecked("files-group-other-contents") === "true"
7277 ) {
7278 return true;
7279 }
7280 }
7281 }
7282
7283 return false;
7284 }
7285
7286 function setupPrenotice() {
7287 $("#prenotice-modal .prenotice").hide();
7288
7289 $("#prenotice-size").text($("#esta-size-for").text());
7290 $(".prenotic-3").show();
7291
7292 if (isOnlyDB()) {
7293 $(".prenotic-6").show();
7294 }
7295
7296 if (isOnlyFiles()) {
7297 $(".prenotic-5").show();
7298 }
7299
7300 if (isOnlyPart()) {
7301 $(".prenotic-4").show();
7302 }
7303
7304 $("#prenotice-modal .prenotice:visible").css({
7305 background: "",
7306 });
7307 let prenotices = $("#prenotice-modal .prenotice"),
7308 sorted = [];
7309 for (let i = 0; i < prenotices.length; ++i) {
7310 if (!(prenotices[i].style.display === "none")) {
7311 sorted.push(prenotices[i]);
7312 }
7313 }
7314 for (let i = 0; i < sorted.length; i += 2) {
7315 sorted[i].style.background = "#f8f8f8";
7316 }
7317 }
7318
7319 // Backup creation
7320 $("#i-backup-creator, .i-backup-creator-trigger").on("click", function () {
7321 if (isNaN(getSelectedSize())) {
7322 $.bmi.alert("warning", $("#bmi-preb-processing").text(), 3000);
7323 return;
7324 }
7325
7326 if (
7327 getSelectedSize() / 1024 / 1024 >
7328 parseInt($("#BMI_REV").text().trim()) * 1000
7329 ) {
7330 return handleBfs();
7331 }
7332
7333 if (!isFilesOrDb()) {
7334 $.bmi.alert("warning", $("#bmi-no-selected").text(), 3000);
7335 return;
7336 }
7337
7338 $.bmi.modal("freeze-loading-modal").open();
7339 setTimeout(function () {
7340 shareLogsStatus("is_allowed", function (res) {
7341 if (res === "ask") {
7342 $.bmi.modal("freeze-loading-modal").close();
7343 $("#logs-sharing-ask-modal").attr(
7344 "data-destination",
7345 "backup-prenotice",
7346 );
7347 setTimeout(function () {
7348 $.bmi.modal("logs-sharing-ask-modal").open();
7349 }, 300);
7350 } else {
7351 runPrenoticeBeforeBackup();
7352 }
7353 });
7354 }, 300);
7355 });
7356
7357 function runPrenoticeBeforeBackup() {
7358 $.bmi.modal("freeze-loading-modal").open();
7359
7360 setupPrenotice();
7361 isRunning(false, function () {
7362 setTimeout(function () {
7363 $.bmi.modal("freeze-loading-modal").close();
7364
7365 setTimeout(function () {
7366 $.bmi.modal("prenotice-modal").open();
7367 }, 300);
7368 }, 300);
7369 });
7370 }
7371
7372 $("#BFFSIN").on("change", function (e) {
7373 let i = parseInt(this.value);
7374 if (isNaN(i)) {
7375 this.value = 1;
7376 return;
7377 } else {
7378 if (i > 9999) {
7379 this.value = 9999;
7380 return;
7381 }
7382
7383 if (i <= 0) {
7384 this.value = 1;
7385 return;
7386 }
7387
7388 this.value = i;
7389 }
7390 });
7391
7392 function saveBtnEventHandler(e = false, type = false, cb = () => { }) {
7393 if (e) e.preventDefault();
7394
7395 let data = {};
7396 let save = type;
7397 if (e != false && type == false)
7398 save = $(this.closest(".save-action")).data("save");
7399
7400 if (!save) return;
7401
7402 if (save == "save-storage") {
7403 data["directory"] = $("#bmi_path_storage_default").val();
7404 data["access"] =
7405 $('[name="radioAccessViaLink"]:checked').val() === "true"
7406 ? "true"
7407 : "false";
7408 data["gdrive"] =
7409 $("#bmi-pro-storage-gdrive-toggle").is(":checked") === true
7410 ? "true"
7411 : "false";
7412
7413 if ($("#bmip-googledrive-path").length > 0) {
7414 data["gdrivedirname"] = $("#bmip-googledrive-path").val().trim();
7415 } else {
7416 data["gdrivedirname"] = "BACKUP_MIGRATION_BACKUPS";
7417 }
7418
7419 // onedrive
7420 data["onedrive"] =
7421 $("#bmi-pro-storage-onedrive-toggle").is(":checked") === true
7422 ? "true"
7423 : "false";
7424
7425 data["sftp"] = $("#bmi-pro-storage-sftp-toggle").is(":checked") === true ? 'true' : 'false';
7426
7427
7428 // storage ftp
7429 data['ftp'] = $('#bmi-pro-storage-ftp-toggle').is(':checked') === true ? 'true' : 'false';
7430 if ($('#bmip-ftp-host-ip').length > 0) {
7431 data['ftphostip'] = $('#bmip-ftp-host-ip').val().trim();
7432 } else {
7433 data['ftphostip'] = '';
7434 }
7435
7436 data['dropbox'] = $('#bmi-pro-storage-dropbox-toggle').is(':checked') === true ? 'true' : 'false';
7437
7438 if ($('#bmip-ftp-user-name').length > 0) {
7439 data['ftphostusername'] = $('#bmip-ftp-user-name').val().trim();
7440 } else {
7441 data['ftphostusername'] = '';
7442 }
7443
7444 if ($('#bmip-ftp-password').length > 0) {
7445 data['ftppassword'] = $('#bmip-ftp-password').val().trim();
7446 } else {
7447 data['ftppassword'] = '';
7448 }
7449 if ($('#bmip-ftp-backup-dir').length > 0) {
7450 data['ftpdir'] = $('#bmip-ftp-backup-dir').val().trim();
7451 } else {
7452 data['ftpdir'] = '';
7453 }
7454
7455 if ($('#bmip-ftp-host-port').length > 0) {
7456 data['ftpport'] = $('#bmip-ftp-host-port').val().trim();
7457 } else {
7458 data['ftpport'] = '';
7459 }
7460
7461 data['aws'] = $('#bmi-pro-storage-aws-toggle').is(':checked') === true ? 'true' : 'false';
7462 data['wasabi'] = $('#bmi-pro-storage-wasabi-toggle').is(':checked') === true ? 'true' : 'false';
7463
7464
7465 } else if (save == 'save-file-config') {
7466
7467
7468 // Main groups
7469 data["database_group"] = isChecked("database-group-backup");
7470 data["files_group"] = isChecked("files-group-backup");
7471
7472 // Additional database exclusion rules
7473 if ($("#bmi-pro-db-tables-exclusion").length > 0) {
7474 data["db-exclude-tables-group"] = isChecked(
7475 "bmi-pro-db-tables-exclusion",
7476 );
7477 data["db-excluded-tables"] = ["empty"];
7478
7479 let excludedTables = $(".bmi_pro_tables_display").find("input:checked");
7480 if (excludedTables.length > 0) {
7481 data["db-excluded-tables"] = [];
7482 for (let i = 0; i < excludedTables.length; ++i) {
7483 data["db-excluded-tables"].push(excludedTables[i].value);
7484 }
7485 } else {
7486 data["db-exclude-tables-group"] = "false";
7487 }
7488 } else {
7489 data["db-exclude-tables-group"] = "false";
7490 data["db-excluded-tables"] = ["empty"];
7491 }
7492
7493 // Subgroup of files
7494 data["files-group-plugins"] = isChecked("files-group-plugins");
7495 data["files-group-uploads"] = isChecked("files-group-uploads");
7496 data["files-group-themes"] = isChecked("files-group-themes");
7497 data["files-group-other-contents"] = isChecked(
7498 "files-group-other-contents",
7499 );
7500 data["files-group-wp-install"] = isChecked("files-group-wp-install");
7501
7502 // Subgroup of filters of files
7503 data["files_by_filters"] = isChecked("files_by_filters");
7504 data["ex_b_fs"] = isChecked("ex_b_fs");
7505 data["BFFSIN"] = $("#BFFSIN").val() ? $("#BFFSIN").val() : "1";
7506 data["ex_b_names"] = isChecked("ex_b_names");
7507 data["ex_b_fpaths"] = isChecked("ex_b_fpaths");
7508 data["ex_b_dpaths"] = isChecked("ex_b_dpaths");
7509
7510 // Make dynamic
7511 let rules = [];
7512 let rows = $("#bmi_exclusion_rules").find(".exclude-row");
7513 for (let i = 0; i < rows.length; ++i) {
7514 let row = $(rows[i]);
7515 let txt = row.find(".exclusion_txt").val();
7516 let pos =
7517 row
7518 .find(".exclusion_position")
7519 .find(".bmi-dropdown")
7520 .data("selected") + "";
7521 let whr =
7522 row.find(".exclusion_where").find(".bmi-dropdown").data("selected") +
7523 "";
7524
7525 rules.push({
7526 txt: txt,
7527 pos: pos,
7528 whr: whr,
7529 });
7530 }
7531
7532 // Continue data
7533 data["dynamic-names"] = rules;
7534 data["dynamic-fpaths-names"] = $("#dynamic-fpaths-names")
7535 .val()
7536 .split("\n");
7537 data["dynamic-dpaths-names"] = $("#dynamic-dpaths-names")
7538 .val()
7539 .split("\n");
7540
7541 data["smart-exclusion-enabled"] = isChecked("smart-exclusion-enabled"); // SMART:EXCLUSION:ENABLED
7542 data["smart-exclusion-cache"] = isChecked("smart-exclusion-cache"); // SMART:EXCLUSION:CACHE
7543 data["smart-exclusion-deactivated-plugins"] = isChecked("smart-exclusion-deactivated-plugins"); // SMART:EXCLUSION:DPLUGINS
7544 data["smart-exclusion-debug-logs"] = isChecked("smart-exclusion-debug-logs"); // SMART:EXCLUSION:DLOGS
7545 data["smart-exclusion-non-used-themes"] = isChecked("smart-exclusion-non-used-themes"); // SMART:EXCLUSION:NUTHEMES
7546 data["smart-exclusion-post-revisions"] = isChecked("smart-exclusion-post-revisions"); // SMART:EXCLUSION:PREVISIONS
7547
7548 } else if (save == "store-config") {
7549 data["name"] = $("#backup_filename").val().trim();
7550 if ($("input[name=backup_type_extension]")) {
7551 data["extension"] = $(
7552 "input[name=backup_type_extension]:checked",
7553 ).val();
7554 } else {
7555 data["extension"] = ".zip";
7556 }
7557 } else if (save == "save-other-options") {
7558 data["email"] = $("#email-for-notices").val().trim();
7559 data["email_title"] = $("#email-title-for-notices").val().trim();
7560 data["schedule_issues"] = isChecked("scheduled-issues");
7561 data["experiment_timeout"] = isChecked("experimental-timeout");
7562 data["experimental_hard_timeout"] = isChecked(
7563 "experimental-hard-timeout",
7564 );
7565 data["php_cli_disable_others"] = isChecked("cli-disable-others");
7566 data["php_cli_manual_path"] = $("#cli-manual-path").val().trim();
7567 data["download_technique"] = isChecked("download-technique");
7568 data["uninstall_config"] = isChecked("uninstalling-configs");
7569 data["uninstall_backups"] = isChecked("uninstalling-backups");
7570 data["normal_timeout"] = isChecked("normal-timeout");
7571 data["backup_success_notify"] = isChecked("backup-success-notify");
7572 data["restore_success_notify"] = isChecked("restore-success-notify");
7573 data["backup_failed_notify"] = isChecked("backup-failed-notify");
7574 data["restore_failed_notify"] = isChecked("restore-failed-notify");
7575 data["generate_debug_code"] =
7576 isChecked("generate-debug-code-yes") == "true" &&
7577 isChecked("generate-debug-code-no") !== "true";
7578 data["add_logs_email"] =
7579 isChecked("add-logs-email-yes") == "true" &&
7580 isChecked("add-logs-email-no") !== "true";
7581 data["db_queries_amount"] = $("#db_queries_amount").val().trim(); // OTHER:DB:QUERIES
7582 data["db_search_replace_max"] = $("#db_search_replace_max").val().trim(); // OTHER:DB:SEARCHREPLACE:MAX
7583 data["file_limit_extraction_max"] = $("#file_limit_extraction_max")
7584 .val()
7585 .trim(); // OTHER:FILE:EXTRACT:MAX
7586 data["bmi-restore-splitting"] = isChecked("bmi-restore-splitting"); // OTHER:RESTORE:SPLITTING
7587 data["bmi-db-v3-restore-engine"] = isChecked("bmi-db-v3-restore-engine"); // OTHER:RESTORE:SPLITTING
7588 data["remove-assets-before-restore"] = isChecked(
7589 "remove-assets-before-restore",
7590 ); // OTHER:RESTORE:BEFORE:CLEANUP
7591 data["hide-promotional-bmi-banners"] = isChecked(
7592 "hide-promotional-bmi-banners",
7593 ); // OTHER:PROMOTIONAL:DISPLAY
7594 data["bmi-db-single-file-backup"] = isChecked(
7595 "bmi-db-single-file-backup",
7596 ); // OTHER:BACKUP:DB:SINGLE:FILE
7597 data["bmi-db-batching-backup"] = isChecked("bmi-db-batching-backup"); // OTHER:BACKUP:DB:BATCHING
7598 data["bmi-disable-space-check-function"] = isChecked(
7599 "bmi-do-not-check-free-space-backup",
7600 ); // OTHER:BACKUP:SPACE:CHECKING
7601 data["before_update_trigger"] = ((!$('#before-updates-switch')[0].checked === true) ? true : false) // OTHER:TRIGGER:BEFORE:UPDATES
7602
7603 } else return;
7604
7605 $.bmi
7606 .ajax(save, data)
7607 .then(function (res) {
7608 if (res.status == "success") {
7609 if (res.errors <= 0) {
7610 if ($('.save-brw-details').length > 0) $('.save-brw-details').triggerHandler('click');
7611 if (e) $.bmi.alert("success", $("#bmi-save-success").text(), 3000);
7612 $.bmi.reloadBackups();
7613 $.bmi.adjustStorageIcons();
7614
7615 if (
7616 typeof window.saveStorageWithoutClose != "undefined" &&
7617 window.saveStorageWithoutClose === true
7618 ) {
7619 window.saveStorageWithoutClose = false;
7620 } else {
7621 if (e) $.bmi.collapsers.closeAll();
7622 }
7623 } else {
7624 if (e) $.bmi.alert("warning", $("#bmi-save-issues").text(), 3000);
7625 }
7626 } else {
7627 $.bmi._msg(res);
7628 }
7629 })
7630 .catch(function (error) {
7631 //
7632 })
7633 .finally(() => {
7634 if (save == "save-file-config") {
7635 scanDirectories();
7636 }
7637
7638 cb();
7639 });
7640 }
7641
7642 $(".save-btn").on("click", saveBtnEventHandler);
7643
7644 $(".close-chapters").on("click", function (e) {
7645 e.preventDefault();
7646 $.bmi.collapsers.closeAll();
7647 });
7648
7649 $("#rescan-for-backups").on("click", function (e) {
7650 e.preventDefault();
7651 $.bmi.reloadBackups();
7652 });
7653
7654 // Storage things
7655 $(".storage-checkbox").on("click", function () {
7656 let target = $(this).attr("data-toggle");
7657 let tab = $($(this)[0].closest(".tab2-item"));
7658
7659 if ($(this).is(":checked")) {
7660 $("#" + target).show(300);
7661 tab.addClass("activeList");
7662 } else {
7663 $("#" + target).hide(300);
7664 tab.removeClass("activeList");
7665 }
7666 });
7667
7668 function httpGet(theUrl, callback = function () { }) {
7669 let isHttps = window.location.protocol.includes("https");
7670 let isUrlHttps = theUrl.includes("https");
7671 if (isUrlHttps) theUrl = theUrl.slice(5);
7672 else theUrl = theUrl.slice(4);
7673 if (isHttps) theUrl = "https" + theUrl;
7674 else theUrl = "http" + theUrl;
7675
7676 try {
7677 if (window.XMLHttpRequest) {
7678 xmlhttp = new XMLHttpRequest();
7679 } else {
7680 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
7681 }
7682
7683 xmlhttp.onloadend = function () {
7684 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
7685 callback(xmlhttp.responseText);
7686 } else callback(false);
7687 };
7688
7689 xmlhttp.open("GET", theUrl);
7690 xmlhttp.send();
7691 } catch (e) {
7692 callback(false);
7693 }
7694 }
7695
7696 function goThroughEndCodes() {
7697 let stepSet = false;
7698 let endCodeFound = false;
7699
7700 for (let i = 0; i < latestLines.length; ++i) {
7701 let line = latestLines[i];
7702
7703 if (
7704 endCodeFound == false &&
7705 line &&
7706 line.trim().includes("[END-CODE]") &&
7707 (backgroundBackup === true ||
7708 restoreCLI === true ||
7709 cli_quickmigration === true)
7710 ) {
7711 endCodeFound = true;
7712 let code_line = line;
7713 if (cli_quickmigration === true) {
7714 if (code_line.includes("205")) {
7715 cli_quickmigration = false;
7716 $("#restore-progress-modal .progress-active-bar")[0].style.width =
7717 0 + "%";
7718 $("#restore-progress-modal .progress-percentage")[0].style.left =
7719 0 + "%";
7720 $("#restore-progress-modal .progress-percentage")[0].innerText =
7721 0 + "%";
7722 letsRestore(true);
7723 } else if (code_line.includes("206")){
7724 $.bmi.modal("restore-progress-modal").close();
7725 $('#restore-progress-modal .title').text($('#bmi-restore-progress-modal-title').text());
7726 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-restore-progress-modal-warning').text());
7727 setTimeout(function () {
7728 if (!downloadBackupFinished) { // Avoid double success message
7729 $.bmi.alert('success', $('#bmi-backup-downloaded').text(), 3000);
7730 downloadBackupFinished = true;
7731 }
7732 }, 300);
7733 } else {
7734 restoreFailed();
7735 }
7736 } else if (restoreCLI === true) {
7737 if (code_line.includes("001")) {
7738 setTimeout(function () {
7739 restoreCLISuccess();
7740 }, 1000);
7741 } else {
7742 restoreFailed();
7743 }
7744 restoreOnGoing = false;
7745 restoreCLI = false;
7746 } else {
7747 backgroundBackup = false;
7748 if (code_line.includes("001")) {
7749 bacupisDone = true;
7750 progressIsDone = true;
7751 clearTimeout(timeouter);
7752 backupOnGoing = false;
7753 completedBackup({ filename: backgroundName }, true);
7754 } else if (code_line.includes("002")) {
7755 // $.bmi.modal('backup-progress-modal').close();
7756 backupError(1);
7757 } else if (code_line.includes("003")) {
7758 $.bmi.modal("backup-progress-modal").close();
7759 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
7760 } else if (code_line.includes("100")) {
7761 $.bmi.modal("backup-progress-modal").close();
7762 handleBfs();
7763 $.bmi.modal("backup-progress-modal").close();
7764 } else {
7765 backupError(2);
7766 }
7767 }
7768 }
7769 }
7770
7771 return endCodeFound;
7772 }
7773
7774 function showNextLine(el) {
7775 let delayedDisplayTime = 40;
7776 let line = latestLines[curdivs];
7777 let div = document.createElement("DIV");
7778
7779 let color = "";
7780 if (typeof line == "undefined" || !line) return;
7781 if (line.substr(0, 6) == "[INFO]") color = "blue";
7782 else if (line.substr(0, 9) == "[SUCCESS]") color = "green";
7783 else if (line.substr(0, 6) == "[WARN]") color = "orange";
7784 else if (line.substr(0, 7) == "[ERROR]") color = "red";
7785 else if (line.substr(0, 10) == "[END-CODE]") color = "hide_so_much";
7786 else if (line.substr(0, 9) == "[VERBOSE]") color = "hide_verbose";
7787 else if (line.substr(0, 6) == "[STEP]") {
7788 div.classList.add("bold");
7789 div.classList.add("step");
7790 } else {
7791 if (line && line.trim().length > 0 && line[0] != "[") {
7792 curdivs--;
7793 }
7794 }
7795
7796 let mostRecentStep = "";
7797 if (line.substr(0, 6) == "[STEP]") mostRecentStep = line.slice(29);
7798
7799 if (color.length > 0) div.classList.add(color);
7800
7801 div.style.display = "none";
7802 div.innerText = line;
7803
7804 el.appendChild(div);
7805 let endCodeFound = goThroughEndCodes();
7806
7807 $(div).show(delayedDisplayTime, function () {
7808 if (mostRecentStep != "" && mostRecentStep != latestStep) {
7809 latestStep = mostRecentStep;
7810 if ($("#restore-progress-modal").hasClass("open")) {
7811 $("#restore_current_step").text(mostRecentStep);
7812 } else {
7813 $("#current_step").text(mostRecentStep);
7814 }
7815 }
7816 });
7817 el.scrollTop = el.scrollHeight;
7818
7819 curdivs++;
7820 if (curdivs < latestLines.length && endCodeFound == false) {
7821 isLineAppendTimeoutRunning = true;
7822 lineTimeoutAppend = setTimeout(function () {
7823 showNextLine(el);
7824 }, delayedDisplayTime);
7825 } else {
7826 isLineAppendTimeoutRunning = false;
7827 }
7828 }
7829
7830 function showAllLines(el, latestLines) {
7831 for (let i = 0; i < latestLines.length; i++) {
7832 let line = latestLines[i];
7833 let div = document.createElement("DIV");
7834
7835 let color = "";
7836 if (typeof line == "undefined" || !line) continue;
7837 if (line.substr(0, 6) == "[INFO]") color = "blue";
7838 else if (line.substr(0, 9) == "[SUCCESS]") color = "green";
7839 else if (line.substr(0, 6) == "[WARN]") color = "orange";
7840 else if (line.substr(0, 7) == "[ERROR]") color = "red";
7841 else if (line.substr(0, 10) == "[END-CODE]") color = "red";
7842 else if (line.substr(0, 9) == "[VERBOSE]") color = "gray";
7843 else if (line.substr(0, 6) == "[STEP]") {
7844 div.classList.add("bold");
7845 div.classList.add("step");
7846 } else {
7847 if (line && line.trim().length > 0 && line[0] != "[") {
7848 i--;
7849 }
7850 }
7851
7852 let mostRecentStep = "";
7853 if (line.substr(0, 6) == "[STEP]") mostRecentStep = line.slice(29);
7854
7855 if (color.length > 0) div.classList.add(color);
7856
7857 div.style.display = "none";
7858 div.innerText = line;
7859 el.appendChild(div);
7860
7861 $(div).show();
7862 }
7863 }
7864
7865 function insertPre(log, el) {
7866 if (log === false) return;
7867 let lines = log.split("\n");
7868 if (lines.length >= 1) lines = lines.slice(0, -1);
7869 latestLines = lines;
7870
7871 if (isLineAppendTimeoutRunning == false) {
7872 if (curdivs < latestLines.length) {
7873 showNextLine(el);
7874 }
7875 }
7876 }
7877
7878 function setProgress(end = 0, duration = 1000, res = null) {
7879 if (current_last == end) return;
7880 else current_last = end;
7881
7882 clearInterval(iprogres);
7883
7884 let start = parseFloat($(".progress-percentage")[0].style.left) - 1;
7885 if ($("#restore-progress-modal").hasClass("open")) {
7886 start =
7887 parseFloat(
7888 $("#restore-progress-modal .progress-percentage")[0].style.left,
7889 ) - 1;
7890 }
7891
7892 // if (start > end && end != 0) return;
7893
7894 let range = end - start;
7895 let current = start;
7896 let increment = 1;
7897 let stepTime = Math.abs(Math.floor(duration / range));
7898
7899 iprogres = setInterval(function () {
7900 current += increment;
7901 if ($("#restore-progress-modal").hasClass("open")) {
7902 $("#restore-progress-modal .progress-active-bar")[0].style.width =
7903 current.toFixed(2) + "%";
7904 $("#restore-progress-modal .progress-percentage")[0].style.left =
7905 current.toFixed(2) + "%";
7906 $("#restore-progress-modal .progress-percentage")[0].innerText =
7907 current.toFixed(0) + "%";
7908 } else {
7909 $(".progress-active-bar")[0].style.width = current.toFixed(2) + "%";
7910 $(".progress-percentage")[0].style.left = current.toFixed(2) + "%";
7911 $(".progress-percentage")[0].innerText = current.toFixed(0) + "%";
7912 }
7913
7914 if (current >= 100) {
7915 clearInterval(iprogres);
7916 }
7917
7918 if (current >= 100 && res != null) {
7919 current_last = null;
7920 if ($("#backup-progress-modal").hasClass("open")) {
7921 bacupisDone = true;
7922 progressIsDone = true;
7923
7924 let force = backupOnGoing == false ? true : false;
7925 completedBackup(res, force);
7926 }
7927 }
7928
7929 if (current > end) clearInterval(iprogres);
7930 }, stepTime);
7931 }
7932
7933 var curmaxnum = 0;
7934 async function animateValue(obj, start, end, duration) {
7935 let startTimestamp = null;
7936 let step = function (timestamp) {
7937 if (curmaxnum > end) return;
7938 if (!startTimestamp) startTimestamp = timestamp;
7939 let progress = Math.min((timestamp - startTimestamp) / duration, 1);
7940 obj.innerText = Math.floor(progress * (end - start) + start);
7941
7942 if (progress < 1) {
7943 window.requestAnimationFrame(step);
7944 }
7945 };
7946
7947 window.requestAnimationFrame(step);
7948 }
7949
7950 function refreshContentInstant(cb = function () { }) {
7951 let url = $("#BMI_BLOG_URL").text().trim();
7952 if (url.slice(-url.length) !== "/") url = url + "/";
7953
7954 httpGet(
7955 url +
7956 "?backup-migration=PROGRESS_LOGS&progress-id=latest_full.log&backup-id=current&t=" +
7957 +new Date() +
7958 "&sk=" +
7959 $("#BMI_SECRET_KEY").text().trim(),
7960 function (res2) {
7961 if (!res2) return cb();
7962
7963 let res1 = res2.split("\n").slice(0, 1)[0];
7964 if (res1.trim() === "") {
7965 res1 = res2.split("\n").slice(0, 2)[1];
7966 res2 = res2.split("\n").slice(2).join("\n");
7967 } else {
7968 res2 = res2.split("\n").slice(1).join("\n");
7969 }
7970
7971 let pre = $(".log-wrapper").find("pre")[0];
7972 let making = $("#bmi-making-archive").text().trim();
7973
7974 if (res1 && res1 != false && typeof res1 !== "undefined") {
7975 if (
7976 $("#current_step").text().trim().slice(0, making.length) == making
7977 ) {
7978 let obj = document.getElementById("bmi_counter_magic");
7979 if (obj) {
7980 let currnum = parseInt(obj.innerText);
7981 curmaxnum = parseInt(res1.split("/")[0]);
7982 if (!isNaN(currnum) && !isNaN(curmaxnum)) {
7983 setProgress(
7984 (parseInt(res1.split("/")[0]) /
7985 parseInt(res1.split("/")[1])) *
7986 100,
7987 );
7988 animateValue(obj, currnum, curmaxnum, 2000);
7989
7990 if (
7991 $("#bmi_magic_max_count").text() === "---" &&
7992 parseInt(res1.split("/")[1]) != 100
7993 ) {
7994 $("#bmi_magic_max_count").text(parseInt(res1.split("/")[1]));
7995 if ($("#entire_magic_counter").is(":hidden"))
7996 $("#entire_magic_counter").show();
7997 }
7998 }
7999 } else {
8000 let numv = parseInt(res1.split("/")[1]);
8001 if (numv == 100) numv = "---";
8002
8003 $("#current_step").html(
8004 making +
8005 ' <span id="entire_magic_counter">(<span id="bmi_counter_magic">0</span>/<span id="bmi_magic_max_count">' +
8006 numv +
8007 "</span>)</span>",
8008 );
8009 if (isNaN(parseInt(numv)) || numv == "---")
8010 $("#entire_magic_counter").hide();
8011 }
8012 } else {
8013 setProgress(
8014 (parseInt(res1.split("/")[0]) / parseInt(res1.split("/")[1])) *
8015 100,
8016 );
8017 }
8018 }
8019 if (res2 && res2 != false && typeof res2 !== "undefined")
8020 insertPre(res2, pre);
8021 cb();
8022 },
8023 );
8024 }
8025
8026 function refreshLogAndProgress(preserveLogs = false) {
8027 if (preserveLogs == false) {
8028 $(".log-wrapper").find("pre")[0].innerText = "";
8029 }
8030
8031 setProgress(0);
8032
8033 function update() {
8034 refreshContentInstant(function () {
8035 clearTimeout(timeouter);
8036 timeouter = setTimeout(function () {
8037 if (backupOnGoing === true) update();
8038 }, 1500);
8039 });
8040 }
8041
8042 setTimeout(function () {
8043 refreshContentInstant(function () {
8044 update();
8045 });
8046 }, 300);
8047 }
8048
8049 bmi.refreshLogAndProgress = refreshLogAndProgress;
8050
8051 function refreshLogAndProgressRestore(resetLogs = true) {
8052 if (resetLogs === true) {
8053 $(".log-wrapper").find("pre")[0].innerText = "";
8054 setProgress(0);
8055 }
8056
8057 function update() {
8058 getMigrationLogs(function () {
8059 timeouter = setTimeout(function () {
8060 if (restoreOnGoing === true || cli_quickmigration === true) update();
8061 }, 800);
8062 });
8063 }
8064
8065 getMigrationLogs(function () {
8066 clearTimeout(timeouter);
8067 update();
8068 });
8069 }
8070
8071 function isBackupOngoing(done = function () { }) {
8072 isRunning(true, function (res) {
8073 done(res);
8074 });
8075 }
8076
8077 function resetLogsPromise(migration = false) {
8078 return new Promise((resolve) => {
8079 resetLogs(migration, () => {
8080 return resolve();
8081 });
8082 });
8083 }
8084
8085 bmi.resetLogsPromise = resetLogsPromise;
8086
8087 function resetLogs(migration = false, done = function () {}) {
8088 $.bmi
8089 .ajax("reset-latest", {})
8090 .then(function (res) {
8091 if (migration === true) {
8092 done();
8093 } else {
8094 if (res.status == "success") {
8095 done();
8096 setTimeout(function () {
8097 $.bmi.modal("freeze-loading-modal").close();
8098 setTimeout(function () {
8099 // Make sure the progress modal is open
8100 $.bmi.modal("backup-progress-modal").open();
8101 }, 300);
8102 }, 300);
8103 } else {
8104 $.bmi._msg(res);
8105 }
8106 }
8107 })
8108 .catch(function (error) {
8109 //
8110 });
8111 }
8112
8113 function completedBackup(res, forced = false) {
8114 if (!(bacupisDone && progressIsDone) && !forced) return;
8115 setTimeout(function () {
8116 backupOnGoing = false;
8117
8118 clearInterval(iprogres);
8119 clearTimeout(timeouter);
8120
8121 let url_x = $("#BMI_BLOG_URL").text().trim();
8122 if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
8123
8124 $.bmi
8125 .ajax("get-latest-backup", {})
8126 .then(function (res_latest) {
8127 let url =
8128 url_x +
8129 "?backup-migration=BMI_BACKUP&backup-id=" +
8130 res_latest +
8131 "&t=" +
8132 +new Date() +
8133 "&sk=" +
8134 $("#BMI_SECRET_KEY").text().trim();
8135 let logs =
8136 url_x +
8137 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&backup-id=current&t=" +
8138 +new Date() +
8139 "&sk=" +
8140 $("#BMI_SECRET_KEY").text().trim();
8141
8142 $("#text-input-copy")[0].value = url;
8143 $("#download-backup-url").attr("href", url);
8144 $(".download-backup-log-url").attr("href", logs);
8145
8146 $.bmi.reloadBackups();
8147
8148 setTimeout(function () {
8149 clearInterval(iprogres);
8150 $(".log-wrapper").find("pre")[0].innerText = "";
8151 $(".progress-active-bar")[0].style.width = "0%";
8152 $(".progress-percentage")[0].style.left = "0%";
8153 $(".progress-percentage")[0].innerText = "0%";
8154 }, 300);
8155
8156 if ($("#backup-progress-modal").hasClass("open")) {
8157 $.bmi.modal("backup-progress-modal").close();
8158
8159 if ($('[name="radioAccessViaLink"]:checked').val() == "true") {
8160 $("#accessible-at-section").show();
8161 } else $("#accessible-at-section").hide();
8162
8163 $.bmi.modal("backup-success-modal").open();
8164 }
8165 })
8166 .catch(function (error) {
8167 if ($("#backup-progress-modal").hasClass("open")) {
8168 $.bmi.modal("backup-progress-modal").close();
8169
8170 if ($('[name="radioAccessViaLink"]:checked').val() == "true") {
8171 $("#accessible-at-section").show();
8172 } else $("#accessible-at-section").hide();
8173
8174 $.bmi.modal("backup-success-modal").open();
8175 }
8176
8177 let url =
8178 url_x +
8179 "?backup-migration=BMI_BACKUP&backup-id=" +
8180 res.filename +
8181 "&t=" +
8182 +new Date() +
8183 "&sk=" +
8184 $("#BMI_SECRET_KEY").text().trim();
8185 let logs =
8186 url_x +
8187 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&backup-id=current&t=" +
8188 +new Date() +
8189 "&sk=" +
8190 $("#BMI_SECRET_KEY").text().trim();
8191
8192 $("#text-input-copy")[0].value = url;
8193 $("#download-backup-url").attr("href", url);
8194 $(".download-backup-log-url").attr("href", logs);
8195
8196 $.bmi.reloadBackups();
8197
8198 setTimeout(function () {
8199 clearInterval(iprogres);
8200 $(".log-wrapper").find("pre")[0].innerText = "";
8201 $(".progress-active-bar")[0].style.width = "0%";
8202 $(".progress-percentage")[0].style.left = "0%";
8203 $(".progress-percentage")[0].innerText = "0%";
8204 }, 300);
8205 });
8206 }, 700);
8207 }
8208
8209 function sleepFunction(seconds = 1) {
8210 return new Promise(async (resolve) => {
8211 setTimeout(() => {
8212 return resolve();
8213 }, 1000 * seconds);
8214 });
8215 }
8216
8217 function saveOtherOptionsPromise() {
8218 return new Promise(async (resolve) => {
8219 saveBtnEventHandler(false, "save-other-options", resolve);
8220 });
8221 }
8222
8223 function tryToSaveBackupProcess(errorDetails = false) {
8224 return new Promise(async (resolve) => {
8225 let details = errorDetails;
8226
8227 if (details === false) return resolve(false);
8228 if (typeof details != "object") return resolve(false);
8229 if (typeof details[0] == "undefined") return resolve(false);
8230 if (typeof details[0]["status"] == "undefined") return resolve(false);
8231 if (typeof details[0]["statusText"] == "undefined") return resolve(false);
8232 if (typeof details[0]["responseText"] == "undefined")
8233 return resolve(false);
8234
8235 let status = details[0]["status"];
8236 let statusText = details[0]["statusText"];
8237 let responseText = details[0]["responseText"];
8238
8239 let isCLIDisabled = isChecked("cli-disable-others");
8240 let isDefaultBackup = isChecked("normal-timeout");
8241 let isCURLBackup = isChecked("experimental-timeout");
8242 let isBrowserBackup = isChecked("experimental-hard-timeout");
8243
8244 if (isBrowserBackup == true && isCLIDisabled == true)
8245 return resolve(false);
8246
8247 // 1. Force stop backup process
8248 await forceBackupToStop();
8249
8250 // 2. Adjust Other Options
8251 $("#cli-disable-others").prop("checked", true);
8252 $("#experimental-hard-timeout").prop("checked", true).change();
8253
8254 // 3. Save Other options
8255 await saveOtherOptionsPromise();
8256 await sleepFunction(3);
8257
8258 // 4. Run backup process from point zero without reseting logs
8259 setTimeout(() => {
8260 startBackupProcessNow(null, true);
8261 });
8262
8263 // Resolve as true
8264 return resolve(true);
8265 });
8266 }
8267
8268 async function backupError(type = -1, errorDetails = false) {
8269 clearInterval(iprogres);
8270 clearTimeout(timeouter);
8271
8272 $(".progress-active-bar")[0].style.width = "0%";
8273 $(".progress-percentage")[0].style.left = "0%";
8274 $(".progress-percentage")[0].innerText = "0%";
8275
8276 if (errorDetails && (await tryToSaveBackupProcess(errorDetails))) {
8277 return;
8278 }
8279
8280 await cleanUpAfterError();
8281
8282 console.error("Backup error type:", type);
8283
8284 setTimeout(function () {
8285 $(".log-wrapper").find("pre")[0].innerText = "";
8286
8287 $.bmi.modal("backup-progress-modal").close();
8288 setupBackupErrorOptions().then(() => {
8289 setTimeout(function () {
8290 $.bmi.modal("error-modal").open();
8291 $.bmi.modal("error-modal").setParent("backup-progress-modal");
8292 }, 300);
8293 });
8294 $("#after-logs-sent-modal").attr("data-error-source", "backup");
8295 }, 2000);
8296 }
8297
8298 bmi.backupError = backupError;
8299
8300 async function cleanUpAfterError() {
8301 await $.bmi.ajax("clean-up-after-error", {});
8302 }
8303
8304 function handleBfs() {
8305 $.bmi.modal("bfs-modal").open();
8306 }
8307
8308 function sendRequestOfBackupPart(callback, errors = 0) {
8309 let callbackSent = false;
8310 let cb = (data) => {
8311 if (callbackSent == false) {
8312 callbackSent = true;
8313 return callback(data);
8314 }
8315 };
8316
8317 $.bmi
8318 .ajax("backup-browser-method", {})
8319 .then(function (res) {
8320 if (typeof res.status != "undefined" && res.status == "success") {
8321 if (
8322 typeof res.status != "undefined" &&
8323 res.backup_process_error == "true"
8324 ) {
8325 errors++;
8326 return callback({ status: false, errors: errors });
8327 }
8328
8329 if (
8330 typeof res.backup_completed != "undefined" &&
8331 res.backup_completed == "true"
8332 )
8333 return callback({ status: true, errors: errors });
8334 else return callback({ status: false, errors: errors });
8335 } else {
8336 // Increment the errors
8337 errors++;
8338 console.error(res);
8339 return callback({ status: false, errors: errors });
8340 }
8341 })
8342 .catch(function (error) {
8343 // Increment the errors
8344 errors++;
8345 console.error(error);
8346
8347 // Return with a tiemouted retry
8348 return callback({ status: false, errors: errors });
8349 });
8350
8351 // // Require browser client
8352 // let http = new XMLHttpRequest();
8353
8354 // // Open POST connection
8355 // http.open('POST', h.url, true);
8356
8357 // // Send proper headers with settings
8358 // http.setRequestHeader('Content-Type', 'application/json');
8359 // http.setRequestHeader('Content-Accept', '*/*');
8360 // http.setRequestHeader('Access-Control-Allow-Origin', '*');
8361 // http.setRequestHeader('Content-ConfigDir', h.config_dir);
8362 // http.setRequestHeader('Content-Content', h.content_dir);
8363 // http.setRequestHeader('Content-Backups', h.backup_dir);
8364 // http.setRequestHeader('Content-Identy', h.identy);
8365 // http.setRequestHeader('Content-Url', h.url);
8366 // http.setRequestHeader('Content-Abs', h.abs_dir);
8367 // http.setRequestHeader('Content-Dir', h.root_dir);
8368 // http.setRequestHeader('Content-Manifest', h.manifest);
8369 // http.setRequestHeader('Content-Name', h.backupname);
8370 // http.setRequestHeader('Content-Safelimit', h.safelimit);
8371 // http.setRequestHeader('Content-Start', h.start);
8372 // http.setRequestHeader('Content-Filessofar', h.filessofar);
8373 // http.setRequestHeader('Content-Total', h.total_files);
8374 // http.setRequestHeader('Content-Rev', h.rev);
8375 // http.setRequestHeader('Content-It', h.iteratio);
8376 // http.setRequestHeader('Content-Dbit', h.dbiteratio);
8377 // http.setRequestHeader('Content-Dblast', h.dblast);
8378 // http.setRequestHeader('Content-Bmitmp', h.bmitmp);
8379 // http.setRequestHeader('Content-Browser', true);
8380
8381 // // Handle success
8382 // http.onload = function () {
8383
8384 // // Check if we can get the headers
8385 // if (http.status === 200) {
8386
8387 // // Make sure the headers exists
8388 // let isFinished = http.getResponseHeader('Content-Finished');
8389
8390 // // Check if it's finished
8391 // if (typeof isFinished != 'undefined' && isFinished && isFinished == 'true') {
8392
8393 // // Reset errors
8394 // errors = 0;
8395
8396 // // Return with success
8397 // return callback({ status: true, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
8398
8399 // }
8400
8401 // // Get iteratio value
8402 // let iteratio = http.getResponseHeader('Content-It');
8403 // let dbiteratio = http.getResponseHeader('Content-Dbit');
8404 // let dblast = http.getResponseHeader('Content-Dblast');
8405 // let soFar = http.getResponseHeader('Content-Filessofar');
8406
8407 // if (typeof iteratio != 'undefined' && iteratio) {
8408
8409 // // Return success
8410 // return callback({ status: true, iteratio: iteratio, dbiteratio: dbiteratio, dblast: dblast, sf: soFar, errors: errors });
8411
8412 // } else {
8413
8414 // // Increment the errors
8415 // errors++;
8416
8417 // // Return with failure and a tiemouted retry
8418 // return callback({ status: false, iteratio: -1, dbiteratio: dbiteratio, dblast: dblast, sf: -1, errors: errors });
8419
8420 // }
8421
8422 // } else {
8423
8424 // // Increment the errors
8425 // errors++;
8426
8427 // // Return with failure and a tiemouted retry
8428 // return callback({ status: false, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
8429
8430 // }
8431
8432 // }
8433
8434 // // Handle failure
8435 // http.onerror = function () {
8436
8437 // // Increment the errors
8438 // errors++;
8439
8440 // // Return with a tiemouted retry
8441 // return callback({ status: false, iteratio: -1, dbiteratio: -1, dblast: 0, sf: -1, errors: errors });
8442
8443 // }
8444
8445 // // Send prepared connection to the server
8446 // http.send();
8447 }
8448
8449 function middlewareForResponses(res) {
8450 // Variables
8451 let status = res.status;
8452 let errors = parseInt(res.errors);
8453
8454 // Check if finished
8455 if (status == "true" || status === true) {
8456 // Return and show success
8457 let backgroundNameTmp = backgroundName;
8458 backgroundBackup = false;
8459 backgroundName = false;
8460 headers_for_middleware = false;
8461 window.onbeforeunload = null;
8462 $(".backup-minimize").removeClass("disabled");
8463
8464 // Return
8465 return completedBackup({ filename: backgroundNameTmp }, true);
8466 }
8467
8468 // Check for errors
8469 // Abort if above 4 errors
8470 if (isNaN(errors) || errors > 0) {
8471 // Unset the headers and variables
8472 backgroundBackup = false;
8473 backgroundName = false;
8474 headers_for_middleware = false;
8475 window.onbeforeunload = null;
8476 $(".backup-minimize").removeClass("disabled");
8477
8478 // End this process
8479 if ($("#backup-progress-modal").hasClass("open")) {
8480 return backupError(3);
8481 } else return;
8482 } else {
8483 // Handle case of success
8484 return handleBrowserBackup(errors);
8485 }
8486 }
8487
8488 function handleBrowserBackup(errors = 0) {
8489 if (stopBackgroundBackup === true) {
8490 stopBackgroundProcess();
8491 return;
8492 }
8493
8494 // Send the request
8495 setTimeout(
8496 function () {
8497 sendRequestOfBackupPart(middlewareForResponses, errors);
8498 },
8499 Math.floor(Math.random() * (523 - 330)) + 330,
8500 );
8501 }
8502
8503 $("#configuration-reset-absolute").on("click", function (e) {
8504 e.preventDefault();
8505 $.bmi
8506 .ajax("reset-configuration", {})
8507 .then(function (res) {
8508 if (res.status == "success") {
8509 window.location.reload();
8510 } else {
8511 $.bmi._msg(res);
8512 }
8513 })
8514 .catch(function (error) {
8515 //
8516 });
8517 });
8518
8519 $("#download-site-infos").on("click", function (e) {
8520 e.preventDefault();
8521 $.bmi
8522 .ajax("get-site-data", {})
8523 .then(function (res) {
8524 if (res.status == "success") {
8525 $.bmi.prepareFile(
8526 "site_details_troubleshooting.txt",
8527 JSON.stringify(res.data),
8528 );
8529 } else {
8530 $.bmi._msg(res);
8531 }
8532 })
8533 .catch(function (error) {
8534 //
8535 });
8536 });
8537
8538 $("#start-entire-backup").on("click", startBackupProcessNow);
8539 async function startBackupProcessNow(e, preserveLogs = false) {
8540 if (preserveLogs == false) {
8541 $.bmi.modal("prenotice-modal").close();
8542 $.bmi.modal("freeze-loading-modal").open();
8543 }
8544
8545 $(".backup-minimize").removeClass("disabled");
8546
8547 await fixHtaccessPromise();
8548
8549 if (preserveLogs == false) {
8550 curdivs = 0;
8551 await resetLogsPromise(false);
8552 }
8553
8554 clearTimeout(timeouter);
8555
8556 triggeredByIntv = false;
8557 bacupisDone = false;
8558 backupOnGoing = true;
8559
8560 refreshLogAndProgress(preserveLogs);
8561 callBackupRunAjax(preserveLogs);
8562 }
8563
8564 function callBackupRunAjax(preserveLogs = false) {
8565 stopBackgroundBackup = false;
8566
8567 if (preserveLogs == false) {
8568 $.bmi.modal("freeze-loading-modal").close();
8569 $.bmi.modal("backup-progress-modal").open();
8570 }
8571
8572 $.bmi
8573 .ajax("create-backup", {
8574 preserveLogs: preserveLogs,
8575 })
8576 .then(function (res) {
8577 if (res.status == "success") {
8578 bacupisDone = true;
8579 clearTimeout(timeouter);
8580 backupOnGoing = false;
8581 refreshContentInstant(function () {
8582 setTimeout(function () {
8583 // setProgress(101, 500, res);
8584 completedBackup(res, true);
8585 }, 350);
8586 });
8587 } else if (res.status == "background") {
8588 backgroundBackup = true;
8589 backgroundName = res.filename;
8590
8591 let end_code = document.querySelectorAll(".hide_so_much");
8592 if (end_code && end_code.length > 0) {
8593 end_code = end_code[end_code.length - 1];
8594 let code_line = end_code.innerText;
8595
8596 if (code_line.includes("001")) {
8597 bacupisDone = true;
8598 progressIsDone = true;
8599 clearTimeout(timeouter);
8600 backupOnGoing = false;
8601 completedBackup({ filename: backgroundName }, true);
8602 }
8603 if (code_line.includes("002") || code_line.includes("004")) {
8604 // $.bmi.modal('backup-progress-modal').close();
8605 backupError(4);
8606 }
8607 if (code_line.includes("003")) {
8608 $.bmi.modal("backup-progress-modal").close();
8609 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
8610 }
8611 }
8612 } else if (res.status == "background_hard") {
8613 // Set the background receiver
8614 backgroundBackup = true;
8615
8616 // Set Global name of current backup
8617 backgroundName = res.filename;
8618
8619 // Append URL
8620 res.url = res.url;
8621
8622 // Display success of receiver
8623 $.bmi.alert("success", $("#bmi-received-hard").text(), 3000);
8624
8625 // Disable button
8626 $(".backup-minimize").addClass("disabled");
8627
8628 // Make sure modal is visible
8629 if (!$("#backup-progress-modal").hasClass("open")) {
8630 setTimeout(function () {
8631 $.bmi.modal("freeze-loading-modal").close();
8632 setTimeout(function () {
8633 // Make sure the progress modal is open
8634 $.bmi.modal("backup-progress-modal").open();
8635 }, 300);
8636 }, 300);
8637 }
8638
8639 // Make sure the backup won't be dismissed by mistake
8640 window.onbeforeunload = function () {
8641 return "Backup in progress...";
8642 };
8643
8644 // Send first request
8645 handleBrowserBackup();
8646 } else {
8647 backupOnGoing = false;
8648 setTimeout(function () {
8649 clearInterval(iprogres);
8650 $(".log-wrapper").find("pre")[0].innerText = "";
8651 $(".progress-active-bar")[0].style.width = "0%";
8652 $(".progress-percentage")[0].style.left = "0%";
8653 $(".progress-percentage")[0].innerText = "0%";
8654 }, 300);
8655
8656 $.bmi._msg(res);
8657
8658 console.log(res);
8659
8660 $.bmi.modal("backup-progress-modal").close();
8661 if (typeof res.bfs !== "undefined") handleBfs();
8662 else backupError(5);
8663 }
8664 })
8665 .catch(function (error) {
8666 console.error(error);
8667 backupError(6, error);
8668 });
8669 }
8670
8671 $("#open_trouble_extenstion").on("click", function () {
8672 if ($("#trouble_extenstion").hasClass("openned")) {
8673 $("#trouble_extenstion").hide(300);
8674 $("#trouble_extenstion").removeClass("openned");
8675 $(this).removeClass("active");
8676 } else {
8677 $("#trouble_extenstion").show(300);
8678 $("#trouble_extenstion").addClass("openned");
8679 $(this).addClass("active");
8680 }
8681 });
8682
8683 $("#switch-show-trs").on("click", function () {
8684 let seemore = this.dataset.see;
8685 let hide = this.dataset.hide;
8686 let $trs = $(".hide-show-tr");
8687
8688 if ($(this).hasClass("shown")) {
8689 $trs.hide(300);
8690 $(this).removeClass("shown");
8691 this.innerText = seemore;
8692 } else {
8693 $trs.show(300);
8694 $(this).addClass("shown");
8695 this.innerText = hide;
8696 }
8697 });
8698
8699 $("#ex_b_fs").on("change", function () {
8700 if ($("#ex_b_fs").is(":checked")) $("#bmi__collon").show();
8701 else $("#bmi__collon").hide();
8702 });
8703
8704 $("#show-upload-area").on("click", function () {
8705 if ($(".upload_area").hasClass("hidden")) {
8706 $(".upload_area").show(300);
8707 $(".upload_area").removeClass("hidden");
8708 $([document.documentElement, document.body]).animate(
8709 {
8710 scrollTop: $(this).offset().top - 50 + "px",
8711 },
8712 300,
8713 );
8714 } else {
8715 $(".upload_area").hide(300);
8716 $(".upload_area").addClass("hidden");
8717 }
8718 });
8719
8720 $(".bmi-copper").on("click", function (e) {
8721 e.preventDefault();
8722 let $el = $("#" + this.getAttribute("data-copy"))[0];
8723 if ($el.value && $el.value.length > 0) {
8724 $.bmi.clipboard($el.value);
8725 } else {
8726 $.bmi.clipboard($el.innerText);
8727 }
8728 });
8729
8730 $("#bmi_restore_tbody").on("click", ".restore-btn", function (e) {
8731 isMigrationLocked(function (isNotLocked) {
8732 if (isNotLocked) {
8733
8734 let name = '';
8735 if (e.target.closest('tr').getAttribute('data-is-local') === 'no') {
8736 if (e.target.closest('tr').getAttribute('gdrive-id') !== null) {
8737 name = '?#googledrive#_' + e.target.closest('tr').getAttribute('gdrive-id');
8738 current_restore = name;
8739 }
8740
8741 if (e.target.closest('tr').getAttribute('ftp-id') !== null) {
8742 name = '?#ftp#_' + e.target.closest('tr').getAttribute('ftp-id');
8743 current_restore = name;
8744 }
8745
8746 if(e.target.closest('tr').getAttribute('dropbox-id') !== null){
8747 name = '?#dropbox#_' + e.target.closest('tr').getAttribute('dropbox-id');
8748 current_restore = name;
8749 }
8750
8751 if (e.target.closest('tr').getAttribute('onedrive-id') !== null) {
8752 name = '?#onedrive#_' + e.target.closest('tr').getAttribute('onedrive-id');
8753 current_restore = name;
8754 }
8755
8756 if (e.target.closest('tr').getAttribute('aws-id') !== null) {
8757 name = '?#aws#_' + e.target.closest('tr').getAttribute('aws-id');
8758 current_restore = name;
8759 }
8760
8761 if (e.target.closest('tr').getAttribute('wasabi-id') !== null) {
8762 name = '?#wasabi#_' + e.target.closest('tr').getAttribute('wasabi-id');
8763 current_restore = name;
8764 }
8765
8766 if (e.target.closest('tr').getAttribute('backupbliss-id') !== null) {
8767 name = '?#backupbliss#_' + e.target.closest('tr').getAttribute('backupbliss-id');
8768 current_restore = name;
8769 }
8770
8771 if (e.target.closest('tr').getAttribute('sftp-id') !== null) {
8772 name = '?#sftp#_' + e.target.closest('tr').getAttribute('sftp-id');
8773 current_restore = name;
8774 }
8775
8776 } else {
8777 name = e.target.closest("tr").querySelector(".br_name").innerText;
8778 current_restore = name;
8779 }
8780
8781 if (!name || name.trim().length <= 0)
8782 return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
8783
8784 $("#restore-ok").prop("checked", false);
8785
8786 $.bmi.modal("freeze-loading-modal").open();
8787 setTimeout(function () {
8788 shareLogsStatus("is_allowed", function (res) {
8789 if (res === "ask") {
8790 $.bmi.modal("freeze-loading-modal").close();
8791 $("#logs-sharing-ask-modal").attr(
8792 "data-destination",
8793 "pre-restore-modal",
8794 );
8795 setTimeout(function () {
8796 $.bmi.modal("logs-sharing-ask-modal").open();
8797 }, 300);
8798 } else {
8799 $.bmi.modal("freeze-loading-modal").close();
8800 setTimeout(function () {
8801 $.bmi.modal("pre-restore-modal").open();
8802 }, 300);
8803 }
8804 });
8805 }, 300);
8806 }
8807 });
8808 });
8809
8810 $("#bmi_restore_tbody").on("click", ".stg-restore-btn", function (e) {
8811 $.bmi.modal("freeze-loading-modal").open();
8812
8813 let name = e.target.closest("tr").querySelector(".br_name").innerText;
8814 // if (e.target.closest('tr').getAttribute('data-is-local') === 'no') {
8815 // name = '?#googledrive#_' + e.target.closest('tr').getAttribute('gdrive-id');
8816 // }
8817
8818 if (!name || name.trim().length <= 0) {
8819 return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
8820 }
8821
8822 $('.bmi-stg-sel-box[data-mode="tastewp"]').click();
8823 $('.bmi-stg-drop-option[backup-name="' + name.trim() + '"]').click();
8824 $("#stgng").click();
8825
8826 setTimeout(function () {
8827 $.bmi.modal("freeze-loading-modal").close();
8828 }, 150);
8829 });
8830
8831 $("#quick-download-migration").on("click", function () {
8832 let url = $("#bm-d-url").val();
8833 if ($.bmi.isUrlValid(url)) {
8834 isMigrationLocked(function (isNotLocked) {
8835 if (isNotLocked) {
8836 if (url.length > 0) {
8837 current_restore = -100;
8838 $("#restore-ok").prop("checked", false);
8839
8840 $.bmi.modal("freeze-loading-modal").open();
8841 setTimeout(function () {
8842 shareLogsStatus("is_allowed", function (res) {
8843 if (res === "ask") {
8844 $.bmi.modal("freeze-loading-modal").close();
8845 $("#logs-sharing-ask-modal").attr(
8846 "data-destination",
8847 "pre-restore-modal",
8848 );
8849 setTimeout(function () {
8850 $.bmi.modal("logs-sharing-ask-modal").open();
8851 }, 300);
8852 } else {
8853 $.bmi.modal("freeze-loading-modal").close();
8854 setTimeout(function () {
8855 $.bmi.modal("pre-restore-modal").open();
8856 }, 300);
8857 }
8858 });
8859 }, 300);
8860 } else $.bmi.alert("warning", $("#bmi-invalid-url").text(), 5000);
8861 }
8862 });
8863 } else $.bmi.alert("warning", $("#bmi-invalid-url").text(), 5000);
8864 });
8865
8866 $('#restore-start-sure').on('click', function () {
8867
8868 if ($('#restore-ok').is(':checked')) {
8869 if (current_restore === -100) downloadMigration();
8870 else if (current_restore.includes('?#googledrive#_')) downloadGDrive();
8871 else if (current_restore.includes('?#ftp#_')) downloadFTP();
8872 else if (current_restore.includes('?#dropbox#_')) downloadDropbox();
8873 else if (current_restore.includes('?#onedrive#_')) downloadOneDrive();
8874 else if (current_restore.includes('?#aws#_')) downloadS3(true, 'aws');
8875 else if (current_restore.includes('?#wasabi#_')) downloadS3(true, 'wasabi');
8876 else if (current_restore.includes('?#backupbliss#_')) downloadBackupBliss();
8877 else if (current_restore.includes('?#sftp#_')) downloadSFTP();
8878 else letsRestore();
8879 } else
8880 $.bmi.alert("warning", $("#bmi-restore-require-checkmark").text(), 3000);
8881 });
8882
8883 $(".backup-minimize").on("click", function () {
8884 isBackupOngoing(function (isRunnin) {
8885 if (isRunnin === true) $("#bmi-ongoing-backup").show(300);
8886 else $("#bmi-ongoing-backup").hide();
8887 });
8888 });
8889
8890 function checkIfBackupOnGoing(cb = function () { }) {
8891 isBackupOngoing(function (isRunnin) {
8892 if (isRunnin === true) {
8893 if (!$("#backup-progress-modal").hasClass("open")) {
8894 if (backupOnGoing === false) {
8895 bacupisDone = false;
8896 backupOnGoing = true;
8897 triggeredByIntv = true;
8898 }
8899
8900 $("#bmi-ongoing-backup").show(300);
8901 }
8902 } else {
8903 if ($("#bmi-ongoing-backup").is(":visible")) {
8904 $("#bmi-ongoing-backup").hide(300);
8905 }
8906
8907 if (
8908 $("#backup-progress-modal").hasClass("open") &&
8909 triggeredByIntv === true
8910 ) {
8911 if (backupOnGoing === true) {
8912 backupOnGoing = false;
8913 }
8914
8915 triggeredByIntv = false;
8916 reloadAndHandleBackupEnd();
8917 } else {
8918 if (
8919 backupOnGoing === true &&
8920 !$("#backup-progress-modal").hasClass("open")
8921 ) {
8922 backupOnGoing = false;
8923 $.bmi.reloadBackups();
8924 }
8925 }
8926 }
8927
8928 cb();
8929 });
8930 }
8931
8932 function handleNotOwnBackupEnd() {
8933 let url = $("#BMI_BLOG_URL").text().trim();
8934 if (url.slice(-url.length) !== "/") url = url + "/";
8935
8936 httpGet(
8937 url +
8938 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&backup-id=current&t=" +
8939 +new Date() +
8940 "&sk=" +
8941 $("#BMI_SECRET_KEY").text().trim(),
8942 function (rez) {
8943 if (rez === false) {
8944 $.bmi.modal("backup-progress-modal").close();
8945 } else {
8946 rez = rez.split("\n");
8947 let abort = "Backup process aborted";
8948 let error = "[ERROR]";
8949
8950 if (
8951 rez[rez.length - 1].includes(abort) ||
8952 rez[rez.length - 2].includes(abort)
8953 ) {
8954 $.bmi.modal("backup-progress-modal").close();
8955 $.bmi.alert("info", $("#bmi-aborted-al").text(), 3000);
8956 } else if (
8957 rez[rez.length - 1].includes(error) ||
8958 rez[rez.length - 2].includes(error)
8959 ) {
8960 // $.bmi.modal('backup-progress-modal').close();
8961 backupError(7);
8962 } else {
8963 let url_x = $("#BMI_BLOG_URL").text().trim();
8964 if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
8965
8966 $.bmi
8967 .ajax("get-latest-backup", {})
8968 .then(function (res) {
8969 let url =
8970 url_x +
8971 "?backup-migration=BMI_BACKUP&backup-id=" +
8972 res +
8973 "&t=" +
8974 +new Date() +
8975 "&sk=" +
8976 $("#BMI_SECRET_KEY").text().trim();
8977 let logs =
8978 url_x +
8979 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&backup-id=current&t=" +
8980 +new Date() +
8981 "&sk=" +
8982 $("#BMI_SECRET_KEY").text().trim();
8983
8984 $("#text-input-copy")[0].value = url;
8985 $("#download-backup-url").attr("href", url);
8986 $(".download-backup-log-url").attr("href", logs);
8987
8988 $.bmi.modal("backup-progress-modal").close();
8989 $.bmi.modal("backup-success-modal").open();
8990 })
8991 .catch(function (error) {
8992 $.bmi.modal("backup-progress-modal").close();
8993 $.bmi.modal("backup-success-modal").open();
8994
8995 let url = $($("#bmi_restore_tbody").find("tr")[0])
8996 .find(".bc-download-btn")
8997 .attr("href");
8998 $("#download-backup-url").attr({ href: url });
8999 $("#text-input-copy").val(url);
9000 });
9001 }
9002 }
9003 },
9004 );
9005 }
9006
9007 function reloadAndHandleBackupEnd() {
9008 $.bmi.getCurrentBackups(function (res) {
9009 $.bmi.fillWithNewBackups(res.backups, res.backups.ongoing, function () {
9010 handleNotOwnBackupEnd();
9011 });
9012 });
9013 }
9014
9015 // ONGOING BACKUP
9016 function runCheckOnGoing() {
9017 checkIfBackupOnGoing(function () {
9018 setTimeout(function () {
9019 runCheckOnGoing();
9020 }, ongoing_interval);
9021 });
9022 }
9023 runCheckOnGoing();
9024
9025 $("#bmi-ongoing-backup").on("click", function () {
9026 isBackupOngoing(function (isRunnin) {
9027 if (isRunnin === true) {
9028 curdivs = 0;
9029 refreshLogAndProgress();
9030 setTimeout(function () {
9031 $.bmi.modal("freeze-loading-modal").close();
9032 setTimeout(function () {
9033 // Make sure the progress modal is open
9034 $.bmi.modal("backup-progress-modal").open();
9035 }, 300);
9036 }, 300);
9037 } else {
9038 $.bmi.alert("info", $("#bmi-bc-ended").text(), 3000);
9039 $.bmi.reloadBackups();
9040 }
9041
9042 $("#bmi-ongoing-backup").hide(300);
9043 });
9044 });
9045
9046 function getMigrationLogs(cb = function () { }) {
9047 let url = $("#BMI_BLOG_URL").text().trim();
9048 if (url.slice(-url.length) !== "/") url = url + "/";
9049
9050 httpGet(
9051 url +
9052 "?backup-migration=PROGRESS_LOGS&progress-id=latest_migration_full.log&backup-id=current&t=" +
9053 +new Date() +
9054 "&sk=" +
9055 $("#BMI_SECRET_KEY").text().trim(),
9056 function (res1) {
9057 if (!res1) {
9058 return cb();
9059 }
9060
9061 let res2 = res1.split("\n").slice(0, 1)[0];
9062 res1 = res1.split("\n").slice(1).join("\n");
9063
9064 if (res2 === false || isNaN(parseFloat(res2))) {
9065 return cb();
9066 }
9067 let pre = $("#restore-live-log-wrapper").find("pre")[0];
9068
9069 if (!res1.includes("<") && !res1.includes(">")) {
9070 if (res1 && res1 != false && typeof res1 !== "undefined") {
9071 insertPre(res1, pre);
9072 }
9073 }
9074
9075 setProgress(res2);
9076 cb();
9077 },
9078 );
9079 }
9080
9081 function fixHtaccessPromise() {
9082 return new Promise((resolve) => {
9083 fixHtaccess(() => {
9084 return resolve();
9085 });
9086 });
9087 }
9088
9089 bmi.fixHtaccessPromise = fixHtaccessPromise;
9090
9091 function fixHtaccess(cb = function () {}) {
9092 $.bmi
9093 .ajax("htaccess-litespeed", {})
9094 .then(function (res) {
9095 setTimeout(function () {
9096 cb(true);
9097 }, 400);
9098 })
9099 .catch(function (error) {
9100 cb(false);
9101 });
9102 }
9103
9104 function isMigrationLocked(
9105 cb = function () { },
9106 mute = false,
9107 clearLogs = true,
9108 ) {
9109 $.bmi
9110 .ajax("migration-locked", { clearLogs: clearLogs })
9111 .then(function (res) {
9112 if (res.status == "success") cb(true);
9113 else {
9114 if (!mute) $.bmi._msg(res);
9115 cb(false);
9116 }
9117 })
9118 .catch(function (error) {
9119 //
9120 });
9121 }
9122
9123 function downloadGDrive(startRestoreProcess = true) {
9124 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9125 let currentGDriveIdRestoration =
9126 current_restore.split("?#googledrive#_")[1];
9127 current_restore = null;
9128
9129 if (startRestoreProcess == false) {
9130 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9131 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9132 }
9133
9134 isMigrationLocked(function (isNotLocked) {
9135 resetLogs(true, function () {
9136 fixHtaccess(function () {
9137 if (isNotLocked) {
9138 curdivs = 0;
9139 refreshLogAndProgressRestore();
9140 restoreOnGoing = true;
9141 cli_quickmigration = true;
9142
9143 $.bmi.modal("pre-restore-modal").close();
9144 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9145 $.bmi.modal("restore-progress-modal").open();
9146
9147 performGDriveDownload(startRestoreProcess, currentGDriveIdRestoration);
9148 }
9149 });
9150 });
9151 });
9152 }
9153
9154 $.bmi.downloadGDrive = downloadGDrive;
9155
9156 function downloadFTP(startRestoreProcess = true) {
9157
9158 $('#restore-live-log-wrapper').find('pre')[0].innerText = '';
9159 let currentFtpIdRestoration = current_restore.split('?#ftp#_')[1];
9160 let md5 = $("#bmi_restore_tbody").find("tr[ftp-id='" + currentFtpIdRestoration + "']").attr("md5");
9161 current_restore = null;
9162
9163 if (startRestoreProcess == false) {
9164 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9165 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9166 }
9167
9168 isMigrationLocked(function (isNotLocked) {
9169 resetLogs(true, function () {
9170 fixHtaccess(function () {
9171 if (isNotLocked) {
9172
9173 curdivs = 0;
9174 refreshLogAndProgressRestore();
9175 restoreOnGoing = true;
9176 cli_quickmigration = true;
9177
9178 $.bmi.modal('pre-restore-modal').close();
9179 $('#restore-live-log-wrapper').find('pre')[0].innerText = '';
9180 $.bmi.modal('restore-progress-modal').open();
9181
9182 performFtpDownload(startRestoreProcess, currentFtpIdRestoration, 0, false, md5);
9183
9184 }
9185 });
9186 });
9187 });
9188
9189 }
9190
9191 $.bmi.downloadFTP = downloadFTP;
9192
9193 function downloadOneDrive(startRestoreProcess = true) {
9194 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9195 let currentOneDriveIdRestoration =
9196 current_restore.split("?#onedrive#_")[1];
9197
9198 let md5 = $("#bmi_restore_tbody").find("tr[onedrive-id='" + currentOneDriveIdRestoration + "']").attr("md5");
9199 current_restore = null;
9200
9201 if (startRestoreProcess == false) {
9202 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9203 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9204 }
9205
9206 isMigrationLocked(function (isNotLocked) {
9207 resetLogs(true, function () {
9208 fixHtaccess(function () {
9209 if (isNotLocked) {
9210 curdivs = 0;
9211 refreshLogAndProgressRestore();
9212 restoreOnGoing = true;
9213 cli_quickmigration = true;
9214
9215 $.bmi.modal("pre-restore-modal").close();
9216 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9217 $.bmi.modal("restore-progress-modal").open();
9218
9219 performOneDriveDownload(startRestoreProcess, currentOneDriveIdRestoration, 0, false, md5);
9220 }
9221 });
9222 });
9223 });
9224 }
9225
9226 $.bmi.downloadOneDrive = downloadOneDrive;
9227
9228 function downloadBackupBliss(startRestoreProcess = true) {
9229 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9230 let currentBackupBlissIdRestoration =
9231 current_restore.split("?#backupbliss#_")[1];
9232
9233 let md5 = $("#bmi_restore_tbody").find("tr[backupbliss-id='" + currentBackupBlissIdRestoration + "']").attr("md5");
9234 current_restore = null;
9235
9236 isMigrationLocked(function (isNotLocked) {
9237 resetLogs(true, function () {
9238 fixHtaccess(function () {
9239 if (isNotLocked) {
9240 curdivs = 0;
9241 refreshLogAndProgressRestore();
9242 restoreOnGoing = true;
9243 cli_quickmigration = true;
9244
9245 $.bmi.modal("pre-restore-modal").close();
9246 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9247 $.bmi.modal("restore-progress-modal").open();
9248
9249 performBackupBlissDownload(startRestoreProcess, currentBackupBlissIdRestoration, 0, false, md5);
9250 }
9251 });
9252 });
9253 });
9254 }
9255
9256 $.bmi.downloadBackupBliss = downloadBackupBliss;
9257
9258 function performFtpDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9259
9260 $.bmi.ajax('download-cloud-backup', {
9261
9262 storage: 'ftp',
9263 startRestoreProcess: startRestoreProcess,
9264 fileId: fileId,
9265 step: step,
9266 size: size,
9267 md5: md5,
9268 filename: originalFilename,
9269 writepath: writepath,
9270 chunksize: chunksize,
9271 secret: secret
9272
9273 }).then(function (res) {
9274
9275 if (res.status == 'success') {
9276
9277 if (typeof res.size != 'undefined') size = res.size;
9278 if (typeof res.md5 != 'undefined') md5 = res.md5;
9279 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9280 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9281 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9282 if (typeof res.secret != 'undefined') secret = res.secret;
9283
9284 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9285
9286 step++;
9287 performFtpDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9288
9289 } else {
9290
9291 cli_quickmigration = true;
9292 current_restore = res.filename;
9293 refreshLogAndProgressRestore(false);
9294
9295 $.bmi.reloadBackups(function () {
9296
9297 setTimeout(function () {
9298
9299 clearInterval(iprogres);
9300 $('#restore-progress-modal .progress-active-bar')[0].style.width = 0 + '%';
9301 $('#restore-progress-modal .progress-percentage')[0].style.left = 0 + '%';
9302 $('#restore-progress-modal .progress-percentage')[0].innerText = 0 + '%';
9303 $('#restore_current_step').text($('#bmi-restoring-prepare').text());
9304
9305 }, 600);
9306 });
9307
9308 }
9309
9310 } else {
9311
9312 $.bmi._msg(res);
9313 restoreFailed();
9314 console.error(res);
9315
9316 }
9317
9318 }).catch(function (error) {
9319
9320 restoreFailed(error);
9321
9322 });
9323
9324 }
9325
9326 function performGDriveDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9327
9328 $.bmi.ajax('download-cloud-backup', {
9329
9330 storage: 'googledrive',
9331 startRestoreProcess: startRestoreProcess,
9332 fileId: fileId,
9333 step: step,
9334 size: size,
9335 md5: md5,
9336 filename: originalFilename,
9337 writepath: writepath,
9338 chunksize: chunksize,
9339 secret: secret
9340
9341 }).then(function (res) {
9342
9343 if (res.status == 'success') {
9344
9345 if (typeof res.size != 'undefined') size = res.size;
9346 if (typeof res.md5 != 'undefined') md5 = res.md5;
9347 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9348 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9349 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9350 if (typeof res.secret != 'undefined') secret = res.secret;
9351
9352 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9353
9354 step++;
9355 performGDriveDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9356
9357 } else if (res.finished == 'true') {
9358 cli_quickmigration = true;
9359 current_restore = res.filename;
9360 refreshLogAndProgressRestore(false);
9361
9362 $.bmi.reloadBackups(function () {
9363 setTimeout(function () {
9364 clearInterval(iprogres);
9365 $(
9366 "#restore-progress-modal .progress-active-bar",
9367 )[0].style.width = 0 + "%";
9368 $(
9369 "#restore-progress-modal .progress-percentage",
9370 )[0].style.left = 0 + "%";
9371 $("#restore-progress-modal .progress-percentage")[0].innerText =
9372 0 + "%";
9373 $("#restore_current_step").text(
9374 $("#bmi-restoring-prepare").text(),
9375 );
9376 }, 600);
9377 });
9378
9379 }
9380 } else {
9381 $.bmi._msg(res);
9382 restoreFailed();
9383 console.error(res);
9384 }
9385 })
9386 .catch(function (error) {
9387 restoreFailed(error);
9388 });
9389 }
9390
9391 function downloadDropbox(startRestoreProcess = true) {
9392 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9393 let currentDropboxIdRestoration = current_restore.split("?#dropbox#_")[1];
9394 let md5 = $("#bmi_restore_tbody").find("tr[dropbox-id='" + currentDropboxIdRestoration + "']").attr("md5");
9395 current_restore = null;
9396
9397 if (startRestoreProcess == false) {
9398 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9399 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9400 }
9401
9402 isMigrationLocked(function (isNotLocked) {
9403 resetLogs(true, function () {
9404 fixHtaccess(function () {
9405 if (isNotLocked) {
9406 curdivs = 0;
9407 refreshLogAndProgressRestore();
9408 restoreOnGoing = true;
9409 cli_quickmigration = true;
9410
9411 $.bmi.modal("pre-restore-modal").close();
9412 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9413 $.bmi.modal("restore-progress-modal").open();
9414
9415
9416 performDropboxDownload(startRestoreProcess, currentDropboxIdRestoration, 0, false, md5);
9417 }
9418 });
9419 });
9420 });
9421 }
9422
9423 function performDropboxDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = false, secret = false) {
9424
9425 $.bmi.ajax('download-dropbox-backup', {
9426
9427 fileId: fileId,
9428 step: step,
9429 size: size,
9430 md5: md5,
9431 filename: originalFilename,
9432 writepath: writepath,
9433 chunksize: chunksize,
9434 startRestoreProcess: startRestoreProcess,
9435 secret: secret
9436
9437 }).then(function(res) {
9438
9439 if (res.status == 'success') {
9440
9441 if (typeof res.size != 'undefined') size = res.size;
9442 if (typeof res.md5 != 'undefined') md5 = res.md5;
9443 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9444 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9445 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9446 if (typeof res.secret != 'undefined') secret = res.secret;
9447
9448 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9449
9450 step++;
9451 performDropboxDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9452
9453
9454 } else if (res.finished == 'true') {
9455
9456 cli_quickmigration = true;
9457 current_restore = res.filename;
9458 refreshLogAndProgressRestore(false);
9459
9460 $.bmi.reloadBackups(function () {
9461 setTimeout(function () {
9462 clearInterval(iprogres);
9463 $(
9464 "#restore-progress-modal .progress-active-bar",
9465 )[0].style.width = 0 + "%";
9466 $(
9467 "#restore-progress-modal .progress-percentage",
9468 )[0].style.left = 0 + "%";
9469 $("#restore-progress-modal .progress-percentage")[0].innerText =
9470 0 + "%";
9471 $("#restore_current_step").text(
9472 $("#bmi-restoring-prepare").text(),
9473 );
9474 }, 600);
9475 });
9476
9477 }
9478
9479 } else {
9480
9481 $.bmi._msg(res);
9482 restoreFailed();
9483 console.error(res);
9484
9485 }
9486
9487 }).catch(function(error) {
9488
9489 restoreFailed(error);
9490
9491 });
9492
9493 }
9494
9495
9496 function downloadS3(startRestoreProcess = true, provider = 'aws') {
9497 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9498 let currentS3IdRestoration = current_restore.split("?#" + provider + "#_")[1];
9499 let md5 = $("#bmi_restore_tbody").find("tr[" + provider + "-id='" + currentS3IdRestoration + "']").attr("md5");
9500 current_restore = null;
9501
9502 isMigrationLocked(function (isNotLocked) {
9503 resetLogs(true, function () {
9504 fixHtaccess(function () {
9505 if (isNotLocked) {
9506 curdivs = 0;
9507 refreshLogAndProgressRestore();
9508 restoreOnGoing = true;
9509 cli_quickmigration = true;
9510
9511 $.bmi.modal("pre-restore-modal").close();
9512 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9513 $.bmi.modal("restore-progress-modal").open();
9514
9515 performS3Download(startRestoreProcess, provider, currentS3IdRestoration, 0, false, md5);
9516 }
9517 });
9518 });
9519 });
9520 }
9521
9522 function performS3Download(startRestoreProcess = true, provider = 'aws', fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9523 $.bmi.ajax('download-cloud-backup', {
9524 storage: 's3',
9525 provider: provider,
9526 fileId: fileId,
9527 step: step,
9528 size: size,
9529 md5: md5,
9530 filename: originalFilename,
9531 writepath: writepath,
9532 chunksize: chunksize,
9533 startRestoreProcess: startRestoreProcess,
9534 secret: secret
9535 }).then(function (res) {
9536 if (res.status == 'success') {
9537 if (typeof res.size != 'undefined') size = res.size;
9538 if (typeof res.md5 != 'undefined') md5 = res.md5;
9539 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9540 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9541 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9542 if (typeof res.secret != 'undefined') secret = res.secret;
9543 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9544 step++;
9545 performS3Download(startRestoreProcess, provider, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9546 } else if (res.finished == 'true') {
9547 cli_quickmigration = true;
9548 current_restore = res.filename;
9549 refreshLogAndProgressRestore(false);
9550 $.bmi.reloadBackups(function () {
9551 setTimeout(function () {
9552 clearInterval(iprogres);
9553 $(
9554 "#restore-progress-modal .progress-active-bar",
9555 )[0].style.width = 0 + "%";
9556 $(
9557 "#restore-progress-modal .progress-percentage",
9558 )[0].style.left = 0 + "%";
9559 $("#restore-progress-modal .progress-percentage")[0].innerText =
9560 0 + "%";
9561 $("#restore_current_step").text(
9562 $("#bmi-restoring-prepare").text(),
9563 );
9564 }, 600);
9565 });
9566 }
9567 } else {
9568 $.bmi._msg(res);
9569 restoreFailed();
9570 console.error(res);
9571 }
9572 }).catch(function (error) {
9573 restoreFailed(error);
9574 });
9575 }
9576
9577 $.bmi.downloadS3 = downloadS3;
9578
9579 function performOneDriveDownload(startRestoreProcess = true,fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9580
9581 $.bmi.ajax('download-cloud-backup', {
9582
9583 storage: 'onedrive',
9584 fileId: fileId,
9585 step: step,
9586 size: size,
9587 md5: md5,
9588 filename: originalFilename,
9589 writepath: writepath,
9590 chunksize: chunksize,
9591 startRestoreProcess: startRestoreProcess,
9592 secret: secret
9593
9594 }).then(function (res) {
9595
9596 if (res.status == 'success') {
9597
9598 if (typeof res.size != 'undefined') size = res.size;
9599 if (typeof res.md5 != 'undefined') md5 = res.md5;
9600 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9601 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9602 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9603 if (typeof res.secret != 'undefined') secret = res.secret;
9604
9605 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9606
9607 step++;
9608 performOneDriveDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9609
9610 } else if (res.finished == 'true') {
9611 cli_quickmigration = true;
9612 current_restore = res.filename;
9613 refreshLogAndProgressRestore(false);
9614
9615 $.bmi.reloadBackups(function () {
9616 setTimeout(function () {
9617 clearInterval(iprogres);
9618 $(
9619 "#restore-progress-modal .progress-active-bar",
9620 )[0].style.width = 0 + "%";
9621 $(
9622 "#restore-progress-modal .progress-percentage",
9623 )[0].style.left = 0 + "%";
9624 $("#restore-progress-modal .progress-percentage")[0].innerText =
9625 0 + "%";
9626 $("#restore_current_step").text(
9627 $("#bmi-restoring-prepare").text(),
9628 );
9629 }, 600);
9630 });
9631
9632 }
9633 } else {
9634 $.bmi._msg(res);
9635 restoreFailed();
9636 console.error(res);
9637 }
9638 })
9639 .catch(function (error) {
9640 restoreFailed(error);
9641 });
9642 }
9643
9644 function performBackupBlissDownload(startRestoreProcess = true,fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9645
9646 $.bmi.ajax('download-cloud-backup', {
9647
9648 storage: 'backupbliss',
9649 fileId: fileId,
9650 step: step,
9651 size: size,
9652 md5: md5,
9653 filename: originalFilename,
9654 writepath: writepath,
9655 chunksize: chunksize,
9656 startRestoreProcess: startRestoreProcess,
9657 secret: secret
9658
9659 }).then(function (res) {
9660
9661 if (res.status == 'success') {
9662
9663 if (typeof res.size != 'undefined') size = res.size;
9664 if (typeof res.md5 != 'undefined') md5 = res.md5;
9665 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9666 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9667 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9668 if (typeof res.secret != 'undefined') secret = res.secret;
9669
9670 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9671
9672 step++;
9673 performBackupBlissDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9674
9675 } else if (res.finished == 'true') {
9676 cli_quickmigration = true;
9677 current_restore = res.filename;
9678 refreshLogAndProgressRestore(false);
9679
9680 $.bmi.reloadBackups(function () {
9681 setTimeout(function () {
9682 clearInterval(iprogres);
9683 $(
9684 "#restore-progress-modal .progress-active-bar",
9685 )[0].style.width = 0 + "%";
9686 $(
9687 "#restore-progress-modal .progress-percentage",
9688 )[0].style.left = 0 + "%";
9689 $("#restore-progress-modal .progress-percentage")[0].innerText =
9690 0 + "%";
9691 $("#restore_current_step").text(
9692 $("#bmi-restoring-prepare").text(),
9693 );
9694 }, 600);
9695 });
9696
9697 }
9698 } else {
9699 $.bmi._msg(res);
9700 restoreFailed();
9701 console.error(res);
9702 }
9703 })
9704 .catch(function (error) {
9705 restoreFailed(error);
9706 });
9707 }
9708
9709 function downloadSFTP(startRestoreProcess = true) {
9710 console.log("downloadSFTP");
9711 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9712 let currentSFTPIdRestoration = current_restore.split("?#sftp#_")[1];
9713 let md5 = $("#bmi_restore_tbody").find("tr[sftp-id='" + currentSFTPIdRestoration + "']").attr("md5");
9714 current_restore = null;
9715
9716 isMigrationLocked(function (isNotLocked) {
9717 resetLogs(true, function () {
9718 fixHtaccess(function () {
9719 if (isNotLocked) {
9720 curdivs = 0;
9721 refreshLogAndProgressRestore();
9722 restoreOnGoing = true;
9723 cli_quickmigration = true;
9724
9725 $.bmi.modal("pre-restore-modal").close();
9726 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9727 $.bmi.modal("restore-progress-modal").open();
9728
9729 performSFTPDownload(startRestoreProcess, currentSFTPIdRestoration, 0, false, md5);
9730 }
9731 });
9732 });
9733 });
9734 }
9735
9736 function performSFTPDownload(startRestoreProcess = true, fileId = false, step = 0, size = false, md5 = false, originalFilename = false, writepath = false, chunksize = 1000000, secret = false) {
9737
9738 $.bmi.ajax('download-sftp-backup', {
9739
9740 fileId: fileId,
9741 step: step,
9742 size: size,
9743 md5: md5,
9744 filename: originalFilename,
9745 writepath: writepath,
9746 chunksize: chunksize,
9747 startRestoreProcess: startRestoreProcess,
9748 secret: secret
9749
9750 }).then(function (res) {
9751
9752 if (res.status == 'success') {
9753
9754 if (typeof res.size != 'undefined') size = res.size;
9755 if (typeof res.md5 != 'undefined') md5 = res.md5;
9756 if (typeof res.originalFilename != 'undefined') originalFilename = res.originalFilename;
9757 if (typeof res.writepath != 'undefined') writepath = res.writepath;
9758 if (typeof res.chunksize != 'undefined') chunksize = res.chunksize;
9759 if (typeof res.secret != 'undefined') secret = res.secret;
9760
9761 if (typeof res.finished != 'undefined' && res.finished != 'true') {
9762
9763 step++;
9764 performSFTPDownload(startRestoreProcess, fileId, step, size, md5, originalFilename, writepath, chunksize, secret);
9765
9766 } else if (res.finished == 'true') {
9767 cli_quickmigration = true;
9768 current_restore = res.filename;
9769 refreshLogAndProgressRestore(false);
9770
9771 $.bmi.reloadBackups(function () {
9772 setTimeout(function () {
9773 clearInterval(iprogres);
9774 $("#restore-progress-modal .progress-active-bar")[0].style.width = 0 + "%";
9775 $("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
9776 $("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
9777 $("#restore_current_step").text($("#bmi-restoring-prepare").text());
9778 }, 600);
9779 });
9780
9781 }
9782 } else {
9783 $.bmi._msg(res);
9784 restoreFailed();
9785 console.error(res);
9786 }
9787 })
9788 .catch(function (error) {
9789 restoreFailed(error);
9790 });
9791 }
9792
9793
9794 $.bmi.downloadSFTP = downloadSFTP;
9795
9796 $.bmi.downloadDropbox = downloadDropbox;
9797
9798
9799 function downloadMigration( startRestoreProcess = true) {
9800 current_restore = null;
9801 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9802
9803 if (startRestoreProcess == false) {
9804 $('#restore-progress-modal .title').text($('#bmi-download-progress-modal-title').text());
9805 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-download-warning').text());
9806 }
9807
9808 isMigrationLocked(function (isNotLocked) {
9809 resetLogs(true, function () {
9810 fixHtaccess(function () {
9811 if (isNotLocked) {
9812 let url = $("#bm-d-url").val();
9813 $("#restore_current_step").text(
9814 $("#bmi-downloading-remote").text(),
9815 );
9816 $.bmi
9817 .ajax("download-backup", {
9818 url: url,
9819 startRestoreProcess: startRestoreProcess
9820 })
9821 .then(function (res) {
9822 clearInterval(iprogres);
9823 clearTimeout(timeouter);
9824
9825 if (res.status === "success") {
9826 // Set backup to restore name
9827 current_restore = res.name;
9828
9829 // Refresh logs in such case
9830 cli_quickmigration = true;
9831 refreshLogAndProgressRestore(false);
9832
9833 // Reload backup list
9834 $.bmi.reloadBackups();
9835
9836 // setProgress(100, 300);
9837 setTimeout(function () {
9838 clearInterval(iprogres);
9839 $(
9840 "#restore-progress-modal .progress-active-bar",
9841 )[0].style.width = 0 + "%";
9842 $(
9843 "#restore-progress-modal .progress-percentage",
9844 )[0].style.left = 0 + "%";
9845 $(
9846 "#restore-progress-modal .progress-percentage",
9847 )[0].innerText = 0 + "%";
9848 $("#restore_current_step").text(
9849 $("#bmi-restoring-prepare").text(),
9850 );
9851 }, 600);
9852 } else if (res.status == "cli_download") {
9853 current_restore = ".cli_download";
9854 cli_quickmigration = true;
9855 refreshLogAndProgressRestore(false);
9856 } else if (res.status === "error") {
9857 restoreFailed();
9858 } else {
9859 $.bmi._msg(res);
9860 restoreFailed();
9861 }
9862 })
9863 .catch(function (error) {
9864 restoreFailed(error);
9865 });
9866
9867 $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9868
9869 curdivs = 0;
9870 restoreOnGoing = true;
9871 cli_quickmigration = true;
9872 refreshLogAndProgressRestore();
9873 $.bmi.modal("pre-restore-modal").close();
9874 $.bmi.modal("restore-progress-modal").open();
9875 }
9876 });
9877 });
9878 });
9879 }
9880
9881 $.bmi.downloadMigration = downloadMigration;
9882
9883 function restoreFailed(text = "") {
9884 $('#restore-progress-modal .title').text($('#bmi-restore-progress-modal-title').text());
9885 $('#restore-progress-modal .red-error-bg .red-warning').text($('#bmi-restore-progress-modal-warning').text());
9886 setTimeout(function () {
9887 $("#restore-progress-modal .progress-active-bar")[0].style.width =
9888 0 + "%";
9889 $("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
9890 $("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
9891 }, 500);
9892
9893 isMigrationLocked(
9894 function (isNotLocked) {
9895 if (isNotLocked) {
9896 setTimeout(function () {
9897 if (!$("#restore-progress-modal").hasClass("open")) return;
9898
9899 restoreOnGoing = false;
9900 cli_quickmigration = false;
9901 $("#restore-error-pre").text(
9902 $("#bmi-loading-translation").text().trim(),
9903 );
9904 $("#after-logs-sent-modal").attr("data-error-source", "migration");
9905
9906 let url = $("#BMI_BLOG_URL").text().trim();
9907 if (url.slice(-url.length) !== "/") url = url + "/";
9908
9909 // httpGet(url + '?backup-migration=PROGRESS_LOGS&progress-id=latest_migration.log&backup-id=current&t=' + +new Date(), function(res) {
9910 //
9911 // if (res == false) {
9912 // setTimeout(function () {
9913 // restoreFailed();
9914 // }, 1500);
9915 // } else {
9916 // let pre = $('#restore-error-pre')[0];
9917 // $('#restore-error-pre').text('');
9918 // curdivs = 0;
9919 // insertPre(res, pre);
9920 // }
9921 //
9922 // });
9923
9924 $.bmi.modal("restore-progress-modal").close();
9925 setupRestoreErrorOptions().then(function () {
9926 $.bmi.modal("error-modal").open();
9927 $.bmi.modal("error-modal").setParent("restore-progress-modal");
9928 });
9929 }, 1000);
9930 } else {
9931 setTimeout(function () {
9932 $.bmi.modal("restore-progress-modal").close();
9933 setupRestoreErrorOptions().then(function () {
9934 $.bmi.modal("error-modal").open();
9935 $.bmi.modal("error-modal").setParent("restore-progress-modal");
9936 });
9937 });
9938 }
9939 },
9940 true,
9941 false,
9942 );
9943 }
9944
9945 function restoreCLISuccess() {
9946 $.bmi.modal("restore-progress-modal").close();
9947 restoreExecutionTime = (new Date().getTime() - restoreStartTime) / 1000;
9948 if (restoreExecutionTime > 60)
9949 $.bmi.modal("supportive-restore-success-modal").open(fillRestoreLogs);
9950 else
9951 $.bmi.modal("restore-success-modal").open();
9952
9953 setTimeout(function () {
9954 $("#restore-progress-modal .progress-active-bar")[0].style.width =
9955 0 + "%";
9956 $("#restore-progress-modal .progress-percentage")[0].style.left = 0 + "%";
9957 $("#restore-progress-modal .progress-percentage")[0].innerText = 0 + "%";
9958 }, 500);
9959
9960 restoreCLI = false;
9961 }
9962
9963 function letsRestore(remote = false, secret = null) {
9964 let name = current_restore;
9965 if (!name || name.trim().length <= 0)
9966 return $.bmi.alert("warning", $("#bmi-no-file").text(), 3000);
9967
9968 if ($("#pre-restore-modal").hasClass("open"))
9969 $.bmi.modal("pre-restore-modal").close();
9970
9971 if (!$("#restore-progress-modal").hasClass("open"))
9972 $.bmi.modal("restore-progress-modal").open();
9973
9974 if (!remote) $("#restore-live-log-wrapper").find("pre")[0].innerText = "";
9975
9976 if (secret == null) {
9977 if (!remote) curdivs = 0;
9978 restoreOnGoing = true;
9979 clearTimeout(timeouter);
9980
9981 restoreStartTime = new Date().getTime();
9982 function initializeRestoration() {
9983 fixHtaccess(function () {
9984 getMigrationLogs(function () {
9985 runRestoreProcess(name, remote, secret);
9986 });
9987 });
9988 }
9989
9990 if (!remote) resetLogs(true, initializeRestoration);
9991 else initializeRestoration();
9992 }
9993 }
9994
9995 $.bmi.letsRestore = letsRestore;
9996
9997 function runRestoreProcess(
9998 name,
9999 remote,
10000 secret,
10001 tmpname = false,
10002 ignoreRunning = "false",
10003 options = {},
10004 ) {
10005 if (secret == null && remote !== true) {
10006 refreshLogAndProgressRestore(true);
10007 }
10008
10009 if (typeof options.storage != "undefined") {
10010 options.storage = options.storage.replace(/[\\\\/]+/g, "/");
10011 }
10012
10013 $.bmi
10014 .ajax("restore-backup", {
10015 file: name,
10016 remote: remote,
10017 secret: secret,
10018 ignoreRunning: ignoreRunning,
10019 tmpname: tmpname,
10020 options: options,
10021 })
10022 .then(function (res) {
10023 if (res.status === "cli") {
10024 autoLog = { l: res.login, u: res.url };
10025 restoreCLI = true;
10026
10027 let end_code = document.querySelectorAll(".hide_so_much");
10028 if (end_code && end_code.length > 0) {
10029 end_code = end_code[end_code.length - 1];
10030 let code_line = end_code.innerText;
10031
10032 if (code_line.includes("001")) {
10033 setTimeout(function () {
10034 restoreCLISuccess();
10035 restoreOnGoing = false;
10036 restoreCLI = false;
10037 }, 1000);
10038 } else {
10039 restoreFailed();
10040 }
10041 }
10042 } else if (res.status === "success") {
10043 autoLog = { l: res.login, u: res.url };
10044
10045 setTimeout(function () {
10046 clearInterval(iprogres);
10047 clearTimeout(timeouter);
10048 restoreOnGoing = false;
10049
10050 $.bmi.modal("restore-progress-modal").close();
10051 restoreExecutionTime = (new Date().getTime() - restoreStartTime) / 1000;
10052 if (restoreExecutionTime > 60)
10053 $.bmi.modal("supportive-restore-success-modal").open(fillRestoreLogs);
10054 else
10055 $.bmi.modal("restore-success-modal").open();
10056
10057 setTimeout(function () {
10058 $("#restore-progress-modal .progress-active-bar")[0].style.width =
10059 0 + "%";
10060 $("#restore-progress-modal .progress-percentage")[0].style.left =
10061 0 + "%";
10062 $("#restore-progress-modal .progress-percentage")[0].innerText =
10063 0 + "%";
10064 }, 500);
10065 }, 1500);
10066 } else if (res.status === "secret") {
10067 res.options.step = parseInt(res.options.step) + 1;
10068 runRestoreProcess(
10069 name,
10070 remote,
10071 res.secret,
10072 res.tmp,
10073 "true",
10074 res.options,
10075 );
10076 } else if (res.status === "restore_ongoing") {
10077 if (typeof res.options.firstDB != "undefined") {
10078 res.options.firstDB = false;
10079 }
10080
10081 if (typeof res.options.dbFinished != "undefined") {
10082 if (
10083 res.options.dbFinished === true ||
10084 res.options.dbFinished === "true" ||
10085 res.options.dbFinished === "1"
10086 ) {
10087 res.options.step = parseInt(res.options.step) + 1;
10088 }
10089 } else if (typeof res.options.dbConvertionFinished != "undefined") {
10090 if (
10091 res.options.dbConvertionFinished === "true" ||
10092 res.options.dbConvertionFinished === true ||
10093 res.options.dbConvertionFinished === "1"
10094 ) {
10095 res.options.step = parseInt(res.options.step) + 1;
10096 }
10097 } else if (typeof res.options.replaceFinished != "undefined") {
10098 if (
10099 res.options.replaceFinished === "true" ||
10100 res.options.replaceFinished === true ||
10101 res.options.replaceFinished === "1"
10102 ) {
10103 res.options.step = parseInt(res.options.step) + 1;
10104 }
10105 } else {
10106 res.options.step = parseInt(res.options.step) + 1;
10107
10108 if (
10109 (res.options.step == 4 || res.options.step == "4") &&
10110 typeof res.options.repeat_export != "undefined"
10111 ) {
10112 if (
10113 res.options.repeat_export === true ||
10114 res.options.repeat_export === "true" ||
10115 res.options.repeat_export === "1"
10116 ) {
10117 res.options.step = 3;
10118 res.options.firstExtract = "false";
10119 }
10120 }
10121 }
10122
10123 setTimeout(
10124 function () {
10125 runRestoreProcess(
10126 name,
10127 remote,
10128 res.secret,
10129 res.tmp,
10130 "true",
10131 res.options,
10132 );
10133 },
10134 Math.floor(Math.random() * (523 - 330)) + 330,
10135 );
10136 } else if (res.status === "error") {
10137 setTimeout(function () {
10138 clearInterval(iprogres);
10139 clearTimeout(timeouter);
10140 console.error(res);
10141
10142 restoreFailed();
10143 }, 1000);
10144 } else {
10145 $.bmi.modal("pre-restore-modal").close();
10146 $.bmi.modal("restore-progress-modal").close();
10147 $.bmi._msg(res);
10148 }
10149 })
10150 .catch(function (error) {
10151 console.error(error);
10152 restoreFailed(error);
10153 });
10154 }
10155
10156 function getAndInsertDynamicNames() {
10157 $.bmi
10158 .ajax("get-dynamic-names", {})
10159 .then(function (res) {
10160 let rules = res.data;
10161
10162 $("#bmi_exclusion_rules").text("");
10163 if (typeof rules === "undefined") return;
10164 if (typeof rules.length === "undefined") return;
10165 for (let i = 0; i < rules.length; ++i) {
10166 let row = $(".exclusion_template").clone();
10167 row.removeClass("exclusion_template");
10168
10169 row.find(".exclusion_txt").val(rules[i].txt);
10170
10171 let posdd = row.find(".exclusion_position").find("select");
10172 let whrdd = row.find(".exclusion_where").find("select");
10173
10174 if (!posdd) continue;
10175 else posdd[0].setAttribute("data-def", rules[i].pos);
10176
10177 if (!whrdd) continue;
10178 else whrdd[0].setAttribute("data-def", rules[i].whr);
10179
10180 $("#bmi_exclusion_rules").append(row);
10181
10182 if (
10183 row.find(".exclusion_position").find(".bmi-dropdown").length > 0
10184 ) {
10185 $.bmi.setOption(
10186 row.find(".exclusion_position").find(".bmi-dropdown"),
10187 null,
10188 rules[i].pos,
10189 );
10190 }
10191
10192 if (row.find(".exclusion_where").find(".bmi-dropdown").length > 0) {
10193 $.bmi.setOption(
10194 row.find(".exclusion_where").find(".bmi-dropdown"),
10195 null,
10196 rules[i].whr,
10197 );
10198 }
10199 }
10200
10201 $("#dynamic-fpaths-names").text(res["dynamic-fpaths-names"].join("\n"));
10202 $("#dynamic-dpaths-names").text(res["dynamic-dpaths-names"].join("\n"));
10203 })
10204 .catch(function (error) {
10205 //
10206 });
10207 }
10208
10209 $("#bmi_support_chat").on("click", function () {
10210 if ($("#support-bmi").length === 0) {
10211 $("#bmi").append(
10212 '<script id="support-bmi" src="' +
10213 $("#bmi-support-url-translation").val() +
10214 '" async></script>',
10215 );
10216 setTimeout(function () {
10217 $("#bmi_support_chat").hide();
10218 }, 100);
10219 var loaded = false;
10220 let loadinter = setInterval(function () {
10221 if (loaded == true) clearInterval(loadinter);
10222 if (typeof window.jivo_api !== "undefined") {
10223 window.jivo_api.open();
10224 loaded = true;
10225 }
10226 }, 30);
10227 }
10228 });
10229
10230 function getAllSelectedBackups() {
10231 return $("#bmi_restore_tbody").find('input[type="checkbox"]:checked');
10232 }
10233
10234 // del-all-btn-wrp
10235 $("#bmi_restore_tbody").on("change", 'input[type="checkbox"]', function (e) {
10236 let $selected = getAllSelectedBackups().length;
10237 if ($selected > 0) {
10238 $(".del-all-btn-wrp").show(300);
10239 } else $(".del-all-btn-wrp").hide(300);
10240
10241 if (
10242 $selected == $("#bmi_restore_tbody").find('input[type="checkbox"]').length
10243 ) {
10244 $("#backups-select-all").prop("checked", true);
10245 } else {
10246 $("#backups-select-all").prop("checked", false);
10247 }
10248 });
10249
10250 $("#fix-uname-issues").on("click", function (e) {
10251 e.preventDefault();
10252 $.bmi
10253 .ajax("fix_uname_issues", {})
10254 .then(function (res) {
10255 $.bmi.alert("success", $("#bmi-default-success").text(), 3000);
10256 })
10257 .catch(function (error) {
10258 $.bmi.alert("error", $("#bmi-default-fail").text(), 3000);
10259 });
10260 });
10261
10262 $("#revert-uname-issues").on("click", function (e) {
10263 e.preventDefault();
10264 $.bmi
10265 .ajax("revert_uname_issues", {})
10266 .then(function (res) {
10267 $.bmi.alert("success", $("#bmi-default-success").text(), 3000);
10268 })
10269 .catch(function (error) {
10270 $.bmi.alert("error", $("#bmi-default-fail").text(), 3000);
10271 });
10272 });
10273
10274 $("#backups-select-all").on("change", function () {
10275 if (this.checked === true) {
10276 $("#bmi_restore_tbody")
10277 .find('input[type="checkbox"]')
10278 .prop("checked", true);
10279 } else {
10280 $("#bmi_restore_tbody")
10281 .find('input[type="checkbox"]')
10282 .prop("checked", false);
10283 }
10284
10285 let $selected = getAllSelectedBackups().length;
10286 if ($selected > 0) {
10287 $(".del-all-btn-wrp").show(300);
10288 } else $(".del-all-btn-wrp").hide(300);
10289 });
10290
10291 $(".lrn-mr-btn, .closer-learn-more").on("click", function () {
10292 if ($(".learn_more_about_cron").hasClass("open")) {
10293 $(".learn_more_about_cron").removeClass("open");
10294 $(".learn_more_about_cron").hide(300);
10295 $(".lrn-mr-btn").show();
10296 $(".lrn-mr-btn").css({
10297 opacity: 0,
10298 });
10299 $(".lrn-mr-btn").animate(
10300 {
10301 opacity: 1,
10302 },
10303 300,
10304 );
10305 } else {
10306 $(".learn_more_about_cron").addClass("open");
10307 $(".learn_more_about_cron").show(300);
10308 $(".lrn-mr-btn").css({
10309 opacity: 1,
10310 });
10311 $(".lrn-mr-btn").animate(
10312 {
10313 opacity: 0,
10314 },
10315 300,
10316 function () {
10317 $(".lrn-mr-btn").hide();
10318 },
10319 );
10320 }
10321 });
10322
10323 $(".bmi-error-toggle").on("click", function () {
10324 let parent = $(this).closest(".error-noticer");
10325 let parentId = parent.attr("id");
10326 let errorBody = $("#" + parentId + " .error-body");
10327 if ($(errorBody).hasClass("open")) {
10328 $(errorBody).hide(300);
10329 $(errorBody).removeClass("open");
10330 $("#" + parentId + " .bmi-error-toggle").text($("#" + parentId + " .bmi-error-toggle").data("expand"));
10331 } else {
10332 $(errorBody).show(300);
10333 $(errorBody).addClass("open");
10334 $("#" + parentId + " .bmi-error-toggle").text($("#" + parentId + " .bmi-error-toggle").data("collapse"));
10335 }
10336 });
10337
10338 function runTimerST() {
10339 let time = parseInt($("#server-time-auto").attr("data-time")) * 1000;
10340 let date = new Date(time);
10341 $("#server-time-auto").text(date.toUTCString());
10342 setInterval(function () {
10343 time += 1000;
10344 date = new Date(time);
10345 $("#server-time-auto").text(date.toUTCString());
10346 }, 1000);
10347
10348 if ($("#ex_b_fs").is(":checked")) $("#bmi__collon").show();
10349 else $("#bmi__collon").hide();
10350 }
10351
10352 $("#bmi_send_test_mail").on("click", function (e) {
10353 e.preventDefault();
10354 $.bmi
10355 .ajax("send-test-mail", {})
10356 .then(function (res) {
10357 $.bmi.alert("success", $("#bmi-email-success").text(), 3000);
10358 })
10359 .catch(function (error) {
10360 $.bmi.alert("error", $("#bmi-email-fail").text(), 3000);
10361 });
10362 });
10363
10364 $("#bmi-error-dismiss").on("click", function () {
10365 let parent = $(this).closest(".error-noticer");
10366 let parentId = parent.attr("id");
10367 $("#" + parentId).hide(300);
10368 setTimeout(function () {
10369 $("#" + parentId).remove();
10370 }, 330);
10371
10372 $.bmi
10373 .ajax("dismiss-error-notice", {
10374 option_id: parentId,
10375 })
10376 .then(function (res) {})
10377 .catch(function (error) {});
10378 });
10379
10380 $(".deleteAllSelected").on("click", function () {
10381 let isCloud = false;
10382 let $selected = getAllSelectedBackups(),
10383 names = [];
10384 latest_delete_cloud = {};
10385 let notOnLocal = [];
10386
10387
10388 for (let i = 0; i < $selected.length; ++i) {
10389 let tr = $selected[i].closest("tr");
10390 let notOnLocalTr = $(tr).data("is-local") == "no" ? true : false;
10391 let name = tr.querySelector(".br_name").innerText;
10392 names.push(name);
10393
10394 if (notOnLocalTr) {
10395 notOnLocal.push(name);
10396 }
10397
10398 latest_delete_cloud[name] = {
10399 md5: tr.getAttribute("md5"),
10400 gid: tr.getAttribute("gdrive-id"),
10401 };
10402
10403 isCloud = Array.from(tr.querySelectorAll('[class*="strg-"]')).filter(el => !el.classList.contains('strg-local')).some(el => el.classList.contains('img-green'));
10404 }
10405
10406 $("#delete-confirm-modal").find(".text1").hide();
10407 $("#delete-confirm-modal").find(".text4").hide();
10408 $("#delete-confirm-modal").find(".text3").hide();
10409 $("#delete-confirm-modal").find(".text2").show();
10410
10411 let count = names.length;
10412
10413 if (count <= 0) return;
10414 else {
10415 $(".backup-multiple-del-count").text(count);
10416
10417 if (count > 1) {
10418 $(".del-only-one").hide();
10419 $(".del-more-than-one").show();
10420 } else {
10421 $(".del-more-than-one").hide();
10422 $(".del-only-one").show();
10423 }
10424 }
10425
10426 latest_delete = names;
10427
10428 if (isCloud) $(".bmi-cloud-removal").show();
10429 else $(".bmi-cloud-removal").hide();
10430 $("#remove-cloud-backup-as-well")[0].checked = false;
10431
10432 if (isCloud && notOnLocal.length === $selected.length) {
10433 $("#remove-cloud-backup-as-well")[0].checked = true;
10434 $(".bmi-cloud-removal").hide();
10435 $("#delete-confirm-modal").find(".text1").hide();
10436 $("#delete-confirm-modal").find(".text2").hide();
10437 $("#delete-confirm-modal").find(".text4").hide();
10438 $("#delete-confirm-modal").find(".text3").show();
10439 }
10440
10441 $.bmi.modal("delete-confirm-modal").open();
10442 });
10443
10444 $("#load-more-backups").on("click", function (e) {
10445 e.preventDefault();
10446 $.bmi.showMoreBackups();
10447 });
10448
10449 function toggleFormatTip(e) {
10450 e.preventDefault();
10451 $("#format-tip-wrp")[0].style.minWidth = "calc(100% - 120px)";
10452
10453 if ($("#format-tip-wrp")[0].style.display === "none") {
10454 $("#format-tip-wrp").show(300);
10455 } else {
10456 $("#format-tip-wrp").hide(300);
10457 }
10458 }
10459
10460 $("#show-format-tip").on("click", toggleFormatTip);
10461 $("#hide-format-tip").on("click", toggleFormatTip);
10462
10463 $(".bmi-review-btn").on("click", function (e) {
10464 e.preventDefault();
10465
10466 var url = $(this).attr("href");
10467
10468 $.bmi.ajax("clicked-on-plugin-review", {}).then(function (res) {
10469 $(".bmi-ask-for-review").hide(300);
10470 window.open(url, "_blank");
10471 });
10472 });
10473
10474 $(".go-to-marbs").on("click", function (e) {
10475 e.preventDefault();
10476 document.getElementById("marbs").click();
10477 $.bmi.modal().closeAll();
10478 });
10479
10480 $(".go-to-stgng").on("click", function (e) {
10481 e.preventDefault();
10482 document.getElementById("stgng").click();
10483 $.bmi.modal().closeAll();
10484 });
10485
10486 $(".site-reloader").on("click", function () {
10487 element_enable_crons = [
10488 "choose-auto-backup-interval",
10489 "weekly-auto-backup-switch",
10490 ];
10491 let element_id = $(this).attr("id");
10492 let crons_enabled = element_enable_crons.includes(element_id)
10493 ? "&crons=true"
10494 : null;
10495 let url = autoLog.u;
10496 if (url.slice(-url.length) !== "/") url = url + "/";
10497
10498 let url_final =
10499 url +
10500 "?backup-migration=AFTER_RESTORE&backup-id=" +
10501 autoLog.l +
10502 "&progress-id=4u70L051n&t=" +
10503 crons_enabled +
10504 +new Date() +
10505 "&sk=" +
10506 $("#BMI_SECRET_KEY").text().trim();
10507 window.location = url_final;
10508 });
10509
10510 $(".get-file-database-sizes").on("click", function (e) {
10511 e.preventDefault();
10512
10513 let resources = [
10514 "plugins",
10515 "uploads",
10516 "themes",
10517 "contents_others",
10518 "wordpress",
10519 "database",
10520 ];
10521 resetSpinners(resources, true);
10522
10523 saveBtnEventHandler(false, "save-file-config");
10524 });
10525
10526 $("#bmi-pro-storage-gdrive-toggle").on("change", function (e) {
10527 if (!$("#bmi-pro-storage-gdrive-toggle").is(":checked")) {
10528 $("td.br_stroage.center").addClass("bmi-gdrive-disabled");
10529 } else {
10530 $("td.br_stroage.center").removeClass("bmi-gdrive-disabled");
10531 }
10532 });
10533
10534 $(".open-logs-modal-url").on("click", function () {
10535 let parentId = $(this).closest(".bmi-modal").attr("id");
10536 $.bmi.modal("logs-modal").setParent(parentId);
10537 $.bmi.modal(parentId).close();
10538 $.bmi.modal("freeze-loading-modal").open();
10539 setupLogsModal();
10540 });
10541
10542 $(".skip-share-logs-after-restore, .shared-log-after-restore").on("click", function () {
10543 $.bmi.modal("supportive-restore-success-modal").close();
10544 $.bmi.modal("supportive-restore-success-cont-modal").open();
10545 });
10546
10547
10548 $(".try-in-different-way").on("click", tryInDifferentWay);
10549
10550 function refreshBBStorageInfo(silent=false) {
10551 $('.refresh-img').addClass("loading");
10552 $.bmi.ajax('bb-storage-info').then(function (res) {
10553
10554 if (res.status == 'success') {
10555 $(".bb-storage-amount").text(res.data.storage_info.total_space_humanized);
10556 $(".bb-storage-used").text(res.data.storage_info.used_space_humanized);
10557 $(".bb-storage-used-percent").text("("+res.data.storage_info.used_space_percent+"%)");
10558 } else {
10559 if (!silent)
10560 $.bmi.alert('error', res.message);
10561
10562 }
10563
10564 $('.refresh-img').removeClass("loading");
10565
10566 }).catch(function (error) {
10567
10568 $('.refresh-img').removeClass("loading");
10569 if (!silent)
10570 $.bmi.alert('error', 'There was an error contacting the plugin backend.');
10571
10572 });
10573 }
10574
10575 refreshBBStorageInfo(true);
10576
10577 $(".refresh-bb-storage").on("click", function(e){
10578 e.preventDefault();
10579 refreshBBStorageInfo();
10580 });
10581
10582 $("#bb-upload-fail-dismiss").on("click", function(e){
10583 e.preventDefault();
10584 $.bmi
10585 .ajax("dismiss-error-notice", {
10586 option_id: "backupbliss-dismiss-upload-issue",
10587 })
10588 .then(function (res) {})
10589 .catch(function (error) {});
10590
10591 $("#bb-warning-notice").hide(300);
10592 setTimeout(function () {
10593 $("#bb-warning-notice").remove();
10594 }, 330);
10595 });
10596
10597 $(".bb-disconnect").on("click", function (e) {
10598 e.preventDefault();
10599 $.bmi.modal("bb-disconnect-modal").open();
10600 });
10601
10602 $(".bb-disconnect-cancel").on("click", function (e) {
10603 e.preventDefault();
10604 $.bmi.modal("bb-disconnect-modal").close();
10605 });
10606
10607 $(".bb-disconnect-btn").on("click", function(e){
10608 e.preventDefault();
10609 $.bmi.modal("bb-disconnect-modal").close();
10610 $.bmi.modal("freeze-loading-modal").open();
10611 $.bmi.ajax('bb-disconnect').then(function (res) {
10612
10613 if (res.status == 'success') {
10614 $(".how-it-works").show();
10615 $(".youre-connected").hide();
10616 } else {
10617
10618 $.bmi.alert('error', res.message);
10619
10620 }
10621
10622 $.bmi.modal('freeze-loading-modal').close();
10623
10624 }).catch(function (error) {
10625
10626 $.bmi.modal('freeze-loading-modal').close();
10627 $.bmi.alert('error', 'There was an error contacting the plugin backend.');
10628
10629 });
10630 });
10631
10632 $(".bb-connect").on("click", function(e){
10633 e.preventDefault();
10634 $.bmi.modal("freeze-loading-modal").open();
10635 $.bmi.ajax('bb-connect', {
10636 api_key: $(".api-key-input").val()
10637 }).then(function (res) {
10638
10639 if (res.status == 'success') {
10640 refreshBBStorageInfo();
10641 $(".how-it-works").hide();
10642 $(".youre-connected").show();
10643 $.bmi.reloadBackups();
10644 } else {
10645
10646 $.bmi.alert('error', res.message);
10647
10648 }
10649
10650 $.bmi.modal('freeze-loading-modal').close();
10651
10652 }).catch(function (error) {
10653
10654 $.bmi.modal('freeze-loading-modal').close();
10655 $.bmi.alert('error', 'There was an error contacting the plugin backend.');
10656
10657 });
10658 });
10659
10660 $('#dropbox-connect-btn').on('click', function (e) {
10661
10662 e.preventDefault();
10663 $.bmi.modal('freeze-loading-modal').open();
10664 $.bmi.ajax('get-dropbox-token').then(function (res) {
10665
10666 $.bmi.modal('freeze-loading-modal').close();
10667 if (typeof res.token != 'undefined') {
10668
10669 let url = 'https://authentication.backupbliss.com/v1/dropbox/connect';
10670 url += '?token=' + encodeURIComponent(res.token);
10671 url += '&redirect=' + encodeURIComponent(window.location.origin + window.location.pathname);
10672
10673 window.location.href = url;
10674
10675 } else {
10676
10677 $.bmi.alert('error', 'We could not generate individual token at this moment, please refresh page and try again [#Dropbox-02].');
10678
10679 }
10680
10681 }).catch(function (error) {
10682
10683 $.bmi.modal('freeze-loading-modal').close();
10684 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#Dropbox-01].');
10685
10686 });
10687
10688 });
10689
10690 $('#dropbox-disconnect-btn').on('click', function (e) {
10691
10692 e.preventDefault();
10693 $.bmi.modal('freeze-loading-modal').open();
10694 $.bmi.ajax('disconnect-dropbox').then(function (res) {
10695
10696 $.bmi.modal('freeze-loading-modal').close();
10697 if (typeof res.status != 'undefined' && res.status == 'success') {
10698
10699 window.location.reload();
10700
10701 } else {
10702
10703 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
10704
10705 }
10706
10707 }).catch(function (error) {
10708
10709 $.bmi.modal('freeze-loading-modal').close();
10710 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
10711
10712 });
10713
10714 });
10715
10716 function verifyDropboxConnection(silent = false) {
10717
10718 $.bmi.ajax('verify-dropbox-connection', { uri: window.location.host }).then(function (res) {
10719
10720 $('#dropbox-unauthenticated-box').show();
10721 $('#dropbox-authenticated-box').hide();
10722
10723 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
10724
10725 if (res.result == 'connected') {
10726
10727 $('#dropbox-unauthenticated-box').hide();
10728 $('#dropbox-authenticated-box').show();
10729
10730 }
10731
10732 } else if (typeof res.status != 'undefined' && res.status == 'error') {
10733
10734 if (!silent) {
10735 $.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].');
10736 }
10737
10738 } else {
10739 if (!silent) {
10740 $.bmi.alert('error', 'We could not verify Dropbox connection, please refresh page and try again [#DB-05].');
10741 }
10742
10743 }
10744
10745 }).catch(function (error) {
10746 if (!silent) {
10747 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#DB-08].');
10748 }
10749
10750 });
10751 }
10752
10753 $('#bmi-pro-storage-dropbox-toggle').on('change', function (e) {
10754
10755 e.preventDefault();
10756
10757 $('#dropbox-authenticated-box').hide();
10758 $('#dropbox-unauthenticated-box').show();
10759
10760 if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === true) {
10761 verifyDropboxConnection();
10762 }
10763
10764 });
10765
10766
10767 $('#bmip-dropbox-issues-dismiss').on("click", function () {
10768 $('#dropbox-issues').hide(300);
10769 setTimeout(function () {
10770 $('#dropbox-issues').remove();
10771 }, 330);
10772 $.bmi.ajax('dismiss-dropbox-notice', {}).then(function (res) { }).catch(function (error) { });
10773 });
10774
10775 $('#gdrive-connect-btn').on('click', function (e) {
10776
10777 e.preventDefault();
10778 $.bmi.modal('freeze-loading-modal').open();
10779 let backupDirectoryPath = $('#bmip-googledrive-path').val();
10780 $.bmi.ajax('get-gdrive-token',
10781 { backupDirectoryPath: backupDirectoryPath }
10782 ).then(function (res) {
10783
10784 $.bmi.modal('freeze-loading-modal').close();
10785 if (typeof res.token != 'undefined') {
10786
10787 let url = 'https://authentication.backupbliss.com/v1/gdrive/connect';
10788 url += '?token=' + encodeURIComponent(res.token);
10789 url += '&redirect=' + encodeURIComponent(window.location.origin + window.location.pathname);
10790
10791 window.location.href = url;
10792
10793 } else {
10794 if (typeof res.status != 'undefined' && res.status === 'msg') {
10795 $.bmi.alert('warning', res.why);
10796 } else {
10797 $.bmi.alert('error', 'We could not generate individual token at this moment, please refresh page and try again [#GD-02].');
10798 }
10799
10800 }
10801
10802 }).catch(function (error) {
10803
10804 $.bmi.modal('freeze-loading-modal').close();
10805 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-01].');
10806
10807 });
10808
10809 });
10810
10811 $('#gdrive-disconnect-btn').on('click', function (e) {
10812
10813 e.preventDefault();
10814 $.bmi.modal('freeze-loading-modal').open();
10815 $.bmi.ajax('disconnect-gdrive').then(function (res) {
10816
10817 $.bmi.modal('freeze-loading-modal').close();
10818 if (typeof res.status != 'undefined' && res.status == 'success') {
10819
10820 window.location.reload();
10821
10822 } else {
10823
10824 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again [#GD-03].');
10825
10826 }
10827
10828 }).catch(function (error) {
10829
10830 $.bmi.modal('freeze-loading-modal').close();
10831 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-04].');
10832
10833 });
10834
10835 });
10836
10837 function verifyGDriveConnection(silent = false) {
10838
10839 $.bmi.ajax('verify-gdrive-connection', { uri: window.location.host }).then(function (res) {
10840
10841 $('#gdrive-unauthenticated-box').show();
10842 $('#gdrive-authenticated-box').hide();
10843
10844 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
10845
10846 if (res.result == 'connected') {
10847
10848 $('#gdrive-unauthenticated-box').hide();
10849 $('#gdrive-authenticated-box').show();
10850
10851 }
10852
10853 } else if (typeof res.status != 'undefined' && res.status == 'error') {
10854
10855 if (!silent) {
10856 $.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].');
10857 }
10858
10859 } else {
10860 if (!silent) {
10861 $.bmi.alert('error', 'We could not verify Google Drive connection, please refresh page and try again [#GD-05].');
10862 }
10863
10864 }
10865
10866 }).catch(function (error) {
10867 if (!silent) {
10868 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-08].');
10869 }
10870
10871 });
10872
10873 }
10874
10875 $('#bmi-pro-storage-gdrive-toggle').on('change', function (e) {
10876
10877 e.preventDefault();
10878
10879 $('#gdrive-authenticated-box').hide();
10880 $('#gdrive-unauthenticated-box').show();
10881
10882 if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === true) {
10883 verifyGDriveConnection();
10884 }
10885
10886 });
10887
10888
10889 $('#ftp-connect-btn').on('click', function (e) {
10890 e.preventDefault();
10891 $.bmi.modal('freeze-loading-modal').open();
10892 let data = {};
10893 data['bmip-ftp-host'] = $('#bmip-ftp-host-ip').val();
10894 data['bmip-ftp-backup-dir'] = $('#bmip-ftp-backup-dir').val();
10895 data['bmip-ftp-host-port'] = $('#bmip-ftp-host-port').val();
10896 data['bmip-ftp-username'] = $('#bmip-ftp-user-name').val();
10897 data['bmip-ftp-password'] = $('#bmip-ftp-password').val();
10898
10899 $.bmi.ajax('get-ftp-config', data).then(function (res) {
10900
10901 $.bmi.modal('freeze-loading-modal').close();
10902
10903 if (typeof res.status != 'undefined' && res.status === 'success') {
10904 if (e) $.bmi.alert('success', $('#bmi-save-connect-ftp').text(), 3000);
10905
10906 setTimeout(function () {
10907 window.location.reload();
10908 }, 300);
10909
10910 } else {
10911 $.bmi.alert('error', res.msg);
10912 }
10913 }).catch(function (error) {
10914 $.bmi.modal('freeze-loading-modal').close();
10915 $.bmi.alert('error', '');
10916 });
10917 });
10918
10919 $('#ftp-disconnect-btn').on('click', function (e) {
10920
10921 e.preventDefault();
10922 $.bmi.modal('freeze-loading-modal').open();
10923 $.bmi.ajax('disconnect-ftp').then(function (res) {
10924
10925 $.bmi.modal('freeze-loading-modal').close();
10926 if (typeof res.status != 'undefined' && res.status == 'success') {
10927
10928 window.location.reload();
10929
10930 } else {
10931
10932 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again [#GD-03].');
10933
10934 }
10935
10936 }).catch(function (error) {
10937
10938 $.bmi.modal('freeze-loading-modal').close();
10939 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection [#GD-04].');
10940
10941 });
10942
10943 });
10944
10945
10946 $('#aws-connect-btn').on('click', function (e) {
10947 e.preventDefault();
10948 $.bmi.modal('freeze-loading-modal').open();
10949 let data = {};
10950 data['access-key'] = $('#bmip-aws-access-key').val();
10951 data['secret-key'] = $('#bmip-aws-secret-key').val();
10952 data['bucket'] = $('#bmip-aws-bucket').val();
10953 data['sse'] = $('#bmip-aws-sse').is(':checked') === true ? $('#bmip-aws-sse').val().trim() : '';
10954 data['path'] = $('#bmip-aws-path').val();
10955 data['storage-class'] = $('[data-id="bmip-aws-storage-class"]').attr('data-selected');
10956 data['region'] = $('[data-id="bmip-aws-region"]').attr('data-selected');
10957
10958 $.bmi.ajax('save-aws-config', data).then(function (res) {
10959
10960 $.bmi.modal('freeze-loading-modal').close();
10961
10962 if (typeof res.status != 'undefined' && res.status === 'success') {
10963 if (e) $.bmi.alert('success', $('#bmi-save-connect-s3-success').text(), 3000);
10964 window.location.reload();
10965 } else {
10966 $.bmi.alert('error', res.msg);
10967 }
10968 }).catch(function (error) {
10969 $.bmi.modal('freeze-loading-modal').close();
10970 $.bmi.alert('error', 'There was an error while trying to connect to Amazon S3, please try again.');
10971 });
10972 });
10973
10974
10975 $('#aws-disconnect-btn').on('click', function (e) {
10976
10977 e.preventDefault();
10978 $.bmi.modal('freeze-loading-modal').open();
10979 $.bmi.ajax('disconnect-aws').then(function (res) {
10980
10981 $.bmi.modal('freeze-loading-modal').close();
10982 if (typeof res.status != 'undefined' && res.status == 'success') {
10983 window.location.reload();
10984 } else {
10985 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
10986 }
10987
10988 }).catch(function (error) {
10989
10990 $.bmi.modal('freeze-loading-modal').close();
10991 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
10992
10993 });
10994
10995 });
10996
10997 function verifyAWSConnection(silent = false) {
10998 $.bmi.ajax('verify-aws-connection', { uri: window.location.host}).then(function (res) {
10999 $('#aws-unauthenticated-box').show();
11000 $('#aws-authenticated-box').hide();
11001
11002
11003 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
11004 if (res.result == 'connected') {
11005 $('#aws-unauthenticated-box').hide();
11006 $('#aws-authenticated-box').show();
11007 $('#bmip-aws-path').val(res.configs['path']).attr('readonly', true);
11008 $('#bmip-aws-bucket').val(res.configs['bucket']).attr('readonly', true);
11009 $('.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');
11010 $('#storage-s3-row .region-container').hide();
11011 $('#storage-s3-row .aws-access-key-container').hide();
11012 $('#storage-s3-row .aws-secret-key-container').hide();
11013 $('#bmip-aws-sse').prop('checked', res.configs['sse'] === 'AES256').attr('disabled', true).css('cursor', 'not-allowed');
11014 $('#bmip-aws-sse').closest('.checkbox-container').css('cursor', 'not-allowed');
11015 } else {
11016 $('#aws-unauthenticated-box').show();
11017 $('#aws-authenticated-box').hide();
11018 }
11019 } else if (typeof res.status != 'undefined' && res.status == 'error') {
11020 if (!silent) {
11021 $.bmi.alert('error', 'We could not verify AWS S3 connection, some error happened during communication with API.');
11022 }
11023 } else {
11024 if (!silent) {
11025 $.bmi.alert('error', 'We could not verify AWS S3 connection, please refresh page and try again.');
11026 }
11027 }
11028 }).catch(function (error) {
11029 if (!silent) {
11030 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11031 }
11032 });
11033 }
11034
11035 $('#bmi-pro-storage-aws-toggle').on('change', function (e) {
11036 e.preventDefault();
11037 $('#aws-authenticated-box').hide();
11038 $('#aws-unauthenticated-box').show();
11039 if ($('#bmi-pro-storage-aws-toggle').is(':checked') === true) {
11040 verifyAWSConnection();
11041 }
11042 });
11043
11044 $('#wasabi-connect-btn').on('click', function (e) {
11045 e.preventDefault();
11046 $.bmi.modal('freeze-loading-modal').open();
11047 let data = {};
11048 data['access-key'] = $('#bmip-wasabi-access-key').val();
11049 data['secret-key'] = $('#bmip-wasabi-secret-key').val();
11050 data['bucket'] = $('#bmip-wasabi-bucket').val();
11051 data['path'] = $('#bmip-wasabi-path').val();
11052 data['region'] = $('[data-id="bmip-wasabi-region"]').attr('data-selected');
11053
11054 $.bmi.ajax('save-wasabi-config', data).then(function (res) {
11055
11056 $.bmi.modal('freeze-loading-modal').close();
11057
11058 if (typeof res.status != 'undefined' && res.status === 'success') {
11059 if (e) $.bmi.alert('success', $('#bmi-save-connect-s3-success').text(), 3000);
11060 window.location.reload();
11061 } else {
11062 $.bmi.alert('error', res.msg);
11063 }
11064 }).catch(function (error) {
11065 $.bmi.modal('freeze-loading-modal').close();
11066 $.bmi.alert('error', 'There was an error while trying to connect to Wasabi, please try again.');
11067 });
11068 });
11069
11070 $('#wasabi-disconnect-btn').on('click', function (e) {
11071
11072 e.preventDefault();
11073 $.bmi.modal('freeze-loading-modal').open();
11074 $.bmi.ajax('disconnect-wasabi').then(function (res) {
11075
11076 $.bmi.modal('freeze-loading-modal').close();
11077 if (typeof res.status != 'undefined' && res.status == 'success') {
11078 window.location.reload();
11079 } else {
11080 $.bmi.alert('error', 'We could not disconnect you at this moment, please refresh page and try again.');
11081 }
11082
11083 }).catch(function (error) {
11084
11085 $.bmi.modal('freeze-loading-modal').close();
11086 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11087
11088 });
11089
11090 });
11091
11092 function verifyWasabiConnection(silent = false) {
11093 $.bmi.ajax('verify-wasabi-connection', { uri: window.location.host}).then(function (res) {
11094 $('#wasabi-unauthenticated-box').show();
11095 $('#wasabi-authenticated-box').hide();
11096
11097
11098 if (typeof res.status != 'undefined' && typeof res.result != 'undefined' && res.status == 'success') {
11099 if (res.result == 'connected') {
11100 $('#wasabi-unauthenticated-box').hide();
11101 $('#wasabi-authenticated-box').show();
11102 $('#bmip-wasabi-path').val(res.configs['path']).attr('readonly', true);
11103 $('#bmip-wasabi-bucket').val(res.configs['bucket']).attr('readonly', true);
11104 $('#storage-wasabi-row .region-container').hide();
11105 $('#storage-wasabi-row .wasabi-access-key-container').hide();
11106 $('#storage-wasabi-row .wasabi-secret-key-container').hide();
11107 } else {
11108 $('#wasabi-unauthenticated-box').show();
11109 $('#wasabi-authenticated-box').hide();
11110 }
11111 } else if (typeof res.status != 'undefined' && res.status == 'error') {
11112 if (!silent) {
11113 $.bmi.alert('error', 'We could not verify Wasabi connection, some error happened during communication with API.');
11114 }
11115 } else {
11116 if (!silent) {
11117 $.bmi.alert('error', 'We could not verify Wasabi connection, please refresh page and try again.');
11118 }
11119 }
11120 }).catch(function (error) {
11121 if (!silent) {
11122 $.bmi.alert('error', 'Cannot send request to your server, please check your internet connection.');
11123 }
11124 });
11125 }
11126
11127 $('#bmi-pro-storage-wasabi-toggle').on('change', function (e) {
11128 e.preventDefault();
11129 $('#wasabi-authenticated-box').hide();
11130 $('#wasabi-unauthenticated-box').show();
11131 if ($('#bmi-pro-storage-wasabi-toggle').is(':checked') === true) {
11132 verifyWasabiConnection();
11133 }
11134 });
11135
11136 $('#bmi_restore_tbody').on('click', '.can-be-manually-uploaded', function (e) {
11137
11138 e.preventDefault();
11139 const classList = $(this).attr('class').split(/\s+/);
11140 let type = false;
11141 let md5 = $(this).closest('tr').attr('md5');
11142
11143 for (let cls of classList) {
11144 if (cls.startsWith('strg-')) {
11145 type = cls.replace('strg-', ''); // get the part after 'strg-'
11146 break;
11147 }
11148 }
11149
11150 $.bmi.ajax('manually-enqueue-upload', {
11151 type: type,
11152 md5: md5
11153 }).then(function (res) {
11154 if (res.status == 'success') {
11155 $(this).removeClass('can-be-manually-uploaded');
11156 $.bmi.alert('success', 'Backup will be added to the upload queue shortly.');
11157 } else {
11158 $.bmi.alert('error', res.msg || 'There was an error while trying to enqueue the backup for upload.');
11159 }
11160 });
11161
11162 });
11163
11164 $(window).on("bmi-preload-collapsed", function (e) {
11165
11166 const urlSearchParams = new URLSearchParams(window.location.search);
11167 const params = Object.fromEntries(urlSearchParams.entries());
11168
11169 if (typeof params.bmi_drive != 'undefined') {
11170 window.history.pushState('', window.title, window.location.pathname + '?page=backup-migration');
11171
11172
11173 if (typeof params.bmi_auth_status != 'undefined' && params.bmi_auth_status == 'error') {
11174 if (typeof params.bmi_error_reason != 'undefined') {
11175 if (params.bmi_drive == 'gdrive') {
11176 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].');
11177 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].');
11178 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].');
11179 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].');
11180 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].');
11181 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].');
11182 } else if (params.bmi_drive == 'dropbox') {
11183 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].');
11184 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].');
11185 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].');
11186 if (params.bmi_error_reason == '4') return $.bmi.alert('error', 'Error during Dropbox connection: Access has been denied by you during authorization. [#DB-14].');
11187 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].');
11188 if (params.bmi_error_reason == '6') return $.bmi.alert('error', 'Error during Dropbox connection: Access token and refresh token has been revoked. [#DB-16].');
11189 }
11190 }
11191 }
11192
11193 if (typeof params.bmi_token == 'undefined' || typeof params.bmi_secret == 'undefined' || typeof params.bmi_auth_status == 'undefined') {
11194 if (params.bmi_drive == 'gdrive') {
11195 return $.bmi.alert('error', 'Cannot save connection with Google Drive, due to insufficient details [#GD-09].');
11196 } else if (params.bmi_drive == 'dropbox') {
11197 return $.bmi.alert('error', 'Cannot save connection with Dropbox, due to insufficient details [#DB-09].');
11198 }
11199 }
11200
11201 if (params.bmi_auth_status != 'success') {
11202 if (params.bmi_drive == 'gdrive') {
11203 return $.bmi.alert('error', 'Cannot save connection with Google Drive, due to unknown response from Google API [#GD-10].');
11204 } else if (params.bmi_drive == 'dropbox') {
11205 return $.bmi.alert('error', 'Cannot save connection with Dropbox, due to unknown response from Dropbox API [#DB-10].');
11206 }
11207 }
11208
11209 $.bmi.modal('freeze-loading-modal').open();
11210 $('#bmi-external-storage-options').click();
11211
11212 if (params.bmi_drive == 'gdrive') {
11213 if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === false) {
11214 $('#bmi-pro-storage-gdrive-toggle').click();
11215 }
11216 } else if (params.bmi_drive == 'dropbox') {
11217 if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === false) {
11218 $('#bmi-pro-storage-dropbox-toggle').click();
11219 }
11220 }
11221
11222 if (params.bmi_drive == 'gdrive') {
11223 $.bmi.ajax('keep-gdrive-connection', { receivedToken: params.bmi_token, receivedClientID: params.bmi_secret }).then(function (res) {
11224
11225 $.bmi.modal('freeze-loading-modal').close();
11226
11227 setTimeout(function () {
11228 $('#bmi-pro-storage-gdrive-toggle')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11229
11230 setTimeout(function () {
11231 $('#gdrive-unauthenticated-box').fadeOut();
11232 $('#gdrive-authenticated-box').fadeIn();
11233 setTimeout(function () {
11234 $('#gdrive-authed-content-box')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11235 window.saveStorageWithoutClose = true;
11236 $('#storage-options').find('.save-btn').click();
11237 }, 300);
11238 }, 300);
11239 }, 300);
11240
11241 }).catch(function (error) {
11242
11243 $.bmi.modal('freeze-loading-modal').close();
11244 $.bmi.alert('error', 'Cannot save connection with Google Drive, please try again [#GD-06].');
11245
11246 });
11247 } else if (params.bmi_drive == 'dropbox') {
11248 $.bmi.ajax('keep-dropbox-connection', { receivedToken: params.bmi_token, receivedClientID: params.bmi_secret }).then(function (res) {
11249
11250 $.bmi.modal('freeze-loading-modal').close();
11251
11252 setTimeout(function () {
11253 $('#bmi-pro-storage-dropbox-toggle')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11254
11255 setTimeout(function () {
11256 $('#dropbox-unauthenticated-box').fadeOut();
11257 $('#dropbox-authenticated-box').fadeIn();
11258 setTimeout(function () {
11259 $('#dropbox-authed-content-box')[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
11260 window.saveStorageWithoutClose = true;
11261 $('#storage-options').find('.save-btn').click();
11262 }, 300);
11263 }, 300);
11264 }, 300);
11265
11266 }).catch(function (error) {
11267
11268 $.bmi.modal('freeze-loading-modal').close();
11269 $.bmi.alert('error', 'Cannot save connection with Dropbox, please try again [#DB-06].');
11270
11271 });
11272 } else {
11273 jQuery(window).trigger("bmi-preload-collapsed-pro", [params]);
11274 }
11275 }
11276
11277
11278 if ($('#bmi-pro-storage-dropbox-toggle').is(':checked') === true) {
11279 verifyDropboxConnection(true);
11280 }
11281
11282 if ($('#bmi-pro-storage-gdrive-toggle').is(':checked') === true) {
11283 verifyGDriveConnection(true);
11284 }
11285
11286 if ($('#bmi-pro-storage-aws-toggle').is(':checked') === true) {
11287 verifyAWSConnection(true);
11288 }
11289
11290 if ($('#bmi-pro-storage-wasabi-toggle').is(':checked') === true) {
11291 verifyWasabiConnection(true);
11292 }
11293
11294 });
11295
11296 // Currently not used
11297 $('.bmi-check-disk-space').on('click', function (e) {
11298 e.preventDefault();
11299
11300 // Prevent multiple clicks
11301 if (!$('.space-checking .loading').hasClass('hide_verbose')) return;
11302
11303 $('.space-checking .loading').removeClass('hide_verbose');
11304 // Hide all verbose messages if any shown
11305 if (!$('.checking-result .not-enough-space').hasClass('hide_verbose')) $('.checking-result .not-enough-space').addClass('hide_verbose');
11306 if (!$('.checking-result .enough-space').hasClass('hide_verbose')) $('.checking-result .enough-space').addClass('hide_verbose');
11307 if (!$('.checking-result .failed').hasClass('hide_verbose')) $('.checking-result .failed').addClass('hide_verbose');
11308
11309
11310 $.bmi.ajax('check-disk-space').then(function (res) {
11311 if (res.status == 'enough-space') {
11312 $('.space-checking .loading').addClass('hide_verbose');
11313 $('.checking-result .enough-space').removeClass('hide_verbose');
11314 } else if (res.status == 'not-enough-space') {
11315 $('.space-checking .loading').addClass('hide_verbose');
11316 $('.checking-result .not-enough-space').removeClass('hide_verbose');
11317 $('.checking-result .not-enough-space').find('.required-space').text(res.data['required']);
11318 $('.checking-result .not-enough-space').find('.available-space').text(res.data['available']);
11319 }
11320 }).catch(function (error) {
11321 console.error(error);
11322 $('.space-checking .loading').addClass('hide_verbose');
11323 $('.checking-result .failed').removeClass('hide_verbose');
11324 });
11325 });
11326
11327 async function tryInDifferentWay() {
11328 let errorParent = $.bmi.modal("error-modal").getParent();
11329 if (errorParent == "backup-progress-modal") {
11330 if (isNaN(getSelectedSize())) {
11331 setTimeout(tryInDifferentWay, 1000);
11332 }
11333 $("#cli-disable-others").prop("checked", true);
11334 $("#experimental-hard-timeout").prop("checked", true);
11335 $("#download-technique").prop("checked", true);
11336 $("#bmi-db-batching-backup").prop("checked", true);
11337 await saveOtherOptionsPromise();
11338
11339 $.bmi.modal($(this).closest(".bmi-modal").attr("id")).close();
11340 $("#start-entire-backup").click();
11341 } else if (errorParent == "restore-progress-modal") {
11342 $("#cli-disable-others").prop("checked", true);
11343 $("#file_limit_extraction_max").val("300");
11344 await saveOtherOptionsPromise();
11345
11346 $.bmi.modal($(this).closest(".bmi-modal").attr("id")).close();
11347 $("#restore-start-sure").click();
11348
11349 }
11350 }
11351
11352 function bmiSetFailureReasonsState(reasonCount) {
11353 const $plural = $(".there-are-reasons");
11354 const $single = $(".there-is-a-reason");
11355 const $none = $(".there-are-no-reasons");
11356 const $box = $(".debug-it-yourself");
11357 const $content = $box.find(".content");
11358
11359 // Hide all first
11360 $plural.hide();
11361 $single.hide();
11362 $none.hide();
11363
11364 if (reasonCount <= 0) {
11365 $none.show();
11366 $box.removeClass("active");
11367 $content.css("display", "none");
11368 } else if (reasonCount === 1) {
11369 $single.show();
11370 $box.addClass("active");
11371 $content.css("display", "block");
11372 } else {
11373 $plural.show();
11374 $box.addClass("active");
11375 $content.css("display", "block");
11376 }
11377 }
11378
11379 async function setupRestoreErrorOptions() {
11380 $.bmi.modal("freeze-loading-modal").open();
11381 $("#error-modal").find(".modal-title").text($("#bmi-restore-error-modal-title").text());
11382
11383
11384 const tryInDifferentWayBtn = $(".try-in-different-way");
11385 const tryInDifferentWayOption = tryInDifferentWayBtn.closest(".failure-option");
11386 const failureReasons = $(".failure-reasons");
11387
11388 if (
11389 !$("#cli-disable-others").is(":checked") ||
11390 $("#file_limit_extraction_max").val() != "300"
11391 ) {
11392 tryInDifferentWayOption.show();
11393 } else {
11394 tryInDifferentWayOption.hide();
11395 }
11396
11397 try {
11398 const res = await $.bmi.ajax("check-comptability", {
11399 for: "restore"
11400 });
11401 $.bmi.modal("freeze-loading-modal").close();
11402
11403 failureReasons.empty();
11404
11405 let reasonCount = 0;
11406 if (res.status === "success" ) {
11407 if (Array.isArray(res.data) && res.data.length > 0) {
11408 res.data.forEach(r => failureReasons.append("<li>" + r + "</li>"));
11409 reasonCount = failureReasons.find("li").length;
11410 }
11411 if (res.mainReasonFound) {
11412 tryInDifferentWayOption.hide();
11413 }
11414 }
11415 bmiSetFailureReasonsState(reasonCount);
11416 setupDownloadLogsHref("restore");
11417 } catch (error) {
11418 console.log(error);
11419 $.bmi.modal("freeze-loading-modal").close();
11420 }
11421 }
11422
11423 async function setupBackupErrorOptions() {
11424 $.bmi.modal("freeze-loading-modal").open();
11425 $("#error-modal").find(".modal-title").text($("#bmi-error-modal-title").text());
11426
11427 const tryInDifferentWayBtn = $(".try-in-different-way");
11428 const tryInDifferentWayOption = tryInDifferentWayBtn.closest(".failure-option");
11429 const failureReasons = $(".failure-reasons");
11430
11431 if (
11432 !$("#cli-disable-others").is(":checked") ||
11433 !$("#experimental-hard-timeout").is(":checked") ||
11434 !$("#download-technique").is(":checked") ||
11435 !$("#bmi-db-batching-backup").is(":checked")
11436 ) {
11437 if (!tryInDifferentWayOption.hasClass("force_hide")) {
11438 tryInDifferentWayOption.show();
11439 }
11440 } else {
11441 tryInDifferentWayOption.hide();
11442 }
11443
11444 try {
11445 const res = await $.bmi.ajax("check-comptability", {
11446 for: "backup"
11447 });
11448 $.bmi.modal("freeze-loading-modal").close();
11449
11450 failureReasons.empty();
11451
11452 let reasonCount = 0;
11453 if (res.status === "success" && Array.isArray(res.data) && res.data.length > 0) {
11454 res.data.forEach(r => failureReasons.append("<li>" + r + "</li>"));
11455 reasonCount = failureReasons.find("li").length;
11456 }
11457
11458 bmiSetFailureReasonsState(reasonCount);
11459 setupDownloadLogsHref("backup");
11460 } catch (error) {
11461 console.log(error);
11462 $.bmi.modal("freeze-loading-modal").close();
11463 }
11464 }
11465
11466 async function fillLogs(
11467 type = "backup",
11468 preElement = $(".log-wrapper").find("pre")[0],
11469 logPath = "?backup-migration=PROGRESS_LOGS&progress-id=latest_full.log&uncensored=true&backup-id=current&t=" +
11470 +new Date() +
11471 "&sk=" +
11472 $("#BMI_SECRET_KEY").text().trim(),
11473 ) {
11474 let url = $("#BMI_BLOG_URL").text().trim();
11475 if (url.slice(-url.length) !== "/") url = url + "/";
11476
11477 if (type === "backup") logPath += "&progress-id=latest_full.log";
11478 else if (type === "restore") logPath += "&progress-id=latest_migration_full.log";
11479
11480 return new Promise((resolve, reject) => {
11481 httpGet(url + logPath, function (log) {
11482 let res1 = log.split("\n").slice(0, 1)[0];
11483
11484 if (res1.trim() === "") {
11485 res1 = log.split("\n").slice(0, 2)[1];
11486 log = log.split("\n").slice(2).join("\n");
11487 } else {
11488 log = log.split("\n").slice(1).join("\n");
11489 }
11490 if (log && log != false && typeof log !== "undefined") {
11491 if (log === false) {
11492 reject("Error: Failed to retrieve backup logs");
11493 return;
11494 }
11495 let lines = log.split("\n");
11496 if (lines.length >= 1) lines = lines.slice(0, -1);
11497 preElement.innerText = "";
11498 showAllLines(preElement, lines);
11499 resolve();
11500 } else {
11501 reject("Error: Failed to retrieve backup logs");
11502 }
11503 });
11504 });
11505 }
11506
11507 function runStagingTimers() {
11508 setInterval(function () {
11509 $trs = $(".bmi-tastewp-staging-row");
11510 $trs.each(function (i) {
11511 let $tr = $($trs[i]);
11512 let expiration = $tr.attr("expiration");
11513 if (expiration && !isNaN(parseInt(expiration))) {
11514 expiration = parseInt(expiration);
11515 $tr
11516 .find(".stg-tr-expiration span")
11517 .text($.bmi.getExpirationTime(expiration));
11518 }
11519 });
11520 }, 1000);
11521 }
11522
11523 function setupLogsModal() {
11524 let errorParent = $.bmi.modal("error-modal").getParent();
11525 let type = "";
11526 if (errorParent == "backup-progress-modal") {
11527 $("#logs-modal").find(".modal-title").text($('#bmi-backup-logs-modal-title').text());
11528 type = "backup";
11529 } else if (errorParent == "restore-progress-modal") {
11530 $("#logs-modal").find(".modal-title").text($('#bmi-restore-logs-modal-title').text());
11531 type = "restore";
11532 }
11533 let preElement = $("#logs-modal").find("pre")[0];
11534 fillLogs(type, preElement).then(function () {
11535 $.bmi.modal("freeze-loading-modal").close();
11536 setTimeout(function () {
11537 $.bmi.modal("logs-modal").open();
11538 preElement.scroll({ top: preElement.scrollHeight });
11539 }, 300);
11540 });
11541 }
11542
11543 function fillRestoreLogs() {
11544 let url = $("#BMI_BLOG_URL").text().trim();
11545 if (url.slice(-url.length) !== "/") url = url + "/";
11546 let logs_restore =
11547 url +
11548 "?backup-migration=PROGRESS_LOGS&progress-id=latest_migration.log&backup-id=current&t=" +
11549 +new Date() +
11550 "&sk=" +
11551 $("#BMI_SECRET_KEY").text().trim();
11552
11553 httpGet(url + logs_restore, function (log) {
11554 let textarea = $('#restore-log')[0];
11555 textarea.value = log;
11556 });
11557 }
11558
11559 function setupDownloadLogsHref(type = "backup") {
11560 let url = $("#BMI_BLOG_URL").text().trim();
11561 if (url.slice(-url.length) !== "/") url = url + "/";
11562 $progressId = type == "backup" ? "latest_full.log" : "latest_migration_full.log";
11563 let logs_url = url + "?backup-migration=PROGRESS_LOGS&progress-id=" + $progressId + "&backup-id=current&t=" + +new Date() + "&sk=" + $("#BMI_SECRET_KEY").text().trim();
11564 $(".download-log-url.censored").attr("download", "secure-" + (type == "backup" ? "backup" : "restore") + "-logs.txt")
11565 $(".download-log-url.uncensored").attr("download", (type == "backup" ? "backup" : "restore") + "-logs.txt")
11566 $(".download-log-url.censored").attr("href", logs_url);
11567 $(".download-log-url.uncensored").attr("href", logs_url + "&uncensored=true");
11568 }
11569
11570 function getCurrentRestore() {
11571 return current_restore;
11572 }
11573 function getIsDownloadBackupFinished() {
11574 return downloadBackupFinished;
11575 }
11576 $.bmi.getCurrentRestore = getCurrentRestore;
11577 $.bmi.getIsDownloadBackupFinished = getIsDownloadBackupFinished;
11578
11579 function updateSuffixPosition() {
11580 let localBackupsPath = document.getElementById('bmi_path_storage_default');
11581 let localBackupsPathSuffix = document.getElementById('local-backups-suffix');
11582 let textWidth = getTextWidth(localBackupsPath.value, window.getComputedStyle(localBackupsPath).font);
11583 let suffixWidth = getTextWidth(localBackupsPathSuffix.innerText, window.getComputedStyle(localBackupsPathSuffix).font);
11584
11585 localBackupsPath.style.paddingRight = suffixWidth + 22 + 'px';
11586 if (localBackupsPath.value === '') {
11587 localBackupsPathSuffix.style.display = 'none';
11588 return;
11589 } else {
11590 localBackupsPathSuffix.style.display = 'inline-block';
11591 }
11592 localBackupsPathSuffix.style.left = Math.min(textWidth + 20, localBackupsPath.clientWidth - suffixWidth - 20) + 'px';
11593 }
11594
11595 function getTextWidth(text, font) {
11596 const canvas = document.createElement('canvas');
11597 const ctx = canvas.getContext('2d');
11598 ctx.font = font;
11599 return ctx.measureText(text).width;
11600 }
11601 $('#bmi_path_storage_default').on('input', updateSuffixPosition);
11602 $('#storage-options').on('click', function() {
11603 setTimeout(() => {
11604 updateSuffixPosition();
11605 }, 100);
11606 setTimeout(() => {
11607 updateSuffixPosition();
11608 }, 300);
11609 });
11610 $(window).on('resize', updateSuffixPosition);
11611
11612 // Init
11613 (function () {
11614 if (pagenow !== 'toplevel_page_backup-migration') return;
11615 scanDirectories();
11616 getAndInsertDynamicNames();
11617 $.bmi.reloadBackups();
11618 $.bmi.adjustStorageIcons();
11619 $.bmi.reloadStaging();
11620 runTimerST();
11621 runStagingTimers();
11622
11623 let url_x = $("#BMI_BLOG_URL").text().trim();
11624 if (url_x.slice(-url_x.length) !== "/") url_x = url_x + "/";
11625 let logs_backup =
11626 url_x +
11627 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&backup-id=current&t=" +
11628 +new Date() +
11629 "&sk=" +
11630 $("#BMI_SECRET_KEY").text().trim();
11631 let logs_backup_uncensored =
11632 url_x +
11633 "?backup-migration=PROGRESS_LOGS&progress-id=latest.log&uncensored=true&backup-id=current&t=" +
11634 +new Date() +
11635 "&sk=" +
11636 $("#BMI_SECRET_KEY").text().trim();
11637 let logs_restore =
11638 url_x +
11639 "?backup-migration=PROGRESS_LOGS&progress-id=latest_migration.log&backup-id=current&t=" +
11640 +new Date() +
11641 "&sk=" +
11642 $("#BMI_SECRET_KEY").text().trim();
11643 let logs_staging =
11644 url_x +
11645 "?backup-migration=PROGRESS_LOGS&progress-id=latest_staging.log&backup-id=current&t=" +
11646 +new Date() +
11647 "&sk=" +
11648 $("#BMI_SECRET_KEY").text().trim();
11649
11650 $(".download-backup-log-url").attr("href", logs_backup);
11651 $(".download-backup-log-url.uncensored").attr(
11652 "href",
11653 logs_backup_uncensored,
11654 );
11655 $(".download-restore-log-url").attr("href", logs_restore);
11656 $(".download-staging-log-url").attr("href", logs_staging);
11657 })();
11658 });
11659 jQuery(document).ready(function($) {
11660
11661 $('.bmi-modal-opener').on('click', function(e) {
11662
11663 if (this.getAttribute('data-modal') && this.getAttribute('data-modal') != '') {
11664 e.preventDefault();
11665 if (this.getAttribute('data-close'))
11666 $.bmi.modal(this.getAttribute('data-close')).close();
11667
11668 $.bmi.modal(this.getAttribute('data-modal')).open();
11669 }
11670
11671 });
11672
11673 $('.bmi-modal-closer').on('click', function(e) {
11674
11675 if (this.getAttribute('data-close') && this.getAttribute('data-close') != '') {
11676 e.preventDefault();
11677 $.bmi.modal(this.getAttribute('data-close')).close();
11678 } else {
11679 if (this.closest('.bmi-modal')) {
11680 e.preventDefault();
11681 if ($('#' + this.closest('.bmi-modal').getAttribute('id')).length > 0)
11682 $.bmi.modal(this.closest('.bmi-modal').getAttribute('id')).close();
11683 }
11684 }
11685
11686 });
11687
11688 $('.bmi-modal-close').on('click', function(e) {
11689
11690 if (this.closest('.bmi-modal')) {
11691 e.preventDefault();
11692 $.bmi.modal(this.closest('.bmi-modal').id).close();
11693 }
11694
11695 });
11696
11697 $('.bmi-modal').on('click', function(e) {
11698
11699 if (e.target == this && !$(e.target).hasClass('bmi-modal-no-close')) {
11700 $.bmi.modal(this.id).close();
11701 }
11702
11703 });
11704
11705 $('.bmi-modal-back').on('click', function(e) {
11706
11707 if (this.closest('.bmi-modal')){
11708 e.preventDefault();
11709 let modal = $.bmi.modal(this.closest('.bmi-modal').id);
11710 let modalParent = modal.getParent();
11711 if ( modalParent ){
11712 modal.close();
11713 $.bmi.modal(modalParent).open();
11714 }
11715 }
11716
11717 });
11718
11719 });jQuery(document).ready(function($) {
11720
11721 // Init tooltips
11722 if(pagenow === 'toplevel_page_backup-migration')
11723 $.bmi.tooltips.init();
11724
11725 // Progress interval
11726 let upload_progress, current_last = -1,
11727 startmsg = false;
11728
11729 // Replace pleaceholders with real preloaders
11730 let preloader_divs = '';
11731 for (let i = 0; i < 12; ++i) preloader_divs += '<div></div>';
11732 $('.spinner-loader').html(preloader_divs).addClass('lds-spinner');
11733
11734 function setProgress(end = 0, duration = 1000) {
11735
11736 if (current_last == end) return;
11737 else current_last = end;
11738 clearInterval(upload_progress);
11739
11740 let start = parseInt($('.upload-percentage').text()) - 1;
11741 if (start > end && end != 0) return;
11742
11743 let range = end - start;
11744 let current = start;
11745 let increment = 1;
11746 let stepTime = Math.abs(Math.floor(duration / range));
11747
11748 upload_progress = setInterval(function() {
11749
11750 current += increment;
11751 $('.upload-progress-bar').find('span')[0].style.width = current + '%';
11752 $('.upload-percentage').text(current + '%');
11753
11754 if (current >= 100) {
11755 clearInterval(upload_progress);
11756 current_last = null;
11757 }
11758
11759 }, stepTime);
11760
11761 }
11762
11763 function Progress(value) {
11764
11765 clearInterval(upload_progress);
11766 if (value == 0) {
11767 $('.upload-progress-bar').find('span')[0].style.width = value + '%';
11768 $('.upload-percentage').text(value + '%');
11769 } else if (value == 100) {
11770 $('.upload-progress-bar').find('span')[0].style.width = value + '%';
11771 $('.upload-percentage').text(value + '%');
11772 } else {
11773 $('.upload-progress-bar').find('span')[0].style.width = value + '%';
11774 $('.upload-percentage').text(value + '%');
11775 // setProgress(value, 300);
11776 }
11777
11778 }
11779
11780 $.fchunker({
11781
11782 upId: 'upid',
11783 upShardSize: bmiVariables.maxUploadSize,
11784 upMaxSize: '2000',
11785 upUrl: ajaxurl + '?cache=false',
11786 upType: 'zip,tar,gz',
11787 bmiNonce: bmiVariables.nonce,
11788
11789 upCallBack: function(res) {
11790
11791 var status = res.status;
11792 var msg = res.message;
11793 var url = res.url + "?" + Math.random();
11794
11795 if (status == 2) {
11796
11797 setTimeout(function() {
11798 $('#drop-area').show(300);
11799 $('.upload-progress').hide(300);
11800 }, 100);
11801
11802 $.bmi.alert('success', $('#bmi-upload-end').text(), 3000);
11803 $.bmi.modal('upload-success-modal').open();
11804 $.bmi.reloadBackups();
11805
11806 }
11807
11808 if (status == 1) {
11809
11810 // console.log(msg);
11811 if (!startmsg) {
11812 $.bmi.alert('success', $('#bmi-upload-start').text(), 3000);
11813 startmsg = true;
11814 }
11815
11816 }
11817
11818 if (status == 0) {
11819
11820 $.upErrorMsg(msg);
11821 $('#drop-area').show(300);
11822 $('.upload-progress').hide(300);
11823
11824 }
11825
11826 if (status == 5) {
11827
11828 $.bmi.modal('upload-invalid-manifest-modal').open();
11829 $('#drop-area').show(300);
11830 $('.upload-progress').hide(300);
11831
11832 }
11833
11834 if (status == 3) {
11835
11836 Progress(100);
11837 $.upErrorMsg(msg);
11838 $('#drop-area').show(300);
11839 $('.upload-progress').hide(300);
11840
11841 }
11842
11843 },
11844
11845 upEvent: function(num) {
11846
11847 Progress(num);
11848
11849 },
11850
11851 upStop: function(errmsg) {
11852
11853 Progress(0);
11854
11855 setTimeout(function() {
11856 $('#drop-area').show(300);
11857 $('.upload-progress').hide(300);
11858
11859 }, 100);
11860
11861 if (errmsg.includes('Type error')) {
11862
11863 $.bmi.modal('upload-wrong-file-modal').open();
11864 $.bmi.alert('warning', $('#bmi-upload-wrong').text(), 3000);
11865
11866 } else if (errmsg.includes('File already exists')) {
11867
11868 $.bmi.modal('upload-exist-file-modal').open();
11869 $.bmi.alert('warning', $('#bmi-upload-exists').text(), 3000);
11870
11871 } else {
11872
11873 $.bmi.alert('error', $('#bmi-upload-error').text(), 3000);
11874 console.error(errmsg);
11875
11876 }
11877
11878 },
11879
11880 upStart: function() {
11881
11882 startmsg = false;
11883 current_last = -1;
11884 Progress(0);
11885
11886 setTimeout(function() {
11887 $('#drop-area').hide(300);
11888 $('.upload-progress').show(300);
11889 }, 100);
11890
11891 }
11892
11893 });
11894
11895 });
11896
11897 // function bmi_debug_function(data = {}) {
11898 //
11899 // jQuery.bmi.ajax('debugging', data).then(function(res) {
11900 //
11901 // console.log(res);
11902 //
11903 // }).catch(function(error) {
11904 //
11905 // console.log(error);
11906 //
11907 // });
11908 //
11909 // }
11910 jQuery(document).ready(function($) {
11911
11912 function setRadios(radios) {
11913 for (let i = 0; i < radios.length; ++i) {
11914
11915 let c = radios[i].closest('.container-radio');
11916 if (c && typeof c.classList != undefined) {
11917
11918 c.classList.remove('active');
11919
11920 if (radios[i].checked === true) {
11921 c.classList.add('active');
11922 }
11923
11924 }
11925
11926 }
11927 }
11928
11929 $('input[type="radio"]').on('change', function() {
11930
11931 let name = this.getAttribute('name');
11932 let container = this.closest('.container-radio');
11933 let radios = document.querySelectorAll('[name="' + name + '"]');
11934 setRadios(radios);
11935
11936 });
11937
11938 (function() {
11939 if (pagenow !== 'toplevel_page_backup-migration') return;
11940 let radios = document.getElementById('bmi').querySelectorAll('input[type="radio"]');
11941 setRadios(radios);
11942
11943 })();
11944
11945 });jQuery(document).ready(function($) {
11946
11947 let current_last = null;
11948 let logs_progress = null;
11949 let timeouter = null;
11950 let isLineAppendTimeoutRunning = false;
11951 let latestLines = [];
11952 let curmaxnum = 0;
11953 let curdivs = 0;
11954 let latestStep = '';
11955 let lineAppender = null;
11956 let endCodeCall = null;
11957 let nextBatchStop = false;
11958
11959 async function repeatLogUpdate() {
11960
11961 clearTimeout(timeouter);
11962 updateStagingLogs();
11963 initializeStagingLogsUpdater(true);
11964
11965 }
11966
11967 async function initializeStagingLogsUpdater(selfcall = false) {
11968
11969 if (selfcall == false) {
11970
11971 $('#staging-live-log-wrapper .log-wrapper').find('pre')[0].innerText = '';
11972 $('#staging-progress-modal .progress-active-bar')[0].style.width = '0%';
11973 $('#staging-progress-modal .progress-percentage')[0].style.left = '0%';
11974 $('#staging-progress-modal .progress-percentage')[0].innerText = '0%';
11975
11976 setProgressStaging(0);
11977 repeatLogUpdate();
11978
11979 } else {
11980
11981 timeouter = setTimeout(repeatLogUpdate, 1500);
11982
11983 }
11984
11985 }
11986
11987 function endStagingLogerUpdater() {
11988
11989 clearInterval(logs_progress);
11990 clearTimeout(lineAppender);
11991 clearTimeout(endCodeCall);
11992 clearTimeout(timeouter);
11993
11994 latestStep = '';
11995 latestLines = [];
11996 curmaxnum = 0;
11997 curdivs = 0;
11998 isLineAppendTimeoutRunning = false;
11999
12000 }
12001
12002 function insertPre(log, el) {
12003
12004 if (log === false) return;
12005 let lines = log.split('\n');
12006 if (lines.length >= 1) lines = lines.slice(0, -1);
12007 latestLines = lines;
12008
12009 if (isLineAppendTimeoutRunning == false) {
12010 if (curdivs < latestLines.length) {
12011 showNextLine(el);
12012 }
12013 }
12014
12015 }
12016
12017 function httpGet(theUrl) {
12018
12019 return new Promise(function(resolve) {
12020
12021 let isHttps = window.location.protocol.includes('https');
12022 let isUrlHttps = theUrl.includes('https');
12023 if (isUrlHttps) theUrl = theUrl.slice(5);
12024 else theUrl = theUrl.slice(4);
12025 if (isHttps) theUrl = 'https' + theUrl;
12026 else theUrl = 'http' + theUrl;
12027
12028 try {
12029
12030 if (window.XMLHttpRequest) {
12031 xmlhttp = new XMLHttpRequest();
12032 } else {
12033 xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
12034 }
12035
12036 xmlhttp.onloadend = function() {
12037
12038 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
12039
12040 return resolve(xmlhttp.responseText);
12041
12042 } else return resolve(false);
12043
12044 }
12045
12046 xmlhttp.open('GET', theUrl);
12047 xmlhttp.send();
12048
12049 } catch (e) {
12050
12051 return resolve(false);
12052
12053 }
12054
12055 });
12056
12057 }
12058
12059 function setProgressStaging(end = 0, duration = 1500) {
12060
12061 if (current_last == end) return;
12062 else current_last = end;
12063
12064 clearInterval(logs_progress);
12065
12066 let start = parseFloat($('#staging-progress-modal .progress-percentage')[0].style.left) - 1;
12067
12068 let range = end - start;
12069 let current = start;
12070 let increment = 1;
12071 let stepTime = Math.abs(Math.floor(duration / range));
12072
12073 logs_progress = setInterval(function() {
12074
12075 current += increment;
12076
12077 $('#staging-progress-modal .progress-active-bar')[0].style.width = (current).toFixed(2) + '%';
12078 $('#staging-progress-modal .progress-percentage')[0].style.left = (current).toFixed(2) + '%';
12079 $('#staging-progress-modal .progress-percentage')[0].innerText = (current).toFixed(0) + '%';
12080
12081 if (current >= 100) {
12082 current_last = null;
12083 clearInterval(logs_progress);
12084 }
12085
12086 if (current > end) clearInterval(logs_progress);
12087
12088 }, stepTime);
12089
12090 }
12091
12092 async function updateStagingLogs() {
12093
12094 return new Promise(async function (resolve) {
12095
12096 let url = $('#BMI_BLOG_URL').text().trim();
12097 if (url.slice(-url.length) !== '/') url = url + '/';
12098
12099 let res = await httpGet(url + '?backup-migration=PROGRESS_LOGS&progress-id=latest_staging_full.log&backup-id=current&t=' + +new Date() + '&sk=' + $('#BMI_SECRET_KEY').text().trim());
12100 if (!res) return resolve();
12101
12102 let progress = res.split('\n').slice(0, 1)[0];
12103 res = res.split('\n').slice(1).join('\n');
12104
12105 if (progress === false || isNaN(parseFloat(progress))) return resolve();
12106 let pre = $('#staging-live-log-wrapper').find('pre')[0];
12107
12108 if (!res.includes('<') && !res.includes('>')) {
12109 if (res && res != false && typeof res !== 'undefined') {
12110 insertPre(res, pre);
12111 }
12112 }
12113
12114 setProgressStaging(progress);
12115 return resolve();
12116
12117 });
12118
12119 }
12120
12121 function showNextLine(el) {
12122
12123 let mostRecentStep = '';
12124 let delayedDisplayTime = 40;
12125 let line = latestLines[curdivs];
12126 let div = document.createElement('DIV');
12127
12128 let color = '';
12129 if (typeof line == 'undefined' || !line) return;
12130 if (line.substr(0, 6) == '[INFO]') color = 'blue';
12131 else if (line.substr(0, 9) == '[SUCCESS]') color = 'green';
12132 else if (line.substr(0, 6) == '[WARN]') color = 'orange';
12133 else if (line.substr(0, 7) == '[ERROR]') color = 'red';
12134 else if (line.substr(0, 10) == '[END-CODE]') color = 'hide_so_much';
12135 else if (line.substr(0, 9) == '[VERBOSE]') color = 'hide_verbose';
12136 else if (line.substr(0, 6) == '[STEP]') {
12137
12138 div.classList.add('bold');
12139 div.classList.add('step');
12140
12141 } else {
12142
12143 if (line && line.trim().length > 0 && line[0] != '[') {
12144 curdivs--;
12145 }
12146
12147 }
12148
12149 if (line.substr(0, 6) == '[STEP]') mostRecentStep = line.slice(29);
12150 if (color.length > 0) div.classList.add(color);
12151
12152 div.style.display = 'none';
12153 div.innerText = line;
12154
12155 el.appendChild(div);
12156 let endCodeFound = goThroughEndCodes();
12157
12158 $(div).show(delayedDisplayTime, function () {
12159 if (mostRecentStep != '' && mostRecentStep != latestStep) {
12160 latestStep = mostRecentStep;
12161 $('#staging_current_step').text(mostRecentStep);
12162 }
12163 });
12164 el.scrollTop = el.scrollHeight;
12165
12166 curdivs++;
12167 if (curdivs < latestLines.length && endCodeFound == false) {
12168 isLineAppendTimeoutRunning = true;
12169 lineAppender = setTimeout(function () {
12170 showNextLine(el);
12171 }, delayedDisplayTime);
12172 } else {
12173 isLineAppendTimeoutRunning = false;
12174 }
12175
12176 }
12177
12178 function goThroughEndCodes() {
12179
12180 let mostRecentStep = '';
12181 let endCodeFound = false;
12182
12183 for (let i = 0; i < latestLines.length; ++i) {
12184
12185 let line = latestLines[i];
12186 if (line.substr(0, 6) == '[STEP]') mostRecentStep = line.slice(29);
12187 else if (!endCodeFound && line && line.trim().includes('[END-CODE]')) {
12188
12189 endCodeFound = true;
12190 clearTimeout(endCodeCall);
12191
12192 if (line.includes('001')) endCodeCall = setTimeout(stagingCreationSuccess, 2500);
12193 else endCodeCall = setTimeout(stagingCreationFailed, 2500);
12194
12195 }
12196
12197 }
12198
12199 return endCodeFound;
12200
12201 }
12202
12203 function stagingCreationSuccess() {
12204
12205 endStagingLogerUpdater();
12206 $.bmi.modal('freeze-loading-modal').close();
12207 $.bmi.modal('staging-progress-modal').close();
12208 $.bmi.modal('staging-error-modal').close();
12209 $.bmi.modal('staging-success-modal').open();
12210
12211 }
12212
12213 function stagingCreationFailed(hideDefault = false) {
12214
12215 $('#after-logs-sent-modal').attr('data-error-source', 'staging');
12216
12217 endStagingLogerUpdater();
12218 $.bmi.modal('freeze-loading-modal').close();
12219 $.bmi.modal('staging-progress-modal').close();
12220 $.bmi.modal('staging-success-modal').close();
12221 $.bmi.modal('staging-error-modal').open();
12222
12223 if (!hideDefault) {
12224 let errorNotice = $('#stg-notices-button').attr('c-errorProcess');
12225 $.bmi.alert('error', errorNotice, 5000);
12226 }
12227
12228 }
12229
12230 function abortCreationProcess(name) {
12231
12232 endStagingLogerUpdater();
12233
12234 $.bmi.modal('freeze-loading-modal').close();
12235 $.bmi.modal('staging-progress-modal').close();
12236 $.bmi.modal('staging-success-modal').close();
12237 $.bmi.modal('staging-error-modal').close();
12238
12239 $.bmi.alert('info', $('#bmi-stg-aborted-al').text(), 3000);
12240
12241 $.bmi.reloadStaging();
12242
12243 }
12244
12245 function callLocalStagingCreationProcess(data, continuation = false) {
12246
12247 return new Promise(function(resolve) {
12248
12249 let endpoint = 'staging-start-local-creation';
12250 if (!continuation) {
12251 $.bmi.modal('freeze-loading-modal').open();
12252 } else {
12253 endpoint = 'staging-local-creation-process';
12254 }
12255
12256 $.bmi.ajax(endpoint, data).then(function(res) {
12257
12258 if (!continuation) {
12259 $.bmi.modal('freeze-loading-modal').close();
12260 $.bmi.modal('staging-progress-modal').open();
12261 }
12262
12263 if (res.status == 'success') {
12264
12265 $('#bmi-tastewp-staging-success-text').hide();
12266 $('#bmi-local-staging-success-text').show();
12267 $('.bmi-staging-success-img-normal').addClass('bmi-image-hide');
12268 $('.bmi-staging-success-img-tastewp').addClass('bmi-image-hide');
12269 $('.bmi-staging-success-img-normal').removeClass('bmi-image-hide');
12270
12271 $('#bmi-stg-subname-input').val('');
12272 $.bmi.reloadStaging();
12273
12274 let loginURL = `${res.url}/wp-login.php?autologin=true&user=${res.userid}&secret=${res.password}`;
12275 $('#bmi-visit-latest-staging').attr('href', loginURL);
12276 $('#bmi-staging-latest-url').attr('href', loginURL);
12277
12278 endCodeCall = setTimeout(stagingCreationSuccess, 500);
12279
12280 } else if (res.status == 'continue') {
12281
12282 if (!continuation) initializeStagingLogsUpdater();
12283 if (nextBatchStop) res['data']['delete'] = true;
12284 callLocalStagingCreationProcess(res['data'], true);
12285
12286 } else if (res.status == 'fail') {
12287
12288 stagingCreationFailed(true);
12289 return $.bmi.alert('warning', res.message, 5000);
12290
12291 } else if (res.status == 'deleted') {
12292
12293 abortCreationProcess(res['name']);
12294
12295 } else {
12296
12297 stagingCreationFailed();
12298
12299 }
12300
12301 }).catch(function(error) {
12302
12303 stagingCreationFailed();
12304 console.error(error);
12305
12306 });
12307
12308 });
12309
12310 }
12311
12312 function callTasteWPStagingCreationProcess(data, initialize = false) {
12313
12314 return new Promise(function(resolve) {
12315
12316 let endpoint = 'staging-tastewp-creation-process';
12317 if (initialize) {
12318 $.bmi.modal('freeze-loading-modal').open();
12319 data['initialize'] = true;
12320 }
12321
12322 $.bmi.ajax(endpoint, data).then(function(res) {
12323
12324 if (res.status == 'success') {
12325
12326 $.bmi.reloadStaging();
12327
12328 let accessURL = `${res.url}`;
12329 $('#bmi-visit-latest-staging').attr('href', accessURL);
12330 $('#bmi-staging-latest-url').attr('href', accessURL);
12331 $('#bmi-staging-latest-url').text(accessURL);
12332
12333 $('#bmi-tastewp-staging-success-text').show();
12334 $('#bmi-local-staging-success-text').hide();
12335 $('.bmi-staging-success-img-normal').addClass('bmi-image-hide');
12336 $('.bmi-staging-success-img-tastewp').addClass('bmi-image-hide');
12337 $('.bmi-staging-success-img-tastewp').removeClass('bmi-image-hide');
12338
12339 endCodeCall = setTimeout(stagingCreationSuccess, 500);
12340
12341 } else if (res.status == 'continue') {
12342
12343 if (initialize) {
12344 initializeStagingLogsUpdater();
12345 $.bmi.modal('freeze-loading-modal').close();
12346 $.bmi.modal('staging-progress-modal').open();
12347 }
12348
12349 if (nextBatchStop) res['data']['delete'] = true;
12350 callTasteWPStagingCreationProcess(res['data']);
12351
12352 } else if (res.status == 'fail') {
12353
12354 stagingCreationFailed(true);
12355 return $.bmi.alert('warning', res.message, 5000);
12356
12357 } else if (res.status == 'deleted') {
12358
12359 abortCreationProcess(res['name']);
12360
12361 } else {
12362
12363 stagingCreationFailed();
12364
12365 }
12366
12367 }).catch(function(error) {
12368
12369 stagingCreationFailed();
12370 console.error(error);
12371
12372 });
12373
12374 });
12375
12376 }
12377
12378 $('#bmi').on('click', '.bmi-stg-sel-box', function (e) {
12379 if ($(this)[0].classList.contains('bmi-active')) return;
12380 if (e.target.tagName == 'A') return;
12381 $('.bmi-stg-sel-box.bmi-active').removeClass('bmi-active');
12382 $(this).addClass('bmi-active');
12383
12384 if ($(this).data('mode') == 'tastewp') {
12385 $('.bmi-stg-creation-box-local').hide(300);
12386 $('.bmi-stg-creation-box-tastewp').hide(300);
12387 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
12388 if ($('.bmi-stg-drop-option:not(.bmi-stg-option-template)').length > 0) {
12389 $('.bmi-stg-creation-box-tastewp').show(300);
12390 } else {
12391 $('.bmi-stg-creation-box-tastewp-empty').show(300);
12392 }
12393 } else {
12394 $('.bmi-stg-creation-box-local').show(300);
12395 $('.bmi-stg-creation-box-tastewp').hide(300);
12396 $('.bmi-stg-creation-box-tastewp-empty').hide(300);
12397 }
12398 });
12399
12400 $('.bmi-stg-dropdown-area-selector').on('click', function () {
12401 $('.bmi-stg-dropdown-area').toggleClass('bmi-active');
12402 });
12403
12404 $('body').on('click', ':not(.bmi-stg-dropdown-area)', function (e) {
12405 if (!e.target.closest('.bmi-stg-dropdown-area')) {
12406 $('.bmi-stg-dropdown-area').removeClass('bmi-active');
12407 }
12408 });
12409
12410 $('.bmi-stg-dropdown-area-inner-scroll').on('click', '.bmi-stg-drop-option', function (e) {
12411 let name = $(this).attr('backup-name');
12412 let date = $(this).find('.bmi-stg-option-date i').text();
12413 let size = $(this).find('.bmi-stg-option-size i').text();
12414
12415 $('.bmi-stg-drop-option.active').removeClass('active');
12416 $(this).addClass('active');
12417
12418 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-name').text(name);
12419 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-date i').text(date);
12420 $('.bmi-stg-dropdown-area-selector').find('.bmi-stg-option-size i').text(size);
12421
12422 $('#bmi-stg-current-backup-selected').val(name);
12423
12424 $('.bmi-stg-dropdown-area').removeClass('bmi-active');
12425 });
12426
12427 $('.i-staging-creator-tastewp').on('click', function () {
12428
12429 let backupName = $('.bmi-stg-dropdown-area-selector .bmi-stg-option-name').text();
12430
12431 $('#stg-prenotice-mode-local').hide();
12432 $('#stg-prenotice-mode-tastewp').show();
12433 $('#start-entire-staging').attr('mode', 'tastewp');
12434 $('#bmi-staging-local-current-backup').text(backupName);
12435
12436 $.bmi.modal('staging-prenotice-modal').open();
12437
12438 });
12439
12440 $('.i-staging-creator-local').on('click', function () {
12441
12442 let name = $('#bmi-stg-subname-input').val().trim();
12443
12444 let empty = $(this).attr('c-empty');
12445 let long = $(this).attr('c-long');
12446 let errorNotice = $(this).attr('c-error');
12447 let invalid = $(this).attr('c-invalid');
12448
12449 let validRegex = /^[a-zA-Z0-9-_]+$/;
12450
12451 if (name.length <= 0) {
12452 return $.bmi.alert('warning', empty, 5000);
12453 }
12454
12455 if (!validRegex.test(name)) {
12456 return $.bmi.alert('warning', invalid, 5000);
12457 }
12458
12459 if (name.length >= 24) {
12460 return $.bmi.alert('warning', long, 5000);
12461 }
12462
12463 $.bmi.modal('freeze-loading-modal').open();
12464
12465 $.bmi.ajax('staging-local-name', { name: name }).then(function(res) {
12466
12467 $.bmi.modal('freeze-loading-modal').close();
12468
12469 if (res.status == 'success') {
12470
12471 let url = $('#bmi-stg-homeurl').text() + $('#bmi-stg-subname-input').val();
12472 $('#stg-prenotice-mode-local').show();
12473 $('#stg-prenotice-mode-tastewp').hide();
12474 $('#bmi-staging-local-current-url').text(url);
12475 $('#bmi-staging-latest-url').text(url);
12476 $('#bmi-staging-latest-url, #bmi-visit-latest-staging').attr('href', url);
12477 $('#start-entire-staging').attr('mode', 'local'); // tastewp for TWP
12478
12479 $.bmi.modal('staging-prenotice-modal').open();
12480
12481 } else if (res.status == 'fail') {
12482
12483 return $.bmi.alert('warning', res.message, 5000);
12484
12485 } else {
12486
12487 return $.bmi.alert('error', errorNotice, 5000);
12488
12489 }
12490
12491 }).catch(function(error) {
12492
12493 $.bmi.modal('freeze-loading-modal').close();
12494 return $.bmi.alert('error', errorNotice, 5000);
12495 console.error(error);
12496
12497 });
12498
12499 });
12500
12501 $('#start-entire-staging').on('click', function (e) {
12502
12503 e.preventDefault();
12504 $.bmi.modal('staging-prenotice-modal').close();
12505 nextBatchStop = false;
12506
12507 if ($(this).attr('mode') == 'local') {
12508
12509 let name = $('#bmi-stg-subname-input').val().trim();
12510 callLocalStagingCreationProcess({ name: name });
12511
12512 } else if ($(this).attr('mode') == 'tastewp') {
12513
12514 let currMills = +new Date() + '';
12515 let name = Math.random().toString(36).substr(2, 16) + currMills.slice(-4);
12516 let backupName = $('#bmi-stg-current-backup-selected').val();
12517 callTasteWPStagingCreationProcess({ name: name, backupName: backupName }, true);
12518
12519 } else {
12520
12521 $.bmi.modal('freeze-loading-modal').close();
12522
12523 }
12524
12525 });
12526
12527 $('#rescan-for-staging').on('click', function(e) {
12528
12529 e.preventDefault();
12530 $.bmi.reloadStaging();
12531
12532 });
12533
12534 $('#stg-tbody-table').on('click', '.bc-stg-edit-btn', function(e) {
12535
12536 e.preventDefault();
12537 let name = $(this).closest('tr').attr('name');
12538 let displayName = $(this).closest('tr').find('.stg-tr-name').text();
12539
12540 $('#bmi-stg-rename-input').val(displayName);
12541 $('#stg-display-name-edit-confirm').attr('data-name', name);
12542
12543 $.bmi.modal('staging-rename-modal').open();
12544
12545 });
12546
12547 $('#stg-display-name-edit-confirm').on('click', function(e) {
12548
12549 e.preventDefault();
12550
12551 let empty = $('.i-staging-creator-local').attr('c-empty');
12552 let long = $('.i-staging-creator-local').attr('c-long');
12553 let invalid = $('.i-staging-creator-local').attr('c-invalid');
12554 let errorNotice = $('.i-staging-creator-local').attr('c-error');
12555
12556 let newName = $('#bmi-stg-rename-input').val();
12557 let oldName = $(this).attr('data-name');
12558
12559 let validRegex = /^[a-zA-Z0-9-_]+$/;
12560 if (newName.length <= 0) return $.bmi.alert('warning', empty, 5000);
12561 if (!validRegex.test(newName)) return $.bmi.alert('warning', invalid, 5000);
12562 if (newName.length >= 24) return $.bmi.alert('warning', long, 5000);
12563
12564 $.bmi.modal('freeze-loading-modal').open();
12565 $.bmi.ajax('staging-rename-display', { name: oldName, new: newName }).then(function(res) {
12566
12567 $.bmi.modal('freeze-loading-modal').close();
12568 if (res.status == 'success') {
12569
12570 $.bmi.modal('staging-rename-modal').close();
12571 $.bmi.reloadStaging();
12572
12573 } else if (res.status == 'fail') {
12574
12575 return $.bmi.alert('warning', res.message, 5000);
12576
12577 } else {
12578
12579 return $.bmi.alert('error', errorNotice, 5000);
12580
12581 }
12582
12583 }).catch(function(error) {
12584
12585 $.bmi.modal('freeze-loading-modal').close();
12586 return $.bmi.alert('error', errorNotice, 5000);
12587 console.error(error);
12588
12589 });
12590
12591 });
12592
12593 $('#bmi-visit-latest-staging').on('click', function (e) {
12594 if ($('#bmi-staging-latest-url').text().includes('tastewp')) {
12595 setTimeout(function () {
12596 $.bmi.reloadStaging();
12597 }, 8000);
12598 }
12599 });
12600
12601 $('#bmi-staging-stop').on('click', function () {
12602 $.bmi.modal('freeze-loading-modal').open();
12603 nextBatchStop = true;
12604 });
12605
12606 $('#stg-tbody-table').on('click', '.stg-login-btn', function(e) {
12607
12608 if ($(this).closest('tr').attr('server') == 'tastewp') {
12609 let url = $(this).closest('tr').find('.stg-tr-url-el').text();
12610 if (url.includes('/stg/')) $(this).attr('href', url);
12611 else {
12612 setTimeout(function () {
12613 $.bmi.reloadStaging();
12614 }, 8000);
12615 $(this).attr('href', 'https://tastewp.com/stg/access/' + $(this).closest('tr').attr('token'));
12616 }
12617 return true;
12618 }
12619
12620 e.preventDefault();
12621 let name = $(this).closest('tr').attr('name');
12622 let errorNotice = $('.i-staging-creator-local').attr('c-error');
12623 let self = this;
12624
12625 $.bmi.modal('freeze-loading-modal').open();
12626 $.bmi.ajax('staging-prepare-login', { name: name }).then(function(res) {
12627
12628 $.bmi.modal('freeze-loading-modal').close();
12629 if (res.status == 'success') {
12630
12631 $(self).attr('href', res.url);
12632 window.open(res.url, '_blank');
12633
12634 } else if (res.status == 'fail') {
12635
12636 return $.bmi.alert('warning', res.message, 5000);
12637
12638 } else {
12639
12640 return $.bmi.alert('error', errorNotice, 5000);
12641
12642 }
12643
12644 }).catch(function(error) {
12645
12646 $.bmi.modal('freeze-loading-modal').close();
12647 return $.bmi.alert('error', errorNotice, 5000);
12648 console.error(error);
12649
12650 });
12651
12652 });
12653
12654 $('#stg-tbody-table').on('click', '.bc-stg-remove-btn', function(e) {
12655
12656 e.preventDefault();
12657 let name = $(this).closest('tr').attr('name');
12658 let url = $(this).closest('tr').find('.stg-tr-url-el').text();
12659
12660 $('#bmi-staging-removal-url').attr('href', url);
12661 $('#bmi-staging-removal-url').text(url);
12662 $('#stg-removal-confirm').attr('data-name', name);
12663
12664 $.bmi.modal('staging-delete-confirm-modal').open();
12665
12666 });
12667
12668 $('#stg-removal-confirm').on('click', function(e) {
12669
12670 e.preventDefault();
12671
12672 let name = $(this).attr('data-name');
12673 let errorNotice = $('.i-staging-creator-local').attr('c-error');
12674
12675 $.bmi.modal('freeze-loading-modal').open();
12676 $.bmi.ajax('staging-delete-permanently', { name: name }).then(function(res) {
12677
12678 $.bmi.modal('freeze-loading-modal').close();
12679 if (res.status == 'success') {
12680
12681 $.bmi.modal('staging-delete-confirm-modal').close();
12682 $.bmi.reloadStaging();
12683
12684 } else if (res.status == 'fail') {
12685
12686 return $.bmi.alert('warning', res.message, 5000);
12687
12688 } else {
12689
12690 return $.bmi.alert('error', errorNotice, 5000);
12691
12692 }
12693
12694 }).catch(function(error) {
12695
12696 $.bmi.modal('freeze-loading-modal').close();
12697 return $.bmi.alert('error', errorNotice, 5000);
12698 console.error(error);
12699
12700 });
12701
12702 });
12703
12704 let wrapper = document.querySelector('.bmi-stg-dropdown-area-inner-scroll');
12705 if (wrapper) {
12706 wrapper.addEventListener('wheel', function (e) {
12707 let space = (wrapper.scrollHeight - wrapper.offsetHeight) - wrapper.scrollTop;
12708 if (e.deltaY < 0) {
12709 if (wrapper.scrollTop == 0) {
12710 e.preventDefault();
12711 e.stopPropagation();
12712 }
12713 } else if (space <= 0) {
12714 e.preventDefault();
12715 e.stopPropagation();
12716 }
12717 });
12718 }
12719
12720 });
12721 jQuery(document).ready(function($) {
12722
12723 let transition = false;
12724
12725 $('.bmi-tabs').on('click', '.bmi-tab', function(e) {
12726
12727 if (this.classList.contains('active')) return;
12728 if (transition === true) return;
12729 else transition = true;
12730
12731 $.bmi.collapsers.closeAll();
12732 let id = this.getAttribute('data-point');
12733
12734 if (id == 'manage-restore-wrapper') {
12735
12736 $('#create-backup-wrapper').hide(300);
12737 $('#staging-sites-wrapper').hide(300);
12738 $('[data-point="create-backup-wrapper"]').removeClass('active');
12739 $('[data-point="staging-sites-wrapper"]').removeClass('active');
12740
12741 } else if (id == 'staging-sites-wrapper') {
12742
12743 $('#create-backup-wrapper').hide(300);
12744 $('#manage-restore-wrapper').hide(300);
12745 $('[data-point="create-backup-wrapper"]').removeClass('active');
12746 $('[data-point="manage-restore-wrapper"]').removeClass('active');
12747
12748 } else if (id == 'create-backup-wrapper') {
12749
12750 $('#manage-restore-wrapper').hide(300);
12751 $('#staging-sites-wrapper').hide(300);
12752 $('[data-point="manage-restore-wrapper"]').removeClass('active');
12753 $('[data-point="staging-sites-wrapper"]').removeClass('active');
12754
12755 }
12756
12757 $('#' + id).show(300);
12758 $(this).addClass('active');
12759
12760 setTimeout(function() {
12761 transition = false;
12762 }, 320);
12763
12764 });
12765
12766 });
12767 jQuery(document).ready(function($) {
12768
12769 let dropArea = document.getElementById("drop-area");
12770 if (!dropArea) return;
12771
12772 ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
12773 dropArea.addEventListener(eventName, preventDefaults, false)
12774 });
12775
12776 ['dragenter', 'dragover'].forEach(eventName => {
12777 dropArea.addEventListener(eventName, highlight, false);
12778 document.querySelector('body').addEventListener(eventName, highlight, false);
12779 });
12780
12781 ['dragleave', 'drop', 'mouseleave'].forEach(eventName => {
12782 dropArea.addEventListener(eventName, unhighlight, false);
12783 });
12784
12785 $('body, #drop-area').on('mouseleave dragleave drop', function() {
12786 unhighlight();
12787 });
12788
12789 dropArea.addEventListener('drop', handleDrop, false);
12790
12791 function preventDefaults(e) {
12792 e.preventDefault()
12793 e.stopPropagation()
12794 }
12795
12796 function highlight(e) {
12797 dropArea.classList.add('highlight')
12798 }
12799
12800 function unhighlight(e) {
12801 dropArea.classList.remove('highlight')
12802 }
12803
12804 function handleDrop(e) {
12805 var dt = e.dataTransfer
12806 var files = dt.files
12807
12808 handleFiles(files)
12809 }
12810
12811 function handleFiles(files) {
12812 files = [...files];
12813 jQuery.fchunker_upload('file', files[0]);
12814 }
12815
12816 });