Zoom & other Page Actions in Web Panels



  • What?
    A veritable contubernium of people have voted for a request to allow Web Panel Zooming. Here's a mod to enable that, and more.

    Preview
    0_1523554011199_2018-04-12_18-26-01.gif

    Installation
    As a custom.js mod:

    /*
    * Panel Actions (A Mod for Vivaldi)
    * LonM.vivaldi.net
    * No Copyright Reserved
    */
    
    (function panel_actions(){
        "use strict";
    
        /*
        Dictionary of additional actions.
        They will be added to the toolbar in the order specified below.
        Enabled: Whether the action is enabled
        Content Script: Wheter this action should execute on the web page or in the browser
        Script: A function(){}
        Display: The innerHTML of the toolbar button
        Display Class: One or more classes to give the button
        */
        const ACTIONS = {
    
            zoom_out: { /* Decrease zoom*/
                enabled: true,
                content_script: false,
                script: function(){
                    const webview = document.querySelector(".panel.webpanel.visible webview");
                    webview.getZoom(current => {
                        webview.setZoom(current -= 0.1, update_zoom_label);
                    });
                },
                display: `-`,
                display_class: `zoom-out`
            },
    
            zoom_reset: { /* Set zoom to 100% */
                enabled: true,
                content_script: false,
                script: function(){
                    const webview = document.querySelector(".panel.webpanel.visible webview");
                    webview.setZoom(1, update_zoom_label);
                },
                display: `100%`,
                display_class: `zoom-reset`
            },
    
            zoom_in: { /* Increase zoom*/
                enabled: true,
                content_script: false,
                script: function(){
                    const webview = document.querySelector(".panel.webpanel.visible webview");
                    webview.getZoom(current => {
                        webview.setZoom(current += 0.1, update_zoom_label);
                    });
                },
                display: `+`,
                display_class: `zoom-in`
            },
    
            invert: {/* Invert the colours on the page */
                enabled: true,
                content_script: true,
                script: function(){
                    const style_element = document.createElement('style');
                    style_element.innerHTML = `
                        html { background-color: white;}
                        body.inverted {filter: invert(1) hue-rotate(180deg);}
                        body.inverted img,
                        body.inverted video {filter: invert(1) hue-rotate(180deg);}`;
                    document.body.appendChild(style_element);
                    document.body.classList.toggle("inverted");
                }, /* eye icon stolen from vivaldi */
                display: `<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" height="10px" width="10px">
                    <path fill-rule="evenodd" d="M8 13c3.636 0 6.764-2.067 8-5-1.236-2.933-4.364-5-8-5s-6.764 2.067-8 5c1.236 3.035 4.364 5 8 5zm0-1c2.209 0 4-1.791 4-4s-1.791-4-4-4-4 1.791-4 4 1.791 4 4 4zm0-2c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z"></path>
                </svg>`,
                display_class: "panel-action-invert"
            },
    
            template: { /* */
                enabled: false,
                content_script: false,
                script: function(){
    
                },
                display: ``,
                display_class: ``
            },
        };
    
        /* Observe chages to the active panel */
        const PANEL_CHANGE_OBSERVER = new MutationObserver(mutationrecords => {
            const panel = document.querySelector(".panel.webpanel.visible");
            if(panel){
                add_panel_controls(panel);
            }
        });
    
        /* Wait until the panel is ready before activating the mod */
        function begin_observe(){
            const panels = document.querySelector("#panels");
            if(panels){
                PANEL_CHANGE_OBSERVER.observe(panels, {attributes: true, subtree: true});
            } else { setTimeout(begin_observe, 500); }
        }
    
        /* Update the label with the correct zoom percentage */
        function update_zoom_label(){
            const webview = document.querySelector(".panel.webpanel.visible webview");
            const panelZoom = document.querySelector(".panel.webpanel.visible .zoom-reset");
            if(!panelZoom){
                return;
            }
            webview.getZoom(current => {
                panelZoom.innerHTML = Math.floor(current * 100) + "%";
            });
        }
    
        /* Injects a content script onto the page */
        function content_script(scriptMethod){
            const webview = document.querySelector("div.panel.webpanel.visible webview");
            const scriptText = "("+scriptMethod+")()";
            webview.executeScript({code: scriptText});
        }
    
        /* Create a panel header toolbar button */
        function panel_mod_button(className, event, display){
            const newBtn = document.createElement("button");
            newBtn.className = className+" button-toolbar-small mod-panel-action";
            newBtn.innerHTML = display;
            newBtn.addEventListener("click", event);
            return newBtn;
        }
    
        /* Create the control buttons for the actions and add them to the specified header */
        function add_panel_controls(panel){
            const alreadyAdded = panel.querySelector("footer");
            if(alreadyAdded){return;}
            const footer = document.createElement("footer");
            for(const key in ACTIONS){
                const action = ACTIONS[key];
                if(action.enabled){
                    const newButton = action.content_script ?
                        panel_mod_button(action.display_class, event => {content_script(action.script);}, action.display) :
                        panel_mod_button(action.display_class, action.script, action.display);
                    footer.appendChild(newButton);
                }
            }
            panel.appendChild(footer);
        }
    
        /* Start 500ms after the browser is opened */
        setTimeout(begin_observe, 500);
    })();
    
    

    Customizing
    You can choose which buttons to show by going through the ACTIONS dictionary and setting the various action enabled properties to true or false. You can also add your own using the template provided in the code.

    Contribution
    If anyone has interesting things beyond zooming that can be done with the panel webview, please share it!

    Updates

    • v2: Use a footer instead of the header
    • v1: Initial Release


  • Cool :P
    Thanks LonM ^^



  • Updated to now use a panel-specific footer instead of a header, to better match how the main window works. Also, can now have an arbitrary number of items in the footer.



  • I love this, this should probably are native in Vivaldi in the future. <3



  • @LonM Can we save a specific zoom value per url in webpanel?
    Would be a really useful addition.



  • @hadden89 Interesting Idea. Worth looking in to. Saving the zoom value is easily doable. The difficulty is in figuring out how to make sure the panel is at the correct zoom level.


  • Vivaldi Ambassador

    @lonm Just found this today, and love it. Great Job!



  • @lonm

    I've made some attempts to get this working, right down to rewriting the whole mod into a nicer fashion. However, there seems to be some odd behaviour. The zoom "mode" of webviews is such that the zoom level always wants to reset. I could switch it out for a different mode but then it would start affecting webviews in the main tabs, which would be undesirable.



  • @hadden89 There is a meta tag:
    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />

    May be in can be injected with js, but no idea ...



Looks like your connection to Vivaldi Forum was lost, please wait while we try to reconnect.