-
-
Save edtoken/e543d1371978813c2dec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
'jquery', | |
'underscore', | |
'backbone', | |
'appdir/default/module', | |
'modules/galleryObject/models/module', | |
'modules/galleryObject/collections/gallery', | |
'modules/galleryObject/views/default', | |
'modules/galleryObject/views/empty', | |
'modules/galleryObject/views/popup', | |
//'modules/galleryObject/views/thumbnail', | |
'modules/galleryObject/views/full', | |
], function ($, | |
_, | |
Backbone, | |
ModuleDefaultClass, | |
ModuleModelClass, | |
GalleryCollectionClass, | |
GalleryDefaultViewClass, | |
GalleryEmptyViewClass, | |
GalleryPopUpViewClass, | |
GalleryFullViewClass) { | |
/** | |
* TODO: иногда не прогружается первая картинка при переключении в popup | |
*/ | |
/** | |
* вспомогательные функции | |
*/ | |
// разбираю объекты с картинками | |
var defaultSizeNames = ['thumbnail', 'thumbnail-popup', 'def', 'popup', 'full']; | |
// порядок разбор изображений: | |
// Если какого-то размера нет - его заменяет тот размер, который стоит с права до конца | |
// соответственно эти два размера становятся одинаковые. | |
//например: ['thumbnail', 'def']; = ['thumbnail', 'def', 'def', 'def']; | |
// последние 3 размера будут одинаковые (сами изображения) | |
var parseImagesObj = function(data){ | |
var out = {}; | |
if(!data) return out; | |
// переданные размеры | |
var sizes = _.keys(data); | |
/** | |
* временный костыль | |
*/ | |
if(sizes.indexOf('catalogBigSinglePreview') >= 0){ | |
sizes.splice(1,0, 'catalogBigSinglePreview_popup'); | |
data['catalogBigSinglePreview_popup'] = _.clone(data['catalogBigSinglePreview']); | |
} | |
var sizeName; | |
_.each(defaultSizeNames, function(name, num){ | |
if(sizes.length){ | |
sizeName = sizes.shift(); | |
} | |
out[name] = data[sizeName]; | |
}); | |
return out; | |
}; | |
// prent controller class | |
// создает первую галерею на странице | |
var GalleryObject = ModuleDefaultClass.extend({ | |
name:'GalleryObject', | |
el:$('body'), | |
events:{ | |
'click .gallery-object-def-list':'eventClickDefaultImage', | |
'click .gallery-object-empty img':'eventClickImageEmpty', | |
'click .gallery-object-popup-btn-item-full':'eventClickPopupButtonFull' | |
}, | |
initialize:function(options){ | |
var self = this; | |
this.collection = new GalleryCollectionClass(); // коллекция со всеми моделями галерей | |
this.model = new ModuleModelClass(); // модель модуля | |
// дочерние галереи | |
this.children = {}; | |
this.children.popup = new GalleryPopUpViewClass(); | |
this.children.full = new GalleryFullViewClass(); | |
this.$el.append(this.children.popup.$el); | |
this.$el.append(this.children.full.$el); | |
// глобальные события | |
$(window).bind('hashchange', function () { | |
self.eventChangeWidowHash(); | |
}); | |
// вернуть дабл клик | |
//.$el.dblclick(function () { | |
// self.parent.model.set({viewType: 'popup'}); | |
//}); | |
this.app.ui.keyPress(39, function (e) { | |
// to right | |
self.eventNextImage(); | |
}); | |
this.app.ui.keyPress(37, function (e) { | |
// to left | |
self.eventPrevImage(); | |
}); | |
this.app.ui.keyPress(27, function (e) { | |
self.eventClosePopupAndFull(); | |
}); | |
}, | |
eventNextImage:function(){ | |
for(var c in this.children){ | |
if(!this.children[c].model || !this.children[c].nextImage) continue; | |
this.children[c].nextImage(); | |
} | |
}, | |
eventPrevImage:function(){ | |
for(var c in this.children){ | |
if(!this.children[c].model || !this.children[c].prevImage) continue; | |
this.children[c].prevImage(); | |
} | |
}, | |
eventClosePopupAndFull:function(){ | |
if(this.children.popup.$el.length){ | |
this.children.popup.eventClickCloseGallery(); | |
} | |
if(this.children.full.$el.length){ | |
this.children.full.eventClickCloseGallery(); | |
} | |
}, | |
// open popup gallery | |
openPopupGallery:function(galleryId, imageId){ | |
if(this.children[galleryId]){ | |
var model = this.children[galleryId].model; | |
var images = model.get('images'); | |
if(images.popup && images.popup.length){ | |
imageId = imageId || false; | |
this.children.popup.hide(); | |
this.children.popup.remove(); | |
this.children.full.remove(); | |
this.children.full.hide(); | |
this.$el.append(this.children.popup.$el); | |
this.children.popup.render( | |
this.children[galleryId].model, | |
imageId, | |
{options:{urlController:this.children[galleryId].options.urlController}} | |
); | |
this.children.popup.show(); | |
} | |
} | |
}, | |
openFullGallery:function(galleryId, imageId){ | |
if(this.children[galleryId]){ | |
var model = this.children[galleryId].model; | |
var images = model.get('images'); | |
if(images.full && images.full.length){ | |
imageId = imageId || false; | |
this.children.popup.hide(); | |
this.children.popup.remove(); | |
this.children.full.remove(); | |
this.children.full.hide(); | |
this.$el.append(this.children.full.$el); | |
this.children.full.render( | |
this.children[galleryId].model, | |
imageId, | |
{options:{urlController:this.children[galleryId].options.urlController}} | |
); | |
this.children.full.show(); | |
} | |
} | |
}, | |
// клик по обычной картинке в галерее default (переход ко второму размеру) | |
eventClickDefaultImage:function(e){ | |
var $galleryNode = $(e.target).parents('.gallery-object'); | |
if($galleryNode.length){ | |
var galleryId = $galleryNode.attr('data-gallery-id'); | |
if(galleryId){ | |
// id открываемого изображения | |
var imageId = (e.target.nodeName == 'IMG') | |
? this.app.hp.images.getImageIdFromUrl(e.target.getAttribute('src')) | |
: this.app.hp.images.getImageIdFromUrl($(e.target).find('img').attr('src')); | |
if(!this.app.getAttr('mobile')){ | |
this.openPopupGallery(galleryId, imageId); | |
}else{ | |
// если мобильный открываю full | |
this.openFullGallery(galleryId, imageId); | |
} | |
} | |
} | |
}, | |
eventClickImageEmpty:function(e){ | |
var $galleryNode = $(e.target).parents('.gallery-object'); | |
if($galleryNode.length){ | |
var galleryId = $galleryNode.attr('data-gallery-id'); | |
if(galleryId){ | |
// id открываемого изображения | |
var imageId = (e.target.nodeName == 'IMG') | |
? this.app.hp.images.getImageIdFromUrl(e.target.getAttribute('src')) | |
: this.app.hp.images.getImageIdFromUrl($(e.target).find('img').attr('src')); | |
if(!this.app.getAttr('mobile')){ | |
this.openPopupGallery(galleryId, imageId); | |
}else{ | |
// если мобильный открываю full | |
this.openFullGallery(galleryId, imageId); | |
} | |
} | |
} | |
}, | |
eventClickPopupButtonFull:function(){ | |
this.children.popup.hide(); | |
var galleryId = this.children.popup.model.id; | |
var activeImg = this.children.popup.model.get('activeImg'); | |
this.openFullGallery(galleryId, activeImg); | |
}, | |
// событие переключение хеша | |
eventChangeWidowHash:function(){ | |
//console.log('window change hash'); | |
}, | |
// событие переключение фулскрин | |
eventChangeWindowScreen:function(){ | |
//console.log('window change full screen') | |
}, | |
// добавление новой галереи | |
// порядок фото обязательно: 0 - миниатюра, 1 - большой, 3 - полноэкранный | |
addGallery:function(images, id, options){ | |
var opt = {}; | |
_.extend(opt, options); | |
opt.galleryType = (opt.galleryType)? opt.galleryType : this.model.get('defaultGalleryType'); // тип галереи по умолчанию | |
// нужен уникальный ID галереи | |
if(!id || this.collection.get(id)) id = _.uniqueId(''); | |
// обновленный объект настроек модуля | |
var moduleOptions = this.getBackendModuleOptions(); | |
// формирую данные картинок | |
var modelInitData = {}; | |
modelInitData.nodeID = 'galleryObject-' + id; | |
var initImages = {}; | |
if(moduleOptions && moduleOptions['images_' + id]){ | |
_.extend(initImages, parseImagesObj(moduleOptions['images_' + id])) | |
} | |
if(images){ | |
_.extend(initImages, parseImagesObj(images)); | |
} | |
modelInitData['images'] = initImages; | |
modelInitData['id'] = id; | |
this.collection.add(modelInitData); | |
var model = this.collection.get(id); | |
//_.extend(opt, {model:model}); | |
opt.model = model; | |
// создаю галерею | |
switch(opt.galleryType){ | |
case 'empty': | |
this.children[model.id] = new GalleryEmptyViewClass(opt); | |
break; | |
case 'def': | |
default: | |
this.children[model.id] = new GalleryDefaultViewClass(opt); | |
break; | |
} | |
this.delegateEvents(); | |
} | |
}); | |
return GalleryObject; | |
}); | |
define([ | |
'jquery', | |
'underscore', | |
'backbone', | |
'modules/galleryObject/urlController' | |
], function ($, | |
_, | |
Backbone, | |
UrlControllerClass) { | |
var urlControllerObj = new UrlControllerClass(); | |
var defaultRemoveFunc = Backbone.View.prototype.remove; | |
// бьазовый класс галереи | |
return Backbone.View.extend({ | |
urlController:urlControllerObj, | |
options:{ | |
canHashChange:false, | |
size:'', // стандартный размер картино default/full/thumbnail | |
buttons:{ | |
navigation:false, | |
close:false, | |
}, | |
renderItems:false, // сразу отрисовать все изображения false - нет, true - все, число - количество с начала | |
classNames:{}, // объект с классами для элементов | |
imgSizes:{}, // данные размера картинок | |
urlController:false, // изменения в модели будут менять урл на странице, false или массив размеров, или true (все) | |
}, | |
events:{}, | |
className:'', | |
// все возможные таймеры | |
timers:{}, | |
tagName:'div', | |
nodes:{}, // кеш DOM | |
children:{}, // child views | |
initialize:function(){}, | |
/** | |
* | |
******* Создание HTMl | |
* | |
*/ | |
/** | |
* Создаст объект с данными одного элемента списка галереи | |
* Использутеся в кеше | |
* @param data | |
* @param num | |
* @returns {{$node: string, node: string, num: *, wrap: string, span: string, image: string, src: string, state: string, rendered: string}} | |
*/ | |
makeItemHTMLData:function(data, num){ | |
var $node = ''; | |
var node = ''; | |
var wrap = ''; | |
var span = ''; | |
var image = ''; | |
var src; | |
var state = ''; | |
var rendered; | |
var id = data.id; | |
var className = this.options.classNames; | |
span += '<span data-src="' + data.src + '" title="' + data.title + '" alt="' + data.title + '" class="span-preload img-responsive preload ' + className.item + '"></span>'; | |
image += '<img src="' + data.src + '" title="' + data.title + '" alt="' + data.title + '" class="img-responsive preload ' + className.item + '">'; | |
src = data.src; | |
wrap += '<li class="' + className.item + '-wrap ' + className.item + '-' + num + ' ' + className.item + '-' + this.options.size; | |
wrap += (data.active !== undefined && className.active) ? ' active ' : ' '; | |
wrap += '" data-num="' + num + '">'; | |
wrap += '<%= image %>'; | |
if (data.sourceText !== undefined && data.sourceText) { | |
wrap += '<span class="' + className.item + '-source">Источник: ' + data.sourceText + '</span>'; | |
} | |
wrap += '</li>'; | |
if(this.options.renderItems){ | |
state = 'process'; | |
rendered = _.template(wrap)({image: image}); | |
}else{ | |
// вот тут надо сделать проверку для renderItems на count | |
rendered = _.template(wrap)({image: span}); | |
} | |
return { | |
id:id, | |
$node:$node, | |
node:node, | |
num:num, | |
wrap:wrap, | |
span:span, | |
image:image, | |
src:src, | |
state:state, | |
rendered:rendered | |
} | |
}, | |
/** | |
* добавить 1 картинку в список + закешировать | |
* @param imgId | |
* @returns {boolean} | |
*/ | |
addListItemHTML:function(imgId){ | |
if (!imgId) return false; | |
var items = this.model.get('images')[this.options.size]; | |
if (!items) return false; // если нет кеша и нет картинок | |
var itemDataAndIndex = _.compact(_.map(items, function (item, num) { | |
if (item.id == imgId) { | |
return { | |
item: item, | |
num: num | |
} | |
} | |
})); | |
if (!itemDataAndIndex || !itemDataAndIndex.length) return false; | |
itemDataAndIndex = itemDataAndIndex.pop(); | |
var itemHTMLData = this.makeItemHTMLData(itemDataAndIndex.item, itemDataAndIndex.num); | |
this.nodes.$list.append(itemHTMLData.rendered); | |
// сохраняю в индекс | |
this.model.addImagesIndexesItem(imgId, this.cid, itemHTMLData); | |
return this.model.getImagesIndexesItem(imgId, this.cid); | |
}, | |
/** | |
* получить закешированный html для картинки по ID | |
* @param imgId | |
* @returns {*} | |
*/ | |
getListItemHTML:function(imgId){ | |
var data = this.model.getImagesIndexesItem(imgId, this.cid); | |
if(data && data.rendered) return data.rendered; | |
return false; | |
}, | |
/** | |
* Создаст HTML для списка элементов галереи | |
* @returns {string} | |
*/ | |
createListHTML:function(){ | |
var self = this; | |
var html = ''; | |
var contentHTML = ''; | |
var buttonsHTML = ''; | |
var itemsHTML = ''; | |
var className = this.options.classNames; | |
var items = this.model.get('images')[this.options.size]; | |
html += '<div class="' + className.wrap + '">'; | |
// нужно нарисовать кноки навигации | |
if(this.options.buttons && this.options.buttons.navigation){ | |
buttonsHTML += '<span class="' + className.nav + '-item '; | |
buttonsHTML += className.nav + '-item-prev ">'; | |
buttonsHTML += '</span>'; | |
buttonsHTML += '<span class="' + className.nav + '-item '; | |
buttonsHTML += className.nav + '-item-next ">'; | |
buttonsHTML += '</span>'; | |
} | |
// кнопки | |
if(this.options.buttons){ | |
buttonsHTML += '<div class="' + className.button + '-wrap ">'; | |
} | |
// кнопка открытия полного экрана | |
if(this.options.buttons && this.options.buttons.full){ | |
buttonsHTML += '<span class="' + className.button + '-item '; | |
buttonsHTML += className.button + '-item-full ">'; | |
buttonsHTML += '</span>'; | |
} | |
// кнопка закрытия вида | |
if(this.options.buttons && this.options.buttons.close){ | |
buttonsHTML += '<span class="' + className.button + '-item '; | |
buttonsHTML += className.button + '-item-close ">'; | |
buttonsHTML += '</span>'; | |
} | |
if(this.options.buttons){ | |
buttonsHTML += '</div>'; | |
} | |
// items | |
if (items){ | |
itemsHTML += '<ul class="' + className.list + '">'; | |
_.each(items, function (item, num) { | |
var itemHTMLData = self.makeItemHTMLData(item, num); | |
itemsHTML += itemHTMLData.rendered; | |
// кеширую картинку | |
// дальше работаю с закешированной информацией об изображении | |
// только, если не добавляю совсем новые | |
self.model.addImagesIndexesItem(item.id, self.cid, itemHTMLData); | |
}); | |
itemsHTML += '</ul>'; | |
} | |
html += contentHTML; | |
html += buttonsHTML; | |
html += itemsHTML; | |
html += '</div>'; | |
return html; | |
}, | |
renderAndLoadItemHTML:function(domCache, callback){ | |
var self = this; | |
// задел на будущее, что бы была возможность "перезагружать картинки" | |
if(domCache.state == '' || domCache.state == 'process'){ | |
var Img = new Image(); | |
Img.onload = function(){ | |
// TODO у картинки подменяется урл быстрее, чем просиходит событие ошибки зигрузки (тут) | |
// поэтому такой вот костылек | |
if(this.src.indexOf('ErrorImage') < 0){ | |
domCache.state = true; | |
domCache.$node.append(domCache.image).addClass('ready').find('.span-preload').remove() | |
if(callback) callback(true, domCache); | |
}else{ | |
// загрузилось изображение с ошибкой | |
var errorImage = self.app.fn.getErrorImage({350:350}, true); | |
domCache.state = false; | |
domCache.$node.addClass('error').html(domCache.image).addClass('ready error').find('img').attr('src', errorImage.src); | |
if(callback) callback(false, domCache); | |
} | |
}; | |
Img.onerror = function(){ | |
// загрузилось изображение с ошибкой | |
var errorImage = self.app.fn.getErrorImage({350:350}, true); | |
domCache.state = false; | |
domCache.$node.addClass('error').html(domCache.image).addClass('ready error').find('img').attr('src', errorImage.src); | |
if(callback) callback(false, domCache); | |
}; | |
Img.src = domCache.src; | |
} | |
// отрисовка состояния ошибки | |
if(domCache.state === false){ | |
var errorImage = self.app.fn.getErrorImage({350:350}, true); | |
domCache.$node.addClass('error'); | |
domCache.$node.find('img').attr('src', errorImage.src).addClass('ready error'); | |
if(callback) callback(false, domCache); | |
} | |
}, | |
/** | |
* | |
******* Обработка событий | |
* | |
*/ | |
/** | |
* тут рождается потеря памяти | |
* опасное место | |
* @returns {exports.eventChangeActiveImg} | |
*/ | |
eventChangeActiveImg:function(){ | |
/** | |
* в этом месте надо всегда следить что бы событие срабатывало только там, где надо | |
* console.log(this.options.size); | |
* @type {exports.eventChangeActiveImg} | |
*/ | |
var self = this; | |
var className = this.options.classNames; | |
var activeImg = this.model.get('activeImg'); | |
if (!activeImg) return this; | |
// закончить | |
function errorLoading(cache){ | |
var errorImage = self.app.fn.getErrorImage({350:350}, true); | |
// | |
if(cache){ | |
//cache.state = false; | |
// self.model.addImagesIndexesItem(cache.id, self.cid, cache); | |
cache.$node.find('img').attr('src', errorImage.src); | |
} | |
if(!cache){ | |
// невозможная ошибка | |
alert('Ошибка работы галереи, пожалуйста обновите страницу'); | |
} | |
}; | |
// беру закешированный DOM и удаляю класс active | |
var allItemsCache = this.model.getImagesIndexesItem(false, this.cid); | |
for(var c in allItemsCache){ | |
if(!allItemsCache[c].$node) continue; | |
allItemsCache[c].$node.removeClass('active'); | |
} | |
var domCache = this.model.getImagesIndexesItem(activeImg, this.cid); | |
// кеша вовсе нет | |
if (!domCache) { | |
// добавляю html в список, записываю кеш, возвращаю кеш | |
domCache = this.addListItemHTML(activeImg); | |
} | |
// в кеше только html | |
if (domCache && !domCache.$node && !domCache.node) { | |
domCache.$node = this.$el.find('.' + className.item + '-' + domCache.num); | |
domCache.node = domCache.$node[0]; | |
} | |
if(domCache && domCache.state === 'process'){ | |
domCache.$node.addClass('active'); | |
self.openImage(domCache); | |
// устанавливаю урл | |
if(self.options.size !== 'def') self.options.canHashChange = true; | |
if(self.options.canHashChange){ | |
// устанавливаю урл | |
if(self.options.urlController && (self.options.urlController == true || self.options.urlController.indexOf(self.options.size) >= 0) ){ | |
self.urlController.setUrl(self.model.toJSON(), self.options.size); | |
} | |
}else{ | |
self.options.canHashChange = true; | |
} | |
} | |
// ошибка в загрузке изображения | |
if(!domCache || (domCache && domCache.state === false)){ | |
// вызываю событие | |
//errorLoading.call(this, domCache); | |
//if(this.errorLoading) this.errorLoading(domCache); | |
domCache.$node.addClass('active'); | |
} | |
if (domCache && domCache.state === '') { | |
// отправил на загрузку изображение | |
// после успешной загрузки добавляю active | |
domCache.$node.addClass('active'); | |
this.renderAndLoadItemHTML(domCache, function(result, cache){ | |
if(result){ | |
self.openImage(cache); | |
if(self.options.size !== 'def') self.options.canHashChange = true; | |
if(self.options.canHashChange){ | |
// устанавливаю урл | |
if(self.options.urlController && (self.options.urlController == true || self.options.urlController.indexOf(self.options.size) >= 0) ){ | |
self.urlController.setUrl(self.model.toJSON(), self.options.size); | |
} | |
}else{ | |
self.options.canHashChange = true; | |
} | |
} | |
if(!result){ | |
// ошибка в загрузке изображения | |
//if(!domCache || (domCache && domCache.state === false)){ | |
// вызываю событие | |
//errorLoading.call(this, domCache); | |
//if(self.errorLoading) this.errorLoading(domCache); | |
//} | |
} | |
}); | |
} | |
if (domCache && domCache.state === true) { | |
domCache.$node.addClass('active'); | |
this.openImage(domCache); | |
if(self.options.size !== 'def') self.options.canHashChange = true; | |
if(this.options.canHashChange){ | |
// устанавливаю урл | |
if(self.options.urlController && (self.options.urlController == true || self.options.urlController.indexOf(self.options.size) >= 0) ){ | |
self.urlController.setUrl(self.model.toJSON(), self.options.size); | |
} | |
}else{ | |
this.options.canHashChange = true; | |
} | |
} | |
}, | |
// надо бы вынести в отдельную вьюху | |
// но пусть живут тут, вынести всегда можно | |
nextImage:function(){ | |
var nextImage; | |
var activeImg = this.model.get('activeImg'); | |
var images = this.model.get('images')[this.options.size]; | |
nextImage = _.find(images, function(img, num){ | |
if(images[num-1] && images[num-1].id == activeImg){ | |
return true; | |
} | |
}); | |
nextImage = (nextImage)? nextImage : _.first(images); | |
if(nextImage && nextImage.id){ | |
this.model.set({activeImg:nextImage.id}) | |
} | |
}, | |
prevImage:function(){ | |
var prevImage; | |
var activeImg = this.model.get('activeImg'); | |
var images = this.model.get('images')[this.options.size]; | |
prevImage = _.find(images, function(img, num){ | |
if(images[num+1] && images[num+1].id == activeImg){ | |
return true; | |
} | |
}); | |
prevImage = (prevImage)? prevImage : _.last(images); | |
if(prevImage && prevImage.id){ | |
this.model.set({activeImg:prevImage.id}) | |
} | |
}, | |
// открыть изображение | |
// на этот момент весь DOM уже будет сформирован | |
// метод переназначается в наследниках | |
openImage:function(imgId){}, | |
// будет вызван при ошибке загрузки изображения | |
errorLoading:function(cache){}, | |
render:function(){}, | |
/** | |
* капец какая важная штука remove - иначе СМЕРТЬ БРАУЗЕРУ | |
* обработчики будут складыватся до бесконечности и вызыватся уже не нужные | |
* | |
* потом надо вынести этот функционал глобально, полезная штука (ранее такое было только для DOM) | |
* для событий моделей и коллекций тоже надо | |
* | |
* переназначаю метод remove, что бы удалить лишние обработчики | |
* дочерние вьюхи или те, кто был создан на время - навешивают события на общую модель для галереи | |
* мне не нужно случайное выполнение методов, | |
* поэтому при удалении вьюхи надо убить все событие на которые была подписана она | |
* или её дети | |
* | |
* @returns {*} | |
*/ | |
remove:function(){ | |
var removeEvents = function(){ | |
var ctx = this; | |
// удаляю обработчики учитывая контекст | |
if(ctx.model && ctx.model.off){ | |
for(var n in ctx){ | |
if(typeof ctx[n] == 'function'){ | |
ctx.model.off(null, ctx[n], ctx); | |
} | |
} | |
} | |
// обойти детей, рекурсия | |
if(ctx.children){ | |
for(var c in ctx.children){ | |
removeEvents.apply(ctx.children[c]) | |
} | |
} | |
}; | |
removeEvents.apply(this); | |
// стандартный remove от Backbone который расширен мной выше | |
// удалит DOM и пр. | |
this.stopListening(); | |
return defaultRemoveFunc.apply(this, arguments); | |
} | |
}); | |
}); | |
define([ | |
'jquery', | |
'underscore', | |
'backbone', | |
'modules/galleryObject/views/base', | |
'modules/galleryObject/views/thumbnails', | |
'libs/stack-blur', | |
], function ($, | |
_, | |
Backbone, | |
BaseGalleryClass, | |
ThumbnailsViewClass, | |
CanvasBlur) { | |
// Вьюха одной галереи | |
var Gallery = BaseGalleryClass.extend({ | |
options:{ | |
size:'def', | |
buttons:{ | |
navigation:true, | |
}, | |
classNames:{ | |
wrap: 'gallery-object-def-wrap', | |
list: 'gallery-object-def-list', | |
item: 'gallery-object-def-item', | |
nav: 'gallery-object-def-nav', | |
button: 'gallery-object-def-btn', | |
canvas: { | |
wrap: 'gallery-object-canvas-wrap', | |
item: 'gallery-object-canvas-item' | |
} | |
}, | |
imgSizes:{ | |
width: 800, | |
height: 600 | |
} | |
}, | |
className:'gallery-object-default', | |
tagName: 'div', | |
events:{ | |
'click .gallery-object-def-nav-item-prev':'eventClickPrevImage', | |
'click .gallery-object-def-nav-item-next':'eventClickNextImage', | |
}, | |
initialize: function (options) { | |
var self = this; | |
this.children = {}; | |
this.nodes = {}; | |
if(options && options.options){ | |
_.extend(this.options, options.options); | |
} | |
this.$el.attr('id', this.model.get('nodeID')); | |
this.$el.attr('data-gallery-id', this.model.id); | |
// удалить галерею при удалении модели | |
this.model.bind('remove', this.remove, this); | |
this.model.bind('change:activeImg', this.eventChangeActiveImg, this); | |
this.children.thumbnails = new ThumbnailsViewClass({model:this.model}); | |
// отрисовка обычной галереи | |
this.render(); | |
// перенести это в urlController | |
var activeImg = this.model.get('activeImg'); | |
if (!activeImg) { | |
// разбираю урл | |
var data = this.urlController.parseUrlToData(); | |
if(data.activeImg){ | |
activeImg = _.find(this.model.get('images')[this.options.size], function(item){ | |
if(item.id == data.activeImg){ | |
return true; | |
} | |
}); | |
} | |
if(!activeImg){ | |
activeImg = _.first(this.model.get('images')[this.options.size]); | |
} | |
if(activeImg && activeImg.id){ | |
this.model.set({activeImg: activeImg.id}); | |
} | |
} | |
}, | |
eventClickPrevImage:function(){ | |
this.prevImage(); | |
}, | |
eventClickNextImage:function(){ | |
this.nextImage(); | |
}, | |
// создает HTML для канвас элемента | |
createCanvasHTML: function () { | |
var html = ''; | |
var className = this.options.classNames.canvas; | |
html += '<div class="' + className.wrap + '">'; | |
html += '<canvas class="' + className.item + '"></canvas>'; | |
html += '</div>'; | |
return html; | |
}, | |
// будет вызван после изменения изображения | |
openImage:function(cache){ | |
var self = this; | |
if(!cache || !cache.$node) return false; | |
var $image = cache.$node.find('img'); | |
if(!$image.length) return false; | |
this.app.hp.images.checkSetImageMargins($image, cache.$node); | |
// render canvas after image load | |
this.app.fn.imageLoader($image, function (resp, img) { | |
if(resp){ | |
self.renderCanvasBG(this.$image[0]); | |
} | |
},{ | |
$image: $image | |
}); | |
}, | |
errorLoading:function(cache){ | |
//console.log('cache', cache) | |
//if(self.model.get('activeImg') == cache.id){ | |
// // переключение на следующую картинку | |
// if(self.nextImage) self.nextImage(); | |
//} | |
}, | |
renderCanvasBG:function(image){ | |
if (this.timers.canvasTimer) { | |
clearTimeout(this.timers.canvasTimer); | |
} | |
var canvas = this.nodes.canvas; | |
var ctx = canvas.getContext("2d"); | |
var renderCanvas = function(img){ | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.fillStyle = 'rgba(0,0,0,0.6)'; | |
// если картинка не передана - все конец | |
if (!img) { | |
return; | |
} | |
// если картинка имеет нулевые размеры - не могу рисовать | |
// если какие-то траблы с канвасными размерами - тоже не могу рисовать | |
if (!img.naturalWidth || !img.naturalHeight || !canvas.height || !canvas.width) { | |
return; | |
} | |
ctx.drawImage(img, 0, 0, canvas.width, canvas.height); | |
CanvasBlur.fn.stackBlurCanvasRGBA(ctx, 0, 0, canvas.width, canvas.height, 10); | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
return this; | |
}; | |
// добавляю задержку перед отрисовкой канваса | |
// что бы не убить браузер если переключают быстро | |
if (this.app.getAttr('mobile')) { | |
this.timers.canvasTimer = setTimeout(function () { | |
renderCanvas(image); | |
}, 400); | |
} else { | |
this.timers.canvasTimer = setTimeout(function () { | |
renderCanvas(image); | |
}, 200); | |
} | |
}, | |
render:function(){ | |
var self = this; | |
var className = this.options.classNames; | |
this.children.main = this.$el.find('.gallery-object-image_main'); | |
// устанавливаю размеры блока галереи | |
var width = this.$el.width(); | |
var defSize = this.options.imgSizes; | |
var calcHeight = Math.ceil((width * defSize.height) / defSize.width); | |
this.$el.html(this.createListHTML.apply(this)); | |
this.$el.append(this.children.thumbnails.render().el); | |
this.nodes = {}; | |
this.nodes.$list = this.$el.find('.' + className.list); | |
this.nodes.$list.parent().append(this.createCanvasHTML()); | |
this.nodes.$canvas = this.$el.find('.' + className.canvas.item); | |
this.nodes.canvas = this.nodes.$canvas[0]; | |
this.nodes.$list.parent().css('height', calcHeight); | |
this.nodes.$canvas.css({ | |
height:calcHeight, | |
width:width | |
}); | |
this.nodes.$list.swipeleft(function(){ | |
self.eventClickNextImage(); | |
}); | |
this.nodes.$list.swiperight(function(){ | |
self.eventClickPrevImage(); | |
}); | |
this.nodes.canvas.width = width; | |
this.nodes.canvas.height = calcHeight; | |
return this; | |
} | |
}); | |
return Gallery; | |
}); | |
define([ | |
'jquery', | |
'underscore', | |
'backbone', | |
'modules/galleryObject/views/base', | |
'modules/galleryObject/views/thumbnails', | |
'modules/galleryObject/models/gallery' | |
], function ($, | |
_, | |
Backbone, | |
BaseGalleryClass, | |
ThumbnailsViewClass, | |
GalleryModelClass) { | |
// вьюхя для вида popup | |
var GalleryPopUpView = BaseGalleryClass.extend({ | |
options: { | |
buttons:{ | |
navigation:true, | |
close:true, | |
full:true | |
}, | |
size: 'popup', | |
classNames: { | |
wrap: 'gallery-object-popup-wrap', | |
list: 'gallery-object-popup-list', | |
item: 'gallery-object-popup-item', | |
nav: 'gallery-object-popup-nav', | |
button: 'gallery-object-popup-btn', | |
canvas: { | |
wrap: 'gallery-object-canvas-wrap', | |
item: 'gallery-object-canvas-item' | |
} | |
} | |
}, | |
className: 'gallery-object-popup', | |
events:{ | |
'click .gallery-object-popup-btn-item-close':'eventClickCloseGallery', | |
'click .gallery-object-popup-nav-item-prev':'eventClickPrevImage', | |
'click .gallery-object-popup-nav-item-next':'eventClickNextImage', | |
'click .gallery-object-popup-item-wrap':'eventClickNextImage' | |
}, | |
initialize: function (options) { | |
if(options && options.options){ | |
_.extend(this.options, options.options); | |
} | |
this.children = {}; | |
this.nodes = {}; | |
this.$el.css('display', 'none'); | |
}, | |
// будет вызван после изменения изображения | |
openImage:function(cache){ | |
var self = this; | |
if(!cache || !cache.$node) return false; | |
var $image = cache.$node.find('img'); | |
if(!$image.length) return false; | |
this.app.hp.images.riseImageGallery($image, cache.$node); | |
setTimeout(function(){ | |
self.app.hp.images.checkSetImageMargins($image, cache.$node); | |
}, 0); | |
}, | |
eventClickCloseGallery:function(){ | |
this.hide(); | |
this.urlController.clearUrl(); | |
this.remove(); | |
}, | |
eventClickPrevImage:function(){ | |
this.prevImage(); | |
}, | |
eventClickNextImage:function(){ | |
this.nextImage(); | |
}, | |
// показать всплывающую галерею | |
show: function () { | |
this.$el.show(); | |
this.app.views.index.trigger('modal:open'); | |
}, | |
// скрыть всплывающую галерею | |
hide: function () { | |
this.$el.hide(); | |
this.app.views.index.trigger('modal:close'); | |
}, | |
// передается модель | |
// в модели есть вся инфа | |
render: function (model, openActiveImg, options) { | |
if (!model) return this; | |
// пересматриваю dom события | |
this.delegateEvents(); | |
var self = this; | |
var data = model.toJSON(); | |
this.models = {}; | |
this.nodes = {}; | |
this.model = new GalleryModelClass(); | |
this.model.bind('remove', this.remove, this); | |
this.model.bind('change:activeImg', this.eventChangeActiveImg, this); | |
if(options && options.options){ | |
_.extend(this.options, options.options); | |
} | |
if(openActiveImg){ | |
// требуемый размер фоток отсутствует | |
if(!data.images[this.options.size]) openActiveImg = false; | |
if(data.images[this.options.size]){ | |
var issetNeedImage = _.find(data.images[this.options.size], function(image){ | |
if(image.id == openActiveImg) return true; | |
}); | |
if(!issetNeedImage) openActiveImg = false; | |
} | |
} | |
if(!data.activeImg && !openActiveImg){ | |
var activeImg = _.first(data.images[this.options.size]); | |
if(activeImg && activeImg.id){ | |
data.activeImg = activeImg.id; | |
} | |
} | |
if(openActiveImg && issetNeedImage){ | |
data.activeImg = issetNeedImage.id; | |
} | |
self.model.set(data, {silent:true}); // опасное место надое сделать без silent | |
// render | |
if(this.children.thumbnails){ | |
this.children.thumbnails.remove(); | |
} | |
var className = this.options.classNames; | |
this.$el.attr('data-gallery-id', this.model.id); | |
this.$el.html(this.createListHTML()); | |
this.nodes.$list = this.$el.find('.' + className.list); | |
setTimeout(function(){ | |
// создаю дочернюю view с миниатюрами | |
self.children.thumbnails = new ThumbnailsViewClass({model: self.model, options:{size:'thumbnail-popup'}}); | |
self.$el.append(self.children.thumbnails.render().el); | |
self.model.trigger('change:activeImg'); | |
}, 0); | |
return this; | |
} | |
}); | |
return GalleryPopUpView; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment