Advertisement
Guest User

Untitled

a guest
Aug 28th, 2016
74
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.75 KB | None | 0 0
  1. (function() {
  2. var PHI, backgroundPositionYCache, calculateBaselineYRatio, calculateTextHighestY, calculateTypeMetrics, clearCanvas, containerIdAttrName, containsAnyNonInlineElements, containsInvalidElements, countParentContainers, destroy, fontAvailable, getBackgroundColor, getBackgroundColorNode, getFirstAvailableFont, getLinkColor, getUnderlineBackgroundPositionY, hasValidLinkContent, init, initLink, initLinkOnHover, isTransparent, isUnderlined, linkAlwysAttrName, linkBgPosAttrName, linkColorAttrName, linkContainers, linkHoverAttrName, linkLargeAttrName, linkSmallAttrName, performanceTimes, renderStyles, selectionColor, sortContainersForCSSPrecendence, styleNode, time, uniqueLinkContainerID;
  3.  
  4. window.SmartUnderline = {
  5. init: function() {},
  6. destroy: function() {}
  7. };
  8.  
  9. if (!(window['getComputedStyle'] && document.documentElement.getAttribute)) {
  10. return;
  11. }
  12.  
  13. PHI = 1.618034;
  14.  
  15. selectionColor = '#b4d5fe';
  16.  
  17. linkColorAttrName = 'data-smart-underline-link-color';
  18.  
  19. linkSmallAttrName = 'data-smart-underline-link-small';
  20.  
  21. linkLargeAttrName = 'data-smart-underline-link-large';
  22.  
  23. linkAlwysAttrName = 'data-smart-underline-link-always';
  24.  
  25. linkBgPosAttrName = 'data-smart-underline-link-background-position';
  26.  
  27. linkHoverAttrName = 'data-smart-underline-link-hover';
  28.  
  29. containerIdAttrName = 'data-smart-underline-container-id';
  30.  
  31. performanceTimes = [];
  32.  
  33. time = function() {
  34. return +(new Date);
  35. };
  36.  
  37. linkContainers = {};
  38.  
  39. uniqueLinkContainerID = (function() {
  40. var id;
  41. id = 0;
  42. return function() {
  43. return id += 1;
  44. };
  45. })();
  46.  
  47. clearCanvas = function(canvas, context) {
  48. return context.clearRect(0, 0, canvas.width, canvas.height);
  49. };
  50.  
  51. calculateTextHighestY = function(text, canvas, context) {
  52. var alpha, highestY, i, j, pixelData, r, ref, ref1, textWidth, x, y;
  53. clearCanvas(canvas, context);
  54. context.fillStyle = 'red';
  55. textWidth = context.measureText(text).width;
  56. context.fillText(text, 0, 0);
  57. highestY = void 0;
  58. for (x = i = 0, ref = textWidth; 0 <= ref ? i <= ref : i >= ref; x = 0 <= ref ? ++i : --i) {
  59. for (y = j = 0, ref1 = canvas.height; 0 <= ref1 ? j <= ref1 : j >= ref1; y = 0 <= ref1 ? ++j : --j) {
  60. pixelData = context.getImageData(x, y, x + 1, y + 1);
  61. r = pixelData.data[0];
  62. alpha = pixelData.data[3];
  63. if (r === 255 && alpha > 50) {
  64. if (!highestY) {
  65. highestY = y;
  66. }
  67. if (y > highestY) {
  68. highestY = y;
  69. }
  70. }
  71. }
  72. }
  73. clearCanvas(canvas, context);
  74. return highestY;
  75. };
  76.  
  77. calculateTypeMetrics = function(computedStyle) {
  78. var baselineY, canvas, context, descenderHeight, gLowestPixel;
  79. canvas = document.createElement('canvas');
  80. context = canvas.getContext('2d');
  81. canvas.height = canvas.width = 2 * parseInt(computedStyle.fontSize, 10);
  82. context.textBaseline = 'top';
  83. context.textAlign = 'start';
  84. context.fontStretch = 1;
  85. context.angle = 0;
  86. context.font = computedStyle.fontVariant + " " + computedStyle.fontStyle + " " + computedStyle.fontWeight + " " + computedStyle.fontSize + "/" + computedStyle.lineHeight + " " + computedStyle.fontFamily;
  87. baselineY = calculateTextHighestY('I', canvas, context);
  88. gLowestPixel = calculateTextHighestY('g', canvas, context);
  89. descenderHeight = gLowestPixel - baselineY;
  90. return {
  91. baselineY: baselineY,
  92. descenderHeight: descenderHeight
  93. };
  94. };
  95.  
  96. calculateBaselineYRatio = function(node) {
  97. var baselinePositionY, baselineYRatio, height, large, largeRect, small, smallRect, test;
  98. test = document.createElement('div');
  99. test.style.display = 'block';
  100. test.style.position = 'absolute';
  101. test.style.bottom = 0;
  102. test.style.right = 0;
  103. test.style.width = 0;
  104. test.style.height = 0;
  105. test.style.margin = 0;
  106. test.style.padding = 0;
  107. test.style.visibility = 'hidden';
  108. test.style.overflow = 'hidden';
  109. test.style.wordWrap = 'normal';
  110. test.style.whiteSpace = 'nowrap';
  111. small = document.createElement('span');
  112. large = document.createElement('span');
  113. small.style.display = 'inline';
  114. large.style.display = 'inline';
  115. small.style.fontSize = '0px';
  116. large.style.fontSize = '2000px';
  117. small.innerHTML = 'X';
  118. large.innerHTML = 'X';
  119. test.appendChild(small);
  120. test.appendChild(large);
  121. node.appendChild(test);
  122. smallRect = small.getBoundingClientRect();
  123. largeRect = large.getBoundingClientRect();
  124. node.removeChild(test);
  125. baselinePositionY = smallRect.top - largeRect.top;
  126. height = largeRect.height;
  127. return baselineYRatio = Math.abs(baselinePositionY / height);
  128. };
  129.  
  130. backgroundPositionYCache = {};
  131.  
  132. getFirstAvailableFont = function(fontFamily) {
  133. var font, fonts, i, len;
  134. fonts = fontFamily.split(',');
  135. for (i = 0, len = fonts.length; i < len; i++) {
  136. font = fonts[i];
  137. if (fontAvailable(font)) {
  138. return font;
  139. }
  140. }
  141. return false;
  142. };
  143.  
  144. fontAvailable = function(font) {
  145. var baselineSize, canvas, context, newSize, text;
  146. canvas = document.createElement('canvas');
  147. context = canvas.getContext('2d');
  148. text = 'abcdefghijklmnopqrstuvwxyz0123456789';
  149. context.font = '72px monospace';
  150. baselineSize = context.measureText(text).width;
  151. context.font = "72px " + font + ", monospace";
  152. newSize = context.measureText(text).width;
  153. if (newSize === baselineSize) {
  154. return false;
  155. }
  156. return true;
  157. };
  158.  
  159. getUnderlineBackgroundPositionY = function(node) {
  160. var adjustment, backgroundPositionY, backgroundPositionYPercent, baselineY, baselineYRatio, cache, cacheKey, clientRects, computedStyle, descenderHeight, descenderY, firstAvailableFont, fontSizeInt, minimumCloseness, ref, textHeight;
  161. computedStyle = getComputedStyle(node);
  162. firstAvailableFont = getFirstAvailableFont(computedStyle.fontFamily);
  163. if (!firstAvailableFont) {
  164. cacheKey = "" + (Math.random());
  165. } else {
  166. cacheKey = "font:" + firstAvailableFont + "size:" + computedStyle.fontSize + "weight:" + computedStyle.fontWeight;
  167. }
  168. cache = backgroundPositionYCache[cacheKey];
  169. if (cache) {
  170. return cache;
  171. }
  172. ref = calculateTypeMetrics(computedStyle), baselineY = ref.baselineY, descenderHeight = ref.descenderHeight;
  173. clientRects = node.getClientRects();
  174. if (!(clientRects != null ? clientRects.length : void 0)) {
  175. return;
  176. }
  177. adjustment = 1;
  178. textHeight = clientRects[0].height - adjustment;
  179. if (-1 < navigator.userAgent.toLowerCase().indexOf('firefox')) {
  180. adjustment = .98;
  181. baselineYRatio = calculateBaselineYRatio(node);
  182. baselineY = baselineYRatio * textHeight * adjustment;
  183. }
  184. descenderY = baselineY + descenderHeight;
  185. fontSizeInt = parseInt(computedStyle.fontSize, 10);
  186. minimumCloseness = 3;
  187. backgroundPositionY = baselineY + Math.max(minimumCloseness, descenderHeight / PHI);
  188. if (descenderHeight === 4) {
  189. backgroundPositionY = descenderY - 1;
  190. }
  191. if (descenderHeight === 3) {
  192. backgroundPositionY = descenderY;
  193. }
  194. backgroundPositionYPercent = Math.round(100 * backgroundPositionY / textHeight);
  195. if (descenderHeight > 2 && fontSizeInt > 10 && backgroundPositionYPercent <= 100) {
  196. backgroundPositionYCache[cacheKey] = backgroundPositionYPercent;
  197. return backgroundPositionYPercent;
  198. }
  199. };
  200.  
  201. isTransparent = function(color) {
  202. var alpha, rgbaAlphaMatch;
  203. if (color === 'transparent' || color === 'rgba(0, 0, 0, 0)') {
  204. return true;
  205. }
  206. rgbaAlphaMatch = color.match(/^rgba\(.*,(.+)\)/i);
  207. if ((rgbaAlphaMatch != null ? rgbaAlphaMatch.length : void 0) === 2) {
  208. alpha = parseFloat(rgbaAlphaMatch[1]);
  209. if (alpha < .0001) {
  210. return true;
  211. }
  212. }
  213. return false;
  214. };
  215.  
  216. getBackgroundColorNode = function(node) {
  217. var backgroundColor, computedStyle, parentNode, reachedRootNode;
  218. computedStyle = getComputedStyle(node);
  219. backgroundColor = computedStyle.backgroundColor;
  220. parentNode = node.parentNode;
  221. reachedRootNode = !parentNode || parentNode === document.documentElement || parentNode === node;
  222. if (computedStyle.backgroundImage !== 'none') {
  223. return null;
  224. }
  225. if (isTransparent(backgroundColor)) {
  226. if (reachedRootNode) {
  227. return node.parentNode || node;
  228. } else {
  229. return getBackgroundColorNode(parentNode);
  230. }
  231. } else {
  232. return node;
  233. }
  234. };
  235.  
  236. hasValidLinkContent = function(node) {
  237. return containsInvalidElements(node) || containsAnyNonInlineElements(node);
  238. };
  239.  
  240. containsInvalidElements = function(node) {
  241. var child, i, len, ref, ref1, ref2;
  242. ref = node.children;
  243. for (i = 0, len = ref.length; i < len; i++) {
  244. child = ref[i];
  245. if ((ref1 = (ref2 = child.tagName) != null ? ref2.toLowerCase() : void 0) === 'img' || ref1 === 'video' || ref1 === 'canvas' || ref1 === 'embed' || ref1 === 'object' || ref1 === 'iframe') {
  246. return true;
  247. }
  248. return containsInvalidElements(child);
  249. }
  250. return false;
  251. };
  252.  
  253. containsAnyNonInlineElements = function(node) {
  254. var child, i, len, ref, style;
  255. ref = node.children;
  256. for (i = 0, len = ref.length; i < len; i++) {
  257. child = ref[i];
  258. style = getComputedStyle(child);
  259. if (style.display !== 'inline') {
  260. return true;
  261. }
  262. return containsAnyNonInlineElements(child);
  263. }
  264. return false;
  265. };
  266.  
  267. getBackgroundColor = function(node) {
  268. var backgroundColor;
  269. backgroundColor = getComputedStyle(node).backgroundColor;
  270. if (node === document.documentElement && isTransparent(backgroundColor)) {
  271. return 'rgb(255, 255, 255)';
  272. } else {
  273. return backgroundColor;
  274. }
  275. };
  276.  
  277. getLinkColor = function(node) {
  278. return getComputedStyle(node).color;
  279. };
  280.  
  281. styleNode = document.createElement('style');
  282.  
  283. countParentContainers = function(node, count) {
  284. var parentNode, reachedRootNode;
  285. if (count == null) {
  286. count = 0;
  287. }
  288. parentNode = node.parentNode;
  289. reachedRootNode = !parentNode || parentNode === document || parentNode === node;
  290. if (reachedRootNode) {
  291. return count;
  292. } else {
  293. if (parentNode.hasAttribute(containerIdAttrName)) {
  294. count += 1;
  295. }
  296. return count + countParentContainers(parentNode);
  297. }
  298. };
  299.  
  300. sortContainersForCSSPrecendence = function(containers) {
  301. var container, id, sorted;
  302. sorted = [];
  303. for (id in containers) {
  304. container = containers[id];
  305. container.depth = countParentContainers(container.container);
  306. sorted.push(container);
  307. }
  308. sorted.sort(function(a, b) {
  309. if (a.depth < b.depth) {
  310. return -1;
  311. }
  312. if (a.depth > b.depth) {
  313. return 1;
  314. }
  315. return 0;
  316. });
  317. return sorted;
  318. };
  319.  
  320. isUnderlined = function(style) {
  321. var i, len, property, ref, ref1;
  322. ref = ['textDecorationLine', 'textDecoration'];
  323. for (i = 0, len = ref.length; i < len; i++) {
  324. property = ref[i];
  325. if ((ref1 = style[property]) != null ? ref1.match(/\bunderline\b/) : void 0) {
  326. return true;
  327. }
  328. }
  329. return false;
  330. };
  331.  
  332. initLink = function(link) {
  333. var backgroundPositionY, container, fontSize, id, style;
  334. style = getComputedStyle(link);
  335. fontSize = parseFloat(style.fontSize);
  336. if (isUnderlined(style) && style.display === 'inline' && fontSize >= 10 && !hasValidLinkContent(link)) {
  337. container = getBackgroundColorNode(link);
  338. if (container) {
  339. backgroundPositionY = getUnderlineBackgroundPositionY(link);
  340. if (backgroundPositionY) {
  341. link.setAttribute(linkColorAttrName, getLinkColor(link));
  342. link.setAttribute(linkBgPosAttrName, backgroundPositionY);
  343. id = container.getAttribute(containerIdAttrName);
  344. if (id) {
  345. linkContainers[id].links.push(link);
  346. } else {
  347. id = uniqueLinkContainerID();
  348. container.setAttribute(containerIdAttrName, id);
  349. linkContainers[id] = {
  350. id: id,
  351. container: container,
  352. links: [link]
  353. };
  354. }
  355. return true;
  356. }
  357. }
  358. }
  359. return false;
  360. };
  361.  
  362. renderStyles = function() {
  363. var backgroundColor, backgroundPositionY, color, container, containersWithPrecedence, i, j, len, len1, link, linkBackgroundPositionYs, linkColors, linkSelector, ref, styles;
  364. styles = '';
  365. containersWithPrecedence = sortContainersForCSSPrecendence(linkContainers);
  366. linkBackgroundPositionYs = {};
  367. for (i = 0, len = containersWithPrecedence.length; i < len; i++) {
  368. container = containersWithPrecedence[i];
  369. linkColors = {};
  370. ref = container.links;
  371. for (j = 0, len1 = ref.length; j < len1; j++) {
  372. link = ref[j];
  373. linkColors[getLinkColor(link)] = true;
  374. linkBackgroundPositionYs[getUnderlineBackgroundPositionY(link)] = true;
  375. }
  376. backgroundColor = getBackgroundColor(container.container);
  377. for (color in linkColors) {
  378. linkSelector = function(modifier) {
  379. if (modifier == null) {
  380. modifier = '';
  381. }
  382. return "[" + containerIdAttrName + "=\"" + container.id + "\"] a[" + linkColorAttrName + "=\"" + color + "\"][" + linkAlwysAttrName + "]" + modifier + ",\n[" + containerIdAttrName + "=\"" + container.id + "\"] a[" + linkColorAttrName + "=\"" + color + "\"][" + linkHoverAttrName + "]" + modifier + ":hover";
  383. };
  384. styles += (linkSelector()) + ", " + (linkSelector(':visited')) + " {\n color: " + color + ";\n text-decoration: none !important;\n text-shadow: 0.03em 0 " + backgroundColor + ", -0.03em 0 " + backgroundColor + ", 0 0.03em " + backgroundColor + ", 0 -0.03em " + backgroundColor + ", 0.06em 0 " + backgroundColor + ", -0.06em 0 " + backgroundColor + ", 0.09em 0 " + backgroundColor + ", -0.09em 0 " + backgroundColor + ", 0.12em 0 " + backgroundColor + ", -0.12em 0 " + backgroundColor + ", 0.15em 0 " + backgroundColor + ", -0.15em 0 " + backgroundColor + ";\n background-color: transparent;\n background-image: -webkit-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -webkit-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -webkit-linear-gradient(" + color + ", " + color + ");\n background-image: -moz-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -moz-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -moz-linear-gradient(" + color + ", " + color + ");\n background-image: -o-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -o-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -o-linear-gradient(" + color + ", " + color + ");\n background-image: -ms-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -ms-linear-gradient(" + backgroundColor + ", " + backgroundColor + "), -ms-linear-gradient(" + color + ", " + color + ");\n background-image: linear-gradient(" + backgroundColor + ", " + backgroundColor + "), linear-gradient(" + backgroundColor + ", " + backgroundColor + "), linear-gradient(" + color + ", " + color + ");\n -webkit-background-size: 0.05em 1px, 0.05em 1px, 1px 1px;\n -moz-background-size: 0.05em 1px, 0.05em 1px, 1px 1px;\n background-size: 0.05em 1px, 0.05em 1px, 1px 1px;\n background-repeat: no-repeat, no-repeat, repeat-x;\n}\n\n" + (linkSelector('::selection')) + " {\n text-shadow: 0.03em 0 " + selectionColor + ", -0.03em 0 " + selectionColor + ", 0 0.03em " + selectionColor + ", 0 -0.03em " + selectionColor + ", 0.06em 0 " + selectionColor + ", -0.06em 0 " + selectionColor + ", 0.09em 0 " + selectionColor + ", -0.09em 0 " + selectionColor + ", 0.12em 0 " + selectionColor + ", -0.12em 0 " + selectionColor + ", 0.15em 0 " + selectionColor + ", -0.15em 0 " + selectionColor + ";\n background: " + selectionColor + ";\n}\n\n" + (linkSelector('::-moz-selection')) + " {\n text-shadow: 0.03em 0 " + selectionColor + ", -0.03em 0 " + selectionColor + ", 0 0.03em " + selectionColor + ", 0 -0.03em " + selectionColor + ", 0.06em 0 " + selectionColor + ", -0.06em 0 " + selectionColor + ", 0.09em 0 " + selectionColor + ", -0.09em 0 " + selectionColor + ", 0.12em 0 " + selectionColor + ", -0.12em 0 " + selectionColor + ", 0.15em 0 " + selectionColor + ", -0.15em 0 " + selectionColor + ";\n background: " + selectionColor + ";\n}";
  385. }
  386. }
  387. for (backgroundPositionY in linkBackgroundPositionYs) {
  388. styles += "a[" + linkBgPosAttrName + "=\"" + backgroundPositionY + "\"] {\n background-position: 0% " + backgroundPositionY + "%, 100% " + backgroundPositionY + "%, 0% " + backgroundPositionY + "%;\n}";
  389. }
  390. return styleNode.innerHTML = styles;
  391. };
  392.  
  393. initLinkOnHover = function() {
  394. var alreadyMadeSmart, link, madeSmart;
  395. link = this;
  396. alreadyMadeSmart = link.hasAttribute(linkHoverAttrName);
  397. if (!alreadyMadeSmart) {
  398. madeSmart = initLink(link);
  399. if (madeSmart) {
  400. link.setAttribute(linkHoverAttrName, '');
  401. return renderStyles();
  402. }
  403. }
  404. };
  405.  
  406. init = function(options) {
  407. var i, len, link, links, madeSmart, startTime;
  408. startTime = time();
  409. links = document.querySelectorAll((options.location ? options.location + ' ' : '') + "a");
  410. if (!links.length) {
  411. return;
  412. }
  413. linkContainers = {};
  414. for (i = 0, len = links.length; i < len; i++) {
  415. link = links[i];
  416. madeSmart = initLink(link);
  417. if (madeSmart) {
  418. link.setAttribute(linkAlwysAttrName, '');
  419. } else {
  420. link.removeEventListener('mouseover', initLinkOnHover);
  421. link.addEventListener('mouseover', initLinkOnHover);
  422. }
  423. }
  424. renderStyles();
  425. document.body.appendChild(styleNode);
  426. return performanceTimes.push(time() - startTime);
  427. };
  428.  
  429. destroy = function() {
  430. var attribute, i, len, ref, ref1, results;
  431. if ((ref = styleNode.parentNode) != null) {
  432. ref.removeChild(styleNode);
  433. }
  434. Array.prototype.forEach.call(document.querySelectorAll("[" + linkHoverAttrName + "]"), function(node) {
  435. return node.removeEventListener(initLinkOnHover);
  436. });
  437. ref1 = [linkColorAttrName, linkSmallAttrName, linkLargeAttrName, linkAlwysAttrName, linkHoverAttrName, containerIdAttrName];
  438. results = [];
  439. for (i = 0, len = ref1.length; i < len; i++) {
  440. attribute = ref1[i];
  441. results.push(Array.prototype.forEach.call(document.querySelectorAll("[" + attribute + "]"), function(node) {
  442. return node.removeAttribute(attribute);
  443. }));
  444. }
  445. return results;
  446. };
  447.  
  448. window.SmartUnderline = {
  449. init: function(options) {
  450. if (options == null) {
  451. options = {};
  452. }
  453. if (document.readyState === 'loading') {
  454. window.addEventListener('DOMContentLoaded', function() {
  455. return init(options);
  456. });
  457. return window.addEventListener('load', function() {
  458. destroy();
  459. return init(options);
  460. });
  461. } else {
  462. destroy();
  463. return init(options);
  464. }
  465. },
  466. destroy: function() {
  467. return destroy();
  468. },
  469. performanceTimes: function() {
  470. return performanceTimes;
  471. }
  472. };
  473.  
  474. }).call(this);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement