├── .gitignore
├── .jshintrc
├── .travis.yml
├── LICENSE
├── README.md
├── index.js
├── lodash.custom.js
├── package-lock.json
├── package.json
└── test
└── log.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 | npm-debug.log
4 | .sass-cache
5 | bower_components
6 | test/fixtures/files/*
7 | test/fixtures/js/*
8 | example.js
9 | .DS_store
10 | doc/
11 | .coveralls.yml
12 | lodash.custom.min.js
13 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "curly": true,
3 | "eqeqeq": true,
4 | "immed": true,
5 | "latedef": "nofunc",
6 | "newcap": true,
7 | "noarg": true,
8 | "plusplus": true,
9 | "sub": true,
10 | "undef": true,
11 | "quotmark": "double",
12 | "unused": true,
13 | "boss": true,
14 | "indent": 4,
15 | "eqnull": true,
16 | "node": true,
17 | "strict": false,
18 | "mocha": true,
19 | "esversion": 6
20 | }
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [2015] [Shane Osbourne]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ##eazy-logger [](https://travis-ci.org/shakyShane/easy-logger) [](https://coveralls.io/r/shakyShane/easy-logger?branch=master)
2 |
3 | [tFunk](https://github.com/shakyShane/tfunk) + [String Substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data)
4 |
5 | 
6 |
7 | ##Install
8 |
9 | ```
10 | $ npm install eazy-logger --save
11 | ```
12 |
13 | ##Usage
14 |
15 | ```js
16 | var logger = require("eazy-logger").Logger({
17 | prefix: "{blue:[}{magenta:easy-logger}{blue:] }",
18 | useLevelPrefixes: true
19 | });
20 | ```
21 |
22 | ```js
23 | /**
24 | * Standard loggers + prefixes
25 | */
26 | logger.debug("Debugging Msg");
27 | logger.info("Info statement");
28 | logger.warn("A little warning with string %s", "substitution");
29 | logger.error("an error occurred in file: {red:%s}", "/users/awesomedev/file.js");
30 | ```
31 |
32 | ```js
33 | /**
34 | * Use string substitution + colours
35 | */
36 | logger.log("error", "Use {green:built-in} %s", "String substitution");
37 | ```
38 |
39 | ```js
40 | /**
41 | * Set an option for the next log statement only
42 | */
43 | logger.setOnce("useLevelPrefixes", true).warn("Use {green:built-in} %s", "String substitution");
44 | ```
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * tFunk for colours/compiler
3 | */
4 | var chalk = require("chalk");
5 |
6 | /**
7 | * Lodash clonedeep & merge
8 | */
9 | var _ = require("./lodash.custom");
10 |
11 | /**
12 | * Default configuration.
13 | * Can be overridden in first constructor arg
14 | */
15 | var defaults = {
16 |
17 | /**
18 | * Initial log level
19 | */
20 | level: "info",
21 |
22 | /**
23 | * Prefix for logger
24 | */
25 | prefix: "",
26 |
27 | /**
28 | * Available levels and their score
29 | */
30 | levels: {
31 | "trace": 100,
32 | "debug": 200,
33 | "warn": 300,
34 | "info": 400,
35 | "error": 500
36 | },
37 |
38 | /**
39 | * Default prefixes
40 | */
41 | prefixes: {
42 | "trace": "[trace] ",
43 | "debug": chalk.yellow("[debug] "),
44 | "info": chalk.cyan("[info] "),
45 | "warn": chalk.magenta("[warn] "),
46 | "error": chalk.red("[error] ")
47 | },
48 |
49 | /**
50 | * Should easy log statement be prefixed with the level?
51 | */
52 | useLevelPrefixes: false
53 | };
54 |
55 |
56 | /**
57 | * @param {Object} config
58 | * @constructor
59 | */
60 | var Logger = function(config) {
61 |
62 | if (!(this instanceof Logger)) {
63 | return new Logger(config);
64 | }
65 |
66 | config = config || {};
67 |
68 | this._mute = false;
69 | var safeConfig = {};
70 | for (var attr in config) {
71 | if (!config.hasOwnProperty(attr)) {
72 | continue;
73 | }
74 | if (attr === "__proto__" || attr === "constructor" || attr === "prototype") {
75 | continue;
76 | }
77 | safeConfig[attr] = config[attr];
78 | }
79 | this.config = _.merge({}, defaults, safeConfig);
80 | this.addLevelMethods(this.config.levels);
81 | this._memo = {};
82 |
83 | return this;
84 | };
85 |
86 | /**
87 | * Set an option once
88 | * @param path
89 | * @param value
90 | */
91 | Logger.prototype.setOnce = function (path, value) {
92 |
93 | if (typeof this.config[path] !== "undefined") {
94 |
95 | if (typeof this._memo[path] === "undefined") {
96 | this._memo[path] = this.config[path];
97 | }
98 |
99 | this.config[path] = value;
100 | }
101 |
102 | return this;
103 | };
104 | /**
105 | * Add convenience method such as
106 | * logger.warn("msg")
107 | * logger.error("msg")
108 | * logger.info("msg")
109 | *
110 | * instead of
111 | * logger.log("warn", "msg");
112 | * @param items
113 | */
114 | Logger.prototype.addLevelMethods = function (items) {
115 | Object.keys(items).forEach(function (item) {
116 | if (!this[item]) {
117 | this[item] = function () {
118 | var args = Array.prototype.slice.call(arguments);
119 | this.log.apply(this, args);
120 | return this;
121 | }.bind(this, item);
122 | }
123 | }, this);
124 | };
125 | /**
126 | * Reset the state of the logger.
127 | * @returns {Logger}
128 | */
129 | Logger.prototype.reset = function () {
130 |
131 | this.setLevel(defaults.level)
132 | .setLevelPrefixes(defaults.useLevelPrefixes)
133 | .mute(false);
134 |
135 | return this;
136 | };
137 |
138 | /**
139 | * @param {String} level
140 | * @returns {boolean}
141 | */
142 | Logger.prototype.canLog = function (level) {
143 | return this.config.levels[level] >= this.config.levels[this.config.level] && !this._mute;
144 | };
145 |
146 | /**
147 | * Log to the console with prefix
148 | * @param {String} level
149 | * @param {String} msg
150 | * @returns {Logger}
151 | */
152 | Logger.prototype.log = function (level, msg) {
153 |
154 | var args = Array.prototype.slice.call(arguments);
155 |
156 | this.logOne(args, msg, level);
157 |
158 | return this;
159 | };
160 |
161 | /**
162 | * Set the log level
163 | * @param {String} level
164 | * @returns {Logger}
165 | */
166 | Logger.prototype.setLevel = function (level) {
167 |
168 | this.config.level = level;
169 |
170 | return this;
171 | };
172 |
173 | /**
174 | * @param {boolean} state
175 | * @returns {Logger}
176 | */
177 | Logger.prototype.setLevelPrefixes = function (state) {
178 |
179 | this.config.useLevelPrefixes = state;
180 |
181 | return this;
182 | };
183 |
184 | /**
185 | * @param prefix
186 | */
187 | Logger.prototype.setPrefix = function (prefix) {
188 | this.config.prefix = strOrFn(prefix);
189 | };
190 |
191 | /**
192 | * @param {String} level
193 | * @param {String} msg
194 | * @returns {Logger}
195 | */
196 | Logger.prototype.unprefixed = function (level, msg) {
197 |
198 | var args = Array.prototype.slice.call(arguments);
199 |
200 | this.logOne(args, msg, level, true);
201 |
202 | return this;
203 | };
204 |
205 | /**
206 | * @param {Array} args
207 | * @param {()=>String} msg
208 | * @param {String} level
209 | * @param {boolean} [unprefixed]
210 | * @returns {Logger}
211 | */
212 | Logger.prototype.logOne = function (args, msg, level, unprefixed) {
213 |
214 | if (!this.canLog(level)) {
215 | return;
216 | }
217 |
218 | args = args.slice(2);
219 |
220 | var incomingMessage = typeof msg === "string" ? msg : msg();
221 |
222 | if (this.config.useLevelPrefixes && !unprefixed) {
223 | incomingMessage = this.config.prefixes[level] + incomingMessage;
224 | }
225 |
226 | var prefix = strOrFn(this.config.prefix);
227 | var result = unprefixed ? [incomingMessage] : [prefix, incomingMessage];
228 |
229 | args.unshift(result.join(""));
230 |
231 | console.log.apply(console, args);
232 |
233 | this.resetTemps();
234 |
235 | return this;
236 | };
237 |
238 | /**
239 | * Reset any temporary value
240 | */
241 | Logger.prototype.resetTemps = function () {
242 |
243 | Object.keys(this._memo).forEach(function (key) {
244 | this.config[key] = this._memo[key];
245 | }, this);
246 | };
247 |
248 | /**
249 | * Mute the logger
250 | */
251 | Logger.prototype.mute = function (bool) {
252 |
253 | this._mute = bool;
254 | return this;
255 | };
256 |
257 | /**
258 | * Clone the instance to share setup
259 | * @param opts
260 | * @returns {Logger}
261 | */
262 | Logger.prototype.clone = function (opts) {
263 |
264 | var config = _.cloneDeep(this.config);
265 |
266 | if (typeof opts === "function") {
267 | config = opts(config) || {};
268 | } else {
269 | config = _.merge({}, config, opts || {});
270 | }
271 |
272 | return new Logger(config);
273 | };
274 |
275 | /**
276 | * @param input
277 | */
278 | function strOrFn(input) {
279 | if (typeof input === "string") {
280 | return input;
281 | }
282 | if (typeof input === "function") {
283 | return input();
284 | }
285 | throw new Error("unreachable");
286 | }
287 |
288 | module.exports.Logger = Logger;
289 |
--------------------------------------------------------------------------------
/lodash.custom.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * lodash (Custom Build)
4 | * Build: `lodash include="cloneDeep,merge" exports="node"`
5 | * Copyright jQuery Foundation and other contributors
6 | * Released under MIT license
7 | * Based on Underscore.js 1.8.3
8 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
9 | */
10 | ;(function() {
11 |
12 | /** Used as a safe reference for `undefined` in pre-ES5 environments. */
13 | var undefined;
14 |
15 | /** Used as the semantic version number. */
16 | var VERSION = '4.12.0';
17 |
18 | /** Used as the size to enable large array optimizations. */
19 | var LARGE_ARRAY_SIZE = 200;
20 |
21 | /** Used as the `TypeError` message for "Functions" methods. */
22 | var FUNC_ERROR_TEXT = 'Expected a function';
23 |
24 | /** Used to stand-in for `undefined` hash values. */
25 | var HASH_UNDEFINED = '__lodash_hash_undefined__';
26 |
27 | /** Used as references for various `Number` constants. */
28 | var INFINITY = 1 / 0,
29 | MAX_SAFE_INTEGER = 9007199254740991,
30 | MAX_INTEGER = 1.7976931348623157e+308,
31 | NAN = 0 / 0;
32 |
33 | /** `Object#toString` result references. */
34 | var argsTag = '[object Arguments]',
35 | arrayTag = '[object Array]',
36 | boolTag = '[object Boolean]',
37 | dateTag = '[object Date]',
38 | errorTag = '[object Error]',
39 | funcTag = '[object Function]',
40 | genTag = '[object GeneratorFunction]',
41 | mapTag = '[object Map]',
42 | numberTag = '[object Number]',
43 | objectTag = '[object Object]',
44 | promiseTag = '[object Promise]',
45 | regexpTag = '[object RegExp]',
46 | setTag = '[object Set]',
47 | stringTag = '[object String]',
48 | symbolTag = '[object Symbol]',
49 | weakMapTag = '[object WeakMap]';
50 |
51 | var arrayBufferTag = '[object ArrayBuffer]',
52 | dataViewTag = '[object DataView]',
53 | float32Tag = '[object Float32Array]',
54 | float64Tag = '[object Float64Array]',
55 | int8Tag = '[object Int8Array]',
56 | int16Tag = '[object Int16Array]',
57 | int32Tag = '[object Int32Array]',
58 | uint8Tag = '[object Uint8Array]',
59 | uint8ClampedTag = '[object Uint8ClampedArray]',
60 | uint16Tag = '[object Uint16Array]',
61 | uint32Tag = '[object Uint32Array]';
62 |
63 | /**
64 | * Used to match `RegExp`
65 | * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns).
66 | */
67 | var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
68 |
69 | /** Used to match leading and trailing whitespace. */
70 | var reTrim = /^\s+|\s+$/g;
71 |
72 | /** Used to match `RegExp` flags from their coerced string values. */
73 | var reFlags = /\w*$/;
74 |
75 | /** Used to detect bad signed hexadecimal string values. */
76 | var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
77 |
78 | /** Used to detect binary string values. */
79 | var reIsBinary = /^0b[01]+$/i;
80 |
81 | /** Used to detect host constructors (Safari). */
82 | var reIsHostCtor = /^\[object .+?Constructor\]$/;
83 |
84 | /** Used to detect octal string values. */
85 | var reIsOctal = /^0o[0-7]+$/i;
86 |
87 | /** Used to detect unsigned integer values. */
88 | var reIsUint = /^(?:0|[1-9]\d*)$/;
89 |
90 | /** Used to identify `toStringTag` values of typed arrays. */
91 | var typedArrayTags = {};
92 | typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
93 | typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
94 | typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
95 | typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
96 | typedArrayTags[uint32Tag] = true;
97 | typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
98 | typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
99 | typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
100 | typedArrayTags[errorTag] = typedArrayTags[funcTag] =
101 | typedArrayTags[mapTag] = typedArrayTags[numberTag] =
102 | typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
103 | typedArrayTags[setTag] = typedArrayTags[stringTag] =
104 | typedArrayTags[weakMapTag] = false;
105 |
106 | /** Used to identify `toStringTag` values supported by `_.clone`. */
107 | var cloneableTags = {};
108 | cloneableTags[argsTag] = cloneableTags[arrayTag] =
109 | cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
110 | cloneableTags[boolTag] = cloneableTags[dateTag] =
111 | cloneableTags[float32Tag] = cloneableTags[float64Tag] =
112 | cloneableTags[int8Tag] = cloneableTags[int16Tag] =
113 | cloneableTags[int32Tag] = cloneableTags[mapTag] =
114 | cloneableTags[numberTag] = cloneableTags[objectTag] =
115 | cloneableTags[regexpTag] = cloneableTags[setTag] =
116 | cloneableTags[stringTag] = cloneableTags[symbolTag] =
117 | cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
118 | cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
119 | cloneableTags[errorTag] = cloneableTags[funcTag] =
120 | cloneableTags[weakMapTag] = false;
121 |
122 | /** Used to determine if values are of the language type `Object`. */
123 | var objectTypes = {
124 | 'function': true,
125 | 'object': true
126 | };
127 |
128 | /** Built-in method references without a dependency on `root`. */
129 | var freeParseInt = parseInt;
130 |
131 | /** Detect free variable `exports`. */
132 | var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType)
133 | ? exports
134 | : undefined;
135 |
136 | /** Detect free variable `module`. */
137 | var freeModule = (objectTypes[typeof module] && module && !module.nodeType)
138 | ? module
139 | : undefined;
140 |
141 | /** Detect the popular CommonJS extension `module.exports`. */
142 | var moduleExports = (freeModule && freeModule.exports === freeExports)
143 | ? freeExports
144 | : undefined;
145 |
146 | /** Detect free variable `global` from Node.js. */
147 | var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global);
148 |
149 | /** Detect free variable `self`. */
150 | var freeSelf = checkGlobal(objectTypes[typeof self] && self);
151 |
152 | /** Detect free variable `window`. */
153 | var freeWindow = checkGlobal(objectTypes[typeof window] && window);
154 |
155 | /** Detect `this` as the global object. */
156 | var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
157 |
158 | /**
159 | * Used as a reference to the global object.
160 | *
161 | * The `this` value is used if it's the global object to avoid Greasemonkey's
162 | * restricted `window` object, otherwise the `window` object is used.
163 | */
164 | var root = freeGlobal ||
165 | ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) ||
166 | freeSelf || thisGlobal || Function('return this')();
167 |
168 | /*--------------------------------------------------------------------------*/
169 |
170 | /**
171 | * Adds the key-value `pair` to `map`.
172 | *
173 | * @private
174 | * @param {Object} map The map to modify.
175 | * @param {Array} pair The key-value pair to add.
176 | * @returns {Object} Returns `map`.
177 | */
178 | function addMapEntry(map, pair) {
179 | // Don't return `Map#set` because it doesn't return the map instance in IE 11.
180 | map.set(pair[0], pair[1]);
181 | return map;
182 | }
183 |
184 | /**
185 | * Adds `value` to `set`.
186 | *
187 | * @private
188 | * @param {Object} set The set to modify.
189 | * @param {*} value The value to add.
190 | * @returns {Object} Returns `set`.
191 | */
192 | function addSetEntry(set, value) {
193 | set.add(value);
194 | return set;
195 | }
196 |
197 | /**
198 | * A faster alternative to `Function#apply`, this function invokes `func`
199 | * with the `this` binding of `thisArg` and the arguments of `args`.
200 | *
201 | * @private
202 | * @param {Function} func The function to invoke.
203 | * @param {*} thisArg The `this` binding of `func`.
204 | * @param {Array} args The arguments to invoke `func` with.
205 | * @returns {*} Returns the result of `func`.
206 | */
207 | function apply(func, thisArg, args) {
208 | var length = args.length;
209 | switch (length) {
210 | case 0: return func.call(thisArg);
211 | case 1: return func.call(thisArg, args[0]);
212 | case 2: return func.call(thisArg, args[0], args[1]);
213 | case 3: return func.call(thisArg, args[0], args[1], args[2]);
214 | }
215 | return func.apply(thisArg, args);
216 | }
217 |
218 | /**
219 | * A specialized version of `_.forEach` for arrays without support for
220 | * iteratee shorthands.
221 | *
222 | * @private
223 | * @param {Array} array The array to iterate over.
224 | * @param {Function} iteratee The function invoked per iteration.
225 | * @returns {Array} Returns `array`.
226 | */
227 | function arrayEach(array, iteratee) {
228 | var index = -1,
229 | length = array.length;
230 |
231 | while (++index < length) {
232 | if (iteratee(array[index], index, array) === false) {
233 | break;
234 | }
235 | }
236 | return array;
237 | }
238 |
239 | /**
240 | * Appends the elements of `values` to `array`.
241 | *
242 | * @private
243 | * @param {Array} array The array to modify.
244 | * @param {Array} values The values to append.
245 | * @returns {Array} Returns `array`.
246 | */
247 | function arrayPush(array, values) {
248 | var index = -1,
249 | length = values.length,
250 | offset = array.length;
251 |
252 | while (++index < length) {
253 | array[offset + index] = values[index];
254 | }
255 | return array;
256 | }
257 |
258 | /**
259 | * A specialized version of `_.reduce` for arrays without support for
260 | * iteratee shorthands.
261 | *
262 | * @private
263 | * @param {Array} array The array to iterate over.
264 | * @param {Function} iteratee The function invoked per iteration.
265 | * @param {*} [accumulator] The initial value.
266 | * @param {boolean} [initAccum] Specify using the first element of `array` as
267 | * the initial value.
268 | * @returns {*} Returns the accumulated value.
269 | */
270 | function arrayReduce(array, iteratee, accumulator, initAccum) {
271 | var index = -1,
272 | length = array.length;
273 |
274 | if (initAccum && length) {
275 | accumulator = array[++index];
276 | }
277 | while (++index < length) {
278 | accumulator = iteratee(accumulator, array[index], index, array);
279 | }
280 | return accumulator;
281 | }
282 |
283 | /**
284 | * The base implementation of `_.times` without support for iteratee shorthands
285 | * or max array length checks.
286 | *
287 | * @private
288 | * @param {number} n The number of times to invoke `iteratee`.
289 | * @param {Function} iteratee The function invoked per iteration.
290 | * @returns {Array} Returns the array of results.
291 | */
292 | function baseTimes(n, iteratee) {
293 | var index = -1,
294 | result = Array(n);
295 |
296 | while (++index < n) {
297 | result[index] = iteratee(index);
298 | }
299 | return result;
300 | }
301 |
302 | /**
303 | * Checks if `value` is a global object.
304 | *
305 | * @private
306 | * @param {*} value The value to check.
307 | * @returns {null|Object} Returns `value` if it's a global object, else `null`.
308 | */
309 | function checkGlobal(value) {
310 | return (value && value.Object === Object) ? value : null;
311 | }
312 |
313 | /**
314 | * Checks if `value` is a host object in IE < 9.
315 | *
316 | * @private
317 | * @param {*} value The value to check.
318 | * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
319 | */
320 | function isHostObject(value) {
321 | // Many host objects are `Object` objects that can coerce to strings
322 | // despite having improperly defined `toString` methods.
323 | var result = false;
324 | if (value != null && typeof value.toString != 'function') {
325 | try {
326 | result = !!(value + '');
327 | } catch (e) {}
328 | }
329 | return result;
330 | }
331 |
332 | /**
333 | * Converts `iterator` to an array.
334 | *
335 | * @private
336 | * @param {Object} iterator The iterator to convert.
337 | * @returns {Array} Returns the converted array.
338 | */
339 | function iteratorToArray(iterator) {
340 | var data,
341 | result = [];
342 |
343 | while (!(data = iterator.next()).done) {
344 | result.push(data.value);
345 | }
346 | return result;
347 | }
348 |
349 | /**
350 | * Converts `map` to its key-value pairs.
351 | *
352 | * @private
353 | * @param {Object} map The map to convert.
354 | * @returns {Array} Returns the key-value pairs.
355 | */
356 | function mapToArray(map) {
357 | var index = -1,
358 | result = Array(map.size);
359 |
360 | map.forEach(function(value, key) {
361 | result[++index] = [key, value];
362 | });
363 | return result;
364 | }
365 |
366 | /**
367 | * Converts `set` to an array of its values.
368 | *
369 | * @private
370 | * @param {Object} set The set to convert.
371 | * @returns {Array} Returns the values.
372 | */
373 | function setToArray(set) {
374 | var index = -1,
375 | result = Array(set.size);
376 |
377 | set.forEach(function(value) {
378 | result[++index] = value;
379 | });
380 | return result;
381 | }
382 |
383 | /*--------------------------------------------------------------------------*/
384 |
385 | /** Used for built-in method references. */
386 | var arrayProto = Array.prototype,
387 | objectProto = Object.prototype;
388 |
389 | /** Used to resolve the decompiled source of functions. */
390 | var funcToString = Function.prototype.toString;
391 |
392 | /** Used to check objects for own properties. */
393 | var hasOwnProperty = objectProto.hasOwnProperty;
394 |
395 | /** Used to infer the `Object` constructor. */
396 | var objectCtorString = funcToString.call(Object);
397 |
398 | /**
399 | * Used to resolve the
400 | * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
401 | * of values.
402 | */
403 | var objectToString = objectProto.toString;
404 |
405 | /** Used to detect if a method is native. */
406 | var reIsNative = RegExp('^' +
407 | funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
408 | .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
409 | );
410 |
411 | /** Built-in value references. */
412 | var Buffer = moduleExports ? root.Buffer : undefined,
413 | Reflect = root.Reflect,
414 | Symbol = root.Symbol,
415 | Uint8Array = root.Uint8Array,
416 | enumerate = Reflect ? Reflect.enumerate : undefined,
417 | getOwnPropertySymbols = Object.getOwnPropertySymbols,
418 | objectCreate = Object.create,
419 | propertyIsEnumerable = objectProto.propertyIsEnumerable,
420 | splice = arrayProto.splice;
421 |
422 | /* Built-in method references for those with the same name as other `lodash` methods. */
423 | var nativeGetPrototype = Object.getPrototypeOf,
424 | nativeKeys = Object.keys,
425 | nativeMax = Math.max;
426 |
427 | /* Built-in method references that are verified to be native. */
428 | var DataView = getNative(root, 'DataView'),
429 | Map = getNative(root, 'Map'),
430 | Promise = getNative(root, 'Promise'),
431 | Set = getNative(root, 'Set'),
432 | WeakMap = getNative(root, 'WeakMap'),
433 | nativeCreate = getNative(Object, 'create');
434 |
435 | /** Used to lookup unminified function names. */
436 | var realNames = {};
437 |
438 | /** Used to detect maps, sets, and weakmaps. */
439 | var dataViewCtorString = toSource(DataView),
440 | mapCtorString = toSource(Map),
441 | promiseCtorString = toSource(Promise),
442 | setCtorString = toSource(Set),
443 | weakMapCtorString = toSource(WeakMap);
444 |
445 | /** Used to convert symbols to primitives and strings. */
446 | var symbolProto = Symbol ? Symbol.prototype : undefined,
447 | symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;
448 |
449 | /*------------------------------------------------------------------------*/
450 |
451 | /**
452 | * Creates a `lodash` object which wraps `value` to enable implicit method
453 | * chain sequences. Methods that operate on and return arrays, collections,
454 | * and functions can be chained together. Methods that retrieve a single value
455 | * or may return a primitive value will automatically end the chain sequence
456 | * and return the unwrapped value. Otherwise, the value must be unwrapped
457 | * with `_#value`.
458 | *
459 | * Explicit chain sequences, which must be unwrapped with `_#value`, may be
460 | * enabled using `_.chain`.
461 | *
462 | * The execution of chained methods is lazy, that is, it's deferred until
463 | * `_#value` is implicitly or explicitly called.
464 | *
465 | * Lazy evaluation allows several methods to support shortcut fusion.
466 | * Shortcut fusion is an optimization to merge iteratee calls; this avoids
467 | * the creation of intermediate arrays and can greatly reduce the number of
468 | * iteratee executions. Sections of a chain sequence qualify for shortcut
469 | * fusion if the section is applied to an array of at least `200` elements
470 | * and any iteratees accept only one argument. The heuristic for whether a
471 | * section qualifies for shortcut fusion is subject to change.
472 | *
473 | * Chaining is supported in custom builds as long as the `_#value` method is
474 | * directly or indirectly included in the build.
475 | *
476 | * In addition to lodash methods, wrappers have `Array` and `String` methods.
477 | *
478 | * The wrapper `Array` methods are:
479 | * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
480 | *
481 | * The wrapper `String` methods are:
482 | * `replace` and `split`
483 | *
484 | * The wrapper methods that support shortcut fusion are:
485 | * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
486 | * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
487 | * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
488 | *
489 | * The chainable wrapper methods are:
490 | * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
491 | * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
492 | * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
493 | * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
494 | * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
495 | * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
496 | * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
497 | * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
498 | * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
499 | * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
500 | * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
501 | * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
502 | * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
503 | * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
504 | * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
505 | * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
506 | * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
507 | * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
508 | * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
509 | * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
510 | * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
511 | * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
512 | * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
513 | * `zipObject`, `zipObjectDeep`, and `zipWith`
514 | *
515 | * The wrapper methods that are **not** chainable by default are:
516 | * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
517 | * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `deburr`, `divide`, `each`,
518 | * `eachRight`, `endsWith`, `eq`, `escape`, `escapeRegExp`, `every`, `find`,
519 | * `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `first`,
520 | * `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,
521 | * `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`,
522 | * `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`,
523 | * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`,
524 | * `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`,
525 | * `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`,
526 | * `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,
527 | * `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`,
528 | * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`,
529 | * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`,
530 | * `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, `min`, `minBy`, `multiply`,
531 | * `noConflict`, `noop`, `now`, `nth`, `pad`, `padEnd`, `padStart`, `parseInt`,
532 | * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`,
533 | * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
534 | * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`,
535 | * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toFinite`,
536 | * `toInteger`, `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`,
537 | * `toString`, `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,
538 | * `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words`
539 | *
540 | * @name _
541 | * @constructor
542 | * @category Seq
543 | * @param {*} value The value to wrap in a `lodash` instance.
544 | * @returns {Object} Returns the new `lodash` wrapper instance.
545 | * @example
546 | *
547 | * function square(n) {
548 | * return n * n;
549 | * }
550 | *
551 | * var wrapped = _([1, 2, 3]);
552 | *
553 | * // Returns an unwrapped value.
554 | * wrapped.reduce(_.add);
555 | * // => 6
556 | *
557 | * // Returns a wrapped value.
558 | * var squares = wrapped.map(square);
559 | *
560 | * _.isArray(squares);
561 | * // => false
562 | *
563 | * _.isArray(squares.value());
564 | * // => true
565 | */
566 | function lodash() {
567 | // No operation performed.
568 | }
569 |
570 | /*------------------------------------------------------------------------*/
571 |
572 | /**
573 | * Creates a hash object.
574 | *
575 | * @private
576 | * @constructor
577 | * @param {Array} [entries] The key-value pairs to cache.
578 | */
579 | function Hash(entries) {
580 | var index = -1,
581 | length = entries ? entries.length : 0;
582 |
583 | this.clear();
584 | while (++index < length) {
585 | var entry = entries[index];
586 | this.set(entry[0], entry[1]);
587 | }
588 | }
589 |
590 | /**
591 | * Removes all key-value entries from the hash.
592 | *
593 | * @private
594 | * @name clear
595 | * @memberOf Hash
596 | */
597 | function hashClear() {
598 | this.__data__ = nativeCreate ? nativeCreate(null) : {};
599 | }
600 |
601 | /**
602 | * Removes `key` and its value from the hash.
603 | *
604 | * @private
605 | * @name delete
606 | * @memberOf Hash
607 | * @param {Object} hash The hash to modify.
608 | * @param {string} key The key of the value to remove.
609 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
610 | */
611 | function hashDelete(key) {
612 | return this.has(key) && delete this.__data__[key];
613 | }
614 |
615 | /**
616 | * Gets the hash value for `key`.
617 | *
618 | * @private
619 | * @name get
620 | * @memberOf Hash
621 | * @param {string} key The key of the value to get.
622 | * @returns {*} Returns the entry value.
623 | */
624 | function hashGet(key) {
625 | var data = this.__data__;
626 | if (nativeCreate) {
627 | var result = data[key];
628 | return result === HASH_UNDEFINED ? undefined : result;
629 | }
630 | return hasOwnProperty.call(data, key) ? data[key] : undefined;
631 | }
632 |
633 | /**
634 | * Checks if a hash value for `key` exists.
635 | *
636 | * @private
637 | * @name has
638 | * @memberOf Hash
639 | * @param {string} key The key of the entry to check.
640 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
641 | */
642 | function hashHas(key) {
643 | var data = this.__data__;
644 | return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
645 | }
646 |
647 | /**
648 | * Sets the hash `key` to `value`.
649 | *
650 | * @private
651 | * @name set
652 | * @memberOf Hash
653 | * @param {string} key The key of the value to set.
654 | * @param {*} value The value to set.
655 | * @returns {Object} Returns the hash instance.
656 | */
657 | function hashSet(key, value) {
658 | var data = this.__data__;
659 | data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
660 | return this;
661 | }
662 |
663 | // Add methods to `Hash`.
664 | Hash.prototype.clear = hashClear;
665 | Hash.prototype['delete'] = hashDelete;
666 | Hash.prototype.get = hashGet;
667 | Hash.prototype.has = hashHas;
668 | Hash.prototype.set = hashSet;
669 |
670 | /*------------------------------------------------------------------------*/
671 |
672 | /**
673 | * Creates an list cache object.
674 | *
675 | * @private
676 | * @constructor
677 | * @param {Array} [entries] The key-value pairs to cache.
678 | */
679 | function ListCache(entries) {
680 | var index = -1,
681 | length = entries ? entries.length : 0;
682 |
683 | this.clear();
684 | while (++index < length) {
685 | var entry = entries[index];
686 | this.set(entry[0], entry[1]);
687 | }
688 | }
689 |
690 | /**
691 | * Removes all key-value entries from the list cache.
692 | *
693 | * @private
694 | * @name clear
695 | * @memberOf ListCache
696 | */
697 | function listCacheClear() {
698 | this.__data__ = [];
699 | }
700 |
701 | /**
702 | * Removes `key` and its value from the list cache.
703 | *
704 | * @private
705 | * @name delete
706 | * @memberOf ListCache
707 | * @param {string} key The key of the value to remove.
708 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
709 | */
710 | function listCacheDelete(key) {
711 | var data = this.__data__,
712 | index = assocIndexOf(data, key);
713 |
714 | if (index < 0) {
715 | return false;
716 | }
717 | var lastIndex = data.length - 1;
718 | if (index == lastIndex) {
719 | data.pop();
720 | } else {
721 | splice.call(data, index, 1);
722 | }
723 | return true;
724 | }
725 |
726 | /**
727 | * Gets the list cache value for `key`.
728 | *
729 | * @private
730 | * @name get
731 | * @memberOf ListCache
732 | * @param {string} key The key of the value to get.
733 | * @returns {*} Returns the entry value.
734 | */
735 | function listCacheGet(key) {
736 | var data = this.__data__,
737 | index = assocIndexOf(data, key);
738 |
739 | return index < 0 ? undefined : data[index][1];
740 | }
741 |
742 | /**
743 | * Checks if a list cache value for `key` exists.
744 | *
745 | * @private
746 | * @name has
747 | * @memberOf ListCache
748 | * @param {string} key The key of the entry to check.
749 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
750 | */
751 | function listCacheHas(key) {
752 | return assocIndexOf(this.__data__, key) > -1;
753 | }
754 |
755 | /**
756 | * Sets the list cache `key` to `value`.
757 | *
758 | * @private
759 | * @name set
760 | * @memberOf ListCache
761 | * @param {string} key The key of the value to set.
762 | * @param {*} value The value to set.
763 | * @returns {Object} Returns the list cache instance.
764 | */
765 | function listCacheSet(key, value) {
766 | var data = this.__data__,
767 | index = assocIndexOf(data, key);
768 |
769 | if (index < 0) {
770 | data.push([key, value]);
771 | } else {
772 | data[index][1] = value;
773 | }
774 | return this;
775 | }
776 |
777 | // Add methods to `ListCache`.
778 | ListCache.prototype.clear = listCacheClear;
779 | ListCache.prototype['delete'] = listCacheDelete;
780 | ListCache.prototype.get = listCacheGet;
781 | ListCache.prototype.has = listCacheHas;
782 | ListCache.prototype.set = listCacheSet;
783 |
784 | /*------------------------------------------------------------------------*/
785 |
786 | /**
787 | * Creates a map cache object to store key-value pairs.
788 | *
789 | * @private
790 | * @constructor
791 | * @param {Array} [entries] The key-value pairs to cache.
792 | */
793 | function MapCache(entries) {
794 | var index = -1,
795 | length = entries ? entries.length : 0;
796 |
797 | this.clear();
798 | while (++index < length) {
799 | var entry = entries[index];
800 | this.set(entry[0], entry[1]);
801 | }
802 | }
803 |
804 | /**
805 | * Removes all key-value entries from the map.
806 | *
807 | * @private
808 | * @name clear
809 | * @memberOf MapCache
810 | */
811 | function mapCacheClear() {
812 | this.__data__ = {
813 | 'hash': new Hash,
814 | 'map': new (Map || ListCache),
815 | 'string': new Hash
816 | };
817 | }
818 |
819 | /**
820 | * Removes `key` and its value from the map.
821 | *
822 | * @private
823 | * @name delete
824 | * @memberOf MapCache
825 | * @param {string} key The key of the value to remove.
826 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
827 | */
828 | function mapCacheDelete(key) {
829 | return getMapData(this, key)['delete'](key);
830 | }
831 |
832 | /**
833 | * Gets the map value for `key`.
834 | *
835 | * @private
836 | * @name get
837 | * @memberOf MapCache
838 | * @param {string} key The key of the value to get.
839 | * @returns {*} Returns the entry value.
840 | */
841 | function mapCacheGet(key) {
842 | return getMapData(this, key).get(key);
843 | }
844 |
845 | /**
846 | * Checks if a map value for `key` exists.
847 | *
848 | * @private
849 | * @name has
850 | * @memberOf MapCache
851 | * @param {string} key The key of the entry to check.
852 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
853 | */
854 | function mapCacheHas(key) {
855 | return getMapData(this, key).has(key);
856 | }
857 |
858 | /**
859 | * Sets the map `key` to `value`.
860 | *
861 | * @private
862 | * @name set
863 | * @memberOf MapCache
864 | * @param {string} key The key of the value to set.
865 | * @param {*} value The value to set.
866 | * @returns {Object} Returns the map cache instance.
867 | */
868 | function mapCacheSet(key, value) {
869 | getMapData(this, key).set(key, value);
870 | return this;
871 | }
872 |
873 | // Add methods to `MapCache`.
874 | MapCache.prototype.clear = mapCacheClear;
875 | MapCache.prototype['delete'] = mapCacheDelete;
876 | MapCache.prototype.get = mapCacheGet;
877 | MapCache.prototype.has = mapCacheHas;
878 | MapCache.prototype.set = mapCacheSet;
879 |
880 | /*------------------------------------------------------------------------*/
881 |
882 | /**
883 | * Creates a stack cache object to store key-value pairs.
884 | *
885 | * @private
886 | * @constructor
887 | * @param {Array} [entries] The key-value pairs to cache.
888 | */
889 | function Stack(entries) {
890 | this.__data__ = new ListCache(entries);
891 | }
892 |
893 | /**
894 | * Removes all key-value entries from the stack.
895 | *
896 | * @private
897 | * @name clear
898 | * @memberOf Stack
899 | */
900 | function stackClear() {
901 | this.__data__ = new ListCache;
902 | }
903 |
904 | /**
905 | * Removes `key` and its value from the stack.
906 | *
907 | * @private
908 | * @name delete
909 | * @memberOf Stack
910 | * @param {string} key The key of the value to remove.
911 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
912 | */
913 | function stackDelete(key) {
914 | return this.__data__['delete'](key);
915 | }
916 |
917 | /**
918 | * Gets the stack value for `key`.
919 | *
920 | * @private
921 | * @name get
922 | * @memberOf Stack
923 | * @param {string} key The key of the value to get.
924 | * @returns {*} Returns the entry value.
925 | */
926 | function stackGet(key) {
927 | return this.__data__.get(key);
928 | }
929 |
930 | /**
931 | * Checks if a stack value for `key` exists.
932 | *
933 | * @private
934 | * @name has
935 | * @memberOf Stack
936 | * @param {string} key The key of the entry to check.
937 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
938 | */
939 | function stackHas(key) {
940 | return this.__data__.has(key);
941 | }
942 |
943 | /**
944 | * Sets the stack `key` to `value`.
945 | *
946 | * @private
947 | * @name set
948 | * @memberOf Stack
949 | * @param {string} key The key of the value to set.
950 | * @param {*} value The value to set.
951 | * @returns {Object} Returns the stack cache instance.
952 | */
953 | function stackSet(key, value) {
954 | var cache = this.__data__;
955 | if (cache instanceof ListCache && cache.__data__.length == LARGE_ARRAY_SIZE) {
956 | cache = this.__data__ = new MapCache(cache.__data__);
957 | }
958 | cache.set(key, value);
959 | return this;
960 | }
961 |
962 | // Add methods to `Stack`.
963 | Stack.prototype.clear = stackClear;
964 | Stack.prototype['delete'] = stackDelete;
965 | Stack.prototype.get = stackGet;
966 | Stack.prototype.has = stackHas;
967 | Stack.prototype.set = stackSet;
968 |
969 | /*------------------------------------------------------------------------*/
970 |
971 | /**
972 | * This function is like `assignValue` except that it doesn't assign
973 | * `undefined` values.
974 | *
975 | * @private
976 | * @param {Object} object The object to modify.
977 | * @param {string} key The key of the property to assign.
978 | * @param {*} value The value to assign.
979 | */
980 | function assignMergeValue(object, key, value) {
981 | if ((value !== undefined && !eq(object[key], value)) ||
982 | (typeof key == 'number' && value === undefined && !(key in object))) {
983 | object[key] = value;
984 | }
985 | }
986 |
987 | /**
988 | * Assigns `value` to `key` of `object` if the existing value is not equivalent
989 | * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
990 | * for equality comparisons.
991 | *
992 | * @private
993 | * @param {Object} object The object to modify.
994 | * @param {string} key The key of the property to assign.
995 | * @param {*} value The value to assign.
996 | */
997 | function assignValue(object, key, value) {
998 | var objValue = object[key];
999 | if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
1000 | (value === undefined && !(key in object))) {
1001 | object[key] = value;
1002 | }
1003 | }
1004 |
1005 | /**
1006 | * Gets the index at which the `key` is found in `array` of key-value pairs.
1007 | *
1008 | * @private
1009 | * @param {Array} array The array to search.
1010 | * @param {*} key The key to search for.
1011 | * @returns {number} Returns the index of the matched value, else `-1`.
1012 | */
1013 | function assocIndexOf(array, key) {
1014 | var length = array.length;
1015 | while (length--) {
1016 | if (eq(array[length][0], key)) {
1017 | return length;
1018 | }
1019 | }
1020 | return -1;
1021 | }
1022 |
1023 | /**
1024 | * The base implementation of `_.assign` without support for multiple sources
1025 | * or `customizer` functions.
1026 | *
1027 | * @private
1028 | * @param {Object} object The destination object.
1029 | * @param {Object} source The source object.
1030 | * @returns {Object} Returns `object`.
1031 | */
1032 | function baseAssign(object, source) {
1033 | return object && copyObject(source, keys(source), object);
1034 | }
1035 |
1036 | /**
1037 | * The base implementation of `_.clone` and `_.cloneDeep` which tracks
1038 | * traversed objects.
1039 | *
1040 | * @private
1041 | * @param {*} value The value to clone.
1042 | * @param {boolean} [isDeep] Specify a deep clone.
1043 | * @param {boolean} [isFull] Specify a clone including symbols.
1044 | * @param {Function} [customizer] The function to customize cloning.
1045 | * @param {string} [key] The key of `value`.
1046 | * @param {Object} [object] The parent object of `value`.
1047 | * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
1048 | * @returns {*} Returns the cloned value.
1049 | */
1050 | function baseClone(value, isDeep, isFull, customizer, key, object, stack) {
1051 | var result;
1052 | if (customizer) {
1053 | result = object ? customizer(value, key, object, stack) : customizer(value);
1054 | }
1055 | if (result !== undefined) {
1056 | return result;
1057 | }
1058 | if (!isObject(value)) {
1059 | return value;
1060 | }
1061 | var isArr = isArray(value);
1062 | if (isArr) {
1063 | result = initCloneArray(value);
1064 | if (!isDeep) {
1065 | return copyArray(value, result);
1066 | }
1067 | } else {
1068 | var tag = getTag(value),
1069 | isFunc = tag == funcTag || tag == genTag;
1070 |
1071 | if (isBuffer(value)) {
1072 | return cloneBuffer(value, isDeep);
1073 | }
1074 | if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
1075 | if (isHostObject(value)) {
1076 | return object ? value : {};
1077 | }
1078 | result = initCloneObject(isFunc ? {} : value);
1079 | if (!isDeep) {
1080 | return copySymbols(value, baseAssign(result, value));
1081 | }
1082 | } else {
1083 | if (!cloneableTags[tag]) {
1084 | return object ? value : {};
1085 | }
1086 | result = initCloneByTag(value, tag, baseClone, isDeep);
1087 | }
1088 | }
1089 | // Check for circular references and return its corresponding clone.
1090 | stack || (stack = new Stack);
1091 | var stacked = stack.get(value);
1092 | if (stacked) {
1093 | return stacked;
1094 | }
1095 | stack.set(value, result);
1096 |
1097 | if (!isArr) {
1098 | var props = isFull ? getAllKeys(value) : keys(value);
1099 | }
1100 | // Recursively populate clone (susceptible to call stack limits).
1101 | arrayEach(props || value, function(subValue, key) {
1102 | if (props) {
1103 | key = subValue;
1104 | subValue = value[key];
1105 | }
1106 | assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));
1107 | });
1108 | return result;
1109 | }
1110 |
1111 | /**
1112 | * The base implementation of `_.create` without support for assigning
1113 | * properties to the created object.
1114 | *
1115 | * @private
1116 | * @param {Object} prototype The object to inherit from.
1117 | * @returns {Object} Returns the new object.
1118 | */
1119 | function baseCreate(proto) {
1120 | return isObject(proto) ? objectCreate(proto) : {};
1121 | }
1122 |
1123 | /**
1124 | * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
1125 | * `keysFunc` and `symbolsFunc` to get the enumerable property names and
1126 | * symbols of `object`.
1127 | *
1128 | * @private
1129 | * @param {Object} object The object to query.
1130 | * @param {Function} keysFunc The function to get the keys of `object`.
1131 | * @param {Function} symbolsFunc The function to get the symbols of `object`.
1132 | * @returns {Array} Returns the array of property names and symbols.
1133 | */
1134 | function baseGetAllKeys(object, keysFunc, symbolsFunc) {
1135 | var result = keysFunc(object);
1136 | return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
1137 | }
1138 |
1139 | /**
1140 | * The base implementation of `_.has` without support for deep paths.
1141 | *
1142 | * @private
1143 | * @param {Object} object The object to query.
1144 | * @param {Array|string} key The key to check.
1145 | * @returns {boolean} Returns `true` if `key` exists, else `false`.
1146 | */
1147 | function baseHas(object, key) {
1148 | // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`,
1149 | // that are composed entirely of index properties, return `false` for
1150 | // `hasOwnProperty` checks of them.
1151 | return hasOwnProperty.call(object, key) ||
1152 | (typeof object == 'object' && key in object && getPrototype(object) === null);
1153 | }
1154 |
1155 | /**
1156 | * The base implementation of `_.keys` which doesn't skip the constructor
1157 | * property of prototypes or treat sparse arrays as dense.
1158 | *
1159 | * @private
1160 | * @param {Object} object The object to query.
1161 | * @returns {Array} Returns the array of property names.
1162 | */
1163 | function baseKeys(object) {
1164 | return nativeKeys(Object(object));
1165 | }
1166 |
1167 | /**
1168 | * The base implementation of `_.keysIn` which doesn't skip the constructor
1169 | * property of prototypes or treat sparse arrays as dense.
1170 | *
1171 | * @private
1172 | * @param {Object} object The object to query.
1173 | * @returns {Array} Returns the array of property names.
1174 | */
1175 | function baseKeysIn(object) {
1176 | object = object == null ? object : Object(object);
1177 |
1178 | var result = [];
1179 | for (var key in object) {
1180 | result.push(key);
1181 | }
1182 | return result;
1183 | }
1184 |
1185 | // Fallback for IE < 9 with es6-shim.
1186 | if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {
1187 | baseKeysIn = function(object) {
1188 | return iteratorToArray(enumerate(object));
1189 | };
1190 | }
1191 |
1192 | /**
1193 | * The base implementation of `_.merge` without support for multiple sources.
1194 | *
1195 | * @private
1196 | * @param {Object} object The destination object.
1197 | * @param {Object} source The source object.
1198 | * @param {number} srcIndex The index of `source`.
1199 | * @param {Function} [customizer] The function to customize merged values.
1200 | * @param {Object} [stack] Tracks traversed source values and their merged
1201 | * counterparts.
1202 | */
1203 | function baseMerge(object, source, srcIndex, customizer, stack) {
1204 | if (object === source) {
1205 | return;
1206 | }
1207 | if (!(isArray(source) || isTypedArray(source))) {
1208 | var props = keysIn(source);
1209 | }
1210 | arrayEach(props || source, function(srcValue, key) {
1211 | if (props) {
1212 | key = srcValue;
1213 | srcValue = source[key];
1214 | }
1215 | if (isObject(srcValue)) {
1216 | stack || (stack = new Stack);
1217 | baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
1218 | }
1219 | else {
1220 | var newValue = customizer
1221 | ? customizer(object[key], srcValue, (key + ''), object, source, stack)
1222 | : undefined;
1223 |
1224 | if (newValue === undefined) {
1225 | newValue = srcValue;
1226 | }
1227 | assignMergeValue(object, key, newValue);
1228 | }
1229 | });
1230 | }
1231 |
1232 | /**
1233 | * A specialized version of `baseMerge` for arrays and objects which performs
1234 | * deep merges and tracks traversed objects enabling objects with circular
1235 | * references to be merged.
1236 | *
1237 | * @private
1238 | * @param {Object} object The destination object.
1239 | * @param {Object} source The source object.
1240 | * @param {string} key The key of the value to merge.
1241 | * @param {number} srcIndex The index of `source`.
1242 | * @param {Function} mergeFunc The function to merge values.
1243 | * @param {Function} [customizer] The function to customize assigned values.
1244 | * @param {Object} [stack] Tracks traversed source values and their merged
1245 | * counterparts.
1246 | */
1247 | function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
1248 | var objValue = object[key],
1249 | srcValue = source[key],
1250 | stacked = stack.get(srcValue);
1251 |
1252 | if (stacked) {
1253 | assignMergeValue(object, key, stacked);
1254 | return;
1255 | }
1256 | var newValue = customizer
1257 | ? customizer(objValue, srcValue, (key + ''), object, source, stack)
1258 | : undefined;
1259 |
1260 | var isCommon = newValue === undefined;
1261 |
1262 | if (isCommon) {
1263 | newValue = srcValue;
1264 | if (isArray(srcValue) || isTypedArray(srcValue)) {
1265 | if (isArray(objValue)) {
1266 | newValue = objValue;
1267 | }
1268 | else if (isArrayLikeObject(objValue)) {
1269 | newValue = copyArray(objValue);
1270 | }
1271 | else {
1272 | isCommon = false;
1273 | newValue = baseClone(srcValue, true);
1274 | }
1275 | }
1276 | else if (isPlainObject(srcValue) || isArguments(srcValue)) {
1277 | if (isArguments(objValue)) {
1278 | newValue = toPlainObject(objValue);
1279 | }
1280 | else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
1281 | isCommon = false;
1282 | newValue = baseClone(srcValue, true);
1283 | }
1284 | else {
1285 | newValue = objValue;
1286 | }
1287 | }
1288 | else {
1289 | isCommon = false;
1290 | }
1291 | }
1292 | stack.set(srcValue, newValue);
1293 |
1294 | if (isCommon) {
1295 | // Recursively merge objects and arrays (susceptible to call stack limits).
1296 | mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
1297 | }
1298 | stack['delete'](srcValue);
1299 | assignMergeValue(object, key, newValue);
1300 | }
1301 |
1302 | /**
1303 | * The base implementation of `_.property` without support for deep paths.
1304 | *
1305 | * @private
1306 | * @param {string} key The key of the property to get.
1307 | * @returns {Function} Returns the new accessor function.
1308 | */
1309 | function baseProperty(key) {
1310 | return function(object) {
1311 | return object == null ? undefined : object[key];
1312 | };
1313 | }
1314 |
1315 | /**
1316 | * Creates a clone of `buffer`.
1317 | *
1318 | * @private
1319 | * @param {Buffer} buffer The buffer to clone.
1320 | * @param {boolean} [isDeep] Specify a deep clone.
1321 | * @returns {Buffer} Returns the cloned buffer.
1322 | */
1323 | function cloneBuffer(buffer, isDeep) {
1324 | if (isDeep) {
1325 | return buffer.slice();
1326 | }
1327 | var result = new buffer.constructor(buffer.length);
1328 | buffer.copy(result);
1329 | return result;
1330 | }
1331 |
1332 | /**
1333 | * Creates a clone of `arrayBuffer`.
1334 | *
1335 | * @private
1336 | * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
1337 | * @returns {ArrayBuffer} Returns the cloned array buffer.
1338 | */
1339 | function cloneArrayBuffer(arrayBuffer) {
1340 | var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
1341 | new Uint8Array(result).set(new Uint8Array(arrayBuffer));
1342 | return result;
1343 | }
1344 |
1345 | /**
1346 | * Creates a clone of `dataView`.
1347 | *
1348 | * @private
1349 | * @param {Object} dataView The data view to clone.
1350 | * @param {boolean} [isDeep] Specify a deep clone.
1351 | * @returns {Object} Returns the cloned data view.
1352 | */
1353 | function cloneDataView(dataView, isDeep) {
1354 | var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
1355 | return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
1356 | }
1357 |
1358 | /**
1359 | * Creates a clone of `map`.
1360 | *
1361 | * @private
1362 | * @param {Object} map The map to clone.
1363 | * @param {Function} cloneFunc The function to clone values.
1364 | * @param {boolean} [isDeep] Specify a deep clone.
1365 | * @returns {Object} Returns the cloned map.
1366 | */
1367 | function cloneMap(map, isDeep, cloneFunc) {
1368 | var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);
1369 | return arrayReduce(array, addMapEntry, new map.constructor);
1370 | }
1371 |
1372 | /**
1373 | * Creates a clone of `regexp`.
1374 | *
1375 | * @private
1376 | * @param {Object} regexp The regexp to clone.
1377 | * @returns {Object} Returns the cloned regexp.
1378 | */
1379 | function cloneRegExp(regexp) {
1380 | var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
1381 | result.lastIndex = regexp.lastIndex;
1382 | return result;
1383 | }
1384 |
1385 | /**
1386 | * Creates a clone of `set`.
1387 | *
1388 | * @private
1389 | * @param {Object} set The set to clone.
1390 | * @param {Function} cloneFunc The function to clone values.
1391 | * @param {boolean} [isDeep] Specify a deep clone.
1392 | * @returns {Object} Returns the cloned set.
1393 | */
1394 | function cloneSet(set, isDeep, cloneFunc) {
1395 | var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);
1396 | return arrayReduce(array, addSetEntry, new set.constructor);
1397 | }
1398 |
1399 | /**
1400 | * Creates a clone of the `symbol` object.
1401 | *
1402 | * @private
1403 | * @param {Object} symbol The symbol object to clone.
1404 | * @returns {Object} Returns the cloned symbol object.
1405 | */
1406 | function cloneSymbol(symbol) {
1407 | return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
1408 | }
1409 |
1410 | /**
1411 | * Creates a clone of `typedArray`.
1412 | *
1413 | * @private
1414 | * @param {Object} typedArray The typed array to clone.
1415 | * @param {boolean} [isDeep] Specify a deep clone.
1416 | * @returns {Object} Returns the cloned typed array.
1417 | */
1418 | function cloneTypedArray(typedArray, isDeep) {
1419 | var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
1420 | return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
1421 | }
1422 |
1423 | /**
1424 | * Copies the values of `source` to `array`.
1425 | *
1426 | * @private
1427 | * @param {Array} source The array to copy values from.
1428 | * @param {Array} [array=[]] The array to copy values to.
1429 | * @returns {Array} Returns `array`.
1430 | */
1431 | function copyArray(source, array) {
1432 | var index = -1,
1433 | length = source.length;
1434 |
1435 | array || (array = Array(length));
1436 | while (++index < length) {
1437 | array[index] = source[index];
1438 | }
1439 | return array;
1440 | }
1441 |
1442 | /**
1443 | * Copies properties of `source` to `object`.
1444 | *
1445 | * @private
1446 | * @param {Object} source The object to copy properties from.
1447 | * @param {Array} props The property identifiers to copy.
1448 | * @param {Object} [object={}] The object to copy properties to.
1449 | * @param {Function} [customizer] The function to customize copied values.
1450 | * @returns {Object} Returns `object`.
1451 | */
1452 | function copyObject(source, props, object, customizer) {
1453 | object || (object = {});
1454 |
1455 | var index = -1,
1456 | length = props.length;
1457 |
1458 | while (++index < length) {
1459 | var key = props[index];
1460 |
1461 | var newValue = customizer
1462 | ? customizer(object[key], source[key], key, object, source)
1463 | : source[key];
1464 |
1465 | assignValue(object, key, newValue);
1466 | }
1467 | return object;
1468 | }
1469 |
1470 | /**
1471 | * Copies own symbol properties of `source` to `object`.
1472 | *
1473 | * @private
1474 | * @param {Object} source The object to copy symbols from.
1475 | * @param {Object} [object={}] The object to copy symbols to.
1476 | * @returns {Object} Returns `object`.
1477 | */
1478 | function copySymbols(source, object) {
1479 | return copyObject(source, getSymbols(source), object);
1480 | }
1481 |
1482 | /**
1483 | * Creates a function like `_.assign`.
1484 | *
1485 | * @private
1486 | * @param {Function} assigner The function to assign values.
1487 | * @returns {Function} Returns the new assigner function.
1488 | */
1489 | function createAssigner(assigner) {
1490 | return rest(function(object, sources) {
1491 | var index = -1,
1492 | length = sources.length,
1493 | customizer = length > 1 ? sources[length - 1] : undefined,
1494 | guard = length > 2 ? sources[2] : undefined;
1495 |
1496 | customizer = (assigner.length > 3 && typeof customizer == 'function')
1497 | ? (length--, customizer)
1498 | : undefined;
1499 |
1500 | if (guard && isIterateeCall(sources[0], sources[1], guard)) {
1501 | customizer = length < 3 ? undefined : customizer;
1502 | length = 1;
1503 | }
1504 | object = Object(object);
1505 | while (++index < length) {
1506 | var source = sources[index];
1507 | if (source) {
1508 | assigner(object, source, index, customizer);
1509 | }
1510 | }
1511 | return object;
1512 | });
1513 | }
1514 |
1515 | /**
1516 | * Creates an array of own enumerable property names and symbols of `object`.
1517 | *
1518 | * @private
1519 | * @param {Object} object The object to query.
1520 | * @returns {Array} Returns the array of property names and symbols.
1521 | */
1522 | function getAllKeys(object) {
1523 | return baseGetAllKeys(object, keys, getSymbols);
1524 | }
1525 |
1526 | /**
1527 | * Gets the "length" property value of `object`.
1528 | *
1529 | * **Note:** This function is used to avoid a
1530 | * [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects
1531 | * Safari on at least iOS 8.1-8.3 ARM64.
1532 | *
1533 | * @private
1534 | * @param {Object} object The object to query.
1535 | * @returns {*} Returns the "length" value.
1536 | */
1537 | var getLength = baseProperty('length');
1538 |
1539 | /**
1540 | * Gets the data for `map`.
1541 | *
1542 | * @private
1543 | * @param {Object} map The map to query.
1544 | * @param {string} key The reference key.
1545 | * @returns {*} Returns the map data.
1546 | */
1547 | function getMapData(map, key) {
1548 | var data = map.__data__;
1549 | return isKeyable(key)
1550 | ? data[typeof key == 'string' ? 'string' : 'hash']
1551 | : data.map;
1552 | }
1553 |
1554 | /**
1555 | * Gets the native function at `key` of `object`.
1556 | *
1557 | * @private
1558 | * @param {Object} object The object to query.
1559 | * @param {string} key The key of the method to get.
1560 | * @returns {*} Returns the function if it's native, else `undefined`.
1561 | */
1562 | function getNative(object, key) {
1563 | var value = object[key];
1564 | return isNative(value) ? value : undefined;
1565 | }
1566 |
1567 | /**
1568 | * Gets the `[[Prototype]]` of `value`.
1569 | *
1570 | * @private
1571 | * @param {*} value The value to query.
1572 | * @returns {null|Object} Returns the `[[Prototype]]`.
1573 | */
1574 | function getPrototype(value) {
1575 | return nativeGetPrototype(Object(value));
1576 | }
1577 |
1578 | /**
1579 | * Creates an array of the own enumerable symbol properties of `object`.
1580 | *
1581 | * @private
1582 | * @param {Object} object The object to query.
1583 | * @returns {Array} Returns the array of symbols.
1584 | */
1585 | function getSymbols(object) {
1586 | // Coerce `object` to an object to avoid non-object errors in V8.
1587 | // See https://bugs.chromium.org/p/v8/issues/detail?id=3443 for more details.
1588 | return getOwnPropertySymbols(Object(object));
1589 | }
1590 |
1591 | // Fallback for IE < 11.
1592 | if (!getOwnPropertySymbols) {
1593 | getSymbols = function() {
1594 | return [];
1595 | };
1596 | }
1597 |
1598 | /**
1599 | * Gets the `toStringTag` of `value`.
1600 | *
1601 | * @private
1602 | * @param {*} value The value to query.
1603 | * @returns {string} Returns the `toStringTag`.
1604 | */
1605 | function getTag(value) {
1606 | return objectToString.call(value);
1607 | }
1608 |
1609 | // Fallback for data views, maps, sets, and weak maps in IE 11,
1610 | // for data views in Edge, and promises in Node.js.
1611 | if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
1612 | (Map && getTag(new Map) != mapTag) ||
1613 | (Promise && getTag(Promise.resolve()) != promiseTag) ||
1614 | (Set && getTag(new Set) != setTag) ||
1615 | (WeakMap && getTag(new WeakMap) != weakMapTag)) {
1616 | getTag = function(value) {
1617 | var result = objectToString.call(value),
1618 | Ctor = result == objectTag ? value.constructor : undefined,
1619 | ctorString = Ctor ? toSource(Ctor) : undefined;
1620 |
1621 | if (ctorString) {
1622 | switch (ctorString) {
1623 | case dataViewCtorString: return dataViewTag;
1624 | case mapCtorString: return mapTag;
1625 | case promiseCtorString: return promiseTag;
1626 | case setCtorString: return setTag;
1627 | case weakMapCtorString: return weakMapTag;
1628 | }
1629 | }
1630 | return result;
1631 | };
1632 | }
1633 |
1634 | /**
1635 | * Initializes an array clone.
1636 | *
1637 | * @private
1638 | * @param {Array} array The array to clone.
1639 | * @returns {Array} Returns the initialized clone.
1640 | */
1641 | function initCloneArray(array) {
1642 | var length = array.length,
1643 | result = array.constructor(length);
1644 |
1645 | // Add properties assigned by `RegExp#exec`.
1646 | if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
1647 | result.index = array.index;
1648 | result.input = array.input;
1649 | }
1650 | return result;
1651 | }
1652 |
1653 | /**
1654 | * Initializes an object clone.
1655 | *
1656 | * @private
1657 | * @param {Object} object The object to clone.
1658 | * @returns {Object} Returns the initialized clone.
1659 | */
1660 | function initCloneObject(object) {
1661 | return (typeof object.constructor == 'function' && !isPrototype(object))
1662 | ? baseCreate(getPrototype(object))
1663 | : {};
1664 | }
1665 |
1666 | /**
1667 | * Initializes an object clone based on its `toStringTag`.
1668 | *
1669 | * **Note:** This function only supports cloning values with tags of
1670 | * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
1671 | *
1672 | * @private
1673 | * @param {Object} object The object to clone.
1674 | * @param {string} tag The `toStringTag` of the object to clone.
1675 | * @param {Function} cloneFunc The function to clone values.
1676 | * @param {boolean} [isDeep] Specify a deep clone.
1677 | * @returns {Object} Returns the initialized clone.
1678 | */
1679 | function initCloneByTag(object, tag, cloneFunc, isDeep) {
1680 | var Ctor = object.constructor;
1681 | switch (tag) {
1682 | case arrayBufferTag:
1683 | return cloneArrayBuffer(object);
1684 |
1685 | case boolTag:
1686 | case dateTag:
1687 | return new Ctor(+object);
1688 |
1689 | case dataViewTag:
1690 | return cloneDataView(object, isDeep);
1691 |
1692 | case float32Tag: case float64Tag:
1693 | case int8Tag: case int16Tag: case int32Tag:
1694 | case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
1695 | return cloneTypedArray(object, isDeep);
1696 |
1697 | case mapTag:
1698 | return cloneMap(object, isDeep, cloneFunc);
1699 |
1700 | case numberTag:
1701 | case stringTag:
1702 | return new Ctor(object);
1703 |
1704 | case regexpTag:
1705 | return cloneRegExp(object);
1706 |
1707 | case setTag:
1708 | return cloneSet(object, isDeep, cloneFunc);
1709 |
1710 | case symbolTag:
1711 | return cloneSymbol(object);
1712 | }
1713 | }
1714 |
1715 | /**
1716 | * Creates an array of index keys for `object` values of arrays,
1717 | * `arguments` objects, and strings, otherwise `null` is returned.
1718 | *
1719 | * @private
1720 | * @param {Object} object The object to query.
1721 | * @returns {Array|null} Returns index keys, else `null`.
1722 | */
1723 | function indexKeys(object) {
1724 | var length = object ? object.length : undefined;
1725 | if (isLength(length) &&
1726 | (isArray(object) || isString(object) || isArguments(object))) {
1727 | return baseTimes(length, String);
1728 | }
1729 | return null;
1730 | }
1731 |
1732 | /**
1733 | * Checks if `value` is a valid array-like index.
1734 | *
1735 | * @private
1736 | * @param {*} value The value to check.
1737 | * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
1738 | * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
1739 | */
1740 | function isIndex(value, length) {
1741 | length = length == null ? MAX_SAFE_INTEGER : length;
1742 | return !!length &&
1743 | (typeof value == 'number' || reIsUint.test(value)) &&
1744 | (value > -1 && value % 1 == 0 && value < length);
1745 | }
1746 |
1747 | /**
1748 | * Checks if the given arguments are from an iteratee call.
1749 | *
1750 | * @private
1751 | * @param {*} value The potential iteratee value argument.
1752 | * @param {*} index The potential iteratee index or key argument.
1753 | * @param {*} object The potential iteratee object argument.
1754 | * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
1755 | * else `false`.
1756 | */
1757 | function isIterateeCall(value, index, object) {
1758 | if (!isObject(object)) {
1759 | return false;
1760 | }
1761 | var type = typeof index;
1762 | if (type == 'number'
1763 | ? (isArrayLike(object) && isIndex(index, object.length))
1764 | : (type == 'string' && index in object)
1765 | ) {
1766 | return eq(object[index], value);
1767 | }
1768 | return false;
1769 | }
1770 |
1771 | /**
1772 | * Checks if `value` is suitable for use as unique object key.
1773 | *
1774 | * @private
1775 | * @param {*} value The value to check.
1776 | * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
1777 | */
1778 | function isKeyable(value) {
1779 | var type = typeof value;
1780 | return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
1781 | ? (value !== '__proto__')
1782 | : (value === null);
1783 | }
1784 |
1785 | /**
1786 | * Checks if `value` is likely a prototype object.
1787 | *
1788 | * @private
1789 | * @param {*} value The value to check.
1790 | * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
1791 | */
1792 | function isPrototype(value) {
1793 | var Ctor = value && value.constructor,
1794 | proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
1795 |
1796 | return value === proto;
1797 | }
1798 |
1799 | /**
1800 | * Converts `func` to its source code.
1801 | *
1802 | * @private
1803 | * @param {Function} func The function to process.
1804 | * @returns {string} Returns the source code.
1805 | */
1806 | function toSource(func) {
1807 | if (func != null) {
1808 | try {
1809 | return funcToString.call(func);
1810 | } catch (e) {}
1811 | try {
1812 | return (func + '');
1813 | } catch (e) {}
1814 | }
1815 | return '';
1816 | }
1817 |
1818 | /*------------------------------------------------------------------------*/
1819 |
1820 | /**
1821 | * Creates a function that invokes `func` with the `this` binding of the
1822 | * created function and arguments from `start` and beyond provided as
1823 | * an array.
1824 | *
1825 | * **Note:** This method is based on the
1826 | * [rest parameter](https://mdn.io/rest_parameters).
1827 | *
1828 | * @static
1829 | * @memberOf _
1830 | * @since 4.0.0
1831 | * @category Function
1832 | * @param {Function} func The function to apply a rest parameter to.
1833 | * @param {number} [start=func.length-1] The start position of the rest parameter.
1834 | * @returns {Function} Returns the new function.
1835 | * @example
1836 | *
1837 | * var say = _.rest(function(what, names) {
1838 | * return what + ' ' + _.initial(names).join(', ') +
1839 | * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
1840 | * });
1841 | *
1842 | * say('hello', 'fred', 'barney', 'pebbles');
1843 | * // => 'hello fred, barney, & pebbles'
1844 | */
1845 | function rest(func, start) {
1846 | if (typeof func != 'function') {
1847 | throw new TypeError(FUNC_ERROR_TEXT);
1848 | }
1849 | start = nativeMax(start === undefined ? (func.length - 1) : toInteger(start), 0);
1850 | return function() {
1851 | var args = arguments,
1852 | index = -1,
1853 | length = nativeMax(args.length - start, 0),
1854 | array = Array(length);
1855 |
1856 | while (++index < length) {
1857 | array[index] = args[start + index];
1858 | }
1859 | switch (start) {
1860 | case 0: return func.call(this, array);
1861 | case 1: return func.call(this, args[0], array);
1862 | case 2: return func.call(this, args[0], args[1], array);
1863 | }
1864 | var otherArgs = Array(start + 1);
1865 | index = -1;
1866 | while (++index < start) {
1867 | otherArgs[index] = args[index];
1868 | }
1869 | otherArgs[start] = array;
1870 | return apply(func, this, otherArgs);
1871 | };
1872 | }
1873 |
1874 | /*------------------------------------------------------------------------*/
1875 |
1876 | /**
1877 | * This method is like `_.clone` except that it recursively clones `value`.
1878 | *
1879 | * @static
1880 | * @memberOf _
1881 | * @since 1.0.0
1882 | * @category Lang
1883 | * @param {*} value The value to recursively clone.
1884 | * @returns {*} Returns the deep cloned value.
1885 | * @see _.clone
1886 | * @example
1887 | *
1888 | * var objects = [{ 'a': 1 }, { 'b': 2 }];
1889 | *
1890 | * var deep = _.cloneDeep(objects);
1891 | * console.log(deep[0] === objects[0]);
1892 | * // => false
1893 | */
1894 | function cloneDeep(value) {
1895 | return baseClone(value, true, true);
1896 | }
1897 |
1898 | /**
1899 | * Performs a
1900 | * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
1901 | * comparison between two values to determine if they are equivalent.
1902 | *
1903 | * @static
1904 | * @memberOf _
1905 | * @since 4.0.0
1906 | * @category Lang
1907 | * @param {*} value The value to compare.
1908 | * @param {*} other The other value to compare.
1909 | * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
1910 | * @example
1911 | *
1912 | * var object = { 'user': 'fred' };
1913 | * var other = { 'user': 'fred' };
1914 | *
1915 | * _.eq(object, object);
1916 | * // => true
1917 | *
1918 | * _.eq(object, other);
1919 | * // => false
1920 | *
1921 | * _.eq('a', 'a');
1922 | * // => true
1923 | *
1924 | * _.eq('a', Object('a'));
1925 | * // => false
1926 | *
1927 | * _.eq(NaN, NaN);
1928 | * // => true
1929 | */
1930 | function eq(value, other) {
1931 | return value === other || (value !== value && other !== other);
1932 | }
1933 |
1934 | /**
1935 | * Checks if `value` is likely an `arguments` object.
1936 | *
1937 | * @static
1938 | * @memberOf _
1939 | * @since 0.1.0
1940 | * @category Lang
1941 | * @param {*} value The value to check.
1942 | * @returns {boolean} Returns `true` if `value` is correctly classified,
1943 | * else `false`.
1944 | * @example
1945 | *
1946 | * _.isArguments(function() { return arguments; }());
1947 | * // => true
1948 | *
1949 | * _.isArguments([1, 2, 3]);
1950 | * // => false
1951 | */
1952 | function isArguments(value) {
1953 | // Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode.
1954 | return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
1955 | (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
1956 | }
1957 |
1958 | /**
1959 | * Checks if `value` is classified as an `Array` object.
1960 | *
1961 | * @static
1962 | * @memberOf _
1963 | * @since 0.1.0
1964 | * @type {Function}
1965 | * @category Lang
1966 | * @param {*} value The value to check.
1967 | * @returns {boolean} Returns `true` if `value` is correctly classified,
1968 | * else `false`.
1969 | * @example
1970 | *
1971 | * _.isArray([1, 2, 3]);
1972 | * // => true
1973 | *
1974 | * _.isArray(document.body.children);
1975 | * // => false
1976 | *
1977 | * _.isArray('abc');
1978 | * // => false
1979 | *
1980 | * _.isArray(_.noop);
1981 | * // => false
1982 | */
1983 | var isArray = Array.isArray;
1984 |
1985 | /**
1986 | * Checks if `value` is array-like. A value is considered array-like if it's
1987 | * not a function and has a `value.length` that's an integer greater than or
1988 | * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
1989 | *
1990 | * @static
1991 | * @memberOf _
1992 | * @since 4.0.0
1993 | * @category Lang
1994 | * @param {*} value The value to check.
1995 | * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
1996 | * @example
1997 | *
1998 | * _.isArrayLike([1, 2, 3]);
1999 | * // => true
2000 | *
2001 | * _.isArrayLike(document.body.children);
2002 | * // => true
2003 | *
2004 | * _.isArrayLike('abc');
2005 | * // => true
2006 | *
2007 | * _.isArrayLike(_.noop);
2008 | * // => false
2009 | */
2010 | function isArrayLike(value) {
2011 | return value != null && isLength(getLength(value)) && !isFunction(value);
2012 | }
2013 |
2014 | /**
2015 | * This method is like `_.isArrayLike` except that it also checks if `value`
2016 | * is an object.
2017 | *
2018 | * @static
2019 | * @memberOf _
2020 | * @since 4.0.0
2021 | * @category Lang
2022 | * @param {*} value The value to check.
2023 | * @returns {boolean} Returns `true` if `value` is an array-like object,
2024 | * else `false`.
2025 | * @example
2026 | *
2027 | * _.isArrayLikeObject([1, 2, 3]);
2028 | * // => true
2029 | *
2030 | * _.isArrayLikeObject(document.body.children);
2031 | * // => true
2032 | *
2033 | * _.isArrayLikeObject('abc');
2034 | * // => false
2035 | *
2036 | * _.isArrayLikeObject(_.noop);
2037 | * // => false
2038 | */
2039 | function isArrayLikeObject(value) {
2040 | return isObjectLike(value) && isArrayLike(value);
2041 | }
2042 |
2043 | /**
2044 | * Checks if `value` is a buffer.
2045 | *
2046 | * @static
2047 | * @memberOf _
2048 | * @since 4.3.0
2049 | * @category Lang
2050 | * @param {*} value The value to check.
2051 | * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
2052 | * @example
2053 | *
2054 | * _.isBuffer(new Buffer(2));
2055 | * // => true
2056 | *
2057 | * _.isBuffer(new Uint8Array(2));
2058 | * // => false
2059 | */
2060 | var isBuffer = !Buffer ? constant(false) : function(value) {
2061 | return value instanceof Buffer;
2062 | };
2063 |
2064 | /**
2065 | * Checks if `value` is classified as a `Function` object.
2066 | *
2067 | * @static
2068 | * @memberOf _
2069 | * @since 0.1.0
2070 | * @category Lang
2071 | * @param {*} value The value to check.
2072 | * @returns {boolean} Returns `true` if `value` is correctly classified,
2073 | * else `false`.
2074 | * @example
2075 | *
2076 | * _.isFunction(_);
2077 | * // => true
2078 | *
2079 | * _.isFunction(/abc/);
2080 | * // => false
2081 | */
2082 | function isFunction(value) {
2083 | // The use of `Object#toString` avoids issues with the `typeof` operator
2084 | // in Safari 8 which returns 'object' for typed array and weak map constructors,
2085 | // and PhantomJS 1.9 which returns 'function' for `NodeList` instances.
2086 | var tag = isObject(value) ? objectToString.call(value) : '';
2087 | return tag == funcTag || tag == genTag;
2088 | }
2089 |
2090 | /**
2091 | * Checks if `value` is a valid array-like length.
2092 | *
2093 | * **Note:** This function is loosely based on
2094 | * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
2095 | *
2096 | * @static
2097 | * @memberOf _
2098 | * @since 4.0.0
2099 | * @category Lang
2100 | * @param {*} value The value to check.
2101 | * @returns {boolean} Returns `true` if `value` is a valid length,
2102 | * else `false`.
2103 | * @example
2104 | *
2105 | * _.isLength(3);
2106 | * // => true
2107 | *
2108 | * _.isLength(Number.MIN_VALUE);
2109 | * // => false
2110 | *
2111 | * _.isLength(Infinity);
2112 | * // => false
2113 | *
2114 | * _.isLength('3');
2115 | * // => false
2116 | */
2117 | function isLength(value) {
2118 | return typeof value == 'number' &&
2119 | value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
2120 | }
2121 |
2122 | /**
2123 | * Checks if `value` is the
2124 | * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)
2125 | * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
2126 | *
2127 | * @static
2128 | * @memberOf _
2129 | * @since 0.1.0
2130 | * @category Lang
2131 | * @param {*} value The value to check.
2132 | * @returns {boolean} Returns `true` if `value` is an object, else `false`.
2133 | * @example
2134 | *
2135 | * _.isObject({});
2136 | * // => true
2137 | *
2138 | * _.isObject([1, 2, 3]);
2139 | * // => true
2140 | *
2141 | * _.isObject(_.noop);
2142 | * // => true
2143 | *
2144 | * _.isObject(null);
2145 | * // => false
2146 | */
2147 | function isObject(value) {
2148 | var type = typeof value;
2149 | return !!value && (type == 'object' || type == 'function');
2150 | }
2151 |
2152 | /**
2153 | * Checks if `value` is object-like. A value is object-like if it's not `null`
2154 | * and has a `typeof` result of "object".
2155 | *
2156 | * @static
2157 | * @memberOf _
2158 | * @since 4.0.0
2159 | * @category Lang
2160 | * @param {*} value The value to check.
2161 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
2162 | * @example
2163 | *
2164 | * _.isObjectLike({});
2165 | * // => true
2166 | *
2167 | * _.isObjectLike([1, 2, 3]);
2168 | * // => true
2169 | *
2170 | * _.isObjectLike(_.noop);
2171 | * // => false
2172 | *
2173 | * _.isObjectLike(null);
2174 | * // => false
2175 | */
2176 | function isObjectLike(value) {
2177 | return !!value && typeof value == 'object';
2178 | }
2179 |
2180 | /**
2181 | * Checks if `value` is a native function.
2182 | *
2183 | * @static
2184 | * @memberOf _
2185 | * @since 3.0.0
2186 | * @category Lang
2187 | * @param {*} value The value to check.
2188 | * @returns {boolean} Returns `true` if `value` is a native function,
2189 | * else `false`.
2190 | * @example
2191 | *
2192 | * _.isNative(Array.prototype.push);
2193 | * // => true
2194 | *
2195 | * _.isNative(_);
2196 | * // => false
2197 | */
2198 | function isNative(value) {
2199 | if (!isObject(value)) {
2200 | return false;
2201 | }
2202 | var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
2203 | return pattern.test(toSource(value));
2204 | }
2205 |
2206 | /**
2207 | * Checks if `value` is a plain object, that is, an object created by the
2208 | * `Object` constructor or one with a `[[Prototype]]` of `null`.
2209 | *
2210 | * @static
2211 | * @memberOf _
2212 | * @since 0.8.0
2213 | * @category Lang
2214 | * @param {*} value The value to check.
2215 | * @returns {boolean} Returns `true` if `value` is a plain object,
2216 | * else `false`.
2217 | * @example
2218 | *
2219 | * function Foo() {
2220 | * this.a = 1;
2221 | * }
2222 | *
2223 | * _.isPlainObject(new Foo);
2224 | * // => false
2225 | *
2226 | * _.isPlainObject([1, 2, 3]);
2227 | * // => false
2228 | *
2229 | * _.isPlainObject({ 'x': 0, 'y': 0 });
2230 | * // => true
2231 | *
2232 | * _.isPlainObject(Object.create(null));
2233 | * // => true
2234 | */
2235 | function isPlainObject(value) {
2236 | if (!isObjectLike(value) ||
2237 | objectToString.call(value) != objectTag || isHostObject(value)) {
2238 | return false;
2239 | }
2240 | var proto = getPrototype(value);
2241 | if (proto === null) {
2242 | return true;
2243 | }
2244 | var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
2245 | return (typeof Ctor == 'function' &&
2246 | Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
2247 | }
2248 |
2249 | /**
2250 | * Checks if `value` is classified as a `String` primitive or object.
2251 | *
2252 | * @static
2253 | * @since 0.1.0
2254 | * @memberOf _
2255 | * @category Lang
2256 | * @param {*} value The value to check.
2257 | * @returns {boolean} Returns `true` if `value` is correctly classified,
2258 | * else `false`.
2259 | * @example
2260 | *
2261 | * _.isString('abc');
2262 | * // => true
2263 | *
2264 | * _.isString(1);
2265 | * // => false
2266 | */
2267 | function isString(value) {
2268 | return typeof value == 'string' ||
2269 | (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag);
2270 | }
2271 |
2272 | /**
2273 | * Checks if `value` is classified as a `Symbol` primitive or object.
2274 | *
2275 | * @static
2276 | * @memberOf _
2277 | * @since 4.0.0
2278 | * @category Lang
2279 | * @param {*} value The value to check.
2280 | * @returns {boolean} Returns `true` if `value` is correctly classified,
2281 | * else `false`.
2282 | * @example
2283 | *
2284 | * _.isSymbol(Symbol.iterator);
2285 | * // => true
2286 | *
2287 | * _.isSymbol('abc');
2288 | * // => false
2289 | */
2290 | function isSymbol(value) {
2291 | return typeof value == 'symbol' ||
2292 | (isObjectLike(value) && objectToString.call(value) == symbolTag);
2293 | }
2294 |
2295 | /**
2296 | * Checks if `value` is classified as a typed array.
2297 | *
2298 | * @static
2299 | * @memberOf _
2300 | * @since 3.0.0
2301 | * @category Lang
2302 | * @param {*} value The value to check.
2303 | * @returns {boolean} Returns `true` if `value` is correctly classified,
2304 | * else `false`.
2305 | * @example
2306 | *
2307 | * _.isTypedArray(new Uint8Array);
2308 | * // => true
2309 | *
2310 | * _.isTypedArray([]);
2311 | * // => false
2312 | */
2313 | function isTypedArray(value) {
2314 | return isObjectLike(value) &&
2315 | isLength(value.length) && !!typedArrayTags[objectToString.call(value)];
2316 | }
2317 |
2318 | /**
2319 | * Converts `value` to a finite number.
2320 | *
2321 | * @static
2322 | * @memberOf _
2323 | * @since 4.12.0
2324 | * @category Lang
2325 | * @param {*} value The value to convert.
2326 | * @returns {number} Returns the converted number.
2327 | * @example
2328 | *
2329 | * _.toFinite(3.2);
2330 | * // => 3.2
2331 | *
2332 | * _.toFinite(Number.MIN_VALUE);
2333 | * // => 5e-324
2334 | *
2335 | * _.toFinite(Infinity);
2336 | * // => 1.7976931348623157e+308
2337 | *
2338 | * _.toFinite('3.2');
2339 | * // => 3.2
2340 | */
2341 | function toFinite(value) {
2342 | if (!value) {
2343 | return value === 0 ? value : 0;
2344 | }
2345 | value = toNumber(value);
2346 | if (value === INFINITY || value === -INFINITY) {
2347 | var sign = (value < 0 ? -1 : 1);
2348 | return sign * MAX_INTEGER;
2349 | }
2350 | return value === value ? value : 0;
2351 | }
2352 |
2353 | /**
2354 | * Converts `value` to an integer.
2355 | *
2356 | * **Note:** This function is loosely based on
2357 | * [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
2358 | *
2359 | * @static
2360 | * @memberOf _
2361 | * @since 4.0.0
2362 | * @category Lang
2363 | * @param {*} value The value to convert.
2364 | * @returns {number} Returns the converted integer.
2365 | * @example
2366 | *
2367 | * _.toInteger(3.2);
2368 | * // => 3
2369 | *
2370 | * _.toInteger(Number.MIN_VALUE);
2371 | * // => 0
2372 | *
2373 | * _.toInteger(Infinity);
2374 | * // => 1.7976931348623157e+308
2375 | *
2376 | * _.toInteger('3.2');
2377 | * // => 3
2378 | */
2379 | function toInteger(value) {
2380 | var result = toFinite(value),
2381 | remainder = result % 1;
2382 |
2383 | return result === result ? (remainder ? result - remainder : result) : 0;
2384 | }
2385 |
2386 | /**
2387 | * Converts `value` to a number.
2388 | *
2389 | * @static
2390 | * @memberOf _
2391 | * @since 4.0.0
2392 | * @category Lang
2393 | * @param {*} value The value to process.
2394 | * @returns {number} Returns the number.
2395 | * @example
2396 | *
2397 | * _.toNumber(3.2);
2398 | * // => 3.2
2399 | *
2400 | * _.toNumber(Number.MIN_VALUE);
2401 | * // => 5e-324
2402 | *
2403 | * _.toNumber(Infinity);
2404 | * // => Infinity
2405 | *
2406 | * _.toNumber('3.2');
2407 | * // => 3.2
2408 | */
2409 | function toNumber(value) {
2410 | if (typeof value == 'number') {
2411 | return value;
2412 | }
2413 | if (isSymbol(value)) {
2414 | return NAN;
2415 | }
2416 | if (isObject(value)) {
2417 | var other = isFunction(value.valueOf) ? value.valueOf() : value;
2418 | value = isObject(other) ? (other + '') : other;
2419 | }
2420 | if (typeof value != 'string') {
2421 | return value === 0 ? value : +value;
2422 | }
2423 | value = value.replace(reTrim, '');
2424 | var isBinary = reIsBinary.test(value);
2425 | return (isBinary || reIsOctal.test(value))
2426 | ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
2427 | : (reIsBadHex.test(value) ? NAN : +value);
2428 | }
2429 |
2430 | /**
2431 | * Converts `value` to a plain object flattening inherited enumerable string
2432 | * keyed properties of `value` to own properties of the plain object.
2433 | *
2434 | * @static
2435 | * @memberOf _
2436 | * @since 3.0.0
2437 | * @category Lang
2438 | * @param {*} value The value to convert.
2439 | * @returns {Object} Returns the converted plain object.
2440 | * @example
2441 | *
2442 | * function Foo() {
2443 | * this.b = 2;
2444 | * }
2445 | *
2446 | * Foo.prototype.c = 3;
2447 | *
2448 | * _.assign({ 'a': 1 }, new Foo);
2449 | * // => { 'a': 1, 'b': 2 }
2450 | *
2451 | * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
2452 | * // => { 'a': 1, 'b': 2, 'c': 3 }
2453 | */
2454 | function toPlainObject(value) {
2455 | return copyObject(value, keysIn(value));
2456 | }
2457 |
2458 | /*------------------------------------------------------------------------*/
2459 |
2460 | /**
2461 | * Creates an array of the own enumerable property names of `object`.
2462 | *
2463 | * **Note:** Non-object values are coerced to objects. See the
2464 | * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
2465 | * for more details.
2466 | *
2467 | * @static
2468 | * @since 0.1.0
2469 | * @memberOf _
2470 | * @category Object
2471 | * @param {Object} object The object to query.
2472 | * @returns {Array} Returns the array of property names.
2473 | * @example
2474 | *
2475 | * function Foo() {
2476 | * this.a = 1;
2477 | * this.b = 2;
2478 | * }
2479 | *
2480 | * Foo.prototype.c = 3;
2481 | *
2482 | * _.keys(new Foo);
2483 | * // => ['a', 'b'] (iteration order is not guaranteed)
2484 | *
2485 | * _.keys('hi');
2486 | * // => ['0', '1']
2487 | */
2488 | function keys(object) {
2489 | var isProto = isPrototype(object);
2490 | if (!(isProto || isArrayLike(object))) {
2491 | return baseKeys(object);
2492 | }
2493 | var indexes = indexKeys(object),
2494 | skipIndexes = !!indexes,
2495 | result = indexes || [],
2496 | length = result.length;
2497 |
2498 | for (var key in object) {
2499 | if (baseHas(object, key) &&
2500 | !(skipIndexes && (key == 'length' || isIndex(key, length))) &&
2501 | !(isProto && key == 'constructor')) {
2502 | result.push(key);
2503 | }
2504 | }
2505 | return result;
2506 | }
2507 |
2508 | /**
2509 | * Creates an array of the own and inherited enumerable property names of `object`.
2510 | *
2511 | * **Note:** Non-object values are coerced to objects.
2512 | *
2513 | * @static
2514 | * @memberOf _
2515 | * @since 3.0.0
2516 | * @category Object
2517 | * @param {Object} object The object to query.
2518 | * @returns {Array} Returns the array of property names.
2519 | * @example
2520 | *
2521 | * function Foo() {
2522 | * this.a = 1;
2523 | * this.b = 2;
2524 | * }
2525 | *
2526 | * Foo.prototype.c = 3;
2527 | *
2528 | * _.keysIn(new Foo);
2529 | * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
2530 | */
2531 | function keysIn(object) {
2532 | var index = -1,
2533 | isProto = isPrototype(object),
2534 | props = baseKeysIn(object),
2535 | propsLength = props.length,
2536 | indexes = indexKeys(object),
2537 | skipIndexes = !!indexes,
2538 | result = indexes || [],
2539 | length = result.length;
2540 |
2541 | while (++index < propsLength) {
2542 | var key = props[index];
2543 | if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
2544 | !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
2545 | result.push(key);
2546 | }
2547 | }
2548 | return result;
2549 | }
2550 |
2551 | /**
2552 | * This method is like `_.assign` except that it recursively merges own and
2553 | * inherited enumerable string keyed properties of source objects into the
2554 | * destination object. Source properties that resolve to `undefined` are
2555 | * skipped if a destination value exists. Array and plain object properties
2556 | * are merged recursively. Other objects and value types are overridden by
2557 | * assignment. Source objects are applied from left to right. Subsequent
2558 | * sources overwrite property assignments of previous sources.
2559 | *
2560 | * **Note:** This method mutates `object`.
2561 | *
2562 | * @static
2563 | * @memberOf _
2564 | * @since 0.5.0
2565 | * @category Object
2566 | * @param {Object} object The destination object.
2567 | * @param {...Object} [sources] The source objects.
2568 | * @returns {Object} Returns `object`.
2569 | * @example
2570 | *
2571 | * var users = {
2572 | * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
2573 | * };
2574 | *
2575 | * var ages = {
2576 | * 'data': [{ 'age': 36 }, { 'age': 40 }]
2577 | * };
2578 | *
2579 | * _.merge(users, ages);
2580 | * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
2581 | */
2582 | var merge = createAssigner(function(object, source, srcIndex) {
2583 | baseMerge(object, source, srcIndex);
2584 | });
2585 |
2586 | /*------------------------------------------------------------------------*/
2587 |
2588 | /**
2589 | * Creates a function that returns `value`.
2590 | *
2591 | * @static
2592 | * @memberOf _
2593 | * @since 2.4.0
2594 | * @category Util
2595 | * @param {*} value The value to return from the new function.
2596 | * @returns {Function} Returns the new constant function.
2597 | * @example
2598 | *
2599 | * var object = { 'user': 'fred' };
2600 | * var getter = _.constant(object);
2601 | *
2602 | * getter() === object;
2603 | * // => true
2604 | */
2605 | function constant(value) {
2606 | return function() {
2607 | return value;
2608 | };
2609 | }
2610 |
2611 | /*------------------------------------------------------------------------*/
2612 |
2613 | // Add methods that return wrapped values in chain sequences.
2614 | lodash.constant = constant;
2615 | lodash.keys = keys;
2616 | lodash.keysIn = keysIn;
2617 | lodash.merge = merge;
2618 | lodash.rest = rest;
2619 | lodash.toPlainObject = toPlainObject;
2620 |
2621 | /*------------------------------------------------------------------------*/
2622 |
2623 | // Add methods that return unwrapped values in chain sequences.
2624 | lodash.cloneDeep = cloneDeep;
2625 | lodash.eq = eq;
2626 | lodash.isArguments = isArguments;
2627 | lodash.isArray = isArray;
2628 | lodash.isArrayLike = isArrayLike;
2629 | lodash.isArrayLikeObject = isArrayLikeObject;
2630 | lodash.isBuffer = isBuffer;
2631 | lodash.isFunction = isFunction;
2632 | lodash.isLength = isLength;
2633 | lodash.isNative = isNative;
2634 | lodash.isObject = isObject;
2635 | lodash.isObjectLike = isObjectLike;
2636 | lodash.isPlainObject = isPlainObject;
2637 | lodash.isString = isString;
2638 | lodash.isSymbol = isSymbol;
2639 | lodash.isTypedArray = isTypedArray;
2640 | lodash.toFinite = toFinite;
2641 | lodash.toInteger = toInteger;
2642 | lodash.toNumber = toNumber;
2643 |
2644 | /*------------------------------------------------------------------------*/
2645 |
2646 | /**
2647 | * The semantic version number.
2648 | *
2649 | * @static
2650 | * @memberOf _
2651 | * @type {string}
2652 | */
2653 | lodash.VERSION = VERSION;
2654 |
2655 | /*--------------------------------------------------------------------------*/
2656 |
2657 | // Expose Lodash on the free variable `window` or `self` when available so it's
2658 | // globally accessible, even when bundled with Browserify, Webpack, etc. This
2659 | // also prevents errors in cases where Lodash is loaded by a script tag in the
2660 | // presence of an AMD loader. See http://requirejs.org/docs/errors.html#mismatch
2661 | // for more details. Use `_.noConflict` to remove Lodash from the global object.
2662 | (freeWindow || freeSelf || {})._ = lodash;
2663 |
2664 | if (freeExports && freeModule) {
2665 | // Export for Node.js.
2666 | if (moduleExports) {
2667 | (freeModule.exports = lodash)._ = lodash;
2668 | }
2669 | // Export for CommonJS support.
2670 | freeExports._ = lodash;
2671 | }
2672 | }.call(this));
2673 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eazy-logger",
3 | "description": "Simple cli logger",
4 | "version": "4.0.1",
5 | "homepage": "https://github.com/shakyshane/easy-logger",
6 | "author": {
7 | "name": "Shane Osbourne"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git://github.com/shakyshane/easy-logger.git"
12 | },
13 | "bugs": {
14 | "url": "https://github.com/shakyshane/easy-logger/issues"
15 | },
16 | "licenses": [
17 | {
18 | "type": "Apache 2.0",
19 | "url": "https://github.com/shakyshane/easy-logger/blob/master/LICENSE"
20 | }
21 | ],
22 | "files": [
23 | "index.js",
24 | "lodash.custom.js",
25 | "example.js"
26 | ],
27 | "main": "index.js",
28 | "engines": {
29 | "node": ">= 0.8.0"
30 | },
31 | "scripts": {
32 | "lint": "jshint index.js test/*.js",
33 | "test": "npm run lint && mocha",
34 | "lodash": "lodash include=cloneDeep,merge exports=node"
35 | },
36 | "dependencies": {
37 | "chalk": "4.1.2"
38 | },
39 | "devDependencies": {
40 | "chai": "^3.5.0",
41 | "jshint": "^2.6.0",
42 | "lodash-cli": "4.12.0",
43 | "mocha": "^10.2.0",
44 | "sinon": "^1.12.2",
45 | "strip-ansi": "^6.0.1"
46 | },
47 | "keywords": [
48 | "plugins"
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/test/log.js:
--------------------------------------------------------------------------------
1 | var assert = require("chai").assert;
2 | var sinon = require("sinon");
3 | var easyLogger = require("../index");
4 | var stripColor = require("strip-ansi");
5 | var chalk = require("chalk");
6 |
7 | var defaultConfig = {
8 | prefix: `${chalk.blue("[")}${chalk.magenta("logger")}${chalk.cyan("]")} `,
9 | prefixes: {
10 | debug: "DEBUG ",
11 | info: "INFO ",
12 | warn: "WARN ",
13 | error: "ERROR "
14 | }
15 | };
16 |
17 | // helper to get un-coloured strings for comparisons
18 | var arg = function (spy, num, argNum) {
19 | var call = spy.getCall(num);
20 | var arg;
21 | if (call.args) {
22 | arg = call.args[argNum];
23 | }
24 | return stripColor(arg);
25 | };
26 |
27 | describe("Logging", function(){
28 | var spy, logger;
29 | before(function () {
30 | spy = sinon.spy(console, "log");
31 | });
32 | beforeEach(function () {
33 | logger = easyLogger.Logger(defaultConfig);
34 | });
35 | after(function () {
36 | spy.restore();
37 | });
38 | afterEach(function () {
39 | spy.reset();
40 | logger.reset();
41 | });
42 | it("can do console.log on info", function(){
43 | logger.log("info", "Running!");
44 | var actual = arg(spy, 0, 0);
45 | var expected = "[logger] Running!";
46 | assert.equal(actual, expected);
47 | });
48 | it("Does not log when level = info & log msg is WARN", function(){
49 | logger.log("warn", "Not found");
50 | sinon.assert.notCalled(spy);
51 | });
52 | it("DOES log after the log level is rest", function(){
53 | logger.log("warn", "Not found");
54 | sinon.assert.notCalled(spy);
55 | logger.setLevel("warn");
56 | logger.log("warn", "Not found");
57 | var actual = arg(spy, 0, 0);
58 | var expected = "[logger] Not found";
59 | assert.equal(actual, expected);
60 | logger.setLevel("error");
61 | logger.log("info", "Welcome!");
62 | sinon.assert.calledOnce(spy);
63 | });
64 | it("Can remove the prefix", function(){
65 | logger.unprefixed("info", "");
66 | sinon.assert.calledWithExactly(spy, "");
67 | });
68 | it("Can remove the level prefixes", function(){
69 | logger.setLevelPrefixes(true);
70 | logger.unprefixed("info", "");
71 | var actual = arg(spy, 0, 0);
72 | var expected = "";
73 | assert.equal(actual, expected);
74 | });
75 | it("Can use the level prefixes", function(){
76 | logger.setLevelPrefixes(true);
77 | logger.log("info", "");
78 | var actual = arg(spy, 0, 0);
79 | var expected = "[logger] INFO ";
80 | assert.equal(actual, expected);
81 | });
82 | it("Can return a cloned logger", function(){
83 | var clone = logger.clone();
84 | clone.setLevelPrefixes(true);
85 | clone.log("info", "");
86 | var actual = arg(spy, 0, 0);
87 | var expected = "[logger] INFO ";
88 | assert.equal(actual, expected);
89 | });
90 | it("Can return multiple cloned loggers", function() {
91 | var clone = logger.clone(function (config) {
92 | config.prefix = config.prefix + "shane ";
93 | return config;
94 | });
95 |
96 | clone.setLevelPrefixes(true);
97 | clone.log("info", "");
98 |
99 | var actual = arg(spy, 0, 0);
100 | var expected = "[logger] shane INFO ";
101 |
102 | assert.equal(actual, expected);
103 |
104 | // Second clone
105 | var clone2 = logger.clone(function (config) {
106 | config.prefix = config.prefix + "Second ";
107 | return config;
108 | });
109 |
110 | clone2.setLevelPrefixes(true);
111 | clone2.log("info", "");
112 |
113 | actual = arg(spy, 1, 0);
114 | expected = "[logger] Second INFO ";
115 | assert.equal(actual, expected);
116 | });
117 | it("Can return a cloned logger with different prefix", function(){
118 | var clone = logger.clone({prefix: "SHANE "});
119 | clone.setLevelPrefixes(true);
120 | clone.log("info", "");
121 | var actual = arg(spy, 0, 0);
122 | var expected = "SHANE INFO ";
123 | assert.equal(actual, expected);
124 | });
125 | it("Can give a callback for creating new config", function(){
126 | var clone = logger.clone(function (config) {
127 | config.prefix = "SHANE ";
128 | return config;
129 | });
130 | clone.setLevelPrefixes(true);
131 | clone.log("info", "");
132 | var actual = arg(spy, 0, 0);
133 | var expected = "SHANE INFO ";
134 | assert.equal(actual, expected);
135 | });
136 | it("Can append to existing prefix via callback", function(){
137 | var logger = new easyLogger.Logger(defaultConfig);
138 | var clone = logger.clone(function (config) {
139 | config.prefix = config.prefix + "[new module] ";
140 | return config;
141 | });
142 | clone.log("info", "");
143 | var actual = arg(spy, 0, 0);
144 | var expected = "[logger] [new module] ";
145 | assert.equal(actual, expected);
146 | });
147 | it("Can append to existing prefix via callback with level prefixes", function(){
148 | var logger = new easyLogger.Logger(defaultConfig);
149 | var clone = logger.clone(function (config) {
150 | config.prefix = config.prefix + "[new module] ";
151 | return config;
152 | });
153 | clone.setLevelPrefixes(true);
154 | clone.log("info", "");
155 | var actual = arg(spy, 0, 0);
156 | var expected = "[logger] [new module] INFO ";
157 | assert.equal(actual, expected);
158 | });
159 | it("can use built-in string replacement", function(){
160 | var logger = new easyLogger.Logger(defaultConfig);
161 | logger.setLevelPrefixes(true);
162 | logger.log("info", "", "http://shakyshane.com/js.js");
163 |
164 | var actual = arg(spy, 0, 0);
165 | var expected = "[logger] INFO ";
166 |
167 | assert.equal(actual, expected);
168 |
169 | actual = arg(spy, 0, 1);
170 | assert.equal(actual, "http://shakyshane.com/js.js");
171 |
172 | });
173 | it("can use built-in string replacement (2)", function(){
174 | var logger = new easyLogger.Logger(defaultConfig);
175 | logger.setLevelPrefixes(true);
176 | logger.log("info", "", "http://shakyshane.com/", "js.js");
177 | var actual = arg(spy, 0, 0);
178 | var expected = "[logger] INFO ";
179 | assert.equal(actual, expected);
180 | actual = arg(spy, 0, 1);
181 | assert.equal(actual, "http://shakyshane.com/");
182 | actual = arg(spy, 0, 2);
183 | assert.equal(actual, "js.js");
184 | });
185 | it("can be used with no configuration", function () {
186 | var logger = new easyLogger.Logger(defaultConfig);
187 | logger.log("no config");
188 | });
189 | it("can use alias methods (INFO)", function () {
190 | var logger = new easyLogger.Logger(defaultConfig);
191 | logger.setLevelPrefixes(true);
192 | logger.info("");
193 | var actual = arg(spy, 0, 0);
194 | var expected = "[logger] INFO ";
195 | assert.equal(actual, expected);
196 | });
197 | it("can use alias methods (INFO)", function () {
198 | var logger = new easyLogger.Logger(defaultConfig);
199 | logger.setLevelPrefixes(true);
200 | logger.info("");
201 | var actual = arg(spy, 0, 0);
202 | var expected = "[logger] INFO ";
203 | assert.equal(actual, expected);
204 | });
205 | it("can chain from alias methods", function () {
206 | var logger = new easyLogger.Logger(defaultConfig);
207 | logger.setLevelPrefixes(true).info("").setLevelPrefixes(false);
208 | var actual = arg(spy, 0, 0);
209 | var expected = "[logger] INFO ";
210 | assert.equal(actual, expected);
211 | });
212 | it("can set an option once", function () {
213 | var logger = new easyLogger.Logger(defaultConfig);
214 | logger.setOnce("useLevelPrefixes", true).info("");
215 | var actual = arg(spy, 0, 0);
216 | var expected = "[logger] INFO ";
217 | assert.equal(actual, expected);
218 |
219 | logger.info("");
220 | actual = arg(spy, 1, 0);
221 | expected = "[logger] ";
222 | assert.equal(actual, expected);
223 | });
224 | it("can set an option once multiple times", function () {
225 | var logger = new easyLogger.Logger(defaultConfig);
226 | logger.setOnce("useLevelPrefixes", true);
227 | logger.setOnce("useLevelPrefixes", true);
228 | logger.info("");
229 |
230 | var actual = arg(spy, 0, 0);
231 | var expected = "[logger] INFO ";
232 | assert.equal(actual, expected);
233 |
234 | logger.info("");
235 | actual = arg(spy, 1, 0);
236 | expected = "[logger] ";
237 | assert.equal(actual, expected);
238 | });
239 | it("can be muted", function () {
240 | var logger = new easyLogger.Logger(defaultConfig);
241 | logger.mute(true);
242 | logger.info("");
243 | sinon.assert.notCalled(spy);
244 | });
245 | it("can be un-muted", function () {
246 | var logger = new easyLogger.Logger(defaultConfig);
247 | logger.mute(true);
248 | logger.info("");
249 | sinon.assert.notCalled(spy);
250 | logger.mute(false);
251 | logger.info("");
252 | var actual = arg(spy, 0, 0);
253 | var expected = "[logger] ";
254 | assert.equal(actual, expected);
255 | });
256 | it("can accept a function for the prefix", function(){
257 | var logger = new easyLogger.Logger({
258 | prefix: function () {
259 | return "PREFIX";
260 | }
261 | });
262 | logger.info("");
263 |
264 | var actual = arg(spy, 0, 0);
265 | var expected = "PREFIX";
266 | assert.equal(actual, expected);
267 | });
268 | it("can SET a function for the prefix", function(){
269 | var logger = new easyLogger.Logger(defaultConfig);
270 | logger.setPrefix(function () {
271 | return "PREFIX";
272 | });
273 | logger.info("");
274 |
275 | var actual = arg(spy, 0, 0);
276 | var expected = "PREFIX";
277 | assert.equal(actual, expected);
278 | });
279 | it("can update the prefix", function(){
280 | var logger = new easyLogger.Logger(defaultConfig);
281 | logger.setPrefix("SHANE");
282 | logger.info("");
283 |
284 | var actual = arg(spy, 0, 0);
285 | var expected = "SHANE";
286 | assert.equal(actual, expected);
287 | });
288 | it("can update the prefix with color included", function(){
289 | var logger = new easyLogger.Logger(defaultConfig);
290 | logger.info("");
291 |
292 | var actual = arg(spy, 0, 0);
293 | var expected = "[logger] ";
294 | assert.equal(actual, expected);
295 |
296 | logger.setPrefix("ERROR: ");
297 | logger.info("");
298 | actual = arg(spy, 1, 0);
299 | expected = "ERROR: ";
300 | assert.equal(actual, expected);
301 | });
302 | it("can print javascript", function(){
303 | var logger = new easyLogger.Logger(defaultConfig);
304 | logger.info(`(function() { console.log("lol!") })()`);
305 |
306 | var actual = arg(spy, 0, 0);
307 | var expected = `[logger] (function() { console.log("lol!") })()`;
308 |
309 | assert.equal(actual, expected);
310 | });
311 |
312 | it("should handle prototype pollution attempts safely", function () {
313 | const lib = easyLogger;
314 | console.log("Before Attack: ", JSON.stringify( Object.getPrototypeOf({})));
315 |
316 | try {
317 | // for multiple functions, uncomment only one for each execution.
318 | lib.Logger(JSON.parse("{\"__proto__\":{\"pollutedKey\":123}}"));
319 | } catch (e) {
320 | }
321 |
322 | console.log("After Attack: ", JSON.stringify(Object.getPrototypeOf({})));
323 |
324 | assert.notProperty(Object.prototype, "pollutedKey", "Prototype pollution occurred");
325 |
326 | // Cleanup if any property was added
327 | delete Object.prototype.pollutedKey;
328 | });
329 | });
330 |
--------------------------------------------------------------------------------