(function($) { var special = $.event.special, uid1 = 'D' + (+new Date()), uid2 = 'D' + (+new Date() + 1); special.scrollstart = { setup: function() { var timer, handler = function(evt) { var _self = this, _args = arguments; if (timer) { clearTimeout(timer); } else { evt.type = 'scrollstart'; $.event.dispatch.apply(_self, _args); } timer = setTimeout(function() { timer = null; }, special.scrollstop.latency); }; $(this).bind('scroll', handler).data(uid1, handler); }, teardown: function() { $(this).unbind('scroll', $(this).data(uid1)); } }; special.scrollstop = { latency: 250, setup: function() { var timer, handler = function(evt) { var _self = this, _args = arguments; if (timer) { clearTimeout(timer); } timer = setTimeout(function() { timer = null; evt.type = 'scrollstop'; $.event.dispatch.apply(_self, _args); }, special.scrollstop.latency); }; $(this).bind('scroll', handler).data(uid2, handler); }, teardown: function() { $(this).unbind('scroll', $(this).data(uid2)); } }; })(jQuery); (function($, window, undefined){ "use strict"; // munge var FUNC = 'function', STRING = 'string', SPINNER_URL = '/images/ajax-loader.gif'; /** * * @param {String} str * @param {Number} count * @param {String} char * @returns {String} */ function StringPadLeft(str, count, char) { var paddingStr = ""; var lenDiff = count - str.length; if (lenDiff > 0) { for (var i = 0; i < lenDiff; i++) paddingStr += char; str = paddingStr.concat(str); } return str; } window.InitViewerToolbar = function($toolbar, viewer) { $toolbar.on('click', '#end', function() { viewer.scrollToPage(viewer.getPageCount()); }).on('click', '#begin', function() { viewer.scrollToPage(1); }).on('click', '#next', function() { viewer.scrollToPage(viewer.getPageNr() + 1); }).on('click', '#previous', function() { viewer.scrollToPage(viewer.getPageNr() - 1); }).on('click', '#zoomin', function() { viewer.zoomIn(); }).on('click', '#zoomout', function() { viewer.zoomOut(); }).on('change', '#zoomselect', function() { var val = this.value; switch (val) { case "page-fit": viewer.fitPage(); break; case "page-width": viewer.fitWidth(); break; default: val = parseFloat(val); if (val) viewer.setScale(val); break; } }).on('click', '#fullscreen', function() { $(document).trigger('fullscreen'); }).on('keypress', '#toolbar-field-page-num', function(e) { if (e.which === 13) { viewer.scrollToPage(parseInt(this.value)); } }).on('blur', '#toolbar-field-page-num', function() { $(this).val(viewer.getPageNr()); }); $('#viewer').bind('pageChange', function() { $('#toolbar-field-page-num').val(viewer.getPageNr()); }).bind('pageAdd', function() { $('#toolbar-total-pages').text(viewer.getPageCount()); }).bind('scaleChange', function(ev, scale) { var percentage = Math.round(scale * 100); var $select = $('#zoomselect'); var custom = $select.find('option[value="custom"]').text(percentage + '%'); $select.val('').val('custom'); $toolbar.find('.fakeselect .select-title').text(custom.text()); }); }; var Viewer = window.Viewer = function(el, options) { /* -------- PRIVATE VARIABLES -------- */ var MIN_SCALE = 0.25, MAX_SCALE = 3; // vars for caching dom elements var _this, $_el, _viewport, $_viewport, $_container, $_dragLayer; // vars for viewer // vars for page var _pageCount = 0, _currentPage = 0, _maxScrollPositionY = 0, _maxPageWidth; // pages cache var _pages = [], _displayedPages = []; var _dragData = {startX:0, startY:0, scrollX: 0, scrollY: 0}; var _dragging = false; var _firstPageResize = true; var _ignoreScroll = false; var _zoomLevels = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3]; var _scale = 1.0; var _options = { imagePathPrefix: '/files/processed/', imageExt: '.png', imageNamePrefix: 'i', pageBorderWidth: 1, pageWidth: 0, pageHeight: 0, pageBreakHeight: 20, usePageCounter: true }; /* -------- PUBLIC SCOPE -------- */ Viewer.prototype = { /** * Initializes Viewer and draws elements * @param {DOMElement/String} el viewer placeholder * @param {Object} options viewer's options */ init: function(el, options) { if (typeof el === STRING) { return Viewer(document.getElementById(el), options); } else if (!el.nodeType) { return undefined; } _this = this; $_el = $(el); $.extend(_options, options); _maxPageWidth = _options.pageWidth + 2 * _options.pageBorderWidth; _initialize(); return _this; }, /** * Modify options */ setOptions: function(options) { $.extend(_options, options); }, /** * Scroll to the specified page number */ scrollToPage: function(pageNum) { pageNum = parseInt(pageNum); if (pageNum <= 0 || pageNum > _pageCount || !$_viewport) return; // var scrollLeft = parseInt((_viewport.scrollWidth - _viewport.clientWidth) / 2); _setScrollPosition(_pages[pageNum].top); _currentPage = pageNum; _updateViewport(); _invokePageChange(); }, /** * Appends to the viewer specified number of pages */ appendPages: function(count) { _appendPages(count); _updateViewport(); _updateCurrentPage(); _invokePageAdd(); }, /** * Returns total number of pages */ getPageCount: function() { return _pageCount; }, getPageNr: function() { return _currentPage; }, getPages: function() { return _pages; }, zoomIn: function() { var scale; for (var i = 0, len = _zoomLevels.length; i < len; i++) { if (_scale < _zoomLevels[i]) { scale = _zoomLevels[i]; scale = _limitScale(scale); if (scale != _scale) _changeScale(scale); return; } } }, zoomOut: function() { var scale; for (var i = _zoomLevels.length - 1; i >= 0; i--) { if (_scale > _zoomLevels[i]) { scale = _zoomLevels[i]; scale = _limitScale(scale); if (scale != _scale) _changeScale(scale); return; } } }, setScale: function(scale) { scale = _limitScale(scale); if (scale != _scale) _changeScale(scale); }, fitWidth: function() { var p = _currentPage; var page = _pages[_currentPage]; var scaleTo = ($_dragLayer.width() - 20) * _scale / (page.width + page.borderSize * 2); scaleTo = _limitScale(scaleTo); if (scaleTo != _scale) _changeScale(scaleTo); _setScrollPosition(null, page.left); this.scrollToPage(p); }, fitPage: function() { var page = _pages[_currentPage]; var scaleTo = $_viewport.height() * _scale / (page.height+ page.borderSize * 2); scaleTo = _limitScale(scaleTo); if (scaleTo != _scale) _changeScale(scaleTo); }, /** * Recalculates viewport's and container's sizes. * Use when viewer is started as hidden */ redraw: function() { _redrawViewer(); _redrawDisplayedPages(); }, toggleFullscreen: function() { $_el.toggleClass('fullscreen'); var top = _viewport.scrollTop, left = _viewport.scrollLeft; if ($_el.is('.fullscreen')) { $_viewport.data('height', $_viewport.height()); $_viewport.css('height', '100%'); } else { var h = $_viewport.data('height'); $_viewport.css('height', h); } _setScrollPosition(top, left); } }; return Viewer.prototype.init(el, options); /* -------- PRIVATE SCOPE -------- */ function _invokePageChange() { $_el.trigger('pageChange'); } function _invokePageAdd() { $_el.trigger('pageAdd'); } function _invokeScaleChange() { $_el.trigger('scaleChange', [_scale]); } /** * Redraws pages that are being displayed in the viewport */ function _redrawDisplayedPages() { var page, i, len; for (i = 0, len = _displayedPages.length; i < len; i++) { page = _displayedPages[i]; page.updatePosition(); } } /** * Recalculates all pages top offset */ function _recalculatePageTop() { for (var i = 2, length = _pages.length; i < length; i++) { var prevPage = _pages[i - 1]; _pages[i].top = prevPage.top + prevPage.height + 2 * prevPage.borderSize + _options.pageBreakHeight; } } /** * Set viewport scroll position to the specified values * @param {Number} top * @param {Number} left */ function _setScrollPosition(top, left) { // modify scoll position if (top !== undefined) _viewport.scrollTop = top; if (left !== undefined) _viewport.scrollLeft = left; } /** * Set pages Container size to the specified values * @param {Number} width * @param {Number} height */ function _setContainerSize(width, height) { var offsetLeft = ($_container.parent().width() - width) / 2; if (offsetLeft < 0) offsetLeft = 0; $_container.parent().css({width : width, height: height}); } function _limitScale(scale) { scale = Math.max(MIN_SCALE, scale); scale = Math.min(MAX_SCALE, scale); return scale; } /** * Changes Zoom and redraws viewer * @param {Number} scale scale */ function _changeScale(scale) { if (scale == _scale) return; // dummy check // set current zoom level _scale = scale; var page = null; var topOffset = 0, maxW = 0; var difScroll = _viewport.scrollTop - _pages[_currentPage].top; // iterate through all pages for (var i = 1, len = _pages.length; i < len; i++) { page = _pages[i]; page.setScale(scale); page.top = topOffset; // calculate offset for the next page topOffset += page.height + _options.pageBreakHeight; // recalculate max page's width maxW = Math.max(page.width, maxW); } _maxPageWidth = maxW; _maxScrollPositionY = topOffset; _setContainerSize(_maxPageWidth, _maxScrollPositionY); // redraw pages with images _redrawDisplayedPages(); var scrollTop = _pages[_currentPage].top + difScroll * scale; _setScrollPosition(scrollTop); _updateViewport(); _invokeScaleChange(); } /** * Appends to the end specified number of pages, updates container's size * @param {Number} count number of pages to append. */ function _appendPages(count) { count = parseInt(count); var maxW = 0; // set new pages to be loaded in memory when are requested for (var i = 1; i <= count; i++) { var pageNr = _pageCount + i; // create new Page Object // _pageCount + i, _options.pageWidth, _options.pageHeight var url = _options.imagePathPrefix; url += _options.imageNamePrefix; if (_options.usePageCounter) url += '_' + StringPadLeft(pageNr.toString(), 2, '0'); url += _options.imageExt; var pageOpts = { num: pageNr, width: _options.pageWidth + _options.pageBorderSize * 2, height: _options.pageHeight + _options.pageBorderSize * 2, imageUrl : url, top: (_options.pageHeight + _options.pageBreakHeight) * (pageNr - 1) }; var page = new Viewer.Page(pageOpts); _pages[pageNr] = page; maxW = Math.max(page.width, maxW); } _pageCount += count; _maxScrollPositionY += count * (_options.pageHeight + _options.pageBreakHeight); _maxPageWidth = maxW; _setContainerSize(_maxPageWidth, _maxScrollPositionY); } /** * Fullfill viewport with pages that ca be displayed and loads them */ function _updateViewport() { var arr = [], x = 0, i, len, page, range = _getRangeOfVisiblePages(); // remove page DOM elements that are out of the range for (i = 0, len = _displayedPages.length; i < len; i++) { page = _displayedPages[i]; if (page.num < range.min || page.num > range.max) { arr[x++] = i; page.remove(); } } // clean up array of displayed pages for (i = 0, len = arr.length; i < len; i++) { _displayedPages.splice(arr[i] - i, 1); } // append new page elements to the container and load those pages for (i = Math.min(range.max, _pageCount); i >= range.min && i > 0; i--) { page = _pages[i]; if (!page.isRendered) { page.load(); page.el.appendTo($_container); _displayedPages.push(page); } } } /** * Returns the range of visible pages with respect to vieport and sentinel size * @return {Object} min and max */ function _getRangeOfVisiblePages() { return { min: _getVisiblePage(_viewport.scrollTop - _options.sentinelSize), max: _getVisiblePage(_viewport.scrollTop + $_viewport.height() + _options.sentinelSize) }; } /** * Returns the visible page with respect to viewport's scroll position * @param {Number} scrollTop optional parameter */ function _getVisiblePage(scrollTop) { if (_pageCount == 0) return 0; if (scrollTop === undefined) scrollTop = _viewport.scrollTop; if (scrollTop <= 0) return 1; if (scrollTop > _viewport.scrollHeight) return _pageCount; // presume that all pages have equal height var pageNr = scrollTop / (_options.pageHeight + _options.pageBreakHeight + 2 * _options.pageBorderSize); // make sure that pageNumber is not out of range pageNr = Math.max(1, Math.ceil(pageNr) + 1); pageNr = Math.min(_pageCount, pageNr); var pageNrChangeOffset = 130; // adjust pageNr to known top offset while (pageNr + 1 < _pageCount && _pages[pageNr].top + pageNrChangeOffset < scrollTop) pageNr++; while (pageNr - 1 > 0 && _pages[pageNr].top - pageNrChangeOffset > scrollTop) pageNr--; return pageNr; } function _startDrag(e) { if (!_dragging) { _dragging = true; e.preventDefault(); // store mouse down data _dragData.startX = e.pageX; _dragData.startY = e.pageY; _dragData.scrollX = _viewport.scrollLeft; _dragData.scrollY = _viewport.scrollTop; $_dragLayer.addClass('dragging'); // "dragging & iframe" Fix $("iframe").not('iframe', $_viewport).each(function() { $('
') .css({ width: this.offsetWidth+"px", height: this.offsetHeight+"px", position: "absolute", opacity: "0.001", zIndex: 1000 }).css($(this).offset()).appendTo("body"); }); } } function _stopDrag(e) { if (_dragging) { _dragging = false; e.preventDefault(); $_dragLayer.removeClass('dragging'); // "dragging & iframe" Fix $('.drag-iframeFix').remove(); $_viewport.trigger('scroll'); } } function _drag(e) { if (_dragging) { e.preventDefault(); var top = _dragData.startY - e.pageY + _dragData.scrollY; var left = _dragData.startX - e.pageX + _dragData.scrollX; _setScrollPosition(top, left); } } /** * Redraws Viewer by calculating width / height of the viewport and container. * Useful when viewer is started as hidden */ function _redrawViewer() { var viewportHeight = $_el.height(); $_viewport.css({height: viewportHeight}); } function _OnPageResized(page, oldSize) { if (_firstPageResize) { _firstPageResize = false; for (var i = 0, len = _pages.length; i < len; i++) if (_pages[i] && _pages[i].num != page.num) _pages[i].setSize(page.width, page.height); _recalculatePageTop(); var lastPage = _pages[_pages.length - 1]; _maxScrollPositionY = lastPage.top + lastPage.height; } else { var difY = page.height - oldSize.height; _maxScrollPositionY += difY; } _maxPageWidth = Math.max(page.width, _maxPageWidth); _setContainerSize(_maxPageWidth, _maxScrollPositionY); var currentPage, diffTopScroll; if (_currentPage) { currentPage = _pages[_currentPage]; diffTopScroll = currentPage.top - _viewport.scrollTop; } _recalculatePageTop(); _redrawDisplayedPages(); if (diffTopScroll !== undefined) { // var scrollLeft = parseInt((_viewport.scrollWidth - _viewport.clientWidth) / 2); var scrollTop = currentPage.top - diffTopScroll * _scale; _setScrollPosition(scrollTop);//, scrollLeft); } } function _updateCurrentPage() { var pageNr = _getVisiblePage(); if (pageNr && _currentPage != pageNr) { _currentPage = pageNr; _invokePageChange(); } } /** * Fills Viewer DOM element with required elements, caches some DOM elements * for better performance */ function _initialize() { var html = ' '; $_el.get(0).innerHTML = html; $_viewport = $_el.find(".viewport"); _viewport = $_viewport[0]; $_container = $_viewport.find(".pages-container"); _redrawViewer(); $_viewport.bind('scroll', function(e) { _updateViewport(); _updateCurrentPage(); }) .bind('scrollstop', function() { var page = _pages[_currentPage]; if (page && page.el) { // $_viewport.animate({scrollLeft: page.el.offset().left}, 300); } }).bind('scrollstart', function() { $_viewport.stop(); }); $_dragLayer = $_el.find('.drag-listener').mousedown(_startDrag); $(document).bind('mouseup', _stopDrag).bind('mousemove', _drag); $_container.on('pageResized', function(e, page, oldSize) { _OnPageResized(page, oldSize); }); } }; Viewer.Page = (function() { function Page(options) { this.bitmap = new Viewer.Bitmap(); // this.iframe = new Viewer.Iframe(); this.num = 0; this.top = 0; this.width = 0; this.height = 0; this.imageUrl = ''; this.isRendered = false; this.borderSize = 1; this.scale = 1; // this.preloaderType = 'iframe'; this.$iframe = undefined; $.extend(this, options); } Page.prototype = { load: function() { if (this.isRendered) return; var _this = this; _this.el = $(_this.toHtml()); _this.isRendered = true; if (_this.preloaderType == 'iframe') { var html = ''; this.$iframe = $(html); this.$iframe.load(function() { }); this.$iframe.appendTo(_this.el.find('.page')); return; } _this.bitmap.load(_this.imageUrl, function(img) { _this.bitmap.height = _this.bitmap.image.height * _this.scale; _this.bitmap.width = _this.bitmap.image.width * _this.scale; var newW = _this.bitmap.width + 2 * _this.borderSize; var newH = _this.bitmap.height + 2 * _this.borderSize; _this.el[0].innerHTML = '