Project

General

Profile

1
/***
2
* SimplBox - v1.0.0 - 2014.08.04
3
* Author: (c) Dendrochronology - @Dendrochronolo - http://genert.laal.ee/
4
* Available for use under the MIT License.
5
***/
6
;(function (window, document, undefined) {
7
    "use strict";
8

    
9
    var docElem = document.documentElement,
10
        bodyElem = document.getElementsByTagName("body")[0],
11

    
12
        FALSE = false,
13
        ATTACHEVENT = "attachEvent",
14
        ADDEVENTLISTENER = "addEventListener",
15

    
16
        isEventListener = ADDEVENTLISTENER in document;
17

    
18
    function SimplBox (p_Elements, p_Options) {
19
        var base = this;
20

    
21
        base.m_Elements = p_Elements;
22
        base.m_UserOptions = p_Options || {};
23
        base.m_Options = {};
24

    
25
        base.m_CurrentTargetElements = FALSE;
26
        base.m_CurrentTargetElementsLength = FALSE;
27
        base.m_CurrentTargetNumber = FALSE;
28
        base.m_CurrentImageElement = FALSE;
29
        base.m_InProgress = FALSE;
30
        base.m_InstalledImageBox = FALSE;
31
        base.m_Alt = FALSE;
32
        base.m_AnimateDone = FALSE; // For browsers that do not support hardware acceleration.
33

    
34
        var __construct = function () {
35
            for (var i in SimplBox.options) {
36
                if (base.m_UserOptions.hasOwnProperty(i)) {
37
                    base.m_Options[i] = base.m_UserOptions[i];
38
                } else {
39
                    base.m_Options[i] = SimplBox.options[i];
40
                }
41
            }
42
        }();
43
    }
44

    
45
    SimplBox.prototype = {
46
        init: function () {
47
            var base = this;
48

    
49
            // API start
50
            base.API_AddEvent = base.addEvent;
51
            base.API_RemoveImageElement = base.removeImageElement;
52
            // API end
53

    
54
            base.checkBrowser();
55
            base.addEvents();
56
        },
57

    
58
        checkBrowser: function () {
59
            var base = this,
60
                isTouch = "ontouchstart" in window || window.navigator.msMaxTouchPoints || navigator.maxTouchPoints || FALSE,
61
                hasPointers = isTouch && (window.navigator.pointerEnabled || window.navigator.msPointerEnabled);
62

    
63
            base.browser = {
64
                "isHardwareAccelerated": (base.getcss3prop("transition") !== "undefined" ? true : FALSE),
65
                "isTouch": isTouch,
66
                "hasPointers": hasPointers
67
            };
68
        },
69

    
70
        addEvents: function () {
71
            var base = this;
72

    
73
            // Add click events on base elements.
74
            for (var i = 0; i < base.m_Elements.length; i++) {
75
                (function (i) {
76
                    base.addEvent(base.m_Elements[i], (base.browser.hasPointers ? "pointerup MSPointerUp" : "click"), function (event) {
77
                        if (event.preventDefault) {
78
                            event.preventDefault();
79
                            event.stopPropagation();
80
                        }
81

    
82
                        if (window.event) {
83
                            window.event.returnValue = FALSE;
84
                            window.event.cancelBubble = FALSE;
85
                        }
86

    
87
                        if (base.isFunction(base.m_Options.onStart())) {
88
                            base.m_Options.onStart(this);
89
                        }
90

    
91
                        base.m_CurrentTargetElements = base.m_Elements;
92
                        base.m_CurrentTargetElementsLength = base.m_Elements.length;
93
                        base.m_CurrentTargetNumber = i;
94

    
95
                        base.openImage(base.m_Elements[base.m_CurrentTargetNumber]);
96
                    });
97
                })(i);
98
            }
99

    
100
            base.addEvent(window, "resize", function () {
101
                base.calculateImagePositionAndSize(base.m_CurrentImageElement, true);
102
            });
103

    
104
            // Add keyboard support.
105
            base.leftAnimationFunction = function () {
106
                if (base.m_CurrentTargetNumber - 1 < 0) {
107
                    base.openImage(base.m_CurrentTargetElements[base.m_CurrentTargetElementsLength - 1], "left");
108
                    base.m_CurrentTargetNumber = base.m_CurrentTargetElementsLength - 1;
109
                } else {
110
                    base.openImage(base.m_CurrentTargetElements[base.m_CurrentTargetNumber - 1], "left");
111
                    base.m_CurrentTargetNumber = base.m_CurrentTargetNumber - 1;
112
                }
113
            };
114

    
115
            base.rightAnimationFunction = function () {
116
                if (base.m_CurrentTargetNumber + 1 > base.m_CurrentTargetElementsLength - 1) {
117
                    base.openImage(base.m_CurrentTargetElements[0], "right");
118
                    base.m_CurrentTargetNumber = 0;
119
                } else {
120
                    base.openImage(base.m_CurrentTargetElements[base.m_CurrentTargetNumber+1], "right");
121
                    base.m_CurrentTargetNumber = base.m_CurrentTargetNumber + 1;
122
                }
123
            };
124

    
125
            if (base.m_Options.enableKeyboard) {
126
                var keyBoard = {
127
                    left: 37,
128
                    right: 39,
129
                    esc: 27
130
                };
131

    
132
                base.addEvent(window, "keydown", function (event) {
133
                    if (!base.m_CurrentImageElement || base.m_InProgress) {
134
                        return;
135
                    }
136

    
137
                    if (event.preventDefault) {
138
                        event.preventDefault();
139
                        event.stopPropagation();
140
                    }
141

    
142
                    if (window.event) {
143
                        var event = window.event;
144
                        window.event.returnValue = FALSE;
145
                        window.event.cancelBubble = FALSE;
146
                    }
147

    
148
                    var keyCode = event.which || event.keyCode;
149

    
150
                    switch (keyCode) {
151
                        case keyBoard.esc: base.removeImageElement(); return FALSE;
152
                        case keyBoard.right: base.rightAnimationFunction(); return FALSE;
153
                        case keyBoard.left: base.leftAnimationFunction(); return FALSE;
154
                    }
155
                });
156
            }
157

    
158
            if (base.m_Options.quitOnDocumentClick) {
159
                base.addEvent(isEventListener ? bodyElem : document, "click", function (event) {
160
                    if (base.m_InProgress) {
161
                        return FALSE;
162
                    }
163

    
164
                    if (event.preventDefault) {
165
                        event.preventDefault();
166
                    }
167

    
168
                    if (window.event) {
169
                        var event = window.event;
170
                    }
171

    
172
                    var target = event.target ? event.target : event.srcElement;
173

    
174
                    if (target && target.id !== base.m_Options.imageElementId && base.m_InstalledImageBox && !base.m_InProgress) {
175
                        base.removeImageElement();
176
                        return FALSE;
177
                    }
178
                });
179
            }
180
        },
181

    
182
        openImage: function (p_Source, p_Direction) {
183
            var base = this,
184
                documentFragment = document.createDocumentFragment(),
185
                imageElement = document.createElement("img"),
186
                imageElementControl = document.getElementById(base.m_Options.imageElementId);
187

    
188
            // If no 1 argument or 1 argument's tagname is not A, return.
189
            if (!p_Source || p_Source.tagName.toLowerCase() !== "a") {
190
                return;
191
            }
192

    
193
            if (imageElementControl) {
194
                bodyElem.removeChild(imageElementControl);
195
                base.m_CurrentImageElement = FALSE;
196
                base.m_InstalledImageBox = FALSE;
197
            }
198

    
199
            base.m_Alt = p_Source.firstChild.getAttribute("alt");
200
            base.m_InProgress = true;
201

    
202
            // Check if it funcion and return.
203
            if (base.isFunction(base.m_Options.onImageLoadStart())) {
204
                base.m_Options.onImageLoadStart();
205
            }
206

    
207
            // Set direction
208
            if (typeof p_Direction !== "undefined") {
209
                switch (p_Direction) {
210
                    case "left": p_Direction = -1; break;
211
                    case "right": p_Direction = 1; break;
212
                }
213
            }
214

    
215
            // Set attributes of new image element.
216
            imageElement.setAttribute("id", base.m_Options.imageElementId);
217
            imageElement.setAttribute("src", p_Source.getAttribute("href"));
218
            imageElement.setAttribute("alt", base.m_Alt);
219
            imageElement.setAttribute("style", "position: fixed; cursor: pointer; opacity: 0;") ;
220

    
221
            // Append to fragment and append fragment to body.
222
            documentFragment.appendChild(imageElement);
223
            bodyElem.appendChild(documentFragment);
224

    
225
            // Set current image element.
226
            base.m_CurrentImageElement = document.getElementById(base.m_Options.imageElementId);
227
            base.m_CurrentImageElement.style.filter = 'alpha(opacity=0)'; // IE 8 opacity
228

    
229
            if (base.browser.isHardwareAccelerated) {
230
                if (typeof p_Direction !== "undefined") {
231
                    base.m_CurrentImageElement.style[base.getcss3prop("transform")] = "translateX(" + (p_Direction * base.m_Options.fadeInDistance) + "px)";
232
                }
233
                base.m_CurrentImageElement.style[base.getcss3prop("transition")] = "all " + base.m_Options.animationSpeed + "ms ease";
234
            }
235

    
236
            // Calculate image position and size and set them.
237
            base.calculateImagePositionAndSize(base.m_CurrentImageElement, FALSE, p_Direction);
238
                    
239
            // Add event listener.
240
            if (base.m_Options.quitOnImageClick) {
241
                base.addEvent(base.m_CurrentImageElement, "click", function (event) {
242
                    if (event.preventDefault) {
243
                        event.preventDefault();
244
                    }
245

    
246
                    if (window.event) {
247
                        window.event.returnValue = FALSE;
248
                    }
249

    
250
                    base.removeImageElement();
251
                });
252
            }
253

    
254
            // Touch events.
255
            if (base.browser.isTouch) { // This check fixes bug in IE 10 & 11 because these browsers have pointers for odd reason(s).
256
                var touchXStart = -1,
257
                    touchXEnd = -1,
258
                    swipeDifference = 0;
259
            
260
                base.addEvent(base.m_CurrentImageElement, "touchstart pointerdown MSPointerDown", function (event) {
261
                    event.preventDefault();
262
                    touchXStart = event.pageX || event.touches[0].pageX;
263
                });
264
            
265
                base.addEvent(base.m_CurrentImageElement, "touchmove pointermove MSPointerMove", function (event) {
266
                    event.preventDefault();
267
                    touchXEnd = event.pageX || event.touches[0].pageX;
268
                    swipeDifference = touchXStart - touchXEnd;
269
            
270
                    if (base.browser.isHardwareAccelerated) {
271
                        base.m_CurrentImageElement.style[base.getcss3prop("transition")] = "none";
272
                        base.m_CurrentImageElement.style[base.getcss3prop("transform")] = "translateX(" + -swipeDifference + "px)";
273
                    }
274
                });
275
            
276
                base.addEvent(base.m_CurrentImageElement, "touchend pointerup pointercancel MSPointerUp MSPointerCancel", function (event) {
277
                    event.preventDefault();
278
            
279
                    if (Math.abs(swipeDifference) > 75) {
280
                        if (swipeDifference < 0) {
281
                            base.leftAnimationFunction();
282
                        } else {
283
                            base.rightAnimationFunction();
284
                        }
285
                    } else {
286
                        base.m_CurrentImageElement.style[base.getcss3prop("transform")] = "translateX(0px)";
287
                    }
288
                });
289
            }
290
        },
291

    
292
        calculateImagePositionAndSize: function (p_Element, p_Resize) {
293
            var base = this,
294
                temporaryImageObject = new Image(),
295
                imageWidth = 0,
296
                imageHeight = 0,
297
                imageSizeRatio = 0;
298

    
299
            // If no element provided, quit.
300
            if (!p_Element) {
301
                return;
302
            }
303

    
304
            base.m_ImageSource = p_Element.getAttribute("src"); // Get element's source attribute for loading image.
305
            base.m_ScreenHeight = window.innerHeight || docElem.offsetHeight; // Get window height.
306
            base.m_ScreenWidth = window.innerWidth || docElem.offsetWidth; // Get window width.
307

    
308
            temporaryImageObject.onload = function () {
309
                var thisImageWidth = this.width,
310
                    thisImageHeight = this.height;
311

    
312
                imageWidth = thisImageWidth;
313
                imageHeight = thisImageHeight;
314
                imageSizeRatio = imageWidth / imageHeight;
315

    
316
                // Height of image is too big to fit in viewport
317
                if (Math.floor(base.m_ScreenWidth / imageSizeRatio) > base.m_ScreenHeight) {
318
                    imageWidth = base.m_ScreenHeight * imageSizeRatio * base.m_Options.imageSize;
319
                    imageHeight = base.m_ScreenHeight * base.m_Options.imageSize;
320
                } else { // Width of image is too big to fit in viewport
321
                    imageWidth = base.m_ScreenWidth * base.m_Options.imageSize;
322
                    imageHeight = base.m_ScreenWidth / imageSizeRatio * base.m_Options.imageSize;
323
                }
324

    
325
                if (imageWidth > thisImageWidth) {
326
                    imageWidth = thisImageWidth;
327
                }
328

    
329
                if (imageHeight > thisImageHeight) {
330
                    imageHeight = thisImageHeight;
331
                }
332

    
333
                // Set style attributes.
334
                p_Element.style.top = ((base.m_ScreenHeight - imageHeight) / 2) + "px";
335
                p_Element.style.left = ((base.m_ScreenWidth - imageWidth) / 2) + "px";
336
                p_Element.style.width = Math.floor(imageWidth) + "px";
337
                p_Element.style.height = Math.floor(imageHeight) + "px";
338

    
339
                if (!p_Resize) {
340
                    setTimeout(function () {
341
                        if (base.browser.isHardwareAccelerated) {
342
                            p_Element.style.opacity = 1;
343
                            p_Element.style[base.getcss3prop("transform")] = "translateX(0px)";
344
                        } else {
345
                            var toOpacity = 1;
346

    
347
                            base.animate({
348
                                delay: 16,
349
                                duration: base.m_Options.animationSpeed,
350
                                delta: base.linear,
351
                                step: function (delta) {
352
                                    p_Element.style.opacity = (toOpacity * delta);
353
                                    p_Element.style.filter = "alpha(opacity=" + ((toOpacity * delta) * 100 ) + ")"; 
354
                                }
355
                            });
356
                        }
357

    
358
                        base.m_InProgress = FALSE;
359
                        base.m_InstalledImageBox = true;
360

    
361
                        if (base.isFunction(base.m_Options.onImageLoadEnd())) {
362
                            base.m_Options.onImageLoadEnd(p_Element);
363
                        }
364
                    }, 100);
365
                }
366
            };
367

    
368
            // Must be last because otherwise onload function won't be load.
369
            temporaryImageObject.src = base.m_ImageSource;
370
        },
371

    
372
        removeImageElement: function () {
373
            var base = this;
374

    
375
            if (!base.m_CurrentImageElement) {
376
                return;
377
            }
378

    
379
            if (base.isFunction(base.m_Options.onEnd())) {
380
                base.m_Options.onEnd();
381
            }
382

    
383
            if (base.m_InProgress) {
384
                if (base.isFunction(base.m_Options.onImageLoadEnd())) {
385
                    base.m_Options.onImageLoadEnd(p_Element);
386
                }
387
            }
388

    
389
            if (base.browser.isHardwareAccelerated) {
390
                base.m_CurrentImageElement.style.opacity = 0;
391
                base.m_CurrentImageElement.style.transition = "opacity 250ms ease";
392
            } else {
393
                var toOpacity = 0;
394

    
395
                base.animate({
396
                    delay: 16,
397
                    duration: 250,
398
                    delta: base.linear,
399
                    step: function (delta) {
400
                        base.m_CurrentImageElement.style.opacity = (toOpacity * delta);
401
                        base.m_CurrentImageElement.style.filter = "alpha(opacity=" + ((toOpacity * delta) * 100 ) + ")"; // IE 8
402
                    }
403
                });
404
            }
405

    
406
            setTimeout(function () {
407
                if (base.m_CurrentImageElement) {
408
                    base.m_CurrentImageElement.parentNode.removeChild(base.m_CurrentImageElement);
409
                }
410

    
411
                base.m_CurrentImageElement = FALSE;
412
                base.m_InstalledImageBox = FALSE;
413
            }, base.browser.isHardwareAccelerated ? 250 : 350); // Duo animate delay, add 100ms.
414
        },
415

    
416
        isFunction: function (p_Function) {
417
            return !!(p_Function && p_Function.constructor && p_Function.call && p_Function.apply);
418
        },
419

    
420
        addEvent: function (p_Element, p_Events, p_Callback) {
421
            var i, j;
422
            p_Events = p_Events.split(" ");
423

    
424
            if (isEventListener) {
425
                if ((p_Element && !(p_Element instanceof Array && !p_Element.length)) && (p_Element.length !== 0) || p_Element === window) {
426
                    for (i = 0; i < p_Events.length; i++) {
427
                        p_Element[ADDEVENTLISTENER](p_Events[i], p_Callback, FALSE);
428
                    }
429
                } else if (p_Element && p_Element[0] !== "undefined") {
430
                    for (i = 0; i < p_Element.length; i++) {
431
                        for (j = 0; j < p_Events.length; j++) {
432
                            p_Element[i][ADDEVENTLISTENER](p_Events[j], p_Callback, FALSE);
433
                        }
434
                    }
435
                }
436
            } else {
437
                for (i = 0; i < p_Events.length; i++) {
438
                    if (p_Events[i].indexOf("keydown") !== -1) {
439
                        document[ATTACHEVENT]("on" + p_Events[i], p_Callback);
440
                    } else {
441
                        if ((p_Element && !(p_Element instanceof Array && !p_Element.length)) && (p_Element.length !== 0) || p_Element === window) {
442
                            p_Element[ATTACHEVENT]("on" + p_Events[i], p_Callback);
443
                        } else if (p_Element && p_Element[0] !== "undefined") {
444
                            for (j = 0; j < p_Element.length; j++) {
445
                                p_Element[j][ATTACHEVENT](p_Events[i], p_Callback);
446
                            }
447
                        }
448
                    }
449
                }
450
            }
451
        },
452

    
453
        animate: function (p_Options) {
454
            var base = this,
455
                start = new Date();
456

    
457
            var id = setInterval(function () {
458
                var timePassed = new Date() - start,
459
                    progress = timePassed / p_Options.duration;
460

    
461
                if (progress > 1) {
462
                    progress = 1;
463
                }
464
    
465
                var delta = p_Options.delta(progress);
466
                p_Options.step(delta);
467
    
468
                if (progress == 1) {
469
                    base.m_AnimateDone = true;
470
                    clearInterval(id);
471
                }
472
            }, p_Options.delay || 10);
473
        },
474

    
475
        linear: function (progress) {
476
            return progress;
477
        },
478

    
479
        getcss3prop: function (p_CSSProp) {
480
            var vendors = ["", "-moz-", "-webkit-", "-o-", "-ms-", "-khtml-"],
481
                camelCase = function (str) {
482
                    return str.replace(/\-([a-z])/gi, function (match, p1) {
483
                        return p1.toUpperCase(); 
484
                    });
485
                };
486

    
487
            for (var i = 0; i < vendors.length; i++) {
488
                var css3propcamel = camelCase(vendors[i] + p_CSSProp)
489

    
490
                if (css3propcamel.substr(0,2) == "Ms") {
491
                    css3propcamel = "m" + css3propcamel.substr(1); 
492
                }
493

    
494
                if (css3propcamel in docElem.style) {
495
                    return css3propcamel;
496
                }
497
            }
498

    
499
            return "undefined";
500
        }
501
    };
502

    
503
    SimplBox.options = {
504
        imageElementId: "simplbox", 
505

    
506
        fadeInDistance: 100,
507
        animationSpeed: 350,
508
        imageSize: 0.8,
509

    
510
        quitOnImageClick: true,
511
        quitOnDocumentClick: true,
512
        enableKeyboard: true,
513

    
514
        onImageLoadStart: function () {},
515
        onImageLoadEnd: function () {},
516
        onStart: function () {},
517
        onEnd: function () {}
518
    };
519

    
520
    window.SimplBox = SimplBox;
521
})(window, document); 
(22-22/24)