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