// pinmarketl-special.js // // Special handling for pinmarklet. // // The pinmarklet was // (a) kludgy. It defined an anonymous script, and a page would need to // re-request the JS every time it needed to popup the pinterest // interaction form. // (b) broken. There were several bugs, including a race condition // in the code that tries to determine the natural size of an image. // Because of that bug, the pinmarklet form would not show, sometimes. // Lame! // // To fix these things, I modified the code and use it instead. This // modified code works for img tags. It does not work with these tags: // video embed object. // // ======================================================== // Usage: // // in the html, include the script just once: // // // also in html, include a pin button: //
// //
// // attach a click event handler to the button, and use this as the handler logic: // // function pinClick(ev, ix) { // PinterestSpecial.popupPinItForm(); // return false; // } // // ======================================================== // // Dino Chiesa // // This code is in the Public Domain. // // Thu, 22 Mar 2012 22:06 // (function(){ var pinmarkletConfig = { idroot: "PIN_" + (new Date()).getTime(), checkpoint: { url: "//api.pinterest.com/v2/domains/info/" }, pin: "//pinterest.com/pin/create/bookmarklet/", minImgSize: 80, thumbCellSize: 200, check: ["meta", "img"], //check: ["meta", "iframe", "embed", "object", "img", "video"], urlRegexi: { vimeo: /^https?:\/\/.*?\.?vimeo\.com\//, facebook: /^https?:\/\/.*?\.?facebook\.com\//, googleReader: /^https?:\/\/.*?\.?google\.com\/reader\//, pinterest: /^https?:\/\/.*?\.?pinterest\.com\//, stumbleUpon: /^https?:\/\/.*?\.?stumbleupon\.com\// }, stumbleFrame: ["tb-stumble-frame", "stumbleFrame"], tag: { video: { youtube: { att: "src", match: [/videoplayback/] } }, embed: { youtube: { att: "src", match: [/^http:\/\/s\.ytimg\.com\/yt/, /^http:\/\/.*?\.?youtube-nocookie\.com\/v/] } }, iframe: { youtube: { att: "src", match: [/^http:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9\-_]+)/] }, vimeo: { att: "src", match: [/^http?s:\/\/vimeo.com\/(\d+)/, /^http:\/\/player\.vimeo\.com\/video\/(\d+)/] } }, object: { youtube: { att: "data", match: [/^http:\/\/.*?\.?youtube-nocookie\.com\/v/] } } }, msg: { check: "", cancelTitle: "Cancel Pin", bustFrame: "We need to remove the StumbleUpon toolbar before you can pin anything. Click OK to do this or Cancel to stay here.", noPin: "This site doesn't allow pinning to Pinterest. Please contact the owner with any questions. Thanks for visiting!", privateDomain: "Sorry, can't pin directly from %privateDomain%.", notFound: "Sorry, couldn't find any large images or video on this page.", installed: "The bookmarklet is installed! Now you can click your Pin It button to pin images as you browse sites around the web." }, pop: "status=no,resizable=no,scrollbars=yes,personalbar=no,directories=no,location=no,toolbar=no,menubar=no,width=632,height=270,left=0,top=0", rules: ["#_bg {position:fixed;z-index:8675309; top:0; right:0; bottom:0; left:0; background-color:#f2f2f2; opacity:.95; }", "#_shim {position:fixed; background: transparent; z-index:8675308; top:0; right:0; bottom:0; left:0; }", "#_bd {position: absolute; text-align: left; padding-top: 36px; top: 0; left: 0; right: 0; z-index:8675320; font:16px hevetica neue,arial,san-serif; }", "#_bd span { zoom:1; display: inline-block; background: #fff; height:200px; width:200px; border: 1px solid #ddd; border-top: none; border-left:none; text-decoration: none; text-shadow: 0 1px #fff; position: relative; cursor: pointer; vertical-align:middle; }", "#_bd span#_logo {background: #FCF9F9 url(http://d3io1k5o0zdpqr.cloudfront.net/images/about/LogoAbout.png) 50% 50% no-repeat; box-shadow: none; }", '#_bd a#_x {height: 36px; line-height: 36px; position: fixed; font-size: 14px; font-weight: bold; display: block; width:auto; top: 0; left: 0; right: 0; margin: 0; background: url("http://d3io1k5o0zdpqr.cloudfront.net/images/fullGradient07Normal.png") repeat-x scroll 0 0 #FFFFFF; border-bottom: 1px solid #CCCCCC; color: #211922; text-align: center; z-index:8675321; }', '#_bd a#_x:active {background-color: #211922; background-image: url("http://d3io1k5o0zdpqr.cloudfront.net/images/fullGradient07Inverted.png"); border-color: #211922; text-shadow: 0 -1px #211922; }', "#_bd a#_x:hover {color: #fff; text-decoration: none; background-color: #1389e5; border-color: #1389e5; text-shadow: 0 -1px #46A0E6;}", "#_bd a img {max-height:200px; max-width:200px; top: 50%; left: 50%; position: absolute; z-index:8675312; }", "#_bd a b { z-index: 8675315; position: absolute; top: 50%; left: 50%; height: 50px; width: 50px; background: transparent url(http://d3io1k5o0zdpqr.cloudfront.net/images/VideoIndicator.png) 0 0 no-repeat; margin-top: -25px; margin-left: -25px; }", "#_bd a cite {z-index: 8675316; position: absolute; font-size: 10px; font-style: normal; bottom: 5px; width: 100px; left: 50%; margin-left: -50px; text-align: center; color: #000; background: #fff; padding: 3px;}", "#_bd span._pinContainer {z-index: 8675320; height: 200px; width: 200px; background: #fff; }", "#_bd span._pinButton {z-index: 8675320; height: 200px; width: 200px; background: transparent; }", "#_bd span._pinButton:hover {height: 200px; width: 200px; background: transparent url(http://d3io1k5o0zdpqr.cloudfront.net/images/PinThis.png) 50% 50% no-repeat; }"] }; function pinmarkletPrep(win, doc, nav, config) { var scriptTagIndex = 0, a = win[config.idroot] = { win: win, doc: doc, nav: nav, config: config, structure: {}, fns: { callback: [], removeNode: function(nodeId) { var node = a.doc.getElementById(nodeId); node && node.parentNode && node.parentNode.removeChild(node); }, get: function(node, attrName) { var a = node[attrName]; a = a || node.getAttribute(attrName); return a; }, make: function(b) { var c = false, e, d; for (e in b) { if (b[e].hasOwnProperty) { c = a.doc.createElement(e); for (d in b[e]) { if (b[e][d].hasOwnProperty) if (typeof b[e][d] === "string") c[d] = b[e][d]; } break; } } return c; }, listen: function(b, c, e) { if (typeof a.win.addEventListener !== "undefined") b.addEventListener(c, e, false); else typeof a.win.attachEvent !== "undefined" && b.attachEvent("on" + c, e) }, getSelection: function() { return ("" + (a.win.getSelection ? a.win.getSelection() : a.doc.getSelection ? a.doc.getSelection() : a.doc.selection.createRange().text)).replace(/(^\s+|\s+$)/g, "") }, pin: function(b) { var c = b.getElementsByTagName("IMG")[0], e = "false", d = a.config.pin + "?", f = (new Date).getTime(); if (b.rel === "video") e = "true"; d = d + "media=" + encodeURIComponent(c.src); d = d + "&url=" + encodeURIComponent(c.getAttribute("url") || a.doc.URL); d = d + "&title=" + encodeURIComponent(a.doc.title); d = d + "&is_video=" + e; d = d + "&description=" + encodeURIComponent(a.values.selectedText || c.title || c.alt); a.values.hazIOS && a.win.setTimeout(function() { a.win.location = "pinit12:" + d }, 25); a.win.open(d, "pin" + f, a.config.pop) }, close: function(b) { if (a.structure.bg) { a.doc.body.removeChild(a.structure.shim); a.doc.body.removeChild(a.structure.bg); a.doc.body.removeChild(a.structure.bd) } win.hazPinningNow = false; b && a.win.alert(b); a.values.hazGoodUrl = false; a.win.scroll(0, a.values.saveScrollTop) }, click: function(b) { b = b || a.win.event; var c = null; if (c = b.target ? b.target.nodeType === 3 ? b.target.parentNode : b.target : b.srcElement) if (c === a.structure.x) a.fns.close(); else if (c.parentNode.className === a.config.idroot + "_pinContainer" || c.className === a.config.idroot + "_pinButton") { a.fns.pin(c.parentNode.getElementsByTagName("A")[0]); a.win.setTimeout(function() { a.fns.close() }, 10) } }, behavior: function() { a.fns.listen(a.structure.bd, "click", a.fns.click) }, presentation: function() { var b = a.fns.make({ STYLE: { type: "text/css" } }), c = a.config.rules.join("\n").replace(/#_/g, "#" + config.idroot + "_").replace(/\._/g, "." + config.idroot + "_"); if (b.styleSheet) b.styleSheet.cssText = c; else b.appendChild(a.doc.createTextNode(c)); a.doc.head.appendChild(b) }, thumb: function(b, c, e, d, f, g) { if (a.values.hazSrc[b] !== true) a.values.hazSrc[b] = true; else if (!a.values.hazIE) return; a.values.hazAtLeastOneGoodThumb = true; d || (d = "image"); var h = a.fns.make({ SPAN: { className: a.config.idroot + "_pinContainer" } }), j = a.fns.make({ A: { rel: d } }), i = new Image, m, n; i.setAttribute("nopin", "nopin"); i.style.visibility = "hidden"; if (f) i.title = f; g && i.setAttribute("url", g); i.onload = function() { m = this.width; n = this.height; this.style.marginTop = n < a.config.thumbCellSize ? -n / 2 + "px" : "-" + a.config.thumbCellSize / 2 + "px"; this.style.marginLeft = m < a.config.thumbCellSize ? -m / 2 + "px" : "-" + a.config.thumbCellSize / 2 + "px"; this.style.visibility = "" }; i.src = b; j.appendChild(i); if (d !== "image") { b = a.fns.make({ B: {} }); j.appendChild(b); } c = a.fns.make({ CITE: { innerHTML: c + " x " + e } }); j.appendChild(c); h.appendChild(j); h.appendChild(a.fns.make({ SPAN: { className: a.config.idroot + "_pinButton" } })); if (d !== "image")(d = a.structure.bd.getElementsByTagName("SPAN")[1]) ? d.parentNode.insertBefore(h, d) : a.structure.bd.appendChild(h); else a.structure.bd.appendChild(h) }, invokeJsonp: function(urlStub, callback) { var scriptId = 'pint-jsonp-script-' + (new Date()).valueOf() + '-' + scriptTagIndex++, cbName = "window['" + a.config.idroot + "'].fns.callback[" + a.fns.callback.length + "]", scriptElt = a.doc.createElement("SCRIPT"); a.fns.callback.push(function(f) { callback(f); a.fns.removeNode(id); }); scriptElt.id = scriptId; scriptElt.src = urlStub + cbName; scriptElt.type = "text/javascript"; scriptElt.charset = "utf-8"; a.values.firstScript.parentNode.insertBefore(scriptElt, a.values.firstScript); }, ping: { vimeo: function(b) { var c; if (b[0] && b[0].thumbnail_large && b[0].embed_privacy === "anywhere") { c = ""; if (b[0].title) c += b[0].title; if (b[0].user_name) c = c + ". Video by " + b[0].user_name; if (b[0].user_description) c = c + ". " + b[0].user_description; c += "."; a.fns.thumb(b[0].thumbnail_large, 150, 200, "video", c, b[0].url) } } }, hazSite: { youtube: { iframe: function(b) { b = b.src.split("?")[0].split("&")[0].split("/"); a.fns.thumb("http://img.youtube.com/vi/" + b.pop() + "/0.jpg", 360, 480, "video") }, video: function(b) { b.getAttribute("data-youtube-id") && a.fns.thumb("http://img.youtube.com/vi/" + b.getAttribute("data-youtube-id") + "/0.jpg", 360, 480, "video") }, embed: function(b) { var c = b.getAttribute("flashvars"), e = ""; (e = c ? c.split("video_id=")[1].split("&")[0] : b.src.split("?")[0].split("&")[0].split("/").pop()) && a.fns.thumb("http://img.youtube.com/vi/" + e + "/0.jpg", 360, 480, "video") }, object: function(b) { b = b.getAttribute("data"); var c = ""; if (b) c = b.split("?")[0].split("&")[0].split("/").pop(); c && a.fns.thumb("http://img.youtube.com/vi/" + c + "/0.jpg", 360, 480, "video") } }, vimeo: { iframe: function(b) { a.fns.invokeJsonp("http://vimeo.com/api/v2/video/" + b.src.split("/").pop() + ".json?callback=", a.fns.ping.vimeo) } } }, insureNoPinterestBlock:function(b) { if (b.name && b.name.toUpperCase() === "PINTEREST") { if (b.content && b.content.toUpperCase() === "NOPIN") { //a.fns.close(a.config.msg.noPin); a.values.noPin = true; } } }, validateNaturalImgSize: function(b, callback) { var c; if (!b.src.match(/^data/)) { // get the natural dimensions of the image c = new Image(); c.onload = function() { if (c.height > a.config.minImgSize && c.width > a.config.minImgSize) { a.fns.thumb(c.src, c.height, c.width); } callback(); }; c.src = b.src; } else { callback(); // done } }, checkTags: function(callback) { var b, c, e, d, f, g, h, j, i, imgChecksComplete = 0, imgChecksRequested = 0, oneImageCheckComplete = function() { imgChecksComplete++; if (imgChecksComplete == imgChecksRequested) { callback(); } }; a.values.tag = []; b = 0; for (c = a.config.check.length; b < c; b += 1) { f = a.doc.getElementsByTagName(a.config.check[b]); e = 0; for (d = f.length; e < d; e += 1) { g = f[e]; if (!g.getAttribute("nopin") && g.style.display !== "none" && g.style.visibility !== "hidden") { a.values.tag.push(g); } } } // pass 1: count the img checks, and do the meta checks b = 0; for (c = a.values.tag.length; b < c; b += 1) { f = a.values.tag[b]; // an element g = f.tagName.toLowerCase(); if (g == 'img') { imgChecksRequested++; } else if (g == "meta") { a.fns.insureNoPinterestBlock(f); } } // pass 2: do the img checks for (b = 0; b < c; b += 1) { f = a.values.tag[b]; // an element g = f.tagName.toLowerCase(); if (g == 'img') { a.fns.validateNaturalImgSize(f, oneImageCheckComplete); } } // edge case: nothing to check, so exhibit synchronous behavior if (imgChecksRequested === 0) { callback(); } }, structure: function() { a.structure.shim = a.fns.make({ IFRAME: { height: "100%", width: "100%", allowTransparency: true, id: a.config.idroot + "_shim" } }); a.structure.shim.setAttribute("nopin", "nopin"); a.doc.body.appendChild(a.structure.shim); a.structure.bg = a.fns.make({ DIV: { id: a.config.idroot + "_bg" } }); a.doc.body.appendChild(a.structure.bg); a.structure.bd = a.fns.make({ DIV: { id: a.config.idroot + "_bd" } }); a.structure.x = a.fns.make({ A: { id: a.config.idroot + "_x", innerHTML: a.config.msg.cancelTitle } }); a.structure.bd.appendChild(a.structure.x); a.structure.bd.appendChild(a.fns.make({ SPAN: { id: a.config.idroot + "_logo" } })); a.doc.body.appendChild(a.structure.bd); a.win.scroll(0, 0); }, checkPage: function(callback) { a.fns.checkTags(callback); }, init: function() { a.doc.body = a.doc.getElementsByTagName("BODY")[0]; a.doc.head = a.doc.getElementsByTagName("HEAD")[0]; if (!(!a.doc.body || !a.doc.head || win.hazPinningNow === true)) { var b, c = a.nav.userAgent; a.values = { saveScrollTop: a.win.pageYOffset, hazAtLeastOneGoodThumb: false, hazSrc: {}, hazCalledForThumb: {}, hazIE: function() { return /msie/i.test(c) && !/opera/i.test(c); }(), hazIOS: function() { return c.match(/iP/) !== null; }(), firstScript: a.doc.getElementsByTagName("SCRIPT")[0], selectedText: a.fns.getSelection() }; b = a.config.checkpoint.url + "?url=" + encodeURIComponent(a.doc.URL) + "&callback="; a.fns.invokeJsonp(b, function(b) { if (b && b.ok === false) { a.fns.close(a.config.msg.noPin); return; } a.fns.structure(); a.fns.presentation(); a.fns.checkPage(function() { if (a.values.noPin) { a.fns.close(a.config.msg.noPin); return; } if ( ! a.values.hazAtLeastOneGoodThumb || a.values.tag.length === 0) { a.fns.close(a.config.msg.notFound); return; } a.fns.behavior(); win.hazPinningNow = true; }); }); } } } }; // export one function. It pops up the pinterest form. win.PinterestSpecial = {popupPinItForm: a.fns.init}; } // this is a one-time thing pinmarkletPrep(window, document, navigator, pinmarkletConfig); }());