Ynet Links and Vids

By Jillian Last update Oct 25, 2005 — Installed 9,623 times. Daily Installs: 4, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 3, 1, 4, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0
// ==UserScript==
// @name          Ynet Links and Vids
// @namespace     Jillian
// @description   Replaces javascript links with normal links on ynet.co.il, and enables embedded video clips in articles (v2.7).
// @include       http://ynet.co.il/*
// @include       http://*.ynet.co.il/*
// @include       http://players.mediazone.co.il/*
// @include       http://switch*.castup.net/*
// ==/UserScript==

// --------------------------------------------------------------------
//
// This is a Greasemonkey  (0.6+) user script.
//
// To install, you need Firefox  http://www.getfirefox.com and
// the Firefox extension called Greasemonkey: http://greasemonkey.mozdev.org/
// Install the Greasemonkey extension then restart Firefox and revisit this script.
//
// To uninstall, go to Tools|GreaseMonkey|Manage User Scripts,
// select the script and click Uninstall.
//
// --------------------------------------------------------------------

// v2.7 - Major refactoring.
//      - Instead of replacing the iframes with divs, the script replaces the iframes' content.
//      - The script now waits for the image to partly load before it goes on checking its size.
//      - All popups are made resizable.
//      - When visisting ynet.co.il, a menu item is added to toggle direct video links,
//         instead of embedding the player inside the page.
//         If Ctrl is pressed the movie is always embedded.
//      - bug fix: previously, some unusually named preview images were not shown.
// v2.6 - new! supports multiple videos in one page.
//      - new! supports the Video section.
//      - new! supports videos in mediazone.co.il windows.
//      - new! spports embedded audio streams.
// v2.5 - updated to embed videos from Mediazone (Ynet's new streaming video provider).
//         added NewsFlash links to the allowed popup list.
// v2.4 - fixed image size bug, and reworked JS links removal.
// v2.3 - remove JS links in talkbacks too.
// v2.2 - bug fix: preview images are also sometimes too big - now set to fixed size.
// v2.1 - bug fix: video clip size was sometimes too big.
// v2.0 - public release: rewrite of a previous script.

// Returns null if expr didn't match anything
function getFirstXPathResult(expr, node)
{
    if (!node) node = document;
    var res = document.evaluate(expr, node, null,
                    XPathResult.FIRST_ORDERED_NODE_TYPE, null);
    return res.singleNodeValue;
}

var DirectVideoLinkCfg = "directVideoLink";

var mainObj = {
    // Elements:
    vidObj : null, // Holds the video object element
    vidDiv : null,
    prevImg : null,

    vidUrl : "",
    prevImgSrc : "",
    iters : 0, // Number of iterations of waitForImageDimentions()
    MaxIterations : 50, // 5 seconds (iterations are in 100ms intervals)

    setVidUrls : function() {
        var url = document.location.href;
        if (url.indexOf("castup.net") != -1) {
            this.vidUrl = unescape(url.split("clipurl=")[1]);
            this.prevImgSrc = this.vidUrl + "&th=1&ndth=1";
            GM_log("prevImgSrc = " + this.prevImgSrc);
        }
        else { // must be mediazone.co.il
            this.vidUrl = document.getElementById("prmURL").value;
            this.prevImgSrc = getFirstXPathResult("//div[@id='spnPreview']/img").src;
        }
    },

    // Create and append an img element to hold the preview image
    appendPreviewImg : function() {
        this.prevImg = document.createElement("img");
        this.prevImg.src = this.prevImgSrc;
        this.prevImg.style.cssText += "cursor: pointer; position: relative; top: 15px;";

        this.waitForImageDimentions();
    },
    
    // Waits for the image to partly load -- until its dimensions are known
    waitForImageDimentions : function() {
        if (!this.prevImg.width && ++this.iters < this.MaxIterations) {
            setTimeout(function() { mainObj.waitForImageDimentions(); }, 100);
            return;
        }
        else if (this.iters >= this.MaxIterations) {
            GM_log("Could not show image at " + this.prevImg.src);
            // Continue with a broken image so the user has something to click on
        }
        this.onPreviewImgReady();
    },
    
    onPreviewImgReady : function() {
        var img = this.prevImg;

        if (img.width > 320)
            img.width = 320;
        if (img.height > 240)
            img.height = 240;
        // If the movie is on the front page, make sure the image doesn't bleed out of the div
        if (innerWidth < 280) {
            img.width = Math.min(img.width, 240); // The height will adjust proportionally
            img.style.top = "6px";
        }
        // General case for image > div, shouldn't happen
        if (img.width > innerWidth) {
            img.width = innerWidth - 10;
        }

        if (GM_getValue(DirectVideoLinkCfg, false)) {
            var iLink = document.createElement("a");
            iLink.href = this.vidUrl;
            iLink.addEventListener("click", onDirectVideoLinkClick, true);
            iLink.appendChild(img);
            this.vidDiv.appendChild(iLink);
        }
        else {
            // Attach the video and clear the div when the user clicks on the image
            img.addEventListener("click", onPrevImgClick, true);
            this.vidDiv.appendChild(img);
        }
    },
    
    createVideoObject : function() {
        var obj = document.createElement("object");
        obj.type = "video/x-ms-wmv";
        // Constrain the size of the player to a maximum of 320x286
        obj.height = Math.min(innerHeight, 240+46); // Player controls are 46px high
        obj.width = Math.min(innerWidth, 320);
        obj.data = this.vidUrl;
        return obj;
    },

    setVideoDivCSSAndHTML : function() {
        this.vidDiv.style.textAlign = "center";
        this.vidDiv.style.backgroundColor = "#5A7DB5";
        //this.vidDiv.style.marginTop = "5px";
        this.vidDiv.innerHTML =
            "<div style='background-color: #38639A; color: white; border-bottom: 1px solid white; height: 45px'>" +
            "<h1 style='font-size: 18px; margin: 0 auto;'>" +
            unescape("Ynet%20%u05D5%u05D9%u05D3%u05D0%u05D5") +
            "</h1><h1 style='font-size: 14px'>" +
            unescape("%u05DC%u05D7%u05E6%u05D5%20%u05DC%u05E6%u05E4%u05D9%u05D4") +
            "</h1></div>";
    },

    setAudioDiv : function() {
        // Use a generic picture
        this.prevImgSrc = "http://go.ynet.co.il/images/play_b.jpg";
        this.vidObj.height = 46; // Show controls only
        this.vidDiv.style.textAlign = "center";
    },

    embedVideo : function() {
        this.setVidUrls();
        this.vidDiv = document.createElement("div");
        this.vidObj = this.createVideoObject()
        if (isAudioOnly()) {
            this.setAudioDiv();
        }
        else {
            this.setVideoDivCSSAndHTML();
        }
        this.vidDiv.style.height = "100%";
        document.body.style.overflow = "hidden"; // Prevents scrollbars
        document.body.removeAttribute("bgcolor"); // For the old castup frames
        this.appendPreviewImg();
        document.body.innerHTML = "";
        document.body.appendChild(this.vidDiv);
    },

    /* ========== main() ========== */
    main : function() {
        if (isVideoFrame()) {
            // Get rid of the annoying error messages caused by playerStateCheck();
            unsafeWindow.playerStateCheck = function() {};
            this.embedVideo();
        }
        else {
            linkifyJSLinks();
            overrideOpen();
            // Create a menu to toggle direct links to movies option
            GM_registerMenuCommand("Toggle direct video links on ynet", toggleDirectVideoLinks);
        }
    }
}; // mainObj


