2 * © 2008-2014 Benoît Pin – Centre de recherche en informatique – MINES ParisTech
4 * Licence Creative Commons http://creativecommons.org/licenses/by-nc/2.0/
13 var keyLeft
= 37, keyRight
= 39;
14 var isTextMime
= /^text\/.+/i;
15 var isAddToSelection
= /.*\/add_to_selection$/;
16 var imgRequestedSize
= /size=(\d+)/;
17 var DEFAULT_IMAGE_SIZES
= [500, 600, 800];
19 FilmSlider = function(filmBar
, slider
, ctxInfos
, image
, toolbar
, breadcrumbs
) {
20 var thisSlider
= this;
21 this.filmBar
= filmBar
;
22 var film
= filmBar
.firstChild
;
23 if (film
.nodeType
=== 3) { film
= film
.nextSibling
; }
26 this.rail
= slider
.parentNode
;
27 this.sliderSpeedRatio
= undefined;
28 this.sliderRatio
= undefined;
29 this.selectedSlide
= undefined;
30 this.selectedSlideInSelection
= undefined;
31 this.cartSlide
= document
.getElementById('cart_slide');
33 this.stretchable
= image
.parentNode
;
34 this.viewMode
= 'medium';
37 this.toolbar
= toolbar
;
39 var bcElements
= breadcrumbs
.getElementsByTagName('a');
40 this.lastBCElement
= bcElements
[bcElements
.length
-1];
41 var imgSrcParts
= image
.src
.split('/');
42 this.lastBCElement
.innerHTML
= imgSrcParts
[imgSrcParts
.length
-2];
43 this.hasBreadcrumbs
= true;
46 this.hasBreadcrumbs
= false;
49 var buttons
= toolbar
.getElementsByTagName('img');
51 for (i
=0 ; i
<buttons
.length
; i
++) {
53 name
= b
.getAttribute('name');
54 if (name
) { this.buttons
[name
] = b
; }
57 this.pendingImage
= new Image();
58 this.pendingImage
.onload = function() {
59 thisSlider
.refreshImage();
61 this.initialized
= false;
63 this.film
.style
.left
='0';
64 this.film
.style
.top
='0';
66 this.filmLength
= ctxInfos
.filmLength
;
67 this.center
= ctxInfos
.center
;
68 this.slideSize
= ctxInfos
.slideSize
;
69 this.ctxUrlTranslation
= ctxInfos
.ctxUrlTranslation
;
71 this.ddHandlers
= {'down' : function(evt
){thisSlider
.mouseDownHandler(evt
);},
72 'move' : function(evt
){thisSlider
.mouseMoveHandler(evt
);},
73 'up' : function(evt
){thisSlider
.mouseUpHandler(evt
);},
74 'out' : function(evt
){thisSlider
.mouseOutHandler(evt
);}
78 this.addEventListeners();
82 FilmSlider
.prototype.resizeSlider = function(evt
) {
83 var filmBarWidth
= getObjectWidth(this.filmBar
);
85 var thisSlider
= this;
86 addListener(window
, 'load', function(evt
){thisSlider
.resizeSlider(evt
);});
90 var filmWidth
= this.slideSize
* this.filmLength
;
91 var sliderRatio
= this.sliderRatio
= filmBarWidth
/ filmWidth
;
92 var sliderWidth
= filmBarWidth
* sliderRatio
;
93 this.rail
.style
.width
= filmBarWidth
+ 'px';
94 this.rail
.style
.display
= 'block';
95 this.rail
.style
.visibility
= 'visible';
96 if (sliderRatio
< 1) {
97 this.slider
.style
.width
= Math
.round(sliderWidth
) + 'px';
98 this.slider
.style
.visibility
= 'visible';
101 this.slider
.style
.visibility
= 'hidden';
104 this.winSize
= {'width' : getWindowWidth(),
105 'height' : getWindowHeight()};
106 this.maxRightPosition
= filmBarWidth
- sliderWidth
;
107 this.sliderSpeedRatio
= - (filmBarWidth
- sliderWidth
) / (filmWidth
- filmBarWidth
);
108 if (!this.initialized
) {
109 this.centerSlide(this.center
);
110 this.selectedSlide
= this.filmBar
.getElementsByTagName('img')[this.center
].parentNode
;
111 this.initialized
= true;
115 FilmSlider
.prototype.fitToScreen = function(evt
) {
117 var thisSlider
= this;
118 addListener(window
, 'resize', function(evt
){thisSlider
._fitToScreen();});
121 FilmSlider
.prototype._fitToScreen = function(evt
) {
122 var wh
= getWindowHeight();
123 var rb
= getObjectTop(this.rail
) + getObjectHeight(this.rail
); // rail bottom
125 var sh
= getObjectHeight(this.stretchable
);
126 var newSize
= sh
+ delta
;
127 this.stretchable
.style
.height
= newSize
+ 'px';
129 var ratio
= this.image
.height
/ this.image
.width
;
130 var bestFitSize
= this.getBestFitSize(ratio
);
131 var currentSize
= parseInt(imgRequestedSize
.exec(this.image
.src
)[1], 10);
132 if (currentSize
!== bestFitSize
) {
133 var src
= this.image
.src
.replace(imgRequestedSize
, 'size=' + bestFitSize
);
134 this.pendingImage
.src
= src
;
138 FilmSlider
.prototype.getBestFitSize = function(ratio
) {
139 var fw
= getObjectWidth(this.stretchable
) - 1;
140 var fh
= getObjectHeight(this.stretchable
) - 1;
144 for (i
=DEFAULT_IMAGE_SIZES
.length
-1 ; i
>0 ; i
--) {
145 irw
= DEFAULT_IMAGE_SIZES
[i
];
147 if (irw
<= fw
&& irh
<= fh
)
152 for (i
=DEFAULT_IMAGE_SIZES
.length
-1 ; i
>0 ; i
--) {
153 irh
= DEFAULT_IMAGE_SIZES
[i
];
155 if (irw
<= fw
&& irh
<= fh
)
159 return DEFAULT_IMAGE_SIZES
[i
];
162 FilmSlider
.prototype.centerSlide = function(slideIndex
) {
163 if (this.sliderRatio
> 1) { return; }
164 var filmBarWidth
= getObjectWidth(this.filmBar
);
165 var x
= slideIndex
* this.slideSize
166 x
= x
- (filmBarWidth
- this.slideSize
) / 2.0;
167 x
= x
* this.sliderSpeedRatio
;
168 var p
= new Point( -x
, 0 )
169 this.setSliderPosition(p
);
172 FilmSlider
.prototype.setSliderPosition = function(point
) {
173 if(point
.x
< 0) { point
.x
= 0; }
174 if (point
.x
> this.maxRightPosition
) { point
.x
= this.maxRightPosition
; }
175 this.slider
.style
.left
= point
.x
+ 'px';
176 this.setFilmPosition(point
);
179 FilmSlider
.prototype.setFilmPosition = function(point
) {
180 this.film
.style
.left
= point
.x
/ this.sliderSpeedRatio
+ 'px';
183 FilmSlider
.prototype.getSliderPosition = function() {
184 var x
= parseInt(this.slider
.style
.left
);
185 var y
= parseInt(this.slider
.style
.top
);
186 var p
= new Point(x
, y
);
190 FilmSlider
.prototype.getFilmPosition = function() {
191 var x
= parseInt(this.film
.style
.left
);
192 var y
= parseInt(this.film
.style
.top
);
193 var p
= new Point(x
, y
);
197 FilmSlider
.prototype.loadSibling = function(previous
) {
200 slide
= this.selectedSlide
.parentNode
.previousSibling
;
201 if (slide
&& slide
.nodeType
===3) { slide
= slide
.previousSibling
; }
204 slide
= this.selectedSlide
.parentNode
.nextSibling
;
205 if (slide
&& slide
.nodeType
===3) { slide
= slide
.nextSibling
; }
208 if (!slide
) { return; }
210 var target
= slide
.getElementsByTagName('a')[0];
211 raiseMouseEvent(target
, 'click');
212 var index
= parseInt(target
.getAttribute('portfolio:position'));
213 this.centerSlide(index
);
217 FilmSlider
.prototype.addEventListeners = function() {
218 var thisSlider
= this;
219 addListener(window
, 'resize', function(evt
){thisSlider
.resizeSlider(evt
);});
220 addListener(this.filmBar
, 'click', function(evt
){thisSlider
.thumbnailClickHandler(evt
);});
221 addListener(this.toolbar
, 'click', function(evt
){thisSlider
.toolbarClickHandler(evt
);});
222 addListener(window
, 'load', function(evt
){thisSlider
.fitToScreen(evt
);});
225 addListener(this.slider
, 'mousedown', this.ddHandlers
['down']);
226 if(browser
.isDOM2Event
){
227 if (browser
.isAppleWebKit
) {
228 this.filmBar
.addEventListener('mousewheel', function(evt
){thisSlider
.mouseWheelHandler(evt
);}, false);
231 addListener(this.filmBar
, 'DOMMouseScroll', function(evt
){thisSlider
.mouseWheelHandler(evt
);});
234 else if (browser
.isIE6up
) {
235 addListener(this.filmBar
, 'mousewheel', function(evt
){thisSlider
.mouseWheelHandler(evt
);});
238 addListener(document
, 'keydown', function(evt
){thisSlider
.keyDownHandler(evt
);});
239 addListener(document
, 'keypress', function(evt
){thisSlider
.keyPressHandler(evt
);});
243 FilmSlider
.prototype.mouseDownHandler = function(evt
) {
244 this.initialClickPoint
= new Point(evt
.clientX
, evt
.clientY
);
245 this.initialPosition
= this.getSliderPosition();
246 this.dragInProgress
= true;
247 addListener(document
, 'mousemove', this.ddHandlers
['move']);
248 addListener(document
, 'mouseup', this.ddHandlers
['up']);
249 addListener(document
.body
, 'mouseout', this.ddHandlers
['out'])
254 FilmSlider
.prototype.mouseMoveHandler = function(evt
) {
255 if(!this.dragInProgress
) { return; }
258 evt
= getEventObject(evt
);
259 var currentPoint
= new Point(evt
.clientX
, evt
.clientY
);
260 var displacement
= currentPoint
.diff(this.initialClickPoint
);
261 this.setSliderPosition(this.initialPosition
.add(displacement
));
264 FilmSlider
.prototype.mouseUpHandler = function(evt
) {
265 this.dragInProgress
= false;
266 evt
= getEventObject(evt
);
267 this.mouseMoveHandler(evt
);
271 FilmSlider
.prototype.mouseOutHandler = function(evt
) {
272 evt
= getEventObject(evt
);
276 x
> this.winSize
['width'] ||
278 y
> this.winSize
['height']
280 this.mouseUpHandler(evt
);
284 FilmSlider
.prototype.thumbnailClickHandler = function(evt
) {
285 var target
= getTargetedObject(evt
);
286 while (target
.tagName
!== 'A' && target
!== this.filmBar
)
287 target
= target
.parentNode
;
288 if (target
.tagName
!== 'A') { return; }
290 if (this.viewMode
=== 'full') {
291 this.mosaique
.unload();
292 this.mosaique
= null;
293 this.viewMode
= 'medium';
296 disablePropagation(evt
);
298 history
.pushState(target
.href
, '', target
.href
);
300 var imgBaseUrl
= target
.href
;
302 if (this.ctxUrlTranslation
[0]) {
303 canonicalImgUrl
= imgBaseUrl
.replace(this.ctxUrlTranslation
[0],
304 this.ctxUrlTranslation
[1]);
306 else { canonicalImgUrl
= imgBaseUrl
; }
308 var ajaxUrl
= imgBaseUrl
+ '/photo_view_ajax';
311 //this.pendingImage.src = canonicalImgUrl + '/getResizedImage?size=600';
312 var thumbnail
= target
.getElementsByTagName('IMG')[0];
313 var bestFitSize
= this.getBestFitSize(thumbnail
.height
/thumbnail
.width
);
314 this.pendingImage
.src
= canonicalImgUrl
+ '/getResizedImage?size=' + bestFitSize
;
317 var fullScreenLink
= this.buttons
['full_screen'].parentNode
;
318 fullScreenLink
.href
= canonicalImgUrl
+ '/zoom_view';
320 var toggleSelectionBtn
= this.buttons
['toggle_selection'];
321 var toggleSelectionLink
= toggleSelectionBtn
.parentNode
;
322 this.selectedSlideInSelection
= (target
.className
==='selected');
323 if (this.selectedSlideInSelection
) {
324 toggleSelectionBtn
.src
= portal_url() + '/unselect_flag_btn.gif';
325 toggleSelectionBtn
.alt
= toggleSelectionLink
.title
= 'Retirer de la sélection';
326 toggleSelectionLink
.href
= canonicalImgUrl
+ '/remove_to_selection';
329 toggleSelectionBtn
.src
= portal_url() + '/select_flag_btn.gif';
330 toggleSelectionBtn
.alt
= toggleSelectionLink
.title
= 'Ajouter à la sélection';
331 toggleSelectionLink
.href
= canonicalImgUrl
+ '/add_to_selection';
334 var showBuyableButtonLink
= this.buttons
['show_buyable'].parentNode
;
335 showBuyableButtonLink
.href
= canonicalImgUrl
+ '/get_slide_buyable_items';
336 this.cartSlide
.innerHTML
= '';
337 this.cartSlide
.style
.visibility
='hidden';
340 var metadataButton
= this.buttons
['edit_metadata']
341 if (metadataButton
) {
342 var metadataEditLink
= metadataButton
.parentNode
;
343 metadataEditLink
.href
= canonicalImgUrl
+ '/photo_edit_form'
347 var req
= new XMLHttpRequest();
348 req
.onreadystatechange = function() {
349 switch (req
.readyState
) {
355 if (! isTextMime
.exec(req
.getResponseHeader('Content-Type'))) {
356 req
.onreadystatechange
= null;
359 window
.location
.href
= thisFS
._fallBackUrl
;
366 if (req
.status
=== '200') { thisFS
.populateViewer(req
); }
368 // //window.location.href = target.href;
369 // console.error(ajaxUrl);
374 req
.open("GET", ajaxUrl
, true);
377 // update old displayed slide className
378 var className
= this.selectedSlide
.className
;
379 var classes
= className
.split(' ');
385 if (name
=== 'displayed') { continue; }
386 else { newClasses
.push(name
); }
389 this.selectedSlide
.className
= newClasses
.join(' ')
391 // hightlight new displayed slide
392 this.selectedSlide
= target
;
393 className
= this.selectedSlide
.className
;
394 classes
= className
.split(' ');
395 classes
.push('displayed');
396 this.selectedSlide
.className
= classes
.join(' ');
400 FilmSlider
.prototype.toolbarClickHandler = function(evt
) {
401 var target
= getTargetedObject(evt
);
402 if(target
.tagName
=== 'IMG' && target
.getAttribute('name')) {
403 switch(target
.getAttribute('name')) {
406 disablePropagation(evt
);
408 var link
= button
.parentNode
;
410 this.loadSibling(true);
414 disablePropagation(evt
);
416 var link
= button
.parentNode
;
418 this.loadSibling(false);
422 disablePropagation(evt
);
423 target
.parentNode
.blur();
424 if (this.viewMode
=== 'full') {
425 this.mosaique
.unload();
426 this.mosaique
= null;
427 this.viewMode
= 'medium';
430 var main
= document
.getElementById('photo_viewer');
431 var url
= target
.parentNode
.href
;
432 url
= url
.substring(0, url
.length
- '/zoom_view'.length
);
433 var margins
= {'top':0, 'right':-1, 'bottom':0, 'left':0};
434 this.mosaique
= new Mosaique(main
, url
, margins
);
435 this.viewMode
= 'full';
438 case 'toggle_selection':
440 disablePropagation(evt
);
442 var link
= button
.parentNode
;
445 var req
= new XMLHttpRequest();
447 req
.open("POST", url
, true);
448 req
.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
452 var parts
= url
.split('/');
453 var canonicalImgUrl
= parts
.slice(0, parts
.length
-1).join('/');
455 if (isAddToSelection
.test(url
)) {
456 button
.src
= portal_url() + '/unselect_flag_btn.gif';
457 button
.alt
= link
.title
= 'Retirer de la sélection';
458 link
.href
= canonicalImgUrl
+ '/remove_to_selection';
459 this.selectedSlide
.className
= 'selected displayed';
460 this.image
.parentNode
.className
= 'selected';
461 this.selectedSlideInSelection
= true;
464 button
.src
= portal_url() + '/select_flag_btn.gif';
465 button
.alt
= link
.title
= 'Ajouter à la sélection';
466 link
.href
= canonicalImgUrl
+ '/add_to_selection';
467 this.selectedSlide
.className
= 'displayed';
468 this.image
.parentNode
.className
= '';
469 this.selectedSlideInSelection
= false;
475 disablePropagation(evt
);
477 var link
= button
.parentNode
;
479 var slide
= this.cartSlide
;
480 slide
.innerHTML
= '';
481 slide
.style
.visibility
= 'visible';
482 var cw
= new CartWidget(slide
, link
.href
);
483 cw
.onCancel = function() {
484 CartWidget
.prototype.onCancel
.apply(this);
485 slide
.style
.visibility
= 'hidden';
487 cw
.onAfterConfirm = function() {
488 slide
.style
.visibility
= 'hidden';
496 case 'edit_metadata' :
498 disablePropagation(evt);
500 if (this.viewMode === 'full') {
501 this.mosaique.unload();
502 this.mosaique = null;
503 this.viewMode = 'medium';
506 var fi = new FragmentImporter(absolute_url());
507 fi.useMacro('metadata_edit_form_macros', 'iptc', 'image_metadata');
515 if(browser
.isDOM2Event
) {
516 if (browser
.isAppleWebKit
) {
517 FilmSlider
.prototype.mouseWheelHandler = function(evt
) {
519 var pos
= this.getSliderPosition();
520 pos
.x
-= evt
.wheelDelta
/ 40;
521 this.setSliderPosition(pos
);
525 FilmSlider
.prototype.mouseWheelHandler = function(evt
) {
527 var pos
= this.getSliderPosition();
528 pos
.x
+= evt
.detail
* 3;
529 this.setSliderPosition(pos
);
533 else if (browser
.isIE6up
) {
534 FilmSlider
.prototype.mouseWheelHandler = function() {
535 var evt
= window
.event
;
536 evt
.returnValue
= false;
537 var pos
= this.getSliderPosition();
538 pos
.x
-= evt
.wheelDelta
/ 40;
539 this.setSliderPosition(pos
);
543 FilmSlider
.prototype.keyDownHandler = function(evt
) {
544 var evt
= getEventObject(evt
);
545 switch (evt
.keyCode
) {
547 this.loadSibling(true);
550 this.loadSibling(false);
558 FilmSlider
.prototype.keyPressHandler = function(evt
) {
559 var target
= getTargetedObject(evt
);
560 if (target
.tagName
=== 'INPUT' || target
.tagName
=== 'TEXTAREA') { return; }
561 var evt
= getEventObject(evt
);
562 var charPress
= String
.fromCharCode((evt
.keyCode
) ? evt
.keyCode
: evt
.which
);
566 raiseMouseEvent(this.buttons
['full_screen'], 'click');
571 FilmSlider
.prototype.populateViewer = function(req
) {
572 var elements
= req
.responseXML
.documentElement
.childNodes
;
573 for(var i
=0 ; i
< elements
.length
; i
++ ) {
574 element
= elements
[i
];
575 switch (element
.nodeName
) {
577 var dest
= document
.getElementById(element
.getAttribute('id'));
578 if (dest
) { dest
.innerHTML
= element
.firstChild
.nodeValue
; }
580 case 'imageattributes' :
581 var link
= this.buttons
['back_to_portfolio'].parentNode
;
582 link
.href
= element
.getAttribute('backToContextUrl');
583 link
= this.buttons
['show_buyable'].parentNode
;
584 var buyable
= element
.getAttribute('buyable');
585 if(buyable
=== 'True') { link
.className
= null; }
586 else if(buyable
=== 'False') { link
.className
= 'hidden'; }
587 this.image
.alt
= element
.getAttribute('alt');
588 this.updateBreadcrumbs(element
.getAttribute('lastBcUrl'),
589 element
.getAttribute('img_id'));
595 FilmSlider
.prototype.refreshImage = function() {
596 this.image
.style
.visibility
= 'hidden';
597 this.image
.src
= this.pendingImage
.src
;
598 this.image
.width
= this.pendingImage
.width
;
599 this.image
.height
= this.pendingImage
.height
;
600 this.image
.style
.visibility
= 'visible';
601 if (this.selectedSlideInSelection
) { this.image
.parentNode
.className
= 'selected'; }
602 else { this.image
.parentNode
.className
= ''; }
605 FilmSlider
.prototype.updateBreadcrumbs = function(url
, title
) {
606 if (this.hasBreadcrumbs
) {
607 this.lastBCElement
.href
= element
.getAttribute('lastBcUrl');
608 this.lastBCElement
.innerHTML
= element
.getAttribute('img_id');
612 FilmSlider
.prototype.startSlideShow = function() {
613 this.slideShowSlide
= this.pendingSlideShowSlide
= this.selectedSlide
;
614 return this.slideShowSlide
.href
;
617 FilmSlider
.prototype.slideShowNext = function() {
618 var nextSlide
= this.slideShowSlide
.parentNode
.nextSibling
;
619 if (nextSlide
&& nextSlide
.nodeType
===3) { nextSlide
= nextSlide
.nextSibling
; }
622 nextSlide
= nextSlide
.getElementsByTagName('a')[0];
623 this.pendingSlideShowSlide
= nextSlide
;
624 return this.pendingSlideShowSlide
.href
;
627 var row
= this.slideShowSlide
.parentNode
.parentNode
;
628 var first
= row
.firstChild
;
629 if (first
.nodeType
===3)
630 first
= first
.nextSibling
;
631 this.pendingSlideShowSlide
= first
.getElementsByTagName('a')[0];
632 return this.pendingSlideShowSlide
.href
;
636 FilmSlider
.prototype.slideShowPrevious = function() {
637 var previousSlide
= this.slideShowSlide
.parentNode
.previousSibling
;
638 if (previousSlide
&& previousSlide
.nodeType
===3) { previousSlide
= previousSlide
.previousSibling
; }
641 previousSlide
= previousSlide
.getElementsByTagName('a')[0];
642 this.pendingSlideShowSlide
= previousSlide
;
643 return this.pendingSlideShowSlide
.href
;
646 var row
= this.slideShowSlide
.parentNode
.parentNode
;
647 var last
= row
.lastChild
;
648 if (last
.nodeType
===3) { last
= last
.previousSibling
; }
649 this.pendingSlideShowSlide
= last
.getElementsByTagName('a')[0];
650 return this.pendingSlideShowSlide
.href
;
654 FilmSlider
.prototype.slideShowImageLoaded = function() {
655 this.slideShowSlide
= this.pendingSlideShowSlide
;
658 FilmSlider
.prototype.stopSlideShow = function() {
659 raiseMouseEvent(this.slideShowSlide
, 'click');
660 var index
= parseInt(this.selectedSlide
.getAttribute('portfolio:position'));
661 this.centerSlide(index
);
666 function Point(x
, y
) {
667 this.x
= Math
.round(x
);
668 this.y
= Math
.round(y
);
670 Point
.prototype.diff = function(point
) { return new Point(this.x
- point
.x
, this.y
- point
.y
); };
671 Point
.prototype.add = function(point
) { return new Point(this.x
+ point
.x
, this.y
+ point
.y
); };
672 Point
.prototype.mul = function(k
) { return new Point(this.x
* k
, this.y
*k
)};
673 Point
.prototype.toString = function() { return "(" + String(this.x
) + ", " + String(this.y
) + ")"; };