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
) { break; }
151 for (i
=DEFAULT_IMAGE_SIZES
.length
-1 ; i
>0 ; i
--) {
152 irh
= DEFAULT_IMAGE_SIZES
[i
];
154 if (irw
<= fw
&& irh
<= fh
) { break; }
157 return DEFAULT_IMAGE_SIZES
[i
];
160 FilmSlider
.prototype.centerSlide = function(slideIndex
) {
161 if (this.sliderRatio
> 1) { return; }
162 var filmBarWidth
= getObjectWidth(this.filmBar
);
163 var x
= slideIndex
* this.slideSize
;
164 x
= x
- (filmBarWidth
- this.slideSize
) / 2.0;
165 x
= x
* this.sliderSpeedRatio
;
166 var p
= new Point( -x
, 0 );
167 this.setSliderPosition(p
);
170 FilmSlider
.prototype.setSliderPosition = function(point
) {
171 if(point
.x
< 0) { point
.x
= 0; }
172 if (point
.x
> this.maxRightPosition
) { point
.x
= this.maxRightPosition
; }
173 this.slider
.style
.left
= point
.x
+ 'px';
174 this.setFilmPosition(point
);
177 FilmSlider
.prototype.setFilmPosition = function(point
) {
178 this.film
.style
.left
= point
.x
/ this.sliderSpeedRatio
+ 'px';
181 FilmSlider
.prototype.getSliderPosition = function() {
182 var x
= parseInt(this.slider
.style
.left
, 10);
183 var y
= parseInt(this.slider
.style
.top
, 10);
184 var p
= new Point(x
, y
);
188 FilmSlider
.prototype.getFilmPosition = function() {
189 var x
= parseInt(this.film
.style
.left
, 10);
190 var y
= parseInt(this.film
.style
.top
, 10);
191 var p
= new Point(x
, y
);
195 FilmSlider
.prototype.loadSibling = function(previous
) {
198 slide
= this.selectedSlide
.parentNode
.previousSibling
;
199 if (slide
&& slide
.nodeType
===3) { slide
= slide
.previousSibling
; }
202 slide
= this.selectedSlide
.parentNode
.nextSibling
;
203 if (slide
&& slide
.nodeType
===3) { slide
= slide
.nextSibling
; }
206 if (!slide
) { return; }
208 var target
= slide
.getElementsByTagName('a')[0];
209 raiseMouseEvent(target
, 'click');
210 var index
= parseInt(target
.getAttribute('portfolio:position'), 10);
211 this.centerSlide(index
);
215 FilmSlider
.prototype.addEventListeners = function() {
216 var thisSlider
= this;
217 addListener(window
, 'resize', function(evt
){thisSlider
.resizeSlider(evt
);});
218 addListener(this.filmBar
, 'click', function(evt
){thisSlider
.thumbnailClickHandler(evt
);});
219 addListener(this.toolbar
, 'click', function(evt
){thisSlider
.toolbarClickHandler(evt
);});
220 addListener(window
, 'load', function(evt
){thisSlider
.fitToScreen(evt
);});
223 addListener(this.slider
, 'mousedown', this.ddHandlers
.down
);
224 if(browser
.isDOM2Event
){
225 if (browser
.isAppleWebKit
) {
226 this.filmBar
.addEventListener('mousewheel', function(evt
){thisSlider
.mouseWheelHandler(evt
);}, false);
229 addListener(this.filmBar
, 'DOMMouseScroll', function(evt
){thisSlider
.mouseWheelHandler(evt
);});
232 else if (browser
.isIE6up
) {
233 addListener(this.filmBar
, 'mousewheel', function(evt
){thisSlider
.mouseWheelHandler(evt
);});
236 addListener(document
, 'keydown', function(evt
){thisSlider
.keyDownHandler(evt
);});
237 addListener(document
, 'keypress', function(evt
){thisSlider
.keyPressHandler(evt
);});
241 FilmSlider
.prototype.mouseDownHandler = function(evt
) {
242 this.initialClickPoint
= new Point(evt
.clientX
, evt
.clientY
);
243 this.initialPosition
= this.getSliderPosition();
244 this.dragInProgress
= true;
245 addListener(document
, 'mousemove', this.ddHandlers
.move);
246 addListener(document
, 'mouseup', this.ddHandlers
.up
);
247 addListener(document
.body
, 'mouseout', this.ddHandlers
.out
);
251 FilmSlider
.prototype.mouseMoveHandler = function(evt
) {
252 if(!this.dragInProgress
) { return; }
255 evt
= getEventObject(evt
);
256 var currentPoint
= new Point(evt
.clientX
, evt
.clientY
);
257 var displacement
= currentPoint
.diff(this.initialClickPoint
);
258 this.setSliderPosition(this.initialPosition
.add(displacement
));
261 FilmSlider
.prototype.mouseUpHandler = function(evt
) {
262 this.dragInProgress
= false;
263 evt
= getEventObject(evt
);
264 this.mouseMoveHandler(evt
);
268 FilmSlider
.prototype.mouseOutHandler = function(evt
) {
269 evt
= getEventObject(evt
);
273 x
> this.winSize
.width
||
275 y
> this.winSize
.height
277 this.mouseUpHandler(evt
);
281 FilmSlider
.prototype.thumbnailClickHandler = function(evt
) {
282 var target
= getTargetedObject(evt
);
283 while (target
.tagName
!== 'A' && target
!== this.filmBar
) { target
= target
.parentNode
; }
284 if (target
.tagName
!== 'A') { return; }
286 if (this.viewMode
=== 'full') {
287 this.mosaique
.unload();
288 this.mosaique
= null;
289 this.viewMode
= 'medium';
292 disablePropagation(evt
);
294 history
.pushState(target
.href
, '', target
.href
);
296 var imgBaseUrl
= target
.href
;
298 if (this.ctxUrlTranslation
[0]) {
299 canonicalImgUrl
= imgBaseUrl
.replace(this.ctxUrlTranslation
[0],
300 this.ctxUrlTranslation
[1]);
302 else { canonicalImgUrl
= imgBaseUrl
; }
304 var ajaxUrl
= imgBaseUrl
+ '/photo_view_ajax';
307 //this.pendingImage.src = canonicalImgUrl + '/getResizedImage?size=600';
308 var thumbnail
= target
.getElementsByTagName('IMG')[0];
309 var bestFitSize
= this.getBestFitSize(thumbnail
.height
/thumbnail
.width
);
310 this.pendingImage
.src
= canonicalImgUrl
+ '/getResizedImage?size=' + bestFitSize
;
313 var fullScreenLink
= this.buttons
.full_screen
.parentNode
;
314 fullScreenLink
.href
= canonicalImgUrl
+ '/zoom_view';
316 var toggleSelectionBtn
= this.buttons
.toggle_selection
;
317 var toggleSelectionLink
= toggleSelectionBtn
.parentNode
;
318 this.selectedSlideInSelection
= (target
.className
==='selected');
319 if (this.selectedSlideInSelection
) {
320 toggleSelectionBtn
.src
= portal_url() + '/unselect_flag_btn.gif';
321 toggleSelectionBtn
.alt
= toggleSelectionLink
.title
= 'Retirer de la sélection';
322 toggleSelectionLink
.href
= canonicalImgUrl
+ '/remove_to_selection';
325 toggleSelectionBtn
.src
= portal_url() + '/select_flag_btn.gif';
326 toggleSelectionBtn
.alt
= toggleSelectionLink
.title
= 'Ajouter à la sélection';
327 toggleSelectionLink
.href
= canonicalImgUrl
+ '/add_to_selection';
330 var showBuyableButtonLink
= this.buttons
.show_buyable
.parentNode
;
331 showBuyableButtonLink
.href
= canonicalImgUrl
+ '/get_slide_buyable_items';
332 this.cartSlide
.innerHTML
= '';
333 this.cartSlide
.style
.visibility
='hidden';
336 var metadataButton
= this.buttons
['edit_metadata']
337 if (metadataButton
) {
338 var metadataEditLink
= metadataButton
.parentNode
;
339 metadataEditLink
.href
= canonicalImgUrl
+ '/photo_edit_form'
343 var req
= new XMLHttpRequest();
344 req
.onreadystatechange = function() {
345 switch (req
.readyState
) {
351 if (! isTextMime
.exec(req
.getResponseHeader('Content-Type'))) {
352 req
.onreadystatechange
= null;
355 window
.location
.href
= thisFS
._fallBackUrl
;
362 if (req
.status
=== '200') { thisFS
.populateViewer(req
); }
364 // //window.location.href = target.href;
365 // console.error(ajaxUrl);
370 req
.open("GET", ajaxUrl
, true);
373 // update old displayed slide className
374 var className
= this.selectedSlide
.className
;
375 var classes
= className
.split(' ');
381 if (name
=== 'displayed') { continue; }
382 else { newClasses
.push(name
); }
385 this.selectedSlide
.className
= newClasses
.join(' ')
387 // hightlight new displayed slide
388 this.selectedSlide
= target
;
389 className
= this.selectedSlide
.className
;
390 classes
= className
.split(' ');
391 classes
.push('displayed');
392 this.selectedSlide
.className
= classes
.join(' ');
396 FilmSlider
.prototype.toolbarClickHandler = function(evt
) {
397 var target
= getTargetedObject(evt
);
398 if(target
.tagName
=== 'IMG' && target
.getAttribute('name')) {
399 switch(target
.getAttribute('name')) {
402 disablePropagation(evt
);
404 var link
= button
.parentNode
;
406 this.loadSibling(true);
410 disablePropagation(evt
);
412 var link
= button
.parentNode
;
414 this.loadSibling(false);
418 disablePropagation(evt
);
419 target
.parentNode
.blur();
420 if (this.viewMode
=== 'full') {
421 this.mosaique
.unload();
422 this.mosaique
= null;
423 this.viewMode
= 'medium';
426 var main
= document
.getElementById('photo_viewer');
427 var url
= target
.parentNode
.href
;
428 url
= url
.substring(0, url
.length
- '/zoom_view'.length
);
429 var margins
= {'top':0, 'right':-1, 'bottom':0, 'left':0};
430 this.mosaique
= new Mosaique(main
, url
, margins
);
431 this.viewMode
= 'full';
434 case 'toggle_selection':
436 disablePropagation(evt
);
438 var link
= button
.parentNode
;
441 var req
= new XMLHttpRequest();
443 req
.open("POST", url
, true);
444 req
.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
448 var parts
= url
.split('/');
449 var canonicalImgUrl
= parts
.slice(0, parts
.length
-1).join('/');
451 if (isAddToSelection
.test(url
)) {
452 button
.src
= portal_url() + '/unselect_flag_btn.gif';
453 button
.alt
= link
.title
= 'Retirer de la sélection';
454 link
.href
= canonicalImgUrl
+ '/remove_to_selection';
455 this.selectedSlide
.className
= 'selected displayed';
456 this.image
.parentNode
.className
= 'selected';
457 this.selectedSlideInSelection
= true;
460 button
.src
= portal_url() + '/select_flag_btn.gif';
461 button
.alt
= link
.title
= 'Ajouter à la sélection';
462 link
.href
= canonicalImgUrl
+ '/add_to_selection';
463 this.selectedSlide
.className
= 'displayed';
464 this.image
.parentNode
.className
= '';
465 this.selectedSlideInSelection
= false;
471 disablePropagation(evt
);
473 var link
= button
.parentNode
;
475 var slide
= this.cartSlide
;
476 slide
.innerHTML
= '';
477 slide
.style
.visibility
= 'visible';
478 var cw
= new CartWidget(slide
, link
.href
);
479 cw
.onCancel = function() {
480 CartWidget
.prototype.onCancel
.apply(this);
481 slide
.style
.visibility
= 'hidden';
483 cw
.onAfterConfirm = function() {
484 slide
.style
.visibility
= 'hidden';
492 case 'edit_metadata' :
494 disablePropagation(evt);
496 if (this.viewMode === 'full') {
497 this.mosaique.unload();
498 this.mosaique = null;
499 this.viewMode = 'medium';
502 var fi = new FragmentImporter(absolute_url());
503 fi.useMacro('metadata_edit_form_macros', 'iptc', 'image_metadata');
511 if(browser
.isDOM2Event
) {
512 if (browser
.isAppleWebKit
) {
513 FilmSlider
.prototype.mouseWheelHandler = function(evt
) {
515 var pos
= this.getSliderPosition();
516 pos
.x
-= evt
.wheelDelta
/ 40;
517 this.setSliderPosition(pos
);
521 FilmSlider
.prototype.mouseWheelHandler = function(evt
) {
523 var pos
= this.getSliderPosition();
524 pos
.x
+= evt
.detail
* 3;
525 this.setSliderPosition(pos
);
529 else if (browser
.isIE6up
) {
530 FilmSlider
.prototype.mouseWheelHandler = function() {
531 var evt
= window
.event
;
532 evt
.returnValue
= false;
533 var pos
= this.getSliderPosition();
534 pos
.x
-= evt
.wheelDelta
/ 40;
535 this.setSliderPosition(pos
);
539 FilmSlider
.prototype.keyDownHandler = function(evt
) {
540 var evt
= getEventObject(evt
);
541 switch (evt
.keyCode
) {
543 this.loadSibling(true);
546 this.loadSibling(false);
554 FilmSlider
.prototype.keyPressHandler = function(evt
) {
555 var target
= getTargetedObject(evt
);
556 if (target
.tagName
=== 'INPUT' || target
.tagName
=== 'TEXTAREA') { return; }
557 var evt
= getEventObject(evt
);
558 var charPress
= String
.fromCharCode((evt
.keyCode
) ? evt
.keyCode
: evt
.which
);
562 raiseMouseEvent(this.buttons
['full_screen'], 'click');
567 FilmSlider
.prototype.populateViewer = function(req
) {
568 var elements
= req
.responseXML
.documentElement
.childNodes
;
569 for(var i
=0 ; i
< elements
.length
; i
++ ) {
570 element
= elements
[i
];
571 switch (element
.nodeName
) {
573 var dest
= document
.getElementById(element
.getAttribute('id'));
574 if (dest
) { dest
.innerHTML
= element
.firstChild
.nodeValue
; }
576 case 'imageattributes' :
577 var link
= this.buttons
['back_to_portfolio'].parentNode
;
578 link
.href
= element
.getAttribute('backToContextUrl');
579 link
= this.buttons
['show_buyable'].parentNode
;
580 var buyable
= element
.getAttribute('buyable');
581 if(buyable
=== 'True') { link
.className
= null; }
582 else if(buyable
=== 'False') { link
.className
= 'hidden'; }
583 this.image
.alt
= element
.getAttribute('alt');
584 this.updateBreadcrumbs(element
.getAttribute('lastBcUrl'),
585 element
.getAttribute('img_id'));
591 FilmSlider
.prototype.refreshImage = function() {
592 this.image
.style
.visibility
= 'hidden';
593 this.image
.src
= this.pendingImage
.src
;
594 this.image
.width
= this.pendingImage
.width
;
595 this.image
.height
= this.pendingImage
.height
;
596 this.image
.style
.visibility
= 'visible';
597 if (this.selectedSlideInSelection
) { this.image
.parentNode
.className
= 'selected'; }
598 else { this.image
.parentNode
.className
= ''; }
601 FilmSlider
.prototype.updateBreadcrumbs = function(url
, title
) {
602 if (this.hasBreadcrumbs
) {
603 this.lastBCElement
.href
= element
.getAttribute('lastBcUrl');
604 this.lastBCElement
.innerHTML
= element
.getAttribute('img_id');
608 FilmSlider
.prototype.startSlideShow = function() {
609 this.slideShowSlide
= this.pendingSlideShowSlide
= this.selectedSlide
;
610 return this.slideShowSlide
.href
;
613 FilmSlider
.prototype.slideShowNext = function() {
614 var nextSlide
= this.slideShowSlide
.parentNode
.nextSibling
;
615 if (nextSlide
&& nextSlide
.nodeType
===3) { nextSlide
= nextSlide
.nextSibling
; }
618 nextSlide
= nextSlide
.getElementsByTagName('a')[0];
619 this.pendingSlideShowSlide
= nextSlide
;
620 return this.pendingSlideShowSlide
.href
;
623 var row
= this.slideShowSlide
.parentNode
.parentNode
;
624 var first
= row
.firstChild
;
625 if (first
.nodeType
===3)
626 first
= first
.nextSibling
;
627 this.pendingSlideShowSlide
= first
.getElementsByTagName('a')[0];
628 return this.pendingSlideShowSlide
.href
;
632 FilmSlider
.prototype.slideShowPrevious = function() {
633 var previousSlide
= this.slideShowSlide
.parentNode
.previousSibling
;
634 if (previousSlide
&& previousSlide
.nodeType
===3) { previousSlide
= previousSlide
.previousSibling
; }
637 previousSlide
= previousSlide
.getElementsByTagName('a')[0];
638 this.pendingSlideShowSlide
= previousSlide
;
639 return this.pendingSlideShowSlide
.href
;
642 var row
= this.slideShowSlide
.parentNode
.parentNode
;
643 var last
= row
.lastChild
;
644 if (last
.nodeType
===3) { last
= last
.previousSibling
; }
645 this.pendingSlideShowSlide
= last
.getElementsByTagName('a')[0];
646 return this.pendingSlideShowSlide
.href
;
650 FilmSlider
.prototype.slideShowImageLoaded = function() {
651 this.slideShowSlide
= this.pendingSlideShowSlide
;
654 FilmSlider
.prototype.stopSlideShow = function() {
655 raiseMouseEvent(this.slideShowSlide
, 'click');
656 var index
= parseInt(this.selectedSlide
.getAttribute('portfolio:position'));
657 this.centerSlide(index
);
662 function Point(x
, y
) {
663 this.x
= Math
.round(x
);
664 this.y
= Math
.round(y
);
666 Point
.prototype.diff = function(point
) { return new Point(this.x
- point
.x
, this.y
- point
.y
); };
667 Point
.prototype.add = function(point
) { return new Point(this.x
+ point
.x
, this.y
+ point
.y
); };
668 Point
.prototype.mul = function(k
) { return new Point(this.x
* k
, this.y
*k
)};
669 Point
.prototype.toString = function() { return "(" + String(this.x
) + ", " + String(this.y
) + ")"; };