PageSourceSearch

https://sandiegosuprentals.com/wp-content/plugins/pricing-table-by…/tables/js/frontend.pts.base.js?ver=1.12.0

js sandiegosuprentals.com collected 2026-09-28 11:03:32 UTC 46,243 bytes, 1,361 lines download raw bytes

1/**
2 * Blocks fabric - main object for whole blocks manipulations
3 */
4function ptsBlockFabric() {
5  this._blocks = [];
6  this._isSorting = false;
7  this._animationSpeed = g_ptsAnimationSpeed;
8}
9ptsBlockFabric.prototype.addFromHtml = function (blockData, jqueryHtml) {
10  var block = this.add(blockData);
11  block.setRaw(jqueryHtml);
12};
13ptsBlockFabric.prototype.add = function (blockData) {
14  var blockData = jQuery.extend({}, blockData);
15  if (!blockData.original_id) {
16    blockData.original_id = blockData.id;
17    blockData.id = 0;
18  }
19  var blockClass = window['ptsBlock_' + blockData.cat_code];
20  if (blockClass) {
21    var block = new blockClass(blockData);
22    var blockIter = this._blocks.push(block);
23    block.setIter(blockIter - 1);
24    return block;
25  } else {
26    // console.log('Block Class For ' + blockData.cat_code + ' Not Found!!!');
27  }
28};
29ptsBlockFabric.prototype.getByViewId = function (viewId) {
30  if (this._blocks && this._blocks.length) {
31    for (var i = 0; i < this._blocks.length; i++) {
32      if (this._blocks[i].get('view_id') == viewId) {
33        return this._blocks[i];
34      }
35    }
36  }
37  return false;
38};
39/**
40 * Base block object - for extending
41 * @param {object} blockData all block data from database (block database row)
42 */
43function ptsBlockBase(blockData) {
44  this._data = blockData;
45  this._$ = null;
46  this._original$ = null;
47  this._id = 0;
48  this._iter = 0;
49  this._elements = [];
50  this._animationSpeed = 300;
51  this._disableContentChange = false;
52  //this._oneTimeElementsInited = false;
53}
54ptsBlockBase.prototype.get = function (key) {
55  return this._data[key];
56};
57ptsBlockBase.prototype.getParam = function (key) {
58  return this._data.params[key] ? this._data.params[key].val : false;
59};
60ptsBlockBase.prototype.setParam = function (key, value) {
61  if (!this._data.params[key]) this._data.params[key] = {};
62  this._data.params[key].val = value;
63};
64ptsBlockBase.prototype.getRaw = function () {
65  return this._$;
66};
67/**
68 * Alias for getRaw method
69 */
70ptsBlockBase.prototype.$ = function () {
71  return this.getRaw();
72};
73ptsBlockBase.prototype.setRaw = function (jqueryHtml) {
74  this._$ = jqueryHtml;
75  this._resetElements();
76  this._initHtml();
77  if (this.getParam('font_family')) {
78    this._setFont(this.getParam('font_family'));
79  }
80};
81ptsBlockBase.prototype._initElements = function () {
82  this._initElementsForArea(this._$);
83};
84ptsBlockBase.prototype._initElementsForArea = function (area) {
85  var block = this,
86    addedElements = [];
87  var initElement = function (htmlEl) {
88    var elementCode = jQuery(htmlEl).data('el'),
89      elementClass = window['ptsElement_' + elementCode];
90    if (elementClass) {
91      var newElement = new elementClass(jQuery(htmlEl), block);
92      newElement._setCode(elementCode);
93      var newIterNum = block._elements.push(newElement);
94      addedElements.push(newElement);
95      newElement.setIterNum(newIterNum - 1); // newIterNum == new length of _elements array, iterator number for element - is new length - 1
96    } else {
97      // if (g_ptsEdit)
98      // console.log('Undefined Element [' + elementCode + '] !!!');
99    }
100  };
101  jQuery(area)
102    .find('.ptsEl')
103    .each(function () {
104      initElement(this);
105    });
106  if (jQuery(area).hasClass('ptsEl')) {
107    initElement(area);
108  }
109  this._afterInitElements();
110  return addedElements;
111};
112ptsBlockBase.prototype._afterInitElements = function () {};
113ptsBlockBase.prototype._resetElements = function () {
114  this._clearElements();
115  this._initElements();
116};
117ptsBlockBase.prototype._clearElements = function () {
118  if (this._elements && this._elements.length) {
119    for (var i = 0; i < this._elements.length; i++) {
120      this._elements[i].destroy();
121    }
122    this._elements = [];
123  }
124};
125ptsBlockBase.prototype.getElements = function () {
126  return this._elements;
127};
128ptsBlockBase.prototype._initHtml = function () {};
129/**
130 * ID number in list of canvas elements
131 * @param {numeric} iter Iterator - number in all blocks array - for this element
132 */
133ptsBlockBase.prototype.setIter = function (iter) {
134  this._iter = iter;
135};
136ptsBlockBase.prototype.showLoader = function (txt) {
137  var loaderHtml = jQuery('#ptsBlockLoader');
138  txt = txt ? txt : loaderHtml.data('base-txt');
139  loaderHtml.find('.ptsBlockLoaderTxt').html(txt);
140  loaderHtml
141    .css({
142      height: this._$.height(),
143      top: this._$.offset().top,
144    })
145    .addClass('active');
146};
147ptsBlockBase.prototype.hideLoader = function () {
148  var loaderHtml = jQuery('#ptsBlockLoader');
149  loaderHtml.removeClass('active');
150};
151ptsBlockBase.prototype._setFont = function (fontFamily) {
152  var $fontLink = this._getFontLink();
153  // If this is not standard font - it should be Google font, so - load it from Google Fonts server here
154
155  if (toeInArrayPts(fontFamily, ptsBuildConst.standardFonts) === false) {
156    $fontLink.attr({
157      href: 'https://fonts.googleapis.com/css?family=' + encodeURIComponent(fontFamily),
158      //,	'data-font-family': fontFamily
159    });
160  }
161  this._$.css({
162    'font-family': fontFamily,
163  });
164  this.setParam('font_family', fontFamily);
165};
166ptsBlockBase.prototype._getFontLink = function () {
167  var $link = this._$.find('link.ptsFont');
168  if (!$link.length) {
169    $link = jQuery('<link class="ptsFont" rel="stylesheet" type="text/css"/>').appendTo(this._$);
170  }
171  return $link;
172};
173ptsBlockBase.prototype.getElementByIterNum = function (iterNum) {
174  return this._elements[iterNum];
175};
176ptsBlockBase.prototype.removeElementByIterNum = function (iterNum) {
177  this._elements.splice(iterNum, 1);
178  if (this._elements && this._elements.length) {
179    for (var i = 0; i < this._elements.length; i++) {
180      this._elements[i].setIterNum(i);
181    }
182  }
183};
184// ptsBlockBase.prototype.destroyElementByIterNum = function(iterNum, clb) {
185// 	this.getElementByIterNum( iterNum ).destroy( clb );	// It will call removeElementByIterNum() inside element destroy method
186// };
187ptsBlockBase.prototype.destroyElementByIterNum = function (iterNum, clb) {
188  var element = this.getElementByIterNum(iterNum);
189  if (element) {
190    element.destroy(clb); // Pass the callback to destroy
191  } else if (clb && typeof clb === 'function') {
192    clb(); // If no element, still call callback
193  }
194};
195/**
196 * Price table block base class
197 */
198function ptsBlock_price_table(blockData) {
199  this._increaseHoverFontPerc = 20; // Increase font on hover effect, %
200  this._$lastHoveredCol = null;
201  this._refreshColsBinded = false;
202  this._onloadHandle = false;
203  this._isAlreadyShowed = false;
204  this._isResponsiveDescInit = false;
205  ptsBlock_price_table.superclass.constructor.apply(this, arguments);
206}
207extendPts(ptsBlock_price_table, ptsBlockBase);
208ptsBlock_price_table.prototype._getColsContainer = function () {
209  return this._$.find('.ptsColsWrapper:first');
210};
211ptsBlock_price_table.prototype._getCols = function (includeDescCol) {
212  return this._getColsContainer().find('.ptsCol' + (includeDescCol ? '' : ':not(.ptsTableDescCol)'));
213};
214ptsBlock_price_table.prototype._initTooltipsForCells = function () {
215  if (this.getParam('disable_custom_tooltip_style') != '1') {
216    var $tooltipstedCells = this._$.find('.ptsCell[title], .ptsColFooter[title], .ptsColHeader[title], .ptsColDesc[title]');
217    if ($tooltipstedCells && $tooltipstedCells.length) {
218      // TODO: Move this to Options and make this part more flexible
219      var tooltipsterSettings = {
220        contentAsHTML: true,
221        interactive: true,
222        speed: 250,
223        delay: 0,
224        animation: 'swing',
225        maxWidth: 450,
226        position: 'top',
227      };
228      $tooltipstedCells.tooltipster(tooltipsterSettings);
229
230      $tooltipstedCells.each(function () {
231        var el = jQuery(this);
232        el.attr('data-title', el.attr('title')).removeAttr('title');
233      });
234    }
235  }
236};
237ptsBlock_price_table.prototype._afterInitElements = function () {
238  ptsBlock_price_table.superclass._afterInitElements.apply(this, arguments);
239  if (parseInt(this.getParam('enb_hover_animation')) == 1) {
240    this._initHoverEffect();
241  }
242  if (this.getParam('table_align')) this._$.addClass('ptsTableAlign_' + this.getParam('table_align'));
243  if (this.getParam('text_align')) this._$.addClass('ptsAlign_' + this.getParam('text_align'));
244  if (this.getParam('ajax')) this._$.addClass('ptsAjax_' + this.getParam('ajax'));
245  if (!this._disableContentChange) {
246    this._refreshCellsHeight();
247  }
248  if (!this._refreshColsBinded) {
249    this._$.bind(
250      'ptsBlockContentChanged',
251      jQuery.proxy(function () {
252        this._refreshCellsHeight();
253      }, this)
254    );
255    this._refreshColsBinded = true;
256  }
257  if (!_ptsIsEditMode()) {
258    // Not for edit mode unfortunatelly ....
259    this._initTooltipsForCells();
260    /*var $tooltipstedCells = this._$.find('.ptsCell[title], .ptsColFooter[title], .ptsColHeader[title], .ptsColDesc[title]');
261
262    if(this.getParam('disable_custom_tooltip_style') != '1') {
263      if($tooltipstedCells && $tooltipstedCells.length) {
264        // TODO: Move this to Options and make this part more flexible
265        var tooltipsterSettings = {
266          contentAsHTML: true
267          ,	interactive: true
268          ,	speed: 250
269          ,	delay: 0
270          ,	animation: 'swing'
271          ,	maxWidth: 450
272          ,	position: 'top'
273        };
274        $tooltipstedCells.tooltipster( tooltipsterSettings );
275      }
276    }*/
277    var self = this,
278      PTS_VISIBLE_SET_HEIGHT_KEY = 'PTS-VISIBLE-SET-HEIGHT-KEY';
279
280    this._fixResponsive();
281
282    jQuery(window).resize(function () {
283      if (!self._$.is(':visible')) {
284        self._$.data(PTS_VISIBLE_SET_HEIGHT_KEY, false);
285      } else {
286        self._fixResponsive();
287        self._refreshCellsHeight();
288      }
289    });
290    jQuery('.ptsTableFrontedShell .ptsPreDisplayStyle').remove();
291    jQuery('.ptsBlock').css({
292      opacity: '1',
293      visibility: 'visible',
294    });
295    jQuery(window).on('toggleChangeCellHeight', function () {
296      self._refreshCellsHeight();
297    });
298    jQuery(function () {
299      var isEnableLazyLoad = function () {
300          return self._$.find('img[data-lazy-src]:not(.lazyloaded)').length > 0;
301        },
302        checkedLazyLoadLib = false;
303
304      if (isEnableLazyLoad()) {
305        checkedLazyLoadLib = true;
306      }
307
308      var observer = new MutationObserver(function (mutationsList) {
309        for (var mutation of mutationsList) {
310          if (mutation.type === 'childList') {
311            jQuery(mutation.addedNodes).each(function () {
312              if (this.nodeType === 1 && this.nodeName === 'IMG' && self._$ && self._$.get(0) && jQuery.contains(self._$.get(0), this)) {
313                if (checkedLazyLoadLib) {
314                  var isLoadedImages = true;
315
316                  self._$.find('img[data-lazy-src]').each(function () {
317                    var $this = jQuery(this);
318
319                    if (!$this.hasClass('lazyloaded') && !$this.hasClass('lazyload')) {
320                      isLoadedImages = false;
321                    }
322                  });
323
324                  if (isLoadedImages) {
325                    checkedLazyLoadLib = false;
326
327                    self._$.find('img[data-lazy-src]').on('load', function () {
328                      self._fixResponsive();
329                      self._refreshCellsHeight();
330                    });
331                  }
332                }
333              }
334            });
335          }
336        }
337
338        if (self._isAlreadyShowed || !self._$) return;
339
340        if (self._$.visible()) {
341          self._isAlreadyShowed = true;
342          self._fixResponsive();
343          self._refreshCellsHeight();
344        }
345      });
346
347      observer.observe(document.body, {
348        childList: true,
349        subtree: true,
350      });
351
352      self.setCalcWidth();
353    });
354  } else {
355    this.columnChidrensWidthCalc();
356  }
357
358  var self = this;
359
360  if (!this._onloadHandle) {
361    this._onloadHandle = true;
362    jQuery(window).on('load', function () {
363      self._refreshCellsHeight();
364    });
365  }
366};
367ptsBlock_price_table.prototype._initHoverEffect = function () {
368  // if(_ptsIsEditMode()) {
369  // 	this.setParam('enb_hover_animation', 1);
370  // 	return;
371  // }
372
373  if (parseInt(this.getParam('enb_hover_animation')) == 1) {
374    var $cols = this._getCols(),
375      self = this;
376    $cols
377      .mouseenter((e) => {
378        self._increaseHoverFont(jQuery(e.currentTarget));
379      })
380      .mouseleave((e) => {
381        self._backHoverFont(jQuery(e.currentTarget));
382      })
383      .mouseover((e) => {
384        self._increaseHoverFont(jQuery(e.currentTarget));
385      })
386      .mouseout((e) => {
387        self._backHoverFont(jQuery(e.currentTarget));
388      });
389  }
390};
391ptsBlock_price_table.prototype._increaseHoverFont = function ($col) {
392  var self = this;
393  if (_ptsIsEditMode()) return; // Not for edit mode unfortunatelly ....
394  var $descCell = $col.find('.ptsColDesc');
395  $col.height($col.height()); // Reset height on frontend.tables.js (listeners: window resize)
396  $descCell.find('span').each(function () {
397    var newFontSize = jQuery(this).data('new-font-size');
398    if (!newFontSize) {
399      var prevFontSize = jQuery(this).css('font-size'),
400        fontUnits = prevFontSize.replace(/\d+/, ''),
401        fontSize = parseInt(str_replace(prevFontSize, fontUnits, ''));
402      if (fontSize && fontUnits) {
403        newFontSize = Math.ceil(fontSize + (self._increaseHoverFontPerc * fontSize) / 100);
404        jQuery(this).data('prev-font-size', prevFontSize).data('font-units', fontUnits).data('new-font-size', newFontSize);
405      }
406    }
407    if (newFontSize) {
408      jQuery(this).css('font-size', newFontSize + jQuery(this).data('font-units'));
409    }
410  });
411
412  if (this.getParam('is_horisontal_row_type') != '1') {
413    if (!$descCell.attr('data-prev-height')) {
414      var descHeight = $descCell.outerHeight();
415      $descCell.attr('data-prev-height', descHeight);
416    } else {
417      descHeight = $descCell.attr('data-prev-height');
418    }
419    $descCell.css({
420      'min-height': descHeight,
421      height: 'auto',
422    });
423  }
424
425  $col.addClass('hover');
426  if (_ptsIsEditMode()) {
427    setTimeout(function () {
428      var colElement = self.getElementByIterNum($col.data('iter-num'));
429      if (colElement) {
430        colElement.repositeMenu();
431      }
432    }, g_ptsHoverAnim); // 300 - standard animation speed
433  }
434};
435ptsBlock_price_table.prototype._backHoverFont = function ($col) {
436  if (_ptsIsEditMode()) return; // Not for edit mode unfortunatelly ....
437  $col.removeClass('hover');
438  var $descCell = $col.find('.ptsColDesc');
439  $descCell.find('span').each(function () {
440    var prevFontSize = jQuery(this).data('prev-font-size');
441    if (prevFontSize) {
442      jQuery(this).css('font-size', prevFontSize);
443    }
444  });
445  setTimeout(function () {
446    $descCell.outerHeight($descCell.data('prev-height'));
447  }, 300); // time is set in css styles, but it always is something around 300ms
448};
449ptsBlock_price_table.prototype._disableHoverEffect = function ($cols) {
450  if (_ptsIsEditMode()) return; // Not for edit mode unfortunatelly ....
451  $cols = $cols ? $cols : this._getCols();
452  //$cols.unbind('hover.animation');
453};
454ptsBlock_price_table.prototype.getColSelectors = function () {
455  return {
456    header: { sel: '.ptsColHeader' },
457    desc: { sel: '.ptsColDesc' },
458    rows: { sel: '.ptsRows' },
459    cells: { sel: '.ptsCell' },
460    footer: { sel: '.ptsColFooter' },
461  };
462};
463ptsBlock_price_table.prototype.getMaxColsSizes = function (widthDesc) {
464  var $cols = this._getCols(widthDesc),
465    sizes = this.getColSelectors();
466
467  $cols.each(function () {
468    //fix effects from bug with large min-height in frontend #266
469    jQuery(this).css({ 'min-height': 'auto' });
470    for (var key in sizes) {
471      if (key == 'rows') continue;
472      var $entity = jQuery(this).find(sizes[key].sel);
473      if ($entity && $entity.length) {
474        if (key == 'cells') {
475          if (!sizes[key].height) sizes[key].height = [];
476          var cellNum = 0;
477          $entity.each(function () {
478            var prevHeight = jQuery(this).outerHeight();
479
480            jQuery(this).css('height', 'auto');
481            var height = jQuery(this).outerHeight();
482
483            if (!sizes[key].height[cellNum] || sizes[key].height[cellNum] < height) {
484              sizes[key].height[cellNum] = height;
485            }
486            //jQuery(this).outerHeight( prevHeight );
487            cellNum++;
488          });
489        } else {
490          var prevHeight = $entity.outerHeight();
491
492          $entity.css('height', 'auto');
493          var height = $entity.outerHeight();
494
495          if (!sizes[key].height || sizes[key].height < height) {
496            sizes[key].height = height;
497          }
498          $entity.outerHeight(prevHeight);
499        }
500      }
501    }
502  });
503  return sizes;
504};
505ptsBlock_price_table.prototype.getColumnWithInfo = function (strWidthAttr) {
506  var trimAttr = strWidthAttr.trim();
507  var number = trimAttr.match('\\d+');
508  var isPerc = trimAttr.match('%');
509  var isPx = trimAttr.match('\\d+');
510  if (number === null || (isPerc === null && isPx === null)) {
511    return null;
512  }
513  return new Object({
514    num: number.length && number.length > 0 && !isNaN(parseInt(number[0])) ? parseInt(number[0]) : null,
515    isPerc: isPerc === null ? false : true,
516  });
517};
518ptsBlock_price_table.prototype.setColsWidth = function (width, perc) {
519  var thatObj = this;
520  if (thatObj.getParam('is_horisontal_row_type') != '1') {
521    if (this.getParam('dsbl_responsive') === '1') {
522      var tableWidth = this._$.width(),
523        fixedValueTableWidth = this._$.width(),
524        $cols = this._getCols(true),
525        notSettedColumnWidthArr = new Array();
526      $cols.each(function () {
527        var col1 = jQuery(this);
528        if (col1.length > 0 && col1[0].style && col1[0].style.width) {
529          var colWidthObj = thatObj.getColumnWithInfo(col1[0].style.width);
530          if (colWidthObj && 'num' in colWidthObj && 'isPerc' in colWidthObj) {
531            var calcColWidth = 0;
532            if (colWidthObj.isPerc) {
533              calcColWidth = (fixedValueTableWidth * colWidthObj.num) / 100;
534            } else {
535              calcColWidth = colWidthObj.num;
536            }
537            var colPdL = parseFloat(col1.css('padding-left')),
538              colPdR = parseFloat(col1.css('padding-right')),
539              colMgL = parseFloat(col1.css('margin-left')),
540              colMgR = parseFloat(col1.css('margin-right')),
541              colSumMarginPadding = 0;
542            if (!isNaN(colPdL)) {
543              colSumMarginPadding += colPdL;
544            }
545            if (!isNaN(colPdR)) {
546              colSumMarginPadding += colPdR;
547            }
548            if (!isNaN(colMgL)) {
549              colSumMarginPadding += colMgL;
550            }
551            if (!isNaN(colMgR)) {
552              colSumMarginPadding += colMgR;
553            }
554            tableWidth -= calcColWidth;
555            calcColWidth = Math.floor(calcColWidth - colSumMarginPadding);
556            if (calcColWidth < 0) {
557              calcColWidth = 0;
558            }
559            col1.width(calcColWidth);
560          } else {
561            notSettedColumnWidthArr[notSettedColumnWidthArr.length] = col1;
562          }
563        } else {
564          notSettedColumnWidthArr[notSettedColumnWidthArr.length] = col1;
565        }
566      });
567      if (tableWidth > 0 && notSettedColumnWidthArr.length > 0) {
568        var calcWidthForNsCol = Math.round(tableWidth / notSettedColumnWidthArr.length);
569        for (var oneNsColumn in notSettedColumnWidthArr) {
570          // set width for Other columns
571          notSettedColumnWidthArr[oneNsColumn].width(calcWidthForNsCol);
572        }
573      }
574    } else {
575      width = parseFloat(width);
576      if (width) {
577        if (!perc) {
578          this.setParam('col_width', width);
579        }
580        var $cols = this._getCols(true);
581        if (perc) {
582          width += '%';
583        } else {
584          width += 'px';
585        }
586        $cols.css({
587          width: width,
588        });
589      }
590    }
591  } else {
592    this.columnChidrensWidthCalc();
593  }
594};
595ptsBlock_price_table.prototype.columnChidrensWidthCalc = function () {
596  if (this.getParam('is_horisontal_row_type') != '1') {
597    return;
598  }
599  // only for HORIZONTAL ROW
600  var $cols = this._getCols(true);
601  $cols.each(function (ind, oneCol) {
602    var currColumn = jQuery(oneCol),
603      emptyChilds = currColumn.find('.ptsTableElementContent').children(':not(:has(">div")):not(".ptsColBadge")'),
604      level1Childs = currColumn.find('.ptsTableElementContent').children(':has(">div"):not(".ptsColBadge")'),
605      level1ChildRows = currColumn.find('.ptsTableElementContent').children('.ptsRows'),
606      level2ChildRows = level1ChildRows.children(),
607      allChildCount = level1Childs.length,
608      level2ChildCount = 1;
609
610    if (level1ChildRows.length > 0 && level2ChildRows.length > 0) {
611      allChildCount += level2ChildRows.length - 1;
612      level2ChildCount = level2ChildRows.length;
613    }
614    //
615    emptyChilds.css({
616      width: '0%',
617      margin: '0',
618      padding: '0',
619    });
620    var oneChildWidth = Math.floor(95 / allChildCount);
621
622    level1Childs.css('width', oneChildWidth + '%');
623    var widthInPixel = level1Childs.css('width');
624    level1ChildRows.css('width', level2ChildCount * oneChildWidth + 2 + '%');
625    level2ChildRows.css('width', widthInPixel);
626  });
627};
628ptsBlock_price_table.prototype.checkColWidthPerc = function () {
629  if (this.getParam('calc_width') === 'table') {
630    this.setColWidthPerc();
631  }
632};
633ptsBlock_price_table.prototype.setColWidthPerc = function () {
634  var $cols = this._getCols(parseInt(this.getParam('enb_desc_col')));
635  this.setColsWidth(100 / $cols.length, true);
636};
637ptsBlock_price_table.prototype.setTableWidth = function (width, measure) {
638  if (width && parseInt(width)) {
639    width = parseInt(width);
640    this.setParam('table_width', width);
641  } else {
642    width = this.getParam('table_width');
643  }
644  if (measure) {
645    this.setParam('table_width_measure', measure);
646  } else {
647    measure = this.getParam('table_width_measure');
648  }
649  this._$.width(width + measure);
650};
651ptsBlock_price_table.prototype.setTableVertPadding = function (padding, measure) {
652  if (padding && parseInt(padding)) {
653    padding = parseInt(padding);
654    this.setParam('vert_padding', padding);
655  } else {
656    padding = this.getParam('vert_padding');
657  }
658  if (measure) {
659    this.setParam('vert_padding_measure', measure);
660  } else {
661    measure = this.getParam('vert_padding_measure');
662  }
663  this._$.find('.ptsCol').css('paddingBottom', padding + 'px');
664};
665ptsBlock_price_table.prototype.setCalcWidth = function (type) {
666  if (type) {
667    this.setParam('calc_width', type);
668  } else {
669    type = this.getParam('calc_width');
670  }
671  switch (type) {
672    case 'table':
673      this.setTableWidth();
674      this.setColWidthPerc();
675      break;
676    case 'col':
677      var enb_desc_col = this.getParam('enb_desc_col') != 0 ? true : false;
678      this._$.width(this._getCols(enb_desc_col).length * this.getParam('col_width'));
679      this.setColsWidth(this.getParam('col_width'));
680      break;
681  }
682};
683ptsBlock_price_table.prototype._fixResponsive = function () {
684  if (this.getParam('is_horisontal_row_type') == '1') {
685    var $parent = this._$.parents('.ptsTableFrontedShell:first').parent(),
686      parentWidth = $parent.width();
687
688    if (jQuery(window).width() < 600) {
689      $parent.addClass('ptsResponcive');
690    } else {
691      $parent.removeClass('ptsResponcive');
692    }
693    return;
694  }
695  var $parent = this._$.parents('.ptsTableFrontedShell:first').parent(),
696    parentWidth = $parent.width(),
697    widthMeasure = this.getParam('table_width_measure'),
698    calcWidth = this.getParam('calc_width'),
699    includeDesc = parseInt(this.getParam('enb_desc_col')),
700    $cols = this._getCols(includeDesc),
701    actualTblWidth = this._$.width(),
702    criticalColWidth = isNaN(parseInt(this.getParam('resp_min_col_width'))) ? 150 : parseInt(this.getParam('resp_min_col_width')),
703    dsblResponsive = parseInt(this.getParam('dsbl_responsive'));
704  this._$.removeClass('ptsBlockMobile');
705  switch (calcWidth) {
706    case 'table':
707      switch (widthMeasure) {
708        case '%':
709          var self = this,
710            removeOtherDescCol = function () {
711              if (!dsblResponsive && !_ptsIsEditMode() && self._$.find('.ptsTableDescCol').length > 1 && includeDesc) {
712                var $descCols = self._$.find('.ptsTableDescCol'),
713                  firstCol = false;
714
715                $descCols.each(function () {
716                  var $this = jQuery(this);
717                  if (!firstCol) {
718                    firstCol = true;
719                    return;
720                  }
721                  $this.remove();
722                });
723              }
724            };
725
726          //removeOtherDescCol();
727
728          $cols = this._getCols(includeDesc);
729          // There can be several desc cols - exclude those invalid numbers
730          var descColNum = $cols.filter('.ptsTableDescCol').length;
731
732          var colsNum = $cols.length - (descColNum > 1 ? descColNum - 1 : 0),
733            currWidth = actualTblWidth / colsNum;
734
735          if (currWidth <= criticalColWidth && !dsblResponsive) {
736            $cols.css('width', '100%');
737
738            if (!_ptsIsEditMode() && includeDesc) {
739              var $descColumn = this._$.find('.ptsTableDescCol').first(),
740                $columns = this._$.find('.ptsCol:not(.ptsTableDescCol)'),
741                firstCol = false;
742              removeOtherDescCol();
743
744              var i = 0;
745              $columns.each(function () {
746                var $this = jQuery(this);
747
748                if (!firstCol) {
749                  firstCol = true;
750                  return;
751                }
752
753                i++;
754                var $descClone = $descColumn.clone();
755                $descClone
756                  .find('.tooltipstered[data-title]')
757                  .removeClass('tooltipstered')
758                  .each(function () {
759                    var el = jQuery(this);
760                    el.attr('title', el.attr('data-title')).removeAttr('data-title');
761                  });
762                $descClone.insertBefore($this);
763              });
764              this._initTooltipsForCells();
765
766              this._isResponsiveDescInit = true;
767              this._$.find('.ptsCol').css('width', '50%');
768            }
769
770            this._$.addClass('ptsBlockMobile');
771            this.setParam('went_to_responsive', 1);
772          } else {
773            removeOtherDescCol();
774            if (this.getParam('went_to_responsive')) {
775              this.setColWidthPerc();
776              this.setParam('went_to_responsive', 0);
777            }
778          }
779          break;
780        case 'px':
781          if (actualTblWidth > parentWidth) {
782            this.setParam('went_to_responsive', this.getParam('table_width'));
783            this.setTableWidth(100, '%'); // make it 100% in this case
784            this._fixResponsive(); // Repeat - to check case '%':
785          } else if (this.getParam('went_to_responsive')) {
786            this.setTableWidth(this.getParam('went_to_responsive'), 'px'); // Back to px width
787            this.setParam('went_to_responsive', 0);
788          }
789          break;
790      }
791      break;
792    case 'col':
793      var colsNum = $cols.length,
794        currWidth = parseFloat(this.getParam('col_width'));
795      if (currWidth * colsNum >= parentWidth) {
796        this.setParam('went_to_responsive', currWidth);
797        this.setParam('table_width', 100); // Set table width to 100%
798        this.setParam('table_width_measure', '%');
799        this.setCalcWidth('table');
800        this._fixResponsive(); // Repeat - to check case '%':
801      } else if (this.getParam('went_to_responsive')) {
802        this.setCalcWidth('col');
803        this.setParam('col_width', this.getParam('went_to_responsive')); // Back to px width
804        this.setParam('went_to_responsive', 0);
805      }
806      break;
807  }
808};
809ptsBlock_price_table.prototype._refreshCellsHeight = function () {
810  // Really important  - keep all this functionality as light as possible, as it can slow down whole builder work
811  var $cols = this._getCols(true),
812    self = this,
813    sizes = this.getMaxColsSizes(true);
814
815  $cols.each(function () {
816    for (var key in sizes) {
817      var $entity = jQuery(this).find(sizes[key].sel);
818      if (self.getParam('is_horisontal_row_type') == '1') {
819        $entity.css({ height: 'auto' });
820      } else {
821        if (key == 'rows') {
822          $entity.css({ height: 'auto' });
823          continue;
824        }
825        if ($entity && $entity.length) {
826          if (key == 'cells') {
827            var cellNum = 0;
828            $entity.each(function () {
829              jQuery(this).css('height', sizes[key].height[cellNum]);
830              cellNum++;
831            });
832          } else {
833            $entity.outerHeight(sizes[key].height);
834
835            if ($entity.outerHeight() != sizes[key].height) {
836              $entity.css('height', sizes[key].height);
837            }
838          }
839        }
840      }
841    }
842  });
843};
844
845// ELEMENTS BASE JS
846
847function ptsElementBase(jqueryHtml, block) {
848  this._iterNum = 0;
849  this._id = 'el_' + mtRand(1, 999999);
850  this._animationSpeed = g_ptsAnimationSpeed;
851  this._$ = jqueryHtml;
852  this._block = block;
853  if (typeof this._menuOriginalId === 'undefined') {
854    this._menuOriginalId = '';
855  }
856  this._innerImgsCount = 0;
857  this._innerImgsLoaded = 0;
858  //this._$menu = null;
859  this._menu = null;
860  this._menuClbs = {};
861  if (typeof this._menuClass === 'undefined') {
862    this._menuClass = 'ptsElementMenu';
863  }
864  this._menuOnBottom = false;
865  this._code = 'base';
866
867  this._initedComplete = false;
868  this._editArea = null;
869  if (typeof this._isMovable === 'undefined') {
870    this._isMovable = false;
871  }
872  this._moveHandler = null;
873  this._sortInProgress = false;
874  if (typeof this._showMenuEvent === 'undefined') {
875    this._showMenuEvent = 'click';
876  }
877  if (typeof this._changeable === 'undefined') {
878    this._changeable = false;
879  }
880  if (g_ptsEdit) {
881    this._init();
882    this._initMenuClbs();
883    this._initMenu();
884
885    var images = this._$.find('img');
886    if (images && (this._innerImgsCount = images.length)) {
887      this._innerImgsLoaded = 0;
888      var self = this;
889      images.load(function () {
890        self._innerImgsLoaded++;
891        if (self._$.find('img').length == self._innerImgsLoaded) {
892          self._afterFullContentLoad();
893        }
894      });
895    }
896  }
897  this._onlyFirstHtmlInit();
898  this._initedComplete = true;
899}
900ptsElementBase.prototype.getId = function () {
901  return this._id;
902};
903ptsElementBase.prototype.getBlock = function () {
904  return this._block;
905};
906ptsElementBase.prototype._onlyFirstHtmlInit = function () {
907  if (this._$ && !this._$.data('first-inited')) {
908    this._$.data('first-inited', 1);
909    return true;
910  }
911  return false;
912};
913ptsElementBase.prototype.setIterNum = function (num) {
914  this._iterNum = num;
915  this._$.data('iter-num', num);
916};
917ptsElementBase.prototype.getIterNum = function () {
918  return this._iterNum;
919};
920ptsElementBase.prototype.$ = function () {
921  return this._$;
922};
923ptsElementBase.prototype.getCode = function () {
924  return this._code;
925};
926ptsElementBase.prototype._setCode = function (code) {
927  this._code = code;
928};
929ptsElementBase.prototype._init = function () {
930  this._beforeInit();
931};
932ptsElementBase.prototype._beforeInit = function () {};
933ptsElementBase.prototype.destroy = function () {};
934ptsElementBase.prototype.get = function (opt) {
935  return jQuery('<div/>')
936    .html(this._$.attr('data-' + opt))
937    .text(); // not .data() - as it should be saved even after page reload, .data() will not create element attribute
938};
939ptsElementBase.prototype.set = function (opt, val) {
940  this._$.attr('data-' + opt, jQuery('<div/>').text(val).html()); // not .data() - as it should be saved even after page reload, .data() will not create element attribute
941};
942ptsElementBase.prototype._getEditArea = function () {
943  if (!this._editArea || this._$.attr('data-reset') == '1') {
944    this._editArea = this._$.children('.ptsElArea');
945    if (!this._editArea.length) {
946      this._editArea = this._$.find('.ptsInputShell');
947      this._$.removeAttr('data-reset');
948    }
949  }
950  return this._editArea;
951};
952ptsElementBase.prototype._getOverlay = function () {
953  return this._$.find('.ptsElOverlay');
954};
955/**
956 * Standart button item
957 */
958function ptsElement_btn(jqueryHtml, block) {
959  if (typeof this._menuOriginalId === 'undefined') {
960    this._menuOriginalId = 'ptsElMenuBtnExl';
961  }
962  this._menuClass = 'ptsElementMenu_btn';
963  this._haveAdditionBgEl = null;
964  this._changeable = true;
965  this.includePostLinks = true;
966  ptsElement_btn.superclass.constructor.apply(this, arguments);
967}
968extendPts(ptsElement_btn, ptsElementBase);
969ptsElement_btn.prototype._onlyFirstHtmlInit = function () {
970  if (ptsElement_btn.superclass._onlyFirstHtmlInit.apply(this, arguments)) {
971    if (this.get('customhover-clb')) {
972      var clbName = this.get('customhover-clb');
973      if (typeof this[clbName] === 'function') {
974        var self = this;
975        this._getEditArea().hover(
976          function () {
977            self[clbName](true, this);
978          },
979          function () {
980            self[clbName](false, this);
981          }
982        );
983      }
984    }
985  }
986};
987ptsElement_btn.prototype._hoverChangeFontColor = function (hover, element) {
988  if (hover) {
989    jQuery(element).data('original-color', this._getEditArea().css('color')).css('color', jQuery(element).parents('.ptsEl:first').attr('data-bgcolor')); // Ugly, but only one way to get this value in dynamic way for now
990  } else {
991    jQuery(element).css('color', jQuery(element).data('original-color'));
992  }
993};
994ptsElement_btn.prototype._hoverChangeBgColor = function (hover, element) {
995  var parentElement = jQuery(element).parents('.ptsEl:first'); // Actual element html
996  if (hover) {
997    parentElement.data('original-color', parentElement.css('background-color')).css('background-color', parentElement.attr('data-bgcolor')); // Ugly, but only one way to get this value in dynamic way for now
998  } else {
999    parentElement.css('background-color', parentElement.data('original-color'));
1000  }
1001};
1002ptsElement_btn.prototype._hoverBorderColor = function (hover, element) {
1003  //var parentElement = jQuery(element).parents('.ptsEl:first');	// Actual element html
1004  if (hover) {
1005    jQuery(element).data('original-color', jQuery(element).css('border-color')).css('border-color', jQuery(element).parents('.ptsEl:first').attr('data-bgcolor')); // Ugly, but only one way to get this value in dynamic way for now
1006  } else {
1007    jQuery(element).css('border-color', jQuery(element).data('original-color'));
1008  }
1009};
1010/**
1011 * Table column element
1012 */
1013function ptsElement_table_col(jqueryHtml, block) {
1014  if (typeof this._menuOriginalId === 'undefined') {
1015    this._menuOriginalId = 'ptsElMenuTableColExl';
1016  }
1017  if (typeof this._menuClass === 'undefined') {
1018    this._menuClass = 'ptsElementMenu_table_col';
1019  }
1020  if (typeof this._isMovable === 'undefined') {
1021    this._isMovable = true;
1022  }
1023  this._showMenuEvent = 'click';
1024  this._colNum = 0;
1025  ptsElement_table_col.superclass.constructor.apply(this, arguments);
1026  /*if(parseInt(this.get('enb-schedule'))) {
1027    console.log(this.getIterNum());
1028  }*/
1029}
1030extendPts(ptsElement_table_col, ptsElementBase);
1031ptsElement_table_col.prototype.setIterNum = function () {
1032  ptsElement_table_col.superclass.setIterNum.apply(this, arguments);
1033  if (!g_ptsEdit && typeof ptsScheduleCheck !== 'undefined') {
1034    ptsScheduleCheck(this);
1035  }
1036};
1037/**
1038 * Table description column element
1039 */
1040function ptsElement_table_col_desc(jqueryHtml, block) {
1041  this._isMovable = false;
1042  ptsElement_table_col_desc.superclass.constructor.apply(this, arguments);
1043  //this.refreshHeight();
1044  //var self = this;
1045  /*this.getBlock().$().bind('ptsBlockContentChanged', function(){
1046    self.refreshHeight();
1047  });*/
1048}
1049extendPts(ptsElement_table_col_desc, ptsElement_table_col);
1050/*ptsElement_table_col_desc.prototype.refreshHeight = function() {
1051  var sizes = this.getBlock().getMaxColsSizes();
1052  for(var key in sizes) {
1053    var $entity = this._$.find(sizes[ key ].sel);
1054    if($entity && $entity.length) {
1055      if(key == 'cells' &&  sizes[ key ].height) {
1056        var cellNum = 0;
1057        $entity.each(function(){
1058          if(typeof(sizes[ key ].height[ cellNum ]) !== 'undefined') {
1059            jQuery(this).css('height', sizes[ key ].height[ cellNum ]);
1060          }
1061          cellNum++;
1062        });
1063      } else {
1064        $entity.css('height', sizes[ key ].height);
1065      }
1066    }
1067  }
1068};*/
1069
1070/**
1071 * Text element
1072 */
1073function ptsElement_table_cell_txt(jqueryHtml, block) {
1074  if (block.getParam('responsive_text')) {
1075    jqueryHtml.find('span, p').responsiveText({ minFontSize: 14 });
1076  }
1077  this.includePostLinks = true;
1078  ptsElement_table_cell_txt.superclass.constructor.apply(this, arguments);
1079}
1080extendPts(ptsElement_table_cell_txt, ptsElementBase);
1081
1082// RESPONSIVE TEXT
1083
1084(function ($) {
1085  $.fn.responsiveText = function (options) {
1086    // Setup options
1087    var settings = $.extend(
1088      {
1089        minFontSize: Number.NEGATIVE_INFINITY,
1090        maxFontSize: Number.POSITIVE_INFINITY,
1091      },
1092      options
1093    );
1094
1095    return this.each(function () {
1096      // Store the object
1097      var $this = $(this),
1098        text = $this.get(0);
1099
1100      if (!text) return;
1101
1102      $this.data('original-font-size', parseFloat(window.getComputedStyle(text).fontSize), 10);
1103
1104      // Resizer() resizes items based on the object width divided by the compressor * 10
1105      var resizer = function () {
1106        var ratio = null,
1107          originalWidth = window.screen.availWidth,
1108          currentWidth = window.innerWidth,
1109          size = $this.data('original-font-size');
1110
1111        if (originalWidth == currentWidth || !size) return;
1112
1113        if (currentWidth != originalWidth) ratio = originalWidth / currentWidth;
1114
1115        if (currentWidth > originalWidth) size *= ratio;
1116        else size /= ratio;
1117
1118        size = Math.max(Math.min(size, settings.maxFontSize), settings.minFontSize);
1119
1120        $this.css('font-size', size + 'px');
1121      };
1122
1123      resizer();
1124
1125      // Call on resize. Opera debounces their resize by default.
1126      $(window).on('resize.responsiveText orientationchange.responsiveText', resizer);
1127    });
1128  };
1129})(jQuery);
1130// TABLES BASE
1131var g_ptsEdit = false,
1132  g_ptsBlockFabric = null,
1133  g_ptsHoverAnim = 300, // Table hover animation lenght, ms - hardcoded for now
1134  g_ptsHoverMargin = 20; // Table hover margin displace, px - hardcoded for now
1135
1136//array with unique_id template that without enabled options header row
1137//overlap toggle elements
1138var g_ptsUniqueIdArray = ['wnOq3v2K', 'sa0DHT4h', 'B1gpCofW', 'ptmJ4YnA', 'r0OL4vCy'];
1139
1140jQuery(document).ready(function () {
1141  _ptsInitFabric();
1142  if (typeof ptsTables !== 'undefined' && ptsTables && ptsTables.length) {
1143    for (var i = 0; i < ptsTables.length; i++) {
1144      g_ptsBlockFabric.addFromHtml(ptsTables[i], jQuery('#' + ptsTables[i].view_id));
1145
1146      jQuery('body').trigger('set_default_position');
1147      //for fix horizontal element size bugs frontend.pro.tables.js
1148      if (ptsTables[i].unique_id === '7m6k5X0i') {
1149        jQuery('#' + ptsTables[i].view_id).attr('data-unique_id', ptsTables[i].unique_id);
1150      }
1151
1152      // fix overlap toggle elements
1153      if (g_ptsUniqueIdArray.indexOf(ptsTables[i].unique_id) != -1) {
1154        jQuery('#' + ptsTables[i].view_id).attr('data-unique_id', ptsTables[i].unique_id);
1155        //if undefined that means options is enabled by default.
1156
1157        var enableHeader = '1';
1158        if (typeof ptsTables[i].params.enb_head_row !== 'undefined') {
1159          enableHeader = typeof ptsTables[i].params.enb_head_row.val !== 'undefined' ? '0' : '1';
1160        }
1161        jQuery(document.body).trigger('overlay_toggle', [ptsTables[i].unique_id, enableHeader]);
1162      }
1163      //jQuery(".ptsTableFrontedShell .tooltipstered").removeAttr("title");
1164    }
1165    jQuery(window).bind('load', function () {
1166      if (typeof g_ptsAllowAddUndo !== 'undefined') {
1167        setTimeout(function () {
1168          g_ptsAllowAddUndo = true;
1169          _ptsAddUndoBuffer();
1170        }, 200);
1171      }
1172    });
1173  }
1174});
1175jQuery(window).on('load', function () {
1176  setTimeout(function () {
1177    jQuery('body').trigger('resize');
1178  }, 500);
1179});
1180//in case images are loading dynamically
1181jQuery('.ptsEl.ptsCol[data-el="table_col"] img').on('load', function () {
1182  jQuery('body').trigger('resize');
1183});
1184function _ptsInitFabric() {
1185  g_ptsBlockFabric = new ptsBlockFabric();
1186}
1187function ptsGetFabric() {
1188  return g_ptsBlockFabric;
1189}
1190function _ptsIsEditMode() {
1191  return typeof g_ptsEditMode !== 'undefined' && g_ptsEditMode;
1192}
1193//TABLES BASE END
1194//MODAL
1195(function ($) {
1196  'use strict';
1197
1198  $.fn.sModal = function (options) {
1199    var self = this,
1200      settings = $.extend(
1201        {
1202          width: 640,
1203          height: 480,
1204          buttons: [],
1205        },
1206        options
1207      ),
1208      $modalOverlay,
1209      $modalContainer,
1210      $closeModalButton,
1211      $modalContent,
1212      $modalButtons,
1213      modalOverlayStyle = {
1214        position: 'fixed',
1215        top: 0,
1216        left: 0,
1217        background: 'rgba(0,0,0,.75)',
1218        width: '100%',
1219        height: '100%',
1220        'z-index': '100000',
1221        display: 'none',
1222        'overflow-y': 'auto',
1223      },
1224      modalContainerStyle = {
1225        width: settings.width,
1226        position: 'absolute',
1227        top: '50%',
1228        left: '50%',
1229        'max-width': 'calc(100% - 1.5em)',
1230        'margin-right': '-50%',
1231        'margin-bottom': '20px',
1232        transform: 'translate(-50%, -50%)',
1233        background: '#fff',
1234        padding: '1em',
1235      },
1236      modalContentStyle = {
1237        height: 'calc(100% - 3.6em)',
1238        'overflow-y': 'auto',
1239        'overflow-x': 'hidden',
1240        padding: '1em',
1241      },
1242      closeButtonStyle = {
1243        position: 'absolute',
1244        right: '10px',
1245        top: '10px',
1246        'font-size': '35px',
1247        color: '#fff',
1248        cursor: 'pointer',
1249      },
1250      modalButtonsStyle = {
1251        position: 'relative',
1252        width: '100%',
1253        'text-align': 'right',
1254        'margin-top': '1em',
1255      };
1256
1257    function closeModal() {
1258      self.trigger('close.before');
1259      if (jQuery('.sModal:visible').length < 2) {
1260        jQuery('body').css('overflow', 'auto');
1261      }
1262      $modalOverlay.fadeOut();
1263      self.trigger('close.after');
1264    }
1265
1266    function openModal() {
1267      jQuery('body').css('overflow', 'hidden');
1268      $modalOverlay.show(0, function () {
1269        resizeModal();
1270        $modalOverlay.hide().fadeIn();
1271      });
1272
1273      if (parseInt(jQuery('.sc-modal-container').css('top')) > jQuery(window).height()) {
1274        var offset = jQuery(window).height() / 2 + 40;
1275        jQuery('.sc-modal-container').css('top', offset);
1276      }
1277    }
1278
1279    function resizeModal() {
1280      var offset = '50%';
1281      if ($modalContainer.height() > jQuery(window).height()) {
1282        offset = $modalContainer.height() / 2 + 40;
1283      }
1284      $modalContainer.css('top', offset);
1285    }
1286
1287    function createInstance($modalTemplate) {
1288      $modalOverlay = jQuery('<div class="sc-modal-overlay" tabindex="0">').css(modalOverlayStyle);
1289
1290      $modalContainer = jQuery('<div class="sc-modal-container">').css(modalContainerStyle);
1291
1292      $closeModalButton = jQuery('<div class="sc-modal-close-button">&times;</div>').css(closeButtonStyle);
1293      $closeModalButton.appendTo($modalOverlay);
1294
1295      $modalContent = jQuery('<div class="sc-modal-content">').css(modalContentStyle);
1296      $modalContent.appendTo($modalContainer);
1297
1298      $modalTemplate.appendTo($modalContent).show();
1299
1300      $modalContainer.appendTo($modalOverlay);
1301
1302      $modalOverlay.appendTo('body');
1303      $modalOverlay.add($closeModalButton).on('click', function (event) {
1304        if (jQuery(this).is(jQuery(event.target))) {
1305          closeModal();
1306        }
1307      });
1308
1309      jQuery(document).on('keyup', function (event) {
1310        if (event.which == 27 && $modalOverlay.is(':visible')) {
1311          closeModal();
1312        }
1313      });
1314
1315      var resizeTimer;
1316
1317      jQuery(window).on('resize', function (event) {
1318        clearTimeout(resizeTimer);
1319        resizeTimer = setTimeout(function () {
1320          resizeModal();
1321        }, 250);
1322      });
1323
1324      if (settings.buttons.length > 0) {
1325        $modalButtons = jQuery('<div class="sc-modal-action-buttons">').css(modalButtonsStyle);
1326
1327        for (var i = 0; i < settings.buttons.length; i++) {
1328          jQuery('<button>')
1329            .attr('class', settings.buttons[i].class || null)
1330            .html(settings.buttons[i].content || null)
1331            .on('click', $.proxy(settings.buttons[i].event, $modalTemplate))
1332            .appendTo($modalButtons);
1333        }
1334
1335        self.$buttons = $modalButtons;
1336
1337        $modalButtons.appendTo($modalContainer);
1338      }
1339    }
1340
1341    createInstance(this);
1342
1343    $.extend(this, {
1344      open: function () {
1345        this.trigger('open.before');
1346        openModal();
1347        this.trigger('open.after');
1348        return this;
1349      },
1350      close: function () {
1351        this.trigger('close.before');
1352        closeModal();
1353        this.trigger('close.after');
1354        return this;
1355      },
1356    });
1357
1358    return this;
1359  };
1360})(jQuery);
1361//MODAL

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.