PageSourceSearch

https://cdnn.1prime.ru//js/libs/ria.photoview.js

js 1prime.ru collected 2026-09-24 08:26:49 UTC 60,110 bytes, 1,472 lines download raw bytes

1/**
2 * <a class="l-photoview__open"
3 *   data-photoview_group="{{ String }}"
4 *   data-photoview_src="{{ Url }}"
5 *   data-photoview_sharelink="{ link for share }"
6 *   data-photoview_sharetitle="{ title for share }"
7 *   data-photoview_shareid="{ article id for share }"
8 *   data-photoview_desc="{ description text }"
9 *   data-photoview_copy="{ copyright text }"
10 *   data-photoview_copylink="{ copyright link}"
11 *   data-photoview_extlink="{ photobank link }">
12 * </a>
13 *
14 *
15 *
16 *  ОТКРЫТЬ
17 *
18 *
19 *
20 *
21 * */
22
23
24;"use strict";
25
26var photoViewWidget = {};
27
28    photoViewWidget.init = function( options ){
29		var _this = this;
30		/** основные слои: контентный и фотослой */
31		_this.$page = ( options && options.page ) ? options.page : $( "#page" );                // выборка слоя данных
32		_this.$el = ( options && options.photoview ) ? options.photoview : $( "#photoView" );   // выборка слоя просмотра фото
33		/** проверка наличия слоев для виджета  */
34		if ( !_this.$page.length || !_this.$el.length ) {
35			console.log('It is not possible to initialize photoViewWidget');
36			return;
37		}
38        /** переменные префиксов */
39		_this.prefix = ( options && options.prefix ) ? options.prefix : 'photoview';
40        _this.classPrefix = '.' + _this.prefix + '__';
41		/** НАСТРОЙКИ */
42		_this.settings = {
43			updateGroupOpen     : false,                    // обновлять набор элементов каждый раз при открытии галереи (для динамически подгружаемого контента)
44			itemsLoop           : true,                     // зацикливать слайды по кругу
45            itemsFilter         : function( item ){         // фильтрация выбранных элементов ( функция )
46                return !$( item ).parents( ".m-clone" ).length;
47            },
48			durationTransition  : 300,                      // длительность transition в ms
49			typeTransition      : 'ease-out',               // тип смены слайда
50			desktopWidth        : 925                       // ширина экрана при которой считается desktop версией
51		};
52		/** СОСТОЯНИЯ */
53		_this.states = {
54			widgetOpen          : false,                    // флаг photoview открыт
55			storeEnabled        : true,                     // поддержка history, флаг надо использовать
56			storeFromHistory    : false,                    // флаг что пришли из history
57			itemChange          : false,                    // флаг "смена слайда"
58            controlsVisible     : true,                     // навигация видима
59            imageTransformation : false,                    // трансформация картинки
60            hammerFunc          : true                      // наличие hammerJS
61		};
62        /** модификаторы */
63		_this.modPhotoviewOpen  = 'm-photoview-open';                        //
64		_this.modeBlur          = 'm-blur';                                          // класс блюра на слой данных
65		_this.modeDisabled      = 'm-disabled';                                  //
66		_this.modeOpen          = 'm-open';                                          //
67		_this.modeHidden        = 'm-hidden';                                      //
68        /** view height */
69        _this.clientHeight = _this.getCh();
70		/** DATA - атрибуты элемента */
71        _this.dataGroup         = _this.prefix + '-group';                    // [photoview]-group       атрибут группы
72        _this.dataSrc           = _this.prefix + '-src';                      // [photoview]-src         атрибут урла картинки
73        _this.dataImageId       = _this.prefix + '-image-id';                 // [photoview]-image-id    атрибут id картинки
74        _this.dataShareLink     = _this.prefix + '-sharelink';                // [photoview]-sharelink   атрибут линк для шары
75        _this.dataShareTitle    = _this.prefix + '-sharetitle';               // [photoview]-sharetitle  атрибут заголовок для шары
76        _this.dataShareId       = _this.prefix + '-shareid';                  // [photoview]-shareid     атрибут id статьи для шары
77
78		/** STORE */
79		_this.store = {
80			name    : 'pv',    // параметр в урле для хранения состояния
81			group   : 'g',    // параметр в атрибуте идентификатор группы
82			photo   : 'p'     // параметр в атрибуте идентификатор фото
83		};
84
85		/** items group info */
86		_this.group = {
87			id      : false,    // номер набора элементов
88			$items  : false,    // выборка слайдов
89			length  : false     // кол-во слайдов
90		};
91		/*_this.sub = {
92			$obj: false
93		};*/
94
95		/** картинка и параметры трансформации */
96		_this.image = {
97			$obj : false,       // объект картинки
98			size : {
99				w: false,       // ширина картинки
100				h: false        // высота картинки
101			},
102			translate : {
103				start_x : false,    // начало отсчета по x для сдвига картинки
104				start_y : false,    // начало отсчета по y для сдвига картинки
105				x : false,          // значение x для трансформации
106				y : false           // значение y для трансформации
107			},
108			scale : {
109				actual : false,     // значение scale
110				min : 1,            // минимальное значение scale
111				max : 3,            // максимальное значнеие scale
112				step : 5            // шаг в процентах при зумировании колесиком
113			},
114			origin : {
115				x: false,           // значение origin x для трансформации
116				y: false            // значение origin y для трансформации
117			}
118		};
119
120		/** инфа по Активному слайду */
121		_this.itemActive = {
122			$obj : false,
123			index : false,
124			src : false
125		};
126		/** инфа по следующему слайду */
127		_this.itemPrev = {
128			$obj : false,
129			index : false,
130			src: false
131		};
132		/** инфа по предыдущему слайду */
133		_this.itemNext = {
134			$obj : false,
135			index : false,
136			src: false
137		};
138
139		/** сохраненние состояний страницы */
140		_this.layerStates = {
141			bodyTop : 0,         // позиция контента по вертикали
142			bodyLeft : 0,        // позиция контента по горизонтали
143            htmlTop : 0,
144            htmlLeft : 0,
145			bodyOverflow : false
146		};
147
148		/** массив селекторов / объектов с динамической позицией исключений из области клика */
149        _this.exceptionDуnamicTap = [
150            _this.getBy( _this.classPrefix + 'desc' )
151        ];
152        _this.disabledHideControls = false;                 // флаг что был тап по элементу исключения
153		/** массив селекторов / объектов исключений из области клика */
154		_this.exceptionTap = [
155			_this.getBy( _this.classPrefix + 'social' ),    // для статичных объктов
156            _this.classPrefix + 'desc a',                   // для объектов создаваемых динамически
157			_this.getBy( _this.classPrefix + 'btn-prev' ),
158			_this.getBy( _this.classPrefix + 'btn-next' )
159		];
160		/** массив селекторов / объектов с переключением видимосии в зависимости от рамеров экрана */
161		_this.variableVisibility = [
162			_this.getBy( _this.classPrefix + 'btn-prev i' ),    // иконка кнопка Prev
163			_this.getBy( _this.classPrefix + 'btn-next i' )     // иконка кнопка Next
164		];
165		/** префиксы браузеров для подстановки стилей с префиксами */
166		_this.vendor_prefixes = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
167		/** Hammer */
168		_this.hammerManager = false;
169		/** Навешиваем стили на слои */
170		_this.setStyle( _this.$page,{
171			'position':'relative',
172			'width':'100%'
173		});
174		_this.setStyle( _this.$el,{
175			'position':'absolute',
176			'top': 0,
177			'right': 0,
178			'left': 0,
179			'z-index': 1000,
180			'display':'none',
181		});
182        /* Запуск кастомного скролла */
183        /* !!! обязательно до  update$Obj иначе осхранятся не корректные указатели !!! */
184        if( typeof photoViewWidget !==  'undefined' ) {
185            _this.getBy( this.classPrefix + 'desc-over' ).theinScroll({
186                theme : 'default-light',
187                autoHide : false
188            });
189        }
190		/** Создаем объекты для работы */
191        _this.update$Obj();
192		/** Вызов обработчиков */
193		_this.addHandlers();
194	};
195    photoViewWidget.update$Obj = function(){
196	    this.$buttonPrev = this.getBy( this.classPrefix + 'btn-prev' );
197	    this.$buttonNext = this.getBy( this.classPrefix + 'btn-next' );
198	    this.$buttonClose = this.getBy( this.classPrefix + 'btn-close' );
199	    this.$canHide = this.getBy( this.classPrefix + 'can-hide' );
200
201	    this.$sub = this.getBy( this.classPrefix + 'sub' );
202
203        this.$itemPrev = this.getBy( this.classPrefix + 'prev' );
204        this.$itemActive = this.getBy( this.classPrefix + 'active' );
205	    this.$itemNext = this.getBy( this.classPrefix + 'next' );
206
207        this.$placeIndexView = this.getBy( this.classPrefix + 'index-view' );
208        this.$placeSocial = this.getBy( this.classPrefix  + 'social' );
209
210        this.$desc = this.getBy( this.classPrefix + 'desc' );
211        this.$descMore = this.getBy( this.classPrefix + 'desc-more' );
212        this.$descOver = this.getBy( this.classPrefix + 'desc-over' );
213        this.$descSize = this.getBy( this.classPrefix + 'desc-size' );
214        this.$descText = this.getBy( this.classPrefix + 'desc-text' );
215    };
216	/**
217	 * Обработчики
218	 */
219    photoViewWidget.addHandlers = function(){
220		var _this = this;
221		/* Если нет Hammerjs выставляем флаг false */
222		if ( typeof Hammer === 'function' ){
223			_this.touchInitHammer();
224		} else {
225			_this.states.hammerFunc = false;
226		}
227		/* Предыдущий слайд */
228		_this.$buttonPrev.on( 'click', function( e ){
229			e.preventDefault();
230			_this.changePrev();
231		});
232		/* Следующий слайд */
233		_this.$buttonNext.on( 'click', function( e ){
234			e.preventDefault();
235			_this.changeNext();
236		});
237		/* Запрет сдвига картинок и описания */
238		$( _this.classPrefix + 'img, ' + _this.classPrefix + 'controls a').on( 'dragstart drag dragstop', function(){
239			return false;
240		});
241		/* Открытие галереи по клику на элемент с data src */
242		$('body').on( 'click', _this.classPrefix + 'open[data-' + _this.dataSrc + ']', function( e ){
243			e.preventDefault();
244			_this.open({
245				clickObj: $( this )
246			});
247		});
248		/* Закрытие photoview по клику на крестик */
249		_this.$buttonClose.on( 'click', function( e ){
250			e.preventDefault();
251			_this.close();
252		});
253		/** ИЗМЕНЕНИЯ РАЗМЕРОВ ОКНА БРАУЗЕРА */
254		$(window).resize( function(){
255			/** видимость, позиция и размеры элементов фотовью */
256            _this.clientHeight = _this.getCh();
257			_this.setPhotoviewHeight();
258			_this.posLayerTop();
259			_this.visibilityEl();
260			_this.updateDataSize();
261			/** данные о трансформации картинки */
262			//_this.imageDataSizeUpdate();
263		});
264		/** WINDOW SCROLL */
265		$( window ).scroll( function(){
266			_this.posLayerLeft();
267		});
268		/** MOUSE SCROLL Y */
269		//_this.addOnWheelY();
270        _this.$descMore.on( 'click', function(){
271            _this.showDescMore();
272        });
273        /* Выставлям клик на объекты с динамичским позиционированием чтобы отменять тап */
274        $.each( _this.objArrCreate( _this.exceptionDуnamicTap ), function( i, v ){
275            $( v ).on( 'click', function(){
276                _this.disabledHideControls = true;
277            });
278        });
279
280		/* Запускаем ресайз для образмеривания элементов */
281		$( window ).resize();
282		/* Проверяем поддержку history подписываемся на события history урл на наличие параметра галереи */
283		_this.storeHistory();
284	};
285	/**
286	 * Прокрутка колесика над слоем фотовью для увеличения / усменьшения изображения
287	 */
288    photoViewWidget.addOnWheelY = function() {
289		if ( this.$el[ 0 ].addEventListener ) {
290			if ( 'onwheel' in document ) {
291				this.$el[ 0 ].addEventListener( "wheel", $.proxy( this.addOnWheelYHandler, this ) );
292			} else if ( 'onmousewheel' in document ) {
293				this.$el[ 0 ].addEventListener( "mousewheel", $.proxy( this.addOnWheelYHandler, this ) );
294			} else {
295				this.$el[ 0 ].addEventListener( "MozMousePixelScroll", $.proxy( this.addOnWheelYHandler, this ) );
296			}
297		} else {
298			this.$el[ 0 ].attachEvent( "onmousewheel", this.addOnWheelYHandler );
299		}
300	};
301	/**
302	 * Обработка прокрутки и вызов функции зума
303	 */
304    photoViewWidget.addOnWheelYHandler = function( e ){
305		e.preventDefault();
306		var _this = this,
307			delta = e.deltaY || e.detail || e.wheelDelta,
308			clientX = e.clientX || false,
309			clientY = e.clientY || false,
310			scale = false,
311			percentStep = _this.image.scale.actual/100*_this.image.scale.step;
312
313		if ( delta > 0 ){
314			scale = _this.image.scale.actual - percentStep < _this.image.scale.min ? _this.image.scale.min : _this.image.scale.actual - percentStep;
315		} else if ( delta < 0 ){
316			scale = _this.image.scale.actual + percentStep > _this.image.scale.max ? _this.image.scale.max : _this.image.scale.actual + percentStep;
317		}
318		if ( scale ){
319			_this.imageScaling({
320				x : clientX,
321				y : clientY,
322				scale : scale,
323				duration : 0
324			});
325		} else {
326			return false;
327		}
328	};
329	/**
330	 * Создание массива объектов из массива селекторов
331	 * Creating an array from an array of selectors
332	 * @param {Array} selectors or objects array
333	 * @returns {Array} objects array
334	 */
335    photoViewWidget.objArrCreate = function( selectorsArr ){
336		var _this = this,
337			objArr = [];
338		$.each( selectorsArr, function( i, v ){
339			var $obj = v;
340			if ( typeof v === 'string' ){
341				$obj = _this.getBy( v )
342			}
343			if ( typeof $obj === 'object' && $obj.length > 0 ){
344				$.each ( $obj, function( k, l ){
345					objArr.push( $( l ) );
346				})
347			}
348		});
349		return objArr;
350	};
351	/**
352	 * Проверка попадания координат в несколько областей
353	 * Checking hit coordinates in several areas
354	 * @param {Number} coordinate X
355	 * @param {Number} coordinate y
356	 * @param {Object / Array objects} objects
357	 * @returns {Boolean} true / false
358	 */
359    photoViewWidget.hitInArr = function( x, y, obj ){
360		var _this = this;
361		if ( x && y && typeof obj === 'object' ) {
362			if ( obj instanceof Array ){
363				var state = false;
364				$.each( obj, function( index, value ){
365					if ( _this.hitIn( x, y, value ) ) {
366						state = true;
367						return;
368					}
369				});
370				return state;
371			} else {
372				return  _this.hitIn( x, y, obj );
373			}
374		} else {
375			return false;
376		}
377	};
378	/**
379	 * Проверка попадания координат в одну область
380	 * Checking hit coordinates in one area
381	 * @param {Number} coordinate X
382	 * @param {Number} coordinate y
383	 * @param {Object} one object
384	 * @returns {Boolean} true / false
385	 */
386    photoViewWidget.hitIn = function( x, y, obj ){
387		var l = obj.offset().left,
388			r = l + obj.outerWidth(),
389			t = obj.offset().top,
390			b = t + obj.outerHeight();
391		    //console.log(l+' - '+x+' - '+r+' === '+t+' - '+y+' - '+b);
392            //console.log(( x >= l && x <= r && y >= t && y <= b ) ? true : false );
393		return ( x >= l && x <= r && y >= t && y <= b ) ? true : false;
394	};
395
396/** TOUCH EVENTS */
397    photoViewWidget.touchInitHammer = function(){
398	    //console.log(' INIT HAMMER ');
399		var _this = this;
400		_this.hammerManager = new Hammer.Manager( _this.$el[ 0 ] );
401		//  PAN
402		_this.hammerManager.add(
403		    new Hammer.Pan({
404                event: 'pan',
405                threshold: 0,
406                pointers: 0
407		    })
408        );
409		//  SWIPE
410		_this.hammerManager.add(
411		    new Hammer.Swipe({
412                event: 'swipe'
413		    })
414        )
415        _this.hammerManager
416            .get( 'swipe' )
417            .recognizeWith( 'pan' );
418		//  PINCH
419		_this.hammerManager.add(
420		    new Hammer.Pinch({
421                threshold: 0
422		    })
423        ).recognizeWith(_this.hammerManager.get('pan'));
424		//  TAB and DOUBLETAP
425		_this.hammerManager.add(
426		    new Hammer.Tap({
427                event: 'doubletap',
428                taps: 2
429		    })
430        );
431		_this.hammerManager.add(
432		    new Hammer.Tap({
433                event: 'tap',
434                taps: 1
435		    })
436        );
437		_this.hammerManager
438            .get( 'doubletap' )
439            .recognizeWith( 'tap' );
440		_this.hammerManager
441            .get( 'tap' )
442            .requireFailure( 'doubletap' );
443		// on action
444		_this.hammerManager.on( "pinchstart pinchmove pinchend", function( ev ) {
445			_this.touchPinch( ev )
446		});
447		_this.hammerManager.on("panstart panmove panend", function( ev ){
448			_this.touchPan(ev)
449		});
450		_this.hammerManager.on("swipeleft swiperight swipeup swipedown", function( ev ){
451			_this.touchSwipe( ev )
452		});
453		_this.hammerManager.on("tap", function( ev ){
454			_this.touchTap( ev )
455		});
456		_this.hammerManager.on("doubletap", function( ev ){
457			_this.touchDoubleTap( ev )
458		});
459	};
460    photoViewWidget.touchPinch = function (ev){
461		//console.log(ev.type);
462	};
463    photoViewWidget.touchPan = function (ev){
464		var _this = this;
465		//console.log('toch pan = '+_this.image.scale.actual);
466		/*if ( _this.image.scale.actual > 1 ){
467			if  (ev.type == 'panstart') {
468				_this.image.translate.start_x = _this.image.translate.x;
469				_this.image.translate.start_y = _this.image.translate.y;
470			}
471			if (ev.type == 'panstart' || ev.type == 'panmove'){
472				_this.image.translate.x = _this.image.translate.start_x + (ev.deltaX / _this.image.scale.actual );
473				_this.image.translate.y = _this.image.translate.start_y + (ev.deltaY / _this.image.scale.actual );
474				_this.requestElTransform({
475					transformStyle: {
476						'transform': 'scale3d(' + _this.image.scale.actual + ', ' + _this.image.scale.actual + ', 1) ' +
477						'translate3d(' + _this.image.translate.x + 'px, ' + _this.image.translate.y + 'px, 0)'
478					}
479				});
480			}
481			//else if (ev.type = 'panend'){
482			 //_this.image.translate.start_x = _this.image.translate.x;
483			 //_this.image.translate.start_y = _this.image.translate.y;
484			 //}
485		}*/
486		if ( $(ev.target).closest('.m-open .the-in-scroll__content').length ) {
487			var scrollBox = _this.$descOver.find('.the-in-scroll__box');
488			if (ev.type == 'panstart') {
489				scrollBox.data('scrollTop', scrollBox.scrollTop() );
490			}
491			if (ev.type == 'panmove') {
492				scrollBox.scrollTop( scrollBox.data('scrollTop') - ev.deltaY );
493			}
494		}
495	};
496    photoViewWidget.touchSwipe = function (ev){
497		var _this = this;
498		if (ev.type == 'swiperight'){
499			_this.changePrev();
500		} else if (ev.type == 'swipeleft'){
501			_this.changeNext();
502		}/* else if (ev.type == 'swipeup' || ev.type == 'swipedown'){
503		 _this.close();
504		 }*/
505	};
506    photoViewWidget.touchTap = function(ev) {
507		var _this = this;
508		//console.log( ev.type + ' x = ' + ev.center.x + ', y = ' + ev.center.y );
509		if ( !this.disabledHideControls && !_this.hitInArr( ev.center.x, ev.center.y, _this.objArrCreate( _this.exceptionTap ) ) ){
510			this.controlsToggle();
511		} else if ( this.disabledHideControls ){
512            this.disabledHideControls = false;
513        }
514	};
515    photoViewWidget.touchDoubleTap = function (ev) {
516		var _this = this;
517		//console.log(ev.type + ' x = ' + ev.center.x + ', y = ' + ev.center.y );
518		 /*if (!_this.hitInArr(ev.center.x, ev.center.y, _this.objArrCreate( _this.exceptionTap ) ) ){
519			_this.imageScaling({
520				x : ev.center.x,
521				y : ev.center.y
522			});
523		}*/
524	};
525    photoViewWidget.checkElPosition = function(){
526
527	};
528	/**
529	 * requestAnimationFrame
530	 */
531	/*photoViewWidget.requestElUpdate = function(param){
532	 var _this = this;
533	 if (!_this.imageTransformation){
534	 _this.requestElAnimFrame(function() {
535	 _this.requestElTransform(param)
536	 });
537	 _this.imageTransformation = true;
538	 }
539	 },
540	 requestElAnimFrame: function(func){
541	 var requestAnimationFrame = window.requestAnimationFrame ||
542	 window.mozRequestAnimationFrame ||
543	 window.webkitRequestAnimationFrame ||
544	 window.msRequestAnimationFrame ||
545	 function(func){
546	 window.setTimeout(func, 1000 / 60);
547	 };
548	 requestAnimationFrame(func);
549	 };*/
550    photoViewWidget.requestElTransform = function(param) {
551		var _this = this,
552			transformDurationBefore = 0,
553			transformDuration = 0;
554
555		// если есть объект стилей навешиваем до трансформации
556		if ( param && typeof param.transformStyleBefore == 'object' && $(param.transformStyleBefore).length > 0){
557			transformDurationBefore = param.transformDurationBefore || 10;
558			console.log(' set transform style before ');
559			_this.setStyle(_this.image.$obj, param.transformStyleBefore);
560		}
561
562		setTimeout(function(){
563
564			if ( param && typeof param.transformStyle == 'object' && $(param.transformStyle).length > 0){
565				transformDuration = (param.transformStyle.transition && param.transformStyle.transition.duration) ? param.transformStyle.transition.duration : (_this.settings.durationTransition || 100) ;
566				console.log(' set transform style ');
567				_this.setStyle(_this.image.$obj, param.transformStyle);
568			}
569
570			setTimeout(function(){
571				if ( param && typeof param.transformStyleAfter == 'object' && $(param.transformStyleAfter).length > 0){
572					console.log(' set transform style after ');
573					_this.setStyle(_this.image.$obj, param.transformStyleAfter);
574				}
575				// если передан callback выполняем
576				if (param && typeof param.callBack === 'function'){
577					console.log(' set transform callback ');
578					param.callBack(_this);
579				}
580			}, transformDuration );
581
582
583		}, transformDurationBefore );
584
585	};
586
587	/** ИНФА по активной картинке */
588    photoViewWidget.imageDataReset = function(){
589		var _this = this;
590		_this.image.$obj = $( _this.classPrefix + 'img', _this.$itemActive );
591		_this.imageDataSizeUpdate();
592		_this.image.scale.actual = 1;
593		_this.image.origin.x = _this.image.size.w / 2;
594		_this.image.origin.y = _this.image.size.h / 2;
595		_this.image.translate.x =_this.image.translate.start_x = 0;
596		_this.image.translate.y =_this.image.translate.start_y = 0;
597	};
598    photoViewWidget.imageDataSizeUpdate = function(){
599		if ( typeof this.image.$obj == 'object' && this.image.$obj.length == 1) {
600			this.image.size.w = this.image.$obj.width();
601			this.image.size.h = this.image.$obj.height();
602		}
603	};
604    photoViewWidget.imageCentering = function(){
605
606	};
607    photoViewWidget.imageTranslateOrigin = function( origin, coord, scaleActual ){
608		return ( ( ( ( origin - this.image.origin[ coord ] ) * ( scaleActual - 1 ) ) / scaleActual ) + this.image.translate[ coord ] );
609	};
610    photoViewWidget.imageTranslateZoom = function( scaleActual, scaleTo, coord ){
611		return ( this.image.translate[ coord ] * scaleActual / scaleTo );
612	};
613    photoViewWidget.imageScaling = function( param ){
614		if ( !param ) return false;
615		console.log( '*** ', param );
616		var _this = this,
617			elW = _this.$el.width(),                //TODO width контейнера
618			elH = _this.$el.height(),               //TODO height контейнера
619			elL = _this.$el.offset().left,          //TODO offset.left контейнера
620			elT = _this.$el.offset().top,           //TODO offset.top контейнера
621			imgW = _this.image.$obj.width(),        //TODO width картинки
622			imgH = _this.image.$obj.height(),       //TODO height картинки
623			imgL = _this.image.$obj.offset().left,  //TODO offset.left картинки
624			imgT = _this.image.$obj.offset().top,   //TODO offset.top картинки
625		// короткие записи для SCALE
626			scaleActual = _this.image.scale.actual, //TODO нынешний scale
627			scaleMin = _this.image.scale.min,       //TODO минимальный размер scale картинки
628			scaleMax = _this.image.scale.max,       //TODO максимальный размер scale картинки
629			scaleTo = param.scale || ( scaleActual >
629 scaleMin ? scaleMin : scaleMax ); //TODO переданный scale или вычисляется если не передан
630
631		_this.image.scale.actual = scaleTo;
632
633		// координаты клика по экрану
634		var	x = param.x || ( elW / 2 ) + elL,       //TODO половина ширины контейнер + левый офсет контейнера
635			y = param.y || ( elH / 2 ) + elT,       //TODO половина высотф контейнер + верхний офсет контейнера
636		// координаты клика / центра зумирования на картинке
637			originX = ( x - imgL ) / scaleActual,     //TODO координата X центра зумирования на картинке
638			originY = ( y - imgT ) / scaleActual;     //TODO координата Y центра зумирования на картинке
639
640
641		/*	console.log(' ');
642		 console.log(' ');
643		 console.log(' ');
644		 console.log('=================================================================');
645		 console.log('=   ON SCREEN   X = '+x+', Y = '+y+', scale actual = '+_this.image.scale.actual);
646		 console.log('= ---------------------------------------------------------------');
647		 console.log('=   ELEMENT     Width = '+elW+', Height = '+elH);
648		 console.log('=               OffsetLeft = '+elL+', OffsetTop = '+elT);
649		 console.log('= ---------------------------------------------------------------');
650		 console.log('=   IMAGE       Width = '+imgW+', Height = '+imgH);
651		 console.log('=               OffsetLeft = '+imgL+', OffsetTop = '+imgT);
652		 console.log('= ---------------------------------------------------------------');
653		 console.log('=   SCALE       actual = '+scaleActual+', new = '+scaleTo);
654		 console.log('=               min = '+scaleMin+', max = '+scaleMax);
655		 console.log('= ---------------------------------------------------------------');
656		 console.log('=   ON IMAGE    X = '+originX+', Y = '+originY);
657		 console.log('= ---------------------------------------------------------------');*/
658
659
660		/** подсчет translate3d для нового origin без изменения Zoom */
661		var translateOriginX = ( ( (originX - _this.image.origin.x ) * (scaleActual-1) ) / scaleActual ) + _this.image.translate.x;
662		var translateOriginY = ( ( (originY - _this.image.origin.y ) * (scaleActual-1) ) / scaleActual ) + _this.image.translate.y;
663
664
665
666		/**     подсчет translate3d для нового zoom без изменения Origin                */
667		/**     x * actualScale - узнаем реальный сдвиг картинки                        */
668		/**     x * actualScale / scaleTo - узнаем оригинальный параметр сдвига         */
669		var translateZoomX = _this.image.translate.x * scaleActual / scaleTo;
670		var	translateZoomY = _this.image.translate.y * scaleActual / scaleTo;
671
672
673		var translateZoomOrigin =   _this.image.translate.x;
674
675
676
677
678		/** */
679		var translateOriginXAfter = (((imgW/2 - originX )*(scaleTo-1))/scaleTo) + translateZoomX;
680		var translateOriginYAfter = (((imgH/2 - originY )*(scaleTo-1))/scaleTo) + translateZoomY;
681
682		originX = 600;
683		originY = 200;
684
685		/*var translate_x = ((_this.image.size.w / 2) - originX) / scaleTo * (scaleTo-1),
686		 translate_y =((_this.image.size.h / 2) - originY) / scaleTo * (scaleTo-1);*/
687
688		var testX = _this.image.origin.x - (((originX - _this.image.origin.x)*(scaleTo-1)/scaleTo) - (originX - _this.image.origin.x));
689		var testY = _this.image.origin.y - (((originY - _this.image.origin.y)*(scaleTo-1)/scaleTo) - (originY - _this.image.origin.y));
690
691
692
693		_this.setStyle(_this.image.$obj, {
694			'transform': 'scale3d(' + scaleTo + ', ' + scaleTo + ', 1) ' +
695			'translate3d('+translateZoomX+'px, '+translateZoomY+'px, 0)'
696		});
697
698
699		_this.image.translate.x = translateZoomX;
700		_this.image.translate.y = translateZoomY;
701		_this.image.scale.actual = scaleTo;
702
703
704		/*_this.requestElTransform({
705		 transformStyleBefore : {
706		 'transform-origin': originX + 'px ' + originY + 'px 0px',
707		 'transform': 'scale3d(' + scaleActual + ', ' + scaleActual + ', 1) ' +
708		 'translate3d('+translateOriginX+'px, '+translateOriginY+'px, 0)'
709		 },
710		 transformStyle : {
711		 'transform-origin': originX + 'px ' + originY + 'px 0px',
712		 'transform': 'scale3d(' + scaleTo + ', ' + scaleTo + ', 1) ' +
713		 'translate3d('+translateZoomX+'px, '+translateZoomY+'px, 0)'
714		 },
715		 transformStyleAfter : {
716		 'transform-origin': imgW/2 + 'px ' + imgH/2 + 'px 0px',
717		 'transform': 'scale3d(' + scaleTo + ', ' + scaleTo + ', 1) ' +
718		 'translate3d('+translateOriginXAfter+'px, '+translateOriginYAfter+'px, 0)'
719		 },
720		 callBack : function(__this){
721		 _this = __this || this;
722		 _this.image.translate.x = translateZoomX;
723		 _this.image.translate.y = translateZoomY;
724		 _this.image.scale.actual = scaleTo;
725		 }
726		 });*/
727
728
729
730
731
732		console.log('originX = '+_this.image.origin.x+', originY = '+_this.image.origin.y+' translateX = '+_this.image.translate.x+' translateY = '+ _this.image.translate.y+' scale = '+_this.image.scale.actual);
733
734
735		/*_this.requestElTransform({
736		 transformDurationBefore : 5,
737		 transformStyleBefore : {
738		 'transform-origin': originX + 'px ' + originY + 'px 0px',
739		 'transform': 'scale3d(' + scaleActual + ', ' + scaleActual + ', 1) ' +
740		 'translate3d('+trXforNewOrigin+'px, '+trYforNewOrigin+'px, 0)'
741		 },
742		 transformStyle : {
743		 transition : {
744		 prop: 'none'
745		 },
746		 transform: 'scale3d(' + scaleTo + ', ' + scaleTo + ', 1) ' +
747		 'translate3d('+trXforNewOrigin+'px, '+trYforNewOrigin+'px, 0)'
748		 },
749
750		 callBack : function(__this){
751		 _this = __this || this;
752
753		 _this.setStyle(_this.image.$obj, {
754		 'transform-origin': originX + 'px ' + originY + 'px 0px',
755		 transform: 'scale3d(' + scaleTo + ', ' + scaleTo + ', 1) ' +
756		 'translate3d(' + translate_x + 'px, ' + translate_y + 'px, 0)'
757		 });
758
759		 // записываем измененные данные
760		 *//**//*_this.image.translate.x = translate_x;
761		 _this.image.translate.y = translate_y;*//**//*
762		 _this.image.scale.actual = scaleTo;
763		 }
764		 });*/
765
766
767
768
769
770	};
771
772	/** open photo view */
773    photoViewWidget.open = function( params ){
774
775        //console.log('OPEN PHOTOVIEW ',params);
776		var _this = this;
777	// Если нет номера группы - присваиваем false
778		if ( !params.group ) params.group = false;
779        params.group = params.group || typeof params.clickObj == 'object' && params.clickObj.attr( 'data-'+_this.dataGroup ) || false;
780    // Обновляем набор эелментов группы если:
781		if (  _this.settings.updateGroupOpen    // - выствален флаг принудительного обновления каждый раз
782            || params.group === false           // - номер группы false - открыть одну картинку
783            || _this.group.id != params.group   // - номер группы не совпадает с ранее отрытой
784            || !_this.group.$items              // - нет набора элементов
785            || !_this.group.length              // - нет кол-ва элементов
786        ) {
787			_this.updateItemsGroup( params );
788		}
789
790	// Если в выборке нет элементов выходим
791		if ( _this.group.length <= 0 ) {
792			console.log( 'PhotoViewWidget ERROR - no items group length' );
793			return false;
794		}
795	// Записываем номер активного слайда
796		_this.itemActive.index = 0;           // по умолчанию 0
797		if ( typeof params.clickObj == 'object' ) {  // если передан объект клика
798            var openedImgId = params.clickObj.attr( 'data-'+_this.dataImageId );
799            // если есть id картинки фильтруем набор и берем индекс(объект клика может быть клоном), если нет id то ищем индекс объекта клика
800			_this.itemActive.index = _this.group.$items.index( openedImgId ? _this.group.$items.filter( '[data-' + _this.dataImageId + '="' + openedImgId + '"]' ) : params.clickObj );
801		} else if ( params.photo ){                   // если передан store номер фото берем индекс из группы по совпадению со store
802			var storeObj = _this.group.$items.filter( function(){
803				return $( this )[ 0 ].photoViewWidget_photo == params.photo ? $( this ) : false;
804			});
805			if ( storeObj.length == 1 ){
806				_this.itemActive.index = _this.group.$items.index( storeObj );
807			}
808		}
809	/* если была открыта галерея закрываем */
810		if( _this.visibleView() ) {
811			_this.close();
812		}
813	/* записывыаем данные о активном, предыдущем и последующем слайдах */
814		_this.getItemsData();
815	/* показываем слой с кнопками */
816		_this.controlsShow();
817	/* Скрываем слой описания */
818		_this.activeDataVisible( false );
819	/* сохраняем состояния body */
820        _this.layerStates = {
821            bodyTop :       $( 'body' ).scrollTop(),
822            bodyLeft :      $( 'body' ).scrollLeft(),
823            htmlTop :       $( 'html' ).scrollTop(),
824            htmlLeft :      $( 'html' ).scrollLeft(),
825            bodyOverflow :  $( 'body' ).css( 'overflow' )
826        };
827	/* выставляем параметры на слой данных */
828		_this.setStyle( _this.$page.addClass( _this.modeBlur ),{
829			'position' : 'fixed',
830			'top' : -( _this.layerStates.bodyTop + _this.layerStates.htmlTop ),
831			'left' : -( _this.layerStates.bodyLeft + _this.layerStates.htmlLeft )
832		});
833	/* выставляем прокрутку страницы на ноль, показываем слой просмотра фото и образмериваем его */
834		_this.setStyle( $( 'body' ).addClass( _this.modPhotoviewOpen ).scrollTop( 0 ),{
835			'overflow' : 'visible'
836		});
837		_this.setStyle( _this.$el,{
838			'display' : 'block'
839		});
840		_this.visibilityEl();
841		_this.setPhotoviewHeight();
842		_this.setImages();
843		_this.states.widgetOpen = true;
844		_this.storeCheck( 'open' );
845		//  EVENT с клавиатуры "prev-37", "next-39" и "esc-27"
846		$( 'body' ).on( 'keydown.photoview' , function( e ){
847			_this.keyDownNavigate( e );
848		});
849	};
850	/** close photo view */
851    photoViewWidget.close = function(){
852		var _this = this;
853		if ( _this.visibleView() ){
854			// скрываем фотовью
855			_this.setStyle( _this.$el.hide(), {
856				'position' : 'absolute'
857			});
858			// удаление урлов и стилей картинокс фотовью
859			_this.cleaning();
860			// выставляем прокрутку контентной части как было, задержка если из history
861			var historyTimeout = _this.states.storeFromHistory ? 5 : 0;
862			setTimeout( function () {
863				// убираем класс размытия с контентной части и позиционируем
864                    _this.setStyle( _this.$page.removeClass( _this.modeBlur ),{
865					'position' : 'relative',
866					'top' : 0,
867					'left' : 0
868				});
869				_this.setStyle( $( 'body' ).removeClass( _this.modPhotoviewOpe
869n ), {
870					'overflow': _this.layerStates.bodyOverflow
871				});
872				$( 'body' )
873					.scrollTop( _this.layerStates.bodyTop )
874					.scrollLeft( _this.layerStates.bodyLeft );
875				$( 'html' )
876					.scrollTop( _this.layerStates.htmlTop )
877					.scrollLeft( _this.layerStates.htmlLeft );
878			}, historyTimeout );
879			// передача в хистори нового хеша
880			_this.storeCheck( 'close' );
881			// состояние что фотовью не открыто
882			_this.states.widgetOpen = false;
883			// убираем обработку надатия клавиш
884			$('body').off( 'keydown.photoview' );
885		}
886	};
887	/** remove src and style images attribute, remove style sub blocks images */
888    photoViewWidget.cleaning = function(){
889		$( this.classPrefix + 'img', this.$el ).removeAttr( 'src' ).removeAttr( 'style' );
890		this.$sub.removeAttr( 'style' );
891	};
892	/**
893	 * обновляем получаем набор элементов по <groupID>, кол-во элементов и устанавливаем или скрываем номер активного
894	 * update image items group
895	 * @param {Object|Number} object params or group id
896	 * @update {Number} this.group.id - group number
897	 * @update {Object} this.group.$items - group items
898	 * @update {Number} this.group.length - group length
899	 * @add {Number} store number
900	 */
901    photoViewWidget.updateItemsGroup = function( params ){
902        var _this = this;
903        // обнуляем информацию об открытой группе
904        _this.group.$items = false;
905        _this.group.length = false;
906        // определяем id группы
907        if ( $.isPlainObject( params ) && typeof params.clickObj == "object" ){
908            _this.group.id = params.clickObj.attr( 'data-'+_this.dataGroup ) || false;
909        } else {
910            _this.group.id = params.group;
911        }
912        // создаем набор объектов
913        if ( _this.group.id === false && typeof params.clickObj == "object" ){
914            _this.group.$items = params.clickObj;
915        } else if ( _this.group.id ){
916            //console.log('DONE DONE DONE')
917            _this.group.$items = $( _this.classPrefix + 'open[data-' + _this.dataGroup + '="' + _this.group.id + '"][data-' + _this.dataSrc + ']', $('body') ).filter(function () {
918                // фильтрация элементов
919                return typeof _this.settings.itemsFilter == 'function' ? _this.settings.itemsFilter( this ) : 1;
920            });
921        }
922        // Добавляем в объект имя картинки для хеша
923        $.each( _this.group.$items, function( i, v ){
924            this.photoViewWidget_photo =  $( v ).data( _this.dataImageId ) || $( v ).data( _this.dataSrc ).match(/.*\/([^\.]+?)\.[a-zA-Z]+$/)[ 1 ]; // имя картинки для хеша
925        });
926        // записываем кол-во элеметов в наборе
927        _this.group.length = _this.group.$items.length
928	};
929	/** Получаем набор данных по слайдам: предыдущий, активный, следующий  */
930    photoViewWidget.getItemsData = function( n ){
931		var _this = this;
932		if ( typeof n === 'number' ) _this.itemActive.index = n;
933		_this.getOneItemData( 'itemActive', _this.itemActive.index );
934		// определяем номер предыдущего элемента
935		if (_this.itemActive.index > 0){
936			_this.itemPrev.index = _this.itemActive.index - 1;
937		} else if ( _this.settings.itemsLoop && _this.itemActive.index == 0 && _this.group.length > 1 ){
938			_this.itemPrev.index = _this.group.length - 1;
939		} else {
940			_this.itemPrev.index = false;
941		}
942		_this.getOneItemData( 'itemPrev', _this.itemPrev.index );
943        // определяем номер следующего элемента
944		if ( _this.itemActive.index + 1 < _this.group.length ){
945			_this.itemNext.index = _this.itemActive.index + 1;
946		} else if ( _this.settings.itemsLoop && _this.itemActive.index + 1 == _this.group.length && _this.group.length > 1 ){
947			_this.itemNext.index = 0;
948		} else {
949			_this.itemNext.index = false;
950		}
951		_this.getOneItemData('itemNext', _this.itemNext.index );
952	};
953	/** Запись набора данных по слайду */
954    photoViewWidget.getOneItemData = function( name, index ){
955		var _this = this;
956		if ( index === false ){
957			_this[ name ].$obj = false;
958			_this[ name ].src = false;
959		} else {
960			_this[ name ].$obj = $( _this.group.$items[ index ] );
961			_this[ name ].src = _this[ name ].$obj.data( _this.dataSrc );
962		}
963	};
964
965/** Загружаем картинки и устанавливаем в верстку */
966    photoViewWidget.setImages = function(arrayItems){
967        var _this = this,
968            itemsName = [ 'itemActive', 'itemPrev', 'itemNext' ];
969        if ( typeof arrayItems === 'object' ) itemsName = arrayItems;
970		$.each( itemsName, function( i, v ){
971			if ( _this[ v ].src !== false ) {
972				var $img = $( _this.classPrefix + 'img', _this[ '$' + v ] ),    // ссылка на елемент
973					$newImage = $( '<img src="' + _this[ v ].src + '">');
974				$img.removeAttr('src');
975				if ( _this.group.$items[ _this[ v ].index ].photoViewWidget_imgLoaded ) {
976					/* TODO remove */ //console.log('Already uploaded ' + _this[ v ].index);
977					$img.attr( 'src', _this[ v ].src );
978					if ( v == 'itemActive' ) _this.setImagesAct();
979				} else {
980					var $loader = $( _this.classPrefix + 'loader', _this[ '$' + v ] );
981					$loader.show();
982					$newImage.on( 'load', function() {
983						/* TODO remove */ //console.log('Images UPLOAD ' +_this[ v ].index + ' ' + _this[ v ].src);
984						_this.group.$items[ _this[ v ].index ].photoViewWidget_imgLoaded = true;
985						$img.attr( 'src', _this[ v ].src );
986						$loader.hide();
987						if ( v == 'itemActive' ) _this.setImagesAct();
988					}).on( 'error', function() {
989						console.log( 'PhotoViewWidget images ERROR ' + _this[ v ].src );
990					});
991				}
992			}
993		});
994		_this.setItemIndexView();
995	};
996    photoViewWidget.setImagesAct = function(){
997		var _this = this;
998		_this.imageDataReset();
999		_this.setActiveData();
1000		_this.updateShare();
1001		_this.updateDataSize( true );
1002	};
1003
1004/** Active item data */
1005    photoViewWidget.setActiveData = function(){
1006		var _this = this,
1007		    openInfo = _this.itemActive.$obj.find( _this.classPrefix + 'open-info > *');
1008		$.each( openInfo , function(){
1009		    $( '.' + $( this ).attr( 'class' ), _this.$el ).html( $( this ).html() );
1010        });
1011	};
1012/** update share link and share title */
1013    photoViewWidget.updateShare = function(){
1014		var $share = $( '.share', this.$placeSocial );
1015		if ( $share.length > 0 ) {
1016            $share.attr({
1017                'data-url': this.itemActive.$obj.data(this.dataShareLink) || location.href,
1018                'data-title': this.itemActive.$obj.data(this.dataShareTitle) || $('meta[ property="og:title" ]').attr("content") || $('head title').html(),
1019                'data-id': this.itemActive.$obj.data(this.dataShareId)
1020            });
1021        };
1022	};
1023/** */
1024    photoViewWidget.updateDataSize = function( change ){
1025        if ( this.visibleView ) {
1026            var _this = this,
1027                destTextH = this.$descText.height(),
1028                descSizeH = this.$descSize.height();
1029            if ( destTextH > 0 ){
1030                // При смене слайда сворачиваем описани для нового
1031                if ( change ) _this.showDescMore( false , true );
1032                // показываем блок описания
1033                _this.activeDataVisible( true );
1034                // проверяем видимость кнопки more
1035                if ( destTextH <= descSizeH ) {
1036                    _this.showDescMore( false, true );
1037                    _this.$descMore.hide()
1038                } else {
1039                    _this.$descMore.show();
1040                }
1041                // выставляем высоту описания
1042                _this.$descOver.stop().height( _this.$desc.hasClass( _this.modeOpen ) ? Math.min( destTextH, parseInt( _this.clientHeight * 0.4 ) ) : Math.min( destTextH, descSizeH ) )
1043            } else {
1044                _this.activeDataVisible( false );
1045            }
1046
1047
1048        }
1049    };
1050/** Показ и скрытие блока описание */
1051    photoViewWidget.activeDataVisible = function( state ){
1052        this.$desc.toggleClass(  this.modeDisabled, ( ( state === true ) ? false : true ) );
1053    };
1054/** клик по кнопке показать больше описания */
1055    photoViewWidget.showDescMore = function( state, fast ){
1056        var _this = this,
1057            destTextH = _this.$descText.height(),
1058            descSizeH = _this.$descSize.height();
1059        if ( state !== false && state !== true ) {
1060            state = _this.$desc.hasClass( _this.modeOpen ) ? false : true;
1061        }
1062    _this.$descOver.stop().animate({
1063            height: state ? Math.min( destTextH, parseInt( _this.clientHeight * 0.4 ) ) : Math.min( destTextH, descSizeH )
1064        }, fast ? 0 : 250, function(){
1065            if ( state === true ) _this.$desc.toggleClass( _this.modeOpen, state );
1066        });
1067        if ( state === false ) _this.$desc.toggleClass( _this.modeOpen, state );
1068        _this.$descOver.scrollTop( 0 );
1069        if (  _this.$descOver.hasClass( 'the-in-scroll' ) ){
1070            _this.$descOver.trigger( 'scroll-to.the-in-scroll' );
1071        }
1072
1073    };
1074/** CONTROL */
1075    photoViewWidget.controlsToggle = function( state ){
1076		var _this = this;
1077		if ( state === true ){
1078			_this.controlsShow();
1079		} else if ( state === false ){
1080			_this.controlsHide();
1081		} else {
1082			if( _this.states.controlsVisible === true ){
1083				_this.controlsHide();
1084			} else {
1085				_this.controlsShow();
1086			}
1087		}
1088	};
1089    photoViewWidget.controlsHide = function(){
1090		var _this = this;
1091        _this.$canHide.stop().animate({
1092			opacity: 0
1093		}, 150, function(){
1094            _this.$canHide.addClass( _this.modeHidden );
1095			_this.states.controlsVisible = false;
1096		});
1097	};
1098    photoViewWidget.controlsShow = function(){
1099		var _this = this;
1100		_this.$canHide.stop().animate({
1101			opacity: 1
1102		}, 150, function(){
1103            _this.$canHide.removeClass( _this.modeHidden );
1104			_this.states.controlsVisible = true;
1105		});
1106	};
1107
1108/** STORE */
1109    photoViewWidget.transferringFormat = function( obj, sign, sep ){
1110            var str='';
1111            sign = sign || '=';
1112            sep = sep || '&';
1113            if ( typeof obj === 'object' ) {
1114                $.each( obj, function ( i, v ) {
1115                    if ( i != '' ) str += ( sep + i + ( v !== undefined ? sign + v : '' ) );
1116                });
1117                return str.replace( sep, '' );
1118            } else if ( typeof obj === 'string' ) {
1119                return obj
1120                    .split( sep )
1121                    .map( function( e ) { return e.split( sign ) } )
1122                    .reduce( function ( o, arr ) { return o[ arr[ 0 ] ] = arr[ 1 ], o; }, {} );
1123            }
1124            return false;
1125        };
1126    photoViewWidget.storeUpdateUrl = function( stateObj, title, paramsObj, replace ){
1127            var _this = this;
1128            if ( _this.states.storeEnabled && !_this.states.storeFromHistory ){
1129                if ( replace ){
1130                    history.replaceState( stateObj, title, _this.storeFormationUrl( paramsObj ) );
1131                } else {
1132                    history.pushState( stateObj, title, _this.storeFormationUrl( paramsObj ) );
1133                }
1134            }
1135            _this.states.storeFromHistory = false;
1136        };
1137    photoViewWidget.storeFormationUrl = function( paramsObj ){
1138            var params = this.transferringFormat( paramsObj );
1139            return location.origin + location.pathname + location.search + ( ( params == '' || params === false ) ? '' : '#' + params );
1140        };
1141    photoViewWidget.storeUpdObjParams = function( objParams, state ){
1142            var _this = this;
1143            if ( state === false ){
1144                delete objParams[ _this.store.name ];
1145            } else {
1146                var objStoreParams = {};
1147                objStoreParams[ _this.store.group ] = _this.group.id;
1148                objStoreParams[ _this.store.photo ] = _this.group.$items[ _this.itemActive.index ].photoViewWidget_photo;
1149                objParams[ _this.store.name ] = _this.transferringFormat( objStoreParams, '%3D', '%2F' );
1150            }
1151            return objParams;
1152        };
1153    photoViewWidget.storeCheck = function( act ){
1154            var _this = this;
1155            if ( !_this.states.storeEnabled ) return false;
1156            var	objParams = _this.transferringFormat( location.hash.replace( '#', '' ) ),
1157                storeParams = objParams[_this.store.name] ? _this.transferringFormat(objParams[_this.store.name], '%3D', '%2F') : false;
1158            if ( act === 'close'){                                                              // Закрытие photoView
1159                _this.storeUpdateUrl( {}, null, _this.storeUpdObjParams( objParams, false ) );
1160            } else if ( act === 'open' ){                                                       // Открытие photoView
1161                _this.storeUpdateUrl( {}, null, _this.storeUpdObjParams( objParams ) );
1162            } else if ( act === 'change' ){                                                     // Переход на другой слайд photoView
1163                _this.storeUpdateUrl( {}, null, _this.storeUpdObjParams( objParams ), true );
1164            } else if ( !_this.states.widgetOpen && storeParams[ _this.store.group ] ){     // Открытие урла С параметром photo_view
1165                _this.open({
1166                    group: storeParams[ _this.store.group ],
1167                    photo: storeParams[ _this.store.photo ] || false
1168                });
1169            } else if ( !storeParams[ _this.store.group ] ){                                // Открытие урла БЕЗ параметра photo_view
1170                _this.close();
1171            }
1172        };
1173    photoViewWidget.storeHistory = function(){
1174		var _this = this;
1175		if ( _this.states.storeEnabled && !!( window.history && history.pushState && history.replaceState ) ){
1176			window.addEventListener( "popstate", function( e ) {
1177				_this.states.storeFromHistory = true;
1178				_this.storeCheck();
1179			}, false );
1180			// обработка story первый раз после загрузки страницы
1181			_this.storeCheck();
1182
1183		} else {
1184			_this.states.storeEnabled = false;
1185		}
1186	};
1187
1188/** VISIBILITY BUTTONS (obj) */
1189
1190	/**
1191	 * видимость кнопок от ширины photoView и кол-ва фотографий
1192	 * visibility buttons
1193	 * @param {Obj | Array object }
1194	 */
1195    photoViewWidget.visibilityEl = function( el ){
1196		var _this = this;
1197		if ( _this.visibleView() ) {
1198			var objArr = _this.objArrCreate( el || _this.variableVisibility );
1199			if ( _this.group.length > 1 && ( !_this.states.hammerFunc || _this.settings.desktopWidth <= _this.getPhotoviewWidth() ) ) {
1200				_this.visibilityElAct( objArr, 'show' );
1201			} else {
1202				_this.visibilityElAct( objArr );
1203			}
1204		}
1205	};
1206    photoViewWidget.visibilityElAct = function( objArr, act ){
1207		if ( typeof objArr === 'object' && ( objArr instanceof Array ) ) {
1208			$.each( objArr, function ( k, v ) {
1209				act === 'show' ? v.show() : v.hide();
1210			});
1211		} else {
1212			act === 'show' ? objArr.show() : objArr.hide();
1213		}
1214	};
1215
1216	/**
1217	 * Следующий слайд
1218	 */
1219    photoViewWidget.changeNext = function(){
1220		this.changeItem( 'itemNext' );
1221	};
1222	/**
1223	 * Прердыдущий слайд
1224	 */
1225    photoViewWidget.changePrev = function(){
1226		this.changeItem( 'itemPrev' );
1227	};
1228	/**
1229	 * Прердыдущий слайд
1230	 * Changes slide
1231	 * @param {String} direction
1232	 */
1233    photoViewWidget.changeItem = function( dir ){
1234		var _this = this;
1235		if ( _this.states.itemChange === false && _this[ dir ].index !== false ) {
1236			_this.states.itemChange = true;
1237			_this.activeDataVisible( false );
1238			var sign = '';
1239			if ( dir === 'itemNext' ) sign = '-';
1240			_this.setStyle( _this.$sub, {
1241				'transition': {
1242				    'prop': 'transform'
1243                },
1244				'transform': 'translate3d(' + sign + ( _this.getPhotoviewWidth() * 1.05 ) + 'px, 0px, 0px)',
1245				'overflow': 'visible'
1246			});
1247			_this.setStyle( _this.$itemActive, {
1248				'transition': {
1249				    'prop': 'transform'
1250                },
1251				'transform': 'translate3d(' + sign + ( _this.getPhotoviewWidth() * 1.05 ) + 'px, 0px, 0px)'
1252			});
1253			// загружаем новые данные о слайдах
1254			_this.getItemsData( _this[ dir ].index);
1255			setTimeout( function() {
1256				// заменяем картинки на новые после окончания сдвига
1257				_this.setImages();
1258				// свдигаем слои назад
1259				_this.setStyle( _this.$sub, {
1260					'transition': {
1261					    'prop': 'none'
1262                    },
1263					'transform': 'translate3d(0px, 0px, 0px)',
1264					'overflow': 'hidden'
1265				});
1266				_this.setStyle( _this.$itemActive, {
1267					'transition': {
1268					    'prop': 'none'
1269                    },
1270					'transform': 'translate3d(0px, 0px, 0px)'
1271				});
1272				_this.states.itemChange = false;
1273				_this.storeCheck( 'change' );
1274			}, _this.settings.durationTransition || 100 );
1275		}
1276	};
1277	/**
1278	 * управление с клавиатуры
1279	 * keydown navigate
1280	 * @param {Code}
1281	 * @param {Object}
1282	 * @returns {Boolean} true|false
1283	 */
1284    photoViewWidget.keyDownNavigate = function( e ){
1285		var _this = this;
1286		if( _this.visibleView() ){
1287			var	keyCode = {
1288				left: 37,   // button - Arrow Left
1289				right: 39,  // button - Arrow Right
1290				esc: 27     // button - Esc
1291			};
1292			switch ( e.keyCode ) {
1293				case keyCode.left:
1294					_this.changePrev();
1295					return true;
1296				case keyCode.right:
1297					_this.changeNext();
1298					return true;
1299				case keyCode.esc:
1300					_this.close();
1301					return true;
1302				default:
1303					return false;
1304			}
1305		}
1306	};
1307	/**
1308	 * устанавливает числа "номер слайда/общее кол-во"
1309	 * @returns {Boolean} true|false
1310	 */
1311    photoViewWidget.setItemIndexView = function(){
1312		var _this = this;
1313		if( _this.visibleView() ){
1314			if ( _this.group.length <= 1 ) {
1315                _this.$placeIndexView.hide();
1316				return false;
1317			} else {
1318				$( 'span', _this.$placeIndexView ).html( ( _this.itemActive.index + 1 ) + ' из ' + _this.group.length );
1319                _this.$placeIndexView.show();
1320				return true;
1321			}
1322		}
1323	};
1324	/**
1325	 * выставить высоту слоя просмотра фотографий равно высоте окна
1326	 * set the height of the photoview as a window
1327	 * @set {Number} value
1328	 * @returns {Boolean} true|false
1329	 */
1330    photoViewWidget.setPhotoviewHeight = function(){
1331		if( this.visibleView() ) {
1332			this.$el.height( this.clientHeight );
1333			return true;
1334		}
1335		return false;
1336	};
1337	/**
1338	 * получить ширину слоя просмотра фотографий
1339	 * get width photoview
1340	 * @returns {Number} value or {Boolean} false
1341	 */
1342    photoViewWidget.getPhotoviewWidth = function(){
1343		if( this.visibleView() ){
1344			return parseInt( this.$el.outerWidth() );
1345		}
1346		return false;
1347	};
1348	/**
1349	 * позиционирование контентной части по горизонтали при открытом окне фотовью
1350	 * position content to horizontal
1351	 * @set {Number} value
1352	 */
1353    photoViewWidget.posLayerLeft = function( $layer ){
1354		$layer = $layer || this.$page;
1355		if( this.visibleView() ) {
1356			this.layerStates.bodyLeft = $( 'body' ).scrollLeft();
1357			this.layerStates.htmlLeft = $( 'html' ).scrollLeft();
1358			$layer.css( 'left', '-' + ( this.layerStates.bodyLeft + this.layerStates.htmlLeft ) + 'px' );
1359		}
1360	};
1361	/**
1362	 * позиционирование контентной части по вертикали при открытом окне фотовью
1363	 * position content to vertical
1364	 * @set {Number} value
1365	 */
1366    photoViewWidget.posLayerTop = function( $layer ){
1367        $layer = $layer || this.$page;
1368		if( this.visibleView() && $layer.css( 'position' ) == 'absolute' ){
1369			var layerIndentTop = parseInt( $layer.css( 'top' ) ),           // top position layer content
1370				layerHeight = $layer.outerHeight();                         // layer content height
1371			if ( ( layerHeight + layerIndentTop ) < this.clientHeight && layerHeight > this.clientHeight ){
1372				this.layerStates.bodyTop = layerHeight-this.clientHeight;
1373			} else if ( layerHeight < this.clientHeight ){
1374				this.layerStates.bodyTop = 0;
1375			}
1376			$layer.css( 'top', '-' + this.layerStates.bodyTop + 'px' );
1377		}
1378	};
1379	/**
1380	 * взять элемент по классу
1381	 * get live element by class name
1382	 * @param {String} selector
1383	 * @returns {Object}
1384	 */
1385    photoViewWidget.getBy = function( selector ) {
1386		return $( selector, this.$el );
1387	};
1388	/**
1389	 * видимость окна просмотра фотографий
1390	 * photoView visible
1391	 * @returns {Boolean} true|false
1392	 */
1393    photoViewWidget.visibleView = function(){
1394		return this.$el.is( ":visible" );
1395	};
1396	/**
1397	 * получение ширины клиента
1398	 * get clients width
1399	 * @returns {Number} value
1400	 */
1401    photoViewWidget.getCw = function(){
1402		return Math.max(window.innerWidth || 0, (document.getElementsByTagName('html')[0] || {}).clientWidth || 0)
1403	};
1404	/**
1405	 * получение высоты клиента
1406	 * get clients height
1407	 * @returns {Number} value
1408	 */
1409    photoViewWidget.getCh = function(){
1410		return Math.max(window.innerHeight || 0, (document.getElementsByTagName('html')[0] || {}).clientHeight || 0)
1411	};
1412	/**
1413	 * установка стиля(ей) на объект
1414	 * set style to object
1415	 * @param {Object} obj
1416	 * @param {String | Object} property | states
1417	 * @param {String/Number | Object} value | states
1418	 */
1419    photoViewWidget.setStyle = function( obj, prop, val ){
1420		var _this = this;
1421		if ( typeof prop === 'object' ){
1422			$.each( prop, function( key, value ){
1423				_this.setStyle( obj, key, value );
1424			});
1425		} else if ( prop === 'transition' && typeof val === 'object' ){
1426			this.setStyleTransition( obj, val );
1427		} else {
1428			obj.css( _this.prefixedStyle( obj[ 0 ].style, prop ), val )
1429		}
1430	};
1431	/**
1432	 * установка стиля transition на объект
1433	 * set transition style to object
1434	 * @param {Object} obj
1435	 * @param {Object} states { prop, <time>, <type>, <delay> }
1436	 */
1437    photoViewWidget.setStyleTransition = function( obj, states ){
1438		var _this = this;
1439		if ( states.prop === 'none' ){
1440			obj.css( _this.prefixedStyle( obj[ 0 ].style, 'transition' ), 'none' );
1441		} else {
1442			var trDuration = ( states.duration || _this.settings.durationTransition || 100 ) + 'ms',
1443				trTiming = states.type || _this.settings.typeTransition || 'ease-out',
1444				trDelay = ( states.delay || '0' ) + 'ms';
1445			obj.css( _this.prefixedStyle( obj[ 0 ].style, 'transition' ), _this.prefixedStyle( obj[ 0 ].style, states.prop ) + ' ' + trDuration + ' ' + trTiming + ' ' + trDelay );
1446		}
1447	};
1448	/**
1449	 * получение префикса для значения свойства
1450	 * get the prefixed property
1451	 * @param {Object} obj
1452	 * @param {String} property
1453	 * @returns {String | Undefined} prefixed
1454	 */
1455    photoViewWidget.prefixedStyle = function( obj, property ) {
1456		var _this = this;
1457		var prefix, prop;
1458		var camelProp = property[ 0 ].toUpperCase() + property.slice( 1 );
1459		var i = 0;
1460		if ( !_this.vendor_prefixes ) _this.vendor_prefixes = [ '', 'webkit', 'Moz', 'MS', 'ms', 'o' ];
1461		while ( i < _this.vendor_prefixes.length ) {
1462			prefix = _this.vendor_prefixes[ i ];
1463			prop = ( prefix ) ? prefix + camelProp : property;
1464			if ( prop in obj ) {
1465				return prop;
1466			}
1467			i++;
1468		}
1469		return property;
1470	};
1471
1472

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.