diff --git a/.DS_Store b/.DS_Store index 8075e3ec..59e937cb 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/ClickToPlugin.fr.safariextension/.DS_Store b/ClickToPlugin.fr.safariextension/.DS_Store index 10fb7ec1..5d3c2697 100644 Binary files a/ClickToPlugin.fr.safariextension/.DS_Store and b/ClickToPlugin.fr.safariextension/.DS_Store differ diff --git a/ClickToPlugin.fr.safariextension/ClickToPlugin.js b/ClickToPlugin.fr.safariextension/ClickToPlugin.js index 387ae0dc..47e4da4c 100644 --- a/ClickToPlugin.fr.safariextension/ClickToPlugin.js +++ b/ClickToPlugin.fr.safariextension/ClickToPlugin.js @@ -1,45 +1,45 @@ -/* -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -NOTE TO SELF -ALWAYS ALWAYS ALWAYS ALWAYS USE the 'var' keyword in 'for' loops!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -*/ - - /**************************** ClickToPlugin class definition *****************************/ function ClickToPlugin() { - this.blockedElements = new Array();// array containing the Flash HTML elements + this.blockedElements = new Array();// array containing the blocked HTML elements this.placeholderElements = new Array();// array containing the corresponding placeholder elements - this.mediaPlayers = new Array();// array containing the HTML5 media players + this.mediaPlayers = new Array();// array containing the HTML5 media players + + /* + Each item in blockedElement will acquire 3 additional properties: + -> tag: 'embed', 'object', or 'applet' + -> plugin: the name of the plugin that would handle the element + -> info: an object; info.src is the source of the embedded content + */ this.settings = null; this.instance = null; this.numberOfBlockedElements = 0; - this.numberOfUnblockedElements = 0; - this.location = window.location.href; + this.numberOfUnblockedElements = 0; + + var _this = this; - var _this = this; - - this.respondToMessageTrampoline = function(event) { + this.respondToMessageTrampoline = function(event) { _this.respondToMessage(event); }; - - this.handleEventTrampoline = function(event) { - _this.handleEvent(event); - }; + + this.handleEventTrampoline = function(event) { + _this.handleEvent(event); + }; - safari.self.addEventListener("message", this.respondToMessageTrampoline, false); - document.addEventListener("beforeload", this.handleEventTrampoline, true); + safari.self.addEventListener("message", this.respondToMessageTrampoline, false); + document.addEventListener("beforeload", this.handleEventTrampoline, true); + // HTML applet elements fire no beforeload event + // bug filed: https://bugs.webkit.org/show_bug.cgi?id=44023 + // Unsatisfactory workaround (Java will run anyway): document.addEventListener("DOMContentLoaded", this.handleEventTrampoline, false); - document.addEventListener("DOMNodeInserted", this.handleEventTrampoline, false); - //document.addEventListener("DOMNodeInsertedIntoDocument", this.handleEventTrampoline, false); + document.addEventListener("DOMNodeInserted", this.handleEventTrampoline, false); document.oncontextmenu = function(event) { - safari.self.tab.setContextMenuEventUserInfo(event, {"location": _this.location, "blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements}); + safari.self.tab.setContextMenuEventUserInfo(event, {"location": window.location.href, "blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements}); }; } @@ -50,31 +50,41 @@ ClickToPlugin.prototype.clearAll = function(elementID) { }; ClickToPlugin.prototype.respondToMessage = function(event) { - if (event.name == "mediaData") { - if(event.message.CTPInstance != this.instance) return; // ignore message from other CTP instances - this.prepMedia(event.message); - } else if (event.name == "loadContent") { - var loadData = event.message.split(","); // [0] CTPInstance, [1] elementID, [2] message - if (loadData[0] != this.instance) return; // ignore message from other CTP instances - if (loadData[2] == "plugin") { - this.loadPluginForElement(loadData[1]); - } else if (loadData[2] == "video") { - this.loadMediaForElement(loadData[1]); - } else if (loadData[2] == "reloadPlugin") { - this.loadInPlugin(loadData[1]); - } else if (loadData[2] == "remove") { - this.removeElement(loadData[1]); - } else if (loadData[2] == "qtp") { - this.launchInQuickTimePlayer(loadData[1]); - } else if (loadData[2] == "show") { - alert(document.HTMLToString(this.blockedElements[loadData[1]])); - } - } else if (event.name == "updateVolume") { - this.setVolumeTo(event.message); - } else if (event.name == "updateOpacity") { - this.setOpacityTo(event.message); - } else if(event.name == "loadAll") { - this.loadAll(); + switch(event.name) { + case "mediaData": + if(event.message.instance != this.instance) return; // ignore message from other CTP instances + this.prepMedia(event.message); + break; + case "loadContent": + var loadData = event.message.split(","); // [0] instance, [1] elementID, [2] message + if (loadData[0] != this.instance) return; // ignore message from other CTP instances + switch(loadData[2]) { + case "plugin": + this.loadPluginForElement(loadData[1]); + break; + case "remove": + this.removeElement(loadData[1]); + break; + case "reload": + this.reloadInPlugin(loadData[1]); + break; + case "qtp": + this.launchInQuickTimePlayer(loadData[1]); + break; + case "show": + alert(document.HTMLToString(this.blockedElements[loadData[1]])); + break; + } + break; + case "loadAll": + this.loadAll(); + break; + case "updateVolume": + this.setVolumeTo(event.message); + break; + case "updateOpacity": + this.setOpacityTo(event.message); + break; } }; @@ -82,9 +92,8 @@ ClickToPlugin.prototype.respondToMessage = function(event) { According to the W3C HTML5 spec, to activate a plugin, -> an 'embed' element must have either the 'src' or the 'type' attribute with nonempty value -> an 'object' element must have either the 'data' or the 'type' attribute with nonempty value. -In the real world, however, (AS IS RECOMMANDED ON ADOBE'S WEBSITE, BY THE WAY!) -one often finds an 'object' with neither 'data' nor 'type', with an 'embed' element -as child with 'src' and/or 'type' attribute set. +In the real world, however, one often finds an 'object' with neither 'data' nor 'type', +with an 'embed' element as child with 'src' and/or 'type' attribute set. */ ClickToPlugin.prototype.handleEvent = function(event) { @@ -102,21 +111,35 @@ ClickToPlugin.prototype.handleEvent = function(event) { ClickToPlugin.prototype.handleDOMContentEvent = function(event) { if(event.target.nodeType != 1 && event.target.nodeType != 9) return; // the node is not an HTML Element nor the document const applets = event.target.getElementsByTagName("applet"); + if(applets.length == 0) return; + // Convert NodeList to Array var appletElements = new Array(); for(var i = 0; i < applets.length; i++) { appletElements.push(applets[i]); } + var tmpAnchor = document.createElement("a"); for(var i = 0; i < appletElements.length; i++) { if(appletElements[i].allowedToLoad) continue; appletElements[i].tag = "applet"; - appletElements[i].source = getSrcOf(appletElements[i]); + appletElements[i].info = new Object(); + + // Get source of applet + if(appletElements[i].code) { + tmpAnchor.href = appletElements[i].code; + appletElements[i].info.src = tmpAnchor.href; + } else if(appletElements[i].hasAttribute("archive")) { + tmpAnchor.href = appletElements[i].getAttribute("archive"); + appletElements[i].info.src = tmpAnchor.href; + } else { + appletElements[i].info.src = ""; + } - var pluginName = safari.self.tab.canLoad(event, {"src": appletElements[i].source, "type": "application/x-java-applet", "classid": "", "params": "", "location": this.location, "width": appletElements[i].offsetWidth, "height": appletElements[i].offsetHeight, "otherInfo": null}); + var pluginName = safari.self.tab.canLoad(event, {"src": appletElements[i].info.src, "type": "application/x-java-applet", "location": window.location.href, "width": appletElements[i].offsetWidth, "height": appletElements[i].offsetHeight}); if(!pluginName) continue; // whitelisted - appletElements[i].plugin = pluginName; + appletElements[i].plugin = "Java"; if(this.settings == null) { this.settings = safari.self.tab.canLoad(event, "getSettings"); @@ -125,15 +148,21 @@ ClickToPlugin.prototype.handleDOMContentEvent = function(event) { this.instance = safari.self.tab.canLoad(event, "getInstance"); } var elementID = this.numberOfBlockedElements++; - this.processBlockedElement(appletElements[i], elementID); // this removes appletElements[0] from the document + // BEGIN DEBUG + if(this.settings["debug"]) { + if(!confirm("ClickToPlugin is about to block embedded content " + this.instance + "." + elementID + ":\n\n" + document.HTMLToString(element))) return; + } + // END DEBUG + + this.processBlockedElement(appletElements[i], elementID); } }; ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { - const element = event.target; - + const element = event.target; + // deal with sIFR script first - if(element instanceof HTMLScriptElement && element.getAttribute("src").indexOf("sifr.js") != (-1)) { + if(element instanceof HTMLScriptElement && element.src.indexOf("sifr.js") != (-1)) { var sIFRData = safari.self.tab.canLoad(event, "sIFR"); if(!sIFRData.canLoad) { // BEGIN DEBUG @@ -151,7 +180,8 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { // the beforeload event is fired again but this time the // flash element must not be blocked if (element.allowedToLoad) return; - if (element instanceof HTMLEmbedElement) { + + if (element instanceof HTMLEmbedElement) { element.tag = "embed"; } else if (element instanceof HTMLObjectElement) { element.tag = "object"; @@ -159,29 +189,20 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { return; } + element.info = getInfo(element, event.url); + + var pluginName = safari.self.tab.canLoad(event, {"src": element.info.src, "type": getTypeOf(element), "classid": element.getAttribute("classid"), "location": window.location.href, "width": element.offsetWidth, "height": element.offsetHeight, "launchInQTP": element.info.target == "quicktimeplayer" ? element.info.href : null}); + if(!pluginName) return; // whitelisted + // Load the user settings if(this.settings == null) { this.settings = safari.self.tab.canLoad(event, "getSettings"); } - // Give an address to this CTP instance to receive messages - if(this.instance == null) { - this.instance = safari.self.tab.canLoad(event, "getInstance"); - } - - element.otherInfo = new Object(); - element.source = getSrcOf(element); - - var pluginName = safari.self.tab.canLoad(event, {"src": element.source, "type": getTypeOf(element), "classid": element.getAttribute("classid"), "params": this.settings["useH264"] ? getParamsOf(element) : "", "location": this.location, "width": element.offsetWidth, "height": element.offsetHeight, "otherInfo": element.otherInfo}); - if(!pluginName) return; // whitelisted - //alert(pluginName); - - - - // if useh264... + // if useh264... // Deal with sIFR Flash - if (element.className == "sIFR-flash" || element.getAttribute("sifr")) { + if (element.className == "sIFR-flash" || element.hasAttribute("sifr")) { if (this.settings["sifrReplacement"] == "autoload") { // BEGIN DEBUG if(this.settings["debug"]) { @@ -193,13 +214,17 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { } // At this point we know we have to block 'element' from loading + var elementID = this.numberOfBlockedElements++; element.plugin = pluginName; - var elementID = this.numberOfBlockedElements++; + + // Give an address to this CTP instance to receive messages + if(this.instance == null) { + this.instance = safari.self.tab.canLoad(event, "getInstance"); + } // BEGIN DEBUG if(this.settings["debug"]) { - if(!confirm(window.location.href + "\n" + window.top.location.href + "\nClickToPlugin is about to block embedded content " + this.instance + "." + elementID + ":\n\n" + document.HTMLToString(element))) return; - //alert(element.source + "\n -- \n" + event.url); + if(!confirm("ClickToPlugin is about to block embedded content " + this.instance + "." + elementID + ":\n\n" + document.HTMLToString(element))) return; } // END DEBUG @@ -209,38 +234,37 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { ClickToPlugin.prototype.prepMedia = function(mediaData) { if(mediaData.playlist.length == 0 || !mediaData.playlist[0].mediaURL) return; - if(!this.mediaPlayers[mediaData.elementID]) { - this.mediaPlayers[mediaData.elementID] = new mediaPlayer(mediaData.isAudio ? "audio" : "video"); - } - if(mediaData.loadAfter) { // just adding stuff to the playlist - // BEGIN DEBUG - if(this.settings["debug"]) { - if(!confirm("Preparing to add " + mediaData.playlist.length + " tracks to the playlist for element " + this.instance +"."+ mediaData.elementID)) return; - } - // END DEBUG - this.mediaPlayers[mediaData.elementID].playlistLength -= mediaData.missed; - this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist); - return; - } - // BEGIN DEBUG - if(this.settings["debug"]) { - var showPlaylist = "(" + mediaData.playlist.length + " track" + (mediaData.playlist.length > 1 ? "s" : "") + ")"; + if(!this.mediaPlayers[mediaData.elementID]) { + this.mediaPlayers[mediaData.elementID] = new mediaPlayer(mediaData.isAudio ? "audio" : "video"); + } + if(mediaData.loadAfter) { // just adding stuff to the playlist + // BEGIN DEBUG + if(this.settings["debug"]) { + if(!confirm("Preparing to add " + mediaData.playlist.length + " tracks to the playlist for element " + this.instance +"."+ mediaData.elementID)) return; + } + // END DEBUG + this.mediaPlayers[mediaData.elementID].playlistLength -= mediaData.missed; + this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist); + return; + } + // BEGIN DEBUG + if(this.settings["debug"]) { + var showPlaylist = "(" + mediaData.playlist.length + " track" + (mediaData.playlist.length > 1 ? "s" : ""); + if(mediaData.playlistLength) showPlaylist += ", expecting " + mediaData.playlistLength; + showPlaylist += ")"; for (var i = 0; i < mediaData.playlist.length; i++) { - showPlaylist += "\n[" + (i + 1) + "] (" + mediaData.playlist[i].mediaType + ")" + (mediaData.playlist[i].mediaType == "video" ? ("\nposterURL: " + mediaData.playlist[i].posterURL) : "") + "\nmediaURL: " + mediaData.playlist[i].mediaURL + "\n"; + showPlaylist += "\n[" + (i + 1) + "] (" + mediaData.playlist[i].mediaType + ")" + "\nposterURL: " + mediaData.playlist[i].posterURL + "\nmediaURL: " + mediaData.playlist[i].mediaURL + "\n"; } if(!confirm("Preparing media for element " + this.instance +"."+ mediaData.elementID + ":\n\nbadgeLabel: " + mediaData.badgeLabel + "\n\nPLAYLIST " + showPlaylist)) return; } // END DEBUG - - // do it backward just in case a loadAfter came first - // can happen for embedded playlists - - this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist, true); - this.mediaPlayers[mediaData.elementID].playlistLength = mediaData.playlistLength ? mediaData.playlistLength : mediaData.playlist.length; - this.mediaPlayers[mediaData.elementID].startTrack = mediaData.startTrack ? mediaData.startTrack : 0; - - this.mediaPlayers[mediaData.elementID].usePlaylistControls = this.settings["usePlaylists"] && !mediaData.noPlaylistControls && this.mediaPlayers[mediaData.elementID].playlistLength > 1; + + this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist, true); + this.mediaPlayers[mediaData.elementID].playlistLength = mediaData.playlistLength ? mediaData.playlistLength : mediaData.playlist.length; + this.mediaPlayers[mediaData.elementID].startTrack = mediaData.startTrack ? mediaData.startTrack : 0; + + this.mediaPlayers[mediaData.elementID].usePlaylistControls = this.settings["usePlaylists"] && !mediaData.noPlaylistControls && this.mediaPlayers[mediaData.elementID].playlistLength > 1; // Check if we should load video at once if(this.settings["H264autoload"]) { @@ -249,17 +273,17 @@ ClickToPlugin.prototype.prepMedia = function(mediaData) { } var badgeLabel = mediaData.badgeLabel; if(!badgeLabel) badgeLabel = "Video"; - + this.displayBadge(badgeLabel, mediaData.elementID); }; ClickToPlugin.prototype.loadPluginForElement = function(elementID) { var placeholderElement = this.placeholderElements[elementID]; - var element = this.blockedElements[elementID]; - element.allowedToLoad = true; + var element = this.blockedElements[elementID]; + element.allowedToLoad = true; if(placeholderElement.parentNode) { placeholderElement.parentNode.replaceChild(element, placeholderElement); - this.numberOfUnblockedElements++; + this.numberOfUnblockedElements++; this.clearAll(elementID); } }; @@ -271,14 +295,14 @@ ClickToPlugin.prototype.loadAll = function() { this.loadPluginForElement(i); } } - this.numberOfUnblockedElements = this.numberOfBlockedElements; + this.numberOfUnblockedElements = this.numberOfBlockedElements; }; -ClickToPlugin.prototype.loadInPlugin = function(elementID) { +ClickToPlugin.prototype.reloadInPlugin = function(elementID) { var containerElement = this.mediaPlayers[elementID].containerElement; - var element = this.blockedElements[elementID]; - element.allowedToLoad = true; - containerElement.parentNode.replaceChild(element, containerElement); + var element = this.blockedElements[elementID]; + element.allowedToLoad = true; + containerElement.parentNode.replaceChild(element, containerElement); this.clearAll(elementID); }; @@ -286,56 +310,54 @@ ClickToPlugin.prototype.loadMediaForElement = function(elementID) { var placeholderElement = this.placeholderElements[elementID]; var contextInfo = { - "CTPInstance": this.instance, + "instance": this.instance, "elementID": elementID, "isH264": true, "plugin": this.blockedElements[elementID].plugin - //"blocked": this.numberOfBlockedElements - this.numberOfUnblockedElements + //"blocked": this.numberOfBlockedElements - this.numberOfUnblockedElements }; - // Initialize player - var w = parseInt(placeholderElement.style.width.replace("px","")); - var h = parseInt(placeholderElement.style.height.replace("px","")); - this.mediaPlayers[elementID].initialize(this.settings["H264behavior"], w, h, this.settings["volume"], contextInfo); - //mediaElement.allowedToLoad = true; // not used for now - - // Load first track - this.mediaPlayers[elementID].loadTrack(0); + // Initialize player + var w = parseInt(placeholderElement.style.width.replace("px","")); + var h = parseInt(placeholderElement.style.height.replace("px","")); + this.mediaPlayers[elementID].initialize(this.settings["H264behavior"], w, h, this.settings["volume"], contextInfo); + // mediaElement.allowedToLoad = true; // not used - // Replace placeholder - placeholderElement.parentNode.replaceChild(this.mediaPlayers[elementID].containerElement, placeholderElement); - this.numberOfUnblockedElements++; + // Replace placeholder and load first track + placeholderElement.parentNode.replaceChild(this.mediaPlayers[elementID].containerElement, placeholderElement); + this.mediaPlayers[elementID].loadTrack(0); + this.numberOfUnblockedElements++; this.placeholderElements[elementID] = null; }; ClickToPlugin.prototype.launchInQuickTimePlayer = function(elementID) { - var track = this.mediaPlayers[elementID].currentTrack; - var element = null; - if(track == null) { - track = 0; - element = this.placeholderElements[elementID]; - } else { - element = this.mediaPlayers[elementID].containerElement; - } + var track = this.mediaPlayers[elementID].currentTrack; + var element = null; + if(track == null) { + track = 0; + element = this.placeholderElements[elementID]; + } else { + element = this.mediaPlayers[elementID].containerElement; + } var mediaURL = this.mediaPlayers[elementID].playlist[track].mediaURL; - var QTObject = document.createElement("embed"); - QTObject.allowedToLoad = true; - QTObject.className = "CTFQTObject"; - QTObject.setAttribute("type", "video/quicktime"); - QTObject.setAttribute("width", "0"); - QTObject.setAttribute("height", "0"); - // need an external URL for source, since QT plugin doesn't accept safari-extension:// protocol - // Apple has a small 1px image for this exact purpose - QTObject.setAttribute("src", "http://images.apple.com/apple-events/includes/qtbutton.mov"); - QTObject.setAttribute("href", mediaURL); - QTObject.setAttribute("target", "quicktimeplayer"); - QTObject.setAttribute("autohref", "true"); - QTObject.setAttribute("controller", "false"); - //QTObject.setAttribute("postdomevents", "true"); - element.appendChild(QTObject); - // There doesn't seem to exist an appropriate event, so we just wait a bit... - setTimeout(function() {element.removeChild(QTObject);}, 100); + var QTObject = document.createElement("embed"); + QTObject.allowedToLoad = true; + QTObject.className = "CTFQTObject"; + QTObject.setAttribute("type", "video/quicktime"); + QTObject.setAttribute("width", "0"); + QTObject.setAttribute("height", "0"); + // need an external URL for source, since QT plugin doesn't accept safari-extension:// protocol + // Apple has a small 1px image for this exact purpose + QTObject.setAttribute("src", "http://images.apple.com/apple-events/includes/qtbutton.mov"); + QTObject.setAttribute("href", mediaURL); + QTObject.setAttribute("target", "quicktimeplayer"); + QTObject.setAttribute("autohref", "true"); + QTObject.setAttribute("controller", "false"); + // QTObject.setAttribute("postdomevents", "true"); + element.appendChild(QTObject); + // There doesn't seem to exist an appropriate event, so we just wait a bit... + setTimeout(function() {element.removeChild(QTObject);}, 100); }; ClickToPlugin.prototype.setVolumeTo = function(volume) { @@ -356,7 +378,7 @@ ClickToPlugin.prototype.removeElement = function(elementID) { element = element.parentNode; } element.parentNode.removeChild(element); - this.numberOfUnblockedElements++; + this.numberOfUnblockedElements++; this.clearAll(elementID); }; @@ -376,11 +398,11 @@ ClickToPlugin.prototype.displayBadge = function(badgeLabel, elementID) { this.placeholderElements[elementID].firstChild.firstChild.firstChild.firstChild.className = "logoContainer hidden"; this.placeholderElements[elementID].firstChild.firstChild.firstChild.firstChild.childNodes[1].className = "logo tmp"; - this.unhideLogo(elementID); + this.unhideLogo(elementID, 0); }; // NOTE: this function should never be called directly (use displayBadge instead) -ClickToPlugin.prototype.unhideLogo = function(elementID) { +ClickToPlugin.prototype.unhideLogo = function(elementID, i) { var logoContainer = this.placeholderElements[elementID].firstChild.firstChild.firstChild.firstChild; var w0 = this.placeholderElements[elementID].offsetWidth; var h0 = this.placeholderElements[elementID].offsetHeight; @@ -390,10 +412,11 @@ ClickToPlugin.prototype.unhideLogo = function(elementID) { var h2 = logoContainer.childNodes[1].offsetHeight; if(w2 == 0 || h2 == 0 || w1 == 0 || h1 == 0 || w0 == 0 || h0 == 0) { + if(i > 9) return; // 2 options: leave the logo hidden (no big deal, and rarely happens), - // or run unhideLogo again later (this might cause unexpected results due to asynchronicity) + // or run unhideLogo again later <- THIS var _this = this; - setTimeout(function() {_this.unhideLogo(elementID);}, 100); // there's no hurry here + setTimeout(function() {_this.unhideLogo(elementID, ++i);}, 100); // there's no hurry here return; } @@ -418,22 +441,22 @@ ClickToPlugin.prototype.unhideLogo = function(elementID) { }; ClickToPlugin.prototype.clickPlaceholder = function(elementID) { - if (this.mediaPlayers[elementID] && this.mediaPlayers[elementID].startTrack != null) { - this.loadMediaForElement(elementID); - } else { + if (this.mediaPlayers[elementID] && this.mediaPlayers[elementID].startTrack != null) { + this.loadMediaForElement(elementID); + } else { this.loadPluginForElement(elementID); - } + } }; ClickToPlugin.prototype.processBlockedElement = function(element, elementID) { // Creating the placeholder element - var placeholderElement = document.createElement("div"); - placeholderElement.style.width = element.offsetWidth + "px"; - placeholderElement.style.height = element.offsetHeight + "px"; - placeholderElement.style.opacity = this.settings["opacity"]; + var placeholderElement = document.createElement("div"); + placeholderElement.style.width = element.offsetWidth + "px"; + placeholderElement.style.height = element.offsetHeight + "px"; + placeholderElement.style.opacity = this.settings["opacity"]; - placeholderElement.className = "clickToFlashPlaceholder"; + placeholderElement.className = "clickToFlashPlaceholder"; // Replacing element by placeholderElement if (element.parentNode) { @@ -445,54 +468,54 @@ ClickToPlugin.prototype.processBlockedElement = function(element, elementID) { alert("Ignoring duplicate element " + this.instance + "." + elementID + "."); } // END DEBUG - this.numberOfUnblockedElements++; + this.numberOfUnblockedElements++; return; } - var _this = this; - placeholderElement.onclick = function(event){_this.clickPlaceholder(elementID);}; - placeholderElement.oncontextmenu = function(event) { - var contextInfo = { - "CTPInstance": _this.instance, + var _this = this; + placeholderElement.onclick = function(event){_this.clickPlaceholder(elementID);}; + placeholderElement.oncontextmenu = function(event) { + var contextInfo = { + "instance": _this.instance, "elementID": elementID, - "src": element.source, + "src": element.info.src, "plugin": element.plugin, - "blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements + //"blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements }; if (_this.mediaPlayers[elementID] && _this.mediaPlayers[elementID].startTrack != null) { - contextInfo.hasH264 = true; + contextInfo.hasH264 = true; _this.mediaPlayers[elementID].setContextInfo(event, contextInfo); } else { - contextInfo.hasH264 = false; - safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); - event.stopPropagation(); + contextInfo.hasH264 = false; + safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); + event.stopPropagation(); } - }; + }; // Building the placeholder - var container = document.createElement("div"); - container.className = "clickToFlashPlaceholderContainer"; - placeholderElement.appendChild(container); - - var verticalPositionElement = document.createElement("div"); - verticalPositionElement.className = "logoVerticalPosition"; - container.appendChild(verticalPositionElement); - - var horizontalPositionElement = document.createElement("div"); - horizontalPositionElement.className = "logoHorizontalPosition"; - verticalPositionElement.appendChild(horizontalPositionElement); - - var logoContainer = document.createElement("div"); - logoContainer.className = "logoContainer nodisplay"; // keep the logo hidden at first - horizontalPositionElement.appendChild(logoContainer); - - var logoElement = document.createElement("div"); - logoElement.className = "logo"; - logoContainer.appendChild(logoElement); - - var logoInsetElement = document.createElement("div"); - logoInsetElement.className = "logo inset"; - logoContainer.appendChild(logoInsetElement); + var container = document.createElement("div"); + container.className = "clickToFlashPlaceholderContainer"; + placeholderElement.appendChild(container); + + var verticalPositionElement = document.createElement("div"); + verticalPositionElement.className = "logoVerticalPosition"; + container.appendChild(verticalPositionElement); + + var horizontalPositionElement = document.createElement("div"); + horizontalPositionElement.className = "logoHorizontalPosition"; + verticalPositionElement.appendChild(horizontalPositionElement); + + var logoContainer = document.createElement("div"); + logoContainer.className = "logoContainer nodisplay"; // keep the logo hidden at first + horizontalPositionElement.appendChild(logoContainer); + + var logoElement = document.createElement("div"); + logoElement.className = "logo"; + logoContainer.appendChild(logoElement); + + var logoInsetElement = document.createElement("div"); + logoInsetElement.className = "logo inset"; + logoContainer.appendChild(logoInsetElement); // Filling the main arrays this.blockedElements[elementID] = element; @@ -503,14 +526,14 @@ ClickToPlugin.prototype.processBlockedElement = function(element, elementID) { if(this.settings["useH264"]) { if(!this.directKill(elementID)) { var elementData = { + "instance": this.instance, + "elementID": elementID, "plugin": element.plugin, - "src": element.source, - "presrc": element.presource, // TEMP!! + "src": element.info.href ? element.info.href : element.info.src, + "presrc": element.info.href ? element.info.src : "", + "image": element.info.image, "params": getParamsOf(element), - "elementID": elementID, - "CTPInstance": this.instance, - "location": this.location, - "image": element.image + "location": window.location.href }; safari.self.tab.dispatchMessage("killPlugin", elementData); } @@ -549,7 +572,7 @@ ClickToPlugin.prototype.directKill = function(elementID) { var mediaData = { "elementID": elementID, "playlist": [{"mediaType": mediaType, "posterURL": mediaElements[0].getAttribute("poster"), "mediaURL": mediaURL}], - "badgeLabel": "Video" + "badgeLabel": mediaType == "audio" ? "Audio" : "Video" }; this.prepMedia(mediaData); return true; diff --git a/ClickToPlugin.fr.safariextension/Info.plist b/ClickToPlugin.fr.safariextension/Info.plist index 61b74786..278f2668 100644 --- a/ClickToPlugin.fr.safariextension/Info.plist +++ b/ClickToPlugin.fr.safariextension/Info.plist @@ -2,56 +2,56 @@ - Author - Marc Hoyois - CFBundleDisplayName - ClickToPlugin - CFBundleIdentifier - com.hoyois.safari.clicktoplugin.fr - CFBundleInfoDictionaryVersion - 6.0 - CFBundleShortVersionString - 1.3.3 - CFBundleVersion - 1.3.3 - Chrome - - Global Page - global.html - - Content - - Scripts - - Start - - functions.js - mediaPlayer.js - ClickToPlugin.js - - - Stylesheets - - styles.css - - - Description - Empêche l'exécution automatique des plugins - ExtensionInfoDictionaryVersion - 1.0 - Permissions - - Website Access - - Include Secure Pages - - Level - All - - - Update Manifest URL - http://hoyois.github.com/safariextensions/updates.plist - Website - http://hoyois.github.com/safariextensions/clicktoplugin/ + Author + Marc Hoyois + CFBundleDisplayName + ClickToPlugin + CFBundleIdentifier + com.hoyois.safari.clicktoplugin.fr + CFBundleInfoDictionaryVersion + 6.0 + CFBundleShortVersionString + 1.3.3 + CFBundleVersion + 1.3.3 + Chrome + + Global Page + global.html + + Content + + Scripts + + Start + + functions.js + mediaPlayer.js + ClickToPlugin.js + + + Stylesheets + + styles.css + + + Description + Empêche l'exécution automatique des plugins + ExtensionInfoDictionaryVersion + 1.0 + Permissions + + Website Access + + Include Secure Pages + + Level + All + + + Update Manifest URL + http://hoyois.github.com/safariextensions/updates.plist + Website + http://hoyois.github.com/safariextensions/clicktoplugin/ diff --git a/ClickToPlugin.fr.safariextension/Settings.plist b/ClickToPlugin.fr.safariextension/Settings.plist index 1c013f63..7dd957e3 100644 --- a/ClickToPlugin.fr.safariextension/Settings.plist +++ b/ClickToPlugin.fr.safariextension/Settings.plist @@ -2,437 +2,437 @@ - - Title - Options MIME - Type - Group - - - DefaultValue - blockAll - Key - block - Title - Bloquer - Titles - - tous les types MIME - tous sauf ceux de la liste verte - seulement ceux de la liste rouge - - Type - RadioButtons - Values - - blockAll - useGreenlist - useRedlist - - - - DefaultValue - - Key - greenlist - Title - Liste verte - Type - TextField - - - DefaultValue - flash, futuresplash, silverlight - Key - redlist - Title - Liste rouge - Type - TextField - - - DefaultValue - - Key - allowQT - Title - Autoriser le plugin QuickTime                           - Type - CheckBox - - - Title - Options vidéo - Type - Group - - - DefaultValue - - Key - useH264 - Title - Rechercher des vidéos pour remplacer              - Type - CheckBox - - - DefaultValue - - Key - replaceFlash - Title - Flash                                              - Type - CheckBox - - - DefaultValue - - Key - replaceSL - Title - Silverlight                                       - Type - CheckBox - - - DefaultValue - - Key - replaceQT - Title - QuickTime                                     - Type - CheckBox - - - DefaultValue - - Key - replaceWM - Title - Windows Media                              - Type - CheckBox - - - DefaultValue - - Key - usePlaylists - Title - Utiliser les listes de lecture                                - Titles - - Ne pas utiliser - N'utiliser qu'en dernier recours - Utiliser sans restrictions - - Type - CheckBox - Values - - 1 - 2 - 3 - - - - DefaultValue - - Key - H264autoload - Title - Effectuer les remplacements automatiquement - Type - CheckBox - - - DefaultValue - autoplay - Key - H264behavior - Title - Une fois effectués - Titles - - Attendre avant de télécharger - Commencer le téléchargement - Commencer la lecture automatiquement - - Type - PopUpButton - Values - - none - buffer - autoplay - - - - DefaultValue - 2 - Key - QTbehavior - Title - Codecs non natifs - Titles - - Ne pas utiliser - N'utiliser qu'en dernier recours - Utiliser sans restrictions - - Type - PopUpButton - Values - - 1 - 2 - 3 - - - - DefaultValue - 3 - Key - maxresolution - Title - Résolution max - Titles - - 480p - 720p - 1080p - 4K - - Type - PopUpButton - Values - - 1 - 2 - 3 - 4 - - - - DefaultValue - 0.5 - Key - volume - MaximumValue - 1 - StepValue - 0.01 - Title - Volume sonore - Type - Slider - - - Title - Liste blanche - Type - Group - - - DefaultValue - - Key - uselocWhitelist - Title - Autoriser les plugins si l'adresse de la page      - Type - CheckBox - - - DefaultValue - www.example.com, www.example2.com - Key - locwhitelist - Title - contient - Type - TextField - - - DefaultValue - - Key - locblacklist - Title - ne contient pas - Type - TextField - - - DefaultValue - - Key - usesrcWhitelist - Title - Autoriser les plugins si l'adresse de l'objet        - Type - CheckBox - - - DefaultValue - - Key - srcwhitelist - Title - contient - Type - TextField - - - DefaultValue - - Key - srcblacklist - Title - ne contient pas - Type - TextField - - - Title - Éléments invisibles - Type - Group - - - DefaultValue - - Key - loadInvisible - Title - Autoriser les éléments aux dimensions             - Type - CheckBox - - - DefaultValue - 1 - Key - maxinvdim - Title - n'excédant pas - Titles - - 1 x 1 px - 2 x 2 px - 4 x 4 px - 8 x 8 px - - Type - PopUpButton - Values - - 1 - 2 - 4 - 8 - - - - Title - Menu contextuel - Type - Group - - - DefaultValue - - Key - useWLcontext - Title - Ajouter à la liste blanche                                   - Type - CheckBox - - - DefaultValue - - Key - useLAcontext - Title - Débloquer tous les plugins                                - Type - CheckBox - - - DefaultValue - - Key - useVScontext - Title - Voir la vidéo sur le site                                      - Type - CheckBox - - - DefaultValue - - Key - useQTcontext - Title - Ouvrir dans QuickTime Player                            - Type - CheckBox - - - Type - Separator - - - DefaultValue - textonly - Key - sifrReplacement - Title - Texte sIFR - Titles - - N'afficher que le texte - Considérer comme Flash - Autoriser - - Type - PopUpButton - Values - - textonly - normal - autoload - - - - DefaultValue - 1 - Key - opacity - MaximumValue - 1 - Secure - - StepValue - 0.01 - Title - Opacité - Type - Slider - - - DefaultValue - - Key - debug - Title - Activer le mode de débogage                            - Type - Hidden - + + Title + Options MIME + Type + Group + + + DefaultValue + blockAll + Key + block + Title + Bloquer + Titles + + tous les types MIME + tous sauf ceux de la liste verte + seulement ceux de la liste rouge + + Type + RadioButtons + Values + + blockAll + useGreenlist + useRedlist + + + + DefaultValue + + Key + greenlist + Title + Liste verte + Type + TextField + + + DefaultValue + flash, futuresplash, silverlight + Key + redlist + Title + Liste rouge + Type + TextField + + + DefaultValue + + Key + allowQT + Title + Autoriser le plugin QuickTime                           + Type + CheckBox + + + Title + Options vidéo + Type + Group + + + DefaultValue + + Key + useH264 + Title + Rechercher des vidéos pour remplacer              + Type + CheckBox + + + DefaultValue + + Key + replaceFlash + Title + Flash                                              + Type + CheckBox + + + DefaultValue + + Key + replaceSL + Title + Silverlight                                       + Type + CheckBox + + + DefaultValue + + Key + replaceQT + Title + QuickTime                                     + Type + CheckBox + + + DefaultValue + + Key + replaceWM + Title + Windows Media                              + Type + CheckBox + + + DefaultValue + + Key + usePlaylists + Title + Utiliser les listes de lecture                                + Titles + + Ne pas utiliser + N'utiliser qu'en dernier recours + Utiliser sans restrictions + + Type + CheckBox + Values + + 1 + 2 + 3 + + + + DefaultValue + + Key + H264autoload + Title + Effectuer les remplacements automatiquement + Type + CheckBox + + + DefaultValue + autoplay + Key + H264behavior + Title + Une fois effectués + Titles + + Attendre avant de télécharger + Commencer le téléchargement + Commencer la lecture automatiquement + + Type + PopUpButton + Values + + none + buffer + autoplay + + + + DefaultValue + 2 + Key + QTbehavior + Title + Codecs non natifs + Titles + + Ne pas utiliser + N'utiliser qu'en dernier recours + Utiliser sans restrictions + + Type + PopUpButton + Values + + 1 + 2 + 3 + + + + DefaultValue + 3 + Key + maxresolution + Title + Résolution max + Titles + + 480p + 720p + 1080p + 4K + + Type + PopUpButton + Values + + 1 + 2 + 3 + 4 + + + + DefaultValue + 0.5 + Key + volume + MaximumValue + 1 + StepValue + 0.01 + Title + Volume sonore + Type + Slider + + + Title + Liste blanche + Type + Group + + + DefaultValue + + Key + uselocWhitelist + Title + Autoriser les plugins si l'adresse de la page      + Type + CheckBox + + + DefaultValue + www.example.com, www.example2.com + Key + locwhitelist + Title + contient + Type + TextField + + + DefaultValue + + Key + locblacklist + Title + ne contient pas + Type + TextField + + + DefaultValue + + Key + usesrcWhitelist + Title + Autoriser les plugins si l'adresse de l'objet        + Type + CheckBox + + + DefaultValue + + Key + srcwhitelist + Title + contient + Type + TextField + + + DefaultValue + + Key + srcblacklist + Title + ne contient pas + Type + TextField + + + Title + Éléments invisibles + Type + Group + + + DefaultValue + + Key + loadInvisible + Title + Autoriser les éléments aux dimensions             + Type + CheckBox + + + DefaultValue + 1 + Key + maxinvdim + Title + n'excédant pas + Titles + + 1 x 1 px + 2 x 2 px + 4 x 4 px + 8 x 8 px + + Type + PopUpButton + Values + + 1 + 2 + 4 + 8 + + + + Title + Menu contextuel + Type + Group + + + DefaultValue + + Key + useWLcontext + Title + Ajouter à la liste blanche                                   + Type + CheckBox + + + DefaultValue + + Key + useLAcontext + Title + Débloquer tous les plugins                                + Type + CheckBox + + + DefaultValue + + Key + useVScontext + Title + Voir la vidéo sur le site                                      + Type + CheckBox + + + DefaultValue + + Key + useQTcontext + Title + Ouvrir dans QuickTime Player                            + Type + CheckBox + + + Type + Separator + + + DefaultValue + textonly + Key + sifrReplacement + Title + Texte sIFR + Titles + + N'afficher que le texte + Considérer comme Flash + Autoriser + + Type + PopUpButton + Values + + textonly + normal + autoload + + + + DefaultValue + 1 + Key + opacity + MaximumValue + 1 + Secure + + StepValue + 0.01 + Title + Opacité + Type + Slider + + + DefaultValue + + Key + debug + Title + Activer le mode de débogage                            + Type + Hidden + diff --git a/ClickToPlugin.fr.safariextension/functions.js b/ClickToPlugin.fr.safariextension/functions.js index 3b440fac..ad40eb3a 100644 --- a/ClickToPlugin.fr.safariextension/functions.js +++ b/ClickToPlugin.fr.safariextension/functions.js @@ -1,91 +1,63 @@ -function getSrcOf(element) { +function getInfo(element, url) { + // gathers attributes of the element that might be needed later on + // Done by a single function so that we only loop once through the children + var info = new Object(); var tmpAnchor = document.createElement("a"); switch (element.tag) { case "embed": + if(element.hasAttribute("qtsrc")) { + tmpAnchor.href = element.getAttribute("qtsrc"); + info.src = tmpAnchor.href; + } + if(element.hasAttribute("href")) { + tmpAnchor.href = element.getAttribute("href"); + info.href = tmpAnchor.href; + } + if(element.hasAttribute("target")) { + info.target = element.getAttribute("target"); + } if(element.hasAttribute("previewimage")) { tmpAnchor.href = element.getAttribute("previewimage"); - element.image = tmpAnchor.href; + info.image = tmpAnchor.href; } - if(element.src) tmpAnchor.href = element.src; - if(element.hasAttribute("qtsrc")) tmpAnchor.href = element.getAttribute("qtsrc"); - element.presource = tmpAnchor.href; - if(element.hasAttribute("target")) element.otherInfo.target = element.getAttribute("target"); - if(element.hasAttribute("href")) { - tmpAnchor.href = element.getAttribute("href"); - } else { - delete element.presource; - if(!element.src) return ""; - } - return tmpAnchor.href; break; case "object": - // NOTE: For silverlight objects element.data is used for something else than the source - // and a param named 'source' is used for the source. So we look for that before - // using element.data var paramElements = element.getElementsByTagName("param"); - var srcParam = null; var qtsrcParam = null; for (i = 0; i < paramElements.length; i++) { - if(!paramElements[i].hasAttribute("value")) continue; + if(!paramElements[i].hasAttribute("value")) continue; var paramName = paramElements[i].getAttribute("name").toLowerCase(); // name attribute is mandatory! - // this is a bit shaky... - // maybe should check first for mimetype and then let getSrcOf depend on type - // eg source for silverlight; src for flash, qt, realplayer; filename for wm... - // this would require 2 successive canLoads? or just use the type attribute?? - // it seems to always be specified for SL, but not QT (uses classid instead) - // damn... maybe better to pass the whole HTMLToString(element) to the global page after all? - if(paramName == "previewimage") { - var tmpAnchor2 = document.createElement("a"); - tmpAnchor2.href = paramElements[i].getAttribute("value"); - element.image = tmpAnchor2.href; - } else if(paramName == "src") { - srcParam = i; - if(!element.presource) { - var tmpAnchor2 = document.createElement("a"); - tmpAnchor2.href = paramElements[i].getAttribute("value"); - element.presource = tmpAnchor2.href; - } - //element.otherInfo.src = paramElements[i].getAttribute("value"); - } else if (paramName == "qtsrc") { - qtsrcParam = i; - var tmpAnchor2 = document.createElement("a"); - tmpAnchor2.href = paramElements[i].getAttribute("value"); - element.presource = tmpAnchor2.href; - //element.otherInfo.qtsrc = paramElements[i].getAttribute("value"); - } else if (paramName == "target") element.otherInfo.target = paramElements[i].getAttribute("value"); - else if(paramName == "movie" || paramName == "source" || paramName == "href" || paramName == "filename") { //|| paramName == "url") { // for oleobject, not supported on Safari (what about the Win version?) - tmpAnchor.href = paramElements[i].getAttribute("value"); + switch(paramName) { + case "source": // Silverlight true source + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.src = tmpAnchor.href; + break; + case "qtsrc": // QuickTime true source + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.src = tmpAnchor.href; + break; + case "href": // QuickTime + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.href = tmpAnchor.href; + break; + case "target": // QuickTime + info.target = paramElements[i].getAttribute("value"); + break; + case "previewimage": // DivX + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.image = tmpAnchor.href; + break; } } - if(tmpAnchor.href) return tmpAnchor.href; - if(qtsrcParam != null) { - element.presource = null; - tmpAnchor.href = paramElements[qtsrcParam].getAttribute("value"); - return tmpAnchor.href; - } else if(srcParam != null) { - element.presource = null; - tmpAnchor.href = paramElements[srcParam].getAttribute("value"); - return tmpAnchor.href; - } - if(element.data) { - tmpAnchor.href = element.data; - return tmpAnchor.href; - } else { - var embedElements = element.getElementsByTagName("embed"); - if(embedElements.length == 0) return ""; - embedElements[0].tag = "embed"; - return getSrcOf(embedElements[0]); - } - return ""; - break; - case "applet": - if(element.code) { - tmpAnchor.href = element.code; - } else if(element.hasAttribute("archive")) { - tmpAnchor.href = element.getAttribute("archive"); - } else return ""; - return tmpAnchor.href; break; } + if(!info.src) { + if(!url) info.src = ""; + else { + tmpAnchor.href = url; + info.src = tmpAnchor.href; + } + } + return info; } function getParamsOf(element) { @@ -132,7 +104,7 @@ function getTypeOf(element) { var paramElements = element.getElementsByTagName("param"); for (i = 0; i < paramElements.length; i++) { if(paramElements[i].getAttribute("name").toLowerCase() == "type") { - return paramElements[i].getAttribute("value"); + return paramElements[i].getAttribute("value"); } } var embedChildren = element.getElementsByTagName("embed"); diff --git a/ClickToPlugin.fr.safariextension/global.html b/ClickToPlugin.fr.safariextension/global.html index 90e01f3e..034769de 100644 --- a/ClickToPlugin.fr.safariextension/global.html +++ b/ClickToPlugin.fr.safariextension/global.html @@ -1,9 +1,10 @@ - - Global HTML + + Global HTML + - + @@ -13,6 +14,6 @@ - - + + \ No newline at end of file diff --git a/ClickToPlugin.fr.safariextension/global.js b/ClickToPlugin.fr.safariextension/global.js index 4e0abdb5..b58bf3b5 100644 --- a/ClickToPlugin.fr.safariextension/global.js +++ b/ClickToPlugin.fr.safariextension/global.js @@ -1,81 +1,19 @@ var CTP_instance = 0; // incremented by one whenever a ClickToPlugin instance with content is created const killers = [new YouTubeKiller(), new VimeoKiller(), new DailymotionKiller(), new VeohKiller(), new JWKiller(), new SLKiller(), new QTKiller(), new WMKiller(), new DivXKiller()]; -function pluginName(plugin) { - if(plugin.name == "Shockwave Flash") return "Flash"; - if(plugin.name == "Silverlight Plug-In") return "Silverlight"; - if(plugin.name.match("Java")) return "Java"; - if(plugin.name.match("QuickTime")) return "QuickTime"; - if(plugin.name.match("Flip4Mac")) return "WM"; - if(plugin.name == "iPhotoPhotocast") return "iPhoto"; - if(plugin.name == "Quartz Composer Plug-In") return "Quartz"; - if(plugin.name == "VideoLAN VLC Plug-in") return "VLC"; - if(plugin.name == "DivX Web Player") return "DivX"; - if(plugin.name == ("RealPlayer Plugin.plugin")) return "RealPlayer"; - return plugin.name; - /*switch (plugin.name) { - case "Flip4Mac Windows Media Web Plugin 2.3.4": return "WM"; - case "Flip4Mac Windows Media Plugin 2.3.4": return "WM"; - case "Silverlight Plug-In": return "Silverlight"; - case "Shockwave Flash": return "Flash"; - case "Switchable Java Plug-in for WebKit": return "Java"; - case "Java Plug-In 2 for NPAPI Browsers": return "Java"; - case "iPhotoPhotocast": return "iPhoto"; - case "QuickTime Plug-in 7.6.6": return "QuickTime"; - case "Quartz Composer Plug-In": return "Quartz"; - default: return plugin.name; - }*/ -} - -/* -LIST OF CLASSID (What is this stuff anyway?) -QuickTime: 02BF25D5-8C17-4B23-BC80-D3488ABDDC6B -WMP 6: 22d6f312-b0f6-11d0-94ab-0080c74c7e95 -WMP >6: 6BF52A52-394A-11D3-B153-00C04F79FAA6 -Flash: d27cdb6e-ae6d-11cf-96b8-444553540000 -Real Player: CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA -?? calendar: 8E27C92B-1264-101C-8A2F-040224009C02 -?? graphics: 369303C2-D7AC-11D0-89D5-00A0C90833E6 -?? slider: F08DF954-8592-11D1-B16A-00C0F0283628 -DivX: 67DABFBF-D0AB-41fa-9C46-CC0F21721616 -*/ - - -function getPluginForType(MIMEType) { // MIMEType is a string - for(var i = 0; i < navigator.plugins.length; i++) { - for(var j = 0; j < navigator.plugins[i].length; j++) { - if(navigator.plugins[i][j].type == MIMEType) return navigator.plugins[i]; - } - } - return null; -} - -function getPluginAndTypeForExt(ext) { - var suffixes = null; - for(var i = 0; i < navigator.plugins.length; i++) { - for(var j = 0; j < navigator.plugins[i].length; j++) { - suffixes = navigator.plugins[i][j].suffixes.split(","); - for(var k = 0; k < suffixes.length; k++) { - if(ext == suffixes[k]) return {"plugin": navigator.plugins[i], "type": navigator.plugins[i][j].type}; - } - } - } - return {"plugin": null, "type": null}; -} - function blockOrAllow(data) { // returns null if element can be loaded, the name of the plugin otherwise // no source and no type -> must allow, it's probably going to pass through here again after being modified by a script if(!data.src && !data.type && !data.classid) return null; // native Safari support - var ext = extractExt(data.src); // used later as well - if(data.type) { - if(isNativeType(data.type)) return null; - } else { - if(isNativeExt(ext)) return null; - } - + var ext = extractExt(data.src); // used later as well + if(data.type) { + if(isNativeType(data.type)) return null; + } else { + if(isNativeExt(ext)) return null; + } + // try not to block objects created by other extensions if(data.src.substring(0,19) == "safari-extension://") return null; @@ -87,55 +25,55 @@ function blockOrAllow(data) { // returns null if element can be loaded, the name } // Deal with whitelisted content - if(safari.extension.settings["uselocWhitelist"]) { + if(safari.extension.settings["uselocWhitelist"]) { var locwhitelist = safari.extension.settings["locwhitelist"].replace(/\s+/g,""); var locblacklist = safari.extension.settings["locblacklist"].replace(/\s+/g,""); - if(locwhitelist) { - locwhitelist = locwhitelist.split(/,(?![^\(]*\))/); + if(locwhitelist) { + locwhitelist = locwhitelist.split(/,(?![^\(]*\))/); // matches all , except those in parentheses (used in regexp) if(matchList(locwhitelist, data.location)) return null; - } - if(locblacklist) { - locblacklist = locblacklist.split(/,(?![^\(]*\))/); - if(!matchList(locblacklist, data.location)) return null; - } + } + if(locblacklist) { + locblacklist = locblacklist.split(/,(?![^\(]*\))/); + if(!matchList(locblacklist, data.location)) return null; + } } - if(safari.extension.settings["usesrcWhitelist"]) { - var srcwhitelist = safari.extension.settings["srcwhitelist"].replace(/\s+/g,""); + if(safari.extension.settings["usesrcWhitelist"]) { + var srcwhitelist = safari.extension.settings["srcwhitelist"].replace(/\s+/g,""); var srcblacklist = safari.extension.settings["srcblacklist"].replace(/\s+/g,""); - if(srcwhitelist) { - srcwhitelist = srcwhitelist.split(/,(?![^\(]*\))/); + if(srcwhitelist) { + srcwhitelist = srcwhitelist.split(/,(?![^\(]*\))/); if(matchList(srcwhitelist, data.src)) return null; - } - if(locblacklist) { - srcblacklist = srcblacklist.split(/,(?![^\(]*\))/); - if(!matchList(srcblacklist, data.src)) return null; - } - } - + } + if(locblacklist) { + srcblacklist = srcblacklist.split(/,(?![^\(]*\))/); + if(!matchList(srcblacklist, data.src)) return null; + } + } + // We use a 'soft' method to get the MIME type // It is not necessarily correct, but always returns a MIME type handled by the correct plugin // To get the correct MIME type an AJAX request would be needed, out of the question here! var plugin = null; - var MIMEType = data.type.replace(/\s+/g,""); - var badgeLabel = "?"; - if(MIMEType) { - badgeLabel = MIMEType.split(";")[0].split("/")[1]; // temporary unless no plugin can be found to play MIMEType - plugin = getPluginForType(MIMEType); - } + var MIMEType = data.type; + var pluginName = "?"; + if(MIMEType) plugin = getPluginForType(MIMEType); if(!plugin && data.src) { var x = getPluginAndTypeForExt(ext); plugin = x.plugin; MIMEType = x.type; } - if(plugin) badgeLabel = pluginName(plugin); + if(plugin) pluginName = getPluginNameFromPlugin(plugin); + else if(MIMEType) pluginName = getPluginNameFromType(MIMEType); + else if(data.classid) pluginName = getPluginNameFromClassid(data.classid.replace("clsid:", "")); - if(safari.extension.settings["allowQT"] && badgeLabel == "QuickTime") return null; + if(safari.extension.settings["allowQT"] && pluginName == "QuickTime") return null; + // Use greenlist/redlist if(MIMEType) { if(safari.extension.settings["block"] == "useRedlist") { var redlist = safari.extension.settings["redlist"].replace(/\s+/g,""); if(!redlist) return null; - redlist = redlist.split(/,(?![^\(]*\))/); // matches all , except those in parentheses (used in regexp) + redlist = redlist.split(/,(?![^\(]*\))/); if(!matchList(redlist, MIMEType, true)) return null; } else if(safari.extension.settings["block"] == "useGreenlist") { var greenlist = safari.extension.settings["greenlist"].replace(/\s+/g,""); @@ -145,18 +83,16 @@ function blockOrAllow(data) { // returns null if element can be loaded, the name } } } - // At this point we know we'll have to block the element + // At this point we know we should block the element - for(var key in data.otherInfo) { - if(key == "target" && data.otherInfo.target == "quicktimeplayer") { - // A quicktime object that would launch QTP - if(confirm("Un objet QuickTime voudrait lancer le lecture de\n\n" + data.src + "\n\ndans QuickTime Player. Voulez-vous l'autoriser?")) { - return null; - } + // Exception: ask the user what to do if a QT object would launch QTP + if(data.launchInQTP) { + if(confirm("Un objet QuickTime voudrait lancer le lecture de\n\n" + data.launchInQTP + "\n\ndans QuickTime Player. Voulez-vous l'autoriser?")) { + return null; } } - return badgeLabel; + return pluginName; } @@ -174,11 +110,6 @@ function respondToMessage(event) { case "killPlugin": killPlugin(event.message); break; - //case "downloadMedia": - //var newTab = safari.application.activeBrowserWindow.openTab("foreground"); - //newTab.url = event.message + ".zip"; - //prompt("Copy the following URL and paste it into the Downloads window.", event.message); - //break; } } @@ -197,73 +128,60 @@ function respondToCanLoad(message) { } } -function printMedia(mediaType) { - switch(mediaType) { - case "video": return "Video"; - case "audio": return "Audio"; - default: return "Video"; - } -} - -function printPlugin(pluginName) { - if(/[A-Z]/.test(pluginName)) return pluginName; - return "Plugin"; -} - function handleContextMenu(event) { - if(!event.userInfo.CTPInstance) { - if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 0) event.contextMenu.appendContextMenuItem("loadall", "Débloquer tous les plugins (" + event.userInfo.blocked + ")"); + if(!event.userInfo.instance) { + if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 0) event.contextMenu.appendContextMenuItem("loadall", "Débloquer tous les plugins (" + event.userInfo.blocked + ")"); if(safari.extension.settings["useWLcontext"]) { - event.contextMenu.appendContextMenuItem("locwhitelist", "Ajouter à la liste blanche\u2026"); + event.contextMenu.appendContextMenuItem("locwhitelist", "Ajouter à la liste blanche\u2026"); } return; } + var pluginName = /[A-Z]/.test(event.userInfo.plugin) ? event.userInfo.plugin : "Plugin"; if(event.userInfo.isH264) { - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",reloadPlugin", "Relancer avec " + event.userInfo.plugin); - if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",qtp", "Ouvrir dans QuickTime Player"); - if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "Voir la vidéo sur " + event.userInfo.siteInfo.name); + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",reload", "Relancer avec " + pluginName); + if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",qtp", "Ouvrir avec QuickTime Player"); + if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "Voir la vidéo sur " + event.userInfo.siteInfo.name); } else { if(event.userInfo.hasH264) { - //event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",video", "Load " + printMedia(event.userInfo.mediaType)); - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",plugin", "Lancer " + printPlugin(event.userInfo.plugin)); - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",remove", "Supprimer " + printPlugin(event.userInfo.plugin)); - //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); - if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",qtp", "Ouvrir dans QuickTime Player"); - if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "Voir la vidéo sur " + event.userInfo.siteInfo.name); - } else { - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",remove", "Supprimer " + printPlugin(event.userInfo.plugin)); - //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); - } + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",plugin", "Lancer " + pluginName); + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",remove", "Supprimer " + pluginName); + //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); + if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",qtp", "Ouvrir avec QuickTime Player"); + if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "Voir la vidéo sur " + event.userInfo.siteInfo.name); + } else { + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",remove", "Supprimer " + pluginName); + //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); + } if(safari.extension.settings["useWLcontext"]) { - event.contextMenu.appendContextMenuItem("srcwhitelist", "Ajouter à la liste blanche\u2026"); + event.contextMenu.appendContextMenuItem("srcwhitelist", "Ajouter à la liste blanche\u2026"); } // BEGIN DEBUG if(safari.extension.settings["debug"]) { - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",show", "Voir l'élément " + event.userInfo.CTPInstance + "." + event.userInfo.elementID); + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",show", "Voir l'élément " + event.userInfo.instance + "." + event.userInfo.elementID); } //END DEBUG } } function doCommand(event) { - switch(event.command) { - case "gotosite": - var newTab = safari.application.activeBrowserWindow.openTab("foreground"); - newTab.url = event.userInfo.siteInfo.url; - break; - case "locwhitelist": - handleWhitelisting(true, event.userInfo.location); - break; - case "srcwhitelist": - handleWhitelisting(false, event.userInfo.src); - break; - case "loadall": - safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadAll", ""); - break; - default: - safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadContent", event.command); - break; - } + switch(event.command) { + case "gotosite": + var newTab = safari.application.activeBrowserWindow.openTab("foreground"); + newTab.url = event.userInfo.siteInfo.url; + break; + case "locwhitelist": + handleWhitelisting(true, event.userInfo.location); + break; + case "srcwhitelist": + handleWhitelisting(false, event.userInfo.src); + break; + case "loadall": + safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadAll", ""); + break; + default: + safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadContent", event.command); + break; + } } function handleWhitelisting (type, url) { @@ -281,25 +199,23 @@ function handleWhitelisting (type, url) { function handleChangeOfSettings(event) { if(event.key == "volume") { - // send to all pages or just the active one?? safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("updateVolume", event.newValue); } else if(event.key = "opacity") { - dispatchMessageToAllPages("updateOpacity", event.newValue); - } + dispatchMessageToAllPages("updateOpacity", event.newValue); + } } -function getSettings() { - var settings = new Object(); - settings.useH264 = safari.extension.settings["useH264"]; - settings.usePlaylists = safari.extension.settings["usePlaylists"]; - //settings.maxres = safari.extension.settings["maxresolution"]; +function getSettings() { // return the settings injected scripts need + var settings = new Object(); + settings.useH264 = safari.extension.settings["useH264"]; + settings.usePlaylists = safari.extension.settings["usePlaylists"]; settings.H264autoload = safari.extension.settings["H264autoload"]; settings.H264behavior = safari.extension.settings["H264behavior"]; settings.volume = safari.extension.settings["volume"]; settings.sifrReplacement = safari.extension.settings["sifrReplacement"]; - settings.opacity = safari.extension.settings["opacity"]; + settings.opacity = safari.extension.settings["opacity"]; settings.debug = safari.extension.settings["debug"]; - return settings; + return settings; } function killPlugin(data) { @@ -307,21 +223,21 @@ function killPlugin(data) { if(killerID == null) return; // BEGIN DEBUG if(safari.extension.settings["debug"]) { - if(!confirm("Killer '" + killers[killerID].name + "' thinks it might be able to process target " + data.CTPInstance +"."+ data.elementID + ".")) return; + if(!confirm("Killer '" + killers[killerID].name + "' thinks it might be able to process target " + data.instance +"."+ data.elementID + ".")) return; } // END DEBUG var callback = function(mediaData) { mediaData.elementID = data.elementID; - mediaData.CTPInstance = data.CTPInstance; + mediaData.instance = data.instance; // the following messsage must be dispatched to all pages to make sure that - // pages or tabs loading in the background get their videoData + // pages or tabs loading in the background get their mediaData dispatchMessageToAllPages("mediaData", mediaData); }; killers[killerID].processElement(data, callback); } function findKillerFor(data) { - for (i = 0; i < killers.length; i++) { + for (i = 0; i < killers.length; i++) { if(killers[i].canKill(data)) return i; } return null; diff --git a/ClickToPlugin.fr.safariextension/globalfunctions.js b/ClickToPlugin.fr.safariextension/globalfunctions.js index 1bc5ad5d..01a68cab 100644 --- a/ClickToPlugin.fr.safariextension/globalfunctions.js +++ b/ClickToPlugin.fr.safariextension/globalfunctions.js @@ -1,40 +1,37 @@ -function makeAbsoluteURI(url) { +function makeAbsoluteURI(url, location) { if(!url) return ""; - var tmpAnchor = document.createElement("a"); - tmpAnchor.href = url; - return tmpAnchor.href; + if(/\/\//.test(url)) return url; // already absolute + location = location.replace(/\/[^\/]*$/, "/"); + if(url[0]=="/") url = url.substring(1); + if(url[0]=="/") { + url = url.substring(1); + location = location.replace(/\/\/.*$/,"//"); + } + return location + url; } -// This function returns false if the url shoudl not be proposed -// as a video replacement, according to the user's settings -/*function getMediaTypeOf(url) { - if (url.match(/(.mp4)|(.mpe{0,1}g)/i) || (safari.extension.settings["QTbehavior"] > 1 && url.match(/(.flv)/i))) return "video"; - if(url.match(/(.mp3)|(.wav)|(.aiff)|(.aac)/i) || (safari.extension.settings["QTbehavior"] > 1 && url.match(/.wma/i))) return "audio"; - return false; -}*/ - function getFlashVariable(flashvars, key) { - if (!flashvars) return ""; - var flashVarsArray = flashvars.split("&"); - for (var i = 0; i < flashVarsArray.length; i++) { - var keyValuePair = flashVarsArray[i].split("="); - if (keyValuePair[0] == key) { - return keyValuePair[1]; - } - } - return ""; + if (!flashvars) return ""; + var flashVarsArray = flashvars.split("&"); + for (var i = 0; i < flashVarsArray.length; i++) { + var keyValuePair = flashVarsArray[i].split("="); + if (keyValuePair[0] == key) { + return keyValuePair[1]; + } + } + return ""; } function getSLVariable(initParams, key) { - if (!initParams) return ""; - var initParamsArray = initParams.split(","); - for (var i = 0; i < initParamsArray.length; i++) { - var keyValuePair = initParamsArray[i].split("="); - if (keyValuePair[0].toLowerCase() == key) { - return keyValuePair[1]; - } - } - return ""; + if (!initParams) return ""; + var initParamsArray = initParams.split(","); + for (var i = 0; i < initParamsArray.length; i++) { + var keyValuePair = initParamsArray[i].split("="); + if (keyValuePair[0].toLowerCase() == key) { + return keyValuePair[1]; + } + } + return ""; } function getMIMEType(resourceURL, handleMIMEType) { @@ -85,10 +82,10 @@ function matchList(list, string, lowerCase) { // set lowerCase to true if 'strin // if s is enclosed in parenthesis, interpret as regexp if (s[0] == "(" && s[s.length - 1] == ")") { try{ - s = new RegExp(s, (lowerCase ? "i" : "")); - } catch (err) { // invalid regexp, just ignore - continue; - } + s = new RegExp(s, (lowerCase ? "i" : "")); + } catch (err) { // invalid regexp, just ignore + continue; + } } else if(lowerCase) { s = s.toLowerCase(); } @@ -97,4 +94,80 @@ function matchList(list, string, lowerCase) { // set lowerCase to true if 'strin } } return false; -} \ No newline at end of file +} + +/*********************** +Plugin detection methods +***********************/ + +function getPluginForType(MIMEType) { // MIMEType is a string + for(var i = 0; i < navigator.plugins.length; i++) { + for(var j = 0; j < navigator.plugins[i].length; j++) { + if(navigator.plugins[i][j].type == MIMEType) return navigator.plugins[i]; + } + } + return null; +} + +function getPluginAndTypeForExt(ext) { + var suffixes = null; + for(var i = 0; i < navigator.plugins.length; i++) { + for(var j = 0; j < navigator.plugins[i].length; j++) { + suffixes = navigator.plugins[i][j].suffixes.split(","); + for(var k = 0; k < suffixes.length; k++) { + if(ext == suffixes[k]) return {"plugin": navigator.plugins[i], "type": navigator.plugins[i][j].type}; + } + } + } + return {"plugin": null, "type": null}; +} + +function getPluginNameFromPlugin(plugin) { + if(plugin.name == "Shockwave Flash") return "Flash"; + if(plugin.name == "Silverlight Plug-In") return "Silverlight"; + if(plugin.name.match("Java")) return "Java"; + if(plugin.name.match("QuickTime")) return "QuickTime"; + if(plugin.name.match("Flip4Mac")) return "WM"; + if(plugin.name == "iPhotoPhotocast") return "iPhoto"; + if(plugin.name == "Quartz Composer Plug-In") return "Quartz"; + if(plugin.name == "VideoLAN VLC Plug-in") return "VLC"; + if(plugin.name == "DivX Web Player") return "DivX"; + if(plugin.name == ("RealPlayer Plugin.plugin")) return "Real"; + return plugin.name; +} + +function getPluginNameFromType(type) { // only used if no installed plugin is found + if(/shockwave-flash/.test(type) || /futuresplash/.test(type)) return "Flash"; + if(/silverlight/.test(type)) return "Silverlight"; + if(/x-java/.test(type)) return "Java"; + if(/x-ms/.test(type)) return "WM"; + if(/x-pn/.test(type)) return "Real"; + type = type.split(";")[0]; + if(type == "video/divx") return "DivX"; + return type.split("/")[1]; +} + +function getPluginNameFromClassid(classid) { // last resort + switch(classid.toLowerCase()) { + case "d27cdb6e-ae6d-11cf-96b8-444553540000": return "Flash"; + case "22d6f312-b0f6-11d0-94ab-0080c74c7e95": return "WM"; + case "6bf52a52-394a-11d3-b153-00c04f79faa6": return "WM"; + case "02bf25d5-8c17-4b23-bc80-d3488abddc6b": return "QuickTime"; + case "cfcdaa03-8be4-11cf-b84b-0020afbbccfa": return "Real"; + case "67dabfbf-d0ab-41fa-9c46-cc0f21721616": return "DivX"; + default: return "?"; + } +} + +/* +LIST OF CLASSIDs +QuickTime: 02BF25D5-8C17-4B23-BC80-D3488ABDDC6B +WMP 6: 22d6f312-b0f6-11d0-94ab-0080c74c7e95 +WMP >6: 6BF52A52-394A-11D3-B153-00C04F79FAA6 +Flash: d27cdb6e-ae6d-11cf-96b8-444553540000 +Real Player: CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA +?? calendar: 8E27C92B-1264-101C-8A2F-040224009C02 +?? graphics: 369303C2-D7AC-11D0-89D5-00A0C90833E6 +?? slider: F08DF954-8592-11D1-B16A-00C0F0283628 +DivX: 67DABFBF-D0AB-41fa-9C46-CC0F21721616 +*/ \ No newline at end of file diff --git a/ClickToPlugin.fr.safariextension/killers/DailymotionKiller.js b/ClickToPlugin.fr.safariextension/killers/DailymotionKiller.js index cee66d97..03fc92ef 100644 --- a/ClickToPlugin.fr.safariextension/killers/DailymotionKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/DailymotionKiller.js @@ -3,7 +3,7 @@ function DailymotionKiller() { } DailymotionKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return (data.src.match("/dmplayerv4/") || data.src.match("www.dailymotion.com")); }; @@ -23,7 +23,7 @@ DailymotionKiller.prototype.processElement = function(data, callback) { DailymotionKiller.prototype.processElementFromSequence = function(sequence, callback) { var posterURL = null; - var videoURL = null; + var videoURL = null; var badgeLabel = "H.264"; if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV) { var URLindex = sequence.indexOf("sdURL"); @@ -33,7 +33,7 @@ DailymotionKiller.prototype.processElementFromSequence = function(sequence, call videoURL = s.replace(/\\\//g,"/"); } } - var URLindex = sequence.indexOf("hqURL"); // there's also an sdURL but it is an FLV video + var URLindex = sequence.indexOf("hqURL"); if (URLindex != -1) { var s = sequence.substring(URLindex+8); s = s.substring(0,s.indexOf("\"")); @@ -48,12 +48,11 @@ DailymotionKiller.prototype.processElementFromSequence = function(sequence, call videoURL = s.replace(/\\\//g,"/"); } } - URLindex = sequence.indexOf("videoPreviewURL"); + URLindex = sequence.indexOf("videoPreviewURL"); if (URLindex != -1) { var s = sequence.substring(URLindex+18); s = s.substring(0,s.indexOf("\"")); posterURL = s.replace(/\\\//g,"/"); - //alert(posterURL); } var videoData = { "playlist": [{"mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], @@ -69,10 +68,10 @@ DailymotionKiller.prototype.processElementFromVideoID = function(videoID, callba req.open("GET", "http://www.dailymotion.com/video/" + videoID, true); req.onload = function() { var sequence = req.responseText.match(toMatch)[0]; - var callbackForEmbed = function(videoData) { - videoData.playlist[0].siteInfo = {"name": "Dailymotion", "url": "http://www.dailymotion.com/video/" + videoID}; - callback(videoData); - } + var callbackForEmbed = function(videoData) { + videoData.playlist[0].siteInfo = {"name": "Dailymotion", "url": "http://www.dailymotion.com/video/" + videoID}; + callback(videoData); + } if(sequence) {_this.processElementFromSequence(unescape(sequence), callbackForEmbed);} }; // BEGIN DEBUG diff --git a/ClickToPlugin.fr.safariextension/killers/DivXKiller.js b/ClickToPlugin.fr.safariextension/killers/DivXKiller.js index 80232ccc..6a4185c8 100644 --- a/ClickToPlugin.fr.safariextension/killers/DivXKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/DivXKiller.js @@ -1,5 +1,5 @@ function DivXKiller() { - this.name = "DivXKiller"; + this.name = "DivXKiller"; } @@ -9,9 +9,9 @@ DivXKiller.prototype.canKill = function(data) { DivXKiller.prototype.processElement = function(data, callback) { - var videoData = { + var videoData = { "playlist": [{"mediaType": "video", "posterURL": data.image, "mediaURL": data.src}], "badgeLabel": "Video" }; - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.fr.safariextension/killers/JWKiller.js b/ClickToPlugin.fr.safariextension/killers/JWKiller.js index d380792d..b6e0fe9e 100644 --- a/ClickToPlugin.fr.safariextension/killers/JWKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/JWKiller.js @@ -1,93 +1,96 @@ function JWKiller() { - this.name = "JWKiller"; + this.name = "JWKiller"; } JWKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; - return (getFlashVariable(data.params, "file") || getFlashVariable(data.params, "playlistfile")); + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + // streams are not supported + return (!getFlashVariable(data.params, "streamer") && (getFlashVariable(data.params, "file") || getFlashVariable(data.params, "playlistfile"))); }; JWKiller.prototype.processElement = function(data, callback) { - var playlistURL = getFlashVariable(data.params, "playlistfile"); + var playlistURL = getFlashVariable(data.params, "playlistfile"); var sourceURL = getFlashVariable(data.params, "file"); - var posterURL = getFlashVariable(data.params, "image"); - - if(safari.extension.settings["usePlaylists"]) { - if(playlistURL) { - this.processElementFromPlaylist(playlistURL, getFlashVariable(data.params, "item"), posterURL, callback); - return; - } - if(sourceURL.match(".xml")) { - this.processElementFromPlaylist(sourceURL, getFlashVariable(data.params, "item"), posterURL, callback); - return; - } - } + var posterURL = getFlashVariable(data.params, "image"); - var sourceURL2 = getFlashVariable(data.params, "real_file"); - if(sourceURL2) sourceURL = sourceURL2; - - var mediaType = checkSrc(sourceURL); - if(!mediaType) return; + if(safari.extension.settings["usePlaylists"]) { + if(playlistURL) { + this.processElementFromPlaylist(playlistURL, data.location, getFlashVariable(data.params, "item"), posterURL, callback); + return; + } + if(/.xml($|\?)/i.test(sourceURL)) { + this.processElementFromPlaylist(sourceURL, data.location, getFlashVariable(data.params, "item"), posterURL, callback); + return; + } + } + + var sourceURL2 = getFlashVariable(data.params, "real_file"); + if(sourceURL2) sourceURL = sourceURL2; + + var mediaType = checkSrc(sourceURL); + if(!mediaType) return; - var mediaData = { - "playlist": [{"mediaType": mediaType, "posterURL": makeAbsoluteURI(posterURL), "mediaURL": makeAbsoluteURI(sourceURL)}], + var mediaData = { + "playlist": [{"mediaType": mediaType, "posterURL": makeAbsoluteURI(posterURL, data.location), "mediaURL": makeAbsoluteURI(sourceURL, data.location)}], "badgeLabel": (mediaType == "video") ? "Video" : "Audio" }; - callback(mediaData); + callback(mediaData); }; // put a more complete function in globalfunctions.js function checkSrc(sourceURL) { - if (sourceURL.match(/(.mp4)|(.mpe{0,1}g)/i)) return "video"; - if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV && sourceURL.match(/.flv/i)) return "video"; - if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/(.wmv)|(.asf)/i)) return "video"; - if(sourceURL.match(/(.mp3)|(.wav)|(.aiff)|(.aac)/i)) return "audio"; - if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/.wma/i)) return "audio"; - return false; + if (sourceURL.match(/.mp4|.mpe{0,1}g|.mov/i)) return "video"; + if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV && sourceURL.match(/.flv/i)) return "video"; + if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/.wmv|.asf/i)) return "video"; + if(sourceURL.match(/.mp3|.wav|.aiff|.aac/i)) return "audio"; + if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/.wma/i)) return "audio"; + return false; }; -JWKiller.prototype.processElementFromPlaylist = function(playlistURL, track, posterURL, callback) { - var req = new XMLHttpRequest(); - var startTrack = track; - playlistURL = makeAbsoluteURI(playlistURL); +JWKiller.prototype.processElementFromPlaylist = function(playlistURL, location, track, posterURL, callback) { + var req = new XMLHttpRequest(); + var startTrack = track; + playlistURL = makeAbsoluteURI(playlistURL, location); req.open('GET', playlistURL, true); req.onload = function() { + var isAudio = true; var x = req.responseXML.getElementsByTagName("track"); - if(!(track >= 0 && track < x.length)) track = 0; - var playlist = new Array(); - var url = ""; - var list = null; - var title = ""; - var poster = null; - for(var i = 0; i < x.length; i++) { - list = x[(i + track) % x.length].getElementsByTagName("location"); - if(list.length > 0) url = list[0].firstChild.nodeValue; - else url = ""; - list = x[(i + track) % x.length].getElementsByTagName("title"); - if(list.length > 0) title = list[0].firstChild.nodeValue; - else title = ""; - list = x[(i + track) % x.length].getElementsByTagName("image"); - if(list.length > 0) poster = list[0].firstChild.nodeValue; - else poster = ""; - var mediaType = checkSrc(url); - if(mediaType) { - playlist.push({"title": title, "mediaType": mediaType, "posterURL": makeAbsoluteURI(poster), "mediaURL": makeAbsoluteURI(url)}); - if(mediaType == "video") isAudio = false; - } else { - if(i >= x.length - track) --startTrack; - } - } - if(playlist.length == 0) return; - if(!playlist[0].posterURL) playlist[0].posterURL = makeAbsoluteURI(posterURL); + if(!(track >= 0 && track < x.length)) track = 0; + var playlist = new Array(); + var url = ""; + var list = null; + var title = ""; + var poster = null; + for(var i = 0; i < x.length; i++) { + list = x[(i + track) % x.length].getElementsByTagName("location"); + if(list.length > 0) url = list[0].firstChild.nodeValue; + else if(i == 0) return; + else continue; + list = x[(i + track) % x.length].getElementsByTagName("title"); + if(list.length > 0) title = list[0].firstChild.nodeValue; + else title = ""; + list = x[(i + track) % x.length].getElementsByTagName("image"); + if(list.length > 0) poster = list[0].firstChild.nodeValue; + else poster = ""; + var mediaType = checkSrc(url); + if(mediaType) { + playlist.push({"title": title, "mediaType": mediaType, "posterURL": makeAbsoluteURI(poster, location), "mediaURL": makeAbsoluteURI(url, location)}); + if(mediaType == "video") isAudio = false; + } else { + if(i == 0) return; + if(i >= x.length - track) --startTrack; + } + } + if(!playlist[0].posterURL) playlist[0].posterURL = makeAbsoluteURI(posterURL, location); var mediaData = { - "startTrack": startTrack, - "isAudio": isAudio, - "playlist": playlist, - "badgeLabel": isAudio ? "Audio" : "Video" - }; - callback(mediaData); + "startTrack": startTrack, + "isAudio": isAudio, + "playlist": playlist, + "badgeLabel": playlist[0].mediaType == "audio" ? "Audio" : "Video" + }; + callback(mediaData); }; - // BEGIN DEBUG + // BEGIN DEBUG if(safari.extension.settings["debug"]) { if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + playlistURL)) return; } diff --git a/ClickToPlugin.fr.safariextension/killers/QTKiller.js b/ClickToPlugin.fr.safariextension/killers/QTKiller.js index f7e83ad5..297914bf 100644 --- a/ClickToPlugin.fr.safariextension/killers/QTKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/QTKiller.js @@ -1,5 +1,5 @@ function QTKiller() { - this.name = "QTKiller"; + this.name = "QTKiller"; } @@ -13,11 +13,11 @@ QTKiller.prototype.processElement = function(data, callback) { var playlist = null; if(data.presrc) playlist = [{"mediaType": "video", "mediaURL": data.presrc}, {"mediaType": "video", "mediaURL": data.src}]; else playlist = [{"mediaType": "video", "mediaURL": data.src}]; - var videoData = { - "noPlaylistControls": true, + var videoData = { + "noPlaylistControls": true, "playlist": playlist, "badgeLabel": "Video" }; - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.fr.safariextension/killers/SLKiller.js b/ClickToPlugin.fr.safariextension/killers/SLKiller.js index 958b0d74..1b1538b0 100644 --- a/ClickToPlugin.fr.safariextension/killers/SLKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/SLKiller.js @@ -1,5 +1,5 @@ function SLKiller() { - this.name = "SLKiller"; + this.name = "SLKiller"; } @@ -14,9 +14,9 @@ SLKiller.prototype.processElement = function(data, callback) { if(!videoURL.match(/\.((wm(?!x))|(asf))/)) return; var posterURL = getSLVariable(data.params, "thumbnail"); - var videoData = { + var videoData = { "playlist": [{"mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], "badgeLabel": "Video" } - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.fr.safariextension/killers/VeohKiller.js b/ClickToPlugin.fr.safariextension/killers/VeohKiller.js index e2e4535a..72beda1c 100644 --- a/ClickToPlugin.fr.safariextension/killers/VeohKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/VeohKiller.js @@ -1,39 +1,38 @@ function VeohKiller() { - this.name = "VeohKiller"; + this.name = "VeohKiller"; } VeohKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return (safari.extension.settings["QTbehavior"] > 1 && canPlayFLV && data.src.match("veoh.com/static/swf/webplayer")); }; VeohKiller.prototype.processElement = function(data, callback) { var videoID = null; - var isEmbed = false; + var isEmbed = false; if(data.params) videoID = getFlashVariable(data.params, "permalinkId"); else { // embedded video - isEmbed = true; + isEmbed = true; var matches = data.src.match(/permalinkId=([^&]*)(?=&)/); if(matches) videoID = matches[0].replace("permalinkId=",""); } var posterURL = null; var videoURL = null; - var request = new XMLHttpRequest(); - request.open('GET', "http://www.veoh.com/rest/video/" + videoID + "/details", true); + var request = new XMLHttpRequest(); + request.open('GET', "http://www.veoh.com/rest/video/" + videoID + "/details", true); request.onload = function() { var element = request.responseXML.getElementsByTagName("video")[0]; if(element) { videoURL = element.getAttribute("fullPreviewHashPath"); //"fullPreviewHashLowPath" posterURL = element.getAttribute("fullHighResImagePath"); - //alert(element.getAttribute("previewHash") + "\n" + element.getAttribute("fullPreviewHashPath") + "\n\n" + element.getAttribute("previewHashLow") + "\n" + element.getAttribute("fullPreviewHashLowPath")); } var videoData = { "playlist": [{"mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], "badgeLabel": "Video" // There's no HD on Veoh, as far as I see, despite what they say. It's < 360p! Am I doing something wrong?? } - if(isEmbed) videoData.playlist[0].siteInfo = {"name": "Veoh", "url": "http://www.veoh.com/browse/videos#watch%3D" + videoID}; + if(isEmbed) videoData.playlist[0].siteInfo = {"name": "Veoh", "url": "http://www.veoh.com/browse/videos#watch%3D" + videoID}; callback(videoData); }; // BEGIN DEBUG @@ -41,5 +40,5 @@ VeohKiller.prototype.processElement = function(data, callback) { if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + "http://www.veoh.com/rest/video/" + videoID + "/details")) return; } // END DEBUG - request.send(null); + request.send(null); }; \ No newline at end of file diff --git a/ClickToPlugin.fr.safariextension/killers/VimeoKiller.js b/ClickToPlugin.fr.safariextension/killers/VimeoKiller.js index 6a2e54ea..26f1c9d4 100644 --- a/ClickToPlugin.fr.safariextension/killers/VimeoKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/VimeoKiller.js @@ -3,7 +3,7 @@ function VimeoKiller() { } VimeoKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return !!data.src.match("moogaloop"); }; @@ -17,40 +17,38 @@ VimeoKiller.prototype.processElement = function(data, callback) { if(!videoID) return; var posterURL = null; - var videoURL = null; + var videoURL = null; var badgeLabel = "H.264"; - var request = new XMLHttpRequest(); - request.open('GET', "http://www.vimeo.com/moogaloop/load/clip:" + videoID + "/", false); - //alert("sending AJAX request..."); + var req = new XMLHttpRequest(); + // this request needs to be synchronous, otherwise Vimeo scripts cause errors + req.open('GET', "http://www.vimeo.com/moogaloop/load/clip:" + videoID + "/", false); // BEGIN DEBUG if(safari.extension.settings["debug"]) { if(!confirm("Killer '" + this.name + "' is about to send a synchronous AJAX request to:\n\n" + "http://www.vimeo.com/moogaloop/load/clip:" + videoID + "/")) return; } // END DEBUG - request.send(null); - //alert("request sent! Answer:\n\n" + request.responseText); - var responseXML = new DOMParser().parseFromString(request.responseText,"text/xml"); + req.send(null); if (safari.extension.settings["maxresolution"] > 1) { - if(responseXML.getElementsByTagName("isHD").length > 0) { - if(responseXML.getElementsByTagName("isHD")[0].childNodes[0].nodeValue == "1") badgeLabel = "HD H.264"; + if(req.responseXML.getElementsByTagName("isHD").length > 0) { + if(req.responseXML.getElementsByTagName("isHD")[0].childNodes[0].nodeValue == "1") badgeLabel = "HD H.264"; } } - if(responseXML.getElementsByTagName("request_signature").length > 0 && responseXML.getElementsByTagName("request_signature_expires").length > 0) { - videoURL = "http://www.vimeo.com/moogaloop/play/clip:" + videoID + "/" + responseXML.getElementsByTagName("request_signature")[0].childNodes[0].nodeValue+ "/" + responseXML.getElementsByTagName("request_signature_expires")[0].childNodes[0].nodeValue+"/?q=" + ((badgeLabel == "H.264") ? "mobile" : "hd"); - } - if(responseXML.getElementsByTagName("thumbnail").length > 0) { - posterURL = responseXML.getElementsByTagName("thumbnail")[0].childNodes[0].nodeValue; + if(req.responseXML.getElementsByTagName("request_signature").length > 0 && req.responseXML.getElementsByTagName("request_signature_expires").length > 0) { + videoURL = "http://www.vimeo.com/moogaloop/play/clip:" + videoID + "/" + req.responseXML.getElementsByTagName("request_signature")[0].childNodes[0].nodeValue+ "/" + req.responseXML.getElementsByTagName("request_signature_expires")[0].childNodes[0].nodeValue+"/?q=" + ((badgeLabel == "H.264") ? "mobile" : "hd"); } - var siteInfo = null; - if(!data.location.match("vimeo.com/")) siteInfo = {"name": "Vimeo", "url": "http://vimeo.com/" + videoID}; + if(req.responseXML.getElementsByTagName("thumbnail").length > 0) { + posterURL = req.responseXML.getElementsByTagName("thumbnail")[0].childNodes[0].nodeValue; + } + var siteInfo = null; + if(!data.location.match("vimeo.com/") || data.location == "http://vimeo.com/") siteInfo = {"name": "Vimeo", "url": "http://vimeo.com/" + videoID}; var videoData = { "playlist": [{"siteInfo": siteInfo, "mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], "badgeLabel": badgeLabel }; - // Some videos on Vimeo are FLV; need to check that this is not the case, cause Safari can't handle them + // Some videos on Vimeo are FLV; need to check that this is not the case if user doesn't want them if(videoURL) { if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV) { callback(videoData); @@ -63,7 +61,7 @@ VimeoKiller.prototype.processElement = function(data, callback) { // BEGIN DEBUG else if(safari.extension.settings["debug"]) { alert("Video found by killer 'VimeoKiller' has MIME type " + MIMEType + " and cannot be played natively by QuickTime."); - } + } // END DEBUD }; // BEGIN DEBUG @@ -72,30 +70,5 @@ VimeoKiller.prototype.processElement = function(data, callback) { } // END DEBUG getMIMEType(videoURL, handleMIMEType); - /*request = new XMLHttpRequest(); - request.open('HEAD', videoURL, true); - var gotContentType = false; - request.onreadystatechange = function () { - if(!gotContentType && request.getResponseHeader('Content-Type')) { - gotContentType = true; - if(request.getResponseHeader('Content-Type') != "video/x-flv") { - callback(videoData); - } - // BEGIN DEBUG - else if(safari.extension.settings["debug"]) { - alert("Video found by killer 'VimeoKiller' has MIME type " + request.getResponseHeader('Content-Type') + " and cannot be played by Safari."); - } - // END DEBUD - request.abort(); - } - }; - // BEGIN DEBUG - if(safari.extension.settings["debug"]) { - if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + videoURL)) return; - } - // END DEBUG - request.send(null);*/ - //alert(request.responseText); - //alert(req.getResponseHeader('Content-Type').split(';')[0]); } }; diff --git a/ClickToPlugin.fr.safariextension/killers/WMKiller.js b/ClickToPlugin.fr.safariextension/killers/WMKiller.js index c9001c1d..e4361592 100644 --- a/ClickToPlugin.fr.safariextension/killers/WMKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/WMKiller.js @@ -1,17 +1,17 @@ function WMKiller() { - this.name = "WMKiller"; + this.name = "WMKiller"; } WMKiller.prototype.canKill = function(data) { - return (data.plugin == "WM" && safari.extension.settings["replaceWM"] && safari.extension.settings["QTbehavior"] > 1 && canPlayWM && data.src.match(/\.((wm(?!x))|(asf))/i)); + return (data.plugin == "WM" && safari.extension.settings["replaceWM"] && safari.extension.settings["QTbehavior"] > 1 && canPlayWM && data.src.match(/\.((wm(?!x)|asf))/i)); }; // should check media type... WMKiller.prototype.processElement = function(data, callback) { - var videoData = { + var videoData = { "playlist": [{"mediaType": "video", "mediaURL": data.src}], "badgeLabel": "Video" }; - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.fr.safariextension/killers/YouTubeKiller.js b/ClickToPlugin.fr.safariextension/killers/YouTubeKiller.js index 547e00d7..397e18aa 100644 --- a/ClickToPlugin.fr.safariextension/killers/YouTubeKiller.js +++ b/ClickToPlugin.fr.safariextension/killers/YouTubeKiller.js @@ -1,181 +1,162 @@ function YouTubeKiller() { - this.name = "YouTubeKiller"; + this.name = "YouTubeKiller"; } YouTubeKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return (data.src.match("ytimg.com") || data.src.match("youtube.com") || data.src.match("youtube-nocookie.com")); }; YouTubeKiller.prototype.processElement = function(data, callback) { if(data.params) { - if(safari.extension.settings["usePlaylists"]) { - // see http://apiblog.youtube.com/2010/03/upcoming-change-to-youtube-video-page.html - // these new YT urls break everything!!!! Video data is loaded dynamically - // after the Flash has been loaded... no more flashvars :( - var URLvars = data.location.split("?")[1]; - var playlistID = null; - if(URLvars) { - URLvars = URLvars.split("&"); - for (var i = 0; i < URLvars.length; i++) { - var keyValuePair = URLvars[i].split("="); - if (keyValuePair[0] == "p") { - playlistID = keyValuePair[1]; - break; - } - } - } - if(playlistID) { - this.buildVideoIDList(data.params, playlistID, 0, new Array(), callback); - } else this.processElementFromFlashVars(data.params, callback); - } else this.processElementFromFlashVars(data.params, callback); + if(safari.extension.settings["usePlaylists"]) { + var URLvars = data.location.split(/#!|\?/)[1]; + var playlistID = null; + if(URLvars) { + URLvars = URLvars.split("&"); + for (var i = 0; i < URLvars.length; i++) { + var keyValuePair = URLvars[i].split("="); + if (keyValuePair[0] == "p") { + playlistID = keyValuePair[1]; + break; + } + } + } + if(playlistID) { + this.buildVideoIDList(data.params, data.location, playlistID, 0, new Array(), callback); + } else this.processElementFromFlashVars(data.params, data.location, callback); + } else this.processElementFromFlashVars(data.params, data.location, callback); return; } // The vid has no flashvars... Only hope is that it is a YouTube /v/ embed var index = data.src.indexOf(".com/v/"); if(index == -1) { - if(safari.extension.settings["usePlaylists"]) { - index = data.src.indexOf(".com/p/"); - if(index == -1) return; - var playlistID = data.src.substring(index + 7); - index = playlistID.indexOf(".com/p/"); - if(index != -1) playlistID = playlistID.substring(index + 7); - index = playlistID.search(/\?|&/); - if(index != -1) playlistID = playlistID.substring(0,index); - this.buildVideoIDList(null, playlistID, 0, new Array(), callback); - } - return; - } + if(safari.extension.settings["usePlaylists"]) { + index = data.src.indexOf(".com/p/"); + if(index == -1) return; + var playlistID = data.src.substring(index + 7); + index = playlistID.indexOf(".com/p/"); + if(index != -1) playlistID = playlistID.substring(index + 7); + index = playlistID.search(/\?|&/); + if(index != -1) playlistID = playlistID.substring(0,index); + this.buildVideoIDList(null, data.location, playlistID, 0, new Array(), callback); + } + return; + } var videoID = data.src.substring(index + 7); - index = videoID.indexOf(".com/v/"); - if(index != -1) videoID = videoID.substring(index + 7); + index = videoID.indexOf(".com/v/"); + if(index != -1) videoID = videoID.substring(index + 7); index = videoID.search(/\?|&/); if(index != -1) videoID = videoID.substring(0,index); this.processElementFromVideoID(videoID, callback); }; -/*YouTubeKiller.prototype.processElementFromPlaylist = function(videoID, plaslistID, callback) { - var playlistURL = function(i) { - return "feed://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50" - }; - var isEmpty = false; - var i = 0; - var req = null; - while(!isEmpty) { - req = new XMLHttpRequest(); - req.open('GET', playlistURL(i), true); - req.onload() - } -};*/ - -// this function adds playlist data -YouTubeKiller.prototype.buildVideoIDList = function(flashvars, playlistID, i, videoIDList, callback) { - req = new XMLHttpRequest(); - req.open('GET', "http://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50", true); - var _this = this; - req.onload = function() { - //alert(req.responseText); - var entries = req.responseXML.getElementsByTagName("entry"); - // this is not always the correct number!! - //if(!imax) imax = parseInt(responseXML.getElementsByTagNameNS("http://a9.com/-/spec/opensearchrss/1.0/", "totalResults")[0].firstChild.nodeValue); - for(var j = 0; j < entries.length; j++) { - try{ - videoIDList.push(entries[j].getElementsByTagNameNS("http://search.yahoo.com/mrss/", "player")[0].getAttribute("url").match(/\?v=[^(&|\?)]*(?=(&|\?))/)[0].replace("?v=","")); - } catch(err) {} - } - if(entries.length < 50) {// we've got the whole list of videoIDs - var track = 0; - var length = videoIDList.length; - if(flashvars) { - var videoID = getFlashVariable(flashvars, "video_id"); - for(var j = 0; j < videoIDList.length; j++) { - if(videoIDList[0] == videoID) {track = j; break;} - videoIDList.push(videoIDList.shift()); - } - //alert(videoIDList.length + " tracks\n\n" + videoIDList.join("\n")); - // load the first video at once - } - var callbackForPlaylist = function(videoData) { - videoData.playlistLength = length; - videoData.startTrack = track; - if(videoData.playlist[0].siteInfo) videoData.playlist[0].siteInfo.url += "&p=" + playlistID; - callback(videoData); - }; - if(flashvars) _this.processElementFromFlashVars(flashvars, callbackForPlaylist); - else _this.processElementFromVideoID(videoIDList[0], callbackForPlaylist); - videoIDList.shift(); - _this.buildPlaylist(videoIDList, playlistID, true, 3, callback); - return; - } - _this.buildVideoIDList(flashvars, playlistID, ++i, videoIDList, callback); - }; - // BEGIN DEBUG +YouTubeKiller.prototype.buildVideoIDList = function(flashvars, location, playlistID, i, videoIDList, callback) { + req = new XMLHttpRequest(); + req.open('GET', "http://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50", true); + var _this = this; + req.onload = function() { + var entries = req.responseXML.getElementsByTagName("entry"); + for(var j = 0; j < entries.length; j++) { + try{ + videoIDList.push(entries[j].getElementsByTagNameNS("http://search.yahoo.com/mrss/", "player")[0].getAttribute("url").match(/\?v=[^(&|\?)]*(?=(&|\?))/)[0].replace("?v=","")); + } catch(err) {} + } + if(entries.length < 50) {// we've got the whole list of videoIDs + var track = 0; + var length = videoIDList.length; + if(flashvars) { + var videoID = getFlashVariable(flashvars, "video_id"); + if(!videoID) { // new YT AJAX player + var matches = location.match(/(!|&)v=[^&]+(&|$)/); + if(!matches) return; + videoID = matches[0].substring(3).replace("&", ""); + flashvars = null; + } + for(var j = 0; j < videoIDList.length; j++) { + if(videoIDList[0] == videoID) {track = j; break;} + videoIDList.push(videoIDList.shift()); + } + } + var callbackForPlaylist = function(videoData) { + videoData.playlistLength = length; + videoData.startTrack = track; + if(videoData.playlist[0].siteInfo) videoData.playlist[0].siteInfo.url += "&p=" + playlistID; + callback(videoData); + }; + // load the first video at once + if(flashvars) _this.processElementFromFlashVars(flashvars, location, callbackForPlaylist); + else _this.processElementFromVideoID(videoIDList[0], callbackForPlaylist); + videoIDList.shift(); + // load the rest of the playlist 3 by 3 + _this.buildPlaylist(videoIDList, playlistID, true, 3, callback); + return; + } + _this.buildVideoIDList(flashvars, location, playlistID, ++i, videoIDList, callback); + }; + // BEGIN DEBUG if(safari.extension.settings["debug"]) { if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + "http://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50")) return; } // END DEBUG - req.send(null); + req.send(null); }; YouTubeKiller.prototype.buildPlaylist = function(videoIDList, playlistID, isFirst, n, callback) { - if(videoIDList.length == 0) return; - var j = 0; - var jmax = videoIDList.length; - if(isFirst) --n; - if(jmax > n) jmax = n; // load by groups of n - if(isFirst) ++n; - var mediaData = {"loadAfter": true, "missed": 0, "playlist": []}; - var _this = this; - var next = function(videoData) { - // this actually works!! - if(videoData.playlist.length > 0) { - videoData.playlist[0].siteInfo.url += "&p=" + playlistID; - mediaData.playlist.push(videoData.playlist[0]); - } else {// playlist is 1 shorter than announced - ++mediaData.missed; - } - ++j; - //alert(mediaData.playlist.length); - if(j == jmax) { - callback(mediaData); - // continue building - _this.buildPlaylist(videoIDList, playlistID, false, n, callback); - } else _this.processElementFromVideoID(videoIDList.shift(), next); - }; - this.processElementFromVideoID(videoIDList.shift(), next); - return; + if(videoIDList.length == 0) return; + var j = 0; + var jmax = videoIDList.length; + if(isFirst) --n; + if(jmax > n) jmax = n; // load by groups of n + if(isFirst) ++n; + var mediaData = {"loadAfter": true, "missed": 0, "playlist": []}; + var _this = this; + var next = function(videoData) { + // this actually works!! + if(videoData.playlist.length > 0) { + videoData.playlist[0].siteInfo.url += "&p=" + playlistID; + mediaData.playlist.push(videoData.playlist[0]); + } else { // playlist is 1 shorter than announced + ++mediaData.missed; + } + ++j; + if(j == jmax) { + callback(mediaData); + _this.buildPlaylist(videoIDList, playlistID, false, n, callback); + } else _this.processElementFromVideoID(videoIDList.shift(), next); + }; + this.processElementFromVideoID(videoIDList.shift(), next); + return; }; YouTubeKiller.prototype.getMediaDataFromURLMap = function(videoID, videoHash, urlMap) { - var availableFormats = []; + var availableFormats = []; var formatInfo = urlMap.split(","); - // alert(formatInfo); - for (var i = 0; i < formatInfo.length; i++) { + for (var i = 0; i < formatInfo.length; i++) { var format = formatInfo[i].split("|"); - availableFormats[format[0]] = format[1]; - } - - var posterURL = "http://i.ytimg.com/vi/" + videoID + "/hqdefault.jpg"; - // this is the 360p video URL - var videoURL = "http://www.youtube.com/get_video?fmt=18&asv=&video_id=" + videoID + "&t=" + videoHash; - var badgeLabel = "H.264"; + availableFormats[format[0]] = format[1]; + } - // Get the highest-quality version set by the user - //var format = 18; + var posterURL = "http://i.ytimg.com/vi/" + videoID + "/hqdefault.jpg"; + // this is the 360p MP4 video URL, always available + var videoURL = "http://www.youtube.com/get_video?fmt=18&asv=&video_id=" + videoID + "&t=" + videoHash; + var badgeLabel = "H.264"; - //alert(req.responseText); - // Only 18, 22, 37, and 38 are h264 playable without plugin in Safari. - // Other container are FLV (0, 5, 6, 34, 35, although the latter two are H.264 360p and 480p), 3GP (13,17), or WebM (43,45) and cannot play natively (even with Perian) - if (availableFormats[38] && safari.extension.settings["maxresolution"] > 3) {// 4K @_@ + /* + Only 18, 22, 37, and 38 are MP4 playable nativey by QuickTime. + Other containers are FLV (0, 5, 6, 34, 35, the latter two are H.264 360p and 480p), + 3GP (13,17), or WebM (43,45) + */ + if (availableFormats[38] && safari.extension.settings["maxresolution"] > 3) {// 4K @_@ badgeLabel = "4K H.264"; - videoURL = availableFormats[38]; + videoURL = availableFormats[38]; } else if (availableFormats[37] && safari.extension.settings["maxresolution"] > 2) {// 1080p badgeLabel = "HD H.264"; - videoURL = availableFormats[37]; - } else if (availableFormats[22] && safari.extension.settings["maxresolution"] > 1) {// 720p + videoURL = availableFormats[37]; + } else if (availableFormats[22] && safari.extension.settings["maxresolution"] > 1) {// 720p badgeLabel = "HD H.264"; - videoURL = availableFormats[22]; - } else if (safari.extension.settings["QTbehavior"] > 2 && canPlayFLV) { + videoURL = availableFormats[22]; + } else if (safari.extension.settings["QTbehavior"] > 2 && canPlayFLV) { if (availableFormats[35]) { // 480p FLV videoURL = availableFormats[35]; } @@ -194,20 +175,28 @@ YouTubeKiller.prototype.getMediaDataFromURLMap = function(videoID, videoHash, ur this.getSDH264FromFmt18(posterURL, videoID, callback); return; // NOTE: possibility 2 seems to always work, so possibility 1 is never needed }*/ - return {"posterURL": posterURL, "videoURL": videoURL, "badgeLabel": badgeLabel}; + return {"posterURL": posterURL, "videoURL": videoURL, "badgeLabel": badgeLabel}; }; -YouTubeKiller.prototype.processElementFromFlashVars = function(flashvars, callback) { - var title = unescape(getFlashVariable(flashvars, "rec_title")).substring(3).replace(/\+/g, " "); +YouTubeKiller.prototype.processElementFromFlashVars = function(flashvars, location, callback) { var videoID = getFlashVariable(flashvars, "video_id"); + // see http://apiblog.youtube.com/2010/03/upcoming-change-to-youtube-video-page.html: + if(!videoID) { // new YT AJAX player (not yet used?) + var matches = location.match(/(!|&)v=[^&]+(&|$)/); + if(!matches) return; + videoID = matches[0].substring(3).replace("&", ""); + this.processElementFromVideoID(videoID, callback); + return; + } var videoHash = getFlashVariable(flashvars, "t"); if(!videoHash) { this.processElementFromVideoID(videoID, callback); return; } - var urlMap = unescape(getFlashVariable(flashvars, "fmt_url_map")); + var title = unescape(getFlashVariable(flashvars, "rec_title")).substring(3).replace(/\+/g, " "); + var urlMap = unescape(getFlashVariable(flashvars, "fmt_url_map")); var x = this.getMediaDataFromURLMap(videoID, videoHash, urlMap); - var videoData = { + var videoData = { "playlist": [{"title": title, "mediaType": "video", "posterURL": x.posterURL, "mediaURL": x.videoURL}], "badgeLabel": x.badgeLabel }; @@ -215,36 +204,35 @@ YouTubeKiller.prototype.processElementFromFlashVars = function(flashvars, callba }; YouTubeKiller.prototype.processElementFromVideoID = function(videoID, callback) { - if(!videoID) return; // needed! + if(!videoID) return; // needed! var toMatch = /\"fmt_url_map\":\s\"[^\"]*\"/; //"// works for both Flash and HTML5 Beta player pages var toMatch2 = /\"t\":\s\"[^\"]*\"/; //"// var _this = this; var req = new XMLHttpRequest (); req.open("GET", "http://www.youtube.com/watch?v=" + videoID, true); req.onload = function() { - var title = ""; - if(safari.extension.settings["usePlaylists"]) { - var toMatchTitle = / 1; - if(this.usePlaylistControls) { - this.initializePlaylistControls(); - } else { - this.initializeDownloadControls(); - } + } + + // Set dimensions + this.width = width; + this.height = height; + this.containerElement.style.width = width + "px"; + this.containerElement.style.height = height + "px"; + + // Set volume + this.mediaElement.volume = volume; + + // Set listeners + var _this = this; // need anonymous function in listeners otherwise the 'this' will refer to the mediaElement! + this.mediaElement.addEventListener("contextmenu", function(event) {_this.setContextInfo(event, contextInfo);}, false); + this.mediaElement.addEventListener("loadedmetadata", function() {_this.fixAspectRatio();}, false); + this.mediaElement.addEventListener("ended", function() {_this.nextTrack();}, false); + this.mediaElement.ondblclick = function() { + _this.switchLoop(); + }; + + if(this.usePlaylistControls) { + this.initializePlaylistControls(); + } else { + this.initializeDownloadControls(); + } }; mediaPlayer.prototype.initializePlaylistControls = function() { - this.playlistControls = document.createElement("div"); - this.playlistControls.className = "CTFplaylistControls"; - - var trackTitle = document.createElement("div"); - trackTitle.className = "CTFtrackTitle"; - this.playlistControls.appendChild(trackTitle); - - var trackSelect = document.createElement("div"); - trackSelect.className = "CTFtrackSelect"; - this.playlistControls.appendChild(trackSelect); - - var trackTitleText = document.createElement("div"); - trackTitleText.className = "CTFtrackTitleText"; - trackTitle.appendChild(trackTitleText); - - var trackTitleTextP = document.createElement("p"); - trackTitleText.appendChild(trackTitleTextP); - - var prevButton = document.createElement("div"); - prevButton.className = "CTFprevButton"; - trackSelect.appendChild(prevButton); - - var trackInput = document.createElement("form"); - trackInput.className = "CTFtrackInput"; - trackSelect.appendChild(trackInput); - - var nextButton = document.createElement("div"); - nextButton.className = "CTFnextButton"; - trackSelect.appendChild(nextButton); + this.playlistControls = document.createElement("div"); + this.playlistControls.className = "CTFplaylistControls"; + + var trackTitle = document.createElement("div"); + trackTitle.className = "CTFtrackTitle"; + this.playlistControls.appendChild(trackTitle); + + var trackSelect = document.createElement("div"); + trackSelect.className = "CTFtrackSelect"; + this.playlistControls.appendChild(trackSelect); + + var trackTitleText = document.createElement("div"); + trackTitleText.className = "CTFtrackTitleText"; + trackTitle.appendChild(trackTitleText); + + var trackTitleTextP = document.createElement("p"); + trackTitleText.appendChild(trackTitleTextP); + + var prevButton = document.createElement("div"); + prevButton.className = "CTFprevButton"; + trackSelect.appendChild(prevButton); + + var trackInput = document.createElement("form"); + trackInput.className = "CTFtrackInput"; + trackSelect.appendChild(trackInput); + + var nextButton = document.createElement("div"); + nextButton.className = "CTFnextButton"; + trackSelect.appendChild(nextButton); - trackInput.innerHTML = "/" + normalize(this.playlist.length, this.playlistLength) + ""; - - var _this = this; - this.mediaElement.onmouseover = function(event) { - this.focus = true; - if(!this.paused && this.readyState > 1) _this.fadeIn(.05); - }; - this.playlistControls.onmouseover = function(event) { - _this.mediaElement.focus = true; - if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeIn(0); - }; - this.mediaElement.onmouseout = function(event) { - // prevents the default controls from disappearing - if(event.relatedTarget && (event.relatedTarget == prevButton || event.relatedTarget == nextButton || event.relatedTarget == trackInput.firstChild || event.relatedTarget == trackTitleTextP.lastChild || event.relatedTarget.hasAttribute("precision"))) { - event.preventDefault(); - } else { - this.focus = false; - if(!this.paused && this.readyState > 1) _this.fadeOut(0); - } - }; - this.playlistControls.onmouseout = function(event) { - _this.mediaElement.focus = false; - if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeOut(.1); - }; - this.mediaElement.focus = false; - this.mediaElement.addEventListener("pause", function(){_this.fadeIn(0);}, false); - this.mediaElement.addEventListener("play", function(){if(!_this.mediaElement.focus) _this.fadeOut(0);}, false); - - trackInput.onsubmit = function(event) { - event.preventDefault(); - var track = this.getElementsByTagName("input")[0].value; - if(!(/^\d+$/.test(track))) return; - track = parseInt(track); - if(track < 1 || track > _this.playlistLength) return; - track = (track - _this.startTrack - 1 + _this.playlistLength) % _this.playlistLength; - if(track == _this.currentTrack) return; - if(track < _this.playlist.length) { - _this.loadTrack(track, true); - } - }; - prevButton.onclick = function() { - if(_this.playlist.length == 1) return; - _this.loadTrack(_this.currentTrack - 1, true); - }; - nextButton.onclick = function() { - if(_this.playlist.length == 1) return; - _this.loadTrack(_this.currentTrack + 1, true); - }; - - // If the controls are shown at once a webkit bug will mess up font smoothing - // when the video starts playing. - // The only way I was able to prevent this is to show controls on loadedmetadata - this.playlistControls.style.opacity = "0"; - this.containerElement.appendChild(this.playlistControls); + trackInput.innerHTML = "/" + normalize(this.playlist.length, this.playlistLength) + ""; + + var _this = this; + this.mediaElement.onmouseover = function(event) { + this.focus = true; + if(!this.paused && this.readyState > 1) _this.fadeIn(.05); + }; + this.playlistControls.onmouseover = function(event) { + _this.mediaElement.focus = true; + if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeIn(0); + }; + this.mediaElement.onmouseout = function(event) { + // prevents the default controls from disappearing + if(event.relatedTarget && (event.relatedTarget == prevButton || event.relatedTarget == nextButton || event.relatedTarget == trackInput.firstChild || event.relatedTarget == trackTitleTextP.lastChild || event.relatedTarget.hasAttribute("precision"))) { + event.preventDefault(); + } else { + this.focus = false; + if(!this.paused && this.readyState > 1) _this.fadeOut(0); + } + }; + this.playlistControls.onmouseout = function(event) { + _this.mediaElement.focus = false; + if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeOut(.1); + }; + this.mediaElement.focus = false; + this.mediaElement.addEventListener("pause", function(){_this.fadeIn(0);}, false); + this.mediaElement.addEventListener("play", function(){if(!_this.mediaElement.focus) _this.fadeOut(0);}, false); + + trackInput.onsubmit = function(event) { + event.preventDefault(); + var track = this.getElementsByTagName("input")[0].value; + if(!(/^\d+$/.test(track))) return; + track = parseInt(track); + if(track < 1 || track > _this.playlistLength) return; + track = (track - _this.startTrack - 1 + _this.playlistLength) % _this.playlistLength; + if(track == _this.currentTrack) return; + if(track < _this.playlist.length) { + _this.loadTrack(track, true); + } + }; + prevButton.onclick = function() { + if(_this.playlist.length == 1) return; + _this.loadTrack(_this.currentTrack - 1, true); + }; + nextButton.onclick = function() { + if(_this.playlist.length == 1) return; + _this.loadTrack(_this.currentTrack + 1, true); + }; + + this.playlistControls.style.opacity = "1"; + this.containerElement.appendChild(this.playlistControls); }; mediaPlayer.prototype.initializeDownloadControls = function() { - this.playlistControls = document.createElement("div"); - this.playlistControls.className = "CTFplaylistControls"; - - var hoverElement = document.createElement("div"); - hoverElement.className = "CTFhoverElement"; - this.playlistControls.innerHTML = "

"; - - this.playlistControls.appendChild(hoverElement); - - var _this = this; - - this.playlistControls.firstChild.onmouseout = function(event) { - this.nextSibling.style.display = "block"; - _this.fadeOut(0) - }; - - this.playlistControls.firstChild.onmouseover = function(event) { - if(event.relatedTarget && event.relatedTarget.className == "CTFhoverElement") { - this.nextSibling.style.display = "none"; - _this.fadeIn(0) - } - }; - hoverElement.onmouseover = function(event) { - this.previousSibling.style.display ="block"; - } - - hoverElement.onmouseout = function(event) { - if(!event.relatedTarget || event.relatedTarget.className != "CTFtitleText") { - this.previousSibling.style.display ="none"; - } - }; - - this.mediaElement.onmouseout = function(event) { - if(event.relatedTarget && (event.relatedTarget == hoverElement || event.relatedTarget.className == "CTFtitleText")) { - event.preventDefault(); - } - } - - this.playlistControls.addEventListener("webkitTransitionEnd", function() {if(this.style.opacity == "0") this.firstChild.style.display = "none";}, false); - - this.playlistControls.style.opacity = "0"; - this.playlistControls.firstChild.style.display = "none"; - //this.playlistControls.firstChild.style.pointerEvents = "auto"; - - this.containerElement.appendChild(this.playlistControls); + this.playlistControls = document.createElement("div"); + this.playlistControls.className = "CTFplaylistControls"; + + var hoverElement = document.createElement("div"); + hoverElement.className = "CTFhoverElement"; + this.playlistControls.innerHTML = "

"; + + this.playlistControls.appendChild(hoverElement); + + var _this = this; + + this.playlistControls.firstChild.onmouseout = function(event) { + this.nextSibling.style.display = "block"; + _this.fadeOut(0) + }; + + this.playlistControls.firstChild.onmouseover = function(event) { + if(event.relatedTarget && event.relatedTarget.className == "CTFhoverElement") { + this.nextSibling.style.display = "none"; + _this.fadeIn(0) + } + }; + hoverElement.onmouseover = function(event) { + this.previousSibling.style.display ="block"; + } + + hoverElement.onmouseout = function(event) { + if(!event.relatedTarget || event.relatedTarget.className != "CTFtitleText") { + this.previousSibling.style.display ="none"; + } + }; + + this.mediaElement.onmouseout = function(event) { + if(event.relatedTarget && (event.relatedTarget == hoverElement || event.relatedTarget.className == "CTFtitleText")) { + event.preventDefault(); + } + } + + this.playlistControls.addEventListener("webkitTransitionEnd", function() {if(this.style.opacity == "0") this.firstChild.style.display = "none";}, false); + + this.playlistControls.style.opacity = "0"; + this.playlistControls.firstChild.style.display = "none"; + + this.containerElement.appendChild(this.playlistControls); }; mediaPlayer.prototype.fadeOut = function(delay) { - this.playlistControls.style.WebkitTransition = "opacity .4s linear " + delay + "s"; - this.playlistControls.style.opacity = "0"; + this.playlistControls.style.WebkitTransition = "opacity .4s linear " + delay + "s"; + this.playlistControls.style.opacity = "0"; }; mediaPlayer.prototype.fadeIn = function(delay) { - this.playlistControls.style.WebkitTransition = "opacity .05s linear " + delay + "s"; - this.playlistControls.style.opacity = "0.93"; + this.playlistControls.style.WebkitTransition = "opacity .05s linear " + delay + "s"; + this.playlistControls.style.opacity = "0.93"; }; mediaPlayer.prototype.fixAspectRatio = function() { - var w = this.mediaElement.videoWidth; - var h = this.mediaElement.videoHeight; - if(w == 0 || h == 0) { // audio source + var w = this.mediaElement.videoWidth; + var h = this.mediaElement.videoHeight; + if(w == 0 || h == 0) { // audio source //this.mediaElement.style.height = "24px"; // the height of the controls this.mediaElement.style.width = this.width + "px"; this.mediaElement.style.height = this.height + "px"; - if(this.playlistControls) this.playlistControls.style.width = this.width + "px"; } else if (w/h > this.width/this.height) { this.mediaElement.style.width = this.width + "px"; this.mediaElement.style.height = ""; - if(this.playlistControls) this.playlistControls.style.width = this.width + "px"; } else { this.mediaElement.style.height = this.height + "px"; this.mediaElement.style.width = ""; - // Apparently webkit uses floor, not round - if(this.playlistControls) this.playlistControls.style.width = Math.floor(w/h*this.height) + "px"; + if(this.playlistControls) { + // Apparently QuickTime uses floor, not round + var width = Math.floor(w/h*this.height); + this.playlistControls.style.width = width + "px"; + if(this.usePlaylistControls) this.playlistControls.getElementsByTagName("p")[0].style.width = (width - this.playlistControls.getElementsByClassName("CTFtrackSelect")[0].offsetWidth - 12) + "px"; + } + } + if(this.usePlaylistControls) { + // need this otherwise a webkit bug messes up font smoothing + this.fadeIn(.05); } - if(this.usePlaylistControls) { - this.fadeIn(.05); - } - + }; mediaPlayer.prototype.resetAspectRatio = function() { - this.mediaElement.style.width = this.width + "px"; - this.mediaElement.style.height = this.height + "px"; - if(this.playlistControls) this.playlistControls.style.width = this.width + "px"; + this.mediaElement.style.width = this.width + "px"; + this.mediaElement.style.height = this.height + "px"; + if(this.playlistControls) { + this.playlistControls.style.width = this.width + "px"; + if(this.usePlaylistControls) this.playlistControls.getElementsByTagName("p")[0].style.width = (this.width - this.playlistControls.getElementsByClassName("CTFtrackSelect")[0].offsetWidth - 12) + "px"; + } }; mediaPlayer.prototype.switchLoop = function() { - if(this.mediaElement.hasAttribute("loop")) this.mediaElement.removeAttribute("loop"); - else this.mediaElement.setAttribute("loop", "true"); + if(this.mediaElement.hasAttribute("loop")) this.mediaElement.removeAttribute("loop"); + else this.mediaElement.setAttribute("loop", "true"); } mediaPlayer.prototype.nextTrack = function() { - if(!this.mediaElement.hasAttribute("loop")) { - if(this.currentTrack + this.startTrack + 1 == this.playlistLength) return; - this.loadTrack(this.currentTrack + 1, true); - } + if(!this.mediaElement.hasAttribute("loop")) { + if(this.currentTrack + this.startTrack + 1 == this.playlistLength) return; + this.loadTrack(this.currentTrack + 1, true); + } }; mediaPlayer.prototype.loadTrack = function(track, autoplay) { - track = track % this.playlist.length; + track = track % this.playlist.length; if(track < 0) track += this.playlist.length; // weird JS behavior - this.resetAspectRatio(); + this.resetAspectRatio(); this.mediaElement.src = this.playlist[track].mediaURL; - // If src is not set before poster, poster is not shown. Webkit bug? - if(this.playlist[track].posterURL) { - if(this.playlist[track].mediaType == "video") { - this.mediaElement.poster = this.playlist[track].posterURL; - this.mediaElement.style.background = ""; - // this.mediaElement.style.backgroundSize = ""; - } else { - if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); - this.mediaElement.style.backgroundImage = "url('" + this.playlist[track].posterURL + "')"; - this.mediaElement.style.backgroundRepeat = "no-repeat"; - this.mediaElement.style.backgroundPosition = "center center"; - // this.mediaElement.style.backgroundSize = "100 %"; - } - } else { - if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); - this.mediaElement.style.background = ""; - } - this.currentTrack = track; - if(autoplay) { + // If src is not set before poster, poster is not shown. Webkit bug? + if(this.playlist[track].posterURL) { + if(this.playlist[track].mediaType == "video") { + this.mediaElement.poster = this.playlist[track].posterURL; + this.mediaElement.style.background = ""; + // this.mediaElement.style.backgroundSize = ""; + } else { + if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); + this.mediaElement.style.backgroundImage = "url('" + this.playlist[track].posterURL + "')"; + this.mediaElement.style.backgroundRepeat = "no-repeat"; + this.mediaElement.style.backgroundPosition = "center center"; + // this.mediaElement.style.backgroundSize = "100 %"; + } + } else { + if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); + this.mediaElement.style.background = ""; + } + this.currentTrack = track; + if(autoplay) { this.mediaElement.setAttribute("preload", "auto"); this.mediaElement.setAttribute("autoplay", "autoplay"); } - if(this.usePlaylistControls) { - var title = this.playlist[track].title; - if(!title) title = "(no title)"; - title = "" + title + ""; - this.playlistControls.getElementsByTagName("p")[0].innerHTML = ((track + this.startTrack) % this.playlistLength + 1) + ". " + title; - var inputField = this.playlistControls.getElementsByTagName("input")[0]; - var newInputField = document.createElement("input"); - newInputField.setAttribute("type", "text"); - newInputField.setAttribute("value", (track + this.startTrack) % this.playlistLength + 1); - newInputField.style.width = (8 * this.playlistLength.toString().length) + "px"; - // simply changing the value does not update if user has used the field - this.playlistControls.getElementsByTagName("form")[0].replaceChild(newInputField, inputField); - //this.playlistControls.getElementsByTagName("input")[0].setAttribute("value", track + this.startTrack + 1); - } else { - var title = "Télécharger " + (this.playlist[track].mediaType == "audio" ? "l'audio" : "la vidéo"); - title = "" + title + ""; - this.playlistControls.getElementsByTagName("p")[0].innerHTML = title; - } + if(this.usePlaylistControls) { + var title = this.playlist[track].title; + if(!title) title = "(no title)"; + title = "" + title + ""; + this.playlistControls.getElementsByTagName("p")[0].innerHTML = ((track + this.startTrack) % this.playlistLength + 1) + ". " + title; + var inputField = this.playlistControls.getElementsByTagName("input")[0]; + var newInputField = document.createElement("input"); + newInputField.setAttribute("type", "text"); + newInputField.setAttribute("value", (track + this.startTrack) % this.playlistLength + 1); + newInputField.style.width = (8 * this.playlistLength.toString().length) + "px"; + // simply changing the value does not update if user has used the field + this.playlistControls.getElementsByTagName("form")[0].replaceChild(newInputField, inputField); + // this.playlistControls.getElementsByTagName("input")[0].setAttribute("value", track + this.startTrack + 1); + } else { + var title = "Télécharger " + (this.playlist[track].mediaType == "audio" ? "l'audio" : "la vidéo"); + title = "" + title + ""; + this.playlistControls.getElementsByTagName("p")[0].innerHTML = title; + } }; mediaPlayer.prototype.setContextInfo = function(event, contextInfo) { - var track = this.currentTrack; - if(track == null) track = 0; - contextInfo.mediaType = this.playlist[track].mediaType; - contextInfo.siteInfo = this.playlist[track].siteInfo; - //if(this.mediaElement) contextInfo.loop = this.mediaElement.hasAttribute("loop"); - // contextInfo.isPlaylist = (this.playlist.length > 1); // not used - safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); - event.stopPropagation(); + var track = this.currentTrack; + if(track == null) track = 0; + contextInfo.mediaType = this.playlist[track].mediaType; + contextInfo.siteInfo = this.playlist[track].siteInfo; + // if(this.mediaElement) contextInfo.loop = this.mediaElement.hasAttribute("loop"); + // contextInfo.isPlaylist = (this.playlist.length > 1); // not used + safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); + event.stopPropagation(); }; mediaPlayer.prototype.addToPlaylist = function(playlist, init) { - if(init) this.playlist = playlist.concat(this.playlist); - else this.playlist = this.playlist.concat(playlist); - if(this.usePlaylistControls && this.playlistControls) { - this.playlistControls.getElementsByTagName("span")[0].innerHTML = "/" + normalize(this.playlist.length + this.startTrack, this.playlistLength); - var width = this.playlistControls.offsetWidth - this.playlistControls.getElementsByClassName("CTFtrackSelect")[0].offsetWidth; - this.playlistControls.getElementsByTagName("p")[0].style.width = (width - 7) + "px"; - } + if(init) this.playlist = playlist.concat(this.playlist); + else this.playlist = this.playlist.concat(playlist); + if(this.usePlaylistControls && this.playlistControls) { + this.playlistControls.getElementsByTagName("span")[0].innerHTML = "/" + normalize(this.playlist.length + this.startTrack, this.playlistLength); + } }; function normalize(n,m) { - if(n > m) return m.toString(); - var string = n.toString(); - while(string.length < m.toString().length) { - string = "0" + string; - } - return string; + if(n > m) return m.toString(); + var string = n.toString(); + while(string.length < m.toString().length) { + string = "0" + string; + } + return string; } diff --git a/ClickToPlugin.fr.safariextension/next.png b/ClickToPlugin.fr.safariextension/next.png index 0cea8b02..72e161b6 100644 Binary files a/ClickToPlugin.fr.safariextension/next.png and b/ClickToPlugin.fr.safariextension/next.png differ diff --git a/ClickToPlugin.fr.safariextension/next_active.png b/ClickToPlugin.fr.safariextension/next_active.png index 3ec0a0ca..4c0fd79d 100644 Binary files a/ClickToPlugin.fr.safariextension/next_active.png and b/ClickToPlugin.fr.safariextension/next_active.png differ diff --git a/ClickToPlugin.fr.safariextension/prev.png b/ClickToPlugin.fr.safariextension/prev.png index a8f8888b..b79a46b6 100644 Binary files a/ClickToPlugin.fr.safariextension/prev.png and b/ClickToPlugin.fr.safariextension/prev.png differ diff --git a/ClickToPlugin.fr.safariextension/prev_active.png b/ClickToPlugin.fr.safariextension/prev_active.png index fcbd8c5b..1b41e105 100644 Binary files a/ClickToPlugin.fr.safariextension/prev_active.png and b/ClickToPlugin.fr.safariextension/prev_active.png differ diff --git a/ClickToPlugin.fr.safariextension/styles.css b/ClickToPlugin.fr.safariextension/styles.css index 63aa3f6b..1a17ac7d 100644 --- a/ClickToPlugin.fr.safariextension/styles.css +++ b/ClickToPlugin.fr.safariextension/styles.css @@ -1,11 +1,11 @@ /* Reset random stuff that might be set by the parent page */ .clickToFlashPlaceholder * { - margin: 0 !important; - padding: 0 !important; - border: 0 !important; - border-radius: 0 !important; - text-align: left !important; - background: none !important; + margin: 0 !important; + padding: 0 !important; + border: 0 !important; + border-radius: 0 !important; + text-align: left !important; + background: none !important; } /********************* @@ -13,11 +13,11 @@ *********************/ .clickToFlashPlaceholder { - background: -webkit-gradient(linear, left top, left bottom, from(rgba(235, 235, 235, 1)), to(rgba(190, 190, 190, 1))) !important; - border: 1px solid rgba(0, 0, 0, 0.5) !important; - -webkit-user-select: none !important; - display: inline-block !important; - -webkit-box-sizing: border-box !important; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(235, 235, 235, 1)), to(rgba(190, 190, 190, 1))) !important; + border: 1px solid rgba(0, 0, 0, 0.5) !important; + -webkit-user-select: none !important; + display: inline-block !important; + -webkit-box-sizing: border-box !important; } .clickToFlashPlaceholder.notable { @@ -29,39 +29,39 @@ } .clickToFlashPlaceholder:active { - background: -webkit-gradient(linear, left bottom, left top, from(rgba(225, 225, 225, 1)), to(rgba(200, 200, 200, 1))) !important; + background: -webkit-gradient(linear, left bottom, left top, from(rgba(225, 225, 225, 1)), to(rgba(200, 200, 200, 1))) !important; } .clickToFlashPlaceholderContainer { - position: relative !important; - width: inherit !important; - height: inherit !important; - margin: -1px !important; + position: relative !important; + width: inherit !important; + height: inherit !important; + margin: -1px !important; overflow: visible !important; } div.logoVerticalPosition { - display: table-cell !important; - vertical-align: middle !important; - width: inherit !important; - height: inherit !important; + display: table-cell !important; + vertical-align: middle !important; + width: inherit !important; + height: inherit !important; } .logoHorizontalPosition { - float: left !important; - position: relative !important; - left: 50% !important; - height: auto !important; - width: auto !important; + float: left !important; + position: relative !important; + left: 50% !important; + height: auto !important; + width: auto !important; overflow: visible !important; } .clickToFlashPlaceholder .logoContainer { - float: left !important; - position: relative !important; - left: -50% !important; - width: auto !important; - height: auto !important; + float: left !important; + position: relative !important; + left: -50% !important; + width: auto !important; + height: auto !important; overflow: visible !important; } @@ -75,40 +75,41 @@ div.logoVerticalPosition { } .clickToFlashPlaceholder .logo { - border-radius: 8px !important; - border: 4px solid rgba(0, 0, 0, 0.3) !important; - font-size: 20px !important; + border-radius: 8px !important; + border: 4px solid rgba(0, 0, 0, 0.3) !important; + font-size: 20px !important; font-style: normal !important; - width: auto !important; + width: auto !important; margin: 0 !important; - color: rgba(0, 0, 0, 0.5) !important; - font-weight: bold !important; - padding: 14px 5px 7px 5px !important; - -webkit-user-select: none !important; - cursor: default !important; - background: none !important; - position: relative !important; - height: auto !important; - line-height: 0 !important; + color: rgba(0, 0, 0, 0.5) !important; + font-weight: bold !important; + padding: 14px 5px 7px 5px !important; + -webkit-user-select: none !important; + white-space: nowrap !important; + cursor: default !important; + background: none !important; + position: relative !important; + height: auto !important; + line-height: 0 !important; font-family: "Helvetica Neue", Helvetica, sans-serif !important; } .clickToFlashPlaceholder .logo.inset { - color: rgba(255, 255, 255, 0.45) !important; - border-color: rgba(255, 255, 255, 0.45) !important; - position: absolute !important; - top: 1px !important; + color: rgba(255, 255, 255, 0.45) !important; + border-color: rgba(255, 255, 255, 0.45) !important; + position: absolute !important; + top: 1px !important; font-family: "Helvetica Neue", Helvetica, sans-serif !important; } .clickToFlashPlaceholder .logoContainer.mini .logo, .clickToFlashPlaceholder .logoContainer.hidden .logo.tmp { color: rgba(50, 50, 50, 0.4) !important; - font-size: 10px !important; + font-size: 10px !important; font-style: normal !important; - border: 2px solid rgba(50, 50, 50, 0.2) !important; - padding: 7px 2px 4px 2px !important; - border-radius: 4px !important; + border: 2px solid rgba(50, 50, 50, 0.2) !important; + padding: 7px 2px 4px 2px !important; + border-radius: 4px !important; } .clickToFlashPlaceholder .logoContainer.hidden .logo.tmp { @@ -116,182 +117,182 @@ div.logoVerticalPosition { } .clickToFlashPlaceholder .logoContainer.mini .logo.inset { - color: rgba(255, 255, 255, 0.25) !important; - border-color: rgba(255, 255, 255, 0.25) !important; + color: rgba(255, 255, 255, 0.25) !important; + border-color: rgba(255, 255, 255, 0.25) !important; } .CTFvideoContainer { - position: relative !important; - display: inline-block !important; - margin: 0 !important; - padding: 0 !important; + position: relative !important; + display: inline-block !important; + margin: 0 !important; + padding: 0 !important; } .CTFvideoElement { - display: block !important; - position: relative !important; - margin: 0px auto 0px auto !important; + display: block !important; + position: relative !important; + margin: 0px auto 0px auto !important; } .CTFQTObject { - position: absolute !important; - width: 0px !important; - height: 0px !important; + position: absolute !important; + width: 0px !important; + height: 0px !important; } /* PLAYLIST CONTROLS */ .CTFplaylistControls { - z-index: 1 !important; - -webkit-user-select: none !important; - display: block !important; - position: relative !important; - bottom: 48px !important; - margin: 0px auto 0px auto !important; - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - padding: 0px !important; - height: 24px !important; - background-image: url('controls_background.png') !important; - background-repeat: repeat-x !important; - /*opacity: 0 !important;*/ - /*-webkit-transition: opacity .5s linear !important;*/ - pointer-events: none !important; - text-rendering: auto !important; + z-index: 1 !important; + -webkit-user-select: none !important; + display: block !important; + position: relative !important; + bottom: 48px !important; + margin: 0px auto 0px auto !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + padding: 0px !important; + height: 24px !important; + background-image: url('controls_background.png') !important; + background-repeat: repeat-x !important; + /*opacity: 0 !important;*/ + /*-webkit-transition: opacity .5s linear !important;*/ + pointer-events: none !important; + text-rendering: auto !important; } .CTFtitleText { - color: white !important; - pointer-events: auto !important; - text-decoration: none !important; - border: 0 !important; + color: white !important; + pointer-events: auto !important; + text-decoration: none !important; + border: 0 !important; } .CTFtitleText:hover { - text-decoration: underline !important; + text-decoration: underline !important; } /*:hover.CTFplaylistControls { - opacity: 1 !important; + opacity: 1 !important; }*/ .CTFplaylistControls div { - height: inherit !important; + height: inherit !important; } .CTFtrackSelect div.CTFprevButton { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - margin: 0 !important; - padding: 0 !important; - width: 35px !important; - background-image: url('prev.png') !important; - background-repeat: no-repeat !important; - background-position: center center !important; - pointer-events: auto !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + margin: 0 !important; + padding: 0 !important; + width: 37px !important; + background-image: url('prev.png') !important; + background-repeat: no-repeat !important; + background-position: center center !important; + pointer-events: auto !important; } .CTFtrackSelect div.CTFprevButton:active { - background-image: url('prev_active.png') !important; + background-image: url('prev_active.png') !important; } .CTFtrackSelect form.CTFtrackInput { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - padding: 0 !important; - margin: 0 !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + padding: 0 !important; + margin: 0 !important; } .CTFtrackSelect div.CTFnextButton { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - margin: 0 0 0 0 !important; - padding: 0 0 0 0 !important; - width: 35px !important; - background-image: url('next.png') !important; - background-repeat: no-repeat !important; - background-position: center center !important; - pointer-events: auto !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + margin: 0 0 0 0 !important; + padding: 0 0 0 0 !important; + width: 37px !important; + background-image: url('next.png') !important; + background-repeat: no-repeat !important; + background-position: center center !important; + pointer-events: auto !important; } .CTFtrackSelect div.CTFnextButton:active { - background-image: url('next_active.png') !important; + background-image: url('next_active.png') !important; } .CTFtrackTitle { - position: absolute !important; - top: 0px !important; - left: 0px !important; - height: 24px !important; + position: absolute !important; + top: 0px !important; + left: 0px !important; + height: 24px !important; } .CTFtrackSelect { - position: absolute !important; - top: 0px !important; - right: 6px !important; - /*margin: 0 2px 0 0 !important;*/ + position: absolute !important; + top: 0px !important; + right: 4px !important; + /*margin: 0 2px 0 0 !important;*/ } .CTFtrackTitle div.CTFtrackTitleText { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - padding: 0 5px 0 7px !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + padding: 0 5px 0 7px !important; } .CTFtrackTitle div.CTFtrackTitleText p { - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - margin: 0 !important; - padding: 0 !important; - white-space: nowrap !important; - overflow: hidden !important; - text-overflow: ellipsis !important; - text-align: left !important; - line-height: normal !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + margin: 0 !important; + padding: 0 !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + text-align: left !important; + line-height: normal !important; } .CTFtrackSelect input { - background-color: rgba(255, 255, 255, 0.1) !important; - border: 1px solid rgba(0, 0, 0, 1) !important; - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - margin: 0 !important; - padding: 0 !important; - text-align: right !important; - pointer-events: auto !important; - height: 12px !important; - line-height: 1 !important; + background-color: rgba(255, 255, 255, 0.1) !important; + border: 1px solid rgba(0, 0, 0, 1) !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + margin: 0 !important; + padding: 0 !important; + text-align: right !important; + pointer-events: auto !important; + height: 12px !important; + line-height: 1 !important; } .CTFtrackSelect span { - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - line-height: 1 !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + line-height: 1 !important; } /*need the specificity*/ div.CTFplaylistControls div.CTFhoverElement { - position: absolute !important; - left: 28px !important; - top: 18px !important; - width: 24px !important; - height: 6px !important; - pointer-events: auto !important; + position: absolute !important; + left: 28px !important; + top: 18px !important; + width: 24px !important; + height: 6px !important; + pointer-events: auto !important; } diff --git a/ClickToPlugin.safariextension/.DS_Store b/ClickToPlugin.safariextension/.DS_Store index 10fb7ec1..5d3c2697 100644 Binary files a/ClickToPlugin.safariextension/.DS_Store and b/ClickToPlugin.safariextension/.DS_Store differ diff --git a/ClickToPlugin.safariextension/ClickToPlugin.js b/ClickToPlugin.safariextension/ClickToPlugin.js index 387ae0dc..47e4da4c 100644 --- a/ClickToPlugin.safariextension/ClickToPlugin.js +++ b/ClickToPlugin.safariextension/ClickToPlugin.js @@ -1,45 +1,45 @@ -/* -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -NOTE TO SELF -ALWAYS ALWAYS ALWAYS ALWAYS USE the 'var' keyword in 'for' loops!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -*/ - - /**************************** ClickToPlugin class definition *****************************/ function ClickToPlugin() { - this.blockedElements = new Array();// array containing the Flash HTML elements + this.blockedElements = new Array();// array containing the blocked HTML elements this.placeholderElements = new Array();// array containing the corresponding placeholder elements - this.mediaPlayers = new Array();// array containing the HTML5 media players + this.mediaPlayers = new Array();// array containing the HTML5 media players + + /* + Each item in blockedElement will acquire 3 additional properties: + -> tag: 'embed', 'object', or 'applet' + -> plugin: the name of the plugin that would handle the element + -> info: an object; info.src is the source of the embedded content + */ this.settings = null; this.instance = null; this.numberOfBlockedElements = 0; - this.numberOfUnblockedElements = 0; - this.location = window.location.href; + this.numberOfUnblockedElements = 0; + + var _this = this; - var _this = this; - - this.respondToMessageTrampoline = function(event) { + this.respondToMessageTrampoline = function(event) { _this.respondToMessage(event); }; - - this.handleEventTrampoline = function(event) { - _this.handleEvent(event); - }; + + this.handleEventTrampoline = function(event) { + _this.handleEvent(event); + }; - safari.self.addEventListener("message", this.respondToMessageTrampoline, false); - document.addEventListener("beforeload", this.handleEventTrampoline, true); + safari.self.addEventListener("message", this.respondToMessageTrampoline, false); + document.addEventListener("beforeload", this.handleEventTrampoline, true); + // HTML applet elements fire no beforeload event + // bug filed: https://bugs.webkit.org/show_bug.cgi?id=44023 + // Unsatisfactory workaround (Java will run anyway): document.addEventListener("DOMContentLoaded", this.handleEventTrampoline, false); - document.addEventListener("DOMNodeInserted", this.handleEventTrampoline, false); - //document.addEventListener("DOMNodeInsertedIntoDocument", this.handleEventTrampoline, false); + document.addEventListener("DOMNodeInserted", this.handleEventTrampoline, false); document.oncontextmenu = function(event) { - safari.self.tab.setContextMenuEventUserInfo(event, {"location": _this.location, "blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements}); + safari.self.tab.setContextMenuEventUserInfo(event, {"location": window.location.href, "blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements}); }; } @@ -50,31 +50,41 @@ ClickToPlugin.prototype.clearAll = function(elementID) { }; ClickToPlugin.prototype.respondToMessage = function(event) { - if (event.name == "mediaData") { - if(event.message.CTPInstance != this.instance) return; // ignore message from other CTP instances - this.prepMedia(event.message); - } else if (event.name == "loadContent") { - var loadData = event.message.split(","); // [0] CTPInstance, [1] elementID, [2] message - if (loadData[0] != this.instance) return; // ignore message from other CTP instances - if (loadData[2] == "plugin") { - this.loadPluginForElement(loadData[1]); - } else if (loadData[2] == "video") { - this.loadMediaForElement(loadData[1]); - } else if (loadData[2] == "reloadPlugin") { - this.loadInPlugin(loadData[1]); - } else if (loadData[2] == "remove") { - this.removeElement(loadData[1]); - } else if (loadData[2] == "qtp") { - this.launchInQuickTimePlayer(loadData[1]); - } else if (loadData[2] == "show") { - alert(document.HTMLToString(this.blockedElements[loadData[1]])); - } - } else if (event.name == "updateVolume") { - this.setVolumeTo(event.message); - } else if (event.name == "updateOpacity") { - this.setOpacityTo(event.message); - } else if(event.name == "loadAll") { - this.loadAll(); + switch(event.name) { + case "mediaData": + if(event.message.instance != this.instance) return; // ignore message from other CTP instances + this.prepMedia(event.message); + break; + case "loadContent": + var loadData = event.message.split(","); // [0] instance, [1] elementID, [2] message + if (loadData[0] != this.instance) return; // ignore message from other CTP instances + switch(loadData[2]) { + case "plugin": + this.loadPluginForElement(loadData[1]); + break; + case "remove": + this.removeElement(loadData[1]); + break; + case "reload": + this.reloadInPlugin(loadData[1]); + break; + case "qtp": + this.launchInQuickTimePlayer(loadData[1]); + break; + case "show": + alert(document.HTMLToString(this.blockedElements[loadData[1]])); + break; + } + break; + case "loadAll": + this.loadAll(); + break; + case "updateVolume": + this.setVolumeTo(event.message); + break; + case "updateOpacity": + this.setOpacityTo(event.message); + break; } }; @@ -82,9 +92,8 @@ ClickToPlugin.prototype.respondToMessage = function(event) { According to the W3C HTML5 spec, to activate a plugin, -> an 'embed' element must have either the 'src' or the 'type' attribute with nonempty value -> an 'object' element must have either the 'data' or the 'type' attribute with nonempty value. -In the real world, however, (AS IS RECOMMANDED ON ADOBE'S WEBSITE, BY THE WAY!) -one often finds an 'object' with neither 'data' nor 'type', with an 'embed' element -as child with 'src' and/or 'type' attribute set. +In the real world, however, one often finds an 'object' with neither 'data' nor 'type', +with an 'embed' element as child with 'src' and/or 'type' attribute set. */ ClickToPlugin.prototype.handleEvent = function(event) { @@ -102,21 +111,35 @@ ClickToPlugin.prototype.handleEvent = function(event) { ClickToPlugin.prototype.handleDOMContentEvent = function(event) { if(event.target.nodeType != 1 && event.target.nodeType != 9) return; // the node is not an HTML Element nor the document const applets = event.target.getElementsByTagName("applet"); + if(applets.length == 0) return; + // Convert NodeList to Array var appletElements = new Array(); for(var i = 0; i < applets.length; i++) { appletElements.push(applets[i]); } + var tmpAnchor = document.createElement("a"); for(var i = 0; i < appletElements.length; i++) { if(appletElements[i].allowedToLoad) continue; appletElements[i].tag = "applet"; - appletElements[i].source = getSrcOf(appletElements[i]); + appletElements[i].info = new Object(); + + // Get source of applet + if(appletElements[i].code) { + tmpAnchor.href = appletElements[i].code; + appletElements[i].info.src = tmpAnchor.href; + } else if(appletElements[i].hasAttribute("archive")) { + tmpAnchor.href = appletElements[i].getAttribute("archive"); + appletElements[i].info.src = tmpAnchor.href; + } else { + appletElements[i].info.src = ""; + } - var pluginName = safari.self.tab.canLoad(event, {"src": appletElements[i].source, "type": "application/x-java-applet", "classid": "", "params": "", "location": this.location, "width": appletElements[i].offsetWidth, "height": appletElements[i].offsetHeight, "otherInfo": null}); + var pluginName = safari.self.tab.canLoad(event, {"src": appletElements[i].info.src, "type": "application/x-java-applet", "location": window.location.href, "width": appletElements[i].offsetWidth, "height": appletElements[i].offsetHeight}); if(!pluginName) continue; // whitelisted - appletElements[i].plugin = pluginName; + appletElements[i].plugin = "Java"; if(this.settings == null) { this.settings = safari.self.tab.canLoad(event, "getSettings"); @@ -125,15 +148,21 @@ ClickToPlugin.prototype.handleDOMContentEvent = function(event) { this.instance = safari.self.tab.canLoad(event, "getInstance"); } var elementID = this.numberOfBlockedElements++; - this.processBlockedElement(appletElements[i], elementID); // this removes appletElements[0] from the document + // BEGIN DEBUG + if(this.settings["debug"]) { + if(!confirm("ClickToPlugin is about to block embedded content " + this.instance + "." + elementID + ":\n\n" + document.HTMLToString(element))) return; + } + // END DEBUG + + this.processBlockedElement(appletElements[i], elementID); } }; ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { - const element = event.target; - + const element = event.target; + // deal with sIFR script first - if(element instanceof HTMLScriptElement && element.getAttribute("src").indexOf("sifr.js") != (-1)) { + if(element instanceof HTMLScriptElement && element.src.indexOf("sifr.js") != (-1)) { var sIFRData = safari.self.tab.canLoad(event, "sIFR"); if(!sIFRData.canLoad) { // BEGIN DEBUG @@ -151,7 +180,8 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { // the beforeload event is fired again but this time the // flash element must not be blocked if (element.allowedToLoad) return; - if (element instanceof HTMLEmbedElement) { + + if (element instanceof HTMLEmbedElement) { element.tag = "embed"; } else if (element instanceof HTMLObjectElement) { element.tag = "object"; @@ -159,29 +189,20 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { return; } + element.info = getInfo(element, event.url); + + var pluginName = safari.self.tab.canLoad(event, {"src": element.info.src, "type": getTypeOf(element), "classid": element.getAttribute("classid"), "location": window.location.href, "width": element.offsetWidth, "height": element.offsetHeight, "launchInQTP": element.info.target == "quicktimeplayer" ? element.info.href : null}); + if(!pluginName) return; // whitelisted + // Load the user settings if(this.settings == null) { this.settings = safari.self.tab.canLoad(event, "getSettings"); } - // Give an address to this CTP instance to receive messages - if(this.instance == null) { - this.instance = safari.self.tab.canLoad(event, "getInstance"); - } - - element.otherInfo = new Object(); - element.source = getSrcOf(element); - - var pluginName = safari.self.tab.canLoad(event, {"src": element.source, "type": getTypeOf(element), "classid": element.getAttribute("classid"), "params": this.settings["useH264"] ? getParamsOf(element) : "", "location": this.location, "width": element.offsetWidth, "height": element.offsetHeight, "otherInfo": element.otherInfo}); - if(!pluginName) return; // whitelisted - //alert(pluginName); - - - - // if useh264... + // if useh264... // Deal with sIFR Flash - if (element.className == "sIFR-flash" || element.getAttribute("sifr")) { + if (element.className == "sIFR-flash" || element.hasAttribute("sifr")) { if (this.settings["sifrReplacement"] == "autoload") { // BEGIN DEBUG if(this.settings["debug"]) { @@ -193,13 +214,17 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { } // At this point we know we have to block 'element' from loading + var elementID = this.numberOfBlockedElements++; element.plugin = pluginName; - var elementID = this.numberOfBlockedElements++; + + // Give an address to this CTP instance to receive messages + if(this.instance == null) { + this.instance = safari.self.tab.canLoad(event, "getInstance"); + } // BEGIN DEBUG if(this.settings["debug"]) { - if(!confirm(window.location.href + "\n" + window.top.location.href + "\nClickToPlugin is about to block embedded content " + this.instance + "." + elementID + ":\n\n" + document.HTMLToString(element))) return; - //alert(element.source + "\n -- \n" + event.url); + if(!confirm("ClickToPlugin is about to block embedded content " + this.instance + "." + elementID + ":\n\n" + document.HTMLToString(element))) return; } // END DEBUG @@ -209,38 +234,37 @@ ClickToPlugin.prototype.handleBeforeLoadEvent = function(event) { ClickToPlugin.prototype.prepMedia = function(mediaData) { if(mediaData.playlist.length == 0 || !mediaData.playlist[0].mediaURL) return; - if(!this.mediaPlayers[mediaData.elementID]) { - this.mediaPlayers[mediaData.elementID] = new mediaPlayer(mediaData.isAudio ? "audio" : "video"); - } - if(mediaData.loadAfter) { // just adding stuff to the playlist - // BEGIN DEBUG - if(this.settings["debug"]) { - if(!confirm("Preparing to add " + mediaData.playlist.length + " tracks to the playlist for element " + this.instance +"."+ mediaData.elementID)) return; - } - // END DEBUG - this.mediaPlayers[mediaData.elementID].playlistLength -= mediaData.missed; - this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist); - return; - } - // BEGIN DEBUG - if(this.settings["debug"]) { - var showPlaylist = "(" + mediaData.playlist.length + " track" + (mediaData.playlist.length > 1 ? "s" : "") + ")"; + if(!this.mediaPlayers[mediaData.elementID]) { + this.mediaPlayers[mediaData.elementID] = new mediaPlayer(mediaData.isAudio ? "audio" : "video"); + } + if(mediaData.loadAfter) { // just adding stuff to the playlist + // BEGIN DEBUG + if(this.settings["debug"]) { + if(!confirm("Preparing to add " + mediaData.playlist.length + " tracks to the playlist for element " + this.instance +"."+ mediaData.elementID)) return; + } + // END DEBUG + this.mediaPlayers[mediaData.elementID].playlistLength -= mediaData.missed; + this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist); + return; + } + // BEGIN DEBUG + if(this.settings["debug"]) { + var showPlaylist = "(" + mediaData.playlist.length + " track" + (mediaData.playlist.length > 1 ? "s" : ""); + if(mediaData.playlistLength) showPlaylist += ", expecting " + mediaData.playlistLength; + showPlaylist += ")"; for (var i = 0; i < mediaData.playlist.length; i++) { - showPlaylist += "\n[" + (i + 1) + "] (" + mediaData.playlist[i].mediaType + ")" + (mediaData.playlist[i].mediaType == "video" ? ("\nposterURL: " + mediaData.playlist[i].posterURL) : "") + "\nmediaURL: " + mediaData.playlist[i].mediaURL + "\n"; + showPlaylist += "\n[" + (i + 1) + "] (" + mediaData.playlist[i].mediaType + ")" + "\nposterURL: " + mediaData.playlist[i].posterURL + "\nmediaURL: " + mediaData.playlist[i].mediaURL + "\n"; } if(!confirm("Preparing media for element " + this.instance +"."+ mediaData.elementID + ":\n\nbadgeLabel: " + mediaData.badgeLabel + "\n\nPLAYLIST " + showPlaylist)) return; } // END DEBUG - - // do it backward just in case a loadAfter came first - // can happen for embedded playlists - - this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist, true); - this.mediaPlayers[mediaData.elementID].playlistLength = mediaData.playlistLength ? mediaData.playlistLength : mediaData.playlist.length; - this.mediaPlayers[mediaData.elementID].startTrack = mediaData.startTrack ? mediaData.startTrack : 0; - - this.mediaPlayers[mediaData.elementID].usePlaylistControls = this.settings["usePlaylists"] && !mediaData.noPlaylistControls && this.mediaPlayers[mediaData.elementID].playlistLength > 1; + + this.mediaPlayers[mediaData.elementID].addToPlaylist(mediaData.playlist, true); + this.mediaPlayers[mediaData.elementID].playlistLength = mediaData.playlistLength ? mediaData.playlistLength : mediaData.playlist.length; + this.mediaPlayers[mediaData.elementID].startTrack = mediaData.startTrack ? mediaData.startTrack : 0; + + this.mediaPlayers[mediaData.elementID].usePlaylistControls = this.settings["usePlaylists"] && !mediaData.noPlaylistControls && this.mediaPlayers[mediaData.elementID].playlistLength > 1; // Check if we should load video at once if(this.settings["H264autoload"]) { @@ -249,17 +273,17 @@ ClickToPlugin.prototype.prepMedia = function(mediaData) { } var badgeLabel = mediaData.badgeLabel; if(!badgeLabel) badgeLabel = "Video"; - + this.displayBadge(badgeLabel, mediaData.elementID); }; ClickToPlugin.prototype.loadPluginForElement = function(elementID) { var placeholderElement = this.placeholderElements[elementID]; - var element = this.blockedElements[elementID]; - element.allowedToLoad = true; + var element = this.blockedElements[elementID]; + element.allowedToLoad = true; if(placeholderElement.parentNode) { placeholderElement.parentNode.replaceChild(element, placeholderElement); - this.numberOfUnblockedElements++; + this.numberOfUnblockedElements++; this.clearAll(elementID); } }; @@ -271,14 +295,14 @@ ClickToPlugin.prototype.loadAll = function() { this.loadPluginForElement(i); } } - this.numberOfUnblockedElements = this.numberOfBlockedElements; + this.numberOfUnblockedElements = this.numberOfBlockedElements; }; -ClickToPlugin.prototype.loadInPlugin = function(elementID) { +ClickToPlugin.prototype.reloadInPlugin = function(elementID) { var containerElement = this.mediaPlayers[elementID].containerElement; - var element = this.blockedElements[elementID]; - element.allowedToLoad = true; - containerElement.parentNode.replaceChild(element, containerElement); + var element = this.blockedElements[elementID]; + element.allowedToLoad = true; + containerElement.parentNode.replaceChild(element, containerElement); this.clearAll(elementID); }; @@ -286,56 +310,54 @@ ClickToPlugin.prototype.loadMediaForElement = function(elementID) { var placeholderElement = this.placeholderElements[elementID]; var contextInfo = { - "CTPInstance": this.instance, + "instance": this.instance, "elementID": elementID, "isH264": true, "plugin": this.blockedElements[elementID].plugin - //"blocked": this.numberOfBlockedElements - this.numberOfUnblockedElements + //"blocked": this.numberOfBlockedElements - this.numberOfUnblockedElements }; - // Initialize player - var w = parseInt(placeholderElement.style.width.replace("px","")); - var h = parseInt(placeholderElement.style.height.replace("px","")); - this.mediaPlayers[elementID].initialize(this.settings["H264behavior"], w, h, this.settings["volume"], contextInfo); - //mediaElement.allowedToLoad = true; // not used for now - - // Load first track - this.mediaPlayers[elementID].loadTrack(0); + // Initialize player + var w = parseInt(placeholderElement.style.width.replace("px","")); + var h = parseInt(placeholderElement.style.height.replace("px","")); + this.mediaPlayers[elementID].initialize(this.settings["H264behavior"], w, h, this.settings["volume"], contextInfo); + // mediaElement.allowedToLoad = true; // not used - // Replace placeholder - placeholderElement.parentNode.replaceChild(this.mediaPlayers[elementID].containerElement, placeholderElement); - this.numberOfUnblockedElements++; + // Replace placeholder and load first track + placeholderElement.parentNode.replaceChild(this.mediaPlayers[elementID].containerElement, placeholderElement); + this.mediaPlayers[elementID].loadTrack(0); + this.numberOfUnblockedElements++; this.placeholderElements[elementID] = null; }; ClickToPlugin.prototype.launchInQuickTimePlayer = function(elementID) { - var track = this.mediaPlayers[elementID].currentTrack; - var element = null; - if(track == null) { - track = 0; - element = this.placeholderElements[elementID]; - } else { - element = this.mediaPlayers[elementID].containerElement; - } + var track = this.mediaPlayers[elementID].currentTrack; + var element = null; + if(track == null) { + track = 0; + element = this.placeholderElements[elementID]; + } else { + element = this.mediaPlayers[elementID].containerElement; + } var mediaURL = this.mediaPlayers[elementID].playlist[track].mediaURL; - var QTObject = document.createElement("embed"); - QTObject.allowedToLoad = true; - QTObject.className = "CTFQTObject"; - QTObject.setAttribute("type", "video/quicktime"); - QTObject.setAttribute("width", "0"); - QTObject.setAttribute("height", "0"); - // need an external URL for source, since QT plugin doesn't accept safari-extension:// protocol - // Apple has a small 1px image for this exact purpose - QTObject.setAttribute("src", "http://images.apple.com/apple-events/includes/qtbutton.mov"); - QTObject.setAttribute("href", mediaURL); - QTObject.setAttribute("target", "quicktimeplayer"); - QTObject.setAttribute("autohref", "true"); - QTObject.setAttribute("controller", "false"); - //QTObject.setAttribute("postdomevents", "true"); - element.appendChild(QTObject); - // There doesn't seem to exist an appropriate event, so we just wait a bit... - setTimeout(function() {element.removeChild(QTObject);}, 100); + var QTObject = document.createElement("embed"); + QTObject.allowedToLoad = true; + QTObject.className = "CTFQTObject"; + QTObject.setAttribute("type", "video/quicktime"); + QTObject.setAttribute("width", "0"); + QTObject.setAttribute("height", "0"); + // need an external URL for source, since QT plugin doesn't accept safari-extension:// protocol + // Apple has a small 1px image for this exact purpose + QTObject.setAttribute("src", "http://images.apple.com/apple-events/includes/qtbutton.mov"); + QTObject.setAttribute("href", mediaURL); + QTObject.setAttribute("target", "quicktimeplayer"); + QTObject.setAttribute("autohref", "true"); + QTObject.setAttribute("controller", "false"); + // QTObject.setAttribute("postdomevents", "true"); + element.appendChild(QTObject); + // There doesn't seem to exist an appropriate event, so we just wait a bit... + setTimeout(function() {element.removeChild(QTObject);}, 100); }; ClickToPlugin.prototype.setVolumeTo = function(volume) { @@ -356,7 +378,7 @@ ClickToPlugin.prototype.removeElement = function(elementID) { element = element.parentNode; } element.parentNode.removeChild(element); - this.numberOfUnblockedElements++; + this.numberOfUnblockedElements++; this.clearAll(elementID); }; @@ -376,11 +398,11 @@ ClickToPlugin.prototype.displayBadge = function(badgeLabel, elementID) { this.placeholderElements[elementID].firstChild.firstChild.firstChild.firstChild.className = "logoContainer hidden"; this.placeholderElements[elementID].firstChild.firstChild.firstChild.firstChild.childNodes[1].className = "logo tmp"; - this.unhideLogo(elementID); + this.unhideLogo(elementID, 0); }; // NOTE: this function should never be called directly (use displayBadge instead) -ClickToPlugin.prototype.unhideLogo = function(elementID) { +ClickToPlugin.prototype.unhideLogo = function(elementID, i) { var logoContainer = this.placeholderElements[elementID].firstChild.firstChild.firstChild.firstChild; var w0 = this.placeholderElements[elementID].offsetWidth; var h0 = this.placeholderElements[elementID].offsetHeight; @@ -390,10 +412,11 @@ ClickToPlugin.prototype.unhideLogo = function(elementID) { var h2 = logoContainer.childNodes[1].offsetHeight; if(w2 == 0 || h2 == 0 || w1 == 0 || h1 == 0 || w0 == 0 || h0 == 0) { + if(i > 9) return; // 2 options: leave the logo hidden (no big deal, and rarely happens), - // or run unhideLogo again later (this might cause unexpected results due to asynchronicity) + // or run unhideLogo again later <- THIS var _this = this; - setTimeout(function() {_this.unhideLogo(elementID);}, 100); // there's no hurry here + setTimeout(function() {_this.unhideLogo(elementID, ++i);}, 100); // there's no hurry here return; } @@ -418,22 +441,22 @@ ClickToPlugin.prototype.unhideLogo = function(elementID) { }; ClickToPlugin.prototype.clickPlaceholder = function(elementID) { - if (this.mediaPlayers[elementID] && this.mediaPlayers[elementID].startTrack != null) { - this.loadMediaForElement(elementID); - } else { + if (this.mediaPlayers[elementID] && this.mediaPlayers[elementID].startTrack != null) { + this.loadMediaForElement(elementID); + } else { this.loadPluginForElement(elementID); - } + } }; ClickToPlugin.prototype.processBlockedElement = function(element, elementID) { // Creating the placeholder element - var placeholderElement = document.createElement("div"); - placeholderElement.style.width = element.offsetWidth + "px"; - placeholderElement.style.height = element.offsetHeight + "px"; - placeholderElement.style.opacity = this.settings["opacity"]; + var placeholderElement = document.createElement("div"); + placeholderElement.style.width = element.offsetWidth + "px"; + placeholderElement.style.height = element.offsetHeight + "px"; + placeholderElement.style.opacity = this.settings["opacity"]; - placeholderElement.className = "clickToFlashPlaceholder"; + placeholderElement.className = "clickToFlashPlaceholder"; // Replacing element by placeholderElement if (element.parentNode) { @@ -445,54 +468,54 @@ ClickToPlugin.prototype.processBlockedElement = function(element, elementID) { alert("Ignoring duplicate element " + this.instance + "." + elementID + "."); } // END DEBUG - this.numberOfUnblockedElements++; + this.numberOfUnblockedElements++; return; } - var _this = this; - placeholderElement.onclick = function(event){_this.clickPlaceholder(elementID);}; - placeholderElement.oncontextmenu = function(event) { - var contextInfo = { - "CTPInstance": _this.instance, + var _this = this; + placeholderElement.onclick = function(event){_this.clickPlaceholder(elementID);}; + placeholderElement.oncontextmenu = function(event) { + var contextInfo = { + "instance": _this.instance, "elementID": elementID, - "src": element.source, + "src": element.info.src, "plugin": element.plugin, - "blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements + //"blocked": _this.numberOfBlockedElements - _this.numberOfUnblockedElements }; if (_this.mediaPlayers[elementID] && _this.mediaPlayers[elementID].startTrack != null) { - contextInfo.hasH264 = true; + contextInfo.hasH264 = true; _this.mediaPlayers[elementID].setContextInfo(event, contextInfo); } else { - contextInfo.hasH264 = false; - safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); - event.stopPropagation(); + contextInfo.hasH264 = false; + safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); + event.stopPropagation(); } - }; + }; // Building the placeholder - var container = document.createElement("div"); - container.className = "clickToFlashPlaceholderContainer"; - placeholderElement.appendChild(container); - - var verticalPositionElement = document.createElement("div"); - verticalPositionElement.className = "logoVerticalPosition"; - container.appendChild(verticalPositionElement); - - var horizontalPositionElement = document.createElement("div"); - horizontalPositionElement.className = "logoHorizontalPosition"; - verticalPositionElement.appendChild(horizontalPositionElement); - - var logoContainer = document.createElement("div"); - logoContainer.className = "logoContainer nodisplay"; // keep the logo hidden at first - horizontalPositionElement.appendChild(logoContainer); - - var logoElement = document.createElement("div"); - logoElement.className = "logo"; - logoContainer.appendChild(logoElement); - - var logoInsetElement = document.createElement("div"); - logoInsetElement.className = "logo inset"; - logoContainer.appendChild(logoInsetElement); + var container = document.createElement("div"); + container.className = "clickToFlashPlaceholderContainer"; + placeholderElement.appendChild(container); + + var verticalPositionElement = document.createElement("div"); + verticalPositionElement.className = "logoVerticalPosition"; + container.appendChild(verticalPositionElement); + + var horizontalPositionElement = document.createElement("div"); + horizontalPositionElement.className = "logoHorizontalPosition"; + verticalPositionElement.appendChild(horizontalPositionElement); + + var logoContainer = document.createElement("div"); + logoContainer.className = "logoContainer nodisplay"; // keep the logo hidden at first + horizontalPositionElement.appendChild(logoContainer); + + var logoElement = document.createElement("div"); + logoElement.className = "logo"; + logoContainer.appendChild(logoElement); + + var logoInsetElement = document.createElement("div"); + logoInsetElement.className = "logo inset"; + logoContainer.appendChild(logoInsetElement); // Filling the main arrays this.blockedElements[elementID] = element; @@ -503,14 +526,14 @@ ClickToPlugin.prototype.processBlockedElement = function(element, elementID) { if(this.settings["useH264"]) { if(!this.directKill(elementID)) { var elementData = { + "instance": this.instance, + "elementID": elementID, "plugin": element.plugin, - "src": element.source, - "presrc": element.presource, // TEMP!! + "src": element.info.href ? element.info.href : element.info.src, + "presrc": element.info.href ? element.info.src : "", + "image": element.info.image, "params": getParamsOf(element), - "elementID": elementID, - "CTPInstance": this.instance, - "location": this.location, - "image": element.image + "location": window.location.href }; safari.self.tab.dispatchMessage("killPlugin", elementData); } @@ -549,7 +572,7 @@ ClickToPlugin.prototype.directKill = function(elementID) { var mediaData = { "elementID": elementID, "playlist": [{"mediaType": mediaType, "posterURL": mediaElements[0].getAttribute("poster"), "mediaURL": mediaURL}], - "badgeLabel": "Video" + "badgeLabel": mediaType == "audio" ? "Audio" : "Video" }; this.prepMedia(mediaData); return true; diff --git a/ClickToPlugin.safariextension/Settings.plist b/ClickToPlugin.safariextension/Settings.plist index 3fef5130..3a358c00 100644 --- a/ClickToPlugin.safariextension/Settings.plist +++ b/ClickToPlugin.safariextension/Settings.plist @@ -2,421 +2,421 @@ - - Title - MIME Settings - Type - Group - - - DefaultValue - blockAll - Key - block - Title - Block - Titles - - all MIME types - all MIME types not matching the greenlist - only the MIME types matching the redlist - - Type - RadioButtons - Values - - blockAll - useGreenlist - useRedlist - - - - DefaultValue - - Key - greenlist - Title - Greenlist - Type - TextField - - - DefaultValue - flash, futuresplash, silverlight - Key - redlist - Title - Redlist - Type - TextField - - - DefaultValue - - Key - allowQT - Title - Allow QuickTime plugin                                  - Type - CheckBox - - - Title - Video Settings - Type - Group - - - DefaultValue - - Key - useH264 - Title - Look for video replacements to                      - Type - CheckBox - - - DefaultValue - - Key - replaceFlash - Title - Flash                                              - Type - CheckBox - - - DefaultValue - - Key - replaceSL - Title - Silverlight                                       - Type - CheckBox - - - DefaultValue - - Key - replaceQT - Title - QuickTime                                     - Type - CheckBox - - - DefaultValue - - Key - replaceWM - Title - Windows Media                              - Type - CheckBox - - - DefaultValue - - Key - usePlaylists - Title - Use playlists                                                    - Type - CheckBox - - - DefaultValue - - Key - H264autoload - Title - Load the video player automatically               - Type - CheckBox - - - DefaultValue - autoplay - Key - H264behavior - Title - Once loaded - Titles - - Do not buffer - Start buffering - Start buffering and play automatically - - Type - PopUpButton - Values - - none - buffer - autoplay - - - - DefaultValue - 2 - Key - QTbehavior - Title - Nonnative codecs - Titles - - Do not use - Use only as a last resort - Use freely - - Type - PopUpButton - Values - - 1 - 2 - 3 - - - - DefaultValue - 3 - Key - maxresolution - Title - Max resolution - Titles - - 480p - 720p - 1080p - 4K - - Type - PopUpButton - Values - - 1 - 2 - 3 - 4 - - - - DefaultValue - 0.5 - Key - volume - MaximumValue - 1 - StepValue - 0.01 - Title - Sound volume - Type - Slider - - - Title - Whitelist - Type - Group - - - DefaultValue - - Key - uselocWhitelist - Title - Allow embedded content on locations            - Type - CheckBox - - - DefaultValue - www.example.com, www.example2.com - Key - locwhitelist - Title - matching - Type - TextField - - - DefaultValue - - Key - locblacklist - Title - not matching - Type - TextField - - - DefaultValue - - Key - usesrcWhitelist - Title - Allow embedded content from sources          - Type - CheckBox - - - DefaultValue - - Key - srcwhitelist - Title - matching - Type - TextField - - - DefaultValue - - Key - srcblacklist - Title - not matching - Type - TextField - - - Title - Invisible elements - Type - Group - - - DefaultValue - - Key - loadInvisible - Title - Allow elements to load if                                - Type - CheckBox - - - DefaultValue - 1 - Key - maxinvdim - Title - size is at most - Titles - - 1 x 1 px - 2 x 2 px - 4 x 4 px - 8 x 8 px - - Type - PopUpButton - Values - - 1 - 2 - 4 - 8 - - - - Title - Contextual Menu Items - Type - Group - - - DefaultValue - - Key - useWLcontext - Title - Add to Whitelist                                              - Type - CheckBox - - - DefaultValue - - Key - useLAcontext - Title - Load All Plugins                                              - Type - CheckBox - - - DefaultValue - - Key - useVScontext - Title - View on Site                                                    - Type - CheckBox - - - DefaultValue - - Key - useQTcontext - Title - View in QuickTime Player                                - Type - CheckBox - - - Type - Separator - - - DefaultValue - textonly - Key - sifrReplacement - Title - sIFR Text - Titles - - Show text only - Treat as regular Flash - Load automatically - - Type - PopUpButton - Values - - textonly - normal - autoload - - - - DefaultValue - 1 - Key - opacity - MaximumValue - 1 - Secure - - StepValue - 0.01 - Title - Opacity - Type - Slider - - - DefaultValue - - Key - debug - + + Title + MIME Settings + Type + Group + + + DefaultValue + blockAll + Key + block + Title + Block + Titles + + all MIME types + all MIME types not matching the greenlist + only the MIME types matching the redlist + + Type + RadioButtons + Values + + blockAll + useGreenlist + useRedlist + + + + DefaultValue + + Key + greenlist + Title + Greenlist + Type + TextField + + + DefaultValue + flash, futuresplash, silverlight + Key + redlist + Title + Redlist + Type + TextField + + + DefaultValue + + Key + allowQT + Title + Allow QuickTime plugin                                  + Type + CheckBox + + + Title + Video Settings + Type + Group + + + DefaultValue + + Key + useH264 + Title + Look for video replacements to                      + Type + CheckBox + + + DefaultValue + + Key + replaceFlash + Title + Flash                                              + Type + CheckBox + + + DefaultValue + + Key + replaceSL + Title + Silverlight                                       + Type + CheckBox + + + DefaultValue + + Key + replaceQT + Title + QuickTime                                     + Type + CheckBox + + + DefaultValue + + Key + replaceWM + Title + Windows Media                              + Type + CheckBox + + + DefaultValue + + Key + usePlaylists + Title + Use playlists                                                    + Type + CheckBox + + + DefaultValue + + Key + H264autoload + Title + Load the video player automatically               + Type + CheckBox + + + DefaultValue + autoplay + Key + H264behavior + Title + Once loaded + Titles + + Do not buffer + Start buffering + Start buffering and play automatically + + Type + PopUpButton + Values + + none + buffer + autoplay + + + + DefaultValue + 2 + Key + QTbehavior + Title + Nonnative codecs + Titles + + Do not use + Use only as a last resort + Use freely + + Type + PopUpButton + Values + + 1 + 2 + 3 + + + + DefaultValue + 3 + Key + maxresolution + Title + Max resolution + Titles + + 480p + 720p + 1080p + 4K + + Type + PopUpButton + Values + + 1 + 2 + 3 + 4 + + + + DefaultValue + 0.5 + Key + volume + MaximumValue + 1 + StepValue + 0.01 + Title + Sound volume + Type + Slider + + + Title + Whitelist + Type + Group + + + DefaultValue + + Key + uselocWhitelist + Title + Allow embedded content on locations            + Type + CheckBox + + + DefaultValue + www.example.com, www.example2.com + Key + locwhitelist + Title + matching + Type + TextField + + + DefaultValue + + Key + locblacklist + Title + not matching + Type + TextField + + + DefaultValue + + Key + usesrcWhitelist + Title + Allow embedded content from sources          + Type + CheckBox + + + DefaultValue + + Key + srcwhitelist + Title + matching + Type + TextField + + + DefaultValue + + Key + srcblacklist + Title + not matching + Type + TextField + + + Title + Invisible elements + Type + Group + + + DefaultValue + + Key + loadInvisible + Title + Allow elements to load if                                + Type + CheckBox + + + DefaultValue + 1 + Key + maxinvdim + Title + size is at most + Titles + + 1 x 1 px + 2 x 2 px + 4 x 4 px + 8 x 8 px + + Type + PopUpButton + Values + + 1 + 2 + 4 + 8 + + + + Title + Contextual Menu Items + Type + Group + + + DefaultValue + + Key + useWLcontext + Title + Add to Whitelist                                              + Type + CheckBox + + + DefaultValue + + Key + useLAcontext + Title + Load All Plugins                                              + Type + CheckBox + + + DefaultValue + + Key + useVScontext + Title + View on Site                                                    + Type + CheckBox + + + DefaultValue + + Key + useQTcontext + Title + View in QuickTime Player                                + Type + CheckBox + + + Type + Separator + + + DefaultValue + textonly + Key + sifrReplacement + Title + sIFR Text + Titles + + Show text only + Treat as regular Flash + Load automatically + + Type + PopUpButton + Values + + textonly + normal + autoload + + + + DefaultValue + 1 + Key + opacity + MaximumValue + 1 + Secure + + StepValue + 0.01 + Title + Opacity + Type + Slider + + + DefaultValue + + Key + debug + diff --git a/ClickToPlugin.safariextension/functions.js b/ClickToPlugin.safariextension/functions.js index 3b440fac..ad40eb3a 100644 --- a/ClickToPlugin.safariextension/functions.js +++ b/ClickToPlugin.safariextension/functions.js @@ -1,91 +1,63 @@ -function getSrcOf(element) { +function getInfo(element, url) { + // gathers attributes of the element that might be needed later on + // Done by a single function so that we only loop once through the children + var info = new Object(); var tmpAnchor = document.createElement("a"); switch (element.tag) { case "embed": + if(element.hasAttribute("qtsrc")) { + tmpAnchor.href = element.getAttribute("qtsrc"); + info.src = tmpAnchor.href; + } + if(element.hasAttribute("href")) { + tmpAnchor.href = element.getAttribute("href"); + info.href = tmpAnchor.href; + } + if(element.hasAttribute("target")) { + info.target = element.getAttribute("target"); + } if(element.hasAttribute("previewimage")) { tmpAnchor.href = element.getAttribute("previewimage"); - element.image = tmpAnchor.href; + info.image = tmpAnchor.href; } - if(element.src) tmpAnchor.href = element.src; - if(element.hasAttribute("qtsrc")) tmpAnchor.href = element.getAttribute("qtsrc"); - element.presource = tmpAnchor.href; - if(element.hasAttribute("target")) element.otherInfo.target = element.getAttribute("target"); - if(element.hasAttribute("href")) { - tmpAnchor.href = element.getAttribute("href"); - } else { - delete element.presource; - if(!element.src) return ""; - } - return tmpAnchor.href; break; case "object": - // NOTE: For silverlight objects element.data is used for something else than the source - // and a param named 'source' is used for the source. So we look for that before - // using element.data var paramElements = element.getElementsByTagName("param"); - var srcParam = null; var qtsrcParam = null; for (i = 0; i < paramElements.length; i++) { - if(!paramElements[i].hasAttribute("value")) continue; + if(!paramElements[i].hasAttribute("value")) continue; var paramName = paramElements[i].getAttribute("name").toLowerCase(); // name attribute is mandatory! - // this is a bit shaky... - // maybe should check first for mimetype and then let getSrcOf depend on type - // eg source for silverlight; src for flash, qt, realplayer; filename for wm... - // this would require 2 successive canLoads? or just use the type attribute?? - // it seems to always be specified for SL, but not QT (uses classid instead) - // damn... maybe better to pass the whole HTMLToString(element) to the global page after all? - if(paramName == "previewimage") { - var tmpAnchor2 = document.createElement("a"); - tmpAnchor2.href = paramElements[i].getAttribute("value"); - element.image = tmpAnchor2.href; - } else if(paramName == "src") { - srcParam = i; - if(!element.presource) { - var tmpAnchor2 = document.createElement("a"); - tmpAnchor2.href = paramElements[i].getAttribute("value"); - element.presource = tmpAnchor2.href; - } - //element.otherInfo.src = paramElements[i].getAttribute("value"); - } else if (paramName == "qtsrc") { - qtsrcParam = i; - var tmpAnchor2 = document.createElement("a"); - tmpAnchor2.href = paramElements[i].getAttribute("value"); - element.presource = tmpAnchor2.href; - //element.otherInfo.qtsrc = paramElements[i].getAttribute("value"); - } else if (paramName == "target") element.otherInfo.target = paramElements[i].getAttribute("value"); - else if(paramName == "movie" || paramName == "source" || paramName == "href" || paramName == "filename") { //|| paramName == "url") { // for oleobject, not supported on Safari (what about the Win version?) - tmpAnchor.href = paramElements[i].getAttribute("value"); + switch(paramName) { + case "source": // Silverlight true source + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.src = tmpAnchor.href; + break; + case "qtsrc": // QuickTime true source + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.src = tmpAnchor.href; + break; + case "href": // QuickTime + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.href = tmpAnchor.href; + break; + case "target": // QuickTime + info.target = paramElements[i].getAttribute("value"); + break; + case "previewimage": // DivX + tmpAnchor.href = paramElements[i].getAttribute("value"); + info.image = tmpAnchor.href; + break; } } - if(tmpAnchor.href) return tmpAnchor.href; - if(qtsrcParam != null) { - element.presource = null; - tmpAnchor.href = paramElements[qtsrcParam].getAttribute("value"); - return tmpAnchor.href; - } else if(srcParam != null) { - element.presource = null; - tmpAnchor.href = paramElements[srcParam].getAttribute("value"); - return tmpAnchor.href; - } - if(element.data) { - tmpAnchor.href = element.data; - return tmpAnchor.href; - } else { - var embedElements = element.getElementsByTagName("embed"); - if(embedElements.length == 0) return ""; - embedElements[0].tag = "embed"; - return getSrcOf(embedElements[0]); - } - return ""; - break; - case "applet": - if(element.code) { - tmpAnchor.href = element.code; - } else if(element.hasAttribute("archive")) { - tmpAnchor.href = element.getAttribute("archive"); - } else return ""; - return tmpAnchor.href; break; } + if(!info.src) { + if(!url) info.src = ""; + else { + tmpAnchor.href = url; + info.src = tmpAnchor.href; + } + } + return info; } function getParamsOf(element) { @@ -132,7 +104,7 @@ function getTypeOf(element) { var paramElements = element.getElementsByTagName("param"); for (i = 0; i < paramElements.length; i++) { if(paramElements[i].getAttribute("name").toLowerCase() == "type") { - return paramElements[i].getAttribute("value"); + return paramElements[i].getAttribute("value"); } } var embedChildren = element.getElementsByTagName("embed"); diff --git a/ClickToPlugin.safariextension/global.html b/ClickToPlugin.safariextension/global.html index 90e01f3e..034769de 100644 --- a/ClickToPlugin.safariextension/global.html +++ b/ClickToPlugin.safariextension/global.html @@ -1,9 +1,10 @@ - - Global HTML + + Global HTML + - + @@ -13,6 +14,6 @@ - - + + \ No newline at end of file diff --git a/ClickToPlugin.safariextension/global.js b/ClickToPlugin.safariextension/global.js index cd607e89..51c5f9e2 100644 --- a/ClickToPlugin.safariextension/global.js +++ b/ClickToPlugin.safariextension/global.js @@ -1,81 +1,19 @@ var CTP_instance = 0; // incremented by one whenever a ClickToPlugin instance with content is created const killers = [new YouTubeKiller(), new VimeoKiller(), new DailymotionKiller(), new VeohKiller(), new JWKiller(), new SLKiller(), new QTKiller(), new WMKiller(), new DivXKiller()]; -function pluginName(plugin) { - if(plugin.name == "Shockwave Flash") return "Flash"; - if(plugin.name == "Silverlight Plug-In") return "Silverlight"; - if(plugin.name.match("Java")) return "Java"; - if(plugin.name.match("QuickTime")) return "QuickTime"; - if(plugin.name.match("Flip4Mac")) return "WM"; - if(plugin.name == "iPhotoPhotocast") return "iPhoto"; - if(plugin.name == "Quartz Composer Plug-In") return "Quartz"; - if(plugin.name == "VideoLAN VLC Plug-in") return "VLC"; - if(plugin.name == "DivX Web Player") return "DivX"; - if(plugin.name == ("RealPlayer Plugin.plugin")) return "RealPlayer"; - return plugin.name; - /*switch (plugin.name) { - case "Flip4Mac Windows Media Web Plugin 2.3.4": return "WM"; - case "Flip4Mac Windows Media Plugin 2.3.4": return "WM"; - case "Silverlight Plug-In": return "Silverlight"; - case "Shockwave Flash": return "Flash"; - case "Switchable Java Plug-in for WebKit": return "Java"; - case "Java Plug-In 2 for NPAPI Browsers": return "Java"; - case "iPhotoPhotocast": return "iPhoto"; - case "QuickTime Plug-in 7.6.6": return "QuickTime"; - case "Quartz Composer Plug-In": return "Quartz"; - default: return plugin.name; - }*/ -} - -/* -LIST OF CLASSID (What is this stuff anyway?) -QuickTime: 02BF25D5-8C17-4B23-BC80-D3488ABDDC6B -WMP 6: 22d6f312-b0f6-11d0-94ab-0080c74c7e95 -WMP >6: 6BF52A52-394A-11D3-B153-00C04F79FAA6 -Flash: d27cdb6e-ae6d-11cf-96b8-444553540000 -Real Player: CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA -?? calendar: 8E27C92B-1264-101C-8A2F-040224009C02 -?? graphics: 369303C2-D7AC-11D0-89D5-00A0C90833E6 -?? slider: F08DF954-8592-11D1-B16A-00C0F0283628 -DivX: 67DABFBF-D0AB-41fa-9C46-CC0F21721616 -*/ - - -function getPluginForType(MIMEType) { // MIMEType is a string - for(var i = 0; i < navigator.plugins.length; i++) { - for(var j = 0; j < navigator.plugins[i].length; j++) { - if(navigator.plugins[i][j].type == MIMEType) return navigator.plugins[i]; - } - } - return null; -} - -function getPluginAndTypeForExt(ext) { - var suffixes = null; - for(var i = 0; i < navigator.plugins.length; i++) { - for(var j = 0; j < navigator.plugins[i].length; j++) { - suffixes = navigator.plugins[i][j].suffixes.split(","); - for(var k = 0; k < suffixes.length; k++) { - if(ext == suffixes[k]) return {"plugin": navigator.plugins[i], "type": navigator.plugins[i][j].type}; - } - } - } - return {"plugin": null, "type": null}; -} - function blockOrAllow(data) { // returns null if element can be loaded, the name of the plugin otherwise // no source and no type -> must allow, it's probably going to pass through here again after being modified by a script if(!data.src && !data.type && !data.classid) return null; // native Safari support - var ext = extractExt(data.src); // used later as well - if(data.type) { - if(isNativeType(data.type)) return null; - } else { - if(isNativeExt(ext)) return null; - } - + var ext = extractExt(data.src); // used later as well + if(data.type) { + if(isNativeType(data.type)) return null; + } else { + if(isNativeExt(ext)) return null; + } + // try not to block objects created by other extensions if(data.src.substring(0,19) == "safari-extension://") return null; @@ -87,55 +25,55 @@ function blockOrAllow(data) { // returns null if element can be loaded, the name } // Deal with whitelisted content - if(safari.extension.settings["uselocWhitelist"]) { + if(safari.extension.settings["uselocWhitelist"]) { var locwhitelist = safari.extension.settings["locwhitelist"].replace(/\s+/g,""); var locblacklist = safari.extension.settings["locblacklist"].replace(/\s+/g,""); - if(locwhitelist) { - locwhitelist = locwhitelist.split(/,(?![^\(]*\))/); + if(locwhitelist) { + locwhitelist = locwhitelist.split(/,(?![^\(]*\))/); // matches all , except those in parentheses (used in regexp) if(matchList(locwhitelist, data.location)) return null; - } - if(locblacklist) { - locblacklist = locblacklist.split(/,(?![^\(]*\))/); - if(!matchList(locblacklist, data.location)) return null; - } + } + if(locblacklist) { + locblacklist = locblacklist.split(/,(?![^\(]*\))/); + if(!matchList(locblacklist, data.location)) return null; + } } - if(safari.extension.settings["usesrcWhitelist"]) { - var srcwhitelist = safari.extension.settings["srcwhitelist"].replace(/\s+/g,""); + if(safari.extension.settings["usesrcWhitelist"]) { + var srcwhitelist = safari.extension.settings["srcwhitelist"].replace(/\s+/g,""); var srcblacklist = safari.extension.settings["srcblacklist"].replace(/\s+/g,""); - if(srcwhitelist) { - srcwhitelist = srcwhitelist.split(/,(?![^\(]*\))/); + if(srcwhitelist) { + srcwhitelist = srcwhitelist.split(/,(?![^\(]*\))/); if(matchList(srcwhitelist, data.src)) return null; - } - if(locblacklist) { - srcblacklist = srcblacklist.split(/,(?![^\(]*\))/); - if(!matchList(srcblacklist, data.src)) return null; - } - } - + } + if(locblacklist) { + srcblacklist = srcblacklist.split(/,(?![^\(]*\))/); + if(!matchList(srcblacklist, data.src)) return null; + } + } + // We use a 'soft' method to get the MIME type // It is not necessarily correct, but always returns a MIME type handled by the correct plugin // To get the correct MIME type an AJAX request would be needed, out of the question here! var plugin = null; - var MIMEType = data.type.replace(/\s+/g,""); - var badgeLabel = "?"; - if(MIMEType) { - badgeLabel = MIMEType.split(";")[0].split("/")[1]; // temporary unless no plugin can be found to play MIMEType - plugin = getPluginForType(MIMEType); - } + var MIMEType = data.type; + var pluginName = "?"; + if(MIMEType) plugin = getPluginForType(MIMEType); if(!plugin && data.src) { var x = getPluginAndTypeForExt(ext); plugin = x.plugin; MIMEType = x.type; } - if(plugin) badgeLabel = pluginName(plugin); + if(plugin) pluginName = getPluginNameFromPlugin(plugin); + else if(MIMEType) pluginName = getPluginNameFromType(MIMEType); + else if(data.classid) pluginName = getPluginNameFromClassid(data.classid.replace("clsid:", "")); - if(safari.extension.settings["allowQT"] && badgeLabel == "QuickTime") return null; + if(safari.extension.settings["allowQT"] && pluginName == "QuickTime") return null; + // Use greenlist/redlist if(MIMEType) { if(safari.extension.settings["block"] == "useRedlist") { var redlist = safari.extension.settings["redlist"].replace(/\s+/g,""); if(!redlist) return null; - redlist = redlist.split(/,(?![^\(]*\))/); // matches all , except those in parentheses (used in regexp) + redlist = redlist.split(/,(?![^\(]*\))/); if(!matchList(redlist, MIMEType, true)) return null; } else if(safari.extension.settings["block"] == "useGreenlist") { var greenlist = safari.extension.settings["greenlist"].replace(/\s+/g,""); @@ -145,18 +83,16 @@ function blockOrAllow(data) { // returns null if element can be loaded, the name } } } - // At this point we know we'll have to block the element + // At this point we know we should block the element - for(var key in data.otherInfo) { - if(key == "target" && data.otherInfo.target == "quicktimeplayer") { - // A quicktime object that would launch QTP - if(confirm("A QuickTime object would like to play\n\n" + data.src + "\n\nin QuickTime Player. Do you want to allow it?")) { - return null; - } + // Exception: ask the user what to do if a QT object would launch QTP + if(data.launchInQTP) { + if(confirm("A QuickTime object would like to play\n\n" + data.launchInQTP + "\n\nin QuickTime Player. Do you want to allow it?")) { + return null; } } - return badgeLabel; + return pluginName; } @@ -174,11 +110,6 @@ function respondToMessage(event) { case "killPlugin": killPlugin(event.message); break; - //case "downloadMedia": - //var newTab = safari.application.activeBrowserWindow.openTab("foreground"); - //newTab.url = event.message + ".zip"; - //prompt("Copy the following URL and paste it into the Downloads window.", event.message); - //break; } } @@ -197,73 +128,60 @@ function respondToCanLoad(message) { } } -function printMedia(mediaType) { - switch(mediaType) { - case "video": return "Video"; - case "audio": return "Audio"; - default: return "Video"; - } -} - -function printPlugin(pluginName) { - if(/[A-Z]/.test(pluginName)) return pluginName; - return "Plugin"; -} - function handleContextMenu(event) { - if(!event.userInfo.CTPInstance) { - if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 0) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); + if(!event.userInfo.instance) { + if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 0) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); if(safari.extension.settings["useWLcontext"]) { event.contextMenu.appendContextMenuItem("locwhitelist", "Add Location to Whitelist\u2026"); } return; } + var pluginName = /[A-Z]/.test(event.userInfo.plugin) ? event.userInfo.plugin : "Plugin"; if(event.userInfo.isH264) { - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",reloadPlugin", "Reload in " + event.userInfo.plugin); - if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",qtp", "View in QuickTime Player"); - if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "View on " + event.userInfo.siteInfo.name); + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",reload", "Reload in " + pluginName); + if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",qtp", "View in QuickTime Player"); + if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "View on " + event.userInfo.siteInfo.name); } else { if(event.userInfo.hasH264) { - //event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",video", "Load " + printMedia(event.userInfo.mediaType)); - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",plugin", "Load " + printPlugin(event.userInfo.plugin)); - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",remove", "Hide " + printPlugin(event.userInfo.plugin)); - //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); - if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",qtp", "View in QuickTime Player"); - if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "View on " + event.userInfo.siteInfo.name); - } else { - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",remove", "Hide " + printPlugin(event.userInfo.plugin)); - //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); - } + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",plugin", "Load " + pluginName); + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",remove", "Hide " + pluginName); + //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); + if(safari.extension.settings["useQTcontext"]) event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",qtp", "View in QuickTime Player"); + if(event.userInfo.siteInfo && safari.extension.settings["useVScontext"]) event.contextMenu.appendContextMenuItem("gotosite", "View on " + event.userInfo.siteInfo.name); + } else { + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",remove", "Hide " + pluginName); + //if(safari.extension.settings["useLAcontext"] && event.userInfo.blocked > 1) event.contextMenu.appendContextMenuItem("loadall", "Load All Plugins (" + event.userInfo.blocked + ")"); + } if(safari.extension.settings["useWLcontext"]) { event.contextMenu.appendContextMenuItem("srcwhitelist", "Add Source to Whitelist\u2026"); } // BEGIN DEBUG if(safari.extension.settings["debug"]) { - event.contextMenu.appendContextMenuItem(event.userInfo.CTPInstance + "," + event.userInfo.elementID + ",show", "Show Element " + event.userInfo.CTPInstance + "." + event.userInfo.elementID); + event.contextMenu.appendContextMenuItem(event.userInfo.instance + "," + event.userInfo.elementID + ",show", "Show Element " + event.userInfo.instance + "." + event.userInfo.elementID); } //END DEBUG } } function doCommand(event) { - switch(event.command) { - case "gotosite": - var newTab = safari.application.activeBrowserWindow.openTab("foreground"); - newTab.url = event.userInfo.siteInfo.url; - break; - case "locwhitelist": - handleWhitelisting(true, event.userInfo.location); - break; - case "srcwhitelist": - handleWhitelisting(false, event.userInfo.src); - break; - case "loadall": - safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadAll", ""); - break; - default: - safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadContent", event.command); - break; - } + switch(event.command) { + case "gotosite": + var newTab = safari.application.activeBrowserWindow.openTab("foreground"); + newTab.url = event.userInfo.siteInfo.url; + break; + case "locwhitelist": + handleWhitelisting(true, event.userInfo.location); + break; + case "srcwhitelist": + handleWhitelisting(false, event.userInfo.src); + break; + case "loadall": + safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadAll", ""); + break; + default: + safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("loadContent", event.command); + break; + } } function handleWhitelisting (type, url) { @@ -281,25 +199,23 @@ function handleWhitelisting (type, url) { function handleChangeOfSettings(event) { if(event.key == "volume") { - // send to all pages or just the active one?? safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("updateVolume", event.newValue); } else if(event.key = "opacity") { - dispatchMessageToAllPages("updateOpacity", event.newValue); - } + dispatchMessageToAllPages("updateOpacity", event.newValue); + } } -function getSettings() { - var settings = new Object(); - settings.useH264 = safari.extension.settings["useH264"]; - settings.usePlaylists = safari.extension.settings["usePlaylists"]; - //settings.maxres = safari.extension.settings["maxresolution"]; +function getSettings() { // return the settings injected scripts need + var settings = new Object(); + settings.useH264 = safari.extension.settings["useH264"]; + settings.usePlaylists = safari.extension.settings["usePlaylists"]; settings.H264autoload = safari.extension.settings["H264autoload"]; settings.H264behavior = safari.extension.settings["H264behavior"]; settings.volume = safari.extension.settings["volume"]; settings.sifrReplacement = safari.extension.settings["sifrReplacement"]; - settings.opacity = safari.extension.settings["opacity"]; + settings.opacity = safari.extension.settings["opacity"]; settings.debug = safari.extension.settings["debug"]; - return settings; + return settings; } function killPlugin(data) { @@ -307,21 +223,21 @@ function killPlugin(data) { if(killerID == null) return; // BEGIN DEBUG if(safari.extension.settings["debug"]) { - if(!confirm("Killer '" + killers[killerID].name + "' thinks it might be able to process target " + data.CTPInstance +"."+ data.elementID + ".")) return; + if(!confirm("Killer '" + killers[killerID].name + "' thinks it might be able to process target " + data.instance +"."+ data.elementID + ".")) return; } // END DEBUG var callback = function(mediaData) { mediaData.elementID = data.elementID; - mediaData.CTPInstance = data.CTPInstance; + mediaData.instance = data.instance; // the following messsage must be dispatched to all pages to make sure that - // pages or tabs loading in the background get their videoData + // pages or tabs loading in the background get their mediaData dispatchMessageToAllPages("mediaData", mediaData); }; killers[killerID].processElement(data, callback); } function findKillerFor(data) { - for (i = 0; i < killers.length; i++) { + for (i = 0; i < killers.length; i++) { if(killers[i].canKill(data)) return i; } return null; diff --git a/ClickToPlugin.safariextension/globalfunctions.js b/ClickToPlugin.safariextension/globalfunctions.js index 1bc5ad5d..01a68cab 100644 --- a/ClickToPlugin.safariextension/globalfunctions.js +++ b/ClickToPlugin.safariextension/globalfunctions.js @@ -1,40 +1,37 @@ -function makeAbsoluteURI(url) { +function makeAbsoluteURI(url, location) { if(!url) return ""; - var tmpAnchor = document.createElement("a"); - tmpAnchor.href = url; - return tmpAnchor.href; + if(/\/\//.test(url)) return url; // already absolute + location = location.replace(/\/[^\/]*$/, "/"); + if(url[0]=="/") url = url.substring(1); + if(url[0]=="/") { + url = url.substring(1); + location = location.replace(/\/\/.*$/,"//"); + } + return location + url; } -// This function returns false if the url shoudl not be proposed -// as a video replacement, according to the user's settings -/*function getMediaTypeOf(url) { - if (url.match(/(.mp4)|(.mpe{0,1}g)/i) || (safari.extension.settings["QTbehavior"] > 1 && url.match(/(.flv)/i))) return "video"; - if(url.match(/(.mp3)|(.wav)|(.aiff)|(.aac)/i) || (safari.extension.settings["QTbehavior"] > 1 && url.match(/.wma/i))) return "audio"; - return false; -}*/ - function getFlashVariable(flashvars, key) { - if (!flashvars) return ""; - var flashVarsArray = flashvars.split("&"); - for (var i = 0; i < flashVarsArray.length; i++) { - var keyValuePair = flashVarsArray[i].split("="); - if (keyValuePair[0] == key) { - return keyValuePair[1]; - } - } - return ""; + if (!flashvars) return ""; + var flashVarsArray = flashvars.split("&"); + for (var i = 0; i < flashVarsArray.length; i++) { + var keyValuePair = flashVarsArray[i].split("="); + if (keyValuePair[0] == key) { + return keyValuePair[1]; + } + } + return ""; } function getSLVariable(initParams, key) { - if (!initParams) return ""; - var initParamsArray = initParams.split(","); - for (var i = 0; i < initParamsArray.length; i++) { - var keyValuePair = initParamsArray[i].split("="); - if (keyValuePair[0].toLowerCase() == key) { - return keyValuePair[1]; - } - } - return ""; + if (!initParams) return ""; + var initParamsArray = initParams.split(","); + for (var i = 0; i < initParamsArray.length; i++) { + var keyValuePair = initParamsArray[i].split("="); + if (keyValuePair[0].toLowerCase() == key) { + return keyValuePair[1]; + } + } + return ""; } function getMIMEType(resourceURL, handleMIMEType) { @@ -85,10 +82,10 @@ function matchList(list, string, lowerCase) { // set lowerCase to true if 'strin // if s is enclosed in parenthesis, interpret as regexp if (s[0] == "(" && s[s.length - 1] == ")") { try{ - s = new RegExp(s, (lowerCase ? "i" : "")); - } catch (err) { // invalid regexp, just ignore - continue; - } + s = new RegExp(s, (lowerCase ? "i" : "")); + } catch (err) { // invalid regexp, just ignore + continue; + } } else if(lowerCase) { s = s.toLowerCase(); } @@ -97,4 +94,80 @@ function matchList(list, string, lowerCase) { // set lowerCase to true if 'strin } } return false; -} \ No newline at end of file +} + +/*********************** +Plugin detection methods +***********************/ + +function getPluginForType(MIMEType) { // MIMEType is a string + for(var i = 0; i < navigator.plugins.length; i++) { + for(var j = 0; j < navigator.plugins[i].length; j++) { + if(navigator.plugins[i][j].type == MIMEType) return navigator.plugins[i]; + } + } + return null; +} + +function getPluginAndTypeForExt(ext) { + var suffixes = null; + for(var i = 0; i < navigator.plugins.length; i++) { + for(var j = 0; j < navigator.plugins[i].length; j++) { + suffixes = navigator.plugins[i][j].suffixes.split(","); + for(var k = 0; k < suffixes.length; k++) { + if(ext == suffixes[k]) return {"plugin": navigator.plugins[i], "type": navigator.plugins[i][j].type}; + } + } + } + return {"plugin": null, "type": null}; +} + +function getPluginNameFromPlugin(plugin) { + if(plugin.name == "Shockwave Flash") return "Flash"; + if(plugin.name == "Silverlight Plug-In") return "Silverlight"; + if(plugin.name.match("Java")) return "Java"; + if(plugin.name.match("QuickTime")) return "QuickTime"; + if(plugin.name.match("Flip4Mac")) return "WM"; + if(plugin.name == "iPhotoPhotocast") return "iPhoto"; + if(plugin.name == "Quartz Composer Plug-In") return "Quartz"; + if(plugin.name == "VideoLAN VLC Plug-in") return "VLC"; + if(plugin.name == "DivX Web Player") return "DivX"; + if(plugin.name == ("RealPlayer Plugin.plugin")) return "Real"; + return plugin.name; +} + +function getPluginNameFromType(type) { // only used if no installed plugin is found + if(/shockwave-flash/.test(type) || /futuresplash/.test(type)) return "Flash"; + if(/silverlight/.test(type)) return "Silverlight"; + if(/x-java/.test(type)) return "Java"; + if(/x-ms/.test(type)) return "WM"; + if(/x-pn/.test(type)) return "Real"; + type = type.split(";")[0]; + if(type == "video/divx") return "DivX"; + return type.split("/")[1]; +} + +function getPluginNameFromClassid(classid) { // last resort + switch(classid.toLowerCase()) { + case "d27cdb6e-ae6d-11cf-96b8-444553540000": return "Flash"; + case "22d6f312-b0f6-11d0-94ab-0080c74c7e95": return "WM"; + case "6bf52a52-394a-11d3-b153-00c04f79faa6": return "WM"; + case "02bf25d5-8c17-4b23-bc80-d3488abddc6b": return "QuickTime"; + case "cfcdaa03-8be4-11cf-b84b-0020afbbccfa": return "Real"; + case "67dabfbf-d0ab-41fa-9c46-cc0f21721616": return "DivX"; + default: return "?"; + } +} + +/* +LIST OF CLASSIDs +QuickTime: 02BF25D5-8C17-4B23-BC80-D3488ABDDC6B +WMP 6: 22d6f312-b0f6-11d0-94ab-0080c74c7e95 +WMP >6: 6BF52A52-394A-11D3-B153-00C04F79FAA6 +Flash: d27cdb6e-ae6d-11cf-96b8-444553540000 +Real Player: CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA +?? calendar: 8E27C92B-1264-101C-8A2F-040224009C02 +?? graphics: 369303C2-D7AC-11D0-89D5-00A0C90833E6 +?? slider: F08DF954-8592-11D1-B16A-00C0F0283628 +DivX: 67DABFBF-D0AB-41fa-9C46-CC0F21721616 +*/ \ No newline at end of file diff --git a/ClickToPlugin.safariextension/killers/DailymotionKiller.js b/ClickToPlugin.safariextension/killers/DailymotionKiller.js index cee66d97..03fc92ef 100644 --- a/ClickToPlugin.safariextension/killers/DailymotionKiller.js +++ b/ClickToPlugin.safariextension/killers/DailymotionKiller.js @@ -3,7 +3,7 @@ function DailymotionKiller() { } DailymotionKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return (data.src.match("/dmplayerv4/") || data.src.match("www.dailymotion.com")); }; @@ -23,7 +23,7 @@ DailymotionKiller.prototype.processElement = function(data, callback) { DailymotionKiller.prototype.processElementFromSequence = function(sequence, callback) { var posterURL = null; - var videoURL = null; + var videoURL = null; var badgeLabel = "H.264"; if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV) { var URLindex = sequence.indexOf("sdURL"); @@ -33,7 +33,7 @@ DailymotionKiller.prototype.processElementFromSequence = function(sequence, call videoURL = s.replace(/\\\//g,"/"); } } - var URLindex = sequence.indexOf("hqURL"); // there's also an sdURL but it is an FLV video + var URLindex = sequence.indexOf("hqURL"); if (URLindex != -1) { var s = sequence.substring(URLindex+8); s = s.substring(0,s.indexOf("\"")); @@ -48,12 +48,11 @@ DailymotionKiller.prototype.processElementFromSequence = function(sequence, call videoURL = s.replace(/\\\//g,"/"); } } - URLindex = sequence.indexOf("videoPreviewURL"); + URLindex = sequence.indexOf("videoPreviewURL"); if (URLindex != -1) { var s = sequence.substring(URLindex+18); s = s.substring(0,s.indexOf("\"")); posterURL = s.replace(/\\\//g,"/"); - //alert(posterURL); } var videoData = { "playlist": [{"mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], @@ -69,10 +68,10 @@ DailymotionKiller.prototype.processElementFromVideoID = function(videoID, callba req.open("GET", "http://www.dailymotion.com/video/" + videoID, true); req.onload = function() { var sequence = req.responseText.match(toMatch)[0]; - var callbackForEmbed = function(videoData) { - videoData.playlist[0].siteInfo = {"name": "Dailymotion", "url": "http://www.dailymotion.com/video/" + videoID}; - callback(videoData); - } + var callbackForEmbed = function(videoData) { + videoData.playlist[0].siteInfo = {"name": "Dailymotion", "url": "http://www.dailymotion.com/video/" + videoID}; + callback(videoData); + } if(sequence) {_this.processElementFromSequence(unescape(sequence), callbackForEmbed);} }; // BEGIN DEBUG diff --git a/ClickToPlugin.safariextension/killers/DivXKiller.js b/ClickToPlugin.safariextension/killers/DivXKiller.js index 80232ccc..6a4185c8 100644 --- a/ClickToPlugin.safariextension/killers/DivXKiller.js +++ b/ClickToPlugin.safariextension/killers/DivXKiller.js @@ -1,5 +1,5 @@ function DivXKiller() { - this.name = "DivXKiller"; + this.name = "DivXKiller"; } @@ -9,9 +9,9 @@ DivXKiller.prototype.canKill = function(data) { DivXKiller.prototype.processElement = function(data, callback) { - var videoData = { + var videoData = { "playlist": [{"mediaType": "video", "posterURL": data.image, "mediaURL": data.src}], "badgeLabel": "Video" }; - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.safariextension/killers/JWKiller.js b/ClickToPlugin.safariextension/killers/JWKiller.js index d380792d..b6e0fe9e 100644 --- a/ClickToPlugin.safariextension/killers/JWKiller.js +++ b/ClickToPlugin.safariextension/killers/JWKiller.js @@ -1,93 +1,96 @@ function JWKiller() { - this.name = "JWKiller"; + this.name = "JWKiller"; } JWKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; - return (getFlashVariable(data.params, "file") || getFlashVariable(data.params, "playlistfile")); + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + // streams are not supported + return (!getFlashVariable(data.params, "streamer") && (getFlashVariable(data.params, "file") || getFlashVariable(data.params, "playlistfile"))); }; JWKiller.prototype.processElement = function(data, callback) { - var playlistURL = getFlashVariable(data.params, "playlistfile"); + var playlistURL = getFlashVariable(data.params, "playlistfile"); var sourceURL = getFlashVariable(data.params, "file"); - var posterURL = getFlashVariable(data.params, "image"); - - if(safari.extension.settings["usePlaylists"]) { - if(playlistURL) { - this.processElementFromPlaylist(playlistURL, getFlashVariable(data.params, "item"), posterURL, callback); - return; - } - if(sourceURL.match(".xml")) { - this.processElementFromPlaylist(sourceURL, getFlashVariable(data.params, "item"), posterURL, callback); - return; - } - } + var posterURL = getFlashVariable(data.params, "image"); - var sourceURL2 = getFlashVariable(data.params, "real_file"); - if(sourceURL2) sourceURL = sourceURL2; - - var mediaType = checkSrc(sourceURL); - if(!mediaType) return; + if(safari.extension.settings["usePlaylists"]) { + if(playlistURL) { + this.processElementFromPlaylist(playlistURL, data.location, getFlashVariable(data.params, "item"), posterURL, callback); + return; + } + if(/.xml($|\?)/i.test(sourceURL)) { + this.processElementFromPlaylist(sourceURL, data.location, getFlashVariable(data.params, "item"), posterURL, callback); + return; + } + } + + var sourceURL2 = getFlashVariable(data.params, "real_file"); + if(sourceURL2) sourceURL = sourceURL2; + + var mediaType = checkSrc(sourceURL); + if(!mediaType) return; - var mediaData = { - "playlist": [{"mediaType": mediaType, "posterURL": makeAbsoluteURI(posterURL), "mediaURL": makeAbsoluteURI(sourceURL)}], + var mediaData = { + "playlist": [{"mediaType": mediaType, "posterURL": makeAbsoluteURI(posterURL, data.location), "mediaURL": makeAbsoluteURI(sourceURL, data.location)}], "badgeLabel": (mediaType == "video") ? "Video" : "Audio" }; - callback(mediaData); + callback(mediaData); }; // put a more complete function in globalfunctions.js function checkSrc(sourceURL) { - if (sourceURL.match(/(.mp4)|(.mpe{0,1}g)/i)) return "video"; - if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV && sourceURL.match(/.flv/i)) return "video"; - if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/(.wmv)|(.asf)/i)) return "video"; - if(sourceURL.match(/(.mp3)|(.wav)|(.aiff)|(.aac)/i)) return "audio"; - if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/.wma/i)) return "audio"; - return false; + if (sourceURL.match(/.mp4|.mpe{0,1}g|.mov/i)) return "video"; + if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV && sourceURL.match(/.flv/i)) return "video"; + if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/.wmv|.asf/i)) return "video"; + if(sourceURL.match(/.mp3|.wav|.aiff|.aac/i)) return "audio"; + if(safari.extension.settings["QTbehavior"] > 1 && canPlayWM && sourceURL.match(/.wma/i)) return "audio"; + return false; }; -JWKiller.prototype.processElementFromPlaylist = function(playlistURL, track, posterURL, callback) { - var req = new XMLHttpRequest(); - var startTrack = track; - playlistURL = makeAbsoluteURI(playlistURL); +JWKiller.prototype.processElementFromPlaylist = function(playlistURL, location, track, posterURL, callback) { + var req = new XMLHttpRequest(); + var startTrack = track; + playlistURL = makeAbsoluteURI(playlistURL, location); req.open('GET', playlistURL, true); req.onload = function() { + var isAudio = true; var x = req.responseXML.getElementsByTagName("track"); - if(!(track >= 0 && track < x.length)) track = 0; - var playlist = new Array(); - var url = ""; - var list = null; - var title = ""; - var poster = null; - for(var i = 0; i < x.length; i++) { - list = x[(i + track) % x.length].getElementsByTagName("location"); - if(list.length > 0) url = list[0].firstChild.nodeValue; - else url = ""; - list = x[(i + track) % x.length].getElementsByTagName("title"); - if(list.length > 0) title = list[0].firstChild.nodeValue; - else title = ""; - list = x[(i + track) % x.length].getElementsByTagName("image"); - if(list.length > 0) poster = list[0].firstChild.nodeValue; - else poster = ""; - var mediaType = checkSrc(url); - if(mediaType) { - playlist.push({"title": title, "mediaType": mediaType, "posterURL": makeAbsoluteURI(poster), "mediaURL": makeAbsoluteURI(url)}); - if(mediaType == "video") isAudio = false; - } else { - if(i >= x.length - track) --startTrack; - } - } - if(playlist.length == 0) return; - if(!playlist[0].posterURL) playlist[0].posterURL = makeAbsoluteURI(posterURL); + if(!(track >= 0 && track < x.length)) track = 0; + var playlist = new Array(); + var url = ""; + var list = null; + var title = ""; + var poster = null; + for(var i = 0; i < x.length; i++) { + list = x[(i + track) % x.length].getElementsByTagName("location"); + if(list.length > 0) url = list[0].firstChild.nodeValue; + else if(i == 0) return; + else continue; + list = x[(i + track) % x.length].getElementsByTagName("title"); + if(list.length > 0) title = list[0].firstChild.nodeValue; + else title = ""; + list = x[(i + track) % x.length].getElementsByTagName("image"); + if(list.length > 0) poster = list[0].firstChild.nodeValue; + else poster = ""; + var mediaType = checkSrc(url); + if(mediaType) { + playlist.push({"title": title, "mediaType": mediaType, "posterURL": makeAbsoluteURI(poster, location), "mediaURL": makeAbsoluteURI(url, location)}); + if(mediaType == "video") isAudio = false; + } else { + if(i == 0) return; + if(i >= x.length - track) --startTrack; + } + } + if(!playlist[0].posterURL) playlist[0].posterURL = makeAbsoluteURI(posterURL, location); var mediaData = { - "startTrack": startTrack, - "isAudio": isAudio, - "playlist": playlist, - "badgeLabel": isAudio ? "Audio" : "Video" - }; - callback(mediaData); + "startTrack": startTrack, + "isAudio": isAudio, + "playlist": playlist, + "badgeLabel": playlist[0].mediaType == "audio" ? "Audio" : "Video" + }; + callback(mediaData); }; - // BEGIN DEBUG + // BEGIN DEBUG if(safari.extension.settings["debug"]) { if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + playlistURL)) return; } diff --git a/ClickToPlugin.safariextension/killers/QTKiller.js b/ClickToPlugin.safariextension/killers/QTKiller.js index f7e83ad5..297914bf 100644 --- a/ClickToPlugin.safariextension/killers/QTKiller.js +++ b/ClickToPlugin.safariextension/killers/QTKiller.js @@ -1,5 +1,5 @@ function QTKiller() { - this.name = "QTKiller"; + this.name = "QTKiller"; } @@ -13,11 +13,11 @@ QTKiller.prototype.processElement = function(data, callback) { var playlist = null; if(data.presrc) playlist = [{"mediaType": "video", "mediaURL": data.presrc}, {"mediaType": "video", "mediaURL": data.src}]; else playlist = [{"mediaType": "video", "mediaURL": data.src}]; - var videoData = { - "noPlaylistControls": true, + var videoData = { + "noPlaylistControls": true, "playlist": playlist, "badgeLabel": "Video" }; - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.safariextension/killers/SLKiller.js b/ClickToPlugin.safariextension/killers/SLKiller.js index 958b0d74..1b1538b0 100644 --- a/ClickToPlugin.safariextension/killers/SLKiller.js +++ b/ClickToPlugin.safariextension/killers/SLKiller.js @@ -1,5 +1,5 @@ function SLKiller() { - this.name = "SLKiller"; + this.name = "SLKiller"; } @@ -14,9 +14,9 @@ SLKiller.prototype.processElement = function(data, callback) { if(!videoURL.match(/\.((wm(?!x))|(asf))/)) return; var posterURL = getSLVariable(data.params, "thumbnail"); - var videoData = { + var videoData = { "playlist": [{"mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], "badgeLabel": "Video" } - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.safariextension/killers/VeohKiller.js b/ClickToPlugin.safariextension/killers/VeohKiller.js index e2e4535a..72beda1c 100644 --- a/ClickToPlugin.safariextension/killers/VeohKiller.js +++ b/ClickToPlugin.safariextension/killers/VeohKiller.js @@ -1,39 +1,38 @@ function VeohKiller() { - this.name = "VeohKiller"; + this.name = "VeohKiller"; } VeohKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return (safari.extension.settings["QTbehavior"] > 1 && canPlayFLV && data.src.match("veoh.com/static/swf/webplayer")); }; VeohKiller.prototype.processElement = function(data, callback) { var videoID = null; - var isEmbed = false; + var isEmbed = false; if(data.params) videoID = getFlashVariable(data.params, "permalinkId"); else { // embedded video - isEmbed = true; + isEmbed = true; var matches = data.src.match(/permalinkId=([^&]*)(?=&)/); if(matches) videoID = matches[0].replace("permalinkId=",""); } var posterURL = null; var videoURL = null; - var request = new XMLHttpRequest(); - request.open('GET', "http://www.veoh.com/rest/video/" + videoID + "/details", true); + var request = new XMLHttpRequest(); + request.open('GET', "http://www.veoh.com/rest/video/" + videoID + "/details", true); request.onload = function() { var element = request.responseXML.getElementsByTagName("video")[0]; if(element) { videoURL = element.getAttribute("fullPreviewHashPath"); //"fullPreviewHashLowPath" posterURL = element.getAttribute("fullHighResImagePath"); - //alert(element.getAttribute("previewHash") + "\n" + element.getAttribute("fullPreviewHashPath") + "\n\n" + element.getAttribute("previewHashLow") + "\n" + element.getAttribute("fullPreviewHashLowPath")); } var videoData = { "playlist": [{"mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], "badgeLabel": "Video" // There's no HD on Veoh, as far as I see, despite what they say. It's < 360p! Am I doing something wrong?? } - if(isEmbed) videoData.playlist[0].siteInfo = {"name": "Veoh", "url": "http://www.veoh.com/browse/videos#watch%3D" + videoID}; + if(isEmbed) videoData.playlist[0].siteInfo = {"name": "Veoh", "url": "http://www.veoh.com/browse/videos#watch%3D" + videoID}; callback(videoData); }; // BEGIN DEBUG @@ -41,5 +40,5 @@ VeohKiller.prototype.processElement = function(data, callback) { if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + "http://www.veoh.com/rest/video/" + videoID + "/details")) return; } // END DEBUG - request.send(null); + request.send(null); }; \ No newline at end of file diff --git a/ClickToPlugin.safariextension/killers/VimeoKiller.js b/ClickToPlugin.safariextension/killers/VimeoKiller.js index 6a2e54ea..26f1c9d4 100644 --- a/ClickToPlugin.safariextension/killers/VimeoKiller.js +++ b/ClickToPlugin.safariextension/killers/VimeoKiller.js @@ -3,7 +3,7 @@ function VimeoKiller() { } VimeoKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return !!data.src.match("moogaloop"); }; @@ -17,40 +17,38 @@ VimeoKiller.prototype.processElement = function(data, callback) { if(!videoID) return; var posterURL = null; - var videoURL = null; + var videoURL = null; var badgeLabel = "H.264"; - var request = new XMLHttpRequest(); - request.open('GET', "http://www.vimeo.com/moogaloop/load/clip:" + videoID + "/", false); - //alert("sending AJAX request..."); + var req = new XMLHttpRequest(); + // this request needs to be synchronous, otherwise Vimeo scripts cause errors + req.open('GET', "http://www.vimeo.com/moogaloop/load/clip:" + videoID + "/", false); // BEGIN DEBUG if(safari.extension.settings["debug"]) { if(!confirm("Killer '" + this.name + "' is about to send a synchronous AJAX request to:\n\n" + "http://www.vimeo.com/moogaloop/load/clip:" + videoID + "/")) return; } // END DEBUG - request.send(null); - //alert("request sent! Answer:\n\n" + request.responseText); - var responseXML = new DOMParser().parseFromString(request.responseText,"text/xml"); + req.send(null); if (safari.extension.settings["maxresolution"] > 1) { - if(responseXML.getElementsByTagName("isHD").length > 0) { - if(responseXML.getElementsByTagName("isHD")[0].childNodes[0].nodeValue == "1") badgeLabel = "HD H.264"; + if(req.responseXML.getElementsByTagName("isHD").length > 0) { + if(req.responseXML.getElementsByTagName("isHD")[0].childNodes[0].nodeValue == "1") badgeLabel = "HD H.264"; } } - if(responseXML.getElementsByTagName("request_signature").length > 0 && responseXML.getElementsByTagName("request_signature_expires").length > 0) { - videoURL = "http://www.vimeo.com/moogaloop/play/clip:" + videoID + "/" + responseXML.getElementsByTagName("request_signature")[0].childNodes[0].nodeValue+ "/" + responseXML.getElementsByTagName("request_signature_expires")[0].childNodes[0].nodeValue+"/?q=" + ((badgeLabel == "H.264") ? "mobile" : "hd"); - } - if(responseXML.getElementsByTagName("thumbnail").length > 0) { - posterURL = responseXML.getElementsByTagName("thumbnail")[0].childNodes[0].nodeValue; + if(req.responseXML.getElementsByTagName("request_signature").length > 0 && req.responseXML.getElementsByTagName("request_signature_expires").length > 0) { + videoURL = "http://www.vimeo.com/moogaloop/play/clip:" + videoID + "/" + req.responseXML.getElementsByTagName("request_signature")[0].childNodes[0].nodeValue+ "/" + req.responseXML.getElementsByTagName("request_signature_expires")[0].childNodes[0].nodeValue+"/?q=" + ((badgeLabel == "H.264") ? "mobile" : "hd"); } - var siteInfo = null; - if(!data.location.match("vimeo.com/")) siteInfo = {"name": "Vimeo", "url": "http://vimeo.com/" + videoID}; + if(req.responseXML.getElementsByTagName("thumbnail").length > 0) { + posterURL = req.responseXML.getElementsByTagName("thumbnail")[0].childNodes[0].nodeValue; + } + var siteInfo = null; + if(!data.location.match("vimeo.com/") || data.location == "http://vimeo.com/") siteInfo = {"name": "Vimeo", "url": "http://vimeo.com/" + videoID}; var videoData = { "playlist": [{"siteInfo": siteInfo, "mediaType": "video", "posterURL": posterURL, "mediaURL": videoURL}], "badgeLabel": badgeLabel }; - // Some videos on Vimeo are FLV; need to check that this is not the case, cause Safari can't handle them + // Some videos on Vimeo are FLV; need to check that this is not the case if user doesn't want them if(videoURL) { if(safari.extension.settings["QTbehavior"] > 1 && canPlayFLV) { callback(videoData); @@ -63,7 +61,7 @@ VimeoKiller.prototype.processElement = function(data, callback) { // BEGIN DEBUG else if(safari.extension.settings["debug"]) { alert("Video found by killer 'VimeoKiller' has MIME type " + MIMEType + " and cannot be played natively by QuickTime."); - } + } // END DEBUD }; // BEGIN DEBUG @@ -72,30 +70,5 @@ VimeoKiller.prototype.processElement = function(data, callback) { } // END DEBUG getMIMEType(videoURL, handleMIMEType); - /*request = new XMLHttpRequest(); - request.open('HEAD', videoURL, true); - var gotContentType = false; - request.onreadystatechange = function () { - if(!gotContentType && request.getResponseHeader('Content-Type')) { - gotContentType = true; - if(request.getResponseHeader('Content-Type') != "video/x-flv") { - callback(videoData); - } - // BEGIN DEBUG - else if(safari.extension.settings["debug"]) { - alert("Video found by killer 'VimeoKiller' has MIME type " + request.getResponseHeader('Content-Type') + " and cannot be played by Safari."); - } - // END DEBUD - request.abort(); - } - }; - // BEGIN DEBUG - if(safari.extension.settings["debug"]) { - if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + videoURL)) return; - } - // END DEBUG - request.send(null);*/ - //alert(request.responseText); - //alert(req.getResponseHeader('Content-Type').split(';')[0]); } }; diff --git a/ClickToPlugin.safariextension/killers/WMKiller.js b/ClickToPlugin.safariextension/killers/WMKiller.js index c9001c1d..e4361592 100644 --- a/ClickToPlugin.safariextension/killers/WMKiller.js +++ b/ClickToPlugin.safariextension/killers/WMKiller.js @@ -1,17 +1,17 @@ function WMKiller() { - this.name = "WMKiller"; + this.name = "WMKiller"; } WMKiller.prototype.canKill = function(data) { - return (data.plugin == "WM" && safari.extension.settings["replaceWM"] && safari.extension.settings["QTbehavior"] > 1 && canPlayWM && data.src.match(/\.((wm(?!x))|(asf))/i)); + return (data.plugin == "WM" && safari.extension.settings["replaceWM"] && safari.extension.settings["QTbehavior"] > 1 && canPlayWM && data.src.match(/\.((wm(?!x)|asf))/i)); }; // should check media type... WMKiller.prototype.processElement = function(data, callback) { - var videoData = { + var videoData = { "playlist": [{"mediaType": "video", "mediaURL": data.src}], "badgeLabel": "Video" }; - callback(videoData); + callback(videoData); }; \ No newline at end of file diff --git a/ClickToPlugin.safariextension/killers/YouTubeKiller.js b/ClickToPlugin.safariextension/killers/YouTubeKiller.js index 547e00d7..397e18aa 100644 --- a/ClickToPlugin.safariextension/killers/YouTubeKiller.js +++ b/ClickToPlugin.safariextension/killers/YouTubeKiller.js @@ -1,181 +1,162 @@ function YouTubeKiller() { - this.name = "YouTubeKiller"; + this.name = "YouTubeKiller"; } YouTubeKiller.prototype.canKill = function(data) { - if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; + if(data.plugin != "Flash" || !safari.extension.settings["replaceFlash"]) return false; return (data.src.match("ytimg.com") || data.src.match("youtube.com") || data.src.match("youtube-nocookie.com")); }; YouTubeKiller.prototype.processElement = function(data, callback) { if(data.params) { - if(safari.extension.settings["usePlaylists"]) { - // see http://apiblog.youtube.com/2010/03/upcoming-change-to-youtube-video-page.html - // these new YT urls break everything!!!! Video data is loaded dynamically - // after the Flash has been loaded... no more flashvars :( - var URLvars = data.location.split("?")[1]; - var playlistID = null; - if(URLvars) { - URLvars = URLvars.split("&"); - for (var i = 0; i < URLvars.length; i++) { - var keyValuePair = URLvars[i].split("="); - if (keyValuePair[0] == "p") { - playlistID = keyValuePair[1]; - break; - } - } - } - if(playlistID) { - this.buildVideoIDList(data.params, playlistID, 0, new Array(), callback); - } else this.processElementFromFlashVars(data.params, callback); - } else this.processElementFromFlashVars(data.params, callback); + if(safari.extension.settings["usePlaylists"]) { + var URLvars = data.location.split(/#!|\?/)[1]; + var playlistID = null; + if(URLvars) { + URLvars = URLvars.split("&"); + for (var i = 0; i < URLvars.length; i++) { + var keyValuePair = URLvars[i].split("="); + if (keyValuePair[0] == "p") { + playlistID = keyValuePair[1]; + break; + } + } + } + if(playlistID) { + this.buildVideoIDList(data.params, data.location, playlistID, 0, new Array(), callback); + } else this.processElementFromFlashVars(data.params, data.location, callback); + } else this.processElementFromFlashVars(data.params, data.location, callback); return; } // The vid has no flashvars... Only hope is that it is a YouTube /v/ embed var index = data.src.indexOf(".com/v/"); if(index == -1) { - if(safari.extension.settings["usePlaylists"]) { - index = data.src.indexOf(".com/p/"); - if(index == -1) return; - var playlistID = data.src.substring(index + 7); - index = playlistID.indexOf(".com/p/"); - if(index != -1) playlistID = playlistID.substring(index + 7); - index = playlistID.search(/\?|&/); - if(index != -1) playlistID = playlistID.substring(0,index); - this.buildVideoIDList(null, playlistID, 0, new Array(), callback); - } - return; - } + if(safari.extension.settings["usePlaylists"]) { + index = data.src.indexOf(".com/p/"); + if(index == -1) return; + var playlistID = data.src.substring(index + 7); + index = playlistID.indexOf(".com/p/"); + if(index != -1) playlistID = playlistID.substring(index + 7); + index = playlistID.search(/\?|&/); + if(index != -1) playlistID = playlistID.substring(0,index); + this.buildVideoIDList(null, data.location, playlistID, 0, new Array(), callback); + } + return; + } var videoID = data.src.substring(index + 7); - index = videoID.indexOf(".com/v/"); - if(index != -1) videoID = videoID.substring(index + 7); + index = videoID.indexOf(".com/v/"); + if(index != -1) videoID = videoID.substring(index + 7); index = videoID.search(/\?|&/); if(index != -1) videoID = videoID.substring(0,index); this.processElementFromVideoID(videoID, callback); }; -/*YouTubeKiller.prototype.processElementFromPlaylist = function(videoID, plaslistID, callback) { - var playlistURL = function(i) { - return "feed://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50" - }; - var isEmpty = false; - var i = 0; - var req = null; - while(!isEmpty) { - req = new XMLHttpRequest(); - req.open('GET', playlistURL(i), true); - req.onload() - } -};*/ - -// this function adds playlist data -YouTubeKiller.prototype.buildVideoIDList = function(flashvars, playlistID, i, videoIDList, callback) { - req = new XMLHttpRequest(); - req.open('GET', "http://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50", true); - var _this = this; - req.onload = function() { - //alert(req.responseText); - var entries = req.responseXML.getElementsByTagName("entry"); - // this is not always the correct number!! - //if(!imax) imax = parseInt(responseXML.getElementsByTagNameNS("http://a9.com/-/spec/opensearchrss/1.0/", "totalResults")[0].firstChild.nodeValue); - for(var j = 0; j < entries.length; j++) { - try{ - videoIDList.push(entries[j].getElementsByTagNameNS("http://search.yahoo.com/mrss/", "player")[0].getAttribute("url").match(/\?v=[^(&|\?)]*(?=(&|\?))/)[0].replace("?v=","")); - } catch(err) {} - } - if(entries.length < 50) {// we've got the whole list of videoIDs - var track = 0; - var length = videoIDList.length; - if(flashvars) { - var videoID = getFlashVariable(flashvars, "video_id"); - for(var j = 0; j < videoIDList.length; j++) { - if(videoIDList[0] == videoID) {track = j; break;} - videoIDList.push(videoIDList.shift()); - } - //alert(videoIDList.length + " tracks\n\n" + videoIDList.join("\n")); - // load the first video at once - } - var callbackForPlaylist = function(videoData) { - videoData.playlistLength = length; - videoData.startTrack = track; - if(videoData.playlist[0].siteInfo) videoData.playlist[0].siteInfo.url += "&p=" + playlistID; - callback(videoData); - }; - if(flashvars) _this.processElementFromFlashVars(flashvars, callbackForPlaylist); - else _this.processElementFromVideoID(videoIDList[0], callbackForPlaylist); - videoIDList.shift(); - _this.buildPlaylist(videoIDList, playlistID, true, 3, callback); - return; - } - _this.buildVideoIDList(flashvars, playlistID, ++i, videoIDList, callback); - }; - // BEGIN DEBUG +YouTubeKiller.prototype.buildVideoIDList = function(flashvars, location, playlistID, i, videoIDList, callback) { + req = new XMLHttpRequest(); + req.open('GET', "http://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50", true); + var _this = this; + req.onload = function() { + var entries = req.responseXML.getElementsByTagName("entry"); + for(var j = 0; j < entries.length; j++) { + try{ + videoIDList.push(entries[j].getElementsByTagNameNS("http://search.yahoo.com/mrss/", "player")[0].getAttribute("url").match(/\?v=[^(&|\?)]*(?=(&|\?))/)[0].replace("?v=","")); + } catch(err) {} + } + if(entries.length < 50) {// we've got the whole list of videoIDs + var track = 0; + var length = videoIDList.length; + if(flashvars) { + var videoID = getFlashVariable(flashvars, "video_id"); + if(!videoID) { // new YT AJAX player + var matches = location.match(/(!|&)v=[^&]+(&|$)/); + if(!matches) return; + videoID = matches[0].substring(3).replace("&", ""); + flashvars = null; + } + for(var j = 0; j < videoIDList.length; j++) { + if(videoIDList[0] == videoID) {track = j; break;} + videoIDList.push(videoIDList.shift()); + } + } + var callbackForPlaylist = function(videoData) { + videoData.playlistLength = length; + videoData.startTrack = track; + if(videoData.playlist[0].siteInfo) videoData.playlist[0].siteInfo.url += "&p=" + playlistID; + callback(videoData); + }; + // load the first video at once + if(flashvars) _this.processElementFromFlashVars(flashvars, location, callbackForPlaylist); + else _this.processElementFromVideoID(videoIDList[0], callbackForPlaylist); + videoIDList.shift(); + // load the rest of the playlist 3 by 3 + _this.buildPlaylist(videoIDList, playlistID, true, 3, callback); + return; + } + _this.buildVideoIDList(flashvars, location, playlistID, ++i, videoIDList, callback); + }; + // BEGIN DEBUG if(safari.extension.settings["debug"]) { if(!confirm("Killer '" + this.name + "' is about to send an asynchronous AJAX request to:\n\n" + "http://gdata.youtube.com/feeds/api/playlists/" + playlistID + "?start-index=" + (50*i + 1) + "&max-results=50")) return; } // END DEBUG - req.send(null); + req.send(null); }; YouTubeKiller.prototype.buildPlaylist = function(videoIDList, playlistID, isFirst, n, callback) { - if(videoIDList.length == 0) return; - var j = 0; - var jmax = videoIDList.length; - if(isFirst) --n; - if(jmax > n) jmax = n; // load by groups of n - if(isFirst) ++n; - var mediaData = {"loadAfter": true, "missed": 0, "playlist": []}; - var _this = this; - var next = function(videoData) { - // this actually works!! - if(videoData.playlist.length > 0) { - videoData.playlist[0].siteInfo.url += "&p=" + playlistID; - mediaData.playlist.push(videoData.playlist[0]); - } else {// playlist is 1 shorter than announced - ++mediaData.missed; - } - ++j; - //alert(mediaData.playlist.length); - if(j == jmax) { - callback(mediaData); - // continue building - _this.buildPlaylist(videoIDList, playlistID, false, n, callback); - } else _this.processElementFromVideoID(videoIDList.shift(), next); - }; - this.processElementFromVideoID(videoIDList.shift(), next); - return; + if(videoIDList.length == 0) return; + var j = 0; + var jmax = videoIDList.length; + if(isFirst) --n; + if(jmax > n) jmax = n; // load by groups of n + if(isFirst) ++n; + var mediaData = {"loadAfter": true, "missed": 0, "playlist": []}; + var _this = this; + var next = function(videoData) { + // this actually works!! + if(videoData.playlist.length > 0) { + videoData.playlist[0].siteInfo.url += "&p=" + playlistID; + mediaData.playlist.push(videoData.playlist[0]); + } else { // playlist is 1 shorter than announced + ++mediaData.missed; + } + ++j; + if(j == jmax) { + callback(mediaData); + _this.buildPlaylist(videoIDList, playlistID, false, n, callback); + } else _this.processElementFromVideoID(videoIDList.shift(), next); + }; + this.processElementFromVideoID(videoIDList.shift(), next); + return; }; YouTubeKiller.prototype.getMediaDataFromURLMap = function(videoID, videoHash, urlMap) { - var availableFormats = []; + var availableFormats = []; var formatInfo = urlMap.split(","); - // alert(formatInfo); - for (var i = 0; i < formatInfo.length; i++) { + for (var i = 0; i < formatInfo.length; i++) { var format = formatInfo[i].split("|"); - availableFormats[format[0]] = format[1]; - } - - var posterURL = "http://i.ytimg.com/vi/" + videoID + "/hqdefault.jpg"; - // this is the 360p video URL - var videoURL = "http://www.youtube.com/get_video?fmt=18&asv=&video_id=" + videoID + "&t=" + videoHash; - var badgeLabel = "H.264"; + availableFormats[format[0]] = format[1]; + } - // Get the highest-quality version set by the user - //var format = 18; + var posterURL = "http://i.ytimg.com/vi/" + videoID + "/hqdefault.jpg"; + // this is the 360p MP4 video URL, always available + var videoURL = "http://www.youtube.com/get_video?fmt=18&asv=&video_id=" + videoID + "&t=" + videoHash; + var badgeLabel = "H.264"; - //alert(req.responseText); - // Only 18, 22, 37, and 38 are h264 playable without plugin in Safari. - // Other container are FLV (0, 5, 6, 34, 35, although the latter two are H.264 360p and 480p), 3GP (13,17), or WebM (43,45) and cannot play natively (even with Perian) - if (availableFormats[38] && safari.extension.settings["maxresolution"] > 3) {// 4K @_@ + /* + Only 18, 22, 37, and 38 are MP4 playable nativey by QuickTime. + Other containers are FLV (0, 5, 6, 34, 35, the latter two are H.264 360p and 480p), + 3GP (13,17), or WebM (43,45) + */ + if (availableFormats[38] && safari.extension.settings["maxresolution"] > 3) {// 4K @_@ badgeLabel = "4K H.264"; - videoURL = availableFormats[38]; + videoURL = availableFormats[38]; } else if (availableFormats[37] && safari.extension.settings["maxresolution"] > 2) {// 1080p badgeLabel = "HD H.264"; - videoURL = availableFormats[37]; - } else if (availableFormats[22] && safari.extension.settings["maxresolution"] > 1) {// 720p + videoURL = availableFormats[37]; + } else if (availableFormats[22] && safari.extension.settings["maxresolution"] > 1) {// 720p badgeLabel = "HD H.264"; - videoURL = availableFormats[22]; - } else if (safari.extension.settings["QTbehavior"] > 2 && canPlayFLV) { + videoURL = availableFormats[22]; + } else if (safari.extension.settings["QTbehavior"] > 2 && canPlayFLV) { if (availableFormats[35]) { // 480p FLV videoURL = availableFormats[35]; } @@ -194,20 +175,28 @@ YouTubeKiller.prototype.getMediaDataFromURLMap = function(videoID, videoHash, ur this.getSDH264FromFmt18(posterURL, videoID, callback); return; // NOTE: possibility 2 seems to always work, so possibility 1 is never needed }*/ - return {"posterURL": posterURL, "videoURL": videoURL, "badgeLabel": badgeLabel}; + return {"posterURL": posterURL, "videoURL": videoURL, "badgeLabel": badgeLabel}; }; -YouTubeKiller.prototype.processElementFromFlashVars = function(flashvars, callback) { - var title = unescape(getFlashVariable(flashvars, "rec_title")).substring(3).replace(/\+/g, " "); +YouTubeKiller.prototype.processElementFromFlashVars = function(flashvars, location, callback) { var videoID = getFlashVariable(flashvars, "video_id"); + // see http://apiblog.youtube.com/2010/03/upcoming-change-to-youtube-video-page.html: + if(!videoID) { // new YT AJAX player (not yet used?) + var matches = location.match(/(!|&)v=[^&]+(&|$)/); + if(!matches) return; + videoID = matches[0].substring(3).replace("&", ""); + this.processElementFromVideoID(videoID, callback); + return; + } var videoHash = getFlashVariable(flashvars, "t"); if(!videoHash) { this.processElementFromVideoID(videoID, callback); return; } - var urlMap = unescape(getFlashVariable(flashvars, "fmt_url_map")); + var title = unescape(getFlashVariable(flashvars, "rec_title")).substring(3).replace(/\+/g, " "); + var urlMap = unescape(getFlashVariable(flashvars, "fmt_url_map")); var x = this.getMediaDataFromURLMap(videoID, videoHash, urlMap); - var videoData = { + var videoData = { "playlist": [{"title": title, "mediaType": "video", "posterURL": x.posterURL, "mediaURL": x.videoURL}], "badgeLabel": x.badgeLabel }; @@ -215,36 +204,35 @@ YouTubeKiller.prototype.processElementFromFlashVars = function(flashvars, callba }; YouTubeKiller.prototype.processElementFromVideoID = function(videoID, callback) { - if(!videoID) return; // needed! + if(!videoID) return; // needed! var toMatch = /\"fmt_url_map\":\s\"[^\"]*\"/; //"// works for both Flash and HTML5 Beta player pages var toMatch2 = /\"t\":\s\"[^\"]*\"/; //"// var _this = this; var req = new XMLHttpRequest (); req.open("GET", "http://www.youtube.com/watch?v=" + videoID, true); req.onload = function() { - var title = ""; - if(safari.extension.settings["usePlaylists"]) { - var toMatchTitle = / 1; - if(this.usePlaylistControls) { - this.initializePlaylistControls(); - } else { - this.initializeDownloadControls(); - } + } + + // Set dimensions + this.width = width; + this.height = height; + this.containerElement.style.width = width + "px"; + this.containerElement.style.height = height + "px"; + + // Set volume + this.mediaElement.volume = volume; + + // Set listeners + var _this = this; // need anonymous function in listeners otherwise the 'this' will refer to the mediaElement! + this.mediaElement.addEventListener("contextmenu", function(event) {_this.setContextInfo(event, contextInfo);}, false); + this.mediaElement.addEventListener("loadedmetadata", function() {_this.fixAspectRatio();}, false); + this.mediaElement.addEventListener("ended", function() {_this.nextTrack();}, false); + this.mediaElement.ondblclick = function() { + _this.switchLoop(); + }; + + if(this.usePlaylistControls) { + this.initializePlaylistControls(); + } else { + this.initializeDownloadControls(); + } }; mediaPlayer.prototype.initializePlaylistControls = function() { - this.playlistControls = document.createElement("div"); - this.playlistControls.className = "CTFplaylistControls"; - - var trackTitle = document.createElement("div"); - trackTitle.className = "CTFtrackTitle"; - this.playlistControls.appendChild(trackTitle); - - var trackSelect = document.createElement("div"); - trackSelect.className = "CTFtrackSelect"; - this.playlistControls.appendChild(trackSelect); - - var trackTitleText = document.createElement("div"); - trackTitleText.className = "CTFtrackTitleText"; - trackTitle.appendChild(trackTitleText); - - var trackTitleTextP = document.createElement("p"); - trackTitleText.appendChild(trackTitleTextP); - - var prevButton = document.createElement("div"); - prevButton.className = "CTFprevButton"; - trackSelect.appendChild(prevButton); - - var trackInput = document.createElement("form"); - trackInput.className = "CTFtrackInput"; - trackSelect.appendChild(trackInput); - - var nextButton = document.createElement("div"); - nextButton.className = "CTFnextButton"; - trackSelect.appendChild(nextButton); + this.playlistControls = document.createElement("div"); + this.playlistControls.className = "CTFplaylistControls"; + + var trackTitle = document.createElement("div"); + trackTitle.className = "CTFtrackTitle"; + this.playlistControls.appendChild(trackTitle); + + var trackSelect = document.createElement("div"); + trackSelect.className = "CTFtrackSelect"; + this.playlistControls.appendChild(trackSelect); + + var trackTitleText = document.createElement("div"); + trackTitleText.className = "CTFtrackTitleText"; + trackTitle.appendChild(trackTitleText); + + var trackTitleTextP = document.createElement("p"); + trackTitleText.appendChild(trackTitleTextP); + + var prevButton = document.createElement("div"); + prevButton.className = "CTFprevButton"; + trackSelect.appendChild(prevButton); + + var trackInput = document.createElement("form"); + trackInput.className = "CTFtrackInput"; + trackSelect.appendChild(trackInput); + + var nextButton = document.createElement("div"); + nextButton.className = "CTFnextButton"; + trackSelect.appendChild(nextButton); - trackInput.innerHTML = "/" + normalize(this.playlist.length, this.playlistLength) + ""; - - var _this = this; - this.mediaElement.onmouseover = function(event) { - this.focus = true; - if(!this.paused && this.readyState > 1) _this.fadeIn(.05); - }; - this.playlistControls.onmouseover = function(event) { - _this.mediaElement.focus = true; - if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeIn(0); - }; - this.mediaElement.onmouseout = function(event) { - // prevents the default controls from disappearing - if(event.relatedTarget && (event.relatedTarget == prevButton || event.relatedTarget == nextButton || event.relatedTarget == trackInput.firstChild || event.relatedTarget == trackTitleTextP.lastChild || event.relatedTarget.hasAttribute("precision"))) { - event.preventDefault(); - } else { - this.focus = false; - if(!this.paused && this.readyState > 1) _this.fadeOut(0); - } - }; - this.playlistControls.onmouseout = function(event) { - _this.mediaElement.focus = false; - if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeOut(.1); - }; - this.mediaElement.focus = false; - this.mediaElement.addEventListener("pause", function(){_this.fadeIn(0);}, false); - this.mediaElement.addEventListener("play", function(){if(!_this.mediaElement.focus) _this.fadeOut(0);}, false); - - trackInput.onsubmit = function(event) { - event.preventDefault(); - var track = this.getElementsByTagName("input")[0].value; - if(!(/^\d+$/.test(track))) return; - track = parseInt(track); - if(track < 1 || track > _this.playlistLength) return; - track = (track - _this.startTrack - 1 + _this.playlistLength) % _this.playlistLength; - if(track == _this.currentTrack) return; - if(track < _this.playlist.length) { - _this.loadTrack(track, true); - } - }; - prevButton.onclick = function() { - if(_this.playlist.length == 1) return; - _this.loadTrack(_this.currentTrack - 1, true); - }; - nextButton.onclick = function() { - if(_this.playlist.length == 1) return; - _this.loadTrack(_this.currentTrack + 1, true); - }; - - // If the controls are shown at once a webkit bug will mess up font smoothing - // when the video starts playing. - // The only way I was able to prevent this is to show controls on loadedmetadata - this.playlistControls.style.opacity = "0"; - this.containerElement.appendChild(this.playlistControls); + trackInput.innerHTML = "/" + normalize(this.playlist.length, this.playlistLength) + ""; + + var _this = this; + this.mediaElement.onmouseover = function(event) { + this.focus = true; + if(!this.paused && this.readyState > 1) _this.fadeIn(.05); + }; + this.playlistControls.onmouseover = function(event) { + _this.mediaElement.focus = true; + if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeIn(0); + }; + this.mediaElement.onmouseout = function(event) { + // prevents the default controls from disappearing + if(event.relatedTarget && (event.relatedTarget == prevButton || event.relatedTarget == nextButton || event.relatedTarget == trackInput.firstChild || event.relatedTarget == trackTitleTextP.lastChild || event.relatedTarget.hasAttribute("precision"))) { + event.preventDefault(); + } else { + this.focus = false; + if(!this.paused && this.readyState > 1) _this.fadeOut(0); + } + }; + this.playlistControls.onmouseout = function(event) { + _this.mediaElement.focus = false; + if(!_this.mediaElement.paused && _this.mediaElement.readyState > 1) _this.fadeOut(.1); + }; + this.mediaElement.focus = false; + this.mediaElement.addEventListener("pause", function(){_this.fadeIn(0);}, false); + this.mediaElement.addEventListener("play", function(){if(!_this.mediaElement.focus) _this.fadeOut(0);}, false); + + trackInput.onsubmit = function(event) { + event.preventDefault(); + var track = this.getElementsByTagName("input")[0].value; + if(!(/^\d+$/.test(track))) return; + track = parseInt(track); + if(track < 1 || track > _this.playlistLength) return; + track = (track - _this.startTrack - 1 + _this.playlistLength) % _this.playlistLength; + if(track == _this.currentTrack) return; + if(track < _this.playlist.length) { + _this.loadTrack(track, true); + } + }; + prevButton.onclick = function() { + if(_this.playlist.length == 1) return; + _this.loadTrack(_this.currentTrack - 1, true); + }; + nextButton.onclick = function() { + if(_this.playlist.length == 1) return; + _this.loadTrack(_this.currentTrack + 1, true); + }; + + this.playlistControls.style.opacity = "1"; + this.containerElement.appendChild(this.playlistControls); }; mediaPlayer.prototype.initializeDownloadControls = function() { - this.playlistControls = document.createElement("div"); - this.playlistControls.className = "CTFplaylistControls"; - - var hoverElement = document.createElement("div"); - hoverElement.className = "CTFhoverElement"; - this.playlistControls.innerHTML = "

"; - - this.playlistControls.appendChild(hoverElement); - - var _this = this; - - this.playlistControls.firstChild.onmouseout = function(event) { - this.nextSibling.style.display = "block"; - _this.fadeOut(0) - }; - - this.playlistControls.firstChild.onmouseover = function(event) { - if(event.relatedTarget && event.relatedTarget.className == "CTFhoverElement") { - this.nextSibling.style.display = "none"; - _this.fadeIn(0) - } - }; - hoverElement.onmouseover = function(event) { - this.previousSibling.style.display ="block"; - } - - hoverElement.onmouseout = function(event) { - if(!event.relatedTarget || event.relatedTarget.className != "CTFtitleText") { - this.previousSibling.style.display ="none"; - } - }; - - this.mediaElement.onmouseout = function(event) { - if(event.relatedTarget && (event.relatedTarget == hoverElement || event.relatedTarget.className == "CTFtitleText")) { - event.preventDefault(); - } - } - - this.playlistControls.addEventListener("webkitTransitionEnd", function() {if(this.style.opacity == "0") this.firstChild.style.display = "none";}, false); - - this.playlistControls.style.opacity = "0"; - this.playlistControls.firstChild.style.display = "none"; - //this.playlistControls.firstChild.style.pointerEvents = "auto"; - - this.containerElement.appendChild(this.playlistControls); + this.playlistControls = document.createElement("div"); + this.playlistControls.className = "CTFplaylistControls"; + + var hoverElement = document.createElement("div"); + hoverElement.className = "CTFhoverElement"; + this.playlistControls.innerHTML = "

"; + + this.playlistControls.appendChild(hoverElement); + + var _this = this; + + this.playlistControls.firstChild.onmouseout = function(event) { + this.nextSibling.style.display = "block"; + _this.fadeOut(0) + }; + + this.playlistControls.firstChild.onmouseover = function(event) { + if(event.relatedTarget && event.relatedTarget.className == "CTFhoverElement") { + this.nextSibling.style.display = "none"; + _this.fadeIn(0) + } + }; + hoverElement.onmouseover = function(event) { + this.previousSibling.style.display ="block"; + } + + hoverElement.onmouseout = function(event) { + if(!event.relatedTarget || event.relatedTarget.className != "CTFtitleText") { + this.previousSibling.style.display ="none"; + } + }; + + this.mediaElement.onmouseout = function(event) { + if(event.relatedTarget && (event.relatedTarget == hoverElement || event.relatedTarget.className == "CTFtitleText")) { + event.preventDefault(); + } + } + + this.playlistControls.addEventListener("webkitTransitionEnd", function() {if(this.style.opacity == "0") this.firstChild.style.display = "none";}, false); + + this.playlistControls.style.opacity = "0"; + this.playlistControls.firstChild.style.display = "none"; + + this.containerElement.appendChild(this.playlistControls); }; mediaPlayer.prototype.fadeOut = function(delay) { - this.playlistControls.style.WebkitTransition = "opacity .4s linear " + delay + "s"; - this.playlistControls.style.opacity = "0"; + this.playlistControls.style.WebkitTransition = "opacity .4s linear " + delay + "s"; + this.playlistControls.style.opacity = "0"; }; mediaPlayer.prototype.fadeIn = function(delay) { - this.playlistControls.style.WebkitTransition = "opacity .05s linear " + delay + "s"; - this.playlistControls.style.opacity = "0.93"; + this.playlistControls.style.WebkitTransition = "opacity .05s linear " + delay + "s"; + this.playlistControls.style.opacity = "0.93"; }; mediaPlayer.prototype.fixAspectRatio = function() { - var w = this.mediaElement.videoWidth; - var h = this.mediaElement.videoHeight; - if(w == 0 || h == 0) { // audio source + var w = this.mediaElement.videoWidth; + var h = this.mediaElement.videoHeight; + if(w == 0 || h == 0) { // audio source //this.mediaElement.style.height = "24px"; // the height of the controls this.mediaElement.style.width = this.width + "px"; this.mediaElement.style.height = this.height + "px"; - if(this.playlistControls) this.playlistControls.style.width = this.width + "px"; } else if (w/h > this.width/this.height) { this.mediaElement.style.width = this.width + "px"; this.mediaElement.style.height = ""; - if(this.playlistControls) this.playlistControls.style.width = this.width + "px"; } else { this.mediaElement.style.height = this.height + "px"; this.mediaElement.style.width = ""; - // Apparently webkit uses floor, not round - if(this.playlistControls) this.playlistControls.style.width = Math.floor(w/h*this.height) + "px"; + if(this.playlistControls) { + // Apparently QuickTime uses floor, not round + var width = Math.floor(w/h*this.height); + this.playlistControls.style.width = width + "px"; + if(this.usePlaylistControls) this.playlistControls.getElementsByTagName("p")[0].style.width = (width - this.playlistControls.getElementsByClassName("CTFtrackSelect")[0].offsetWidth - 12) + "px"; + } + } + if(this.usePlaylistControls) { + // need this otherwise a webkit bug messes up font smoothing + this.fadeIn(.05); } - if(this.usePlaylistControls) { - this.fadeIn(.05); - } - + }; mediaPlayer.prototype.resetAspectRatio = function() { - this.mediaElement.style.width = this.width + "px"; - this.mediaElement.style.height = this.height + "px"; - if(this.playlistControls) this.playlistControls.style.width = this.width + "px"; + this.mediaElement.style.width = this.width + "px"; + this.mediaElement.style.height = this.height + "px"; + if(this.playlistControls) { + this.playlistControls.style.width = this.width + "px"; + if(this.usePlaylistControls) this.playlistControls.getElementsByTagName("p")[0].style.width = (this.width - this.playlistControls.getElementsByClassName("CTFtrackSelect")[0].offsetWidth - 12) + "px"; + } }; mediaPlayer.prototype.switchLoop = function() { - if(this.mediaElement.hasAttribute("loop")) this.mediaElement.removeAttribute("loop"); - else this.mediaElement.setAttribute("loop", "true"); + if(this.mediaElement.hasAttribute("loop")) this.mediaElement.removeAttribute("loop"); + else this.mediaElement.setAttribute("loop", "true"); } mediaPlayer.prototype.nextTrack = function() { - if(!this.mediaElement.hasAttribute("loop")) { - if(this.currentTrack + this.startTrack + 1 == this.playlistLength) return; - this.loadTrack(this.currentTrack + 1, true); - } + if(!this.mediaElement.hasAttribute("loop")) { + if(this.currentTrack + this.startTrack + 1 == this.playlistLength) return; + this.loadTrack(this.currentTrack + 1, true); + } }; mediaPlayer.prototype.loadTrack = function(track, autoplay) { - track = track % this.playlist.length; + track = track % this.playlist.length; if(track < 0) track += this.playlist.length; // weird JS behavior - this.resetAspectRatio(); + this.resetAspectRatio(); this.mediaElement.src = this.playlist[track].mediaURL; - // If src is not set before poster, poster is not shown. Webkit bug? - if(this.playlist[track].posterURL) { - if(this.playlist[track].mediaType == "video") { - this.mediaElement.poster = this.playlist[track].posterURL; - this.mediaElement.style.background = ""; - // this.mediaElement.style.backgroundSize = ""; - } else { - if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); - this.mediaElement.style.backgroundImage = "url('" + this.playlist[track].posterURL + "')"; - this.mediaElement.style.backgroundRepeat = "no-repeat"; - this.mediaElement.style.backgroundPosition = "center center"; - // this.mediaElement.style.backgroundSize = "100 %"; - } - } else { - if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); - this.mediaElement.style.background = ""; - } - this.currentTrack = track; - if(autoplay) { + // If src is not set before poster, poster is not shown. Webkit bug? + if(this.playlist[track].posterURL) { + if(this.playlist[track].mediaType == "video") { + this.mediaElement.poster = this.playlist[track].posterURL; + this.mediaElement.style.background = ""; + // this.mediaElement.style.backgroundSize = ""; + } else { + if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); + this.mediaElement.style.backgroundImage = "url('" + this.playlist[track].posterURL + "')"; + this.mediaElement.style.backgroundRepeat = "no-repeat"; + this.mediaElement.style.backgroundPosition = "center center"; + // this.mediaElement.style.backgroundSize = "100 %"; + } + } else { + if(this.mediaElement.hasAttribute("poster")) this.mediaElement.removeAttribute("poster"); + this.mediaElement.style.background = ""; + } + this.currentTrack = track; + if(autoplay) { this.mediaElement.setAttribute("preload", "auto"); this.mediaElement.setAttribute("autoplay", "autoplay"); } - if(this.usePlaylistControls) { - var title = this.playlist[track].title; - if(!title) title = "(no title)"; - title = "" + title + ""; - this.playlistControls.getElementsByTagName("p")[0].innerHTML = ((track + this.startTrack) % this.playlistLength + 1) + ". " + title; - var inputField = this.playlistControls.getElementsByTagName("input")[0]; - var newInputField = document.createElement("input"); - newInputField.setAttribute("type", "text"); - newInputField.setAttribute("value", (track + this.startTrack) % this.playlistLength + 1); - newInputField.style.width = (8 * this.playlistLength.toString().length) + "px"; - // simply changing the value does not update if user has used the field - this.playlistControls.getElementsByTagName("form")[0].replaceChild(newInputField, inputField); - //this.playlistControls.getElementsByTagName("input")[0].setAttribute("value", track + this.startTrack + 1); - } else { - var title = "Download " + (this.playlist[track].mediaType == "audio" ? "Audio" : "Video"); - title = "" + title + ""; - this.playlistControls.getElementsByTagName("p")[0].innerHTML = title; - } + if(this.usePlaylistControls) { + var title = this.playlist[track].title; + if(!title) title = "(no title)"; + title = "" + title + ""; + this.playlistControls.getElementsByTagName("p")[0].innerHTML = ((track + this.startTrack) % this.playlistLength + 1) + ". " + title; + var inputField = this.playlistControls.getElementsByTagName("input")[0]; + var newInputField = document.createElement("input"); + newInputField.setAttribute("type", "text"); + newInputField.setAttribute("value", (track + this.startTrack) % this.playlistLength + 1); + newInputField.style.width = (8 * this.playlistLength.toString().length) + "px"; + // simply changing the value does not update if user has used the field + this.playlistControls.getElementsByTagName("form")[0].replaceChild(newInputField, inputField); + // this.playlistControls.getElementsByTagName("input")[0].setAttribute("value", track + this.startTrack + 1); + } else { + var title = "Download " + (this.playlist[track].mediaType == "audio" ? "Audio" : "Video"); + title = "" + title + ""; + this.playlistControls.getElementsByTagName("p")[0].innerHTML = title; + } }; mediaPlayer.prototype.setContextInfo = function(event, contextInfo) { - var track = this.currentTrack; - if(track == null) track = 0; - contextInfo.mediaType = this.playlist[track].mediaType; - contextInfo.siteInfo = this.playlist[track].siteInfo; - //if(this.mediaElement) contextInfo.loop = this.mediaElement.hasAttribute("loop"); - // contextInfo.isPlaylist = (this.playlist.length > 1); // not used - safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); - event.stopPropagation(); + var track = this.currentTrack; + if(track == null) track = 0; + contextInfo.mediaType = this.playlist[track].mediaType; + contextInfo.siteInfo = this.playlist[track].siteInfo; + // if(this.mediaElement) contextInfo.loop = this.mediaElement.hasAttribute("loop"); + // contextInfo.isPlaylist = (this.playlist.length > 1); // not used + safari.self.tab.setContextMenuEventUserInfo(event, contextInfo); + event.stopPropagation(); }; mediaPlayer.prototype.addToPlaylist = function(playlist, init) { - if(init) this.playlist = playlist.concat(this.playlist); - else this.playlist = this.playlist.concat(playlist); - if(this.usePlaylistControls && this.playlistControls) { - this.playlistControls.getElementsByTagName("span")[0].innerHTML = "/" + normalize(this.playlist.length + this.startTrack, this.playlistLength); - var width = this.playlistControls.offsetWidth - this.playlistControls.getElementsByClassName("CTFtrackSelect")[0].offsetWidth; - this.playlistControls.getElementsByTagName("p")[0].style.width = (width - 7) + "px"; - } + if(init) this.playlist = playlist.concat(this.playlist); + else this.playlist = this.playlist.concat(playlist); + if(this.usePlaylistControls && this.playlistControls) { + this.playlistControls.getElementsByTagName("span")[0].innerHTML = "/" + normalize(this.playlist.length + this.startTrack, this.playlistLength); + } }; function normalize(n,m) { - if(n > m) return m.toString(); - var string = n.toString(); - while(string.length < m.toString().length) { - string = "0" + string; - } - return string; + if(n > m) return m.toString(); + var string = n.toString(); + while(string.length < m.toString().length) { + string = "0" + string; + } + return string; } diff --git a/ClickToPlugin.safariextension/next.png b/ClickToPlugin.safariextension/next.png index 0cea8b02..72e161b6 100644 Binary files a/ClickToPlugin.safariextension/next.png and b/ClickToPlugin.safariextension/next.png differ diff --git a/ClickToPlugin.safariextension/next_active.png b/ClickToPlugin.safariextension/next_active.png index 3ec0a0ca..4c0fd79d 100644 Binary files a/ClickToPlugin.safariextension/next_active.png and b/ClickToPlugin.safariextension/next_active.png differ diff --git a/ClickToPlugin.safariextension/prev.png b/ClickToPlugin.safariextension/prev.png index a8f8888b..b79a46b6 100644 Binary files a/ClickToPlugin.safariextension/prev.png and b/ClickToPlugin.safariextension/prev.png differ diff --git a/ClickToPlugin.safariextension/prev_active.png b/ClickToPlugin.safariextension/prev_active.png index fcbd8c5b..1b41e105 100644 Binary files a/ClickToPlugin.safariextension/prev_active.png and b/ClickToPlugin.safariextension/prev_active.png differ diff --git a/ClickToPlugin.safariextension/styles.css b/ClickToPlugin.safariextension/styles.css index 63aa3f6b..1a17ac7d 100644 --- a/ClickToPlugin.safariextension/styles.css +++ b/ClickToPlugin.safariextension/styles.css @@ -1,11 +1,11 @@ /* Reset random stuff that might be set by the parent page */ .clickToFlashPlaceholder * { - margin: 0 !important; - padding: 0 !important; - border: 0 !important; - border-radius: 0 !important; - text-align: left !important; - background: none !important; + margin: 0 !important; + padding: 0 !important; + border: 0 !important; + border-radius: 0 !important; + text-align: left !important; + background: none !important; } /********************* @@ -13,11 +13,11 @@ *********************/ .clickToFlashPlaceholder { - background: -webkit-gradient(linear, left top, left bottom, from(rgba(235, 235, 235, 1)), to(rgba(190, 190, 190, 1))) !important; - border: 1px solid rgba(0, 0, 0, 0.5) !important; - -webkit-user-select: none !important; - display: inline-block !important; - -webkit-box-sizing: border-box !important; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(235, 235, 235, 1)), to(rgba(190, 190, 190, 1))) !important; + border: 1px solid rgba(0, 0, 0, 0.5) !important; + -webkit-user-select: none !important; + display: inline-block !important; + -webkit-box-sizing: border-box !important; } .clickToFlashPlaceholder.notable { @@ -29,39 +29,39 @@ } .clickToFlashPlaceholder:active { - background: -webkit-gradient(linear, left bottom, left top, from(rgba(225, 225, 225, 1)), to(rgba(200, 200, 200, 1))) !important; + background: -webkit-gradient(linear, left bottom, left top, from(rgba(225, 225, 225, 1)), to(rgba(200, 200, 200, 1))) !important; } .clickToFlashPlaceholderContainer { - position: relative !important; - width: inherit !important; - height: inherit !important; - margin: -1px !important; + position: relative !important; + width: inherit !important; + height: inherit !important; + margin: -1px !important; overflow: visible !important; } div.logoVerticalPosition { - display: table-cell !important; - vertical-align: middle !important; - width: inherit !important; - height: inherit !important; + display: table-cell !important; + vertical-align: middle !important; + width: inherit !important; + height: inherit !important; } .logoHorizontalPosition { - float: left !important; - position: relative !important; - left: 50% !important; - height: auto !important; - width: auto !important; + float: left !important; + position: relative !important; + left: 50% !important; + height: auto !important; + width: auto !important; overflow: visible !important; } .clickToFlashPlaceholder .logoContainer { - float: left !important; - position: relative !important; - left: -50% !important; - width: auto !important; - height: auto !important; + float: left !important; + position: relative !important; + left: -50% !important; + width: auto !important; + height: auto !important; overflow: visible !important; } @@ -75,40 +75,41 @@ div.logoVerticalPosition { } .clickToFlashPlaceholder .logo { - border-radius: 8px !important; - border: 4px solid rgba(0, 0, 0, 0.3) !important; - font-size: 20px !important; + border-radius: 8px !important; + border: 4px solid rgba(0, 0, 0, 0.3) !important; + font-size: 20px !important; font-style: normal !important; - width: auto !important; + width: auto !important; margin: 0 !important; - color: rgba(0, 0, 0, 0.5) !important; - font-weight: bold !important; - padding: 14px 5px 7px 5px !important; - -webkit-user-select: none !important; - cursor: default !important; - background: none !important; - position: relative !important; - height: auto !important; - line-height: 0 !important; + color: rgba(0, 0, 0, 0.5) !important; + font-weight: bold !important; + padding: 14px 5px 7px 5px !important; + -webkit-user-select: none !important; + white-space: nowrap !important; + cursor: default !important; + background: none !important; + position: relative !important; + height: auto !important; + line-height: 0 !important; font-family: "Helvetica Neue", Helvetica, sans-serif !important; } .clickToFlashPlaceholder .logo.inset { - color: rgba(255, 255, 255, 0.45) !important; - border-color: rgba(255, 255, 255, 0.45) !important; - position: absolute !important; - top: 1px !important; + color: rgba(255, 255, 255, 0.45) !important; + border-color: rgba(255, 255, 255, 0.45) !important; + position: absolute !important; + top: 1px !important; font-family: "Helvetica Neue", Helvetica, sans-serif !important; } .clickToFlashPlaceholder .logoContainer.mini .logo, .clickToFlashPlaceholder .logoContainer.hidden .logo.tmp { color: rgba(50, 50, 50, 0.4) !important; - font-size: 10px !important; + font-size: 10px !important; font-style: normal !important; - border: 2px solid rgba(50, 50, 50, 0.2) !important; - padding: 7px 2px 4px 2px !important; - border-radius: 4px !important; + border: 2px solid rgba(50, 50, 50, 0.2) !important; + padding: 7px 2px 4px 2px !important; + border-radius: 4px !important; } .clickToFlashPlaceholder .logoContainer.hidden .logo.tmp { @@ -116,182 +117,182 @@ div.logoVerticalPosition { } .clickToFlashPlaceholder .logoContainer.mini .logo.inset { - color: rgba(255, 255, 255, 0.25) !important; - border-color: rgba(255, 255, 255, 0.25) !important; + color: rgba(255, 255, 255, 0.25) !important; + border-color: rgba(255, 255, 255, 0.25) !important; } .CTFvideoContainer { - position: relative !important; - display: inline-block !important; - margin: 0 !important; - padding: 0 !important; + position: relative !important; + display: inline-block !important; + margin: 0 !important; + padding: 0 !important; } .CTFvideoElement { - display: block !important; - position: relative !important; - margin: 0px auto 0px auto !important; + display: block !important; + position: relative !important; + margin: 0px auto 0px auto !important; } .CTFQTObject { - position: absolute !important; - width: 0px !important; - height: 0px !important; + position: absolute !important; + width: 0px !important; + height: 0px !important; } /* PLAYLIST CONTROLS */ .CTFplaylistControls { - z-index: 1 !important; - -webkit-user-select: none !important; - display: block !important; - position: relative !important; - bottom: 48px !important; - margin: 0px auto 0px auto !important; - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - padding: 0px !important; - height: 24px !important; - background-image: url('controls_background.png') !important; - background-repeat: repeat-x !important; - /*opacity: 0 !important;*/ - /*-webkit-transition: opacity .5s linear !important;*/ - pointer-events: none !important; - text-rendering: auto !important; + z-index: 1 !important; + -webkit-user-select: none !important; + display: block !important; + position: relative !important; + bottom: 48px !important; + margin: 0px auto 0px auto !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + padding: 0px !important; + height: 24px !important; + background-image: url('controls_background.png') !important; + background-repeat: repeat-x !important; + /*opacity: 0 !important;*/ + /*-webkit-transition: opacity .5s linear !important;*/ + pointer-events: none !important; + text-rendering: auto !important; } .CTFtitleText { - color: white !important; - pointer-events: auto !important; - text-decoration: none !important; - border: 0 !important; + color: white !important; + pointer-events: auto !important; + text-decoration: none !important; + border: 0 !important; } .CTFtitleText:hover { - text-decoration: underline !important; + text-decoration: underline !important; } /*:hover.CTFplaylistControls { - opacity: 1 !important; + opacity: 1 !important; }*/ .CTFplaylistControls div { - height: inherit !important; + height: inherit !important; } .CTFtrackSelect div.CTFprevButton { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - margin: 0 !important; - padding: 0 !important; - width: 35px !important; - background-image: url('prev.png') !important; - background-repeat: no-repeat !important; - background-position: center center !important; - pointer-events: auto !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + margin: 0 !important; + padding: 0 !important; + width: 37px !important; + background-image: url('prev.png') !important; + background-repeat: no-repeat !important; + background-position: center center !important; + pointer-events: auto !important; } .CTFtrackSelect div.CTFprevButton:active { - background-image: url('prev_active.png') !important; + background-image: url('prev_active.png') !important; } .CTFtrackSelect form.CTFtrackInput { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - padding: 0 !important; - margin: 0 !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + padding: 0 !important; + margin: 0 !important; } .CTFtrackSelect div.CTFnextButton { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - margin: 0 0 0 0 !important; - padding: 0 0 0 0 !important; - width: 35px !important; - background-image: url('next.png') !important; - background-repeat: no-repeat !important; - background-position: center center !important; - pointer-events: auto !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + margin: 0 0 0 0 !important; + padding: 0 0 0 0 !important; + width: 37px !important; + background-image: url('next.png') !important; + background-repeat: no-repeat !important; + background-position: center center !important; + pointer-events: auto !important; } .CTFtrackSelect div.CTFnextButton:active { - background-image: url('next_active.png') !important; + background-image: url('next_active.png') !important; } .CTFtrackTitle { - position: absolute !important; - top: 0px !important; - left: 0px !important; - height: 24px !important; + position: absolute !important; + top: 0px !important; + left: 0px !important; + height: 24px !important; } .CTFtrackSelect { - position: absolute !important; - top: 0px !important; - right: 6px !important; - /*margin: 0 2px 0 0 !important;*/ + position: absolute !important; + top: 0px !important; + right: 4px !important; + /*margin: 0 2px 0 0 !important;*/ } .CTFtrackTitle div.CTFtrackTitleText { - display: table-cell !important; - vertical-align: middle !important; - height: inherit !important; - padding: 0 5px 0 7px !important; + display: table-cell !important; + vertical-align: middle !important; + height: inherit !important; + padding: 0 5px 0 7px !important; } .CTFtrackTitle div.CTFtrackTitleText p { - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - margin: 0 !important; - padding: 0 !important; - white-space: nowrap !important; - overflow: hidden !important; - text-overflow: ellipsis !important; - text-align: left !important; - line-height: normal !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + margin: 0 !important; + padding: 0 !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + text-align: left !important; + line-height: normal !important; } .CTFtrackSelect input { - background-color: rgba(255, 255, 255, 0.1) !important; - border: 1px solid rgba(0, 0, 0, 1) !important; - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - margin: 0 !important; - padding: 0 !important; - text-align: right !important; - pointer-events: auto !important; - height: 12px !important; - line-height: 1 !important; + background-color: rgba(255, 255, 255, 0.1) !important; + border: 1px solid rgba(0, 0, 0, 1) !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + margin: 0 !important; + padding: 0 !important; + text-align: right !important; + pointer-events: auto !important; + height: 12px !important; + line-height: 1 !important; } .CTFtrackSelect span { - color: white !important; - font-family: "Lucida Grande", Helvetica, sans-serif !important; - font-style: normal !important; - font-size: 10px !important; - font-weight: normal !important; - line-height: 1 !important; + color: white !important; + font-family: "Lucida Grande", Helvetica, sans-serif !important; + font-style: normal !important; + font-size: 10px !important; + font-weight: normal !important; + line-height: 1 !important; } /*need the specificity*/ div.CTFplaylistControls div.CTFhoverElement { - position: absolute !important; - left: 28px !important; - top: 18px !important; - width: 24px !important; - height: 6px !important; - pointer-events: auto !important; + position: absolute !important; + left: 28px !important; + top: 18px !important; + width: 24px !important; + height: 6px !important; + pointer-events: auto !important; }