retreaverjs-rails 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1251,3 +1251,638 @@
1251
1251
  ;(function (context) {
1252
1252
  context.Callpixels = window.Retreaver;
1253
1253
  })(window);
1254
+ ;/**
1255
+ * findAndReplaceDOMText v 0.4.3
1256
+ * @author James Padolsey http://james.padolsey.com
1257
+ * @license http://unlicense.org/UNLICENSE
1258
+ *
1259
+ * Matches the text of a DOM node against a regular expression
1260
+ * and replaces each match (or node-separated portions of the match)
1261
+ * in the specified element.
1262
+ */
1263
+
1264
+ if (typeof(Retreaver) === 'undefined') {
1265
+ var Retreaver = {};
1266
+ }
1267
+
1268
+ if (typeof(Retreaver.Vendor) === 'undefined') {
1269
+ Retreaver.Vendor = {};
1270
+ }
1271
+
1272
+ (function (root, factory) {
1273
+ if (typeof module === 'object' && module.exports) {
1274
+ // Node/CommonJS
1275
+ module.exports = factory();
1276
+ } else if (typeof define === 'function' && define.amd) {
1277
+ // AMD. Register as an anonymous module.
1278
+ define(factory);
1279
+ } else {
1280
+ // Browser globals
1281
+ root.findAndReplaceDOMText = factory();
1282
+ }
1283
+ }(Retreaver.Vendor, function factory() {
1284
+
1285
+ var PORTION_MODE_RETAIN = 'retain';
1286
+ var PORTION_MODE_FIRST = 'first';
1287
+
1288
+ var doc = document;
1289
+ var hasOwn = {}.hasOwnProperty;
1290
+
1291
+ function escapeRegExp(s) {
1292
+ return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
1293
+ }
1294
+
1295
+ function exposed() {
1296
+ // Try deprecated arg signature first:
1297
+ return deprecated.apply(null, arguments) || findAndReplaceDOMText.apply(null, arguments);
1298
+ }
1299
+
1300
+ function deprecated(regex, node, replacement, captureGroup, elFilter) {
1301
+ if ((node && !node.nodeType) && arguments.length <= 2) {
1302
+ return false;
1303
+ }
1304
+ var isReplacementFunction = typeof replacement == 'function';
1305
+
1306
+ if (isReplacementFunction) {
1307
+ replacement = (function(original) {
1308
+ return function(portion, match) {
1309
+ return original(portion.text, match.startIndex);
1310
+ };
1311
+ }(replacement));
1312
+ }
1313
+
1314
+ // Awkward support for deprecated argument signature (<0.4.0)
1315
+ var instance = findAndReplaceDOMText(node, {
1316
+
1317
+ find: regex,
1318
+
1319
+ wrap: isReplacementFunction ? null : replacement,
1320
+ replace: isReplacementFunction ? replacement : '$' + (captureGroup || '&'),
1321
+
1322
+ prepMatch: function(m, mi) {
1323
+
1324
+ // Support captureGroup (a deprecated feature)
1325
+
1326
+ if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches';
1327
+
1328
+ if (captureGroup > 0) {
1329
+ var cg = m[captureGroup];
1330
+ m.index += m[0].indexOf(cg);
1331
+ m[0] = cg;
1332
+ }
1333
+
1334
+ m.endIndex = m.index + m[0].length;
1335
+ m.startIndex = m.index;
1336
+ m.index = mi;
1337
+
1338
+ return m;
1339
+ },
1340
+ filterElements: elFilter
1341
+ });
1342
+
1343
+ exposed.revert = function() {
1344
+ return instance.revert();
1345
+ };
1346
+
1347
+ return true;
1348
+ }
1349
+
1350
+ /**
1351
+ * findAndReplaceDOMText
1352
+ *
1353
+ * Locates matches and replaces with replacementNode
1354
+ *
1355
+ * @param {Node} node Element or Text node to search within
1356
+ * @param {RegExp} options.find The regular expression to match
1357
+ * @param {String|Element} [options.wrap] A NodeName, or a Node to clone
1358
+ * @param {String|Function} [options.replace='$&'] What to replace each match with
1359
+ * @param {Function} [options.filterElements] A Function to be called to check whether to
1360
+ * process an element. (returning true = process element,
1361
+ * returning false = avoid element)
1362
+ */
1363
+ function findAndReplaceDOMText(node, options) {
1364
+ return new Finder(node, options);
1365
+ }
1366
+
1367
+ exposed.NON_PROSE_ELEMENTS = {
1368
+ br:1, hr:1,
1369
+ // Media / Source elements:
1370
+ script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
1371
+ // Input elements
1372
+ input:1, textarea:1, select:1, option:1, optgroup: 1, button:1
1373
+ };
1374
+
1375
+ exposed.NON_CONTIGUOUS_PROSE_ELEMENTS = {
1376
+
1377
+ // Elements that will not contain prose or block elements where we don't
1378
+ // want prose to be matches across element borders:
1379
+
1380
+ // Block Elements
1381
+ address:1, article:1, aside:1, blockquote:1, dd:1, div:1,
1382
+ dl:1, fieldset:1, figcaption:1, figure:1, footer:1, form:1, h1:1, h2:1, h3:1,
1383
+ h4:1, h5:1, h6:1, header:1, hgroup:1, hr:1, main:1, nav:1, noscript:1, ol:1,
1384
+ output:1, p:1, pre:1, section:1, ul:1,
1385
+ // Other misc. elements that are not part of continuous inline prose:
1386
+ br:1, li: 1, summary: 1, dt:1, details:1, rp:1, rt:1, rtc:1,
1387
+ // Media / Source elements:
1388
+ script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
1389
+ // Input elements
1390
+ input:1, textarea:1, select:1, option:1, optgroup:1, button:1,
1391
+ // Table related elements:
1392
+ table:1, tbody:1, thead:1, th:1, tr:1, td:1, caption:1, col:1, tfoot:1, colgroup:1
1393
+
1394
+ };
1395
+
1396
+ exposed.NON_INLINE_PROSE = function(el) {
1397
+ return hasOwn.call(exposed.NON_CONTIGUOUS_PROSE_ELEMENTS, el.nodeName.toLowerCase());
1398
+ };
1399
+
1400
+ // Presets accessed via `options.preset` when calling findAndReplaceDOMText():
1401
+ exposed.PRESETS = {
1402
+ prose: {
1403
+ forceContext: exposed.NON_INLINE_PROSE,
1404
+ filterElements: function(el) {
1405
+ return !hasOwn.call(exposed.NON_PROSE_ELEMENTS, el.nodeName.toLowerCase());
1406
+ }
1407
+ }
1408
+ };
1409
+
1410
+ exposed.Finder = Finder;
1411
+
1412
+ /**
1413
+ * Finder -- encapsulates logic to find and replace.
1414
+ */
1415
+ function Finder(node, options) {
1416
+
1417
+ var preset = options.preset && exposed.PRESETS[options.preset];
1418
+
1419
+ options.portionMode = options.portionMode || PORTION_MODE_RETAIN;
1420
+
1421
+ if (preset) {
1422
+ for (var i in preset) {
1423
+ if (hasOwn.call(preset, i) && !hasOwn.call(options, i)) {
1424
+ options[i] = preset[i];
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ this.node = node;
1430
+ this.options = options;
1431
+
1432
+ // Enable match-preparation method to be passed as option:
1433
+ this.prepMatch = options.prepMatch || this.prepMatch;
1434
+
1435
+ this.reverts = [];
1436
+
1437
+ this.matches = this.search();
1438
+
1439
+ if (this.matches.length) {
1440
+ this.processMatches();
1441
+ }
1442
+
1443
+ }
1444
+
1445
+ Finder.prototype = {
1446
+
1447
+ /**
1448
+ * Searches for all matches that comply with the instance's 'match' option
1449
+ */
1450
+ search: function() {
1451
+
1452
+ var match;
1453
+ var matchIndex = 0;
1454
+ var offset = 0;
1455
+ var regex = this.options.find;
1456
+ var textAggregation = this.getAggregateText();
1457
+ var matches = [];
1458
+ var self = this;
1459
+
1460
+ regex = typeof regex === 'string' ? RegExp(escapeRegExp(regex), 'g') : regex;
1461
+
1462
+ matchAggregation(textAggregation);
1463
+
1464
+ function matchAggregation(textAggregation) {
1465
+ for (var i = 0, l = textAggregation.length; i < l; ++i) {
1466
+
1467
+ var text = textAggregation[i];
1468
+
1469
+ if (typeof text !== 'string') {
1470
+ // Deal with nested contexts: (recursive)
1471
+ matchAggregation(text);
1472
+ continue;
1473
+ }
1474
+
1475
+ if (regex.global) {
1476
+ while (match = regex.exec(text)) {
1477
+ matches.push(self.prepMatch(match, matchIndex++, offset));
1478
+ }
1479
+ } else {
1480
+ if (match = text.match(regex)) {
1481
+ matches.push(self.prepMatch(match, 0, offset));
1482
+ }
1483
+ }
1484
+
1485
+ offset += text.length;
1486
+ }
1487
+ }
1488
+
1489
+ return matches;
1490
+
1491
+ },
1492
+
1493
+ /**
1494
+ * Prepares a single match with useful meta info:
1495
+ */
1496
+ prepMatch: function(match, matchIndex, characterOffset) {
1497
+
1498
+ if (!match[0]) {
1499
+ throw new Error('findAndReplaceDOMText cannot handle zero-length matches');
1500
+ }
1501
+
1502
+ match.endIndex = characterOffset + match.index + match[0].length;
1503
+ match.startIndex = characterOffset + match.index;
1504
+ match.index = matchIndex;
1505
+
1506
+ return match;
1507
+ },
1508
+
1509
+ /**
1510
+ * Gets aggregate text within subject node
1511
+ */
1512
+ getAggregateText: function() {
1513
+
1514
+ var elementFilter = this.options.filterElements;
1515
+ var forceContext = this.options.forceContext;
1516
+
1517
+ return getText(this.node);
1518
+
1519
+ /**
1520
+ * Gets aggregate text of a node without resorting
1521
+ * to broken innerText/textContent
1522
+ */
1523
+ function getText(node) {
1524
+
1525
+ if (node.nodeType === 3) {
1526
+ return [node.data];
1527
+ }
1528
+
1529
+ if (elementFilter && !elementFilter(node)) {
1530
+ return [];
1531
+ }
1532
+
1533
+ var txt = [''];
1534
+ var i = 0;
1535
+
1536
+ if (node = node.firstChild) do {
1537
+
1538
+ if (node.nodeType === 3) {
1539
+ txt[i] += node.data;
1540
+ continue;
1541
+ }
1542
+
1543
+ var innerText = getText(node);
1544
+
1545
+ if (
1546
+ forceContext &&
1547
+ node.nodeType === 1 &&
1548
+ (forceContext === true || forceContext(node))
1549
+ ) {
1550
+ txt[++i] = innerText;
1551
+ txt[++i] = '';
1552
+ } else {
1553
+ if (typeof innerText[0] === 'string') {
1554
+ // Bridge nested text-node data so that they're
1555
+ // not considered their own contexts:
1556
+ // I.e. ['some', ['thing']] -> ['something']
1557
+ txt[i] += innerText.shift();
1558
+ }
1559
+ if (innerText.length) {
1560
+ txt[++i] = innerText;
1561
+ txt[++i] = '';
1562
+ }
1563
+ }
1564
+ } while (node = node.nextSibling);
1565
+
1566
+ return txt;
1567
+
1568
+ }
1569
+
1570
+ },
1571
+
1572
+ /**
1573
+ * Steps through the target node, looking for matches, and
1574
+ * calling replaceFn when a match is found.
1575
+ */
1576
+ processMatches: function() {
1577
+
1578
+ var matches = this.matches;
1579
+ var node = this.node;
1580
+ var elementFilter = this.options.filterElements;
1581
+
1582
+ var startPortion,
1583
+ endPortion,
1584
+ innerPortions = [],
1585
+ curNode = node,
1586
+ match = matches.shift(),
1587
+ atIndex = 0, // i.e. nodeAtIndex
1588
+ matchIndex = 0,
1589
+ portionIndex = 0,
1590
+ doAvoidNode,
1591
+ nodeStack = [node];
1592
+
1593
+ out: while (true) {
1594
+
1595
+ if (curNode.nodeType === 3) {
1596
+
1597
+ if (!endPortion && curNode.length + atIndex >= match.endIndex) {
1598
+
1599
+ // We've found the ending
1600
+ endPortion = {
1601
+ node: curNode,
1602
+ index: portionIndex++,
1603
+ text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex),
1604
+ indexInMatch: atIndex - match.startIndex,
1605
+ indexInNode: match.startIndex - atIndex, // always zero for end-portions
1606
+ endIndexInNode: match.endIndex - atIndex,
1607
+ isEnd: true
1608
+ };
1609
+
1610
+ } else if (startPortion) {
1611
+ // Intersecting node
1612
+ innerPortions.push({
1613
+ node: curNode,
1614
+ index: portionIndex++,
1615
+ text: curNode.data,
1616
+ indexInMatch: atIndex - match.startIndex,
1617
+ indexInNode: 0 // always zero for inner-portions
1618
+ });
1619
+ }
1620
+
1621
+ if (!startPortion && curNode.length + atIndex > match.startIndex) {
1622
+ // We've found the match start
1623
+ startPortion = {
1624
+ node: curNode,
1625
+ index: portionIndex++,
1626
+ indexInMatch: 0,
1627
+ indexInNode: match.startIndex - atIndex,
1628
+ endIndexInNode: match.endIndex - atIndex,
1629
+ text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex)
1630
+ };
1631
+ }
1632
+
1633
+ atIndex += curNode.data.length;
1634
+
1635
+ }
1636
+
1637
+ doAvoidNode = curNode.nodeType === 1 && elementFilter && !elementFilter(curNode);
1638
+
1639
+ if (startPortion && endPortion) {
1640
+
1641
+ curNode = this.replaceMatch(match, startPortion, innerPortions, endPortion);
1642
+
1643
+ // processMatches has to return the node that replaced the endNode
1644
+ // and then we step back so we can continue from the end of the
1645
+ // match:
1646
+
1647
+ atIndex -= (endPortion.node.data.length - endPortion.endIndexInNode);
1648
+
1649
+ startPortion = null;
1650
+ endPortion = null;
1651
+ innerPortions = [];
1652
+ match = matches.shift();
1653
+ portionIndex = 0;
1654
+ matchIndex++;
1655
+
1656
+ if (!match) {
1657
+ break; // no more matches
1658
+ }
1659
+
1660
+ } else if (
1661
+ !doAvoidNode &&
1662
+ (curNode.firstChild || curNode.nextSibling)
1663
+ ) {
1664
+ // Move down or forward:
1665
+ if (curNode.firstChild) {
1666
+ nodeStack.push(curNode);
1667
+ curNode = curNode.firstChild;
1668
+ } else {
1669
+ curNode = curNode.nextSibling;
1670
+ }
1671
+ continue;
1672
+ }
1673
+
1674
+ // Move forward or up:
1675
+ while (true) {
1676
+ if (curNode.nextSibling) {
1677
+ curNode = curNode.nextSibling;
1678
+ break;
1679
+ }
1680
+ curNode = nodeStack.pop();
1681
+ if (curNode === node) {
1682
+ break out;
1683
+ }
1684
+ }
1685
+
1686
+ }
1687
+
1688
+ },
1689
+
1690
+ /**
1691
+ * Reverts ... TODO
1692
+ */
1693
+ revert: function() {
1694
+ // Reversion occurs backwards so as to avoid nodes subsequently
1695
+ // replaced during the matching phase (a forward process):
1696
+ for (var l = this.reverts.length; l--;) {
1697
+ this.reverts[l]();
1698
+ }
1699
+ this.reverts = [];
1700
+ },
1701
+
1702
+ prepareReplacementString: function(string, portion, match) {
1703
+ var portionMode = this.options.portionMode;
1704
+ if (
1705
+ portionMode === PORTION_MODE_FIRST &&
1706
+ portion.indexInMatch > 0
1707
+ ) {
1708
+ return '';
1709
+ }
1710
+ string = string.replace(/\$(\d+|&|`|')/g, function($0, t) {
1711
+ var replacement;
1712
+ switch(t) {
1713
+ case '&':
1714
+ replacement = match[0];
1715
+ break;
1716
+ case '`':
1717
+ replacement = match.input.substring(0, match.startIndex);
1718
+ break;
1719
+ case '\'':
1720
+ replacement = match.input.substring(match.endIndex);
1721
+ break;
1722
+ default:
1723
+ replacement = match[+t];
1724
+ }
1725
+ return replacement;
1726
+ });
1727
+
1728
+ if (portionMode === PORTION_MODE_FIRST) {
1729
+ return string;
1730
+ }
1731
+
1732
+ if (portion.isEnd) {
1733
+ return string.substring(portion.indexInMatch);
1734
+ }
1735
+
1736
+ return string.substring(portion.indexInMatch, portion.indexInMatch + portion.text.length);
1737
+ },
1738
+
1739
+ getPortionReplacementNode: function(portion, match) {
1740
+
1741
+ var replacement = this.options.replace || '$&';
1742
+ var wrapper = this.options.wrap;
1743
+
1744
+ if (wrapper && wrapper.nodeType) {
1745
+ // Wrapper has been provided as a stencil-node for us to clone:
1746
+ var clone = doc.createElement('div');
1747
+ clone.innerHTML = wrapper.outerHTML || new XMLSerializer().serializeToString(wrapper);
1748
+ wrapper = clone.firstChild;
1749
+ }
1750
+
1751
+ if (typeof replacement == 'function') {
1752
+ replacement = replacement(portion, match);
1753
+ if (replacement && replacement.nodeType) {
1754
+ return replacement;
1755
+ }
1756
+ return doc.createTextNode(String(replacement));
1757
+ }
1758
+
1759
+ var el = typeof wrapper == 'string' ? doc.createElement(wrapper) : wrapper;
1760
+
1761
+ replacement = doc.createTextNode(
1762
+ this.prepareReplacementString(
1763
+ replacement, portion, match
1764
+ )
1765
+ );
1766
+
1767
+ if (!replacement.data) {
1768
+ return replacement;
1769
+ }
1770
+
1771
+ if (!el) {
1772
+ return replacement;
1773
+ }
1774
+
1775
+ el.appendChild(replacement);
1776
+
1777
+ return el;
1778
+ },
1779
+
1780
+ replaceMatch: function(match, startPortion, innerPortions, endPortion) {
1781
+
1782
+ var matchStartNode = startPortion.node;
1783
+ var matchEndNode = endPortion.node;
1784
+
1785
+ var precedingTextNode;
1786
+ var followingTextNode;
1787
+
1788
+ if (matchStartNode === matchEndNode) {
1789
+
1790
+ var node = matchStartNode;
1791
+
1792
+ if (startPortion.indexInNode > 0) {
1793
+ // Add `before` text node (before the match)
1794
+ precedingTextNode = doc.createTextNode(node.data.substring(0, startPortion.indexInNode));
1795
+ node.parentNode.insertBefore(precedingTextNode, node);
1796
+ }
1797
+
1798
+ // Create the replacement node:
1799
+ var newNode = this.getPortionReplacementNode(
1800
+ endPortion,
1801
+ match
1802
+ );
1803
+
1804
+ node.parentNode.insertBefore(newNode, node);
1805
+
1806
+ if (endPortion.endIndexInNode < node.length) { // ?????
1807
+ // Add `after` text node (after the match)
1808
+ followingTextNode = doc.createTextNode(node.data.substring(endPortion.endIndexInNode));
1809
+ node.parentNode.insertBefore(followingTextNode, node);
1810
+ }
1811
+
1812
+ node.parentNode.removeChild(node);
1813
+
1814
+ this.reverts.push(function() {
1815
+ if (precedingTextNode === newNode.previousSibling) {
1816
+ precedingTextNode.parentNode.removeChild(precedingTextNode);
1817
+ }
1818
+ if (followingTextNode === newNode.nextSibling) {
1819
+ followingTextNode.parentNode.removeChild(followingTextNode);
1820
+ }
1821
+ newNode.parentNode.replaceChild(node, newNode);
1822
+ });
1823
+
1824
+ return newNode;
1825
+
1826
+ } else {
1827
+ // Replace matchStartNode -> [innerMatchNodes...] -> matchEndNode (in that order)
1828
+
1829
+
1830
+ precedingTextNode = doc.createTextNode(
1831
+ matchStartNode.data.substring(0, startPortion.indexInNode)
1832
+ );
1833
+
1834
+ followingTextNode = doc.createTextNode(
1835
+ matchEndNode.data.substring(endPortion.endIndexInNode)
1836
+ );
1837
+
1838
+ var firstNode = this.getPortionReplacementNode(
1839
+ startPortion,
1840
+ match
1841
+ );
1842
+
1843
+ var innerNodes = [];
1844
+
1845
+ for (var i = 0, l = innerPortions.length; i < l; ++i) {
1846
+ var portion = innerPortions[i];
1847
+ var innerNode = this.getPortionReplacementNode(
1848
+ portion,
1849
+ match
1850
+ );
1851
+ portion.node.parentNode.replaceChild(innerNode, portion.node);
1852
+ this.reverts.push((function(portion, innerNode) {
1853
+ return function() {
1854
+ innerNode.parentNode.replaceChild(portion.node, innerNode);
1855
+ };
1856
+ }(portion, innerNode)));
1857
+ innerNodes.push(innerNode);
1858
+ }
1859
+
1860
+ var lastNode = this.getPortionReplacementNode(
1861
+ endPortion,
1862
+ match
1863
+ );
1864
+
1865
+ matchStartNode.parentNode.insertBefore(precedingTextNode, matchStartNode);
1866
+ matchStartNode.parentNode.insertBefore(firstNode, matchStartNode);
1867
+ matchStartNode.parentNode.removeChild(matchStartNode);
1868
+
1869
+ matchEndNode.parentNode.insertBefore(lastNode, matchEndNode);
1870
+ matchEndNode.parentNode.insertBefore(followingTextNode, matchEndNode);
1871
+ matchEndNode.parentNode.removeChild(matchEndNode);
1872
+
1873
+ this.reverts.push(function() {
1874
+ precedingTextNode.parentNode.removeChild(precedingTextNode);
1875
+ firstNode.parentNode.replaceChild(matchStartNode, firstNode);
1876
+ followingTextNode.parentNode.removeChild(followingTextNode);
1877
+ lastNode.parentNode.replaceChild(matchEndNode, lastNode);
1878
+ });
1879
+
1880
+ return lastNode;
1881
+ }
1882
+ }
1883
+
1884
+ };
1885
+
1886
+ return exposed;
1887
+
1888
+ }));