Skip to content

Instantly share code, notes, and snippets.

@EliseWei
Created March 31, 2015 15:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EliseWei/810ce13e309b827a2561 to your computer and use it in GitHub Desktop.
Save EliseWei/810ce13e309b827a2561 to your computer and use it in GitHub Desktop.
Magnetic Poetry js plugin
// Triggered by an image map area with href="#magnetic"
(function() {
// Starting magnet z-index.
var z = 1000;
var d = document;
var b = d.getElementsByTagName('body')[0];
var triggerGrandparents = [];
function initPanel() {
// In case the parsing takes a while, add a wait message.
var waitDiv = d.createElement('div');
waitDiv.id = 'waitMsg';
waitDiv.style.display = 'block';
waitDiv.style.width = '50%';
waitDiv.style.textAlign = 'center';
waitDiv.style.color = '#000000';
waitDiv.style.backgroundColor = '#ffffff';
waitDiv.style.border = 'solid 2px #000000';
waitDiv.style.borderRadius = '10px';
waitDiv.style.padding = '50px 0';
waitDiv.style.position = 'absolute';
waitDiv.style.top = '250px';
waitDiv.style.left = '25%';
waitDiv.innerHTML = 'One moment...';
b.appendChild(waitDiv);
// Add magnet styling.
var styleString = 'div.mag{display:inline-block;width:auto !important;margin:0 !important; ' +
'padding:0 !important; position:relative} div.mag em{display:block;visibility:hidden; ' +
'padding:0 .2em;border:solid 1px #000; font-family: "Times New Roman" !important;}' +
'div.mag span{background-color:#fff;border:solid 1px #999 !important;padding:0 .2em; ' +
'white-space:nowrap; color:#000 !important; display:inline-block;cursor:pointer; ' +
'position:absolute;left:0px;top:0px; font-family: "Times New Roman" !important;}';
insertStyleSheet(styleString);
// Kick off parsing.
parseText(b);
};
// This function steps through the DOM (with some exclusions), looking for text and images
// to turn into poetry elements.
function parseText(p) {
if (p.id != 'waitMsg') {
for (var t = 0, l = triggerGrandparents.length; t < l; t++) {
if (p === triggerGrandparents[t]) {
return;
}
}
// Reset some styles on the element.
p.style.backgroundColor = 'transparent';
p.style.backgroundImage = 'none';
p.style.overflow = 'visible';
p.style.border = '0';
var c = p.childNodes;
var nC = c.length;
for (var i = 0; i < nC; i++) {
if (c[i].nodeType == '3' &&
c[i].textContent.replace(/^[\s]+|[\s]+$|\t+|\r+|\n+|\0+/, '').length > 1) {
// If a text node with content after stripping leading/trailing whitespace...
var nS = c[i].nextSibling;
if (nS) {
var sW = d.createElement('div');
sW.style.display = 'inline';
}
// Split into words on whitespace.
var w = c[i].textContent.split(/\s/);
c[i].textContent = '';
for (var j = 0; j < w.length; j++) {
var pReg = /[\.\,\"\:\;\?\!\@]+/;
var nwReg = /[^A-z0-9\'\-\u2019\u2013]+/g;
if (w[j].search(pReg) != -1 && w[j].match(pReg).length < w[j].length) {
// Special characters in the word itself. Need to split further.
var nW = new Array;
var t = w[j];
var tS = t.search(pReg);
var tML = t.match(pReg)[0].length;
var l = 0;
while (tS >= 0 && tML != t.length) {
if (tS == 0) {nW[l] = t.substr(0, tML);t = t.substr(tML);}
else {nW[l] = t.substr(0, tS).replace(nwReg, '');t = t.substr(tS);}
l++;
tS = t.search(pReg);
if (tS < 0) {nW[l] = t.replace(nwReg, ''); break}
tML = t.match(pReg)[0].length;
if (tML == t.length) {nW[l] = t; break}
}
for (var k = 0; k < nW.length; k++) {
if (nS) {sW.appendChild(magWrap(nW[k]))}
else {p.appendChild(magWrap(nW[k]));}
}
} else {
w[j] = w[j].replace(nwReg, '');
if (w[j].length > 0) {
if (nS) {sW.appendChild(magWrap(w[j]))}
else {p.appendChild(magWrap(w[j]));}
}
}
}
if (nS) {p.replaceChild(sW, c[i]);}
} else if (c[i].nodeType == '1' && c[i].nodeName.toLowerCase() == 'img') {
// Wrap images.
var iStr = '<img ';
var q = 0;
for (q = 0; q < c[i].attributes.length; q++) {
iStr += c[i].attributes[q].name + '="' + c[i].attributes[q].value + '" ';
}
iStr += '/>';
p.replaceChild(magWrap(iStr), c[i]);
} else if (c[i].nodeType == '1' && c[i].nodeName.toLowerCase() == 'map') {
// Clear out image maps.
p.removeChild(c[i]);
} else if (c[i].nodeType == '1' && c[i].nodeName.toLowerCase() != 'script') {
// If an HTML element other than a script, go deeper.
parseText(c[i]);
}
}
}
if (d.getElementById('waitMsg')) {
// Clear out wait message.
d.getElementById('waitMsg').style.display = 'none';
}
};
// Function to generate each magnetic element and identical placeholder element in original flow.
function magWrap(content, tag) {
// Div wrapping element.
var dW = d.createElement('div');
dW.className = 'mag';
// Hidden element to hold the space.
var h = d.createElement('em');
h.style.visibility = 'hidden';
h.innerHTML = content;
// The magnetic element for drag/drop.
var m = d.createElement('span');
m.innerHTML = content;
m.onclick = stopEvents;
m.onmousedown = mClickStartHandler;
m.onmouseup = function(ev) {
if (this.onmousemove) {
this.onmousemove = null;
}
stopEvents(ev);
};
dW.appendChild(h);
dW.appendChild(m);
return dW;
};
function mClickStartHandler(ev) {
ev = ev || window.event;
var mS = this;
mS.style.zIndex = z;
// Every click, increment z, so newly-interacted-with elements stack on top.
z++;
// Center the magnetic span to original click point.
var parentPos = getPos(mS.parentNode);
var xDisp = parentPos.x + .5 * mS.offsetWidth;
var yDisp = parentPos.y + .5 * mS.offsetHeight;
// Bind dragging functionality only on click.
d.onmousemove = function(e) {
posx = eventRealXY(e).x;
posy = eventRealXY(e).y;
mS.style.left = posx - xDisp + 'px';
mS.style.top = posy - yDisp + 'px';
};
// Unbind on mouseup, even if user somehow gets mouse off of element.
d.onmouseup = this.onmouseup = function(ev) {
if (d.onmousemove) d.onmousemove = null;
stopEvents(ev);
};
stopEvents(ev);
};
// Utility functions
function insertStyleSheet(cssString) {
var styleNode = d.createElement('style');
styleNode.type = 'text/css';
// browser detection (based on prototype.js)
if (!!(window.attachEvent && !window.opera)) {
styleNode.styleSheet.cssText = cssString;
}else {
var styleText = d.createTextNode(cssString);
styleNode.appendChild(styleText);
}
d.getElementsByTagName('head')[0].appendChild(styleNode);
};
// Navigate up the DOM to get the element's position relative to the body for CSS purposes.
function getPos(el) {
for (var lx = 0, ly = 0; el != null;) {
lx += el.offsetLeft;
ly += el.offsetTop;
el = el.offsetParent;
}
return {
x: lx,
y: ly
};
};
function stopEvents(ev) {
ev.preventDefault();
if (ev.stopPropagation) {
ev.stopPropagation();
}
// For IE
ev.cancelBubble = true;
ev.returnValue = false;
return false;
};
// Get coordinates of an event, relative to the body (cross-browser).
function eventRealXY(ev) {
var xCoord, yCoord;
if (ev.pageX || ev.pageY) {
xCoord = ev.pageX;
yCoord = ev.pageY;
} else {
xCoord = ev.clientX + d.body.scrollLeft + d.documentElement.scrollLeft;
yCoord = ev.clientY + d.body.scrollTop + d.documentElement.scrollTop;
}
return {
x: xCoord,
y: yCoord
};
};
// Bind initialization to the banner clickzone.
var clickzones = d.getElementsByTagName('area');
for (var i = 0, l = clickzones.length; i < l; i++) {
var href = clickzones[i].getAttribute('href');
if (href === '#magnetic') {
var banner = clickzones[i].parentNode && clickzones[i].parentNode.parentNode;
if (banner) {
triggerGrandparents.push(banner);
}
clickzones[i].onclick = initPanel;
}
}
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment