Web Page Source Annotation Tool

后端 未结 2 1174
别跟我提以往
别跟我提以往 2020-12-11 07:51
  • Carnegie Mellon University
  • 5000 Forbes Avenue, Pittsburgh, PA 15213
相关标签:
2条回答
  • 2020-12-11 07:57

    A chrome extension can automate this functionality, you can further extend this skeleton functionality for all possibilities.

    The following skeleton adds a context menu for right click event of mouse for selection event1

    A Menu is added to chrome browser and is activated when a selection is made as shown in this screen shot

    Context Menu Appearance after selection of text

    enter image description here

    1- Selection context event fires when a selection of text is done by mouse click

    Demonstration

    Look at jsfiddle, after installation of chrome extension it annotates with user-defined tags

    HTML Code Before

    enter image description here

    HTML Code After Selection

    Select a <li> text from output console of jsfiddle through context menu added to chrome browser,you can see DOM is also changed!

    enter image description here

    Code Reference

    manifest.json

    manifest file binds content script(s) and background page(s) to extension.

    {
        "name": "Annotation Tool",
        "description": "http://stackoverflow.com/questions/14244498/web-page-source-annotation-tool",
        "version": "1",
        "manifest_version": 2,
        "content_scripts": [
            {
                "matches": [
                    "<all_urls>"
                ],
                "js": [
                    "myscript.js"
                ],
                "all_frames": true
            }
        ],
        "permissions": [
            "contextMenus",
            "<all_urls>",
            "tabs"
        ],
        "background": {
            "scripts": [
                "background.js"
            ]
        },
        "icons": {
            "16": "screen.png",
            "48": "screen.png",
            "128": "screen.png"
        }
    }
    

    background.js

    Create(s) Context menu and binds it to browser and activates context menu execution through message passing.

    var _selection_univ = chrome.contextMenus.create({
        "title": "Add <univ> tag for %s ",
        "id": "_selection_univ",
        "onclick": reportclick,
        "contexts": ["selection"]
    }, function () {
        console.log("Context Menu 2 Created");
    });
    var _selection_address = chrome.contextMenus.create({
        "title": "Add <address> tag for %s ",
        "id": "_selection_address",
        "onclick": reportclick,
        "contexts": ["selection"]
    }, function () {
        console.log("Context Menu 2 Created");
    });
    //Add number of variables here for your functionality
    function reportclick(info, tab) {
        switch (info.menuItemId) {
            case "_selection_univ":
                chrome.tabs.sendMessage(tab.id, "univ");//Notify Content Script for univ
                break;
            case "_selection_address":
                chrome.tabs.sendMessage(tab.id, "address");//Notify Content Script for address
                break;
            default:
                console.log("Handle default case..");
        }
    }
    

    myscript.js

    //Handle DOM Changes here
    chrome.extension.onMessage.addListener(function (message, sender, response) {
        switch (message) {
            //Hanlde [univ] tag 
            case "univ":
                if (document.getSelection().baseNode != null) document.getSelection().baseNode.parentNode.innerHTML = "[univ]" + document.getSelection().baseNode.parentNode.innerHTML + "[/univ]";
                break;
            //Hanlde [address] tag  
            case "address":
                if (document.getSelection().baseNode != null) document.getSelection().baseNode.parentNode.innerHTML = "[address]" + document.getSelection().baseNode.parentNode.innerHTML + "[/address]";
                break;
            default:
                console.log("Handle default case..");
        }
    });
    

    Further Extension

    If you want to further add few more context menu(s)

    1) create a variable for context menu as shown here in background.js

    var _selection_Some_Tag = chrome.contextMenus.create({
        "title": "Add [SOME TAG] tag for %s ",
        "id": "_selection_univ",
        "onclick": reportclick,
        "contexts": ["selection"]
    }, function () {
        console.log("Context Menu for Some Tag Created");//In Call Back
    });
    

    2) add a case for switch in background page as shown here

    case "_selection_your_case":
        chrome.tabs.sendMessage(tab.id, "_your_tag_content"); //Notify Content Script for address
        break;
    

    3) handle your custom tag in content scripts by adding code as shown here

    //Hanlde [your custom] tag 
    case "univ":
        if (document.getSelection().baseNode != null) document.getSelection().baseNode.parentNode.innerHTML = "[your tag]" + document.getSelection().baseNode.parentNode.innerHTML + "[/your tag]";
        break;
    

    Testing and Loading Extension

    Check How to Load an Extension for testing and extending this script.

    References

    • Chrome Extension.
    • Background Page
    • Content Scripts
    • Context Menu
    • Message Passing
    • Extension API
    • DOM Selection

    EDIT 1

    You can use following code of chrome extension for

    • Tool Bar Instead of Context Menu
    • Replacing only selected text
    • Saving File to Sand Boxed Location

    To use this code use any of your fav icon(s) and put them in chrome directory for every tag [univ] and use corresponding names in css file here

    background-image: url(chrome-extension://MSG_@@extension_id/YOUR_ICON_NAME.png);

    manifest.json

    Registering css and java script for annotation tool.

    {
        "name": "Annotation Tool",
        "description": "http://stackoverflow.com/questions/14244498/web-page-source-annotation-tool",
        "version": "1",
        "manifest_version": 2,
        "content_scripts": [
            {
                "matches": [
                    "<all_urls>"
                ],
                "css": [
                    "myscript.css"
                ],
                "js": [
                    "jquery.js",
                    "myscript.js"
                ],
                "all_frames": true
            }
        ],
        "permissions": [
            "contextMenus",
            "<all_urls>",
            "tabs"
        ],
        "icons": {
            "16": "screen.png",
            "48": "screen.png",
            "128": "screen.png"
        },
        "web_accessible_resources": [
            "icon1.png",
            "icon2.png"
        ]
    }
    

    myscript.css

    Binding Icons here.

    #root #univ {
        display: inline-block;
        z-index: 100000;
        height: 22px;
        width: 26px;
        background-image: url(chrome-extension://__MSG_@@extension_id__/icon1.png);
    }
    #root #addr {
        display: inline-block;
        z-index: 100000;
        height: 22px;
        width: 26px;
        background-image: url(chrome-extension://__MSG_@@extension_id__/icon2.png);
    }
    

    myscript.js

    Code for updating selected text with custom tags.

    //Intialize counters to default values
    clicking = false;
    selecting = false;
    
    //Set the toolbar to some invalid position so it will not appear unless a selection is made
    var currentMousePos = {
        x: -100,
        y: -100
    };
    
    $(document).mousedown(function () {
        //Click is started
        clicking = true;
    });
    
    //Tool bar to add
    $('body').append("<div id='root' style='position: absolute; left:" + currentMousePos.x + "px; top:" + currentMousePos.y + "px; display: block;'><a id='univ' href='javascript:void(0);'>&nbsp;</a><a id='addr' href='javascript:void(0);' >&nbsp;</a></div>");
    
    
    $(document).mouseup(function (event) {
        if (selecting) {
            //He is selecting text
            $("#root").attr("style", "position: absolute; left:" + currentMousePos.x + "px; top:" + currentMousePos.y + "px; display: block;");
        } else {
            //He just clicked
            $("#root").attr("style", "display: none;");
        }
        //Reset counters
        clicking = false;
        selecting = false;
    });
    
    $(document).mousemove(function () {
        if (clicking) {
            //He did not simply click , but he is selecting some text
            selecting = true;
            //Track current position to put toolbar
            currentMousePos.x = event.pageX;
            currentMousePos.y = event.pageY;
        }
    });
    
    $("div #addr").click(function () {
        //Get Selected text
        var selection = document.getSelection();
        //Add your tags and prepare replacing content
        var html = "[addr]" + selection + "[/addr]";
        if (selection.getRangeAt && selection.rangeCount) {
            //Chrome supports only one range fire fox supports multiple ranges
            range = document.getSelection().getRangeAt(0);
            //remove selection
            range.deleteContents();
            //Create a node
            node = range.createContextualFragment(html);
            //Add the custom node
            range.insertNode(node);
        }
    });
    
    $("div #univ").click(function () {
        //Get Selected text
        var selection = document.getSelection();
        //Add your tags and prepare replacing content
        var html = "[univ]" + selection + "[/univ]";
        if (selection.getRangeAt && selection.rangeCount) {
            //Chrome supports only one range fire fox supports multiple ranges
            range = document.getSelection().getRangeAt(0);
            //remove selection
            range.deleteContents();
            //Create a node
            node = range.createContextualFragment(html);
            //Add the custom node
            range.insertNode(node);
        }
    });
    

    Output1

    Now You can replace any part of text

    enter image description here

    Output 2

    Replace any web page

    enter image description here

    Saving file to chosen Location

    It is possible to download the page using chrome.pageCapture API, but to a sand boxed location.

    Sample Implementation on using pageCapture API

    manifest.json

    {
        "name": "Page Capture Demo",
        "description": "This demos Page Capture MHTML Functionality",
        "permissions": [
            "pageCapture"
        ],
        "browser_action": {
            "default_icon": "screen.png",
            "default_popup": "popup.html"
        },
        "manifest_version": 2,
        "version": "1"
    }
    

    popup.html

    <html>
    
        <head>
            <script src="popup.js"></script>
        </head>
    
        <body>
            <div id="pushhere"></div>
        </body>
    
    </html>
    

    popup.js

    function capture() {
        chrome.tabs.query({
            "active": true,
            "currentWindow": true,
            "status": "complete"
        }, function (tabs) {
            chrome.pageCapture.saveAsMHTML({
                "tabId": tabs[0].id
            }, function (data) {
                var reader = new FileReader();
                reader.onload = function (eventt) {
                    console.log(eventt.target.result);
                    document.getElementById('pushhere').innerHTML = eventt.target.result;
                    //window.open(eventt.target.result);
                };
                reader.readAsText(data);
                //window.open(data);
            });
        });
    }
    window.onload = capture;
    

    Test this code using steps above by picking your icons of choice, hope this helps :)

    Edit 2

    • Accessing contents of HTML File(s) images, js and css files is possible from chrome extension
    • Accessing Local Disk System(Reading and storing data to them) is not supported from chrome extensions(for security reasons)
    • You can save files to sand-boxed location which however are not accessible for general access.
    0 讨论(0)
  • 2020-12-11 08:05

    A browser extension should be fine. A standalone application would need to utilize a full-fledged browser, which is cumbersome.

    Only with a browser extension (in contrast to a bookmarklet) you would have the permission to save the results directly to the filesystem. You can add the "Annotate" button to the browser (G)UI as well.

    However, storing thousands of HTML files on your harddisk might not be your aim. Instead, you could set up a simple database server to which you post the annotation results. A small bookmarklet with some ajax code would be enough on the clientside then.

    0 讨论(0)
提交回复
热议问题