Source: modules/print.js

/**
 * Printer Handler. Will recognise if CTRL+P is pressed / the menu has been used
 * It is important to use the internal functions to trigger printing or it can not
 * be assured that all pages have been laoded and the report is being printed with
 * all of its content
 * 
 * @namespace
 */

/* global debug */
/* global updateInputValue */
/* global MenubarActions */
/* global getId */
/* global LoadingView */
/* global loadpage */
/* global _pageCache */
/* global getTranslation */
/* global documentExport */
/* global generator */

var printListener = {

    printMenu: false,
    
    didShowError: false,
    
    printingFormats : [],
    
    isOnline: true,
    
    /**
     * Attach the print dialog to a button
     * @param {object}  button to use as base for the print menu
     * @param {object}  element to prepend the new button to
     * @param {boolean} isOnline if the browser is online. Otherwise: no need for extra print methods.
     * @function
     */
    buildPrint: function (parentButton, insertBefore, isOnline) {

        var menu = new generator.submenuContainer(parentButton);
        menu.menuContainer.addClassName('__complexSubmenu');
        printListener.printMenu = menu;
        printListener.isOnline = isOnline;

        if (typeof insertBefore != 'undefined') {
            getId('__menuBar').insertBefore(menu.submenu, insertBefore);
        } else {
            getId('__menuBar').appendChild(menu.submenu);
        }
    },

    /**
     * Show the print dialog if there are more than one handler. If exactly one: execute
     * @param {object} event the triggered event
     */
    showPrint: function () {

        var isOpen = printListener.printMenu.menuContainer.hasClassName('open');
        if (!isOpen) {
            
            if ( !printListener.isOnline ) {
                // If the viewer is offline, we only need the browser printing.
                printListener.resetPrintingFormats();
            }
            
            // open Elements will be closed but not the other way arround.
            if ( printListener.printingFormats.length < 1 ) { return true; }
            else if ( printListener.printingFormats.length == 1 ) { setTimeout( printListener.printingFormats[0].executeFunction ); return true; }

            printListener.printMenu.clear();
            printListener.printingFormats.forEach(function (value) {
                var elem = document.createElement('div');
                elem.appendChild(document.createTextNode(getTranslation(value.printerName)));
                elem.addClassName('right');
                generator.addDescription(elem, value.description);
                printListener.printMenu.addEntry(elem, function() {
                    value.executeFunction.call();
                    printListener.showPrint.call();
                }, false, false, true);
            });
        }
        
        printListener.printMenu.menuContainer.addRemoveClass('open', !isOpen);
        printListener.printMenu.parentElement.setIsCurrent(!isOpen);
		return true;
    },

    /**
     * Add a printing format to the print listener
     * @param {string}   name            the name of the listener (can be a translation key)
     * @param {string}   description     the description of the listener (can be a translation key)
     * @param {function} executeFunction the function to execute when this print format is selected
     */
    addFormat: function(name, description, executeFunction) {
        var format = {
            // Default print function
            printerName: name,
            description: description,
            executeFunction: executeFunction
        };
        
        // Check for existing entries of same name
        var existing = printListener.printingFormats.filter(function(element){
            return (element.printerName == format.printerName );
        });
        
        // Add only once
        if ( existing.length === 0 ) {
            printListener.printingFormats.push( format );
        }
    },
    
    /**
     * Clears the printing formats list and adds the default browser printing.
     */
    resetPrintingFormats: function() {
        // Add the default print listener
        printListener.printingFormats = [];
        printListener.addFormat( 'print.browserPrint', 'menuBar.print.hint', browserPrint.print);
    },

    /**
     * Do something before printing is switched on (media query)
     * @param {object} e the event
     */
    beforePrint: function (_e) {
		debug("Changed to printing");
	},

    /**
     * Do something after printing is switched back (media query)
     * @param {object} e the event
     */
    afterPrint: function (_e) {
		debug("Changed back to normal");
	},

    handleMediaChange: function (mql) {
        if (mql.matches) {
            printListener.beforePrint(mql);
        } else {
            printListener.afterPrint(mql);
        }
    }
};

