97c7f7960b7c1ca8587aa1bfef03581fe285720b
[Portfolio.git] / skins / photo_lightbox_viewer.js
1 /*
2 * 2008-2014 Benoit Pin - MINES ParisTech
3 * http://plinn.org
4 * Licence Creative Commons http://creativecommons.org/licenses/by-nc/2.0/
5 */
6
7
8 var Lightbox;
9
10 (function(){
11
12 var reSelected = /.*selected.*/;
13
14 Lightbox = function(grid, toolbar, complete, container_type) {
15 var self = this;
16 this.grid = grid;
17 this._buildSlidesIndex(); // set this.slides and this.lastSlide;
18 this.fetchingDisabled = false;
19 this.complete = complete;
20 this.container_type = container_type;
21 this.toolbar = toolbar;
22 if (toolbar) {
23 this.toolbarFixed = false;
24 addListener(window, 'scroll', function(evt){self.windowScrollToolbarlHandler(evt);});
25 }
26 addListener(window, 'scroll', function(evt){self.windowScrollGridHandler(evt);});
27 registerStartupFunction(function(){ self.windowScrollGridHandler();});
28 this.lastCBChecked = undefined;
29 this.form = undefined;
30 var parent = this.grid.parentNode;
31 while(parent) {
32 parent = parent.parentNode;
33 if (parent.tagName === 'FORM') {
34 this.form = parent;
35 break;
36 }
37 else if (parent.tagName === 'BODY') {
38 break;
39 }
40 }
41 addListener(this.grid, 'click', function(evt){self.mouseClickHandler(evt);});
42 if (this.form) {
43 var fm = this.fm = new FormManager(this.form);
44 addListener(this.form, 'change', function(evt){self.onChangeHandler(evt);});
45 fm.onBeforeSubmit = function(fm_, evt) {return self.onBeforeSubmit(fm_, evt);};
46 fm.onResponseLoad = function(req) {return self.onResponseLoad(req);};
47 }
48
49 // drag and drop
50 this.disableDefaultDragging();
51 addListener(this.grid, 'dragstart', function(evt){self.onDragStart(evt);});
52 addListener(this.grid, 'dragover', function(evt){self.onDragOver(evt);});
53 addListener(this.grid, 'dragend', function(evt){self.onDragEnd(evt);});
54 };
55
56 Lightbox.prototype._buildSlidesIndex = function() {
57 this.slides = [];
58 var node, i;
59 for (i=0 ; i<this.grid.childNodes.length ; i++) {
60 node = this.grid.childNodes[i];
61 if (node.nodeType === 1) { // is element
62 this.slides.push(node);
63 }
64 }
65 this.lastSlide = this.slides[this.slides.length-1];
66 };
67
68 Lightbox.prototype.windowScrollToolbarlHandler = function(evt) {
69 if (this.toolbar.offsetTop < getWindowScrollY() && !this.toolbarFixed) {
70 this.toolbarFixed = true;
71 this.backThreshold = this.toolbar.offsetTop;
72 this.switchToolBarPositioning(true);
73 }
74 else if (this.toolbarFixed && getWindowScrollY() < this.backThreshold) {
75 this.toolbarFixed = false;
76 this.switchToolBarPositioning(false);
77 }
78 };
79 Lightbox.prototype.windowScrollGridHandler = function(evt) {
80 if (!this.complete &&
81 !this.fetchingDisabled &&
82 getWindowScrollY() >
83 (this.lastSlide.firstElementChild || this.lastSlide.children[0]).offsetTop
84 - getWindowHeight()) {
85 this.fetchingDisabled = true;
86 this.fetchTail();
87 }
88 };
89
90 Lightbox.prototype.mouseClickHandler = function(evt) {
91 var target = getTargetedObject(evt);
92 if (target.tagName === 'IMG') {
93 var img = target;
94 var link = target.parentNode;
95 var button = link.parentNode;
96 var slide = button.parentNode;
97 var req, url;
98 if (link.tagName === 'A') {
99 switch(link.getAttribute('name')) {
100 case 'add_to_selection':
101 disableDefault(evt);
102 link.blur();
103 req = new XMLHttpRequest();
104 url = link.href;
105 req.open("POST", url, true);
106 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
107 req.send("ajax=1");
108
109 slide.className = 'selected';
110
111 link.setAttribute('name', 'remove_to_selection');
112 link.href = url.replace(/(.*\/)add_to_selection$/, '$1remove_to_selection');
113 link.title = img.alt = 'Retirer de la sélection';
114 button.className = "button slide-deselect";
115 break;
116
117 case 'remove_to_selection':
118 disableDefault(evt);
119 link.blur();
120 req = new XMLHttpRequest();
121 url = link.href;
122 req.open("POST", url, true);
123 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
124 req.send("ajax=1");
125 slide.className = null;
126 link.setAttribute('name', 'add_to_selection');
127 link.href = url.replace(/(.*\/)remove_to_selection$/, '$1add_to_selection');
128 link.title = img.alt = 'Ajouter à la sélection';
129 button.className = "button slide-select";
130 break;
131
132 case 'add_to_cart' :
133 disableDefault(evt);
134 slide.widget = new CartWidget(slide, link.href);
135 break;
136
137 case 'hide_for_anonymous':
138 disableDefault(evt);
139 link.blur();
140 req = new XMLHttpRequest();
141 url = link.href;
142 req.open("POST", url, true);
143 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
144 req.send(null);
145 slide.className = 'hidden-slide';
146 link.setAttribute('name', 'show_for_anonymous');
147 link.href = url.replace(/(.*\/)hideForAnonymous$/, '$1resetHide');
148 link.title = img.alt = 'Montrer au anonymes';
149 button.className = "button slide-show";
150 break;
151
152 case 'show_for_anonymous':
153 disableDefault(evt);
154 link.blur();
155 req = new XMLHttpRequest();
156 url = link.href;
157 req.open("POST", url, true);
158 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
159 req.send(null);
160 slide.className = null;
161 link.setAttribute('name', 'hide_for_anonymous');
162 link.href = url.replace(/(.*\/)resetHide$/, '$1hideForAnonymous');
163 link.title = img.alt = 'Masquer pour les anonymes';
164 button.className = "button slide-hide";
165 break;
166 }
167 }
168 } else if(target.tagName === 'INPUT' && target.type === 'checkbox') {
169 var cb = target;
170 if (cb.checked) {
171 cb.setAttribute('checked', 'checked');
172 }
173 else {
174 cb.removeAttribute('checked');
175 }
176 this.selectCBRange(evt);
177 }
178 };
179
180 Lightbox.prototype.onChangeHandler = function(evt) {
181 var target = getTargetedObject(evt);
182 if (target.name === 'sort_on') {
183 this.fm.submitButton = {'name' : 'set_sorting', 'value' : 'ok'};
184 this.fm.submit(evt);
185 }
186 };
187
188 Lightbox.prototype.onBeforeSubmit = function(fm, evt) {
189 switch(fm.submitButton.name) {
190 case 'delete' :
191 this.hideSelection();
192 break;
193 }
194 };
195
196 Lightbox.prototype.onResponseLoad = function(req) {
197 switch(req.responseXML.documentElement.nodeName) {
198 case 'deleted' :
199 this.deleteSelection();
200 break;
201 case 'error' :
202 this.showSelection();
203 break;
204 case 'sorted' :
205 this.fm.submitButton = undefined;
206 this.refreshGrid();
207 break;
208 default :
209 this.fm.loadResponse(req);
210 break;
211 }
212 };
213
214 Lightbox.prototype.switchToolBarPositioning = function(fixed) {
215 var tbs = this.toolbar.style;
216 if (fixed) {
217 this.toolbar.defaultCssText = this.toolbar.style.cssText;
218 tbs.width = String(this.toolbar.offsetWidth) + 'px';
219 tbs.height = String(this.toolbar.offsetHeight) + 'px';
220 tbs.position = 'fixed';
221 tbs.top = '0';
222 this.toolbarPlaceholder = document.createElement('div');
223 var phs = this.toolbarPlaceholder.style;
224 phs.cssText = tbs.cssText;
225 phs.position = 'relative';
226 this.toolbar.parentNode.insertBefore(this.toolbarPlaceholder, this.toolbar);
227 }
228 else {
229 this.toolbarPlaceholder.parentNode.removeChild(this.toolbarPlaceholder);
230 tbs.cssText = this.toolbar.defaultCssText;
231 }
232 };
233
234
235 Lightbox.prototype.hideSelection = function() {
236 var i, e, slide;
237 for (i=0 ; i<this.form.elements.length ; i++) {
238 e = this.form.elements[i];
239 if (e.type === 'checkbox' && e.checked) {
240 slide = e.parentNode.parentNode;
241 slide.classList.add('zero_opacity');
242 }
243 }
244 };
245
246 Lightbox.prototype.showSelection = function() {
247 var i, e, slide;
248 for (i=0 ; i<this.form.elements.length ; i++) {
249 e = this.form.elements[i];
250 if (e.type === 'checkbox' && e.checked) {
251 slide = e.parentNode.parentNode;
252 slide.classList.remove('zero_opacity');
253 }
254 }
255 };
256
257 Lightbox.prototype.deleteSelection = function() {
258 var i, e, slide;
259 for (i=0 ; i<this.form.elements.length ; i++) {
260 e = this.form.elements[i];
261 if (e.type === 'checkbox' && e.checked) {
262 slide = e.parentNode.parentNode;
263 slide.classList.add('zero_width');
264 }
265 }
266 var self = this;
267 // if you change this, delay you should also change this css rule :
268 // .lightbox span { transition: width 1s
269 setTimeout(function(){self._removeSelection();}, 1000);
270 };
271
272 Lightbox.prototype._removeSelection = function() {
273 var i, e;
274 var toRemove = [];
275 for (i=0 ; i<this.form.elements.length ; i++) {
276 e = this.form.elements[i];
277 if (e.type === 'checkbox' && e.checked) {
278 toRemove.push(e.parentNode.parentNode);
279 }
280 }
281 for (i=0 ; i<toRemove.length ; i++) {
282 this.grid.removeChild(toRemove[i]);
283 }
284 this._buildSlidesIndex();
285 this.cbIndex = undefined;
286 this.windowScrollGridHandler();
287 };
288
289 Lightbox.prototype.getCBIndex = function(cb) {
290 if (!this.cbIndex) {
291 // build checkbox index
292 this.cbIndex = [];
293 var i, node, c;
294 for (i=0 ; i<this.slides.length ; i++) {
295 node = this.slides[i];
296 c = node.getElementsByTagName('input')[0];
297 c.index = this.cbIndex.length;
298 this.cbIndex.push(c);
299 }
300 }
301 return cb.index;
302 };
303
304 Lightbox.prototype.selectCBRange = function(evt) {
305 var target = getTargetedObject(evt);
306 evt = getEventObject(evt);
307 var shift = evt.shiftKey;
308 if (shift && this.lastCBChecked) {
309 var from = this.getCBIndex(this.lastCBChecked);
310 var to = this.getCBIndex(target);
311 var start = Math.min(from, to);
312 var stop = Math.max(from, to);
313 var i;
314 for (i=start ; i<stop ; i++ ) {
315 this.cbIndex[i].setAttribute('checked', 'checked');
316 }
317 }
318 else if (target.checked) {
319 this.lastCBChecked = target;
320 }
321 else {
322 this.lastCBChecked = undefined;
323 }
324 };
325
326 Lightbox.prototype.refreshGrid = function() {
327 var req = new XMLHttpRequest();
328 self = this;
329 req.onreadystatechange = function() {
330 switch (req.readyState) {
331 case 1 :
332 showProgressImage();
333 break;
334 case 4 :
335 hideProgressImage();
336 if (req.status === 200) {
337 self._refreshGrid(req);
338 }
339 break;
340 }
341 };
342
343 var url = absolute_url() +
344 '/portfolio_thumbnails_tail?start:int=0&size:int=' +
345 this.slides.length;
346 req.open('GET', url, true);
347 req.send();
348 };
349
350 Lightbox.prototype._refreshGrid = function(req) {
351 var doc = req.responseXML.documentElement;
352 var i, node;
353 var j = 0;
354 for (i=0 ; i<doc.childNodes.length ; i++) {
355 node = doc.childNodes[i];
356 if (node.nodeType === 1) {
357 node = getCopyOfNode(node);
358 this.disableDefaultDragging(node);
359 this.grid.replaceChild(node, this.slides[j]);
360 this.slides[j] = node;
361 j++;
362 }
363 }
364 this.cbIndex = undefined;
365 };
366
367 Lightbox.prototype.fetchTail = function() {
368 var req = new XMLHttpRequest();
369 var self = this;
370 req.onreadystatechange = function() {
371 switch (req.readyState) {
372 case 1 :
373 showProgressImage();
374 break;
375 case 4 :
376 hideProgressImage();
377 if (req.status === 200) {
378 self._appendTail(req);
379 }
380 break;
381 }
382 };
383
384 var url = absolute_url() +
385 '/portfolio_thumbnails_tail?start:int=' +
386 String(this.slides.length) +
387 '&size:int=10' +
388 '&container_type=' +
389 this.container_type;
390 req.open('GET', url, true);
391 req.send();
392 };
393
394 Lightbox.prototype._appendTail = function(req) {
395 var doc = req.responseXML.documentElement;
396 var i, node, c;
397 for (i=0 ; i<doc.childNodes.length ; i++) {
398 node = doc.childNodes[i];
399 if (node.nodeType === 1) {
400 this.lastSlide = this.grid.appendChild(getCopyOfNode(node));
401 this.disableDefaultDragging(this.lastSlide);
402 this.slides.push(this.lastSlide);
403 if (this.cbIndex) {
404 c = this.lastSlide.getElementsByTagName('input')[0];
405 c.index = this.cbIndex.length;
406 this.cbIndex.push(c);
407
408 }
409 }
410 }
411 this.fetchingDisabled = false;
412 if (doc.getAttribute('nomore')) {
413 this.complete = true;
414 }
415 this.windowScrollGridHandler();
416 };
417
418
419 var _outlineSelectedSlide;
420 if (browser.isGecko) {
421 _outlineSelectedSlide = function(slide) {
422 slide.className = 'selected';
423 };
424 }
425 else {
426 _outlineSelectedSlide = function(slide) {
427 if (slide.className &&
428 !reSelected.test(slide.className)) {
429 slide.className = slide.className + ' selected';
430 }
431 };
432 }
433
434 if (browser.isGecko) {
435 Lightbox.prototype.disableDefaultDragging = function(element) {
436 if (!element) {
437 element = this.grid;
438 }
439 var i, j, name, elements;
440 var elementsNames = ['a', 'img'];
441 for (i=0 ; i < elementsNames.length ; i++) {
442 name = elementsNames[i];
443 elements = element.getElementsByTagName(name);
444 for (j=0 ; j < elements.length ; j++) {
445 elements[j].draggable=false;
446 }
447 }
448 };
449 }
450 else {
451 Lightbox.prototype.disableDefaultDragging = function() {};
452 }
453
454 Lightbox.prototype.getSelectedSlides = function() {
455 var i, e, slide;
456 var slides = [];
457 for (i=0 ; i<this.form.elements.length ; i++) {
458 e = this.form.elements[i];
459 if (e.type === 'checkbox' && e.checked) {
460 slide = e.parentNode.parentNode;
461 slides.push(slide);
462 }
463 }
464 return slides;
465 };
466
467 Lightbox.prototype.onDragStart = function(evt) {
468 var target = getTargetedObject(evt);
469 this.dragged = target;
470 this.draggedSelection = this.getSelectedSlides();
471 if (this.draggedSelection.indexOf(target) === -1) {
472 this.draggedSelection.push(target);
473 }
474 evt.dataTransfer.setData('text', '');
475 var i, slide;
476 for(i=0 ; i<this.draggedSelection.length ; i++) {
477 slide = this.draggedSelection[i];
478 slide.style.opacity = 0;
479 slide.style.width = 0;
480 }
481 };
482
483 Lightbox.prototype.onDragOver = function(evt) {
484 var target = getTargetedObject(evt);
485 if (!target) {return;}
486 while(target.className !== 'slide') {
487 target = target.parentNode;
488 }
489 target = target.parentNode;
490 if (target !== this.dragged) {
491 target.classList.add('dragover');
492 }
493 if (this.lastDropTarget && this.lastDropTarget !== target) {
494 this.lastDropTarget.classList.remove('dragover');
495 }
496 this.lastDropTarget = target;
497 };
498
499 Lightbox.prototype.onDragEnd = function(evt) {
500 if (this.lastDropTarget) {
501 this.lastDropTarget.classList.remove('dragover');
502 var i, slide;
503 this.pendingMovedSlides = [];
504 for(i=this.draggedSelection.length -1 ; i>=0 ; i--) {
505 slide = this.draggedSelection[i].cloneNode(true);
506 this.pendingMovedSlides.push(slide);
507 this.grid.insertBefore(slide, this.lastDropTarget.nextSibling);
508 slide.style.opacity = 1;
509 slide.style.width = '';
510 }
511 this.moveSelectedPhotos();
512 }
513 // this.draggedSelection = this.lastDropTarget
514 this.dragged = undefined;
515 };
516
517 Lightbox.prototype.moveSelectedPhotos = function() {
518 var req = new XMLHttpRequest();
519 self = this;
520 req.onreadystatechange = function() {
521 switch (req.readyState) {
522 case 1 :
523 showProgressImage();
524 break;
525 case 4 :
526 hideProgressImage();
527 self._moveSelectedPhotos(req)
528 break;
529 }
530 };
531
532 var url = absolute_url() + '/portfolio_move_photos';
533 req.open("POST", url, true);
534 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
535 var query = 'container_type=' + this.container_type;
536 var i;
537 for (i=0 ; i<this.draggedSelection.length ; i++) {
538 query += '&uids:list=' + this.draggedSelection[i].getAttribute('name');
539 }
540 query += '&afterUid=' + this.lastDropTarget.getAttribute('name');
541 req.send(query);
542 };
543
544 Lightbox.prototype._moveSelectedPhotos = function(req) {
545 var i, slide, cb;
546 if (req.status === 200) {
547 var doc = req.responseXML.documentElement;
548 if (doc.nodeName === 'ok') {
549 for(i=0 ; i<this.draggedSelection.length ; i++) {
550 slide = this.draggedSelection[i];
551 this.grid.removeChild(slide);
552 cb = this.pendingMovedSlides[i].getElementsByTagName('input')[0]
553 cb.checked = false;
554 cb.removeAttribute('checked');
555 }
556 this.pendingMovedSlides = undefined;
557 this.cbIndex = undefined;
558 return;
559 }
560 }
561
562 for(i=0 ; i<this.pendingMovedSlides.length ; i++) {
563 slide = this.pendingMovedSlides[i];
564 this.grid.removeChild(slide);
565 }
566
567 for(i=0 ; i<this.draggedSelection.length ; i++) {
568 slide = this.draggedSelection[i];
569 slide.style.opacity = 1;
570 slide.style.width = '';
571 }
572 };
573
574 }());