c030fbbc7abd3777c27cfb934ccf8b6bda9e77e0
[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) {
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.toolbar = toolbar;
21 if (toolbar) {
22 this.toolbarFixed = false;
23 addListener(window, 'scroll', function(evt){self.windowScrollToolbarlHandler(evt);});
24 }
25 addListener(window, 'scroll', function(evt){self.windowScrollGridHandler(evt);});
26 addListener(window, 'load', function(evt){ self.windowScrollGridHandler();});
27 this.lastCBChecked = undefined;
28 this.form = undefined;
29 var parent = this.grid.parentNode;
30 while(parent) {
31 parent = parent.parentNode;
32 if (parent.tagName === 'FORM') {
33 this.form = parent;
34 break;
35 }
36 else if (parent.tagName === 'BODY') {
37 break;
38 }
39 }
40 addListener(this.grid, 'click', function(evt){self.mouseClickHandler(evt);});
41 if (this.form) {
42 var fm = this.fm = new FormManager(this.form);
43 addListener(this.form, 'change', function(evt){self.onChangeHandler(evt);});
44 fm.onBeforeSubmit = function(fm_, evt) {return self.onBeforeSubmit(fm_, evt);};
45 fm.onResponseLoad = function(req) {return self.onResponseLoad(req);};
46 }
47 };
48
49 Lightbox.prototype._buildSlidesIndex = function() {
50 this.slides = [];
51 var node, i;
52 for (i=0 ; i<this.grid.childNodes.length ; i++) {
53 node = this.grid.childNodes[i];
54 if (node.nodeType === 1) { // is element
55 this.slides.push(node);
56 }
57 }
58 this.lastSlide = this.slides[this.slides.length-1];
59 };
60
61 Lightbox.prototype.windowScrollToolbarlHandler = function(evt) {
62 if (this.toolbar.offsetTop < window.scrollY && !this.toolbarFixed) {
63 this.toolbarFixed = true;
64 this.backThreshold = this.toolbar.offsetTop;
65 this.switchToolBarPositioning(true);
66 }
67 else if (this.toolbarFixed && window.scrollY < this.backThreshold) {
68 this.toolbarFixed = false;
69 this.switchToolBarPositioning(false);
70 }
71 };
72 Lightbox.prototype.windowScrollGridHandler = function(evt) {
73 if (!this.complete &&
74 !this.fetchingDisabled &&
75 window.scrollY > this.lastSlide.firstElementChild.offsetTop - getWindowHeight()) {
76 this.fetchingDisabled = true;
77 this.fetchTail();
78 }
79 };
80
81 Lightbox.prototype.mouseClickHandler = function(evt) {
82 var target = getTargetedObject(evt);
83 if (target.tagName === 'IMG') {
84 var img = target;
85 var link = target.parentNode;
86 var button = link.parentNode;
87 var slide = button.parentNode;
88 var req, url;
89 if (link.tagName === 'A') {
90 switch(link.getAttribute('name')) {
91 case 'add_to_selection':
92 disableDefault(evt);
93 link.blur();
94 req = new XMLHttpRequest();
95 url = link.href;
96 req.open("POST", url, true);
97 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
98 req.send("ajax=1");
99
100 slide.className = 'selected';
101
102 link.setAttribute('name', 'remove_to_selection');
103 link.href = url.replace(/(.*\/)add_to_selection$/, '$1remove_to_selection');
104 link.title = img.alt = 'Retirer de la sélection';
105 button.className = "button slide-deselect";
106 break;
107
108 case 'remove_to_selection':
109 disableDefault(evt);
110 link.blur();
111 req = new XMLHttpRequest();
112 url = link.href;
113 req.open("POST", url, true);
114 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
115 req.send("ajax=1");
116 slide.className = null;
117 link.setAttribute('name', 'add_to_selection');
118 link.href = url.replace(/(.*\/)remove_to_selection$/, '$1add_to_selection');
119 link.title = img.alt = 'Ajouter à la sélection';
120 button.className = "button slide-select";
121 break;
122
123 case 'add_to_cart' :
124 disableDefault(evt);
125 slide.widget = new CartWidget(slide, link.href);
126 break;
127
128 case 'hide_for_anonymous':
129 disableDefault(evt);
130 link.blur();
131 req = new XMLHttpRequest();
132 url = link.href;
133 req.open("POST", url, true);
134 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
135 req.send(null);
136 slide.className = 'hidden-slide';
137 link.setAttribute('name', 'show_for_anonymous');
138 link.href = url.replace(/(.*\/)hideForAnonymous$/, '$1resetHide');
139 link.title = img.alt = 'Montrer au anonymes';
140 button.className = "button slide-show";
141 break;
142
143 case 'show_for_anonymous':
144 disableDefault(evt);
145 link.blur();
146 req = new XMLHttpRequest();
147 url = link.href;
148 req.open("POST", url, true);
149 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
150 req.send(null);
151 slide.className = null;
152 link.setAttribute('name', 'hide_for_anonymous');
153 link.href = url.replace(/(.*\/)resetHide$/, '$1hideForAnonymous');
154 link.title = img.alt = 'Masquer pour les anonymes';
155 button.className = "button slide-hide";
156 break;
157 }
158 }
159 } else if(target.tagName === 'INPUT' && target.type === 'checkbox') {
160 var cb = target;
161 if (cb.checked) {
162 cb.setAttribute('checked', 'checked');
163 }
164 else {
165 cb.removeAttribute('checked');
166 }
167 this.selectCBRange(evt);
168 }
169 };
170
171 Lightbox.prototype.onChangeHandler = function(evt) {
172 var target = getTargetedObject(evt);
173 if (target.name === 'sort_on') {
174 this.fm.submitButton = {'name' : 'set_sorting', 'value' : 'ok'};
175 this.fm.submit(evt);
176 }
177 };
178
179 Lightbox.prototype.onBeforeSubmit = function(fm, evt) {
180 switch(fm.submitButton.name) {
181 case 'delete' :
182 this.hideSelection();
183 break;
184 }
185 };
186
187 Lightbox.prototype.onResponseLoad = function(req) {
188 switch(req.responseXML.documentElement.nodeName) {
189 case 'deleted' :
190 this.deleteSelection();
191 break;
192 case 'error' :
193 this.showSelection();
194 break;
195 case 'sorted' :
196 this.fm.submitButton = undefined;
197 this.refreshGrid();
198 break;
199 }
200 };
201
202 Lightbox.prototype.switchToolBarPositioning = function(fixed) {
203 var tbs = this.toolbar.style;
204 if (fixed) {
205 this.toolbar.defaultCssText = this.toolbar.style.cssText;
206 tbs.width = String(this.toolbar.offsetWidth) + 'px';
207 tbs.height = String(this.toolbar.offsetHeight) + 'px';
208 tbs.position = 'fixed';
209 tbs.top = '0';
210 this.toolbarPlaceholder = document.createElement('div');
211 var phs = this.toolbarPlaceholder.style;
212 phs.cssText = tbs.cssText;
213 phs.position = 'relative';
214 this.toolbar.parentNode.insertBefore(this.toolbarPlaceholder, this.toolbar);
215 }
216 else {
217 this.toolbarPlaceholder.parentNode.removeChild(this.toolbarPlaceholder);
218 tbs.cssText = this.toolbar.defaultCssText;
219 }
220 };
221
222
223 Lightbox.prototype.hideSelection = function() {
224 var i, e, slide;
225 for (i=0 ; i<this.form.elements.length ; i++) {
226 e = this.form.elements[i];
227 if (e.type === 'checkbox' && e.checked) {
228 slide = e.parentNode.parentNode;
229 slide.classList.add('zero_opacity');
230 }
231 }
232 };
233
234 Lightbox.prototype.showSelection = function() {
235 var i, e, slide;
236 for (i=0 ; i<this.form.elements.length ; i++) {
237 e = this.form.elements[i];
238 if (e.type === 'checkbox' && e.checked) {
239 slide = e.parentNode.parentNode;
240 slide.classList.remove('zero_opacity');
241 }
242 }
243 };
244
245 Lightbox.prototype.deleteSelection = function() {
246 var i, e, slide;
247 for (i=0 ; i<this.form.elements.length ; i++) {
248 e = this.form.elements[i];
249 if (e.type === 'checkbox' && e.checked) {
250 slide = e.parentNode.parentNode;
251 slide.classList.add('zero_width');
252 }
253 }
254 var self = this;
255 // if you change this, delay you should also change this css rule :
256 // .lightbox span { transition: width 1s
257 setTimeout(function(){self._removeSelection();}, 1000);
258 };
259
260 Lightbox.prototype._removeSelection = function() {
261 var i, e;
262 var toRemove = [];
263 for (i=0 ; i<this.form.elements.length ; i++) {
264 e = this.form.elements[i];
265 if (e.type === 'checkbox' && e.checked) {
266 toRemove.push(e.parentNode.parentNode);
267 }
268 }
269 for (i=0 ; i<toRemove.length ; i++) {
270 this.grid.removeChild(toRemove[i]);
271 }
272 this._buildSlidesIndex();
273 this.cbIndex = undefined;
274 this.windowScrollGridHandler();
275 };
276
277 Lightbox.prototype.getCBIndex = function(cb) {
278 if (!this.cbIndex) {
279 // build checkbox index
280 this.cbIndex = [];
281 var i, node, c;
282 for (i=0 ; i<this.slides.length ; i++) {
283 node = this.slides[i];
284 c = node.getElementsByTagName('input')[0];
285 c.index = this.cbIndex.length;
286 this.cbIndex.push(c);
287 }
288 }
289 return cb.index;
290 };
291
292 Lightbox.prototype.selectCBRange = function(evt) {
293 var target = getTargetedObject(evt);
294 evt = getEventObject(evt);
295 var shift = evt.shiftKey;
296 if (shift && this.lastCBChecked) {
297 var from = this.getCBIndex(this.lastCBChecked);
298 var to = this.getCBIndex(target);
299 var start = Math.min(from, to);
300 var stop = Math.max(from, to);
301 var i;
302 for (i=start ; i<stop ; i++ ) {
303 this.cbIndex[i].setAttribute('checked', 'checked');
304 }
305 }
306 else if (target.checked) {
307 this.lastCBChecked = target;
308 }
309 else {
310 this.lastCBChecked = undefined;
311 }
312 };
313
314 Lightbox.prototype.refreshGrid = function() {
315 var req = new XMLHttpRequest();
316 self = this;
317 req.onreadystatechange = function() {
318 switch (req.readyState) {
319 case 1 :
320 showProgressImage();
321 break;
322 case 4 :
323 hideProgressImage();
324 if (req.status === 200) {
325 self._refreshGrid(req)
326 }
327 break;
328 }
329 };
330
331 var url = absolute_url() +
332 '/portfolio_thumbnails_tail?start:int=0&size:int=' +
333 this.slides.length;
334 req.open('GET', url, true);
335 req.send();
336 };
337
338 Lightbox.prototype._refreshGrid = function(req) {
339 var doc = req.responseXML.documentElement;
340 var i, node;
341 var j = 0;
342 for (i=0 ; i<doc.childNodes.length ; i++) {
343 node = doc.childNodes[i];
344 if (node.nodeType === 1) {
345 node = getCopyOfNode(node);
346 this.grid.replaceChild(node, this.slides[j]);
347 this.slides[j] = node;
348 j++;
349 }
350 }
351 this.cbIndex = undefined;
352 };
353
354 Lightbox.prototype.fetchTail = function() {
355 var req = new XMLHttpRequest();
356 self = this;
357 req.onreadystatechange = function() {
358 switch (req.readyState) {
359 case 1 :
360 showProgressImage();
361 break;
362 case 4 :
363 hideProgressImage();
364 if (req.status === 200) {
365 self._appendTail(req)
366 }
367 break;
368 }
369 };
370
371 var url = absolute_url() +
372 '/portfolio_thumbnails_tail?start:int=' +
373 String(this.slides.length + 1 ) +
374 '&size:int=10';
375 req.open('GET', url, true);
376 req.send();
377 };
378
379 Lightbox.prototype._appendTail = function(req) {
380 var doc = req.responseXML.documentElement;
381 var i, node, c;
382 for (i=0 ; i<doc.childNodes.length ; i++) {
383 node = doc.childNodes[i];
384 if (node.nodeType === 1) {
385 this.lastSlide = this.grid.appendChild(getCopyOfNode(node));
386 this.slides.push(this.lastSlide);
387 if (this.cbIndex) {
388 c = this.lastSlide.getElementsByTagName('input')[0];
389 c.index = this.cbIndex.length;
390 this.cbIndex.push(c);
391
392 }
393 }
394 }
395 this.fetchingDisabled = false;
396 if (doc.getAttribute('nomore')) {
397 this.complete = true;
398 }
399 this.windowScrollGridHandler();
400 };
401
402
403 var _outlineSelectedSlide;
404 if (browser.isGecko) {
405 _outlineSelectedSlide = function(slide) {
406 slide.className = 'selected';
407 };
408 }
409 else {
410 _outlineSelectedSlide = function(slide) {
411 if (slide.className &&
412 !reSelected.test(slide.className)) {
413 slide.className = slide.className + ' selected';
414 }
415 };
416 }
417
418 }());