// 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);
}());