1'use strict';
2
3var anObject = require('./_an-object');
4var toObject = require('./_to-object');
5var toLength = require('./_to-length');
6var toInteger = require('./_to-integer');
7var advanceStringIndex = require('./_advance-string-index');
8var regExpExec = require('./_regexp-exec-abstract');
9var max = Math.max;
10var min = Math.min;
11var floor = Math.floor;
12var SUBSTITUTION_SYMBOLS = /\$([$&`']|\d\d?|<[^>]*>)/g;
13var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&`']|\d\d?)/g;
14
15var maybeToString = function (it) {
16  return it === undefined ? it : String(it);
17};
18
19// @@replace logic
20require('./_fix-re-wks')('replace', 2, function (defined, REPLACE, $replace, maybeCallNative) {
21  return [
22    // `String.prototype.replace` method
23    // https://tc39.github.io/ecma262/#sec-string.prototype.replace
24    function replace(searchValue, replaceValue) {
25      var O = defined(this);
26      var fn = searchValue == undefined ? undefined : searchValue[REPLACE];
27      return fn !== undefined
28        ? fn.call(searchValue, O, replaceValue)
29        : $replace.call(String(O), searchValue, replaceValue);
30    },
31    // `RegExp.prototype[@@replace]` method
32    // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace
33    function (regexp, replaceValue) {
34      var res = maybeCallNative($replace, regexp, this, replaceValue);
35      if (res.done) return res.value;
36
37      var rx = anObject(regexp);
38      var S = String(this);
39      var functionalReplace = typeof replaceValue === 'function';
40      if (!functionalReplace) replaceValue = String(replaceValue);
41      var global = rx.global;
42      if (global) {
43        var fullUnicode = rx.unicode;
44        rx.lastIndex = 0;
45      }
46      var results = [];
47      while (true) {
48        var result = regExpExec(rx, S);
49        if (result === null) break;
50        results.push(result);
51        if (!global) break;
52        var matchStr = String(result[0]);
53        if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
54      }
55      var accumulatedResult = '';
56      var nextSourcePosition = 0;
57      for (var i = 0; i < results.length; i++) {
58        result = results[i];
59        var matched = String(result[0]);
60        var position = max(min(toInteger(result.index), S.length), 0);
61        var captures = [];
62        // NOTE: This is equivalent to
63        //   captures = result.slice(1).map(maybeToString)
64        // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
65        // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
66        // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
67        for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
68        var namedCaptures = result.groups;
69        if (functionalReplace) {
70          var replacerArgs = [matched].concat(captures, position, S);
71          if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
72          var replacement = String(replaceValue.apply(undefined, replacerArgs));
73        } else {
74          replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
75        }
76        if (position >= nextSourcePosition) {
77          accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
78          nextSourcePosition = position + matched.length;
79        }
80      }
81      return accumulatedResult + S.slice(nextSourcePosition);
82    }
83  ];
84
85    // https://tc39.github.io/ecma262/#sec-getsubstitution
86  function getSubstitution(matched, str, position, captures, namedCaptures, replacement) {
87    var tailPos = position + matched.length;
88    var m = captures.length;
89    var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
90    if (namedCaptures !== undefined) {
91      namedCaptures = toObject(namedCaptures);
92      symbols = SUBSTITUTION_SYMBOLS;
93    }
94    return $replace.call(replacement, symbols, function (match, ch) {
95      var capture;
96      switch (ch.charAt(0)) {
97        case '$': return '$';
98        case '&': return matched;
99        case '`': return str.slice(0, position);
100        case "'": return str.slice(tailPos);
101        case '<':
102          capture = namedCaptures[ch.slice(1, -1)];
103          break;
104        default: // \d\d?
105          var n = +ch;
106          if (n === 0) return match;
107          if (n > m) {
108            var f = floor(n / 10);
109            if (f === 0) return match;
110            if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
111            return match;
112          }
113          capture = captures[n - 1];
114      }
115      return capture === undefined ? '' : capture;
116    });
117  }
118});
119