function onPrevImgClick(evt, div) {
    if (!div) div = this.parentNode;
    div.innerHTML = "";
    div.style.backgroundColor = "";
    div.appendChild(mainObj.vidObj);
}

// Even if videos are linked directly, embed the video if Ctrl is pressed.
function onDirectVideoLinkClick(evt) {
    if (evt.ctrlKey) {
        evt.preventDefault();
        evt.stopPropagation();
        onPrevImgClick(null, this.parentNode);
    }
}

function toggleDirectVideoLinks() {
    var direct = !GM_getValue(DirectVideoLinkCfg, false);
    GM_setValue(DirectVideoLinkCfg, direct);
    alert("Direct video links will be " + ((direct) ? "enabled" : "disabled"));
    document.location.replace(document.location.href);
}

function isAudioOnly() {
    // If the iframe is so short it means it's audio only
    // +20 is a safe margin against future changes
    return (innerHeight <= 60+20);
}

function isVideoFrame() {
    // castup.net serves a special page for Firefox users, w/o a video object
    return ( (/mediazone/.test(document.location.hostname) && getFirstXPathResult("//object"))
            || /castup/.test(document.location.hostname));
}

function linkifyJSLinks() {
    // Attributes to remove
    var attrs = "onclick oncontextmenu ondeactivate onblur onmouseover onmouseout".split(" ");
    // Links that contain these keywords are allowed to open in a popup window
    var popupRE =
        /CdaDisplayGallery|CdaArticleSendtoForm|CdaTalkBack|CdaViewBlogTalkBack|CdaNewsFlash/;
    
    // Traverse over the urls; if they're javascript and do not contain the keywords above,
    // remove the attributes and change the href to the real url.
    var links = document.getElementsByTagName("a");
    var curLink;
    for (var i = 0; (curLink = links[i]); i++) {
        if (popupRE.test(curLink.href)) {
            continue;
        }
    
        // From my experience this is the safest method to get the URL
        var cmFunc = curLink.getAttribute("oncontextmenu");
        if (cmFunc) {
            var href = cmFunc.slice("this.href=\"".length, -1); // Until the matching apos.
            if (href[0] == ' ') {
                // Sometimes ynet mistakenly puts a space before the URL
                href = href.substr(1);
            }
            curLink.href = href;
            for (a in attrs) {
                curLink.removeAttribute(attrs[a]);
            }
        }
        // Handle simple JS links, such as in talkbacks
        else if (/(openInnewWindow|openWin|open)\b/.test(curLink.href)) {
            // Assumes the url is the first thing between quotes
            curLink.href = unescape(/\(['"](.+?)["']/.exec(curLink.href)[1]);
        }
    }
    
    linkifySpVideoLinks();
}

function linkifySpVideoLinks() {
    var uw = unsafeWindow;

    if (uw.playVideo) {
        uw.old_playVideo = uw.playVideo;
        uw.playVideo = function() {
            try { uw.old_playVideo.apply(null, arguments); } catch(e) {}
            // old_playVideo() set the global tempArticleUrl to the current story's url.
            document.getElementById("fullArticleUrl").href = uw.tempArticleUrl;
        }
    }
    else if (uw.vDisplay) {
        // If there's no playVideo() but there is vDisplay(), 
        // signal our version of open() that we don't want a new window
        uw.old_vDisplay = uw.vDisplay;
        uw.vDisplay = function() {
            uw.override = true;
            uw.old_vDisplay.apply(null, arguments);
        }
    }
}

// Makes all popups resizable, and optionally redirects instead of opening a new window.
function overrideOpen() {
    unsafeWindow.open = function (url, name, feats) {
        if (!feats) feats = "";
        feats += ",resizable=yes";
        if (unsafeWindow.override)
            document.location.assign(url);
        else
            return open(url, name, feats);
    }
}

mainObj.main();