Guest User

Apple Trailer Download HD+ Bg Colour Fix

a guest
Jan 18th, 2014
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name            Apple Trailer Download HD+
  3. // @namespace       http://www.digitaledgestudios.nl/
  4. // @author          mhu
  5. // @description     Download movie trailers from Apple. Based on the script by JC2k8
  6. // @include         http://trailers.apple.com/trailers/*/*
  7. // @exclude         http://trailers.apple.com/trailers/*/*/gallery/*
  8. // @version         2.0.8
  9. // @require         http://userscripts.org/scripts/source/87345.user.js
  10. // @require         http://userscripts.org/scripts/source/98574.user.js
  11. // @grant           GM_addStyle
  12. // @grant           GM_xmlhttpRequest
  13. // @grant           GM_registerMenuCommand
  14. // @grant           GM_setClipboard
  15. // @noframes
  16. // ==/UserScript==
  17.  
  18.  
  19. (function() {
  20.     /**
  21.     * This is where we start. Inject styles and start getting the links.
  22.     * Will run automatically after the load event has fired.
  23.     */
  24.     function runScript() {
  25.         var css = '#atdContainer {background-color: rgba(0,0,0, 0.9); border:0px solid white; border-radius:5px; bottom:5px; box-shadow:0 -1px 5px rgba(0, 0, 0, 0.75); color:#CCC; display:table; font-family:Arial,sans-serif; height:35px; max-width:200px; min-width:120px; opacity:.9; padding:0 5px; position:fixed; right:10px; text-align:left; z-index:900;} ' +
  26.             '#atdHeader { background-color: rgba(0,0,0, 1); border-top-left-radius: 5px; border-top-right-radius: 5px; box-shadow:-1px -1px 2px black inset; margin: 0 -5px; padding: 2px 3px 5px; text-align: center; } ' +
  27.             '#atdHeaderLink { color: #FFFFFF; cursor:pointer; font: bold 0.9em/1.1em arial,sans-serif; text-shadow: 1px 0 rgba(0, 0, 0, 0.9); } ' +
  28.             '#atdContainer li {display: list-item; font-size:1em; line-height:1.4em; padding-right: 10px; text-align: right} ' +
  29.             '#atdContainer li > a {color:#CCCCCC; font-weight:normal; font-size:0.9em; text-decoration:none;} ' +
  30.             '#atdContainer li.error {color:#D90000; font-weight:normal; font-size:0.9em; text-decoration:none;} ' +
  31.             '#atd480p, #atd720p, #atd1080p, #atdLoader {box-shadow:-1px -1px 3px #000000 inset;} ' +
  32.             '#atd1080p {padding-bottom:4px; } ' +
  33.             '#atdContainer > div:not(.toggle) {margin:0 -5px; padding:3px 5px;} ' +
  34.             '#atdContainer .toggle { box-shadow: 0px -1px 3px rgba(255, 255, 255, 0.25) inset; color: white; font-size: 1em; font-weight: bold; margin: 0 -5px; padding: 0 5px 0 10px; } ' +
  35.             '#atdContainer .toggle:hover {margin: 0 -6px; box-shadow:0 2px 5px rgba(0, 0, 0, 0.75);} ' +
  36.             '#atdContainer .toggle:last-of-type {margin-bottom:1px;} ' +
  37.             '#atdContainer > div:not(#atdError) {cursor:pointer;} ' +
  38.             '#atdLoader {background:url("data:image/gif;base64,R0lGODlhKwALAPEAAP///wAAAIKCggAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAKwALAAACMoSOCMuW2diD88UKG95W88uF4DaGWFmhZid93pq+pwxnLUnXh8ou+sSz+T64oCAyTBUAACH5BAkKAAAALAAAAAArAAsAAAI9xI4IyyAPYWOxmoTHrHzzmGHe94xkmJifyqFKQ0pwLLgHa82xrekkDrIBZRQab1jyfY7KTtPimixiUsevAAAh+QQJCgAAACwAAAAAKwALAAACPYSOCMswD2FjqZpqW9xv4g8KE7d54XmMpNSgqLoOpgvC60xjNonnyc7p+VKamKw1zDCMR8rp8pksYlKorgAAIfkECQoAAAAsAAAAACsACwAAAkCEjgjLltnYmJS6Bxt+sfq5ZUyoNJ9HHlEqdCfFrqn7DrE2m7Wdj/2y45FkQ13t5itKdshFExC8YCLOEBX6AhQAADsAAAAAAAAAAAA=") center 15px no-repeat;} ' +
  39.             '#atdLoaderText {font-size:0.9em; margin:0; padding:30px 0 5px; text-align:center;} ' +
  40.             '#atdError {background-image:-moz-linear-gradient(center top, rgba(255, 0, 0, 0.2), rgba(255, 0, 0, 0.6)); color:black;} ' +
  41.             '.atdListing {margin-bottom:0}' +
  42.             '.errorMsg {font-weight:bold; font-size:0.8em;}' +
  43.             '.roundBottomCorners {border-bottom-left-radius:5px; border-bottom-right-radius:5px;} ' +
  44.             '.hidden {display:none;}';
  45.         if ($('#trailers > h2')) {
  46.             //css += getTrailerListingColor(); that function doesn't work properly any more, so disabling.
  47.         } else {
  48.             bErr = true;
  49.         }
  50.         addNewStyle(css);
  51.         // add header
  52.         atdcontainer.appendChild(createAtdHeader());
  53.  
  54.         // Show loading animation until fetching necessary data has finished.
  55.         atdcontainer.appendChild(createLoader());
  56.         document.body.insertBefore(atdcontainer, document.body.firstChild);
  57.  
  58.         // let's scan for links (async)
  59.         intervID = setTimeout(function() {
  60.             GM_xmlhttpRequest({
  61.                 "method" : "GET",
  62.                 "synchronous" : false,
  63.                 "url" : window.location.href + "includes/large.html",
  64.                 "onload" : function(xhr)
  65.                 {
  66.                     // analyse the contents
  67.                     if (!xhr.responseText || xhr.responseText.indexOf(APPLEERRORINDICATOR) >= 0) {
  68.                         parseOldTrailerPage(); // try old page type
  69.                     }
  70.                     else {
  71.                         getTrailerPages(xhr.responseText);
  72.                     }
  73.                 },
  74.                 "onerror":function() {
  75.                     showError("Could not download includes/large.html");
  76.                 }
  77.             });
  78.         }, 250);
  79.     }
  80.  
  81.     /**
  82.     * Loads the trailer subpage containing the download links (async)
  83.     */
  84.     function getDownloadlinks(order, url, title, cb) {
  85.         GM_xmlhttpRequest({
  86.             "url" : url,
  87.             "method" : "GET",
  88.             "synchronous" : false,
  89.             "url" : window.location.href + url,
  90.             "onload" : function(xhr)
  91.             {
  92.                 // analyse the contents
  93.                 if (!xhr.responseText || xhr.responseText.indexOf(APPLEERRORINDICATOR) >= 0) {
  94.                     showError("Could not download includes/large.html");
  95.                 }
  96.                 else {
  97.                     insertDownloadLink(xhr.responseText, order, title);
  98.                 }
  99.                 if (cb) cb();
  100.             },
  101.             "onerror" : function() {
  102.                 showError("Could not download trailer subpage");
  103.                 if (cb) cb();
  104.             }
  105.         });
  106.     }
  107.  
  108.     /**
  109.     * Adds the trailer links to the respective arrays
  110.     */
  111.     function insertDownloadLink(page, order, title) {
  112.         var reLink = new RegExp('<a class="movieLink" href="([^\\?"]+).mov', 'img'),
  113.             duplicate, res, url;
  114.  
  115.         // get all the HD links from the trailer page
  116.         reLink.lastIndex = 0;
  117.         if ((result = reLink.exec(page))) {
  118.             if (result.length >= 2) {
  119.                 writeLog("- Trailer link found: " + result[1] + ".mov");
  120.                 url = result[1].replace(/_h?([0-9]+p?)$/, '').replace("http://movietrailers.apple.com", "http://trailers.apple.com");
  121.  
  122.                 // avoid duplicates
  123.                 duplicate = false;
  124.                 for (var k=0; k<arr480.length; k++)
  125.                 {
  126.                     if (arr480[k] && arr480[k].href == url + "_h480p.mov") {
  127.                         duplicate = true;
  128.                         break;
  129.                     }
  130.                 }
  131.  
  132.                 if (!duplicate) {
  133.                     arr480[order] = createElem('a', {'textContent':title, 'href':url + "_h480p.mov", 'title':'Download 480p'});
  134.                     arr720[order] = createElem('a', {'textContent':title, 'href':url + "_h720p.mov", 'title':'Download 720p'});
  135.                     arr1080[order] = createElem('a', {'textContent':title, 'href':url + "_h1080p.mov", 'title':'Download 1080p'});
  136.                 } else {
  137.                     arr480[order] = null;
  138.                     arr720[order] = null;
  139.                     arr1080[order] = null;
  140.                 }
  141.             }
  142.         }
  143.     }
  144.  
  145.     /**
  146.     * Scan the page for HD quicktime movies. Get all <a> tags with specific href's
  147.     */
  148.     function getTrailerPages(page) {
  149.         var src_url, src_title,
  150.         reLink = new RegExp('<a href="includes/([^#"]+)#?.[^"]*"', 'img'),
  151.         reTitle_old = new RegExp('<h4>([^<]+)</h4>', 'img'),
  152.         reTitle_new = new RegExp('<h3 title="[^"]+">([^<]+)</h3>', 'img'),
  153.         result = null,
  154.         links = [],
  155.         titles = [],
  156.         retryCount, si;
  157.  
  158.         // get all the HD links from the trailer page
  159.         reLink.lastIndex = 0;
  160.         while ((result = reLink.exec(page))) {
  161.             if (result.length >= 2 && links.indexOfCI(result[1]) < 0) {
  162.                 writeLog("Trailer page found: " + result[1]);
  163.                 links.push(result[1]);
  164.             }
  165.         }
  166.  
  167.         // get all the titles from the trailer page
  168.         reTitle_old.lastIndex = 0;
  169.         while ((result = reTitle_old.exec(page))) {
  170.             if (result.length >= 2) {
  171.                 writeLog("Trailer title found: " + result[1]);
  172.                 titles.push(result[1]);
  173.             }
  174.         }
  175.  
  176.         if (titles.length == 0) {
  177.             reTitle_new.lastIndex = 0;
  178.             while ((result = reTitle_new.exec(page))) {
  179.                 if (result.length >= 2) {
  180.                     writeLog("Trailer title found: " + result[1]);
  181.                     titles.push(result[1]);
  182.                 }
  183.             }
  184.         }
  185.  
  186.         if (links.length > 0) {
  187.             if (bErr) {
  188.                 // something happened earlier on so we couldn't retrieve data for adaptive coloring - let's try again.
  189.                 // if we still can't retrieve the color value a fallback color will be supplied by the function.
  190.                 addNewStyle(getTrailerListingColor());
  191.             }
  192.  
  193.             requests = links.length;
  194.             for (var j=0; j < links.length; j++) {
  195.                 src_url = "includes/"+links[j];
  196.                 src_title = (links.length == titles.length ? titles[j] : ("Trailer "+(j+1)));
  197.  
  198.                 arr480.push(src_title + " (unavailable)");
  199.                 arr720.push(src_title + " (unavailable)");
  200.                 arr1080.push(src_title + " (unavailable)");
  201.  
  202.                 getDownloadlinks(j, src_url, src_title, function() {requests--;});
  203.             }
  204.  
  205.             retryCount = 0;
  206.             si = setInterval(function() {
  207.                 if (requests === 0) {
  208.                     clearInterval(si);
  209.                     fillAdtContainer();
  210.                     return;
  211.                 }
  212.  
  213.                 retryCount++;
  214.                 if (retryCount > 10) {
  215.                     clearInterval(si);
  216.                     showError("Timeout occurred");
  217.                 }
  218.             }, 500);
  219.         } else {
  220.             showError('');
  221.         }
  222.     }
  223.  
  224.     /**
  225.     * Scan the old style page for HD quicktime movies. Get all <a> tags with specific href's
  226.     * (eg: http://trailers.apple.com/trailers/disney/ponyo/)
  227.     */
  228.     function parseOldTrailerPage() {
  229.         var src_url, src_title, duplicate, links, titles;
  230.  
  231.         try {
  232.             links = Array.filter($('#content').getElementsByClassName('hd'), function(elem) {
  233.                 // we're only interested in links with a href pointing to a .mov
  234.                 if (elem.nodeName !== 'A' || !elem.hasAttribute('href')) {
  235.                     return false;
  236.                 }
  237.                 return (elem.getAttribute('href').endsWith('1080p.mov'));
  238.             });
  239.  
  240.             titles = Array.filter($('.trailer-nav').getElementsByClassName('text'), function(elem) {
  241.                 // we're only interested in spans with class text
  242.                 return (elem.nodeName === 'SPAN');
  243.             });
  244.         }
  245.         catch(err) {
  246.             links = [];
  247.         }
  248.  
  249.         if (links.length > 0) {
  250.             if (bErr) {
  251.                 // something happened earlier on so we couldn't retrieve data for adaptive coloring - let's try again.
  252.                 // if we still can't retrieve the color value a fallback color will be supplied by the function.
  253.                 addNewStyle(getTrailerListingColor());
  254.             }
  255.  
  256.             for (var j=0; j < links.length; j++) {
  257.                 src_url = links[j].getAttribute('href').replace(".mov", "");
  258.                 src_title = (links.length == titles.length ? titles[j].textContent || titles[j].innerText : ("Trailer "+(j+1)));
  259.  
  260.                 writeLog("- Trailer link found: " + src_url + ".mov");
  261.                 src_url = src_url.replace(/_h?([0-9]+p?)$/, '').toLowerCase();
  262.  
  263.                 // avoid duplicates
  264.                 duplicate = false;
  265.                 for (var k=0; k<arr480.length; k++)
  266.                 {
  267.                     if (arr480[k] && arr480[k].href == src_url + "_h480p.mov") {
  268.                         duplicate = true;
  269.                         break;
  270.                     }
  271.                 }
  272.  
  273.                 if (!duplicate) {
  274.                     arr480.push(createElem('a', {'textContent':src_title, 'href':src_url + "_h480p.mov", 'title':'Download 480p'}));
  275.                     arr720.push(createElem('a', {'textContent':src_title, 'href':src_url + "_h720p.mov", 'title':'Download 720p'}));
  276.                     arr1080.push(createElem('a', {'textContent':src_title, 'href':src_url + "_h1080p.mov", 'title':'Download 1080p'}));
  277.                 }
  278.             }
  279.  
  280.             fillAdtContainer();
  281.         } else {
  282.             showError('');
  283.         }
  284.     }
  285.  
  286.     /**
  287.     * Fill the already prepared container with all the trailer listings and
  288.     * the respective toggles.
  289.     */
  290.     function fillAdtContainer() {
  291.         var cont = null,
  292.         ul = null;
  293.         for (var j = 0, arr = null, caption = ''; j < 3; j++) {
  294.             if (j == 0) {
  295.                 arr = arr480;
  296.                 caption = '480p';
  297.             } else if (j == 1) {
  298.                 arr = arr720;
  299.                 caption = '720p';
  300.             } else {
  301.                 arr = arr1080;
  302.                 caption = '1080p';
  303.             }
  304.  
  305.             cont = prepContainer('atd' + caption),
  306.             ul = createElem('ul', {'className':'atdListing'});
  307.             atdcontainer.appendChild(createToggle(caption))
  308.             for (var i = 0, len = arr.length, li = null; i < len; i++) {
  309.                 if (!arr[i]) {
  310.                     continue;
  311.                 }
  312.  
  313.                 if (typeof(arr[i]) == "string") {
  314.                     li = createElem('li', {'textContent':arr[i], 'className':'error'});
  315.                 }
  316.                 else {
  317.                     li = createElem('li');
  318.                     li.appendChild(arr[i]);
  319.                 }
  320.                 ul.appendChild(li);
  321.             }
  322.             cont.appendChild(ul);
  323.             atdcontainer.appendChild(cont);
  324.             if (atdcontainer.style.display != 'table') { atdcontainer.style.display = 'table'; }
  325.         }
  326.         removeNode($('#atdLoader'));
  327.     }
  328.  
  329.     function copyToClipboard(e) {
  330.         if (typeof GM_setClipboard === 'undefined') {
  331.             return;
  332.         }
  333.  
  334.         var s = "",
  335.             count = 0,
  336.             elem = e.target,
  337.             caption = elem.id;
  338.  
  339.         if (caption.endsWith('480p')) {
  340.             arr = arr480;
  341.         } else if (caption.endsWith('720p')) {
  342.             arr = arr720;
  343.         } else {
  344.             arr = arr1080;
  345.         }
  346.  
  347.         for (var i = 0, len = arr.length; i < len; i++) {
  348.             if (typeof(arr[i]) != "string" && arr[i].href) {
  349.                 s += arr[i].href + "\n";
  350.                 count++;
  351.             }
  352.         }
  353.  
  354.         if (s !== "") {
  355.             GM_setClipboard(s);
  356.             writeLog(count + " movie link(s) copied to clipboard");
  357.         }
  358.  
  359.         if (e.stopPropagation) {
  360.             e.stopPropagation();
  361.         }
  362.         if (e.preventDefault) {
  363.             e.preventDefault();
  364.         }
  365.         e.cancelBubble = true;
  366.         e.returnValue = false;
  367.         return false;
  368.     }
  369.  
  370.     function showError(msg) {
  371.         var omg = createElem('div', {'id':'atdError', 'textContent':ERROR_MSG, 'className':'roundBottomCorners'});
  372.         if (msg !== '') {
  373.             omg.appendChild(createElem('br'));
  374.             omg.appendChild(createElem('span', {'textContent':msg, 'className':'errorMsg'}));
  375.         }
  376.         atdcontainer.appendChild(omg);
  377.         removeNode($('#atdLoader'));
  378.     }
  379.  
  380.     // Prototypes ---------------------------------
  381.     /**
  382.     * Determine whether a string starts with a certain string.
  383.     */
  384.     String.prototype.startsWith = function(str) { return (this.indexOf(str) === 0); }
  385.  
  386.     /**
  387.     * Determine whether a string ends with a certain string.
  388.     */
  389.     String.prototype.endsWith = function(str) { return this.indexOf(str, this.length - str.length) !== -1; }
  390.  
  391.     // case-insensitive indexOf function for arrays
  392.     if(typeof Array.prototype.indexOfCI == 'undefined')
  393.     {
  394.         Array.prototype.indexOfCI = function(s) {
  395.             if (s === null || (typeof s == "undefined"))
  396.             {
  397.                 return -1;
  398.             }
  399.             for (var i=0; i < this.length; i++)
  400.             {
  401.                 if (this[i].toLowerCase() == s.toLowerCase())
  402.                 {
  403.                     return i;
  404.                 }
  405.             }
  406.             return -1;
  407.         };
  408.     }
  409.  
  410.     // Helper functions ---------------------------
  411.     /**
  412.     * Creates a new element.
  413.     * @param {String} elem The element to create
  414.     * @param {Object} attrs The new element's attributes
  415.     * @returns {HtmlElement} The created element
  416.     */
  417.     function createElem (elem, attrs) {
  418.         var newElem = document.createElement(elem);
  419.         for (var a in attrs) {
  420.             if (a === 'textContent') {
  421.                 newElem.appendChild(document.createTextNode(attrs[a]));
  422.             } else {
  423.                 newElem[a] = attrs[a];
  424.             }
  425.         }
  426.         return newElem;
  427.     }
  428.  
  429.     /**
  430.     * Create the side panel header. Create a P element and add an A element to
  431.     * quickly access this script's preferences.
  432.     * @returns {HtmlElement}
  433.     */
  434.     function createAtdHeader() {
  435.         var atdHeader = createElem('p', {'id':'atdHeader'});
  436.         var atdHeaderLink = createElem('a', {'id':'atdHeaderLink', 'textContent':'Download Links:'});
  437.         atdHeaderLink.addEventListener('click', function() { devtools.config.open(); }, false);
  438.         atdHeader.appendChild(atdHeaderLink);
  439.         return atdHeader;
  440.     }
  441.  
  442.     /**
  443.     * Create and return the busy animation container. Processing all the
  444.     * links can take a while if there are a lot of trailers.
  445.     * @returns {HtmlElement}
  446.     */
  447.     function createLoader() {
  448.         var ldr = createElem('div', {'id':'atdLoader', 'className':'roundBottomCorners'});
  449.         ldr.appendChild(createElem('p', {'id':'atdLoaderText', 'textContent':'Gathering ...'}));
  450.         return ldr;
  451.     }
  452.  
  453.     /**
  454.     * Create a DIV element which will serve as a toggle as well as
  455.     * an indicator for the three sections (480p, 720p, 1080p).
  456.     * Also adds an event listener (click) to the DIV element to control
  457.     * the toggling.
  458.     */
  459.     function createToggle(caption) {
  460.         var div = createElem('div', {'id':'atdTgl' + caption, 'textContent':caption, 'className':'toggle', 'title':caption + ' trailers'});
  461.         if (caption == '1080p' && devtools.config.get('defaultSize') != '1080p') { addClass(div, 'roundBottomCorners'); }
  462.         div.addEventListener('click', toggleVisibility, false);
  463.         div.addEventListener('contextmenu', copyToClipboard, false);
  464.         return div;
  465.     }
  466.  
  467.     /**
  468.     * Prepare a container (DIV element) for the trailer listings (UL).
  469.     * @param {String} newId The id of the container element
  470.     * @returns {HtmlElement} The created DIV element
  471.     */
  472.     function prepContainer(newId) {
  473.         var elem = createElem('div', {'id':newId});
  474.         if (!newId.endsWith(devtools.config.get('defaultSize'))) { elem.className = 'hidden'; }
  475.         if (newId.endsWith('1080p')) { addClass(elem, 'roundBottomCorners'); }
  476.         return elem;
  477.     }
  478.  
  479.     /**
  480.     * Return the final computed value of an element's CSS property.
  481.     * @param {HtmlElement} elem The element
  482.     * @param {String} prop The property
  483.     * @returns {String} The final computed value
  484.     */
  485.     function getCssProp(elem, prop) { return window.getComputedStyle(elem, null).getPropertyValue(prop); }
  486.  
  487.     /**
  488.     * Return an element matching the specified selector.
  489.     * @param {String} selector The selector
  490.     * @param {Node} root Start looking here
  491.     * @returns {HtmlElement|null} Search result
  492.     */
  493.     function $(selector, root) {
  494.         var e = null;
  495.         root = root || document;
  496.         if (/^#(?!(?:[\w]+)?[ \.,\+\[~>#])/.test(selector)) {
  497.             e = root.getElementById(selector.substring(1));
  498.         } else {
  499.             e = root.querySelector(selector);
  500.         }
  501.         return e;
  502.     }
  503.  
  504.     /**
  505.     * Removes a node from the DOM.
  506.     * @param {HTMLElement} nod The node to remove
  507.     */
  508.     function removeNode(nod) { if (nod) { nod.parentNode.removeChild(nod); } }
  509.  
  510.     /**
  511.     * Add a class to the className attribute.
  512.     * @param {HtmlElement} elem The element to check
  513.     * @param {String} cls The class to add
  514.     */
  515.     function addClass(elem, cls) {
  516.         if (elem.nodeType === 1) {
  517.             if (!elem.className) {
  518.                 elem.className = cls;
  519.             } else {
  520.                 if (!hasClass(elem, cls)) { elem.className += " " + cls; }
  521.             }
  522.         }
  523.     }
  524.  
  525.     /**
  526.     * Remove a class from the className attribute. If it is the last attribute or
  527.     * if cls isn't specified, the class attribute will be removed.
  528.     * @param {HtmlElement} elem The element to change
  529.     * @param {String} cls The class to remove
  530.     */
  531.     function remClass(elem, cls) {
  532.         if (elem.nodeType === 1) {
  533.             if (cls) {
  534.                 if (elem.className === cls) {
  535.                     elem.removeAttribute('class');
  536.                 } else {
  537.                     if (hasClass(elem, cls)) {
  538.                         var cn = ' ' + elem.className + ' ';
  539.                         cn = cn.replace(' ' + cls + ' ','');
  540.                         elem.className = cn.trim();
  541.                     }
  542.                 }
  543.             } else {
  544.                 elem.removeAttribute('class');
  545.             }
  546.         }
  547.     }
  548.  
  549.     /**
  550.     * Determine whether a className attribute has a specific class attached.
  551.     * @param {HtmlElement} elem The element to check
  552.     * @param {String} cls The class to look for
  553.     * @returns {Boolean} Does the element have the class?
  554.     */
  555.     function hasClass(elem, cls) {
  556.         if (!elem.className) return false;
  557.         var cn = ' ' + elem.className + ' ';
  558.         return cn.indexOf(' ' + cls + ' ') > -1;
  559.     }
  560.  
  561.     /**
  562.     * Event listener for showing/hiding the respective trailer listings.
  563.     * In case of the 1080p section, create rounded bottom borders if closed.
  564.     * @param {Event} e The event
  565.     */
  566.     function toggleVisibility(e) {
  567.         var elem = e.target,
  568.         toggleElem = $('#' + elem.id + ' + div');
  569.  
  570.         if (!hasClass(toggleElem, 'hidden')) {
  571.             addClass(toggleElem, 'hidden');
  572.             if (elem.id.endsWith('1080p')) { addClass(elem, 'roundBottomCorners'); }
  573.         } else {
  574.             remClass(toggleElem, 'hidden');
  575.             if (elem.id.endsWith('1080p')) { remClass(elem, 'roundBottomCorners'); }
  576.         }
  577.     }
  578.  
  579.     /**
  580.     * Adds a new CSS ruleset to the page. Uses GM_addStyle API; fallback in place.
  581.     * @param {String} style Contains the CSS rules to add to the page
  582.     */
  583.     function addNewStyle(newStyle) {
  584.         if (typeof GM_addStyle !== 'undefined') {
  585.             GM_addStyle(newStyle);
  586.         } else {
  587.             var heads = document.getElementsByTagName('head');
  588.             if (heads.length > 0) {
  589.                 var node = document.createElement('style');
  590.                 node.type = 'text/css';
  591.                 node.appendChild(document.createTextNode(newStyle));
  592.                 heads[0].appendChild(node);
  593.             }
  594.         }
  595.     }
  596.  
  597.     /**
  598.     * Travel up the DOM until the parent has the specified class and return the node.
  599.     * @param {HtmlElement} elm The starting element
  600.     * @param {String} cls The targeted parent has this class
  601.     * @returns {HtmlElement} The targeted parent element
  602.     */
  603.     function parentUntilClassIs(elm, cls) {
  604.         var p = elm;
  605.         while (p.parentNode) {
  606.             p = p.parentNode;
  607.             if (hasClass(p, cls)) {
  608.                 break;
  609.             }
  610.         }
  611.         return p;
  612.     }
  613.  
  614.     /**
  615.     * Determine whether n is a number or not.
  616.     * @param {String|Number} n The string/number to check
  617.     * @returns {Boolean} n can be interpreted as number
  618.     */
  619.     function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); }
  620.  
  621.     function writeLog(s) {
  622.         if (window.console) {
  623.             console.log(s);
  624.         }
  625.     }
  626.  
  627.     /**
  628.     * Return the color used in Apple's trailer listing. If that's not
  629.     * possible, assume Apple's default blue color scheme.
  630.     * @returns {String} A CSS rule containing the trailer listing color
  631.     */
  632.     function getTrailerListingColor() {
  633.         var heading = $('#trailers > h2'), cssProp;
  634.         if (heading) { cssProp = getCssProp(heading, 'background-color'); }
  635.         if (!cssProp) { cssProp = 'rgb(2, 131, 224)'; }
  636.         return '#atdContainer > div.toggle {background:-moz-linear-gradient(left bottom, ' + cssProp + ' 5%, rgba(0,0,0,.75) 85%) repeat scroll 0 0 transparent;';
  637.     }
  638.  
  639.     // Scriptish users don't need this line because of @noframes
  640.     if (unsafeWindow && unsafeWindow.top !== unsafeWindow.self) { return; }
  641.  
  642.     // CONSTANTS ----------------------------------
  643.     var ERROR_MSG  = 'No downloadable trailers found',
  644.     APPLEERRORINDICATOR = 's.pageType="errorPage"';
  645.  
  646.     // VARIABLES ----------------------------------
  647.     var arr480 = [],
  648.     arr720 = [],
  649.     arr1080 = [],
  650.     sources = [],
  651.     atdcontainer = createElem('div', {'id':'atdContainer'}),
  652.     intervID = 0;
  653.     tryCount = 0;
  654.     bErr = false;
  655.  
  656.     // Config ---------------------------------------------------------
  657.     var sizes = { 'Small (480p)':'480p', 'Normal (720p)':'720p', 'Large (1080p)':'1080p' };
  658.     devtools.config.init({
  659.         title: 'Apple Trailer Download HD+',
  660.         settings: {
  661.             'defaultSize': { type: 'select', label: 'Default trailer size (open panel)', defaultValue: sizes['Large (1080p)'], options: sizes },
  662.         },
  663.         // style the preferences menu
  664.         css: '#devtools-wrapper .dialog { border:thin solid black; border-radius: 5px !important; box-shadow:5px 5px 15px #000000 !important; width: 280px !important; padding:0 !important;} ' +
  665.         '#devtools-wrapper .dialog input[type="checkbox"]{margin-bottom:10px !important;} ' +
  666.         '#devtools-wrapper .dialog .dialog-title { border-radius:5px 5px 0 0 !important;} ' +
  667.         '#devtools-wrapper .dialog .dialog-close {border:0 none !important; border-radius:0 5px 0 0 !important;} ' +
  668.         '#devtools-wrapper .dialog .dialog-footer button {border-radius: 0 0 5px 5px !important;} ' +
  669.         '#devtools-wrapper .dialog input[type="checkbox"] {margin-bottom:5px !important; margin-right:5px !important;} ' +
  670.         '#devtools-wrapper .dialog .dialog-content {border-top: thin solid black !important; padding:15px 10px !important; margin:0 !important;} ' +
  671.         '#devtools-wrapper .dialog .dialog-title span {font-family:Verdana,Arial,sans-serif !important; font-size:13px !important;} ' +
  672.         '#devtools-wrapper .dialog .dialog-footer {padding:5px 0 !important;} ' +
  673.         '#devtools-wrapper .dialog .dialog-footer button {border-radius:5px !important;} ' +
  674.         '#devtools-wrapper.mask {background-color:rgba(0, 0, 0, 0.6) !important;}'
  675.     });
  676.     // register menu command to access preferences
  677.     GM_registerMenuCommand('Apple Trailer Download HD+ Preferences...', function() { devtools.config.open(); });
  678.  
  679.     // wait for the document to be fully loaded
  680.     window.addEventListener("load", function() { runScript(); }, false);
  681. })();
Add Comment
Please, Sign In to add comment