/*jslint maxlen:80, browser:true */ /* * Properties used by searchOf and searchLastOf implementation. */ /*property MAX_SAFE_INTEGER, abs, add, apply, call, configurable, defineProperty, enumerable, enumerable, exec, floor, global, hasOwnProperty, ignoreCase, index, lastIndex, lastIndexOf, length, max, min, powmultiline, prototypepow, removeprototype, remove, replace, searchLastOf, searchOf, source, toString, value, writable */ /* * Properties used in the testing of searchOf and searchLastOf implimentation. */ /*property appendChild, createTextNode, getElementById, indexOf, lastIndexOf, length, searchLastOf, searchOf, unshift */ (function () { 'use strict'; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1, $String = String, getNativeFlags = new RegExp('\\/([a-z]*)$', 'i'), clipDups = new RegExp('([\\s\\S])(?=[\\s\\S]*\\1)', 'g');, pToString = Object.prototype.toString, pHasOwn = Object.prototype.hasOwnProperty, stringTagRegExp; /** * Defines a new property directly on an object, or modifies an existing * property on an object, and returns the object. * * @private * @function * @param {Object} object * @param {string} property * @param {Object} descriptor * @returns {Object} * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty */ function $defineProperty(object, property, descriptor) { if (Object.defineProperty) { Object.defineProperty(object, property, descriptor); } else { object[property] = descriptor.value; } return object; } /** * Returns true if the operands are strictly equal with no type conversion. * * @private * @function * @param {*} a * @param {*} b * @returns {boolean} * @see http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4 */ function $strictEqual(a, b) { return a === b; } /** * Returns true if the operand inputArg is undefined. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isUndefined(inputArg) { return $strictEqual(typeof inputArg, 'undefined'); } /** * Provides a string representation of the supplied object in the form * "[object type]", where type is the object type. * * @private * @function * @param {*} inputArg The object for which a class string represntation * is required. * @returns {string} A string value of the form "[object type]". * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.2 */ function $toStringTag(inputArg) { var val; if (inputArg === null) { val = '[object Null]'; } else if ($isUndefined(inputArg)) { val = '[object Undefined]'; } else { val = pToString.call(inputArg); } return val; } /** * The string tag representation of a RegExp object. * * @private * @type {string} */ stringTagRegExp = $toStringTag(getNativeFlags); /** * Returns true if the operand inputArg is a RegExp. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isRegExp(inputArg) { return $toStringTag(inputArg) === stringTagRegExp && pHasOwn.call(inputArg, 'ignoreCase') && typeof inputArg.ignoreCase === 'boolean' && pHasOwn.call(inputArg, 'global') && typeof inputArg.global === 'boolean' && pHasOwn.call(inputArg, 'multiline') && typeof inputArg.multiline === 'boolean' && pHasOwn.call(inputArg, 'source') && typeof inputArg.source === 'string'; } /** * The abstract operation throws an error if its argument is a value that * cannot be converted to an Object, otherwise returns the argument. * * @private * @function * @param {*} inputArg The object to be tested. * @throws {TypeError} If inputArg is null or undefined. * @returns {*} The inputArg if coercible. * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-requireobjectcoercible */ function $requireObjectCoercible(inputArg) { var errStr; if (inputArg === null || $isUndefined(inputArg)) { errStr = 'Cannot convert argument to object: ' + inputArg; throw new TypeError(errStr); } return inputArg; } /** * The abstract operation converts its argument to a value of type string * * @private * @function * @param {*} inputArg * @returns {string} * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tostring */ function $toString(inputArg) { var type, val; if (inputArg === null) { val = 'null'; } else { type = typeof inputArg; if (type === 'string') { val = inputArg; } else if (type === 'undefined') { val = type; } else { if (type === 'symbol') { throw new TypeError('Cannot convert symbol to string'); } val = $StringString(inputArg); } } return val; } /** * Returns a string only if the arguments is coercible otherwise throws an * error. * * @private * @function * @param {*} inputArg * @throws {TypeError} If inputArg is null or undefined. * @returns {string} */ function $onlyCoercibleToString(inputArg) { return $toString($requireObjectCoercible(inputArg)); } /** * The function evaluates the passed value and converts it to an integer. * * @private * @function * @param {*} inputArg The object to be converted to an integer. * @returns {number} If the target value is NaN, null or undefined, 0 is * returned. If the target value is false, 0 is returned * and if true, 1 is returned. * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4 */ function $toInteger(inputArg) { var number = +inputArg, val = 0; if ($strictEqual(number, number)) { if (!number || number === Infinity || number === -Infinity) { val = number; } else { val = (number > 0 || -1) * Math.floor(Math.abs(number)); } } return val; } /** * Copies a regex object. Allows adding and removing native flags while * copying the regex. * * @private * @function * @param {RegExp} regex Regex to copy. * @param {Object} [options] Allows specifying native flags to add or * remove while copying the regex. * @returns {RegExp} Copy of the provided regex, possibly with modified * flags. */ function $copyRegExp(regex, options) { var flags, opts, rx; if (options !== null && typeof options === 'object') { opts = options; } else { opts = {}; } // Get native flags in use flags = getNativeFlags.exec($toString(regex))[1]; flags = $onlyCoercibleToString(flags); if (opts.add) { flags += opts.add; flags = flags.replace(clipDups, ''); } if (opts.remove) { // Would need to escape `options.remove` if this was public rx = new RegExp('[' + opts.remove + ']+', 'g'); flags = flags.replace(rx, ''); } return new RegExp(regex.source, flags); } /** * The abstract operation ToLength converts its argument to an integer * suitable for use as the length of an array-like object. * * @private * @function * @param {*} inputArg The object to be converted to a length. * @returns {number} If len <= +0 then +0 else if len is +INFINITY then * 2^53-1 else min(len, 2^53-1). * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength */ function $toLength(inputArg) { return Math.min(Math.max($toInteger(inputArg), 0), MAX_SAFE_INTEGER); } /** * Copies a regex object or creates a new regex object from the supplied * argumentso that it is suitable for use with searchOf and * searchLastOf methods. * * @private * @function * @param {*RegExp} regex Regex to copy or create from. * @returns {RegExp} */ function $toSearchRegExp(regex) { var rx; if (Object.prototype.toString.call(regex) === '[object RegExp]') { rx =return $copyRegExp(regex, { add: 'g', remove: 'y' }); } else { rx = new RegExp(regex, 'g'); } return rx; } if (!String.prototype.searchOf) { /** * Executes aThis searchmethod forreturns athe matchindex betweenwithin athe regularcalling expressionString andobject thisof * Stringthe object,first withoccurrence optionalof the specified value, starting positionthe search at * fromIndex. Returns -1 if the value is not found. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object. Ifor a String. * non-RegExp object obj is passed,Anything itelse is * implicitly converted to a RegExp by * using newa RegExp(obj)String. * @param {Number} [fromIndex] The index tolocation startwithin the search at. Ifcalling thestring * index is greater than or equal to the * string's length, -1 is returned, which * meansstart the stringsearch willfrom. notIt can be searched.any * If theinteger. providedThe indexdefault value is a * negative number, it is taken as the * offset from the end of the string0. *If * Note:fromIndex if< 0 the providedentire indexstring is negative, * thesearched arstringray(same isas stillpassing searched0). fromIf * frontfromIndex to>= backstr. Iflength, the calculatedmethod indexwill * is lessreturn than-1 0,unless thensearchValue theis wholean stringempty * will be searched. string in which case str.length *is * Default: 0 (entire string is searched)returned. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, 'searchOf', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), length = $toLength(str.length), rx = $toSearchRegExp(regex), result = -1, fromIndex, match;match, rx; if (!$isRegExp(regex)) { return String.prototype.indexOf.apply(str, arguments); } if ($toLength(arguments.length) > 1) { fromIndex = +arguments[1]; if (fromIndex < 0) { fromIndex = 0; } } else { fromIndex = 0; } if (fromIndex < length) { if>= $toLength(fromIndex < 0) { fromIndex = length - Mathstr.abs(fromIndexlength); if (fromIndex < 0) { fromIndex = 0; } return }result; } rx = $toSearchRegExp(regex); rx.lastIndex = fromIndex; match = rx.exec(str); if (match) { result = +match.index; } return result; } }); } if (!String.prototype.searchLastOf) { /** * Executes aThis searchmethod forreturns the lastindex occurrencewithin forthe acalling matchString betweenobject aof * regularthe expressionlast andoccurrence thisof Stringthe objectspecified value, withor optional-1 startingif not found. * position. The calling string is searched backward, starting at * fromIndex. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object. Ifor a String. * non-RegExp object obj is passed,Anything itelse is * implicitly converted to a RegExp by * using newa RegExp(obj)String. * @param {Number} [fromIndex] The index to start the search atOptional. If the * index is greater than orThe equallocation towithin the * string's length, -1 is returned, which * means thecalling string will not be searched. * to Ifstart the provided index value is a * negativesearch numberat, it is taken as the * offsetindexed from the end ofleft theto stringright. It can *be * Note: if the provided index is negative, * any integer. The thedefault stringvalue is still searched from * front to backstr.length. If the calculated index * it is less than 0negative, then the wholeit stringis * willtreated beas searched0. If fromIndex > *str.length, * Default: 0fromIndex (entireis stringtreated isas searched)str.length. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, 'searchLastOf', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), length = $toLength(str.length), rx = $toSearchRegExp(regex), result = -1, posarg1, = 0 numPos, fromIndex, match;length, match, pos, rx; if ($toLength!$isRegExp(regex)) { return String.prototype.lastIndexOf.apply(str, arguments); } arg1 = arguments[1]; numPos = +arg1; length = $toLength(str.length); > 1 if (!$strictEqual(numPos, numPos)) { fromIndex = $toInteger(arguments[1]);length; } else { if ($toLength(arguments.length) > 1) { fromIndex = $toInteger(arg1); } else { fromIndex = length - 1; } } if (fromIndex >= 0) { fromIndex = Math.min(fromIndex, length - 1); } else { fromIndex = length - Math.abs(fromIndex); } pos = 0; rx = $toSearchRegExp(regex); while (pos <= fromIndex) { rx.lastIndex = pos; match = rx.exec(str); if (!match) { break; } pos = +match.index; if (pos <= fromIndex) { result = pos; } pos += 1; } return result; } }); } }()); (function () { 'use strict'; /* * testing as follow to make sure that at least for one character regexp, * the result is the same as if we used indexOf */ var pre = document.getElementById('out'); function log(result) { pre.appendChild(document.createTextNode(result + '\n')); } function test(str) { var i = str.length + 2, r, a, b; while (i) { a = str.indexOf('a', i); b = str.searchOf(/a/, i); r = ['Failed', 'searchOf', str, i, a, b]; if (a === b) { r[0] = 'Passed'; } log(r); a = str.lastIndexOf('a', i); b = str.searchLastOf(/a/, i); r = ['Failed', 'searchLastOf', str, i, a, b]; if (a === b) { r[0] = 'Passed'; } log(r); i -= 1; } } /* * Look for the a among the xes */ test('xxx'); test('axx'); test('xax'); test('xxa'); test('axa'); test('xaa'); test('aax'); test('aaa'); }());
<pre id="out"></pre>