/**
 * Default browser printing handler. Will trigger the multipage to be loaded.
 * After that the browsers printing capabilities will be used.
 * 
 * If a threshhold (99 pages by default) is exceeded, a PDF will be generated instead
 * 
 * @namespace
 */ 
var browserPrint = {

    pageCount: 1,

    maxPagesToPrint: 99,

    timeoutHandler: null,

	/**
	 * Trigger the printing using command and window print.
 	 */
	printInternal : function() {
		window.fireEvent('htmlviewer.print');
	},

    /**
     * This should be the only way to trigger printing
     */
    print: function () {

        var menu = MenubarActions();
        if (browserPrint.maxPagesToPrint < parseInt(menu.maxPages) && documentExport ) {
            return documentExport.printPDF();
        }

        // Check for all Pages to be loaded (synchronously!)
        browserPrint.switchMode(function (finishFunction) {
            debug("loaded");

            var loadPrintPages = function () {
                browserPrint.pageCount = menu.maxPages;

                var checkCountOrLoad = function () {
                    if (--browserPrint.pageCount > 0) {
                        loadNextPage(browserPrint.pageCount);
                    } else {
						clearTimeout( browserPrint.timeoutHandler );
                        browserPrint.timeoutHandler = setTimeout(function () {
	
	                        // Reset
	                        browserPrint.pageCount = 1;
                            (new LoadingView()).hide();

							setTimeout(browserPrint.printInternal);
                        }, 1000);
                    }
                    (new LoadingView()).hide();
                };

                var loadNextPage = function (nextPage) {

                    (new LoadingView()).show(getTranslation("print.browserPrint") + " " + (menu.maxPages - nextPage + 1) + "/" + menu.maxPages);
                    var loadThePage = new loadpage(nextPage, null, null, true);

                    loadThePage.pageFinished = function (_wasFullyLoaded) {

                        // wait until all images are loaded.
                        debug("Page " + nextPage + " is Finished, now wait for all images to be ready.");

                        /*if (wasFullyLoaded) { // 2018-06-27 - redundant?!
                            debug("This page had been loaded alread.");
                            // checkCountOrLoad();
                            return;
                        }*/

                        var page = _pageCache.getPage(loadThePage.ajax).pageCurrent();
                        var images = getId(page).getElementsByTagName('img');

                        var wait = function () {

                            for(var i = 0; i < images.length; i++) {
                                if (!images[i].complete) {
                                    setTimeout(wait, 200);
                                    return;
                                }
                            }

                            checkCountOrLoad();
                        };

                        wait();
                    };

                    debug("Starting to Load Page " + nextPage + ".");
                    loadThePage.startLoading();

                };

                loadNextPage(browserPrint.pageCount);
            };

            if (typeof finishFunction == 'function') {
                finishFunction(loadPrintPages);
            } else {
                loadPrintPages();
            }
        });

        (new LoadingView()).show(); 
    },

    switchMode: function (finishFunction) {

        if (!getId('endless-mode').isCurrent()) {
            (new MenubarActions()).endlessMode(true, finishFunction);
        } else if (typeof finishFunction == 'function') {
            finishFunction();
        }
    }
};

window.addEvent( 'htmlviewer.print', function(){
	var didPrint = false;
	try {
		didPrint = document.execCommand('print', true, false );
	} catch(e){
		debug(e);
	}
	didPrint || window.print();
});

printListener.resetPrintingFormats();

$.Events.addInitEvent(function () {
    try {
        var mpl = window.matchMedia("print");
        mpl.addListener(updateInputValue(printListener.handleMediaChange, 250, "mediaChange"));
    } catch (e) {
        debug("matchMedia not available " + e);
    }
});