Repository: sup-fiveyear/Notes Branch: master Commit: 55525b4f3ca7 Files: 334 Total size: 3.6 MB Directory structure: gitextract_l21fk2d5/ ├── .gitignore ├── .obsidian/ │ ├── app.json │ ├── appearance.json │ ├── community-plugins.json │ ├── core-plugins.json │ ├── daily-notes.json │ ├── graph.json │ ├── hotkeys.json │ ├── page-preview.json │ ├── plugins/ │ │ ├── file-explorer-note-count/ │ │ │ ├── data.json │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── styles.css │ │ ├── obsidian-outliner/ │ │ │ ├── data.json │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── styles.css │ │ ├── recent-files-obsidian/ │ │ │ ├── data.json │ │ │ ├── main.js │ │ │ ├── manifest.json │ │ │ └── styles.css │ │ └── table-editor-obsidian/ │ │ ├── data.json │ │ ├── main.js │ │ ├── manifest.json │ │ └── styles.css │ ├── starred.json │ ├── templates.json │ ├── themes/ │ │ ├── Atom.css │ │ └── Obsidian Nord.css │ ├── workspace │ └── workspaces.json ├── 1. cs/ │ ├── 《CSAPP》/ │ │ └── chapter 6 - the memory hierarchy.md │ ├── 《Problem solving with algorithms and Data Structures Using Python》/ │ │ ├── index.md │ │ ├── 何为数据结构与算法?.md │ │ └── 线性数据结构/ │ │ ├── index.md │ │ ├── my_stack.py │ │ └── 栈.md │ └── 存储体系.md ├── 2. coding/ │ ├── 1-beginner/ │ │ ├── 0. 贪心/ │ │ │ └── 455. assign-cookies.md │ │ ├── 1. sorting/ │ │ │ ├── 26. 删除有序数组中的重复项.md │ │ │ ├── 283. 移动零.md │ │ │ ├── 475. 供暖器.md │ │ │ ├── 56. 合并区间.md │ │ │ ├── 80. 删除有序数组中的重复项 II.md │ │ │ ├── 881. 救生艇.md │ │ │ ├── 912. 排序数组.md │ │ │ ├── Array Deduplication III.md │ │ │ └── pointer.md │ │ ├── 3. linked-list/ │ │ │ └── 206. 反转链表.md │ │ ├── 4. queue & stack/ │ │ │ └── 1047. 删除字符串中的所有相邻重复项.md │ │ ├── binary-search/ │ │ │ └── 704. Binary Search.md │ │ ├── dp/ │ │ │ ├── 1139. 最大的以 1 为边界的正方形.md │ │ │ ├── 139. 单词拆分.md │ │ │ ├── 221. 最大正方形.md │ │ │ ├── 45.跳跃游戏 II.md │ │ │ ├── 485. 最大连续 1 的个数.md │ │ │ ├── 53.最大子数组和.md │ │ │ ├── 55.跳跃游戏.md │ │ │ ├── 72. 编辑距离.md │ │ │ ├── 764. 最大加号标志.md │ │ │ ├── Longest Ascending SubArray.md │ │ │ ├── temp.js │ │ │ ├── 剑指 Offer 14- I. 剪绳子.md │ │ │ └── 面试题 17.24. 最大子矩阵.md │ │ └── recursion/ │ │ ├── 25. K 个一组翻转链表.md │ │ ├── 344. 反转字符串.md │ │ ├── 408. 有效单词缩写.md │ │ ├── 50. Pow(x, n).md │ │ ├── 509. 斐波那契数.md │ │ ├── 51. N 皇后.md │ │ ├── 59. 螺旋矩阵 II.md │ │ └── 92. 反转链表 II.md │ ├── 2-training/ │ │ ├── 1014.最佳观光组合.md │ │ ├── 103. 二叉树的锯齿形层序遍历.md │ │ ├── 1154. 一年中的第几天.md │ │ ├── 121. 买卖股票的最佳时机#P0#3.md │ │ ├── 1609. 奇偶树.md │ │ ├── 1644. 二叉树的最近公共祖先 II.md │ │ ├── 1705. 吃苹果的最大数目.md │ │ ├── 198.打家劫舍.md │ │ ├── 2100. 适合打劫银行的日子.md │ │ ├── 2109. 向字符串添加空格.md │ │ ├── 236. 二叉树的最近公共祖先.md │ │ ├── 300.最长递增子序列.md │ │ ├── 846. 一手顺子.js │ │ ├── index.md │ │ └── 面试题 02.06. 回文链表.md │ ├── 3-contest/ │ │ ├── 273/ │ │ │ ├── 1.js │ │ │ ├── 2 copy.js │ │ │ ├── 2.js │ │ │ ├── 3 copy.js │ │ │ └── 3.js │ │ ├── 274/ │ │ │ ├── 1.js │ │ │ ├── 2.js │ │ │ ├── 3.js │ │ │ └── 4.js │ │ ├── test.js │ │ ├── 第 273 场周赛.md │ │ └── 第二题模拟练习/ │ │ ├── 1.js │ │ ├── 2.js │ │ └── 3.js │ ├── 4-books/ │ │ └── 《挑战程序设计竞赛》/ │ │ ├── 1. 抽签.js │ │ ├── 2. 三角形.js │ │ └── tempCodeRunnerFile.js │ ├── 51. N 皇后.md │ ├── 59. 螺旋矩阵 II.md │ └── index.md ├── 3. sd/ │ └── FE/ │ └── index.md ├── DOM检查 ?react 解析器?开源工具?.md ├── README.md ├── temp/ │ ├── 1维 - 动态规划.md │ ├── leetcode 拷贝题目模板.md │ ├── 内推模板.md │ └── 大部头读书笔记.md ├── todo/ │ └── fe/ │ ├── TODO/ │ │ └── 10月第一周.md │ ├── fe-README.md │ ├── koa_/ │ │ ├── app.js │ │ ├── my_koa/ │ │ │ ├── package.json │ │ │ └── src/ │ │ │ ├── Application.js │ │ │ ├── context.js │ │ │ ├── request.js │ │ │ └── response.js │ │ └── package.json │ ├── notes-JavaScript/ │ │ ├── DOM/ │ │ │ └── DOM汇总.md │ │ ├── ES5/ │ │ │ ├── ES5 - new & instanceof 实现.md │ │ │ ├── ES5 - 你说一下你对原型的认识.md │ │ │ ├── ES5-this & call & apply & bind.md │ │ │ ├── ES5-原型原型链.md │ │ │ ├── ES5-变量提升.md │ │ │ ├── ES5-数据类型.md │ │ │ ├── ES5-继承.md │ │ │ └── ES5-闭包.md │ │ ├── ES6/ │ │ │ ├── ES6 - class 总结.md │ │ │ ├── ES6 - extends 总结.md │ │ │ ├── Promise/ │ │ │ │ ├── Promise - 实现 Promise.race() && Promise.all() && Promise.allsettled().md │ │ │ │ ├── promiseA+.js │ │ │ │ ├── promise练习.md │ │ │ │ ├── promisify.md │ │ │ │ ├── 实现PromiseA+.md │ │ │ │ └── 来一沓Promise.md │ │ │ ├── README.MD │ │ │ └── 新增数据结构/ │ │ │ ├── map/ │ │ │ │ └── map.md │ │ │ └── set/ │ │ │ └── set.md │ │ ├── README.md │ │ ├── 函数式编程/ │ │ │ └── README.md │ │ ├── 实现极简发布订阅.md │ │ ├── 数组常见API及实现.md │ │ ├── 说说async的原理.md │ │ ├── 高阶函数之after函数.md │ │ ├── 高阶函数之函数柯里化.md │ │ └── 高阶函数之初见AOP.md │ ├── notes-nodeJs/ │ │ ├── 1. NodeJs有哪些特点?应用场景又有哪些?.md │ │ ├── 2. V8 libuv 和 Nodejs之间的关系?.md │ │ ├── 3. 说说你对global 以及global.process的理解?.md │ │ ├── 4. 说一下Liubv的事件循环机制.md │ │ ├── 5. 说说你对模块化的理解?.md │ │ ├── README.md │ │ ├── notes-htmlcss/ │ │ │ ├── flex 布局.md │ │ │ ├── html汇总.md │ │ │ └── 常见居中.md │ │ ├── npm有了解吗?.md │ │ ├── 说一下Nodejs的异步编程吧.md │ │ └── 请你实现一下Event模块.md │ ├── notes-vue/ │ │ ├── $mount和模板编译原理.md │ │ ├── $watch和computed.md │ │ ├── vue-router.md │ │ ├── vuex.md │ │ ├── 响应式原理.md │ │ ├── 常见面试题汇总.md │ │ ├── 来,唠一下生命周期.md │ │ ├── 渲染详细流程.md │ │ ├── 虚拟DOM.md │ │ ├── 说一下nextTick?.md │ │ ├── 说一下你对$nextTick的理解.md │ │ ├── 说说指令的原理.md │ │ └── 面试题.md │ ├── notes-性能优化/ │ │ └── 浏览器/ │ │ ├── README.md │ │ ├── 函数节流.md │ │ └── 函数防抖.md │ ├── notes-浏览器/ │ │ ├── DOM.md │ │ ├── README.md │ │ ├── 浏览器基础/ │ │ │ ├── 说一下AJAX.md │ │ │ ├── 说一下垃圾回收策略?.md │ │ │ ├── 说一下浏览器事件循环.md │ │ │ ├── 跨域通信.md │ │ │ └── 输入URL后发生了什么.md │ │ └── 浏览器性能优化/ │ │ └── README.md │ ├── notes-网络/ │ │ ├── HTTP/ │ │ │ ├── 1.你是如何理解HTTP的?特点有哪些?.md │ │ │ ├── 10.说一下 HTTP 的内容协商?.md │ │ │ ├── 11.HTTP 重定向有了解吗?.md │ │ │ ├── 12.了解HTTP长连接吧?那队头阻塞呢?.md │ │ │ ├── 13.HTTP传输大文件有了解过吗?.md │ │ │ ├── 2. 你知道HTTP都有哪些版本吗?.md │ │ │ ├── 3.说一下你对域名的理解?.md │ │ │ ├── 4.说说你对URL 和 URI 的理解.md │ │ │ ├── 5. 描述一下HTTP报文结构.md │ │ │ ├── 6. 如何理解请求(响应)上下文?.md │ │ │ ├── 7. 请求方法和状态码有哪些?.md │ │ │ ├── 8.说说你对cookie的理解.md │ │ │ ├── 9.说下你了解的缓存策略.md │ │ │ ├── HTTP 性能优化方向有哪些?.md │ │ │ ├── README.md │ │ │ ├── index.md │ │ │ ├── 摘出的笔记.md │ │ │ └── 说说HTTP2 和HTTP3.md │ │ ├── HTTPS/ │ │ │ ├── HTTP 和 HTTPS 有什么区别?.md │ │ │ ├── HTTPS优化策略有了解吗?.md │ │ │ ├── README.md │ │ │ ├── TLS 握手讲一下吧?.md │ │ │ ├── 摘要算法&数据签名.md │ │ │ └── 说说对称加密和非对称加密的理解?.md │ │ ├── IP/ │ │ │ ├── TCP建立连接?.md │ │ │ └── 说说你是如何理解TCP的?.md │ │ └── TCP/ │ │ ├── README.md │ │ ├── TCP如何做到多路复用.md │ │ ├── TCP建立连接?.md │ │ ├── 知道TCP的keepalive机制吗.md │ │ ├── 知道慢启动、拥塞避免、快速重传和快速恢复吗.md │ │ ├── 简述一下TCP报文结构.md │ │ ├── 说一下TCP的滑动窗口.md │ │ ├── 说说TCP三次握手吧.md │ │ ├── 说说TCP挥手过程.md │ │ ├── 说说你对MSS的理解.md │ │ ├── 说说你对滑动窗口的理解.md │ │ ├── 说说你对超时重传的理解.md │ │ ├── 说说你是如何理解TCP的?.md │ │ ├── 说说如何减少网络过程中的小报文?.md │ │ └── 那你知道选择性重传算法(SACK)吗?.md │ ├── notes-设计模式/ │ │ ├── README.MD │ │ └── 创建模式/ │ │ ├── README.md │ │ └── Singleton.ts │ ├── vue 手写/ │ │ ├── hash和history原理.html │ │ └── src/ │ │ ├── compiler/ │ │ │ ├── index.js │ │ │ └── parser-html.js │ │ ├── index.js │ │ ├── init.js │ │ ├── lifecycle.js │ │ ├── observer/ │ │ │ ├── array.js │ │ │ ├── dep.js │ │ │ ├── index.js │ │ │ └── watcher.js │ │ ├── render.js │ │ ├── state.js │ │ ├── util/ │ │ │ └── index.js │ │ └── vdom/ │ │ ├── create-element.js │ │ └── patch.js │ ├── webpack/ │ │ ├── 原理 - 懒加载:单入口文件和多入口文件实现懒加载.md │ │ ├── 原理 - 模块化:如何同时支持CommonJs规范和ES module规范.md │ │ └── 基础总结.md │ ├── webpack 手写/ │ │ └── 实现打包器/ │ │ ├── bin/ │ │ │ └── packer.js │ │ ├── lib/ │ │ │ ├── Compilar.js │ │ │ └── template.ejs │ │ └── package.json │ ├── 待迁移-ES6/ │ │ └── class编译/ │ │ ├── extends.js │ │ └── 基础设施.js │ ├── 待迁移-强化代码/ │ │ ├── Observer.js │ │ ├── bind函数.js │ │ ├── deepClone.js │ │ ├── eventEmitter.js │ │ ├── flatten.js │ │ ├── index.html │ │ ├── lastEvent.js │ │ └── 发布订阅.js │ ├── 正则表达式/ │ │ └── 元字符.md │ ├── 练习-CSS面试部分/ │ │ ├── FlexIndex.html │ │ ├── Grid.css │ │ ├── Grid.html │ │ ├── flex.css │ │ ├── grid练习/ │ │ │ ├── index.htm.html │ │ │ └── 九宫格.css │ │ ├── 三角形.css │ │ ├── 外边距合并.css │ │ ├── 布局方案/ │ │ │ ├── 三栏布局.html │ │ │ └── 两栏布局.html │ │ └── 水平垂直居中/ │ │ ├── index.html │ │ └── 定宽高.css │ ├── 练习-JavaScript部分/ │ │ ├── AJAX.js │ │ ├── ES6 class编译/ │ │ │ └── extends.js │ │ ├── tools.js │ │ ├── 功能函数汇总/ │ │ │ ├── index.md │ │ │ ├── 函数深度比较.js │ │ │ ├── 函数节流.js │ │ │ ├── 函数防抖.js │ │ │ ├── 数组平拍.js │ │ │ ├── 浅拷贝和深拷贝.js │ │ │ └── 解析URL参数.js │ │ ├── 图片懒加载.js │ │ ├── 手写assign.js │ │ ├── 手写create.js │ │ ├── 手写instanceof.js │ │ ├── 手写new.js │ │ ├── 手写深拷贝.js │ │ ├── 继承/ │ │ │ ├── 1. 原型链继承.js │ │ │ ├── 2. 借用构造函数继承.js │ │ │ ├── 3. 组合继承.js │ │ │ ├── 4. 原型式继承.js │ │ │ └── 5. 寄生组合式继承.js │ │ ├── 请你实现一个LRU缓存算法.js │ │ └── 跨域.html │ ├── 练习-Nodejs/ │ │ ├── eventEmitter.js │ │ └── globalObj.js │ ├── 练习-实际开发/ │ │ └── 渲染10W节点/ │ │ ├── README.md │ │ ├── 傻循环.html │ │ └── 时间分片.html │ ├── 练习-数据结构算法刷题/ │ │ └── 链表/ │ │ ├── 160. 相交链表.js │ │ ├── 203. 移除链表元素.js │ │ ├── 4.插入有序链表.js │ │ ├── 445. 两数相加 II.js │ │ ├── 9. 两两交换链表中的节点.js │ │ ├── x. 回文链表.js │ │ ├── 两数相加/ │ │ │ ├── 2. 两数相加.js │ │ │ └── 两数相加.md │ │ ├── 临时.md │ │ ├── 分隔链表/ │ │ │ ├── 86. 分隔链表.js │ │ │ └── README.md │ │ ├── 合并两个有序链表/ │ │ │ ├── 21. 合并两个有序链表.js │ │ │ └── README.md │ │ ├── 环形链表/ │ │ │ ├── 141. 环形链表.js │ │ │ └── README.md │ │ ├── 翻转链表/ │ │ │ ├── 25. K 个一组翻转链表.js │ │ │ ├── 92. 反转链表 II.js │ │ │ ├── README.md │ │ │ └── 面试题24. 反转链表.js │ │ ├── 重排链表/ │ │ │ ├── 143. 重排链表.js │ │ │ └── README.md │ │ ├── 链表的中间结点/ │ │ │ ├── 876.链表中间节点.js │ │ │ └── README.md │ │ ├── 面试题18. 删除链表的节点.js │ │ └── 面试题22. 链表中倒数第k个节点.js │ ├── 组件/ │ │ └── vue-懒加载/ │ │ ├── Note.md │ │ └── v-lazy.js │ └── 记录-面试DEMO/ │ └── 01.md ├── 操作系统层面的调用栈管理?.md └── 浏览器历史管理.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .vscode .idea ================================================ FILE: .obsidian/app.json ================================================ { "vimMode": true, "attachmentFolderPath": "静态", "alwaysUpdateLinks": true, "promptDelete": false, "autoPairBrackets": true, "foldIndent": true, "defaultViewMode": "preview", "showLineNumber": true, "emacsyKeys": false, "readableLineLength": false, "legacyEditor": false, "livePreview": false, "foldHeading": true } ================================================ FILE: .obsidian/appearance.json ================================================ { "baseFontSize": 19, "theme": "obsidian", "translucency": false, "baseFontSizeAction": true, "cssTheme": "Atom" } ================================================ FILE: .obsidian/community-plugins.json ================================================ [ "file-explorer-note-count", "obsidian-outliner", "recent-files-obsidian", "table-editor-obsidian" ] ================================================ FILE: .obsidian/core-plugins.json ================================================ [ "file-explorer", "global-search", "switcher", "graph", "backlink", "page-preview", "templates", "note-composer", "command-palette", "starred", "markdown-importer", "outline", "word-count", "open-with-default-app", "workspaces", "file-recovery" ] ================================================ FILE: .obsidian/daily-notes.json ================================================ { "autorun": true, "folder": "log/2021", "template": "" } ================================================ FILE: .obsidian/graph.json ================================================ { "collapse-filter": false, "search": "path:/new", "showTags": false, "showAttachments": false, "hideUnresolved": true, "showOrphans": true, "collapse-color-groups": false, "colorGroups": [ { "query": "path:coding", "color": { "a": 1, "rgb": 14048348 } }, { "query": "path:/", "color": { "a": 1, "rgb": 11392604 } } ], "collapse-display": false, "showArrow": true, "textFadeMultiplier": 0, "nodeSizeMultiplier": 1.05488061797753, "lineSizeMultiplier": 2.87336397058824, "collapse-forces": false, "centerStrength": 0.545863970588235, "repelStrength": 10, "linkStrength": 0.635110294117647, "linkDistance": 250, "scale": 1.4056547396473347, "close": true } ================================================ FILE: .obsidian/hotkeys.json ================================================ { "table-editor-obsidian:format-all-tables": [ { "modifiers": [ "Alt", "Ctrl", "Shift" ], "key": "T" } ], "editor:toggle-fold": [ { "modifiers": [ "Ctrl", "Mod" ], "key": "-" } ] } ================================================ FILE: .obsidian/page-preview.json ================================================ { "preview": true } ================================================ FILE: .obsidian/plugins/file-explorer-note-count/data.json ================================================ { "showAllNumbers": true, "filterList": [ "md" ], "blacklist": false, "filterFolderNote": true } ================================================ FILE: .obsidian/plugins/file-explorer-note-count/main.js ================================================ /* THIS IS A GENERATED/BUNDLED FILE BY ROLLUP if you want to view the source visit the plugins github repository */ 'use strict'; var require$$0 = require('obsidian'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function createCommonjsModule(fn) { var module = { exports: {} }; return fn(module, module.exports), module.exports; } /* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source visit the plugins github repository */ var lib = createCommonjsModule(function (module, exports) { var a=Object.create;var n=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var g=Object.getPrototypeOf,f=Object.prototype.hasOwnProperty;var d=e=>n(e,"__esModule",{value:!0});var p=(e,o)=>{d(e);for(var t in o)n(e,t,{get:o[t],enumerable:!0});},u=(e,o,t)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of F(o))!f.call(e,r)&&r!=="default"&&n(e,r,{get:()=>o[r],enumerable:!(t=s(o,r))||t.enumerable});return e},T=e=>u(d(n(e!=null?a(g(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);p(exports,{NoteLoc:()=>l,getApi:()=>i,isPluginEnabled:()=>N,registerApi:()=>P});T(require$$0__default['default']);var l;(function(r){r[r.Index=0]="Index",r[r.Inside=1]="Inside",r[r.Outside=2]="Outside";})(l||(l={}));var i=e=>{var o;return e?(o=e.app.plugins.plugins["folder-note-core"])==null?void 0:o.api:window.FolderNoteAPIv0},N=e=>e.app.plugins.enabledPlugins.has("folder-note-core"),P=(e,o)=>(e.app.vault.on("folder-note:api-ready",o),i(e)); }); // 'path' module extracted from Node.js v8.11.1 (only the posix part) function assertPath(path) { if (typeof path !== 'string') { throw new TypeError('Path must be a string. Received ' + JSON.stringify(path)); } } // Resolves . and .. elements in a path with directory names function normalizeStringPosix(path, allowAboveRoot) { var res = ''; var lastSegmentLength = 0; var lastSlash = -1; var dots = 0; var code; for (var i = 0; i <= path.length; ++i) { if (i < path.length) code = path.charCodeAt(i); else if (code === 47 /*/*/) break; else code = 47 /*/*/; if (code === 47 /*/*/) { if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) { if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 /*.*/ || res.charCodeAt(res.length - 2) !== 46 /*.*/) { if (res.length > 2) { var lastSlashIndex = res.lastIndexOf('/'); if (lastSlashIndex !== res.length - 1) { if (lastSlashIndex === -1) { res = ''; lastSegmentLength = 0; } else { res = res.slice(0, lastSlashIndex); lastSegmentLength = res.length - 1 - res.lastIndexOf('/'); } lastSlash = i; dots = 0; continue; } } else if (res.length === 2 || res.length === 1) { res = ''; lastSegmentLength = 0; lastSlash = i; dots = 0; continue; } } if (allowAboveRoot) { if (res.length > 0) res += '/..'; else res = '..'; lastSegmentLength = 2; } } else { if (res.length > 0) res += '/' + path.slice(lastSlash + 1, i); else res = path.slice(lastSlash + 1, i); lastSegmentLength = i - lastSlash - 1; } lastSlash = i; dots = 0; } else if (code === 46 /*.*/ && dots !== -1) { ++dots; } else { dots = -1; } } return res; } function _format(sep, pathObject) { var dir = pathObject.dir || pathObject.root; var base = pathObject.base || (pathObject.name || '') + (pathObject.ext || ''); if (!dir) { return base; } if (dir === pathObject.root) { return dir + base; } return dir + sep + base; } var posix = { // path.resolve([from ...], to) resolve: function resolve() { var resolvedPath = ''; var resolvedAbsolute = false; var cwd; for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path; if (i >= 0) path = arguments[i]; else { if (cwd === undefined) cwd = process.cwd(); path = cwd; } assertPath(path); // Skip empty entries if (path.length === 0) { continue; } resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path.charCodeAt(0) === 47 /*/*/; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) // Normalize the path resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute); if (resolvedAbsolute) { if (resolvedPath.length > 0) return '/' + resolvedPath; else return '/'; } else if (resolvedPath.length > 0) { return resolvedPath; } else { return '.'; } }, normalize: function normalize(path) { assertPath(path); if (path.length === 0) return '.'; var isAbsolute = path.charCodeAt(0) === 47 /*/*/; var trailingSeparator = path.charCodeAt(path.length - 1) === 47 /*/*/; // Normalize the path path = normalizeStringPosix(path, !isAbsolute); if (path.length === 0 && !isAbsolute) path = '.'; if (path.length > 0 && trailingSeparator) path += '/'; if (isAbsolute) return '/' + path; return path; }, isAbsolute: function isAbsolute(path) { assertPath(path); return path.length > 0 && path.charCodeAt(0) === 47 /*/*/; }, join: function join() { if (arguments.length === 0) return '.'; var joined; for (var i = 0; i < arguments.length; ++i) { var arg = arguments[i]; assertPath(arg); if (arg.length > 0) { if (joined === undefined) joined = arg; else joined += '/' + arg; } } if (joined === undefined) return '.'; return posix.normalize(joined); }, relative: function relative(from, to) { assertPath(from); assertPath(to); if (from === to) return ''; from = posix.resolve(from); to = posix.resolve(to); if (from === to) return ''; // Trim any leading backslashes var fromStart = 1; for (; fromStart < from.length; ++fromStart) { if (from.charCodeAt(fromStart) !== 47 /*/*/) break; } var fromEnd = from.length; var fromLen = fromEnd - fromStart; // Trim any leading backslashes var toStart = 1; for (; toStart < to.length; ++toStart) { if (to.charCodeAt(toStart) !== 47 /*/*/) break; } var toEnd = to.length; var toLen = toEnd - toStart; // Compare paths to find the longest common path from root var length = fromLen < toLen ? fromLen : toLen; var lastCommonSep = -1; var i = 0; for (; i <= length; ++i) { if (i === length) { if (toLen > length) { if (to.charCodeAt(toStart + i) === 47 /*/*/) { // We get here if `from` is the exact base path for `to`. // For example: from='/foo/bar'; to='/foo/bar/baz' return to.slice(toStart + i + 1); } else if (i === 0) { // We get here if `from` is the root // For example: from='/'; to='/foo' return to.slice(toStart + i); } } else if (fromLen > length) { if (from.charCodeAt(fromStart + i) === 47 /*/*/) { // We get here if `to` is the exact base path for `from`. // For example: from='/foo/bar/baz'; to='/foo/bar' lastCommonSep = i; } else if (i === 0) { // We get here if `to` is the root. // For example: from='/foo'; to='/' lastCommonSep = 0; } } break; } var fromCode = from.charCodeAt(fromStart + i); var toCode = to.charCodeAt(toStart + i); if (fromCode !== toCode) break; else if (fromCode === 47 /*/*/) lastCommonSep = i; } var out = ''; // Generate the relative path based on the path difference between `to` // and `from` for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { if (i === fromEnd || from.charCodeAt(i) === 47 /*/*/) { if (out.length === 0) out += '..'; else out += '/..'; } } // Lastly, append the rest of the destination (`to`) path that comes after // the common path parts if (out.length > 0) return out + to.slice(toStart + lastCommonSep); else { toStart += lastCommonSep; if (to.charCodeAt(toStart) === 47 /*/*/) ++toStart; return to.slice(toStart); } }, _makeLong: function _makeLong(path) { return path; }, dirname: function dirname(path) { assertPath(path); if (path.length === 0) return '.'; var code = path.charCodeAt(0); var hasRoot = code === 47 /*/*/; var end = -1; var matchedSlash = true; for (var i = path.length - 1; i >= 1; --i) { code = path.charCodeAt(i); if (code === 47 /*/*/) { if (!matchedSlash) { end = i; break; } } else { // We saw the first non-path separator matchedSlash = false; } } if (end === -1) return hasRoot ? '/' : '.'; if (hasRoot && end === 1) return '//'; return path.slice(0, end); }, basename: function basename(path, ext) { if (ext !== undefined && typeof ext !== 'string') throw new TypeError('"ext" argument must be a string'); assertPath(path); var start = 0; var end = -1; var matchedSlash = true; var i; if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { if (ext.length === path.length && ext === path) return ''; var extIdx = ext.length - 1; var firstNonSlashEnd = -1; for (i = path.length - 1; i >= 0; --i) { var code = path.charCodeAt(i); if (code === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { start = i + 1; break; } } else { if (firstNonSlashEnd === -1) { // We saw the first non-path separator, remember this index in case // we need it if the extension ends up not matching matchedSlash = false; firstNonSlashEnd = i + 1; } if (extIdx >= 0) { // Try to match the explicit extension if (code === ext.charCodeAt(extIdx)) { if (--extIdx === -1) { // We matched the extension, so mark this as the end of our path // component end = i; } } else { // Extension does not match, so our result is the entire path // component extIdx = -1; end = firstNonSlashEnd; } } } } if (start === end) end = firstNonSlashEnd;else if (end === -1) end = path.length; return path.slice(start, end); } else { for (i = path.length - 1; i >= 0; --i) { if (path.charCodeAt(i) === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { start = i + 1; break; } } else if (end === -1) { // We saw the first non-path separator, mark this as the end of our // path component matchedSlash = false; end = i + 1; } } if (end === -1) return ''; return path.slice(start, end); } }, extname: function extname(path) { assertPath(path); var startDot = -1; var startPart = 0; var end = -1; var matchedSlash = true; // Track the state of characters (if any) we see before our first dot and // after any path separator we find var preDotState = 0; for (var i = path.length - 1; i >= 0; --i) { var code = path.charCodeAt(i); if (code === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { // We saw the first non-path separator, mark this as the end of our // extension matchedSlash = false; end = i + 1; } if (code === 46 /*.*/) { // If this is our first dot, mark it as the start of our extension if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension preDotState = -1; } } if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot preDotState === 0 || // The (right-most) trimmed path component is exactly '..' preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { return ''; } return path.slice(startDot, end); }, format: function format(pathObject) { if (pathObject === null || typeof pathObject !== 'object') { throw new TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject); } return _format('/', pathObject); }, parse: function parse(path) { assertPath(path); var ret = { root: '', dir: '', base: '', ext: '', name: '' }; if (path.length === 0) return ret; var code = path.charCodeAt(0); var isAbsolute = code === 47 /*/*/; var start; if (isAbsolute) { ret.root = '/'; start = 1; } else { start = 0; } var startDot = -1; var startPart = 0; var end = -1; var matchedSlash = true; var i = path.length - 1; // Track the state of characters (if any) we see before our first dot and // after any path separator we find var preDotState = 0; // Get non-dir info for (; i >= start; --i) { code = path.charCodeAt(i); if (code === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { // We saw the first non-path separator, mark this as the end of our // extension matchedSlash = false; end = i + 1; } if (code === 46 /*.*/) { // If this is our first dot, mark it as the start of our extension if (startDot === -1) startDot = i;else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension preDotState = -1; } } if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot preDotState === 0 || // The (right-most) trimmed path component is exactly '..' preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { if (end !== -1) { if (startPart === 0 && isAbsolute) ret.base = ret.name = path.slice(1, end);else ret.base = ret.name = path.slice(startPart, end); } } else { if (startPart === 0 && isAbsolute) { ret.name = path.slice(1, startDot); ret.base = path.slice(1, end); } else { ret.name = path.slice(startPart, startDot); ret.base = path.slice(startPart, end); } ret.ext = path.slice(startDot, end); } if (startPart > 0) ret.dir = path.slice(0, startPart - 1);else if (isAbsolute) ret.dir = '/'; return ret; }, sep: '/', delimiter: ':', win32: null, posix: null }; posix.posix = posix; var pathBrowserify = posix; const withSubfolderClass = 'oz-with-subfolder'; const showAllNumbersClass = 'oz-show-all-num'; const rootHiddenClass = 'oz-root-hidden'; const isFolder = (item) => item.file instanceof require$$0.TFolder; const iterateItems = (items, callback) => { for (const key in items) { if (!Object.prototype.hasOwnProperty.call(items, key)) continue; callback(items[key]); } }; const getParentPath = (src) => { if (src === '/') return null; const path = pathBrowserify.dirname(src); if (path === '.') return '/'; else return path; }; const equals = (arr1, arr2) => { // if the other array is a falsy value, return if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false; // compare lengths - can save a lot of time if (arr1.length != arr2.length) return false; return arr1.every((v, i) => v === arr2[i]); }; const isParent = (parent, child) => { if (child === parent) return false; if (parent === '/') parent = ''; if (child === '/') child = ''; const parentTokens = parent.split('/').filter((i) => i.length); return parentTokens.every((t, i) => child.split('/')[i] === t); }; function around(obj, factories) { const removers = Object.keys(factories).map(key => around1(obj, key, factories[key])); return removers.length === 1 ? removers[0] : function () { removers.forEach(r => r()); }; } function around1(obj, method, createWrapper) { const original = obj[method], hadOwn = obj.hasOwnProperty(method); let current = createWrapper(original); // Let our wrapper inherit static props from the wrapping method, // and the wrapping method, props from the original method if (original) Object.setPrototypeOf(current, original); Object.setPrototypeOf(wrapper, current); obj[method] = wrapper; // Return a callback to allow safe removal return remove; function wrapper(...args) { // If we have been deactivated and are no longer wrapped, remove ourselves if (current === original && obj[method] === wrapper) remove(); return current.apply(this, args); } function remove() { // If no other patches, just do a direct removal if (obj[method] === wrapper) { if (hadOwn) obj[method] = original; else delete obj[method]; } if (current === original) return; // Else pass future calls through, and remove wrapper from the prototype chain current = original; Object.setPrototypeOf(wrapper, original || Function); } } const countFolderChildren = (folder, filter) => { let count = 0; for (const af of folder.children) { if (filter(af)) count++; if (af instanceof require$$0.TFolder) count += countFolderChildren(af, filter); } return count; }; /** filter out all path that is the parent of existing path */ const filterParent = (pathList) => { const list = Array.from(pathList); list.sort(); for (let i = 0; i < list.length; i++) { if (i < list.length - 1 && (list[i] === list[i + 1] || isParent(list[i], list[i + 1]))) { list.shift(); i--; } } return new Set(list); }; /** get all parents and add to set if not exist */ const getAllParents = (path, set) => { let parent = getParentPath(path); while (parent && !set.has(parent)) { set.add(parent); parent = getParentPath(parent); } }; /** * Update folder count of target's parent */ const updateCount = (targetList, plugin) => { const set = filterParent(targetList); for (const path of targetList) { getAllParents(path, set); } // set count of path const { fileExplorer, fileFilter } = plugin; if (!fileExplorer) { console.error('fileExplorer missing'); return; } for (const path of set) { // check if path available if (!fileExplorer.fileItems[path]) continue; setCount(fileExplorer.fileItems[path], fileFilter); } // empty waitingList targetList.length = 0; }; const setupCount = (plugin, revert = false) => { if (!plugin.fileExplorer) throw new Error('fileExplorer not found'); iterateItems(plugin.fileExplorer.fileItems, (item) => { if (!isFolder(item)) return; if (revert) removeCount(item); else setCount(item, plugin.fileFilter); }); }; const setCount = (item, filter) => { // if (item.file.isRoot()) return; const count = countFolderChildren(item.file, filter); item.titleEl.dataset['count'] = count.toString(); item.titleEl.toggleClass(withSubfolderClass, Array.isArray(item.file.children) && item.file.children.some((af) => af instanceof require$$0.TFolder)); }; const removeCount = (item) => { if (item.titleEl.dataset['count']) delete item.titleEl.dataset['count']; item.titleEl.removeClass(withSubfolderClass); }; class VaultHandler { constructor(plugin) { this.waitingList = []; this.update = require$$0.debounce(() => updateCount(this.waitingList, this.plugin), 500, true); this.handler = (...args) => { var _a; for (const arg of args) { const path = arg instanceof require$$0.TAbstractFile ? arg.path : arg; this.waitingList.push((_a = getParentPath(path)) !== null && _a !== void 0 ? _a : '/'); } this.update(); }; this.registerVaultEvent = () => { this.plugin.registerEvent(this.vault.on('create', this.handler)); this.plugin.registerEvent(this.vault.on('rename', this.handler)); this.plugin.registerEvent(this.vault.on('delete', this.handler)); }; this.plugin = plugin; } get app() { return this.plugin.app; } get vault() { return this.plugin.app.vault; } } const DEFAULT_SETTINGS = { showAllNumbers: false, filterList: ['md'], blacklist: false, filterFolderNote: true, }; class FENoteCountSettingTab extends require$$0.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; } get showOnlyNoteValue() { const { settings } = this.plugin; return (settings.blacklist === DEFAULT_SETTINGS.blacklist && equals(settings.filterList, DEFAULT_SETTINGS.filterList)); } set showOnlyNoteValue(value) { const { blacklist, filterList } = DEFAULT_SETTINGS; this.plugin.settings.blacklist = blacklist; if (value) { // do deep copy this.plugin.settings.filterList = Array.from(filterList); } else { this.plugin.settings.filterList.length = 0; } } display() { let { containerEl } = this; containerEl.empty(); containerEl.createEl('h2', { text: 'File Explorer Note Count Settings', }); new require$$0.Setting(containerEl) .setName('Show All Numbers') .setDesc('Turn on this option if you want to see the number of notes even after you expand the collapsed folders') .addToggle((toggle) => toggle .setValue(this.plugin.settings.showAllNumbers) .onChange((value) => { document.body.toggleClass('oz-show-all-num', value); this.plugin.settings.showAllNumbers = value; this.plugin.saveSettings(); })); new require$$0.Setting(containerEl) .setName('Exclude Folder Note from Counts') .setDesc(createFragment((frag) => { frag.appendText('Only work with'); frag.createEl('a', { href: 'https://github.com/aidenlx/folder-note-core', text: 'Folder Note Core', }); frag.appendText(' Installed and Enabled'); })) .addToggle((toggle) => toggle .setValue(this.plugin.settings.filterFolderNote) .onChange((value) => { this.plugin.settings.filterFolderNote = value; this.plugin.saveSettings(); })); this.filterOpt(); } filterOpt() { new require$$0.Setting(this.containerEl) .setName('Show Only Markdown Notes') .setDesc('Turn off this option to choose file that should be counted') .addToggle((toggle) => toggle.setValue(this.showOnlyNoteValue).onChange((value) => { this.showOnlyNoteValue = value; this.plugin.reloadCount(); this.plugin.saveSettings(); this.display(); })); if (!this.showOnlyNoteValue) { new require$$0.Setting(this.containerEl) .setName('Filter List') .setDesc(createFragment((descEl) => { descEl.appendText('Extension list to include/exclude file during counting'); descEl.appendChild(document.createElement('br')); descEl.appendText('Separated by comma'); })) .addTextArea((text) => { const onChange = (value) => __awaiter(this, void 0, void 0, function* () { const list = value.split(',').map((v) => v.trim()); this.plugin.settings.filterList = list; this.plugin.reloadCount(); yield this.plugin.saveSettings(); }); text.setPlaceholder('Leave it empty to count all types of files'); text.setValue(this.plugin.settings.filterList.join(', ')).onChange(require$$0.debounce(onChange, 500, true)); text.inputEl.rows = 2; text.inputEl.cols = 25; }); new require$$0.Setting(this.containerEl) .setName('Enable Blacklist') .setDesc('Turn on this option to use Filter List to exclude files') .addToggle((toggle) => toggle .setValue(this.plugin.settings.blacklist) .onChange((value) => { this.plugin.settings.blacklist = value; this.plugin.reloadCount(); this.plugin.saveSettings(); })); } } } class FileExplorerNoteCount extends require$$0.Plugin { constructor() { super(...arguments); this.settings = DEFAULT_SETTINGS; this.vaultHandler = new VaultHandler(this); /** compatible with theme that hide root folder */ this.doHiddenRoot = (revert = false) => { if (!this.fileExplorer) { console.error('file-explorer not found'); return; } const root = this.fileExplorer.fileItems['/']; const styles = getComputedStyle(root.titleInnerEl); const setup = () => { const shouldHide = styles.display === 'none' || styles.color === 'rgba(0, 0, 0, 0)'; root.titleEl.toggleClass(rootHiddenClass, !revert && shouldHide); }; if (styles.display !== '') setup(); else { let count = 0; const doId = window.setInterval(() => { if (count > 10) { console.error('%o styles empty', root.titleInnerEl); window.clearInterval(doId); } else if (styles.display === '') { count++; } else { setup(); window.clearInterval(doId); } }, 100); } }; this.initialize = (revert = false) => { const doWithFileExplorer = (callback) => { let leaves, count = 0; const tryGetView = () => { leaves = this.app.workspace.getLeavesOfType('file-explorer'); if (leaves.length === 0) { if (count++ > 5) console.error('failed to get file-explorer'); else { console.log('file-explorer not found, retrying...'); setTimeout(tryGetView, 500); } } else { if (leaves.length > 1) console.warn('more then one file-explorer'); callback(leaves[0].view); } }; tryGetView(); }; const getViewHandler = (revert) => (view) => { this.fileExplorer = view; setupCount(this, revert); this.doHiddenRoot(revert); if (!revert) { this.registerEvent(this.app.workspace.on('css-change', this.doHiddenRoot)); this.vaultHandler.registerVaultEvent(); if (this.settings.showAllNumbers) document.body.addClass('oz-show-all-num'); } else { for (const el of document.getElementsByClassName(withSubfolderClass)) { el.removeClass(withSubfolderClass); } document.body.removeClass(showAllNumbersClass); } if (!revert) { // when file explorer is closed (workspace changed) // try to update fehanlder with new file explorer instance this.register(around(view, { onClose: (next) => function () { setTimeout(() => doWithFileExplorer(getViewHandler(false)), 1e3); return next.apply(this); }, })); } }; doWithFileExplorer(getViewHandler(revert)); }; } onload() { return __awaiter(this, void 0, void 0, function* () { console.log('loading FileExplorerNoteCount'); this.addSettingTab(new FENoteCountSettingTab(this.app, this)); yield this.loadSettings(); this.app.workspace.onLayoutReady(this.initialize); }); } onunload() { console.log('unloading FileExplorerNoteCount'); this.initialize(true); } loadSettings() { return __awaiter(this, void 0, void 0, function* () { this.settings = Object.assign(Object.assign({}, this.settings), (yield this.loadData())); }); } saveSettings() { return __awaiter(this, void 0, void 0, function* () { yield this.saveData(this.settings); }); } reloadCount() { setupCount(this); } get fileFilter() { let list = this.settings.filterList; return (af) => { if (af instanceof require$$0.TFile) { let api; if (this.settings.filterFolderNote && (api = lib.getApi(this)) && !!api.getFolderFromNote(af)) { return false; } const { extension: target } = af; // if list is empty, filter nothing if (list.length === 0) return true; else if (this.settings.blacklist) return !list.includes(target); else return list.includes(target); } else return false; }; } } module.exports = FileExplorerNoteCount; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsiLi4vbm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsIi4uL25vZGVfbW9kdWxlcy9AYWlkZW5seC9mb2xkZXItbm90ZS1jb3JlL2xpYi9pbmRleC5qcyIsIi4uL25vZGVfbW9kdWxlcy9wYXRoLWJyb3dzZXJpZnkvaW5kZXguanMiLCIuLi9zcmMvc3JjL21pc2MudHMiLCIuLi9ub2RlX21vZHVsZXMvbW9ua2V5LWFyb3VuZC9tanMvaW5kZXguanMiLCIuLi9zcmMvc3JjL2ZvbGRlci1jb3VudC50cyIsIi4uL3NyYy9zcmMvdmF1bHQtaGFuZGxlci50cyIsIi4uL3NyYy9zcmMvc2V0dGluZ3MudHMiLCIuLi9zcmMvc3JjL2ZlYy1tYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpudWxsLCJuYW1lcyI6WyJyZXF1aXJlJCQwIiwiVEZvbGRlciIsImRpcm5hbWUiLCJkZWJvdW5jZSIsIlRBYnN0cmFjdEZpbGUiLCJQbHVnaW5TZXR0aW5nVGFiIiwiU2V0dGluZyIsIlBsdWdpbiIsIlRGaWxlIiwiZ2V0QXBpIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQXVEQTtBQUNPLFNBQVMsU0FBUyxDQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRTtBQUM3RCxJQUFJLFNBQVMsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLE9BQU8sS0FBSyxZQUFZLENBQUMsR0FBRyxLQUFLLEdBQUcsSUFBSSxDQUFDLENBQUMsVUFBVSxPQUFPLEVBQUUsRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRTtBQUNoSCxJQUFJLE9BQU8sS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLE9BQU8sQ0FBQyxFQUFFLFVBQVUsT0FBTyxFQUFFLE1BQU0sRUFBRTtBQUMvRCxRQUFRLFNBQVMsU0FBUyxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7QUFDbkcsUUFBUSxTQUFTLFFBQVEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7QUFDdEcsUUFBUSxTQUFTLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxNQUFNLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDLEVBQUU7QUFDdEgsUUFBUSxJQUFJLENBQUMsQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsVUFBVSxJQUFJLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFDOUUsS0FBSyxDQUFDLENBQUM7QUFDUDs7Ozs7Ozs7Ozs7OztBQ3hFQSxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLHdCQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsT0FBTyxDQUFDLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxFQUFFLFVBQVUsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFVBQVUsRUFBRSxTQUFTLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFPLENBQUMsQ0FBQ0EsOEJBQW1CLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBUyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVB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 ================================================ FILE: .obsidian/plugins/file-explorer-note-count/manifest.json ================================================ { "id": "file-explorer-note-count", "name": "File Explorer Note Count", "version": "1.2.0", "minAppVersion": "0.11.13", "description": "The plugin helps you to see the number of notes under each folder within the file explorer.", "author": "Ozan Tellioglu", "authorUrl": "https://www.ozan.pl", "isDesktopOnly": false } ================================================ FILE: .obsidian/plugins/file-explorer-note-count/styles.css ================================================ .nav-folder-title[data-count]::after { content: attr(data-count); display: inline-block; position: relative; font-size: calc(100% * 0.8); margin-right: 4px; /* border-radius: 3px; */ padding: 2px 0; /* background-color: var(--background-secondary-alt); */ transition: opacity 100ms ease-in-out; } .nav-folder-title.oz-root-hidden[data-count]::after { display: none; } body:not(.oz-show-all-num) .nav-folder:not(.is-collapsed) > .nav-folder-title.oz-with-subfolder[data-count]:not([data-path='/'])::after { opacity: 0; } .nav-folder-title[data-count='0']::after { display: none; } .nav-folder-title-content { flex-grow: 1; } ================================================ FILE: .obsidian/plugins/obsidian-outliner/data.json ================================================ { "styleLists": true, "debug": false, "stickCursor": true, "betterEnter": true, "selectAll": true, "disableZoomNotification": false, "hideWarning": false } ================================================ FILE: .obsidian/plugins/obsidian-outliner/main.js ================================================ 'use strict'; var obsidian = require('obsidian'); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } const DEFAULT_SETTINGS = { styleLists: false, debug: false, stickCursor: true, betterEnter: true, selectAll: true, disableZoomNotification: false, hideWarning: false, }; class SettingsService { constructor(storage) { this.storage = storage; this.handlers = new Map(); } get styleLists() { return this.values.styleLists; } set styleLists(value) { this.set("styleLists", value); } get debug() { return this.values.debug; } set debug(value) { this.set("debug", value); } get stickCursor() { return this.values.stickCursor; } set stickCursor(value) { this.set("stickCursor", value); } get betterEnter() { return this.values.betterEnter; } set betterEnter(value) { this.set("betterEnter", value); } get selectAll() { return this.values.selectAll; } set selectAll(value) { this.set("selectAll", value); } get disableZoomNotification() { return this.values.disableZoomNotification; } set disableZoomNotification(value) { this.set("disableZoomNotification", value); } get hideWarning() { return this.values.hideWarning; } set hideWarning(value) { this.set("hideWarning", value); } onChange(key, cb) { if (!this.handlers.has(key)) { this.handlers.set(key, new Set()); } this.handlers.get(key).add(cb); } removeCallback(key, cb) { const handlers = this.handlers.get(key); if (handlers) { handlers.delete(cb); } } reset() { for (const [k, v] of Object.entries(DEFAULT_SETTINGS)) { this.set(k, v); } } load() { return __awaiter(this, void 0, void 0, function* () { this.values = Object.assign({}, DEFAULT_SETTINGS, yield this.storage.loadData()); }); } save() { return __awaiter(this, void 0, void 0, function* () { yield this.storage.saveData(this.values); }); } set(key, value) { this.values[key] = value; const callbacks = this.handlers.get(key); if (!callbacks) { return; } for (const cb of callbacks.values()) { cb(value); } } } class ObsidianOutlinerPluginSettingTab extends obsidian.PluginSettingTab { constructor(app, plugin, settings) { super(app, plugin); this.settings = settings; } display() { const { containerEl } = this; containerEl.empty(); new obsidian.Setting(containerEl) .setName("Improve the style of your lists") .setDesc("Styles are only compatible with built-in Obsidian themes and may not be compatible with other themes. Styles only work well with tab size 4.") .addToggle((toggle) => { toggle.setValue(this.settings.styleLists).onChange((value) => __awaiter(this, void 0, void 0, function* () { this.settings.styleLists = value; yield this.settings.save(); })); }); new obsidian.Setting(containerEl) .setName("Stick the cursor to the content") .setDesc("Don't let the cursor move to the bullet position.") .addToggle((toggle) => { toggle.setValue(this.settings.stickCursor).onChange((value) => __awaiter(this, void 0, void 0, function* () { this.settings.stickCursor = value; yield this.settings.save(); })); }); new obsidian.Setting(containerEl) .setName("Enhance the Enter key") .setDesc("Make the Enter key behave the same as other outliners.") .addToggle((toggle) => { toggle.setValue(this.settings.betterEnter).onChange((value) => __awaiter(this, void 0, void 0, function* () { this.settings.betterEnter = value; yield this.settings.save(); })); }); new obsidian.Setting(containerEl) .setName("Enhance the Ctrl+A or Cmd+A behavior") .setDesc("Press the hotkey once to select the current list item. Press the hotkey twice to select the entire list.") .addToggle((toggle) => { toggle.setValue(this.settings.selectAll).onChange((value) => __awaiter(this, void 0, void 0, function* () { this.settings.selectAll = value; yield this.settings.save(); })); }); new obsidian.Setting(containerEl) .setName("Disable notification about Obsidian Zoom plugin") .addToggle((toggle) => { toggle .setValue(this.settings.disableZoomNotification) .onChange((value) => __awaiter(this, void 0, void 0, function* () { this.settings.disableZoomNotification = value; yield this.settings.save(); })); }); new obsidian.Setting(containerEl) .setName("Hide the warning about four-space tabs") .addToggle((toggle) => { toggle.setValue(this.settings.hideWarning).onChange((value) => __awaiter(this, void 0, void 0, function* () { this.settings.hideWarning = value; yield this.settings.save(); })); }); new obsidian.Setting(containerEl) .setName("Debug mode") .setDesc("Open DevTools (Command+Option+I or Control+Shift+I) to copy the debug logs.") .addToggle((toggle) => { toggle.setValue(this.settings.debug).onChange((value) => __awaiter(this, void 0, void 0, function* () { this.settings.debug = value; yield this.settings.save(); })); }); } } class ObsidianService { constructor(app) { this.app = app; } getObsidianTabsSettigns() { return Object.assign({ useTab: true, tabSize: 4 }, this.app.vault.config); } getObsidianFoldSettigns() { return Object.assign({ foldIndent: false }, this.app.vault.config); } getActiveLeafDisplayText() { return this.app.workspace.activeLeaf.getDisplayText(); } createCommandCallback(cb) { return () => { const view = this.app.workspace.getActiveViewOfType(obsidian.MarkdownView); if (!view) { return; } const editor = view.sourceMode.cmEditor; const shouldStopPropagation = cb(editor); if (!shouldStopPropagation && window.event && window.event.type === "keydown") { editor.triggerOnKeyDown(window.event); } }; } } function cmpPos(a, b) { return a.line - b.line || a.ch - b.ch; } function maxPos(a, b) { return cmpPos(a, b) < 0 ? b : a; } function minPos(a, b) { return cmpPos(a, b) < 0 ? a : b; } class List { constructor(root, indent, bullet, spaceAfterBullet, firstLine, folded) { this.root = root; this.indent = indent; this.bullet = bullet; this.spaceAfterBullet = spaceAfterBullet; this.folded = folded; this.parent = null; this.children = []; this.notesIndent = null; this.lines = []; this.lines.push(firstLine); } getNotesIndent() { return this.notesIndent; } setNotesIndent(notesIndent) { if (this.notesIndent !== null) { throw new Error(`Notes indent already provided`); } this.notesIndent = notesIndent; } addLine(text) { if (this.notesIndent === null) { throw new Error(`Unable to add line, notes indent should be provided first`); } this.lines.push(text); } replaceLines(lines) { if (lines.length > 1 && this.notesIndent === null) { throw new Error(`Unable to add line, notes indent should be provided first`); } this.lines = lines; } getLineCount() { return this.lines.length; } getRoot() { return this.root; } getChildren() { return this.children.concat(); } getLinesInfo() { const startLine = this.root.getContentLinesRangeOf(this)[0]; return this.lines.map((row, i) => { const line = startLine + i; const startCh = i === 0 ? this.getContentStartCh() : this.notesIndent.length; const endCh = startCh + row.length; return { text: row, from: { line, ch: startCh }, to: { line, ch: endCh }, }; }); } getLines() { return this.lines.concat(); } getFirstLineContentStart() { const startLine = this.root.getContentLinesRangeOf(this)[0]; return { line: startLine, ch: this.getContentStartCh(), }; } getLastLineContentEnd() { const endLine = this.root.getContentLinesRangeOf(this)[1]; const endCh = this.lines.length === 1 ? this.getContentStartCh() + this.lines[0].length : this.notesIndent.length + this.lines[this.lines.length - 1].length; return { line: endLine, ch: endCh, }; } getContentStartCh() { return this.indent.length + this.bullet.length + 1; } isFolded() { if (this.folded) { return true; } if (this.parent) { return this.parent.isFolded(); } return false; } isFoldRoot() { let parent = this.getParent(); while (parent) { if (parent.folded) { return false; } parent = parent.getParent(); } return this.folded; } getLevel() { if (!this.parent) { return 0; } return this.parent.getLevel() + 1; } unindentContent(from, till) { this.indent = this.indent.slice(0, from) + this.indent.slice(till); if (this.notesIndent !== null) { this.notesIndent = this.notesIndent.slice(0, from) + this.notesIndent.slice(till); } for (const child of this.children) { child.unindentContent(from, till); } } indentContent(indentPos, indentChars) { this.indent = this.indent.slice(0, indentPos) + indentChars + this.indent.slice(indentPos); if (this.notesIndent !== null) { this.notesIndent = this.notesIndent.slice(0, indentPos) + indentChars + this.notesIndent.slice(indentPos); } for (const child of this.children) { child.indentContent(indentPos, indentChars); } } getFirstLineIndent() { return this.indent; } getBullet() { return this.bullet; } getSpaceAfterBullet() { return this.spaceAfterBullet; } replateBullet(bullet) { this.bullet = bullet; } getParent() { return this.parent; } addBeforeAll(list) { this.children.unshift(list); list.parent = this; } addAfterAll(list) { this.children.push(list); list.parent = this; } removeChild(list) { const i = this.children.indexOf(list); this.children.splice(i, 1); list.parent = null; } addBefore(before, list) { const i = this.children.indexOf(before); this.children.splice(i, 0, list); list.parent = this; } addAfter(before, list) { const i = this.children.indexOf(before); this.children.splice(i + 1, 0, list); list.parent = this; } getPrevSiblingOf(list) { const i = this.children.indexOf(list); return i > 0 ? this.children[i - 1] : null; } getNextSiblingOf(list) { const i = this.children.indexOf(list); return i >= 0 && i < this.children.length ? this.children[i + 1] : null; } isEmpty() { return this.children.length === 0; } print() { let res = ""; for (let i = 0; i < this.lines.length; i++) { res += i === 0 ? this.indent + this.bullet + this.spaceAfterBullet : this.notesIndent; res += this.lines[i]; res += "\n"; } for (const child of this.children) { res += child.print(); } return res; } } class Root { constructor(start, end, selections) { this.start = start; this.end = end; this.rootList = new List(this, "", "", "", "", false); this.selections = []; this.replaceSelections(selections); } getRootList() { return this.rootList; } getRange() { return [Object.assign({}, this.start), Object.assign({}, this.end)]; } getSelections() { return this.selections.map((s) => ({ anchor: Object.assign({}, s.anchor), head: Object.assign({}, s.head), })); } hasSingleCursor() { if (!this.hasSingleSelection()) { return false; } const selection = this.selections[0]; return (selection.anchor.line === selection.head.line && selection.anchor.ch === selection.head.ch); } hasSingleSelection() { return this.selections.length === 1; } getCursor() { return Object.assign({}, this.selections[this.selections.length - 1].head); } replaceCursor(cursor) { this.selections = [{ anchor: cursor, head: cursor }]; } replaceSelections(selections) { if (selections.length < 1) { throw new Error(`Unable to create Root without selections`); } this.selections = selections; } getListUnderCursor() { return this.getListUnderLine(this.getCursor().line); } getListUnderLine(line) { if (line < this.start.line || line > this.end.line) { return; } let result = null; let index = this.start.line; const visitArr = (ll) => { for (const l of ll) { const listFromLine = index; const listTillLine = listFromLine + l.getLineCount() - 1; if (line >= listFromLine && line <= listTillLine) { result = l; } else { index = listTillLine + 1; visitArr(l.getChildren()); } if (result !== null) { return; } } }; visitArr(this.rootList.getChildren()); return result; } getContentLinesRangeOf(list) { let result = null; let line = this.start.line; const visitArr = (ll) => { for (const l of ll) { const listFromLine = line; const listTillLine = listFromLine + l.getLineCount() - 1; if (l === list) { result = [listFromLine, listTillLine]; } else { line = listTillLine + 1; visitArr(l.getChildren()); } if (result !== null) { return; } } }; visitArr(this.rootList.getChildren()); return result; } getChildren() { return this.rootList.getChildren(); } print() { let res = ""; for (const child of this.rootList.getChildren()) { res += child.print(); } return res.replace(/\n$/, ""); } } const bulletSign = `(?:[-*+]|\\d+\\.)`; const listItemWithoutSpacesRe = new RegExp(`^${bulletSign}( |\t)`); const listItemRe = new RegExp(`^[ \t]*${bulletSign}( |\t)`); const stringWithSpacesRe = new RegExp(`^[ \t]+`); const parseListItemRe = new RegExp(`^([ \t]*)(${bulletSign})( |\t)(.*)$`); class ListsService { constructor(loggerService, obsidianService) { this.loggerService = loggerService; this.obsidianService = obsidianService; } evalOperation(root, op, editor) { op.perform(); if (op.shouldUpdate()) { this.applyChanges(editor, root); } return { shouldUpdate: op.shouldUpdate(), shouldStopPropagation: op.shouldStopPropagation(), }; } performOperation(cb, editor, cursor = editor.getCursor()) { const root = this.parseList(editor, cursor); if (!root) { return { shouldUpdate: false, shouldStopPropagation: false }; } const op = cb(root); return this.evalOperation(root, op, editor); } parseList(editor, cursor = editor.getCursor()) { const d = this.loggerService.bind("parseList"); const error = (msg) => { d(msg); return null; }; const line = editor.getLine(cursor.line); let listLookingPos = null; if (this.isListItem(line)) { listLookingPos = cursor.line; } else if (this.isLineWithIndent(line)) { let listLookingPosSearch = cursor.line - 1; while (listLookingPosSearch >= editor.firstLine()) { const line = editor.getLine(listLookingPosSearch); if (this.isListItem(line)) { listLookingPos = listLookingPosSearch; break; } else if (this.isLineWithIndent(line)) { listLookingPosSearch--; } else { break; } } } if (listLookingPos == null) { return null; } let listStartLine = null; let listStartLineLookup = listLookingPos; while (listStartLineLookup >= editor.firstLine()) { const line = editor.getLine(listStartLineLookup); if (!this.isListItem(line) && !this.isLineWithIndent(line)) { break; } if (this.isListItemWithoutSpaces(line)) { listStartLine = listStartLineLookup; } listStartLineLookup--; } if (listStartLine === null) { return null; } let listEndLine = listLookingPos; let listEndLineLookup = listLookingPos; while (listEndLineLookup <= editor.lastLine()) { const line = editor.getLine(listEndLineLookup); if (!this.isListItem(line) && !this.isLineWithIndent(line)) { break; } if (!this.isEmptyLine(line)) { listEndLine = listEndLineLookup; } listEndLineLookup++; } if (listStartLine > cursor.line || listEndLine < cursor.line) { return null; } const root = new Root({ line: listStartLine, ch: 0 }, { line: listEndLine, ch: editor.getLine(listEndLine).length }, editor.listSelections().map((r) => ({ anchor: { line: r.anchor.line, ch: r.anchor.ch }, head: { line: r.head.line, ch: r.head.ch }, }))); let currentParent = root.getRootList(); let currentList = null; let currentIndent = ""; for (let l = listStartLine; l <= listEndLine; l++) { const line = editor.getLine(l); const matches = parseListItemRe.exec(line); if (matches) { const [_, indent, bullet, spaceAfterBullet, content] = matches; const compareLength = Math.min(currentIndent.length, indent.length); const indentSlice = indent.slice(0, compareLength); const currentIndentSlice = currentIndent.slice(0, compareLength); if (indentSlice !== currentIndentSlice) { const expected = currentIndentSlice .replace(/ /g, "S") .replace(/\t/g, "T"); const got = indentSlice.replace(/ /g, "S").replace(/\t/g, "T"); return error(`Unable to parse list: expected indent "${expected}", got "${got}"`); } if (indent.length > currentIndent.length) { currentParent = currentList; currentIndent = indent; } else if (indent.length < currentIndent.length) { while (currentParent.getFirstLineIndent().length >= indent.length && currentParent.getParent()) { currentParent = currentParent.getParent(); } currentIndent = indent; } const folded = !!editor.isFolded({ line: Math.min(l + 1, listEndLine), ch: 0, }); currentList = new List(root, indent, bullet, spaceAfterBullet, content, folded); currentParent.addAfterAll(currentList); } else if (this.isLineWithIndent(line)) { if (!currentList) { return error(`Unable to parse list: expected list item, got empty line`); } const indentToCheck = currentList.getNotesIndent() || currentIndent; if (line.indexOf(indentToCheck) !== 0) { const expected = indentToCheck.replace(/ /g, "S").replace(/\t/g, "T"); const got = line .match(/^[ \t]*/)[0] .replace(/ /g, "S") .replace(/\t/g, "T"); return error(`Unable to parse list: expected indent "${expected}", got "${got}"`); } if (!currentList.getNotesIndent()) { const matches = line.match(/^[ \t]+/); if (!matches || matches[0].length <= currentIndent.length) { return error(`Unable to parse list: expected some indent, got no indent`); } currentList.setNotesIndent(matches[0]); } currentList.addLine(line.slice(currentList.getNotesIndent().length)); } else { return error(`Unable to parse list: expected list item or note, got "${line}"`); } } return root; } applyChanges(editor, root) { const rootRange = root.getRange(); const oldString = editor.getRange(rootRange[0], rootRange[1]); const newString = root.print(); const fromLine = rootRange[0].line; const toLine = rootRange[1].line; for (let l = fromLine; l <= toLine; l++) { editor.foldCode(l, null, "unfold"); } let changeFrom = Object.assign({}, rootRange[0]); let changeTo = Object.assign({}, rootRange[1]); let oldTmp = oldString; let newTmp = newString; while (true) { const nlIndex = oldTmp.indexOf("\n"); if (nlIndex < 0) { break; } const oldLine = oldTmp.slice(0, nlIndex + 1); const newLine = newTmp.slice(0, oldLine.length); if (oldLine !== newLine) { break; } changeFrom.line++; oldTmp = oldTmp.slice(oldLine.length); newTmp = newTmp.slice(oldLine.length); } while (true) { const nlIndex = oldTmp.lastIndexOf("\n"); if (nlIndex < 0) { break; } const oldLine = oldTmp.slice(nlIndex); const newLine = newTmp.slice(-oldLine.length); if (oldLine !== newLine) { break; } oldTmp = oldTmp.slice(0, -oldLine.length); newTmp = newTmp.slice(0, -oldLine.length); const nlIndex2 = oldTmp.lastIndexOf("\n"); changeTo.ch = nlIndex2 >= 0 ? oldTmp.length - nlIndex2 - 1 : oldTmp.length; changeTo.line--; } if (oldTmp !== newTmp) { editor.replaceRange(newTmp, changeFrom, changeTo); } editor.setSelections(root.getSelections()); // TODO: lines could be different because of deletetion for (let l = fromLine; l <= toLine; l++) { const line = root.getListUnderLine(l); if (line && line.isFoldRoot()) { editor.foldCode(l); } } } getDefaultIndentChars() { const { useTab, tabSize } = this.obsidianService.getObsidianTabsSettigns(); return useTab ? "\t" : new Array(tabSize).fill(" ").join(""); } isEmptyLine(line) { return line.length === 0; } isLineWithIndent(line) { return stringWithSpacesRe.test(line); } isListItem(line) { return listItemRe.test(line); } isListItemWithoutSpaces(line) { return listItemWithoutSpacesRe.test(line); } } class LoggerService { constructor(settingsService) { this.settingsService = settingsService; } log(method, ...args) { if (!this.settingsService.debug) { return; } console.info(method, ...args); } bind(method) { return (...args) => this.log(method, ...args); } } const STATUS_BAR_TEXT = `Outliner styles only work with four-space tabs. Please check Obsidian settings.`; class ListsStylesFeature { constructor(plugin, settingsService, obsidianService) { this.plugin = plugin; this.settingsService = settingsService; this.obsidianService = obsidianService; this.onStyleListsSettingChange = (styleLists) => { if (styleLists) { this.addListsStyles(); } else { this.removeListsStyles(); } }; } load() { return __awaiter(this, void 0, void 0, function* () { if (this.settingsService.styleLists) { this.addListsStyles(); } this.settingsService.onChange("styleLists", this.onStyleListsSettingChange); this.addStatusBarText(); this.startStatusBarInterval(); }); } unload() { return __awaiter(this, void 0, void 0, function* () { clearInterval(this.interval); if (this.statusBarText.parentElement) { this.statusBarText.parentElement.removeChild(this.statusBarText); } this.settingsService.removeCallback("styleLists", this.onStyleListsSettingChange); this.removeListsStyles(); }); } startStatusBarInterval() { let visible = false; this.interval = window.setInterval(() => { const { tabSize } = this.obsidianService.getObsidianTabsSettigns(); const shouldBeVisible = this.settingsService.styleLists && !(tabSize === 4) && !this.settingsService.hideWarning; if (shouldBeVisible && !visible) { this.statusBarText.style.display = "block"; visible = true; } else if (!shouldBeVisible && visible) { this.statusBarText.style.display = "none"; visible = false; } }, 1000); } addStatusBarText() { this.statusBarText = this.plugin.addStatusBarItem(); this.statusBarText.style.color = "red"; this.statusBarText.style.display = "none"; this.statusBarText.setText(STATUS_BAR_TEXT); } addListsStyles() { document.body.classList.add("outliner-plugin-bls"); } removeListsStyles() { document.body.classList.remove("outliner-plugin-bls"); } } function recalculateNumericBullets(root) { function visit(parent) { let index = 1; for (const child of parent.getChildren()) { if (/\d+\./.test(child.getBullet())) { child.replateBullet(`${index++}.`); } visit(child); } } visit(root); } class MoveLeftOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; const list = root.getListUnderCursor(); const parent = list.getParent(); const grandParent = parent.getParent(); if (!grandParent) { return; } this.updated = true; const listStartLineBefore = root.getContentLinesRangeOf(list)[0]; const indentRmFrom = parent.getFirstLineIndent().length; const indentRmTill = list.getFirstLineIndent().length; parent.removeChild(list); grandParent.addAfter(parent, list); list.unindentContent(indentRmFrom, indentRmTill); const listStartLineAfter = root.getContentLinesRangeOf(list)[0]; const lineDiff = listStartLineAfter - listStartLineBefore; const chDiff = indentRmTill - indentRmFrom; const cursor = root.getCursor(); root.replaceCursor({ line: cursor.line + lineDiff, ch: cursor.ch - chDiff, }); recalculateNumericBullets(root); } } function isEmptyLineOrEmptyCheckbox(line) { return line === "" || line === "[ ] "; } class OutdentIfLineIsEmptyOperation { constructor(root) { this.root = root; this.moveLeftOp = new MoveLeftOperation(root); } shouldStopPropagation() { return this.moveLeftOp.shouldStopPropagation(); } shouldUpdate() { return this.moveLeftOp.shouldUpdate(); } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } const list = root.getListUnderCursor(); const lines = list.getLines(); if (lines.length > 1 || !isEmptyLineOrEmptyCheckbox(lines[0]) || list.getLevel() === 1) { return; } this.moveLeftOp.perform(); } } function isEnter$1(e) { return ((e.keyCode === 13 || e.code === "Enter") && e.shiftKey === false && e.metaKey === false && e.altKey === false && e.ctrlKey === false); } class EnterOutdentIfLineIsEmptyFeature { constructor(plugin, settingsService, listsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.imeService = imeService; this.onKeyDown = (cm, e) => { if (!this.settingsService.betterEnter || !isEnter$1(e) || this.imeService.isIMEOpened()) { return; } const { shouldStopPropagation } = this.listsService.performOperation((root) => new OutdentIfLineIsEmptyOperation(root), cm); if (shouldStopPropagation) { e.preventDefault(); e.stopPropagation(); } }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } class CreateNewItemOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } const list = root.getListUnderCursor(); const lines = list.getLinesInfo(); if (lines.length === 1 && isEmptyLineOrEmptyCheckbox(lines[0].text)) { return; } const cursor = root.getCursor(); const lineUnderCursor = lines.find((l) => l.from.line === cursor.line); if (cursor.ch < lineUnderCursor.from.ch) { return; } const { oldLines, newLines } = lines.reduce((acc, line) => { if (cursor.line > line.from.line) { acc.oldLines.push(line.text); } else if (cursor.line === line.from.line) { const a = line.text.slice(0, cursor.ch - line.from.ch); const b = line.text.slice(cursor.ch - line.from.ch); acc.oldLines.push(a); acc.newLines.push(b); } else if (cursor.line < line.from.line) { acc.newLines.push(line.text); } return acc; }, { oldLines: [], newLines: [], }); const codeBlockBacticks = oldLines.join("\n").split("```").length - 1; const isInsideCodeblock = codeBlockBacticks > 0 && codeBlockBacticks % 2 !== 0; if (isInsideCodeblock) { return; } this.stopPropagation = true; this.updated = true; const endPos = list.getLastLineContentEnd(); const onChildLevel = !list.isEmpty() && cursor.line === endPos.line && cursor.ch === endPos.ch; const indent = onChildLevel ? list.getChildren()[0].getFirstLineIndent() : list.getFirstLineIndent(); const bullet = onChildLevel ? list.getChildren()[0].getBullet() : list.getBullet(); const spaceAfterBullet = onChildLevel ? list.getChildren()[0].getSpaceAfterBullet() : list.getSpaceAfterBullet(); const prefix = oldLines[0].match(/^\[[ x]\]/) ? "[ ] " : ""; const newList = new List(list.getRoot(), indent, bullet, spaceAfterBullet, prefix + newLines.shift(), false); if (newLines.length > 0) { newList.setNotesIndent(list.getNotesIndent()); for (const line of newLines) { newList.addLine(line); } } if (onChildLevel) { list.addBeforeAll(newList); } else { const children = list.getChildren(); for (const child of children) { list.removeChild(child); newList.addAfterAll(child); } list.getParent().addAfter(list, newList); } list.replaceLines(oldLines); const newListStart = newList.getFirstLineContentStart(); root.replaceCursor({ line: newListStart.line, ch: newListStart.ch + prefix.length, }); recalculateNumericBullets(root); } } function isEnter(e) { return ((e.keyCode === 13 || e.code === "Enter") && e.shiftKey === false && e.metaKey === false && e.altKey === false && e.ctrlKey === false); } class EnterShouldCreateNewItemFeature { constructor(plugin, settingsService, listsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.imeService = imeService; this.onKeyDown = (cm, e) => { if (!this.settingsService.betterEnter || !isEnter(e) || this.imeService.isIMEOpened()) { return; } const { shouldStopPropagation } = this.listsService.performOperation((root) => new CreateNewItemOperation(root), cm); if (shouldStopPropagation) { e.preventDefault(); e.stopPropagation(); } }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } class MoveCursorToPreviousUnfoldedLineOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } const list = this.root.getListUnderCursor(); const cursor = this.root.getCursor(); const lines = list.getLinesInfo(); const lineNo = lines.findIndex((l) => cursor.ch === l.from.ch && cursor.line === l.from.line); if (lineNo === 0) { this.moveCursorToPreviousUnfoldedItem(root, cursor); } else if (lineNo > 0) { this.moveCursorToPreviousNoteLine(root, lines, lineNo); } } moveCursorToPreviousNoteLine(root, lines, lineNo) { this.stopPropagation = true; this.updated = true; root.replaceCursor(lines[lineNo - 1].to); } moveCursorToPreviousUnfoldedItem(root, cursor) { const prev = root.getListUnderLine(cursor.line - 1); if (!prev) { return; } this.stopPropagation = true; this.updated = true; if (prev.isFolded()) { let foldRoot = prev; while (!foldRoot.isFoldRoot()) { foldRoot = foldRoot.getParent(); } const firstLineEnd = foldRoot.getLinesInfo()[0].to; root.replaceCursor(firstLineEnd); } else { root.replaceCursor(prev.getLastLineContentEnd()); } } } function isArrowLeft(e) { return ((e.keyCode === 37 || e.code === "ArrowLeft") && e.shiftKey === false && e.metaKey === false && e.altKey === false && e.ctrlKey === false); } function isCtrlArrowLeft(e) { return ((e.keyCode === 37 || e.code === "ArrowLeft") && e.shiftKey === false && e.metaKey === false && e.altKey === false && e.ctrlKey === true); } class MoveCursorToPreviousUnfoldedLineFeature { constructor(plugin, settingsService, listsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.imeService = imeService; this.onKeyDown = (cm, event) => { if (!this.settingsService.stickCursor || this.imeService.isIMEOpened()) { return; } if (isArrowLeft(event) || (!obsidian.Platform.isMacOS && isCtrlArrowLeft(event))) { const { shouldStopPropagation } = this.listsService.performOperation((root) => new MoveCursorToPreviousUnfoldedLineOperation(root), cm); if (shouldStopPropagation) { event.preventDefault(); event.stopPropagation(); } } }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } class EnsureCursorInListContentOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; const cursor = root.getCursor(); const list = root.getListUnderCursor(); const contentStart = list.getFirstLineContentStart(); const linePrefix = contentStart.line === cursor.line ? contentStart.ch : list.getNotesIndent().length; if (cursor.ch < linePrefix) { this.updated = true; root.replaceCursor({ line: cursor.line, ch: linePrefix, }); } } } class EnsureCursorIsInUnfoldedLineOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; const cursor = root.getCursor(); const list = root.getListUnderCursor(); if (!list.isFolded()) { return; } let foldRoot = list; while (!foldRoot.isFoldRoot()) { foldRoot = foldRoot.getParent(); } const firstLineEnd = foldRoot.getLinesInfo()[0].to; if (cursor.line > firstLineEnd.line) { this.updated = true; root.replaceCursor(firstLineEnd); } } } class EnsureCursorInListContentFeature { constructor(plugin, settingsService, listsService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.handleCursorActivity = (cm) => { if (!this.settingsService.stickCursor) { return; } this.listsService.performOperation((root) => new EnsureCursorIsInUnfoldedLineOperation(root), cm); this.listsService.performOperation((root) => new EnsureCursorInListContentOperation(root), cm); }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("cursorActivity", this.handleCursorActivity); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("cursorActivity", this.handleCursorActivity); }); }); } } class DeleteAndMergeWithPreviousLineOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } const list = root.getListUnderCursor(); const cursor = root.getCursor(); const lines = list.getLinesInfo(); const lineNo = lines.findIndex((l) => cursor.ch === l.from.ch && cursor.line === l.from.line); if (lineNo === 0) { this.mergeWithPreviousItem(root, cursor, list); } else if (lineNo > 0) { this.mergeNotes(root, cursor, list, lines, lineNo); } } mergeNotes(root, cursor, list, lines, lineNo) { this.stopPropagation = true; this.updated = true; const prevLineNo = lineNo - 1; root.replaceCursor({ line: cursor.line - 1, ch: lines[prevLineNo].text.length + lines[prevLineNo].from.ch, }); lines[prevLineNo].text += lines[lineNo].text; lines.splice(lineNo, 1); list.replaceLines(lines.map((l) => l.text)); } mergeWithPreviousItem(root, cursor, list) { if (root.getChildren()[0] === list && list.getChildren().length === 0) { return; } this.stopPropagation = true; const prev = root.getListUnderLine(cursor.line - 1); if (!prev) { return; } const bothAreEmpty = prev.isEmpty() && list.isEmpty(); const prevIsEmptyAndSameLevel = prev.isEmpty() && !list.isEmpty() && prev.getLevel() == list.getLevel(); const listIsEmptyAndPrevIsParent = list.isEmpty() && prev.getLevel() == list.getLevel() - 1; if (bothAreEmpty || prevIsEmptyAndSameLevel || listIsEmptyAndPrevIsParent) { this.updated = true; const parent = list.getParent(); const prevEnd = prev.getLastLineContentEnd(); if (!prev.getNotesIndent() && list.getNotesIndent()) { prev.setNotesIndent(prev.getFirstLineIndent() + list.getNotesIndent().slice(list.getFirstLineIndent().length)); } const oldLines = prev.getLines(); const newLines = list.getLines(); oldLines[oldLines.length - 1] += newLines[0]; const resultLines = oldLines.concat(newLines.slice(1)); prev.replaceLines(resultLines); parent.removeChild(list); for (const c of list.getChildren()) { list.removeChild(c); prev.addAfterAll(c); } root.replaceCursor(prevEnd); recalculateNumericBullets(root); } } } class DeleteAndMergeWithNextLineOperation { constructor(root) { this.root = root; this.deleteAndMergeWithPrevious = new DeleteAndMergeWithPreviousLineOperation(root); } shouldStopPropagation() { return this.deleteAndMergeWithPrevious.shouldStopPropagation(); } shouldUpdate() { return this.deleteAndMergeWithPrevious.shouldUpdate(); } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } const list = root.getListUnderCursor(); const cursor = root.getCursor(); const lines = list.getLinesInfo(); const lineNo = lines.findIndex((l) => cursor.ch === l.to.ch && cursor.line === l.to.line); if (lineNo === lines.length - 1) { const nextLine = lines[lineNo].to.line + 1; const nextList = root.getListUnderLine(nextLine); root.replaceCursor(nextList.getFirstLineContentStart()); this.deleteAndMergeWithPrevious.perform(); } else if (lineNo >= 0) { root.replaceCursor(lines[lineNo + 1].from); this.deleteAndMergeWithPrevious.perform(); } } } class DeleteTillLineStartOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; this.updated = true; const cursor = root.getCursor(); const list = root.getListUnderCursor(); const lines = list.getLinesInfo(); const lineNo = lines.findIndex((l) => l.from.line === cursor.line); lines[lineNo].text = lines[lineNo].text.slice(cursor.ch - lines[lineNo].from.ch); list.replaceLines(lines.map((l) => l.text)); root.replaceCursor(lines[lineNo].from); } } function isBackspace(e) { return ((e.keyCode === 8 || e.code === "Backspace") && e.shiftKey === false && e.metaKey === false && e.altKey === false && e.ctrlKey === false); } function isCmdBackspace(e) { return ((e.keyCode === 8 || e.code === "Backspace") && e.shiftKey === false && e.metaKey === true && e.altKey === false && e.ctrlKey === false); } function isDelete(e) { return ((e.keyCode === 46 || e.code === "Delete") && e.shiftKey === false && e.metaKey === false && e.altKey === false && e.ctrlKey === false); } class DeleteShouldIgnoreBulletsFeature { constructor(plugin, settingsService, listsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.imeService = imeService; this.onKeyDown = (cm, event) => { if (!this.settingsService.stickCursor || this.imeService.isIMEOpened()) { return; } if (isBackspace(event)) { const { shouldStopPropagation } = this.listsService.performOperation((root) => new DeleteAndMergeWithPreviousLineOperation(root), cm); if (shouldStopPropagation) { event.preventDefault(); event.stopPropagation(); } } if (obsidian.Platform.isMacOS && isCmdBackspace(event)) { const { shouldStopPropagation } = this.listsService.performOperation((root) => new DeleteTillLineStartOperation(root), cm); if (shouldStopPropagation) { event.preventDefault(); event.stopPropagation(); } } if (isDelete(event)) { const { shouldStopPropagation } = this.listsService.performOperation((root) => new DeleteAndMergeWithNextLineOperation(root), cm); if (shouldStopPropagation) { event.preventDefault(); event.stopPropagation(); } } }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } class SelectTillLineStartOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; this.updated = true; const cursor = root.getCursor(); const list = root.getListUnderCursor(); const lines = list.getLinesInfo(); const lineNo = lines.findIndex((l) => l.from.line === cursor.line); root.replaceSelections([{ head: lines[lineNo].from, anchor: cursor }]); } } function isCmdShiftLeft(e) { return ((e.keyCode === 37 || e.code === "ArrowLeft") && e.shiftKey === true && e.metaKey === true && e.altKey === false && e.ctrlKey === false); } class SelectionShouldIgnoreBulletsFeature { constructor(plugin, settingsService, listsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.imeService = imeService; this.onKeyDown = (cm, event) => { if (!this.settingsService.stickCursor || this.imeService.isIMEOpened()) { return; } if (obsidian.Platform.isMacOS && isCmdShiftLeft(event)) { const { shouldStopPropagation } = this.listsService.performOperation((root) => new SelectTillLineStartOperation(root), cm); if (shouldStopPropagation) { event.preventDefault(); event.stopPropagation(); } } }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } function isCmdDotOrCmdShiftDot(e) { return ((e.keyCode === 190 || e.code === "Period") && e.metaKey === true && e.altKey === false && e.ctrlKey === false); } function isCtrlDotOrCtrlShiftDot(e) { return ((e.keyCode === 190 || e.code === "Period") && e.metaKey === false && e.altKey === false && e.ctrlKey === true); } function isModDotOrModShiftDot(e) { return obsidian.Platform.isMacOS ? isCmdDotOrCmdShiftDot(e) : isCtrlDotOrCtrlShiftDot(e); } class ZoomFeature { constructor(plugin, settingsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.imeService = imeService; this.onKeyDown = (cm, e) => { if (window.ObsidianZoomPlugin || this.settingsService.disableZoomNotification || !isModDotOrModShiftDot(e) || this.imeService.isIMEOpened()) { return; } new obsidian.Notice(`Zooming support has been removed from the Obsidian Outliner plugin. Please install the Obsidian Zoom plugin.`, 5000); }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } class FoldFeature { constructor(plugin, obsidianService) { this.plugin = plugin; this.obsidianService = obsidianService; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.addCommand({ id: "fold", name: "Fold the list", callback: this.obsidianService.createCommandCallback(this.fold.bind(this)), hotkeys: [ { modifiers: ["Mod"], key: "ArrowUp", }, ], }); this.plugin.addCommand({ id: "unfold", name: "Unfold the list", callback: this.obsidianService.createCommandCallback(this.unfold.bind(this)), hotkeys: [ { modifiers: ["Mod"], key: "ArrowDown", }, ], }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { }); } setFold(editor, type) { if (!this.obsidianService.getObsidianFoldSettigns().foldIndent) { new obsidian.Notice(`Unable to ${type} because folding is disabled. Please enable "Fold indent" in Obsidian settings.`, 5000); return true; } editor.foldCode(editor.getCursor(), null, type); return true; } fold(editor) { return this.setFold(editor, "fold"); } unfold(editor) { return this.setFold(editor, "unfold"); } } class SelectAllOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleSelection()) { return; } const selection = root.getSelections()[0]; const [rootStart, rootEnd] = root.getRange(); const selectionFrom = minPos(selection.anchor, selection.head); const selectionTo = maxPos(selection.anchor, selection.head); if (selectionFrom.line < rootStart.line || selectionTo.line > rootEnd.line) { return false; } if (selectionFrom.line === rootStart.line && selectionFrom.ch === rootStart.ch && selectionTo.line === rootEnd.line && selectionTo.ch === rootEnd.ch) { return false; } const list = root.getListUnderCursor(); const contentStart = list.getFirstLineContentStart(); const contentEnd = list.getLastLineContentEnd(); if (selectionFrom.line < contentStart.line || selectionTo.line > contentEnd.line) { return false; } this.stopPropagation = true; this.updated = true; if (selectionFrom.line === contentStart.line && selectionFrom.ch === contentStart.ch && selectionTo.line === contentEnd.line && selectionTo.ch === contentEnd.ch) { // select all list root.replaceSelections([{ anchor: rootStart, head: rootEnd }]); } else { // select all line root.replaceSelections([{ anchor: contentStart, head: contentEnd }]); } return true; } } function isCmdA(e) { return ((e.keyCode === 65 || e.code === "KeyA") && e.shiftKey === false && e.metaKey === true && e.altKey === false && e.ctrlKey === false); } function isCtrlA(e) { return ((e.keyCode === 65 || e.code === "KeyA") && e.shiftKey === false && e.metaKey === false && e.altKey === false && e.ctrlKey === true); } function isSelectAll(e) { return obsidian.Platform.isMacOS ? isCmdA(e) : isCtrlA(e); } class SelectAllFeature { constructor(plugin, settingsService, listsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.imeService = imeService; this.onKeyDown = (cm, event) => { if (!this.settingsService.selectAll || !isSelectAll(event) || this.imeService.isIMEOpened()) { return; } const { shouldStopPropagation } = this.listsService.performOperation((root) => new SelectAllOperation(root), cm); if (shouldStopPropagation) { event.preventDefault(); event.stopPropagation(); } }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } class MoveRightOperation { constructor(root, defaultIndentChars) { this.root = root; this.defaultIndentChars = defaultIndentChars; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; const list = root.getListUnderCursor(); const parent = list.getParent(); const prev = parent.getPrevSiblingOf(list); if (!prev) { return; } this.updated = true; const listStartLineBefore = root.getContentLinesRangeOf(list)[0]; const indentPos = list.getFirstLineIndent().length; let indentChars = ""; if (indentChars === "" && !prev.isEmpty()) { indentChars = prev .getChildren()[0] .getFirstLineIndent() .slice(prev.getFirstLineIndent().length); } if (indentChars === "") { indentChars = list .getFirstLineIndent() .slice(parent.getFirstLineIndent().length); } if (indentChars === "" && !list.isEmpty()) { indentChars = list.getChildren()[0].getFirstLineIndent(); } if (indentChars === "") { indentChars = this.defaultIndentChars; } parent.removeChild(list); prev.addAfterAll(list); list.indentContent(indentPos, indentChars); const listStartLineAfter = root.getContentLinesRangeOf(list)[0]; const lineDiff = listStartLineAfter - listStartLineBefore; const cursor = root.getCursor(); root.replaceCursor({ line: cursor.line + lineDiff, ch: cursor.ch + indentChars.length, }); recalculateNumericBullets(root); } } class MoveDownOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; const list = root.getListUnderCursor(); const parent = list.getParent(); const grandParent = parent.getParent(); const next = parent.getNextSiblingOf(list); const listStartLineBefore = root.getContentLinesRangeOf(list)[0]; if (!next && grandParent) { const newParent = grandParent.getNextSiblingOf(parent); if (newParent) { this.updated = true; parent.removeChild(list); newParent.addBeforeAll(list); } } else if (next) { this.updated = true; parent.removeChild(list); parent.addAfter(next, list); } if (!this.updated) { return; } const listStartLineAfter = root.getContentLinesRangeOf(list)[0]; const lineDiff = listStartLineAfter - listStartLineBefore; const cursor = root.getCursor(); root.replaceCursor({ line: cursor.line + lineDiff, ch: cursor.ch, }); recalculateNumericBullets(root); } } class MoveUpOperation { constructor(root) { this.root = root; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } this.stopPropagation = true; const list = root.getListUnderCursor(); const parent = list.getParent(); const grandParent = parent.getParent(); const prev = parent.getPrevSiblingOf(list); const listStartLineBefore = root.getContentLinesRangeOf(list)[0]; if (!prev && grandParent) { const newParent = grandParent.getPrevSiblingOf(parent); if (newParent) { this.updated = true; parent.removeChild(list); newParent.addAfterAll(list); } } else if (prev) { this.updated = true; parent.removeChild(list); parent.addBefore(prev, list); } if (!this.updated) { return; } const listStartLineAfter = root.getContentLinesRangeOf(list)[0]; const lineDiff = listStartLineAfter - listStartLineBefore; const cursor = root.getCursor(); root.replaceCursor({ line: cursor.line + lineDiff, ch: cursor.ch, }); recalculateNumericBullets(root); } } class MoveItemsFeature { constructor(plugin, obsidianService, listsService, imeService) { this.plugin = plugin; this.obsidianService = obsidianService; this.listsService = listsService; this.imeService = imeService; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.addCommand({ id: "move-list-item-up", name: "Move list and sublists up", callback: this.obsidianService.createCommandCallback(this.moveListElementUp.bind(this)), hotkeys: [ { modifiers: ["Mod", "Shift"], key: "ArrowUp", }, ], }); this.plugin.addCommand({ id: "move-list-item-down", name: "Move list and sublists down", callback: this.obsidianService.createCommandCallback(this.moveListElementDown.bind(this)), hotkeys: [ { modifiers: ["Mod", "Shift"], key: "ArrowDown", }, ], }); this.plugin.addCommand({ id: "indent-list", name: "Indent the list and sublists", callback: this.obsidianService.createCommandCallback(this.moveListElementRight.bind(this)), hotkeys: [ { modifiers: [], key: "Tab", }, ], }); this.plugin.addCommand({ id: "outdent-list", name: "Outdent the list and sublists", callback: this.obsidianService.createCommandCallback(this.moveListElementLeft.bind(this)), hotkeys: [ { modifiers: ["Shift"], key: "Tab", }, ], }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { }); } moveListElementDown(editor) { const { shouldStopPropagation } = this.listsService.performOperation((root) => new MoveDownOperation(root), editor); return shouldStopPropagation; } moveListElementUp(editor) { const { shouldStopPropagation } = this.listsService.performOperation((root) => new MoveUpOperation(root), editor); return shouldStopPropagation; } moveListElementRight(editor) { if (this.imeService.isIMEOpened()) { return true; } const { shouldStopPropagation } = this.listsService.performOperation((root) => new MoveRightOperation(root, this.listsService.getDefaultIndentChars()), editor); return shouldStopPropagation; } moveListElementLeft(editor) { if (this.imeService.isIMEOpened()) { return true; } const { shouldStopPropagation } = this.listsService.performOperation((root) => new MoveLeftOperation(root), editor); return shouldStopPropagation; } } class CreateNoteLineOperation { constructor(root, defaultIndentChars) { this.root = root; this.defaultIndentChars = defaultIndentChars; this.stopPropagation = false; this.updated = false; } shouldStopPropagation() { return this.stopPropagation; } shouldUpdate() { return this.updated; } perform() { const { root } = this; if (!root.hasSingleCursor()) { return; } const cursor = root.getCursor(); const list = root.getListUnderCursor(); const lineUnderCursor = list .getLinesInfo() .find((l) => l.from.line === cursor.line); if (cursor.ch < lineUnderCursor.from.ch) { return; } this.stopPropagation = true; this.updated = true; if (!list.getNotesIndent()) { const indent = list.isEmpty() ? list.getFirstLineIndent() + this.defaultIndentChars : list.getChildren()[0].getFirstLineIndent(); list.setNotesIndent(indent); } const lines = list.getLinesInfo().reduce((acc, line) => { if (cursor.line === line.from.line) { acc.push(line.text.slice(0, cursor.ch - line.from.ch)); acc.push(line.text.slice(cursor.ch - line.from.ch)); } else { acc.push(line.text); } return acc; }, []); list.replaceLines(lines); root.replaceCursor({ line: cursor.line + 1, ch: list.getNotesIndent().length, }); } } function isShiftEnter(e) { return ((e.keyCode === 13 || e.code === "Enter") && e.shiftKey === true && e.metaKey === false && e.altKey === false && e.ctrlKey === false); } class ShiftEnterShouldCreateNoteFeature { constructor(plugin, settingsService, listsService, imeService) { this.plugin = plugin; this.settingsService = settingsService; this.listsService = listsService; this.imeService = imeService; this.onKeyDown = (cm, e) => { if (!this.settingsService.betterEnter || !isShiftEnter(e) || this.imeService.isIMEOpened()) { return; } const { shouldStopPropagation } = this.listsService.performOperation((root) => new CreateNoteLineOperation(root, this.listsService.getDefaultIndentChars()), cm); if (shouldStopPropagation) { e.preventDefault(); e.stopPropagation(); } }; } load() { return __awaiter(this, void 0, void 0, function* () { this.plugin.registerCodeMirror((cm) => { cm.on("keydown", this.onKeyDown); }); }); } unload() { return __awaiter(this, void 0, void 0, function* () { this.plugin.app.workspace.iterateCodeMirrors((cm) => { cm.off("keydown", this.onKeyDown); }); }); } } class IMEService { constructor() { this.composition = false; this.onCompositionStart = () => { this.composition = true; }; this.onCompositionEnd = () => { this.composition = false; }; } load() { return __awaiter(this, void 0, void 0, function* () { document.addEventListener("compositionstart", this.onCompositionStart); document.addEventListener("compositionend", this.onCompositionEnd); }); } unload() { return __awaiter(this, void 0, void 0, function* () { document.removeEventListener("compositionend", this.onCompositionEnd); document.removeEventListener("compositionstart", this.onCompositionStart); }); } isIMEOpened() { return this.composition; } } class ObsidianOutlinerPlugin extends obsidian.Plugin { onload() { return __awaiter(this, void 0, void 0, function* () { console.log(`Loading obsidian-outliner`); this.settingsService = new SettingsService(this); yield this.settingsService.load(); this.loggerService = new LoggerService(this.settingsService); this.obsidianService = new ObsidianService(this.app); this.listsService = new ListsService(this.loggerService, this.obsidianService); this.imeService = new IMEService(); yield this.imeService.load(); this.addSettingTab(new ObsidianOutlinerPluginSettingTab(this.app, this, this.settingsService)); this.features = [ new ListsStylesFeature(this, this.settingsService, this.obsidianService), new EnterOutdentIfLineIsEmptyFeature(this, this.settingsService, this.listsService, this.imeService), new EnterShouldCreateNewItemFeature(this, this.settingsService, this.listsService, this.imeService), new EnsureCursorInListContentFeature(this, this.settingsService, this.listsService), new MoveCursorToPreviousUnfoldedLineFeature(this, this.settingsService, this.listsService, this.imeService), new DeleteShouldIgnoreBulletsFeature(this, this.settingsService, this.listsService, this.imeService), new SelectionShouldIgnoreBulletsFeature(this, this.settingsService, this.listsService, this.imeService), new ZoomFeature(this, this.settingsService, this.imeService), new FoldFeature(this, this.obsidianService), new SelectAllFeature(this, this.settingsService, this.listsService, this.imeService), new MoveItemsFeature(this, this.obsidianService, this.listsService, this.imeService), new ShiftEnterShouldCreateNoteFeature(this, this.settingsService, this.listsService, this.imeService), ]; for (const feature of this.features) { yield feature.load(); } }); } onunload() { return __awaiter(this, void 0, void 0, function* () { console.log(`Unloading obsidian-outliner`); yield this.imeService.unload(); for (const feature of this.features) { yield feature.unload(); } }); } } module.exports = ObsidianOutlinerPlugin; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInNyYy9zZXJ2aWNlcy9TZXR0aW5nc1NlcnZpY2UudHMiLCJzcmMvc2VydmljZXMvT2JzaWRpYW5TZXJ2aWNlLnRzIiwic3JjL3Jvb3QvaW5kZXgudHMiLCJzcmMvc2VydmljZXMvTGlzdHNTZXJ2aWNlLnRzIiwic3JjL3NlcnZpY2VzL0xvZ2dlclNlcnZpY2UudHMiLCJzcmMvZmVhdHVyZXMvTGlzdHNTdHlsZXNGZWF0dXJlLnRzIiwic3JjL3Jvb3QvcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0cy50cyIsInNyYy9vcGVyYXRpb25zL01vdmVMZWZ0T3BlcmF0aW9uLnRzIiwic3JjL3V0aWxzL2lzRW1wdHlMaW5lT3JFbXB0eUNoZWNrYm94LnRzIiwic3JjL29wZXJhdGlvbnMvT3V0ZGVudElmTGluZUlzRW1wdHlPcGVyYXRpb24udHMiLCJzcmMvZmVhdHVyZXMvRW50ZXJPdXRkZW50SWZMaW5lSXNFbXB0eUZlYXR1cmUudHMiLCJzcmMvb3BlcmF0aW9ucy9DcmVhdGVOZXdJdGVtT3BlcmF0aW9uLnRzIiwic3JjL2ZlYXR1cmVzL0VudGVyU2hvdWxkQ3JlYXRlTmV3SXRlbU9uQ2hpbGRMZXZlbEZlYXR1cmUudHMiLCJzcmMvb3BlcmF0aW9ucy9Nb3ZlQ3Vyc29yVG9QcmV2aW91c1VuZm9sZGVkTGluZU9wZXJhdGlvbi50cyIsInNyYy9mZWF0dXJlcy9Nb3ZlQ3Vyc29yVG9QcmV2aW91c1VuZm9sZGVkTGluZUZlYXR1cmUudHMiLCJzcmMvb3BlcmF0aW9ucy9FbnN1cmVDdXJzb3JJbkxpc3RDb250ZW50T3BlcmF0aW9uLnRzIiwic3JjL29wZXJhdGlvbnMvRW5zdXJlQ3Vyc29ySXNJblVuZm9sZGVkTGluZU9wZXJhdGlvbi50cyIsInNyYy9mZWF0dXJlcy9FbnN1cmVDdXJzb3JJbkxpc3RDb250ZW50RmVhdHVyZS50cyIsInNyYy9vcGVyYXRpb25zL0RlbGV0ZUFuZE1lcmdlV2l0aFByZXZpb3VzTGluZU9wZXJhdGlvbi50cyIsInNyYy9vcGVyYXRpb25zL0RlbGV0ZUFuZE1lcmdlV2l0aE5leHRMaW5lT3BlcmF0aW9uLnRzIiwic3JjL29wZXJhdGlvbnMvRGVsZXRlVGlsbExpbmVTdGFydE9wZXJhdGlvbi50cyIsInNyYy9mZWF0dXJlcy9EZWxldGVTaG91bGRJZ25vcmVCdWxsZXRzRmVhdHVyZS50cyIsInNyYy9vcGVyYXRpb25zL1NlbGVjdFRpbGxMaW5lU3RhcnRPcGVyYXRpb24udHMiLCJzcmMvZmVhdHVyZXMvU2VsZWN0aW9uU2hvdWxkSWdub3JlQnVsbGV0c0ZlYXR1cmUudHMiLCJzcmMvZmVhdHVyZXMvWm9vbUZlYXR1cmUudHMiLCJzcmMvZmVhdHVyZXMvRm9sZEZlYXR1cmUudHMiLCJzcmMvb3BlcmF0aW9ucy9TZWxlY3RBbGxPcGVyYXRpb24udHMiLCJzcmMvZmVhdHVyZXMvU2VsZWN0QWxsRmVhdHVyZS50cyIsInNyYy9vcGVyYXRpb25zL01vdmVSaWdodE9wZXJhdGlvbi50cyIsInNyYy9vcGVyYXRpb25zL01vdmVEb3duT3BlcmF0aW9uLnRzIiwic3JjL29wZXJhdGlvbnMvTW92ZVVwT3BlcmF0aW9uLnRzIiwic3JjL2ZlYXR1cmVzL01vdmVJdGVtc0ZlYXR1cmUudHMiLCJzcmMvb3BlcmF0aW9ucy9DcmVhdGVOb3RlTGluZU9wZXJhdGlvbi50cyIsInNyYy9mZWF0dXJlcy9TaGlmdEVudGVyU2hvdWxkQ3JlYXRlTm90ZUZlYXR1cmUudHMiLCJzcmMvc2VydmljZXMvSU1FU2VydmljZS50cyIsInNyYy9PYnNpZGlhbk91dGxpbmVyUGx1Z2luLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qISAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5cclxuXHJcblBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG5wdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQuXHJcblxyXG5USEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiIEFORCBUSEUgQVVUSE9SIERJU0NMQUlNUyBBTEwgV0FSUkFOVElFUyBXSVRIXHJcblJFR0FSRCBUTyBUSElTIFNPRlRXQVJFIElOQ0xVRElORyBBTEwgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWVxyXG5BTkQgRklUTkVTUy4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUiBCRSBMSUFCTEUgRk9SIEFOWSBTUEVDSUFMLCBESVJFQ1QsXHJcbklORElSRUNULCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgT1IgQU5ZIERBTUFHRVMgV0hBVFNPRVZFUiBSRVNVTFRJTkcgRlJPTVxyXG5MT1NTIE9GIFVTRSwgREFUQSBPUiBQUk9GSVRTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgTkVHTElHRU5DRSBPUlxyXG5PVEhFUiBUT1JUSU9VUyBBQ1RJT04sIEFSSVNJTkcgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgVVNFIE9SXHJcblBFUkZPUk1BTkNFIE9GIFRISVMgU09GVFdBUkUuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICovXHJcbi8qIGdsb2JhbCBSZWZsZWN0LCBQcm9taXNlICovXHJcblxyXG52YXIgZXh0ZW5kU3RhdGljcyA9IGZ1bmN0aW9uKGQsIGIpIHtcclxuICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHxcclxuICAgICAgICAoeyBfX3Byb3RvX186IFtdIH0gaW5zdGFuY2VvZiBBcnJheSAmJiBmdW5jdGlvbiAoZCwgYikgeyBkLl9fcHJvdG9fXyA9IGI7IH0pIHx8XHJcbiAgICAgICAgZnVuY3Rpb24gKGQsIGIpIHsgZm9yICh2YXIgcCBpbiBiKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGIsIHApKSBkW3BdID0gYltwXTsgfTtcclxuICAgIHJldHVybiBleHRlbmRTdGF0aWNzKGQsIGIpO1xyXG59O1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZXh0ZW5kcyhkLCBiKSB7XHJcbiAgICBpZiAodHlwZW9mIGIgIT09IFwiZnVuY3Rpb25cIiAmJiBiICE9PSBudWxsKVxyXG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDbGFzcyBleHRlbmRzIHZhbHVlIFwiICsgU3RyaW5nKGIpICsgXCIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbFwiKTtcclxuICAgIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbiAgICBmdW5jdGlvbiBfXygpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGQ7IH1cclxuICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTtcclxufVxyXG5cclxuZXhwb3J0IHZhciBfX2Fzc2lnbiA9IGZ1bmN0aW9uKCkge1xyXG4gICAgX19hc3NpZ24gPSBPYmplY3QuYXNzaWduIHx8IGZ1bmN0aW9uIF9fYXNzaWduKHQpIHtcclxuICAgICAgICBmb3IgKHZhciBzLCBpID0gMSwgbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgPCBuOyBpKyspIHtcclxuICAgICAgICAgICAgcyA9IGFyZ3VtZW50c1tpXTtcclxuICAgICAgICAgICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApKSB0W3BdID0gc1twXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHQ7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gX19hc3NpZ24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcmVzdChzLCBlKSB7XHJcbiAgICB2YXIgdCA9IHt9O1xyXG4gICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApICYmIGUuaW5kZXhPZihwKSA8IDApXHJcbiAgICAgICAgdFtwXSA9IHNbcF07XHJcbiAgICBpZiAocyAhPSBudWxsICYmIHR5cGVvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzID09PSBcImZ1bmN0aW9uXCIpXHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIHAgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKHMpOyBpIDwgcC5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICBpZiAoZS5pbmRleE9mKHBbaV0pIDwgMCAmJiBPYmplY3QucHJvdG90eXBlLnByb3BlcnR5SXNFbnVtZXJhYmxlLmNhbGwocywgcFtpXSkpXHJcbiAgICAgICAgICAgICAgICB0W3BbaV1dID0gc1twW2ldXTtcclxuICAgICAgICB9XHJcbiAgICByZXR1cm4gdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpIHtcclxuICAgIHZhciBjID0gYXJndW1lbnRzLmxlbmd0aCwgciA9IGMgPCAzID8gdGFyZ2V0IDogZGVzYyA9PT0gbnVsbCA/IGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHRhcmdldCwga2V5KSA6IGRlc2MsIGQ7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUgPT09IFwiZnVuY3Rpb25cIikgciA9IFJlZmxlY3QuZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpO1xyXG4gICAgZWxzZSBmb3IgKHZhciBpID0gZGVjb3JhdG9ycy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkgaWYgKGQgPSBkZWNvcmF0b3JzW2ldKSByID0gKGMgPCAzID8gZChyKSA6IGMgPiAzID8gZCh0YXJnZXQsIGtleSwgcikgOiBkKHRhcmdldCwga2V5KSkgfHwgcjtcclxuICAgIHJldHVybiBjID4gMyAmJiByICYmIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGtleSwgciksIHI7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3BhcmFtKHBhcmFtSW5kZXgsIGRlY29yYXRvcikge1xyXG4gICAgcmV0dXJuIGZ1bmN0aW9uICh0YXJnZXQsIGtleSkgeyBkZWNvcmF0b3IodGFyZ2V0LCBrZXksIHBhcmFtSW5kZXgpOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX21ldGFkYXRhKG1ldGFkYXRhS2V5LCBtZXRhZGF0YVZhbHVlKSB7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QubWV0YWRhdGEgPT09IFwiZnVuY3Rpb25cIikgcmV0dXJuIFJlZmxlY3QubWV0YWRhdGEobWV0YWRhdGFLZXksIG1ldGFkYXRhVmFsdWUpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hd2FpdGVyKHRoaXNBcmcsIF9hcmd1bWVudHMsIFAsIGdlbmVyYXRvcikge1xyXG4gICAgZnVuY3Rpb24gYWRvcHQodmFsdWUpIHsgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgUCA/IHZhbHVlIDogbmV3IFAoZnVuY3Rpb24gKHJlc29sdmUpIHsgcmVzb2x2ZSh2YWx1ZSk7IH0pOyB9XHJcbiAgICByZXR1cm4gbmV3IChQIHx8IChQID0gUHJvbWlzZSkpKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcclxuICAgICAgICBmdW5jdGlvbiBmdWxmaWxsZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3IubmV4dCh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gcmVqZWN0ZWQodmFsdWUpIHsgdHJ5IHsgc3RlcChnZW5lcmF0b3JbXCJ0aHJvd1wiXSh2YWx1ZSkpOyB9IGNhdGNoIChlKSB7IHJlamVjdChlKTsgfSB9XHJcbiAgICAgICAgZnVuY3Rpb24gc3RlcChyZXN1bHQpIHsgcmVzdWx0LmRvbmUgPyByZXNvbHZlKHJlc3VsdC52YWx1ZSkgOiBhZG9wdChyZXN1bHQudmFsdWUpLnRoZW4oZnVsZmlsbGVkLCByZWplY3RlZCk7IH1cclxuICAgICAgICBzdGVwKChnZW5lcmF0b3IgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSkpLm5leHQoKSk7XHJcbiAgICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZ2VuZXJhdG9yKHRoaXNBcmcsIGJvZHkpIHtcclxuICAgIHZhciBfID0geyBsYWJlbDogMCwgc2VudDogZnVuY3Rpb24oKSB7IGlmICh0WzBdICYgMSkgdGhyb3cgdFsxXTsgcmV0dXJuIHRbMV07IH0sIHRyeXM6IFtdLCBvcHM6IFtdIH0sIGYsIHksIHQsIGc7XHJcbiAgICByZXR1cm4gZyA9IHsgbmV4dDogdmVyYigwKSwgXCJ0aHJvd1wiOiB2ZXJiKDEpLCBcInJldHVyblwiOiB2ZXJiKDIpIH0sIHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiAoZ1tTeW1ib2wuaXRlcmF0b3JdID0gZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzOyB9KSwgZztcclxuICAgIGZ1bmN0aW9uIHZlcmIobikgeyByZXR1cm4gZnVuY3Rpb24gKHYpIHsgcmV0dXJuIHN0ZXAoW24sIHZdKTsgfTsgfVxyXG4gICAgZnVuY3Rpb24gc3RlcChvcCkge1xyXG4gICAgICAgIGlmIChmKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiR2VuZXJhdG9yIGlzIGFscmVhZHkgZXhlY3V0aW5nLlwiKTtcclxuICAgICAgICB3aGlsZSAoXykgdHJ5IHtcclxuICAgICAgICAgICAgaWYgKGYgPSAxLCB5ICYmICh0ID0gb3BbMF0gJiAyID8geVtcInJldHVyblwiXSA6IG9wWzBdID8geVtcInRocm93XCJdIHx8ICgodCA9IHlbXCJyZXR1cm5cIl0pICYmIHQuY2FsbCh5KSwgMCkgOiB5Lm5leHQpICYmICEodCA9IHQuY2FsbCh5LCBvcFsxXSkpLmRvbmUpIHJldHVybiB0O1xyXG4gICAgICAgICAgICBpZiAoeSA9IDAsIHQpIG9wID0gW29wWzBdICYgMiwgdC52YWx1ZV07XHJcbiAgICAgICAgICAgIHN3aXRjaCAob3BbMF0pIHtcclxuICAgICAgICAgICAgICAgIGNhc2UgMDogY2FzZSAxOiB0ID0gb3A7IGJyZWFrO1xyXG4gICAgICAgICAgICAgICAgY2FzZSA0OiBfLmxhYmVsKys7IHJldHVybiB7IHZhbHVlOiBvcFsxXSwgZG9uZTogZmFsc2UgfTtcclxuICAgICAgICAgICAgICAgIGNhc2UgNTogXy5sYWJlbCsrOyB5ID0gb3BbMV07IG9wID0gWzBdOyBjb250aW51ZTtcclxuICAgICAgICAgICAgICAgIGNhc2UgNzogb3AgPSBfLm9wcy5wb3AoKTsgXy50cnlzLnBvcCgpOyBjb250aW51ZTtcclxuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKCEodCA9IF8udHJ5cywgdCA9IHQubGVuZ3RoID4gMCAmJiB0W3QubGVuZ3RoIC0gMV0pICYmIChvcFswXSA9PT0gNiB8fCBvcFswXSA9PT0gMikpIHsgXyA9IDA7IGNvbnRpbnVlOyB9XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKG9wWzBdID09PSAzICYmICghdCB8fCAob3BbMV0gPiB0WzBdICYmIG9wWzFdIDwgdFszXSkpKSB7IF8ubGFiZWwgPSBvcFsxXTsgYnJlYWs7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAob3BbMF0gPT09IDYgJiYgXy5sYWJlbCA8IHRbMV0pIHsgXy5sYWJlbCA9IHRbMV07IHQgPSBvcDsgYnJlYWs7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAodCAmJiBfLmxhYmVsIDwgdFsyXSkgeyBfLmxhYmVsID0gdFsyXTsgXy5vcHMucHVzaChvcCk7IGJyZWFrOyB9XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRbMl0pIF8ub3BzLnBvcCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIF8udHJ5cy5wb3AoKTsgY29udGludWU7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgb3AgPSBib2R5LmNhbGwodGhpc0FyZywgXyk7XHJcbiAgICAgICAgfSBjYXRjaCAoZSkgeyBvcCA9IFs2LCBlXTsgeSA9IDA7IH0gZmluYWxseSB7IGYgPSB0ID0gMDsgfVxyXG4gICAgICAgIGlmIChvcFswXSAmIDUpIHRocm93IG9wWzFdOyByZXR1cm4geyB2YWx1ZTogb3BbMF0gPyBvcFsxXSA6IHZvaWQgMCwgZG9uZTogdHJ1ZSB9O1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgdmFyIF9fY3JlYXRlQmluZGluZyA9IE9iamVjdC5jcmVhdGUgPyAoZnVuY3Rpb24obywgbSwgaywgazIpIHtcclxuICAgIGlmIChrMiA9PT0gdW5kZWZpbmVkKSBrMiA9IGs7XHJcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkobywgazIsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBmdW5jdGlvbigpIHsgcmV0dXJuIG1ba107IH0gfSk7XHJcbn0pIDogKGZ1bmN0aW9uKG8sIG0sIGssIGsyKSB7XHJcbiAgICBpZiAoazIgPT09IHVuZGVmaW5lZCkgazIgPSBrO1xyXG4gICAgb1trMl0gPSBtW2tdO1xyXG59KTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2V4cG9ydFN0YXIobSwgbykge1xyXG4gICAgZm9yICh2YXIgcCBpbiBtKSBpZiAocCAhPT0gXCJkZWZhdWx0XCIgJiYgIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvLCBwKSkgX19jcmVhdGVCaW5kaW5nKG8sIG0sIHApO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX192YWx1ZXMobykge1xyXG4gICAgdmFyIHMgPSB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgU3ltYm9sLml0ZXJhdG9yLCBtID0gcyAmJiBvW3NdLCBpID0gMDtcclxuICAgIGlmIChtKSByZXR1cm4gbS5jYWxsKG8pO1xyXG4gICAgaWYgKG8gJiYgdHlwZW9mIG8ubGVuZ3RoID09PSBcIm51bWJlclwiKSByZXR1cm4ge1xyXG4gICAgICAgIG5leHQ6IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgaWYgKG8gJiYgaSA+PSBvLmxlbmd0aCkgbyA9IHZvaWQgMDtcclxuICAgICAgICAgICAgcmV0dXJuIHsgdmFsdWU6IG8gJiYgb1tpKytdLCBkb25lOiAhbyB9O1xyXG4gICAgICAgIH1cclxuICAgIH07XHJcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKHMgPyBcIk9iamVjdCBpcyBub3QgaXRlcmFibGUuXCIgOiBcIlN5bWJvbC5pdGVyYXRvciBpcyBub3QgZGVmaW5lZC5cIik7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3JlYWQobywgbikge1xyXG4gICAgdmFyIG0gPSB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgb1tTeW1ib2wuaXRlcmF0b3JdO1xyXG4gICAgaWYgKCFtKSByZXR1cm4gbztcclxuICAgIHZhciBpID0gbS5jYWxsKG8pLCByLCBhciA9IFtdLCBlO1xyXG4gICAgdHJ5IHtcclxuICAgICAgICB3aGlsZSAoKG4gPT09IHZvaWQgMCB8fCBuLS0gPiAwKSAmJiAhKHIgPSBpLm5leHQoKSkuZG9uZSkgYXIucHVzaChyLnZhbHVlKTtcclxuICAgIH1cclxuICAgIGNhdGNoIChlcnJvcikgeyBlID0geyBlcnJvcjogZXJyb3IgfTsgfVxyXG4gICAgZmluYWxseSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgaWYgKHIgJiYgIXIuZG9uZSAmJiAobSA9IGlbXCJyZXR1cm5cIl0pKSBtLmNhbGwoaSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGZpbmFsbHkgeyBpZiAoZSkgdGhyb3cgZS5lcnJvcjsgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIGFyO1xyXG59XHJcblxyXG4vKiogQGRlcHJlY2F0ZWQgKi9cclxuZXhwb3J0IGZ1bmN0aW9uIF9fc3ByZWFkKCkge1xyXG4gICAgZm9yICh2YXIgYXIgPSBbXSwgaSA9IDA7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspXHJcbiAgICAgICAgYXIgPSBhci5jb25jYXQoX19yZWFkKGFyZ3VtZW50c1tpXSkpO1xyXG4gICAgcmV0dXJuIGFyO1xyXG59XHJcblxyXG4vKiogQGRlcHJlY2F0ZWQgKi9cclxuZXhwb3J0IGZ1bmN0aW9uIF9fc3ByZWFkQXJyYXlzKCkge1xyXG4gICAgZm9yICh2YXIgcyA9IDAsIGkgPSAwLCBpbCA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgPCBpbDsgaSsrKSBzICs9IGFyZ3VtZW50c1tpXS5sZW5ndGg7XHJcbiAgICBmb3IgKHZhciByID0gQXJyYXkocyksIGsgPSAwLCBpID0gMDsgaSA8IGlsOyBpKyspXHJcbiAgICAgICAgZm9yICh2YXIgYSA9IGFyZ3VtZW50c1tpXSwgaiA9IDAsIGpsID0gYS5sZW5ndGg7IGogPCBqbDsgaisrLCBrKyspXHJcbiAgICAgICAgICAgIHJba10gPSBhW2pdO1xyXG4gICAgcmV0dXJuIHI7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3NwcmVhZEFycmF5KHRvLCBmcm9tLCBwYWNrKSB7XHJcbiAgICBpZiAocGFjayB8fCBhcmd1bWVudHMubGVuZ3RoID09PSAyKSBmb3IgKHZhciBpID0gMCwgbCA9IGZyb20ubGVuZ3RoLCBhcjsgaSA8IGw7IGkrKykge1xyXG4gICAgICAgIGlmIChhciB8fCAhKGkgaW4gZnJvbSkpIHtcclxuICAgICAgICAgICAgaWYgKCFhcikgYXIgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChmcm9tLCAwLCBpKTtcclxuICAgICAgICAgICAgYXJbaV0gPSBmcm9tW2ldO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuICAgIHJldHVybiB0by5jb25jYXQoYXIgfHwgQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoZnJvbSkpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hd2FpdCh2KSB7XHJcbiAgICByZXR1cm4gdGhpcyBpbnN0YW5jZW9mIF9fYXdhaXQgPyAodGhpcy52ID0gdiwgdGhpcykgOiBuZXcgX19hd2FpdCh2KTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXN5bmNHZW5lcmF0b3IodGhpc0FyZywgX2FyZ3VtZW50cywgZ2VuZXJhdG9yKSB7XHJcbiAgICBpZiAoIVN5bWJvbC5hc3luY0l0ZXJhdG9yKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3ltYm9sLmFzeW5jSXRlcmF0b3IgaXMgbm90IGRlZmluZWQuXCIpO1xyXG4gICAgdmFyIGcgPSBnZW5lcmF0b3IuYXBwbHkodGhpc0FyZywgX2FyZ3VtZW50cyB8fCBbXSksIGksIHEgPSBbXTtcclxuICAgIHJldHVybiBpID0ge30sIHZlcmIoXCJuZXh0XCIpLCB2ZXJiKFwidGhyb3dcIiksIHZlcmIoXCJyZXR1cm5cIiksIGlbU3ltYm9sLmFzeW5jSXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaTtcclxuICAgIGZ1bmN0aW9uIHZlcmIobikgeyBpZiAoZ1tuXSkgaVtuXSA9IGZ1bmN0aW9uICh2KSB7IHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbiAoYSwgYikgeyBxLnB1c2goW24sIHYsIGEsIGJdKSA+IDEgfHwgcmVzdW1lKG4sIHYpOyB9KTsgfTsgfVxyXG4gICAgZnVuY3Rpb24gcmVzdW1lKG4sIHYpIHsgdHJ5IHsgc3RlcChnW25dKHYpKTsgfSBjYXRjaCAoZSkgeyBzZXR0bGUocVswXVszXSwgZSk7IH0gfVxyXG4gICAgZnVuY3Rpb24gc3RlcChyKSB7IHIudmFsdWUgaW5zdGFuY2VvZiBfX2F3YWl0ID8gUHJvbWlzZS5yZXNvbHZlKHIudmFsdWUudikudGhlbihmdWxmaWxsLCByZWplY3QpIDogc2V0dGxlKHFbMF1bMl0sIHIpOyB9XHJcbiAgICBmdW5jdGlvbiBmdWxmaWxsKHZhbHVlKSB7IHJlc3VtZShcIm5leHRcIiwgdmFsdWUpOyB9XHJcbiAgICBmdW5jdGlvbiByZWplY3QodmFsdWUpIHsgcmVzdW1lKFwidGhyb3dcIiwgdmFsdWUpOyB9XHJcbiAgICBmdW5jdGlvbiBzZXR0bGUoZiwgdikgeyBpZiAoZih2KSwgcS5zaGlmdCgpLCBxLmxlbmd0aCkgcmVzdW1lKHFbMF1bMF0sIHFbMF1bMV0pOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2FzeW5jRGVsZWdhdG9yKG8pIHtcclxuICAgIHZhciBpLCBwO1xyXG4gICAgcmV0dXJuIGkgPSB7fSwgdmVyYihcIm5leHRcIiksIHZlcmIoXCJ0aHJvd1wiLCBmdW5jdGlvbiAoZSkgeyB0aHJvdyBlOyB9KSwgdmVyYihcInJldHVyblwiKSwgaVtTeW1ib2wuaXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaTtcclxuICAgIGZ1bmN0aW9uIHZlcmIobiwgZikgeyBpW25dID0gb1tuXSA/IGZ1bmN0aW9uICh2KSB7IHJldHVybiAocCA9ICFwKSA/IHsgdmFsdWU6IF9fYXdhaXQob1tuXSh2KSksIGRvbmU6IG4gPT09IFwicmV0dXJuXCIgfSA6IGYgPyBmKHYpIDogdjsgfSA6IGY7IH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXN5bmNWYWx1ZXMobykge1xyXG4gICAgaWYgKCFTeW1ib2wuYXN5bmNJdGVyYXRvcikgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN5bWJvbC5hc3luY0l0ZXJhdG9yIGlzIG5vdCBkZWZpbmVkLlwiKTtcclxuICAgIHZhciBtID0gb1tTeW1ib2wuYXN5bmNJdGVyYXRvcl0sIGk7XHJcbiAgICByZXR1cm4gbSA/IG0uY2FsbChvKSA6IChvID0gdHlwZW9mIF9fdmFsdWVzID09PSBcImZ1bmN0aW9uXCIgPyBfX3ZhbHVlcyhvKSA6IG9bU3ltYm9sLml0ZXJhdG9yXSgpLCBpID0ge30sIHZlcmIoXCJuZXh0XCIpLCB2ZXJiKFwidGhyb3dcIiksIHZlcmIoXCJyZXR1cm5cIiksIGlbU3ltYm9sLmFzeW5jSXRlcmF0b3JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfSwgaSk7XHJcbiAgICBmdW5jdGlvbiB2ZXJiKG4pIHsgaVtuXSA9IG9bbl0gJiYgZnVuY3Rpb24gKHYpIHsgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHsgdiA9IG9bbl0odiksIHNldHRsZShyZXNvbHZlLCByZWplY3QsIHYuZG9uZSwgdi52YWx1ZSk7IH0pOyB9OyB9XHJcbiAgICBmdW5jdGlvbiBzZXR0bGUocmVzb2x2ZSwgcmVqZWN0LCBkLCB2KSB7IFByb21pc2UucmVzb2x2ZSh2KS50aGVuKGZ1bmN0aW9uKHYpIHsgcmVzb2x2ZSh7IHZhbHVlOiB2LCBkb25lOiBkIH0pOyB9LCByZWplY3QpOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX21ha2VUZW1wbGF0ZU9iamVjdChjb29rZWQsIHJhdykge1xyXG4gICAgaWYgKE9iamVjdC5kZWZpbmVQcm9wZXJ0eSkgeyBPYmplY3QuZGVmaW5lUHJvcGVydHkoY29va2VkLCBcInJhd1wiLCB7IHZhbHVlOiByYXcgfSk7IH0gZWxzZSB7IGNvb2tlZC5yYXcgPSByYXc7IH1cclxuICAgIHJldHVybiBjb29rZWQ7XHJcbn07XHJcblxyXG52YXIgX19zZXRNb2R1bGVEZWZhdWx0ID0gT2JqZWN0LmNyZWF0ZSA/IChmdW5jdGlvbihvLCB2KSB7XHJcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkobywgXCJkZWZhdWx0XCIsIHsgZW51bWVyYWJsZTogdHJ1ZSwgdmFsdWU6IHYgfSk7XHJcbn0pIDogZnVuY3Rpb24obywgdikge1xyXG4gICAgb1tcImRlZmF1bHRcIl0gPSB2O1xyXG59O1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9faW1wb3J0U3Rhcihtb2QpIHtcclxuICAgIGlmIChtb2QgJiYgbW9kLl9fZXNNb2R1bGUpIHJldHVybiBtb2Q7XHJcbiAgICB2YXIgcmVzdWx0ID0ge307XHJcbiAgICBpZiAobW9kICE9IG51bGwpIGZvciAodmFyIGsgaW4gbW9kKSBpZiAoayAhPT0gXCJkZWZhdWx0XCIgJiYgT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG1vZCwgaykpIF9fY3JlYXRlQmluZGluZyhyZXN1bHQsIG1vZCwgayk7XHJcbiAgICBfX3NldE1vZHVsZURlZmF1bHQocmVzdWx0LCBtb2QpO1xyXG4gICAgcmV0dXJuIHJlc3VsdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9faW1wb3J0RGVmYXVsdChtb2QpIHtcclxuICAgIHJldHVybiAobW9kICYmIG1vZC5fX2VzTW9kdWxlKSA/IG1vZCA6IHsgZGVmYXVsdDogbW9kIH07XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2NsYXNzUHJpdmF0ZUZpZWxkR2V0KHJlY2VpdmVyLCBzdGF0ZSwga2luZCwgZikge1xyXG4gICAgaWYgKGtpbmQgPT09IFwiYVwiICYmICFmKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiUHJpdmF0ZSBhY2Nlc3NvciB3YXMgZGVmaW5lZCB3aXRob3V0IGEgZ2V0dGVyXCIpO1xyXG4gICAgaWYgKHR5cGVvZiBzdGF0ZSA9PT0gXCJmdW5jdGlvblwiID8gcmVjZWl2ZXIgIT09IHN0YXRlIHx8ICFmIDogIXN0YXRlLmhhcyhyZWNlaXZlcikpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgcmVhZCBwcml2YXRlIG1lbWJlciBmcm9tIGFuIG9iamVjdCB3aG9zZSBjbGFzcyBkaWQgbm90IGRlY2xhcmUgaXRcIik7XHJcbiAgICByZXR1cm4ga2luZCA9PT0gXCJtXCIgPyBmIDoga2luZCA9PT0gXCJhXCIgPyBmLmNhbGwocmVjZWl2ZXIpIDogZiA/IGYudmFsdWUgOiBzdGF0ZS5nZXQocmVjZWl2ZXIpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19jbGFzc1ByaXZhdGVGaWVsZFNldChyZWNlaXZlciwgc3RhdGUsIHZhbHVlLCBraW5kLCBmKSB7XHJcbiAgICBpZiAoa2luZCA9PT0gXCJtXCIpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJQcml2YXRlIG1ldGhvZCBpcyBub3Qgd3JpdGFibGVcIik7XHJcbiAgICBpZiAoa2luZCA9PT0gXCJhXCIgJiYgIWYpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJQcml2YXRlIGFjY2Vzc29yIHdhcyBkZWZpbmVkIHdpdGhvdXQgYSBzZXR0ZXJcIik7XHJcbiAgICBpZiAodHlwZW9mIHN0YXRlID09PSBcImZ1bmN0aW9uXCIgPyByZWNlaXZlciAhPT0gc3RhdGUgfHwgIWYgOiAhc3RhdGUuaGFzKHJlY2VpdmVyKSkgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNhbm5vdCB3cml0ZSBwcml2YXRlIG1lbWJlciB0byBhbiBvYmplY3Qgd2hvc2UgY2xhc3MgZGlkIG5vdCBkZWNsYXJlIGl0XCIpO1xyXG4gICAgcmV0dXJuIChraW5kID09PSBcImFcIiA/IGYuY2FsbChyZWNlaXZlciwgdmFsdWUpIDogZiA/IGYudmFsdWUgPSB2YWx1ZSA6IHN0YXRlLnNldChyZWNlaXZlciwgdmFsdWUpKSwgdmFsdWU7XHJcbn1cclxuIiwiaW1wb3J0IHsgQXBwLCBQbHVnaW5TZXR0aW5nVGFiLCBQbHVnaW5fMiwgU2V0dGluZyB9IGZyb20gXCJvYnNpZGlhblwiO1xuXG5leHBvcnQgaW50ZXJmYWNlIE9ic2lkaWFuT3V0bGluZXJQbHVnaW5TZXR0aW5ncyB7XG4gIHN0eWxlTGlzdHM6IGJvb2xlYW47XG4gIGRlYnVnOiBib29sZWFuO1xuICBzdGlja0N1cnNvcjogYm9vbGVhbjtcbiAgYmV0dGVyRW50ZXI6IGJvb2xlYW47XG4gIHNlbGVjdEFsbDogYm9vbGVhbjtcbiAgZGlzYWJsZVpvb21Ob3RpZmljYXRpb246IGJvb2xlYW47XG4gIGhpZGVXYXJuaW5nOiBib29sZWFuO1xufVxuXG5jb25zdCBERUZBVUxUX1NFVFRJTkdTOiBPYnNpZGlhbk91dGxpbmVyUGx1Z2luU2V0dGluZ3MgPSB7XG4gIHN0eWxlTGlzdHM6IGZhbHNlLFxuICBkZWJ1ZzogZmFsc2UsXG4gIHN0aWNrQ3Vyc29yOiB0cnVlLFxuICBiZXR0ZXJFbnRlcjogdHJ1ZSxcbiAgc2VsZWN0QWxsOiB0cnVlLFxuICBkaXNhYmxlWm9vbU5vdGlmaWNhdGlvbjogZmFsc2UsXG4gIGhpZGVXYXJuaW5nOiBmYWxzZSxcbn07XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3RvcmFnZSB7XG4gIGxvYWREYXRhKCk6IFByb21pc2U8T2JzaWRpYW5PdXRsaW5lclBsdWdpblNldHRpbmdzPjtcbiAgc2F2ZURhdGEoc2V0dGlnbnM6IE9ic2lkaWFuT3V0bGluZXJQbHVnaW5TZXR0aW5ncyk6IFByb21pc2U8dm9pZD47XG59XG5cbnR5cGUgSyA9IGtleW9mIE9ic2lkaWFuT3V0bGluZXJQbHVnaW5TZXR0aW5ncztcbnR5cGUgVjxUIGV4dGVuZHMgSz4gPSBPYnNpZGlhbk91dGxpbmVyUGx1Z2luU2V0dGluZ3NbVF07XG50eXBlIENhbGxiYWNrPFQgZXh0ZW5kcyBLPiA9IChjYjogVjxUPikgPT4gdm9pZDtcblxuZXhwb3J0IGNsYXNzIFNldHRpbmdzU2VydmljZSBpbXBsZW1lbnRzIE9ic2lkaWFuT3V0bGluZXJQbHVnaW5TZXR0aW5ncyB7XG4gIHByaXZhdGUgc3RvcmFnZTogU3RvcmFnZTtcbiAgcHJpdmF0ZSB2YWx1ZXM6IE9ic2lkaWFuT3V0bGluZXJQbHVnaW5TZXR0aW5ncztcbiAgcHJpdmF0ZSBoYW5kbGVyczogTWFwPEssIFNldDxDYWxsYmFjazxLPj4+O1xuXG4gIGNvbnN0cnVjdG9yKHN0b3JhZ2U6IFN0b3JhZ2UpIHtcbiAgICB0aGlzLnN0b3JhZ2UgPSBzdG9yYWdlO1xuICAgIHRoaXMuaGFuZGxlcnMgPSBuZXcgTWFwKCk7XG4gIH1cblxuICBnZXQgc3R5bGVMaXN0cygpIHtcbiAgICByZXR1cm4gdGhpcy52YWx1ZXMuc3R5bGVMaXN0cztcbiAgfVxuICBzZXQgc3R5bGVMaXN0cyh2YWx1ZTogYm9vbGVhbikge1xuICAgIHRoaXMuc2V0KFwic3R5bGVMaXN0c1wiLCB2YWx1ZSk7XG4gIH1cblxuICBnZXQgZGVidWcoKSB7XG4gICAgcmV0dXJuIHRoaXMudmFsdWVzLmRlYnVnO1xuICB9XG4gIHNldCBkZWJ1Zyh2YWx1ZTogYm9vbGVhbikge1xuICAgIHRoaXMuc2V0KFwiZGVidWdcIiwgdmFsdWUpO1xuICB9XG5cbiAgZ2V0IHN0aWNrQ3Vyc29yKCkge1xuICAgIHJldHVybiB0aGlzLnZhbHVlcy5zdGlja0N1cnNvcjtcbiAgfVxuICBzZXQgc3RpY2tDdXJzb3IodmFsdWU6IGJvb2xlYW4pIHtcbiAgICB0aGlzLnNldChcInN0aWNrQ3Vyc29yXCIsIHZhbHVlKTtcbiAgfVxuXG4gIGdldCBiZXR0ZXJFbnRlcigpIHtcbiAgICByZXR1cm4gdGhpcy52YWx1ZXMuYmV0dGVyRW50ZXI7XG4gIH1cbiAgc2V0IGJldHRlckVudGVyKHZhbHVlOiBib29sZWFuKSB7XG4gICAgdGhpcy5zZXQoXCJiZXR0ZXJFbnRlclwiLCB2YWx1ZSk7XG4gIH1cblxuICBnZXQgc2VsZWN0QWxsKCkge1xuICAgIHJldHVybiB0aGlzLnZhbHVlcy5zZWxlY3RBbGw7XG4gIH1cbiAgc2V0IHNlbGVjdEFsbCh2YWx1ZTogYm9vbGVhbikge1xuICAgIHRoaXMuc2V0KFwic2VsZWN0QWxsXCIsIHZhbHVlKTtcbiAgfVxuXG4gIGdldCBkaXNhYmxlWm9vbU5vdGlmaWNhdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy52YWx1ZXMuZGlzYWJsZVpvb21Ob3RpZmljYXRpb247XG4gIH1cbiAgc2V0IGRpc2FibGVab29tTm90aWZpY2F0aW9uKHZhbHVlOiBib29sZWFuKSB7XG4gICAgdGhpcy5zZXQoXCJkaXNhYmxlWm9vbU5vdGlmaWNhdGlvblwiLCB2YWx1ZSk7XG4gIH1cblxuICBnZXQgaGlkZVdhcm5pbmcoKSB7XG4gICAgcmV0dXJuIHRoaXMudmFsdWVzLmhpZGVXYXJuaW5nO1xuICB9XG4gIHNldCBoaWRlV2FybmluZyh2YWx1ZTogYm9vbGVhbikge1xuICAgIHRoaXMuc2V0KFwiaGlkZVdhcm5pbmdcIiwgdmFsdWUpO1xuICB9XG5cbiAgb25DaGFuZ2U8VCBleHRlbmRzIEs+KGtleTogVCwgY2I6IENhbGxiYWNrPFQ+KSB7XG4gICAgaWYgKCF0aGlzLmhhbmRsZXJzLmhhcyhrZXkpKSB7XG4gICAgICB0aGlzLmhhbmRsZXJzLnNldChrZXksIG5ldyBTZXQoKSk7XG4gICAgfVxuXG4gICAgdGhpcy5oYW5kbGVycy5nZXQoa2V5KS5hZGQoY2IpO1xuICB9XG5cbiAgcmVtb3ZlQ2FsbGJhY2s8VCBleHRlbmRzIEs+KGtleTogVCwgY2I6IENhbGxiYWNrPFQ+KTogdm9pZCB7XG4gICAgY29uc3QgaGFuZGxlcnMgPSB0aGlzLmhhbmRsZXJzLmdldChrZXkpO1xuXG4gICAgaWYgKGhhbmRsZXJzKSB7XG4gICAgICBoYW5kbGVycy5kZWxldGUoY2IpO1xuICAgIH1cbiAgfVxuXG4gIHJlc2V0KCkge1xuICAgIGZvciAoY29uc3QgW2ssIHZdIG9mIE9iamVjdC5lbnRyaWVzKERFRkFVTFRfU0VUVElOR1MpKSB7XG4gICAgICB0aGlzLnNldChrLCB2KTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBsb2FkKCkge1xuICAgIHRoaXMudmFsdWVzID0gT2JqZWN0LmFzc2lnbihcbiAgICAgIHt9LFxuICAgICAgREVGQVVMVF9TRVRUSU5HUyxcbiAgICAgIGF3YWl0IHRoaXMuc3RvcmFnZS5sb2FkRGF0YSgpXG4gICAgKTtcbiAgfVxuXG4gIGFzeW5jIHNhdmUoKSB7XG4gICAgYXdhaXQgdGhpcy5zdG9yYWdlLnNhdmVEYXRhKHRoaXMudmFsdWVzKTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0PFQgZXh0ZW5kcyBLPihrZXk6IFQsIHZhbHVlOiBWPEs+KTogdm9pZCB7XG4gICAgdGhpcy52YWx1ZXNba2V5XSA9IHZhbHVlO1xuICAgIGNvbnN0IGNhbGxiYWNrcyA9IHRoaXMuaGFuZGxlcnMuZ2V0KGtleSk7XG5cbiAgICBpZiAoIWNhbGxiYWNrcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgY2Igb2YgY2FsbGJhY2tzLnZhbHVlcygpKSB7XG4gICAgICBjYih2YWx1ZSk7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBPYnNpZGlhbk91dGxpbmVyUGx1Z2luU2V0dGluZ1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xuICBjb25zdHJ1Y3RvcihhcHA6IEFwcCwgcGx1Z2luOiBQbHVnaW5fMiwgcHJpdmF0ZSBzZXR0aW5nczogU2V0dGluZ3NTZXJ2aWNlKSB7XG4gICAgc3VwZXIoYXBwLCBwbHVnaW4pO1xuICB9XG5cbiAgZGlzcGxheSgpOiB2b2lkIHtcbiAgICBjb25zdCB7IGNvbnRhaW5lckVsIH0gPSB0aGlzO1xuXG4gICAgY29udGFpbmVyRWwuZW1wdHkoKTtcblxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgLnNldE5hbWUoXCJJbXByb3ZlIHRoZSBzdHlsZSBvZiB5b3VyIGxpc3RzXCIpXG4gICAgICAuc2V0RGVzYyhcbiAgICAgICAgXCJTdHlsZXMgYXJlIG9ubHkgY29tcGF0aWJsZSB3aXRoIGJ1aWx0LWluIE9ic2lkaWFuIHRoZW1lcyBhbmQgbWF5IG5vdCBiZSBjb21wYXRpYmxlIHdpdGggb3RoZXIgdGhlbWVzLiBTdHlsZXMgb25seSB3b3JrIHdlbGwgd2l0aCB0YWIgc2l6ZSA0LlwiXG4gICAgICApXG4gICAgICAuYWRkVG9nZ2xlKCh0b2dnbGUpID0+IHtcbiAgICAgICAgdG9nZ2xlLnNldFZhbHVlKHRoaXMuc2V0dGluZ3Muc3R5bGVMaXN0cykub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG4gICAgICAgICAgdGhpcy5zZXR0aW5ncy5zdHlsZUxpc3RzID0gdmFsdWU7XG4gICAgICAgICAgYXdhaXQgdGhpcy5zZXR0aW5ncy5zYXZlKCk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG5cbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcbiAgICAgIC5zZXROYW1lKFwiU3RpY2sgdGhlIGN1cnNvciB0byB0aGUgY29udGVudFwiKVxuICAgICAgLnNldERlc2MoXCJEb24ndCBsZXQgdGhlIGN1cnNvciBtb3ZlIHRvIHRoZSBidWxsZXQgcG9zaXRpb24uXCIpXG4gICAgICAuYWRkVG9nZ2xlKCh0b2dnbGUpID0+IHtcbiAgICAgICAgdG9nZ2xlLnNldFZhbHVlKHRoaXMuc2V0dGluZ3Muc3RpY2tDdXJzb3IpLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgIHRoaXMuc2V0dGluZ3Muc3RpY2tDdXJzb3IgPSB2YWx1ZTtcbiAgICAgICAgICBhd2FpdCB0aGlzLnNldHRpbmdzLnNhdmUoKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcblxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgLnNldE5hbWUoXCJFbmhhbmNlIHRoZSBFbnRlciBrZXlcIilcbiAgICAgIC5zZXREZXNjKFwiTWFrZSB0aGUgRW50ZXIga2V5IGJlaGF2ZSB0aGUgc2FtZSBhcyBvdGhlciBvdXRsaW5lcnMuXCIpXG4gICAgICAuYWRkVG9nZ2xlKCh0b2dnbGUpID0+IHtcbiAgICAgICAgdG9nZ2xlLnNldFZhbHVlKHRoaXMuc2V0dGluZ3MuYmV0dGVyRW50ZXIpLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgIHRoaXMuc2V0dGluZ3MuYmV0dGVyRW50ZXIgPSB2YWx1ZTtcbiAgICAgICAgICBhd2FpdCB0aGlzLnNldHRpbmdzLnNhdmUoKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcblxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgLnNldE5hbWUoXCJFbmhhbmNlIHRoZSBDdHJsK0Egb3IgQ21kK0EgYmVoYXZpb3JcIilcbiAgICAgIC5zZXREZXNjKFxuICAgICAgICBcIlByZXNzIHRoZSBob3RrZXkgb25jZSB0byBzZWxlY3QgdGhlIGN1cnJlbnQgbGlzdCBpdGVtLiBQcmVzcyB0aGUgaG90a2V5IHR3aWNlIHRvIHNlbGVjdCB0aGUgZW50aXJlIGxpc3QuXCJcbiAgICAgIClcbiAgICAgIC5hZGRUb2dnbGUoKHRvZ2dsZSkgPT4ge1xuICAgICAgICB0b2dnbGUuc2V0VmFsdWUodGhpcy5zZXR0aW5ncy5zZWxlY3RBbGwpLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgIHRoaXMuc2V0dGluZ3Muc2VsZWN0QWxsID0gdmFsdWU7XG4gICAgICAgICAgYXdhaXQgdGhpcy5zZXR0aW5ncy5zYXZlKCk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG5cbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcbiAgICAgIC5zZXROYW1lKFwiRGlzYWJsZSBub3RpZmljYXRpb24gYWJvdXQgT2JzaWRpYW4gWm9vbSBwbHVnaW5cIilcbiAgICAgIC5hZGRUb2dnbGUoKHRvZ2dsZSkgPT4ge1xuICAgICAgICB0b2dnbGVcbiAgICAgICAgICAuc2V0VmFsdWUodGhpcy5zZXR0aW5ncy5kaXNhYmxlWm9vbU5vdGlmaWNhdGlvbilcbiAgICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNldHRpbmdzLmRpc2FibGVab29tTm90aWZpY2F0aW9uID0gdmFsdWU7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNldHRpbmdzLnNhdmUoKTtcbiAgICAgICAgICB9KTtcbiAgICAgIH0pO1xuXG4gICAgbmV3IFNldHRpbmcoY29udGFpbmVyRWwpXG4gICAgICAuc2V0TmFtZShcIkhpZGUgdGhlIHdhcm5pbmcgYWJvdXQgZm91ci1zcGFjZSB0YWJzXCIpXG4gICAgICAuYWRkVG9nZ2xlKCh0b2dnbGUpID0+IHtcbiAgICAgICAgdG9nZ2xlLnNldFZhbHVlKHRoaXMuc2V0dGluZ3MuaGlkZVdhcm5pbmcpLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgIHRoaXMuc2V0dGluZ3MuaGlkZVdhcm5pbmcgPSB2YWx1ZTtcbiAgICAgICAgICBhd2FpdCB0aGlzLnNldHRpbmdzLnNhdmUoKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcblxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgLnNldE5hbWUoXCJEZWJ1ZyBtb2RlXCIpXG4gICAgICAuc2V0RGVzYyhcbiAgICAgICAgXCJPcGVuIERldlRvb2xzIChDb21tYW5kK09wdGlvbitJIG9yIENvbnRyb2wrU2hpZnQrSSkgdG8gY29weSB0aGUgZGVidWcgbG9ncy5cIlxuICAgICAgKVxuICAgICAgLmFkZFRvZ2dsZSgodG9nZ2xlKSA9PiB7XG4gICAgICAgIHRvZ2dsZS5zZXRWYWx1ZSh0aGlzLnNldHRpbmdzLmRlYnVnKS5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcbiAgICAgICAgICB0aGlzLnNldHRpbmdzLmRlYnVnID0gdmFsdWU7XG4gICAgICAgICAgYXdhaXQgdGhpcy5zZXR0aW5ncy5zYXZlKCk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gIH1cbn1cbiIsImltcG9ydCB7IEFwcCwgTWFya2Rvd25WaWV3IH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSU9ic2lkaWFuVGFic1NldHRpZ25zIHtcbiAgdXNlVGFiOiBib29sZWFuO1xuICB0YWJTaXplOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSU9ic2lkaWFuRm9sZFNldHRpZ25zIHtcbiAgZm9sZEluZGVudDogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGNsYXNzIE9ic2lkaWFuU2VydmljZSB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgYXBwOiBBcHApIHt9XG5cbiAgZ2V0T2JzaWRpYW5UYWJzU2V0dGlnbnMoKTogSU9ic2lkaWFuVGFic1NldHRpZ25zIHtcbiAgICByZXR1cm4ge1xuICAgICAgdXNlVGFiOiB0cnVlLFxuICAgICAgdGFiU2l6ZTogNCxcbiAgICAgIC4uLih0aGlzLmFwcC52YXVsdCBhcyBhbnkpLmNvbmZpZyxcbiAgICB9O1xuICB9XG5cbiAgZ2V0T2JzaWRpYW5Gb2xkU2V0dGlnbnMoKTogSU9ic2lkaWFuRm9sZFNldHRpZ25zIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9sZEluZGVudDogZmFsc2UsXG4gICAgICAuLi4odGhpcy5hcHAudmF1bHQgYXMgYW55KS5jb25maWcsXG4gICAgfTtcbiAgfVxuXG4gIGdldEFjdGl2ZUxlYWZEaXNwbGF5VGV4dCgpIHtcbiAgICByZXR1cm4gdGhpcy5hcHAud29ya3NwYWNlLmFjdGl2ZUxlYWYuZ2V0RGlzcGxheVRleHQoKTtcbiAgfVxuXG4gIGNyZWF0ZUNvbW1hbmRDYWxsYmFjayhjYjogKGVkaXRvcjogQ29kZU1pcnJvci5FZGl0b3IpID0+IGJvb2xlYW4pIHtcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgY29uc3QgdmlldyA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRBY3RpdmVWaWV3T2ZUeXBlKE1hcmtkb3duVmlldyk7XG5cbiAgICAgIGlmICghdmlldykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGVkaXRvciA9ICh2aWV3IGFzIGFueSkuc291cmNlTW9kZS5jbUVkaXRvcjtcblxuICAgICAgY29uc3Qgc2hvdWxkU3RvcFByb3BhZ2F0aW9uID0gY2IoZWRpdG9yKTtcblxuICAgICAgaWYgKFxuICAgICAgICAhc2hvdWxkU3RvcFByb3BhZ2F0aW9uICYmXG4gICAgICAgIHdpbmRvdy5ldmVudCAmJlxuICAgICAgICB3aW5kb3cuZXZlbnQudHlwZSA9PT0gXCJrZXlkb3duXCJcbiAgICAgICkge1xuICAgICAgICAoZWRpdG9yIGFzIGFueSkudHJpZ2dlck9uS2V5RG93bih3aW5kb3cuZXZlbnQpO1xuICAgICAgfVxuICAgIH07XG4gIH1cbn1cbiIsImV4cG9ydCBmdW5jdGlvbiBjbXBQb3MoYTogSVBvc2l0aW9uLCBiOiBJUG9zaXRpb24pIHtcbiAgcmV0dXJuIGEubGluZSAtIGIubGluZSB8fCBhLmNoIC0gYi5jaDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1heFBvcyhhOiBJUG9zaXRpb24sIGI6IElQb3NpdGlvbikge1xuICByZXR1cm4gY21wUG9zKGEsIGIpIDwgMCA/IGIgOiBhO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWluUG9zKGE6IElQb3NpdGlvbiwgYjogSVBvc2l0aW9uKSB7XG4gIHJldHVybiBjbXBQb3MoYSwgYikgPCAwID8gYSA6IGI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSVBvc2l0aW9uIHtcbiAgY2g6IG51bWJlcjtcbiAgbGluZTogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIElMaXN0TGluZSB7XG4gIHRleHQ6IHN0cmluZztcbiAgZnJvbTogSVBvc2l0aW9uO1xuICB0bzogSVBvc2l0aW9uO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIElSYW5nZSB7XG4gIGFuY2hvcjogSVBvc2l0aW9uO1xuICBoZWFkOiBJUG9zaXRpb247XG59XG5cbmV4cG9ydCBjbGFzcyBMaXN0IHtcbiAgcHJpdmF0ZSBwYXJlbnQ6IExpc3QgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjaGlsZHJlbjogTGlzdFtdID0gW107XG4gIHByaXZhdGUgbm90ZXNJbmRlbnQ6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGxpbmVzOiBzdHJpbmdbXSA9IFtdO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcm9vdDogUm9vdCxcbiAgICBwcml2YXRlIGluZGVudDogc3RyaW5nLFxuICAgIHByaXZhdGUgYnVsbGV0OiBzdHJpbmcsXG4gICAgcHJpdmF0ZSBzcGFjZUFmdGVyQnVsbGV0OiBzdHJpbmcsXG4gICAgZmlyc3RMaW5lOiBzdHJpbmcsXG4gICAgcHJpdmF0ZSBmb2xkZWQ6IGJvb2xlYW5cbiAgKSB7XG4gICAgdGhpcy5saW5lcy5wdXNoKGZpcnN0TGluZSk7XG4gIH1cblxuICBnZXROb3Rlc0luZGVudCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5ub3Rlc0luZGVudDtcbiAgfVxuXG4gIHNldE5vdGVzSW5kZW50KG5vdGVzSW5kZW50OiBzdHJpbmcpIHtcbiAgICBpZiAodGhpcy5ub3Rlc0luZGVudCAhPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBOb3RlcyBpbmRlbnQgYWxyZWFkeSBwcm92aWRlZGApO1xuICAgIH1cbiAgICB0aGlzLm5vdGVzSW5kZW50ID0gbm90ZXNJbmRlbnQ7XG4gIH1cblxuICBhZGRMaW5lKHRleHQ6IHN0cmluZykge1xuICAgIGlmICh0aGlzLm5vdGVzSW5kZW50ID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBVbmFibGUgdG8gYWRkIGxpbmUsIG5vdGVzIGluZGVudCBzaG91bGQgYmUgcHJvdmlkZWQgZmlyc3RgXG4gICAgICApO1xuICAgIH1cblxuICAgIHRoaXMubGluZXMucHVzaCh0ZXh0KTtcbiAgfVxuXG4gIHJlcGxhY2VMaW5lcyhsaW5lczogc3RyaW5nW10pIHtcbiAgICBpZiAobGluZXMubGVuZ3RoID4gMSAmJiB0aGlzLm5vdGVzSW5kZW50ID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBVbmFibGUgdG8gYWRkIGxpbmUsIG5vdGVzIGluZGVudCBzaG91bGQgYmUgcHJvdmlkZWQgZmlyc3RgXG4gICAgICApO1xuICAgIH1cblxuICAgIHRoaXMubGluZXMgPSBsaW5lcztcbiAgfVxuXG4gIGdldExpbmVDb3VudCgpIHtcbiAgICByZXR1cm4gdGhpcy5saW5lcy5sZW5ndGg7XG4gIH1cblxuICBnZXRSb290KCkge1xuICAgIHJldHVybiB0aGlzLnJvb3Q7XG4gIH1cblxuICBnZXRDaGlsZHJlbigpIHtcbiAgICByZXR1cm4gdGhpcy5jaGlsZHJlbi5jb25jYXQoKTtcbiAgfVxuXG4gIGdldExpbmVzSW5mbygpOiBJTGlzdExpbmVbXSB7XG4gICAgY29uc3Qgc3RhcnRMaW5lID0gdGhpcy5yb290LmdldENvbnRlbnRMaW5lc1JhbmdlT2YodGhpcylbMF07XG5cbiAgICByZXR1cm4gdGhpcy5saW5lcy5tYXAoKHJvdywgaSkgPT4ge1xuICAgICAgY29uc3QgbGluZSA9IHN0YXJ0TGluZSArIGk7XG4gICAgICBjb25zdCBzdGFydENoID1cbiAgICAgICAgaSA9PT0gMCA/IHRoaXMuZ2V0Q29udGVudFN0YXJ0Q2goKSA6IHRoaXMubm90ZXNJbmRlbnQubGVuZ3RoO1xuICAgICAgY29uc3QgZW5kQ2ggPSBzdGFydENoICsgcm93Lmxlbmd0aDtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdGV4dDogcm93LFxuICAgICAgICBmcm9tOiB7IGxpbmUsIGNoOiBzdGFydENoIH0sXG4gICAgICAgIHRvOiB7IGxpbmUsIGNoOiBlbmRDaCB9LFxuICAgICAgfTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldExpbmVzKCk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gdGhpcy5saW5lcy5jb25jYXQoKTtcbiAgfVxuXG4gIGdldEZpcnN0TGluZUNvbnRlbnRTdGFydCgpIHtcbiAgICBjb25zdCBzdGFydExpbmUgPSB0aGlzLnJvb3QuZ2V0Q29udGVudExpbmVzUmFuZ2VPZih0aGlzKVswXTtcblxuICAgIHJldHVybiB7XG4gICAgICBsaW5lOiBzdGFydExpbmUsXG4gICAgICBjaDogdGhpcy5nZXRDb250ZW50U3RhcnRDaCgpLFxuICAgIH07XG4gIH1cblxuICBnZXRMYXN0TGluZUNvbnRlbnRFbmQoKSB7XG4gICAgY29uc3QgZW5kTGluZSA9IHRoaXMucm9vdC5nZXRDb250ZW50TGluZXNSYW5nZU9mKHRoaXMpWzFdO1xuICAgIGNvbnN0IGVuZENoID1cbiAgICAgIHRoaXMubGluZXMubGVuZ3RoID09PSAxXG4gICAgICAgID8gdGhpcy5nZXRDb250ZW50U3RhcnRDaCgpICsgdGhpcy5saW5lc1swXS5sZW5ndGhcbiAgICAgICAgOiB0aGlzLm5vdGVzSW5kZW50Lmxlbmd0aCArIHRoaXMubGluZXNbdGhpcy5saW5lcy5sZW5ndGggLSAxXS5sZW5ndGg7XG5cbiAgICByZXR1cm4ge1xuICAgICAgbGluZTogZW5kTGluZSxcbiAgICAgIGNoOiBlbmRDaCxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRDb250ZW50U3RhcnRDaCgpIHtcbiAgICByZXR1cm4gdGhpcy5pbmRlbnQubGVuZ3RoICsgdGhpcy5idWxsZXQubGVuZ3RoICsgMTtcbiAgfVxuXG4gIGlzRm9sZGVkKCk6IGJvb2xlYW4ge1xuICAgIGlmICh0aGlzLmZvbGRlZCkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMucGFyZW50KSB7XG4gICAgICByZXR1cm4gdGhpcy5wYXJlbnQuaXNGb2xkZWQoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBpc0ZvbGRSb290KCkge1xuICAgIGxldCBwYXJlbnQgPSB0aGlzLmdldFBhcmVudCgpO1xuXG4gICAgd2hpbGUgKHBhcmVudCkge1xuICAgICAgaWYgKHBhcmVudC5mb2xkZWQpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICBwYXJlbnQgPSBwYXJlbnQuZ2V0UGFyZW50KCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZm9sZGVkO1xuICB9XG5cbiAgZ2V0TGV2ZWwoKTogbnVtYmVyIHtcbiAgICBpZiAoIXRoaXMucGFyZW50KSB7XG4gICAgICByZXR1cm4gMDtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5wYXJlbnQuZ2V0TGV2ZWwoKSArIDE7XG4gIH1cblxuICB1bmluZGVudENvbnRlbnQoZnJvbTogbnVtYmVyLCB0aWxsOiBudW1iZXIpIHtcbiAgICB0aGlzLmluZGVudCA9IHRoaXMuaW5kZW50LnNsaWNlKDAsIGZyb20pICsgdGhpcy5pbmRlbnQuc2xpY2UodGlsbCk7XG4gICAgaWYgKHRoaXMubm90ZXNJbmRlbnQgIT09IG51bGwpIHtcbiAgICAgIHRoaXMubm90ZXNJbmRlbnQgPVxuICAgICAgICB0aGlzLm5vdGVzSW5kZW50LnNsaWNlKDAsIGZyb20pICsgdGhpcy5ub3Rlc0luZGVudC5zbGljZSh0aWxsKTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIHRoaXMuY2hpbGRyZW4pIHtcbiAgICAgIGNoaWxkLnVuaW5kZW50Q29udGVudChmcm9tLCB0aWxsKTtcbiAgICB9XG4gIH1cblxuICBpbmRlbnRDb250ZW50KGluZGVudFBvczogbnVtYmVyLCBpbmRlbnRDaGFyczogc3RyaW5nKSB7XG4gICAgdGhpcy5pbmRlbnQgPVxuICAgICAgdGhpcy5pbmRlbnQuc2xpY2UoMCwgaW5kZW50UG9zKSArXG4gICAgICBpbmRlbnRDaGFycyArXG4gICAgICB0aGlzLmluZGVudC5zbGljZShpbmRlbnRQb3MpO1xuICAgIGlmICh0aGlzLm5vdGVzSW5kZW50ICE9PSBudWxsKSB7XG4gICAgICB0aGlzLm5vdGVzSW5kZW50ID1cbiAgICAgICAgdGhpcy5ub3Rlc0luZGVudC5zbGljZSgwLCBpbmRlbnRQb3MpICtcbiAgICAgICAgaW5kZW50Q2hhcnMgK1xuICAgICAgICB0aGlzLm5vdGVzSW5kZW50LnNsaWNlKGluZGVudFBvcyk7XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBjaGlsZCBvZiB0aGlzLmNoaWxkcmVuKSB7XG4gICAgICBjaGlsZC5pbmRlbnRDb250ZW50KGluZGVudFBvcywgaW5kZW50Q2hhcnMpO1xuICAgIH1cbiAgfVxuXG4gIGdldEZpcnN0TGluZUluZGVudCgpIHtcbiAgICByZXR1cm4gdGhpcy5pbmRlbnQ7XG4gIH1cblxuICBnZXRCdWxsZXQoKSB7XG4gICAgcmV0dXJuIHRoaXMuYnVsbGV0O1xuICB9XG5cbiAgZ2V0U3BhY2VBZnRlckJ1bGxldCgpIHtcbiAgICByZXR1cm4gdGhpcy5zcGFjZUFmdGVyQnVsbGV0O1xuICB9XG5cbiAgcmVwbGF0ZUJ1bGxldChidWxsZXQ6IHN0cmluZykge1xuICAgIHRoaXMuYnVsbGV0ID0gYnVsbGV0O1xuICB9XG5cbiAgZ2V0UGFyZW50KCkge1xuICAgIHJldHVybiB0aGlzLnBhcmVudDtcbiAgfVxuXG4gIGFkZEJlZm9yZUFsbChsaXN0OiBMaXN0KSB7XG4gICAgdGhpcy5jaGlsZHJlbi51bnNoaWZ0KGxpc3QpO1xuICAgIGxpc3QucGFyZW50ID0gdGhpcztcbiAgfVxuXG4gIGFkZEFmdGVyQWxsKGxpc3Q6IExpc3QpIHtcbiAgICB0aGlzLmNoaWxkcmVuLnB1c2gobGlzdCk7XG4gICAgbGlzdC5wYXJlbnQgPSB0aGlzO1xuICB9XG5cbiAgcmVtb3ZlQ2hpbGQobGlzdDogTGlzdCkge1xuICAgIGNvbnN0IGkgPSB0aGlzLmNoaWxkcmVuLmluZGV4T2YobGlzdCk7XG4gICAgdGhpcy5jaGlsZHJlbi5zcGxpY2UoaSwgMSk7XG4gICAgbGlzdC5wYXJlbnQgPSBudWxsO1xuICB9XG5cbiAgYWRkQmVmb3JlKGJlZm9yZTogTGlzdCwgbGlzdDogTGlzdCkge1xuICAgIGNvbnN0IGkgPSB0aGlzLmNoaWxkcmVuLmluZGV4T2YoYmVmb3JlKTtcbiAgICB0aGlzLmNoaWxkcmVuLnNwbGljZShpLCAwLCBsaXN0KTtcbiAgICBsaXN0LnBhcmVudCA9IHRoaXM7XG4gIH1cblxuICBhZGRBZnRlcihiZWZvcmU6IExpc3QsIGxpc3Q6IExpc3QpIHtcbiAgICBjb25zdCBpID0gdGhpcy5jaGlsZHJlbi5pbmRleE9mKGJlZm9yZSk7XG4gICAgdGhpcy5jaGlsZHJlbi5zcGxpY2UoaSArIDEsIDAsIGxpc3QpO1xuICAgIGxpc3QucGFyZW50ID0gdGhpcztcbiAgfVxuXG4gIGdldFByZXZTaWJsaW5nT2YobGlzdDogTGlzdCkge1xuICAgIGNvbnN0IGkgPSB0aGlzLmNoaWxkcmVuLmluZGV4T2YobGlzdCk7XG4gICAgcmV0dXJuIGkgPiAwID8gdGhpcy5jaGlsZHJlbltpIC0gMV0gOiBudWxsO1xuICB9XG5cbiAgZ2V0TmV4dFNpYmxpbmdPZihsaXN0OiBMaXN0KSB7XG4gICAgY29uc3QgaSA9IHRoaXMuY2hpbGRyZW4uaW5kZXhPZihsaXN0KTtcbiAgICByZXR1cm4gaSA+PSAwICYmIGkgPCB0aGlzLmNoaWxkcmVuLmxlbmd0aCA/IHRoaXMuY2hpbGRyZW5baSArIDFdIDogbnVsbDtcbiAgfVxuXG4gIGlzRW1wdHkoKSB7XG4gICAgcmV0dXJuIHRoaXMuY2hpbGRyZW4ubGVuZ3RoID09PSAwO1xuICB9XG5cbiAgcHJpbnQoKSB7XG4gICAgbGV0IHJlcyA9IFwiXCI7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHJlcyArPSBpID09PSAwID8gdGhpcy5pbmRlbnQgKyB0aGlzLmJ1bGxldCArIHRoaXMuc3BhY2VBZnRlckJ1bGxldCA6IHRoaXMubm90ZXNJbmRlbnQ7XG4gICAgICByZXMgKz0gdGhpcy5saW5lc1tpXTtcbiAgICAgIHJlcyArPSBcIlxcblwiO1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgY2hpbGQgb2YgdGhpcy5jaGlsZHJlbikge1xuICAgICAgcmVzICs9IGNoaWxkLnByaW50KCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlcztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgUm9vdCB7XG4gIHByaXZhdGUgcm9vdExpc3QgPSBuZXcgTGlzdCh0aGlzLCBcIlwiLCBcIlwiLCBcIlwiLCBcIlwiLCBmYWxzZSk7XG4gIHByaXZhdGUgc2VsZWN0aW9uczogSVJhbmdlW10gPSBbXTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHN0YXJ0OiBJUG9zaXRpb24sXG4gICAgcHJpdmF0ZSBlbmQ6IElQb3NpdGlvbixcbiAgICBzZWxlY3Rpb25zOiBJUmFuZ2VbXVxuICApIHtcbiAgICB0aGlzLnJlcGxhY2VTZWxlY3Rpb25zKHNlbGVjdGlvbnMpO1xuICB9XG5cbiAgZ2V0Um9vdExpc3QoKSB7XG4gICAgcmV0dXJuIHRoaXMucm9vdExpc3Q7XG4gIH1cblxuICBnZXRSYW5nZSgpOiBbSVBvc2l0aW9uLCBJUG9zaXRpb25dIHtcbiAgICByZXR1cm4gW3sgLi4udGhpcy5zdGFydCB9LCB7IC4uLnRoaXMuZW5kIH1dO1xuICB9XG5cbiAgZ2V0U2VsZWN0aW9ucygpOiBJUmFuZ2VbXSB7XG4gICAgcmV0dXJuIHRoaXMuc2VsZWN0aW9ucy5tYXAoKHMpID0+ICh7XG4gICAgICBhbmNob3I6IHsgLi4ucy5hbmNob3IgfSxcbiAgICAgIGhlYWQ6IHsgLi4ucy5oZWFkIH0sXG4gICAgfSkpO1xuICB9XG5cbiAgaGFzU2luZ2xlQ3Vyc29yKCkge1xuICAgIGlmICghdGhpcy5oYXNTaW5nbGVTZWxlY3Rpb24oKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGNvbnN0IHNlbGVjdGlvbiA9IHRoaXMuc2VsZWN0aW9uc1swXTtcblxuICAgIHJldHVybiAoXG4gICAgICBzZWxlY3Rpb24uYW5jaG9yLmxpbmUgPT09IHNlbGVjdGlvbi5oZWFkLmxpbmUgJiZcbiAgICAgIHNlbGVjdGlvbi5hbmNob3IuY2ggPT09IHNlbGVjdGlvbi5oZWFkLmNoXG4gICAgKTtcbiAgfVxuXG4gIGhhc1NpbmdsZVNlbGVjdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3Rpb25zLmxlbmd0aCA9PT0gMTtcbiAgfVxuXG4gIGdldEN1cnNvcigpIHtcbiAgICByZXR1cm4geyAuLi50aGlzLnNlbGVjdGlvbnNbdGhpcy5zZWxlY3Rpb25zLmxlbmd0aCAtIDFdLmhlYWQgfTtcbiAgfVxuXG4gIHJlcGxhY2VDdXJzb3IoY3Vyc29yOiBJUG9zaXRpb24pIHtcbiAgICB0aGlzLnNlbGVjdGlvbnMgPSBbeyBhbmNob3I6IGN1cnNvciwgaGVhZDogY3Vyc29yIH1dO1xuICB9XG5cbiAgcmVwbGFjZVNlbGVjdGlvbnMoc2VsZWN0aW9uczogSVJhbmdlW10pIHtcbiAgICBpZiAoc2VsZWN0aW9ucy5sZW5ndGggPCAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFVuYWJsZSB0byBjcmVhdGUgUm9vdCB3aXRob3V0IHNlbGVjdGlvbnNgKTtcbiAgICB9XG4gICAgdGhpcy5zZWxlY3Rpb25zID0gc2VsZWN0aW9ucztcbiAgfVxuXG4gIGdldExpc3RVbmRlckN1cnNvcigpOiBMaXN0IHtcbiAgICByZXR1cm4gdGhpcy5nZXRMaXN0VW5kZXJMaW5lKHRoaXMuZ2V0Q3Vyc29yKCkubGluZSk7XG4gIH1cblxuICBnZXRMaXN0VW5kZXJMaW5lKGxpbmU6IG51bWJlcikge1xuICAgIGlmIChsaW5lIDwgdGhpcy5zdGFydC5saW5lIHx8IGxpbmUgPiB0aGlzLmVuZC5saW5lKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IHJlc3VsdDogTGlzdCA9IG51bGw7XG4gICAgbGV0IGluZGV4OiBudW1iZXIgPSB0aGlzLnN0YXJ0LmxpbmU7XG5cbiAgICBjb25zdCB2aXNpdEFyciA9IChsbDogTGlzdFtdKSA9PiB7XG4gICAgICBmb3IgKGNvbnN0IGwgb2YgbGwpIHtcbiAgICAgICAgY29uc3QgbGlzdEZyb21MaW5lID0gaW5kZXg7XG4gICAgICAgIGNvbnN0IGxpc3RUaWxsTGluZSA9IGxpc3RGcm9tTGluZSArIGwuZ2V0TGluZUNvdW50KCkgLSAxO1xuXG4gICAgICAgIGlmIChsaW5lID49IGxpc3RGcm9tTGluZSAmJiBsaW5lIDw9IGxpc3RUaWxsTGluZSkge1xuICAgICAgICAgIHJlc3VsdCA9IGw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaW5kZXggPSBsaXN0VGlsbExpbmUgKyAxO1xuICAgICAgICAgIHZpc2l0QXJyKGwuZ2V0Q2hpbGRyZW4oKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHJlc3VsdCAhPT0gbnVsbCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG5cbiAgICB2aXNpdEFycih0aGlzLnJvb3RMaXN0LmdldENoaWxkcmVuKCkpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGdldENvbnRlbnRMaW5lc1JhbmdlT2YobGlzdDogTGlzdCk6IFtudW1iZXIsIG51bWJlcl0gfCBudWxsIHtcbiAgICBsZXQgcmVzdWx0OiBbbnVtYmVyLCBudW1iZXJdIHwgbnVsbCA9IG51bGw7XG4gICAgbGV0IGxpbmU6IG51bWJlciA9IHRoaXMuc3RhcnQubGluZTtcblxuICAgIGNvbnN0IHZpc2l0QXJyID0gKGxsOiBMaXN0W10pID0+IHtcbiAgICAgIGZvciAoY29uc3QgbCBvZiBsbCkge1xuICAgICAgICBjb25zdCBsaXN0RnJvbUxpbmUgPSBsaW5lO1xuICAgICAgICBjb25zdCBsaXN0VGlsbExpbmUgPSBsaXN0RnJvbUxpbmUgKyBsLmdldExpbmVDb3VudCgpIC0gMTtcblxuICAgICAgICBpZiAobCA9PT0gbGlzdCkge1xuICAgICAgICAgIHJlc3VsdCA9IFtsaXN0RnJvbUxpbmUsIGxpc3RUaWxsTGluZV07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbGluZSA9IGxpc3RUaWxsTGluZSArIDE7XG4gICAgICAgICAgdmlzaXRBcnIobC5nZXRDaGlsZHJlbigpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZXN1bHQgIT09IG51bGwpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgdmlzaXRBcnIodGhpcy5yb290TGlzdC5nZXRDaGlsZHJlbigpKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBnZXRDaGlsZHJlbigpIHtcbiAgICByZXR1cm4gdGhpcy5yb290TGlzdC5nZXRDaGlsZHJlbigpO1xuICB9XG5cbiAgcHJpbnQoKSB7XG4gICAgbGV0IHJlcyA9IFwiXCI7XG5cbiAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIHRoaXMucm9vdExpc3QuZ2V0Q2hpbGRyZW4oKSkge1xuICAgICAgcmVzICs9IGNoaWxkLnByaW50KCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlcy5yZXBsYWNlKC9cXG4kLywgXCJcIik7XG4gIH1cbn1cbiIsImltcG9ydCB7IExvZ2dlclNlcnZpY2UgfSBmcm9tIFwiLi9Mb2dnZXJTZXJ2aWNlXCI7XG5pbXBvcnQgeyBPYnNpZGlhblNlcnZpY2UgfSBmcm9tIFwiLi9PYnNpZGlhblNlcnZpY2VcIjtcbmltcG9ydCB7IExpc3QsIFJvb3QgfSBmcm9tIFwiLi4vcm9vdFwiO1xuaW1wb3J0IHsgSU9wZXJhdGlvbiB9IGZyb20gXCIuLi9vcGVyYXRpb25zL0lPcGVyYXRpb25cIjtcblxuY29uc3QgYnVsbGV0U2lnbiA9IGAoPzpbLSorXXxcXFxcZCtcXFxcLilgO1xuXG5jb25zdCBsaXN0SXRlbVdpdGhvdXRTcGFjZXNSZSA9IG5ldyBSZWdFeHAoYF4ke2J1bGxldFNpZ259KCB8XFx0KWApO1xuY29uc3QgbGlzdEl0ZW1SZSA9IG5ldyBSZWdFeHAoYF5bIFxcdF0qJHtidWxsZXRTaWdufSggfFxcdClgKTtcbmNvbnN0IHN0cmluZ1dpdGhTcGFjZXNSZSA9IG5ldyBSZWdFeHAoYF5bIFxcdF0rYCk7XG5jb25zdCBwYXJzZUxpc3RJdGVtUmUgPSBuZXcgUmVnRXhwKGBeKFsgXFx0XSopKCR7YnVsbGV0U2lnbn0pKCB8XFx0KSguKikkYCk7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSUFwcGx5Q2hhbmdlc0xpc3Qge1xuICBpc0ZvbGRSb290KCk6IGJvb2xlYW47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSUFwcGx5Q2hhbmdlc1Jvb3Qge1xuICBnZXRSYW5nZSgpOiBbQ29kZU1pcnJvci5Qb3NpdGlvbiwgQ29kZU1pcnJvci5Qb3NpdGlvbl07XG4gIGdldFNlbGVjdGlvbnMoKTogeyBhbmNob3I6IENvZGVNaXJyb3IuUG9zaXRpb247IGhlYWQ6IENvZGVNaXJyb3IuUG9zaXRpb24gfVtdO1xuICBwcmludCgpOiBzdHJpbmc7XG4gIGdldExpc3RVbmRlckxpbmUobDogbnVtYmVyKTogSUFwcGx5Q2hhbmdlc0xpc3Q7XG59XG5cbmludGVyZmFjZSBJUGFyc2VMaXN0TGlzdCB7XG4gIGdldEZpcnN0TGluZUluZGVudCgpOiBzdHJpbmc7XG4gIHNldE5vdGVzSW5kZW50KG5vdGVzSW5kZW50OiBzdHJpbmcpOiB2b2lkO1xuICBnZXROb3Rlc0luZGVudCgpOiBzdHJpbmcgfCBudWxsO1xuICBhZGRMaW5lKHRleHQ6IHN0cmluZyk6IHZvaWQ7XG4gIGdldFBhcmVudCgpOiBJUGFyc2VMaXN0TGlzdCB8IG51bGw7XG4gIGFkZEFmdGVyQWxsKGxpc3Q6IElQYXJzZUxpc3RMaXN0KTogdm9pZDtcbn1cblxuZXhwb3J0IGNsYXNzIExpc3RzU2VydmljZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgbG9nZ2VyU2VydmljZTogTG9nZ2VyU2VydmljZSxcbiAgICBwcml2YXRlIG9ic2lkaWFuU2VydmljZTogT2JzaWRpYW5TZXJ2aWNlXG4gICkge31cblxuICBldmFsT3BlcmF0aW9uKHJvb3Q6IFJvb3QsIG9wOiBJT3BlcmF0aW9uLCBlZGl0b3I6IENvZGVNaXJyb3IuRWRpdG9yKSB7XG4gICAgb3AucGVyZm9ybSgpO1xuXG4gICAgaWYgKG9wLnNob3VsZFVwZGF0ZSgpKSB7XG4gICAgICB0aGlzLmFwcGx5Q2hhbmdlcyhlZGl0b3IsIHJvb3QpO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBzaG91bGRVcGRhdGU6IG9wLnNob3VsZFVwZGF0ZSgpLFxuICAgICAgc2hvdWxkU3RvcFByb3BhZ2F0aW9uOiBvcC5zaG91bGRTdG9wUHJvcGFnYXRpb24oKSxcbiAgICB9O1xuICB9XG5cbiAgcGVyZm9ybU9wZXJhdGlvbihcbiAgICBjYjogKHJvb3Q6IFJvb3QpID0+IElPcGVyYXRpb24sXG4gICAgZWRpdG9yOiBDb2RlTWlycm9yLkVkaXRvcixcbiAgICBjdXJzb3IgPSBlZGl0b3IuZ2V0Q3Vyc29yKClcbiAgKSB7XG4gICAgY29uc3Qgcm9vdCA9IHRoaXMucGFyc2VMaXN0KGVkaXRvciwgY3Vyc29yKTtcblxuICAgIGlmICghcm9vdCkge1xuICAgICAgcmV0dXJuIHsgc2hvdWxkVXBkYXRlOiBmYWxzZSwgc2hvdWxkU3RvcFByb3BhZ2F0aW9uOiBmYWxzZSB9O1xuICAgIH1cblxuICAgIGNvbnN0IG9wID0gY2Iocm9vdCk7XG5cbiAgICByZXR1cm4gdGhpcy5ldmFsT3BlcmF0aW9uKHJvb3QsIG9wLCBlZGl0b3IpO1xuICB9XG5cbiAgcGFyc2VMaXN0KFxuICAgIGVkaXRvcjogQ29kZU1pcnJvci5FZGl0b3IsXG4gICAgY3Vyc29yID0gZWRpdG9yLmdldEN1cnNvcigpXG4gICk6IFJvb3QgfCBudWxsIHtcbiAgICBjb25zdCBkID0gdGhpcy5sb2dnZXJTZXJ2aWNlLmJpbmQoXCJwYXJzZUxpc3RcIik7XG4gICAgY29uc3QgZXJyb3IgPSAobXNnOiBzdHJpbmcpOiBudWxsID0+IHtcbiAgICAgIGQobXNnKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH07XG5cbiAgICBjb25zdCBsaW5lID0gZWRpdG9yLmdldExpbmUoY3Vyc29yLmxpbmUpO1xuXG4gICAgbGV0IGxpc3RMb29raW5nUG9zOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAgIGlmICh0aGlzLmlzTGlzdEl0ZW0obGluZSkpIHtcbiAgICAgIGxpc3RMb29raW5nUG9zID0gY3Vyc29yLmxpbmU7XG4gICAgfSBlbHNlIGlmICh0aGlzLmlzTGluZVdpdGhJbmRlbnQobGluZSkpIHtcbiAgICAgIGxldCBsaXN0TG9va2luZ1Bvc1NlYXJjaCA9IGN1cnNvci5saW5lIC0gMTtcbiAgICAgIHdoaWxlIChsaXN0TG9va2luZ1Bvc1NlYXJjaCA+PSBlZGl0b3IuZmlyc3RMaW5lKCkpIHtcbiAgICAgICAgY29uc3QgbGluZSA9IGVkaXRvci5nZXRMaW5lKGxpc3RMb29raW5nUG9zU2VhcmNoKTtcbiAgICAgICAgaWYgKHRoaXMuaXNMaXN0SXRlbShsaW5lKSkge1xuICAgICAgICAgIGxpc3RMb29raW5nUG9zID0gbGlzdExvb2tpbmdQb3NTZWFyY2g7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH0gZWxzZSBpZiAodGhpcy5pc0xpbmVXaXRoSW5kZW50KGxpbmUpKSB7XG4gICAgICAgICAgbGlzdExvb2tpbmdQb3NTZWFyY2gtLTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChsaXN0TG9va2luZ1BvcyA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBsZXQgbGlzdFN0YXJ0TGluZTogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG4gICAgbGV0IGxpc3RTdGFydExpbmVMb29rdXAgPSBsaXN0TG9va2luZ1BvcztcbiAgICB3aGlsZSAobGlzdFN0YXJ0TGluZUxvb2t1cCA+PSBlZGl0b3IuZmlyc3RMaW5lKCkpIHtcbiAgICAgIGNvbnN0IGxpbmUgPSBlZGl0b3IuZ2V0TGluZShsaXN0U3RhcnRMaW5lTG9va3VwKTtcbiAgICAgIGlmICghdGhpcy5pc0xpc3RJdGVtKGxpbmUpICYmICF0aGlzLmlzTGluZVdpdGhJbmRlbnQobGluZSkpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5pc0xpc3RJdGVtV2l0aG91dFNwYWNlcyhsaW5lKSkge1xuICAgICAgICBsaXN0U3RhcnRMaW5lID0gbGlzdFN0YXJ0TGluZUxvb2t1cDtcbiAgICAgIH1cbiAgICAgIGxpc3RTdGFydExpbmVMb29rdXAtLTtcbiAgICB9XG5cbiAgICBpZiAobGlzdFN0YXJ0TGluZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgbGV0IGxpc3RFbmRMaW5lID0gbGlzdExvb2tpbmdQb3M7XG4gICAgbGV0IGxpc3RFbmRMaW5lTG9va3VwID0gbGlzdExvb2tpbmdQb3M7XG4gICAgd2hpbGUgKGxpc3RFbmRMaW5lTG9va3VwIDw9IGVkaXRvci5sYXN0TGluZSgpKSB7XG4gICAgICBjb25zdCBsaW5lID0gZWRpdG9yLmdldExpbmUobGlzdEVuZExpbmVMb29rdXApO1xuICAgICAgaWYgKCF0aGlzLmlzTGlzdEl0ZW0obGluZSkgJiYgIXRoaXMuaXNMaW5lV2l0aEluZGVudChsaW5lKSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGlmICghdGhpcy5pc0VtcHR5TGluZShsaW5lKSkge1xuICAgICAgICBsaXN0RW5kTGluZSA9IGxpc3RFbmRMaW5lTG9va3VwO1xuICAgICAgfVxuICAgICAgbGlzdEVuZExpbmVMb29rdXArKztcbiAgICB9XG5cbiAgICBpZiAobGlzdFN0YXJ0TGluZSA+IGN1cnNvci5saW5lIHx8IGxpc3RFbmRMaW5lIDwgY3Vyc29yLmxpbmUpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IHJvb3QgPSBuZXcgUm9vdChcbiAgICAgIHsgbGluZTogbGlzdFN0YXJ0TGluZSwgY2g6IDAgfSxcbiAgICAgIHsgbGluZTogbGlzdEVuZExpbmUsIGNoOiBlZGl0b3IuZ2V0TGluZShsaXN0RW5kTGluZSkubGVuZ3RoIH0sXG4gICAgICBlZGl0b3IubGlzdFNlbGVjdGlvbnMoKS5tYXAoKHIpID0+ICh7XG4gICAgICAgIGFuY2hvcjogeyBsaW5lOiByLmFuY2hvci5saW5lLCBjaDogci5hbmNob3IuY2ggfSxcbiAgICAgICAgaGVhZDogeyBsaW5lOiByLmhlYWQubGluZSwgY2g6IHIuaGVhZC5jaCB9LFxuICAgICAgfSkpXG4gICAgKTtcblxuICAgIGxldCBjdXJyZW50UGFyZW50OiBJUGFyc2VMaXN0TGlzdCA9IHJvb3QuZ2V0Um9vdExpc3QoKTtcbiAgICBsZXQgY3VycmVudExpc3Q6IElQYXJzZUxpc3RMaXN0IHwgbnVsbCA9IG51bGw7XG4gICAgbGV0IGN1cnJlbnRJbmRlbnQgPSBcIlwiO1xuXG4gICAgZm9yIChsZXQgbCA9IGxpc3RTdGFydExpbmU7IGwgPD0gbGlzdEVuZExpbmU7IGwrKykge1xuICAgICAgY29uc3QgbGluZSA9IGVkaXRvci5nZXRMaW5lKGwpO1xuICAgICAgY29uc3QgbWF0Y2hlcyA9IHBhcnNlTGlzdEl0ZW1SZS5leGVjKGxpbmUpO1xuXG4gICAgICBpZiAobWF0Y2hlcykge1xuICAgICAgICBjb25zdCBbXywgaW5kZW50LCBidWxsZXQsIHNwYWNlQWZ0ZXJCdWxsZXQsIGNvbnRlbnRdID0gbWF0Y2hlcztcblxuICAgICAgICBjb25zdCBjb21wYXJlTGVuZ3RoID0gTWF0aC5taW4oY3VycmVudEluZGVudC5sZW5ndGgsIGluZGVudC5sZW5ndGgpO1xuICAgICAgICBjb25zdCBpbmRlbnRTbGljZSA9IGluZGVudC5zbGljZSgwLCBjb21wYXJlTGVuZ3RoKTtcbiAgICAgICAgY29uc3QgY3VycmVudEluZGVudFNsaWNlID0gY3VycmVudEluZGVudC5zbGljZSgwLCBjb21wYXJlTGVuZ3RoKTtcblxuICAgICAgICBpZiAoaW5kZW50U2xpY2UgIT09IGN1cnJlbnRJbmRlbnRTbGljZSkge1xuICAgICAgICAgIGNvbnN0IGV4cGVjdGVkID0gY3VycmVudEluZGVudFNsaWNlXG4gICAgICAgICAgICAucmVwbGFjZSgvIC9nLCBcIlNcIilcbiAgICAgICAgICAgIC5yZXBsYWNlKC9cXHQvZywgXCJUXCIpO1xuICAgICAgICAgIGNvbnN0IGdvdCA9IGluZGVudFNsaWNlLnJlcGxhY2UoLyAvZywgXCJTXCIpLnJlcGxhY2UoL1xcdC9nLCBcIlRcIik7XG5cbiAgICAgICAgICByZXR1cm4gZXJyb3IoXG4gICAgICAgICAgICBgVW5hYmxlIHRvIHBhcnNlIGxpc3Q6IGV4cGVjdGVkIGluZGVudCBcIiR7ZXhwZWN0ZWR9XCIsIGdvdCBcIiR7Z290fVwiYFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaW5kZW50Lmxlbmd0aCA+IGN1cnJlbnRJbmRlbnQubGVuZ3RoKSB7XG4gICAgICAgICAgY3VycmVudFBhcmVudCA9IGN1cnJlbnRMaXN0O1xuICAgICAgICAgIGN1cnJlbnRJbmRlbnQgPSBpbmRlbnQ7XG4gICAgICAgIH0gZWxzZSBpZiAoaW5kZW50Lmxlbmd0aCA8IGN1cnJlbnRJbmRlbnQubGVuZ3RoKSB7XG4gICAgICAgICAgd2hpbGUgKFxuICAgICAgICAgICAgY3VycmVudFBhcmVudC5nZXRGaXJzdExpbmVJbmRlbnQoKS5sZW5ndGggPj0gaW5kZW50Lmxlbmd0aCAmJlxuICAgICAgICAgICAgY3VycmVudFBhcmVudC5nZXRQYXJlbnQoKVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgY3VycmVudFBhcmVudCA9IGN1cnJlbnRQYXJlbnQuZ2V0UGFyZW50KCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGN1cnJlbnRJbmRlbnQgPSBpbmRlbnQ7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBmb2xkZWQgPSAhIShlZGl0b3IgYXMgYW55KS5pc0ZvbGRlZCh7XG4gICAgICAgICAgbGluZTogTWF0aC5taW4obCArIDEsIGxpc3RFbmRMaW5lKSxcbiAgICAgICAgICBjaDogMCxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgY3VycmVudExpc3QgPSBuZXcgTGlzdChyb290LCBpbmRlbnQsIGJ1bGxldCwgc3BhY2VBZnRlckJ1bGxldCwgY29udGVudCwgZm9sZGVkKTtcbiAgICAgICAgY3VycmVudFBhcmVudC5hZGRBZnRlckFsbChjdXJyZW50TGlzdCk7XG4gICAgICB9IGVsc2UgaWYgKHRoaXMuaXNMaW5lV2l0aEluZGVudChsaW5lKSkge1xuICAgICAgICBpZiAoIWN1cnJlbnRMaXN0KSB7XG4gICAgICAgICAgcmV0dXJuIGVycm9yKFxuICAgICAgICAgICAgYFVuYWJsZSB0byBwYXJzZSBsaXN0OiBleHBlY3RlZCBsaXN0IGl0ZW0sIGdvdCBlbXB0eSBsaW5lYFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBpbmRlbnRUb0NoZWNrID0gY3VycmVudExpc3QuZ2V0Tm90ZXNJbmRlbnQoKSB8fCBjdXJyZW50SW5kZW50O1xuXG4gICAgICAgIGlmIChsaW5lLmluZGV4T2YoaW5kZW50VG9DaGVjaykgIT09IDApIHtcbiAgICAgICAgICBjb25zdCBleHBlY3RlZCA9IGluZGVudFRvQ2hlY2sucmVwbGFjZSgvIC9nLCBcIlNcIikucmVwbGFjZSgvXFx0L2csIFwiVFwiKTtcbiAgICAgICAgICBjb25zdCBnb3QgPSBsaW5lXG4gICAgICAgICAgICAubWF0Y2goL15bIFxcdF0qLylbMF1cbiAgICAgICAgICAgIC5yZXBsYWNlKC8gL2csIFwiU1wiKVxuICAgICAgICAgICAgLnJlcGxhY2UoL1xcdC9nLCBcIlRcIik7XG5cbiAgICAgICAgICByZXR1cm4gZXJyb3IoXG4gICAgICAgICAgICBgVW5hYmxlIHRvIHBhcnNlIGxpc3Q6IGV4cGVjdGVkIGluZGVudCBcIiR7ZXhwZWN0ZWR9XCIsIGdvdCBcIiR7Z290fVwiYFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWN1cnJlbnRMaXN0LmdldE5vdGVzSW5kZW50KCkpIHtcbiAgICAgICAgICBjb25zdCBtYXRjaGVzID0gbGluZS5tYXRjaCgvXlsgXFx0XSsvKTtcblxuICAgICAgICAgIGlmICghbWF0Y2hlcyB8fCBtYXRjaGVzWzBdLmxlbmd0aCA8PSBjdXJyZW50SW5kZW50Lmxlbmd0aCkge1xuICAgICAgICAgICAgcmV0dXJuIGVycm9yKFxuICAgICAgICAgICAgICBgVW5hYmxlIHRvIHBhcnNlIGxpc3Q6IGV4cGVjdGVkIHNvbWUgaW5kZW50LCBnb3Qgbm8gaW5kZW50YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjdXJyZW50TGlzdC5zZXROb3Rlc0luZGVudChtYXRjaGVzWzBdKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGN1cnJlbnRMaXN0LmFkZExpbmUobGluZS5zbGljZShjdXJyZW50TGlzdC5nZXROb3Rlc0luZGVudCgpLmxlbmd0aCkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGVycm9yKFxuICAgICAgICAgIGBVbmFibGUgdG8gcGFyc2UgbGlzdDogZXhwZWN0ZWQgbGlzdCBpdGVtIG9yIG5vdGUsIGdvdCBcIiR7bGluZX1cImBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcm9vdDtcbiAgfVxuXG4gIHByaXZhdGUgYXBwbHlDaGFuZ2VzKGVkaXRvcjogQ29kZU1pcnJvci5FZGl0b3IsIHJvb3Q6IElBcHBseUNoYW5nZXNSb290KSB7XG4gICAgY29uc3Qgcm9vdFJhbmdlID0gcm9vdC5nZXRSYW5nZSgpO1xuICAgIGNvbnN0IG9sZFN0cmluZyA9IGVkaXRvci5nZXRSYW5nZShyb290UmFuZ2VbMF0sIHJvb3RSYW5nZVsxXSk7XG4gICAgY29uc3QgbmV3U3RyaW5nID0gcm9vdC5wcmludCgpO1xuXG4gICAgY29uc3QgZnJvbUxpbmUgPSByb290UmFuZ2VbMF0ubGluZTtcbiAgICBjb25zdCB0b0xpbmUgPSByb290UmFuZ2VbMV0ubGluZTtcblxuICAgIGZvciAobGV0IGwgPSBmcm9tTGluZTsgbCA8PSB0b0xpbmU7IGwrKykge1xuICAgICAgKGVkaXRvciBhcyBhbnkpLmZvbGRDb2RlKGwsIG51bGwsIFwidW5mb2xkXCIpO1xuICAgIH1cblxuICAgIGxldCBjaGFuZ2VGcm9tID0geyAuLi5yb290UmFuZ2VbMF0gfTtcbiAgICBsZXQgY2hhbmdlVG8gPSB7IC4uLnJvb3RSYW5nZVsxXSB9O1xuICAgIGxldCBvbGRUbXAgPSBvbGRTdHJpbmc7XG4gICAgbGV0IG5ld1RtcCA9IG5ld1N0cmluZztcblxuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICBjb25zdCBubEluZGV4ID0gb2xkVG1wLmluZGV4T2YoXCJcXG5cIik7XG4gICAgICBpZiAobmxJbmRleCA8IDApIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBjb25zdCBvbGRMaW5lID0gb2xkVG1wLnNsaWNlKDAsIG5sSW5kZXggKyAxKTtcbiAgICAgIGNvbnN0IG5ld0xpbmUgPSBuZXdUbXAuc2xpY2UoMCwgb2xkTGluZS5sZW5ndGgpO1xuICAgICAgaWYgKG9sZExpbmUgIT09IG5ld0xpbmUpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBjaGFuZ2VGcm9tLmxpbmUrKztcbiAgICAgIG9sZFRtcCA9IG9sZFRtcC5zbGljZShvbGRMaW5lLmxlbmd0aCk7XG4gICAgICBuZXdUbXAgPSBuZXdUbXAuc2xpY2Uob2xkTGluZS5sZW5ndGgpO1xuICAgIH1cbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgY29uc3QgbmxJbmRleCA9IG9sZFRtcC5sYXN0SW5kZXhPZihcIlxcblwiKTtcbiAgICAgIGlmIChubEluZGV4IDwgMCkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNvbnN0IG9sZExpbmUgPSBvbGRUbXAuc2xpY2UobmxJbmRleCk7XG4gICAgICBjb25zdCBuZXdMaW5lID0gbmV3VG1wLnNsaWNlKC1vbGRMaW5lLmxlbmd0aCk7XG4gICAgICBpZiAob2xkTGluZSAhPT0gbmV3TGluZSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIG9sZFRtcCA9IG9sZFRtcC5zbGljZSgwLCAtb2xkTGluZS5sZW5ndGgpO1xuICAgICAgbmV3VG1wID0gbmV3VG1wLnNsaWNlKDAsIC1vbGRMaW5lLmxlbmd0aCk7XG5cbiAgICAgIGNvbnN0IG5sSW5kZXgyID0gb2xkVG1wLmxhc3RJbmRleE9mKFwiXFxuXCIpO1xuICAgICAgY2hhbmdlVG8uY2ggPVxuICAgICAgICBubEluZGV4MiA+PSAwID8gb2xkVG1wLmxlbmd0aCAtIG5sSW5kZXgyIC0gMSA6IG9sZFRtcC5sZW5ndGg7XG4gICAgICBjaGFuZ2VUby5saW5lLS07XG4gICAgfVxuXG4gICAgaWYgKG9sZFRtcCAhPT0gbmV3VG1wKSB7XG4gICAgICBlZGl0b3IucmVwbGFjZVJhbmdlKG5ld1RtcCwgY2hhbmdlRnJvbSwgY2hhbmdlVG8pO1xuICAgIH1cblxuICAgIGVkaXRvci5zZXRTZWxlY3Rpb25zKHJvb3QuZ2V0U2VsZWN0aW9ucygpKTtcblxuICAgIC8vIFRPRE86IGxpbmVzIGNvdWxkIGJlIGRpZmZlcmVudCBiZWNhdXNlIG9mIGRlbGV0ZXRpb25cbiAgICBmb3IgKGxldCBsID0gZnJvbUxpbmU7IGwgPD0gdG9MaW5lOyBsKyspIHtcbiAgICAgIGNvbnN0IGxpbmUgPSByb290LmdldExpc3RVbmRlckxpbmUobCk7XG4gICAgICBpZiAobGluZSAmJiBsaW5lLmlzRm9sZFJvb3QoKSkge1xuICAgICAgICAoZWRpdG9yIGFzIGFueSkuZm9sZENvZGUobCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgZ2V0RGVmYXVsdEluZGVudENoYXJzKCkge1xuICAgIGNvbnN0IHsgdXNlVGFiLCB0YWJTaXplIH0gPSB0aGlzLm9ic2lkaWFuU2VydmljZS5nZXRPYnNpZGlhblRhYnNTZXR0aWducygpO1xuXG4gICAgcmV0dXJuIHVzZVRhYiA/IFwiXFx0XCIgOiBuZXcgQXJyYXkodGFiU2l6ZSkuZmlsbChcIiBcIikuam9pbihcIlwiKTtcbiAgfVxuXG4gIHByaXZhdGUgaXNFbXB0eUxpbmUobGluZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGxpbmUubGVuZ3RoID09PSAwO1xuICB9XG5cbiAgcHJpdmF0ZSBpc0xpbmVXaXRoSW5kZW50KGxpbmU6IHN0cmluZykge1xuICAgIHJldHVybiBzdHJpbmdXaXRoU3BhY2VzUmUudGVzdChsaW5lKTtcbiAgfVxuXG4gIHByaXZhdGUgaXNMaXN0SXRlbShsaW5lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gbGlzdEl0ZW1SZS50ZXN0KGxpbmUpO1xuICB9XG5cbiAgcHJpdmF0ZSBpc0xpc3RJdGVtV2l0aG91dFNwYWNlcyhsaW5lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gbGlzdEl0ZW1XaXRob3V0U3BhY2VzUmUudGVzdChsaW5lKTtcbiAgfVxufVxuIiwiaW1wb3J0IHsgU2V0dGluZ3NTZXJ2aWNlIH0gZnJvbSBcIi4vU2V0dGluZ3NTZXJ2aWNlXCI7XG5cbmV4cG9ydCBjbGFzcyBMb2dnZXJTZXJ2aWNlIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBzZXR0aW5nc1NlcnZpY2U6IFNldHRpbmdzU2VydmljZSkge31cblxuICBsb2cobWV0aG9kOiBzdHJpbmcsIC4uLmFyZ3M6IGFueVtdKSB7XG4gICAgaWYgKCF0aGlzLnNldHRpbmdzU2VydmljZS5kZWJ1Zykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnNvbGUuaW5mbyhtZXRob2QsIC4uLmFyZ3MpO1xuICB9XG5cbiAgYmluZChtZXRob2Q6IHN0cmluZykge1xuICAgIHJldHVybiAoLi4uYXJnczogYW55W10pID0+IHRoaXMubG9nKG1ldGhvZCwgLi4uYXJncyk7XG4gIH1cbn1cbiIsImltcG9ydCB7IFNldHRpbmdzU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9TZXR0aW5nc1NlcnZpY2VcIjtcbmltcG9ydCB7IFBsdWdpbl8yIH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5pbXBvcnQgeyBJRmVhdHVyZSB9IGZyb20gXCIuL0lGZWF0dXJlXCI7XG5pbXBvcnQgeyBPYnNpZGlhblNlcnZpY2UgfSBmcm9tIFwiLi4vc2VydmljZXMvT2JzaWRpYW5TZXJ2aWNlXCI7XG5cbmNvbnN0IFNUQVRVU19CQVJfVEVYVCA9IGBPdXRsaW5lciBzdHlsZXMgb25seSB3b3JrIHdpdGggZm91ci1zcGFjZSB0YWJzLiBQbGVhc2UgY2hlY2sgT2JzaWRpYW4gc2V0dGluZ3MuYDtcblxuZXhwb3J0IGNsYXNzIExpc3RzU3R5bGVzRmVhdHVyZSBpbXBsZW1lbnRzIElGZWF0dXJlIHtcbiAgcHJpdmF0ZSBzdGF0dXNCYXJUZXh0OiBIVE1MRWxlbWVudDtcbiAgcHJpdmF0ZSBpbnRlcnZhbDogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcGx1Z2luOiBQbHVnaW5fMixcbiAgICBwcml2YXRlIHNldHRpbmdzU2VydmljZTogU2V0dGluZ3NTZXJ2aWNlLFxuICAgIHByaXZhdGUgb2JzaWRpYW5TZXJ2aWNlOiBPYnNpZGlhblNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIGxvYWQoKSB7XG4gICAgaWYgKHRoaXMuc2V0dGluZ3NTZXJ2aWNlLnN0eWxlTGlzdHMpIHtcbiAgICAgIHRoaXMuYWRkTGlzdHNTdHlsZXMoKTtcbiAgICB9XG5cbiAgICB0aGlzLnNldHRpbmdzU2VydmljZS5vbkNoYW5nZShcInN0eWxlTGlzdHNcIiwgdGhpcy5vblN0eWxlTGlzdHNTZXR0aW5nQ2hhbmdlKTtcblxuICAgIHRoaXMuYWRkU3RhdHVzQmFyVGV4dCgpO1xuICAgIHRoaXMuc3RhcnRTdGF0dXNCYXJJbnRlcnZhbCgpO1xuICB9XG5cbiAgYXN5bmMgdW5sb2FkKCkge1xuICAgIGNsZWFySW50ZXJ2YWwodGhpcy5pbnRlcnZhbCk7XG4gICAgaWYgKHRoaXMuc3RhdHVzQmFyVGV4dC5wYXJlbnRFbGVtZW50KSB7XG4gICAgICB0aGlzLnN0YXR1c0JhclRleHQucGFyZW50RWxlbWVudC5yZW1vdmVDaGlsZCh0aGlzLnN0YXR1c0JhclRleHQpO1xuICAgIH1cbiAgICB0aGlzLnNldHRpbmdzU2VydmljZS5yZW1vdmVDYWxsYmFjayhcbiAgICAgIFwic3R5bGVMaXN0c1wiLFxuICAgICAgdGhpcy5vblN0eWxlTGlzdHNTZXR0aW5nQ2hhbmdlXG4gICAgKTtcbiAgICB0aGlzLnJlbW92ZUxpc3RzU3R5bGVzKCk7XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0U3RhdHVzQmFySW50ZXJ2YWwoKSB7XG4gICAgbGV0IHZpc2libGU6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIHRoaXMuaW50ZXJ2YWwgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgY29uc3QgeyB0YWJTaXplIH0gPVxuICAgICAgICB0aGlzLm9ic2lkaWFuU2VydmljZS5nZXRPYnNpZGlhblRhYnNTZXR0aWducygpO1xuXG4gICAgICBjb25zdCBzaG91bGRCZVZpc2libGUgPVxuICAgICAgICB0aGlzLnNldHRpbmdzU2VydmljZS5zdHlsZUxpc3RzICYmXG4gICAgICAgICEodGFiU2l6ZSA9PT0gNCkgJiZcbiAgICAgICAgIXRoaXMuc2V0dGluZ3NTZXJ2aWNlLmhpZGVXYXJuaW5nO1xuXG4gICAgICBpZiAoc2hvdWxkQmVWaXNpYmxlICYmICF2aXNpYmxlKSB7XG4gICAgICAgIHRoaXMuc3RhdHVzQmFyVGV4dC5zdHlsZS5kaXNwbGF5ID0gXCJibG9ja1wiO1xuICAgICAgICB2aXNpYmxlID0gdHJ1ZTtcbiAgICAgIH0gZWxzZSBpZiAoIXNob3VsZEJlVmlzaWJsZSAmJiB2aXNpYmxlKSB7XG4gICAgICAgIHRoaXMuc3RhdHVzQmFyVGV4dC5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCI7XG4gICAgICAgIHZpc2libGUgPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9LCAxMDAwKTtcbiAgfVxuXG4gIHByaXZhdGUgb25TdHlsZUxpc3RzU2V0dGluZ0NoYW5nZSA9IChzdHlsZUxpc3RzOiBib29sZWFuKSA9PiB7XG4gICAgaWYgKHN0eWxlTGlzdHMpIHtcbiAgICAgIHRoaXMuYWRkTGlzdHNTdHlsZXMoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZW1vdmVMaXN0c1N0eWxlcygpO1xuICAgIH1cbiAgfTtcblxuICBwcml2YXRlIGFkZFN0YXR1c0JhclRleHQoKSB7XG4gICAgdGhpcy5zdGF0dXNCYXJUZXh0ID0gdGhpcy5wbHVnaW4uYWRkU3RhdHVzQmFySXRlbSgpO1xuICAgIHRoaXMuc3RhdHVzQmFyVGV4dC5zdHlsZS5jb2xvciA9IFwicmVkXCI7XG4gICAgdGhpcy5zdGF0dXNCYXJUZXh0LnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcbiAgICB0aGlzLnN0YXR1c0JhclRleHQuc2V0VGV4dChTVEFUVVNfQkFSX1RFWFQpO1xuICB9XG5cbiAgcHJpdmF0ZSBhZGRMaXN0c1N0eWxlcygpIHtcbiAgICBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5hZGQoXCJvdXRsaW5lci1wbHVnaW4tYmxzXCIpO1xuICB9XG5cbiAgcHJpdmF0ZSByZW1vdmVMaXN0c1N0eWxlcygpIHtcbiAgICBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5yZW1vdmUoXCJvdXRsaW5lci1wbHVnaW4tYmxzXCIpO1xuICB9XG59XG4iLCJpbXBvcnQgeyBMaXN0LCBSb290IH0gZnJvbSBcIi5cIjtcblxuZXhwb3J0IGZ1bmN0aW9uIHJlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHMocm9vdDogUm9vdCkge1xuICBmdW5jdGlvbiB2aXNpdChwYXJlbnQ6IFJvb3QgfCBMaXN0KSB7XG4gICAgbGV0IGluZGV4ID0gMTtcblxuICAgIGZvciAoY29uc3QgY2hpbGQgb2YgcGFyZW50LmdldENoaWxkcmVuKCkpIHtcbiAgICAgIGlmICgvXFxkK1xcLi8udGVzdChjaGlsZC5nZXRCdWxsZXQoKSkpIHtcbiAgICAgICAgY2hpbGQucmVwbGF0ZUJ1bGxldChgJHtpbmRleCsrfS5gKTtcbiAgICAgIH1cblxuICAgICAgdmlzaXQoY2hpbGQpO1xuICAgIH1cbiAgfVxuXG4gIHZpc2l0KHJvb3QpO1xufVxuIiwiaW1wb3J0IHsgcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0cyB9IGZyb20gXCJzcmMvcm9vdC9yZWNhbGN1bGF0ZU51bWVyaWNCdWxsZXRzXCI7XG5pbXBvcnQgeyBSb290IH0gZnJvbSBcIi4uL3Jvb3RcIjtcbmltcG9ydCB7IElPcGVyYXRpb24gfSBmcm9tIFwiLi9JT3BlcmF0aW9uXCI7XG5cbmV4cG9ydCBjbGFzcyBNb3ZlTGVmdE9wZXJhdGlvbiBpbXBsZW1lbnRzIElPcGVyYXRpb24ge1xuICBwcml2YXRlIHN0b3BQcm9wYWdhdGlvbiA9IGZhbHNlO1xuICBwcml2YXRlIHVwZGF0ZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvb3Q6IFJvb3QpIHt9XG5cbiAgc2hvdWxkU3RvcFByb3BhZ2F0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3BQcm9wYWdhdGlvbjtcbiAgfVxuXG4gIHNob3VsZFVwZGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy51cGRhdGVkO1xuICB9XG5cbiAgcGVyZm9ybSgpIHtcbiAgICBjb25zdCB7IHJvb3QgfSA9IHRoaXM7XG5cbiAgICBpZiAoIXJvb3QuaGFzU2luZ2xlQ3Vyc29yKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnN0b3BQcm9wYWdhdGlvbiA9IHRydWU7XG5cbiAgICBjb25zdCBsaXN0ID0gcm9vdC5nZXRMaXN0VW5kZXJDdXJzb3IoKTtcbiAgICBjb25zdCBwYXJlbnQgPSBsaXN0LmdldFBhcmVudCgpO1xuICAgIGNvbnN0IGdyYW5kUGFyZW50ID0gcGFyZW50LmdldFBhcmVudCgpO1xuXG4gICAgaWYgKCFncmFuZFBhcmVudCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMudXBkYXRlZCA9IHRydWU7XG5cbiAgICBjb25zdCBsaXN0U3RhcnRMaW5lQmVmb3JlID0gcm9vdC5nZXRDb250ZW50TGluZXNSYW5nZU9mKGxpc3QpWzBdO1xuICAgIGNvbnN0IGluZGVudFJtRnJvbSA9IHBhcmVudC5nZXRGaXJzdExpbmVJbmRlbnQoKS5sZW5ndGg7XG4gICAgY29uc3QgaW5kZW50Um1UaWxsID0gbGlzdC5nZXRGaXJzdExpbmVJbmRlbnQoKS5sZW5ndGg7XG5cbiAgICBwYXJlbnQucmVtb3ZlQ2hpbGQobGlzdCk7XG4gICAgZ3JhbmRQYXJlbnQuYWRkQWZ0ZXIocGFyZW50LCBsaXN0KTtcbiAgICBsaXN0LnVuaW5kZW50Q29udGVudChpbmRlbnRSbUZyb20sIGluZGVudFJtVGlsbCk7XG5cbiAgICBjb25zdCBsaXN0U3RhcnRMaW5lQWZ0ZXIgPSByb290LmdldENvbnRlbnRMaW5lc1JhbmdlT2YobGlzdClbMF07XG4gICAgY29uc3QgbGluZURpZmYgPSBsaXN0U3RhcnRMaW5lQWZ0ZXIgLSBsaXN0U3RhcnRMaW5lQmVmb3JlO1xuICAgIGNvbnN0IGNoRGlmZiA9IGluZGVudFJtVGlsbCAtIGluZGVudFJtRnJvbTtcblxuICAgIGNvbnN0IGN1cnNvciA9IHJvb3QuZ2V0Q3Vyc29yKCk7XG4gICAgcm9vdC5yZXBsYWNlQ3Vyc29yKHtcbiAgICAgIGxpbmU6IGN1cnNvci5saW5lICsgbGluZURpZmYsXG4gICAgICBjaDogY3Vyc29yLmNoIC0gY2hEaWZmLFxuICAgIH0pO1xuXG4gICAgcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0cyhyb290KTtcbiAgfVxufVxuIiwiZXhwb3J0IGZ1bmN0aW9uIGlzRW1wdHlMaW5lT3JFbXB0eUNoZWNrYm94KGxpbmU6IHN0cmluZykge1xuICByZXR1cm4gbGluZSA9PT0gXCJcIiB8fCBsaW5lID09PSBcIlsgXSBcIjtcbn1cbiIsImltcG9ydCB7IFJvb3QgfSBmcm9tIFwiLi4vcm9vdFwiO1xuaW1wb3J0IHsgTW92ZUxlZnRPcGVyYXRpb24gfSBmcm9tIFwiLi9Nb3ZlTGVmdE9wZXJhdGlvblwiO1xuaW1wb3J0IHsgSU9wZXJhdGlvbiB9IGZyb20gXCIuL0lPcGVyYXRpb25cIjtcbmltcG9ydCB7IGlzRW1wdHlMaW5lT3JFbXB0eUNoZWNrYm94IH0gZnJvbSBcIi4uL3V0aWxzL2lzRW1wdHlMaW5lT3JFbXB0eUNoZWNrYm94XCI7XG5cbmV4cG9ydCBjbGFzcyBPdXRkZW50SWZMaW5lSXNFbXB0eU9wZXJhdGlvbiBpbXBsZW1lbnRzIElPcGVyYXRpb24ge1xuICBwcml2YXRlIG1vdmVMZWZ0T3A6IE1vdmVMZWZ0T3BlcmF0aW9uO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcm9vdDogUm9vdCkge1xuICAgIHRoaXMubW92ZUxlZnRPcCA9IG5ldyBNb3ZlTGVmdE9wZXJhdGlvbihyb290KTtcbiAgfVxuXG4gIHNob3VsZFN0b3BQcm9wYWdhdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5tb3ZlTGVmdE9wLnNob3VsZFN0b3BQcm9wYWdhdGlvbigpO1xuICB9XG5cbiAgc2hvdWxkVXBkYXRlKCkge1xuICAgIHJldHVybiB0aGlzLm1vdmVMZWZ0T3Auc2hvdWxkVXBkYXRlKCk7XG4gIH1cblxuICBwZXJmb3JtKCkge1xuICAgIGNvbnN0IHsgcm9vdCB9ID0gdGhpcztcblxuICAgIGlmICghcm9vdC5oYXNTaW5nbGVDdXJzb3IoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGxpc3QgPSByb290LmdldExpc3RVbmRlckN1cnNvcigpO1xuICAgIGNvbnN0IGxpbmVzID0gbGlzdC5nZXRMaW5lcygpO1xuXG4gICAgaWYgKFxuICAgICAgbGluZXMubGVuZ3RoID4gMSB8fFxuICAgICAgIWlzRW1wdHlMaW5lT3JFbXB0eUNoZWNrYm94KGxpbmVzWzBdKSB8fFxuICAgICAgbGlzdC5nZXRMZXZlbCgpID09PSAxXG4gICAgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5tb3ZlTGVmdE9wLnBlcmZvcm0oKTtcbiAgfVxufVxuIiwiaW1wb3J0IHsgUGx1Z2luXzIgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB7IE91dGRlbnRJZkxpbmVJc0VtcHR5T3BlcmF0aW9uIH0gZnJvbSBcIi4uL29wZXJhdGlvbnMvT3V0ZGVudElmTGluZUlzRW1wdHlPcGVyYXRpb25cIjtcbmltcG9ydCB7IElGZWF0dXJlIH0gZnJvbSBcIi4vSUZlYXR1cmVcIjtcbmltcG9ydCB7IExpc3RzU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9MaXN0c1NlcnZpY2VcIjtcbmltcG9ydCB7IFNldHRpbmdzU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9TZXR0aW5nc1NlcnZpY2VcIjtcbmltcG9ydCB7IElNRVNlcnZpY2UgfSBmcm9tIFwic3JjL3NlcnZpY2VzL0lNRVNlcnZpY2VcIjtcblxuZnVuY3Rpb24gaXNFbnRlcihlOiBLZXlib2FyZEV2ZW50KSB7XG4gIHJldHVybiAoXG4gICAgKGUua2V5Q29kZSA9PT0gMTMgfHwgZS5jb2RlID09PSBcIkVudGVyXCIpICYmXG4gICAgZS5zaGlmdEtleSA9PT0gZmFsc2UgJiZcbiAgICBlLm1ldGFLZXkgPT09IGZhbHNlICYmXG4gICAgZS5hbHRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5jdHJsS2V5ID09PSBmYWxzZVxuICApO1xufVxuXG5leHBvcnQgY2xhc3MgRW50ZXJPdXRkZW50SWZMaW5lSXNFbXB0eUZlYXR1cmUgaW1wbGVtZW50cyBJRmVhdHVyZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcGx1Z2luOiBQbHVnaW5fMixcbiAgICBwcml2YXRlIHNldHRpbmdzU2VydmljZTogU2V0dGluZ3NTZXJ2aWNlLFxuICAgIHByaXZhdGUgbGlzdHNTZXJ2aWNlOiBMaXN0c1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBpbWVTZXJ2aWNlOiBJTUVTZXJ2aWNlXG4gICkge31cblxuICBhc3luYyBsb2FkKCkge1xuICAgIHRoaXMucGx1Z2luLnJlZ2lzdGVyQ29kZU1pcnJvcigoY20pID0+IHtcbiAgICAgIGNtLm9uKFwia2V5ZG93blwiLCB0aGlzLm9uS2V5RG93bik7XG4gICAgfSk7XG4gIH1cblxuICBhc3luYyB1bmxvYWQoKSB7XG4gICAgdGhpcy5wbHVnaW4uYXBwLndvcmtzcGFjZS5pdGVyYXRlQ29kZU1pcnJvcnMoKGNtKSA9PiB7XG4gICAgICBjbS5vZmYoXCJrZXlkb3duXCIsIHRoaXMub25LZXlEb3duKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgb25LZXlEb3duID0gKGNtOiBDb2RlTWlycm9yLkVkaXRvciwgZTogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgIGlmIChcbiAgICAgICF0aGlzLnNldHRpbmdzU2VydmljZS5iZXR0ZXJFbnRlciB8fFxuICAgICAgIWlzRW50ZXIoZSkgfHxcbiAgICAgIHRoaXMuaW1lU2VydmljZS5pc0lNRU9wZW5lZCgpXG4gICAgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT4gbmV3IE91dGRlbnRJZkxpbmVJc0VtcHR5T3BlcmF0aW9uKHJvb3QpLFxuICAgICAgY21cbiAgICApO1xuXG4gICAgaWYgKHNob3VsZFN0b3BQcm9wYWdhdGlvbikge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICB9XG4gIH07XG59XG4iLCJpbXBvcnQgeyByZWNhbGN1bGF0ZU51bWVyaWNCdWxsZXRzIH0gZnJvbSBcInNyYy9yb290L3JlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHNcIjtcbmltcG9ydCB7IGlzRW1wdHlMaW5lT3JFbXB0eUNoZWNrYm94IH0gZnJvbSBcInNyYy91dGlscy9pc0VtcHR5TGluZU9yRW1wdHlDaGVja2JveFwiO1xuaW1wb3J0IHsgTGlzdCwgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgQ3JlYXRlTmV3SXRlbU9wZXJhdGlvbiBpbXBsZW1lbnRzIElPcGVyYXRpb24ge1xuICBwcml2YXRlIHN0b3BQcm9wYWdhdGlvbiA9IGZhbHNlO1xuICBwcml2YXRlIHVwZGF0ZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvb3Q6IFJvb3QpIHt9XG5cbiAgc2hvdWxkU3RvcFByb3BhZ2F0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3BQcm9wYWdhdGlvbjtcbiAgfVxuXG4gIHNob3VsZFVwZGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy51cGRhdGVkO1xuICB9XG5cbiAgcGVyZm9ybSgpIHtcbiAgICBjb25zdCB7IHJvb3QgfSA9IHRoaXM7XG5cbiAgICBpZiAoIXJvb3QuaGFzU2luZ2xlQ3Vyc29yKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBsaXN0ID0gcm9vdC5nZXRMaXN0VW5kZXJDdXJzb3IoKTtcbiAgICBjb25zdCBsaW5lcyA9IGxpc3QuZ2V0TGluZXNJbmZvKCk7XG5cbiAgICBpZiAobGluZXMubGVuZ3RoID09PSAxICYmIGlzRW1wdHlMaW5lT3JFbXB0eUNoZWNrYm94KGxpbmVzWzBdLnRleHQpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgY3Vyc29yID0gcm9vdC5nZXRDdXJzb3IoKTtcbiAgICBjb25zdCBsaW5lVW5kZXJDdXJzb3IgPSBsaW5lcy5maW5kKChsKSA9PiBsLmZyb20ubGluZSA9PT0gY3Vyc29yLmxpbmUpO1xuXG4gICAgaWYgKGN1cnNvci5jaCA8IGxpbmVVbmRlckN1cnNvci5mcm9tLmNoKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgeyBvbGRMaW5lcywgbmV3TGluZXMgfSA9IGxpbmVzLnJlZHVjZShcbiAgICAgIChhY2MsIGxpbmUpID0+IHtcbiAgICAgICAgaWYgKGN1cnNvci5saW5lID4gbGluZS5mcm9tLmxpbmUpIHtcbiAgICAgICAgICBhY2Mub2xkTGluZXMucHVzaChsaW5lLnRleHQpO1xuICAgICAgICB9IGVsc2UgaWYgKGN1cnNvci5saW5lID09PSBsaW5lLmZyb20ubGluZSkge1xuICAgICAgICAgIGNvbnN0IGEgPSBsaW5lLnRleHQuc2xpY2UoMCwgY3Vyc29yLmNoIC0gbGluZS5mcm9tLmNoKTtcbiAgICAgICAgICBjb25zdCBiID0gbGluZS50ZXh0LnNsaWNlKGN1cnNvci5jaCAtIGxpbmUuZnJvbS5jaCk7XG4gICAgICAgICAgYWNjLm9sZExpbmVzLnB1c2goYSk7XG4gICAgICAgICAgYWNjLm5ld0xpbmVzLnB1c2goYik7XG4gICAgICAgIH0gZWxzZSBpZiAoY3Vyc29yLmxpbmUgPCBsaW5lLmZyb20ubGluZSkge1xuICAgICAgICAgIGFjYy5uZXdMaW5lcy5wdXNoKGxpbmUudGV4dCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgb2xkTGluZXM6IFtdLFxuICAgICAgICBuZXdMaW5lczogW10sXG4gICAgICB9XG4gICAgKTtcblxuICAgIGNvbnN0IGNvZGVCbG9ja0JhY3RpY2tzID0gb2xkTGluZXMuam9pbihcIlxcblwiKS5zcGxpdChcImBgYFwiKS5sZW5ndGggLSAxO1xuICAgIGNvbnN0IGlzSW5zaWRlQ29kZWJsb2NrID1cbiAgICAgIGNvZGVCbG9ja0JhY3RpY2tzID4gMCAmJiBjb2RlQmxvY2tCYWN0aWNrcyAlIDIgIT09IDA7XG5cbiAgICBpZiAoaXNJbnNpZGVDb2RlYmxvY2spIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnN0b3BQcm9wYWdhdGlvbiA9IHRydWU7XG4gICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcblxuICAgIGNvbnN0IGVuZFBvcyA9IGxpc3QuZ2V0TGFzdExpbmVDb250ZW50RW5kKCk7XG4gICAgY29uc3Qgb25DaGlsZExldmVsID1cbiAgICAgICFsaXN0LmlzRW1wdHkoKSAmJiBjdXJzb3IubGluZSA9PT0gZW5kUG9zLmxpbmUgJiYgY3Vyc29yLmNoID09PSBlbmRQb3MuY2g7XG5cbiAgICBjb25zdCBpbmRlbnQgPSBvbkNoaWxkTGV2ZWxcbiAgICAgID8gbGlzdC5nZXRDaGlsZHJlbigpWzBdLmdldEZpcnN0TGluZUluZGVudCgpXG4gICAgICA6IGxpc3QuZ2V0Rmlyc3RMaW5lSW5kZW50KCk7XG5cbiAgICBjb25zdCBidWxsZXQgPSBvbkNoaWxkTGV2ZWxcbiAgICAgID8gbGlzdC5nZXRDaGlsZHJlbigpWzBdLmdldEJ1bGxldCgpXG4gICAgICA6IGxpc3QuZ2V0QnVsbGV0KCk7XG5cbiAgICBjb25zdCBzcGFjZUFmdGVyQnVsbGV0ID0gb25DaGlsZExldmVsXG4gICAgICA/IGxpc3QuZ2V0Q2hpbGRyZW4oKVswXS5nZXRTcGFjZUFmdGVyQnVsbGV0KClcbiAgICAgIDogbGlzdC5nZXRTcGFjZUFmdGVyQnVsbGV0KCk7XG5cbiAgICBjb25zdCBwcmVmaXggPSBvbGRMaW5lc1swXS5tYXRjaCgvXlxcW1sgeF1cXF0vKSA/IFwiWyBdIFwiIDogXCJcIjtcblxuICAgIGNvbnN0IG5ld0xpc3QgPSBuZXcgTGlzdChcbiAgICAgIGxpc3QuZ2V0Um9vdCgpLFxuICAgICAgaW5kZW50LFxuICAgICAgYnVsbGV0LFxuICAgICAgc3BhY2VBZnRlckJ1bGxldCxcbiAgICAgIHByZWZpeCArIG5ld0xpbmVzLnNoaWZ0KCksXG4gICAgICBmYWxzZVxuICAgICk7XG5cbiAgICBpZiAobmV3TGluZXMubGVuZ3RoID4gMCkge1xuICAgICAgbmV3TGlzdC5zZXROb3Rlc0luZGVudChsaXN0LmdldE5vdGVzSW5kZW50KCkpO1xuICAgICAgZm9yIChjb25zdCBsaW5lIG9mIG5ld0xpbmVzKSB7XG4gICAgICAgIG5ld0xpc3QuYWRkTGluZShsaW5lKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAob25DaGlsZExldmVsKSB7XG4gICAgICBsaXN0LmFkZEJlZm9yZUFsbChuZXdMaXN0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgY2hpbGRyZW4gPSBsaXN0LmdldENoaWxkcmVuKCk7XG4gICAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIGNoaWxkcmVuKSB7XG4gICAgICAgIGxpc3QucmVtb3ZlQ2hpbGQoY2hpbGQpO1xuICAgICAgICBuZXdMaXN0LmFkZEFmdGVyQWxsKGNoaWxkKTtcbiAgICAgIH1cblxuICAgICAgbGlzdC5nZXRQYXJlbnQoKS5hZGRBZnRlcihsaXN0LCBuZXdMaXN0KTtcbiAgICB9XG5cbiAgICBsaXN0LnJlcGxhY2VMaW5lcyhvbGRMaW5lcyk7XG5cbiAgICBjb25zdCBuZXdMaXN0U3RhcnQgPSBuZXdMaXN0LmdldEZpcnN0TGluZUNvbnRlbnRTdGFydCgpO1xuICAgIHJvb3QucmVwbGFjZUN1cnNvcih7XG4gICAgICBsaW5lOiBuZXdMaXN0U3RhcnQubGluZSxcbiAgICAgIGNoOiBuZXdMaXN0U3RhcnQuY2ggKyBwcmVmaXgubGVuZ3RoLFxuICAgIH0pO1xuXG4gICAgcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0cyhyb290KTtcbiAgfVxufVxuIiwiaW1wb3J0IHsgUGx1Z2luXzIgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB7IENyZWF0ZU5ld0l0ZW1PcGVyYXRpb24gfSBmcm9tIFwiLi4vb3BlcmF0aW9ucy9DcmVhdGVOZXdJdGVtT3BlcmF0aW9uXCI7XG5pbXBvcnQgeyBJRmVhdHVyZSB9IGZyb20gXCIuL0lGZWF0dXJlXCI7XG5pbXBvcnQgeyBMaXN0c1NlcnZpY2UgfSBmcm9tIFwiLi4vc2VydmljZXMvTGlzdHNTZXJ2aWNlXCI7XG5pbXBvcnQgeyBTZXR0aW5nc1NlcnZpY2UgfSBmcm9tIFwiLi4vc2VydmljZXMvU2V0dGluZ3NTZXJ2aWNlXCI7XG5pbXBvcnQgeyBJTUVTZXJ2aWNlIH0gZnJvbSBcInNyYy9zZXJ2aWNlcy9JTUVTZXJ2aWNlXCI7XG5cbmZ1bmN0aW9uIGlzRW50ZXIoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gKFxuICAgIChlLmtleUNvZGUgPT09IDEzIHx8IGUuY29kZSA9PT0gXCJFbnRlclwiKSAmJlxuICAgIGUuc2hpZnRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5tZXRhS2V5ID09PSBmYWxzZSAmJlxuICAgIGUuYWx0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUuY3RybEtleSA9PT0gZmFsc2VcbiAgKTtcbn1cblxuZXhwb3J0IGNsYXNzIEVudGVyU2hvdWxkQ3JlYXRlTmV3SXRlbUZlYXR1cmUgaW1wbGVtZW50cyBJRmVhdHVyZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcGx1Z2luOiBQbHVnaW5fMixcbiAgICBwcml2YXRlIHNldHRpbmdzU2VydmljZTogU2V0dGluZ3NTZXJ2aWNlLFxuICAgIHByaXZhdGUgbGlzdHNTZXJ2aWNlOiBMaXN0c1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBpbWVTZXJ2aWNlOiBJTUVTZXJ2aWNlXG4gICkge31cblxuICBhc3luYyBsb2FkKCkge1xuICAgIHRoaXMucGx1Z2luLnJlZ2lzdGVyQ29kZU1pcnJvcigoY20pID0+IHtcbiAgICAgIGNtLm9uKFwia2V5ZG93blwiLCB0aGlzLm9uS2V5RG93bik7XG4gICAgfSk7XG4gIH1cblxuICBhc3luYyB1bmxvYWQoKSB7XG4gICAgdGhpcy5wbHVnaW4uYXBwLndvcmtzcGFjZS5pdGVyYXRlQ29kZU1pcnJvcnMoKGNtKSA9PiB7XG4gICAgICBjbS5vZmYoXCJrZXlkb3duXCIsIHRoaXMub25LZXlEb3duKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgb25LZXlEb3duID0gKGNtOiBDb2RlTWlycm9yLkVkaXRvciwgZTogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgIGlmIChcbiAgICAgICF0aGlzLnNldHRpbmdzU2VydmljZS5iZXR0ZXJFbnRlciB8fFxuICAgICAgIWlzRW50ZXIoZSkgfHxcbiAgICAgIHRoaXMuaW1lU2VydmljZS5pc0lNRU9wZW5lZCgpXG4gICAgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT4gbmV3IENyZWF0ZU5ld0l0ZW1PcGVyYXRpb24ocm9vdCksXG4gICAgICBjbVxuICAgICk7XG5cbiAgICBpZiAoc2hvdWxkU3RvcFByb3BhZ2F0aW9uKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBlLnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIH1cbiAgfTtcbn1cbiIsImltcG9ydCB7IElMaXN0TGluZSwgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgTW92ZUN1cnNvclRvUHJldmlvdXNVbmZvbGRlZExpbmVPcGVyYXRpb24gaW1wbGVtZW50cyBJT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBzdG9wUHJvcGFnYXRpb24gPSBmYWxzZTtcbiAgcHJpdmF0ZSB1cGRhdGVkID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByb290OiBSb290KSB7fVxuXG4gIHNob3VsZFN0b3BQcm9wYWdhdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5zdG9wUHJvcGFnYXRpb247XG4gIH1cblxuICBzaG91bGRVcGRhdGUoKSB7XG4gICAgcmV0dXJuIHRoaXMudXBkYXRlZDtcbiAgfVxuXG4gIHBlcmZvcm0oKSB7XG4gICAgY29uc3QgeyByb290IH0gPSB0aGlzO1xuXG4gICAgaWYgKCFyb290Lmhhc1NpbmdsZUN1cnNvcigpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgbGlzdCA9IHRoaXMucm9vdC5nZXRMaXN0VW5kZXJDdXJzb3IoKTtcbiAgICBjb25zdCBjdXJzb3IgPSB0aGlzLnJvb3QuZ2V0Q3Vyc29yKCk7XG4gICAgY29uc3QgbGluZXMgPSBsaXN0LmdldExpbmVzSW5mbygpO1xuICAgIGNvbnN0IGxpbmVObyA9IGxpbmVzLmZpbmRJbmRleChcbiAgICAgIChsKSA9PiBjdXJzb3IuY2ggPT09IGwuZnJvbS5jaCAmJiBjdXJzb3IubGluZSA9PT0gbC5mcm9tLmxpbmVcbiAgICApO1xuXG4gICAgaWYgKGxpbmVObyA9PT0gMCkge1xuICAgICAgdGhpcy5tb3ZlQ3Vyc29yVG9QcmV2aW91c1VuZm9sZGVkSXRlbShyb290LCBjdXJzb3IpO1xuICAgIH0gZWxzZSBpZiAobGluZU5vID4gMCkge1xuICAgICAgdGhpcy5tb3ZlQ3Vyc29yVG9QcmV2aW91c05vdGVMaW5lKHJvb3QsIGxpbmVzLCBsaW5lTm8pO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgbW92ZUN1cnNvclRvUHJldmlvdXNOb3RlTGluZShcbiAgICByb290OiBSb290LFxuICAgIGxpbmVzOiBJTGlzdExpbmVbXSxcbiAgICBsaW5lTm86IG51bWJlclxuICApIHtcbiAgICB0aGlzLnN0b3BQcm9wYWdhdGlvbiA9IHRydWU7XG4gICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcblxuICAgIHJvb3QucmVwbGFjZUN1cnNvcihsaW5lc1tsaW5lTm8gLSAxXS50byk7XG4gIH1cblxuICBwcml2YXRlIG1vdmVDdXJzb3JUb1ByZXZpb3VzVW5mb2xkZWRJdGVtKHJvb3Q6IFJvb3QsIGN1cnNvcjogSVBvc2l0aW9uKSB7XG4gICAgY29uc3QgcHJldiA9IHJvb3QuZ2V0TGlzdFVuZGVyTGluZShjdXJzb3IubGluZSAtIDEpO1xuXG4gICAgaWYgKCFwcmV2KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5zdG9wUHJvcGFnYXRpb24gPSB0cnVlO1xuICAgIHRoaXMudXBkYXRlZCA9IHRydWU7XG5cbiAgICBpZiAocHJldi5pc0ZvbGRlZCgpKSB7XG4gICAgICBsZXQgZm9sZFJvb3QgPSBwcmV2O1xuICAgICAgd2hpbGUgKCFmb2xkUm9vdC5pc0ZvbGRSb290KCkpIHtcbiAgICAgICAgZm9sZFJvb3QgPSBmb2xkUm9vdC5nZXRQYXJlbnQoKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgZmlyc3RMaW5lRW5kID0gZm9sZFJvb3QuZ2V0TGluZXNJbmZvKClbMF0udG87XG4gICAgICByb290LnJlcGxhY2VDdXJzb3IoZmlyc3RMaW5lRW5kKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcm9vdC5yZXBsYWNlQ3Vyc29yKHByZXYuZ2V0TGFzdExpbmVDb250ZW50RW5kKCkpO1xuICAgIH1cbiAgfVxufVxuIiwiaW1wb3J0IHsgUGxhdGZvcm0sIFBsdWdpbl8yIH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5pbXBvcnQgeyBJRmVhdHVyZSB9IGZyb20gXCIuL0lGZWF0dXJlXCI7XG5pbXBvcnQgeyBMaXN0c1NlcnZpY2UgfSBmcm9tIFwiLi4vc2VydmljZXMvTGlzdHNTZXJ2aWNlXCI7XG5pbXBvcnQgeyBNb3ZlQ3Vyc29yVG9QcmV2aW91c1VuZm9sZGVkTGluZU9wZXJhdGlvbiB9IGZyb20gXCIuLi9vcGVyYXRpb25zL01vdmVDdXJzb3JUb1ByZXZpb3VzVW5mb2xkZWRMaW5lT3BlcmF0aW9uXCI7XG5pbXBvcnQgeyBTZXR0aW5nc1NlcnZpY2UgfSBmcm9tIFwiLi4vc2VydmljZXMvU2V0dGluZ3NTZXJ2aWNlXCI7XG5pbXBvcnQgeyBJTUVTZXJ2aWNlIH0gZnJvbSBcInNyYy9zZXJ2aWNlcy9JTUVTZXJ2aWNlXCI7XG5cbmZ1bmN0aW9uIGlzQXJyb3dMZWZ0KGU6IEtleWJvYXJkRXZlbnQpIHtcbiAgcmV0dXJuIChcbiAgICAoZS5rZXlDb2RlID09PSAzNyB8fCBlLmNvZGUgPT09IFwiQXJyb3dMZWZ0XCIpICYmXG4gICAgZS5zaGlmdEtleSA9PT0gZmFsc2UgJiZcbiAgICBlLm1ldGFLZXkgPT09IGZhbHNlICYmXG4gICAgZS5hbHRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5jdHJsS2V5ID09PSBmYWxzZVxuICApO1xufVxuXG5mdW5jdGlvbiBpc0N0cmxBcnJvd0xlZnQoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gKFxuICAgIChlLmtleUNvZGUgPT09IDM3IHx8IGUuY29kZSA9PT0gXCJBcnJvd0xlZnRcIikgJiZcbiAgICBlLnNoaWZ0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUubWV0YUtleSA9PT0gZmFsc2UgJiZcbiAgICBlLmFsdEtleSA9PT0gZmFsc2UgJiZcbiAgICBlLmN0cmxLZXkgPT09IHRydWVcbiAgKTtcbn1cblxuZXhwb3J0IGNsYXNzIE1vdmVDdXJzb3JUb1ByZXZpb3VzVW5mb2xkZWRMaW5lRmVhdHVyZSBpbXBsZW1lbnRzIElGZWF0dXJlIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBwbHVnaW46IFBsdWdpbl8yLFxuICAgIHByaXZhdGUgc2V0dGluZ3NTZXJ2aWNlOiBTZXR0aW5nc1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBsaXN0c1NlcnZpY2U6IExpc3RzU2VydmljZSxcbiAgICBwcml2YXRlIGltZVNlcnZpY2U6IElNRVNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIGxvYWQoKSB7XG4gICAgdGhpcy5wbHVnaW4ucmVnaXN0ZXJDb2RlTWlycm9yKChjbSkgPT4ge1xuICAgICAgY20ub24oXCJrZXlkb3duXCIsIHRoaXMub25LZXlEb3duKTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIHVubG9hZCgpIHtcbiAgICB0aGlzLnBsdWdpbi5hcHAud29ya3NwYWNlLml0ZXJhdGVDb2RlTWlycm9ycygoY20pID0+IHtcbiAgICAgIGNtLm9mZihcImtleWRvd25cIiwgdGhpcy5vbktleURvd24pO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBvbktleURvd24gPSAoY206IENvZGVNaXJyb3IuRWRpdG9yLCBldmVudDogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgIGlmICghdGhpcy5zZXR0aW5nc1NlcnZpY2Uuc3RpY2tDdXJzb3IgfHwgdGhpcy5pbWVTZXJ2aWNlLmlzSU1FT3BlbmVkKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoaXNBcnJvd0xlZnQoZXZlbnQpIHx8ICghUGxhdGZvcm0uaXNNYWNPUyAmJiBpc0N0cmxBcnJvd0xlZnQoZXZlbnQpKSkge1xuICAgICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAgIChyb290KSA9PiBuZXcgTW92ZUN1cnNvclRvUHJldmlvdXNVbmZvbGRlZExpbmVPcGVyYXRpb24ocm9vdCksXG4gICAgICAgIGNtXG4gICAgICApO1xuXG4gICAgICBpZiAoc2hvdWxkU3RvcFByb3BhZ2F0aW9uKSB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbn1cbiIsImltcG9ydCB7IFJvb3QgfSBmcm9tIFwiLi4vcm9vdFwiO1xuaW1wb3J0IHsgSU9wZXJhdGlvbiB9IGZyb20gXCIuL0lPcGVyYXRpb25cIjtcblxuZXhwb3J0IGNsYXNzIEVuc3VyZUN1cnNvckluTGlzdENvbnRlbnRPcGVyYXRpb24gaW1wbGVtZW50cyBJT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBzdG9wUHJvcGFnYXRpb24gPSBmYWxzZTtcbiAgcHJpdmF0ZSB1cGRhdGVkID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByb290OiBSb290KSB7fVxuXG4gIHNob3VsZFN0b3BQcm9wYWdhdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5zdG9wUHJvcGFnYXRpb247XG4gIH1cblxuICBzaG91bGRVcGRhdGUoKSB7XG4gICAgcmV0dXJuIHRoaXMudXBkYXRlZDtcbiAgfVxuXG4gIHBlcmZvcm0oKSB7XG4gICAgY29uc3QgeyByb290IH0gPSB0aGlzO1xuXG4gICAgaWYgKCFyb290Lmhhc1NpbmdsZUN1cnNvcigpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5zdG9wUHJvcGFnYXRpb24gPSB0cnVlO1xuXG4gICAgY29uc3QgY3Vyc29yID0gcm9vdC5nZXRDdXJzb3IoKTtcbiAgICBjb25zdCBsaXN0ID0gcm9vdC5nZXRMaXN0VW5kZXJDdXJzb3IoKTtcbiAgICBjb25zdCBjb250ZW50U3RhcnQgPSBsaXN0LmdldEZpcnN0TGluZUNvbnRlbnRTdGFydCgpO1xuICAgIGNvbnN0IGxpbmVQcmVmaXggPVxuICAgICAgY29udGVudFN0YXJ0LmxpbmUgPT09IGN1cnNvci5saW5lXG4gICAgICAgID8gY29udGVudFN0YXJ0LmNoXG4gICAgICAgIDogbGlzdC5nZXROb3Rlc0luZGVudCgpLmxlbmd0aDtcblxuICAgIGlmIChjdXJzb3IuY2ggPCBsaW5lUHJlZml4KSB7XG4gICAgICB0aGlzLnVwZGF0ZWQgPSB0cnVlO1xuICAgICAgcm9vdC5yZXBsYWNlQ3Vyc29yKHtcbiAgICAgICAgbGluZTogY3Vyc29yLmxpbmUsXG4gICAgICAgIGNoOiBsaW5lUHJlZml4LFxuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG4iLCJpbXBvcnQgeyBSb290IH0gZnJvbSBcIi4uL3Jvb3RcIjtcbmltcG9ydCB7IElPcGVyYXRpb24gfSBmcm9tIFwiLi9JT3BlcmF0aW9uXCI7XG5cbmV4cG9ydCBjbGFzcyBFbnN1cmVDdXJzb3JJc0luVW5mb2xkZWRMaW5lT3BlcmF0aW9uIGltcGxlbWVudHMgSU9wZXJhdGlvbiB7XG4gIHByaXZhdGUgc3RvcFByb3BhZ2F0aW9uID0gZmFsc2U7XG4gIHByaXZhdGUgdXBkYXRlZCA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcm9vdDogUm9vdCkge31cblxuICBzaG91bGRTdG9wUHJvcGFnYXRpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcFByb3BhZ2F0aW9uO1xuICB9XG5cbiAgc2hvdWxkVXBkYXRlKCkge1xuICAgIHJldHVybiB0aGlzLnVwZGF0ZWQ7XG4gIH1cblxuICBwZXJmb3JtKCkge1xuICAgIGNvbnN0IHsgcm9vdCB9ID0gdGhpcztcblxuICAgIGlmICghcm9vdC5oYXNTaW5nbGVDdXJzb3IoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuc3RvcFByb3BhZ2F0aW9uID0gdHJ1ZTtcblxuICAgIGNvbnN0IGN1cnNvciA9IHJvb3QuZ2V0Q3Vyc29yKCk7XG5cbiAgICBjb25zdCBsaXN0ID0gcm9vdC5nZXRMaXN0VW5kZXJDdXJzb3IoKTtcbiAgICBpZiAoIWxpc3QuaXNGb2xkZWQoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGxldCBmb2xkUm9vdCA9IGxpc3Q7XG4gICAgd2hpbGUgKCFmb2xkUm9vdC5pc0ZvbGRSb290KCkpIHtcbiAgICAgIGZvbGRSb290ID0gZm9sZFJvb3QuZ2V0UGFyZW50KCk7XG4gICAgfVxuXG4gICAgY29uc3QgZmlyc3RMaW5lRW5kID0gZm9sZFJvb3QuZ2V0TGluZXNJbmZvKClbMF0udG87XG5cbiAgICBpZiAoY3Vyc29yLmxpbmUgPiBmaXJzdExpbmVFbmQubGluZSkge1xuICAgICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcbiAgICAgIHJvb3QucmVwbGFjZUN1cnNvcihmaXJzdExpbmVFbmQpO1xuICAgIH1cbiAgfVxufVxuIiwiaW1wb3J0IHsgUGx1Z2luXzIgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB7IElGZWF0dXJlIH0gZnJvbSBcIi4vSUZlYXR1cmVcIjtcbmltcG9ydCB7IExpc3RzU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9MaXN0c1NlcnZpY2VcIjtcbmltcG9ydCB7IEVuc3VyZUN1cnNvckluTGlzdENvbnRlbnRPcGVyYXRpb24gfSBmcm9tIFwiLi4vb3BlcmF0aW9ucy9FbnN1cmVDdXJzb3JJbkxpc3RDb250ZW50T3BlcmF0aW9uXCI7XG5pbXBvcnQgeyBFbnN1cmVDdXJzb3JJc0luVW5mb2xkZWRMaW5lT3BlcmF0aW9uIH0gZnJvbSBcIi4uL29wZXJhdGlvbnMvRW5zdXJlQ3Vyc29ySXNJblVuZm9sZGVkTGluZU9wZXJhdGlvblwiO1xuaW1wb3J0IHsgU2V0dGluZ3NTZXJ2aWNlIH0gZnJvbSBcIi4uL3NlcnZpY2VzL1NldHRpbmdzU2VydmljZVwiO1xuXG5leHBvcnQgY2xhc3MgRW5zdXJlQ3Vyc29ySW5MaXN0Q29udGVudEZlYXR1cmUgaW1wbGVtZW50cyBJRmVhdHVyZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcGx1Z2luOiBQbHVnaW5fMixcbiAgICBwcml2YXRlIHNldHRpbmdzU2VydmljZTogU2V0dGluZ3NTZXJ2aWNlLFxuICAgIHByaXZhdGUgbGlzdHNTZXJ2aWNlOiBMaXN0c1NlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIGxvYWQoKSB7XG4gICAgdGhpcy5wbHVnaW4ucmVnaXN0ZXJDb2RlTWlycm9yKChjbSkgPT4ge1xuICAgICAgY20ub24oXCJjdXJzb3JBY3Rpdml0eVwiLCB0aGlzLmhhbmRsZUN1cnNvckFjdGl2aXR5KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIHVubG9hZCgpIHtcbiAgICB0aGlzLnBsdWdpbi5hcHAud29ya3NwYWNlLml0ZXJhdGVDb2RlTWlycm9ycygoY20pID0+IHtcbiAgICAgIGNtLm9mZihcImN1cnNvckFjdGl2aXR5XCIsIHRoaXMuaGFuZGxlQ3Vyc29yQWN0aXZpdHkpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBoYW5kbGVDdXJzb3JBY3Rpdml0eSA9IChjbTogQ29kZU1pcnJvci5FZGl0b3IpID0+IHtcbiAgICBpZiAoIXRoaXMuc2V0dGluZ3NTZXJ2aWNlLnN0aWNrQ3Vyc29yKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5saXN0c1NlcnZpY2UucGVyZm9ybU9wZXJhdGlvbihcbiAgICAgIChyb290KSA9PiBuZXcgRW5zdXJlQ3Vyc29ySXNJblVuZm9sZGVkTGluZU9wZXJhdGlvbihyb290KSxcbiAgICAgIGNtXG4gICAgKTtcblxuICAgIHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT4gbmV3IEVuc3VyZUN1cnNvckluTGlzdENvbnRlbnRPcGVyYXRpb24ocm9vdCksXG4gICAgICBjbVxuICAgICk7XG4gIH07XG59XG4iLCJpbXBvcnQgeyByZWNhbGN1bGF0ZU51bWVyaWNCdWxsZXRzIH0gZnJvbSBcInNyYy9yb290L3JlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHNcIjtcbmltcG9ydCB7IElMaXN0TGluZSwgTGlzdCwgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgRGVsZXRlQW5kTWVyZ2VXaXRoUHJldmlvdXNMaW5lT3BlcmF0aW9uIGltcGxlbWVudHMgSU9wZXJhdGlvbiB7XG4gIHByaXZhdGUgc3RvcFByb3BhZ2F0aW9uID0gZmFsc2U7XG4gIHByaXZhdGUgdXBkYXRlZCA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcm9vdDogUm9vdCkge31cblxuICBzaG91bGRTdG9wUHJvcGFnYXRpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcFByb3BhZ2F0aW9uO1xuICB9XG5cbiAgc2hvdWxkVXBkYXRlKCkge1xuICAgIHJldHVybiB0aGlzLnVwZGF0ZWQ7XG4gIH1cblxuICBwZXJmb3JtKCkge1xuICAgIGNvbnN0IHsgcm9vdCB9ID0gdGhpcztcblxuICAgIGlmICghcm9vdC5oYXNTaW5nbGVDdXJzb3IoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGxpc3QgPSByb290LmdldExpc3RVbmRlckN1cnNvcigpO1xuICAgIGNvbnN0IGN1cnNvciA9IHJvb3QuZ2V0Q3Vyc29yKCk7XG4gICAgY29uc3QgbGluZXMgPSBsaXN0LmdldExpbmVzSW5mbygpO1xuXG4gICAgY29uc3QgbGluZU5vID0gbGluZXMuZmluZEluZGV4KFxuICAgICAgKGwpID0+IGN1cnNvci5jaCA9PT0gbC5mcm9tLmNoICYmIGN1cnNvci5saW5lID09PSBsLmZyb20ubGluZVxuICAgICk7XG5cbiAgICBpZiAobGluZU5vID09PSAwKSB7XG4gICAgICB0aGlzLm1lcmdlV2l0aFByZXZpb3VzSXRlbShyb290LCBjdXJzb3IsIGxpc3QpO1xuICAgIH0gZWxzZSBpZiAobGluZU5vID4gMCkge1xuICAgICAgdGhpcy5tZXJnZU5vdGVzKHJvb3QsIGN1cnNvciwgbGlzdCwgbGluZXMsIGxpbmVObyk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBtZXJnZU5vdGVzKFxuICAgIHJvb3Q6IFJvb3QsXG4gICAgY3Vyc29yOiBJUG9zaXRpb24sXG4gICAgbGlzdDogTGlzdCxcbiAgICBsaW5lczogSUxpc3RMaW5lW10sXG4gICAgbGluZU5vOiBudW1iZXJcbiAgKSB7XG4gICAgdGhpcy5zdG9wUHJvcGFnYXRpb24gPSB0cnVlO1xuICAgIHRoaXMudXBkYXRlZCA9IHRydWU7XG5cbiAgICBjb25zdCBwcmV2TGluZU5vID0gbGluZU5vIC0gMTtcblxuICAgIHJvb3QucmVwbGFjZUN1cnNvcih7XG4gICAgICBsaW5lOiBjdXJzb3IubGluZSAtIDEsXG4gICAgICBjaDogbGluZXNbcHJldkxpbmVOb10udGV4dC5sZW5ndGggKyBsaW5lc1twcmV2TGluZU5vXS5mcm9tLmNoLFxuICAgIH0pO1xuXG4gICAgbGluZXNbcHJldkxpbmVOb10udGV4dCArPSBsaW5lc1tsaW5lTm9dLnRleHQ7XG4gICAgbGluZXMuc3BsaWNlKGxpbmVObywgMSk7XG5cbiAgICBsaXN0LnJlcGxhY2VMaW5lcyhsaW5lcy5tYXAoKGwpID0+IGwudGV4dCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBtZXJnZVdpdGhQcmV2aW91c0l0ZW0ocm9vdDogUm9vdCwgY3Vyc29yOiBJUG9zaXRpb24sIGxpc3Q6IExpc3QpIHtcbiAgICBpZiAocm9vdC5nZXRDaGlsZHJlbigpWzBdID09PSBsaXN0ICYmIGxpc3QuZ2V0Q2hpbGRyZW4oKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnN0b3BQcm9wYWdhdGlvbiA9IHRydWU7XG5cbiAgICBjb25zdCBwcmV2ID0gcm9vdC5nZXRMaXN0VW5kZXJMaW5lKGN1cnNvci5saW5lIC0gMSk7XG5cbiAgICBpZiAoIXByZXYpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBib3RoQXJlRW1wdHkgPSBwcmV2LmlzRW1wdHkoKSAmJiBsaXN0LmlzRW1wdHkoKTtcbiAgICBjb25zdCBwcmV2SXNFbXB0eUFuZFNhbWVMZXZlbCA9XG4gICAgICBwcmV2LmlzRW1wdHkoKSAmJiAhbGlzdC5pc0VtcHR5KCkgJiYgcHJldi5nZXRMZXZlbCgpID09IGxpc3QuZ2V0TGV2ZWwoKTtcbiAgICBjb25zdCBsaXN0SXNFbXB0eUFuZFByZXZJc1BhcmVudCA9XG4gICAgICBsaXN0LmlzRW1wdHkoKSAmJiBwcmV2LmdldExldmVsKCkgPT0gbGlzdC5nZXRMZXZlbCgpIC0gMTtcblxuICAgIGlmIChib3RoQXJlRW1wdHkgfHwgcHJldklzRW1wdHlBbmRTYW1lTGV2ZWwgfHwgbGlzdElzRW1wdHlBbmRQcmV2SXNQYXJlbnQpIHtcbiAgICAgIHRoaXMudXBkYXRlZCA9IHRydWU7XG5cbiAgICAgIGNvbnN0IHBhcmVudCA9IGxpc3QuZ2V0UGFyZW50KCk7XG4gICAgICBjb25zdCBwcmV2RW5kID0gcHJldi5nZXRMYXN0TGluZUNvbnRlbnRFbmQoKTtcblxuICAgICAgaWYgKCFwcmV2LmdldE5vdGVzSW5kZW50KCkgJiYgbGlzdC5nZXROb3Rlc0luZGVudCgpKSB7XG4gICAgICAgIHByZXYuc2V0Tm90ZXNJbmRlbnQoXG4gICAgICAgICAgcHJldi5nZXRGaXJzdExpbmVJbmRlbnQoKSArXG4gICAgICAgICAgICBsaXN0LmdldE5vdGVzSW5kZW50KCkuc2xpY2UobGlzdC5nZXRGaXJzdExpbmVJbmRlbnQoKS5sZW5ndGgpXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG9sZExpbmVzID0gcHJldi5nZXRMaW5lcygpO1xuICAgICAgY29uc3QgbmV3TGluZXMgPSBsaXN0LmdldExpbmVzKCk7XG4gICAgICBvbGRMaW5lc1tvbGRMaW5lcy5sZW5ndGggLSAxXSArPSBuZXdMaW5lc1swXTtcbiAgICAgIGNvbnN0IHJlc3VsdExpbmVzID0gb2xkTGluZXMuY29uY2F0KG5ld0xpbmVzLnNsaWNlKDEpKTtcblxuICAgICAgcHJldi5yZXBsYWNlTGluZXMocmVzdWx0TGluZXMpO1xuICAgICAgcGFyZW50LnJlbW92ZUNoaWxkKGxpc3QpO1xuXG4gICAgICBmb3IgKGNvbnN0IGMgb2YgbGlzdC5nZXRDaGlsZHJlbigpKSB7XG4gICAgICAgIGxpc3QucmVtb3ZlQ2hpbGQoYyk7XG4gICAgICAgIHByZXYuYWRkQWZ0ZXJBbGwoYyk7XG4gICAgICB9XG5cbiAgICAgIHJvb3QucmVwbGFjZUN1cnNvcihwcmV2RW5kKTtcblxuICAgICAgcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0cyhyb290KTtcbiAgICB9XG4gIH1cbn1cbiIsImltcG9ydCB7IFJvb3QgfSBmcm9tIFwiLi4vcm9vdFwiO1xuaW1wb3J0IHsgRGVsZXRlQW5kTWVyZ2VXaXRoUHJldmlvdXNMaW5lT3BlcmF0aW9uIH0gZnJvbSBcIi4vRGVsZXRlQW5kTWVyZ2VXaXRoUHJldmlvdXNMaW5lT3BlcmF0aW9uXCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgRGVsZXRlQW5kTWVyZ2VXaXRoTmV4dExpbmVPcGVyYXRpb24gaW1wbGVtZW50cyBJT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBkZWxldGVBbmRNZXJnZVdpdGhQcmV2aW91czogRGVsZXRlQW5kTWVyZ2VXaXRoUHJldmlvdXNMaW5lT3BlcmF0aW9uO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcm9vdDogUm9vdCkge1xuICAgIHRoaXMuZGVsZXRlQW5kTWVyZ2VXaXRoUHJldmlvdXMgPVxuICAgICAgbmV3IERlbGV0ZUFuZE1lcmdlV2l0aFByZXZpb3VzTGluZU9wZXJhdGlvbihyb290KTtcbiAgfVxuXG4gIHNob3VsZFN0b3BQcm9wYWdhdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5kZWxldGVBbmRNZXJnZVdpdGhQcmV2aW91cy5zaG91bGRTdG9wUHJvcGFnYXRpb24oKTtcbiAgfVxuXG4gIHNob3VsZFVwZGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5kZWxldGVBbmRNZXJnZVdpdGhQcmV2aW91cy5zaG91bGRVcGRhdGUoKTtcbiAgfVxuXG4gIHBlcmZvcm0oKSB7XG4gICAgY29uc3QgeyByb290IH0gPSB0aGlzO1xuXG4gICAgaWYgKCFyb290Lmhhc1NpbmdsZUN1cnNvcigpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgbGlzdCA9IHJvb3QuZ2V0TGlzdFVuZGVyQ3Vyc29yKCk7XG4gICAgY29uc3QgY3Vyc29yID0gcm9vdC5nZXRDdXJzb3IoKTtcbiAgICBjb25zdCBsaW5lcyA9IGxpc3QuZ2V0TGluZXNJbmZvKCk7XG5cbiAgICBjb25zdCBsaW5lTm8gPSBsaW5lcy5maW5kSW5kZXgoXG4gICAgICAobCkgPT4gY3Vyc29yLmNoID09PSBsLnRvLmNoICYmIGN1cnNvci5saW5lID09PSBsLnRvLmxpbmVcbiAgICApO1xuXG4gICAgaWYgKGxpbmVObyA9PT0gbGluZXMubGVuZ3RoIC0gMSkge1xuICAgICAgY29uc3QgbmV4dExpbmUgPSBsaW5lc1tsaW5lTm9dLnRvLmxpbmUgKyAxO1xuICAgICAgY29uc3QgbmV4dExpc3QgPSByb290LmdldExpc3RVbmRlckxpbmUobmV4dExpbmUpO1xuICAgICAgcm9vdC5yZXBsYWNlQ3Vyc29yKG5leHRMaXN0LmdldEZpcnN0TGluZUNvbnRlbnRTdGFydCgpKTtcbiAgICAgIHRoaXMuZGVsZXRlQW5kTWVyZ2VXaXRoUHJldmlvdXMucGVyZm9ybSgpO1xuICAgIH0gZWxzZSBpZiAobGluZU5vID49IDApIHtcbiAgICAgIHJvb3QucmVwbGFjZUN1cnNvcihsaW5lc1tsaW5lTm8gKyAxXS5mcm9tKTtcbiAgICAgIHRoaXMuZGVsZXRlQW5kTWVyZ2VXaXRoUHJldmlvdXMucGVyZm9ybSgpO1xuICAgIH1cbiAgfVxufVxuIiwiaW1wb3J0IHsgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgRGVsZXRlVGlsbExpbmVTdGFydE9wZXJhdGlvbiBpbXBsZW1lbnRzIElPcGVyYXRpb24ge1xuICBwcml2YXRlIHN0b3BQcm9wYWdhdGlvbiA9IGZhbHNlO1xuICBwcml2YXRlIHVwZGF0ZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvb3Q6IFJvb3QpIHt9XG5cbiAgc2hvdWxkU3RvcFByb3BhZ2F0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3BQcm9wYWdhdGlvbjtcbiAgfVxuXG4gIHNob3VsZFVwZGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy51cGRhdGVkO1xuICB9XG5cbiAgcGVyZm9ybSgpIHtcbiAgICBjb25zdCB7IHJvb3QgfSA9IHRoaXM7XG5cbiAgICBpZiAoIXJvb3QuaGFzU2luZ2xlQ3Vyc29yKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnN0b3BQcm9wYWdhdGlvbiA9IHRydWU7XG4gICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcblxuICAgIGNvbnN0IGN1cnNvciA9IHJvb3QuZ2V0Q3Vyc29yKCk7XG4gICAgY29uc3QgbGlzdCA9IHJvb3QuZ2V0TGlzdFVuZGVyQ3Vyc29yKCk7XG4gICAgY29uc3QgbGluZXMgPSBsaXN0LmdldExpbmVzSW5mbygpO1xuICAgIGNvbnN0IGxpbmVObyA9IGxpbmVzLmZpbmRJbmRleCgobCkgPT4gbC5mcm9tLmxpbmUgPT09IGN1cnNvci5saW5lKTtcblxuICAgIGxpbmVzW2xpbmVOb10udGV4dCA9IGxpbmVzW2xpbmVOb10udGV4dC5zbGljZShcbiAgICAgIGN1cnNvci5jaCAtIGxpbmVzW2xpbmVOb10uZnJvbS5jaFxuICAgICk7XG5cbiAgICBsaXN0LnJlcGxhY2VMaW5lcyhsaW5lcy5tYXAoKGwpID0+IGwudGV4dCkpO1xuICAgIHJvb3QucmVwbGFjZUN1cnNvcihsaW5lc1tsaW5lTm9dLmZyb20pO1xuICB9XG59XG4iLCJpbXBvcnQgeyBQbGF0Zm9ybSwgUGx1Z2luXzIgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB7IElGZWF0dXJlIH0gZnJvbSBcIi4vSUZlYXR1cmVcIjtcbmltcG9ydCB7IExpc3RzU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9MaXN0c1NlcnZpY2VcIjtcbmltcG9ydCB7IERlbGV0ZUFuZE1lcmdlV2l0aE5leHRMaW5lT3BlcmF0aW9uIH0gZnJvbSBcIi4uL29wZXJhdGlvbnMvRGVsZXRlQW5kTWVyZ2VXaXRoTmV4dExpbmVPcGVyYXRpb25cIjtcbmltcG9ydCB7IERlbGV0ZUFuZE1lcmdlV2l0aFByZXZpb3VzTGluZU9wZXJhdGlvbiB9IGZyb20gXCIuLi9vcGVyYXRpb25zL0RlbGV0ZUFuZE1lcmdlV2l0aFByZXZpb3VzTGluZU9wZXJhdGlvblwiO1xuaW1wb3J0IHsgRGVsZXRlVGlsbExpbmVTdGFydE9wZXJhdGlvbiB9IGZyb20gXCIuLi9vcGVyYXRpb25zL0RlbGV0ZVRpbGxMaW5lU3RhcnRPcGVyYXRpb25cIjtcbmltcG9ydCB7IFNldHRpbmdzU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9TZXR0aW5nc1NlcnZpY2VcIjtcbmltcG9ydCB7IElNRVNlcnZpY2UgfSBmcm9tIFwic3JjL3NlcnZpY2VzL0lNRVNlcnZpY2VcIjtcblxuZnVuY3Rpb24gaXNCYWNrc3BhY2UoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gKFxuICAgIChlLmtleUNvZGUgPT09IDggfHwgZS5jb2RlID09PSBcIkJhY2tzcGFjZVwiKSAmJlxuICAgIGUuc2hpZnRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5tZXRhS2V5ID09PSBmYWxzZSAmJlxuICAgIGUuYWx0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUuY3RybEtleSA9PT0gZmFsc2VcbiAgKTtcbn1cblxuZnVuY3Rpb24gaXNDbWRCYWNrc3BhY2UoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gKFxuICAgIChlLmtleUNvZGUgPT09IDggfHwgZS5jb2RlID09PSBcIkJhY2tzcGFjZVwiKSAmJlxuICAgIGUuc2hpZnRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5tZXRhS2V5ID09PSB0cnVlICYmXG4gICAgZS5hbHRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5jdHJsS2V5ID09PSBmYWxzZVxuICApO1xufVxuXG5mdW5jdGlvbiBpc0RlbGV0ZShlOiBLZXlib2FyZEV2ZW50KSB7XG4gIHJldHVybiAoXG4gICAgKGUua2V5Q29kZSA9PT0gNDYgfHwgZS5jb2RlID09PSBcIkRlbGV0ZVwiKSAmJlxuICAgIGUuc2hpZnRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5tZXRhS2V5ID09PSBmYWxzZSAmJlxuICAgIGUuYWx0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUuY3RybEtleSA9PT0gZmFsc2VcbiAgKTtcbn1cblxuZXhwb3J0IGNsYXNzIERlbGV0ZVNob3VsZElnbm9yZUJ1bGxldHNGZWF0dXJlIGltcGxlbWVudHMgSUZlYXR1cmUge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHBsdWdpbjogUGx1Z2luXzIsXG4gICAgcHJpdmF0ZSBzZXR0aW5nc1NlcnZpY2U6IFNldHRpbmdzU2VydmljZSxcbiAgICBwcml2YXRlIGxpc3RzU2VydmljZTogTGlzdHNTZXJ2aWNlLFxuICAgIHByaXZhdGUgaW1lU2VydmljZTogSU1FU2VydmljZVxuICApIHt9XG5cbiAgYXN5bmMgbG9hZCgpIHtcbiAgICB0aGlzLnBsdWdpbi5yZWdpc3RlckNvZGVNaXJyb3IoKGNtKSA9PiB7XG4gICAgICBjbS5vbihcImtleWRvd25cIiwgdGhpcy5vbktleURvd24pO1xuICAgIH0pO1xuICB9XG5cbiAgYXN5bmMgdW5sb2FkKCkge1xuICAgIHRoaXMucGx1Z2luLmFwcC53b3Jrc3BhY2UuaXRlcmF0ZUNvZGVNaXJyb3JzKChjbSkgPT4ge1xuICAgICAgY20ub2ZmKFwia2V5ZG93blwiLCB0aGlzLm9uS2V5RG93bik7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIG9uS2V5RG93biA9IChjbTogQ29kZU1pcnJvci5FZGl0b3IsIGV2ZW50OiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgaWYgKCF0aGlzLnNldHRpbmdzU2VydmljZS5zdGlja0N1cnNvciB8fCB0aGlzLmltZVNlcnZpY2UuaXNJTUVPcGVuZWQoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChpc0JhY2tzcGFjZShldmVudCkpIHtcbiAgICAgIGNvbnN0IHsgc2hvdWxkU3RvcFByb3BhZ2F0aW9uIH0gPSB0aGlzLmxpc3RzU2VydmljZS5wZXJmb3JtT3BlcmF0aW9uKFxuICAgICAgICAocm9vdCkgPT4gbmV3IERlbGV0ZUFuZE1lcmdlV2l0aFByZXZpb3VzTGluZU9wZXJhdGlvbihyb290KSxcbiAgICAgICAgY21cbiAgICAgICk7XG5cbiAgICAgIGlmIChzaG91bGRTdG9wUHJvcGFnYXRpb24pIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKFBsYXRmb3JtLmlzTWFjT1MgJiYgaXNDbWRCYWNrc3BhY2UoZXZlbnQpKSB7XG4gICAgICBjb25zdCB7IHNob3VsZFN0b3BQcm9wYWdhdGlvbiB9ID0gdGhpcy5saXN0c1NlcnZpY2UucGVyZm9ybU9wZXJhdGlvbihcbiAgICAgICAgKHJvb3QpID0+IG5ldyBEZWxldGVUaWxsTGluZVN0YXJ0T3BlcmF0aW9uKHJvb3QpLFxuICAgICAgICBjbVxuICAgICAgKTtcblxuICAgICAgaWYgKHNob3VsZFN0b3BQcm9wYWdhdGlvbikge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaXNEZWxldGUoZXZlbnQpKSB7XG4gICAgICBjb25zdCB7IHNob3VsZFN0b3BQcm9wYWdhdGlvbiB9ID0gdGhpcy5saXN0c1NlcnZpY2UucGVyZm9ybU9wZXJhdGlvbihcbiAgICAgICAgKHJvb3QpID0+IG5ldyBEZWxldGVBbmRNZXJnZVdpdGhOZXh0TGluZU9wZXJhdGlvbihyb290KSxcbiAgICAgICAgY21cbiAgICAgICk7XG5cbiAgICAgIGlmIChzaG91bGRTdG9wUHJvcGFnYXRpb24pIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICB9XG4gICAgfVxuICB9O1xufVxuIiwiaW1wb3J0IHsgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgU2VsZWN0VGlsbExpbmVTdGFydE9wZXJhdGlvbiBpbXBsZW1lbnRzIElPcGVyYXRpb24ge1xuICBwcml2YXRlIHN0b3BQcm9wYWdhdGlvbiA9IGZhbHNlO1xuICBwcml2YXRlIHVwZGF0ZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvb3Q6IFJvb3QpIHt9XG5cbiAgc2hvdWxkU3RvcFByb3BhZ2F0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3BQcm9wYWdhdGlvbjtcbiAgfVxuXG4gIHNob3VsZFVwZGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy51cGRhdGVkO1xuICB9XG5cbiAgcGVyZm9ybSgpIHtcbiAgICBjb25zdCB7IHJvb3QgfSA9IHRoaXM7XG5cbiAgICBpZiAoIXJvb3QuaGFzU2luZ2xlQ3Vyc29yKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnN0b3BQcm9wYWdhdGlvbiA9IHRydWU7XG4gICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcblxuICAgIGNvbnN0IGN1cnNvciA9IHJvb3QuZ2V0Q3Vyc29yKCk7XG4gICAgY29uc3QgbGlzdCA9IHJvb3QuZ2V0TGlzdFVuZGVyQ3Vyc29yKCk7XG4gICAgY29uc3QgbGluZXMgPSBsaXN0LmdldExpbmVzSW5mbygpO1xuICAgIGNvbnN0IGxpbmVObyA9IGxpbmVzLmZpbmRJbmRleCgobCkgPT4gbC5mcm9tLmxpbmUgPT09IGN1cnNvci5saW5lKTtcblxuICAgIHJvb3QucmVwbGFjZVNlbGVjdGlvbnMoW3sgaGVhZDogbGluZXNbbGluZU5vXS5mcm9tLCBhbmNob3I6IGN1cnNvciB9XSk7XG4gIH1cbn1cbiIsImltcG9ydCB7IFBsYXRmb3JtLCBQbHVnaW5fMiB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHsgTGlzdHNTZXJ2aWNlIH0gZnJvbSBcIi4uL3NlcnZpY2VzL0xpc3RzU2VydmljZVwiO1xuaW1wb3J0IHsgU2VsZWN0VGlsbExpbmVTdGFydE9wZXJhdGlvbiB9IGZyb20gXCIuLi9vcGVyYXRpb25zL1NlbGVjdFRpbGxMaW5lU3RhcnRPcGVyYXRpb25cIjtcbmltcG9ydCB7IElGZWF0dXJlIH0gZnJvbSBcIi4vSUZlYXR1cmVcIjtcbmltcG9ydCB7IFNldHRpbmdzU2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9TZXR0aW5nc1NlcnZpY2VcIjtcbmltcG9ydCB7IElNRVNlcnZpY2UgfSBmcm9tIFwic3JjL3NlcnZpY2VzL0lNRVNlcnZpY2VcIjtcblxuZnVuY3Rpb24gaXNDbWRTaGlmdExlZnQoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gKFxuICAgIChlLmtleUNvZGUgPT09IDM3IHx8IGUuY29kZSA9PT0gXCJBcnJvd0xlZnRcIikgJiZcbiAgICBlLnNoaWZ0S2V5ID09PSB0cnVlICYmXG4gICAgZS5tZXRhS2V5ID09PSB0cnVlICYmXG4gICAgZS5hbHRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5jdHJsS2V5ID09PSBmYWxzZVxuICApO1xufVxuXG5leHBvcnQgY2xhc3MgU2VsZWN0aW9uU2hvdWxkSWdub3JlQnVsbGV0c0ZlYXR1cmUgaW1wbGVtZW50cyBJRmVhdHVyZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcGx1Z2luOiBQbHVnaW5fMixcbiAgICBwcml2YXRlIHNldHRpbmdzU2VydmljZTogU2V0dGluZ3NTZXJ2aWNlLFxuICAgIHByaXZhdGUgbGlzdHNTZXJ2aWNlOiBMaXN0c1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBpbWVTZXJ2aWNlOiBJTUVTZXJ2aWNlXG4gICkge31cblxuICBhc3luYyBsb2FkKCkge1xuICAgIHRoaXMucGx1Z2luLnJlZ2lzdGVyQ29kZU1pcnJvcigoY20pID0+IHtcbiAgICAgIGNtLm9uKFwia2V5ZG93blwiLCB0aGlzLm9uS2V5RG93bik7XG4gICAgfSk7XG4gIH1cblxuICBhc3luYyB1bmxvYWQoKSB7XG4gICAgdGhpcy5wbHVnaW4uYXBwLndvcmtzcGFjZS5pdGVyYXRlQ29kZU1pcnJvcnMoKGNtKSA9PiB7XG4gICAgICBjbS5vZmYoXCJrZXlkb3duXCIsIHRoaXMub25LZXlEb3duKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgb25LZXlEb3duID0gKGNtOiBDb2RlTWlycm9yLkVkaXRvciwgZXZlbnQ6IEtleWJvYXJkRXZlbnQpID0+IHtcbiAgICBpZiAoIXRoaXMuc2V0dGluZ3NTZXJ2aWNlLnN0aWNrQ3Vyc29yIHx8IHRoaXMuaW1lU2VydmljZS5pc0lNRU9wZW5lZCgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKFBsYXRmb3JtLmlzTWFjT1MgJiYgaXNDbWRTaGlmdExlZnQoZXZlbnQpKSB7XG4gICAgICBjb25zdCB7IHNob3VsZFN0b3BQcm9wYWdhdGlvbiB9ID0gdGhpcy5saXN0c1NlcnZpY2UucGVyZm9ybU9wZXJhdGlvbihcbiAgICAgICAgKHJvb3QpID0+IG5ldyBTZWxlY3RUaWxsTGluZVN0YXJ0T3BlcmF0aW9uKHJvb3QpLFxuICAgICAgICBjbVxuICAgICAgKTtcblxuICAgICAgaWYgKHNob3VsZFN0b3BQcm9wYWdhdGlvbikge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG59XG4iLCJpbXBvcnQgeyBOb3RpY2UsIFBsYXRmb3JtLCBQbHVnaW5fMiB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHsgSU1FU2VydmljZSB9IGZyb20gXCJzcmMvc2VydmljZXMvSU1FU2VydmljZVwiO1xuaW1wb3J0IHsgU2V0dGluZ3NTZXJ2aWNlIH0gZnJvbSBcInNyYy9zZXJ2aWNlcy9TZXR0aW5nc1NlcnZpY2VcIjtcbmltcG9ydCB7IElGZWF0dXJlIH0gZnJvbSBcIi4vSUZlYXR1cmVcIjtcblxuZnVuY3Rpb24gaXNDbWREb3RPckNtZFNoaWZ0RG90KGU6IEtleWJvYXJkRXZlbnQpIHtcbiAgcmV0dXJuIChcbiAgICAoZS5rZXlDb2RlID09PSAxOTAgfHwgZS5jb2RlID09PSBcIlBlcmlvZFwiKSAmJlxuICAgIGUubWV0YUtleSA9PT0gdHJ1ZSAmJlxuICAgIGUuYWx0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUuY3RybEtleSA9PT0gZmFsc2VcbiAgKTtcbn1cblxuZnVuY3Rpb24gaXNDdHJsRG90T3JDdHJsU2hpZnREb3QoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gKFxuICAgIChlLmtleUNvZGUgPT09IDE5MCB8fCBlLmNvZGUgPT09IFwiUGVyaW9kXCIpICYmXG4gICAgZS5tZXRhS2V5ID09PSBmYWxzZSAmJlxuICAgIGUuYWx0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUuY3RybEtleSA9PT0gdHJ1ZVxuICApO1xufVxuXG5mdW5jdGlvbiBpc01vZERvdE9yTW9kU2hpZnREb3QoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gUGxhdGZvcm0uaXNNYWNPU1xuICAgID8gaXNDbWREb3RPckNtZFNoaWZ0RG90KGUpXG4gICAgOiBpc0N0cmxEb3RPckN0cmxTaGlmdERvdChlKTtcbn1cblxuZXhwb3J0IGNsYXNzIFpvb21GZWF0dXJlIGltcGxlbWVudHMgSUZlYXR1cmUge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHBsdWdpbjogUGx1Z2luXzIsXG4gICAgcHJpdmF0ZSBzZXR0aW5nc1NlcnZpY2U6IFNldHRpbmdzU2VydmljZSxcbiAgICBwcml2YXRlIGltZVNlcnZpY2U6IElNRVNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIGxvYWQoKSB7XG4gICAgdGhpcy5wbHVnaW4ucmVnaXN0ZXJDb2RlTWlycm9yKChjbSkgPT4ge1xuICAgICAgY20ub24oXCJrZXlkb3duXCIsIHRoaXMub25LZXlEb3duKTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIHVubG9hZCgpIHtcbiAgICB0aGlzLnBsdWdpbi5hcHAud29ya3NwYWNlLml0ZXJhdGVDb2RlTWlycm9ycygoY20pID0+IHtcbiAgICAgIGNtLm9mZihcImtleWRvd25cIiwgdGhpcy5vbktleURvd24pO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBvbktleURvd24gPSAoY206IENvZGVNaXJyb3IuRWRpdG9yLCBlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgaWYgKFxuICAgICAgKHdpbmRvdyBhcyBhbnkpLk9ic2lkaWFuWm9vbVBsdWdpbiB8fFxuICAgICAgdGhpcy5zZXR0aW5nc1NlcnZpY2UuZGlzYWJsZVpvb21Ob3RpZmljYXRpb24gfHxcbiAgICAgICFpc01vZERvdE9yTW9kU2hpZnREb3QoZSkgfHxcbiAgICAgIHRoaXMuaW1lU2VydmljZS5pc0lNRU9wZW5lZCgpXG4gICAgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbmV3IE5vdGljZShcbiAgICAgIGBab29taW5nIHN1cHBvcnQgaGFzIGJlZW4gcmVtb3ZlZCBmcm9tIHRoZSBPYnNpZGlhbiBPdXRsaW5lciBwbHVnaW4uIFBsZWFzZSBpbnN0YWxsIHRoZSBPYnNpZGlhbiBab29tIHBsdWdpbi5gLFxuICAgICAgNTAwMFxuICAgICk7XG4gIH07XG59XG4iLCJpbXBvcnQgeyBOb3RpY2UsIFBsdWdpbl8yIH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5pbXBvcnQgeyBPYnNpZGlhblNlcnZpY2UgfSBmcm9tIFwiLi4vc2VydmljZXMvT2JzaWRpYW5TZXJ2aWNlXCI7XG5pbXBvcnQgeyBJRmVhdHVyZSB9IGZyb20gXCIuL0lGZWF0dXJlXCI7XG5cbmV4cG9ydCBjbGFzcyBGb2xkRmVhdHVyZSBpbXBsZW1lbnRzIElGZWF0dXJlIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBwbHVnaW46IFBsdWdpbl8yLFxuICAgIHByaXZhdGUgb2JzaWRpYW5TZXJ2aWNlOiBPYnNpZGlhblNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIGxvYWQoKSB7XG4gICAgdGhpcy5wbHVnaW4uYWRkQ29tbWFuZCh7XG4gICAgICBpZDogXCJmb2xkXCIsXG4gICAgICBuYW1lOiBcIkZvbGQgdGhlIGxpc3RcIixcbiAgICAgIGNhbGxiYWNrOiB0aGlzLm9ic2lkaWFuU2VydmljZS5jcmVhdGVDb21tYW5kQ2FsbGJhY2soXG4gICAgICAgIHRoaXMuZm9sZC5iaW5kKHRoaXMpXG4gICAgICApLFxuICAgICAgaG90a2V5czogW1xuICAgICAgICB7XG4gICAgICAgICAgbW9kaWZpZXJzOiBbXCJNb2RcIl0sXG4gICAgICAgICAga2V5OiBcIkFycm93VXBcIixcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICB0aGlzLnBsdWdpbi5hZGRDb21tYW5kKHtcbiAgICAgIGlkOiBcInVuZm9sZFwiLFxuICAgICAgbmFtZTogXCJVbmZvbGQgdGhlIGxpc3RcIixcbiAgICAgIGNhbGxiYWNrOiB0aGlzLm9ic2lkaWFuU2VydmljZS5jcmVhdGVDb21tYW5kQ2FsbGJhY2soXG4gICAgICAgIHRoaXMudW5mb2xkLmJpbmQodGhpcylcbiAgICAgICksXG4gICAgICBob3RrZXlzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBtb2RpZmllcnM6IFtcIk1vZFwiXSxcbiAgICAgICAgICBrZXk6IFwiQXJyb3dEb3duXCIsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuICB9XG5cbiAgYXN5bmMgdW5sb2FkKCkge31cblxuICBwcml2YXRlIHNldEZvbGQoZWRpdG9yOiBDb2RlTWlycm9yLkVkaXRvciwgdHlwZTogXCJmb2xkXCIgfCBcInVuZm9sZFwiKSB7XG4gICAgaWYgKCF0aGlzLm9ic2lkaWFuU2VydmljZS5nZXRPYnNpZGlhbkZvbGRTZXR0aWducygpLmZvbGRJbmRlbnQpIHtcbiAgICAgIG5ldyBOb3RpY2UoXG4gICAgICAgIGBVbmFibGUgdG8gJHt0eXBlfSBiZWNhdXNlIGZvbGRpbmcgaXMgZGlzYWJsZWQuIFBsZWFzZSBlbmFibGUgXCJGb2xkIGluZGVudFwiIGluIE9ic2lkaWFuIHNldHRpbmdzLmAsXG4gICAgICAgIDUwMDBcbiAgICAgICk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICAoZWRpdG9yIGFzIGFueSkuZm9sZENvZGUoZWRpdG9yLmdldEN1cnNvcigpLCBudWxsLCB0eXBlKTtcblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgcHJpdmF0ZSBmb2xkKGVkaXRvcjogQ29kZU1pcnJvci5FZGl0b3IpIHtcbiAgICByZXR1cm4gdGhpcy5zZXRGb2xkKGVkaXRvciwgXCJmb2xkXCIpO1xuICB9XG5cbiAgcHJpdmF0ZSB1bmZvbGQoZWRpdG9yOiBDb2RlTWlycm9yLkVkaXRvcikge1xuICAgIHJldHVybiB0aGlzLnNldEZvbGQoZWRpdG9yLCBcInVuZm9sZFwiKTtcbiAgfVxufVxuIiwiaW1wb3J0IHsgbWF4UG9zLCBtaW5Qb3MsIFJvb3QgfSBmcm9tIFwiLi4vcm9vdFwiO1xuaW1wb3J0IHsgSU9wZXJhdGlvbiB9IGZyb20gXCIuL0lPcGVyYXRpb25cIjtcblxuZXhwb3J0IGNsYXNzIFNlbGVjdEFsbE9wZXJhdGlvbiBpbXBsZW1lbnRzIElPcGVyYXRpb24ge1xuICBwcml2YXRlIHN0b3BQcm9wYWdhdGlvbiA9IGZhbHNlO1xuICBwcml2YXRlIHVwZGF0ZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvb3Q6IFJvb3QpIHt9XG5cbiAgc2hvdWxkU3RvcFByb3BhZ2F0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3BQcm9wYWdhdGlvbjtcbiAgfVxuXG4gIHNob3VsZFVwZGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy51cGRhdGVkO1xuICB9XG5cbiAgcGVyZm9ybSgpIHtcbiAgICBjb25zdCB7IHJvb3QgfSA9IHRoaXM7XG5cbiAgICBpZiAoIXJvb3QuaGFzU2luZ2xlU2VsZWN0aW9uKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBzZWxlY3Rpb24gPSByb290LmdldFNlbGVjdGlvbnMoKVswXTtcbiAgICBjb25zdCBbcm9vdFN0YXJ0LCByb290RW5kXSA9IHJvb3QuZ2V0UmFuZ2UoKTtcblxuICAgIGNvbnN0IHNlbGVjdGlvbkZyb20gPSBtaW5Qb3Moc2VsZWN0aW9uLmFuY2hvciwgc2VsZWN0aW9uLmhlYWQpO1xuICAgIGNvbnN0IHNlbGVjdGlvblRvID0gbWF4UG9zKHNlbGVjdGlvbi5hbmNob3IsIHNlbGVjdGlvbi5oZWFkKTtcblxuICAgIGlmIChcbiAgICAgIHNlbGVjdGlvbkZyb20ubGluZSA8IHJvb3RTdGFydC5saW5lIHx8XG4gICAgICBzZWxlY3Rpb25Uby5saW5lID4gcm9vdEVuZC5saW5lXG4gICAgKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgaWYgKFxuICAgICAgc2VsZWN0aW9uRnJvbS5saW5lID09PSByb290U3RhcnQubGluZSAmJlxuICAgICAgc2VsZWN0aW9uRnJvbS5jaCA9PT0gcm9vdFN0YXJ0LmNoICYmXG4gICAgICBzZWxlY3Rpb25Uby5saW5lID09PSByb290RW5kLmxpbmUgJiZcbiAgICAgIHNlbGVjdGlvblRvLmNoID09PSByb290RW5kLmNoXG4gICAgKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgY29uc3QgbGlzdCA9IHJvb3QuZ2V0TGlzdFVuZGVyQ3Vyc29yKCk7XG4gICAgY29uc3QgY29udGVudFN0YXJ0ID0gbGlzdC5nZXRGaXJzdExpbmVDb250ZW50U3RhcnQoKTtcbiAgICBjb25zdCBjb250ZW50RW5kID0gbGlzdC5nZXRMYXN0TGluZUNvbnRlbnRFbmQoKTtcblxuICAgIGlmIChcbiAgICAgIHNlbGVjdGlvbkZyb20ubGluZSA8IGNvbnRlbnRTdGFydC5saW5lIHx8XG4gICAgICBzZWxlY3Rpb25Uby5saW5lID4gY29udGVudEVuZC5saW5lXG4gICAgKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgdGhpcy5zdG9wUHJvcGFnYXRpb24gPSB0cnVlO1xuICAgIHRoaXMudXBkYXRlZCA9IHRydWU7XG5cbiAgICBpZiAoXG4gICAgICBzZWxlY3Rpb25Gcm9tLmxpbmUgPT09IGNvbnRlbnRTdGFydC5saW5lICYmXG4gICAgICBzZWxlY3Rpb25Gcm9tLmNoID09PSBjb250ZW50U3RhcnQuY2ggJiZcbiAgICAgIHNlbGVjdGlvblRvLmxpbmUgPT09IGNvbnRlbnRFbmQubGluZSAmJlxuICAgICAgc2VsZWN0aW9uVG8uY2ggPT09IGNvbnRlbnRFbmQuY2hcbiAgICApIHtcbiAgICAgIC8vIHNlbGVjdCBhbGwgbGlzdFxuICAgICAgcm9vdC5yZXBsYWNlU2VsZWN0aW9ucyhbeyBhbmNob3I6IHJvb3RTdGFydCwgaGVhZDogcm9vdEVuZCB9XSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIHNlbGVjdCBhbGwgbGluZVxuICAgICAgcm9vdC5yZXBsYWNlU2VsZWN0aW9ucyhbeyBhbmNob3I6IGNvbnRlbnRTdGFydCwgaGVhZDogY29udGVudEVuZCB9XSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cbn1cbiIsImltcG9ydCB7IFBsYXRmb3JtLCBQbHVnaW5fMiB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHsgTGlzdHNTZXJ2aWNlIH0gZnJvbSBcIi4uL3NlcnZpY2VzL0xpc3RzU2VydmljZVwiO1xuaW1wb3J0IHsgU2VsZWN0QWxsT3BlcmF0aW9uIH0gZnJvbSBcIi4uL29wZXJhdGlvbnMvU2VsZWN0QWxsT3BlcmF0aW9uXCI7XG5pbXBvcnQgeyBTZXR0aW5nc1NlcnZpY2UgfSBmcm9tIFwiLi4vc2VydmljZXMvU2V0dGluZ3NTZXJ2aWNlXCI7XG5pbXBvcnQgeyBJRmVhdHVyZSB9IGZyb20gXCIuL0lGZWF0dXJlXCI7XG5pbXBvcnQgeyBJTUVTZXJ2aWNlIH0gZnJvbSBcInNyYy9zZXJ2aWNlcy9JTUVTZXJ2aWNlXCI7XG5cbmZ1bmN0aW9uIGlzQ21kQShlOiBLZXlib2FyZEV2ZW50KSB7XG4gIHJldHVybiAoXG4gICAgKGUua2V5Q29kZSA9PT0gNjUgfHwgZS5jb2RlID09PSBcIktleUFcIikgJiZcbiAgICBlLnNoaWZ0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUubWV0YUtleSA9PT0gdHJ1ZSAmJlxuICAgIGUuYWx0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUuY3RybEtleSA9PT0gZmFsc2VcbiAgKTtcbn1cblxuZnVuY3Rpb24gaXNDdHJsQShlOiBLZXlib2FyZEV2ZW50KSB7XG4gIHJldHVybiAoXG4gICAgKGUua2V5Q29kZSA9PT0gNjUgfHwgZS5jb2RlID09PSBcIktleUFcIikgJiZcbiAgICBlLnNoaWZ0S2V5ID09PSBmYWxzZSAmJlxuICAgIGUubWV0YUtleSA9PT0gZmFsc2UgJiZcbiAgICBlLmFsdEtleSA9PT0gZmFsc2UgJiZcbiAgICBlLmN0cmxLZXkgPT09IHRydWVcbiAgKTtcbn1cblxuZnVuY3Rpb24gaXNTZWxlY3RBbGwoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gUGxhdGZvcm0uaXNNYWNPUyA/IGlzQ21kQShlKSA6IGlzQ3RybEEoZSk7XG59XG5cbmV4cG9ydCBjbGFzcyBTZWxlY3RBbGxGZWF0dXJlIGltcGxlbWVudHMgSUZlYXR1cmUge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHBsdWdpbjogUGx1Z2luXzIsXG4gICAgcHJpdmF0ZSBzZXR0aW5nc1NlcnZpY2U6IFNldHRpbmdzU2VydmljZSxcbiAgICBwcml2YXRlIGxpc3RzU2VydmljZTogTGlzdHNTZXJ2aWNlLFxuICAgIHByaXZhdGUgaW1lU2VydmljZTogSU1FU2VydmljZVxuICApIHt9XG5cbiAgYXN5bmMgbG9hZCgpIHtcbiAgICB0aGlzLnBsdWdpbi5yZWdpc3RlckNvZGVNaXJyb3IoKGNtKSA9PiB7XG4gICAgICBjbS5vbihcImtleWRvd25cIiwgdGhpcy5vbktleURvd24pO1xuICAgIH0pO1xuICB9XG5cbiAgYXN5bmMgdW5sb2FkKCkge1xuICAgIHRoaXMucGx1Z2luLmFwcC53b3Jrc3BhY2UuaXRlcmF0ZUNvZGVNaXJyb3JzKChjbSkgPT4ge1xuICAgICAgY20ub2ZmKFwia2V5ZG93blwiLCB0aGlzLm9uS2V5RG93bik7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIG9uS2V5RG93biA9IChjbTogQ29kZU1pcnJvci5FZGl0b3IsIGV2ZW50OiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgaWYgKFxuICAgICAgIXRoaXMuc2V0dGluZ3NTZXJ2aWNlLnNlbGVjdEFsbCB8fFxuICAgICAgIWlzU2VsZWN0QWxsKGV2ZW50KSB8fFxuICAgICAgdGhpcy5pbWVTZXJ2aWNlLmlzSU1FT3BlbmVkKClcbiAgICApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCB7IHNob3VsZFN0b3BQcm9wYWdhdGlvbiB9ID0gdGhpcy5saXN0c1NlcnZpY2UucGVyZm9ybU9wZXJhdGlvbihcbiAgICAgIChyb290KSA9PiBuZXcgU2VsZWN0QWxsT3BlcmF0aW9uKHJvb3QpLFxuICAgICAgY21cbiAgICApO1xuXG4gICAgaWYgKHNob3VsZFN0b3BQcm9wYWdhdGlvbikge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIH1cbiAgfTtcbn1cbiIsImltcG9ydCB7IHJlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHMgfSBmcm9tIFwic3JjL3Jvb3QvcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0c1wiO1xuaW1wb3J0IHsgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgTW92ZVJpZ2h0T3BlcmF0aW9uIGltcGxlbWVudHMgSU9wZXJhdGlvbiB7XG4gIHByaXZhdGUgc3RvcFByb3BhZ2F0aW9uID0gZmFsc2U7XG4gIHByaXZhdGUgdXBkYXRlZCA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcm9vdDogUm9vdCwgcHJpdmF0ZSBkZWZhdWx0SW5kZW50Q2hhcnM6IHN0cmluZykge31cblxuICBzaG91bGRTdG9wUHJvcGFnYXRpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcFByb3BhZ2F0aW9uO1xuICB9XG5cbiAgc2hvdWxkVXBkYXRlKCkge1xuICAgIHJldHVybiB0aGlzLnVwZGF0ZWQ7XG4gIH1cblxuICBwZXJmb3JtKCkge1xuICAgIGNvbnN0IHsgcm9vdCB9ID0gdGhpcztcblxuICAgIGlmICghcm9vdC5oYXNTaW5nbGVDdXJzb3IoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuc3RvcFByb3BhZ2F0aW9uID0gdHJ1ZTtcblxuICAgIGNvbnN0IGxpc3QgPSByb290LmdldExpc3RVbmRlckN1cnNvcigpO1xuICAgIGNvbnN0IHBhcmVudCA9IGxpc3QuZ2V0UGFyZW50KCk7XG4gICAgY29uc3QgcHJldiA9IHBhcmVudC5nZXRQcmV2U2libGluZ09mKGxpc3QpO1xuXG4gICAgaWYgKCFwcmV2KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcblxuICAgIGNvbnN0IGxpc3RTdGFydExpbmVCZWZvcmUgPSByb290LmdldENvbnRlbnRMaW5lc1JhbmdlT2YobGlzdClbMF07XG5cbiAgICBjb25zdCBpbmRlbnRQb3MgPSBsaXN0LmdldEZpcnN0TGluZUluZGVudCgpLmxlbmd0aDtcbiAgICBsZXQgaW5kZW50Q2hhcnMgPSBcIlwiO1xuXG4gICAgaWYgKGluZGVudENoYXJzID09PSBcIlwiICYmICFwcmV2LmlzRW1wdHkoKSkge1xuICAgICAgaW5kZW50Q2hhcnMgPSBwcmV2XG4gICAgICAgIC5nZXRDaGlsZHJlbigpWzBdXG4gICAgICAgIC5nZXRGaXJzdExpbmVJbmRlbnQoKVxuICAgICAgICAuc2xpY2UocHJldi5nZXRGaXJzdExpbmVJbmRlbnQoKS5sZW5ndGgpO1xuICAgIH1cblxuICAgIGlmIChpbmRlbnRDaGFycyA9PT0gXCJcIikge1xuICAgICAgaW5kZW50Q2hhcnMgPSBsaXN0XG4gICAgICAgIC5nZXRGaXJzdExpbmVJbmRlbnQoKVxuICAgICAgICAuc2xpY2UocGFyZW50LmdldEZpcnN0TGluZUluZGVudCgpLmxlbmd0aCk7XG4gICAgfVxuXG4gICAgaWYgKGluZGVudENoYXJzID09PSBcIlwiICYmICFsaXN0LmlzRW1wdHkoKSkge1xuICAgICAgaW5kZW50Q2hhcnMgPSBsaXN0LmdldENoaWxkcmVuKClbMF0uZ2V0Rmlyc3RMaW5lSW5kZW50KCk7XG4gICAgfVxuXG4gICAgaWYgKGluZGVudENoYXJzID09PSBcIlwiKSB7XG4gICAgICBpbmRlbnRDaGFycyA9IHRoaXMuZGVmYXVsdEluZGVudENoYXJzO1xuICAgIH1cblxuICAgIHBhcmVudC5yZW1vdmVDaGlsZChsaXN0KTtcbiAgICBwcmV2LmFkZEFmdGVyQWxsKGxpc3QpO1xuICAgIGxpc3QuaW5kZW50Q29udGVudChpbmRlbnRQb3MsIGluZGVudENoYXJzKTtcblxuICAgIGNvbnN0IGxpc3RTdGFydExpbmVBZnRlciA9IHJvb3QuZ2V0Q29udGVudExpbmVzUmFuZ2VPZihsaXN0KVswXTtcbiAgICBjb25zdCBsaW5lRGlmZiA9IGxpc3RTdGFydExpbmVBZnRlciAtIGxpc3RTdGFydExpbmVCZWZvcmU7XG5cbiAgICBjb25zdCBjdXJzb3IgPSByb290LmdldEN1cnNvcigpO1xuICAgIHJvb3QucmVwbGFjZUN1cnNvcih7XG4gICAgICBsaW5lOiBjdXJzb3IubGluZSArIGxpbmVEaWZmLFxuICAgICAgY2g6IGN1cnNvci5jaCArIGluZGVudENoYXJzLmxlbmd0aCxcbiAgICB9KTtcblxuICAgIHJlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHMocm9vdCk7XG4gIH1cbn1cbiIsImltcG9ydCB7IHJlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHMgfSBmcm9tIFwic3JjL3Jvb3QvcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0c1wiO1xuaW1wb3J0IHsgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgTW92ZURvd25PcGVyYXRpb24gaW1wbGVtZW50cyBJT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBzdG9wUHJvcGFnYXRpb24gPSBmYWxzZTtcbiAgcHJpdmF0ZSB1cGRhdGVkID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByb290OiBSb290KSB7fVxuXG4gIHNob3VsZFN0b3BQcm9wYWdhdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5zdG9wUHJvcGFnYXRpb247XG4gIH1cblxuICBzaG91bGRVcGRhdGUoKSB7XG4gICAgcmV0dXJuIHRoaXMudXBkYXRlZDtcbiAgfVxuXG4gIHBlcmZvcm0oKSB7XG4gICAgY29uc3QgeyByb290IH0gPSB0aGlzO1xuXG4gICAgaWYgKCFyb290Lmhhc1NpbmdsZUN1cnNvcigpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5zdG9wUHJvcGFnYXRpb24gPSB0cnVlO1xuXG4gICAgY29uc3QgbGlzdCA9IHJvb3QuZ2V0TGlzdFVuZGVyQ3Vyc29yKCk7XG4gICAgY29uc3QgcGFyZW50ID0gbGlzdC5nZXRQYXJlbnQoKTtcbiAgICBjb25zdCBncmFuZFBhcmVudCA9IHBhcmVudC5nZXRQYXJlbnQoKTtcbiAgICBjb25zdCBuZXh0ID0gcGFyZW50LmdldE5leHRTaWJsaW5nT2YobGlzdCk7XG5cbiAgICBjb25zdCBsaXN0U3RhcnRMaW5lQmVmb3JlID0gcm9vdC5nZXRDb250ZW50TGluZXNSYW5nZU9mKGxpc3QpWzBdO1xuXG4gICAgaWYgKCFuZXh0ICYmIGdyYW5kUGFyZW50KSB7XG4gICAgICBjb25zdCBuZXdQYXJlbnQgPSBncmFuZFBhcmVudC5nZXROZXh0U2libGluZ09mKHBhcmVudCk7XG5cbiAgICAgIGlmIChuZXdQYXJlbnQpIHtcbiAgICAgICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcbiAgICAgICAgcGFyZW50LnJlbW92ZUNoaWxkKGxpc3QpO1xuICAgICAgICBuZXdQYXJlbnQuYWRkQmVmb3JlQWxsKGxpc3QpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAobmV4dCkge1xuICAgICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcbiAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChsaXN0KTtcbiAgICAgIHBhcmVudC5hZGRBZnRlcihuZXh0LCBsaXN0KTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMudXBkYXRlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGxpc3RTdGFydExpbmVBZnRlciA9IHJvb3QuZ2V0Q29udGVudExpbmVzUmFuZ2VPZihsaXN0KVswXTtcbiAgICBjb25zdCBsaW5lRGlmZiA9IGxpc3RTdGFydExpbmVBZnRlciAtIGxpc3RTdGFydExpbmVCZWZvcmU7XG5cbiAgICBjb25zdCBjdXJzb3IgPSByb290LmdldEN1cnNvcigpO1xuICAgIHJvb3QucmVwbGFjZUN1cnNvcih7XG4gICAgICBsaW5lOiBjdXJzb3IubGluZSArIGxpbmVEaWZmLFxuICAgICAgY2g6IGN1cnNvci5jaCxcbiAgICB9KTtcblxuICAgIHJlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHMocm9vdCk7XG4gIH1cbn1cbiIsImltcG9ydCB7IHJlY2FsY3VsYXRlTnVtZXJpY0J1bGxldHMgfSBmcm9tIFwic3JjL3Jvb3QvcmVjYWxjdWxhdGVOdW1lcmljQnVsbGV0c1wiO1xuaW1wb3J0IHsgUm9vdCB9IGZyb20gXCIuLi9yb290XCI7XG5pbXBvcnQgeyBJT3BlcmF0aW9uIH0gZnJvbSBcIi4vSU9wZXJhdGlvblwiO1xuXG5leHBvcnQgY2xhc3MgTW92ZVVwT3BlcmF0aW9uIGltcGxlbWVudHMgSU9wZXJhdGlvbiB7XG4gIHByaXZhdGUgc3RvcFByb3BhZ2F0aW9uID0gZmFsc2U7XG4gIHByaXZhdGUgdXBkYXRlZCA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcm9vdDogUm9vdCkge31cblxuICBzaG91bGRTdG9wUHJvcGFnYXRpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcFByb3BhZ2F0aW9uO1xuICB9XG5cbiAgc2hvdWxkVXBkYXRlKCkge1xuICAgIHJldHVybiB0aGlzLnVwZGF0ZWQ7XG4gIH1cblxuICBwZXJmb3JtKCkge1xuICAgIGNvbnN0IHsgcm9vdCB9ID0gdGhpcztcblxuICAgIGlmICghcm9vdC5oYXNTaW5nbGVDdXJzb3IoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuc3RvcFByb3BhZ2F0aW9uID0gdHJ1ZTtcblxuICAgIGNvbnN0IGxpc3QgPSByb290LmdldExpc3RVbmRlckN1cnNvcigpO1xuICAgIGNvbnN0IHBhcmVudCA9IGxpc3QuZ2V0UGFyZW50KCk7XG4gICAgY29uc3QgZ3JhbmRQYXJlbnQgPSBwYXJlbnQuZ2V0UGFyZW50KCk7XG4gICAgY29uc3QgcHJldiA9IHBhcmVudC5nZXRQcmV2U2libGluZ09mKGxpc3QpO1xuXG4gICAgY29uc3QgbGlzdFN0YXJ0TGluZUJlZm9yZSA9IHJvb3QuZ2V0Q29udGVudExpbmVzUmFuZ2VPZihsaXN0KVswXTtcblxuICAgIGlmICghcHJldiAmJiBncmFuZFBhcmVudCkge1xuICAgICAgY29uc3QgbmV3UGFyZW50ID0gZ3JhbmRQYXJlbnQuZ2V0UHJldlNpYmxpbmdPZihwYXJlbnQpO1xuXG4gICAgICBpZiAobmV3UGFyZW50KSB7XG4gICAgICAgIHRoaXMudXBkYXRlZCA9IHRydWU7XG4gICAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChsaXN0KTtcbiAgICAgICAgbmV3UGFyZW50LmFkZEFmdGVyQWxsKGxpc3QpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAocHJldikge1xuICAgICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcbiAgICAgIHBhcmVudC5yZW1vdmVDaGlsZChsaXN0KTtcbiAgICAgIHBhcmVudC5hZGRCZWZvcmUocHJldiwgbGlzdCk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLnVwZGF0ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBsaXN0U3RhcnRMaW5lQWZ0ZXIgPSByb290LmdldENvbnRlbnRMaW5lc1JhbmdlT2YobGlzdClbMF07XG4gICAgY29uc3QgbGluZURpZmYgPSBsaXN0U3RhcnRMaW5lQWZ0ZXIgLSBsaXN0U3RhcnRMaW5lQmVmb3JlO1xuXG4gICAgY29uc3QgY3Vyc29yID0gcm9vdC5nZXRDdXJzb3IoKTtcbiAgICByb290LnJlcGxhY2VDdXJzb3Ioe1xuICAgICAgbGluZTogY3Vyc29yLmxpbmUgKyBsaW5lRGlmZixcbiAgICAgIGNoOiBjdXJzb3IuY2gsXG4gICAgfSk7XG5cbiAgICByZWNhbGN1bGF0ZU51bWVyaWNCdWxsZXRzKHJvb3QpO1xuICB9XG59XG4iLCJpbXBvcnQgeyBQbHVnaW5fMiB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHsgTGlzdHNTZXJ2aWNlIH0gZnJvbSBcIi4uL3NlcnZpY2VzL0xpc3RzU2VydmljZVwiO1xuaW1wb3J0IHsgTW92ZUxlZnRPcGVyYXRpb24gfSBmcm9tIFwiLi4vb3BlcmF0aW9ucy9Nb3ZlTGVmdE9wZXJhdGlvblwiO1xuaW1wb3J0IHsgT2JzaWRpYW5TZXJ2aWNlIH0gZnJvbSBcIi4uL3NlcnZpY2VzL09ic2lkaWFuU2VydmljZVwiO1xuaW1wb3J0IHsgSUZlYXR1cmUgfSBmcm9tIFwiLi9JRmVhdHVyZVwiO1xuaW1wb3J0IHsgTW92ZVJpZ2h0T3BlcmF0aW9uIH0gZnJvbSBcIi4uL29wZXJhdGlvbnMvTW92ZVJpZ2h0T3BlcmF0aW9uXCI7XG5pbXBvcnQgeyBNb3ZlRG93bk9wZXJhdGlvbiB9IGZyb20gXCIuLi9vcGVyYXRpb25zL01vdmVEb3duT3BlcmF0aW9uXCI7XG5pbXBvcnQgeyBNb3ZlVXBPcGVyYXRpb24gfSBmcm9tIFwiLi4vb3BlcmF0aW9ucy9Nb3ZlVXBPcGVyYXRpb25cIjtcbmltcG9ydCB7IElNRVNlcnZpY2UgfSBmcm9tIFwic3JjL3NlcnZpY2VzL0lNRVNlcnZpY2VcIjtcblxuZXhwb3J0IGNsYXNzIE1vdmVJdGVtc0ZlYXR1cmUgaW1wbGVtZW50cyBJRmVhdHVyZSB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcGx1Z2luOiBQbHVnaW5fMixcbiAgICBwcml2YXRlIG9ic2lkaWFuU2VydmljZTogT2JzaWRpYW5TZXJ2aWNlLFxuICAgIHByaXZhdGUgbGlzdHNTZXJ2aWNlOiBMaXN0c1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBpbWVTZXJ2aWNlOiBJTUVTZXJ2aWNlXG4gICkge31cblxuICBhc3luYyBsb2FkKCkge1xuICAgIHRoaXMucGx1Z2luLmFkZENvbW1hbmQoe1xuICAgICAgaWQ6IFwibW92ZS1saXN0LWl0ZW0tdXBcIixcbiAgICAgIG5hbWU6IFwiTW92ZSBsaXN0IGFuZCBzdWJsaXN0cyB1cFwiLFxuICAgICAgY2FsbGJhY2s6IHRoaXMub2JzaWRpYW5TZXJ2aWNlLmNyZWF0ZUNvbW1hbmRDYWxsYmFjayhcbiAgICAgICAgdGhpcy5tb3ZlTGlzdEVsZW1lbnRVcC5iaW5kKHRoaXMpXG4gICAgICApLFxuICAgICAgaG90a2V5czogW1xuICAgICAgICB7XG4gICAgICAgICAgbW9kaWZpZXJzOiBbXCJNb2RcIiwgXCJTaGlmdFwiXSxcbiAgICAgICAgICBrZXk6IFwiQXJyb3dVcFwiLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIHRoaXMucGx1Z2luLmFkZENvbW1hbmQoe1xuICAgICAgaWQ6IFwibW92ZS1saXN0LWl0ZW0tZG93blwiLFxuICAgICAgbmFtZTogXCJNb3ZlIGxpc3QgYW5kIHN1Ymxpc3RzIGRvd25cIixcbiAgICAgIGNhbGxiYWNrOiB0aGlzLm9ic2lkaWFuU2VydmljZS5jcmVhdGVDb21tYW5kQ2FsbGJhY2soXG4gICAgICAgIHRoaXMubW92ZUxpc3RFbGVtZW50RG93bi5iaW5kKHRoaXMpXG4gICAgICApLFxuICAgICAgaG90a2V5czogW1xuICAgICAgICB7XG4gICAgICAgICAgbW9kaWZpZXJzOiBbXCJNb2RcIiwgXCJTaGlmdFwiXSxcbiAgICAgICAgICBrZXk6IFwiQXJyb3dEb3duXCIsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuXG4gICAgdGhpcy5wbHVnaW4uYWRkQ29tbWFuZCh7XG4gICAgICBpZDogXCJpbmRlbnQtbGlzdFwiLFxuICAgICAgbmFtZTogXCJJbmRlbnQgdGhlIGxpc3QgYW5kIHN1Ymxpc3RzXCIsXG4gICAgICBjYWxsYmFjazogdGhpcy5vYnNpZGlhblNlcnZpY2UuY3JlYXRlQ29tbWFuZENhbGxiYWNrKFxuICAgICAgICB0aGlzLm1vdmVMaXN0RWxlbWVudFJpZ2h0LmJpbmQodGhpcylcbiAgICAgICksXG4gICAgICBob3RrZXlzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBtb2RpZmllcnM6IFtdLFxuICAgICAgICAgIGtleTogXCJUYWJcIixcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICB0aGlzLnBsdWdpbi5hZGRDb21tYW5kKHtcbiAgICAgIGlkOiBcIm91dGRlbnQtbGlzdFwiLFxuICAgICAgbmFtZTogXCJPdXRkZW50IHRoZSBsaXN0IGFuZCBzdWJsaXN0c1wiLFxuICAgICAgY2FsbGJhY2s6IHRoaXMub2JzaWRpYW5TZXJ2aWNlLmNyZWF0ZUNvbW1hbmRDYWxsYmFjayhcbiAgICAgICAgdGhpcy5tb3ZlTGlzdEVsZW1lbnRMZWZ0LmJpbmQodGhpcylcbiAgICAgICksXG4gICAgICBob3RrZXlzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBtb2RpZmllcnM6IFtcIlNoaWZ0XCJdLFxuICAgICAgICAgIGtleTogXCJUYWJcIixcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG4gIH1cblxuICBhc3luYyB1bmxvYWQoKSB7fVxuXG4gIHByaXZhdGUgbW92ZUxpc3RFbGVtZW50RG93bihlZGl0b3I6IENvZGVNaXJyb3IuRWRpdG9yKSB7XG4gICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT4gbmV3IE1vdmVEb3duT3BlcmF0aW9uKHJvb3QpLFxuICAgICAgZWRpdG9yXG4gICAgKTtcbiAgICByZXR1cm4gc2hvdWxkU3RvcFByb3BhZ2F0aW9uO1xuICB9XG5cbiAgcHJpdmF0ZSBtb3ZlTGlzdEVsZW1lbnRVcChlZGl0b3I6IENvZGVNaXJyb3IuRWRpdG9yKSB7XG4gICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT4gbmV3IE1vdmVVcE9wZXJhdGlvbihyb290KSxcbiAgICAgIGVkaXRvclxuICAgICk7XG4gICAgcmV0dXJuIHNob3VsZFN0b3BQcm9wYWdhdGlvbjtcbiAgfVxuXG4gIHByaXZhdGUgbW92ZUxpc3RFbGVtZW50UmlnaHQoZWRpdG9yOiBDb2RlTWlycm9yLkVkaXRvcikge1xuICAgIGlmICh0aGlzLmltZVNlcnZpY2UuaXNJTUVPcGVuZWQoKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT5cbiAgICAgICAgbmV3IE1vdmVSaWdodE9wZXJhdGlvbihyb290LCB0aGlzLmxpc3RzU2VydmljZS5nZXREZWZhdWx0SW5kZW50Q2hhcnMoKSksXG4gICAgICBlZGl0b3JcbiAgICApO1xuICAgIHJldHVybiBzaG91bGRTdG9wUHJvcGFnYXRpb247XG4gIH1cblxuICBwcml2YXRlIG1vdmVMaXN0RWxlbWVudExlZnQoZWRpdG9yOiBDb2RlTWlycm9yLkVkaXRvcikge1xuICAgIGlmICh0aGlzLmltZVNlcnZpY2UuaXNJTUVPcGVuZWQoKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT4gbmV3IE1vdmVMZWZ0T3BlcmF0aW9uKHJvb3QpLFxuICAgICAgZWRpdG9yXG4gICAgKTtcbiAgICByZXR1cm4gc2hvdWxkU3RvcFByb3BhZ2F0aW9uO1xuICB9XG59XG4iLCJpbXBvcnQgeyBSb290IH0gZnJvbSBcIi4uL3Jvb3RcIjtcbmltcG9ydCB7IElPcGVyYXRpb24gfSBmcm9tIFwiLi9JT3BlcmF0aW9uXCI7XG5cbmV4cG9ydCBjbGFzcyBDcmVhdGVOb3RlTGluZU9wZXJhdGlvbiBpbXBsZW1lbnRzIElPcGVyYXRpb24ge1xuICBwcml2YXRlIHN0b3BQcm9wYWdhdGlvbiA9IGZhbHNlO1xuICBwcml2YXRlIHVwZGF0ZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvb3Q6IFJvb3QsIHByaXZhdGUgZGVmYXVsdEluZGVudENoYXJzOiBzdHJpbmcpIHt9XG5cbiAgc2hvdWxkU3RvcFByb3BhZ2F0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3BQcm9wYWdhdGlvbjtcbiAgfVxuXG4gIHNob3VsZFVwZGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy51cGRhdGVkO1xuICB9XG5cbiAgcGVyZm9ybSgpIHtcbiAgICBjb25zdCB7IHJvb3QgfSA9IHRoaXM7XG5cbiAgICBpZiAoIXJvb3QuaGFzU2luZ2xlQ3Vyc29yKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBjdXJzb3IgPSByb290LmdldEN1cnNvcigpO1xuICAgIGNvbnN0IGxpc3QgPSByb290LmdldExpc3RVbmRlckN1cnNvcigpO1xuICAgIGNvbnN0IGxpbmVVbmRlckN1cnNvciA9IGxpc3RcbiAgICAgIC5nZXRMaW5lc0luZm8oKVxuICAgICAgLmZpbmQoKGwpID0+IGwuZnJvbS5saW5lID09PSBjdXJzb3IubGluZSk7XG5cbiAgICBpZiAoY3Vyc29yLmNoIDwgbGluZVVuZGVyQ3Vyc29yLmZyb20uY2gpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnN0b3BQcm9wYWdhdGlvbiA9IHRydWU7XG4gICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcblxuICAgIGlmICghbGlzdC5nZXROb3Rlc0luZGVudCgpKSB7XG4gICAgICBjb25zdCBpbmRlbnQgPSBsaXN0LmlzRW1wdHkoKVxuICAgICAgICA/IGxpc3QuZ2V0Rmlyc3RMaW5lSW5kZW50KCkgKyB0aGlzLmRlZmF1bHRJbmRlbnRDaGFyc1xuICAgICAgICA6IGxpc3QuZ2V0Q2hpbGRyZW4oKVswXS5nZXRGaXJzdExpbmVJbmRlbnQoKTtcblxuICAgICAgbGlzdC5zZXROb3Rlc0luZGVudChpbmRlbnQpO1xuICAgIH1cblxuICAgIGNvbnN0IGxpbmVzID0gbGlzdC5nZXRMaW5lc0luZm8oKS5yZWR1Y2UoKGFjYywgbGluZSkgPT4ge1xuICAgICAgaWYgKGN1cnNvci5saW5lID09PSBsaW5lLmZyb20ubGluZSkge1xuICAgICAgICBhY2MucHVzaChsaW5lLnRleHQuc2xpY2UoMCwgY3Vyc29yLmNoIC0gbGluZS5mcm9tLmNoKSk7XG4gICAgICAgIGFjYy5wdXNoKGxpbmUudGV4dC5zbGljZShjdXJzb3IuY2ggLSBsaW5lLmZyb20uY2gpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGFjYy5wdXNoKGxpbmUudGV4dCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBhY2M7XG4gICAgfSwgW10gYXMgc3RyaW5nW10pO1xuXG4gICAgbGlzdC5yZXBsYWNlTGluZXMobGluZXMpO1xuXG4gICAgcm9vdC5yZXBsYWNlQ3Vyc29yKHtcbiAgICAgIGxpbmU6IGN1cnNvci5saW5lICsgMSxcbiAgICAgIGNoOiBsaXN0LmdldE5vdGVzSW5kZW50KCkubGVuZ3RoLFxuICAgIH0pO1xuICB9XG59XG4iLCJpbXBvcnQgeyBQbHVnaW5fMiB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHsgQ3JlYXRlTm90ZUxpbmVPcGVyYXRpb24gfSBmcm9tIFwiLi4vb3BlcmF0aW9ucy9DcmVhdGVOb3RlTGluZU9wZXJhdGlvblwiO1xuaW1wb3J0IHsgSUZlYXR1cmUgfSBmcm9tIFwiLi9JRmVhdHVyZVwiO1xuaW1wb3J0IHsgTGlzdHNTZXJ2aWNlIH0gZnJvbSBcIi4uL3NlcnZpY2VzL0xpc3RzU2VydmljZVwiO1xuaW1wb3J0IHsgU2V0dGluZ3NTZXJ2aWNlIH0gZnJvbSBcIi4uL3NlcnZpY2VzL1NldHRpbmdzU2VydmljZVwiO1xuaW1wb3J0IHsgSU1FU2VydmljZSB9IGZyb20gXCJzcmMvc2VydmljZXMvSU1FU2VydmljZVwiO1xuXG5mdW5jdGlvbiBpc1NoaWZ0RW50ZXIoZTogS2V5Ym9hcmRFdmVudCkge1xuICByZXR1cm4gKFxuICAgIChlLmtleUNvZGUgPT09IDEzIHx8IGUuY29kZSA9PT0gXCJFbnRlclwiKSAmJlxuICAgIGUuc2hpZnRLZXkgPT09IHRydWUgJiZcbiAgICBlLm1ldGFLZXkgPT09IGZhbHNlICYmXG4gICAgZS5hbHRLZXkgPT09IGZhbHNlICYmXG4gICAgZS5jdHJsS2V5ID09PSBmYWxzZVxuICApO1xufVxuXG5leHBvcnQgY2xhc3MgU2hpZnRFbnRlclNob3VsZENyZWF0ZU5vdGVGZWF0dXJlIGltcGxlbWVudHMgSUZlYXR1cmUge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHBsdWdpbjogUGx1Z2luXzIsXG4gICAgcHJpdmF0ZSBzZXR0aW5nc1NlcnZpY2U6IFNldHRpbmdzU2VydmljZSxcbiAgICBwcml2YXRlIGxpc3RzU2VydmljZTogTGlzdHNTZXJ2aWNlLFxuICAgIHByaXZhdGUgaW1lU2VydmljZTogSU1FU2VydmljZVxuICApIHt9XG5cbiAgYXN5bmMgbG9hZCgpIHtcbiAgICB0aGlzLnBsdWdpbi5yZWdpc3RlckNvZGVNaXJyb3IoKGNtKSA9PiB7XG4gICAgICBjbS5vbihcImtleWRvd25cIiwgdGhpcy5vbktleURvd24pO1xuICAgIH0pO1xuICB9XG5cbiAgYXN5bmMgdW5sb2FkKCkge1xuICAgIHRoaXMucGx1Z2luLmFwcC53b3Jrc3BhY2UuaXRlcmF0ZUNvZGVNaXJyb3JzKChjbSkgPT4ge1xuICAgICAgY20ub2ZmKFwia2V5ZG93blwiLCB0aGlzLm9uS2V5RG93bik7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIG9uS2V5RG93biA9IChjbTogQ29kZU1pcnJvci5FZGl0b3IsIGU6IEtleWJvYXJkRXZlbnQpID0+IHtcbiAgICBpZiAoXG4gICAgICAhdGhpcy5zZXR0aW5nc1NlcnZpY2UuYmV0dGVyRW50ZXIgfHxcbiAgICAgICFpc1NoaWZ0RW50ZXIoZSkgfHxcbiAgICAgIHRoaXMuaW1lU2VydmljZS5pc0lNRU9wZW5lZCgpXG4gICAgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgeyBzaG91bGRTdG9wUHJvcGFnYXRpb24gfSA9IHRoaXMubGlzdHNTZXJ2aWNlLnBlcmZvcm1PcGVyYXRpb24oXG4gICAgICAocm9vdCkgPT5cbiAgICAgICAgbmV3IENyZWF0ZU5vdGVMaW5lT3BlcmF0aW9uKFxuICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgdGhpcy5saXN0c1NlcnZpY2UuZ2V0RGVmYXVsdEluZGVudENoYXJzKClcbiAgICAgICAgKSxcbiAgICAgIGNtXG4gICAgKTtcblxuICAgIGlmIChzaG91bGRTdG9wUHJvcGFnYXRpb24pIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgfVxuICB9O1xufVxuIiwiZXhwb3J0IGNsYXNzIElNRVNlcnZpY2Uge1xuICBwcml2YXRlIGNvbXBvc2l0aW9uID0gZmFsc2U7XG5cbiAgYXN5bmMgbG9hZCgpIHtcbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKFwiY29tcG9zaXRpb25zdGFydFwiLCB0aGlzLm9uQ29tcG9zaXRpb25TdGFydCk7XG4gICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcImNvbXBvc2l0aW9uZW5kXCIsIHRoaXMub25Db21wb3NpdGlvbkVuZCk7XG4gIH1cblxuICBhc3luYyB1bmxvYWQoKSB7XG4gICAgZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImNvbXBvc2l0aW9uZW5kXCIsIHRoaXMub25Db21wb3NpdGlvbkVuZCk7XG4gICAgZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImNvbXBvc2l0aW9uc3RhcnRcIiwgdGhpcy5vbkNvbXBvc2l0aW9uU3RhcnQpO1xuICB9XG5cbiAgaXNJTUVPcGVuZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuY29tcG9zaXRpb247XG4gIH1cblxuICBwcml2YXRlIG9uQ29tcG9zaXRpb25TdGFydCA9ICgpID0+IHtcbiAgICB0aGlzLmNvbXBvc2l0aW9uID0gdHJ1ZTtcbiAgfTtcblxuICBwcml2YXRlIG9uQ29tcG9zaXRpb25FbmQgPSAoKSA9PiB7XG4gICAgdGhpcy5jb21wb3NpdGlvbiA9IGZhbHNlO1xuICB9O1xufVxuIiwiaW1wb3J0IHsgUGx1Z2luIH0gZnJvbSBcIm9ic2lkaWFuXCI7XHJcbmltcG9ydCB7XHJcbiAgT2JzaWRpYW5PdXRsaW5lclBsdWdpblNldHRpbmdUYWIsXHJcbiAgU2V0dGluZ3NTZXJ2aWNlLFxyXG59IGZyb20gXCIuL3NlcnZpY2VzL1NldHRpbmdzU2VydmljZVwiO1xyXG5pbXBvcnQgeyBJRmVhdHVyZSB9IGZyb20gXCIuL2ZlYXR1cmVzL0lGZWF0dXJlXCI7XHJcbmltcG9ydCB7IE9ic2lkaWFuU2VydmljZSB9IGZyb20gXCIuL3NlcnZpY2VzL09ic2lkaWFuU2VydmljZVwiO1xyXG5pbXBvcnQgeyBMaXN0c1NlcnZpY2UgfSBmcm9tIFwiLi9zZXJ2aWNlcy9MaXN0c1NlcnZpY2VcIjtcclxuaW1wb3J0IHsgTG9nZ2VyU2VydmljZSB9IGZyb20gXCIuL3NlcnZpY2VzL0xvZ2dlclNlcnZpY2VcIjtcclxuaW1wb3J0IHsgTGlzdHNTdHlsZXNGZWF0dXJlIH0gZnJvbSBcIi4vZmVhdHVyZXMvTGlzdHNTdHlsZXNGZWF0dXJlXCI7XHJcbmltcG9ydCB7IEVudGVyT3V0ZGVudElmTGluZUlzRW1wdHlGZWF0dXJlIH0gZnJvbSBcIi4vZmVhdHVyZXMvRW50ZXJPdXRkZW50SWZMaW5lSXNFbXB0eUZlYXR1cmVcIjtcclxuaW1wb3J0IHsgRW50ZXJTaG91bGRDcmVhdGVOZXdJdGVtRmVhdHVyZSB9IGZyb20gXCIuL2ZlYXR1cmVzL0VudGVyU2hvdWxkQ3JlYXRlTmV3SXRlbU9uQ2hpbGRMZXZlbEZlYXR1cmVcIjtcclxuaW1wb3J0IHsgTW92ZUN1cnNvclRvUHJldmlvdXNVbmZvbGRlZExpbmVGZWF0dXJlIH0gZnJvbSBcIi4vZmVhdHVyZXMvTW92ZUN1cnNvclRvUHJldmlvdXNVbmZvbGRlZExpbmVGZWF0dXJlXCI7XHJcbmltcG9ydCB7IEVuc3VyZUN1cnNvckluTGlzdENvbnRlbnRGZWF0dXJlIH0gZnJvbSBcIi4vZmVhdHVyZXMvRW5zdXJlQ3Vyc29ySW5MaXN0Q29udGVudEZlYXR1cmVcIjtcclxuaW1wb3J0IHsgRGVsZXRlU2hvdWxkSWdub3JlQnVsbGV0c0ZlYXR1cmUgfSBmcm9tIFwiLi9mZWF0dXJlcy9EZWxldGVTaG91bGRJZ25vcmVCdWxsZXRzRmVhdHVyZVwiO1xyXG5pbXBvcnQgeyBTZWxlY3Rpb25TaG91bGRJZ25vcmVCdWxsZXRzRmVhdHVyZSB9IGZyb20gXCIuL2ZlYXR1cmVzL1NlbGVjdGlvblNob3VsZElnbm9yZUJ1bGxldHNGZWF0dXJlXCI7XHJcbmltcG9ydCB7IFpvb21GZWF0dXJlIH0gZnJvbSBcIi4vZmVhdHVyZXMvWm9vbUZlYXR1cmVcIjtcclxuaW1wb3J0IHsgRm9sZEZlYXR1cmUgfSBmcm9tIFwiLi9mZWF0dXJlcy9Gb2xkRmVhdHVyZVwiO1xyXG5pbXBvcnQgeyBTZWxlY3RBbGxGZWF0dXJlIH0gZnJvbSBcIi4vZmVhdHVyZXMvU2VsZWN0QWxsRmVhdHVyZVwiO1xyXG5pbXBvcnQgeyBNb3ZlSXRlbXNGZWF0dXJlIH0gZnJvbSBcIi4vZmVhdHVyZXMvTW92ZUl0ZW1zRmVhdHVyZVwiO1xyXG5pbXBvcnQgeyBTaGlmdEVudGVyU2hvdWxkQ3JlYXRlTm90ZUZlYXR1cmUgfSBmcm9tIFwiLi9mZWF0dXJlcy9TaGlmdEVudGVyU2hvdWxkQ3JlYXRlTm90ZUZlYXR1cmVcIjtcclxuaW1wb3J0IHsgSU1FU2VydmljZSB9IGZyb20gXCIuL3NlcnZpY2VzL0lNRVNlcnZpY2VcIjtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE9ic2lkaWFuT3V0bGluZXJQbHVnaW4gZXh0ZW5kcyBQbHVnaW4ge1xyXG4gIHByaXZhdGUgZmVhdHVyZXM6IElGZWF0dXJlW107XHJcbiAgcHJvdGVjdGVkIHNldHRpbmdzU2VydmljZTogU2V0dGluZ3NTZXJ2aWNlO1xyXG4gIHByaXZhdGUgbG9nZ2VyU2VydmljZTogTG9nZ2VyU2VydmljZTtcclxuICBwcml2YXRlIG9ic2lkaWFuU2VydmljZTogT2JzaWRpYW5TZXJ2aWNlO1xyXG4gIHByaXZhdGUgbGlzdHNTZXJ2aWNlOiBMaXN0c1NlcnZpY2U7XHJcbiAgcHJpdmF0ZSBpbWVTZXJ2aWNlOiBJTUVTZXJ2aWNlO1xyXG5cclxuICBhc3luYyBvbmxvYWQoKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgTG9hZGluZyBvYnNpZGlhbi1vdXRsaW5lcmApO1xyXG5cclxuICAgIHRoaXMuc2V0dGluZ3NTZXJ2aWNlID0gbmV3IFNldHRpbmdzU2VydmljZSh0aGlzKTtcclxuICAgIGF3YWl0IHRoaXMuc2V0dGluZ3NTZXJ2aWNlLmxvYWQoKTtcclxuXHJcbiAgICB0aGlzLmxvZ2dlclNlcnZpY2UgPSBuZXcgTG9nZ2VyU2VydmljZSh0aGlzLnNldHRpbmdzU2VydmljZSk7XHJcblxyXG4gICAgdGhpcy5vYnNpZGlhblNlcnZpY2UgPSBuZXcgT2JzaWRpYW5TZXJ2aWNlKHRoaXMuYXBwKTtcclxuICAgIHRoaXMubGlzdHNTZXJ2aWNlID0gbmV3IExpc3RzU2VydmljZShcclxuICAgICAgdGhpcy5sb2dnZXJTZXJ2aWNlLFxyXG4gICAgICB0aGlzLm9ic2lkaWFuU2VydmljZVxyXG4gICAgKTtcclxuXHJcbiAgICB0aGlzLmltZVNlcnZpY2UgPSBuZXcgSU1FU2VydmljZSgpO1xyXG4gICAgYXdhaXQgdGhpcy5pbWVTZXJ2aWNlLmxvYWQoKTtcclxuXHJcbiAgICB0aGlzLmFkZFNldHRpbmdUYWIoXHJcbiAgICAgIG5ldyBPYnNpZGlhbk91dGxpbmVyUGx1Z2luU2V0dGluZ1RhYih0aGlzLmFwcCwgdGhpcywgdGhpcy5zZXR0aW5nc1NlcnZpY2UpXHJcbiAgICApO1xyXG5cclxuICAgIHRoaXMuZmVhdHVyZXMgPSBbXHJcbiAgICAgIG5ldyBMaXN0c1N0eWxlc0ZlYXR1cmUodGhpcywgdGhpcy5zZXR0aW5nc1NlcnZpY2UsIHRoaXMub2JzaWRpYW5TZXJ2aWNlKSxcclxuICAgICAgbmV3IEVudGVyT3V0ZGVudElmTGluZUlzRW1wdHlGZWF0dXJlKFxyXG4gICAgICAgIHRoaXMsXHJcbiAgICAgICAgdGhpcy5zZXR0aW5nc1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5saXN0c1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5pbWVTZXJ2aWNlXHJcbiAgICAgICksXHJcbiAgICAgIG5ldyBFbnRlclNob3VsZENyZWF0ZU5ld0l0ZW1GZWF0dXJlKFxyXG4gICAgICAgIHRoaXMsXHJcbiAgICAgICAgdGhpcy5zZXR0aW5nc1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5saXN0c1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5pbWVTZXJ2aWNlXHJcbiAgICAgICksXHJcbiAgICAgIG5ldyBFbnN1cmVDdXJzb3JJbkxpc3RDb250ZW50RmVhdHVyZShcclxuICAgICAgICB0aGlzLFxyXG4gICAgICAgIHRoaXMuc2V0dGluZ3NTZXJ2aWNlLFxyXG4gICAgICAgIHRoaXMubGlzdHNTZXJ2aWNlXHJcbiAgICAgICksXHJcbiAgICAgIG5ldyBNb3ZlQ3Vyc29yVG9QcmV2aW91c1VuZm9sZGVkTGluZUZlYXR1cmUoXHJcbiAgICAgICAgdGhpcyxcclxuICAgICAgICB0aGlzLnNldHRpbmdzU2VydmljZSxcclxuICAgICAgICB0aGlzLmxpc3RzU2VydmljZSxcclxuICAgICAgICB0aGlzLmltZVNlcnZpY2VcclxuICAgICAgKSxcclxuICAgICAgbmV3IERlbGV0ZVNob3VsZElnbm9yZUJ1bGxldHNGZWF0dXJlKFxyXG4gICAgICAgIHRoaXMsXHJcbiAgICAgICAgdGhpcy5zZXR0aW5nc1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5saXN0c1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5pbWVTZXJ2aWNlXHJcbiAgICAgICksXHJcbiAgICAgIG5ldyBTZWxlY3Rpb25TaG91bGRJZ25vcmVCdWxsZXRzRmVhdHVyZShcclxuICAgICAgICB0aGlzLFxyXG4gICAgICAgIHRoaXMuc2V0dGluZ3NTZXJ2aWNlLFxyXG4gICAgICAgIHRoaXMubGlzdHNTZXJ2aWNlLFxyXG4gICAgICAgIHRoaXMuaW1lU2VydmljZVxyXG4gICAgICApLFxyXG4gICAgICBuZXcgWm9vbUZlYXR1cmUodGhpcywgdGhpcy5zZXR0aW5nc1NlcnZpY2UsIHRoaXMuaW1lU2VydmljZSksXHJcbiAgICAgIG5ldyBGb2xkRmVhdHVyZSh0aGlzLCB0aGlzLm9ic2lkaWFuU2VydmljZSksXHJcbiAgICAgIG5ldyBTZWxlY3RBbGxGZWF0dXJlKFxyXG4gICAgICAgIHRoaXMsXHJcbiAgICAgICAgdGhpcy5zZXR0aW5nc1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5saXN0c1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5pbWVTZXJ2aWNlXHJcbiAgICAgICksXHJcbiAgICAgIG5ldyBNb3ZlSXRlbXNGZWF0dXJlKFxyXG4gICAgICAgIHRoaXMsXHJcbiAgICAgICAgdGhpcy5vYnNpZGlhblNlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5saXN0c1NlcnZpY2UsXHJcbiAgICAgICAgdGhpcy5pbWVTZXJ2aWNlXHJcbiAgICAgICksXHJcbiAgICAgIG5ldyBTaGlmdEVudGVyU2hvdWxkQ3JlYXRlTm90ZUZlYXR1cmUoXHJcbiAgICAgICAgdGhpcyxcclxuICAgICAgICB0aGlzLnNldHRpbmdzU2VydmljZSxcclxuICAgICAgICB0aGlzLmxpc3RzU2VydmljZSxcclxuICAgICAgICB0aGlzLmltZVNlcnZpY2VcclxuICAgICAgKSxcclxuICAgIF07XHJcblxyXG4gICAgZm9yIChjb25zdCBmZWF0dXJlIG9mIHRoaXMuZmVhdHVyZXMpIHtcclxuICAgICAgYXdhaXQgZmVhdHVyZS5sb2FkKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBhc3luYyBvbnVubG9hZCgpIHtcclxuICAgIGNvbnNvbGUubG9nKGBVbmxvYWRpbmcgb2JzaWRpYW4tb3V0bGluZXJgKTtcclxuXHJcbiAgICBhd2FpdCB0aGlzLmltZVNlcnZpY2UudW5sb2FkKCk7XHJcblxyXG4gICAgZm9yIChjb25zdCBmZWF0dXJlIG9mIHRoaXMuZmVhdHVyZXMpIHtcclxuICAgICAgYXdhaXQgZmVhdHVyZS51bmxvYWQoKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuIl0sIm5hbWVzIjpbIlBsdWdpblNldHRpbmdUYWIiLCJTZXR0aW5nIiwiTWFya2Rvd25WaWV3IiwiaXNFbnRlciIsIlBsYXRmb3JtIiwiTm90aWNlIiwi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 ================================================ FILE: .obsidian/plugins/obsidian-outliner/manifest.json ================================================ { "id": "obsidian-outliner", "name": "Outliner", "version": "1.4.1", "minAppVersion": "0.12.19", "description": "Work with your lists like in Workflowy or RoamResearch.", "author": "Viacheslav Slinko", "authorUrl": "https://github.com/vslinko", "isDesktopOnly": true } ================================================ FILE: .obsidian/plugins/obsidian-outliner/styles.css ================================================ /* lists */ .outliner-plugin-bls .cm-s-obsidian .HyperMD-list-line { padding-top: 0.4em; } /* bullets */ .outliner-plugin-bls .cm-s-obsidian span.cm-formatting-list { letter-spacing: 3px; } .outliner-plugin-bls .cm-s-obsidian span.cm-formatting-list-ul { color: var(--background-primary) !important; } .outliner-plugin-bls .cm-s-obsidian span.cm-formatting-list-ul:before { content: "•"; position: absolute; margin-left: -3px; margin-top: -5px; font-size: 24px; color: var(--text-muted); visibility: visible !important; } /* lines */ .outliner-plugin-bls .cm-s-obsidian .HyperMD-list-line { overflow: hidden; } .cm-hmd-list-indent { position: relative; } .outliner-plugin-bls .CodeMirror-line .cm-hmd-list-indent::before { content: ''; display: block; position: absolute; background-image: linear-gradient(to right, var(--text-faint) 1px, transparent 1px); /* https://developer.mozilla.org/en-US/docs/Web/CSS/length#units */ background-size: 1.8ch 1px; background-position-x: 3px; width: 100%; left: 0; top: -1.6em; padding-bottom: 999em; } ================================================ FILE: .obsidian/plugins/recent-files-obsidian/data.json ================================================ { "recentFiles": [ { "basename": "README", "path": "README.md" }, { "basename": "index", "path": "1. cs/《Problem solving with algorithms and Data Structures Using Python》/线性数据结构/index.md" }, { "basename": "index", "path": "2. coding/index.md" }, { "basename": "内推模板", "path": "temp/内推模板.md" }, { "basename": "DOM检查 ?react 解析器?开源工具?", "path": "DOM检查 ?react 解析器?开源工具?.md" }, { "basename": "leetcode 拷贝题目模板", "path": "temp/leetcode 拷贝题目模板.md" }, { "basename": "index", "path": "1. cs/《Problem solving with algorithms and Data Structures Using Python》/index.md" }, { "basename": "操作系统层面的调用栈管理?", "path": "操作系统层面的调用栈管理?.md" }, { "basename": "栈", "path": "1. cs/《Problem solving with algorithms and Data Structures Using Python》/线性数据结构/栈.md" }, { "basename": "何为数据结构与算法?", "path": "1. cs/《Problem solving with algorithms and Data Structures Using Python》/何为数据结构与算法?.md" }, { "basename": "浏览器历史管理", "path": "浏览器历史管理.md" }, { "basename": "Pasted image 20220102214927", "path": "static/Pasted image 20220102214927.png" }, { "basename": "51. N 皇后", "path": "2. coding/51. N 皇后.md" }, { "basename": "index", "path": "3. sd/FE/index.md" }, { "basename": "198.打家劫舍", "path": "2. coding/2-training/198.打家劫舍.md" }, { "basename": "283. 移动零", "path": "2. coding/1-beginner/1. sorting/283. 移动零.md" }, { "basename": "index", "path": "2. coding/2-training/index.md" }, { "basename": "2109. 向字符串添加空格", "path": "2. coding/2-training/2109. 向字符串添加空格.md" }, { "basename": "72. 编辑距离", "path": "2. coding/1-beginner/dp/72. 编辑距离.md" }, { "basename": "1014.最佳观光组合", "path": "2. coding/2-training/1014.最佳观光组合.md" }, { "basename": "221. 最大正方形", "path": "2. coding/1-beginner/dp/221. 最大正方形.md" }, { "basename": "59. 螺旋矩阵 II", "path": "2. coding/59. 螺旋矩阵 II.md" }, { "basename": "59. 螺旋矩阵 II", "path": "coding/beginner/recursion/59. 螺旋矩阵 II.md" }, { "basename": "25. K 个一组翻转链表", "path": "coding/beginner/recursion/25. K 个一组翻转链表.md" }, { "basename": "51. N 皇后", "path": "coding/beginner/recursion/51. N 皇后.md" }, { "basename": "509. 斐波那契数", "path": "coding/beginner/recursion/509. 斐波那契数.md" }, { "basename": "chapter 6 - the memory hierarchy", "path": "1. cs/《CSAPP》/chapter 6 - the memory hierarchy.md" }, { "basename": "大部头读书笔记", "path": "temp/大部头读书笔记.md" }, { "basename": "存储体系", "path": "1. cs/存储体系.md" } ], "omittedPaths": [], "maxLength": null } ================================================ FILE: .obsidian/plugins/recent-files-obsidian/main.js ================================================ 'use strict'; var obsidian = require('obsidian'); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var defaultMaxLength = 50; var DEFAULT_DATA = { recentFiles: [], omittedPaths: [], maxLength: null, }; var RecentFilesListViewType = 'recent-files'; var RecentFilesListView = /** @class */ (function (_super) { __extends(RecentFilesListView, _super); function RecentFilesListView(leaf, plugin, data) { var _this = _super.call(this, leaf) || this; _this.redraw = function () { var openFile = _this.app.workspace.getActiveFile(); var rootEl = createDiv({ cls: 'nav-folder mod-root' }); var childrenEl = rootEl.createDiv({ cls: 'nav-folder-children' }); _this.data.recentFiles.forEach(function (currentFile) { var navFile = childrenEl.createDiv({ cls: 'nav-file' }); var navFileTitle = navFile.createDiv({ cls: 'nav-file-title' }); if (openFile && currentFile.path === openFile.path) { navFileTitle.addClass('is-active'); } navFileTitle.createDiv({ cls: 'nav-file-title-content', text: currentFile.basename, }); navFile.setAttr('draggable', 'true'); navFile.addEventListener('dragstart', function (event) { var file = _this.app.metadataCache.getFirstLinkpathDest(currentFile.path, ''); // eslint-disable-next-line @typescript-eslint/no-explicit-any var dragManager = _this.app.dragManager; var dragData = dragManager.dragFile(event, file); dragManager.onDragStart(event, dragData); }); navFile.addEventListener('mouseover', function (event) { _this.app.workspace.trigger('hover-link', { event: event, source: RecentFilesListViewType, hoverParent: rootEl, targetEl: navFile, linktext: currentFile.path, }); }); navFile.addEventListener('contextmenu', function (event) { var menu = new obsidian.Menu(_this.app); var file = _this.app.vault.getAbstractFileByPath(currentFile.path); _this.app.workspace.trigger('file-menu', menu, file, 'link-context-menu', _this.leaf); menu.showAtPosition({ x: event.clientX, y: event.clientY }); }); navFile.addEventListener('click', function (event) { _this.focusFile(currentFile, event.ctrlKey || event.metaKey); }); }); var contentEl = _this.containerEl.children[1]; contentEl.empty(); contentEl.appendChild(rootEl); }; _this.updateData = function (file) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: this.data.recentFiles = this.data.recentFiles.filter(function (currFile) { return currFile.path !== file.path; }); this.data.recentFiles.unshift({ basename: file.basename, path: file.path, }); return [4 /*yield*/, this.plugin.pruneLength()]; case 1: _a.sent(); // Handles the save return [2 /*return*/]; } }); }); }; _this.update = function (openedFile) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!openedFile || !this.plugin.shouldAddFile(openedFile)) { return [2 /*return*/]; } return [4 /*yield*/, this.updateData(openedFile)]; case 1: _a.sent(); this.redraw(); return [2 /*return*/]; } }); }); }; /** * Open the provided file in the most recent leaf. * * @param shouldSplit Whether the file should be opened in a new split, or in * the most recent split. If the most recent split is pinned, this is set to * true. */ _this.focusFile = function (file, shouldSplit) { if (shouldSplit === void 0) { shouldSplit = false; } var targetFile = _this.app.vault .getFiles() .find(function (f) { return f.path === file.path; }); if (targetFile) { var leaf = _this.app.workspace.getMostRecentLeaf(); var createLeaf = shouldSplit || leaf.getViewState().pinned; if (createLeaf) { leaf = _this.app.workspace.createLeafBySplit(leaf); } leaf.openFile(targetFile); } else { new obsidian.Notice('Cannot find a file with that name'); _this.data.recentFiles = _this.data.recentFiles.filter(function (fp) { return fp.path !== file.path; }); _this.plugin.saveData(); _this.redraw(); } }; _this.plugin = plugin; _this.data = data; _this.redraw(); return _this; } RecentFilesListView.prototype.getViewType = function () { return RecentFilesListViewType; }; RecentFilesListView.prototype.getDisplayText = function () { return 'Recent Files'; }; RecentFilesListView.prototype.getIcon = function () { return 'clock'; }; RecentFilesListView.prototype.onHeaderMenu = function (menu) { var _this = this; menu .addItem(function (item) { item .setTitle('Clear list') .setIcon('sweep') .onClick(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: this.data.recentFiles = []; return [4 /*yield*/, this.plugin.saveData()]; case 1: _a.sent(); this.redraw(); return [2 /*return*/]; } }); }); }); }) .addItem(function (item) { item .setTitle('Close') .setIcon('cross') .onClick(function () { _this.app.workspace.detachLeavesOfType(RecentFilesListViewType); }); }); }; RecentFilesListView.prototype.load = function () { _super.prototype.load.call(this); this.registerEvent(this.app.workspace.on('file-open', this.update)); }; return RecentFilesListView; }(obsidian.ItemView)); var RecentFilesPlugin = /** @class */ (function (_super) { __extends(RecentFilesPlugin, _super); function RecentFilesPlugin() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.pruneOmittedFiles = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: this.data.recentFiles = this.data.recentFiles.filter(this.shouldAddFile); return [4 /*yield*/, this.saveData()]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; _this.pruneLength = function () { return __awaiter(_this, void 0, void 0, function () { var toRemove; return __generator(this, function (_a) { switch (_a.label) { case 0: toRemove = this.data.recentFiles.length - (this.data.maxLength || defaultMaxLength); if (toRemove > 0) { this.data.recentFiles.splice(this.data.recentFiles.length - toRemove, toRemove); } return [4 /*yield*/, this.saveData()]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; _this.shouldAddFile = function (file) { var patterns = _this.data.omittedPaths.filter(function (path) { return path.length > 0; }); var fileMatchesRegex = function (pattern) { try { return new RegExp(pattern).test(file.path); } catch (err) { console.error('Recent Files: Invalid regex pattern: ' + pattern); return false; } }; return !patterns.some(fileMatchesRegex); }; _this.initView = function () { return __awaiter(_this, void 0, void 0, function () { var leaf, _i, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: leaf = null; _i = 0, _a = this.app.workspace.getLeavesOfType(RecentFilesListViewType); _b.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 4]; leaf = _a[_i]; if (leaf.view instanceof RecentFilesListView) return [2 /*return*/]; // The view instance was created by an older version of the plugin, // so clear it and recreate it (so it'll be the new version). // This avoids the need to reload Obsidian to update the plugin. return [4 /*yield*/, leaf.setViewState({ type: 'empty' })]; case 2: // The view instance was created by an older version of the plugin, // so clear it and recreate it (so it'll be the new version). // This avoids the need to reload Obsidian to update the plugin. _b.sent(); return [3 /*break*/, 4]; case 3: _i++; return [3 /*break*/, 1]; case 4: (leaf !== null && leaf !== void 0 ? leaf : this.app.workspace.getLeftLeaf(false)).setViewState({ type: RecentFilesListViewType, active: true, }); return [2 /*return*/]; } }); }); }; _this.handleRename = function (file, oldPath) { return __awaiter(_this, void 0, void 0, function () { var entry; return __generator(this, function (_a) { switch (_a.label) { case 0: entry = this.data.recentFiles.find(function (recentFile) { return recentFile.path === oldPath; }); if (!entry) return [3 /*break*/, 2]; entry.path = file.path; entry.basename = this.trimExtension(file.name); this.view.redraw(); return [4 /*yield*/, this.saveData()]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/]; } }); }); }; _this.handleDelete = function (file) { return __awaiter(_this, void 0, void 0, function () { var beforeLen; return __generator(this, function (_a) { switch (_a.label) { case 0: beforeLen = this.data.recentFiles.length; this.data.recentFiles = this.data.recentFiles.filter(function (recentFile) { return recentFile.path !== file.path; }); if (!(beforeLen !== this.data.recentFiles.length)) return [3 /*break*/, 2]; this.view.redraw(); return [4 /*yield*/, this.saveData()]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/]; } }); }); }; // trimExtension can be used to turn a filename into a basename when // interacting with a TAbstractFile that does not have a basename property. // private readonly trimExtension = (name: string): string => name.split('.')[0]; // from: https://stackoverflow.com/a/4250408/617864 _this.trimExtension = function (name) { return name.replace(/\.[^/.]+$/, ''); }; return _this; } RecentFilesPlugin.prototype.onload = function () { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: console.log('Recent Files: Loading plugin v' + this.manifest.version); return [4 /*yield*/, this.loadData()]; case 1: _a.sent(); obsidian.addIcon('sweep', sweepIcon); this.registerView(RecentFilesListViewType, function (leaf) { return (_this.view = new RecentFilesListView(leaf, _this, _this.data)); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any this.app.workspace.registerHoverLinkSource(RecentFilesListViewType, { display: 'Recent Files', defaultMod: true, }); if (this.app.workspace.layoutReady) { this.initView(); } else { this.registerEvent(this.app.workspace.on('layout-ready', this.initView)); } this.registerEvent(this.app.vault.on('rename', this.handleRename)); this.registerEvent(this.app.vault.on('delete', this.handleDelete)); this.addSettingTab(new RecentFilesSettingTab(this.app, this)); return [2 /*return*/]; } }); }); }; RecentFilesPlugin.prototype.onunload = function () { // eslint-disable-next-line @typescript-eslint/no-explicit-any this.app.workspace.unregisterHoverLinkSource(RecentFilesListViewType); }; RecentFilesPlugin.prototype.loadData = function () { return __awaiter(this, void 0, void 0, function () { var _a, _b, _c, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: _a = this; _c = (_b = Object).assign; _d = [DEFAULT_DATA]; return [4 /*yield*/, _super.prototype.loadData.call(this)]; case 1: _a.data = _c.apply(_b, _d.concat([_e.sent()])); if (!this.data.maxLength) { console.log('Recent Files: maxLength is not set, using default (' + defaultMaxLength.toString() + ')'); } return [2 /*return*/]; } }); }); }; RecentFilesPlugin.prototype.saveData = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, _super.prototype.saveData.call(this, this.data)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; return RecentFilesPlugin; }(obsidian.Plugin)); var RecentFilesSettingTab = /** @class */ (function (_super) { __extends(RecentFilesSettingTab, _super); function RecentFilesSettingTab(app, plugin) { var _this = _super.call(this, app, plugin) || this; _this.plugin = plugin; return _this; } RecentFilesSettingTab.prototype.display = function () { var _this = this; var containerEl = this.containerEl; containerEl.empty(); containerEl.createEl('h2', { text: 'Recent Files List' }); var fragment = document.createDocumentFragment(); var link = document.createElement('a'); link.href = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#writing_a_regular_expression_pattern'; link.text = 'MDN - Regular expressions'; fragment.append('RegExp patterns to ignore. One pattern per line. See '); fragment.append(link); fragment.append(' for help.'); new obsidian.Setting(containerEl) .setName('Omitted pathname patterns') .setDesc(fragment) .addTextArea(function (textArea) { textArea.inputEl.setAttr('rows', 6); textArea .setPlaceholder('^daily/\n\\.png$\nfoobar.*baz') .setValue(_this.plugin.data.omittedPaths.join('\n')); textArea.inputEl.onblur = function (e) { var patterns = e.target.value; _this.plugin.data.omittedPaths = patterns.split('\n'); _this.plugin.pruneOmittedFiles(); _this.plugin.view.redraw(); }; }); new obsidian.Setting(containerEl) .setName('List length') .setDesc('Maximum number of filenames to keep in the list.') .addText(function (text) { var _a; text.inputEl.setAttr('type', 'number'); text.inputEl.setAttr('placeholder', defaultMaxLength); text .setValue((_a = _this.plugin.data.maxLength) === null || _a === void 0 ? void 0 : _a.toString()) .onChange(function (value) { var parsed = parseInt(value, 10); if (!Number.isNaN(parsed) && parsed <= 0) { new obsidian.Notice('List length must be a positive integer'); return; } }); text.inputEl.onblur = function (e) { var maxfiles = e.target.value; var parsed = parseInt(maxfiles, 10); _this.plugin.data.maxLength = parsed; _this.plugin.pruneLength(); _this.plugin.view.redraw(); }; }); var div = containerEl.createEl('div', { cls: 'recent-files-donation', }); var donateText = document.createElement('p'); donateText.appendText('If this plugin adds value for you and you would like to help support ' + 'continued development, please use the buttons below:'); div.appendChild(donateText); var parser = new DOMParser(); div.appendChild(createDonateButton('https://paypal.me/tgrosinger', parser.parseFromString(paypal, 'text/xml').documentElement)); div.appendChild(createDonateButton('https://www.buymeacoffee.com/tgrosinger', parser.parseFromString(buyMeACoffee, 'text/xml').documentElement)); }; return RecentFilesSettingTab; }(obsidian.PluginSettingTab)); var createDonateButton = function (link, img) { var a = document.createElement('a'); a.setAttribute('href', link); a.addClass('recent-files-donate-button'); a.appendChild(img); return a; }; var sweepIcon = "\n\n \n \n \n \n \n \n"; var buyMeACoffee = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; var paypal = "\n\n\n\n\n\n\n\n"; module.exports = RecentFilesPlugin; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsIm1haW4udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyohICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcbkNvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLlxyXG5cclxuUGVybWlzc2lvbiB0byB1c2UsIGNvcHksIG1vZGlmeSwgYW5kL29yIGRpc3RyaWJ1dGUgdGhpcyBzb2Z0d2FyZSBmb3IgYW55XHJcbnB1cnBvc2Ugd2l0aCBvciB3aXRob3V0IGZlZSBpcyBoZXJlYnkgZ3JhbnRlZC5cclxuXHJcblRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIgQU5EIFRIRSBBVVRIT1IgRElTQ0xBSU1TIEFMTCBXQVJSQU5USUVTIFdJVEhcclxuUkVHQVJEIFRPIFRISVMgU09GVFdBUkUgSU5DTFVESU5HIEFMTCBJTVBMSUVEIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZXHJcbkFORCBGSVRORVNTLiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SIEJFIExJQUJMRSBGT1IgQU5ZIFNQRUNJQUwsIERJUkVDVCxcclxuSU5ESVJFQ1QsIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyBPUiBBTlkgREFNQUdFUyBXSEFUU09FVkVSIFJFU1VMVElORyBGUk9NXHJcbkxPU1MgT0YgVVNFLCBEQVRBIE9SIFBST0ZJVFMsIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBORUdMSUdFTkNFIE9SXHJcbk9USEVSIFRPUlRJT1VTIEFDVElPTiwgQVJJU0lORyBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBVU0UgT1JcclxuUEVSRk9STUFOQ0UgT0YgVEhJUyBTT0ZUV0FSRS5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiogKi9cclxuLyogZ2xvYmFsIFJlZmxlY3QsIFByb21pc2UgKi9cclxuXHJcbnZhciBleHRlbmRTdGF0aWNzID0gZnVuY3Rpb24oZCwgYikge1xyXG4gICAgZXh0ZW5kU3RhdGljcyA9IE9iamVjdC5zZXRQcm90b3R5cGVPZiB8fFxyXG4gICAgICAgICh7IF9fcHJvdG9fXzogW10gfSBpbnN0YW5jZW9mIEFycmF5ICYmIGZ1bmN0aW9uIChkLCBiKSB7IGQuX19wcm90b19fID0gYjsgfSkgfHxcclxuICAgICAgICBmdW5jdGlvbiAoZCwgYikgeyBmb3IgKHZhciBwIGluIGIpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoYiwgcCkpIGRbcF0gPSBiW3BdOyB9O1xyXG4gICAgcmV0dXJuIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbn07XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19leHRlbmRzKGQsIGIpIHtcclxuICAgIGlmICh0eXBlb2YgYiAhPT0gXCJmdW5jdGlvblwiICYmIGIgIT09IG51bGwpXHJcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNsYXNzIGV4dGVuZHMgdmFsdWUgXCIgKyBTdHJpbmcoYikgKyBcIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsXCIpO1xyXG4gICAgZXh0ZW5kU3RhdGljcyhkLCBiKTtcclxuICAgIGZ1bmN0aW9uIF9fKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gZDsgfVxyXG4gICAgZC5wcm90b3R5cGUgPSBiID09PSBudWxsID8gT2JqZWN0LmNyZWF0ZShiKSA6IChfXy5wcm90b3R5cGUgPSBiLnByb3RvdHlwZSwgbmV3IF9fKCkpO1xyXG59XHJcblxyXG5leHBvcnQgdmFyIF9fYXNzaWduID0gZnVuY3Rpb24oKSB7XHJcbiAgICBfX2Fzc2lnbiA9IE9iamVjdC5hc3NpZ24gfHwgZnVuY3Rpb24gX19hc3NpZ24odCkge1xyXG4gICAgICAgIGZvciAodmFyIHMsIGkgPSAxLCBuID0gYXJndW1lbnRzLmxlbmd0aDsgaSA8IG47IGkrKykge1xyXG4gICAgICAgICAgICBzID0gYXJndW1lbnRzW2ldO1xyXG4gICAgICAgICAgICBmb3IgKHZhciBwIGluIHMpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocywgcCkpIHRbcF0gPSBzW3BdO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gdDtcclxuICAgIH1cclxuICAgIHJldHVybiBfX2Fzc2lnbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19yZXN0KHMsIGUpIHtcclxuICAgIHZhciB0ID0ge307XHJcbiAgICBmb3IgKHZhciBwIGluIHMpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocywgcCkgJiYgZS5pbmRleE9mKHApIDwgMClcclxuICAgICAgICB0W3BdID0gc1twXTtcclxuICAgIGlmIChzICE9IG51bGwgJiYgdHlwZW9mIE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMgPT09IFwiZnVuY3Rpb25cIilcclxuICAgICAgICBmb3IgKHZhciBpID0gMCwgcCA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMocyk7IGkgPCBwLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgICAgIGlmIChlLmluZGV4T2YocFtpXSkgPCAwICYmIE9iamVjdC5wcm90b3R5cGUucHJvcGVydHlJc0VudW1lcmFibGUuY2FsbChzLCBwW2ldKSlcclxuICAgICAgICAgICAgICAgIHRbcFtpXV0gPSBzW3BbaV1dO1xyXG4gICAgICAgIH1cclxuICAgIHJldHVybiB0O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19kZWNvcmF0ZShkZWNvcmF0b3JzLCB0YXJnZXQsIGtleSwgZGVzYykge1xyXG4gICAgdmFyIGMgPSBhcmd1bWVudHMubGVuZ3RoLCByID0gYyA8IDMgPyB0YXJnZXQgOiBkZXNjID09PSBudWxsID8gZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodGFyZ2V0LCBrZXkpIDogZGVzYywgZDtcclxuICAgIGlmICh0eXBlb2YgUmVmbGVjdCA9PT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSA9PT0gXCJmdW5jdGlvblwiKSByID0gUmVmbGVjdC5kZWNvcmF0ZShkZWNvcmF0b3JzLCB0YXJnZXQsIGtleSwgZGVzYyk7XHJcbiAgICBlbHNlIGZvciAodmFyIGkgPSBkZWNvcmF0b3JzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSBpZiAoZCA9IGRlY29yYXRvcnNbaV0pIHIgPSAoYyA8IDMgPyBkKHIpIDogYyA+IDMgPyBkKHRhcmdldCwga2V5LCByKSA6IGQodGFyZ2V0LCBrZXkpKSB8fCByO1xyXG4gICAgcmV0dXJuIGMgPiAzICYmIHIgJiYgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwga2V5LCByKSwgcjtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcGFyYW0ocGFyYW1JbmRleCwgZGVjb3JhdG9yKSB7XHJcbiAgICByZXR1cm4gZnVuY3Rpb24gKHRhcmdldCwga2V5KSB7IGRlY29yYXRvcih0YXJnZXQsIGtleSwgcGFyYW1JbmRleCk7IH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fbWV0YWRhdGEobWV0YWRhdGFLZXksIG1ldGFkYXRhVmFsdWUpIHtcclxuICAgIGlmICh0eXBlb2YgUmVmbGVjdCA9PT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgUmVmbGVjdC5tZXRhZGF0YSA9PT0gXCJmdW5jdGlvblwiKSByZXR1cm4gUmVmbGVjdC5tZXRhZGF0YShtZXRhZGF0YUtleSwgbWV0YWRhdGFWYWx1ZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2F3YWl0ZXIodGhpc0FyZywgX2FyZ3VtZW50cywgUCwgZ2VuZXJhdG9yKSB7XHJcbiAgICBmdW5jdGlvbiBhZG9wdCh2YWx1ZSkgeyByZXR1cm4gdmFsdWUgaW5zdGFuY2VvZiBQID8gdmFsdWUgOiBuZXcgUChmdW5jdGlvbiAocmVzb2x2ZSkgeyByZXNvbHZlKHZhbHVlKTsgfSk7IH1cclxuICAgIHJldHVybiBuZXcgKFAgfHwgKFAgPSBQcm9taXNlKSkoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkge1xyXG4gICAgICAgIGZ1bmN0aW9uIGZ1bGZpbGxlZCh2YWx1ZSkgeyB0cnkgeyBzdGVwKGdlbmVyYXRvci5uZXh0KHZhbHVlKSk7IH0gY2F0Y2ggKGUpIHsgcmVqZWN0KGUpOyB9IH1cclxuICAgICAgICBmdW5jdGlvbiByZWplY3RlZCh2YWx1ZSkgeyB0cnkgeyBzdGVwKGdlbmVyYXRvcltcInRocm93XCJdKHZhbHVlKSk7IH0gY2F0Y2ggKGUpIHsgcmVqZWN0KGUpOyB9IH1cclxuICAgICAgICBmdW5jdGlvbiBzdGVwKHJlc3VsdCkgeyByZXN1bHQuZG9uZSA/IHJlc29sdmUocmVzdWx0LnZhbHVlKSA6IGFkb3B0KHJlc3VsdC52YWx1ZSkudGhlbihmdWxmaWxsZWQsIHJlamVjdGVkKTsgfVxyXG4gICAgICAgIHN0ZXAoKGdlbmVyYXRvciA9IGdlbmVyYXRvci5hcHBseSh0aGlzQXJnLCBfYXJndW1lbnRzIHx8IFtdKSkubmV4dCgpKTtcclxuICAgIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19nZW5lcmF0b3IodGhpc0FyZywgYm9keSkge1xyXG4gICAgdmFyIF8gPSB7IGxhYmVsOiAwLCBzZW50OiBmdW5jdGlvbigpIHsgaWYgKHRbMF0gJiAxKSB0aHJvdyB0WzFdOyByZXR1cm4gdFsxXTsgfSwgdHJ5czogW10sIG9wczogW10gfSwgZiwgeSwgdCwgZztcclxuICAgIHJldHVybiBnID0geyBuZXh0OiB2ZXJiKDApLCBcInRocm93XCI6IHZlcmIoMSksIFwicmV0dXJuXCI6IHZlcmIoMikgfSwgdHlwZW9mIFN5bWJvbCA9PT0gXCJmdW5jdGlvblwiICYmIChnW1N5bWJvbC5pdGVyYXRvcl0gPSBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXM7IH0pLCBnO1xyXG4gICAgZnVuY3Rpb24gdmVyYihuKSB7IHJldHVybiBmdW5jdGlvbiAodikgeyByZXR1cm4gc3RlcChbbiwgdl0pOyB9OyB9XHJcbiAgICBmdW5jdGlvbiBzdGVwKG9wKSB7XHJcbiAgICAgICAgaWYgKGYpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJHZW5lcmF0b3IgaXMgYWxyZWFkeSBleGVjdXRpbmcuXCIpO1xyXG4gICAgICAgIHdoaWxlIChfKSB0cnkge1xyXG4gICAgICAgICAgICBpZiAoZiA9IDEsIHkgJiYgKHQgPSBvcFswXSAmIDIgPyB5W1wicmV0dXJuXCJdIDogb3BbMF0gPyB5W1widGhyb3dcIl0gfHwgKCh0ID0geVtcInJldHVyblwiXSkgJiYgdC5jYWxsKHkpLCAwKSA6IHkubmV4dCkgJiYgISh0ID0gdC5jYWxsKHksIG9wWzFdKSkuZG9uZSkgcmV0dXJuIHQ7XHJcbiAgICAgICAgICAgIGlmICh5ID0gMCwgdCkgb3AgPSBbb3BbMF0gJiAyLCB0LnZhbHVlXTtcclxuICAgICAgICAgICAgc3dpdGNoIChvcFswXSkge1xyXG4gICAgICAgICAgICAgICAgY2FzZSAwOiBjYXNlIDE6IHQgPSBvcDsgYnJlYWs7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDQ6IF8ubGFiZWwrKzsgcmV0dXJuIHsgdmFsdWU6IG9wWzFdLCBkb25lOiBmYWxzZSB9O1xyXG4gICAgICAgICAgICAgICAgY2FzZSA1OiBfLmxhYmVsKys7IHkgPSBvcFsxXTsgb3AgPSBbMF07IGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgY2FzZSA3OiBvcCA9IF8ub3BzLnBvcCgpOyBfLnRyeXMucG9wKCk7IGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcclxuICAgICAgICAgICAgICAgICAgICBpZiAoISh0ID0gXy50cnlzLCB0ID0gdC5sZW5ndGggPiAwICYmIHRbdC5sZW5ndGggLSAxXSkgJiYgKG9wWzBdID09PSA2IHx8IG9wWzBdID09PSAyKSkgeyBfID0gMDsgY29udGludWU7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAob3BbMF0gPT09IDMgJiYgKCF0IHx8IChvcFsxXSA+IHRbMF0gJiYgb3BbMV0gPCB0WzNdKSkpIHsgXy5sYWJlbCA9IG9wWzFdOyBicmVhazsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmIChvcFswXSA9PT0gNiAmJiBfLmxhYmVsIDwgdFsxXSkgeyBfLmxhYmVsID0gdFsxXTsgdCA9IG9wOyBicmVhazsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmICh0ICYmIF8ubGFiZWwgPCB0WzJdKSB7IF8ubGFiZWwgPSB0WzJdOyBfLm9wcy5wdXNoKG9wKTsgYnJlYWs7IH1cclxuICAgICAgICAgICAgICAgICAgICBpZiAodFsyXSkgXy5vcHMucG9wKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgXy50cnlzLnBvcCgpOyBjb250aW51ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBvcCA9IGJvZHkuY2FsbCh0aGlzQXJnLCBfKTtcclxuICAgICAgICB9IGNhdGNoIChlKSB7IG9wID0gWzYsIGVdOyB5ID0gMDsgfSBmaW5hbGx5IHsgZiA9IHQgPSAwOyB9XHJcbiAgICAgICAgaWYgKG9wWzBdICYgNSkgdGhyb3cgb3BbMV07IHJldHVybiB7IHZhbHVlOiBvcFswXSA/IG9wWzFdIDogdm9pZCAwLCBkb25lOiB0cnVlIH07XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCB2YXIgX19jcmVhdGVCaW5kaW5nID0gT2JqZWN0LmNyZWF0ZSA/IChmdW5jdGlvbihvLCBtLCBrLCBrMikge1xyXG4gICAgaWYgKGsyID09PSB1bmRlZmluZWQpIGsyID0gaztcclxuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvLCBrMiwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGZ1bmN0aW9uKCkgeyByZXR1cm4gbVtrXTsgfSB9KTtcclxufSkgOiAoZnVuY3Rpb24obywgbSwgaywgazIpIHtcclxuICAgIGlmIChrMiA9PT0gdW5kZWZpbmVkKSBrMiA9IGs7XHJcbiAgICBvW2syXSA9IG1ba107XHJcbn0pO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZXhwb3J0U3RhcihtLCBvKSB7XHJcbiAgICBmb3IgKHZhciBwIGluIG0pIGlmIChwICE9PSBcImRlZmF1bHRcIiAmJiAhT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG8sIHApKSBfX2NyZWF0ZUJpbmRpbmcobywgbSwgcCk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3ZhbHVlcyhvKSB7XHJcbiAgICB2YXIgcyA9IHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiBTeW1ib2wuaXRlcmF0b3IsIG0gPSBzICYmIG9bc10sIGkgPSAwO1xyXG4gICAgaWYgKG0pIHJldHVybiBtLmNhbGwobyk7XHJcbiAgICBpZiAobyAmJiB0eXBlb2Ygby5sZW5ndGggPT09IFwibnVtYmVyXCIpIHJldHVybiB7XHJcbiAgICAgICAgbmV4dDogZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICBpZiAobyAmJiBpID49IG8ubGVuZ3RoKSBvID0gdm9pZCAwO1xyXG4gICAgICAgICAgICByZXR1cm4geyB2YWx1ZTogbyAmJiBvW2krK10sIGRvbmU6ICFvIH07XHJcbiAgICAgICAgfVxyXG4gICAgfTtcclxuICAgIHRocm93IG5ldyBUeXBlRXJyb3IocyA/IFwiT2JqZWN0IGlzIG5vdCBpdGVyYWJsZS5cIiA6IFwiU3ltYm9sLml0ZXJhdG9yIGlzIG5vdCBkZWZpbmVkLlwiKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcmVhZChvLCBuKSB7XHJcbiAgICB2YXIgbSA9IHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiBvW1N5bWJvbC5pdGVyYXRvcl07XHJcbiAgICBpZiAoIW0pIHJldHVybiBvO1xyXG4gICAgdmFyIGkgPSBtLmNhbGwobyksIHIsIGFyID0gW10sIGU7XHJcbiAgICB0cnkge1xyXG4gICAgICAgIHdoaWxlICgobiA9PT0gdm9pZCAwIHx8IG4tLSA+IDApICYmICEociA9IGkubmV4dCgpKS5kb25lKSBhci5wdXNoKHIudmFsdWUpO1xyXG4gICAgfVxyXG4gICAgY2F0Y2ggKGVycm9yKSB7IGUgPSB7IGVycm9yOiBlcnJvciB9OyB9XHJcbiAgICBmaW5hbGx5IHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBpZiAociAmJiAhci5kb25lICYmIChtID0gaVtcInJldHVyblwiXSkpIG0uY2FsbChpKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZmluYWxseSB7IGlmIChlKSB0aHJvdyBlLmVycm9yOyB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gYXI7XHJcbn1cclxuXHJcbi8qKiBAZGVwcmVjYXRlZCAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX19zcHJlYWQoKSB7XHJcbiAgICBmb3IgKHZhciBhciA9IFtdLCBpID0gMDsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKylcclxuICAgICAgICBhciA9IGFyLmNvbmNhdChfX3JlYWQoYXJndW1lbnRzW2ldKSk7XHJcbiAgICByZXR1cm4gYXI7XHJcbn1cclxuXHJcbi8qKiBAZGVwcmVjYXRlZCAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX19zcHJlYWRBcnJheXMoKSB7XHJcbiAgICBmb3IgKHZhciBzID0gMCwgaSA9IDAsIGlsID0gYXJndW1lbnRzLmxlbmd0aDsgaSA8IGlsOyBpKyspIHMgKz0gYXJndW1lbnRzW2ldLmxlbmd0aDtcclxuICAgIGZvciAodmFyIHIgPSBBcnJheShzKSwgayA9IDAsIGkgPSAwOyBpIDwgaWw7IGkrKylcclxuICAgICAgICBmb3IgKHZhciBhID0gYXJndW1lbnRzW2ldLCBqID0gMCwgamwgPSBhLmxlbmd0aDsgaiA8IGpsOyBqKyssIGsrKylcclxuICAgICAgICAgICAgcltrXSA9IGFbal07XHJcbiAgICByZXR1cm4gcjtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fc3ByZWFkQXJyYXkodG8sIGZyb20pIHtcclxuICAgIGZvciAodmFyIGkgPSAwLCBpbCA9IGZyb20ubGVuZ3RoLCBqID0gdG8ubGVuZ3RoOyBpIDwgaWw7IGkrKywgaisrKVxyXG4gICAgICAgIHRvW2pdID0gZnJvbVtpXTtcclxuICAgIHJldHVybiB0bztcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXdhaXQodikge1xyXG4gICAgcmV0dXJuIHRoaXMgaW5zdGFuY2VvZiBfX2F3YWl0ID8gKHRoaXMudiA9IHYsIHRoaXMpIDogbmV3IF9fYXdhaXQodik7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2FzeW5jR2VuZXJhdG9yKHRoaXNBcmcsIF9hcmd1bWVudHMsIGdlbmVyYXRvcikge1xyXG4gICAgaWYgKCFTeW1ib2wuYXN5bmNJdGVyYXRvcikgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN5bWJvbC5hc3luY0l0ZXJhdG9yIGlzIG5vdCBkZWZpbmVkLlwiKTtcclxuICAgIHZhciBnID0gZ2VuZXJhdG9yLmFwcGx5KHRoaXNBcmcsIF9hcmd1bWVudHMgfHwgW10pLCBpLCBxID0gW107XHJcbiAgICByZXR1cm4gaSA9IHt9LCB2ZXJiKFwibmV4dFwiKSwgdmVyYihcInRocm93XCIpLCB2ZXJiKFwicmV0dXJuXCIpLCBpW1N5bWJvbC5hc3luY0l0ZXJhdG9yXSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXM7IH0sIGk7XHJcbiAgICBmdW5jdGlvbiB2ZXJiKG4pIHsgaWYgKGdbbl0pIGlbbl0gPSBmdW5jdGlvbiAodikgeyByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKGEsIGIpIHsgcS5wdXNoKFtuLCB2LCBhLCBiXSkgPiAxIHx8IHJlc3VtZShuLCB2KTsgfSk7IH07IH1cclxuICAgIGZ1bmN0aW9uIHJlc3VtZShuLCB2KSB7IHRyeSB7IHN0ZXAoZ1tuXSh2KSk7IH0gY2F0Y2ggKGUpIHsgc2V0dGxlKHFbMF1bM10sIGUpOyB9IH1cclxuICAgIGZ1bmN0aW9uIHN0ZXAocikgeyByLnZhbHVlIGluc3RhbmNlb2YgX19hd2FpdCA/IFByb21pc2UucmVzb2x2ZShyLnZhbHVlLnYpLnRoZW4oZnVsZmlsbCwgcmVqZWN0KSA6IHNldHRsZShxWzBdWzJdLCByKTsgfVxyXG4gICAgZnVuY3Rpb24gZnVsZmlsbCh2YWx1ZSkgeyByZXN1bWUoXCJuZXh0XCIsIHZhbHVlKTsgfVxyXG4gICAgZnVuY3Rpb24gcmVqZWN0KHZhbHVlKSB7IHJlc3VtZShcInRocm93XCIsIHZhbHVlKTsgfVxyXG4gICAgZnVuY3Rpb24gc2V0dGxlKGYsIHYpIHsgaWYgKGYodiksIHEuc2hpZnQoKSwgcS5sZW5ndGgpIHJlc3VtZShxWzBdWzBdLCBxWzBdWzFdKTsgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hc3luY0RlbGVnYXRvcihvKSB7XHJcbiAgICB2YXIgaSwgcDtcclxuICAgIHJldHVybiBpID0ge30sIHZlcmIoXCJuZXh0XCIpLCB2ZXJiKFwidGhyb3dcIiwgZnVuY3Rpb24gKGUpIHsgdGhyb3cgZTsgfSksIHZlcmIoXCJyZXR1cm5cIiksIGlbU3ltYm9sLml0ZXJhdG9yXSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXM7IH0sIGk7XHJcbiAgICBmdW5jdGlvbiB2ZXJiKG4sIGYpIHsgaVtuXSA9IG9bbl0gPyBmdW5jdGlvbiAodikgeyByZXR1cm4gKHAgPSAhcCkgPyB7IHZhbHVlOiBfX2F3YWl0KG9bbl0odikpLCBkb25lOiBuID09PSBcInJldHVyblwiIH0gOiBmID8gZih2KSA6IHY7IH0gOiBmOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2FzeW5jVmFsdWVzKG8pIHtcclxuICAgIGlmICghU3ltYm9sLmFzeW5jSXRlcmF0b3IpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJTeW1ib2wuYXN5bmNJdGVyYXRvciBpcyBub3QgZGVmaW5lZC5cIik7XHJcbiAgICB2YXIgbSA9IG9bU3ltYm9sLmFzeW5jSXRlcmF0b3JdLCBpO1xyXG4gICAgcmV0dXJuIG0gPyBtLmNhbGwobykgOiAobyA9IHR5cGVvZiBfX3ZhbHVlcyA9PT0gXCJmdW5jdGlvblwiID8gX192YWx1ZXMobykgOiBvW1N5bWJvbC5pdGVyYXRvcl0oKSwgaSA9IHt9LCB2ZXJiKFwibmV4dFwiKSwgdmVyYihcInRocm93XCIpLCB2ZXJiKFwicmV0dXJuXCIpLCBpW1N5bWJvbC5hc3luY0l0ZXJhdG9yXSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXM7IH0sIGkpO1xyXG4gICAgZnVuY3Rpb24gdmVyYihuKSB7IGlbbl0gPSBvW25dICYmIGZ1bmN0aW9uICh2KSB7IHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7IHYgPSBvW25dKHYpLCBzZXR0bGUocmVzb2x2ZSwgcmVqZWN0LCB2LmRvbmUsIHYudmFsdWUpOyB9KTsgfTsgfVxyXG4gICAgZnVuY3Rpb24gc2V0dGxlKHJlc29sdmUsIHJlamVjdCwgZCwgdikgeyBQcm9taXNlLnJlc29sdmUodikudGhlbihmdW5jdGlvbih2KSB7IHJlc29sdmUoeyB2YWx1ZTogdiwgZG9uZTogZCB9KTsgfSwgcmVqZWN0KTsgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19tYWtlVGVtcGxhdGVPYmplY3QoY29va2VkLCByYXcpIHtcclxuICAgIGlmIChPYmplY3QuZGVmaW5lUHJvcGVydHkpIHsgT2JqZWN0LmRlZmluZVByb3BlcnR5KGNvb2tlZCwgXCJyYXdcIiwgeyB2YWx1ZTogcmF3IH0pOyB9IGVsc2UgeyBjb29rZWQucmF3ID0gcmF3OyB9XHJcbiAgICByZXR1cm4gY29va2VkO1xyXG59O1xyXG5cclxudmFyIF9fc2V0TW9kdWxlRGVmYXVsdCA9IE9iamVjdC5jcmVhdGUgPyAoZnVuY3Rpb24obywgdikge1xyXG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KG8sIFwiZGVmYXVsdFwiLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2IH0pO1xyXG59KSA6IGZ1bmN0aW9uKG8sIHYpIHtcclxuICAgIG9bXCJkZWZhdWx0XCJdID0gdjtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2ltcG9ydFN0YXIobW9kKSB7XHJcbiAgICBpZiAobW9kICYmIG1vZC5fX2VzTW9kdWxlKSByZXR1cm4gbW9kO1xyXG4gICAgdmFyIHJlc3VsdCA9IHt9O1xyXG4gICAgaWYgKG1vZCAhPSBudWxsKSBmb3IgKHZhciBrIGluIG1vZCkgaWYgKGsgIT09IFwiZGVmYXVsdFwiICYmIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChtb2QsIGspKSBfX2NyZWF0ZUJpbmRpbmcocmVzdWx0LCBtb2QsIGspO1xyXG4gICAgX19zZXRNb2R1bGVEZWZhdWx0KHJlc3VsdCwgbW9kKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2ltcG9ydERlZmF1bHQobW9kKSB7XHJcbiAgICByZXR1cm4gKG1vZCAmJiBtb2QuX19lc01vZHVsZSkgPyBtb2QgOiB7IGRlZmF1bHQ6IG1vZCB9O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19jbGFzc1ByaXZhdGVGaWVsZEdldChyZWNlaXZlciwgcHJpdmF0ZU1hcCkge1xyXG4gICAgaWYgKCFwcml2YXRlTWFwLmhhcyhyZWNlaXZlcikpIHtcclxuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiYXR0ZW1wdGVkIHRvIGdldCBwcml2YXRlIGZpZWxkIG9uIG5vbi1pbnN0YW5jZVwiKTtcclxuICAgIH1cclxuICAgIHJldHVybiBwcml2YXRlTWFwLmdldChyZWNlaXZlcik7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2NsYXNzUHJpdmF0ZUZpZWxkU2V0KHJlY2VpdmVyLCBwcml2YXRlTWFwLCB2YWx1ZSkge1xyXG4gICAgaWYgKCFwcml2YXRlTWFwLmhhcyhyZWNlaXZlcikpIHtcclxuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiYXR0ZW1wdGVkIHRvIHNldCBwcml2YXRlIGZpZWxkIG9uIG5vbi1pbnN0YW5jZVwiKTtcclxuICAgIH1cclxuICAgIHByaXZhdGVNYXAuc2V0KHJlY2VpdmVyLCB2YWx1ZSk7XHJcbiAgICByZXR1cm4gdmFsdWU7XHJcbn1cclxuIiwiaW1wb3J0IHtcbiAgYWRkSWNvbixcbiAgQXBwLFxuICBJdGVtVmlldyxcbiAgTWVudSxcbiAgTm90aWNlLFxuICBQbHVnaW4sXG4gIFBsdWdpblNldHRpbmdUYWIsXG4gIFNldHRpbmcsXG4gIFRBYnN0cmFjdEZpbGUsXG4gIFRGaWxlLFxuICBXb3Jrc3BhY2VMZWFmLFxufSBmcm9tICdvYnNpZGlhbic7XG5cbmludGVyZmFjZSBGaWxlUGF0aCB7XG4gIHBhdGg6IHN0cmluZztcbiAgYmFzZW5hbWU6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIFJlY2VudEZpbGVzRGF0YSB7XG4gIHJlY2VudEZpbGVzOiBGaWxlUGF0aFtdO1xuICBvbWl0dGVkUGF0aHM6IHN0cmluZ1tdO1xuICBtYXhMZW5ndGg6IG51bWJlcjtcbn1cblxuY29uc3QgZGVmYXVsdE1heExlbmd0aDogbnVtYmVyID0gNTA7XG5cbmNvbnN0IERFRkFVTFRfREFUQTogUmVjZW50RmlsZXNEYXRhID0ge1xuICByZWNlbnRGaWxlczogW10sXG4gIG9taXR0ZWRQYXRoczogW10sXG4gIG1heExlbmd0aDogbnVsbCxcbn07XG5cbmNvbnN0IFJlY2VudEZpbGVzTGlzdFZpZXdUeXBlID0gJ3JlY2VudC1maWxlcyc7XG5cbmNsYXNzIFJlY2VudEZpbGVzTGlzdFZpZXcgZXh0ZW5kcyBJdGVtVmlldyB7XG4gIHByaXZhdGUgcmVhZG9ubHkgcGx1Z2luOiBSZWNlbnRGaWxlc1BsdWdpbjtcbiAgcHJpdmF0ZSBkYXRhOiBSZWNlbnRGaWxlc0RhdGE7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgbGVhZjogV29ya3NwYWNlTGVhZixcbiAgICBwbHVnaW46IFJlY2VudEZpbGVzUGx1Z2luLFxuICAgIGRhdGE6IFJlY2VudEZpbGVzRGF0YSxcbiAgKSB7XG4gICAgc3VwZXIobGVhZik7XG5cbiAgICB0aGlzLnBsdWdpbiA9IHBsdWdpbjtcbiAgICB0aGlzLmRhdGEgPSBkYXRhO1xuICAgIHRoaXMucmVkcmF3KCk7XG4gIH1cblxuICBwdWJsaWMgZ2V0Vmlld1R5cGUoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gUmVjZW50RmlsZXNMaXN0Vmlld1R5cGU7XG4gIH1cblxuICBwdWJsaWMgZ2V0RGlzcGxheVRleHQoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gJ1JlY2VudCBGaWxlcyc7XG4gIH1cblxuICBwdWJsaWMgZ2V0SWNvbigpOiBzdHJpbmcge1xuICAgIHJldHVybiAnY2xvY2snO1xuICB9XG5cbiAgcHVibGljIG9uSGVhZGVyTWVudShtZW51OiBNZW51KTogdm9pZCB7XG4gICAgbWVudVxuICAgICAgLmFkZEl0ZW0oKGl0ZW0pID0+IHtcbiAgICAgICAgaXRlbVxuICAgICAgICAgIC5zZXRUaXRsZSgnQ2xlYXIgbGlzdCcpXG4gICAgICAgICAgLnNldEljb24oJ3N3ZWVwJylcbiAgICAgICAgICAub25DbGljayhhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmRhdGEucmVjZW50RmlsZXMgPSBbXTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucGx1Z2luLnNhdmVEYXRhKCk7XG4gICAgICAgICAgICB0aGlzLnJlZHJhdygpO1xuICAgICAgICAgIH0pO1xuICAgICAgfSlcbiAgICAgIC5hZGRJdGVtKChpdGVtKSA9PiB7XG4gICAgICAgIGl0ZW1cbiAgICAgICAgICAuc2V0VGl0bGUoJ0Nsb3NlJylcbiAgICAgICAgICAuc2V0SWNvbignY3Jvc3MnKVxuICAgICAgICAgIC5vbkNsaWNrKCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuYXBwLndvcmtzcGFjZS5kZXRhY2hMZWF2ZXNPZlR5cGUoUmVjZW50RmlsZXNMaXN0Vmlld1R5cGUpO1xuICAgICAgICAgIH0pO1xuICAgICAgfSk7XG4gIH1cblxuICBwdWJsaWMgbG9hZCgpOiB2b2lkIHtcbiAgICBzdXBlci5sb2FkKCk7XG4gICAgdGhpcy5yZWdpc3RlckV2ZW50KHRoaXMuYXBwLndvcmtzcGFjZS5vbignZmlsZS1vcGVuJywgdGhpcy51cGRhdGUpKTtcbiAgfVxuXG4gIHB1YmxpYyByZWFkb25seSByZWRyYXcgPSAoKTogdm9pZCA9PiB7XG4gICAgY29uc3Qgb3BlbkZpbGUgPSB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0QWN0aXZlRmlsZSgpO1xuXG4gICAgY29uc3Qgcm9vdEVsID0gY3JlYXRlRGl2KHsgY2xzOiAnbmF2LWZvbGRlciBtb2Qtcm9vdCcgfSk7XG4gICAgY29uc3QgY2hpbGRyZW5FbCA9IHJvb3RFbC5jcmVhdGVEaXYoeyBjbHM6ICduYXYtZm9sZGVyLWNoaWxkcmVuJyB9KTtcblxuICAgIHRoaXMuZGF0YS5yZWNlbnRGaWxlcy5mb3JFYWNoKChjdXJyZW50RmlsZSkgPT4ge1xuICAgICAgY29uc3QgbmF2RmlsZSA9IGNoaWxkcmVuRWwuY3JlYXRlRGl2KHsgY2xzOiAnbmF2LWZpbGUnIH0pO1xuICAgICAgY29uc3QgbmF2RmlsZVRpdGxlID0gbmF2RmlsZS5jcmVhdGVEaXYoeyBjbHM6ICduYXYtZmlsZS10aXRsZScgfSk7XG5cbiAgICAgIGlmIChvcGVuRmlsZSAmJiBjdXJyZW50RmlsZS5wYXRoID09PSBvcGVuRmlsZS5wYXRoKSB7XG4gICAgICAgIG5hdkZpbGVUaXRsZS5hZGRDbGFzcygnaXMtYWN0aXZlJyk7XG4gICAgICB9XG5cbiAgICAgIG5hdkZpbGVUaXRsZS5jcmVhdGVEaXYoe1xuICAgICAgICBjbHM6ICduYXYtZmlsZS10aXRsZS1jb250ZW50JyxcbiAgICAgICAgdGV4dDogY3VycmVudEZpbGUuYmFzZW5hbWUsXG4gICAgICB9KTtcblxuICAgICAgbmF2RmlsZS5zZXRBdHRyKCdkcmFnZ2FibGUnLCAndHJ1ZScpO1xuICAgICAgbmF2RmlsZS5hZGRFdmVudExpc3RlbmVyKCdkcmFnc3RhcnQnLCAoZXZlbnQ6IERyYWdFdmVudCkgPT4ge1xuICAgICAgICBjb25zdCBmaWxlID0gdGhpcy5hcHAubWV0YWRhdGFDYWNoZS5nZXRGaXJzdExpbmtwYXRoRGVzdChcbiAgICAgICAgICBjdXJyZW50RmlsZS5wYXRoLFxuICAgICAgICAgICcnLFxuICAgICAgICApO1xuXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gICAgICAgIGNvbnN0IGRyYWdNYW5hZ2VyID0gKHRoaXMuYXBwIGFzIGFueSkuZHJhZ01hbmFnZXI7XG4gICAgICAgIGNvbnN0IGRyYWdEYXRhID0gZHJhZ01hbmFnZXIuZHJhZ0ZpbGUoZXZlbnQsIGZpbGUpO1xuICAgICAgICBkcmFnTWFuYWdlci5vbkRyYWdTdGFydChldmVudCwgZHJhZ0RhdGEpO1xuICAgICAgfSk7XG5cbiAgICAgIG5hdkZpbGUuYWRkRXZlbnRMaXN0ZW5lcignbW91c2VvdmVyJywgKGV2ZW50OiBNb3VzZUV2ZW50KSA9PiB7XG4gICAgICAgIHRoaXMuYXBwLndvcmtzcGFjZS50cmlnZ2VyKCdob3Zlci1saW5rJywge1xuICAgICAgICAgIGV2ZW50LFxuICAgICAgICAgIHNvdXJjZTogUmVjZW50RmlsZXNMaXN0Vmlld1R5cGUsXG4gICAgICAgICAgaG92ZXJQYXJlbnQ6IHJvb3RFbCxcbiAgICAgICAgICB0YXJnZXRFbDogbmF2RmlsZSxcbiAgICAgICAgICBsaW5rdGV4dDogY3VycmVudEZpbGUucGF0aCxcbiAgICAgICAgfSk7XG4gICAgICB9KTtcblxuICAgICAgbmF2RmlsZS5hZGRFdmVudExpc3RlbmVyKCdjb250ZXh0bWVudScsIChldmVudDogTW91c2VFdmVudCkgPT4ge1xuICAgICAgICBjb25zdCBtZW51ID0gbmV3IE1lbnUodGhpcy5hcHApO1xuICAgICAgICBjb25zdCBmaWxlID0gdGhpcy5hcHAudmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKGN1cnJlbnRGaWxlLnBhdGgpO1xuICAgICAgICB0aGlzLmFwcC53b3Jrc3BhY2UudHJpZ2dlcihcbiAgICAgICAgICAnZmlsZS1tZW51JyxcbiAgICAgICAgICBtZW51LFxuICAgICAgICAgIGZpbGUsXG4gICAgICAgICAgJ2xpbmstY29udGV4dC1tZW51JyxcbiAgICAgICAgICB0aGlzLmxlYWYsXG4gICAgICAgICk7XG4gICAgICAgIG1lbnUuc2hvd0F0UG9zaXRpb24oeyB4OiBldmVudC5jbGllbnRYLCB5OiBldmVudC5jbGllbnRZIH0pO1xuICAgICAgfSk7XG5cbiAgICAgIG5hdkZpbGUuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoZXZlbnQ6IE1vdXNlRXZlbnQpID0+IHtcbiAgICAgICAgdGhpcy5mb2N1c0ZpbGUoY3VycmVudEZpbGUsIGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIGNvbnN0IGNvbnRlbnRFbCA9IHRoaXMuY29udGFpbmVyRWwuY2hpbGRyZW5bMV07XG4gICAgY29udGVudEVsLmVtcHR5KCk7XG4gICAgY29udGVudEVsLmFwcGVuZENoaWxkKHJvb3RFbCk7XG4gIH07XG5cbiAgcHJpdmF0ZSByZWFkb25seSB1cGRhdGVEYXRhID0gYXN5bmMgKGZpbGU6IFRGaWxlKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgdGhpcy5kYXRhLnJlY2VudEZpbGVzID0gdGhpcy5kYXRhLnJlY2VudEZpbGVzLmZpbHRlcihcbiAgICAgIChjdXJyRmlsZSkgPT4gY3VyckZpbGUucGF0aCAhPT0gZmlsZS5wYXRoLFxuICAgICk7XG4gICAgdGhpcy5kYXRhLnJlY2VudEZpbGVzLnVuc2hpZnQoe1xuICAgICAgYmFzZW5hbWU6IGZpbGUuYmFzZW5hbWUsXG4gICAgICBwYXRoOiBmaWxlLnBhdGgsXG4gICAgfSk7XG5cbiAgICBhd2FpdCB0aGlzLnBsdWdpbi5wcnVuZUxlbmd0aCgpOyAvLyBIYW5kbGVzIHRoZSBzYXZlXG4gIH07XG5cbiAgcHJpdmF0ZSByZWFkb25seSB1cGRhdGUgPSBhc3luYyAob3BlbmVkRmlsZTogVEZpbGUpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBpZiAoIW9wZW5lZEZpbGUgfHwgIXRoaXMucGx1Z2luLnNob3VsZEFkZEZpbGUob3BlbmVkRmlsZSkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLnVwZGF0ZURhdGEob3BlbmVkRmlsZSk7XG4gICAgdGhpcy5yZWRyYXcoKTtcbiAgfTtcblxuICAvKipcbiAgICogT3BlbiB0aGUgcHJvdmlkZWQgZmlsZSBpbiB0aGUgbW9zdCByZWNlbnQgbGVhZi5cbiAgICpcbiAgICogQHBhcmFtIHNob3VsZFNwbGl0IFdoZXRoZXIgdGhlIGZpbGUgc2hvdWxkIGJlIG9wZW5lZCBpbiBhIG5ldyBzcGxpdCwgb3IgaW5cbiAgICogdGhlIG1vc3QgcmVjZW50IHNwbGl0LiBJZiB0aGUgbW9zdCByZWNlbnQgc3BsaXQgaXMgcGlubmVkLCB0aGlzIGlzIHNldCB0b1xuICAgKiB0cnVlLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBmb2N1c0ZpbGUgPSAoZmlsZTogRmlsZVBhdGgsIHNob3VsZFNwbGl0ID0gZmFsc2UpOiB2b2lkID0+IHtcbiAgICBjb25zdCB0YXJnZXRGaWxlID0gdGhpcy5hcHAudmF1bHRcbiAgICAgIC5nZXRGaWxlcygpXG4gICAgICAuZmluZCgoZikgPT4gZi5wYXRoID09PSBmaWxlLnBhdGgpO1xuXG4gICAgaWYgKHRhcmdldEZpbGUpIHtcbiAgICAgIGxldCBsZWFmID0gdGhpcy5hcHAud29ya3NwYWNlLmdldE1vc3RSZWNlbnRMZWFmKCk7XG5cbiAgICAgIGNvbnN0IGNyZWF0ZUxlYWYgPSBzaG91bGRTcGxpdCB8fCBsZWFmLmdldFZpZXdTdGF0ZSgpLnBpbm5lZDtcbiAgICAgIGlmIChjcmVhdGVMZWFmKSB7XG4gICAgICAgIGxlYWYgPSB0aGlzLmFwcC53b3Jrc3BhY2UuY3JlYXRlTGVhZkJ5U3BsaXQobGVhZik7XG4gICAgICB9XG4gICAgICBsZWFmLm9wZW5GaWxlKHRhcmdldEZpbGUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBuZXcgTm90aWNlKCdDYW5ub3QgZmluZCBhIGZpbGUgd2l0aCB0aGF0IG5hbWUnKTtcbiAgICAgIHRoaXMuZGF0YS5yZWNlbnRGaWxlcyA9IHRoaXMuZGF0YS5yZWNlbnRGaWxlcy5maWx0ZXIoXG4gICAgICAgIChmcCkgPT4gZnAucGF0aCAhPT0gZmlsZS5wYXRoLFxuICAgICAgKTtcbiAgICAgIHRoaXMucGx1Z2luLnNhdmVEYXRhKCk7XG4gICAgICB0aGlzLnJlZHJhdygpO1xuICAgIH1cbiAgfTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUmVjZW50RmlsZXNQbHVnaW4gZXh0ZW5kcyBQbHVnaW4ge1xuICBwdWJsaWMgZGF0YTogUmVjZW50RmlsZXNEYXRhO1xuICBwdWJsaWMgdmlldzogUmVjZW50RmlsZXNMaXN0VmlldztcblxuICBwdWJsaWMgYXN5bmMgb25sb2FkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnNvbGUubG9nKCdSZWNlbnQgRmlsZXM6IExvYWRpbmcgcGx1Z2luIHYnICsgdGhpcy5tYW5pZmVzdC52ZXJzaW9uKTtcblxuICAgIGF3YWl0IHRoaXMubG9hZERhdGEoKTtcblxuICAgIGFkZEljb24oJ3N3ZWVwJywgc3dlZXBJY29uKTtcblxuICAgIHRoaXMucmVnaXN0ZXJWaWV3KFxuICAgICAgUmVjZW50RmlsZXNMaXN0Vmlld1R5cGUsXG4gICAgICAobGVhZikgPT4gKHRoaXMudmlldyA9IG5ldyBSZWNlbnRGaWxlc0xpc3RWaWV3KGxlYWYsIHRoaXMsIHRoaXMuZGF0YSkpLFxuICAgICk7XG5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICAgICh0aGlzLmFwcC53b3Jrc3BhY2UgYXMgYW55KS5yZWdpc3RlckhvdmVyTGlua1NvdXJjZShcbiAgICAgIFJlY2VudEZpbGVzTGlzdFZpZXdUeXBlLFxuICAgICAge1xuICAgICAgICBkaXNwbGF5OiAnUmVjZW50IEZpbGVzJyxcbiAgICAgICAgZGVmYXVsdE1vZDogdHJ1ZSxcbiAgICAgIH0sXG4gICAgKTtcblxuICAgIGlmICh0aGlzLmFwcC53b3Jrc3BhY2UubGF5b3V0UmVhZHkpIHtcbiAgICAgIHRoaXMuaW5pdFZpZXcoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZWdpc3RlckV2ZW50KHRoaXMuYXBwLndvcmtzcGFjZS5vbignbGF5b3V0LXJlYWR5JywgdGhpcy5pbml0VmlldykpO1xuICAgIH1cblxuICAgIHRoaXMucmVnaXN0ZXJFdmVudCh0aGlzLmFwcC52YXVsdC5vbigncmVuYW1lJywgdGhpcy5oYW5kbGVSZW5hbWUpKTtcbiAgICB0aGlzLnJlZ2lzdGVyRXZlbnQodGhpcy5hcHAudmF1bHQub24oJ2RlbGV0ZScsIHRoaXMuaGFuZGxlRGVsZXRlKSk7XG5cbiAgICB0aGlzLmFkZFNldHRpbmdUYWIobmV3IFJlY2VudEZpbGVzU2V0dGluZ1RhYih0aGlzLmFwcCwgdGhpcykpO1xuICB9XG5cbiAgcHVibGljIG9udW5sb2FkKCk6IHZvaWQge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gICAgKHRoaXMuYXBwLndvcmtzcGFjZSBhcyBhbnkpLnVucmVnaXN0ZXJIb3ZlckxpbmtTb3VyY2UoXG4gICAgICBSZWNlbnRGaWxlc0xpc3RWaWV3VHlwZSxcbiAgICApO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIGxvYWREYXRhKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuZGF0YSA9IE9iamVjdC5hc3NpZ24oREVGQVVMVF9EQVRBLCBhd2FpdCBzdXBlci5sb2FkRGF0YSgpKTtcbiAgICBpZiAoIXRoaXMuZGF0YS5tYXhMZW5ndGgpIHtcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAnUmVjZW50IEZpbGVzOiBtYXhMZW5ndGggaXMgbm90IHNldCwgdXNpbmcgZGVmYXVsdCAoJyArXG4gICAgICAgICAgZGVmYXVsdE1heExlbmd0aC50b1N0cmluZygpICtcbiAgICAgICAgICAnKScsXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBzYXZlRGF0YSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBzdXBlci5zYXZlRGF0YSh0aGlzLmRhdGEpO1xuICB9XG5cbiAgcHVibGljIHJlYWRvbmx5IHBydW5lT21pdHRlZEZpbGVzID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIHRoaXMuZGF0YS5yZWNlbnRGaWxlcyA9IHRoaXMuZGF0YS5yZWNlbnRGaWxlcy5maWx0ZXIodGhpcy5zaG91bGRBZGRGaWxlKTtcbiAgICBhd2FpdCB0aGlzLnNhdmVEYXRhKCk7XG4gIH07XG5cbiAgcHVibGljIHJlYWRvbmx5IHBydW5lTGVuZ3RoID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGNvbnN0IHRvUmVtb3ZlID1cbiAgICAgIHRoaXMuZGF0YS5yZWNlbnRGaWxlcy5sZW5ndGggLSAodGhpcy5kYXRhLm1heExlbmd0aCB8fCBkZWZhdWx0TWF4TGVuZ3RoKTtcbiAgICBpZiAodG9SZW1vdmUgPiAwKSB7XG4gICAgICB0aGlzLmRhdGEucmVjZW50RmlsZXMuc3BsaWNlKFxuICAgICAgICB0aGlzLmRhdGEucmVjZW50RmlsZXMubGVuZ3RoIC0gdG9SZW1vdmUsXG4gICAgICAgIHRvUmVtb3ZlLFxuICAgICAgKTtcbiAgICB9XG4gICAgYXdhaXQgdGhpcy5zYXZlRGF0YSgpO1xuICB9O1xuXG4gIHB1YmxpYyByZWFkb25seSBzaG91bGRBZGRGaWxlID0gKGZpbGU6IEZpbGVQYXRoKTogYm9vbGVhbiA9PiB7XG4gICAgY29uc3QgcGF0dGVybnM6IHN0cmluZ1tdID0gdGhpcy5kYXRhLm9taXR0ZWRQYXRocy5maWx0ZXIoXG4gICAgICAocGF0aCkgPT4gcGF0aC5sZW5ndGggPiAwLFxuICAgICk7XG4gICAgY29uc3QgZmlsZU1hdGNoZXNSZWdleCA9IChwYXR0ZXJuOiBzdHJpbmcpOiBib29sZWFuID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBuZXcgUmVnRXhwKHBhdHRlcm4pLnRlc3QoZmlsZS5wYXRoKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdSZWNlbnQgRmlsZXM6IEludmFsaWQgcmVnZXggcGF0dGVybjogJyArIHBhdHRlcm4pO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfTtcbiAgICByZXR1cm4gIXBhdHRlcm5zLnNvbWUoZmlsZU1hdGNoZXNSZWdleCk7XG4gIH07XG5cbiAgcHJpdmF0ZSByZWFkb25seSBpbml0VmlldyA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBsZXQgbGVhZjogV29ya3NwYWNlTGVhZiA9IG51bGw7XG4gICAgZm9yIChsZWFmIG9mIHRoaXMuYXBwLndvcmtzcGFjZS5nZXRMZWF2ZXNPZlR5cGUoUmVjZW50RmlsZXNMaXN0Vmlld1R5cGUpKSB7XG4gICAgICBpZiAobGVhZi52aWV3IGluc3RhbmNlb2YgUmVjZW50RmlsZXNMaXN0VmlldykgcmV0dXJuO1xuICAgICAgLy8gVGhlIHZpZXcgaW5zdGFuY2Ugd2FzIGNyZWF0ZWQgYnkgYW4gb2xkZXIgdmVyc2lvbiBvZiB0aGUgcGx1Z2luLFxuICAgICAgLy8gc28gY2xlYXIgaXQgYW5kIHJlY3JlYXRlIGl0IChzbyBpdCdsbCBiZSB0aGUgbmV3IHZlcnNpb24pLlxuICAgICAgLy8gVGhpcyBhdm9pZHMgdGhlIG5lZWQgdG8gcmVsb2FkIE9ic2lkaWFuIHRvIHVwZGF0ZSB0aGUgcGx1Z2luLlxuICAgICAgYXdhaXQgbGVhZi5zZXRWaWV3U3RhdGUoeyB0eXBlOiAnZW1wdHknIH0pO1xuICAgICAgYnJlYWs7XG4gICAgfVxuICAgIChsZWFmID8/IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRMZWZ0TGVhZihmYWxzZSkpLnNldFZpZXdTdGF0ZSh7XG4gICAgICB0eXBlOiBSZWNlbnRGaWxlc0xpc3RWaWV3VHlwZSxcbiAgICAgIGFjdGl2ZTogdHJ1ZSxcbiAgICB9KTtcbiAgfTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGhhbmRsZVJlbmFtZSA9IGFzeW5jIChcbiAgICBmaWxlOiBUQWJzdHJhY3RGaWxlLFxuICAgIG9sZFBhdGg6IHN0cmluZyxcbiAgKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgY29uc3QgZW50cnkgPSB0aGlzLmRhdGEucmVjZW50RmlsZXMuZmluZChcbiAgICAgIChyZWNlbnRGaWxlKSA9PiByZWNlbnRGaWxlLnBhdGggPT09IG9sZFBhdGgsXG4gICAgKTtcbiAgICBpZiAoZW50cnkpIHtcbiAgICAgIGVudHJ5LnBhdGggPSBmaWxlLnBhdGg7XG4gICAgICBlbnRyeS5iYXNlbmFtZSA9IHRoaXMudHJpbUV4dGVuc2lvbihmaWxlLm5hbWUpO1xuICAgICAgdGhpcy52aWV3LnJlZHJhdygpO1xuICAgICAgYXdhaXQgdGhpcy5zYXZlRGF0YSgpO1xuICAgIH1cbiAgfTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGhhbmRsZURlbGV0ZSA9IGFzeW5jIChcbiAgICBmaWxlOiBUQWJzdHJhY3RGaWxlLFxuICApOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBjb25zdCBiZWZvcmVMZW4gPSB0aGlzLmRhdGEucmVjZW50RmlsZXMubGVuZ3RoO1xuICAgIHRoaXMuZGF0YS5yZWNlbnRGaWxlcyA9IHRoaXMuZGF0YS5yZWNlbnRGaWxlcy5maWx0ZXIoXG4gICAgICAocmVjZW50RmlsZSkgPT4gcmVjZW50RmlsZS5wYXRoICE9PSBmaWxlLnBhdGgsXG4gICAgKTtcblxuICAgIGlmIChiZWZvcmVMZW4gIT09IHRoaXMuZGF0YS5yZWNlbnRGaWxlcy5sZW5ndGgpIHtcbiAgICAgIHRoaXMudmlldy5yZWRyYXcoKTtcbiAgICAgIGF3YWl0IHRoaXMuc2F2ZURhdGEoKTtcbiAgICB9XG4gIH07XG5cbiAgLy8gdHJpbUV4dGVuc2lvbiBjYW4gYmUgdXNlZCB0byB0dXJuIGEgZmlsZW5hbWUgaW50byBhIGJhc2VuYW1lIHdoZW5cbiAgLy8gaW50ZXJhY3Rpbmcgd2l0aCBhIFRBYnN0cmFjdEZpbGUgdGhhdCBkb2VzIG5vdCBoYXZlIGEgYmFzZW5hbWUgcHJvcGVydHkuXG4gIC8vIHByaXZhdGUgcmVhZG9ubHkgdHJpbUV4dGVuc2lvbiA9IChuYW1lOiBzdHJpbmcpOiBzdHJpbmcgPT4gbmFtZS5zcGxpdCgnLicpWzBdO1xuICAvLyBmcm9tOiBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNDI1MDQwOC82MTc4NjRcbiAgcHJpdmF0ZSByZWFkb25seSB0cmltRXh0ZW5zaW9uID0gKG5hbWU6IHN0cmluZyk6IHN0cmluZyA9PlxuICAgIG5hbWUucmVwbGFjZSgvXFwuW14vLl0rJC8sICcnKTtcbn1cblxuY2xhc3MgUmVjZW50RmlsZXNTZXR0aW5nVGFiIGV4dGVuZHMgUGx1Z2luU2V0dGluZ1RhYiB7XG4gIHByaXZhdGUgcmVhZG9ubHkgcGx1Z2luOiBSZWNlbnRGaWxlc1BsdWdpbjtcblxuICBjb25zdHJ1Y3RvcihhcHA6IEFwcCwgcGx1Z2luOiBSZWNlbnRGaWxlc1BsdWdpbikge1xuICAgIHN1cGVyKGFwcCwgcGx1Z2luKTtcbiAgICB0aGlzLnBsdWdpbiA9IHBsdWdpbjtcbiAgfVxuXG4gIHB1YmxpYyBkaXNwbGF5KCk6IHZvaWQge1xuICAgIGNvbnN0IHsgY29udGFpbmVyRWwgfSA9IHRoaXM7XG4gICAgY29udGFpbmVyRWwuZW1wdHkoKTtcbiAgICBjb250YWluZXJFbC5jcmVhdGVFbCgnaDInLCB7IHRleHQ6ICdSZWNlbnQgRmlsZXMgTGlzdCcgfSk7XG5cbiAgICBjb25zdCBmcmFnbWVudCA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtcbiAgICBjb25zdCBsaW5rID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICAgIGxpbmsuaHJlZiA9XG4gICAgICAnaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9HdWlkZS9SZWd1bGFyX0V4cHJlc3Npb25zI3dyaXRpbmdfYV9yZWd1bGFyX2V4cHJlc3Npb25fcGF0dGVybic7XG4gICAgbGluay50ZXh0ID0gJ01ETiAtIFJlZ3VsYXIgZXhwcmVzc2lvbnMnO1xuICAgIGZyYWdtZW50LmFwcGVuZCgnUmVnRXhwIHBhdHRlcm5zIHRvIGlnbm9yZS4gT25lIHBhdHRlcm4gcGVyIGxpbmUuIFNlZSAnKTtcbiAgICBmcmFnbWVudC5hcHBlbmQobGluayk7XG4gICAgZnJhZ21lbnQuYXBwZW5kKCcgZm9yIGhlbHAuJyk7XG5cbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcbiAgICAgIC5zZXROYW1lKCdPbWl0dGVkIHBhdGhuYW1lIHBhdHRlcm5zJylcbiAgICAgIC5zZXREZXNjKGZyYWdtZW50KVxuICAgICAgLmFkZFRleHRBcmVhKCh0ZXh0QXJlYSkgPT4ge1xuICAgICAgICB0ZXh0QXJlYS5pbnB1dEVsLnNldEF0dHIoJ3Jvd3MnLCA2KTtcbiAgICAgICAgdGV4dEFyZWFcbiAgICAgICAgICAuc2V0UGxhY2Vob2xkZXIoJ15kYWlseS9cXG5cXFxcLnBuZyRcXG5mb29iYXIuKmJheicpXG4gICAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLmRhdGEub21pdHRlZFBhdGhzLmpvaW4oJ1xcbicpKTtcbiAgICAgICAgdGV4dEFyZWEuaW5wdXRFbC5vbmJsdXIgPSAoZTogRm9jdXNFdmVudCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHBhdHRlcm5zID0gKGUudGFyZ2V0IGFzIEhUTUxJbnB1dEVsZW1lbnQpLnZhbHVlO1xuICAgICAgICAgIHRoaXMucGx1Z2luLmRhdGEub21pdHRlZFBhdGhzID0gcGF0dGVybnMuc3BsaXQoJ1xcbicpO1xuICAgICAgICAgIHRoaXMucGx1Z2luLnBydW5lT21pdHRlZEZpbGVzKCk7XG4gICAgICAgICAgdGhpcy5wbHVnaW4udmlldy5yZWRyYXcoKTtcbiAgICAgICAgfTtcbiAgICAgIH0pO1xuXG4gICAgbmV3IFNldHRpbmcoY29udGFpbmVyRWwpXG4gICAgICAuc2V0TmFtZSgnTGlzdCBsZW5ndGgnKVxuICAgICAgLnNldERlc2MoJ01heGltdW0gbnVtYmVyIG9mIGZpbGVuYW1lcyB0byBrZWVwIGluIHRoZSBsaXN0LicpXG4gICAgICAuYWRkVGV4dCgodGV4dCkgPT4ge1xuICAgICAgICB0ZXh0LmlucHV0RWwuc2V0QXR0cigndHlwZScsICdudW1iZXInKTtcbiAgICAgICAgdGV4dC5pbnB1dEVsLnNldEF0dHIoJ3BsYWNlaG9sZGVyJywgZGVmYXVsdE1heExlbmd0aCk7XG4gICAgICAgIHRleHRcbiAgICAgICAgICAuc2V0VmFsdWUodGhpcy5wbHVnaW4uZGF0YS5tYXhMZW5ndGg/LnRvU3RyaW5nKCkpXG4gICAgICAgICAgLm9uQ2hhbmdlKCh2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gcGFyc2VJbnQodmFsdWUsIDEwKTtcbiAgICAgICAgICAgIGlmICghTnVtYmVyLmlzTmFOKHBhcnNlZCkgJiYgcGFyc2VkIDw9IDApIHtcbiAgICAgICAgICAgICAgbmV3IE5vdGljZSgnTGlzdCBsZW5ndGggbXVzdCBiZSBhIHBvc2l0aXZlIGludGVnZXInKTtcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB0ZXh0LmlucHV0RWwub25ibHVyID0gKGU6IEZvY3VzRXZlbnQpID0+IHtcbiAgICAgICAgICBjb25zdCBtYXhmaWxlcyA9IChlLnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50KS52YWx1ZTtcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUludChtYXhmaWxlcywgMTApO1xuICAgICAgICAgIHRoaXMucGx1Z2luLmRhdGEubWF4TGVuZ3RoID0gcGFyc2VkO1xuICAgICAgICAgIHRoaXMucGx1Z2luLnBydW5lTGVuZ3RoKCk7XG4gICAgICAgICAgdGhpcy5wbHVnaW4udmlldy5yZWRyYXcoKTtcbiAgICAgICAgfTtcbiAgICAgIH0pO1xuXG4gICAgY29uc3QgZGl2ID0gY29udGFpbmVyRWwuY3JlYXRlRWwoJ2RpdicsIHtcbiAgICAgIGNsczogJ3JlY2VudC1maWxlcy1kb25hdGlvbicsXG4gICAgfSk7XG5cbiAgICBjb25zdCBkb25hdGVUZXh0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgncCcpO1xuICAgIGRvbmF0ZVRleHQuYXBwZW5kVGV4dChcbiAgICAgICdJZiB0aGlzIHBsdWdpbiBhZGRzIHZhbHVlIGZvciB5b3UgYW5kIHlvdSB3b3VsZCBsaWtlIHRvIGhlbHAgc3VwcG9ydCAnICtcbiAgICAgICAgJ2NvbnRpbnVlZCBkZXZlbG9wbWVudCwgcGxlYXNlIHVzZSB0aGUgYnV0dG9ucyBiZWxvdzonLFxuICAgICk7XG4gICAgZGl2LmFwcGVuZENoaWxkKGRvbmF0ZVRleHQpO1xuXG4gICAgY29uc3QgcGFyc2VyID0gbmV3IERPTVBhcnNlcigpO1xuXG4gICAgZGl2LmFwcGVuZENoaWxkKFxuICAgICAgY3JlYXRlRG9uYXRlQnV0dG9uKFxuICAgICAgICAnaHR0cHM6Ly9wYXlwYWwubWUvdGdyb3NpbmdlcicsXG4gICAgICAgIHBhcnNlci5wYXJzZUZyb21TdHJpbmcocGF5cGFsLCAndGV4dC94bWwnKS5kb2N1bWVudEVsZW1lbnQsXG4gICAgICApLFxuICAgICk7XG5cbiAgICBkaXYuYXBwZW5kQ2hpbGQoXG4gICAgICBjcmVhdGVEb25hdGVCdXR0b24oXG4gICAgICAgICdodHRwczovL3d3dy5idXltZWFjb2ZmZWUuY29tL3Rncm9zaW5nZXInLFxuICAgICAgICBwYXJzZXIucGFyc2VGcm9tU3RyaW5nKGJ1eU1lQUNvZmZlZSwgJ3RleHQveG1sJykuZG9jdW1lbnRFbGVtZW50LFxuICAgICAgKSxcbiAgICApO1xuICB9XG59XG5cbmNvbnN0IGNyZWF0ZURvbmF0ZUJ1dHRvbiA9IChsaW5rOiBzdHJpbmcsIGltZzogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudCA9PiB7XG4gIGNvbnN0IGEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gIGEuc2V0QXR0cmlidXRlKCdocmVmJywgbGluayk7XG4gIGEuYWRkQ2xhc3MoJ3JlY2VudC1maWxlcy1kb25hdGUtYnV0dG9uJyk7XG4gIGEuYXBwZW5kQ2hpbGQoaW1nKTtcbiAgcmV0dXJuIGE7XG59O1xuXG5jb25zdCBzd2VlcEljb24gPSBgXG48c3ZnIGZpbGw9XCJjdXJyZW50Q29sb3JcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiB2ZXJzaW9uPVwiMS4xXCIgdmlld0JveD1cIjAgMCA1MTIgNTEyXCIgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiPlxuICA8cGF0aCBkPVwibTQ5NS43MiAxLjU4MmMtNy40NTYtMy42OTEtMTYuNDIxLTAuNzAzLTIwLjE0MiA2LjY5NGwtMTM2LjkyIDI3NC4wOC0yNi44MTgtMTMuNDMzYy0yMi4yMDctMTEuMTE4LTQ5LjI3Ny0yLjA2NS02MC4zOTYgMjAuMDgzbC02LjcxMyAxMy40MDUgMTYwLjk2IDgwLjYxNiA2LjcxMy0xMy40MTFjMTEuMDg3LTIyLjE0MyAyLjIyNy00OS4xOC0yMC4wODMtNjAuMzgxbC0yNi44MjMtMTMuNDM1IDEzNi45Mi0yNzQuMDhjMy43MDYtNy40MTIgMC43MDMtMTYuNDIxLTYuNjk0LTIwLjE0MXpcIi8+XG4gIDxjaXJjbGUgY3g9XCIxNzNcIiBjeT1cIjQ5N1wiIHI9XCIxNVwiLz5cbiAgPGNpcmNsZSBjeD1cIjIzXCIgY3k9XCI0MDdcIiByPVwiMTVcIi8+XG4gIDxjaXJjbGUgY3g9XCI4M1wiIGN5PVwiNDM3XCIgcj1cIjE1XCIvPlxuICA8cGF0aCBkPVwibTExMyA0ODJoLTYwYy04LjI3NiAwLTE1LTYuNzI0LTE1LTE1IDAtOC4yOTEtNi43MDktMTUtMTUtMTVzLTE1IDYuNzA5LTE1IDE1YzAgMjQuODE0IDIwLjE4NiA0NSA0NSA0NWg2MGM4LjI5MSAwIDE1LTYuNzA5IDE1LTE1cy02LjcwOS0xNS0xNS0xNXpcIi8+XG4gIDxwYXRoIGQ9XCJtMTA4LjY0IDM4OC4wN2MtNi41NjMgMC44Mi0xMS44MDcgNS44NDUtMTIuOTIgMTIuMzQ5LTEuMTEzIDYuNTE5IDIuMTUzIDEyLjk5MyA4LjA1NyAxNS45NTJsNzEuNjc1IDM1Ljg4OWMxMi45MzUgNi40NzUgMjcuMjMxIDkuMDUzIDQxLjE3NyA3LjU3My0xLjY0MSA2LjY1IDEuNDc5IDEzLjc4NCA3Ljg1MiAxNi45OTJsNjcuMDYxIDMzLjU4OWM1LjYzNiAyLjc4IDEyLjE2OSAxLjggMTYuNjg1LTIuMTk3IDIuMzQ3LTIuMDkxIDUzLjQzNi00OC4wNTYgODMuMy05OC43MThsLTE2MS42LTgwLjk0Yy0zNi4yMDggNDguMTA5LTEyMC4zNiA1OS4zOS0xMjEuMjggNTkuNTExelwiLz5cbjwvc3ZnPmA7XG5cbmNvbnN0IGJ1eU1lQUNvZmZlZSA9IGBcbjxzdmcgd2lkdGg9XCIxNTBcIiBoZWlnaHQ9XCI0MlwiIHZpZXdCb3g9XCIwIDAgMjYwIDczXCIgZmlsbD1cIm5vbmVcIiB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCI+XG48cGF0aCBkPVwiTTAgMTEuNjhDMCA1LjIyOTMyIDUuMjI5MzEgMCAxMS42OCAwSDI0OC4yQzI1NC42NTEgMCAyNTkuODggNS4yMjkzMSAyNTkuODggMTEuNjhWNjEuMzJDMjU5Ljg4IDY3Ljc3MDcgMjU0LjY1MSA3MyAyNDguMiA3M0gxMS42OEM1LjIyOTMxIDczIDAgNjcuNzcwNyAwIDYxLjMyVjExLjY4WlwiIGZpbGw9XCIjRkZERDAwXCIvPlxuPHBhdGggZD1cIk01Mi4yNTY2IDI0LjAwNzhMNTIuMjI0NiAyMy45ODg5TDUyLjE1MDQgMjMuOTY2M0M1Mi4xODAyIDIzLjk5MTUgNTIuMjE3NiAyNC4wMDYxIDUyLjI1NjYgMjQuMDA3OFpcIiBmaWxsPVwiIzBEMEMyMlwiLz5cbjxwYXRoIGQ9XCJNNTIuNzI0OCAyNy4zNDU3TDUyLjY4OTUgMjcuMzU1Nkw1Mi43MjQ4IDI3LjM0NTdaXCIgZmlsbD1cIiMwRDBDMjJcIi8+XG48cGF0aCBkPVwiTTUyLjI3MDEgMjQuMDAyNEM1Mi4yNjYgMjQuMDAxOSA1Mi4yNjE5IDI0LjAwMDkgNTIuMjU4IDIzLjk5OTVDNTIuMjU3OCAyNC4wMDIyIDUyLjI1NzggMjQuMDA0OSA1Mi4yNTggMjQuMDA3NkM1Mi4yNjI0IDI0LjAwNyA1Mi4yNjY2IDI0LjAwNTIgNTIuMjcwMSAyNC4wMDI0WlwiIGZpbGw9XCIjMEQwQzIyXCIvPlxuPHBhdGggZD1cIk01Mi4yNTc4IDI0LjAwOTRINTIuMjY0M1YyNC4wMDU0TDUyLjI1NzggMjQuMDA5NFpcIiBmaWxsPVwiIzBEMEMyMlwiLz5cbjxwYXRoIGQ9XCJNNTIuNjk3MyAyNy4zMzk0TDUyLjc1MTMgMjcuMzA4Nkw1Mi43NzE0IDI3LjI5NzNMNTIuNzg5NyAyNy4yNzc4QzUyLjc1NTQgMjcuMjkyNiA1Mi43MjQxIDI3LjMxMzUgNTIuNjk3MyAyNy4zMzk0WlwiIGZpbGw9XCIjMEQwQzIyXCIvPlxuPHBhdGggZD1cIk01Mi4zNDg0IDI0LjA4MTJMNTIuMjk1NiAyNC4wMzFMNTIuMjU5OCAyNC4wMTE1QzUyLjI3OSAyNC4wNDU0IDUyLjMxMDggMjQuMDcwNSA1Mi4zNDg0IDI0LjA4MTJaXCIgZmlsbD1cIiMwRDBDMjJcIi8+XG48cGF0aCBkPVwiTTM5LjA2ODQgNTYuNDY5QzM5LjAyNjIgNTYuNDg3MiAzOC45ODkzIDU2LjUxNTggMzguOTYwOSA1Ni41NTJMMzguOTk0MyA1Ni41MzA2QzM5LjAxNjkgNTYuNTA5OCAzOS4wNDg5IDU2LjQ4NTMgMzkuMDY4NCA1Ni40NjlaXCIgZmlsbD1cIiMwRDBDMjJcIi8+XG48cGF0aCBkPVwiTTQ2Ljc4MDIgNTQuOTUxOEM0Ni43ODAyIDU0LjkwNDEgNDYuNzU2OSA1NC45MTI5IDQ2Ljc2MjYgNTUuMDgyNkM0Ni43NjI2IDU1LjA2ODcgNDYuNzY4MyA1NS4wNTQ5IDQ2Ljc3MDggNTUuMDQxN0M0Ni43NzM5IDU1LjAxMTUgNDYuNzc2NCA1NC45ODIgNDYuNzgwMiA1NC45NTE4WlwiIGZpbGw9XCIjMEQwQzIyXCIvPlxuPHBhdGggZD1cIk00NS45ODQ0IDU2LjQ2OUM0NS45NDIyIDU2LjQ4NzIgNDUuOTA1MyA1Ni41MTU4IDQ1Ljg3NyA1Ni41NTJMNDUuOTEwMyA1Ni41MzA2QzQ1LjkzMjkgNTYuNTA5OCA0NS45NjQ5IDU2LjQ4NTMgNDUuOTg0NCA1Ni40NjlaXCIgZmlsbD1cIiMwRDBDMjJcIi8+XG48cGF0aCBkPVwiTTMzLjYzMDcgNTYuODMwMUMzMy41OTg3IDU2LjgwMjMgMzMuNTU5NSA1Ni43ODQgMzMuNTE3NiA1Ni43NzczQzMzLjU1MTUgNTYuNzkzNyAzMy41ODU1IDU2LjgxIDMzLjYwODEgNTYuODIyNkwzMy42MzA3IDU2LjgzMDFaXCIgZmlsbD1cIiMwRDBDMjJcIi8+XG48cGF0aCBkPVwiTTMyLjQxMTggNTUuNjU5OEMzMi40MDY4IDU1LjYxMDMgMzIuMzkxNiA1NS41NjI0IDMyLjM2NzIgNTUuNTE5QzMyLjM4NDUgNTUuNTY0MiAzMi4zOTkgNTUuNjEwNCAzMi40MTA2IDU1LjY1NzNMMzIuNDExOCA1NS42NTk4WlwiIGZpbGw9XCIjMEQwQzIyXCIvPlxuPHBhdGggZD1cIk00MC42MjMgMzQuNzIyMUMzOC45NDQ5IDM1LjQ0MDUgMzcuMDQwNCAzNi4yNTUxIDM0LjU3MjIgMzYuMjU1MUMzMy41Mzk3IDM2LjI1MzEgMzIuNTEyMiAzNi4xMTE0IDMxLjUxNzYgMzUuODM0TDMzLjIyNDcgNTMuMzYwNUMzMy4yODUxIDU0LjA5MyAzMy42MTg4IDU0Ljc3NjEgMzQuMTU5NSA1NS4yNzM5QzM0LjcwMDMgNTUuNzcxOCAzNS40MDg1IDU2LjA0ODIgMzYuMTQzNSA1Ni4wNDhDMzYuMTQzNSA1Ni4wNDggMzguNTY0IDU2LjE3MzcgMzkuMzcxNiA1Ni4xNzM3QzQwLjI0MDkgNTYuMTczNyA0Mi44NDc0IDU2LjA0OCA0Mi44NDc0IDU2LjA0OEM0My41ODIzIDU2LjA0OCA0NC4yOTA0IDU1Ljc3MTYgNDQuODMxIDU1LjI3MzdDNDUuMzcxNiA1NC43NzU5IDQ1LjcwNTIgNTQuMDkyOSA0NS43NjU2IDUzLjM2MDVMNDcuNTk0IDMzLjk5M0M0Ni43NzY5IDMzLjcxNCA0NS45NTIzIDMzLjUyODYgNDUuMDIyNyAzMy41Mjg2QzQzLjQxNSAzMy41Mjc5IDQyLjExOTYgMzQuMDgxNyA0MC42MjMgMzQuNzIyMVpcIiBmaWxsPVwid2hpdGVcIi8+XG48cGF0aCBkPVwiTTI2LjIzNDQgMjcuMjQ0OUwyNi4yNjMzIDI3LjI3MTlMMjYuMjgyMSAyNy4yODMyQzI2LjI2NzYgMjcuMjY4OCAyNi4yNTE2IDI3LjI1NTkgMjYuMjM0NCAyNy4yNDQ5WlwiIGZpbGw9XCIjMEQwQzIyXCIvPlxuPHBhdGggZD1cIk01NS40OTA2IDI1LjYyNzRMNTUuMjMzNiAyNC4zMzA3QzU1LjAwMjkgMjMuMTY3MyA1NC40NzkzIDIyLjA2OCA1My4yODUxIDIxLjY0NzVDNTIuOTAyNCAyMS41MTMgNTIuNDY4IDIxLjQ1NTIgNTIuMTc0NSAyMS4xNzY4QzUxLjg4MSAyMC44OTgzIDUxLjc5NDMgMjAuNDY1OSA1MS43MjY0IDIwLjA2NDlDNTEuNjAwNyAxOS4zMjg5IDUxLjQ4MjUgMTguNTkyMyA1MS4zNTM3IDE3Ljg1NzVDNTEuMjQyNCAxNy4yMjU5IDUxLjE1NDQgMTYuNTE2MyA1MC44NjQ3IDE1LjkzNjhDNTAuNDg3NiAxNS4xNTg2IDQ5LjcwNSAxNC43MDM2IDQ4LjkyNjkgMTQuNDAyNUM0OC41MjgyIDE0LjI1MzcgNDguMTIxMyAxNC4xMjc4IDQ3LjcwODIgMTQuMDI1NEM0NS43NjQyIDEzLjUxMjUgNDMuNzIwMiAxMy4zMjQgNDEuNzIwMiAxMy4yMTY1QzM5LjMxOTcgMTMuMDg0IDM2LjkxMjggMTMuMTIzOSAzNC41MTggMTMuMzM1OUMzMi43MzU1IDEzLjQ5ODEgMzAuODU4MSAxMy42OTQyIDI5LjE2NDIgMTQuMzEwOEMyOC41NDUxIDE0LjUzNjQgMjcuOTA3MSAxNC44MDczIDI3LjQzNjQgMTUuMjg1NkMyNi44NTg3IDE1Ljg3MzMgMjYuNjcwMiAxNi43ODIxIDI3LjA5MTkgMTcuNTE1QzI3LjM5MTcgMTguMDM1NCAyNy44OTk2IDE4LjQwMzEgMjguNDM4MiAxOC42NDYzQzI5LjEzOTggMTguOTU5NyAyOS44NzI2IDE5LjE5ODIgMzAuNjI0MiAxOS4zNTc4QzMyLjcxNzIgMTkuODIwNCAzNC44ODUgMjAuMDAyMSAzNy4wMjMzIDIwLjA3OTRDMzkuMzkzMiAyMC4xNzUgNDEuNzY3IDIwLjA5NzUgNDQuMTI1NiAxOS44NDc0QzQ0LjcwODkgMTkuNzgzMyA0NS4yOTExIDE5LjcwNjQgNDUuODcyMyAxOS42MTY4QzQ2LjU1NjggMTkuNTExOCA0Ni45OTYxIDE4LjYxNjggNDYuNzk0MyAxNy45OTMzQzQ2LjU1MyAxNy4yNDc5IDQ1LjkwNDQgMTYuOTU4NyA0NS4xNzA5IDE3LjA3MTJDNDUuMDYyOCAxNy4wODgyIDQ0Ljk1NTMgMTcuMTAzOSA0NC44NDcyIDE3LjExOTZMNDQuNzY5MiAxNy4xMzFDNDQuNTIwOCAxNy4xNjI0IDQ0LjI3MjMgMTcuMTkxNyA0NC4wMjM4IDE3LjIxOUM0My41MTA1IDE3LjI3NDMgNDIuOTk1OSAxNy4zMTk1IDQyLjQ4MDEgMTcuMzU0N0M0MS4zMjQ5IDE3LjQzNTIgNDAuMTY2NSAxNy40NzIyIDM5LjAwODggMTcuNDc0MUMzNy44NzEyIDE3LjQ3NDEgMzYuNzMyOSAxNy40NDIxIDM1LjU5NzggMTcuMzY3M0MzNS4wNzk5IDE3LjMzMzMgMzQuNTYzMiAxNy4yOTAyIDM0LjA0NzggMTcuMjM3OEMzMy44MTM0IDE3LjIxMzMgMzMuNTc5NiAxNy4xODc1IDMzLjM0NTggMTcuMTU4NkwzMy4xMjMzIDE3LjEzMDNMMzMuMDc0OSAxNy4xMjM0TDMyLjg0NDIgMTcuMDkwMUMzMi4zNzI4IDE3LjAxOTEgMzEuOTAxNCAxNi45Mzc0IDMxLjQzNSAxNi44Mzg3QzMxLjM4OCAxNi44MjgzIDMxLjM0NTkgMTYuODAyMSAzMS4zMTU3IDE2Ljc2NDVDMzEuMjg1NiAxNi43MjY5IDMxLjI2OTEgMTYuNjgwMSAzMS4yNjkxIDE2LjYzMTlDMzEuMjY5MSAxNi41ODM3IDMxLjI4NTYgMTYuNTM2OSAzMS4zMTU3IDE2LjQ5OTNDMzEuMzQ1OSAxNi40NjE3IDMxLjM4OCAxNi40MzU2IDMxLjQzNSAxNi40MjUxSDMxLjQ0MzhDMzEuODQ4IDE2LjMzOSAzMi4yNTUzIDE2LjI2NTUgMzIuNjYzOCAxNi4yMDE0QzMyLjggMTYuMTggMzIuOTM2NiAxNi4xNTkgMzMuMDczNiAxNi4xMzg1SDMzLjA3NzRDMzMuMzMzMiAxNi4xMjE1IDMzLjU5MDMgMTYuMDc1NyAzMy44NDQ4IDE2LjA0NTVDMzYuMDU5NSAxNS44MTUxIDM4LjI4NzQgMTUuNzM2NiA0MC41MTI4IDE1LjgxMDRDNDEuNTkzMyAxNS44NDE5IDQyLjY3MzEgMTUuOTA1MyA0My43NDg1IDE2LjAxNDdDNDMuOTc5OCAxNi4wMzg2IDQ0LjIwOTggMTYuMDYzNyA0NC40Mzk5IDE2LjA5MkM0NC41Mjc5IDE2LjEwMjcgNDQuNjE2NSAxNi4xMTUzIDQ0LjcwNTEgMTYuMTI1OUw0NC44ODM2IDE2LjE1MTdDNDUuNDA0IDE2LjIyOTIgNDUuOTIxNyAxNi4zMjMzIDQ2LjQzNjcgMTYuNDMzOUM0Ny4xOTk3IDE2LjU5OTkgNDguMTc5NiAxNi42NTM5IDQ4LjUxOSAxNy40ODk4QzQ4LjYyNzEgMTcuNzU1MSA0OC42NzYxIDE4LjA0OTkgNDguNzM1OSAxOC4zMjgzTDQ4LjgxMTkgMTguNjgzNEM0OC44MTM5IDE4LjY4OTggNDguODE1NCAxOC42OTYzIDQ4LjgxNjMgMTguNzAyOUM0OC45OTYxIDE5LjU0MDkgNDkuMTc2IDIwLjM3OSA0OS4zNTYyIDIxLjIxN0M0OS4zNjk0IDIxLjI3ODkgNDkuMzY5NyAyMS4zNDI5IDQ5LjM1NzEgMjEuNDA0OUM0OS4zNDQ1IDIxLjQ2NjkgNDkuMzE5MyAyMS41MjU3IDQ5LjI4MjkgMjEuNTc3NkM0OS4yNDY2IDIxLjYyOTQgNDkuMiAyMS42NzMyIDQ5LjE0NiAyMS43MDYyQzQ5LjA5MiAyMS43MzkyIDQ5LjAzMTcgMjEuNzYwOCA0OC45NjkgMjEuNzY5NUg0OC45NjRMNDguODU0IDIxLjc4NDZMNDguNzQ1MyAyMS43OTlDNDguNDAwOSAyMS44NDM5IDQ4LjA1NiAyMS44ODU4IDQ3LjcxMDcgMjEuOTI0N0M0Ny4wMzA3IDIyLjAwMjIgNDYuMzQ5NiAyMi4wNjkzIDQ1LjY2NzQgMjIuMTI1OUM0NC4zMTE5IDIyLjIzODYgNDIuOTUzNiAyMi4zMTI1IDQxLjU5MjcgMjIuMzQ3N0M0MC44OTkyIDIyLjM2NjIgNDAuMjA1OSAyMi4zNzQ4IDM5LjUxMjkgMjIuMzczNUMzNi43NTQzIDIyLjM3MTMgMzMuOTk4MSAyMi4yMTEgMzEuMjU3OCAyMS44OTMzQzMwLjk2MTEgMjEuODU4MSAzMC42NjQ1IDIxLjgyMDQgMzAuMzY3OCAyMS43ODIxQzMwLjU5NzggMjEuODExNiAzMC4yMDA2IDIxLjc1OTQgMzAuMTIwMiAyMS43NDgxQzI5LjkzMTYgMjEuNzIxNyAyOS43NDMxIDIxLjY5NDMgMjkuNTU0NSAyMS42NjU4QzI4LjkyMTYgMjEuNTcwOSAyOC4yOTI0IDIxLjQ1NCAyNy42NjA3IDIxLjM1MTVDMjYuODk3MSAyMS4yMjU4IDI2LjE2NjcgMjEuMjg4NyAyNS40NzYgMjEuNjY1OEMyNC45MDkgMjEuOTc2IDI0LjQ1MDEgMjIuNDUxOCAyNC4xNjA1IDIzLjAyOTdDMjMuODYyNiAyMy42NDU2IDIzLjc3MzkgMjQuMzE2MyAyMy42NDA3IDI0Ljk3ODFDMjMuNTA3NCAyNS42Mzk5IDIzLjMgMjYuMzUyMSAyMy4zNzg2IDI3LjAzMTVDMjMuNTQ3NyAyOC40OTc5IDI0LjU3MjggMjkuNjg5NSAyNi4wNDczIDI5Ljk1NkMyNy40MzQ1IDMwLjIwNzQgMjguODI5MiAzMC40MTExIDMwLjIyNzYgMzAuNTg0NkMzNS43MjEyIDMxLjI1NzQgNDEuMjcxMSAzMS4zMzc5IDQ2Ljc4MTggMzAuODI0N0M0Ny4yMzA1IDMwLjc4MjggNDcuNjc4NyAzMC43MzcxIDQ4LjEyNjIgMzAuNjg3NkM0OC4yNjYgMzAuNjcyMyA0OC40MDc0IDMwLjY4ODQgNDguNTQwMSAzMC43MzQ4QzQ4LjY3MjkgMzAuNzgxMiA0OC43OTM2IDMwLjg1NjYgNDguODkzNCAzMC45NTU3QzQ4Ljk5MzIgMzEuMDU0OCA0OS4wNjk1IDMxLjE3NDkgNDkuMTE2OSAzMS4zMDczQzQ5LjE2NDIgMzEuNDM5NyA0OS4xODE0IDMxLjU4MTEgNDkuMTY3IDMxLjcyMDlMNDkuMDI3NSAzMy4wNzczQzQ4Ljc0NjMgMzUuODE4MSA0OC40NjUyIDM4LjU1ODcgNDguMTg0IDQxLjI5OUM0Ny44OTA3IDQ0LjE3NjkgNDcuNTk1NSA0Ny4wNTQ1IDQ3LjI5ODQgNDkuOTMxOUM0Ny4yMTQ2IDUwLjc0MjIgNDcuMTMwOCA1MS41NTI0IDQ3LjA0NyA1Mi4zNjI0QzQ2Ljk2NjYgNTMuMTYgNDYuOTU1MiA1My45ODI3IDQ2LjgwMzggNTQuNzcwOUM0Ni41NjQ5IDU2LjAxMDMgNDUuNzI1OCA1Ni43NzE1IDQ0LjUwMTUgNTcuMDQ5OUM0My4zNzk4IDU3LjMwNTIgNDIuMjMzOSA1Ny40MzkyIDQxLjA4MzYgNTcuNDQ5N0MzOS44MDgzIDU3LjQ1NjYgMzguNTMzNiA1Ny40IDM3LjI1ODMgNTcuNDA2OUMzNS44OTcgNTcuNDE0NSAzNC4yMjk1IDU3LjI4ODcgMzMuMTc4NiA1Ni4yNzU2QzMyLjI1NTMgNTUuMzg1NiAzMi4xMjc3IDUzLjk5MjEgMzIuMDAyIDUyLjc4NzJDMzEuODM0NCA1MS4xOTIgMzEuNjY4MiA0OS41OTcxIDMxLjUwMzYgNDguMDAyM0wzMC41Nzk2IDM5LjEzNDRMMjkuOTgxOSAzMy4zOTY2QzI5Ljk3MTggMzMuMzAxNyAyOS45NjE4IDMzLjIwOCAyOS45NTI0IDMzLjExMjVDMjkuODgwNyAzMi40MjggMjkuMzk2MSAzMS43NTggMjguNjMyNCAzMS43OTI2QzI3Ljk3ODggMzEuODIxNSAyNy4yMzU5IDMyLjM3NzEgMjcuMzEyNSAzMy4xMTI1TDI3Ljc1NTcgMzcuMzY2NEwyOC42NzIgNDYuMTY1N0MyOC45MzMxIDQ4LjY2NTIgMjkuMTkzNSA1MS4xNjUgMjkuNDUzMyA1My42NjUzQzI5LjUwMzYgNTQuMTQ0MiAyOS41NTA3IDU0LjYyNDQgMjkuNjAzNSA1NS4xMDM0QzI5Ljg5MDggNTcuNzIwNSAzMS44ODk1IDU5LjEzMSAzNC4zNjQ2IDU5LjUyODJDMzUuODEwMiA1OS43NjA3IDM3LjI5MSA1OS44MDg1IDM4Ljc1OCA1OS44MzI0QzQwLjYzODYgNTkuODYyNiA0Mi41MzggNTkuOTM0OCA0NC4zODc3IDU5LjU5NDJDNDcuMTI4NyA1OS4wOTE0IDQ5LjE4NTMgNTcuMjYxMSA0OS40Nzg4IDU0LjQyMkM0OS41NjI2IDUzLjYwMjQgNDkuNjQ2NCA1Mi43ODI2IDQ5LjczMDIgNTEuOTYyNkM1MC4wMDg4IDQ5LjI1MDcgNTAuMjg3MSA0Ni41Mzg2IDUwLjU2NDkgNDMuODI2M0w1MS40NzM3IDM0Ljk2NDFMNTEuODkwNCAzMC45MDI2QzUxLjkxMTIgMzAuNzAxMiA1MS45OTYyIDMwLjUxMTggNTIuMTMzIDMwLjM2MjVDNTIuMjY5NyAzMC4yMTMyIDUyLjQ1MDkgMzAuMTExOSA1Mi42NDk3IDMwLjA3MzZDNTMuNDMzNSAyOS45MjA4IDU0LjE4MjcgMjkuNjYgNTQuNzQwMiAyOS4wNjM1QzU1LjYyNzcgMjguMTEzOCA1NS44MDQzIDI2Ljg3NTYgNTUuNDkwNiAyNS42Mjc0Wk0yNi4wMDcxIDI2LjUwMzVDMjYuMDE5IDI2LjQ5NzkgMjUuOTk3IDI2LjYwMDMgMjUuOTg3NiAyNi42NDgxQzI1Ljk4NTcgMjYuNTc1OCAyNS45ODk1IDI2LjUxMTcgMjYuMDA3MSAyNi41MDM1Wk0yNi4wODMxIDI3LjA5MThDMjYuMDg5NCAyNy4wODc0IDI2LjEwODMgMjcuMTEyNiAyNi4xMjc4IDI3LjE0MjhDMjYuMDk4MiAyNy4xMTUxIDI2LjA3OTQgMjcuMDk0NCAyNi4wODI1IDI3LjA5MThIMjYuMDgzMVpNMjYuMTU3OSAyNy4xOTA1QzI2LjE4NSAyNy4yMzY0IDI2LjE5OTQgMjcuMjY1MyAyNi4xNTc5IDI3LjE5MDVWMjcuMTkwNVpNMjYuMzA4MiAyNy4zMTI1SDI2LjMxMTlDMjYuMzExOSAyNy4zMTY5IDI2LjMxODggMjcuMzIxMyAyNi4zMjE0IDI3LjMyNTdDMjYuMzE3MiAyNy4zMjA4IDI2LjMxMjYgMjcuMzE2NCAyNi4zMDc1IDI3LjMxMjVIMjYuMzA4MlpNNTIuNjEzMiAyNy4xMzAyQzUyLjMzMTcgMjcuMzk3OSA1MS45MDc0IDI3LjUyMjQgNTEuNDg4MiAyNy41ODQ2QzQ2Ljc4NjggMjguMjgyMyA0Mi4wMTY5IDI4LjYzNTUgMzcuMjY0IDI4LjQ3OTZDMzMuODYyNCAyOC4zNjMzIDMwLjQ5NjcgMjcuOTg1NiAyNy4xMjkgMjcuNTA5OEMyNi43OTkgMjcuNDYzMyAyNi40NDE0IDI3LjQwMyAyNi4yMTQ1IDI3LjE1OTdDMjUuNzg3MSAyNi43MDA5IDI1Ljk5NyAyNS43NzcgMjYuMTA4MyAyNS4yMjI2QzI2LjIxMDEgMjQuNzE0OCAyNi40MDUgMjQuMDM3OCAyNy4wMDkgMjMuOTY1NkMyNy45NTE4IDIzLjg1NDkgMjkuMDQ2NiAyNC4yNTI4IDI5Ljk3OTQgMjQuMzk0MkMzMS4xMDIzIDI0LjU2NTYgMzIuMjI5NSAyNC43MDI4IDMzLjM2MDkgMjQuODA1OUMzOC4xODkyIDI1LjI0NTkgNDMuMDk4NiAyNS4xNzc0IDQ3LjkwNTYgMjQuNTMzN0M0OC43ODE3IDI0LjQxNiA0OS42NTQ4IDI0LjI3OTIgNTAuNTI0NiAyNC4xMjMzQzUxLjI5OTYgMjMuOTg0NCA1Mi4xNTg4IDIzLjcyMzYgNTIuNjI3MSAyNC41MjYyQzUyLjk0ODIgMjUuMDczIDUyLjk5MSAyNS44MDQ2IDUyLjk0MTMgMjYuNDIyNUM1Mi45MjYgMjYuNjkxNyA1Mi44MDg0IDI2Ljk0NDggNTIuNjEyNiAyNy4xMzAySDUyLjYxMzJaXCIgZmlsbD1cIiMwRDBDMjJcIi8+XG48cGF0aCBmaWxsLXJ1bGU9XCJldmVub2RkXCIgY2xpcC1ydWxlPVwiZXZlbm9kZFwiIGQ9XCJNODEuMTMwMiA0MC4xOTI5QzgwLjg1NTYgNDAuNzE2OSA4MC40NzgxIDQxLjE3MzIgNzkuOTk3OCA0MS41NjA0Qzc5LjUxNzUgNDEuOTQ3OSA3OC45NTcxIDQyLjI2MzMgNzguMzE2NiA0Mi41MDYyQzc3LjY3NjEgNDIuNzQ5NyA3Ny4wMzE1IDQyLjkxMzEgNzYuMzgzNSA0Mi45OTY0Qzc1LjczNTIgNDMuMDc5OSA3NS4xMDYgNDMuMDcyNyA3NC40OTYzIDQyLjk3MzVDNzMuODg2MyA0Mi44NzQ5IDczLjM2NzQgNDIuNjczNyA3Mi45NDA4IDQyLjM2OTVMNzMuNDIxNCAzNy4zNzc5QzczLjg2MzMgMzcuMjI2MSA3NC40MTk3IDM3LjA3MDMgNzUuMDkwOSAzNi45MTA3Qzc1Ljc2MTkgMzYuNzUxMyA3Ni40NTIgMzYuNjM3MSA3Ny4xNjEzIDM2LjU2ODlDNzcuODcwNSAzNi41MDAzIDc4LjU0MTIgMzYuNTA4NCA3OS4xNzQ0IDM2LjU5MTdDNzkuODA2OCAzNi42NzUzIDgwLjMwNjUgMzYuODc2NSA4MC42NzI1IDM3LjE5NThDODAuODcwNyAzNy4zNzggODEuMDM4NyAzNy41NzU0IDgxLjE3NiAzNy43ODgzQzgxLjMxMyAzOC4wMDExIDgxLjM5NjkgMzguMjIxNCA4MS40Mjc2IDM4LjQ0OTNDODEuNTAzNyAzOS4wODc1IDgxLjQwNDcgMzkuNjY4NyA4MS4xMzAyIDQwLjE5MjlaTTc0LjE1MyAyOS41NjAyQzc0LjQ3MzQgMjkuMzYyNyA3NC44NTg1IDI5LjE4NzcgNzUuMzA4MyAyOS4wMzU2Qzc1Ljc1ODEgMjguODg0MSA3Ni4yMTk1IDI4Ljc3NzQgNzYuNjkyMyAyOC43MTY3Qzc3LjE2NDggMjguNjU2MiA3Ny42MjYyIDI4LjY0ODEgNzguMDc2MyAyOC42OTM4Qzc4LjUyNTggMjguNzM5NSA3OC45MjI4IDI4Ljg2NDcgNzkuMjY1OSAyOS4wNjk3Qzc5LjYwODkgMjkuMjc1MSA3OS44NjQzIDI5LjU3MTQgODAuMDMyIDI5Ljk1ODZDODAuMTk5NyAzMC4zNDY0IDgwLjI0NTYgMzAuODM2NSA4MC4xNjkzIDMxLjQyOUM4MC4xMDgzIDMxLjkwMDEgNzkuOTIxMSAzMi4yOTkxIDc5LjYwODkgMzIuNjI1NkM3OS4yOTYzIDMyLjk1MjYgNzguOTE0NyAzMy4yMjU5IDc4LjQ2NTIgMzMuNDQ2MkM3OC4wMTU0IDMzLjY2NjggNzcuNTM4OCAzMy44NDE1IDc3LjAzNTYgMzMuOTcwMkM3Ni41MzIxIDM0LjA5OTcgNzYuMDQ3NyAzNC4xOTQ5IDc1LjU4MjggMzQuMjU1M0M3NS4xMTc2IDM0LjMxNjMgNzQuNzEzNyAzNC4zNTQ1IDc0LjM3MDYgMzQuMzY5MkM3NC4wMjczIDM0LjM4NDUgNzMuODAyMSAzNC4zOTIxIDczLjY5NTYgMzQuMzkyMUw3NC4xNTMgMjkuNTYwMlpNODMuNjAwNyAzNi45Njc2QzgzLjM1NjYgMzYuNDM2MSA4My4wMjg3IDM1Ljk2ODkgODIuNjE3MiAzNS41NjU4QzgyLjIwNTQgMzUuMTYzMyA4MS43MTcgMzQuODcwOSA4MS4xNTMxIDM0LjY4ODVDODEuMzk2OSAzNC40OTEgODEuNjM3MSAzNC4xNzk1IDgxLjg3MzcgMzMuNzUzOUM4Mi4xMDk5IDMzLjMyODggODIuMzExOSAzMi44NjUgODIuNDc5NiAzMi4zNjM2QzgyLjY0NzQgMzEuODYxOSA4Mi43NjIgMzEuMzU3IDgyLjgyMjkgMzAuODQ3OEM4Mi44ODM2IDMwLjMzODkgODIuODYwNyAyOS45MDIgODIuNzU0NCAyOS41MzdDODIuNDk0NyAyOC42MjU2IDgyLjA4NyAyNy45MTE0IDgxLjUzMDMgMjcuMzk0NkM4MC45NzM0IDI2Ljg3ODIgODAuMzI1NyAyNi41MjExIDc5LjU4NiAyNi4zMjMzQzc4Ljg0NjIgMjYuMTI2NCA3OC4wMzA0IDI2LjA4NDIgNzcuMTM4MyAyNi4xOTgxQzc2LjI0NjIgMjYuMzEyIDc1LjMzNDcgMjYuNTM2MSA3NC40MDQ5IDI2Ljg3MDRDNzQuNDA0OSAyNi43OTQ2IDc0LjQxMjQgMjYuNzE0OCA3NC40Mjc4IDI2LjYzMTJDNzQuNDQyNiAyNi41NDggNzQuNDUwNCAyNi40NjA0IDc0LjQ1MDQgMjYuMzY5Qzc0LjQ1MDQgMjYuMTQxMSA3NC4zMzYxIDI1Ljk0MzkgNzQuMTA3NCAyNS43NzY1QzczLjg3ODcgMjUuNjA5MyA3My42MTU1IDI1LjUxMDcgNzMuMzE4MyAyNS40ODAxQzczLjAyMDkgMjUuNDUgNzIuNzMxIDI1LjUxNDIgNzIuNDQ4OSAyNS42NzM4QzcyLjE2NjUgMjUuODMzNCA3MS45NzIxIDI2LjEyNjQgNzEuODY1NiAyNi41NTExQzcxLjc0MzQgMjcuOTE4OSA3MS42MjE1IDI5LjMzOTggNzEuNDk5NiAzMC44MTM0QzcxLjM3NzQgMzIuMjg3NSA3MS4yNDggMzMuNzc2NyA3MS4xMTA3IDM1LjI4MTJDNzAuOTczNSAzNi43ODU1IDcwLjgzNjIgMzguMjc4NCA3MC42OTg5IDM5Ljc1OThDNzAuNTYxNiA0MS4yNDE0IDcwLjQyNDQgNDIuNjY1OSA3MC4yODcxIDQ0LjAzMzNDNzAuMzMzIDQ0LjQ0MzYgNzAuNDQ3MyA0NC43NjI5IDcwLjYzMDQgNDQuOTkwN0M3MC44MTMzIDQ1LjIxODkgNzEuMDI2OCA0NS4zNTU2IDcxLjI3MDkgNDUuNDAxQzcxLjUxNDcgNDUuNDQ2NyA3MS43NzA0IDQ1LjQwNDUgNzIuMDM3MSA0NS4yNzU1QzcyLjMwMzggNDUuMTQ2OSA3Mi41MzY1IDQ0LjkyMjIgNzIuNzM1IDQ0LjYwMzJDNzMuMzQ0NyA0NC45Mzc1IDc0LjAzMTEgNDUuMTU0MSA3NC43OTM4IDQ1LjI1M0M3NS41NTYxIDQ1LjM1MTYgNzYuMzI5OCA0NS4zNTE2IDc3LjExNTcgNDUuMjUzQzc3LjkwMDcgNDUuMTU0MSA3OC42NzQ3IDQ0Ljk2ODIgNzkuNDM3NCA0NC42OTQzQzgwLjE5OTcgNDQuNDIxMSA4MC44OTM2IDQ0LjA3OSA4MS41MTkgNDMuNjY5QzgyLjE0NDEgNDMuMjU4NiA4Mi42NzAzIDQyLjc5MTEgODMuMDk3NSA0Mi4yNjcxQzgzLjUyNDQgNDEuNzQyNiA4My44MDY1IDQxLjE3NjcgODMuOTQzNyA0MC41NjkxQzg0LjA4MSAzOS45NDYgODQuMTE5IDM5LjMyMzEgODQuMDU4MSAzOC43QzgzLjk5NzEgMzguMDc3MSA4My44NDQ1IDM3LjUgODMuNjAwNyAzNi45Njc2WlwiIGZpbGw9XCIjMEQwQzIzXCIvPlxuPHBhdGggZmlsbC1ydWxlPVwiZXZlbm9kZFwiIGNsaXAtcnVsZT1cImV2ZW5vZGRcIiBkPVwiTTEwNS45MTUgNDkuMDAxN0MxMDUuODMyIDQ5LjUwMzEgMTA1LjcxMyA1MC4wMzExIDEwNS41NjEgNTAuNTg2QzEwNS40MDggNTEuMTQwMyAxMDUuMjI5IDUxLjY0NTggMTA1LjAyMyA1Mi4xMDE4QzEwNC44MTggNTIuNTU3NSAxMDQuNTg5IDUyLjkyNTYgMTA0LjMzNyA1My4yMDdDMTA0LjA4NSA1My40ODggMTAzLjgxNSA1My42MDYgMTAzLjUyNSA1My41NjA2QzEwMy4yOTYgNTMuNTI5NyAxMDMuMTUxIDUzLjM4NTQgMTAzLjA5MSA1My4xMjc0QzEwMy4wMjkgNTIuODY4NiAxMDMuMDI5IDUyLjU0OTcgMTAzLjA5MSA1Mi4xN0MxMDMuMTUxIDUxLjc5MDEgMTAzLjI2OSA1MS4zNjA3IDEwMy40NDUgNTAuODgyMUMxMDMuNjIgNTAuNDAzNSAxMDMuODM0IDQ5LjkyODQgMTA0LjA4NSA0OS40NTc3QzEwNC4zMzcgNDguOTg2NCAxMDQuNjIzIDQ4LjUzNDcgMTA0Ljk0MyA0OC4xMDE1QzEwNS4yNjQgNDcuNjY4NiAxMDUuNTk5IDQ3LjMwNzUgMTA1Ljk1IDQ3LjAxODlDMTA2LjAyNiA0Ny4xMSAxMDYuMDYgNDcuMzM3OCAxMDYuMDUzIDQ3LjcwMjhDMTA2LjA0NSA0OC4wNjc0IDEwNS45OTkgNDguNTAwNiAxMDUuOTE1IDQ5LjAwMTdaTTExMy42NyAzOS4xMDk3QzExMy40NjQgMzguODgxOSAxMTMuMjEzIDM4Ljc1MjkgMTEyLjkxNSAzOC43MjIzQzExMi42MTggMzguNjkxOSAxMTIuMzE3IDM4Ljg1OSAxMTIuMDEyIDM5LjIyMzdDMTExLjgxMyAzOS41ODgzIDExMS41NjIgMzkuOTM3OSAxMTEuMjU3IDQwLjI3MjJDMTEwLjk1MiA0MC42MDY3IDExMC42MzUgNDAuOTEwMyAxMTAuMzA3IDQxLjE4MzlDMTA5Ljk4IDQxLjQ1NzIgMTA5LjY2NyA0MS42OTMxIDEwOS4zNyA0MS44OTAzQzEwOS4wNzIgNDIuMDg4MSAxMDguODQgNDIuMjMyNCAxMDguNjcyIDQyLjMyMzVDMTA4LjYxMSA0MS44Mzc0IDEwOC41NzYgNDEuMzEzMiAxMDguNTY5IDQwLjc1MDdDMTA4LjU2MSA0MC4xODg2IDEwOC41NzMgMzkuNjE5IDEwOC42MDMgMzkuMDQxNUMxMDguNjQ5IDM4LjIyMDkgMTA4Ljc0NCAzNy4zOTMgMTA4Ljg4OSAzNi41NTdDMTA5LjAzNCAzNS43MjEzIDEwOS4yNDQgMzQuOTAwNyAxMDkuNTE4IDM0LjA5NTFDMTA5LjUxOCAzMy42NyAxMDkuNDE5IDMzLjMyNDIgMTA5LjIyMSAzMy4wNTgyQzEwOS4wMjIgMzIuNzkyNCAxMDguNzgyIDMyLjYyNSAxMDguNSAzMi41NTY3QzEwOC4yMTggMzIuNDg4NSAxMDcuOTI5IDMyLjUyNjQgMTA3LjYzMSAzMi42NzA3QzEwNy4zMzQgMzIuODE1MyAxMDcuMDc4IDMzLjA3NzUgMTA2Ljg2NSAzMy40NTY5QzEwNi42ODIgMzMuOTU4NiAxMDYuNDcyIDM0LjUyMDcgMTA2LjIzNiAzNS4xNDM2QzEwNS45OTkgMzUuNzY2NyAxMDUuNzMyIDM2LjQwMTIgMTA1LjQzNSAzNy4wNDY5QzEwNS4xMzggMzcuNjkzMSAxMDQuODA2IDM4LjMxOTcgMTA0LjQ0IDM4LjkyNzNDMTA0LjA3NCAzOS41MzU0IDEwMy42NzQgNDAuMDc1IDEwMy4yMzkgNDAuNTQ1N0MxMDIuODA0IDQxLjAxNjggMTAyLjMzMSA0MS4zODU0IDEwMS44MjEgNDEuNjUxMkMxMDEuMzEgNDEuOTE3MiAxMDAuNzU3IDQyLjAzNDkgMTAwLjE2MiA0Mi4wMDQ1Qzk5Ljg4NzYgNDEuOTI4NSA5OS42ODkzIDQxLjcyMzUgOTkuNTY3NSA0MS4zODg5Qzk5LjQ0NTMgNDEuMDU0OSA5OS4zNzMgNDAuNjM2OCA5OS4zNTA0IDQwLjEzNTRDOTkuMzI3NSAzOS42MzQgOTkuMzUwNCAzOS4wODMxIDk5LjQxODkgMzguNDgyOEM5OS40ODc3IDM3Ljg4MjggOTkuNTc5MSAzNy4yODYzIDk5LjY5MzQgMzYuNjkzOEM5OS44MDc4IDM2LjEwMSA5OS45MzM3IDM1LjUzODkgMTAwLjA3MSAzNS4wMDcxQzEwMC4yMDggMzQuNDc1MyAxMDAuMzM3IDM0LjAyNjggMTAwLjQ2IDMzLjY2MjJDMTAwLjY0MyAzMy4yMjE4IDEwMC42NDMgMzIuODUyOSAxMDAuNDYgMzIuNTU2N0MxMDAuMjc3IDMyLjI2MDQgMTAwLjAyNSAzMi4wNjMxIDk5LjcwNSAzMS45NjRDOTkuMzg0NiAzMS44NjU0IDk5LjA0ODkgMzEuODY5NCA5OC42OTgzIDMxLjk3NTVDOTguMzQ3NCAzMi4wODE5IDk4LjA5NTggMzIuMzE3MyA5Ny45NDM1IDMyLjY4MkM5Ny42ODQgMzMuMzA1NCA5Ny40NDc1IDM0LjAwNCA5Ny4yMzQyIDM0Ljc3OUM5Ny4wMjA2IDM1LjU1MzkgOTYuODQ5MSAzNi4zNTU4IDk2LjcxOTcgMzcuMTgzNkM5Ni41ODk2IDM4LjAxMjEgOTYuNTE3MSAzOC44MzI3IDk2LjUwMiAzOS42NDU2Qzk2LjUwMTEgMzkuNjk4NSA5Ni41MDM3IDM5Ljc0ODggOTYuNTAzNCAzOS44MDE0Qzk2LjE3MDkgNDAuNjg0OCA5NS44NTQgNDEuMzUyNSA5NS41NTMgNDEuNzk5MkM5NS4xNjQxIDQyLjM3NyA5NC43MjUzIDQyLjYyNzcgOTQuMjM3NSA0Mi41NTEzQzk0LjAyMzYgNDIuNDYwMyA5My44ODMyIDQyLjI0NzcgOTMuODE0NyA0MS45MTMyQzkzLjc0NTMgNDEuNTc5MiA5My43MjI3IDQxLjE2ODkgOTMuNzQ1MyA0MC42ODIyQzkzLjc2ODggNDAuMTk2NCA5My44MjYgMzkuNjQ1NiA5My45MTcxIDM5LjAyOTlDOTQuMDA5MSAzOC40MTQ2IDk0LjEyMjkgMzcuNzc2NCA5NC4yNjAxIDM3LjExNTRDOTQuMzk3NyAzNi40NTQxIDk0LjU0MjUgMzUuNzg5OSA5NC42OTQ5IDM1LjEyMUM5NC44NDcyIDM0LjQ1MjUgOTQuOTg0NSAzMy44MjE4IDk1LjEwNyAzMy4yMjkxQzk1LjA5MTYgMzIuNjk3MyA5NC45MzUyIDMyLjI5MSA5NC42Mzc3IDMyLjAwOTdDOTQuMzQwNSAzMS43Mjg5IDkzLjkyNDcgMzEuNjE4NyA5My4zOTEzIDMxLjY3OTFDOTMuMDI1MyAzMS44MzEyIDkyLjc1NDIgMzIuMDI5IDkyLjU3OSAzMi4yNzE5QzkyLjQwMzQgMzIuNTE0OCA5Mi4yNjIzIDMyLjgyNjUgOTIuMTU1OCAzMy4yMDYyQzkyLjA5NDYgMzMuNDA0IDkyLjAwMzIgMzMuNzk5IDkxLjg4MTMgMzQuMzkxOEM5MS43NTkxIDM0Ljk4NCA5MS42MDMgMzUuNjY0NCA5MS40MTIzIDM2LjQzMTVDOTEuMjIxNyAzNy4xOTkyIDkwLjk5NjcgMzguMDAwNSA5MC43Mzc2IDM4LjgzNjJDOTAuNDc4MSAzOS42NzE5IDkwLjE4ODUgNDAuNDI4MyA4OS44Njg0IDQxLjEwNDFDODkuNTQ4IDQxLjc4MDEgODkuMTk3MiA0Mi4zMjM1IDg4LjgxNjEgNDIuNzMzOEM4OC40MzQ4IDQzLjE0MzggODguMDIzIDQzLjMxMTMgODcuNTgwNyA0My4yMzUyQzg3LjMzNjYgNDMuMTg5NSA4Ny4xODA1IDQyLjkzODggODcuMTEyIDQyLjQ4MzFDODcuMDQzMiA0Mi4wMjcxIDg3LjAzMTkgNDEuNDY1MyA4Ny4wNzc1IDQwLjc5NjRDODcuMTIzMyA0MC4xMjc5IDg3LjIxNDggMzkuMzk0NiA4Ny4zNTIgMzguNTk3MUM4Ny40ODkzIDM3Ljc5OTMgODcuNjMgMzcuMDQzNCA4Ny43NzUyIDM2LjMyODlDODcuOTIgMzUuNjE0OSA4OC4wNTM1IDM0Ljk4NCA4OC4xNzU2IDM0LjQzNzJDODguMjk3NSAzMy44OTAxIDg4LjM4MTQgMzMuNTI1NCA4OC40MjcyIDMzLjM0MzNDODguNDI3MiAzMi45MDI2IDg4LjMyNzcgMzIuNTQ5NSA4OC4xMjk4IDMyLjI4MzJDODcuOTMxMyAzMi4wMTc4IDg3LjY5MTMgMzEuODUwMyA4Ny40MDkyIDMxLjc4MThDODcuMTI2OCAzMS43MTM2IDg2LjgzNzIgMzEuNzUxNCA4Ni41NCAzMS44OTU3Qzg2LjI0MjYgMzIuMDQwMyA4NS45ODcyIDMyLjMwMjYgODUuNzczNiAzMi42ODJDODUuNjk3MyAzMy4wOTIzIDg1LjU5OCAzMy41Njc0IDg1LjQ3NjEgMzQuMTA2N0M4NS4zNTM5IDM0LjY0NTkgODUuMjM2MSAzNS4yMDA2IDg1LjEyMTggMzUuNzcwNUM4NS4wMDc0IDM2LjM0MDQgODQuOTAwMyAzNi44OTg4IDg0LjgwMTQgMzcuNDQ1OUM4NC43MDIxIDM3Ljk5MyA4NC42Mjk5IDM4LjQ3MTYgODQuNTg0IDM4Ljg4MTlDODQuNTUzNiAzOS4yMDA4IDg0LjUxOSAzOS41OTIzIDg0LjQ4MTMgNDAuMDU1NkM4NC40NDMgNDAuNTE5NCA4NC40MjM4IDQxLjAwOTIgODQuNDIzOCA0MS41MjU3Qzg0LjQyMzggNDIuMDQyNyA4NC40NjE4IDQyLjU1NTQgODQuNTM4NSA0My4wNjQzQzg0LjYxNDUgNDMuNTczNSA4NC43NTE4IDQ0LjA0MDggODQuOTUgNDQuNDY1OUM4NS4xNDgyIDQ0Ljg5MTUgODUuNDI2NSA0NS4yNDA4IDg1Ljc4NTIgNDUuNTE0NEM4Ni4xNDMzIDQ1Ljc4NzkgODYuNTk3MiA0NS45Mzk3IDg3LjE0NjMgNDUuOTcwNEM4Ny43MTAxIDQ2LjAwMDUgODguMjAyIDQ1Ljk1OTEgODguNjIxNyA0NS44NDQ5Qzg5LjA0MSA0NS43MzEgODkuNDIyMSA0NS41NTIzIDg5Ljc2NTQgNDUuMzA5MUM5MC4xMDg0IDQ1LjA2NjUgOTAuNDIxIDQ0Ljc3NzYgOTAuNzAzMyA0NC40NDNDOTAuOTg1MSA0NC4xMDkxIDkxLjI2MzcgNDMuNzQ0NCA5MS41MzgzIDQzLjM0OTFDOTEuNzk3NCA0My45MjY5IDkyLjEzMjkgNDQuMzc0OCA5Mi41NDQ3IDQ0LjY5NEM5Mi45NTY1IDQ1LjAxMyA5My4zOTEzIDQ1LjIwMzIgOTMuODQ4NiA0NS4yNjM3Qzk0LjMwNiA0NS4zMjQxIDk0Ljc3MTUgNDUuMjYwMiA5NS4yNDQyIDQ1LjA2OTlDOTUuNzE2NyA0NC44ODAzIDk2LjE0MzYgNDQuNTU3MyA5Ni41MjUyIDQ0LjEwMTJDOTYuNzc2MiA0My44MjE2IDk3LjAxMzEgNDMuNTAzOCA5Ny4yMzU0IDQzLjE1MjVDOTcuMzI5NyA0My4zMTcgOTcuNDMwMSA0My40NzU4IDk3LjU0MyA0My42MjI0Qzk3LjkxNjggNDQuMTA5MSA5OC40MjQgNDQuNDQzIDk5LjA2NDUgNDQuNjI1NUM5OS43NTA2IDQ0LjgwOCAxMDAuNDIxIDQ0LjgzODYgMTAxLjA3NyA0NC43MTY5QzEwMS43MzMgNDQuNTk1NCAxMDIuMzU4IDQ0LjM3NDggMTAyLjk1MyA0NC4wNTU5QzEwMy41NDggNDMuNzM2NiAxMDQuMTAxIDQzLjM1MzIgMTA0LjYxMiA0Mi45MDQ3QzEwNS4xMjIgNDIuNDU2NSAxMDUuNTY4IDQxLjk4OTUgMTA1Ljk1IDQxLjUwMjhDMTA1LjkzNCA0MS44NTI0IDEwNS45MjcgNDIuMTgzMiAxMDUuOTI3IDQyLjQ5NDRDMTA1LjkyNyA0Mi44MDYxIDEwNS45MTkgNDMuMTQzOCAxMDUuOTA0IDQzLjUwODhDMTA1LjE0MSA0NC4wNDA4IDEwNC40MjEgNDQuNjc5IDEwMy43NDIgNDUuNDIzM0MxMDMuMDY0IDQ2LjE2NzYgMTAyLjQ2OSA0Ni45NjE2IDEwMS45NTggNDcuODA1MUMxMDEuNDQ3IDQ4LjY0ODMgMTAxLjA0NyA0OS41MDMxIDEwMC43NTcgNTAuMzY5MUMxMDAuNDY3IDUxLjIzNTcgMTAwLjMyNiA1Mi4wNDQ1IDEwMC4zMzQgNTIuNzk2OUMxMDAuMzQxIDUzLjU0OSAxMDAuNTIxIDU0LjIwNiAxMDAuODcxIDU0Ljc2ODFDMTAxLjIyMiA1NS4zMzA2IDEwMS43OTQgNTUuNzMzMSAxMDIuNTg3IDU1Ljk3NjNDMTAzLjQxMSA1Ni4yMzQ4IDEwNC4xMzUgNTYuMjQyIDEwNC43NiA1NS45OTkxQzEwNS4zODYgNTUuNzU1OSAxMDUuOTMxIDU1LjM1MzEgMTA2LjM5NiA1NC43OTFDMTA2Ljg2MSA1NC4yMjg5IDEwNy4yNDIgNTMuNTQ5IDEwNy41NCA1Mi43NTEyQzEwNy44MzcgNTEuOTUzNCAxMDguMDczIDUxLjEyMTUgMTA4LjI0OSA1MC4yNTU1QzEwOC40MjQgNDkuMzg5NCAxMDguNTM1IDQ4LjUzNzkgMTA4LjU4IDQ3LjcwMjhDMTA4LjYyNiA0Ni44NjY4IDEwOC42MjYgNDYuMTIxOSAxMDguNTggNDUuNDY4N0MxMDkuODkyIDQ0LjkyMTkgMTEwLjk2NyA0NC4yMzA1IDExMS44MDYgNDMuMzk0NUMxMTIuNjQ1IDQyLjU1OTQgMTEzLjMzOCA0MS42Nzc4IDExMy44ODcgNDAuNzUwN0MxMTQuMDU1IDQwLjUyMjkgMTE0LjExMiA0MC4yNDkzIDExNC4wNTkgMzkuOTMwNEMxMTQuMDA2IDM5LjYxMTEgMTEzLjg3NiAzOS4zMzc2IDExMy42NyAzOS4xMDk3WlwiIGZpbGw9XCIjMEQwQzIzXCIvPlxuPHBhdGggZmlsbC1ydWxlPVwiZXZlbm9kZFwiIGNsaXAtcnVsZT1cImV2ZW5vZGRcIiBkPVwiTTE0Mi41MyAzNy42NTE1QzE0Mi41NzUgMzcuMzAyMiAxNDIuNjQ0IDM2LjkzMzUgMTQyLjczNSAzNi41NDZDMTQyLjgyNyAzNi4xNTg1IDE0Mi45NDEgMzUuNzgyMyAxNDMuMDc5IDM1LjQxNzdDMTQzLjIxNiAzNS4wNTMxIDE0My4zNzYgMzQuNzM3OSAxNDMuNTU5IDM0LjQ3MThDMTQzLjc0MiAzNC4yMDYxIDE0My45MzcgMzQuMDE2MSAxNDQuMTQyIDMzLjkwMTlDMTQ0LjM0OCAzMy43ODgzIDE0NC41NTggMzMuNzk5NSAxNDQuNzcxIDMzLjkzNkMxNDUgMzQuMDczMSAxNDUuMTQxIDM0LjM2MTcgMTQ1LjE5NSAzNC44MDIxQzE0NS4yNDggMzUuMjQzMyAxNDUuMTk1IDM1LjcxNDEgMTQ1LjAzNCAzNi4yMTU1QzE0NC44NzQgMzYuNzE3MiAxNDQuNTg4IDM3LjE4NzkgMTQ0LjE3NyAzNy42Mjg2QzE0My43NjUgMzguMDY5NiAxNDMuMjA4IDM4LjM1NzkgMTQyLjUwNyAzOC40OTQ3QzE0Mi40NzYgMzguMjgyNCAxNDIuNDg0IDM4LjAwMTEgMTQyLjUzIDM3LjY1MTVaTTE1MC40NTYgMzguNTg1N0MxNTAuMjA0IDM4LjUxMDMgMTQ5Ljk2NCAzOC41MDI1IDE0OS43MzUgMzguNTYzMkMxNDkuNTA2IDM4LjYyMzkgMTQ5LjM2MSAzOC43ODM1IDE0OS4zMDEgMzkuMDQyQzE0OS4xNzggMzkuNTI4MSAxNDguOTg0IDQwLjAyNTggMTQ4LjcxNyA0MC41MzQ3QzE0OC40NSA0MS4wNDM5IDE0OC4xMjIgNDEuNTI2MiAxNDcuNzM0IDQxLjk4MjJDMTQ3LjM0NSA0Mi40MzggMTQ2LjkwNiA0Mi44NDA4IDE0Ni40MTggNDMuMTkwMUMxNDUuOTMgNDMuNTM5NyAxNDUuNDE5IDQzLjc5MDQgMTQ0Ljg4NiA0My45NDIyQzE0NC4zNTEgNDQuMTA5NiAxNDMuOTEgNDQuMTI4NCAxNDMuNTU5IDQzLjk5OTFDMTQzLjIwOCA0My44NzA1IDE0Mi45MyA0My42NDk4IDE0Mi43MjQgNDMuMzM4NEMxNDIuNTE4IDQzLjAyNyAxNDIuMzY5IDQyLjY1MDggMTQyLjI3OCA0Mi4yMTAxQzE0Mi4xODYgNDEuNzY5NCAxNDIuMTMzIDQxLjMxMzcgMTQyLjExOCA0MC44NDI0QzE0Mi45ODcgNDAuOTAzNCAxNDMuNzYxIDQwLjc0NzggMTQ0LjQ0IDQwLjM3NTFDMTQ1LjExOCA0MC4wMDMyIDE0NS42OTQgMzkuNTA5IDE0Ni4xNjcgMzguODkzN0MxNDYuNjM5IDM4LjI3ODQgMTQ2Ljk5OCAzNy41ODcgMTQ3LjI0MiAzNi44MTk1QzE0Ny40ODUgMzYuMDUyNCAxNDcuNjIzIDM1LjI4ODcgMTQ3LjY1MyAzNC41Mjg4QzE0Ny42NjkgMzMuODE0NiAxNDcuNTYyIDMzLjIxMDggMTQ3LjMzMyAzMi43MTY5QzE0Ny4xMDUgMzIuMjIzMyAxNDYuNzk2IDMxLjgzOSAxNDYuNDA3IDMxLjU2NThDMTQ2LjAxOCAzMS4yOTIyIDE0NS41NzIgMzEuMTMyNiAxNDUuMDY5IDMxLjA4NzJDMTQ0LjU2NiAzMS4wNDE1IDE0NC4wNTQgMzEuMTEgMTQzLjUzNiAzMS4yOTIyQzE0Mi45MSAzMS41MDUgMTQyLjM4MSAzMS44NTA2IDE0MS45NDYgMzIuMzI5NEMxNDEuNTEyIDMyLjgwOCAxNDEuMTQ5IDMzLjM2MjkgMTQwLjg2IDMzLjk5MzNDMTQwLjU3IDM0LjYyMzkgMTQwLjM0MSAzNS4zMDM4IDE0MC4xNzMgMzYuMDMzQzE0MC4wMDUgMzYuNzYyNiAxMzkuODgzIDM3LjQ4MDYgMTM5LjgwNyAzOC4xODczQzEzOS43MzkgMzguODIxNCAxMzkuNzAyIDM5LjQyNzggMTM5LjY4OSA0MC4wMTNDMTM5LjY1NyA0MC4wODc0IDEzOS42MjUgNDAuMTU4OCAxMzkuNTkgNDAuMjM4M0MxMzkuMzU0IDQwLjc3ODIgMTM5LjA3OSA0MS4zMDYyIDEzOC43NjYgNDEuODIyNkMxMzguNDU0IDQyLjMzOTQgMTM4LjEwNyA0Mi43NzI1IDEzNy43MjYgNDMuMTIxOEMxMzcuMzQ0IDQzLjQ3MTQgMTM2Ljk0OCA0My41OTI5IDEzNi41MzYgNDMuNDg2NUMxMzYuMjkyIDQzLjQyNiAxMzYuMTU5IDQzLjE0NDQgMTM2LjEzNiA0Mi42NDMzQzEzNi4xMTMgNDIuMTQxNiAxMzYuMTM5IDQxLjUxODcgMTM2LjIxNiA0MC43NzQxQzEzNi4yOTIgNDAuMDI5OCAxMzYuMzggMzkuMjIzOSAxMzYuNDc5IDM4LjM1NzlDMTM2LjU3OCAzNy40OTE4IDEzNi42MjggMzYuNjY0IDEzNi42MjggMzUuODczN0MxMzYuNjI4IDM1LjE4OTggMTM2LjQ5OCAzNC41MzI5IDEzNi4yMzkgMzMuOTAxOUMxMzUuOTc5IDMzLjI3MTggMTM1LjYyNSAzMi43NDczIDEzNS4xNzUgMzIuMzI5NEMxMzQuNzI1IDMxLjkxMTMgMTM0LjIwMyAzMS42MzQgMTMzLjYwOCAzMS40OTc1QzEzMy4wMTMgMzEuMzYwNSAxMzIuMzczIDMxLjQ1MTggMTMxLjY4NyAzMS43NzA4QzEzMSAzMi4wOSAxMzAuNDU1IDMyLjUzODIgMTMwLjA1MSAzMy4xMTU3QzEyOS42NDcgMzMuNjkzNCAxMjkuMjc3IDM0LjMwMDkgMTI4Ljk0MiAzNC45MzkxQzEyOC44MTkgMzQuNDUyOCAxMjguNjQxIDM0LjAwMTEgMTI4LjQwNCAzMy41ODNDMTI4LjE2NyAzMy4xNjUxIDEyNy44NzggMzIuODAwNSAxMjcuNTM1IDMyLjQ4ODhDMTI3LjE5MSAzMi4xNzc2IDEyNi44MDYgMzEuOTM0NCAxMjYuMzggMzEuNzU5NUMxMjUuOTUzIDMxLjU4NTEgMTI1LjUwMiAzMS40OTc1IDEyNS4wMyAzMS40OTc1QzEyNC41NzIgMzEuNDk3NSAxMjQuMTQ5IDMxLjU4NTEgMTIzLjc2IDMxLjc1OTVDMTIzLjM3MSAzMS45MzQ0IDEyMy4wMTcgMzIuMTU4MyAxMjIuNjk2IDMyLjQzMThDMTIyLjM3NiAzMi43MDU2IDEyMi4wODcgMzMuMDEzIDEyMS44MjcgMzMuMzU1MUMxMjEuNTY4IDMzLjY5NjkgMTIxLjMzOSAzNC4wMzUyIDEyMS4xNDEgMzQuMzY5MkMxMjEuMTEgMzMuOTc0MiAxMjEuMDc2IDMzLjYyODYgMTIxLjAzOCAzMy4zMzJDMTIxIDMzLjAzNTkgMTIwLjkzMSAzMi43ODUyIDEyMC44MzIgMzIuNTgwMUMxMjAuNzMzIDMyLjM3NDggMTIwLjU5MiAzMi4yMTkzIDEyMC40MDkgMzIuMTEyOUMxMjAuMjI2IDMyLjAwNjcgMTE5Ljk2NyAzMS45NTMyIDExOS42MzIgMzEuOTUzMkMxMTkuNDY0IDMxLjk1MzIgMTE5LjI5NiAzMS45ODc0IDExOS4xMjggMzIuMDU1NkMxMTguOTYgMzIuMTI0MSAxMTguODExIDMyLjIxOTMgMTE4LjY4MiAzMi4zNDA3QzExOC41NTIgMzIuNDYyNyAxMTguNDUzIDMyLjYxMDUgMTE4LjM4NSAzMi43ODUyQzExOC4zMTYgMzIuOTU5OCAxMTguMjk3IDMzLjE2MTQgMTE4LjMyNyAzMy4zODkyQzExOC4zNDIgMzMuNTU2NiAxMTguMzg1IDMzLjc1NzYgMTE4LjQ1MyAzMy45OTMzQzExOC41MjIgMzQuMjI4OSAxMTguNTg3IDM0LjUzNjkgMTE4LjY0OCAzNC45MTYzQzExOC43MDggMzUuMjk2MiAxMTguNzU4IDM1Ljc1NiAxMTguNzk2IDM2LjI5NTNDMTE4LjgzNCAzNi44MzQ5IDExOC44NDYgMzcuNDk1OSAxMTguODMxIDM4LjI3ODRDMTE4LjgxNSAzOS4wNjExIDExOC43NTggMzkuOTc2MyAxMTguNjU5IDQxLjAyNDhDMTE4LjU2IDQyLjA3MzMgMTE4LjQwMyA0My4yODkgMTE4LjE5IDQ0LjY3MTRDMTE4LjE2IDQ0Ljk5MDcgMTE4LjI4MiA0NS4yNDkyIDExOC41NTYgNDUuNDQ2N0MxMTguODMxIDQ1LjY0MzkgMTE5LjE0MyA0NS43NTc4IDExOS40OTQgNDUuNzg4NUMxMTkuODQ1IDQ1LjgxODggMTIwLjE3NyA0NS43NTc4IDEyMC40ODkgNDUuNjA2M0MxMjAuODAyIDQ1LjQ1MzkgMTIwLjk4MSA0NS4xODgyIDEyMS4wMjcgNDQuODA4NUMxMjEuMDcyIDQ0LjA5NDMgMTIxLjE2IDQzLjMzNDcgMTIxLjI5IDQyLjUyOUMxMjEuNDE5IDQxLjcyNCAxMjEuNTc5IDQwLjkyNjIgMTIxLjc3IDQwLjEzNTlDMTIxLjk2MSAzOS4zNDYgMTIyLjE3OCAzOC41OTM4IDEyMi40MjIgMzcuODc5M0MxMjIuNjY2IDM3LjE2NTEgMTIyLjkzNyAzNi41MzQ3IDEyMy4yMzQgMzUuOTg3NkMxMjMuNTMyIDM1LjQ0MDUgMTIzLjg0IDM1LjAwMzkgMTI0LjE2MSAzNC42NzcxQzEyNC40ODEgMzQuMzUwNCAxMjQuODE2IDM0LjE4NyAxMjUuMTY3IDM0LjE4N0MxMjUuNTk0IDM0LjE4NyAxMjUuOTI2IDM0LjM4MDUgMTI2LjE2MiAzNC43Njc5QzEyNi4zOTggMzUuMTU1NyAxMjYuNTY2IDM1LjY1MzYgMTI2LjY2NiAzNi4yNjA5QzEyNi43NjUgMzYuODY5IDEyNi44MSAzNy41MzQxIDEyNi44MDMgMzguMjU1NUMxMjYuNzk1IDM4Ljk3NzMgMTI2Ljc2NSAzOS42NzI0IDEyNi43MTEgNDAuMzQxQzEyNi42NTggNDEuMDA5OCAxMjYuNTk3IDQxLjYwNiAxMjYuNTI4IDQyLjEzMDNDMTI2LjQ2IDQyLjY1NDUgMTI2LjQxIDQzLjAxNTcgMTI2LjM4IDQzLjIxMjlDMTI2LjM4IDQzLjU2MjUgMTI2LjUxMyA0My44Mzk1IDEyNi43OCA0NC4wNDQ4QzEyNy4wNDYgNDQuMjQ5OCAxMjcuMzQ0IDQ0LjM3MTYgMTI3LjY3MiA0NC40MDk1QzEyOCA0NC40NDc2IDEyOC4zMDkgNDQuMzg2NiAxMjguNTk4IDQ0LjIyN0MxMjguODg4IDQ0LjA2NzQgMTI5LjA1NiA0My43OTgyIDEyOS4xMDIgNDMuNDE3OUMxMjkuMjU0IDQyLjMyNCAxMjkuNDY0IDQxLjIyNjQgMTI5LjczMSA0MC4xMjQ3QzEyOS45OTcgMzkuMDIzIDEzMC4zMDMgMzguMDM1NSAxMzAuNjQ2IDM3LjE2MTZDMTMwLjk4OSAzNi4yODc4IDEzMS4zNyAzNS41NzM1IDEzMS43OSAzNS4wMTg5QzEzMi4yMDkgMzQuNDY0NiAxMzIuNjU1IDM0LjE4NyAxMzMuMTI4IDM0LjE4N0MxMzMuMzcxIDM0LjE4NyAxMzMuNTU5IDM0LjM1NDQgMTMzLjY4OCAzNC42ODg0QzEzMy44MTggMzUuMDIyNyAxMzMuODgzIDM1LjQ3ODQgMTMzLjg4MyAzNi4wNTU5QzEzMy44ODMgMzYuNDgxNSAxMzMuODQ4IDM2LjkxODQgMTMzLjc4IDM3LjM2NjZDMTMzLjcxMSAzNy44MTQ4IDEzMy42MzEgMzguMjc4NCAxMzMuNTQgMzguNzU2OUMxMzMuNDQ4IDM5LjIzNTggMTMzLjM2OCAzOS43MjU2IDEzMy4yOTkgNDAuMjI3QzEzMy4yMzEgNDAuNzI4NyAxMzMuMTk2IDQxLjI1MjcgMTMzLjE5NiA0MS43OTk4QzEzMy4xOTYgNDIuMTc5NyAxMzMuMjM1IDQyLjYyMDQgMTMzLjMxMSA0My4xMjE4QzEzMy4zODcgNDMuNjIyOSAxMzMuNTMyIDQ0LjA5ODMgMTMzLjc0NSA0NC41NDYyQzEzMy45NTkgNDQuOTk0NyAxMzQuMjUyIDQ1LjM3NDQgMTM0LjYyNiA0NS42ODU4QzEzNSA0NS45OTczIDEzNS40NzYgNDYuMTUzMSAxMzYuMDU2IDQ2LjE1MzFDMTM2LjkyNSA0Ni4xNTMxIDEzNy42OTUgNDUuOTY2OSAxMzguMzY2IDQ1LjU5NDdDMTM5LjAzNyA0NS4yMjI2IDEzOS42MTMgNDQuNzM2NSAxNDAuMDkzIDQ0LjEzNjJDMTQwLjExOCA0NC4xMDQ3IDE0MC4xNDEgNDQuMDcxMSAxNDAuMTY1IDQ0LjAzOTlDMTQwLjIwMiA0NC4xMjg3IDE0MC4yMzUgNDQuMjIyNyAxNDAuMjc2IDQ0LjMwNzFDMTQwLjYwNCA0NC45NzU2IDE0MS4wNSA0NS40OTIxIDE0MS42MTUgNDUuODU3QzE0Mi4xNzggNDYuMjIxNiAxNDIuODQyIDQ2LjQyMjkgMTQzLjYwNSA0Ni40NjExQzE0NC4zNjcgNDYuNDk4NyAxNDUuMTk4IDQ2LjM1ODEgMTQ2LjA5OCA0Ni4wMzkyQzE0Ni43NjkgNDUuNzk2IDE0Ny4zNTIgNDUuNDkyMSAxNDcuODQ4IDQ1LjEyNzVDMTQ4LjM0MyA0NC43NjI4IDE0OC43ODkgNDQuMzE4NCAxNDkuMTg2IDQzLjc5NDFDMTQ5LjU4MyA0My4yNjk5IDE0OS45NDUgNDIuNjY1OCAxNTAuMjczIDQxLjk4MjJDMTUwLjYwMSA0MS4yOTgxIDE1MC45MzIgNDAuNTE1OSAxNTEuMjY4IDM5LjYzNDJDMTUxLjMyOSAzOS4zOTE2IDE1MS4yNzIgMzkuMTc1MSAxNTEuMDk3IDM4Ljk4NDhDMTUwLjkyMSAzOC43OTUxIDE1MC43MDggMzguNjYyMSAxNTAuNDU2IDM4LjU4NTdaXCIgZmlsbD1cIiMwRDBDMjNcIi8+XG48cGF0aCBmaWxsLXJ1bGU9XCJldmVub2RkXCIgY2xpcC1ydWxlPVwiZXZlbm9kZFwiIGQ9XCJNMTYyLjg4NyAzNi4wNDM0QzE2Mi44MSAzNi40OTE4IDE2Mi43MDcgMzYuOTg2IDE2Mi41NzggMzcuNTI1QzE2Mi40NDggMzguMDY0NiAxNjIuMjg0IDM4LjYyMyAxNjIuMDg2IDM5LjIwMDRDMTYxLjg4OCAzOS43Nzc5IDE2MS42NDQgNDAuMjk4NCAxNjEuMzU0IDQwLjc2MTZDMTYxLjA2NCA0MS4yMjU0IDE2MC43MzMgNDEuNTkzNSAxNjAuMzU5IDQxLjg2NzFDMTU5Ljk4NSA0Mi4xNDA2IDE1OS41NTUgNDIuMjU0NiAxNTkuMDY2IDQyLjIwODlDMTU4LjgyMiA0Mi4xNzg4IDE1OC42MzUgNDIuMDExNyAxNTguNTA2IDQxLjcwNzVDMTU4LjM3NiA0MS40MDM4IDE1OC4zMDggNDEuMDE2MSAxNTguMyA0MC41NDVDMTU4LjI5MiA0MC4wNzQzIDE1OC4zMzQgMzkuNTU3NSAxNTguNDI2IDM4Ljk5NTFDMTU4LjUxNyAzOC40MzMzIDE1OC42NTggMzcuODgyMSAxNTguODQ5IDM3LjM0MjZDMTU5LjA0IDM2LjgwMzYgMTU5LjI3MiAzNi4zMDU2IDE1OS41NDcgMzUuODQ5NkMxNTkuODIxIDM1LjM5MzkgMTYwLjEzOCAzNS4wNDA1IDE2MC40OTYgMzQuNzg5OEMxNjAuODU0IDM0LjUzOTEgMTYxLjI0NyAzNC40MjE3IDE2MS42NzQgMzQuNDM2NUMxNjIuMTAxIDM0LjQ1MTggMTYyLjU1OSAzNC42NjQzIDE2My4wNDcgMzUuMDc0N0MxNjMuMDE2IDM1LjI3MjUgMTYyLjk2MyAzNS41OTU0IDE2Mi44ODcgMzYuMDQzNFpNMTcxLjAxOSAzNy43ODdDMTcwLjc4MiAzNy42NjU2IDE3MC41MzggMzcuNjM5MiAxNzAuMjg3IDM3LjcwNzVDMTcwLjAzNSAzNy43NzU3IDE2OS44NTYgMzguMDA3NiAxNjkuNzQ5IDM4LjQwMjZDMTY5LjY4OCAzOC44MjgzIDE2OS41NTEgMzkuMzI5NCAxNjkuMzM4IDM5LjkwNjlDMTY5LjEyNCA0MC40ODQzIDE2OC44NjEgNDEuMDMxNyAxNjguNTQ4IDQxLjU0NzhDMTY4LjIzNiA0Mi4wNjQ2IDE2Ny44NzcgNDIuNDk0IDE2Ny40NzMgNDIuODM1OEMxNjcuMDY5IDQzLjE3NzggMTY2LjYzOCA0My4zMzM3IDE2Ni4xODEgNDMuMzAyOEMxNjUuNzk5IDQzLjI3MjcgMTY1LjUzMiA0My4wNzkgMTY1LjM4IDQyLjcyMThDMTY1LjIyNyA0Mi4zNjQ3IDE2NS4xNDcgNDEuOTE2OCAxNjUuMTQgNDEuMzc2OUMxNjUuMTMyIDQwLjgzOCAxNjUuMTg2IDQwLjIzMDEgMTY1LjMgMzkuNTUzOEMxNjUuNDE0IDM4Ljg3NzcgMTY1LjU1MiAzOC4yMDU0IDE2NS43MTIgMzcuNTM2M0MxNjUuODcyIDM2Ljg2OCAxNjYuMDM2IDM2LjIyNTggMTY2LjIwNCAzNS42MTA1QzE2Ni4zNzEgMzQuOTk1MSAxNjYuNTA4IDM0LjQ3NDcgMTY2LjYxNiAzNC4wNDkzQzE2Ni43MzggMzMuNjY5MyAxNjYuNjk5IDMzLjM0NjYgMTY2LjUwMSAzMy4wODAzQzE2Ni4zMDMgMzIuODE0OSAxNjYuMDU1IDMyLjYyNDYgMTY1Ljc1OCAzMi41MTA3QzE2NS40NiAzMi4zOTY3IDE2NS4xNTkgMzIuMzY2NCAxNjQuODU0IDMyLjQxOTZDMTY0LjU0OSAzMi40NzI4IDE2NC4zNTEgMzIuNjM2MiAxNjQuMjU5IDMyLjkwOTRDMTYzLjM1OSAzMi4xMzQ1IDE2Mi40OTQgMzEuNzE2NiAxNjEuNjYzIDMxLjY1NTlDMTYwLjgzMSAzMS41OTUyIDE2MC4wNjUgMzEuNzc3NiAxNTkuMzY0IDMyLjIwM0MxNTguNjYyIDMyLjYyODQgMTU4LjA0MSAzMy4yNDM3IDE1Ny41IDM0LjA0OTNDMTU2Ljk1OCAzNC44NTQ5IDE1Ni41MiAzNS43MzIyIDE1Ni4xODQgMzYuNjgxOEMxNTUuODQ5IDM3LjYzMTQgMTU1LjYzOSAzOC42MDA0IDE1NS41NTUgMzkuNTg3OUMxNTUuNDcxIDQwLjU3NTcgMTU1LjUzNiA0MS40NzYxIDE1NS43NSA0Mi4yODlDMTU1Ljk2MyA0My4xMDE4IDE1Ni4zNCA0My43NjY5IDE1Ni44ODIgNDQuMjgzQzE1Ny40MjMgNDQuNzk5OCAxNTguMTU5IDQ1LjA1ODMgMTU5LjA4OSA0NS4wNTgzQzE1OS41MDEgNDUuMDU4MyAxNTkuODk4IDQ0Ljk3NDcgMTYwLjI3OSA0NC44MDc2QzE2MC42NiA0NC42NDAxIDE2MS4wMTEgNDQuNDQyNiAxNjEuMzMxIDQ0LjIxNDhDMTYxLjY1MSA0My45ODY5IDE2MS45MzMgNDMuNzQ3NSAxNjIuMTc4IDQzLjQ5NjhDMTYyLjQyMSA0My4yNDYxIDE2Mi42MTIgNDMuMDM3MyAxNjIuNzQ5IDQyLjg2OTlDMTYyLjg1NiA0My40MTcgMTYzLjAzMiA0My44ODA4IDE2My4yNzYgNDQuMjYwNUMxNjMuNTE5IDQ0LjY0MDEgMTYzLjc5OCA0NC45NTIxIDE2NC4xMTEgNDUuMTk0OEMxNjQuNDIzIDQ1LjQzNzYgMTY0Ljc1MSA0NS42MTY0IDE2NS4wOTQgNDUuNzMwNkMxNjUuNDM3IDQ1Ljg0NDUgMTY1Ljc2OSA0NS45MDE1IDE2Ni4wODkgNDUuOTAxNUMxNjYuODA2IDQ1LjkwMTUgMTY3LjQ3NyA0NS42NTgzIDE2OC4xMDIgNDUuMTcxOUMxNjguNzI3IDQ0LjY4NjEgMTY5LjI4OCA0NC4wODkzIDE2OS43ODQgNDMuMzgyOUMxNzAuMjc5IDQyLjY3NjIgMTcwLjY4NyA0MS45MzE5IDE3MS4wMDcgNDEuMTQ5MUMxNzEuMzI4IDQwLjM2NjYgMTcxLjU0MSAzOS42NzE1IDE3MS42NDggMzkuMDYzNEMxNzEuNzU1IDM4LjgzNTUgMTcxLjczNSAzOC41OTY0IDE3MS41OTEgMzguMzQ1N0MxNzEuNDQ2IDM4LjA5NSAxNzEuMjU1IDM3LjkwOSAxNzEuMDE5IDM3Ljc4N1pcIiBmaWxsPVwiIzBEMEMyM1wiLz5cbjxwYXRoIGZpbGwtcnVsZT1cImV2ZW5vZGRcIiBjbGlwLXJ1bGU9XCJldmVub2RkXCIgZD1cIk0yMTIuMTk0IDUwLjM3MDFDMjEyLjA2NCA1MC44ODY2IDIxMS44NjIgNTEuMzIzOCAyMTEuNTg3IDUxLjY4MDZDMjExLjMxMyA1Mi4wMzc3IDIxMC45NyA1Mi4yMjM5IDIxMC41NTggNTIuMjM5M0MyMTAuMjk5IDUyLjI1NDMgMjEwLjEwMSA1Mi4xMTc1IDIwOS45NjMgNTEuODI4OUMyMDkuODI2IDUxLjU0MDEgMjA5LjczMSA1MS4xNjc5IDIwOS42NzggNTAuNzEyMkMyMDkuNjI0IDUwLjI1NjIgMjA5LjYwMSA0OS43NDcgMjA5LjYwOSA0OS4xODQ5QzIwOS42MTYgNDguNjIyNyAyMDkuNjM5IDQ4LjA2ODEgMjA5LjY3OCA0Ny41MjFDMjA5LjcxNSA0Ni45NzQyIDIwOS43NjEgNDYuNDY0NyAyMDkuODE1IDQ1Ljk5MzlDMjA5Ljg2OCA0NS41MjI2IDIwOS45MSA0NS4xNTg2IDIwOS45NCA0NC45QzIxMC40NTkgNDQuOTYwOCAyMTAuODkgNDUuMTg0NiAyMTEuMjMzIDQ1LjU3MjNDMjExLjU3NiA0NS45NTk4IDIxMS44MzkgNDYuNDE5MyAyMTIuMDIyIDQ2Ljk1MTRDMjEyLjIwNSA0Ny40ODMxIDIxMi4zMTIgNDguMDU2OCAyMTIuMzQzIDQ4LjY3MjJDMjEyLjM3MyA0OS4yODc1IDIxMi4zMjMgNDkuODUzNCAyMTIuMTk0IDUwLjM3MDFaTTIwMy45MTMgNTAuMzcwMUMyMDMuNzgzIDUwLjg4NjYgMjAzLjU4MSA1MS4zMjM4IDIwMy4zMDcgNTEuNjgwNkMyMDMuMDMyIDUyLjAzNzcgMjAyLjY4OSA1Mi4yMjM5IDIwMi4yNzcgNTIuMjM5M0MyMDIuMDE4IDUyLjI1NDMgMjAxLjgyIDUyLjExNzUgMjAxLjY4MyA1MS44Mjg5QzIwMS41NDUgNTEuNTQwMSAyMDEuNDUgNTEuMTY3OSAyMDEuMzk3IDUwLjcxMjJDMjAxLjM0MyA1MC4yNTYyIDIwMS4zMiA0OS43NDcgMjAxLjMyOCA0OS4xODQ5QzIwMS4zMzYgNDguNjIyNyAyMDEuMzU4IDQ4LjA2ODEgMjAxLjM5NyA0Ny41MjFDMjAxLjQzNCA0Ni45NzQyIDIwMS40OCA0Ni40NjQ3IDIwMS41MzQgNDUuOTkzOUMyMDEuNTg3IDQ1LjUyMjYgMjAxLjYyOSA0NS4xNTg2IDIwMS42NiA0NC45QzIwMi4xNzggNDQuOTYwOCAyMDIuNjA5IDQ1LjE4NDYgMjAyLjk1MiA0NS41NzIzQzIwMy4yOTUgNDUuOTU5OCAyMDMuNTU4IDQ2LjQxOTMgMjAzLjc0MSA0Ni45NTE0QzIwMy45MjQgNDcuNDgzMSAyMDQuMDMxIDQ4LjA1NjggMjA0LjA2MiA0OC42NzIyQzIwNC4wOTIgNDkuMjg3NSAyMDQuMDQyIDQ5Ljg1MzQgMjAzLjkxMyA1MC4zNzAxWk0xOTUuNDE1IDM3LjQyNDFDMTk1LjM5OSAzNy43ODg0IDE5NS4zNjUgMzguMTExNCAxOTUuMzEyIDM4LjM5MjVDMTk1LjI1OCAzOC42NzQxIDE5NS4xODYgMzguODUyMiAxOTUuMDk1IDM4LjkyODNDMTk0LjkyNyAzOC44MzY5IDE5NC43MjEgMzguNjAxOCAxOTQuNDc3IDM4LjIyMTZDMTk0LjIzMyAzNy44NDE5IDE5NC4wNDIgMzcuNDEyMiAxOTMuOTA1IDM2LjkzMzZDMTkzLjc2OCAzNi40NTUxIDE5My43MjUgMzUuOTg0MyAxOTMuNzc5IDM1LjUyMDVDMTkzLjgzMiAzNS4wNTczIDE5NC4wNzMgMzQuNjk2NyAxOTQuNSAzNC40Mzc5QzE5NC42NjcgMzQuMzQ2OCAxOTQuODEyIDM0LjM4MDkgMTk0LjkzNCAzNC41NDA1QzE5NS4wNTYgMzQuNzAwMSAxOTUuMTU1IDM0LjkzMTggMTk1LjIzMiAzNS4yMzU3QzE5NS4zMDggMzUuNTM5OSAxOTUuMzYxIDM1Ljg4OTIgMTk1LjM5MiAzNi4yODQyQzE5NS40MjIgMzYuNjc5NSAxOTUuNDMgMzcuMDU5MSAxOTUuNDE1IDM3LjQyNDFaTTE5My4zOSA0MS45NzExQzE5My4xNTQgNDIuMjIxNSAxOTIuODkgNDIuNDM4MSAxOTIuNjAxIDQyLjYyMDZDMTkyLjMxMSA0Mi44MDMgMTkyLjAxNCA0Mi45Mzk4IDE5MS43MDkgNDMuMDMwOUMxOTEuNDA0IDQzLjEyMjMgMTkxLjEyOSA0My4xNDQ4IDE5MC44ODUgNDMuMDk5MUMxOTAuMTk5IDQyLjk2MjcgMTg5LjY3MyA0Mi42NjYgMTg5LjMwNyA0Mi4yMTAzQzE4OC45NDEgNDEuNzU0NSAxODguNzA4IDQxLjIxOSAxODguNjA5IDQwLjYwMzdDMTg4LjUxIDM5Ljk4ODEgMTg4LjUyMSAzOS4zMzA4IDE4OC42NDQgMzguNjMxOUMxODguNzY1IDM3LjkzMyAxODguOTcxIDM3LjI4MzUgMTg5LjI2MSAzNi42ODMyQzE4OS41NTEgMzYuMDgyOSAxODkuOTAyIDM1LjU2NjIgMTkwLjMxMyAzNS4xMzMzQzE5MC43MjUgMzQuNzAwMSAxOTEuMTc1IDM0LjQzMDYgMTkxLjY2MyAzNC4zMjM5QzE5MS40OCAzNS4wOTg5IDE5MS40MTkgMzUuOTAwNyAxOTEuNDggMzYuNzI4NkMxOTEuNTQxIDM3LjU1NjggMTkxLjczOSAzOC4zMzU1IDE5Mi4wNzUgMzkuMDY0OEMxOTIuMjg4IDM5LjUwNiAxOTIuNTQ0IDM5LjkwODIgMTkyLjg0MSA0MC4yNzI5QzE5My4xMzkgNDAuNjM3OCAxOTMuNTAxIDQwLjk0OTIgMTkzLjkyOCA0MS4yMDc1QzE5My44MDYgNDEuNDY2IDE5My42MjYgNDEuNzIwNCAxOTMuMzkgNDEuOTcxMVpNMjE4LjcwMiAzNy42NTE5QzIxOC43NDcgMzcuMzAyNiAyMTguODE2IDM2LjkzMzYgMjE4LjkwOCAzNi41NDYyQzIxOC45OTkgMzYuMTU5IDIxOS4xMTQgMzUuNzgyOCAyMTkuMjUxIDM1LjQxODFDMjE5LjM4OCAzNS4wNTMyIDIxOS41NDggMzQuNzM4IDIxOS43MzEgMzQuNDcyM0MyMTkuOTE0IDM0LjIwNjUgMjIwLjEwOCAzNC4wMTYzIDIyMC4zMTQgMzMuOTAyNEMyMjAuNTIgMzMuNzg4NCAyMjAuNzMgMzMuNzk5NyAyMjAuOTQzIDMzLjkzNjVDMjIxLjE3MiAzNC4wNzM1IDIyMS4zMTMgMzQuMzYyMSAyMjEuMzY3IDM0LjgwMjVDMjIxLjQyIDM1LjI0MzUgMjIxLjM2NyAzNS43MTQyIDIyMS4yMDcgMzYuMjE1OUMyMjEuMDQ2IDM2LjcxNzMgMjIwLjc2MSAzNy4xODg0IDIyMC4zNDkgMzcuNjI4OEMyMTkuOTM3IDM4LjA3IDIxOS4zOCAzOC4zNTgzIDIxOC42NzkgMzguNDk1MUMyMTguNjQ4IDM4LjI4MjYgMjE4LjY1NiAzOC4wMDE1IDIxOC43MDIgMzcuNjUxOVpNMjI3LjkyMSAzNy42NTE5QzIyNy45NjYgMzcuMzAyNiAyMjguMDM1IDM2LjkzMzYgMjI4LjEyNiAzNi41NDYyQzIyOC4yMTggMzYuMTU5IDIyOC4zMzIgMzUuNzgyOCAyMjguNDcgMzUuNDE4MUMyMjguNjA3IDM1LjA1MzIgMjI4Ljc2NyAzNC43MzggMjI4Ljk1IDM0LjQ3MjNDMjI5LjEzMyAzNC4yMDY1IDIyOS4zMjggMzQuMDE2MyAyMjkuNTMzIDMzLjkwMjRDMjI5LjczOSAzMy43ODg0IDIyOS45NDkgMzMuNzk5NyAyMzAuMTYyIDMzLjkzNjVDMjMwLjM5MSAzNC4wNzM1IDIzMC41MzIgMzQuMzYyMSAyMzAuNTg2IDM0LjgwMjVDMjMwLjYzOSAzNS4yNDM1IDIzMC41ODYgMzUuNzE0MiAyMzAuNDI1IDM2LjIxNTlDMjMwLjI2NSAzNi43MTczIDIyOS45NzkgMzcuMTg4NCAyMjkuNTY4IDM3LjYyODhDMjI5LjE1NiAzOC4wNyAyMjguNTk5IDM4LjM1ODMgMjI3Ljg5OCAzOC40OTUxQzIyNy44NjcgMzguMjgyNiAyMjcuODc1IDM4LjAwMTUgMjI3LjkyMSAzNy42NTE5Wk0yMzYuNDg4IDM4Ljk4NTJDMjM2LjMxMiAzOC43OTU1IDIzNi4wOTkgMzguNjYyNSAyMzUuODQ3IDM4LjU4NjJDMjM1LjU5NSAzOC41MTA0IDIzNS4zNTUgMzguNTAyOSAyMzUuMTI2IDM4LjU2MzZDMjM0Ljg5NyAzOC42MjQ0IDIzNC43NTIgMzguNzg0IDIzNC42OTIgMzkuMDQyMkMyMzQuNTcgMzkuNTI4NiAyMzQuMzc1IDQwLjAyNjIgMjM0LjEwOCA0MC41MzQ5QzIzMy44NDEgNDEuMDQ0NCAyMzMuNTE0IDQxLjUyNjcgMjMzLjEyNSA0MS45ODI0QzIzMi43MzYgNDIuNDM4MSAyMzIuMjk3IDQyLjg0MTIgMjMxLjgxIDQzLjE5MDVDMjMxLjMyMSA0My41NDAxIDIzMC44MSA0My43OTA4IDIzMC4yNzcgNDMuOTQyM0MyMjkuNzQzIDQ0LjExMDEgMjI5LjMwMSA0NC4xMjg5IDIyOC45NSA0My45OTk2QzIyOC41OTkgNDMuODcwNiAyMjguMzIxIDQzLjY1MDMgMjI4LjExNSA0My4zMzg5QzIyNy45MDkgNDMuMDI3MSAyMjcuNzYxIDQyLjY1MTIgMjI3LjY2OSA0Mi4yMTAzQzIyNy41NzggNDEuNzY5OSAyMjcuNTI0IDQxLjMxNDIgMjI3LjUwOSA0MC44NDI4QzIyOC4zNzggNDAuOTAzOCAyMjkuMTUyIDQwLjc0ODMgMjI5LjgzMSA0MC4zNzU1QzIzMC41MDkgNDAuMDAzNCAyMzEuMDg1IDM5LjUwOTIgMjMxLjU1OCAzOC44OTM5QzIzMi4wMzEgMzguMjc4OCAyMzIuMzg5IDM3LjU4NzQgMjMyLjYzMyAzNi44MkMyMzIuODc3IDM2LjA1MjYgMjMzLjAxNCAzNS4yODkyIDIzMy4wNDUgMzQuNTI5M0MyMzMuMDYgMzMuODE1IDIzMi45NTMgMzMuMjExIDIzMi43MjQgMzIuNzE3MUMyMzIuNDk2IDMyLjIyMzUgMjMyLjE4NyAzMS44Mzk1IDIzMS43OTggMzEuNTY2MkMyMzEuNDA5IDMxLjI5MjQgMjMwLjk2MyAzMS4xMzMgMjMwLjQ2IDMxLjA4NzRDMjI5Ljk1NyAzMS4wNDE3IDIyOS40NDUgMzEuMTEwNSAyMjguOTI3IDMxLjI5MjRDMjI4LjMwMiAzMS41MDU1IDIyNy43NzIgMzEuODUxIDIyNy4zMzggMzIuMzI5NkMyMjYuOTAzIDMyLjgwODUgMjI2LjU0IDMzLjM2MzQgMjI2LjI1MSAzMy45OTM0QzIyNS45NjEgMzQuNjI0NCAyMjUuNzMyIDM1LjMwMzkgMjI1LjU2NCAzNi4wMzM1QzIyNS4zOTYgMzYuNzYyNyAyMjUuMjc0IDM3LjQ4MSAyMjUuMTk5IDM4LjE4NzRDMjI1LjEyNCAzOC44NzMgMjI1LjA4NCAzOS41MjkyIDIyNS4wNzUgNDAuMTU3MkMyMjUuMDE3IDQwLjI4MjQgMjI0Ljk1NiA0MC40MDgyIDIyNC44ODkgNDAuNTM0OUMyMjQuNjIyIDQxLjA0NDQgMjI0LjI5NSA0MS41MjY3IDIyMy45MDYgNDEuOTgyNEMyMjMuNTE3IDQyLjQzODEgMjIzLjA3OCA0Mi44NDEyIDIyMi41OTEgNDMuMTkwNUMyMjIuMTAyIDQzLjU0MDEgMjIxLjU5MiA0My43OTA4IDIyMS4wNTggNDMuOTQyM0MyMjAuNTI0IDQ0LjExMDEgMjIwLjA4MiA0NC4xMjg5IDIxOS43MzEgNDMuOTk5NkMyMTkuMzggNDMuODcwNiAyMTkuMTAyIDQzLjY1MDMgMjE4Ljg5NiA0My4zMzg5QzIxOC42OTEgNDMuMDI3MSAyMTguNTQyIDQyLjY1MTIgMjE4LjQ1IDQyLjIxMDNDMjE4LjM1OSA0MS43Njk5IDIxOC4zMDUgNDEuMzE0MiAyMTguMjkgNDAuODQyOEMyMTkuMTU5IDQwLjkwMzggMjE5LjkzMyA0MC43NDgzIDIyMC42MTIgNDAuMzc1NUMyMjEuMjkgNDAuMDAzNCAyMjEuODY2IDM5LjUwOTIgMjIyLjMzOSAzOC44OTM5QzIyMi44MTEgMzguMjc4OCAyMjMuMTcgMzcuNTg3NCAyMjMuNDE0IDM2LjgyQzIyMy42NTggMzYuMDUyNiAyMjMuNzk1IDM1LjI4OTIgMjIzLjgyNiAzNC41MjkzQzIyMy44NDEgMzMuODE1IDIyMy43MzQgMzMuMjExIDIyMy41MDYgMzIuNzE3MUMyMjMuMjc3IDMyLjIyMzUgMjIyLjk2OCAzMS44Mzk1IDIyMi41NzkgMzEuNTY2MkMyMjIuMTkgMzEuMjkyNCAyMjEuNzQ0IDMxLjEzMyAyMjEuMjQxIDMxLjA4NzRDMjIwLjczOCAzMS4wNDE3IDIyMC4yMjcgMzEuMTEwNSAyMTkuNzA4IDMxLjI5MjRDMjE5LjA4MyAzMS41MDU1IDIxOC41NTMgMzEuODUxIDIxOC4xMTkgMzIuMzI5NkMyMTcuNjg0IDMyLjgwODUgMjE3LjMyMSAzMy4zNjM0IDIxNy4wMzIgMzMuOTkzNEMyMTYuNzQyIDM0LjYyNDQgMjE2LjUxMyAzNS4zMDM5IDIxNi4zNDYgMzYuMDMzNUMyMTYuMTc4IDM2Ljc2MjcgMjE2LjA1NiAzNy40ODEgMjE1Ljk4IDM4LjE4NzRDMjE1LjkzNiAzOC41ODU5IDIxNS45MDcgMzguOTcyMiAyMTUuODg2IDM5LjM1MTZDMjE1LjczOSAzOS40NzY1IDIxNS41OTUgMzkuNjAyMyAyMTUuNDQyIDM5LjcyNThDMjE0LjkxNiA0MC4xNTE0IDIxNC4zNjMgNDAuNTM0OSAyMTMuNzg0IDQwLjg3NjlDMjEzLjIwNCA0MS4yMTkgMjEyLjYwMSA0MS41MDAxIDIxMS45NzcgNDEuNzIwNEMyMTEuMzUxIDQxLjk0MDggMjEwLjcxIDQyLjA3MzggMjEwLjA1NSA0Mi4xMTkyTDIxMS40NzMgMjYuOTg0N0MyMTEuNTY1IDI2LjY2NTUgMjExLjUxOSAyNi4zODQ3IDIxMS4zMzYgMjYuMTQxNUMyMTEuMTUzIDI1Ljg5ODMgMjEwLjkxNiAyNS43MzEyIDIxMC42MjcgMjUuNjQwMUMyMTAuMzM3IDI1LjU0ODggMjEwLjAyOCAyNS41NTY2IDIwOS43IDI1LjY2MjdDMjA5LjM3MiAyNS43Njk0IDIwOS4xMDIgMjYuMDEyNiAyMDguODg4IDI2LjM5MTlDMjA4Ljc4MSAyNi45Njk3IDIwOC42NzEgMjcuNzU5NyAyMDguNTU3IDI4Ljc2MjVDMjA4LjQ0MiAyOS43NjUzIDIwOC4zMjggMzAuODU5NSAyMDguMjEzIDMyLjA0NDhDMjA4LjA5OSAzMy4yMyAyMDcuOTg1IDM0LjQ1MzIgMjA3Ljg3IDM1LjcxNDJDMjA3Ljc1NiAzNi45NzU5IDIwNy42NTcgMzguMTUzMyAyMDcuNTczIDM5LjI0NzJDMjA3LjU2OSAzOS4yOTU4IDIwNy41NjYgMzkuMzM5OCAyMDcuNTYyIDM5LjM4NzhDMjA3LjQyOSAzOS41MDA1IDIwNy4yOTkgMzkuNjE0MiAyMDcuMTYxIDM5LjcyNThDMjA2LjYzNSA0MC4xNTE0IDIwNi4wODIgNDAuNTM0OSAyMDUuNTAzIDQwLjg3NjlDMjA0LjkyMyA0MS4yMTkgMjA0LjMyMSA0MS41MDAxIDIwMy42OTYgNDEuNzIwNEMyMDMuMDcgNDEuOTQwOCAyMDIuNDI5IDQyLjA3MzggMjAxLjc3NCA0Mi4xMTkyTDIwMy4xOTIgMjYuOTg0N0MyMDMuMjg0IDI2LjY2NTUgMjAzLjIzOCAyNi4zODQ3IDIwMy4wNTUgMjYuMTQxNUMyMDIuODcyIDI1Ljg5ODMgMjAyLjYzNSAyNS43MzEyIDIwMi4zNDYgMjUuNjQwMUMyMDIuMDU2IDI1LjU0ODggMjAxLjc0NyAyNS41NTY2IDIwMS40MTkgMjUuNjYyN0MyMDEuMDkxIDI1Ljc2OTQgMjAwLjgyMSAyNi4wMTI2IDIwMC42MDcgMjYuMzkxOUMyMDAuNTAxIDI2Ljk2OTcgMjAwLjM5IDI3Ljc1OTcgMjAwLjI3NiAyOC43NjI1QzIwMC4xNjEgMjkuNzY1MyAyMDAuMDQ3IDMwLjg1OTUgMTk5LjkzMyAzMi4wNDQ4QzE5OS44MTggMzMuMjMgMTk5LjcwNCAzNC40NTMyIDE5OS41ODkgMzUuNzE0MkMxOTkuNDc1IDM2Ljk3NTkgMTk5LjM3NiAzOC4xNTMzIDE5OS4yOTIgMzkuMjQ3MkMxOTkuMjkgMzkuMjY5MiAxOTkuMjg5IDM5LjI4OTEgMTk5LjI4NyAzOS4zMTExQzE5OS4wNDggMzkuNDIxOSAxOTguNzg2IDM5LjUxOSAxOTguNTAzIDM5LjYwMDZDMTk4LjIxMyAzOS42ODQ0IDE5Ny44ODUgMzkuNzMzOSAxOTcuNTE5IDM5Ljc0ODlDMTk3LjU4IDM5LjQ3NTEgMTk3LjYzIDM5LjE3MTIgMTk3LjY2OCAzOC44MzY5QzE5Ny43MDYgMzguNTAyOSAxOTcuNzM3IDM4LjE1MzMgMTk3Ljc2IDM3Ljc4ODRDMTk3Ljc4MiAzNy40MjQxIDE5Ny43OSAzNy4wNTkxIDE5Ny43ODIgMzYuNjk0NUMxOTcuNzc0IDM2LjMyOTYgMTk3Ljc1NSAzNS45OTU2IDE5Ny43MjUgMzUuNjkxNEMxOTcuNjQ5IDM1LjAzODUgMTk3LjUwOCAzNC40MTkxIDE5Ny4zMDIgMzMuODMzOEMxOTcuMDk2IDMzLjI0OTEgMTk2LjgxOCAzMi43NTkzIDE5Ni40NjcgMzIuMzYzN0MxOTYuMTE2IDMxLjk2ODcgMTk1LjY3OCAzMS43MDI3IDE5NS4xNTEgMzEuNTY2MkMxOTQuNjI2IDMxLjQyOTQgMTk0LjAxMiAzMS40NzQ4IDE5My4zMSAzMS43MDI3QzE5Mi4yNzMgMzEuNTY2MiAxOTEuMzM5IDMxLjY2MTMgMTkwLjUwOCAzMS45ODc4QzE4OS42NzcgMzIuMzE0OSAxODguOTU2IDMyLjc4OTQgMTg4LjM0NiAzMy40MTIyQzE4Ny43MzYgMzQuMDM1NyAxODcuMjM3IDM0Ljc2ODQgMTg2Ljg0OCAzNS42MTE5QzE4Ni40NTkgMzYuNDU1MSAxODYuMiAzNy4zMjE0IDE4Ni4wNyAzOC4yMUMxODYuMDE1IDM4LjU4NjggMTg1Ljk4OCAzOC45NjE4IDE4NS45OCAzOS4zMzZDMTg1Ljc0NCAzOS44MTc3IDE4NS40ODYgNDAuMjM4OCAxODUuMjAxIDQwLjU5MjFDMTg0Ljc5NyA0MS4wOTM1IDE4NC4zNzcgNDEuNTAzOCAxODMuOTQzIDQxLjgyMjhDMTgzLjUwOCA0Mi4xNDIgMTgzLjA3NyA0Mi4zODUyIDE4Mi42NSA0Mi41NTIzQzE4Mi4yMjMgNDIuNzE5OCAxODEuODQyIDQyLjgzMzcgMTgxLjUwNyA0Mi44OTQxQzE4MS4xMSA0Mi45NzAyIDE4MC43MjkgNDIuOTc4IDE4MC4zNjMgNDIuOTE3QzE3OS45OTcgNDIuODU2NSAxNzkuNjYxIDQyLjY4MTYgMTc5LjM1NyA0Mi4zOTI3QzE3OS4xMTIgNDIuMTgwMiAxNzguOTI1IDQxLjgzODEgMTc4Ljc5NiA0MS4zNjcxQzE3OC42NjYgNDAuODk2IDE3OC41OSA0MC4zNjA4IDE3OC41NjcgMzkuNzYwMkMxNzguNTQ0IDM5LjE1OTkgMTc4LjU2NyAzOC41MzMgMTc4LjYzNiAzNy44Nzk4QzE3OC43MDUgMzcuMjI2NiAxNzguODIyIDM2LjYwNzIgMTc4Ljk5IDM2LjAyMjJDMTc5LjE1OCAzNS40MzcyIDE3OS4zNzEgMzQuOTEzIDE3OS42MzEgMzQuNDQ5MkMxNzkuODkgMzMuOTg2MiAxODAuMTk1IDMzLjY1NTQgMTgwLjU0NiAzMy40NTc5QzE4MC43NDQgMzMuNDg4NiAxODAuODY2IDMzLjYwNiAxODAuOTEyIDMzLjgxMUMxODAuOTU4IDM0LjAxNjMgMTgwLjk2OSAzNC4yNTk1IDE4MC45NDYgMzQuNTQwNUMxODAuOTIzIDM0LjgyMTkgMTgwLjg4OSAzNS4xMTA1IDE4MC44NDMgMzUuNDA2NkMxODAuNzk3IDM1LjcwMyAxODAuNzc1IDM1Ljk1MDIgMTgwLjc3NSAzNi4xNDc0QzE4MC44NTEgMzYuNTU3NyAxODAuOTk5IDM2Ljg3NyAxODEuMjIxIDM3LjEwNDhDMTgxLjQ0MSAzNy4zMzI3IDE4MS42OSAzNy40NjYgMTgxLjk2NCAzNy41MDM2QzE4Mi4yMzkgMzcuNTQxNyAxODIuNTA5IDM3LjQ3NzMgMTgyLjc3NiAzNy4zMDk4QzE4My4wNDMgMzcuMTQzIDE4My4yNiAzNi44NzcgMTgzLjQyOCAzNi41MTJDMTgzLjQ0MyAzNi41Mjc0IDE4My40NjYgMzYuNTM0OSAxODMuNDk3IDM2LjUzNDlMMTgzLjgxNyAzMy42NDA0QzE4My45MDkgMzMuMjQ1MSAxODMuODQ3IDMyLjg5NTggMTgzLjYzNCAzMi41OTE5QzE4My40MiAzMi4yODggMTgzLjEzOCAzMi4xMTMgMTgyLjc4OCAzMi4wNjc2QzE4Mi4zNDUgMzEuNDI5NCAxODEuNzQ3IDMxLjA5MTQgMTgwLjk5MiAzMS4wNTMyQzE4MC4yMzcgMzEuMDE1NCAxNzkuNDYzIDMxLjI2MjMgMTc4LjY3IDMxLjc5NDFDMTc4LjE4MiAzMi4xNDQgMTc3Ljc1MSAzMi42MjYgMTc3LjM3OCAzMy4yNDEzQzE3Ny4wMDQgMzMuODU3IDE3Ni42OTkgMzQuNTQwNSAxNzYuNDYzIDM1LjI5MjZDMTc2LjIyNiAzNi4wNDQ4IDE3Ni4wNTggMzYuODM5MSAxNzUuOTU5IDM3LjY3NDhDMTc1Ljg2IDM4LjUxMDQgMTc1Ljg0MSAzOS4zMjM2IDE3NS45MDIgNDAuMTEzM0MxNzUuOTYzIDQwLjkwMzggMTc2LjEwNCA0MS42NDg0IDE3Ni4zMjUgNDIuMzQ3QzE3Ni41NDYgNDMuMDQ2MiAxNzYuODU1IDQzLjYzMTIgMTc3LjI1MiA0NC4xMDJDMTc3LjU4NyA0NC41MTIzIDE3Ny45NjggNDQuODEyNyAxNzguMzk1IDQ1LjAwMjdDMTc4LjgyMiA0NS4xOTI3IDE3OS4yNjggNDUuMzEwMSAxNzkuNzM0IDQ1LjM1NThDMTgwLjE5OSA0NS40MDEyIDE4MC42NiA0NS4zODIxIDE4MS4xMTggNDUuMjk4OEMxODEuNTc1IDQ1LjIxNTUgMTgyLjAxIDQ1LjA5NzggMTgyLjQyMSA0NC45NDU0QzE4Mi45NTUgNDQuNzQ4MiAxODMuNTA1IDQ0LjQ5NzIgMTg0LjA2OSA0NC4xOTMzQzE4NC42MzMgNDMuODg5NyAxODUuMTc0IDQzLjUyNDggMTg1LjY5MyA0My4wOTkxQzE4NS45NjYgNDIuODc1MyAxODYuMjI4IDQyLjYzMTMgMTg2LjQ4MiA0Mi4zNjk2QzE4Ni41OTggNDIuNjU1MyAxODYuNzI3IDQyLjkzMTcgMTg2Ljg4MiA0My4xOTA1QzE4Ny4yOTQgNDMuODc0MSAxODcuODUgNDQuNDI5IDE4OC41NTIgNDQuODU0NEMxODkuMjUzIDQ1LjI3OTcgMTkwLjExNSA0NS40ODQ0IDE5MS4xMzcgNDUuNDY5N0MxOTIuMjM1IDQ1LjQ1NDQgMTkzLjI0OSA0NS4xNzc0IDE5NC4xOCA0NC42Mzc4QzE5NS4xMSA0NC4wOTg4IDE5NS44NzIgNDMuMzA0MiAxOTYuNDY3IDQyLjI1NkMxOTcuMzU4IDQyLjI1NiAxOTguMjM0IDQyLjEwOTYgMTk5LjA5NiA0MS44MTlDMTk5LjA4OSA0MS45MTEgMTk5LjA4MSA0Mi4wMDc5IDE5OS4wNzUgNDIuMDk2NkMxOTkuMDE0IDQyLjkwMTkgMTk4Ljk4MyA0My40NDg3IDE5OC45ODMgNDMuNzM3NkMxOTguOTY4IDQ0LjIzOSAxOTguOTM0IDQ0Ljg1ODEgMTk4Ljg4IDQ1LjU5NDlDMTk4LjgyNyA0Ni4zMzIgMTk4Ljc5MyA0Ny4xMDY5IDE5OC43NzggNDcuOTE5OEMxOTguNzYzIDQ4LjczMjYgMTk4Ljc5MyA0OS41NTMyIDE5OC44NjkgNTAuMzgxN0MxOTguOTQ1IDUxLjIwOTYgMTk5LjEwNSA1MS45NjIgMTk5LjM0OSA1Mi42MzgzQzE5OS41OTMgNTMuMzE0MSAxOTkuOTQgNTMuODg3OCAyMDAuMzkgNTQuMzU5MUMyMDAuODQgNTQuODI5OSAyMDEuNDMxIDU1LjExMTIgMjAyLjE2MyA1NS4yMDIzQzIwMi45NDEgNTUuMzA4NCAyMDMuNjEyIDU1LjE3MTcgMjA0LjE3NiA1NC43OTJDMjA0Ljc0IDU0LjQxMiAyMDUuMTk4IDUzLjg5MTggMjA1LjU0OSA1My4yMzA4QzIwNS44OTkgNTIuNTY5NSAyMDYuMTQ3IDUxLjgwNjEgMjA2LjI5MiA1MC45NDAxQzIwNi40MzcgNTAuMDc0IDIwNi40NzkgNDkuMjAzOSAyMDYuNDE4IDQ4LjMzMDFDMjA2LjM1NyA0Ny40NTYyIDIwNi4xOTYgNDYuNjMyMSAyMDUuOTM3IDQ1Ljg1NzVDMjA1LjY3OCA0NS4wODIyIDIwNS4zMTkgNDQuNDQ0IDIwNC44NjIgNDMuOTQyM0MyMDUuMTM3IDQzLjg2NjkgMjA1LjQ2NSA0My43MjI2IDIwNS44NDYgNDMuNTA5NUMyMDYuMjI3IDQzLjI5NjkgMjA2LjYyIDQzLjA1NzUgMjA3LjAyNCA0Mi43OTE1QzIwNy4xMjMgNDIuNzI2MSAyMDcuMjIxIDQyLjY1NzMgMjA3LjMyIDQyLjU5MDJDMjA3LjI4MyA0My4xMjg2IDIwNy4yNjQgNDMuNTEyNiAyMDcuMjY0IDQzLjczNzZDMjA3LjI0OSA0NC4yMzkgMjA3LjIxNSA0NC44NTgxIDIwNy4xNjEgNDUuNTk0OUMyMDcuMTA4IDQ2LjMzMiAyMDcuMDczIDQ3LjEwNjkgMjA3LjA1OCA0Ny45MTk4QzIwNy4wNDMgNDguNzMyNiAyMDcuMDczIDQ5LjU1MzIgMjA3LjE1IDUwLjM4MTdDMjA3LjIyNiA1MS4yMDk2IDIwNy4zODYgNTEuOTYyIDIwNy42MyA1Mi42MzgzQzIwNy44NzQgNTMuMzE0MSAyMDguMjIxIDUzLjg4NzggMjA4LjY3MSA1NC4zNTkxQzIwOS4xMjEgNTQuODI5OSAyMDkuNzEyIDU1LjExMTIgMjEwLjQ0NCA1NS4yMDIzQzIxMS4yMjEgNTUuMzA4NCAyMTEuODkyIDU1LjE3MTcgMjEyLjQ1NyA1NC43OTJDMjEzLjAyMSA1NC40MTIgMjEzLjQ3OCA1My44OTE4IDIxMy44MyA1My4yMzA4QzIxNC4xOCA1Mi41Njk1IDIxNC40MjggNTEuODA2MSAyMTQuNTczIDUwLjk0MDFDMjE0LjcxOCA1MC4wNzQgMjE0Ljc1OSA0OS4yMDM5IDIxNC42OTkgNDguMzMwMUMyMTQuNjM3IDQ3LjQ1NjIgMjE0LjQ3NyA0Ni42MzIxIDIxNC4yMTggNDUuODU3NUMyMTMuOTU5IDQ1LjA4MjIgMjEzLjYwMSA0NC40NDQgMjEzLjE0MyA0My45NDIzQzIxMy40MTggNDMuODY2OSAyMTMuNzQ1IDQzLjcyMjYgMjE0LjEyNyA0My41MDk1QzIxNC41MDggNDMuMjk2OSAyMTQuOSA0My4wNTc1IDIxNS4zMDUgNDIuNzkxNUMyMTUuNTE1IDQyLjY1MzMgMjE1LjcyNCA0Mi41MTA3IDIxNS45MzIgNDIuMzY0MUMyMTYuMDEgNDMuMTA3MiAyMTYuMTc5IDQzLjc1OSAyMTYuNDQ4IDQ0LjMwNzNDMjE2Ljc3NiA0NC45NzYxIDIxNy4yMjIgNDUuNDkyNSAyMTcuNzg3IDQ1Ljg1NzVDMjE4LjM1MSA0Ni4yMjE4IDIxOS4wMTQgNDYuNDIzNCAyMTkuNzc3IDQ2LjQ2MTJDMjIwLjUzOSA0Ni40OTg4IDIyMS4zNyA0Ni4zNTg2IDIyMi4yNzEgNDYuMDM5M0MyMjIuOTQxIDQ1Ljc5NjUgMjIzLjUyNSA0NS40OTI1IDIyNC4wMiA0NS4xMjc5QzIyNC41MTYgNDQuNzYzIDIyNC45NjIgNDQuMzE4NSAyMjUuMzU4IDQzLjc5NDZDMjI1LjM4MSA0My43NjQyIDIyNS40MDMgNDMuNzMxMyAyMjUuNDI1IDQzLjcwMDZDMjI1LjQ5NiA0My45MTM0IDIyNS41NzQgNDQuMTE3OSAyMjUuNjY3IDQ0LjMwNzNDMjI1Ljk5NSA0NC45NzYxIDIyNi40NDEgNDUuNDkyNSAyMjcuMDA2IDQ1Ljg1NzVDMjI3LjU2OSA0Ni4yMjE4IDIyOC4yMzMgNDYuNDIzNCAyMjguOTk2IDQ2LjQ2MTJDMjI5Ljc1OCA0Ni40OTg4IDIzMC41ODkgNDYuMzU4NiAyMzEuNDg5IDQ2LjAzOTNDMjMyLjE2IDQ1Ljc5NjUgMjMyLjc0NCA0NS40OTI1IDIzMy4yMzkgNDUuMTI3OUMyMzMuNzM1IDQ0Ljc2MyAyMzQuMTgxIDQ0LjMxODUgMjM0LjU3NyA0My43OTQ2QzIzNC45NzQgNDMuMjcgMjM1LjMzNiA0Mi42NjYgMjM1LjY2NCA0MS45ODI0QzIzNS45OTIgNDEuMjk4NSAyMzYuMzIzIDQwLjUxNjQgMjM2LjY1OSAzOS42MzQ3QzIzNi43MiAzOS4zOTE4IDIzNi42NjMgMzkuMTc1MiAyMzYuNDg4IDM4Ljk4NTJaXCIgZmlsbD1cIiMwRDBDMjNcIi8+XG48L3N2Zz5gO1xuXG5jb25zdCBwYXlwYWwgPSBgXG48c3ZnIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiB3aWR0aD1cIjE1MFwiIGhlaWdodD1cIjQwXCI+XG48cGF0aCBmaWxsPVwiIzI1M0I4MFwiIGQ9XCJNNDYuMjExIDYuNzQ5aC02LjgzOWEuOTUuOTUgMCAwIDAtLjkzOS44MDJsLTIuNzY2IDE3LjUzN2EuNTcuNTcgMCAwIDAgLjU2NC42NThoMy4yNjVhLjk1Ljk1IDAgMCAwIC45MzktLjgwM2wuNzQ2LTQuNzNhLjk1Ljk1IDAgMCAxIC45MzgtLjgwM2gyLjE2NWM0LjUwNSAwIDcuMTA1LTIuMTggNy43ODQtNi41LjMwNi0xLjg5LjAxMy0zLjM3NS0uODcyLTQuNDE1LS45NzItMS4xNDItMi42OTYtMS43NDYtNC45ODUtMS43NDZ6TTQ3IDEzLjE1NGMtLjM3NCAyLjQ1NC0yLjI0OSAyLjQ1NC00LjA2MiAyLjQ1NGgtMS4wMzJsLjcyNC00LjU4M2EuNTcuNTcgMCAwIDEgLjU2My0uNDgxaC40NzNjMS4yMzUgMCAyLjQgMCAzLjAwMi43MDQuMzU5LjQyLjQ2OSAxLjA0NC4zMzIgMS45MDZ6TTY2LjY1NCAxMy4wNzVoLTMuMjc1YS41Ny41NyAwIDAgMC0uNTYzLjQ4MWwtLjE0NS45MTYtLjIyOS0uMzMyYy0uNzA5LTEuMDI5LTIuMjktMS4zNzMtMy44NjgtMS4zNzMtMy42MTkgMC02LjcxIDIuNzQxLTcuMzEyIDYuNTg2LS4zMTMgMS45MTguMTMyIDMuNzUyIDEuMjIgNS4wMzEuOTk4IDEuMTc2IDIuNDI2IDEuNjY2IDQuMTI1IDEuNjY2IDIuOTE2IDAgNC41MzMtMS44NzUgNC41MzMtMS44NzVsLS4xNDYuOTFhLjU3LjU3IDAgMCAwIC41NjIuNjZoMi45NWEuOTUuOTUgMCAwIDAgLjkzOS0uODAzbDEuNzctMTEuMjA5YS41NjguNTY4IDAgMCAwLS41NjEtLjY1OHptLTQuNTY1IDYuMzc0Yy0uMzE2IDEuODcxLTEuODAxIDMuMTI3LTMuNjk1IDMuMTI3LS45NTEgMC0xLjcxMS0uMzA1LTIuMTk5LS44ODMtLjQ4NC0uNTc0LS42NjgtMS4zOTEtLjUxNC0yLjMwMS4yOTUtMS44NTUgMS44MDUtMy4xNTIgMy42Ny0zLjE1Mi45MyAwIDEuNjg2LjMwOSAyLjE4NC44OTIuNDk5LjU4OS42OTcgMS40MTEuNTU0IDIuMzE3ek04NC4wOTYgMTMuMDc1aC0zLjI5MWEuOTU0Ljk1NCAwIDAgMC0uNzg3LjQxN2wtNC41MzkgNi42ODYtMS45MjQtNi40MjVhLjk1My45NTMgMCAwIDAtLjkxMi0uNjc4aC0zLjIzNGEuNTcuNTcgMCAwIDAtLjU0MS43NTRsMy42MjUgMTAuNjM4LTMuNDA4IDQuODExYS41Ny41NyAwIDAgMCAuNDY1LjloMy4yODdhLjk0OS45NDkgMCAwIDAgLjc4MS0uNDA4bDEwLjk0Ni0xNS44YS41Ny41NyAwIDAgMC0uNDY4LS44OTV6XCIvPlxuPHBhdGggZmlsbD1cIiMxNzlCRDdcIiBkPVwiTTk0Ljk5MiA2Ljc0OWgtNi44NGEuOTUuOTUgMCAwIDAtLjkzOC44MDJsLTIuNzY2IDE3LjUzN2EuNTY5LjU2OSAwIDAgMCAuNTYyLjY1OGgzLjUxYS42NjUuNjY1IDAgMCAwIC42NTYtLjU2MmwuNzg1LTQuOTcxYS45NS45NSAwIDAgMSAuOTM4LS44MDNoMi4xNjRjNC41MDYgMCA3LjEwNS0yLjE4IDcuNzg1LTYuNS4zMDctMS44OS4wMTItMy4zNzUtLjg3My00LjQxNS0uOTcxLTEuMTQyLTIuNjk0LTEuNzQ2LTQuOTgzLTEuNzQ2em0uNzg5IDYuNDA1Yy0uMzczIDIuNDU0LTIuMjQ4IDIuNDU0LTQuMDYyIDIuNDU0aC0xLjAzMWwuNzI1LTQuNTgzYS41NjguNTY4IDAgMCAxIC41NjItLjQ4MWguNDczYzEuMjM0IDAgMi40IDAgMy4wMDIuNzA0LjM1OS40Mi40NjggMS4wNDQuMzMxIDEuOTA2ek0xMTUuNDM0IDEzLjA3NWgtMy4yNzNhLjU2Ny41NjcgMCAwIDAtLjU2Mi40ODFsLS4xNDUuOTE2LS4yMy0uMzMyYy0uNzA5LTEuMDI5LTIuMjg5LTEuMzczLTMuODY3LTEuMzczLTMuNjE5IDAtNi43MDkgMi43NDEtNy4zMTEgNi41ODYtLjMxMiAxLjkxOC4xMzEgMy43NTIgMS4yMTkgNS4wMzEgMSAxLjE3NiAyLjQyNiAxLjY2NiA0LjEyNSAxLjY2NiAyLjkxNiAwIDQuNTMzLTEuODc1IDQuNTMzLTEuODc1bC0uMTQ2LjkxYS41Ny41NyAwIDAgMCAuNTY0LjY2aDIuOTQ5YS45NS45NSAwIDAgMCAuOTM4LS44MDNsMS43NzEtMTEuMjA5YS41NzEuNTcxIDAgMCAwLS41NjUtLjY1OHptLTQuNTY1IDYuMzc0Yy0uMzE0IDEuODcxLTEuODAxIDMuMTI3LTMuNjk1IDMuMTI3LS45NDkgMC0xLjcxMS0uMzA1LTIuMTk5LS44ODMtLjQ4NC0uNTc0LS42NjYtMS4zOTEtLjUxNC0yLjMwMS4yOTctMS44NTUgMS44MDUtMy4xNTIgMy42Ny0zLjE1Mi45MyAwIDEuNjg2LjMwOSAyLjE4NC44OTIuNTAxLjU4OS42OTkgMS40MTEuNTU0IDIuMzE3ek0xMTkuMjk1IDcuMjNsLTIuODA3IDE3Ljg1OGEuNTY5LjU2OSAwIDAgMCAuNTYyLjY1OGgyLjgyMmMuNDY5IDAgLjg2Ny0uMzQuOTM5LS44MDNsMi43NjgtMTcuNTM2YS41Ny41NyAwIDAgMC0uNTYyLS42NTloLTMuMTZhLjU3MS41NzEgMCAwIDAtLjU2Mi40ODJ6XCIvPlxuPHBhdGggZmlsbD1cIiMyNTNCODBcIiBkPVwiTTcuMjY2IDI5LjE1NGwuNTIzLTMuMzIyLTEuMTY1LS4wMjdIMS4wNjFMNC45MjcgMS4yOTJhLjMxNi4zMTYgMCAwIDEgLjMxNC0uMjY4aDkuMzhjMy4xMTQgMCA1LjI2My42NDggNi4zODUgMS45MjcuNTI2LjYuODYxIDEuMjI3IDEuMDIzIDEuOTE3LjE3LjcyNC4xNzMgMS41ODkuMDA3IDIuNjQ0bC0uMDEyLjA3N3YuNjc2bC41MjYuMjk4YTMuNjkgMy42OSAwIDAgMSAxLjA2NS44MTJjLjQ1LjUxMy43NDEgMS4xNjUuODY0IDEuOTM4LjEyNy43OTUuMDg1IDEuNzQxLS4xMjMgMi44MTItLjI0IDEuMjMyLS42MjggMi4zMDUtMS4xNTIgMy4xODNhNi41NDcgNi41NDcgMCAwIDEtMS44MjUgMmMtLjY5Ni40OTQtMS41MjMuODY5LTIuNDU4IDEuMTA5LS45MDYuMjM2LTEuOTM5LjM1NS0zLjA3Mi4zNTVoLS43M2MtLjUyMiAwLTEuMDI5LjE4OC0xLjQyNy41MjVhMi4yMSAyLjIxIDAgMCAwLS43NDQgMS4zMjhsLS4wNTUuMjk5LS45MjQgNS44NTUtLjA0Mi4yMTVjLS4wMTEuMDY4LS4wMy4xMDItLjA1OC4xMjVhLjE1NS4xNTUgMCAwIDEtLjA5Ni4wMzVINy4yNjZ6XCIvPlxuPHBhdGggZmlsbD1cIiMxNzlCRDdcIiBkPVwiTTIzLjA0OCA3LjY2N2MtLjAyOC4xNzktLjA2LjM2Mi0uMDk2LjU1LTEuMjM3IDYuMzUxLTUuNDY5IDguNTQ1LTEwLjg3NCA4LjU0NUg5LjMyNmMtLjY2MSAwLTEuMjE4LjQ4LTEuMzIxIDEuMTMyTDYuNTk2IDI2LjgzbC0uMzk5IDIuNTMzYS43MDQuNzA0IDAgMCAwIC42OTUuODE0aDQuODgxYy41NzggMCAxLjA2OS0uNDIgMS4xNi0uOTlsLjA0OC0uMjQ4LjkxOS01LjgzMi4wNTktLjMyYy4wOS0uNTcyLjU4Mi0uOTkyIDEuMTYtLjk5MmguNzNjNC43MjkgMCA4LjQzMS0xLjkyIDkuNTEzLTcuNDc2LjQ1Mi0yLjMyMS4yMTgtNC4yNTktLjk3OC01LjYyMmE0LjY2NyA0LjY2NyAwIDAgMC0xLjMzNi0xLjAzelwiLz5cbjxwYXRoIGZpbGw9XCIjMjIyRDY1XCIgZD1cIk0yMS43NTQgNy4xNTFhOS43NTcgOS43NTcgMCAwIDAtMS4yMDMtLjI2NyAxNS4yODQgMTUuMjg0IDAgMCAwLTIuNDI2LS4xNzdoLTcuMzUyYTEuMTcyIDEuMTcyIDAgMCAwLTEuMTU5Ljk5Mkw4LjA1IDE3LjYwNWwtLjA0NS4yODlhMS4zMzYgMS4zMzYgMCAwIDEgMS4zMjEtMS4xMzJoMi43NTJjNS40MDUgMCA5LjYzNy0yLjE5NSAxMC44NzQtOC41NDUuMDM3LS4xODguMDY4LS4zNzEuMDk2LS41NWE2LjU5NCA2LjU5NCAwIDAgMC0xLjAxNy0uNDI5IDkuMDQ1IDkuMDQ1IDAgMCAwLS4yNzctLjA4N3pcIi8+XG48cGF0aCBmaWxsPVwiIzI1M0I4MFwiIGQ9XCJNOS42MTQgNy42OTlhMS4xNjkgMS4xNjkgMCAwIDEgMS4xNTktLjk5MWg3LjM1MmMuODcxIDAgMS42ODQuMDU3IDIuNDI2LjE3N2E5Ljc1NyA5Ljc1NyAwIDAgMSAxLjQ4MS4zNTNjLjM2NS4xMjEuNzA0LjI2NCAxLjAxNy40MjkuMzY4LTIuMzQ3LS4wMDMtMy45NDUtMS4yNzItNS4zOTJDMjAuMzc4LjY4MiAxNy44NTMgMCAxNC42MjIgMGgtOS4zOGMtLjY2IDAtMS4yMjMuNDgtMS4zMjUgMS4xMzNMLjAxIDI1Ljg5OGEuODA2LjgwNiAwIDAgMCAuNzk1LjkzMmg1Ljc5MWwxLjQ1NC05LjIyNSAxLjU2NC05LjkwNnpcIi8+XG48L3N2Zz5gO1xuIl0sIm5hbWVzIjpbIk1lbnUiLCJOb3RpY2UiLCJJdGVtVmlldyIsImFkZEljb24iLCJQbHVnaW4iLCJTZXR0aW5nIiwiUGx1Z2luU2V0dGluZ1RhYiJdLCJtYXBwaW5ncyI6Ijs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSSxhQUFhLEdBQUcsU0FBUyxDQUFDLEVBQUUsQ0FBQyxFQUFFO0FBQ25DLElBQUksYUFBYSxHQUFHLE1BQU0sQ0FBQyxjQUFjO0FBQ3pDLFNBQVMsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLFlBQVksS0FBSyxJQUFJLFVBQVUsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxTQUFTLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUNwRixRQUFRLFVBQVUsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLEtBQUssSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUMxRyxJQUFJLE9BQU8sYUFBYSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUMvQixDQUFDLENBQUM7QUFDRjtBQUNPLFNBQVMsU0FBUyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUU7QUFDaEMsSUFBSSxJQUFJLE9BQU8sQ0FBQyxLQUFLLFVBQVUsSUFBSSxDQUFDLEtBQUssSUFBSTtBQUM3QyxRQUFRLE1BQU0sSUFBSSxTQUFTLENBQUMsc0JBQXNCLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLCtCQUErQixDQUFDLENBQUM7QUFDbEcsSUFBSSxhQUFhLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ3hCLElBQUksU0FBUyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQyxFQUFFO0FBQzNDLElBQUksQ0FBQyxDQUFDLFNBQVMsR0FBRyxDQUFDLEtBQUssSUFBSSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztBQUN6RixDQUFDO0FBdUNEO0FBQ08sU0FBUyxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFO0FBQzdELElBQUksU0FBUyxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsT0FBTyxLQUFLLFlBQVksQ0FBQyxHQUFHLEtBQUssR0FBRyxJQUFJLENBQUMsQ0FBQyxVQUFVLE9BQU8sRUFBRSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFO0FBQ2hILElBQUksT0FBTyxLQUFLLENBQUMsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDLEVBQUUsVUFBVSxPQUFPLEVBQUUsTUFBTSxFQUFFO0FBQy9ELFFBQVEsU0FBUyxTQUFTLENBQUMsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtBQUNuRyxRQUFRLFNBQVMsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtBQUN0RyxRQUFRLFNBQVMsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUMsRUFBRTtBQUN0SCxRQUFRLElBQUksQ0FBQyxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxVQUFVLElBQUksRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUM5RSxLQUFLLENBQUMsQ0FBQztBQUNQLENBQUM7QUFDRDtBQUNPLFNBQVMsV0FBVyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUU7QUFDM0MsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ3JILElBQUksT0FBTyxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLE9BQU8sTUFBTSxLQUFLLFVBQVUsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLFdBQVcsRUFBRSxPQUFPLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDN0osSUFBSSxTQUFTLElBQUksQ0FBQyxDQUFDLEVBQUUsRUFBRSxPQUFPLFVBQVUsQ0FBQyxFQUFFLEVBQUUsT0FBTyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRTtBQUN0RSxJQUFJLFNBQVMsSUFBSSxDQUFDLEVBQUUsRUFBRTtBQUN0QixRQUFRLElBQUksQ0FBQyxFQUFFLE1BQU0sSUFBSSxTQUFTLENBQUMsaUNBQWlDLENBQUMsQ0FBQztBQUN0RSxRQUFRLE9BQU8sQ0FBQyxFQUFFLElBQUk7QUFDdEIsWUFBWSxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztBQUN6SyxZQUFZLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDcEQsWUFBWSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDekIsZ0JBQWdCLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLE1BQU07QUFDOUMsZ0JBQWdCLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQztBQUN4RSxnQkFBZ0IsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUztBQUNqRSxnQkFBZ0IsS0FBSyxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsU0FBUztBQUNqRSxnQkFBZ0I7QUFDaEIsb0JBQW9CLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLEVBQUU7QUFDaEksb0JBQW9CLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUU7QUFDMUcsb0JBQW9CLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRTtBQUN6RixvQkFBb0IsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVB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 ================================================ FILE: .obsidian/plugins/recent-files-obsidian/manifest.json ================================================ { "id": "recent-files-obsidian", "name": "Recent Files", "version": "1.3.1", "minAppVersion": "0.11.6", "description": "List files by most recently opened", "author": "Tony Grosinger", "authorUrl": "https://grosinger.net", "isDesktopOnly": false } ================================================ FILE: .obsidian/plugins/recent-files-obsidian/styles.css ================================================ .recent-files-donation { width: 70%; margin: 0 auto; text-align: center; } .recent-files-donate-button { margin: 10px; } ================================================ FILE: .obsidian/plugins/table-editor-obsidian/data.json ================================================ { "formatType": "normal", "showRibbonIcon": true, "bindEnter": true, "bindTab": true } ================================================ FILE: .obsidian/plugins/table-editor-obsidian/main.js ================================================ 'use strict'; var obsidian = require('obsidian'); var state = require('@codemirror/state'); var view = require('@codemirror/view'); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var icons = { spreadsheet: "\n\n \n", alignLeft: "\n\n \n \n \n \n \n \n", alignCenter: "\n\n \n \n \n \n \n \n", alignRight: "\n\n \n \n \n \n", deleteColumn: "\n\n \n \n", deleteRow: "\n\n \n \n \n", insertColumn: "\n\n \n \n \n \n \n \n", insertRow: "\n\n \n \n \n \n \n \n", moveColumnLeft: "\n\n \n \n \n", moveColumnRight: "\n\n \n \n \n", moveRowDown: "\n\n \n \n \n", moveRowUp: "\n\n \n \n \n", sortAsc: "\n\n \n \n \n \n \n \n", sortDesc: "\n\n \n \n \n \n \n \n", formula: "\n\n \n \n", help: "\n\n \n \n \n", csv: "\n\n \n", arrowenter: "\n\n \n", arrowtab: "\n\n \n", }; var addIcons = function () { Object.keys(icons).forEach(function (key) { // Don't add the 'help' icon here as it will overwrite the native one. if (key !== 'help') { obsidian.addIcon(key, icons[key]); } }); }; var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, basedir, module) { return module = { path: basedir, exports: {}, require: function (path, base) { return commonjsRequire(path, (base === undefined || base === null) ? module.path : base); } }, fn(module, module.exports), module.exports; } function getAugmentedNamespace(n) { if (n.__esModule) return n; var a = Object.defineProperty({}, '__esModule', {value: true}); Object.keys(n).forEach(function (k) { var d = Object.getOwnPropertyDescriptor(n, k); Object.defineProperty(a, k, d.get ? d : { enumerable: true, get: function () { return n[k]; } }); }); return a; } function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); } var point = createCommonjsModule(function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.Point = void 0; /** * A `Point` represents a point in the text editor. */ class Point { /** * Creates a new `Point` object. * * @param row - Row of the point, starts from 0. * @param column - Column of the point, starts from 0. */ constructor(row, column) { this.row = row; this.column = column; } /** * Checks if the point is equal to another point. */ equals(point) { return this.row === point.row && this.column === point.column; } } exports.Point = Point; }); var range = createCommonjsModule(function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.Range = void 0; /** * A `Range` object represents a range in the text editor. */ class Range { /** * Creates a new `Range` object. * * @param start - The start point of the range. * @param end - The end point of the range. */ constructor(start, end) { this.start = start; this.end = end; } } exports.Range = Range; }); var focus = createCommonjsModule(function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.Focus = void 0; /** * A `Focus` object represents which cell is focused in the table. * * Note that `row` and `column` properties specifiy a cell's position in the * table, not the cursor's position in the text editor as {@link Point} class. * * @private */ class Focus { /** * Creates a new `Focus` object. * * @param row - Row of the focused cell. * @param column - Column of the focused cell. * @param offset - Raw offset in the cell. */ constructor(row, column, offset) { this.row = row; this.column = column; this.offset = offset; } /** * Checks if two focuses point the same cell. * Offsets are ignored. */ posEquals(focus) { return this.row === focus.row && this.column === focus.column; } /** * Creates a copy of the focus object by setting its row to the specified value. * * @param row - Row of the focused cell. * @returns A new focus object with the specified row. */ setRow(row) { return new Focus(row, this.column, this.offset); } /** * Creates a copy of the focus object by setting its column to the specified value. * * @param column - Column of the focused cell. * @returns A new focus object with the specified column. */ setColumn(column) { return new Focus(this.row, column, this.offset); } /** * Creates a copy of the focus object by setting its offset to the specified value. * * @param offset - Offset in the focused cell. * @returns A new focus object with the specified offset. */ setOffset(offset) { return new Focus(this.row, this.column, offset); } } exports.Focus = Focus; }); var alignment = createCommonjsModule(function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.HeaderAlignment = exports.DefaultAlignment = exports.Alignment = void 0; /** * Represents column alignment. * * - `Alignment.NONE` - Use default alignment. * - `Alignment.LEFT` - Align left. * - `Alignment.RIGHT` - Align right. * - `Alignment.CENTER` - Align center. * */ var Alignment; (function (Alignment) { Alignment["NONE"] = "none"; Alignment["LEFT"] = "left"; Alignment["RIGHT"] = "right"; Alignment["CENTER"] = "center"; })(Alignment = exports.Alignment || (exports.Alignment = {})); /** * Represents default column alignment * * - `DefaultAlignment.LEFT` - Align left. * - `DefaultAlignment.RIGHT` - Align right. * - `DefaultAlignment.CENTER` - Align center. * */ var DefaultAlignment; (function (DefaultAlignment) { DefaultAlignment["LEFT"] = "left"; DefaultAlignment["RIGHT"] = "right"; DefaultAlignment["CENTER"] = "center"; })(DefaultAlignment = exports.DefaultAlignment || (exports.DefaultAlignment = {})); /** * Represents alignment of header cells. * * - `HeaderAlignment.FOLLOW` - Follow column's alignment. * - `HeaderAlignment.LEFT` - Align left. * - `HeaderAlignment.RIGHT` - Align right. * - `HeaderAlignment.CENTER` - Align center. * */ var HeaderAlignment; (function (HeaderAlignment) { HeaderAlignment["FOLLOW"] = "follow"; HeaderAlignment["LEFT"] = "left"; HeaderAlignment["RIGHT"] = "right"; HeaderAlignment["CENTER"] = "center"; })(HeaderAlignment = exports.HeaderAlignment || (exports.HeaderAlignment = {})); }); var tableCell = createCommonjsModule(function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.TableCell = void 0; /** * A `TableCell` object represents a table cell. * * @private */ class TableCell { /** * Creates a new `TableCell` object. * * @param rawContent - Raw content of the cell. */ constructor(rawContent) { this.rawContent = rawContent; this.content = rawContent.trim(); this.paddingLeft = this.content === '' ? this.rawContent === '' ? 0 : 1 : this.rawContent.length - this.rawContent.trimLeft().length; this.paddingRight = this.rawContent.length - this.content.length - this.paddingLeft; } /** * Convers the cell to a text representation. * * @returns The raw content of the cell. */ toText() { return this.rawContent; } /** * Checks if the cell is a delimiter i.e. it only contains hyphens `-` with optional one * leading and trailing colons `:`. * * @returns `true` if the cell is a delimiter. */ isDelimiter() { return /^\s*:?-+:?\s*$/.test(this.rawContent); } /** * Returns the alignment the cell represents. * * @returns The alignment the cell represents; `undefined` if the cell is not a delimiter. */ getAlignment() { if (!this.isDelimiter()) { return undefined; } if (this.content[0] === ':') { if (this.content[this.content.length - 1] === ':') { return alignment.Alignment.CENTER; } return alignment.Alignment.LEFT; } if (this.content[this.content.length - 1] === ':') { return alignment.Alignment.RIGHT; } return alignment.Alignment.NONE; } /** * Computes a relative position in the trimmed content from that in the raw content. * * @param rawOffset - Relative position in the raw content. * @returns - Relative position in the trimmed content. */ computeContentOffset(rawOffset) { if (this.content === '') { return 0; } if (rawOffset < this.paddingLeft) { return 0; } if (rawOffset < this.paddingLeft + this.content.length) { return rawOffset - this.paddingLeft; } return this.content.length; } /** * Computes a relative position in the raw content from that in the trimmed content. * * @param contentOffset - Relative position in the trimmed content. * @returns - Relative position in the raw content. */ computeRawOffset(contentOffset) { return contentOffset + this.paddingLeft; } } exports.TableCell = TableCell; }); var tableRow = createCommonjsModule(function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.TableRow = void 0; /** * A `TableRow` object represents a table row. * * @private */ class TableRow { /** * Creates a new `TableRow` objec. * * @param cells - Cells that the row contains. * @param marginLeft - Margin string at the left of the row. * @param marginRight - Margin string at the right of the row. */ constructor(cells, marginLeft, marginRight) { this._cells = cells.slice(); this.marginLeft = marginLeft; this.marginRight = marginRight; } /** * Gets the number of the cells in the row. */ getWidth() { return this._cells.length; } /** * Returns the cells that the row contains. */ getCells() { return this._cells.slice(); } /** * Gets a cell at the specified index. * * @param index - Index. * @returns The cell at the specified index if exists; `undefined` if no cell is found. */ getCellAt(index) { return this._cells[index]; } /** * Sets a cell in the row to a new value, returning a copy of the row * with the modified value. * * If an invalid index is provided, the row will be unchanged. */ setCellAt(index, value) { const cells = this.getCells(); // a copy cells[index] = new tableCell.TableCell(value); return new TableRow(cells, this.marginLeft, this.marginRight); } /** * Convers the row to a text representation. */ toText() { if (this._cells.length === 0) { return this.marginLeft; } const cells = this._cells.map((cell) => cell.toText()).join('|'); return `${this.marginLeft}|${cells}|${this.marginRight}`; } /** * Checks if the row is a delimiter or not. * * @returns `true` if the row is a delimiter i.e. all the cells contained are delimiters. */ isDelimiter() { return this._cells.every((cell) => cell.isDelimiter()); } } exports.TableRow = TableRow; }); var neverthrow = createCommonjsModule(function (module, exports) { /** * Retrieved from * https://github.com/supermacro/neverthrow * * MIT License * Copyright (c) 2019 Giorgio Delgado */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Err = exports.Ok = exports.err = exports.ok = void 0; const ok = (value) => new Ok(value); exports.ok = ok; const err = (err) => new Err(err); exports.err = err; class Ok { constructor(value) { this.value = value; this.match = (ok, _err) => ok(this.value); } isOk() { return true; } isErr() { return !this.isOk(); } map(f) { return exports.ok(f(this.value)); } mapErr(_f) { return exports.ok(this.value); } // add info on how this is really useful for converting a // Result, E1> // into a Result andThen(f) { return f(this.value); } unwrapOr(_v) { return this.value; } _unsafeUnwrap() { return this.value; } _unsafeUnwrapErr() { throw new Error('Called `_unsafeUnwrapErr` on an Ok'); } } exports.Ok = Ok; class Err { constructor(error) { this.error = error; this.match = (_ok, err) => err(this.error); } isOk() { return false; } isErr() { return !this.isOk(); } map(_f) { return exports.err(this.error); } mapErr(f) { return exports.err(f(this.error)); } andThen(_f) { return exports.err(this.error); } unwrapOr(v) { return v; } _unsafeUnwrap() { throw new Error('Called `_unsafeUnwrap` on an Err'); } _unsafeUnwrapErr() { return this.error; } } exports.Err = Err; }); var ast_utils = createCommonjsModule(function (module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.prettyPrintAST = exports.checkChildLength = exports.checkType = exports.errRelativeReferenceIndex = exports.errIndex0 = void 0; exports.errIndex0 = new Error('Index 0 used to create a reference'); exports.errRelativeReferenceIndex = new Error('Can not use relative reference where absolute reference is required'); const checkType = (ast, ...expectedTypes) => { if (expectedTypes.indexOf(ast.type) >= 0) { return; } return new Error(`Formula element '${ast.text}' is a ${ast.type} but expected ` + `one of ${expectedTypes} in this position.`); }; exports.checkType = checkType; const checkChildLength = (ast, len) => { if (ast.children.length === len) { return; } return new Error(`Formula element '${ast.text}' was expected to have ${len} ` + `elements, but had ${ast.children.length}`); }; exports.checkChildLength = checkChildLength; const prettyPrintAST = (token, level = 0) => { console.log(' '.repeat(level) + `|-${token.type}${token.children.length === 0 ? '=' + token.text : ''}`); if (token.children) { token.children.forEach((c) => { exports.prettyPrintAST(c, level + 1); }); } }; exports.prettyPrintAST = prettyPrintAST; }); var lodash = createCommonjsModule(function (module, exports) { (function() { /** Used as a safe reference for `undefined` in pre-ES5 environments. */ var undefined$1; /** Used as the semantic version number. */ var VERSION = '4.17.20'; /** Used as the size to enable large array optimizations. */ var LARGE_ARRAY_SIZE = 200; /** Error message constants. */ var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.', FUNC_ERROR_TEXT = 'Expected a function'; /** Used to stand-in for `undefined` hash values. */ var HASH_UNDEFINED = '__lodash_hash_undefined__'; /** Used as the maximum memoize cache size. */ var MAX_MEMOIZE_SIZE = 500; /** Used as the internal argument placeholder. */ var PLACEHOLDER = '__lodash_placeholder__'; /** Used to compose bitmasks for cloning. */ var CLONE_DEEP_FLAG = 1, CLONE_FLAT_FLAG = 2, CLONE_SYMBOLS_FLAG = 4; /** Used to compose bitmasks for value comparisons. */ var COMPARE_PARTIAL_FLAG = 1, COMPARE_UNORDERED_FLAG = 2; /** Used to compose bitmasks for function metadata. */ var WRAP_BIND_FLAG = 1, WRAP_BIND_KEY_FLAG = 2, WRAP_CURRY_BOUND_FLAG = 4, WRAP_CURRY_FLAG = 8, WRAP_CURRY_RIGHT_FLAG = 16, WRAP_PARTIAL_FLAG = 32, WRAP_PARTIAL_RIGHT_FLAG = 64, WRAP_ARY_FLAG = 128, WRAP_REARG_FLAG = 256, WRAP_FLIP_FLAG = 512; /** Used as default options for `_.truncate`. */ var DEFAULT_TRUNC_LENGTH = 30, DEFAULT_TRUNC_OMISSION = '...'; /** Used to detect hot functions by number of calls within a span of milliseconds. */ var HOT_COUNT = 800, HOT_SPAN = 16; /** Used to indicate the type of lazy iteratees. */ var LAZY_FILTER_FLAG = 1, LAZY_MAP_FLAG = 2, LAZY_WHILE_FLAG = 3; /** Used as references for various `Number` constants. */ var INFINITY = 1 / 0, MAX_SAFE_INTEGER = 9007199254740991, MAX_INTEGER = 1.7976931348623157e+308, NAN = 0 / 0; /** Used as references for the maximum length and index of an array. */ var MAX_ARRAY_LENGTH = 4294967295, MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; /** Used to associate wrap methods with their bit flags. */ var wrapFlags = [ ['ary', WRAP_ARY_FLAG], ['bind', WRAP_BIND_FLAG], ['bindKey', WRAP_BIND_KEY_FLAG], ['curry', WRAP_CURRY_FLAG], ['curryRight', WRAP_CURRY_RIGHT_FLAG], ['flip', WRAP_FLIP_FLAG], ['partial', WRAP_PARTIAL_FLAG], ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], ['rearg', WRAP_REARG_FLAG] ]; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', arrayTag = '[object Array]', asyncTag = '[object AsyncFunction]', boolTag = '[object Boolean]', dateTag = '[object Date]', domExcTag = '[object DOMException]', errorTag = '[object Error]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', mapTag = '[object Map]', numberTag = '[object Number]', nullTag = '[object Null]', objectTag = '[object Object]', promiseTag = '[object Promise]', proxyTag = '[object Proxy]', regexpTag = '[object RegExp]', setTag = '[object Set]', stringTag = '[object String]', symbolTag = '[object Symbol]', undefinedTag = '[object Undefined]', weakMapTag = '[object WeakMap]', weakSetTag = '[object WeakSet]'; var arrayBufferTag = '[object ArrayBuffer]', dataViewTag = '[object DataView]', float32Tag = '[object Float32Array]', float64Tag = '[object Float64Array]', int8Tag = '[object Int8Array]', int16Tag = '[object Int16Array]', int32Tag = '[object Int32Array]', uint8Tag = '[object Uint8Array]', uint8ClampedTag = '[object Uint8ClampedArray]', uint16Tag = '[object Uint16Array]', uint32Tag = '[object Uint32Array]'; /** Used to match empty string literals in compiled template source. */ var reEmptyStringLeading = /\b__p \+= '';/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; /** Used to match HTML entities and HTML characters. */ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g, reUnescapedHtml = /[&<>"']/g, reHasEscapedHtml = RegExp(reEscapedHtml.source), reHasUnescapedHtml = RegExp(reUnescapedHtml.source); /** Used to match template delimiters. */ var reEscape = /<%-([\s\S]+?)%>/g, reEvaluate = /<%([\s\S]+?)%>/g, reInterpolate = /<%=([\s\S]+?)%>/g; /** Used to match property names within property paths. */ var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, reIsPlainProp = /^\w*$/, rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; /** * Used to match `RegExp` * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). */ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, reHasRegExpChar = RegExp(reRegExpChar.source); /** Used to match leading and trailing whitespace. */ var reTrim = /^\s+|\s+$/g, reTrimStart = /^\s+/, reTrimEnd = /\s+$/; /** Used to match wrap detail comments. */ var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/, reSplitDetails = /,? & /; /** Used to match words composed of alphanumeric characters. */ var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; /** Used to match backslashes in property paths. */ var reEscapeChar = /\\(\\)?/g; /** * Used to match * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). */ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; /** Used to match `RegExp` flags from their coerced string values. */ var reFlags = /\w*$/; /** Used to detect bad signed hexadecimal string values. */ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; /** Used to detect binary string values. */ var reIsBinary = /^0b[01]+$/i; /** Used to detect host constructors (Safari). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** Used to detect octal string values. */ var reIsOctal = /^0o[0-7]+$/i; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** Used to match Latin Unicode letters (excluding mathematical operators). */ var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; /** Used to ensure capturing order of template delimiters. */ var reNoMatch = /($^)/; /** Used to match unescaped characters in compiled string literals. */ var reUnescapedString = /['\n\r\u2028\u2029\\]/g; /** Used to compose unicode character classes. */ var rsAstralRange = '\\ud800-\\udfff', rsComboMarksRange = '\\u0300-\\u036f', reComboHalfMarksRange = '\\ufe20-\\ufe2f', rsComboSymbolsRange = '\\u20d0-\\u20ff', rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, rsDingbatRange = '\\u2700-\\u27bf', rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', rsPunctuationRange = '\\u2000-\\u206f', rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', rsVarRange = '\\ufe0e\\ufe0f', rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; /** Used to compose unicode capture groups. */ var rsApos = "['\u2019]", rsAstral = '[' + rsAstralRange + ']', rsBreak = '[' + rsBreakRange + ']', rsCombo = '[' + rsComboRange + ']', rsDigits = '\\d+', rsDingbat = '[' + rsDingbatRange + ']', rsLower = '[' + rsLowerRange + ']', rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', rsFitz = '\\ud83c[\\udffb-\\udfff]', rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', rsNonAstral = '[^' + rsAstralRange + ']', rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', rsUpper = '[' + rsUpperRange + ']', rsZWJ = '\\u200d'; /** Used to compose unicode regexes. */ var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', reOptMod = rsModifier + '?', rsOptVar = '[' + rsVarRange + ']?', rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])', rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])', rsSeq = rsOptVar + reOptMod + rsOptJoin, rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; /** Used to match apostrophes. */ var reApos = RegExp(rsApos, 'g'); /** * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). */ var reComboMark = RegExp(rsCombo, 'g'); /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); /** Used to match complex or compound words. */ var reUnicodeWord = RegExp([ rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, rsUpper + '+' + rsOptContrUpper, rsOrdUpper, rsOrdLower, rsDigits, rsEmoji ].join('|'), 'g'); /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); /** Used to detect strings that need a more robust regexp to match words. */ var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; /** Used to assign default `context` object properties. */ var contextProps = [ 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout' ]; /** Used to make template sourceURLs easier to identify. */ var templateCounter = -1; /** Used to identify `toStringTag` values of typed arrays. */ var typedArrayTags = {}; typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; /** Used to identify `toStringTag` values supported by `_.clone`. */ var cloneableTags = {}; cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false; /** Used to map Latin Unicode letters to basic Latin letters. */ var deburredLetters = { // Latin-1 Supplement block. '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', '\xc7': 'C', '\xe7': 'c', '\xd0': 'D', '\xf0': 'd', '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', '\xd1': 'N', '\xf1': 'n', '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', '\xc6': 'Ae', '\xe6': 'ae', '\xde': 'Th', '\xfe': 'th', '\xdf': 'ss', // Latin Extended-A block. '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', '\u0134': 'J', '\u0135': 'j', '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', '\u0163': 't', '\u0165': 't', '\u0167': 't', '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', '\u0174': 'W', '\u0175': 'w', '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', '\u0132': 'IJ', '\u0133': 'ij', '\u0152': 'Oe', '\u0153': 'oe', '\u0149': "'n", '\u017f': 's' }; /** Used to map characters to HTML entities. */ var htmlEscapes = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; /** Used to map HTML entities to characters. */ var htmlUnescapes = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'" }; /** Used to escape characters for inclusion in compiled string literals. */ var stringEscapes = { '\\': '\\', "'": "'", '\n': 'n', '\r': 'r', '\u2028': 'u2028', '\u2029': 'u2029' }; /** Built-in method references without a dependency on `root`. */ var freeParseFloat = parseFloat, freeParseInt = parseInt; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** Detect free variable `exports`. */ var freeExports = exports && !exports.nodeType && exports; /** Detect free variable `module`. */ var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports`. */ var moduleExports = freeModule && freeModule.exports === freeExports; /** Detect free variable `process` from Node.js. */ var freeProcess = moduleExports && freeGlobal.process; /** Used to access faster Node.js helpers. */ var nodeUtil = (function() { try { // Use `util.types` for Node.js 10+. var types = freeModule && freeModule.require && freeModule.require('util').types; if (types) { return types; } // Legacy `process.binding('util')` for Node.js < 10. return freeProcess && freeProcess.binding && freeProcess.binding('util'); } catch (e) {} }()); /* Node.js helper references. */ var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer, nodeIsDate = nodeUtil && nodeUtil.isDate, nodeIsMap = nodeUtil && nodeUtil.isMap, nodeIsRegExp = nodeUtil && nodeUtil.isRegExp, nodeIsSet = nodeUtil && nodeUtil.isSet, nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; /*--------------------------------------------------------------------------*/ /** * A faster alternative to `Function#apply`, this function invokes `func` * with the `this` binding of `thisArg` and the arguments of `args`. * * @private * @param {Function} func The function to invoke. * @param {*} thisArg The `this` binding of `func`. * @param {Array} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ function apply(func, thisArg, args) { switch (args.length) { case 0: return func.call(thisArg); case 1: return func.call(thisArg, args[0]); case 2: return func.call(thisArg, args[0], args[1]); case 3: return func.call(thisArg, args[0], args[1], args[2]); } return func.apply(thisArg, args); } /** * A specialized version of `baseAggregator` for arrays. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} setter The function to set `accumulator` values. * @param {Function} iteratee The iteratee to transform keys. * @param {Object} accumulator The initial aggregated object. * @returns {Function} Returns `accumulator`. */ function arrayAggregator(array, setter, iteratee, accumulator) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { var value = array[index]; setter(accumulator, value, iteratee(value), array); } return accumulator; } /** * A specialized version of `_.forEach` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns `array`. */ function arrayEach(array, iteratee) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (iteratee(array[index], index, array) === false) { break; } } return array; } /** * A specialized version of `_.forEachRight` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns `array`. */ function arrayEachRight(array, iteratee) { var length = array == null ? 0 : array.length; while (length--) { if (iteratee(array[length], length, array) === false) { break; } } return array; } /** * A specialized version of `_.every` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false`. */ function arrayEvery(array, predicate) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (!predicate(array[index], index, array)) { return false; } } return true; } /** * A specialized version of `_.filter` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {Array} Returns the new filtered array. */ function arrayFilter(array, predicate) { var index = -1, length = array == null ? 0 : array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result[resIndex++] = value; } } return result; } /** * A specialized version of `_.includes` for arrays without support for * specifying an index to search from. * * @private * @param {Array} [array] The array to inspect. * @param {*} target The value to search for. * @returns {boolean} Returns `true` if `target` is found, else `false`. */ function arrayIncludes(array, value) { var length = array == null ? 0 : array.length; return !!length && baseIndexOf(array, value, 0) > -1; } /** * This function is like `arrayIncludes` except that it accepts a comparator. * * @private * @param {Array} [array] The array to inspect. * @param {*} target The value to search for. * @param {Function} comparator The comparator invoked per element. * @returns {boolean} Returns `true` if `target` is found, else `false`. */ function arrayIncludesWith(array, value, comparator) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (comparator(value, array[index])) { return true; } } return false; } /** * A specialized version of `_.map` for arrays without support for iteratee * shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the new mapped array. */ function arrayMap(array, iteratee) { var index = -1, length = array == null ? 0 : array.length, result = Array(length); while (++index < length) { result[index] = iteratee(array[index], index, array); } return result; } /** * Appends the elements of `values` to `array`. * * @private * @param {Array} array The array to modify. * @param {Array} values The values to append. * @returns {Array} Returns `array`. */ function arrayPush(array, values) { var index = -1, length = values.length, offset = array.length; while (++index < length) { array[offset + index] = values[index]; } return array; } /** * A specialized version of `_.reduce` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initAccum] Specify using the first element of `array` as * the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduce(array, iteratee, accumulator, initAccum) { var index = -1, length = array == null ? 0 : array.length; if (initAccum && length) { accumulator = array[++index]; } while (++index < length) { accumulator = iteratee(accumulator, array[index], index, array); } return accumulator; } /** * A specialized version of `_.reduceRight` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initAccum] Specify using the last element of `array` as * the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduceRight(array, iteratee, accumulator, initAccum) { var length = array == null ? 0 : array.length; if (initAccum && length) { accumulator = array[--length]; } while (length--) { accumulator = iteratee(accumulator, array[length], length, array); } return accumulator; } /** * A specialized version of `_.some` for arrays without support for iteratee * shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. */ function arraySome(array, predicate) { var index = -1, length = array == null ? 0 : array.length; while (++index < length) { if (predicate(array[index], index, array)) { return true; } } return false; } /** * Gets the size of an ASCII `string`. * * @private * @param {string} string The string inspect. * @returns {number} Returns the string size. */ var asciiSize = baseProperty('length'); /** * Converts an ASCII `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function asciiToArray(string) { return string.split(''); } /** * Splits an ASCII `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function asciiWords(string) { return string.match(reAsciiWord) || []; } /** * The base implementation of methods like `_.findKey` and `_.findLastKey`, * without support for iteratee shorthands, which iterates over `collection` * using `eachFunc`. * * @private * @param {Array|Object} collection The collection to inspect. * @param {Function} predicate The function invoked per iteration. * @param {Function} eachFunc The function to iterate over `collection`. * @returns {*} Returns the found element or its key, else `undefined`. */ function baseFindKey(collection, predicate, eachFunc) { var result; eachFunc(collection, function(value, key, collection) { if (predicate(value, key, collection)) { result = key; return false; } }); return result; } /** * The base implementation of `_.findIndex` and `_.findLastIndex` without * support for iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Function} predicate The function invoked per iteration. * @param {number} fromIndex The index to search from. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {number} Returns the index of the matched value, else `-1`. */ function baseFindIndex(array, predicate, fromIndex, fromRight) { var length = array.length, index = fromIndex + (fromRight ? 1 : -1); while ((fromRight ? index-- : ++index < length)) { if (predicate(array[index], index, array)) { return index; } } return -1; } /** * The base implementation of `_.indexOf` without `fromIndex` bounds checks. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ function baseIndexOf(array, value, fromIndex) { return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex); } /** * This function is like `baseIndexOf` except that it accepts a comparator. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @param {Function} comparator The comparator invoked per element. * @returns {number} Returns the index of the matched value, else `-1`. */ function baseIndexOfWith(array, value, fromIndex, comparator) { var index = fromIndex - 1, length = array.length; while (++index < length) { if (comparator(array[index], value)) { return index; } } return -1; } /** * The base implementation of `_.isNaN` without support for number objects. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. */ function baseIsNaN(value) { return value !== value; } /** * The base implementation of `_.mean` and `_.meanBy` without support for * iteratee shorthands. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {number} Returns the mean. */ function baseMean(array, iteratee) { var length = array == null ? 0 : array.length; return length ? (baseSum(array, iteratee) / length) : NAN; } /** * The base implementation of `_.property` without support for deep paths. * * @private * @param {string} key The key of the property to get. * @returns {Function} Returns the new accessor function. */ function baseProperty(key) { return function(object) { return object == null ? undefined$1 : object[key]; }; } /** * The base implementation of `_.propertyOf` without support for deep paths. * * @private * @param {Object} object The object to query. * @returns {Function} Returns the new accessor function. */ function basePropertyOf(object) { return function(key) { return object == null ? undefined$1 : object[key]; }; } /** * The base implementation of `_.reduce` and `_.reduceRight`, without support * for iteratee shorthands, which iterates over `collection` using `eachFunc`. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} accumulator The initial value. * @param {boolean} initAccum Specify using the first or last element of * `collection` as the initial value. * @param {Function} eachFunc The function to iterate over `collection`. * @returns {*} Returns the accumulated value. */ function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { eachFunc(collection, function(value, index, collection) { accumulator = initAccum ? (initAccum = false, value) : iteratee(accumulator, value, index, collection); }); return accumulator; } /** * The base implementation of `_.sortBy` which uses `comparer` to define the * sort order of `array` and replaces criteria objects with their corresponding * values. * * @private * @param {Array} array The array to sort. * @param {Function} comparer The function to define sort order. * @returns {Array} Returns `array`. */ function baseSortBy(array, comparer) { var length = array.length; array.sort(comparer); while (length--) { array[length] = array[length].value; } return array; } /** * The base implementation of `_.sum` and `_.sumBy` without support for * iteratee shorthands. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {number} Returns the sum. */ function baseSum(array, iteratee) { var result, index = -1, length = array.length; while (++index < length) { var current = iteratee(array[index]); if (current !== undefined$1) { result = result === undefined$1 ? current : (result + current); } } return result; } /** * The base implementation of `_.times` without support for iteratee shorthands * or max array length checks. * * @private * @param {number} n The number of times to invoke `iteratee`. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the array of results. */ function baseTimes(n, iteratee) { var index = -1, result = Array(n); while (++index < n) { result[index] = iteratee(index); } return result; } /** * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array * of key-value pairs for `object` corresponding to the property names of `props`. * * @private * @param {Object} object The object to query. * @param {Array} props The property names to get values for. * @returns {Object} Returns the key-value pairs. */ function baseToPairs(object, props) { return arrayMap(props, function(key) { return [key, object[key]]; }); } /** * The base implementation of `_.unary` without support for storing metadata. * * @private * @param {Function} func The function to cap arguments for. * @returns {Function} Returns the new capped function. */ function baseUnary(func) { return function(value) { return func(value); }; } /** * The base implementation of `_.values` and `_.valuesIn` which creates an * array of `object` property values corresponding to the property names * of `props`. * * @private * @param {Object} object The object to query. * @param {Array} props The property names to get values for. * @returns {Object} Returns the array of property values. */ function baseValues(object, props) { return arrayMap(props, function(key) { return object[key]; }); } /** * Checks if a `cache` value for `key` exists. * * @private * @param {Object} cache The cache to query. * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function cacheHas(cache, key) { return cache.has(key); } /** * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol * that is not found in the character symbols. * * @private * @param {Array} strSymbols The string symbols to inspect. * @param {Array} chrSymbols The character symbols to find. * @returns {number} Returns the index of the first unmatched string symbol. */ function charsStartIndex(strSymbols, chrSymbols) { var index = -1, length = strSymbols.length; while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} return index; } /** * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol * that is not found in the character symbols. * * @private * @param {Array} strSymbols The string symbols to inspect. * @param {Array} chrSymbols The character symbols to find. * @returns {number} Returns the index of the last unmatched string symbol. */ function charsEndIndex(strSymbols, chrSymbols) { var index = strSymbols.length; while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} return index; } /** * Gets the number of `placeholder` occurrences in `array`. * * @private * @param {Array} array The array to inspect. * @param {*} placeholder The placeholder to search for. * @returns {number} Returns the placeholder count. */ function countHolders(array, placeholder) { var length = array.length, result = 0; while (length--) { if (array[length] === placeholder) { ++result; } } return result; } /** * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A * letters to basic Latin letters. * * @private * @param {string} letter The matched letter to deburr. * @returns {string} Returns the deburred letter. */ var deburrLetter = basePropertyOf(deburredLetters); /** * Used by `_.escape` to convert characters to HTML entities. * * @private * @param {string} chr The matched character to escape. * @returns {string} Returns the escaped character. */ var escapeHtmlChar = basePropertyOf(htmlEscapes); /** * Used by `_.template` to escape characters for inclusion in compiled string literals. * * @private * @param {string} chr The matched character to escape. * @returns {string} Returns the escaped character. */ function escapeStringChar(chr) { return '\\' + stringEscapes[chr]; } /** * Gets the value at `key` of `object`. * * @private * @param {Object} [object] The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function getValue(object, key) { return object == null ? undefined$1 : object[key]; } /** * Checks if `string` contains Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a symbol is found, else `false`. */ function hasUnicode(string) { return reHasUnicode.test(string); } /** * Checks if `string` contains a word composed of Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a word is found, else `false`. */ function hasUnicodeWord(string) { return reHasUnicodeWord.test(string); } /** * Converts `iterator` to an array. * * @private * @param {Object} iterator The iterator to convert. * @returns {Array} Returns the converted array. */ function iteratorToArray(iterator) { var data, result = []; while (!(data = iterator.next()).done) { result.push(data.value); } return result; } /** * Converts `map` to its key-value pairs. * * @private * @param {Object} map The map to convert. * @returns {Array} Returns the key-value pairs. */ function mapToArray(map) { var index = -1, result = Array(map.size); map.forEach(function(value, key) { result[++index] = [key, value]; }); return result; } /** * Creates a unary function that invokes `func` with its argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** * Replaces all `placeholder` elements in `array` with an internal placeholder * and returns an array of their indexes. * * @private * @param {Array} array The array to modify. * @param {*} placeholder The placeholder to replace. * @returns {Array} Returns the new array of placeholder indexes. */ function replaceHolders(array, placeholder) { var index = -1, length = array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index]; if (value === placeholder || value === PLACEHOLDER) { array[index] = PLACEHOLDER; result[resIndex++] = index; } } return result; } /** * Converts `set` to an array of its values. * * @private * @param {Object} set The set to convert. * @returns {Array} Returns the values. */ function setToArray(set) { var index = -1, result = Array(set.size); set.forEach(function(value) { result[++index] = value; }); return result; } /** * Converts `set` to its value-value pairs. * * @private * @param {Object} set The set to convert. * @returns {Array} Returns the value-value pairs. */ function setToPairs(set) { var index = -1, result = Array(set.size); set.forEach(function(value) { result[++index] = [value, value]; }); return result; } /** * A specialized version of `_.indexOf` which performs strict equality * comparisons of values, i.e. `===`. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ function strictIndexOf(array, value, fromIndex) { var index = fromIndex - 1, length = array.length; while (++index < length) { if (array[index] === value) { return index; } } return -1; } /** * A specialized version of `_.lastIndexOf` which performs strict equality * comparisons of values, i.e. `===`. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ function strictLastIndexOf(array, value, fromIndex) { var index = fromIndex + 1; while (index--) { if (array[index] === value) { return index; } } return index; } /** * Gets the number of symbols in `string`. * * @private * @param {string} string The string to inspect. * @returns {number} Returns the string size. */ function stringSize(string) { return hasUnicode(string) ? unicodeSize(string) : asciiSize(string); } /** * Converts `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function stringToArray(string) { return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string); } /** * Used by `_.unescape` to convert HTML entities to characters. * * @private * @param {string} chr The matched character to unescape. * @returns {string} Returns the unescaped character. */ var unescapeHtmlChar = basePropertyOf(htmlUnescapes); /** * Gets the size of a Unicode `string`. * * @private * @param {string} string The string inspect. * @returns {number} Returns the string size. */ function unicodeSize(string) { var result = reUnicode.lastIndex = 0; while (reUnicode.test(string)) { ++result; } return result; } /** * Converts a Unicode `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function unicodeToArray(string) { return string.match(reUnicode) || []; } /** * Splits a Unicode `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function unicodeWords(string) { return string.match(reUnicodeWord) || []; } /*--------------------------------------------------------------------------*/ /** * Create a new pristine `lodash` function using the `context` object. * * @static * @memberOf _ * @since 1.1.0 * @category Util * @param {Object} [context=root] The context object. * @returns {Function} Returns a new `lodash` function. * @example * * _.mixin({ 'foo': _.constant('foo') }); * * var lodash = _.runInContext(); * lodash.mixin({ 'bar': lodash.constant('bar') }); * * _.isFunction(_.foo); * // => true * _.isFunction(_.bar); * // => false * * lodash.isFunction(lodash.foo); * // => false * lodash.isFunction(lodash.bar); * // => true * * // Create a suped-up `defer` in Node.js. * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; */ var runInContext = (function runInContext(context) { context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps)); /** Built-in constructor references. */ var Array = context.Array, Date = context.Date, Error = context.Error, Function = context.Function, Math = context.Math, Object = context.Object, RegExp = context.RegExp, String = context.String, TypeError = context.TypeError; /** Used for built-in method references. */ var arrayProto = Array.prototype, funcProto = Function.prototype, objectProto = Object.prototype; /** Used to detect overreaching core-js shims. */ var coreJsData = context['__core-js_shared__']; /** Used to resolve the decompiled source of functions. */ var funcToString = funcProto.toString; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** Used to generate unique IDs. */ var idCounter = 0; /** Used to detect methods masquerading as native. */ var maskSrcKey = (function() { var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); return uid ? ('Symbol(src)_1.' + uid) : ''; }()); /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var nativeObjectToString = objectProto.toString; /** Used to infer the `Object` constructor. */ var objectCtorString = funcToString.call(Object); /** Used to restore the original `_` reference in `_.noConflict`. */ var oldDash = root._; /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** Built-in value references. */ var Buffer = moduleExports ? context.Buffer : undefined$1, Symbol = context.Symbol, Uint8Array = context.Uint8Array, allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined$1, getPrototype = overArg(Object.getPrototypeOf, Object), objectCreate = Object.create, propertyIsEnumerable = objectProto.propertyIsEnumerable, splice = arrayProto.splice, spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined$1, symIterator = Symbol ? Symbol.iterator : undefined$1, symToStringTag = Symbol ? Symbol.toStringTag : undefined$1; var defineProperty = (function() { try { var func = getNative(Object, 'defineProperty'); func({}, '', {}); return func; } catch (e) {} }()); /** Mocked built-ins. */ var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout, ctxNow = Date && Date.now !== root.Date.now && Date.now, ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeCeil = Math.ceil, nativeFloor = Math.floor, nativeGetSymbols = Object.getOwnPropertySymbols, nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined$1, nativeIsFinite = context.isFinite, nativeJoin = arrayProto.join, nativeKeys = overArg(Object.keys, Object), nativeMax = Math.max, nativeMin = Math.min, nativeNow = Date.now, nativeParseInt = context.parseInt, nativeRandom = Math.random, nativeReverse = arrayProto.reverse; /* Built-in method references that are verified to be native. */ var DataView = getNative(context, 'DataView'), Map = getNative(context, 'Map'), Promise = getNative(context, 'Promise'), Set = getNative(context, 'Set'), WeakMap = getNative(context, 'WeakMap'), nativeCreate = getNative(Object, 'create'); /** Used to store function metadata. */ var metaMap = WeakMap && new WeakMap; /** Used to lookup unminified function names. */ var realNames = {}; /** Used to detect maps, sets, and weakmaps. */ var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map), promiseCtorString = toSource(Promise), setCtorString = toSource(Set), weakMapCtorString = toSource(WeakMap); /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol ? Symbol.prototype : undefined$1, symbolValueOf = symbolProto ? symbolProto.valueOf : undefined$1, symbolToString = symbolProto ? symbolProto.toString : undefined$1; /*------------------------------------------------------------------------*/ /** * Creates a `lodash` object which wraps `value` to enable implicit method * chain sequences. Methods that operate on and return arrays, collections, * and functions can be chained together. Methods that retrieve a single value * or may return a primitive value will automatically end the chain sequence * and return the unwrapped value. Otherwise, the value must be unwrapped * with `_#value`. * * Explicit chain sequences, which must be unwrapped with `_#value`, may be * enabled using `_.chain`. * * The execution of chained methods is lazy, that is, it's deferred until * `_#value` is implicitly or explicitly called. * * Lazy evaluation allows several methods to support shortcut fusion. * Shortcut fusion is an optimization to merge iteratee calls; this avoids * the creation of intermediate arrays and can greatly reduce the number of * iteratee executions. Sections of a chain sequence qualify for shortcut * fusion if the section is applied to an array and iteratees accept only * one argument. The heuristic for whether a section qualifies for shortcut * fusion is subject to change. * * Chaining is supported in custom builds as long as the `_#value` method is * directly or indirectly included in the build. * * In addition to lodash methods, wrappers have `Array` and `String` methods. * * The wrapper `Array` methods are: * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` * * The wrapper `String` methods are: * `replace` and `split` * * The wrapper methods that support shortcut fusion are: * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` * * The chainable wrapper methods are: * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, * `zipObject`, `zipObjectDeep`, and `zipWith` * * The wrapper methods that are **not** chainable by default are: * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, * `upperFirst`, `value`, and `words` * * @name _ * @constructor * @category Seq * @param {*} value The value to wrap in a `lodash` instance. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * function square(n) { * return n * n; * } * * var wrapped = _([1, 2, 3]); * * // Returns an unwrapped value. * wrapped.reduce(_.add); * // => 6 * * // Returns a wrapped value. * var squares = wrapped.map(square); * * _.isArray(squares); * // => false * * _.isArray(squares.value()); * // => true */ function lodash(value) { if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { if (value instanceof LodashWrapper) { return value; } if (hasOwnProperty.call(value, '__wrapped__')) { return wrapperClone(value); } } return new LodashWrapper(value); } /** * The base implementation of `_.create` without support for assigning * properties to the created object. * * @private * @param {Object} proto The object to inherit from. * @returns {Object} Returns the new object. */ var baseCreate = (function() { function object() {} return function(proto) { if (!isObject(proto)) { return {}; } if (objectCreate) { return objectCreate(proto); } object.prototype = proto; var result = new object; object.prototype = undefined$1; return result; }; }()); /** * The function whose prototype chain sequence wrappers inherit from. * * @private */ function baseLodash() { // No operation performed. } /** * The base constructor for creating `lodash` wrapper objects. * * @private * @param {*} value The value to wrap. * @param {boolean} [chainAll] Enable explicit method chain sequences. */ function LodashWrapper(value, chainAll) { this.__wrapped__ = value; this.__actions__ = []; this.__chain__ = !!chainAll; this.__index__ = 0; this.__values__ = undefined$1; } /** * By default, the template delimiters used by lodash are like those in * embedded Ruby (ERB) as well as ES2015 template strings. Change the * following template settings to use alternative delimiters. * * @static * @memberOf _ * @type {Object} */ lodash.templateSettings = { /** * Used to detect `data` property values to be HTML-escaped. * * @memberOf _.templateSettings * @type {RegExp} */ 'escape': reEscape, /** * Used to detect code to be evaluated. * * @memberOf _.templateSettings * @type {RegExp} */ 'evaluate': reEvaluate, /** * Used to detect `data` property values to inject. * * @memberOf _.templateSettings * @type {RegExp} */ 'interpolate': reInterpolate, /** * Used to reference the data object in the template text. * * @memberOf _.templateSettings * @type {string} */ 'variable': '', /** * Used to import variables into the compiled template. * * @memberOf _.templateSettings * @type {Object} */ 'imports': { /** * A reference to the `lodash` function. * * @memberOf _.templateSettings.imports * @type {Function} */ '_': lodash } }; // Ensure wrappers are instances of `baseLodash`. lodash.prototype = baseLodash.prototype; lodash.prototype.constructor = lodash; LodashWrapper.prototype = baseCreate(baseLodash.prototype); LodashWrapper.prototype.constructor = LodashWrapper; /*------------------------------------------------------------------------*/ /** * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. * * @private * @constructor * @param {*} value The value to wrap. */ function LazyWrapper(value) { this.__wrapped__ = value; this.__actions__ = []; this.__dir__ = 1; this.__filtered__ = false; this.__iteratees__ = []; this.__takeCount__ = MAX_ARRAY_LENGTH; this.__views__ = []; } /** * Creates a clone of the lazy wrapper object. * * @private * @name clone * @memberOf LazyWrapper * @returns {Object} Returns the cloned `LazyWrapper` object. */ function lazyClone() { var result = new LazyWrapper(this.__wrapped__); result.__actions__ = copyArray(this.__actions__); result.__dir__ = this.__dir__; result.__filtered__ = this.__filtered__; result.__iteratees__ = copyArray(this.__iteratees__); result.__takeCount__ = this.__takeCount__; result.__views__ = copyArray(this.__views__); return result; } /** * Reverses the direction of lazy iteration. * * @private * @name reverse * @memberOf LazyWrapper * @returns {Object} Returns the new reversed `LazyWrapper` object. */ function lazyReverse() { if (this.__filtered__) { var result = new LazyWrapper(this); result.__dir__ = -1; result.__filtered__ = true; } else { result = this.clone(); result.__dir__ *= -1; } return result; } /** * Extracts the unwrapped value from its lazy wrapper. * * @private * @name value * @memberOf LazyWrapper * @returns {*} Returns the unwrapped value. */ function lazyValue() { var array = this.__wrapped__.value(), dir = this.__dir__, isArr = isArray(array), isRight = dir < 0, arrLength = isArr ? array.length : 0, view = getView(0, arrLength, this.__views__), start = view.start, end = view.end, length = end - start, index = isRight ? end : (start - 1), iteratees = this.__iteratees__, iterLength = iteratees.length, resIndex = 0, takeCount = nativeMin(length, this.__takeCount__); if (!isArr || (!isRight && arrLength == length && takeCount == length)) { return baseWrapperValue(array, this.__actions__); } var result = []; outer: while (length-- && resIndex < takeCount) { index += dir; var iterIndex = -1, value = array[index]; while (++iterIndex < iterLength) { var data = iteratees[iterIndex], iteratee = data.iteratee, type = data.type, computed = iteratee(value); if (type == LAZY_MAP_FLAG) { value = computed; } else if (!computed) { if (type == LAZY_FILTER_FLAG) { continue outer; } else { break outer; } } } result[resIndex++] = value; } return result; } // Ensure `LazyWrapper` is an instance of `baseLodash`. LazyWrapper.prototype = baseCreate(baseLodash.prototype); LazyWrapper.prototype.constructor = LazyWrapper; /*------------------------------------------------------------------------*/ /** * Creates a hash object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Hash(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the hash. * * @private * @name clear * @memberOf Hash */ function hashClear() { this.__data__ = nativeCreate ? nativeCreate(null) : {}; this.size = 0; } /** * Removes `key` and its value from the hash. * * @private * @name delete * @memberOf Hash * @param {Object} hash The hash to modify. * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function hashDelete(key) { var result = this.has(key) && delete this.__data__[key]; this.size -= result ? 1 : 0; return result; } /** * Gets the hash value for `key`. * * @private * @name get * @memberOf Hash * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function hashGet(key) { var data = this.__data__; if (nativeCreate) { var result = data[key]; return result === HASH_UNDEFINED ? undefined$1 : result; } return hasOwnProperty.call(data, key) ? data[key] : undefined$1; } /** * Checks if a hash value for `key` exists. * * @private * @name has * @memberOf Hash * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function hashHas(key) { var data = this.__data__; return nativeCreate ? (data[key] !== undefined$1) : hasOwnProperty.call(data, key); } /** * Sets the hash `key` to `value`. * * @private * @name set * @memberOf Hash * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the hash instance. */ function hashSet(key, value) { var data = this.__data__; this.size += this.has(key) ? 0 : 1; data[key] = (nativeCreate && value === undefined$1) ? HASH_UNDEFINED : value; return this; } // Add methods to `Hash`. Hash.prototype.clear = hashClear; Hash.prototype['delete'] = hashDelete; Hash.prototype.get = hashGet; Hash.prototype.has = hashHas; Hash.prototype.set = hashSet; /*------------------------------------------------------------------------*/ /** * Creates an list cache object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function ListCache(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the list cache. * * @private * @name clear * @memberOf ListCache */ function listCacheClear() { this.__data__ = []; this.size = 0; } /** * Removes `key` and its value from the list cache. * * @private * @name delete * @memberOf ListCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function listCacheDelete(key) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { return false; } var lastIndex = data.length - 1; if (index == lastIndex) { data.pop(); } else { splice.call(data, index, 1); } --this.size; return true; } /** * Gets the list cache value for `key`. * * @private * @name get * @memberOf ListCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function listCacheGet(key) { var data = this.__data__, index = assocIndexOf(data, key); return index < 0 ? undefined$1 : data[index][1]; } /** * Checks if a list cache value for `key` exists. * * @private * @name has * @memberOf ListCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function listCacheHas(key) { return assocIndexOf(this.__data__, key) > -1; } /** * Sets the list cache `key` to `value`. * * @private * @name set * @memberOf ListCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the list cache instance. */ function listCacheSet(key, value) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { ++this.size; data.push([key, value]); } else { data[index][1] = value; } return this; } // Add methods to `ListCache`. ListCache.prototype.clear = listCacheClear; ListCache.prototype['delete'] = listCacheDelete; ListCache.prototype.get = listCacheGet; ListCache.prototype.has = listCacheHas; ListCache.prototype.set = listCacheSet; /*------------------------------------------------------------------------*/ /** * Creates a map cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function MapCache(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the map. * * @private * @name clear * @memberOf MapCache */ function mapCacheClear() { this.size = 0; this.__data__ = { 'hash': new Hash, 'map': new (Map || ListCache), 'string': new Hash }; } /** * Removes `key` and its value from the map. * * @private * @name delete * @memberOf MapCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function mapCacheDelete(key) { var result = getMapData(this, key)['delete'](key); this.size -= result ? 1 : 0; return result; } /** * Gets the map value for `key`. * * @private * @name get * @memberOf MapCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function mapCacheGet(key) { return getMapData(this, key).get(key); } /** * Checks if a map value for `key` exists. * * @private * @name has * @memberOf MapCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function mapCacheHas(key) { return getMapData(this, key).has(key); } /** * Sets the map `key` to `value`. * * @private * @name set * @memberOf MapCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the map cache instance. */ function mapCacheSet(key, value) { var data = getMapData(this, key), size = data.size; data.set(key, value); this.size += data.size == size ? 0 : 1; return this; } // Add methods to `MapCache`. MapCache.prototype.clear = mapCacheClear; MapCache.prototype['delete'] = mapCacheDelete; MapCache.prototype.get = mapCacheGet; MapCache.prototype.has = mapCacheHas; MapCache.prototype.set = mapCacheSet; /*------------------------------------------------------------------------*/ /** * * Creates an array cache object to store unique values. * * @private * @constructor * @param {Array} [values] The values to cache. */ function SetCache(values) { var index = -1, length = values == null ? 0 : values.length; this.__data__ = new MapCache; while (++index < length) { this.add(values[index]); } } /** * Adds `value` to the array cache. * * @private * @name add * @memberOf SetCache * @alias push * @param {*} value The value to cache. * @returns {Object} Returns the cache instance. */ function setCacheAdd(value) { this.__data__.set(value, HASH_UNDEFINED); return this; } /** * Checks if `value` is in the array cache. * * @private * @name has * @memberOf SetCache * @param {*} value The value to search for. * @returns {number} Returns `true` if `value` is found, else `false`. */ function setCacheHas(value) { return this.__data__.has(value); } // Add methods to `SetCache`. SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; SetCache.prototype.has = setCacheHas; /*------------------------------------------------------------------------*/ /** * Creates a stack cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Stack(entries) { var data = this.__data__ = new ListCache(entries); this.size = data.size; } /** * Removes all key-value entries from the stack. * * @private * @name clear * @memberOf Stack */ function stackClear() { this.__data__ = new ListCache; this.size = 0; } /** * Removes `key` and its value from the stack. * * @private * @name delete * @memberOf Stack * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function stackDelete(key) { var data = this.__data__, result = data['delete'](key); this.size = data.size; return result; } /** * Gets the stack value for `key`. * * @private * @name get * @memberOf Stack * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function stackGet(key) { return this.__data__.get(key); } /** * Checks if a stack value for `key` exists. * * @private * @name has * @memberOf Stack * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function stackHas(key) { return this.__data__.has(key); } /** * Sets the stack `key` to `value`. * * @private * @name set * @memberOf Stack * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the stack cache instance. */ function stackSet(key, value) { var data = this.__data__; if (data instanceof ListCache) { var pairs = data.__data__; if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { pairs.push([key, value]); this.size = ++data.size; return this; } data = this.__data__ = new MapCache(pairs); } data.set(key, value); this.size = data.size; return this; } // Add methods to `Stack`. Stack.prototype.clear = stackClear; Stack.prototype['delete'] = stackDelete; Stack.prototype.get = stackGet; Stack.prototype.has = stackHas; Stack.prototype.set = stackSet; /*------------------------------------------------------------------------*/ /** * Creates an array of the enumerable property names of the array-like `value`. * * @private * @param {*} value The value to query. * @param {boolean} inherited Specify returning inherited property names. * @returns {Array} Returns the array of property names. */ function arrayLikeKeys(value, inherited) { var isArr = isArray(value), isArg = !isArr && isArguments(value), isBuff = !isArr && !isArg && isBuffer(value), isType = !isArr && !isArg && !isBuff && isTypedArray(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes(value.length, String) : [], length = result.length; for (var key in value) { if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && ( // Safari 9 has enumerable `arguments.length` in strict mode. key == 'length' || // Node.js 0.10 has enumerable non-index properties on buffers. (isBuff && (key == 'offset' || key == 'parent')) || // PhantomJS 2 has enumerable non-index properties on typed arrays. (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || // Skip index properties. isIndex(key, length) ))) { result.push(key); } } return result; } /** * A specialized version of `_.sample` for arrays. * * @private * @param {Array} array The array to sample. * @returns {*} Returns the random element. */ function arraySample(array) { var length = array.length; return length ? array[baseRandom(0, length - 1)] : undefined$1; } /** * A specialized version of `_.sampleSize` for arrays. * * @private * @param {Array} array The array to sample. * @param {number} n The number of elements to sample. * @returns {Array} Returns the random elements. */ function arraySampleSize(array, n) { return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); } /** * A specialized version of `_.shuffle` for arrays. * * @private * @param {Array} array The array to shuffle. * @returns {Array} Returns the new shuffled array. */ function arrayShuffle(array) { return shuffleSelf(copyArray(array)); } /** * This function is like `assignValue` except that it doesn't assign * `undefined` values. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignMergeValue(object, key, value) { if ((value !== undefined$1 && !eq(object[key], value)) || (value === undefined$1 && !(key in object))) { baseAssignValue(object, key, value); } } /** * Assigns `value` to `key` of `object` if the existing value is not equivalent * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignValue(object, key, value) { var objValue = object[key]; if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || (value === undefined$1 && !(key in object))) { baseAssignValue(object, key, value); } } /** * Gets the index at which the `key` is found in `array` of key-value pairs. * * @private * @param {Array} array The array to inspect. * @param {*} key The key to search for. * @returns {number} Returns the index of the matched value, else `-1`. */ function assocIndexOf(array, key) { var length = array.length; while (length--) { if (eq(array[length][0], key)) { return length; } } return -1; } /** * Aggregates elements of `collection` on `accumulator` with keys transformed * by `iteratee` and values set by `setter`. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} setter The function to set `accumulator` values. * @param {Function} iteratee The iteratee to transform keys. * @param {Object} accumulator The initial aggregated object. * @returns {Function} Returns `accumulator`. */ function baseAggregator(collection, setter, iteratee, accumulator) { baseEach(collection, function(value, key, collection) { setter(accumulator, value, iteratee(value), collection); }); return accumulator; } /** * The base implementation of `_.assign` without support for multiple sources * or `customizer` functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @returns {Object} Returns `object`. */ function baseAssign(object, source) { return object && copyObject(source, keys(source), object); } /** * The base implementation of `_.assignIn` without support for multiple sources * or `customizer` functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @returns {Object} Returns `object`. */ function baseAssignIn(object, source) { return object && copyObject(source, keysIn(source), object); } /** * The base implementation of `assignValue` and `assignMergeValue` without * value checks. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function baseAssignValue(object, key, value) { if (key == '__proto__' && defineProperty) { defineProperty(object, key, { 'configurable': true, 'enumerable': true, 'value': value, 'writable': true }); } else { object[key] = value; } } /** * The base implementation of `_.at` without support for individual paths. * * @private * @param {Object} object The object to iterate over. * @param {string[]} paths The property paths to pick. * @returns {Array} Returns the picked elements. */ function baseAt(object, paths) { var index = -1, length = paths.length, result = Array(length), skip = object == null; while (++index < length) { result[index] = skip ? undefined$1 : get(object, paths[index]); } return result; } /** * The base implementation of `_.clamp` which doesn't coerce arguments. * * @private * @param {number} number The number to clamp. * @param {number} [lower] The lower bound. * @param {number} upper The upper bound. * @returns {number} Returns the clamped number. */ function baseClamp(number, lower, upper) { if (number === number) { if (upper !== undefined$1) { number = number <= upper ? number : upper; } if (lower !== undefined$1) { number = number >= lower ? number : lower; } } return number; } /** * The base implementation of `_.clone` and `_.cloneDeep` which tracks * traversed objects. * * @private * @param {*} value The value to clone. * @param {boolean} bitmask The bitmask flags. * 1 - Deep clone * 2 - Flatten inherited properties * 4 - Clone symbols * @param {Function} [customizer] The function to customize cloning. * @param {string} [key] The key of `value`. * @param {Object} [object] The parent object of `value`. * @param {Object} [stack] Tracks traversed objects and their clone counterparts. * @returns {*} Returns the cloned value. */ function baseClone(value, bitmask, customizer, key, object, stack) { var result, isDeep = bitmask & CLONE_DEEP_FLAG, isFlat = bitmask & CLONE_FLAT_FLAG, isFull = bitmask & CLONE_SYMBOLS_FLAG; if (customizer) { result = object ? customizer(value, key, object, stack) : customizer(value); } if (result !== undefined$1) { return result; } if (!isObject(value)) { return value; } var isArr = isArray(value); if (isArr) { result = initCloneArray(value); if (!isDeep) { return copyArray(value, result); } } else { var tag = getTag(value), isFunc = tag == funcTag || tag == genTag; if (isBuffer(value)) { return cloneBuffer(value, isDeep); } if (tag == objectTag || tag == argsTag || (isFunc && !object)) { result = (isFlat || isFunc) ? {} : initCloneObject(value); if (!isDeep) { return isFlat ? copySymbolsIn(value, baseAssignIn(result, value)) : copySymbols(value, baseAssign(result, value)); } } else { if (!cloneableTags[tag]) { return object ? value : {}; } result = initCloneByTag(value, tag, isDeep); } } // Check for circular references and return its corresponding clone. stack || (stack = new Stack); var stacked = stack.get(value); if (stacked) { return stacked; } stack.set(value, result); if (isSet(value)) { value.forEach(function(subValue) { result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)); }); } else if (isMap(value)) { value.forEach(function(subValue, key) { result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)); }); } var keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : (isFlat ? keysIn : keys); var props = isArr ? undefined$1 : keysFunc(value); arrayEach(props || value, function(subValue, key) { if (props) { key = subValue; subValue = value[key]; } // Recursively populate clone (susceptible to call stack limits). assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); }); return result; } /** * The base implementation of `_.conforms` which doesn't clone `source`. * * @private * @param {Object} source The object of property predicates to conform to. * @returns {Function} Returns the new spec function. */ function baseConforms(source) { var props = keys(source); return function(object) { return baseConformsTo(object, source, props); }; } /** * The base implementation of `_.conformsTo` which accepts `props` to check. * * @private * @param {Object} object The object to inspect. * @param {Object} source The object of property predicates to conform to. * @returns {boolean} Returns `true` if `object` conforms, else `false`. */ function baseConformsTo(object, source, props) { var length = props.length; if (object == null) { return !length; } object = Object(object); while (length--) { var key = props[length], predicate = source[key], value = object[key]; if ((value === undefined$1 && !(key in object)) || !predicate(value)) { return false; } } return true; } /** * The base implementation of `_.delay` and `_.defer` which accepts `args` * to provide to `func`. * * @private * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @param {Array} args The arguments to provide to `func`. * @returns {number|Object} Returns the timer id or timeout object. */ function baseDelay(func, wait, args) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } return setTimeout(function() { func.apply(undefined$1, args); }, wait); } /** * The base implementation of methods like `_.difference` without support * for excluding multiple arrays or iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Array} values The values to exclude. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. */ function baseDifference(array, values, iteratee, comparator) { var index = -1, includes = arrayIncludes, isCommon = true, length = array.length, result = [], valuesLength = values.length; if (!length) { return result; } if (iteratee) { values = arrayMap(values, baseUnary(iteratee)); } if (comparator) { includes = arrayIncludesWith; isCommon = false; } else if (values.length >= LARGE_ARRAY_SIZE) { includes = cacheHas; isCommon = false; values = new SetCache(values); } outer: while (++index < length) { var value = array[index], computed = iteratee == null ? value : iteratee(value); value = (comparator || value !== 0) ? value : 0; if (isCommon && computed === computed) { var valuesIndex = valuesLength; while (valuesIndex--) { if (values[valuesIndex] === computed) { continue outer; } } result.push(value); } else if (!includes(values, computed, comparator)) { result.push(value); } } return result; } /** * The base implementation of `_.forEach` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object} Returns `collection`. */ var baseEach = createBaseEach(baseForOwn); /** * The base implementation of `_.forEachRight` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object} Returns `collection`. */ var baseEachRight = createBaseEach(baseForOwnRight, true); /** * The base implementation of `_.every` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false` */ function baseEvery(collection, predicate) { var result = true; baseEach(collection, function(value, index, collection) { result = !!predicate(value, index, collection); return result; }); return result; } /** * The base implementation of methods like `_.max` and `_.min` which accepts a * `comparator` to determine the extremum value. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The iteratee invoked per iteration. * @param {Function} comparator The comparator used to compare values. * @returns {*} Returns the extremum value. */ function baseExtremum(array, iteratee, comparator) { var index = -1, length = array.length; while (++index < length) { var value = array[index], current = iteratee(value); if (current != null && (computed === undefined$1 ? (current === current && !isSymbol(current)) : comparator(current, computed) )) { var computed = current, result = value; } } return result; } /** * The base implementation of `_.fill` without an iteratee call guard. * * @private * @param {Array} array The array to fill. * @param {*} value The value to fill `array` with. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns `array`. */ function baseFill(array, value, start, end) { var length = array.length; start = toInteger(start); if (start < 0) { start = -start > length ? 0 : (length + start); } end = (end === undefined$1 || end > length) ? length : toInteger(end); if (end < 0) { end += length; } end = start > end ? 0 : toLength(end); while (start < end) { array[start++] = value; } return array; } /** * The base implementation of `_.filter` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {Array} Returns the new filtered array. */ function baseFilter(collection, predicate) { var result = []; baseEach(collection, function(value, index, collection) { if (predicate(value, index, collection)) { result.push(value); } }); return result; } /** * The base implementation of `_.flatten` with support for restricting flattening. * * @private * @param {Array} array The array to flatten. * @param {number} depth The maximum recursion depth. * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. * @param {Array} [result=[]] The initial result value. * @returns {Array} Returns the new flattened array. */ function baseFlatten(array, depth, predicate, isStrict, result) { var index = -1, length = array.length; predicate || (predicate = isFlattenable); result || (result = []); while (++index < length) { var value = array[index]; if (depth > 0 && predicate(value)) { if (depth > 1) { // Recursively flatten arrays (susceptible to call stack limits). baseFlatten(value, depth - 1, predicate, isStrict, result); } else { arrayPush(result, value); } } else if (!isStrict) { result[result.length] = value; } } return result; } /** * The base implementation of `baseForOwn` which iterates over `object` * properties returned by `keysFunc` and invokes `iteratee` for each property. * Iteratee functions may exit iteration early by explicitly returning `false`. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ var baseFor = createBaseFor(); /** * This function is like `baseFor` except that it iterates over properties * in the opposite order. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ var baseForRight = createBaseFor(true); /** * The base implementation of `_.forOwn` without support for iteratee shorthands. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ function baseForOwn(object, iteratee) { return object && baseFor(object, iteratee, keys); } /** * The base implementation of `_.forOwnRight` without support for iteratee shorthands. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ function baseForOwnRight(object, iteratee) { return object && baseForRight(object, iteratee, keys); } /** * The base implementation of `_.functions` which creates an array of * `object` function property names filtered from `props`. * * @private * @param {Object} object The object to inspect. * @param {Array} props The property names to filter. * @returns {Array} Returns the function names. */ function baseFunctions(object, props) { return arrayFilter(props, function(key) { return isFunction(object[key]); }); } /** * The base implementation of `_.get` without support for default values. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @returns {*} Returns the resolved value. */ function baseGet(object, path) { path = castPath(path, object); var index = 0, length = path.length; while (object != null && index < length) { object = object[toKey(path[index++])]; } return (index && index == length) ? object : undefined$1; } /** * The base implementation of `getAllKeys` and `getAllKeysIn` which uses * `keysFunc` and `symbolsFunc` to get the enumerable property names and * symbols of `object`. * * @private * @param {Object} object The object to query. * @param {Function} keysFunc The function to get the keys of `object`. * @param {Function} symbolsFunc The function to get the symbols of `object`. * @returns {Array} Returns the array of property names and symbols. */ function baseGetAllKeys(object, keysFunc, symbolsFunc) { var result = keysFunc(object); return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); } /** * The base implementation of `getTag` without fallbacks for buggy environments. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ function baseGetTag(value) { if (value == null) { return value === undefined$1 ? undefinedTag : nullTag; } return (symToStringTag && symToStringTag in Object(value)) ? getRawTag(value) : objectToString(value); } /** * The base implementation of `_.gt` which doesn't coerce arguments. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is greater than `other`, * else `false`. */ function baseGt(value, other) { return value > other; } /** * The base implementation of `_.has` without support for deep paths. * * @private * @param {Object} [object] The object to query. * @param {Array|string} key The key to check. * @returns {boolean} Returns `true` if `key` exists, else `false`. */ function baseHas(object, key) { return object != null && hasOwnProperty.call(object, key); } /** * The base implementation of `_.hasIn` without support for deep paths. * * @private * @param {Object} [object] The object to query. * @param {Array|string} key The key to check. * @returns {boolean} Returns `true` if `key` exists, else `false`. */ function baseHasIn(object, key) { return object != null && key in Object(object); } /** * The base implementation of `_.inRange` which doesn't coerce arguments. * * @private * @param {number} number The number to check. * @param {number} start The start of the range. * @param {number} end The end of the range. * @returns {boolean} Returns `true` if `number` is in the range, else `false`. */ function baseInRange(number, start, end) { return number >= nativeMin(start, end) && number < nativeMax(start, end); } /** * The base implementation of methods like `_.intersection`, without support * for iteratee shorthands, that accepts an array of arrays to inspect. * * @private * @param {Array} arrays The arrays to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of shared values. */ function baseIntersection(arrays, iteratee, comparator) { var includes = comparator ? arrayIncludesWith : arrayIncludes, length = arrays[0].length, othLength = arrays.length, othIndex = othLength, caches = Array(othLength), maxLength = Infinity, result = []; while (othIndex--) { var array = arrays[othIndex]; if (othIndex && iteratee) { array = arrayMap(array, baseUnary(iteratee)); } maxLength = nativeMin(array.length, maxLength); caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) ? new SetCache(othIndex && array) : undefined$1; } array = arrays[0]; var index = -1, seen = caches[0]; outer: while (++index < length && result.length < maxLength) { var value = array[index], computed = iteratee ? iteratee(value) : value; value = (comparator || value !== 0) ? value : 0; if (!(seen ? cacheHas(seen, computed) : includes(result, computed, comparator) )) { othIndex = othLength; while (--othIndex) { var cache = caches[othIndex]; if (!(cache ? cacheHas(cache, computed) : includes(arrays[othIndex], computed, comparator)) ) { continue outer; } } if (seen) { seen.push(computed); } result.push(value); } } return result; } /** * The base implementation of `_.invert` and `_.invertBy` which inverts * `object` with values transformed by `iteratee` and set by `setter`. * * @private * @param {Object} object The object to iterate over. * @param {Function} setter The function to set `accumulator` values. * @param {Function} iteratee The iteratee to transform values. * @param {Object} accumulator The initial inverted object. * @returns {Function} Returns `accumulator`. */ function baseInverter(object, setter, iteratee, accumulator) { baseForOwn(object, function(value, key, object) { setter(accumulator, iteratee(value), key, object); }); return accumulator; } /** * The base implementation of `_.invoke` without support for individual * method arguments. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the method to invoke. * @param {Array} args The arguments to invoke the method with. * @returns {*} Returns the result of the invoked method. */ function baseInvoke(object, path, args) { path = castPath(path, object); object = parent(object, path); var func = object == null ? object : object[toKey(last(path))]; return func == null ? undefined$1 : apply(func, object, args); } /** * The base implementation of `_.isArguments`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, */ function baseIsArguments(value) { return isObjectLike(value) && baseGetTag(value) == argsTag; } /** * The base implementation of `_.isArrayBuffer` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. */ function baseIsArrayBuffer(value) { return isObjectLike(value) && baseGetTag(value) == arrayBufferTag; } /** * The base implementation of `_.isDate` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a date object, else `false`. */ function baseIsDate(value) { return isObjectLike(value) && baseGetTag(value) == dateTag; } /** * The base implementation of `_.isEqual` which supports partial comparisons * and tracks traversed objects. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @param {boolean} bitmask The bitmask flags. * 1 - Unordered comparison * 2 - Partial comparison * @param {Function} [customizer] The function to customize comparisons. * @param {Object} [stack] Tracks traversed `value` and `other` objects. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ function baseIsEqual(value, other, bitmask, customizer, stack) { if (value === other) { return true; } if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { return value !== value && other !== other; } return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); } /** * A specialized version of `baseIsEqual` for arrays and objects which performs * deep comparisons and tracks traversed objects enabling objects with circular * references to be compared. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} [stack] Tracks traversed `object` and `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { var objIsArr = isArray(object), othIsArr = isArray(other), objTag = objIsArr ? arrayTag : getTag(object), othTag = othIsArr ? arrayTag : getTag(other); objTag = objTag == argsTag ? objectTag : objTag; othTag = othTag == argsTag ? objectTag : othTag; var objIsObj = objTag == objectTag, othIsObj = othTag == objectTag, isSameTag = objTag == othTag; if (isSameTag && isBuffer(object)) { if (!isBuffer(other)) { return false; } objIsArr = true; objIsObj = false; } if (isSameTag && !objIsObj) { stack || (stack = new Stack); return (objIsArr || isTypedArray(object)) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); } if (!(bitmask & COMPARE_PARTIAL_FLAG)) { var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); if (objIsWrapped || othIsWrapped) { var objUnwrapped = objIsWrapped ? object.value() : object, othUnwrapped = othIsWrapped ? other.value() : other; stack || (stack = new Stack); return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); } } if (!isSameTag) { return false; } stack || (stack = new Stack); return equalObjects(object, other, bitmask, customizer, equalFunc, stack); } /** * The base implementation of `_.isMap` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a map, else `false`. */ function baseIsMap(value) { return isObjectLike(value) && getTag(value) == mapTag; } /** * The base implementation of `_.isMatch` without support for iteratee shorthands. * * @private * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. * @param {Array} matchData The property names, values, and compare flags to match. * @param {Function} [customizer] The function to customize comparisons. * @returns {boolean} Returns `true` if `object` is a match, else `false`. */ function baseIsMatch(object, source, matchData, customizer) { var index = matchData.length, length = index, noCustomizer = !customizer; if (object == null) { return !length; } object = Object(object); while (index--) { var data = matchData[index]; if ((noCustomizer && data[2]) ? data[1] !== object[data[0]] : !(data[0] in object) ) { return false; } } while (++index < length) { data = matchData[index]; var key = data[0], objValue = object[key], srcValue = data[1]; if (noCustomizer && data[2]) { if (objValue === undefined$1 && !(key in object)) { return false; } } else { var stack = new Stack; if (customizer) { var result = customizer(objValue, srcValue, key, object, source, stack); } if (!(result === undefined$1 ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result )) { return false; } } } return true; } /** * The base implementation of `_.isNative` without bad shim checks. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. */ function baseIsNative(value) { if (!isObject(value) || isMasked(value)) { return false; } var pattern = isFunction(value) ? reIsNative : reIsHostCtor; return pattern.test(toSource(value)); } /** * The base implementation of `_.isRegExp` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. */ function baseIsRegExp(value) { return isObjectLike(value) && baseGetTag(value) == regexpTag; } /** * The base implementation of `_.isSet` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a set, else `false`. */ function baseIsSet(value) { return isObjectLike(value) && getTag(value) == setTag; } /** * The base implementation of `_.isTypedArray` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. */ function baseIsTypedArray(value) { return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; } /** * The base implementation of `_.iteratee`. * * @private * @param {*} [value=_.identity] The value to convert to an iteratee. * @returns {Function} Returns the iteratee. */ function baseIteratee(value) { // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. if (typeof value == 'function') { return value; } if (value == null) { return identity; } if (typeof value == 'object') { return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value); } return property(value); } /** * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function baseKeys(object) { if (!isPrototype(object)) { return nativeKeys(object); } var result = []; for (var key in Object(object)) { if (hasOwnProperty.call(object, key) && key != 'constructor') { result.push(key); } } return result; } /** * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function baseKeysIn(object) { if (!isObject(object)) { return nativeKeysIn(object); } var isProto = isPrototype(object), result = []; for (var key in object) { if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { result.push(key); } } return result; } /** * The base implementation of `_.lt` which doesn't coerce arguments. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is less than `other`, * else `false`. */ function baseLt(value, other) { return value < other; } /** * The base implementation of `_.map` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the new mapped array. */ function baseMap(collection, iteratee) { var index = -1, result = isArrayLike(collection) ? Array(collection.length) : []; baseEach(collection, function(value, key, collection) { result[++index] = iteratee(value, key, collection); }); return result; } /** * The base implementation of `_.matches` which doesn't clone `source`. * * @private * @param {Object} source The object of property values to match. * @returns {Function} Returns the new spec function. */ function baseMatches(source) { var matchData = getMatchData(source); if (matchData.length == 1 && matchData[0][2]) { return matchesStrictComparable(matchData[0][0], matchData[0][1]); } return function(object) { return object === source || baseIsMatch(object, source, matchData); }; } /** * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. * * @private * @param {string} path The path of the property to get. * @param {*} srcValue The value to match. * @returns {Function} Returns the new spec function. */ function baseMatchesProperty(path, srcValue) { if (isKey(path) && isStrictComparable(srcValue)) { return matchesStrictComparable(toKey(path), srcValue); } return function(object) { var objValue = get(object, path); return (objValue === undefined$1 && objValue === srcValue) ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); }; } /** * The base implementation of `_.merge` without support for multiple sources. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {number} srcIndex The index of `source`. * @param {Function} [customizer] The function to customize merged values. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. */ function baseMerge(object, source, srcIndex, customizer, stack) { if (object === source) { return; } baseFor(source, function(srcValue, key) { stack || (stack = new Stack); if (isObject(srcValue)) { baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); } else { var newValue = customizer ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) : undefined$1; if (newValue === undefined$1) { newValue = srcValue; } assignMergeValue(object, key, newValue); } }, keysIn); } /** * A specialized version of `baseMerge` for arrays and objects which performs * deep merges and tracks traversed objects enabling objects with circular * references to be merged. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {string} key The key of the value to merge. * @param {number} srcIndex The index of `source`. * @param {Function} mergeFunc The function to merge values. * @param {Function} [customizer] The function to customize assigned values. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. */ function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { var objValue = safeGet(object, key), srcValue = safeGet(source, key), stacked = stack.get(srcValue); if (stacked) { assignMergeValue(object, key, stacked); return; } var newValue = customizer ? customizer(objValue, srcValue, (key + ''), object, source, stack) : undefined$1; var isCommon = newValue === undefined$1; if (isCommon) { var isArr = isArray(srcValue), isBuff = !isArr && isBuffer(srcValue), isTyped = !isArr && !isBuff && isTypedArray(srcValue); newValue = srcValue; if (isArr || isBuff || isTyped) { if (isArray(objValue)) { newValue = objValue; } else if (isArrayLikeObject(objValue)) { newValue = copyArray(objValue); } else if (isBuff) { isCommon = false; newValue = cloneBuffer(srcValue, true); } else if (isTyped) { isCommon = false; newValue = cloneTypedArray(srcValue, true); } else { newValue = []; } } else if (isPlainObject(srcValue) || isArguments(srcValue)) { newValue = objValue; if (isArguments(objValue)) { newValue = toPlainObject(objValue); } else if (!isObject(objValue) || isFunction(objValue)) { newValue = initCloneObject(srcValue); } } else { isCommon = false; } } if (isCommon) { // Recursively merge objects and arrays (susceptible to call stack limits). stack.set(srcValue, newValue); mergeFunc(newValue, srcValue, srcIndex, customizer, stack); stack['delete'](srcValue); } assignMergeValue(object, key, newValue); } /** * The base implementation of `_.nth` which doesn't coerce arguments. * * @private * @param {Array} array The array to query. * @param {number} n The index of the element to return. * @returns {*} Returns the nth element of `array`. */ function baseNth(array, n) { var length = array.length; if (!length) { return; } n += n < 0 ? length : 0; return isIndex(n, length) ? array[n] : undefined$1; } /** * The base implementation of `_.orderBy` without param guards. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. * @param {string[]} orders The sort orders of `iteratees`. * @returns {Array} Returns the new sorted array. */ function baseOrderBy(collection, iteratees, orders) { if (iteratees.length) { iteratees = arrayMap(iteratees, function(iteratee) { if (isArray(iteratee)) { return function(value) { return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee); } } return iteratee; }); } else { iteratees = [identity]; } var index = -1; iteratees = arrayMap(iteratees, baseUnary(getIteratee())); var result = baseMap(collection, function(value, key, collection) { var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); return { 'criteria': criteria, 'index': ++index, 'value': value }; }); return baseSortBy(result, function(object, other) { return compareMultiple(object, other, orders); }); } /** * The base implementation of `_.pick` without support for individual * property identifiers. * * @private * @param {Object} object The source object. * @param {string[]} paths The property paths to pick. * @returns {Object} Returns the new object. */ function basePick(object, paths) { return basePickBy(object, paths, function(value, path) { return hasIn(object, path); }); } /** * The base implementation of `_.pickBy` without support for iteratee shorthands. * * @private * @param {Object} object The source object. * @param {string[]} paths The property paths to pick. * @param {Function} predicate The function invoked per property. * @returns {Object} Returns the new object. */ function basePickBy(object, paths, predicate) { var index = -1, length = paths.length, result = {}; while (++index < length) { var path = paths[index], value = baseGet(object, path); if (predicate(value, path)) { baseSet(result, castPath(path, object), value); } } return result; } /** * A specialized version of `baseProperty` which supports deep paths. * * @private * @param {Array|string} path The path of the property to get. * @returns {Function} Returns the new accessor function. */ function basePropertyDeep(path) { return function(object) { return baseGet(object, path); }; } /** * The base implementation of `_.pullAllBy` without support for iteratee * shorthands. * * @private * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns `array`. */ function basePullAll(array, values, iteratee, comparator) { var indexOf = comparator ? baseIndexOfWith : baseIndexOf, index = -1, length = values.length, seen = array; if (array === values) { values = copyArray(values); } if (iteratee) { seen = arrayMap(array, baseUnary(iteratee)); } while (++index < length) { var fromIndex = 0, value = values[index], computed = iteratee ? iteratee(value) : value; while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { if (seen !== array) { splice.call(seen, fromIndex, 1); } splice.call(array, fromIndex, 1); } } return array; } /** * The base implementation of `_.pullAt` without support for individual * indexes or capturing the removed elements. * * @private * @param {Array} array The array to modify. * @param {number[]} indexes The indexes of elements to remove. * @returns {Array} Returns `array`. */ function basePullAt(array, indexes) { var length = array ? indexes.length : 0, lastIndex = length - 1; while (length--) { var index = indexes[length]; if (length == lastIndex || index !== previous) { var previous = index; if (isIndex(index)) { splice.call(array, index, 1); } else { baseUnset(array, index); } } } return array; } /** * The base implementation of `_.random` without support for returning * floating-point numbers. * * @private * @param {number} lower The lower bound. * @param {number} upper The upper bound. * @returns {number} Returns the random number. */ function baseRandom(lower, upper) { return lower + nativeFloor(nativeRandom() * (upper - lower + 1)); } /** * The base implementation of `_.range` and `_.rangeRight` which doesn't * coerce arguments. * * @private * @param {number} start The start of the range. * @param {number} end The end of the range. * @param {number} step The value to increment or decrement by. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Array} Returns the range of numbers. */ function baseRange(start, end, step, fromRight) { var index = -1, length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), result = Array(length); while (length--) { result[fromRight ? length : ++index] = start; start += step; } return result; } /** * The base implementation of `_.repeat` which doesn't coerce arguments. * * @private * @param {string} string The string to repeat. * @param {number} n The number of times to repeat the string. * @returns {string} Returns the repeated string. */ function baseRepeat(string, n) { var result = ''; if (!string || n < 1 || n > MAX_SAFE_INTEGER) { return result; } // Leverage the exponentiation by squaring algorithm for a faster repeat. // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. do { if (n % 2) { result += string; } n = nativeFloor(n / 2); if (n) { string += string; } } while (n); return result; } /** * The base implementation of `_.rest` which doesn't validate or coerce arguments. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. */ function baseRest(func, start) { return setToString(overRest(func, start, identity), func + ''); } /** * The base implementation of `_.sample`. * * @private * @param {Array|Object} collection The collection to sample. * @returns {*} Returns the random element. */ function baseSample(collection) { return arraySample(values(collection)); } /** * The base implementation of `_.sampleSize` without param guards. * * @private * @param {Array|Object} collection The collection to sample. * @param {number} n The number of elements to sample. * @returns {Array} Returns the random elements. */ function baseSampleSize(collection, n) { var array = values(collection); return shuffleSelf(array, baseClamp(n, 0, array.length)); } /** * The base implementation of `_.set`. * * @private * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @param {Function} [customizer] The function to customize path creation. * @returns {Object} Returns `object`. */ function baseSet(object, path, value, customizer) { if (!isObject(object)) { return object; } path = castPath(path, object); var index = -1, length = path.length, lastIndex = length - 1, nested = object; while (nested != null && ++index < length) { var key = toKey(path[index]), newValue = value; if (key === '__proto__' || key === 'constructor' || key === 'prototype') { return object; } if (index != lastIndex) { var objValue = nested[key]; newValue = customizer ? customizer(objValue, key, nested) : undefined$1; if (newValue === undefined$1) { newValue = isObject(objValue) ? objValue : (isIndex(path[index + 1]) ? [] : {}); } } assignValue(nested, key, newValue); nested = nested[key]; } return object; } /** * The base implementation of `setData` without support for hot loop shorting. * * @private * @param {Function} func The function to associate metadata with. * @param {*} data The metadata. * @returns {Function} Returns `func`. */ var baseSetData = !metaMap ? identity : function(func, data) { metaMap.set(func, data); return func; }; /** * The base implementation of `setToString` without support for hot loop shorting. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var baseSetToString = !defineProperty ? identity : function(func, string) { return defineProperty(func, 'toString', { 'configurable': true, 'enumerable': false, 'value': constant(string), 'writable': true }); }; /** * The base implementation of `_.shuffle`. * * @private * @param {Array|Object} collection The collection to shuffle. * @returns {Array} Returns the new shuffled array. */ function baseShuffle(collection) { return shuffleSelf(values(collection)); } /** * The base implementation of `_.slice` without an iteratee call guard. * * @private * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function baseSlice(array, start, end) { var index = -1, length = array.length; if (start < 0) { start = -start > length ? 0 : (length + start); } end = end > length ? length : end; if (end < 0) { end += length; } length = start > end ? 0 : ((end - start) >>> 0); start >>>= 0; var result = Array(length); while (++index < length) { result[index] = array[index + start]; } return result; } /** * The base implementation of `_.some` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. */ function baseSome(collection, predicate) { var result; baseEach(collection, function(value, index, collection) { result = predicate(value, index, collection); return !result; }); return !!result; } /** * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which * performs a binary search of `array` to determine the index at which `value` * should be inserted into `array` in order to maintain its sort order. * * @private * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {boolean} [retHighest] Specify returning the highest qualified index. * @returns {number} Returns the index at which `value` should be inserted * into `array`. */ function baseSortedIndex(array, value, retHighest) { var low = 0, high = array == null ? low : array.length; if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { while (low < high) { var mid = (low + high) >>> 1, computed = array[mid]; if (computed !== null && !isSymbol(computed) && (retHighest ? (computed <= value) : (computed < value))) { low = mid + 1; } else { high = mid; } } return high; } return baseSortedIndexBy(array, value, identity, retHighest); } /** * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` * which invokes `iteratee` for `value` and each element of `array` to compute * their sort ranking. The iteratee is invoked with one argument; (value). * * @private * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function} iteratee The iteratee invoked per element. * @param {boolean} [retHighest] Specify returning the highest qualified index. * @returns {number} Returns the index at which `value` should be inserted * into `array`. */ function baseSortedIndexBy(array, value, iteratee, retHighest) { var low = 0, high = array == null ? 0 : array.length; if (high === 0) { return 0; } value = iteratee(value); var valIsNaN = value !== value, valIsNull = value === null, valIsSymbol = isSymbol(value), valIsUndefined = value === undefined$1; while (low < high) { var mid = nativeFloor((low + high) / 2), computed = iteratee(array[mid]), othIsDefined = computed !== undefined$1, othIsNull = computed === null, othIsReflexive = computed === computed, othIsSymbol = isSymbol(computed); if (valIsNaN) { var setLow = retHighest || othIsReflexive; } else if (valIsUndefined) { setLow = othIsReflexive && (retHighest || othIsDefined); } else if (valIsNull) { setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); } else if (valIsSymbol) { setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); } else if (othIsNull || othIsSymbol) { setLow = false; } else { setLow = retHighest ? (computed <= value) : (computed < value); } if (setLow) { low = mid + 1; } else { high = mid; } } return nativeMin(high, MAX_ARRAY_INDEX); } /** * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without * support for iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @returns {Array} Returns the new duplicate free array. */ function baseSortedUniq(array, iteratee) { var index = -1, length = array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index], computed = iteratee ? iteratee(value) : value; if (!index || !eq(computed, seen)) { var seen = computed; result[resIndex++] = value === 0 ? 0 : value; } } return result; } /** * The base implementation of `_.toNumber` which doesn't ensure correct * conversions of binary, hexadecimal, or octal string values. * * @private * @param {*} value The value to process. * @returns {number} Returns the number. */ function baseToNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol(value)) { return NAN; } return +value; } /** * The base implementation of `_.toString` which doesn't convert nullish * values to empty strings. * * @private * @param {*} value The value to process. * @returns {string} Returns the string. */ function baseToString(value) { // Exit early for strings to avoid a performance hit in some environments. if (typeof value == 'string') { return value; } if (isArray(value)) { // Recursively convert values (susceptible to call stack limits). return arrayMap(value, baseToString) + ''; } if (isSymbol(value)) { return symbolToString ? symbolToString.call(value) : ''; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } /** * The base implementation of `_.uniqBy` without support for iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new duplicate free array. */ function baseUniq(array, iteratee, comparator) { var index = -1, includes = arrayIncludes, length = array.length, isCommon = true, result = [], seen = result; if (comparator) { isCommon = false; includes = arrayIncludesWith; } else if (length >= LARGE_ARRAY_SIZE) { var set = iteratee ? null : createSet(array); if (set) { return setToArray(set); } isCommon = false; includes = cacheHas; seen = new SetCache; } else { seen = iteratee ? [] : result; } outer: while (++index < length) { var value = array[index], computed = iteratee ? iteratee(value) : value; value = (comparator || value !== 0) ? value : 0; if (isCommon && computed === computed) { var seenIndex = seen.length; while (seenIndex--) { if (seen[seenIndex] === computed) { continue outer; } } if (iteratee) { seen.push(computed); } result.push(value); } else if (!includes(seen, computed, comparator)) { if (seen !== result) { seen.push(computed); } result.push(value); } } return result; } /** * The base implementation of `_.unset`. * * @private * @param {Object} object The object to modify. * @param {Array|string} path The property path to unset. * @returns {boolean} Returns `true` if the property is deleted, else `false`. */ function baseUnset(object, path) { path = castPath(path, object); object = parent(object, path); return object == null || delete object[toKey(last(path))]; } /** * The base implementation of `_.update`. * * @private * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to update. * @param {Function} updater The function to produce the updated value. * @param {Function} [customizer] The function to customize path creation. * @returns {Object} Returns `object`. */ function baseUpdate(object, path, updater, customizer) { return baseSet(object, path, updater(baseGet(object, path)), customizer); } /** * The base implementation of methods like `_.dropWhile` and `_.takeWhile` * without support for iteratee shorthands. * * @private * @param {Array} array The array to query. * @param {Function} predicate The function invoked per iteration. * @param {boolean} [isDrop] Specify dropping elements instead of taking them. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Array} Returns the slice of `array`. */ function baseWhile(array, predicate, isDrop, fromRight) { var length = array.length, index = fromRight ? length : -1; while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} return isDrop ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); } /** * The base implementation of `wrapperValue` which returns the result of * performing a sequence of actions on the unwrapped `value`, where each * successive action is supplied the return value of the previous. * * @private * @param {*} value The unwrapped value. * @param {Array} actions Actions to perform to resolve the unwrapped value. * @returns {*} Returns the resolved value. */ function baseWrapperValue(value, actions) { var result = value; if (result instanceof LazyWrapper) { result = result.value(); } return arrayReduce(actions, function(result, action) { return action.func.apply(action.thisArg, arrayPush([result], action.args)); }, result); } /** * The base implementation of methods like `_.xor`, without support for * iteratee shorthands, that accepts an array of arrays to inspect. * * @private * @param {Array} arrays The arrays to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of values. */ function baseXor(arrays, iteratee, comparator) { var length = arrays.length; if (length < 2) { return length ? baseUniq(arrays[0]) : []; } var index = -1, result = Array(length); while (++index < length) { var array = arrays[index], othIndex = -1; while (++othIndex < length) { if (othIndex != index) { result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator); } } } return baseUniq(baseFlatten(result, 1), iteratee, comparator); } /** * This base implementation of `_.zipObject` which assigns values using `assignFunc`. * * @private * @param {Array} props The property identifiers. * @param {Array} values The property values. * @param {Function} assignFunc The function to assign values. * @returns {Object} Returns the new object. */ function baseZipObject(props, values, assignFunc) { var index = -1, length = props.length, valsLength = values.length, result = {}; while (++index < length) { var value = index < valsLength ? values[index] : undefined$1; assignFunc(result, props[index], value); } return result; } /** * Casts `value` to an empty array if it's not an array like object. * * @private * @param {*} value The value to inspect. * @returns {Array|Object} Returns the cast array-like object. */ function castArrayLikeObject(value) { return isArrayLikeObject(value) ? value : []; } /** * Casts `value` to `identity` if it's not a function. * * @private * @param {*} value The value to inspect. * @returns {Function} Returns cast function. */ function castFunction(value) { return typeof value == 'function' ? value : identity; } /** * Casts `value` to a path array if it's not one. * * @private * @param {*} value The value to inspect. * @param {Object} [object] The object to query keys on. * @returns {Array} Returns the cast property path array. */ function castPath(value, object) { if (isArray(value)) { return value; } return isKey(value, object) ? [value] : stringToPath(toString(value)); } /** * A `baseRest` alias which can be replaced with `identity` by module * replacement plugins. * * @private * @type {Function} * @param {Function} func The function to apply a rest parameter to. * @returns {Function} Returns the new function. */ var castRest = baseRest; /** * Casts `array` to a slice if it's needed. * * @private * @param {Array} array The array to inspect. * @param {number} start The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the cast slice. */ function castSlice(array, start, end) { var length = array.length; end = end === undefined$1 ? length : end; return (!start && end >= length) ? array : baseSlice(array, start, end); } /** * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout). * * @private * @param {number|Object} id The timer id or timeout object of the timer to clear. */ var clearTimeout = ctxClearTimeout || function(id) { return root.clearTimeout(id); }; /** * Creates a clone of `buffer`. * * @private * @param {Buffer} buffer The buffer to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Buffer} Returns the cloned buffer. */ function cloneBuffer(buffer, isDeep) { if (isDeep) { return buffer.slice(); } var length = buffer.length, result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); buffer.copy(result); return result; } /** * Creates a clone of `arrayBuffer`. * * @private * @param {ArrayBuffer} arrayBuffer The array buffer to clone. * @returns {ArrayBuffer} Returns the cloned array buffer. */ function cloneArrayBuffer(arrayBuffer) { var result = new arrayBuffer.constructor(arrayBuffer.byteLength); new Uint8Array(result).set(new Uint8Array(arrayBuffer)); return result; } /** * Creates a clone of `dataView`. * * @private * @param {Object} dataView The data view to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned data view. */ function cloneDataView(dataView, isDeep) { var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); } /** * Creates a clone of `regexp`. * * @private * @param {Object} regexp The regexp to clone. * @returns {Object} Returns the cloned regexp. */ function cloneRegExp(regexp) { var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); result.lastIndex = regexp.lastIndex; return result; } /** * Creates a clone of the `symbol` object. * * @private * @param {Object} symbol The symbol object to clone. * @returns {Object} Returns the cloned symbol object. */ function cloneSymbol(symbol) { return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; } /** * Creates a clone of `typedArray`. * * @private * @param {Object} typedArray The typed array to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned typed array. */ function cloneTypedArray(typedArray, isDeep) { var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); } /** * Compares values to sort them in ascending order. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {number} Returns the sort order indicator for `value`. */ function compareAscending(value, other) { if (value !== other) { var valIsDefined = value !== undefined$1, valIsNull = value === null, valIsReflexive = value === value, valIsSymbol = isSymbol(value); var othIsDefined = other !== undefined$1, othIsNull = other === null, othIsReflexive = other === other, othIsSymbol = isSymbol(other); if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || (valIsNull && othIsDefined && othIsReflexive) || (!valIsDefined && othIsReflexive) || !valIsReflexive) { return 1; } if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || (othIsNull && valIsDefined && valIsReflexive) || (!othIsDefined && valIsReflexive) || !othIsReflexive) { return -1; } } return 0; } /** * Used by `_.orderBy` to compare multiple properties of a value to another * and stable sort them. * * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, * specify an order of "desc" for descending or "asc" for ascending sort order * of corresponding values. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {boolean[]|string[]} orders The order to sort by for each property. * @returns {number} Returns the sort order indicator for `object`. */ function compareMultiple(object, other, orders) { var index = -1, objCriteria = object.criteria, othCriteria = other.criteria, length = objCriteria.length, ordersLength = orders.length; while (++index < length) { var result = compareAscending(objCriteria[index], othCriteria[index]); if (result) { if (index >= ordersLength) { return result; } var order = orders[index]; return result * (order == 'desc' ? -1 : 1); } } // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications // that causes it, under certain circumstances, to provide the same value for // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 // for more details. // // This also ensures a stable sort in V8 and other engines. // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. return object.index - other.index; } /** * Creates an array that is the composition of partially applied arguments, * placeholders, and provided arguments into a single array of arguments. * * @private * @param {Array} args The provided arguments. * @param {Array} partials The arguments to prepend to those provided. * @param {Array} holders The `partials` placeholder indexes. * @params {boolean} [isCurried] Specify composing for a curried function. * @returns {Array} Returns the new array of composed arguments. */ function composeArgs(args, partials, holders, isCurried) { var argsIndex = -1, argsLength = args.length, holdersLength = holders.length, leftIndex = -1, leftLength = partials.length, rangeLength = nativeMax(argsLength - holdersLength, 0), result = Array(leftLength + rangeLength), isUncurried = !isCurried; while (++leftIndex < leftLength) { result[leftIndex] = partials[leftIndex]; } while (++argsIndex < holdersLength) { if (isUncurried || argsIndex < argsLength) { result[holders[argsIndex]] = args[argsIndex]; } } while (rangeLength--) { result[leftIndex++] = args[argsIndex++]; } return result; } /** * This function is like `composeArgs` except that the arguments composition * is tailored for `_.partialRight`. * * @private * @param {Array} args The provided arguments. * @param {Array} partials The arguments to append to those provided. * @param {Array} holders The `partials` placeholder indexes. * @params {boolean} [isCurried] Specify composing for a curried function. * @returns {Array} Returns the new array of composed arguments. */ function composeArgsRight(args, partials, holders, isCurried) { var argsIndex = -1, argsLength = args.length, holdersIndex = -1, holdersLength = holders.length, rightIndex = -1, rightLength = partials.length, rangeLength = nativeMax(argsLength - holdersLength, 0), result = Array(rangeLength + rightLength), isUncurried = !isCurried; while (++argsIndex < rangeLength) { result[argsIndex] = args[argsIndex]; } var offset = argsIndex; while (++rightIndex < rightLength) { result[offset + rightIndex] = partials[rightIndex]; } while (++holdersIndex < holdersLength) { if (isUncurried || argsIndex < argsLength) { result[offset + holders[holdersIndex]] = args[argsIndex++]; } } return result; } /** * Copies the values of `source` to `array`. * * @private * @param {Array} source The array to copy values from. * @param {Array} [array=[]] The array to copy values to. * @returns {Array} Returns `array`. */ function copyArray(source, array) { var index = -1, length = source.length; array || (array = Array(length)); while (++index < length) { array[index] = source[index]; } return array; } /** * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. * @param {Array} props The property identifiers to copy. * @param {Object} [object={}] The object to copy properties to. * @param {Function} [customizer] The function to customize copied values. * @returns {Object} Returns `object`. */ function copyObject(source, props, object, customizer) { var isNew = !object; object || (object = {}); var index = -1, length = props.length; while (++index < length) { var key = props[index]; var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined$1; if (newValue === undefined$1) { newValue = source[key]; } if (isNew) { baseAssignValue(object, key, newValue); } else { assignValue(object, key, newValue); } } return object; } /** * Copies own symbols of `source` to `object`. * * @private * @param {Object} source The object to copy symbols from. * @param {Object} [object={}] The object to copy symbols to. * @returns {Object} Returns `object`. */ function copySymbols(source, object) { return copyObject(source, getSymbols(source), object); } /** * Copies own and inherited symbols of `source` to `object`. * * @private * @param {Object} source The object to copy symbols from. * @param {Object} [object={}] The object to copy symbols to. * @returns {Object} Returns `object`. */ function copySymbolsIn(source, object) { return copyObject(source, getSymbolsIn(source), object); } /** * Creates a function like `_.groupBy`. * * @private * @param {Function} setter The function to set accumulator values. * @param {Function} [initializer] The accumulator object initializer. * @returns {Function} Returns the new aggregator function. */ function createAggregator(setter, initializer) { return function(collection, iteratee) { var func = isArray(collection) ? arrayAggregator : baseAggregator, accumulator = initializer ? initializer() : {}; return func(collection, setter, getIteratee(iteratee, 2), accumulator); }; } /** * Creates a function like `_.assign`. * * @private * @param {Function} assigner The function to assign values. * @returns {Function} Returns the new assigner function. */ function createAssigner(assigner) { return baseRest(function(object, sources) { var index = -1, length = sources.length, customizer = length > 1 ? sources[length - 1] : undefined$1, guard = length > 2 ? sources[2] : undefined$1; customizer = (assigner.length > 3 && typeof customizer == 'function') ? (length--, customizer) : undefined$1; if (guard && isIterateeCall(sources[0], sources[1], guard)) { customizer = length < 3 ? undefined$1 : customizer; length = 1; } object = Object(object); while (++index < length) { var source = sources[index]; if (source) { assigner(object, source, index, customizer); } } return object; }); } /** * Creates a `baseEach` or `baseEachRight` function. * * @private * @param {Function} eachFunc The function to iterate over a collection. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new base function. */ function createBaseEach(eachFunc, fromRight) { return function(collection, iteratee) { if (collection == null) { return collection; } if (!isArrayLike(collection)) { return eachFunc(collection, iteratee); } var length = collection.length, index = fromRight ? length : -1, iterable = Object(collection); while ((fromRight ? index-- : ++index < length)) { if (iteratee(iterable[index], index, iterable) === false) { break; } } return collection; }; } /** * Creates a base function for methods like `_.forIn` and `_.forOwn`. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new base function. */ function createBaseFor(fromRight) { return function(object, iteratee, keysFunc) { var index = -1, iterable = Object(object), props = keysFunc(object), length = props.length; while (length--) { var key = props[fromRight ? length : ++index]; if (iteratee(iterable[key], key, iterable) === false) { break; } } return object; }; } /** * Creates a function that wraps `func` to invoke it with the optional `this` * binding of `thisArg`. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {*} [thisArg] The `this` binding of `func`. * @returns {Function} Returns the new wrapped function. */ function createBind(func, bitmask, thisArg) { var isBind = bitmask & WRAP_BIND_FLAG, Ctor = createCtor(func); function wrapper() { var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; return fn.apply(isBind ? thisArg : this, arguments); } return wrapper; } /** * Creates a function like `_.lowerFirst`. * * @private * @param {string} methodName The name of the `String` case method to use. * @returns {Function} Returns the new case function. */ function createCaseFirst(methodName) { return function(string) { string = toString(string); var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined$1; var chr = strSymbols ? strSymbols[0] : string.charAt(0); var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1); return chr[methodName]() + trailing; }; } /** * Creates a function like `_.camelCase`. * * @private * @param {Function} callback The function to combine each word. * @returns {Function} Returns the new compounder function. */ function createCompounder(callback) { return function(string) { return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); }; } /** * Creates a function that produces an instance of `Ctor` regardless of * whether it was invoked as part of a `new` expression or by `call` or `apply`. * * @private * @param {Function} Ctor The constructor to wrap. * @returns {Function} Returns the new wrapped function. */ function createCtor(Ctor) { return function() { // Use a `switch` statement to work with class constructors. See // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist // for more details. var args = arguments; switch (args.length) { case 0: return new Ctor; case 1: return new Ctor(args[0]); case 2: return new Ctor(args[0], args[1]); case 3: return new Ctor(args[0], args[1], args[2]); case 4: return new Ctor(args[0], args[1], args[2], args[3]); case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); } var thisBinding = baseCreate(Ctor.prototype), result = Ctor.apply(thisBinding, args); // Mimic the constructor's `return` behavior. // See https://es5.github.io/#x13.2.2 for more details. return isObject(result) ? result : thisBinding; }; } /** * Creates a function that wraps `func` to enable currying. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {number} arity The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createCurry(func, bitmask, arity) { var Ctor = createCtor(func); function wrapper() { var length = arguments.length, args = Array(length), index = length, placeholder = getHolder(wrapper); while (index--) { args[index] = arguments[index]; } var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder) ? [] : replaceHolders(args, placeholder); length -= holders.length; if (length < arity) { return createRecurry( func, bitmask, createHybrid, wrapper.placeholder, undefined$1, args, holders, undefined$1, undefined$1, arity - length); } var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; return apply(fn, this, args); } return wrapper; } /** * Creates a `_.find` or `_.findLast` function. * * @private * @param {Function} findIndexFunc The function to find the collection index. * @returns {Function} Returns the new find function. */ function createFind(findIndexFunc) { return function(collection, predicate, fromIndex) { var iterable = Object(collection); if (!isArrayLike(collection)) { var iteratee = getIteratee(predicate, 3); collection = keys(collection); predicate = function(key) { return iteratee(iterable[key], key, iterable); }; } var index = findIndexFunc(collection, predicate, fromIndex); return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined$1; }; } /** * Creates a `_.flow` or `_.flowRight` function. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new flow function. */ function createFlow(fromRight) { return flatRest(function(funcs) { var length = funcs.length, index = length, prereq = LodashWrapper.prototype.thru; if (fromRight) { funcs.reverse(); } while (index--) { var func = funcs[index]; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } if (prereq && !wrapper && getFuncName(func) == 'wrapper') { var wrapper = new LodashWrapper([], true); } } index = wrapper ? index : length; while (++index < length) { func = funcs[index]; var funcName = getFuncName(func), data = funcName == 'wrapper' ? getData(func) : undefined$1; if (data && isLaziable(data[0]) && data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && !data[4].length && data[9] == 1 ) { wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); } else { wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func); } } return function() { var args = arguments, value = args[0]; if (wrapper && args.length == 1 && isArray(value)) { return wrapper.plant(value).value(); } var index = 0, result = length ? funcs[index].apply(this, args) : value; while (++index < length) { result = funcs[index].call(this, result); } return result; }; }); } /** * Creates a function that wraps `func` to invoke it with optional `this` * binding of `thisArg`, partial application, and currying. * * @private * @param {Function|string} func The function or method name to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to prepend to those provided to * the new function. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [partialsRight] The arguments to append to those provided * to the new function. * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { var isAry = bitmask & WRAP_ARY_FLAG, isBind = bitmask & WRAP_BIND_FLAG, isBindKey = bitmask & WRAP_BIND_KEY_FLAG, isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG), isFlip = bitmask & WRAP_FLIP_FLAG, Ctor = isBindKey ? undefined$1 : createCtor(func); function wrapper() { var length = arguments.length, args = Array(length), index = length; while (index--) { args[index] = arguments[index]; } if (isCurried) { var placeholder = getHolder(wrapper), holdersCount = countHolders(args, placeholder); } if (partials) { args = composeArgs(args, partials, holders, isCurried); } if (partialsRight) { args = composeArgsRight(args, partialsRight, holdersRight, isCurried); } length -= holdersCount; if (isCurried && length < arity) { var newHolders = replaceHolders(args, placeholder); return createRecurry( func, bitmask, createHybrid, wrapper.placeholder, thisArg, args, newHolders, argPos, ary, arity - length ); } var thisBinding = isBind ? thisArg : this, fn = isBindKey ? thisBinding[func] : func; length = args.length; if (argPos) { args = reorder(args, argPos); } else if (isFlip && length > 1) { args.reverse(); } if (isAry && ary < length) { args.length = ary; } if (this && this !== root && this instanceof wrapper) { fn = Ctor || createCtor(fn); } return fn.apply(thisBinding, args); } return wrapper; } /** * Creates a function like `_.invertBy`. * * @private * @param {Function} setter The function to set accumulator values. * @param {Function} toIteratee The function to resolve iteratees. * @returns {Function} Returns the new inverter function. */ function createInverter(setter, toIteratee) { return function(object, iteratee) { return baseInverter(object, setter, toIteratee(iteratee), {}); }; } /** * Creates a function that performs a mathematical operation on two values. * * @private * @param {Function} operator The function to perform the operation. * @param {number} [defaultValue] The value used for `undefined` arguments. * @returns {Function} Returns the new mathematical operation function. */ function createMathOperation(operator, defaultValue) { return function(value, other) { var result; if (value === undefined$1 && other === undefined$1) { return defaultValue; } if (value !== undefined$1) { result = value; } if (other !== undefined$1) { if (result === undefined$1) { return other; } if (typeof value == 'string' || typeof other == 'string') { value = baseToString(value); other = baseToString(other); } else { value = baseToNumber(value); other = baseToNumber(other); } result = operator(value, other); } return result; }; } /** * Creates a function like `_.over`. * * @private * @param {Function} arrayFunc The function to iterate over iteratees. * @returns {Function} Returns the new over function. */ function createOver(arrayFunc) { return flatRest(function(iteratees) { iteratees = arrayMap(iteratees, baseUnary(getIteratee())); return baseRest(function(args) { var thisArg = this; return arrayFunc(iteratees, function(iteratee) { return apply(iteratee, thisArg, args); }); }); }); } /** * Creates the padding for `string` based on `length`. The `chars` string * is truncated if the number of characters exceeds `length`. * * @private * @param {number} length The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padding for `string`. */ function createPadding(length, chars) { chars = chars === undefined$1 ? ' ' : baseToString(chars); var charsLength = chars.length; if (charsLength < 2) { return charsLength ? baseRepeat(chars, length) : chars; } var result = baseRepeat(chars, nativeCeil(length / stringSize(chars))); return hasUnicode(chars) ? castSlice(stringToArray(result), 0, length).join('') : result.slice(0, length); } /** * Creates a function that wraps `func` to invoke it with the `this` binding * of `thisArg` and `partials` prepended to the arguments it receives. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {*} thisArg The `this` binding of `func`. * @param {Array} partials The arguments to prepend to those provided to * the new function. * @returns {Function} Returns the new wrapped function. */ function createPartial(func, bitmask, thisArg, partials) { var isBind = bitmask & WRAP_BIND_FLAG, Ctor = createCtor(func); function wrapper() { var argsIndex = -1, argsLength = arguments.length, leftIndex = -1, leftLength = partials.length, args = Array(leftLength + argsLength), fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; while (++leftIndex < leftLength) { args[leftIndex] = partials[leftIndex]; } while (argsLength--) { args[leftIndex++] = arguments[++argsIndex]; } return apply(fn, isBind ? thisArg : this, args); } return wrapper; } /** * Creates a `_.range` or `_.rangeRight` function. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new range function. */ function createRange(fromRight) { return function(start, end, step) { if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { end = step = undefined$1; } // Ensure the sign of `-0` is preserved. start = toFinite(start); if (end === undefined$1) { end = start; start = 0; } else { end = toFinite(end); } step = step === undefined$1 ? (start < end ? 1 : -1) : toFinite(step); return baseRange(start, end, step, fromRight); }; } /** * Creates a function that performs a relational operation on two values. * * @private * @param {Function} operator The function to perform the operation. * @returns {Function} Returns the new relational operation function. */ function createRelationalOperation(operator) { return function(value, other) { if (!(typeof value == 'string' && typeof other == 'string')) { value = toNumber(value); other = toNumber(other); } return operator(value, other); }; } /** * Creates a function that wraps `func` to continue currying. * * @private * @param {Function} func The function to wrap. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @param {Function} wrapFunc The function to create the `func` wrapper. * @param {*} placeholder The placeholder value. * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to prepend to those provided to * the new function. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { var isCurry = bitmask & WRAP_CURRY_FLAG, newHolders = isCurry ? holders : undefined$1, newHoldersRight = isCurry ? undefined$1 : holders, newPartials = isCurry ? partials : undefined$1, newPartialsRight = isCurry ? undefined$1 : partials; bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG); bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG); if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG); } var newData = [ func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity ]; var result = wrapFunc.apply(undefined$1, newData); if (isLaziable(func)) { setData(result, newData); } result.placeholder = placeholder; return setWrapToString(result, func, bitmask); } /** * Creates a function like `_.round`. * * @private * @param {string} methodName The name of the `Math` method to use when rounding. * @returns {Function} Returns the new round function. */ function createRound(methodName) { var func = Math[methodName]; return function(number, precision) { number = toNumber(number); precision = precision == null ? 0 : nativeMin(toInteger(precision), 292); if (precision && nativeIsFinite(number)) { // Shift with exponential notation to avoid floating-point issues. // See [MDN](https://mdn.io/round#Examples) for more details. var pair = (toString(number) + 'e').split('e'), value = func(pair[0] + 'e' + (+pair[1] + precision)); pair = (toString(value) + 'e').split('e'); return +(pair[0] + 'e' + (+pair[1] - precision)); } return func(number); }; } /** * Creates a set object of `values`. * * @private * @param {Array} values The values to add to the set. * @returns {Object} Returns the new set. */ var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { return new Set(values); }; /** * Creates a `_.toPairs` or `_.toPairsIn` function. * * @private * @param {Function} keysFunc The function to get the keys of a given object. * @returns {Function} Returns the new pairs function. */ function createToPairs(keysFunc) { return function(object) { var tag = getTag(object); if (tag == mapTag) { return mapToArray(object); } if (tag == setTag) { return setToPairs(object); } return baseToPairs(object, keysFunc(object)); }; } /** * Creates a function that either curries or invokes `func` with optional * `this` binding and partially applied arguments. * * @private * @param {Function|string} func The function or method name to wrap. * @param {number} bitmask The bitmask flags. * 1 - `_.bind` * 2 - `_.bindKey` * 4 - `_.curry` or `_.curryRight` of a bound function * 8 - `_.curry` * 16 - `_.curryRight` * 32 - `_.partial` * 64 - `_.partialRight` * 128 - `_.rearg` * 256 - `_.ary` * 512 - `_.flip` * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to be partially applied. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; if (!isBindKey && typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } var length = partials ? partials.length : 0; if (!length) { bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); partials = holders = undefined$1; } ary = ary === undefined$1 ? ary : nativeMax(toInteger(ary), 0); arity = arity === undefined$1 ? arity : toInteger(arity); length -= holders ? holders.length : 0; if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { var partialsRight = partials, holdersRight = holders; partials = holders = undefined$1; } var data = isBindKey ? undefined$1 : getData(func); var newData = [ func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity ]; if (data) { mergeData(newData, data); } func = newData[0]; bitmask = newData[1]; thisArg = newData[2]; partials = newData[3]; holders = newData[4]; arity = newData[9] = newData[9] === undefined$1 ? (isBindKey ? 0 : func.length) : nativeMax(newData[9] - length, 0); if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); } if (!bitmask || bitmask == WRAP_BIND_FLAG) { var result = createBind(func, bitmask, thisArg); } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { result = createCurry(func, bitmask, arity); } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { result = createPartial(func, bitmask, thisArg, partials); } else { result = createHybrid.apply(undefined$1, newData); } var setter = data ? baseSetData : setData; return setWrapToString(setter(result, newData), func, bitmask); } /** * Used by `_.defaults` to customize its `_.assignIn` use to assign properties * of source objects to the destination object for all destination properties * that resolve to `undefined`. * * @private * @param {*} objValue The destination value. * @param {*} srcValue The source value. * @param {string} key The key of the property to assign. * @param {Object} object The parent object of `objValue`. * @returns {*} Returns the value to assign. */ function customDefaultsAssignIn(objValue, srcValue, key, object) { if (objValue === undefined$1 || (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) { return srcValue; } return objValue; } /** * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source * objects into destination objects that are passed thru. * * @private * @param {*} objValue The destination value. * @param {*} srcValue The source value. * @param {string} key The key of the property to merge. * @param {Object} object The parent object of `objValue`. * @param {Object} source The parent object of `srcValue`. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. * @returns {*} Returns the value to assign. */ function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { if (isObject(objValue) && isObject(srcValue)) { // Recursively merge objects and arrays (susceptible to call stack limits). stack.set(srcValue, objValue); baseMerge(objValue, srcValue, undefined$1, customDefaultsMerge, stack); stack['delete'](srcValue); } return objValue; } /** * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain * objects. * * @private * @param {*} value The value to inspect. * @param {string} key The key of the property to inspect. * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. */ function customOmitClone(value) { return isPlainObject(value) ? undefined$1 : value; } /** * A specialized version of `baseIsEqualDeep` for arrays with support for * partial deep comparisons. * * @private * @param {Array} array The array to compare. * @param {Array} other The other array to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} stack Tracks traversed `array` and `other` objects. * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. */ function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length; if (arrLength != othLength && !(isPartial && othLength > arrLength)) { return false; } // Check that cyclic values are equal. var arrStacked = stack.get(array); var othStacked = stack.get(other); if (arrStacked && othStacked) { return arrStacked == other && othStacked == array; } var index = -1, result = true, seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined$1; stack.set(array, other); stack.set(other, array); // Ignore non-index properties. while (++index < arrLength) { var arrValue = array[index], othValue = other[index]; if (customizer) { var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack); } if (compared !== undefined$1) { if (compared) { continue; } result = false; break; } // Recursively compare arrays (susceptible to call stack limits). if (seen) { if (!arraySome(other, function(othValue, othIndex) { if (!cacheHas(seen, othIndex) && (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { return seen.push(othIndex); } })) { result = false; break; } } else if (!( arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack) )) { result = false; break; } } stack['delete'](array); stack['delete'](other); return result; } /** * A specialized version of `baseIsEqualDeep` for comparing objects of * the same `toStringTag`. * * **Note:** This function only supports comparing values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {string} tag The `toStringTag` of the objects to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} stack Tracks traversed `object` and `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { switch (tag) { case dataViewTag: if ((object.byteLength != other.byteLength) || (object.byteOffset != other.byteOffset)) { return false; } object = object.buffer; other = other.buffer; case arrayBufferTag: if ((object.byteLength != other.byteLength) || !equalFunc(new Uint8Array(object), new Uint8Array(other))) { return false; } return true; case boolTag: case dateTag: case numberTag: // Coerce booleans to `1` or `0` and dates to milliseconds. // Invalid dates are coerced to `NaN`. return eq(+object, +other); case errorTag: return object.name == other.name && object.message == other.message; case regexpTag: case stringTag: // Coerce regexes to strings and treat strings, primitives and objects, // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring // for more details. return object == (other + ''); case mapTag: var convert = mapToArray; case setTag: var isPartial = bitmask & COMPARE_PARTIAL_FLAG; convert || (convert = setToArray); if (object.size != other.size && !isPartial) { return false; } // Assume cyclic values are equal. var stacked = stack.get(object); if (stacked) { return stacked == other; } bitmask |= COMPARE_UNORDERED_FLAG; // Recursively compare objects (susceptible to call stack limits). stack.set(object, other); var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); stack['delete'](object); return result; case symbolTag: if (symbolValueOf) { return symbolValueOf.call(object) == symbolValueOf.call(other); } } return false; } /** * A specialized version of `baseIsEqualDeep` for objects with support for * partial deep comparisons. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. * @param {Function} customizer The function to customize comparisons. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Object} stack Tracks traversed `object` and `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { var isPartial = bitmask & COMPARE_PARTIAL_FLAG, objProps = getAllKeys(object), objLength = objProps.length, othProps = getAllKeys(other), othLength = othProps.length; if (objLength != othLength && !isPartial) { return false; } var index = objLength; while (index--) { var key = objProps[index]; if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { return false; } } // Check that cyclic values are equal. var objStacked = stack.get(object); var othStacked = stack.get(other); if (objStacked && othStacked) { return objStacked == other && othStacked == object; } var result = true; stack.set(object, other); stack.set(other, object); var skipCtor = isPartial; while (++index < objLength) { key = objProps[index]; var objValue = object[key], othValue = other[key]; if (customizer) { var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack); } // Recursively compare objects (susceptible to call stack limits). if (!(compared === undefined$1 ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) : compared )) { result = false; break; } skipCtor || (skipCtor = key == 'constructor'); } if (result && !skipCtor) { var objCtor = object.constructor, othCtor = other.constructor; // Non `Object` object instances with different constructors are not equal. if (objCtor != othCtor && ('constructor' in object && 'constructor' in other) && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) { result = false; } } stack['delete'](object); stack['delete'](other); return result; } /** * A specialized version of `baseRest` which flattens the rest array. * * @private * @param {Function} func The function to apply a rest parameter to. * @returns {Function} Returns the new function. */ function flatRest(func) { return setToString(overRest(func, undefined$1, flatten), func + ''); } /** * Creates an array of own enumerable property names and symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names and symbols. */ function getAllKeys(object) { return baseGetAllKeys(object, keys, getSymbols); } /** * Creates an array of own and inherited enumerable property names and * symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names and symbols. */ function getAllKeysIn(object) { return baseGetAllKeys(object, keysIn, getSymbolsIn); } /** * Gets metadata for `func`. * * @private * @param {Function} func The function to query. * @returns {*} Returns the metadata for `func`. */ var getData = !metaMap ? noop : function(func) { return metaMap.get(func); }; /** * Gets the name of `func`. * * @private * @param {Function} func The function to query. * @returns {string} Returns the function name. */ function getFuncName(func) { var result = (func.name + ''), array = realNames[result], length = hasOwnProperty.call(realNames, result) ? array.length : 0; while (length--) { var data = array[length], otherFunc = data.func; if (otherFunc == null || otherFunc == func) { return data.name; } } return result; } /** * Gets the argument placeholder value for `func`. * * @private * @param {Function} func The function to inspect. * @returns {*} Returns the placeholder value. */ function getHolder(func) { var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; return object.placeholder; } /** * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, * this function returns the custom method, otherwise it returns `baseIteratee`. * If arguments are provided, the chosen function is invoked with them and * its result is returned. * * @private * @param {*} [value] The value to convert to an iteratee. * @param {number} [arity] The arity of the created iteratee. * @returns {Function} Returns the chosen function or its result. */ function getIteratee() { var result = lodash.iteratee || iteratee; result = result === iteratee ? baseIteratee : result; return arguments.length ? result(arguments[0], arguments[1]) : result; } /** * Gets the data for `map`. * * @private * @param {Object} map The map to query. * @param {string} key The reference key. * @returns {*} Returns the map data. */ function getMapData(map, key) { var data = map.__data__; return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map; } /** * Gets the property names, values, and compare flags of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the match data of `object`. */ function getMatchData(object) { var result = keys(object), length = result.length; while (length--) { var key = result[length], value = object[key]; result[length] = [key, value, isStrictComparable(value)]; } return result; } /** * Gets the native function at `key` of `object`. * * @private * @param {Object} object The object to query. * @param {string} key The key of the method to get. * @returns {*} Returns the function if it's native, else `undefined`. */ function getNative(object, key) { var value = getValue(object, key); return baseIsNative(value) ? value : undefined$1; } /** * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. * * @private * @param {*} value The value to query. * @returns {string} Returns the raw `toStringTag`. */ function getRawTag(value) { var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag]; try { value[symToStringTag] = undefined$1; var unmasked = true; } catch (e) {} var result = nativeObjectToString.call(value); if (unmasked) { if (isOwn) { value[symToStringTag] = tag; } else { delete value[symToStringTag]; } } return result; } /** * Creates an array of the own enumerable symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of symbols. */ var getSymbols = !nativeGetSymbols ? stubArray : function(object) { if (object == null) { return []; } object = Object(object); return arrayFilter(nativeGetSymbols(object), function(symbol) { return propertyIsEnumerable.call(object, symbol); }); }; /** * Creates an array of the own and inherited enumerable symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of symbols. */ var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { var result = []; while (object) { arrayPush(result, getSymbols(object)); object = getPrototype(object); } return result; }; /** * Gets the `toStringTag` of `value`. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ var getTag = baseGetTag; // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || (Map && getTag(new Map) != mapTag) || (Promise && getTag(Promise.resolve()) != promiseTag) || (Set && getTag(new Set) != setTag) || (WeakMap && getTag(new WeakMap) != weakMapTag)) { getTag = function(value) { var result = baseGetTag(value), Ctor = result == objectTag ? value.constructor : undefined$1, ctorString = Ctor ? toSource(Ctor) : ''; if (ctorString) { switch (ctorString) { case dataViewCtorString: return dataViewTag; case mapCtorString: return mapTag; case promiseCtorString: return promiseTag; case setCtorString: return setTag; case weakMapCtorString: return weakMapTag; } } return result; }; } /** * Gets the view, applying any `transforms` to the `start` and `end` positions. * * @private * @param {number} start The start of the view. * @param {number} end The end of the view. * @param {Array} transforms The transformations to apply to the view. * @returns {Object} Returns an object containing the `start` and `end` * positions of the view. */ function getView(start, end, transforms) { var index = -1, length = transforms.length; while (++index < length) { var data = transforms[index], size = data.size; switch (data.type) { case 'drop': start += size; break; case 'dropRight': end -= size; break; case 'take': end = nativeMin(end, start + size); break; case 'takeRight': start = nativeMax(start, end - size); break; } } return { 'start': start, 'end': end }; } /** * Extracts wrapper details from the `source` body comment. * * @private * @param {string} source The source to inspect. * @returns {Array} Returns the wrapper details. */ function getWrapDetails(source) { var match = source.match(reWrapDetails); return match ? match[1].split(reSplitDetails) : []; } /** * Checks if `path` exists on `object`. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @param {Function} hasFunc The function to check properties. * @returns {boolean} Returns `true` if `path` exists, else `false`. */ function hasPath(object, path, hasFunc) { path = castPath(path, object); var index = -1, length = path.length, result = false; while (++index < length) { var key = toKey(path[index]); if (!(result = object != null && hasFunc(object, key))) { break; } object = object[key]; } if (result || ++index != length) { return result; } length = object == null ? 0 : object.length; return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object)); } /** * Initializes an array clone. * * @private * @param {Array} array The array to clone. * @returns {Array} Returns the initialized clone. */ function initCloneArray(array) { var length = array.length, result = new array.constructor(length); // Add properties assigned by `RegExp#exec`. if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { result.index = array.index; result.input = array.input; } return result; } /** * Initializes an object clone. * * @private * @param {Object} object The object to clone. * @returns {Object} Returns the initialized clone. */ function initCloneObject(object) { return (typeof object.constructor == 'function' && !isPrototype(object)) ? baseCreate(getPrototype(object)) : {}; } /** * Initializes an object clone based on its `toStringTag`. * * **Note:** This function only supports cloning values with tags of * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. * * @private * @param {Object} object The object to clone. * @param {string} tag The `toStringTag` of the object to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the initialized clone. */ function initCloneByTag(object, tag, isDeep) { var Ctor = object.constructor; switch (tag) { case arrayBufferTag: return cloneArrayBuffer(object); case boolTag: case dateTag: return new Ctor(+object); case dataViewTag: return cloneDataView(object, isDeep); case float32Tag: case float64Tag: case int8Tag: case int16Tag: case int32Tag: case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: return cloneTypedArray(object, isDeep); case mapTag: return new Ctor; case numberTag: case stringTag: return new Ctor(object); case regexpTag: return cloneRegExp(object); case setTag: return new Ctor; case symbolTag: return cloneSymbol(object); } } /** * Inserts wrapper `details` in a comment at the top of the `source` body. * * @private * @param {string} source The source to modify. * @returns {Array} details The details to insert. * @returns {string} Returns the modified source. */ function insertWrapDetails(source, details) { var length = details.length; if (!length) { return source; } var lastIndex = length - 1; details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; details = details.join(length > 2 ? ', ' : ' '); return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); } /** * Checks if `value` is a flattenable `arguments` object or array. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. */ function isFlattenable(value) { return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]); } /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ function isIndex(value, length) { var type = typeof value; length = length == null ? MAX_SAFE_INTEGER : length; return !!length && (type == 'number' || (type != 'symbol' && reIsUint.test(value))) && (value > -1 && value % 1 == 0 && value < length); } /** * Checks if the given arguments are from an iteratee call. * * @private * @param {*} value The potential iteratee value argument. * @param {*} index The potential iteratee index or key argument. * @param {*} object The potential iteratee object argument. * @returns {boolean} Returns `true` if the arguments are from an iteratee call, * else `false`. */ function isIterateeCall(value, index, object) { if (!isObject(object)) { return false; } var type = typeof index; if (type == 'number' ? (isArrayLike(object) && isIndex(index, object.length)) : (type == 'string' && index in object) ) { return eq(object[index], value); } return false; } /** * Checks if `value` is a property name and not a property path. * * @private * @param {*} value The value to check. * @param {Object} [object] The object to query keys on. * @returns {boolean} Returns `true` if `value` is a property name, else `false`. */ function isKey(value, object) { if (isArray(value)) { return false; } var type = typeof value; if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) { return true; } return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || (object != null && value in Object(object)); } /** * Checks if `value` is suitable for use as unique object key. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is suitable, else `false`. */ function isKeyable(value) { var type = typeof value; return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') ? (value !== '__proto__') : (value === null); } /** * Checks if `func` has a lazy counterpart. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` has a lazy counterpart, * else `false`. */ function isLaziable(func) { var funcName = getFuncName(func), other = lodash[funcName]; if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { return false; } if (func === other) { return true; } var data = getData(other); return !!data && func === data[0]; } /** * Checks if `func` has its source masked. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` is masked, else `false`. */ function isMasked(func) { return !!maskSrcKey && (maskSrcKey in func); } /** * Checks if `func` is capable of being masked. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `func` is maskable, else `false`. */ var isMaskable = coreJsData ? isFunction : stubFalse; /** * Checks if `value` is likely a prototype object. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. */ function isPrototype(value) { var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; return value === proto; } /** * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` if suitable for strict * equality comparisons, else `false`. */ function isStrictComparable(value) { return value === value && !isObject(value); } /** * A specialized version of `matchesProperty` for source values suitable * for strict equality comparisons, i.e. `===`. * * @private * @param {string} key The key of the property to get. * @param {*} srcValue The value to match. * @returns {Function} Returns the new spec function. */ function matchesStrictComparable(key, srcValue) { return function(object) { if (object == null) { return false; } return object[key] === srcValue && (srcValue !== undefined$1 || (key in Object(object))); }; } /** * A specialized version of `_.memoize` which clears the memoized function's * cache when it exceeds `MAX_MEMOIZE_SIZE`. * * @private * @param {Function} func The function to have its output memoized. * @returns {Function} Returns the new memoized function. */ function memoizeCapped(func) { var result = memoize(func, function(key) { if (cache.size === MAX_MEMOIZE_SIZE) { cache.clear(); } return key; }); var cache = result.cache; return result; } /** * Merges the function metadata of `source` into `data`. * * Merging metadata reduces the number of wrappers used to invoke a function. * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` * may be applied regardless of execution order. Methods like `_.ary` and * `_.rearg` modify function arguments, making the order in which they are * executed important, preventing the merging of metadata. However, we make * an exception for a safe combined case where curried functions have `_.ary` * and or `_.rearg` applied. * * @private * @param {Array} data The destination metadata. * @param {Array} source The source metadata. * @returns {Array} Returns `data`. */ function mergeData(data, source) { var bitmask = data[1], srcBitmask = source[1], newBitmask = bitmask | srcBitmask, isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG); var isCombo = ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) || ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) || ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG)); // Exit early if metadata can't be merged. if (!(isCommon || isCombo)) { return data; } // Use source `thisArg` if available. if (srcBitmask & WRAP_BIND_FLAG) { data[2] = source[2]; // Set when currying a bound function. newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG; } // Compose partial arguments. var value = source[3]; if (value) { var partials = data[3]; data[3] = partials ? composeArgs(partials, value, source[4]) : value; data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4]; } // Compose partial right arguments. value = source[5]; if (value) { partials = data[5]; data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6]; } // Use source `argPos` if available. value = source[7]; if (value) { data[7] = value; } // Use source `ary` if it's smaller. if (srcBitmask & WRAP_ARY_FLAG) { data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); } // Use source `arity` if one is not provided. if (data[9] == null) { data[9] = source[9]; } // Use source `func` and merge bitmasks. data[0] = source[0]; data[1] = newBitmask; return data; } /** * This function is like * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * except that it includes inherited enumerable properties. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function nativeKeysIn(object) { var result = []; if (object != null) { for (var key in Object(object)) { result.push(key); } } return result; } /** * Converts `value` to a string using `Object.prototype.toString`. * * @private * @param {*} value The value to convert. * @returns {string} Returns the converted string. */ function objectToString(value) { return nativeObjectToString.call(value); } /** * A specialized version of `baseRest` which transforms the rest array. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @param {Function} transform The rest array transform. * @returns {Function} Returns the new function. */ function overRest(func, start, transform) { start = nativeMax(start === undefined$1 ? (func.length - 1) : start, 0); return function() { var args = arguments, index = -1, length = nativeMax(args.length - start, 0), array = Array(length); while (++index < length) { array[index] = args[start + index]; } index = -1; var otherArgs = Array(start + 1); while (++index < start) { otherArgs[index] = args[index]; } otherArgs[start] = transform(array); return apply(func, this, otherArgs); }; } /** * Gets the parent value at `path` of `object`. * * @private * @param {Object} object The object to query. * @param {Array} path The path to get the parent value of. * @returns {*} Returns the parent value. */ function parent(object, path) { return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); } /** * Reorder `array` according to the specified indexes where the element at * the first index is assigned as the first element, the element at * the second index is assigned as the second element, and so on. * * @private * @param {Array} array The array to reorder. * @param {Array} indexes The arranged array indexes. * @returns {Array} Returns `array`. */ function reorder(array, indexes) { var arrLength = array.length, length = nativeMin(indexes.length, arrLength), oldArray = copyArray(array); while (length--) { var index = indexes[length]; array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined$1; } return array; } /** * Gets the value at `key`, unless `key` is "__proto__" or "constructor". * * @private * @param {Object} object The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function safeGet(object, key) { if (key === 'constructor' && typeof object[key] === 'function') { return; } if (key == '__proto__') { return; } return object[key]; } /** * Sets metadata for `func`. * * **Note:** If this function becomes hot, i.e. is invoked a lot in a short * period of time, it will trip its breaker and transition to an identity * function to avoid garbage collection pauses in V8. See * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) * for more details. * * @private * @param {Function} func The function to associate metadata with. * @param {*} data The metadata. * @returns {Function} Returns `func`. */ var setData = shortOut(baseSetData); /** * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout). * * @private * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @returns {number|Object} Returns the timer id or timeout object. */ var setTimeout = ctxSetTimeout || function(func, wait) { return root.setTimeout(func, wait); }; /** * Sets the `toString` method of `func` to return `string`. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var setToString = shortOut(baseSetToString); /** * Sets the `toString` method of `wrapper` to mimic the source of `reference` * with wrapper details in a comment at the top of the source body. * * @private * @param {Function} wrapper The function to modify. * @param {Function} reference The reference function. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @returns {Function} Returns `wrapper`. */ function setWrapToString(wrapper, reference, bitmask) { var source = (reference + ''); return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); } /** * Creates a function that'll short out and invoke `identity` instead * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` * milliseconds. * * @private * @param {Function} func The function to restrict. * @returns {Function} Returns the new shortable function. */ function shortOut(func) { var count = 0, lastCalled = 0; return function() { var stamp = nativeNow(), remaining = HOT_SPAN - (stamp - lastCalled); lastCalled = stamp; if (remaining > 0) { if (++count >= HOT_COUNT) { return arguments[0]; } } else { count = 0; } return func.apply(undefined$1, arguments); }; } /** * A specialized version of `_.shuffle` which mutates and sets the size of `array`. * * @private * @param {Array} array The array to shuffle. * @param {number} [size=array.length] The size of `array`. * @returns {Array} Returns `array`. */ function shuffleSelf(array, size) { var index = -1, length = array.length, lastIndex = length - 1; size = size === undefined$1 ? length : size; while (++index < size) { var rand = baseRandom(index, lastIndex), value = array[rand]; array[rand] = array[index]; array[index] = value; } array.length = size; return array; } /** * Converts `string` to a property path array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the property path array. */ var stringToPath = memoizeCapped(function(string) { var result = []; if (string.charCodeAt(0) === 46 /* . */) { result.push(''); } string.replace(rePropName, function(match, number, quote, subString) { result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match)); }); return result; }); /** * Converts `value` to a string key if it's not a string or symbol. * * @private * @param {*} value The value to inspect. * @returns {string|symbol} Returns the key. */ function toKey(value) { if (typeof value == 'string' || isSymbol(value)) { return value; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } /** * Converts `func` to its source code. * * @private * @param {Function} func The function to convert. * @returns {string} Returns the source code. */ function toSource(func) { if (func != null) { try { return funcToString.call(func); } catch (e) {} try { return (func + ''); } catch (e) {} } return ''; } /** * Updates wrapper `details` based on `bitmask` flags. * * @private * @returns {Array} details The details to modify. * @param {number} bitmask The bitmask flags. See `createWrap` for more details. * @returns {Array} Returns `details`. */ function updateWrapDetails(details, bitmask) { arrayEach(wrapFlags, function(pair) { var value = '_.' + pair[0]; if ((bitmask & pair[1]) && !arrayIncludes(details, value)) { details.push(value); } }); return details.sort(); } /** * Creates a clone of `wrapper`. * * @private * @param {Object} wrapper The wrapper to clone. * @returns {Object} Returns the cloned wrapper. */ function wrapperClone(wrapper) { if (wrapper instanceof LazyWrapper) { return wrapper.clone(); } var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); result.__actions__ = copyArray(wrapper.__actions__); result.__index__ = wrapper.__index__; result.__values__ = wrapper.__values__; return result; } /*------------------------------------------------------------------------*/ /** * Creates an array of elements split into groups the length of `size`. * If `array` can't be split evenly, the final chunk will be the remaining * elements. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to process. * @param {number} [size=1] The length of each chunk * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the new array of chunks. * @example * * _.chunk(['a', 'b', 'c', 'd'], 2); * // => [['a', 'b'], ['c', 'd']] * * _.chunk(['a', 'b', 'c', 'd'], 3); * // => [['a', 'b', 'c'], ['d']] */ function chunk(array, size, guard) { if ((guard ? isIterateeCall(array, size, guard) : size === undefined$1)) { size = 1; } else { size = nativeMax(toInteger(size), 0); } var length = array == null ? 0 : array.length; if (!length || size < 1) { return []; } var index = 0, resIndex = 0, result = Array(nativeCeil(length / size)); while (index < length) { result[resIndex++] = baseSlice(array, index, (index += size)); } return result; } /** * Creates an array with all falsey values removed. The values `false`, `null`, * `0`, `""`, `undefined`, and `NaN` are falsey. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to compact. * @returns {Array} Returns the new array of filtered values. * @example * * _.compact([0, 1, false, 2, '', 3]); * // => [1, 2, 3] */ function compact(array) { var index = -1, length = array == null ? 0 : array.length, resIndex = 0, result = []; while (++index < length) { var value = array[index]; if (value) { result[resIndex++] = value; } } return result; } /** * Creates a new array concatenating `array` with any additional arrays * and/or values. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to concatenate. * @param {...*} [values] The values to concatenate. * @returns {Array} Returns the new concatenated array. * @example * * var array = [1]; * var other = _.concat(array, 2, [3], [[4]]); * * console.log(other); * // => [1, 2, 3, [4]] * * console.log(array); * // => [1] */ function concat() { var length = arguments.length; if (!length) { return []; } var args = Array(length - 1), array = arguments[0], index = length; while (index--) { args[index - 1] = arguments[index]; } return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); } /** * Creates an array of `array` values not included in the other given arrays * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. The order and references of result values are * determined by the first array. * * **Note:** Unlike `_.pullAll`, this method returns a new array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @returns {Array} Returns the new array of filtered values. * @see _.without, _.xor * @example * * _.difference([2, 1], [2, 3]); * // => [1] */ var difference = baseRest(function(array, values) { return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : []; }); /** * This method is like `_.difference` except that it accepts `iteratee` which * is invoked for each element of `array` and `values` to generate the criterion * by which they're compared. The order and references of result values are * determined by the first array. The iteratee is invoked with one argument: * (value). * * **Note:** Unlike `_.pullAllBy`, this method returns a new array. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); * // => [1.2] * * // The `_.property` iteratee shorthand. * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); * // => [{ 'x': 2 }] */ var differenceBy = baseRest(function(array, values) { var iteratee = last(values); if (isArrayLikeObject(iteratee)) { iteratee = undefined$1; } return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) : []; }); /** * This method is like `_.difference` except that it accepts `comparator` * which is invoked to compare elements of `array` to `values`. The order and * references of result values are determined by the first array. The comparator * is invoked with two arguments: (arrVal, othVal). * * **Note:** Unlike `_.pullAllWith`, this method returns a new array. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); * // => [{ 'x': 2, 'y': 1 }] */ var differenceWith = baseRest(function(array, values) { var comparator = last(values); if (isArrayLikeObject(comparator)) { comparator = undefined$1; } return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined$1, comparator) : []; }); /** * Creates a slice of `array` with `n` elements dropped from the beginning. * * @static * @memberOf _ * @since 0.5.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to drop. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.drop([1, 2, 3]); * // => [2, 3] * * _.drop([1, 2, 3], 2); * // => [3] * * _.drop([1, 2, 3], 5); * // => [] * * _.drop([1, 2, 3], 0); * // => [1, 2, 3] */ function drop(array, n, guard) { var length = array == null ? 0 : array.length; if (!length) { return []; } n = (guard || n === undefined$1) ? 1 : toInteger(n); return baseSlice(array, n < 0 ? 0 : n, length); } /** * Creates a slice of `array` with `n` elements dropped from the end. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to drop. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.dropRight([1, 2, 3]); * // => [1, 2] * * _.dropRight([1, 2, 3], 2); * // => [1] * * _.dropRight([1, 2, 3], 5); * // => [] * * _.dropRight([1, 2, 3], 0); * // => [1, 2, 3] */ function dropRight(array, n, guard) { var length = array == null ? 0 : array.length; if (!length) { return []; } n = (guard || n === undefined$1) ? 1 : toInteger(n); n = length - n; return baseSlice(array, 0, n < 0 ? 0 : n); } /** * Creates a slice of `array` excluding elements dropped from the end. * Elements are dropped until `predicate` returns falsey. The predicate is * invoked with three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': false } * ]; * * _.dropRightWhile(users, function(o) { return !o.active; }); * // => objects for ['barney'] * * // The `_.matches` iteratee shorthand. * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); * // => objects for ['barney', 'fred'] * * // The `_.matchesProperty` iteratee shorthand. * _.dropRightWhile(users, ['active', false]); * // => objects for ['barney'] * * // The `_.property` iteratee shorthand. * _.dropRightWhile(users, 'active'); * // => objects for ['barney', 'fred', 'pebbles'] */ function dropRightWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3), true, true) : []; } /** * Creates a slice of `array` excluding elements dropped from the beginning. * Elements are dropped until `predicate` returns falsey. The predicate is * invoked with three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': false }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': true } * ]; * * _.dropWhile(users, function(o) { return !o.active; }); * // => objects for ['pebbles'] * * // The `_.matches` iteratee shorthand. * _.dropWhile(users, { 'user': 'barney', 'active': false }); * // => objects for ['fred', 'pebbles'] * * // The `_.matchesProperty` iteratee shorthand. * _.dropWhile(users, ['active', false]); * // => objects for ['pebbles'] * * // The `_.property` iteratee shorthand. * _.dropWhile(users, 'active'); * // => objects for ['barney', 'fred', 'pebbles'] */ function dropWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3), true) : []; } /** * Fills elements of `array` with `value` from `start` up to, but not * including, `end`. * * **Note:** This method mutates `array`. * * @static * @memberOf _ * @since 3.2.0 * @category Array * @param {Array} array The array to fill. * @param {*} value The value to fill `array` with. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns `array`. * @example * * var array = [1, 2, 3]; * * _.fill(array, 'a'); * console.log(array); * // => ['a', 'a', 'a'] * * _.fill(Array(3), 2); * // => [2, 2, 2] * * _.fill([4, 6, 8, 10], '*', 1, 3); * // => [4, '*', '*', 10] */ function fill(array, value, start, end) { var length = array == null ? 0 : array.length; if (!length) { return []; } if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { start = 0; end = length; } return baseFill(array, value, start, end); } /** * This method is like `_.find` except that it returns the index of the first * element `predicate` returns truthy for instead of the element itself. * * @static * @memberOf _ * @since 1.1.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=0] The index to search from. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var users = [ * { 'user': 'barney', 'active': false }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': true } * ]; * * _.findIndex(users, function(o) { return o.user == 'barney'; }); * // => 0 * * // The `_.matches` iteratee shorthand. * _.findIndex(users, { 'user': 'fred', 'active': false }); * // => 1 * * // The `_.matchesProperty` iteratee shorthand. * _.findIndex(users, ['active', false]); * // => 0 * * // The `_.property` iteratee shorthand. * _.findIndex(users, 'active'); * // => 2 */ function findIndex(array, predicate, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = fromIndex == null ? 0 : toInteger(fromIndex); if (index < 0) { index = nativeMax(length + index, 0); } return baseFindIndex(array, getIteratee(predicate, 3), index); } /** * This method is like `_.findIndex` except that it iterates over elements * of `collection` from right to left. * * @static * @memberOf _ * @since 2.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=array.length-1] The index to search from. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': false } * ]; * * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); * // => 2 * * // The `_.matches` iteratee shorthand. * _.findLastIndex(users, { 'user': 'barney', 'active': true }); * // => 0 * * // The `_.matchesProperty` iteratee shorthand. * _.findLastIndex(users, ['active', false]); * // => 2 * * // The `_.property` iteratee shorthand. * _.findLastIndex(users, 'active'); * // => 0 */ function findLastIndex(array, predicate, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = length - 1; if (fromIndex !== undefined$1) { index = toInteger(fromIndex); index = fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); } return baseFindIndex(array, getIteratee(predicate, 3), index, true); } /** * Flattens `array` a single level deep. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to flatten. * @returns {Array} Returns the new flattened array. * @example * * _.flatten([1, [2, [3, [4]], 5]]); * // => [1, 2, [3, [4]], 5] */ function flatten(array) { var length = array == null ? 0 : array.length; return length ? baseFlatten(array, 1) : []; } /** * Recursively flattens `array`. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to flatten. * @returns {Array} Returns the new flattened array. * @example * * _.flattenDeep([1, [2, [3, [4]], 5]]); * // => [1, 2, 3, 4, 5] */ function flattenDeep(array) { var length = array == null ? 0 : array.length; return length ? baseFlatten(array, INFINITY) : []; } /** * Recursively flatten `array` up to `depth` times. * * @static * @memberOf _ * @since 4.4.0 * @category Array * @param {Array} array The array to flatten. * @param {number} [depth=1] The maximum recursion depth. * @returns {Array} Returns the new flattened array. * @example * * var array = [1, [2, [3, [4]], 5]]; * * _.flattenDepth(array, 1); * // => [1, 2, [3, [4]], 5] * * _.flattenDepth(array, 2); * // => [1, 2, 3, [4], 5] */ function flattenDepth(array, depth) { var length = array == null ? 0 : array.length; if (!length) { return []; } depth = depth === undefined$1 ? 1 : toInteger(depth); return baseFlatten(array, depth); } /** * The inverse of `_.toPairs`; this method returns an object composed * from key-value `pairs`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} pairs The key-value pairs. * @returns {Object} Returns the new object. * @example * * _.fromPairs([['a', 1], ['b', 2]]); * // => { 'a': 1, 'b': 2 } */ function fromPairs(pairs) { var index = -1, length = pairs == null ? 0 : pairs.length, result = {}; while (++index < length) { var pair = pairs[index]; result[pair[0]] = pair[1]; } return result; } /** * Gets the first element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @alias first * @category Array * @param {Array} array The array to query. * @returns {*} Returns the first element of `array`. * @example * * _.head([1, 2, 3]); * // => 1 * * _.head([]); * // => undefined */ function head(array) { return (array && array.length) ? array[0] : undefined$1; } /** * Gets the index at which the first occurrence of `value` is found in `array` * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. If `fromIndex` is negative, it's used as the * offset from the end of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} [fromIndex=0] The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.indexOf([1, 2, 1, 2], 2); * // => 1 * * // Search from the `fromIndex`. * _.indexOf([1, 2, 1, 2], 2, 2); * // => 3 */ function indexOf(array, value, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = fromIndex == null ? 0 : toInteger(fromIndex); if (index < 0) { index = nativeMax(length + index, 0); } return baseIndexOf(array, value, index); } /** * Gets all but the last element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @returns {Array} Returns the slice of `array`. * @example * * _.initial([1, 2, 3]); * // => [1, 2] */ function initial(array) { var length = array == null ? 0 : array.length; return length ? baseSlice(array, 0, -1) : []; } /** * Creates an array of unique values that are included in all given arrays * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. The order and references of result values are * determined by the first array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of intersecting values. * @example * * _.intersection([2, 1], [2, 3]); * // => [2] */ var intersection = baseRest(function(arrays) { var mapped = arrayMap(arrays, castArrayLikeObject); return (mapped.length && mapped[0] === arrays[0]) ? baseIntersection(mapped) : []; }); /** * This method is like `_.intersection` except that it accepts `iteratee` * which is invoked for each element of each `arrays` to generate the criterion * by which they're compared. The order and references of result values are * determined by the first array. The iteratee is invoked with one argument: * (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of intersecting values. * @example * * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); * // => [2.1] * * // The `_.property` iteratee shorthand. * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }] */ var intersectionBy = baseRest(function(arrays) { var iteratee = last(arrays), mapped = arrayMap(arrays, castArrayLikeObject); if (iteratee === last(mapped)) { iteratee = undefined$1; } else { mapped.pop(); } return (mapped.length && mapped[0] === arrays[0]) ? baseIntersection(mapped, getIteratee(iteratee, 2)) : []; }); /** * This method is like `_.intersection` except that it accepts `comparator` * which is invoked to compare elements of `arrays`. The order and references * of result values are determined by the first array. The comparator is * invoked with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of intersecting values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.intersectionWith(objects, others, _.isEqual); * // => [{ 'x': 1, 'y': 2 }] */ var intersectionWith = baseRest(function(arrays) { var comparator = last(arrays), mapped = arrayMap(arrays, castArrayLikeObject); comparator = typeof comparator == 'function' ? comparator : undefined$1; if (comparator) { mapped.pop(); } return (mapped.length && mapped[0] === arrays[0]) ? baseIntersection(mapped, undefined$1, comparator) : []; }); /** * Converts all elements in `array` into a string separated by `separator`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to convert. * @param {string} [separator=','] The element separator. * @returns {string} Returns the joined string. * @example * * _.join(['a', 'b', 'c'], '~'); * // => 'a~b~c' */ function join(array, separator) { return array == null ? '' : nativeJoin.call(array, separator); } /** * Gets the last element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @returns {*} Returns the last element of `array`. * @example * * _.last([1, 2, 3]); * // => 3 */ function last(array) { var length = array == null ? 0 : array.length; return length ? array[length - 1] : undefined$1; } /** * This method is like `_.indexOf` except that it iterates over elements of * `array` from right to left. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} [fromIndex=array.length-1] The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.lastIndexOf([1, 2, 1, 2], 2); * // => 3 * * // Search from the `fromIndex`. * _.lastIndexOf([1, 2, 1, 2], 2, 2); * // => 1 */ function lastIndexOf(array, value, fromIndex) { var length = array == null ? 0 : array.length; if (!length) { return -1; } var index = length; if (fromIndex !== undefined$1) { index = toInteger(fromIndex); index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); } return value === value ? strictLastIndexOf(array, value, index) : baseFindIndex(array, baseIsNaN, index, true); } /** * Gets the element at index `n` of `array`. If `n` is negative, the nth * element from the end is returned. * * @static * @memberOf _ * @since 4.11.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=0] The index of the element to return. * @returns {*} Returns the nth element of `array`. * @example * * var array = ['a', 'b', 'c', 'd']; * * _.nth(array, 1); * // => 'b' * * _.nth(array, -2); * // => 'c'; */ function nth(array, n) { return (array && array.length) ? baseNth(array, toInteger(n)) : undefined$1; } /** * Removes all given values from `array` using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` * to remove elements from an array by predicate. * * @static * @memberOf _ * @since 2.0.0 * @category Array * @param {Array} array The array to modify. * @param {...*} [values] The values to remove. * @returns {Array} Returns `array`. * @example * * var array = ['a', 'b', 'c', 'a', 'b', 'c']; * * _.pull(array, 'a', 'c'); * console.log(array); * // => ['b', 'b'] */ var pull = baseRest(pullAll); /** * This method is like `_.pull` except that it accepts an array of values to remove. * * **Note:** Unlike `_.difference`, this method mutates `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @returns {Array} Returns `array`. * @example * * var array = ['a', 'b', 'c', 'a', 'b', 'c']; * * _.pullAll(array, ['a', 'c']); * console.log(array); * // => ['b', 'b'] */ function pullAll(array, values) { return (array && array.length && values && values.length) ? basePullAll(array, values) : array; } /** * This method is like `_.pullAll` except that it accepts `iteratee` which is * invoked for each element of `array` and `values` to generate the criterion * by which they're compared. The iteratee is invoked with one argument: (value). * * **Note:** Unlike `_.differenceBy`, this method mutates `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns `array`. * @example * * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; * * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); * console.log(array); * // => [{ 'x': 2 }] */ function pullAllBy(array, values, iteratee) { return (array && array.length && values && values.length) ? basePullAll(array, values, getIteratee(iteratee, 2)) : array; } /** * This method is like `_.pullAll` except that it accepts `comparator` which * is invoked to compare elements of `array` to `values`. The comparator is * invoked with two arguments: (arrVal, othVal). * * **Note:** Unlike `_.differenceWith`, this method mutates `array`. * * @static * @memberOf _ * @since 4.6.0 * @category Array * @param {Array} array The array to modify. * @param {Array} values The values to remove. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns `array`. * @example * * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; * * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); * console.log(array); * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] */ function pullAllWith(array, values, comparator) { return (array && array.length && values && values.length) ? basePullAll(array, values, undefined$1, comparator) : array; } /** * Removes elements from `array` corresponding to `indexes` and returns an * array of removed elements. * * **Note:** Unlike `_.at`, this method mutates `array`. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to modify. * @param {...(number|number[])} [indexes] The indexes of elements to remove. * @returns {Array} Returns the new array of removed elements. * @example * * var array = ['a', 'b', 'c', 'd']; * var pulled = _.pullAt(array, [1, 3]); * * console.log(array); * // => ['a', 'c'] * * console.log(pulled); * // => ['b', 'd'] */ var pullAt = flatRest(function(array, indexes) { var length = array == null ? 0 : array.length, result = baseAt(array, indexes); basePullAt(array, arrayMap(indexes, function(index) { return isIndex(index, length) ? +index : index; }).sort(compareAscending)); return result; }); /** * Removes all elements from `array` that `predicate` returns truthy for * and returns an array of the removed elements. The predicate is invoked * with three arguments: (value, index, array). * * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` * to pull elements from an array by value. * * @static * @memberOf _ * @since 2.0.0 * @category Array * @param {Array} array The array to modify. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the new array of removed elements. * @example * * var array = [1, 2, 3, 4]; * var evens = _.remove(array, function(n) { * return n % 2 == 0; * }); * * console.log(array); * // => [1, 3] * * console.log(evens); * // => [2, 4] */ function remove(array, predicate) { var result = []; if (!(array && array.length)) { return result; } var index = -1, indexes = [], length = array.length; predicate = getIteratee(predicate, 3); while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result.push(value); indexes.push(index); } } basePullAt(array, indexes); return result; } /** * Reverses `array` so that the first element becomes the last, the second * element becomes the second to last, and so on. * * **Note:** This method mutates `array` and is based on * [`Array#reverse`](https://mdn.io/Array/reverse). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to modify. * @returns {Array} Returns `array`. * @example * * var array = [1, 2, 3]; * * _.reverse(array); * // => [3, 2, 1] * * console.log(array); * // => [3, 2, 1] */ function reverse(array) { return array == null ? array : nativeReverse.call(array); } /** * Creates a slice of `array` from `start` up to, but not including, `end`. * * **Note:** This method is used instead of * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are * returned. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function slice(array, start, end) { var length = array == null ? 0 : array.length; if (!length) { return []; } if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { start = 0; end = length; } else { start = start == null ? 0 : toInteger(start); end = end === undefined$1 ? length : toInteger(end); } return baseSlice(array, start, end); } /** * Uses a binary search to determine the lowest index at which `value` * should be inserted into `array` in order to maintain its sort order. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedIndex([30, 50], 40); * // => 1 */ function sortedIndex(array, value) { return baseSortedIndex(array, value); } /** * This method is like `_.sortedIndex` except that it accepts `iteratee` * which is invoked for `value` and each element of `array` to compute their * sort ranking. The iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * var objects = [{ 'x': 4 }, { 'x': 5 }]; * * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); * // => 0 * * // The `_.property` iteratee shorthand. * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); * // => 0 */ function sortedIndexBy(array, value, iteratee) { return baseSortedIndexBy(array, value, getIteratee(iteratee, 2)); } /** * This method is like `_.indexOf` except that it performs a binary * search on a sorted `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.sortedIndexOf([4, 5, 5, 5, 6], 5); * // => 1 */ function sortedIndexOf(array, value) { var length = array == null ? 0 : array.length; if (length) { var index = baseSortedIndex(array, value); if (index < length && eq(array[index], value)) { return index; } } return -1; } /** * This method is like `_.sortedIndex` except that it returns the highest * index at which `value` should be inserted into `array` in order to * maintain its sort order. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedLastIndex([4, 5, 5, 5, 6], 5); * // => 4 */ function sortedLastIndex(array, value) { return baseSortedIndex(array, value, true); } /** * This method is like `_.sortedLastIndex` except that it accepts `iteratee` * which is invoked for `value` and each element of `array` to compute their * sort ranking. The iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * var objects = [{ 'x': 4 }, { 'x': 5 }]; * * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); * // => 1 * * // The `_.property` iteratee shorthand. * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); * // => 1 */ function sortedLastIndexBy(array, value, iteratee) { return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true); } /** * This method is like `_.lastIndexOf` except that it performs a binary * search on a sorted `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); * // => 3 */ function sortedLastIndexOf(array, value) { var length = array == null ? 0 : array.length; if (length) { var index = baseSortedIndex(array, value, true) - 1; if (eq(array[index], value)) { return index; } } return -1; } /** * This method is like `_.uniq` except that it's designed and optimized * for sorted arrays. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @returns {Array} Returns the new duplicate free array. * @example * * _.sortedUniq([1, 1, 2]); * // => [1, 2] */ function sortedUniq(array) { return (array && array.length) ? baseSortedUniq(array) : []; } /** * This method is like `_.uniqBy` except that it's designed and optimized * for sorted arrays. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [iteratee] The iteratee invoked per element. * @returns {Array} Returns the new duplicate free array. * @example * * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); * // => [1.1, 2.3] */ function sortedUniqBy(array, iteratee) { return (array && array.length) ? baseSortedUniq(array, getIteratee(iteratee, 2)) : []; } /** * Gets all but the first element of `array`. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to query. * @returns {Array} Returns the slice of `array`. * @example * * _.tail([1, 2, 3]); * // => [2, 3] */ function tail(array) { var length = array == null ? 0 : array.length; return length ? baseSlice(array, 1, length) : []; } /** * Creates a slice of `array` with `n` elements taken from the beginning. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to take. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.take([1, 2, 3]); * // => [1] * * _.take([1, 2, 3], 2); * // => [1, 2] * * _.take([1, 2, 3], 5); * // => [1, 2, 3] * * _.take([1, 2, 3], 0); * // => [] */ function take(array, n, guard) { if (!(array && array.length)) { return []; } n = (guard || n === undefined$1) ? 1 : toInteger(n); return baseSlice(array, 0, n < 0 ? 0 : n); } /** * Creates a slice of `array` with `n` elements taken from the end. * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to take. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.takeRight([1, 2, 3]); * // => [3] * * _.takeRight([1, 2, 3], 2); * // => [2, 3] * * _.takeRight([1, 2, 3], 5); * // => [1, 2, 3] * * _.takeRight([1, 2, 3], 0); * // => [] */ function takeRight(array, n, guard) { var length = array == null ? 0 : array.length; if (!length) { return []; } n = (guard || n === undefined$1) ? 1 : toInteger(n); n = length - n; return baseSlice(array, n < 0 ? 0 : n, length); } /** * Creates a slice of `array` with elements taken from the end. Elements are * taken until `predicate` returns falsey. The predicate is invoked with * three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': false } * ]; * * _.takeRightWhile(users, function(o) { return !o.active; }); * // => objects for ['fred', 'pebbles'] * * // The `_.matches` iteratee shorthand. * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); * // => objects for ['pebbles'] * * // The `_.matchesProperty` iteratee shorthand. * _.takeRightWhile(users, ['active', false]); * // => objects for ['fred', 'pebbles'] * * // The `_.property` iteratee shorthand. * _.takeRightWhile(users, 'active'); * // => [] */ function takeRightWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3), false, true) : []; } /** * Creates a slice of `array` with elements taken from the beginning. Elements * are taken until `predicate` returns falsey. The predicate is invoked with * three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': false }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': true } * ]; * * _.takeWhile(users, function(o) { return !o.active; }); * // => objects for ['barney', 'fred'] * * // The `_.matches` iteratee shorthand. * _.takeWhile(users, { 'user': 'barney', 'active': false }); * // => objects for ['barney'] * * // The `_.matchesProperty` iteratee shorthand. * _.takeWhile(users, ['active', false]); * // => objects for ['barney', 'fred'] * * // The `_.property` iteratee shorthand. * _.takeWhile(users, 'active'); * // => [] */ function takeWhile(array, predicate) { return (array && array.length) ? baseWhile(array, getIteratee(predicate, 3)) : []; } /** * Creates an array of unique values, in order, from all given arrays using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of combined values. * @example * * _.union([2], [1, 2]); * // => [2, 1] */ var union = baseRest(function(arrays) { return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); }); /** * This method is like `_.union` except that it accepts `iteratee` which is * invoked for each element of each `arrays` to generate the criterion by * which uniqueness is computed. Result values are chosen from the first * array in which the value occurs. The iteratee is invoked with one argument: * (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of combined values. * @example * * _.unionBy([2.1], [1.2, 2.3], Math.floor); * // => [2.1, 1.2] * * // The `_.property` iteratee shorthand. * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }, { 'x': 2 }] */ var unionBy = baseRest(function(arrays) { var iteratee = last(arrays); if (isArrayLikeObject(iteratee)) { iteratee = undefined$1; } return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)); }); /** * This method is like `_.union` except that it accepts `comparator` which * is invoked to compare elements of `arrays`. Result values are chosen from * the first array in which the value occurs. The comparator is invoked * with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of combined values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.unionWith(objects, others, _.isEqual); * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] */ var unionWith = baseRest(function(arrays) { var comparator = last(arrays); comparator = typeof comparator == 'function' ? comparator : undefined$1; return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined$1, comparator); }); /** * Creates a duplicate-free version of an array, using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons, in which only the first occurrence of each element * is kept. The order of result values is determined by the order they occur * in the array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @returns {Array} Returns the new duplicate free array. * @example * * _.uniq([2, 1, 2]); * // => [2, 1] */ function uniq(array) { return (array && array.length) ? baseUniq(array) : []; } /** * This method is like `_.uniq` except that it accepts `iteratee` which is * invoked for each element in `array` to generate the criterion by which * uniqueness is computed. The order of result values is determined by the * order they occur in the array. The iteratee is invoked with one argument: * (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new duplicate free array. * @example * * _.uniqBy([2.1, 1.2, 2.3], Math.floor); * // => [2.1, 1.2] * * // The `_.property` iteratee shorthand. * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }, { 'x': 2 }] */ function uniqBy(array, iteratee) { return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : []; } /** * This method is like `_.uniq` except that it accepts `comparator` which * is invoked to compare elements of `array`. The order of result values is * determined by the order they occur in the array.The comparator is invoked * with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new duplicate free array. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.uniqWith(objects, _.isEqual); * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] */ function uniqWith(array, comparator) { comparator = typeof comparator == 'function' ? comparator : undefined$1; return (array && array.length) ? baseUniq(array, undefined$1, comparator) : []; } /** * This method is like `_.zip` except that it accepts an array of grouped * elements and creates an array regrouping the elements to their pre-zip * configuration. * * @static * @memberOf _ * @since 1.2.0 * @category Array * @param {Array} array The array of grouped elements to process. * @returns {Array} Returns the new array of regrouped elements. * @example * * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]); * // => [['a', 1, true], ['b', 2, false]] * * _.unzip(zipped); * // => [['a', 'b'], [1, 2], [true, false]] */ function unzip(array) { if (!(array && array.length)) { return []; } var length = 0; array = arrayFilter(array, function(group) { if (isArrayLikeObject(group)) { length = nativeMax(group.length, length); return true; } }); return baseTimes(length, function(index) { return arrayMap(array, baseProperty(index)); }); } /** * This method is like `_.unzip` except that it accepts `iteratee` to specify * how regrouped values should be combined. The iteratee is invoked with the * elements of each group: (...group). * * @static * @memberOf _ * @since 3.8.0 * @category Array * @param {Array} array The array of grouped elements to process. * @param {Function} [iteratee=_.identity] The function to combine * regrouped values. * @returns {Array} Returns the new array of regrouped elements. * @example * * var zipped = _.zip([1, 2], [10, 20], [100, 200]); * // => [[1, 10, 100], [2, 20, 200]] * * _.unzipWith(zipped, _.add); * // => [3, 30, 300] */ function unzipWith(array, iteratee) { if (!(array && array.length)) { return []; } var result = unzip(array); if (iteratee == null) { return result; } return arrayMap(result, function(group) { return apply(iteratee, undefined$1, group); }); } /** * Creates an array excluding all given values using * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * **Note:** Unlike `_.pull`, this method returns a new array. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {...*} [values] The values to exclude. * @returns {Array} Returns the new array of filtered values. * @see _.difference, _.xor * @example * * _.without([2, 1, 2, 3], 1, 2); * // => [3] */ var without = baseRest(function(array, values) { return isArrayLikeObject(array) ? baseDifference(array, values) : []; }); /** * Creates an array of unique values that is the * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) * of the given arrays. The order of result values is determined by the order * they occur in the arrays. * * @static * @memberOf _ * @since 2.4.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of filtered values. * @see _.difference, _.without * @example * * _.xor([2, 1], [2, 3]); * // => [1, 3] */ var xor = baseRest(function(arrays) { return baseXor(arrayFilter(arrays, isArrayLikeObject)); }); /** * This method is like `_.xor` except that it accepts `iteratee` which is * invoked for each element of each `arrays` to generate the criterion by * which by which they're compared. The order of result values is determined * by the order they occur in the arrays. The iteratee is invoked with one * argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); * // => [1.2, 3.4] * * // The `_.property` iteratee shorthand. * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 2 }] */ var xorBy = baseRest(function(arrays) { var iteratee = last(arrays); if (isArrayLikeObject(iteratee)) { iteratee = undefined$1; } return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2)); }); /** * This method is like `_.xor` except that it accepts `comparator` which is * invoked to compare elements of `arrays`. The order of result values is * determined by the order they occur in the arrays. The comparator is invoked * with two arguments: (arrVal, othVal). * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {...Array} [arrays] The arrays to inspect. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; * * _.xorWith(objects, others, _.isEqual); * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] */ var xorWith = baseRest(function(arrays) { var comparator = last(arrays); comparator = typeof comparator == 'function' ? comparator : undefined$1; return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined$1, comparator); }); /** * Creates an array of grouped elements, the first of which contains the * first elements of the given arrays, the second of which contains the * second elements of the given arrays, and so on. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {...Array} [arrays] The arrays to process. * @returns {Array} Returns the new array of grouped elements. * @example * * _.zip(['a', 'b'], [1, 2], [true, false]); * // => [['a', 1, true], ['b', 2, false]] */ var zip = baseRest(unzip); /** * This method is like `_.fromPairs` except that it accepts two arrays, * one of property identifiers and one of corresponding values. * * @static * @memberOf _ * @since 0.4.0 * @category Array * @param {Array} [props=[]] The property identifiers. * @param {Array} [values=[]] The property values. * @returns {Object} Returns the new object. * @example * * _.zipObject(['a', 'b'], [1, 2]); * // => { 'a': 1, 'b': 2 } */ function zipObject(props, values) { return baseZipObject(props || [], values || [], assignValue); } /** * This method is like `_.zipObject` except that it supports property paths. * * @static * @memberOf _ * @since 4.1.0 * @category Array * @param {Array} [props=[]] The property identifiers. * @param {Array} [values=[]] The property values. * @returns {Object} Returns the new object. * @example * * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]); * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } */ function zipObjectDeep(props, values) { return baseZipObject(props || [], values || [], baseSet); } /** * This method is like `_.zip` except that it accepts `iteratee` to specify * how grouped values should be combined. The iteratee is invoked with the * elements of each group: (...group). * * @static * @memberOf _ * @since 3.8.0 * @category Array * @param {...Array} [arrays] The arrays to process. * @param {Function} [iteratee=_.identity] The function to combine * grouped values. * @returns {Array} Returns the new array of grouped elements. * @example * * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) { * return a + b + c; * }); * // => [111, 222] */ var zipWith = baseRest(function(arrays) { var length = arrays.length, iteratee = length > 1 ? arrays[length - 1] : undefined$1; iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined$1; return unzipWith(arrays, iteratee); }); /*------------------------------------------------------------------------*/ /** * Creates a `lodash` wrapper instance that wraps `value` with explicit method * chain sequences enabled. The result of such sequences must be unwrapped * with `_#value`. * * @static * @memberOf _ * @since 1.3.0 * @category Seq * @param {*} value The value to wrap. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 }, * { 'user': 'pebbles', 'age': 1 } * ]; * * var youngest = _ * .chain(users) * .sortBy('age') * .map(function(o) { * return o.user + ' is ' + o.age; * }) * .head() * .value(); * // => 'pebbles is 1' */ function chain(value) { var result = lodash(value); result.__chain__ = true; return result; } /** * This method invokes `interceptor` and returns `value`. The interceptor * is invoked with one argument; (value). The purpose of this method is to * "tap into" a method chain sequence in order to modify intermediate results. * * @static * @memberOf _ * @since 0.1.0 * @category Seq * @param {*} value The value to provide to `interceptor`. * @param {Function} interceptor The function to invoke. * @returns {*} Returns `value`. * @example * * _([1, 2, 3]) * .tap(function(array) { * // Mutate input array. * array.pop(); * }) * .reverse() * .value(); * // => [2, 1] */ function tap(value, interceptor) { interceptor(value); return value; } /** * This method is like `_.tap` except that it returns the result of `interceptor`. * The purpose of this method is to "pass thru" values replacing intermediate * results in a method chain sequence. * * @static * @memberOf _ * @since 3.0.0 * @category Seq * @param {*} value The value to provide to `interceptor`. * @param {Function} interceptor The function to invoke. * @returns {*} Returns the result of `interceptor`. * @example * * _(' abc ') * .chain() * .trim() * .thru(function(value) { * return [value]; * }) * .value(); * // => ['abc'] */ function thru(value, interceptor) { return interceptor(value); } /** * This method is the wrapper version of `_.at`. * * @name at * @memberOf _ * @since 1.0.0 * @category Seq * @param {...(string|string[])} [paths] The property paths to pick. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; * * _(object).at(['a[0].b.c', 'a[1]']).value(); * // => [3, 4] */ var wrapperAt = flatRest(function(paths) { var length = paths.length, start = length ? paths[0] : 0, value = this.__wrapped__, interceptor = function(object) { return baseAt(object, paths); }; if (length > 1 || this.__actions__.length || !(value instanceof LazyWrapper) || !isIndex(start)) { return this.thru(interceptor); } value = value.slice(start, +start + (length ? 1 : 0)); value.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined$1 }); return new LodashWrapper(value, this.__chain__).thru(function(array) { if (length && !array.length) { array.push(undefined$1); } return array; }); }); /** * Creates a `lodash` wrapper instance with explicit method chain sequences enabled. * * @name chain * @memberOf _ * @since 0.1.0 * @category Seq * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * // A sequence without explicit chaining. * _(users).head(); * // => { 'user': 'barney', 'age': 36 } * * // A sequence with explicit chaining. * _(users) * .chain() * .head() * .pick('user') * .value(); * // => { 'user': 'barney' } */ function wrapperChain() { return chain(this); } /** * Executes the chain sequence and returns the wrapped result. * * @name commit * @memberOf _ * @since 3.2.0 * @category Seq * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var array = [1, 2]; * var wrapped = _(array).push(3); * * console.log(array); * // => [1, 2] * * wrapped = wrapped.commit(); * console.log(array); * // => [1, 2, 3] * * wrapped.last(); * // => 3 * * console.log(array); * // => [1, 2, 3] */ function wrapperCommit() { return new LodashWrapper(this.value(), this.__chain__); } /** * Gets the next value on a wrapped object following the * [iterator protocol](https://mdn.io/iteration_protocols#iterator). * * @name next * @memberOf _ * @since 4.0.0 * @category Seq * @returns {Object} Returns the next iterator value. * @example * * var wrapped = _([1, 2]); * * wrapped.next(); * // => { 'done': false, 'value': 1 } * * wrapped.next(); * // => { 'done': false, 'value': 2 } * * wrapped.next(); * // => { 'done': true, 'value': undefined } */ function wrapperNext() { if (this.__values__ === undefined$1) { this.__values__ = toArray(this.value()); } var done = this.__index__ >= this.__values__.length, value = done ? undefined$1 : this.__values__[this.__index__++]; return { 'done': done, 'value': value }; } /** * Enables the wrapper to be iterable. * * @name Symbol.iterator * @memberOf _ * @since 4.0.0 * @category Seq * @returns {Object} Returns the wrapper object. * @example * * var wrapped = _([1, 2]); * * wrapped[Symbol.iterator]() === wrapped; * // => true * * Array.from(wrapped); * // => [1, 2] */ function wrapperToIterator() { return this; } /** * Creates a clone of the chain sequence planting `value` as the wrapped value. * * @name plant * @memberOf _ * @since 3.2.0 * @category Seq * @param {*} value The value to plant. * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * function square(n) { * return n * n; * } * * var wrapped = _([1, 2]).map(square); * var other = wrapped.plant([3, 4]); * * other.value(); * // => [9, 16] * * wrapped.value(); * // => [1, 4] */ function wrapperPlant(value) { var result, parent = this; while (parent instanceof baseLodash) { var clone = wrapperClone(parent); clone.__index__ = 0; clone.__values__ = undefined$1; if (result) { previous.__wrapped__ = clone; } else { result = clone; } var previous = clone; parent = parent.__wrapped__; } previous.__wrapped__ = value; return result; } /** * This method is the wrapper version of `_.reverse`. * * **Note:** This method mutates the wrapped array. * * @name reverse * @memberOf _ * @since 0.1.0 * @category Seq * @returns {Object} Returns the new `lodash` wrapper instance. * @example * * var array = [1, 2, 3]; * * _(array).reverse().value() * // => [3, 2, 1] * * console.log(array); * // => [3, 2, 1] */ function wrapperReverse() { var value = this.__wrapped__; if (value instanceof LazyWrapper) { var wrapped = value; if (this.__actions__.length) { wrapped = new LazyWrapper(this); } wrapped = wrapped.reverse(); wrapped.__actions__.push({ 'func': thru, 'args': [reverse], 'thisArg': undefined$1 }); return new LodashWrapper(wrapped, this.__chain__); } return this.thru(reverse); } /** * Executes the chain sequence to resolve the unwrapped value. * * @name value * @memberOf _ * @since 0.1.0 * @alias toJSON, valueOf * @category Seq * @returns {*} Returns the resolved unwrapped value. * @example * * _([1, 2, 3]).value(); * // => [1, 2, 3] */ function wrapperValue() { return baseWrapperValue(this.__wrapped__, this.__actions__); } /*------------------------------------------------------------------------*/ /** * Creates an object composed of keys generated from the results of running * each element of `collection` thru `iteratee`. The corresponding value of * each key is the number of times the key was returned by `iteratee`. The * iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 0.5.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The iteratee to transform keys. * @returns {Object} Returns the composed aggregate object. * @example * * _.countBy([6.1, 4.2, 6.3], Math.floor); * // => { '4': 1, '6': 2 } * * // The `_.property` iteratee shorthand. * _.countBy(['one', 'two', 'three'], 'length'); * // => { '3': 2, '5': 1 } */ var countBy = createAggregator(function(result, value, key) { if (hasOwnProperty.call(result, key)) { ++result[key]; } else { baseAssignValue(result, key, 1); } }); /** * Checks if `predicate` returns truthy for **all** elements of `collection`. * Iteration is stopped once `predicate` returns falsey. The predicate is * invoked with three arguments: (value, index|key, collection). * * **Note:** This method returns `true` for * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of * elements of empty collections. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false`. * @example * * _.every([true, 1, null, 'yes'], Boolean); * // => false * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': false } * ]; * * // The `_.matches` iteratee shorthand. * _.every(users, { 'user': 'barney', 'active': false }); * // => false * * // The `_.matchesProperty` iteratee shorthand. * _.every(users, ['active', false]); * // => true * * // The `_.property` iteratee shorthand. * _.every(users, 'active'); * // => false */ function every(collection, predicate, guard) { var func = isArray(collection) ? arrayEvery : baseEvery; if (guard && isIterateeCall(collection, predicate, guard)) { predicate = undefined$1; } return func(collection, getIteratee(predicate, 3)); } /** * Iterates over elements of `collection`, returning an array of all elements * `predicate` returns truthy for. The predicate is invoked with three * arguments: (value, index|key, collection). * * **Note:** Unlike `_.remove`, this method returns a new array. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the new filtered array. * @see _.reject * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': true }, * { 'user': 'fred', 'age': 40, 'active': false } * ]; * * _.filter(users, function(o) { return !o.active; }); * // => objects for ['fred'] * * // The `_.matches` iteratee shorthand. * _.filter(users, { 'age': 36, 'active': true }); * // => objects for ['barney'] * * // The `_.matchesProperty` iteratee shorthand. * _.filter(users, ['active', false]); * // => objects for ['fred'] * * // The `_.property` iteratee shorthand. * _.filter(users, 'active'); * // => objects for ['barney'] * * // Combining several predicates using `_.overEvery` or `_.overSome`. * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]])); * // => objects for ['fred', 'barney'] */ function filter(collection, predicate) { var func = isArray(collection) ? arrayFilter : baseFilter; return func(collection, getIteratee(predicate, 3)); } /** * Iterates over elements of `collection`, returning the first element * `predicate` returns truthy for. The predicate is invoked with three * arguments: (value, index|key, collection). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=0] The index to search from. * @returns {*} Returns the matched element, else `undefined`. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': true }, * { 'user': 'fred', 'age': 40, 'active': false }, * { 'user': 'pebbles', 'age': 1, 'active': true } * ]; * * _.find(users, function(o) { return o.age < 40; }); * // => object for 'barney' * * // The `_.matches` iteratee shorthand. * _.find(users, { 'age': 1, 'active': true }); * // => object for 'pebbles' * * // The `_.matchesProperty` iteratee shorthand. * _.find(users, ['active', false]); * // => object for 'fred' * * // The `_.property` iteratee shorthand. * _.find(users, 'active'); * // => object for 'barney' */ var find = createFind(findIndex); /** * This method is like `_.find` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @since 2.0.0 * @category Collection * @param {Array|Object} collection The collection to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param {number} [fromIndex=collection.length-1] The index to search from. * @returns {*} Returns the matched element, else `undefined`. * @example * * _.findLast([1, 2, 3, 4], function(n) { * return n % 2 == 1; * }); * // => 3 */ var findLast = createFind(findLastIndex); /** * Creates a flattened array of values by running each element in `collection` * thru `iteratee` and flattening the mapped results. The iteratee is invoked * with three arguments: (value, index|key, collection). * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array} Returns the new flattened array. * @example * * function duplicate(n) { * return [n, n]; * } * * _.flatMap([1, 2], duplicate); * // => [1, 1, 2, 2] */ function flatMap(collection, iteratee) { return baseFlatten(map(collection, iteratee), 1); } /** * This method is like `_.flatMap` except that it recursively flattens the * mapped results. * * @static * @memberOf _ * @since 4.7.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array} Returns the new flattened array. * @example * * function duplicate(n) { * return [[[n, n]]]; * } * * _.flatMapDeep([1, 2], duplicate); * // => [1, 1, 2, 2] */ function flatMapDeep(collection, iteratee) { return baseFlatten(map(collection, iteratee), INFINITY); } /** * This method is like `_.flatMap` except that it recursively flattens the * mapped results up to `depth` times. * * @static * @memberOf _ * @since 4.7.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {number} [depth=1] The maximum recursion depth. * @returns {Array} Returns the new flattened array. * @example * * function duplicate(n) { * return [[[n, n]]]; * } * * _.flatMapDepth([1, 2], duplicate, 2); * // => [[1, 1], [2, 2]] */ function flatMapDepth(collection, iteratee, depth) { depth = depth === undefined$1 ? 1 : toInteger(depth); return baseFlatten(map(collection, iteratee), depth); } /** * Iterates over elements of `collection` and invokes `iteratee` for each element. * The iteratee is invoked with three arguments: (value, index|key, collection). * Iteratee functions may exit iteration early by explicitly returning `false`. * * **Note:** As with other "Collections" methods, objects with a "length" * property are iterated like arrays. To avoid this behavior use `_.forIn` * or `_.forOwn` for object iteration. * * @static * @memberOf _ * @since 0.1.0 * @alias each * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array|Object} Returns `collection`. * @see _.forEachRight * @example * * _.forEach([1, 2], function(value) { * console.log(value); * }); * // => Logs `1` then `2`. * * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { * console.log(key); * }); * // => Logs 'a' then 'b' (iteration order is not guaranteed). */ function forEach(collection, iteratee) { var func = isArray(collection) ? arrayEach : baseEach; return func(collection, getIteratee(iteratee, 3)); } /** * This method is like `_.forEach` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @since 2.0.0 * @alias eachRight * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array|Object} Returns `collection`. * @see _.forEach * @example * * _.forEachRight([1, 2], function(value) { * console.log(value); * }); * // => Logs `2` then `1`. */ function forEachRight(collection, iteratee) { var func = isArray(collection) ? arrayEachRight : baseEachRight; return func(collection, getIteratee(iteratee, 3)); } /** * Creates an object composed of keys generated from the results of running * each element of `collection` thru `iteratee`. The order of grouped values * is determined by the order they occur in `collection`. The corresponding * value of each key is an array of elements responsible for generating the * key. The iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The iteratee to transform keys. * @returns {Object} Returns the composed aggregate object. * @example * * _.groupBy([6.1, 4.2, 6.3], Math.floor); * // => { '4': [4.2], '6': [6.1, 6.3] } * * // The `_.property` iteratee shorthand. * _.groupBy(['one', 'two', 'three'], 'length'); * // => { '3': ['one', 'two'], '5': ['three'] } */ var groupBy = createAggregator(function(result, value, key) { if (hasOwnProperty.call(result, key)) { result[key].push(value); } else { baseAssignValue(result, key, [value]); } }); /** * Checks if `value` is in `collection`. If `collection` is a string, it's * checked for a substring of `value`, otherwise * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * is used for equality comparisons. If `fromIndex` is negative, it's used as * the offset from the end of `collection`. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object|string} collection The collection to inspect. * @param {*} value The value to search for. * @param {number} [fromIndex=0] The index to search from. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. * @returns {boolean} Returns `true` if `value` is found, else `false`. * @example * * _.includes([1, 2, 3], 1); * // => true * * _.includes([1, 2, 3], 1, 2); * // => false * * _.includes({ 'a': 1, 'b': 2 }, 1); * // => true * * _.includes('abcd', 'bc'); * // => true */ function includes(collection, value, fromIndex, guard) { collection = isArrayLike(collection) ? collection : values(collection); fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; var length = collection.length; if (fromIndex < 0) { fromIndex = nativeMax(length + fromIndex, 0); } return isString(collection) ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) : (!!length && baseIndexOf(collection, value, fromIndex) > -1); } /** * Invokes the method at `path` of each element in `collection`, returning * an array of the results of each invoked method. Any additional arguments * are provided to each invoked method. If `path` is a function, it's invoked * for, and `this` bound to, each element in `collection`. * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Array|Function|string} path The path of the method to invoke or * the function invoked per iteration. * @param {...*} [args] The arguments to invoke each method with. * @returns {Array} Returns the array of results. * @example * * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); * // => [[1, 5, 7], [1, 2, 3]] * * _.invokeMap([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ var invokeMap = baseRest(function(collection, path, args) { var index = -1, isFunc = typeof path == 'function', result = isArrayLike(collection) ? Array(collection.length) : []; baseEach(collection, function(value) { result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); }); return result; }); /** * Creates an object composed of keys generated from the results of running * each element of `collection` thru `iteratee`. The corresponding value of * each key is the last element responsible for generating the key. The * iteratee is invoked with one argument: (value). * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The iteratee to transform keys. * @returns {Object} Returns the composed aggregate object. * @example * * var array = [ * { 'dir': 'left', 'code': 97 }, * { 'dir': 'right', 'code': 100 } * ]; * * _.keyBy(array, function(o) { * return String.fromCharCode(o.code); * }); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } * * _.keyBy(array, 'dir'); * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } */ var keyBy = createAggregator(function(result, value, key) { baseAssignValue(result, key, value); }); /** * Creates an array of values by running each element in `collection` thru * `iteratee`. The iteratee is invoked with three arguments: * (value, index|key, collection). * * Many lodash methods are guarded to work as iteratees for methods like * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. * * The guarded methods are: * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, * `template`, `trim`, `trimEnd`, `trimStart`, and `words` * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Array} Returns the new mapped array. * @example * * function square(n) { * return n * n; * } * * _.map([4, 8], square); * // => [16, 64] * * _.map({ 'a': 4, 'b': 8 }, square); * // => [16, 64] (iteration order is not guaranteed) * * var users = [ * { 'user': 'barney' }, * { 'user': 'fred' } * ]; * * // The `_.property` iteratee shorthand. * _.map(users, 'user'); * // => ['barney', 'fred'] */ function map(collection, iteratee) { var func = isArray(collection) ? arrayMap : baseMap; return func(collection, getIteratee(iteratee, 3)); } /** * This method is like `_.sortBy` except that it allows specifying the sort * orders of the iteratees to sort by. If `orders` is unspecified, all values * are sorted in ascending order. Otherwise, specify an order of "desc" for * descending or "asc" for ascending sort order of corresponding values. * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] * The iteratees to sort by. * @param {string[]} [orders] The sort orders of `iteratees`. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. * @returns {Array} Returns the new sorted array. * @example * * var users = [ * { 'user': 'fred', 'age': 48 }, * { 'user': 'barney', 'age': 34 }, * { 'user': 'fred', 'age': 40 }, * { 'user': 'barney', 'age': 36 } * ]; * * // Sort by `user` in ascending order and by `age` in descending order. * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] */ function orderBy(collection, iteratees, orders, guard) { if (collection == null) { return []; } if (!isArray(iteratees)) { iteratees = iteratees == null ? [] : [iteratees]; } orders = guard ? undefined$1 : orders; if (!isArray(orders)) { orders = orders == null ? [] : [orders]; } return baseOrderBy(collection, iteratees, orders); } /** * Creates an array of elements split into two groups, the first of which * contains elements `predicate` returns truthy for, the second of which * contains elements `predicate` returns falsey for. The predicate is * invoked with one argument: (value). * * @static * @memberOf _ * @since 3.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the array of grouped elements. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true }, * { 'user': 'pebbles', 'age': 1, 'active': false } * ]; * * _.partition(users, function(o) { return o.active; }); * // => objects for [['fred'], ['barney', 'pebbles']] * * // The `_.matches` iteratee shorthand. * _.partition(users, { 'age': 1, 'active': false }); * // => objects for [['pebbles'], ['barney', 'fred']] * * // The `_.matchesProperty` iteratee shorthand. * _.partition(users, ['active', false]); * // => objects for [['barney', 'pebbles'], ['fred']] * * // The `_.property` iteratee shorthand. * _.partition(users, 'active'); * // => objects for [['fred'], ['barney', 'pebbles']] */ var partition = createAggregator(function(result, value, key) { result[key ? 0 : 1].push(value); }, function() { return [[], []]; }); /** * Reduces `collection` to a value which is the accumulated result of running * each element in `collection` thru `iteratee`, where each successive * invocation is supplied the return value of the previous. If `accumulator` * is not given, the first element of `collection` is used as the initial * value. The iteratee is invoked with four arguments: * (accumulator, value, index|key, collection). * * Many lodash methods are guarded to work as iteratees for methods like * `_.reduce`, `_.reduceRight`, and `_.transform`. * * The guarded methods are: * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, * and `sortBy` * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The initial value. * @returns {*} Returns the accumulated value. * @see _.reduceRight * @example * * _.reduce([1, 2], function(sum, n) { * return sum + n; * }, 0); * // => 3 * * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { * (result[value] || (result[value] = [])).push(key); * return result; * }, {}); * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) */ function reduce(collection, iteratee, accumulator) { var func = isArray(collection) ? arrayReduce : baseReduce, initAccum = arguments.length < 3; return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach); } /** * This method is like `_.reduce` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The initial value. * @returns {*} Returns the accumulated value. * @see _.reduce * @example * * var array = [[0, 1], [2, 3], [4, 5]]; * * _.reduceRight(array, function(flattened, other) { * return flattened.concat(other); * }, []); * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, iteratee, accumulator) { var func = isArray(collection) ? arrayReduceRight : baseReduce, initAccum = arguments.length < 3; return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); } /** * The opposite of `_.filter`; this method returns the elements of `collection` * that `predicate` does **not** return truthy for. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the new filtered array. * @see _.filter * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true } * ]; * * _.reject(users, function(o) { return !o.active; }); * // => objects for ['fred'] * * // The `_.matches` iteratee shorthand. * _.reject(users, { 'age': 40, 'active': true }); * // => objects for ['barney'] * * // The `_.matchesProperty` iteratee shorthand. * _.reject(users, ['active', false]); * // => objects for ['fred'] * * // The `_.property` iteratee shorthand. * _.reject(users, 'active'); * // => objects for ['barney'] */ function reject(collection, predicate) { var func = isArray(collection) ? arrayFilter : baseFilter; return func(collection, negate(getIteratee(predicate, 3))); } /** * Gets a random element from `collection`. * * @static * @memberOf _ * @since 2.0.0 * @category Collection * @param {Array|Object} collection The collection to sample. * @returns {*} Returns the random element. * @example * * _.sample([1, 2, 3, 4]); * // => 2 */ function sample(collection) { var func = isArray(collection) ? arraySample : baseSample; return func(collection); } /** * Gets `n` random elements at unique keys from `collection` up to the * size of `collection`. * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to sample. * @param {number} [n=1] The number of elements to sample. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the random elements. * @example * * _.sampleSize([1, 2, 3], 2); * // => [3, 1] * * _.sampleSize([1, 2, 3], 4); * // => [2, 3, 1] */ function sampleSize(collection, n, guard) { if ((guard ? isIterateeCall(collection, n, guard) : n === undefined$1)) { n = 1; } else { n = toInteger(n); } var func = isArray(collection) ? arraySampleSize : baseSampleSize; return func(collection, n); } /** * Creates an array of shuffled values, using a version of the * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to shuffle. * @returns {Array} Returns the new shuffled array. * @example * * _.shuffle([1, 2, 3, 4]); * // => [4, 1, 3, 2] */ function shuffle(collection) { var func = isArray(collection) ? arrayShuffle : baseShuffle; return func(collection); } /** * Gets the size of `collection` by returning its length for array-like * values or the number of own enumerable string keyed properties for objects. * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object|string} collection The collection to inspect. * @returns {number} Returns the collection size. * @example * * _.size([1, 2, 3]); * // => 3 * * _.size({ 'a': 1, 'b': 2 }); * // => 2 * * _.size('pebbles'); * // => 7 */ function size(collection) { if (collection == null) { return 0; } if (isArrayLike(collection)) { return isString(collection) ? stringSize(collection) : collection.length; } var tag = getTag(collection); if (tag == mapTag || tag == setTag) { return collection.size; } return baseKeys(collection).length; } /** * Checks if `predicate` returns truthy for **any** element of `collection`. * Iteration is stopped once `predicate` returns truthy. The predicate is * invoked with three arguments: (value, index|key, collection). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. * @example * * _.some([null, 0, 'yes', false], Boolean); * // => true * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false } * ]; * * // The `_.matches` iteratee shorthand. * _.some(users, { 'user': 'barney', 'active': false }); * // => false * * // The `_.matchesProperty` iteratee shorthand. * _.some(users, ['active', false]); * // => true * * // The `_.property` iteratee shorthand. * _.some(users, 'active'); * // => true */ function some(collection, predicate, guard) { var func = isArray(collection) ? arraySome : baseSome; if (guard && isIterateeCall(collection, predicate, guard)) { predicate = undefined$1; } return func(collection, getIteratee(predicate, 3)); } /** * Creates an array of elements, sorted in ascending order by the results of * running each element in a collection thru each iteratee. This method * performs a stable sort, that is, it preserves the original sort order of * equal elements. The iteratees are invoked with one argument: (value). * * @static * @memberOf _ * @since 0.1.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {...(Function|Function[])} [iteratees=[_.identity]] * The iteratees to sort by. * @returns {Array} Returns the new sorted array. * @example * * var users = [ * { 'user': 'fred', 'age': 48 }, * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 30 }, * { 'user': 'barney', 'age': 34 } * ]; * * _.sortBy(users, [function(o) { return o.user; }]); * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]] * * _.sortBy(users, ['user', 'age']); * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]] */ var sortBy = baseRest(function(collection, iteratees) { if (collection == null) { return []; } var length = iteratees.length; if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { iteratees = []; } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { iteratees = [iteratees[0]]; } return baseOrderBy(collection, baseFlatten(iteratees, 1), []); }); /*------------------------------------------------------------------------*/ /** * Gets the timestamp of the number of milliseconds that have elapsed since * the Unix epoch (1 January 1970 00:00:00 UTC). * * @static * @memberOf _ * @since 2.4.0 * @category Date * @returns {number} Returns the timestamp. * @example * * _.defer(function(stamp) { * console.log(_.now() - stamp); * }, _.now()); * // => Logs the number of milliseconds it took for the deferred invocation. */ var now = ctxNow || function() { return root.Date.now(); }; /*------------------------------------------------------------------------*/ /** * The opposite of `_.before`; this method creates a function that invokes * `func` once it's called `n` or more times. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {number} n The number of calls before `func` is invoked. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var saves = ['profile', 'settings']; * * var done = _.after(saves.length, function() { * console.log('done saving!'); * }); * * _.forEach(saves, function(type) { * asyncSave({ 'type': type, 'complete': done }); * }); * // => Logs 'done saving!' after the two async saves have completed. */ function after(n, func) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } n = toInteger(n); return function() { if (--n < 1) { return func.apply(this, arguments); } }; } /** * Creates a function that invokes `func`, with up to `n` arguments, * ignoring any additional arguments. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} func The function to cap arguments for. * @param {number} [n=func.length] The arity cap. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the new capped function. * @example * * _.map(['6', '8', '10'], _.ary(parseInt, 1)); * // => [6, 8, 10] */ function ary(func, n, guard) { n = guard ? undefined$1 : n; n = (func && n == null) ? func.length : n; return createWrap(func, WRAP_ARY_FLAG, undefined$1, undefined$1, undefined$1, undefined$1, n); } /** * Creates a function that invokes `func`, with the `this` binding and arguments * of the created function, while it's called less than `n` times. Subsequent * calls to the created function return the result of the last `func` invocation. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {number} n The number of calls at which `func` is no longer invoked. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * jQuery(element).on('click', _.before(5, addContactToList)); * // => Allows adding up to 4 contacts to the list. */ function before(n, func) { var result; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } n = toInteger(n); return function() { if (--n > 0) { result = func.apply(this, arguments); } if (n <= 1) { func = undefined$1; } return result; }; } /** * Creates a function that invokes `func` with the `this` binding of `thisArg` * and `partials` prepended to the arguments it receives. * * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for partially applied arguments. * * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" * property of bound functions. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to bind. * @param {*} thisArg The `this` binding of `func`. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * function greet(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * } * * var object = { 'user': 'fred' }; * * var bound = _.bind(greet, object, 'hi'); * bound('!'); * // => 'hi fred!' * * // Bound with placeholders. * var bound = _.bind(greet, object, _, '!'); * bound('hi'); * // => 'hi fred!' */ var bind = baseRest(function(func, thisArg, partials) { var bitmask = WRAP_BIND_FLAG; if (partials.length) { var holders = replaceHolders(partials, getHolder(bind)); bitmask |= WRAP_PARTIAL_FLAG; } return createWrap(func, bitmask, thisArg, partials, holders); }); /** * Creates a function that invokes the method at `object[key]` with `partials` * prepended to the arguments it receives. * * This method differs from `_.bind` by allowing bound functions to reference * methods that may be redefined or don't yet exist. See * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) * for more details. * * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * @static * @memberOf _ * @since 0.10.0 * @category Function * @param {Object} object The object to invoke the method on. * @param {string} key The key of the method. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * var object = { * 'user': 'fred', * 'greet': function(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * } * }; * * var bound = _.bindKey(object, 'greet', 'hi'); * bound('!'); * // => 'hi fred!' * * object.greet = function(greeting, punctuation) { * return greeting + 'ya ' + this.user + punctuation; * }; * * bound('!'); * // => 'hiya fred!' * * // Bound with placeholders. * var bound = _.bindKey(object, 'greet', _, '!'); * bound('hi'); * // => 'hiya fred!' */ var bindKey = baseRest(function(object, key, partials) { var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG; if (partials.length) { var holders = replaceHolders(partials, getHolder(bindKey)); bitmask |= WRAP_PARTIAL_FLAG; } return createWrap(key, bitmask, object, partials, holders); }); /** * Creates a function that accepts arguments of `func` and either invokes * `func` returning its result, if at least `arity` number of arguments have * been provided, or returns a function that accepts the remaining `func` * arguments, and so on. The arity of `func` may be specified if `func.length` * is not sufficient. * * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for provided arguments. * * **Note:** This method doesn't set the "length" property of curried functions. * * @static * @memberOf _ * @since 2.0.0 * @category Function * @param {Function} func The function to curry. * @param {number} [arity=func.length] The arity of `func`. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the new curried function. * @example * * var abc = function(a, b, c) { * return [a, b, c]; * }; * * var curried = _.curry(abc); * * curried(1)(2)(3); * // => [1, 2, 3] * * curried(1, 2)(3); * // => [1, 2, 3] * * curried(1, 2, 3); * // => [1, 2, 3] * * // Curried with placeholders. * curried(1)(_, 3)(2); * // => [1, 2, 3] */ function curry(func, arity, guard) { arity = guard ? undefined$1 : arity; var result = createWrap(func, WRAP_CURRY_FLAG, undefined$1, undefined$1, undefined$1, undefined$1, undefined$1, arity); result.placeholder = curry.placeholder; return result; } /** * This method is like `_.curry` except that arguments are applied to `func` * in the manner of `_.partialRight` instead of `_.partial`. * * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for provided arguments. * * **Note:** This method doesn't set the "length" property of curried functions. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} func The function to curry. * @param {number} [arity=func.length] The arity of `func`. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the new curried function. * @example * * var abc = function(a, b, c) { * return [a, b, c]; * }; * * var curried = _.curryRight(abc); * * curried(3)(2)(1); * // => [1, 2, 3] * * curried(2, 3)(1); * // => [1, 2, 3] * * curried(1, 2, 3); * // => [1, 2, 3] * * // Curried with placeholders. * curried(3)(1, _)(2); * // => [1, 2, 3] */ function curryRight(func, arity, guard) { arity = guard ? undefined$1 : arity; var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined$1, undefined$1, undefined$1, undefined$1, undefined$1, arity); result.placeholder = curryRight.placeholder; return result; } /** * Creates a debounced function that delays invoking `func` until after `wait` * milliseconds have elapsed since the last time the debounced function was * invoked. The debounced function comes with a `cancel` method to cancel * delayed `func` invocations and a `flush` method to immediately invoke them. * Provide `options` to indicate whether `func` should be invoked on the * leading and/or trailing edge of the `wait` timeout. The `func` is invoked * with the last arguments provided to the debounced function. Subsequent * calls to the debounced function return the result of the last `func` * invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the debounced function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.debounce` and `_.throttle`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to debounce. * @param {number} [wait=0] The number of milliseconds to delay. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=false] * Specify invoking on the leading edge of the timeout. * @param {number} [options.maxWait] * The maximum time `func` is allowed to be delayed before it's invoked. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * // Avoid costly calculations while the window size is in flux. * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); * * // Invoke `sendMail` when clicked, debouncing subsequent calls. * jQuery(element).on('click', _.debounce(sendMail, 300, { * 'leading': true, * 'trailing': false * })); * * // Ensure `batchLog` is invoked once after 1 second of debounced calls. * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); * var source = new EventSource('/stream'); * jQuery(source).on('message', debounced); * * // Cancel the trailing debounced invocation. * jQuery(window).on('popstate', debounced.cancel); */ function debounce(func, wait, options) { var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } wait = toNumber(wait) || 0; if (isObject(options)) { leading = !!options.leading; maxing = 'maxWait' in options; maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; trailing = 'trailing' in options ? !!options.trailing : trailing; } function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = undefined$1; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } function leadingEdge(time) { // Reset any `maxWait` timer. lastInvokeTime = time; // Start the timer for the trailing edge. timerId = setTimeout(timerExpired, wait); // Invoke the leading edge. return leading ? invokeFunc(time) : result; } function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall; return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; } function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating // it as the trailing edge, or we've hit the `maxWait` limit. return (lastCallTime === undefined$1 || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); } function timerExpired() { var time = now(); if (shouldInvoke(time)) { return trailingEdge(time); } // Restart the timer. timerId = setTimeout(timerExpired, remainingWait(time)); } function trailingEdge(time) { timerId = undefined$1; // Only invoke if we have `lastArgs` which means `func` has been // debounced at least once. if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = undefined$1; return result; } function cancel() { if (timerId !== undefined$1) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = undefined$1; } function flush() { return timerId === undefined$1 ? result : trailingEdge(now()); } function debounced() { var time = now(), isInvoking = shouldInvoke(time); lastArgs = arguments; lastThis = this; lastCallTime = time; if (isInvoking) { if (timerId === undefined$1) { return leadingEdge(lastCallTime); } if (maxing) { // Handle invocations in a tight loop. clearTimeout(timerId); timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } if (timerId === undefined$1) { timerId = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; return debounced; } /** * Defers invoking the `func` until the current call stack has cleared. Any * additional arguments are provided to `func` when it's invoked. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to defer. * @param {...*} [args] The arguments to invoke `func` with. * @returns {number} Returns the timer id. * @example * * _.defer(function(text) { * console.log(text); * }, 'deferred'); * // => Logs 'deferred' after one millisecond. */ var defer = baseRest(function(func, args) { return baseDelay(func, 1, args); }); /** * Invokes `func` after `wait` milliseconds. Any additional arguments are * provided to `func` when it's invoked. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @param {...*} [args] The arguments to invoke `func` with. * @returns {number} Returns the timer id. * @example * * _.delay(function(text) { * console.log(text); * }, 1000, 'later'); * // => Logs 'later' after one second. */ var delay = baseRest(function(func, wait, args) { return baseDelay(func, toNumber(wait) || 0, args); }); /** * Creates a function that invokes `func` with arguments reversed. * * @static * @memberOf _ * @since 4.0.0 * @category Function * @param {Function} func The function to flip arguments for. * @returns {Function} Returns the new flipped function. * @example * * var flipped = _.flip(function() { * return _.toArray(arguments); * }); * * flipped('a', 'b', 'c', 'd'); * // => ['d', 'c', 'b', 'a'] */ function flip(func) { return createWrap(func, WRAP_FLIP_FLAG); } /** * Creates a function that memoizes the result of `func`. If `resolver` is * provided, it determines the cache key for storing the result based on the * arguments provided to the memoized function. By default, the first argument * provided to the memoized function is used as the map cache key. The `func` * is invoked with the `this` binding of the memoized function. * * **Note:** The cache is exposed as the `cache` property on the memoized * function. Its creation may be customized by replacing the `_.memoize.Cache` * constructor with one whose instances implement the * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) * method interface of `clear`, `delete`, `get`, `has`, and `set`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to have its output memoized. * @param {Function} [resolver] The function to resolve the cache key. * @returns {Function} Returns the new memoized function. * @example * * var object = { 'a': 1, 'b': 2 }; * var other = { 'c': 3, 'd': 4 }; * * var values = _.memoize(_.values); * values(object); * // => [1, 2] * * values(other); * // => [3, 4] * * object.a = 2; * values(object); * // => [1, 2] * * // Modify the result cache. * values.cache.set(object, ['a', 'b']); * values(object); * // => ['a', 'b'] * * // Replace `_.memoize.Cache`. * _.memoize.Cache = WeakMap; */ function memoize(func, resolver) { if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { throw new TypeError(FUNC_ERROR_TEXT); } var memoized = function() { var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache; if (cache.has(key)) { return cache.get(key); } var result = func.apply(this, args); memoized.cache = cache.set(key, result) || cache; return result; }; memoized.cache = new (memoize.Cache || MapCache); return memoized; } // Expose `MapCache`. memoize.Cache = MapCache; /** * Creates a function that negates the result of the predicate `func`. The * `func` predicate is invoked with the `this` binding and arguments of the * created function. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} predicate The predicate to negate. * @returns {Function} Returns the new negated function. * @example * * function isEven(n) { * return n % 2 == 0; * } * * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); * // => [1, 3, 5] */ function negate(predicate) { if (typeof predicate != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } return function() { var args = arguments; switch (args.length) { case 0: return !predicate.call(this); case 1: return !predicate.call(this, args[0]); case 2: return !predicate.call(this, args[0], args[1]); case 3: return !predicate.call(this, args[0], args[1], args[2]); } return !predicate.apply(this, args); }; } /** * Creates a function that is restricted to invoking `func` once. Repeat calls * to the function return the value of the first invocation. The `func` is * invoked with the `this` binding and arguments of the created function. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var initialize = _.once(createApplication); * initialize(); * initialize(); * // => `createApplication` is invoked once */ function once(func) { return before(2, func); } /** * Creates a function that invokes `func` with its arguments transformed. * * @static * @since 4.0.0 * @memberOf _ * @category Function * @param {Function} func The function to wrap. * @param {...(Function|Function[])} [transforms=[_.identity]] * The argument transforms. * @returns {Function} Returns the new function. * @example * * function doubled(n) { * return n * 2; * } * * function square(n) { * return n * n; * } * * var func = _.overArgs(function(x, y) { * return [x, y]; * }, [square, doubled]); * * func(9, 3); * // => [81, 6] * * func(10, 5); * // => [100, 10] */ var overArgs = castRest(function(func, transforms) { transforms = (transforms.length == 1 && isArray(transforms[0])) ? arrayMap(transforms[0], baseUnary(getIteratee())) : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee())); var funcsLength = transforms.length; return baseRest(function(args) { var index = -1, length = nativeMin(args.length, funcsLength); while (++index < length) { args[index] = transforms[index].call(this, args[index]); } return apply(func, this, args); }); }); /** * Creates a function that invokes `func` with `partials` prepended to the * arguments it receives. This method is like `_.bind` except it does **not** * alter the `this` binding. * * The `_.partial.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * **Note:** This method doesn't set the "length" property of partially * applied functions. * * @static * @memberOf _ * @since 0.2.0 * @category Function * @param {Function} func The function to partially apply arguments to. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * function greet(greeting, name) { * return greeting + ' ' + name; * } * * var sayHelloTo = _.partial(greet, 'hello'); * sayHelloTo('fred'); * // => 'hello fred' * * // Partially applied with placeholders. * var greetFred = _.partial(greet, _, 'fred'); * greetFred('hi'); * // => 'hi fred' */ var partial = baseRest(function(func, partials) { var holders = replaceHolders(partials, getHolder(partial)); return createWrap(func, WRAP_PARTIAL_FLAG, undefined$1, partials, holders); }); /** * This method is like `_.partial` except that partially applied arguments * are appended to the arguments it receives. * * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * **Note:** This method doesn't set the "length" property of partially * applied functions. * * @static * @memberOf _ * @since 1.0.0 * @category Function * @param {Function} func The function to partially apply arguments to. * @param {...*} [partials] The arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * function greet(greeting, name) { * return greeting + ' ' + name; * } * * var greetFred = _.partialRight(greet, 'fred'); * greetFred('hi'); * // => 'hi fred' * * // Partially applied with placeholders. * var sayHelloTo = _.partialRight(greet, 'hello', _); * sayHelloTo('fred'); * // => 'hello fred' */ var partialRight = baseRest(function(func, partials) { var holders = replaceHolders(partials, getHolder(partialRight)); return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined$1, partials, holders); }); /** * Creates a function that invokes `func` with arguments arranged according * to the specified `indexes` where the argument value at the first index is * provided as the first argument, the argument value at the second index is * provided as the second argument, and so on. * * @static * @memberOf _ * @since 3.0.0 * @category Function * @param {Function} func The function to rearrange arguments for. * @param {...(number|number[])} indexes The arranged argument indexes. * @returns {Function} Returns the new function. * @example * * var rearged = _.rearg(function(a, b, c) { * return [a, b, c]; * }, [2, 0, 1]); * * rearged('b', 'c', 'a') * // => ['a', 'b', 'c'] */ var rearg = flatRest(function(func, indexes) { return createWrap(func, WRAP_REARG_FLAG, undefined$1, undefined$1, undefined$1, indexes); }); /** * Creates a function that invokes `func` with the `this` binding of the * created function and arguments from `start` and beyond provided as * an array. * * **Note:** This method is based on the * [rest parameter](https://mdn.io/rest_parameters). * * @static * @memberOf _ * @since 4.0.0 * @category Function * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. * @example * * var say = _.rest(function(what, names) { * return what + ' ' + _.initial(names).join(', ') + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); * }); * * say('hello', 'fred', 'barney', 'pebbles'); * // => 'hello fred, barney, & pebbles' */ function rest(func, start) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } start = start === undefined$1 ? start : toInteger(start); return baseRest(func, start); } /** * Creates a function that invokes `func` with the `this` binding of the * create function and an array of arguments much like * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). * * **Note:** This method is based on the * [spread operator](https://mdn.io/spread_operator). * * @static * @memberOf _ * @since 3.2.0 * @category Function * @param {Function} func The function to spread arguments over. * @param {number} [start=0] The start position of the spread. * @returns {Function} Returns the new function. * @example * * var say = _.spread(function(who, what) { * return who + ' says ' + what; * }); * * say(['fred', 'hello']); * // => 'fred says hello' * * var numbers = Promise.all([ * Promise.resolve(40), * Promise.resolve(36) * ]); * * numbers.then(_.spread(function(x, y) { * return x + y; * })); * // => a Promise of 76 */ function spread(func, start) { if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } start = start == null ? 0 : nativeMax(toInteger(start), 0); return baseRest(function(args) { var array = args[start], otherArgs = castSlice(args, 0, start); if (array) { arrayPush(otherArgs, array); } return apply(func, this, otherArgs); }); } /** * Creates a throttled function that only invokes `func` at most once per * every `wait` milliseconds. The throttled function comes with a `cancel` * method to cancel delayed `func` invocations and a `flush` method to * immediately invoke them. Provide `options` to indicate whether `func` * should be invoked on the leading and/or trailing edge of the `wait` * timeout. The `func` is invoked with the last arguments provided to the * throttled function. Subsequent calls to the throttled function return the * result of the last `func` invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the throttled function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.throttle` and `_.debounce`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to throttle. * @param {number} [wait=0] The number of milliseconds to throttle invocations to. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=true] * Specify invoking on the leading edge of the timeout. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * * // Avoid excessively updating the position while scrolling. * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); * * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); * jQuery(element).on('click', throttled); * * // Cancel the trailing throttled invocation. * jQuery(window).on('popstate', throttled.cancel); */ function throttle(func, wait, options) { var leading = true, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } if (isObject(options)) { leading = 'leading' in options ? !!options.leading : leading; trailing = 'trailing' in options ? !!options.trailing : trailing; } return debounce(func, wait, { 'leading': leading, 'maxWait': wait, 'trailing': trailing }); } /** * Creates a function that accepts up to one argument, ignoring any * additional arguments. * * @static * @memberOf _ * @since 4.0.0 * @category Function * @param {Function} func The function to cap arguments for. * @returns {Function} Returns the new capped function. * @example * * _.map(['6', '8', '10'], _.unary(parseInt)); * // => [6, 8, 10] */ function unary(func) { return ary(func, 1); } /** * Creates a function that provides `value` to `wrapper` as its first * argument. Any additional arguments provided to the function are appended * to those provided to the `wrapper`. The wrapper is invoked with the `this` * binding of the created function. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {*} value The value to wrap. * @param {Function} [wrapper=identity] The wrapper function. * @returns {Function} Returns the new function. * @example * * var p = _.wrap(_.escape, function(func, text) { * return '

' + func(text) + '

'; * }); * * p('fred, barney, & pebbles'); * // => '

fred, barney, & pebbles

' */ function wrap(value, wrapper) { return partial(castFunction(wrapper), value); } /*------------------------------------------------------------------------*/ /** * Casts `value` as an array if it's not one. * * @static * @memberOf _ * @since 4.4.0 * @category Lang * @param {*} value The value to inspect. * @returns {Array} Returns the cast array. * @example * * _.castArray(1); * // => [1] * * _.castArray({ 'a': 1 }); * // => [{ 'a': 1 }] * * _.castArray('abc'); * // => ['abc'] * * _.castArray(null); * // => [null] * * _.castArray(undefined); * // => [undefined] * * _.castArray(); * // => [] * * var array = [1, 2, 3]; * console.log(_.castArray(array) === array); * // => true */ function castArray() { if (!arguments.length) { return []; } var value = arguments[0]; return isArray(value) ? value : [value]; } /** * Creates a shallow clone of `value`. * * **Note:** This method is loosely based on the * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) * and supports cloning arrays, array buffers, booleans, date objects, maps, * numbers, `Object` objects, regexes, sets, strings, symbols, and typed * arrays. The own enumerable properties of `arguments` objects are cloned * as plain objects. An empty object is returned for uncloneable values such * as error objects, functions, DOM nodes, and WeakMaps. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to clone. * @returns {*} Returns the cloned value. * @see _.cloneDeep * @example * * var objects = [{ 'a': 1 }, { 'b': 2 }]; * * var shallow = _.clone(objects); * console.log(shallow[0] === objects[0]); * // => true */ function clone(value) { return baseClone(value, CLONE_SYMBOLS_FLAG); } /** * This method is like `_.clone` except that it accepts `customizer` which * is invoked to produce the cloned value. If `customizer` returns `undefined`, * cloning is handled by the method instead. The `customizer` is invoked with * up to four arguments; (value [, index|key, object, stack]). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to clone. * @param {Function} [customizer] The function to customize cloning. * @returns {*} Returns the cloned value. * @see _.cloneDeepWith * @example * * function customizer(value) { * if (_.isElement(value)) { * return value.cloneNode(false); * } * } * * var el = _.cloneWith(document.body, customizer); * * console.log(el === document.body); * // => false * console.log(el.nodeName); * // => 'BODY' * console.log(el.childNodes.length); * // => 0 */ function cloneWith(value, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined$1; return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); } /** * This method is like `_.clone` except that it recursively clones `value`. * * @static * @memberOf _ * @since 1.0.0 * @category Lang * @param {*} value The value to recursively clone. * @returns {*} Returns the deep cloned value. * @see _.clone * @example * * var objects = [{ 'a': 1 }, { 'b': 2 }]; * * var deep = _.cloneDeep(objects); * console.log(deep[0] === objects[0]); * // => false */ function cloneDeep(value) { return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); } /** * This method is like `_.cloneWith` except that it recursively clones `value`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to recursively clone. * @param {Function} [customizer] The function to customize cloning. * @returns {*} Returns the deep cloned value. * @see _.cloneWith * @example * * function customizer(value) { * if (_.isElement(value)) { * return value.cloneNode(true); * } * } * * var el = _.cloneDeepWith(document.body, customizer); * * console.log(el === document.body); * // => false * console.log(el.nodeName); * // => 'BODY' * console.log(el.childNodes.length); * // => 20 */ function cloneDeepWith(value, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined$1; return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); } /** * Checks if `object` conforms to `source` by invoking the predicate * properties of `source` with the corresponding property values of `object`. * * **Note:** This method is equivalent to `_.conforms` when `source` is * partially applied. * * @static * @memberOf _ * @since 4.14.0 * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property predicates to conform to. * @returns {boolean} Returns `true` if `object` conforms, else `false`. * @example * * var object = { 'a': 1, 'b': 2 }; * * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); * // => true * * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); * // => false */ function conformsTo(object, source) { return source == null || baseConformsTo(object, source, keys(source)); } /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.eq(object, object); * // => true * * _.eq(object, other); * // => false * * _.eq('a', 'a'); * // => true * * _.eq('a', Object('a')); * // => false * * _.eq(NaN, NaN); * // => true */ function eq(value, other) { return value === other || (value !== value && other !== other); } /** * Checks if `value` is greater than `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is greater than `other`, * else `false`. * @see _.lt * @example * * _.gt(3, 1); * // => true * * _.gt(3, 3); * // => false * * _.gt(1, 3); * // => false */ var gt = createRelationalOperation(baseGt); /** * Checks if `value` is greater than or equal to `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is greater than or equal to * `other`, else `false`. * @see _.lte * @example * * _.gte(3, 1); * // => true * * _.gte(3, 3); * // => true * * _.gte(1, 3); * // => false */ var gte = createRelationalOperation(function(value, other) { return value >= other; }); /** * Checks if `value` is likely an `arguments` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, * else `false`. * @example * * _.isArguments(function() { return arguments; }()); * // => true * * _.isArguments([1, 2, 3]); * // => false */ var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); }; /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ var isArray = Array.isArray; /** * Checks if `value` is classified as an `ArrayBuffer` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. * @example * * _.isArrayBuffer(new ArrayBuffer(2)); * // => true * * _.isArrayBuffer(new Array(2)); * // => false */ var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * _.isArrayLike([1, 2, 3]); * // => true * * _.isArrayLike(document.body.children); * // => true * * _.isArrayLike('abc'); * // => true * * _.isArrayLike(_.noop); * // => false */ function isArrayLike(value) { return value != null && isLength(value.length) && !isFunction(value); } /** * This method is like `_.isArrayLike` except that it also checks if `value` * is an object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array-like object, * else `false`. * @example * * _.isArrayLikeObject([1, 2, 3]); * // => true * * _.isArrayLikeObject(document.body.children); * // => true * * _.isArrayLikeObject('abc'); * // => false * * _.isArrayLikeObject(_.noop); * // => false */ function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value); } /** * Checks if `value` is classified as a boolean primitive or object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. * @example * * _.isBoolean(false); * // => true * * _.isBoolean(null); * // => false */ function isBoolean(value) { return value === true || value === false || (isObjectLike(value) && baseGetTag(value) == boolTag); } /** * Checks if `value` is a buffer. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. * @example * * _.isBuffer(new Buffer(2)); * // => true * * _.isBuffer(new Uint8Array(2)); * // => false */ var isBuffer = nativeIsBuffer || stubFalse; /** * Checks if `value` is classified as a `Date` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a date object, else `false`. * @example * * _.isDate(new Date); * // => true * * _.isDate('Mon April 23 2012'); * // => false */ var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; /** * Checks if `value` is likely a DOM element. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. * @example * * _.isElement(document.body); * // => true * * _.isElement(''); * // => false */ function isElement(value) { return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); } /** * Checks if `value` is an empty object, collection, map, or set. * * Objects are considered empty if they have no own enumerable string keyed * properties. * * Array-like values such as `arguments` objects, arrays, buffers, strings, or * jQuery-like collections are considered empty if they have a `length` of `0`. * Similarly, maps and sets are considered empty if they have a `size` of `0`. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is empty, else `false`. * @example * * _.isEmpty(null); * // => true * * _.isEmpty(true); * // => true * * _.isEmpty(1); * // => true * * _.isEmpty([1, 2, 3]); * // => false * * _.isEmpty({ 'a': 1 }); * // => false */ function isEmpty(value) { if (value == null) { return true; } if (isArrayLike(value) && (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || isBuffer(value) || isTypedArray(value) || isArguments(value))) { return !value.length; } var tag = getTag(value); if (tag == mapTag || tag == setTag) { return !value.size; } if (isPrototype(value)) { return !baseKeys(value).length; } for (var key in value) { if (hasOwnProperty.call(value, key)) { return false; } } return true; } /** * Performs a deep comparison between two values to determine if they are * equivalent. * * **Note:** This method supports comparing arrays, array buffers, booleans, * date objects, error objects, maps, numbers, `Object` objects, regexes, * sets, strings, symbols, and typed arrays. `Object` objects are compared * by their own, not inherited, enumerable properties. Functions and DOM * nodes are compared by strict equality, i.e. `===`. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.isEqual(object, other); * // => true * * object === other; * // => false */ function isEqual(value, other) { return baseIsEqual(value, other); } /** * This method is like `_.isEqual` except that it accepts `customizer` which * is invoked to compare values. If `customizer` returns `undefined`, comparisons * are handled by the method instead. The `customizer` is invoked with up to * six arguments: (objValue, othValue [, index|key, object, other, stack]). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @param {Function} [customizer] The function to customize comparisons. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * function isGreeting(value) { * return /^h(?:i|ello)$/.test(value); * } * * function customizer(objValue, othValue) { * if (isGreeting(objValue) && isGreeting(othValue)) { * return true; * } * } * * var array = ['hello', 'goodbye']; * var other = ['hi', 'goodbye']; * * _.isEqualWith(array, other, customizer); * // => true */ function isEqualWith(value, other, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined$1; var result = customizer ? customizer(value, other) : undefined$1; return result === undefined$1 ? baseIsEqual(value, other, undefined$1, customizer) : !!result; } /** * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, * `SyntaxError`, `TypeError`, or `URIError` object. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an error object, else `false`. * @example * * _.isError(new Error); * // => true * * _.isError(Error); * // => false */ function isError(value) { if (!isObjectLike(value)) { return false; } var tag = baseGetTag(value); return tag == errorTag || tag == domExcTag || (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)); } /** * Checks if `value` is a finite primitive number. * * **Note:** This method is based on * [`Number.isFinite`](https://mdn.io/Number/isFinite). * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. * @example * * _.isFinite(3); * // => true * * _.isFinite(Number.MIN_VALUE); * // => true * * _.isFinite(Infinity); * // => false * * _.isFinite('3'); * // => false */ function isFinite(value) { return typeof value == 'number' && nativeIsFinite(value); } /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { if (!isObject(value)) { return false; } // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 9 which returns 'object' for typed arrays and other constructors. var tag = baseGetTag(value); return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; } /** * Checks if `value` is an integer. * * **Note:** This method is based on * [`Number.isInteger`](https://mdn.io/Number/isInteger). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an integer, else `false`. * @example * * _.isInteger(3); * // => true * * _.isInteger(Number.MIN_VALUE); * // => false * * _.isInteger(Infinity); * // => false * * _.isInteger('3'); * // => false */ function isInteger(value) { return typeof value == 'number' && value == toInteger(value); } /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function'); } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return value != null && typeof value == 'object'; } /** * Checks if `value` is classified as a `Map` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a map, else `false`. * @example * * _.isMap(new Map); * // => true * * _.isMap(new WeakMap); * // => false */ var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; /** * Performs a partial deep comparison between `object` and `source` to * determine if `object` contains equivalent property values. * * **Note:** This method is equivalent to `_.matches` when `source` is * partially applied. * * Partial comparisons will match empty array and empty object `source` * values against any array or object value, respectively. See `_.isEqual` * for a list of supported value comparisons. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. * @returns {boolean} Returns `true` if `object` is a match, else `false`. * @example * * var object = { 'a': 1, 'b': 2 }; * * _.isMatch(object, { 'b': 2 }); * // => true * * _.isMatch(object, { 'b': 1 }); * // => false */ function isMatch(object, source) { return object === source || baseIsMatch(object, source, getMatchData(source)); } /** * This method is like `_.isMatch` except that it accepts `customizer` which * is invoked to compare values. If `customizer` returns `undefined`, comparisons * are handled by the method instead. The `customizer` is invoked with five * arguments: (objValue, srcValue, index|key, object, source). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. * @param {Function} [customizer] The function to customize comparisons. * @returns {boolean} Returns `true` if `object` is a match, else `false`. * @example * * function isGreeting(value) { * return /^h(?:i|ello)$/.test(value); * } * * function customizer(objValue, srcValue) { * if (isGreeting(objValue) && isGreeting(srcValue)) { * return true; * } * } * * var object = { 'greeting': 'hello' }; * var source = { 'greeting': 'hi' }; * * _.isMatchWith(object, source, customizer); * // => true */ function isMatchWith(object, source, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined$1; return baseIsMatch(object, source, getMatchData(source), customizer); } /** * Checks if `value` is `NaN`. * * **Note:** This method is based on * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for * `undefined` and other non-number values. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. * @example * * _.isNaN(NaN); * // => true * * _.isNaN(new Number(NaN)); * // => true * * isNaN(undefined); * // => true * * _.isNaN(undefined); * // => false */ function isNaN(value) { // An `NaN` primitive is the only value that is not equal to itself. // Perform the `toStringTag` check first to avoid errors with some // ActiveX objects in IE. return isNumber(value) && value != +value; } /** * Checks if `value` is a pristine native function. * * **Note:** This method can't reliably detect native functions in the presence * of the core-js package because core-js circumvents this kind of detection. * Despite multiple requests, the core-js maintainer has made it clear: any * attempt to fix the detection will be obstructed. As a result, we're left * with little choice but to throw an error. Unfortunately, this also affects * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), * which rely on core-js. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. * @example * * _.isNative(Array.prototype.push); * // => true * * _.isNative(_); * // => false */ function isNative(value) { if (isMaskable(value)) { throw new Error(CORE_ERROR_TEXT); } return baseIsNative(value); } /** * Checks if `value` is `null`. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `null`, else `false`. * @example * * _.isNull(null); * // => true * * _.isNull(void 0); * // => false */ function isNull(value) { return value === null; } /** * Checks if `value` is `null` or `undefined`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is nullish, else `false`. * @example * * _.isNil(null); * // => true * * _.isNil(void 0); * // => true * * _.isNil(NaN); * // => false */ function isNil(value) { return value == null; } /** * Checks if `value` is classified as a `Number` primitive or object. * * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are * classified as numbers, use the `_.isFinite` method. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a number, else `false`. * @example * * _.isNumber(3); * // => true * * _.isNumber(Number.MIN_VALUE); * // => true * * _.isNumber(Infinity); * // => true * * _.isNumber('3'); * // => false */ function isNumber(value) { return typeof value == 'number' || (isObjectLike(value) && baseGetTag(value) == numberTag); } /** * Checks if `value` is a plain object, that is, an object created by the * `Object` constructor or one with a `[[Prototype]]` of `null`. * * @static * @memberOf _ * @since 0.8.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. * @example * * function Foo() { * this.a = 1; * } * * _.isPlainObject(new Foo); * // => false * * _.isPlainObject([1, 2, 3]); * // => false * * _.isPlainObject({ 'x': 0, 'y': 0 }); * // => true * * _.isPlainObject(Object.create(null)); * // => true */ function isPlainObject(value) { if (!isObjectLike(value) || baseGetTag(value) != objectTag) { return false; } var proto = getPrototype(value); if (proto === null) { return true; } var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString; } /** * Checks if `value` is classified as a `RegExp` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. * @example * * _.isRegExp(/abc/); * // => true * * _.isRegExp('/abc/'); * // => false */ var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; /** * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 * double precision number which isn't the result of a rounded unsafe integer. * * **Note:** This method is based on * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. * @example * * _.isSafeInteger(3); * // => true * * _.isSafeInteger(Number.MIN_VALUE); * // => false * * _.isSafeInteger(Infinity); * // => false * * _.isSafeInteger('3'); * // => false */ function isSafeInteger(value) { return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is classified as a `Set` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a set, else `false`. * @example * * _.isSet(new Set); * // => true * * _.isSet(new WeakSet); * // => false */ var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; /** * Checks if `value` is classified as a `String` primitive or object. * * @static * @since 0.1.0 * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a string, else `false`. * @example * * _.isString('abc'); * // => true * * _.isString(1); * // => false */ function isString(value) { return typeof value == 'string' || (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag); } /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ function isSymbol(value) { return typeof value == 'symbol' || (isObjectLike(value) && baseGetTag(value) == symbolTag); } /** * Checks if `value` is classified as a typed array. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. * @example * * _.isTypedArray(new Uint8Array); * // => true * * _.isTypedArray([]); * // => false */ var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; /** * Checks if `value` is `undefined`. * * @static * @since 0.1.0 * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. * @example * * _.isUndefined(void 0); * // => true * * _.isUndefined(null); * // => false */ function isUndefined(value) { return value === undefined$1; } /** * Checks if `value` is classified as a `WeakMap` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. * @example * * _.isWeakMap(new WeakMap); * // => true * * _.isWeakMap(new Map); * // => false */ function isWeakMap(value) { return isObjectLike(value) && getTag(value) == weakMapTag; } /** * Checks if `value` is classified as a `WeakSet` object. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. * @example * * _.isWeakSet(new WeakSet); * // => true * * _.isWeakSet(new Set); * // => false */ function isWeakSet(value) { return isObjectLike(value) && baseGetTag(value) == weakSetTag; } /** * Checks if `value` is less than `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is less than `other`, * else `false`. * @see _.gt * @example * * _.lt(1, 3); * // => true * * _.lt(3, 3); * // => false * * _.lt(3, 1); * // => false */ var lt = createRelationalOperation(baseLt); /** * Checks if `value` is less than or equal to `other`. * * @static * @memberOf _ * @since 3.9.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if `value` is less than or equal to * `other`, else `false`. * @see _.gte * @example * * _.lte(1, 3); * // => true * * _.lte(3, 3); * // => true * * _.lte(3, 1); * // => false */ var lte = createRelationalOperation(function(value, other) { return value <= other; }); /** * Converts `value` to an array. * * @static * @since 0.1.0 * @memberOf _ * @category Lang * @param {*} value The value to convert. * @returns {Array} Returns the converted array. * @example * * _.toArray({ 'a': 1, 'b': 2 }); * // => [1, 2] * * _.toArray('abc'); * // => ['a', 'b', 'c'] * * _.toArray(1); * // => [] * * _.toArray(null); * // => [] */ function toArray(value) { if (!value) { return []; } if (isArrayLike(value)) { return isString(value) ? stringToArray(value) : copyArray(value); } if (symIterator && value[symIterator]) { return iteratorToArray(value[symIterator]()); } var tag = getTag(value), func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values); return func(value); } /** * Converts `value` to a finite number. * * @static * @memberOf _ * @since 4.12.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted number. * @example * * _.toFinite(3.2); * // => 3.2 * * _.toFinite(Number.MIN_VALUE); * // => 5e-324 * * _.toFinite(Infinity); * // => 1.7976931348623157e+308 * * _.toFinite('3.2'); * // => 3.2 */ function toFinite(value) { if (!value) { return value === 0 ? value : 0; } value = toNumber(value); if (value === INFINITY || value === -INFINITY) { var sign = (value < 0 ? -1 : 1); return sign * MAX_INTEGER; } return value === value ? value : 0; } /** * Converts `value` to an integer. * * **Note:** This method is loosely based on * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted integer. * @example * * _.toInteger(3.2); * // => 3 * * _.toInteger(Number.MIN_VALUE); * // => 0 * * _.toInteger(Infinity); * // => 1.7976931348623157e+308 * * _.toInteger('3.2'); * // => 3 */ function toInteger(value) { var result = toFinite(value), remainder = result % 1; return result === result ? (remainder ? result - remainder : result) : 0; } /** * Converts `value` to an integer suitable for use as the length of an * array-like object. * * **Note:** This method is based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted integer. * @example * * _.toLength(3.2); * // => 3 * * _.toLength(Number.MIN_VALUE); * // => 0 * * _.toLength(Infinity); * // => 4294967295 * * _.toLength('3.2'); * // => 3 */ function toLength(value) { return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; } /** * Converts `value` to a number. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {number} Returns the number. * @example * * _.toNumber(3.2); * // => 3.2 * * _.toNumber(Number.MIN_VALUE); * // => 5e-324 * * _.toNumber(Infinity); * // => Infinity * * _.toNumber('3.2'); * // => 3.2 */ function toNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol(value)) { return NAN; } if (isObject(value)) { var other = typeof value.valueOf == 'function' ? value.valueOf() : value; value = isObject(other) ? (other + '') : other; } if (typeof value != 'string') { return value === 0 ? value : +value; } value = value.replace(reTrim, ''); var isBinary = reIsBinary.test(value); return (isBinary || reIsOctal.test(value)) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : (reIsBadHex.test(value) ? NAN : +value); } /** * Converts `value` to a plain object flattening inherited enumerable string * keyed properties of `value` to own properties of the plain object. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to convert. * @returns {Object} Returns the converted plain object. * @example * * function Foo() { * this.b = 2; * } * * Foo.prototype.c = 3; * * _.assign({ 'a': 1 }, new Foo); * // => { 'a': 1, 'b': 2 } * * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); * // => { 'a': 1, 'b': 2, 'c': 3 } */ function toPlainObject(value) { return copyObject(value, keysIn(value)); } /** * Converts `value` to a safe integer. A safe integer can be compared and * represented correctly. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {number} Returns the converted integer. * @example * * _.toSafeInteger(3.2); * // => 3 * * _.toSafeInteger(Number.MIN_VALUE); * // => 0 * * _.toSafeInteger(Infinity); * // => 9007199254740991 * * _.toSafeInteger('3.2'); * // => 3 */ function toSafeInteger(value) { return value ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) : (value === 0 ? value : 0); } /** * Converts `value` to a string. An empty string is returned for `null` * and `undefined` values. The sign of `-0` is preserved. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to convert. * @returns {string} Returns the converted string. * @example * * _.toString(null); * // => '' * * _.toString(-0); * // => '-0' * * _.toString([1, 2, 3]); * // => '1,2,3' */ function toString(value) { return value == null ? '' : baseToString(value); } /*------------------------------------------------------------------------*/ /** * Assigns own enumerable string keyed properties of source objects to the * destination object. Source objects are applied from left to right. * Subsequent sources overwrite property assignments of previous sources. * * **Note:** This method mutates `object` and is loosely based on * [`Object.assign`](https://mdn.io/Object/assign). * * @static * @memberOf _ * @since 0.10.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.assignIn * @example * * function Foo() { * this.a = 1; * } * * function Bar() { * this.c = 3; * } * * Foo.prototype.b = 2; * Bar.prototype.d = 4; * * _.assign({ 'a': 0 }, new Foo, new Bar); * // => { 'a': 1, 'c': 3 } */ var assign = createAssigner(function(object, source) { if (isPrototype(source) || isArrayLike(source)) { copyObject(source, keys(source), object); return; } for (var key in source) { if (hasOwnProperty.call(source, key)) { assignValue(object, key, source[key]); } } }); /** * This method is like `_.assign` except that it iterates over own and * inherited source properties. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @alias extend * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.assign * @example * * function Foo() { * this.a = 1; * } * * function Bar() { * this.c = 3; * } * * Foo.prototype.b = 2; * Bar.prototype.d = 4; * * _.assignIn({ 'a': 0 }, new Foo, new Bar); * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } */ var assignIn = createAssigner(function(object, source) { copyObject(source, keysIn(source), object); }); /** * This method is like `_.assignIn` except that it accepts `customizer` * which is invoked to produce the assigned values. If `customizer` returns * `undefined`, assignment is handled by the method instead. The `customizer` * is invoked with five arguments: (objValue, srcValue, key, object, source). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @alias extendWith * @category Object * @param {Object} object The destination object. * @param {...Object} sources The source objects. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @see _.assignWith * @example * * function customizer(objValue, srcValue) { * return _.isUndefined(objValue) ? srcValue : objValue; * } * * var defaults = _.partialRight(_.assignInWith, customizer); * * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); * // => { 'a': 1, 'b': 2 } */ var assignInWith = createAssigner(function(object, source, srcIndex, customizer) { copyObject(source, keysIn(source), object, customizer); }); /** * This method is like `_.assign` except that it accepts `customizer` * which is invoked to produce the assigned values. If `customizer` returns * `undefined`, assignment is handled by the method instead. The `customizer` * is invoked with five arguments: (objValue, srcValue, key, object, source). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The destination object. * @param {...Object} sources The source objects. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @see _.assignInWith * @example * * function customizer(objValue, srcValue) { * return _.isUndefined(objValue) ? srcValue : objValue; * } * * var defaults = _.partialRight(_.assignWith, customizer); * * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); * // => { 'a': 1, 'b': 2 } */ var assignWith = createAssigner(function(object, source, srcIndex, customizer) { copyObject(source, keys(source), object, customizer); }); /** * Creates an array of values corresponding to `paths` of `object`. * * @static * @memberOf _ * @since 1.0.0 * @category Object * @param {Object} object The object to iterate over. * @param {...(string|string[])} [paths] The property paths to pick. * @returns {Array} Returns the picked values. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; * * _.at(object, ['a[0].b.c', 'a[1]']); * // => [3, 4] */ var at = flatRest(baseAt); /** * Creates an object that inherits from the `prototype` object. If a * `properties` object is given, its own enumerable string keyed properties * are assigned to the created object. * * @static * @memberOf _ * @since 2.3.0 * @category Object * @param {Object} prototype The object to inherit from. * @param {Object} [properties] The properties to assign to the object. * @returns {Object} Returns the new object. * @example * * function Shape() { * this.x = 0; * this.y = 0; * } * * function Circle() { * Shape.call(this); * } * * Circle.prototype = _.create(Shape.prototype, { * 'constructor': Circle * }); * * var circle = new Circle; * circle instanceof Circle; * // => true * * circle instanceof Shape; * // => true */ function create(prototype, properties) { var result = baseCreate(prototype); return properties == null ? result : baseAssign(result, properties); } /** * Assigns own and inherited enumerable string keyed properties of source * objects to the destination object for all destination properties that * resolve to `undefined`. Source objects are applied from left to right. * Once a property is set, additional values of the same property are ignored. * * **Note:** This method mutates `object`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.defaultsDeep * @example * * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); * // => { 'a': 1, 'b': 2 } */ var defaults = baseRest(function(object, sources) { object = Object(object); var index = -1; var length = sources.length; var guard = length > 2 ? sources[2] : undefined$1; if (guard && isIterateeCall(sources[0], sources[1], guard)) { length = 1; } while (++index < length) { var source = sources[index]; var props = keysIn(source); var propsIndex = -1; var propsLength = props.length; while (++propsIndex < propsLength) { var key = props[propsIndex]; var value = object[key]; if (value === undefined$1 || (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) { object[key] = source[key]; } } } return object; }); /** * This method is like `_.defaults` except that it recursively assigns * default properties. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 3.10.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @see _.defaults * @example * * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); * // => { 'a': { 'b': 2, 'c': 3 } } */ var defaultsDeep = baseRest(function(args) { args.push(undefined$1, customDefaultsMerge); return apply(mergeWith, undefined$1, args); }); /** * This method is like `_.find` except that it returns the key of the first * element `predicate` returns truthy for instead of the element itself. * * @static * @memberOf _ * @since 1.1.0 * @category Object * @param {Object} object The object to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {string|undefined} Returns the key of the matched element, * else `undefined`. * @example * * var users = { * 'barney': { 'age': 36, 'active': true }, * 'fred': { 'age': 40, 'active': false }, * 'pebbles': { 'age': 1, 'active': true } * }; * * _.findKey(users, function(o) { return o.age < 40; }); * // => 'barney' (iteration order is not guaranteed) * * // The `_.matches` iteratee shorthand. * _.findKey(users, { 'age': 1, 'active': true }); * // => 'pebbles' * * // The `_.matchesProperty` iteratee shorthand. * _.findKey(users, ['active', false]); * // => 'fred' * * // The `_.property` iteratee shorthand. * _.findKey(users, 'active'); * // => 'barney' */ function findKey(object, predicate) { return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); } /** * This method is like `_.findKey` except that it iterates over elements of * a collection in the opposite order. * * @static * @memberOf _ * @since 2.0.0 * @category Object * @param {Object} object The object to inspect. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {string|undefined} Returns the key of the matched element, * else `undefined`. * @example * * var users = { * 'barney': { 'age': 36, 'active': true }, * 'fred': { 'age': 40, 'active': false }, * 'pebbles': { 'age': 1, 'active': true } * }; * * _.findLastKey(users, function(o) { return o.age < 40; }); * // => returns 'pebbles' assuming `_.findKey` returns 'barney' * * // The `_.matches` iteratee shorthand. * _.findLastKey(users, { 'age': 36, 'active': true }); * // => 'barney' * * // The `_.matchesProperty` iteratee shorthand. * _.findLastKey(users, ['active', false]); * // => 'fred' * * // The `_.property` iteratee shorthand. * _.findLastKey(users, 'active'); * // => 'pebbles' */ function findLastKey(object, predicate) { return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); } /** * Iterates over own and inherited enumerable string keyed properties of an * object and invokes `iteratee` for each property. The iteratee is invoked * with three arguments: (value, key, object). Iteratee functions may exit * iteration early by explicitly returning `false`. * * @static * @memberOf _ * @since 0.3.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forInRight * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forIn(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). */ function forIn(object, iteratee) { return object == null ? object : baseFor(object, getIteratee(iteratee, 3), keysIn); } /** * This method is like `_.forIn` except that it iterates over properties of * `object` in the opposite order. * * @static * @memberOf _ * @since 2.0.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forIn * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forInRight(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. */ function forInRight(object, iteratee) { return object == null ? object : baseForRight(object, getIteratee(iteratee, 3), keysIn); } /** * Iterates over own enumerable string keyed properties of an object and * invokes `iteratee` for each property. The iteratee is invoked with three * arguments: (value, key, object). Iteratee functions may exit iteration * early by explicitly returning `false`. * * @static * @memberOf _ * @since 0.3.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forOwnRight * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forOwn(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'a' then 'b' (iteration order is not guaranteed). */ function forOwn(object, iteratee) { return object && baseForOwn(object, getIteratee(iteratee, 3)); } /** * This method is like `_.forOwn` except that it iterates over properties of * `object` in the opposite order. * * @static * @memberOf _ * @since 2.0.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns `object`. * @see _.forOwn * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forOwnRight(new Foo, function(value, key) { * console.log(key); * }); * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. */ function forOwnRight(object, iteratee) { return object && baseForOwnRight(object, getIteratee(iteratee, 3)); } /** * Creates an array of function property names from own enumerable properties * of `object`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the function names. * @see _.functionsIn * @example * * function Foo() { * this.a = _.constant('a'); * this.b = _.constant('b'); * } * * Foo.prototype.c = _.constant('c'); * * _.functions(new Foo); * // => ['a', 'b'] */ function functions(object) { return object == null ? [] : baseFunctions(object, keys(object)); } /** * Creates an array of function property names from own and inherited * enumerable properties of `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the function names. * @see _.functions * @example * * function Foo() { * this.a = _.constant('a'); * this.b = _.constant('b'); * } * * Foo.prototype.c = _.constant('c'); * * _.functionsIn(new Foo); * // => ['a', 'b', 'c'] */ function functionsIn(object) { return object == null ? [] : baseFunctions(object, keysIn(object)); } /** * Gets the value at `path` of `object`. If the resolved value is * `undefined`, the `defaultValue` is returned in its place. * * @static * @memberOf _ * @since 3.7.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @param {*} [defaultValue] The value returned for `undefined` resolved values. * @returns {*} Returns the resolved value. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.get(object, 'a[0].b.c'); * // => 3 * * _.get(object, ['a', '0', 'b', 'c']); * // => 3 * * _.get(object, 'a.b.c', 'default'); * // => 'default' */ function get(object, path, defaultValue) { var result = object == null ? undefined$1 : baseGet(object, path); return result === undefined$1 ? defaultValue : result; } /** * Checks if `path` is a direct property of `object`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @returns {boolean} Returns `true` if `path` exists, else `false`. * @example * * var object = { 'a': { 'b': 2 } }; * var other = _.create({ 'a': _.create({ 'b': 2 }) }); * * _.has(object, 'a'); * // => true * * _.has(object, 'a.b'); * // => true * * _.has(object, ['a', 'b']); * // => true * * _.has(other, 'a'); * // => false */ function has(object, path) { return object != null && hasPath(object, path, baseHas); } /** * Checks if `path` is a direct or inherited property of `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @returns {boolean} Returns `true` if `path` exists, else `false`. * @example * * var object = _.create({ 'a': _.create({ 'b': 2 }) }); * * _.hasIn(object, 'a'); * // => true * * _.hasIn(object, 'a.b'); * // => true * * _.hasIn(object, ['a', 'b']); * // => true * * _.hasIn(object, 'b'); * // => false */ function hasIn(object, path) { return object != null && hasPath(object, path, baseHasIn); } /** * Creates an object composed of the inverted keys and values of `object`. * If `object` contains duplicate values, subsequent values overwrite * property assignments of previous values. * * @static * @memberOf _ * @since 0.7.0 * @category Object * @param {Object} object The object to invert. * @returns {Object} Returns the new inverted object. * @example * * var object = { 'a': 1, 'b': 2, 'c': 1 }; * * _.invert(object); * // => { '1': 'c', '2': 'b' } */ var invert = createInverter(function(result, value, key) { if (value != null && typeof value.toString != 'function') { value = nativeObjectToString.call(value); } result[value] = key; }, constant(identity)); /** * This method is like `_.invert` except that the inverted object is generated * from the results of running each element of `object` thru `iteratee`. The * corresponding inverted value of each inverted key is an array of keys * responsible for generating the inverted value. The iteratee is invoked * with one argument: (value). * * @static * @memberOf _ * @since 4.1.0 * @category Object * @param {Object} object The object to invert. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Object} Returns the new inverted object. * @example * * var object = { 'a': 1, 'b': 2, 'c': 1 }; * * _.invertBy(object); * // => { '1': ['a', 'c'], '2': ['b'] } * * _.invertBy(object, function(value) { * return 'group' + value; * }); * // => { 'group1': ['a', 'c'], 'group2': ['b'] } */ var invertBy = createInverter(function(result, value, key) { if (value != null && typeof value.toString != 'function') { value = nativeObjectToString.call(value); } if (hasOwnProperty.call(result, value)) { result[value].push(key); } else { result[value] = [key]; } }, getIteratee); /** * Invokes the method at `path` of `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the method to invoke. * @param {...*} [args] The arguments to invoke the method with. * @returns {*} Returns the result of the invoked method. * @example * * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; * * _.invoke(object, 'a[0].b.c.slice', 1, 3); * // => [2, 3] */ var invoke = baseRest(baseInvoke); /** * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * for more details. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keys(new Foo); * // => ['a', 'b'] (iteration order is not guaranteed) * * _.keys('hi'); * // => ['0', '1'] */ function keys(object) { return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); } /** * Creates an array of the own and inherited enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @since 3.0.0 * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keysIn(new Foo); * // => ['a', 'b', 'c'] (iteration order is not guaranteed) */ function keysIn(object) { return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); } /** * The opposite of `_.mapValues`; this method creates an object with the * same values as `object` and keys generated by running each own enumerable * string keyed property of `object` thru `iteratee`. The iteratee is invoked * with three arguments: (value, key, object). * * @static * @memberOf _ * @since 3.8.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns the new mapped object. * @see _.mapValues * @example * * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { * return key + value; * }); * // => { 'a1': 1, 'b2': 2 } */ function mapKeys(object, iteratee) { var result = {}; iteratee = getIteratee(iteratee, 3); baseForOwn(object, function(value, key, object) { baseAssignValue(result, iteratee(value, key, object), value); }); return result; } /** * Creates an object with the same keys as `object` and values generated * by running each own enumerable string keyed property of `object` thru * `iteratee`. The iteratee is invoked with three arguments: * (value, key, object). * * @static * @memberOf _ * @since 2.4.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @returns {Object} Returns the new mapped object. * @see _.mapKeys * @example * * var users = { * 'fred': { 'user': 'fred', 'age': 40 }, * 'pebbles': { 'user': 'pebbles', 'age': 1 } * }; * * _.mapValues(users, function(o) { return o.age; }); * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) * * // The `_.property` iteratee shorthand. * _.mapValues(users, 'age'); * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) */ function mapValues(object, iteratee) { var result = {}; iteratee = getIteratee(iteratee, 3); baseForOwn(object, function(value, key, object) { baseAssignValue(result, key, iteratee(value, key, object)); }); return result; } /** * This method is like `_.assign` except that it recursively merges own and * inherited enumerable string keyed properties of source objects into the * destination object. Source properties that resolve to `undefined` are * skipped if a destination value exists. Array and plain object properties * are merged recursively. Other objects and value types are overridden by * assignment. Source objects are applied from left to right. Subsequent * sources overwrite property assignments of previous sources. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 0.5.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @example * * var object = { * 'a': [{ 'b': 2 }, { 'd': 4 }] * }; * * var other = { * 'a': [{ 'c': 3 }, { 'e': 5 }] * }; * * _.merge(object, other); * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } */ var merge = createAssigner(function(object, source, srcIndex) { baseMerge(object, source, srcIndex); }); /** * This method is like `_.merge` except that it accepts `customizer` which * is invoked to produce the merged values of the destination and source * properties. If `customizer` returns `undefined`, merging is handled by the * method instead. The `customizer` is invoked with six arguments: * (objValue, srcValue, key, object, source, stack). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The destination object. * @param {...Object} sources The source objects. * @param {Function} customizer The function to customize assigned values. * @returns {Object} Returns `object`. * @example * * function customizer(objValue, srcValue) { * if (_.isArray(objValue)) { * return objValue.concat(srcValue); * } * } * * var object = { 'a': [1], 'b': [2] }; * var other = { 'a': [3], 'b': [4] }; * * _.mergeWith(object, other, customizer); * // => { 'a': [1, 3], 'b': [2, 4] } */ var mergeWith = createAssigner(function(object, source, srcIndex, customizer) { baseMerge(object, source, srcIndex, customizer); }); /** * The opposite of `_.pick`; this method creates an object composed of the * own and inherited enumerable property paths of `object` that are not omitted. * * **Note:** This method is considerably slower than `_.pick`. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The source object. * @param {...(string|string[])} [paths] The property paths to omit. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.omit(object, ['a', 'c']); * // => { 'b': '2' } */ var omit = flatRest(function(object, paths) { var result = {}; if (object == null) { return result; } var isDeep = false; paths = arrayMap(paths, function(path) { path = castPath(path, object); isDeep || (isDeep = path.length > 1); return path; }); copyObject(object, getAllKeysIn(object), result); if (isDeep) { result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); } var length = paths.length; while (length--) { baseUnset(result, paths[length]); } return result; }); /** * The opposite of `_.pickBy`; this method creates an object composed of * the own and inherited enumerable string keyed properties of `object` that * `predicate` doesn't return truthy for. The predicate is invoked with two * arguments: (value, key). * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The source object. * @param {Function} [predicate=_.identity] The function invoked per property. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.omitBy(object, _.isNumber); * // => { 'b': '2' } */ function omitBy(object, predicate) { return pickBy(object, negate(getIteratee(predicate))); } /** * Creates an object composed of the picked `object` properties. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The source object. * @param {...(string|string[])} [paths] The property paths to pick. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.pick(object, ['a', 'c']); * // => { 'a': 1, 'c': 3 } */ var pick = flatRest(function(object, paths) { return object == null ? {} : basePick(object, paths); }); /** * Creates an object composed of the `object` properties `predicate` returns * truthy for. The predicate is invoked with two arguments: (value, key). * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The source object. * @param {Function} [predicate=_.identity] The function invoked per property. * @returns {Object} Returns the new object. * @example * * var object = { 'a': 1, 'b': '2', 'c': 3 }; * * _.pickBy(object, _.isNumber); * // => { 'a': 1, 'c': 3 } */ function pickBy(object, predicate) { if (object == null) { return {}; } var props = arrayMap(getAllKeysIn(object), function(prop) { return [prop]; }); predicate = getIteratee(predicate); return basePickBy(object, props, function(value, path) { return predicate(value, path[0]); }); } /** * This method is like `_.get` except that if the resolved value is a * function it's invoked with the `this` binding of its parent object and * its result is returned. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the property to resolve. * @param {*} [defaultValue] The value returned for `undefined` resolved values. * @returns {*} Returns the resolved value. * @example * * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; * * _.result(object, 'a[0].b.c1'); * // => 3 * * _.result(object, 'a[0].b.c2'); * // => 4 * * _.result(object, 'a[0].b.c3', 'default'); * // => 'default' * * _.result(object, 'a[0].b.c3', _.constant('default')); * // => 'default' */ function result(object, path, defaultValue) { path = castPath(path, object); var index = -1, length = path.length; // Ensure the loop is entered when path is empty. if (!length) { length = 1; object = undefined$1; } while (++index < length) { var value = object == null ? undefined$1 : object[toKey(path[index])]; if (value === undefined$1) { index = length; value = defaultValue; } object = isFunction(value) ? value.call(object) : value; } return object; } /** * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, * it's created. Arrays are created for missing index properties while objects * are created for all other missing properties. Use `_.setWith` to customize * `path` creation. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 3.7.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @returns {Object} Returns `object`. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.set(object, 'a[0].b.c', 4); * console.log(object.a[0].b.c); * // => 4 * * _.set(object, ['x', '0', 'y', 'z'], 5); * console.log(object.x[0].y.z); * // => 5 */ function set(object, path, value) { return object == null ? object : baseSet(object, path, value); } /** * This method is like `_.set` except that it accepts `customizer` which is * invoked to produce the objects of `path`. If `customizer` returns `undefined` * path creation is handled by the method instead. The `customizer` is invoked * with three arguments: (nsValue, key, nsObject). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @example * * var object = {}; * * _.setWith(object, '[0][1]', 'a', Object); * // => { '0': { '1': 'a' } } */ function setWith(object, path, value, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined$1; return object == null ? object : baseSet(object, path, value, customizer); } /** * Creates an array of own enumerable string keyed-value pairs for `object` * which can be consumed by `_.fromPairs`. If `object` is a map or set, its * entries are returned. * * @static * @memberOf _ * @since 4.0.0 * @alias entries * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the key-value pairs. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.toPairs(new Foo); * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) */ var toPairs = createToPairs(keys); /** * Creates an array of own and inherited enumerable string keyed-value pairs * for `object` which can be consumed by `_.fromPairs`. If `object` is a map * or set, its entries are returned. * * @static * @memberOf _ * @since 4.0.0 * @alias entriesIn * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the key-value pairs. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.toPairsIn(new Foo); * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) */ var toPairsIn = createToPairs(keysIn); /** * An alternative to `_.reduce`; this method transforms `object` to a new * `accumulator` object which is the result of running each of its own * enumerable string keyed properties thru `iteratee`, with each invocation * potentially mutating the `accumulator` object. If `accumulator` is not * provided, a new object with the same `[[Prototype]]` will be used. The * iteratee is invoked with four arguments: (accumulator, value, key, object). * Iteratee functions may exit iteration early by explicitly returning `false`. * * @static * @memberOf _ * @since 1.3.0 * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The custom accumulator value. * @returns {*} Returns the accumulated value. * @example * * _.transform([2, 3, 4], function(result, n) { * result.push(n *= n); * return n % 2 == 0; * }, []); * // => [4, 9] * * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { * (result[value] || (result[value] = [])).push(key); * }, {}); * // => { '1': ['a', 'c'], '2': ['b'] } */ function transform(object, iteratee, accumulator) { var isArr = isArray(object), isArrLike = isArr || isBuffer(object) || isTypedArray(object); iteratee = getIteratee(iteratee, 4); if (accumulator == null) { var Ctor = object && object.constructor; if (isArrLike) { accumulator = isArr ? new Ctor : []; } else if (isObject(object)) { accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; } else { accumulator = {}; } } (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) { return iteratee(accumulator, value, index, object); }); return accumulator; } /** * Removes the property at `path` of `object`. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to unset. * @returns {boolean} Returns `true` if the property is deleted, else `false`. * @example * * var object = { 'a': [{ 'b': { 'c': 7 } }] }; * _.unset(object, 'a[0].b.c'); * // => true * * console.log(object); * // => { 'a': [{ 'b': {} }] }; * * _.unset(object, ['a', '0', 'b', 'c']); * // => true * * console.log(object); * // => { 'a': [{ 'b': {} }] }; */ function unset(object, path) { return object == null ? true : baseUnset(object, path); } /** * This method is like `_.set` except that accepts `updater` to produce the * value to set. Use `_.updateWith` to customize `path` creation. The `updater` * is invoked with one argument: (value). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.6.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {Function} updater The function to produce the updated value. * @returns {Object} Returns `object`. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.update(object, 'a[0].b.c', function(n) { return n * n; }); * console.log(object.a[0].b.c); * // => 9 * * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); * console.log(object.x[0].y.z); * // => 0 */ function update(object, path, updater) { return object == null ? object : baseUpdate(object, path, castFunction(updater)); } /** * This method is like `_.update` except that it accepts `customizer` which is * invoked to produce the objects of `path`. If `customizer` returns `undefined` * path creation is handled by the method instead. The `customizer` is invoked * with three arguments: (nsValue, key, nsObject). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.6.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {Function} updater The function to produce the updated value. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @example * * var object = {}; * * _.updateWith(object, '[0][1]', _.constant('a'), Object); * // => { '0': { '1': 'a' } } */ function updateWith(object, path, updater, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined$1; return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); } /** * Creates an array of the own enumerable string keyed property values of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property values. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.values(new Foo); * // => [1, 2] (iteration order is not guaranteed) * * _.values('hi'); * // => ['h', 'i'] */ function values(object) { return object == null ? [] : baseValues(object, keys(object)); } /** * Creates an array of the own and inherited enumerable string keyed property * values of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @since 3.0.0 * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property values. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.valuesIn(new Foo); * // => [1, 2, 3] (iteration order is not guaranteed) */ function valuesIn(object) { return object == null ? [] : baseValues(object, keysIn(object)); } /*------------------------------------------------------------------------*/ /** * Clamps `number` within the inclusive `lower` and `upper` bounds. * * @static * @memberOf _ * @since 4.0.0 * @category Number * @param {number} number The number to clamp. * @param {number} [lower] The lower bound. * @param {number} upper The upper bound. * @returns {number} Returns the clamped number. * @example * * _.clamp(-10, -5, 5); * // => -5 * * _.clamp(10, -5, 5); * // => 5 */ function clamp(number, lower, upper) { if (upper === undefined$1) { upper = lower; lower = undefined$1; } if (upper !== undefined$1) { upper = toNumber(upper); upper = upper === upper ? upper : 0; } if (lower !== undefined$1) { lower = toNumber(lower); lower = lower === lower ? lower : 0; } return baseClamp(toNumber(number), lower, upper); } /** * Checks if `n` is between `start` and up to, but not including, `end`. If * `end` is not specified, it's set to `start` with `start` then set to `0`. * If `start` is greater than `end` the params are swapped to support * negative ranges. * * @static * @memberOf _ * @since 3.3.0 * @category Number * @param {number} number The number to check. * @param {number} [start=0] The start of the range. * @param {number} end The end of the range. * @returns {boolean} Returns `true` if `number` is in the range, else `false`. * @see _.range, _.rangeRight * @example * * _.inRange(3, 2, 4); * // => true * * _.inRange(4, 8); * // => true * * _.inRange(4, 2); * // => false * * _.inRange(2, 2); * // => false * * _.inRange(1.2, 2); * // => true * * _.inRange(5.2, 4); * // => false * * _.inRange(-3, -2, -6); * // => true */ function inRange(number, start, end) { start = toFinite(start); if (end === undefined$1) { end = start; start = 0; } else { end = toFinite(end); } number = toNumber(number); return baseInRange(number, start, end); } /** * Produces a random number between the inclusive `lower` and `upper` bounds. * If only one argument is provided a number between `0` and the given number * is returned. If `floating` is `true`, or either `lower` or `upper` are * floats, a floating-point number is returned instead of an integer. * * **Note:** JavaScript follows the IEEE-754 standard for resolving * floating-point values which can produce unexpected results. * * @static * @memberOf _ * @since 0.7.0 * @category Number * @param {number} [lower=0] The lower bound. * @param {number} [upper=1] The upper bound. * @param {boolean} [floating] Specify returning a floating-point number. * @returns {number} Returns the random number. * @example * * _.random(0, 5); * // => an integer between 0 and 5 * * _.random(5); * // => also an integer between 0 and 5 * * _.random(5, true); * // => a floating-point number between 0 and 5 * * _.random(1.2, 5.2); * // => a floating-point number between 1.2 and 5.2 */ function random(lower, upper, floating) { if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { upper = floating = undefined$1; } if (floating === undefined$1) { if (typeof upper == 'boolean') { floating = upper; upper = undefined$1; } else if (typeof lower == 'boolean') { floating = lower; lower = undefined$1; } } if (lower === undefined$1 && upper === undefined$1) { lower = 0; upper = 1; } else { lower = toFinite(lower); if (upper === undefined$1) { upper = lower; lower = 0; } else { upper = toFinite(upper); } } if (lower > upper) { var temp = lower; lower = upper; upper = temp; } if (floating || lower % 1 || upper % 1) { var rand = nativeRandom(); return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper); } return baseRandom(lower, upper); } /*------------------------------------------------------------------------*/ /** * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the camel cased string. * @example * * _.camelCase('Foo Bar'); * // => 'fooBar' * * _.camelCase('--foo-bar--'); * // => 'fooBar' * * _.camelCase('__FOO_BAR__'); * // => 'fooBar' */ var camelCase = createCompounder(function(result, word, index) { word = word.toLowerCase(); return result + (index ? capitalize(word) : word); }); /** * Converts the first character of `string` to upper case and the remaining * to lower case. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to capitalize. * @returns {string} Returns the capitalized string. * @example * * _.capitalize('FRED'); * // => 'Fred' */ function capitalize(string) { return upperFirst(toString(string).toLowerCase()); } /** * Deburrs `string` by converting * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) * letters to basic Latin letters and removing * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to deburr. * @returns {string} Returns the deburred string. * @example * * _.deburr('déjà vu'); * // => 'deja vu' */ function deburr(string) { string = toString(string); return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); } /** * Checks if `string` ends with the given target string. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to inspect. * @param {string} [target] The string to search for. * @param {number} [position=string.length] The position to search up to. * @returns {boolean} Returns `true` if `string` ends with `target`, * else `false`. * @example * * _.endsWith('abc', 'c'); * // => true * * _.endsWith('abc', 'b'); * // => false * * _.endsWith('abc', 'b', 2); * // => true */ function endsWith(string, target, position) { string = toString(string); target = baseToString(target); var length = string.length; position = position === undefined$1 ? length : baseClamp(toInteger(position), 0, length); var end = position; position -= target.length; return position >= 0 && string.slice(position, end) == target; } /** * Converts the characters "&", "<", ">", '"', and "'" in `string` to their * corresponding HTML entities. * * **Note:** No other characters are escaped. To escape additional * characters use a third-party library like [_he_](https://mths.be/he). * * Though the ">" character is escaped for symmetry, characters like * ">" and "/" don't need escaping in HTML and have no special meaning * unless they're part of a tag or unquoted attribute value. See * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) * (under "semi-related fun fact") for more details. * * When working with HTML you should always * [quote attribute values](http://wonko.com/post/html-escaping) to reduce * XSS vectors. * * @static * @since 0.1.0 * @memberOf _ * @category String * @param {string} [string=''] The string to escape. * @returns {string} Returns the escaped string. * @example * * _.escape('fred, barney, & pebbles'); * // => 'fred, barney, & pebbles' */ function escape(string) { string = toString(string); return (string && reHasUnescapedHtml.test(string)) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string; } /** * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to escape. * @returns {string} Returns the escaped string. * @example * * _.escapeRegExp('[lodash](https://lodash.com/)'); * // => '\[lodash\]\(https://lodash\.com/\)' */ function escapeRegExp(string) { string = toString(string); return (string && reHasRegExpChar.test(string)) ? string.replace(reRegExpChar, '\\$&') : string; } /** * Converts `string` to * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the kebab cased string. * @example * * _.kebabCase('Foo Bar'); * // => 'foo-bar' * * _.kebabCase('fooBar'); * // => 'foo-bar' * * _.kebabCase('__FOO_BAR__'); * // => 'foo-bar' */ var kebabCase = createCompounder(function(result, word, index) { return result + (index ? '-' : '') + word.toLowerCase(); }); /** * Converts `string`, as space separated words, to lower case. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the lower cased string. * @example * * _.lowerCase('--Foo-Bar--'); * // => 'foo bar' * * _.lowerCase('fooBar'); * // => 'foo bar' * * _.lowerCase('__FOO_BAR__'); * // => 'foo bar' */ var lowerCase = createCompounder(function(result, word, index) { return result + (index ? ' ' : '') + word.toLowerCase(); }); /** * Converts the first character of `string` to lower case. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the converted string. * @example * * _.lowerFirst('Fred'); * // => 'fred' * * _.lowerFirst('FRED'); * // => 'fRED' */ var lowerFirst = createCaseFirst('toLowerCase'); /** * Pads `string` on the left and right sides if it's shorter than `length`. * Padding characters are truncated if they can't be evenly divided by `length`. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.pad('abc', 8); * // => ' abc ' * * _.pad('abc', 8, '_-'); * // => '_-abc_-_' * * _.pad('abc', 3); * // => 'abc' */ function pad(string, length, chars) { string = toString(string); length = toInteger(length); var strLength = length ? stringSize(string) : 0; if (!length || strLength >= length) { return string; } var mid = (length - strLength) / 2; return ( createPadding(nativeFloor(mid), chars) + string + createPadding(nativeCeil(mid), chars) ); } /** * Pads `string` on the right side if it's shorter than `length`. Padding * characters are truncated if they exceed `length`. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.padEnd('abc', 6); * // => 'abc ' * * _.padEnd('abc', 6, '_-'); * // => 'abc_-_' * * _.padEnd('abc', 3); * // => 'abc' */ function padEnd(string, length, chars) { string = toString(string); length = toInteger(length); var strLength = length ? stringSize(string) : 0; return (length && strLength < length) ? (string + createPadding(length - strLength, chars)) : string; } /** * Pads `string` on the left side if it's shorter than `length`. Padding * characters are truncated if they exceed `length`. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.padStart('abc', 6); * // => ' abc' * * _.padStart('abc', 6, '_-'); * // => '_-_abc' * * _.padStart('abc', 3); * // => 'abc' */ function padStart(string, length, chars) { string = toString(string); length = toInteger(length); var strLength = length ? stringSize(string) : 0; return (length && strLength < length) ? (createPadding(length - strLength, chars) + string) : string; } /** * Converts `string` to an integer of the specified radix. If `radix` is * `undefined` or `0`, a `radix` of `10` is used unless `value` is a * hexadecimal, in which case a `radix` of `16` is used. * * **Note:** This method aligns with the * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. * * @static * @memberOf _ * @since 1.1.0 * @category String * @param {string} string The string to convert. * @param {number} [radix=10] The radix to interpret `value` by. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {number} Returns the converted integer. * @example * * _.parseInt('08'); * // => 8 * * _.map(['6', '08', '10'], _.parseInt); * // => [6, 8, 10] */ function parseInt(string, radix, guard) { if (guard || radix == null) { radix = 0; } else if (radix) { radix = +radix; } return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); } /** * Repeats the given string `n` times. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to repeat. * @param {number} [n=1] The number of times to repeat the string. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {string} Returns the repeated string. * @example * * _.repeat('*', 3); * // => '***' * * _.repeat('abc', 2); * // => 'abcabc' * * _.repeat('abc', 0); * // => '' */ function repeat(string, n, guard) { if ((guard ? isIterateeCall(string, n, guard) : n === undefined$1)) { n = 1; } else { n = toInteger(n); } return baseRepeat(toString(string), n); } /** * Replaces matches for `pattern` in `string` with `replacement`. * * **Note:** This method is based on * [`String#replace`](https://mdn.io/String/replace). * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to modify. * @param {RegExp|string} pattern The pattern to replace. * @param {Function|string} replacement The match replacement. * @returns {string} Returns the modified string. * @example * * _.replace('Hi Fred', 'Fred', 'Barney'); * // => 'Hi Barney' */ function replace() { var args = arguments, string = toString(args[0]); return args.length < 3 ? string : string.replace(args[1], args[2]); } /** * Converts `string` to * [snake case](https://en.wikipedia.org/wiki/Snake_case). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the snake cased string. * @example * * _.snakeCase('Foo Bar'); * // => 'foo_bar' * * _.snakeCase('fooBar'); * // => 'foo_bar' * * _.snakeCase('--FOO-BAR--'); * // => 'foo_bar' */ var snakeCase = createCompounder(function(result, word, index) { return result + (index ? '_' : '') + word.toLowerCase(); }); /** * Splits `string` by `separator`. * * **Note:** This method is based on * [`String#split`](https://mdn.io/String/split). * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to split. * @param {RegExp|string} separator The separator pattern to split by. * @param {number} [limit] The length to truncate results to. * @returns {Array} Returns the string segments. * @example * * _.split('a-b-c', '-', 2); * // => ['a', 'b'] */ function split(string, separator, limit) { if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { separator = limit = undefined$1; } limit = limit === undefined$1 ? MAX_ARRAY_LENGTH : limit >>> 0; if (!limit) { return []; } string = toString(string); if (string && ( typeof separator == 'string' || (separator != null && !isRegExp(separator)) )) { separator = baseToString(separator); if (!separator && hasUnicode(string)) { return castSlice(stringToArray(string), 0, limit); } } return string.split(separator, limit); } /** * Converts `string` to * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). * * @static * @memberOf _ * @since 3.1.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the start cased string. * @example * * _.startCase('--foo-bar--'); * // => 'Foo Bar' * * _.startCase('fooBar'); * // => 'Foo Bar' * * _.startCase('__FOO_BAR__'); * // => 'FOO BAR' */ var startCase = createCompounder(function(result, word, index) { return result + (index ? ' ' : '') + upperFirst(word); }); /** * Checks if `string` starts with the given target string. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to inspect. * @param {string} [target] The string to search for. * @param {number} [position=0] The position to search from. * @returns {boolean} Returns `true` if `string` starts with `target`, * else `false`. * @example * * _.startsWith('abc', 'a'); * // => true * * _.startsWith('abc', 'b'); * // => false * * _.startsWith('abc', 'b', 1); * // => true */ function startsWith(string, target, position) { string = toString(string); position = position == null ? 0 : baseClamp(toInteger(position), 0, string.length); target = baseToString(target); return string.slice(position, position + target.length) == target; } /** * Creates a compiled template function that can interpolate data properties * in "interpolate" delimiters, HTML-escape interpolated data properties in * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data * properties may be accessed as free variables in the template. If a setting * object is given, it takes precedence over `_.templateSettings` values. * * **Note:** In the development build `_.template` utilizes * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) * for easier debugging. * * For more information on precompiling templates see * [lodash's custom builds documentation](https://lodash.com/custom-builds). * * For more information on Chrome extension sandboxes see * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). * * @static * @since 0.1.0 * @memberOf _ * @category String * @param {string} [string=''] The template string. * @param {Object} [options={}] The options object. * @param {RegExp} [options.escape=_.templateSettings.escape] * The HTML "escape" delimiter. * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] * The "evaluate" delimiter. * @param {Object} [options.imports=_.templateSettings.imports] * An object to import into the template as free variables. * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] * The "interpolate" delimiter. * @param {string} [options.sourceURL='lodash.templateSources[n]'] * The sourceURL of the compiled template. * @param {string} [options.variable='obj'] * The data object variable name. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Function} Returns the compiled template function. * @example * * // Use the "interpolate" delimiter to create a compiled template. * var compiled = _.template('hello <%= user %>!'); * compiled({ 'user': 'fred' }); * // => 'hello fred!' * * // Use the HTML "escape" delimiter to escape data property values. * var compiled = _.template('<%- value %>'); * compiled({ 'value': ' ``` ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5 - new & instanceof 实现.md ================================================ ## new new操作符的三个过程: 1. 创建一个继承自该构造函数关联的原型的实例对象 2. 将构造函数的作用域赋给新对象并执行构造函数(修改this) 3. 如果构造函数返回值位对象,返回这个对象,否则返回新对象。 ```javascript function myNew (constructor,…args) { let to = {}; to.__proto__ = constructor.prototype; // 修改原型 /* * let F = function () {} * F.prototype = constructor.prototype; * to = new F() // 用到了new… 但是性能比__proto__好 * */ let result = constructor.apply(to,args); // 尝试执行,并拿到结果 return (typeof result === 'object') ? result : to; } ``` 谈到原型链时,不得不提instanceof 操作符,该操作符用于判断对象是否在指定的原型链上,如果存在即返回true。 ## instanceof 原理实现 - 判断实例对象是否存在目标构造函数的原型中 - 移动_proto_指针 - 如果不存在,则最终会遍历完整个原型链(到Null)。 - 注意类型保护 ``` javascript /* @params {Object} child 对象(ES5中) @params {Function} parent 构造函数 - 用于匹配 child 原型链是否存在 与 parent 构造函数 关联的原型对象 */ function _instanceof (child,parent) { // es5 if(typeof child !== 'object' || child !== null) { throw Error('') } // es6 能够接受原始值的,经过一个包装,类型保护 child = new Object(child); // 防止下面获取parent.xxx出错,做一个类型保护 if(parent === null) { return false; } let prevPrototype = Object.getPrototypeOf(child) let originPrototype = parent.prototype while(true) { if(prevPrototype === null) return false if(prevPrototype === originPrototype) return true prevPfrototype = Object.getPrototypeOf(prevPfrototype) } } ``` ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5 - 你说一下你对原型的认识.md ================================================ ## ES5 - 你说一下你对原型的认识 在解释什么是原型时,我目前的思路是首先讲清楚 构造函数 - 原型对象 - 实例对象 三者之间的关系,也就是大体画出如下关系图: ![20200617215313]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200617215313.png) 紧接着使用手画的图例说一下如下几个关键概念的含义: ### prototype、constructor、[[prototype]]、\__proto__\ prototype:每创建的一个函数都有一个prototype属性,该属性是一个指针,指向该函数的原型对象 constructor:默认情况下,所有的原型对象都会获得一个指向构造函数的 **不可枚举的属性**。 [[prototype]]: ES5标准中,指向原型的指针称之为[[prototype]] \__proto__\: 在ES5中,没有提供直接访问原型的方法,因此每个浏览器厂商都提供一个访问原型的方法:\__proto\__ > 注意:这个链接是在实例对象和原型之间,而不是 实例对象和构造函数之间。 最后说一下 在原型模型中,通过原型链查找变量时的细节点: ### 细节 **遮蔽效果**: 实例对象可以访问原型的属性/方法,但不能通过实例对象重写原型的方法。如果在实例对象上重写原型属性/方法会发生**遮蔽效果**,除非直接调用xxx.prototype.xxx直接重写,这样是可以的。 **重写原型**: 当原型对象被重写时,他的constructer属性也随之被修改,指向Object的同时该属性描述符中的`[[Enumerable]]`特性会被设置成true(因此可以被枚举出来) ![20200617213135]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200617213135.png) ### new new操作符的四个过程: 1. 创建一个继承自该构造函数原型的实例对象 2. 将构造函数的作用域赋给新对象并执行(修改this) 3. 如果构造函数返回值位对象,返回这个对象,否则返回新对象 function myNew (constructor,…args) { let to = {}; to.__proto__ = constructor.prototype; // 也可以使用Object.setPrototypeof() // eg: Object.setPrototypeof(to,constructor.prototype) // let result = constructor.apply(to,args); return (typeof result === “object”) ? result : to; } ``` 谈到原型链时,不得不提instanceof 操作符,该操作符用于判断对象是否在指定的原型链上,如果存在即返回true。 ### instanceof 原理实现 - 判断实例对象是否存在目标构造函数的原型中 - 移动_proto_指针 - 如果不存在,则最终会遍历完整个原型链(到Null)。 ``` javascript let _instanceof = (child,parent) => { if(parent === null) return false; let parentPrototype = parent.prototype; // 待移动的原型指针 let childProto = Object.getProptypeOf(child) while(true){ if(childProto === null){// 查找完毕整个原型链 return false; } // 找到原型对象 if(childProto === parentPrototype) { return true } // step by step childProto = Object.getProptypeOf(childProto) } } ``` ### 遍历原型链的性能问题 在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。 遍历对象的属性时,原型链上的**每个**可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,需要配合`Object.hasOwnProperty`方法。 > hasOwnProperty 是 JavaScript 中唯一一个处理属性并且**不会**遍历原型链的方法。 最后如果有机会,延展一下能过拓展原型链的方法有哪些,以及之间的区别以及性能问题 ### 拓展原型链的方法 **new** * 优点:标准方法,兼容性好 * 缺点:构造函数式初始化可能会给对象带来不必要的方法和属性 **Object.create** (IE9+) * 优点: 一次性设置 \__proto\__ 属性,以便浏览器能够优化对象 * 缺点: 第二个参数可能会成为一个性能黑洞(属性描述符) **setPrototypeOf**:(IE9+) - 允许动态操作对象的原型,甚至能强制给通过 Object.create(null) 创建出来的没有原型的对象添加一个原型。 - 但是该方法的性能不好,因为浏览器无法对动态设置的原型进行性能优化 > 动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译 \__proto\__ : - 各个浏览器封装的方法,兼容性极佳,但并不是标准行为。 - 应该完全将其抛弃因为这个行为完全不具备性能可言,和setPrototypeOf类似的性能问题 ### Object.create原理 创建一个对象,该对象的_proto_指针指向指定的原型,同时可以通过第二个参数设置属性描述符/存储描述符 pollfill ```javascript if (typeof Object.create !== "function") { Object.create = function (proto) { if ( typeof proto !== "object" && typeof proto !== "function") { throw new Error('error') } function F () {} F.prototype = proto; return new F(); } } ``` ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5-this & call & apply & bind.md ================================================ ## this 执行上下文中包含了变量环境、词法环境、外部环境,但其实还有一个 this 没有提及,具体你可以参考下图: ![20200806114418]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200806114418.png) ### 本质 由于函数可以在**不同的运行环境执行(一等公民)**,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境 ### 能够改变this指向的场景 1. 通过new 关键字调用改变constructer里面的this (构造函数的也一样),指向实例对象 2. 通过call,apply,bind函数进行调用,参数是谁指向谁 3. 对象调用方法,this指向调用的对象 4. 全局作用域下指向window 5. 箭头函数的this由他的外层this决定,本身不存在this 6. 回调函数(对象中方法直接执行,定时器,等等),如果没有强制措施指向window [JavaScript 的 this 原理 - 阮一峰的网络日志](https://www.ruanyifeng.com/blog/2018/06/javascript-this.html) ## call、apply、bind ### 区别 三者共同点: - 改变函数this指向 不同点: - call & apply 会将函数立即执行,bind 需要手动执行 - call one by one的接受参数, apply 接受的是一个数组作为参数 - bind 返回的函数可以作为构造函数使用 ### call、apply 在实现call 和 apply时,需要注意通过向context挂载方法的方式改变其this指向,在执行完毕前需要销毁。 call/apply: ```js function _call(context = window,...args) { let fn = this; context.fn = fn; let res = context.fn(args) delete context.fn; return res; } function _apply(context = window,args) { let fn = this; context.fn = fn; if(!args) { context.fn() }else { context.fn(args) } delete context.fn; return res; } ``` ### bind 在实现bind时候注意三个过程: * 完成基本功能,改变this并返回新的函数待执行 * 处理两次参数合并 * 当返回的函数被new调用时,实例能够访问调用bind的函数的**原型对象** 同时 this 指向当前**实例对象** bind: ```js /** * * @param {*} context * @returns {func} bindedFunc */ function bind(context,...args) { let oirignfn = this; let bindFunc = (...bindArgs) => { context = this instanceof bindFunc ? this : context; oirignfn.apply(context,args.concat(bindArgs)) } function F () {} F.prototype = fn.prototype bindFunc.prototype = new F(); return bindFunc } ``` ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5-原型原型链.md ================================================ ## 原型 & 原型链 ### 原型 每个构造函数都一个`prototype`属性,该属性指向了一个对象。这个对象就是构造函数被调用时创建的实例的原型。 所以原型可以这样理解:每一个JavaScript对象(!null)在创建时都会关联到一个原型上,我们可以同过**'继承'**的方式从原型对象上获取属性或方法。 #### `__proto__` 对象和原型之间的指针称之为`__proto__` ![20200807115441]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200807115441.png) ### 原型链 所谓的原型链,就是形象上的将多个原型之间通过`__proto__`连成一个链条。这样实例在查找属性时,会沿着当前原型链向下查找,直到找到需要的属性或方法 否则会一直找到为null。 ![20200807115640]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200807115640.png) ### prototype、constructor、[[prototype]]、__proto__ prototype:每创建的一个函数都有一个prototype属性,该属性是一个指针,指向该函数的原型对象 constructor:默认情况下,所有的原型对象都会获得一个指向构造函数的 **不可枚举的属性**。 [[prototype]]: ES5标准中,指向原型的指针称之为[[prototype]] __proto__: 在ES5中,没有提供直接访问原型的方法,因此每个浏览器厂商都提供一个访问原型的方法:__proto__ > 注意:这个链接是在实例对象和原型之间,而不是 实例对象和构造函数之间。 最后说一下 在原型模型中,通过原型链查找变量时的细节点: ### 细节 #### 遮蔽效果 实例对象可以访问原型的属性/方法,但不能通过实例对象重写原型的方法。如果在实例对象上重写原型属性/方法会发生**遮蔽效果**,除非直接调用xxx.prototype.xxx直接重写,这样是可以的。 #### 重写原型 当原型对象被重写时,他的constructer属性也随之被修改,指向Object的同时该属性描述符中的**[[Enumerable]]特性**会被设置成`true`(因此可以被枚举出来) ### 遍历原型链的性能问题 在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。 遍历对象的属性时,原型链上的**每个**可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,需要配合`Object.hasOwnProperty`方法。 > hasOwnProperty 是 JavaScript 中唯一一个处理属性并且**不会**遍历原型链的方法。 最后如果有机会,延展一下能过拓展原型链的方法有哪些,以及之间的区别以及性能问题 ### 拓展原型链的方法 **new** * 优点:标准方法,兼容性好 * 缺点:构造函数式初始化可能会给对象带来不必要的方法和属性 **Object.create** (IE9+) * 优点: 一次性设置 __proto__ 属性,以便浏览器能够优化对象 * 缺点: 第二个参数可能会成为一个性能黑洞(属性描述符) **setPrototypeOf**:(IE9+) - 允许动态操作对象的原型,甚至能强制给通过 Object.create(null) 创建出来的没有原型的对象添加一个原型。 - 但是该方法的性能不好,因为浏览器无法对动态设置的原型进行性能优化 > 动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译 __proto__ : - 各个浏览器封装的方法,兼容性极佳,但并不是标准行为。 - 应该完全将其抛弃因为这个行为完全不具备性能可言,和setPrototypeOf类似的性能问题 ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5-变量提升.md ================================================ ## JS执行机制 & 预编译 ### 变量提升特点 所谓的变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。 **表象** ```javascript showName() console.log(myname) var myname = '测试' function showName() { console.log('函数 showName 被执行'); } ``` 结果: 函数 showName 被执行 myname:undefined ```javascript console.log(test) function test () {} var test = 'test' ``` 结果: 打印函数体 **特点:变量提升(默认值会被设置为 undefined),函数声明提升,发生覆盖时函数优先级高** 之所以js需要变量提升,是因为js属于即时编译型语言,为了能够在执行时获得更快的速度,需要进行预编译环节。 “变量提升”并不意味着变量和函数的声明会在物理层面移动到代码的最前面,**实际上变量和函数声明在代码里的位置是不会改变的,而且是在编译阶段被 JavaScript 引擎放入内存中**。 ## JS执行机制(先编译,再执行) 一段 JavaScript 代码在执行之前需要被 JavaScript 引擎编译,**编译**完成之后,才会进入**执行**阶段。 ![未知](https://user-images.githubusercontent.com/53052047/79989482-79d99780-84e2-11ea-89c4-4c42d7b98bd2.png) **编译阶段**: 生成两部分内容,执行期上下文 和 可执行代码。 **执行上下文是 JavaScript 执行一段代码时的运行环境**,用于确定在执行期间用到的变量、this、对象等。 对于当前只需要知道:在执行上下文中存在一个**变量环境的对象**(Viriable Environment),该对象中保存了**变量提升的内容**,比如上面代码中的变量 myname 和函数 showName,都保存在该对象中。 执行阶段:在执行阶段会按照顺序执行代码,具体细节参考v8执行js机制; - [ ] v8 **总结:** * JavaScript 代码执行过程中,需要先做**变量提升**,而之所以需要实现变量提升,是因为 JavaScript 代码在执行之前需要先**编译**。 * 在**编译阶段**,变量和函数会被存放到**变量环境**中,变量的默认值会被设置为 undefined;在代码**执行阶段**,JavaScript 引擎会从变量环境中去查找自定义的变量和函数。 * 如果在编译阶段,存在两个相同的函数,那后定义的会覆盖掉之前定义的。 ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5-数据类型.md ================================================ ## 数据类型 ### 原始值和引用值 #### 区别 在**表现上**有如下几点区别: 1. **复制** - 原始值复制后处在**不同栈帧**中,**变量互不影响**。 - 引用值复制的是**栈中存储的地址**,会指向堆内存中同一个对象,容易造成影响(一发而动全身) 2. **比较** - 对于原始类型,比较的是栈帧中的值 - 对于引用类型,比较栈中存储的(地址)指针 3. **值传递** - 原始类型作为函数参数传递时,这个参数副本就是值本身; - 引用类型作为参数传递时,这个参数副本是指向堆内存的(地址)指针 请默念:**ECMAScript中所有函数的参数都是按值传递的** **本质上的区别是由于:堆栈模型(原始值保存在栈内存中,引用值保存在堆内存中)** #### 堆栈模型 **栈内存特点**: 1. 存储的值**大小固定**,空间较**小**。可以**直接操作变量**运行效率**高** **堆内存:** 存储的值**大小不定**,**可动态调整**,空间较**大**,无法直接操作,**需借指针读写内部数据**,运行效率**低** ### 值类型 原始值类型:number、boolean、string、null、undefined、symbol、bigint #### symbol - ES6新增的一种原始值类型,通过使用`Symbol()`进行创建。 > 不能通过new的方式调用Symbol,它是原始值不是构造函数 - [ ] 一个symbol值能够作为对象属性的标识符(对象的key都能保存什么变量来的?一个字符串一个数字?:详见map) > 对象设置键时,只能是 字符串,现在多一个Symbol - 每一个从Symbol返回的symbol值都是唯一的,除了使用Symbol.for注册的变量。 - 遍历对象key的方法,不会把 Symbol 的 key 遍历出来,需要使用专门遍历方法:`getOwnPropertySymbol()` > Object.getOwnPropertyNames() Object.keys() - 不能发生类型转换,例如 字符串拼接,数学运算。 **应用场景**: - 对象的属性名都是字符串,在Mixin 模式下会有命名冲突的问题。因此我们可以使用Symbol 保存key值,使得唯一有效。 - 为了代码可维护性,我们应该消除**魔术字符串**,解除代码中的强耦合,因此可以使用Symbol进行管理 #### 0.1 + 0.2 !== 0.3 64位浮点数,精度丢失,64位浮点数语言通病。 #### typeof null === "object" 早期实现js时,对于null的存储单元最后三位和object一样都是000,因此会将null检测成为object - [ ] bigint ### 基本包装类 规定了3种特殊引用类型:Boolean Number String 称为基本类型包装类。使得原始值可以像引用类型一样去处理(添加属性)同时具有相应的特殊行为。 第一步: 创建类Object实例。 第二步: 调用实例方法。 第三步: 执行完方法立即销毁这个实例。 = null 置空 ### 类型判断 - typeof #### 用途 typeof 操作符返回一个字符串,表示未经计算的操作数的类型。 ```js typeof operand typeof(operand) ``` 注意 是有**优先级之分**的,例如这个例子: ```js var iData = 99; typeof iData + ' Wisen'; // 'number Wisen' typeof (iData + ' Wisen'); // 'string' ``` 常见示例: ```js // number typeof 37 === 'number'; typeof 3.14 === 'number'; typeof(42) === 'number'; typeof Math.LN2 === 'number'; typeof Infinity === 'number'; typeof NaN === 'number'; //! 因为NaN它是 "Not-A-Number" (非数值) 的缩写 typeof Number(1) === 'number'; //! Number构造函数 会尝试把参数解析成数值 typeof 42n === 'bigint'; // string typeof '' === 'string'; typeof 'bla' === 'string'; typeof `template literal` === 'string'; typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串 typeof (typeof 1) === 'string'; //! typeof 总是返回一个字符串 typeof String(1) === 'string'; //! String 将任意值转换为字符串,比 toString 更安全 // boolean typeof true === 'boolean'; typeof false === 'boolean'; typeof Boolean(1) === 'boolean'; //! Boolean() 会基于参数是真值还是虚值进行转换 typeof !!(1) === 'boolean'; // 两次调用 ! (逻辑非) 操作符相当于 Boolean() // Symbols typeof Symbol() === 'symbol'; typeof Symbol('foo') === 'symbol'; typeof Symbol.iterator === 'symbol'; // Undefined typeof undefined === 'undefined'; typeof declaredButUndefinedVariable === 'undefined'; // 未声明的变量,或者 发生变量提升的变量(但不存在暂时死区) typeof undeclaredVariable === 'undefined'; // !!! // typeof 总能保证对任何所给的操作数返回一个字符串。即便是没有声明的标识符,typeof 也能返回 'undefined' // 使用 typeof 永远不会抛出错误 // 但是 在加入了块级作用域的 let 和 const 之后,在其被声明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 ReferenceError。 // 其根本原因是暂时性死区的存在 typeof undeclaredVariable === 'undefined'; // 不存在的 typeof newLetVariable; // ReferenceError typeof newConstVariable; // ReferenceError typeof newClass; // ReferenceError let newLetVariable; const newConstVariable = 'hello'; class newClass{}; // 对象 typeof {a: 1} === 'object'; // !!! // 使用 Array.isArray 或者 Object.prototype.toString.call // 区分数组和普通对象 typeof [1, 2, 4] === 'object'; typeof new Date() === 'object'; typeof /regex/ === 'object'; // !!! // 下面的例子令人迷惑,非常危险 typeof new Boolean(true) === 'object'; typeof new Number(1) === 'object'; typeof new String('abc') === 'object'; // 函数 typeof function() {} === 'function'; typeof class C {} === 'function' typeof Math.sin === 'function'; ``` #### new 操作符 涉及到装箱操作: ```js // 除 Function 外的所有构造函数的类型都是 'object' var str = new String('String'); var num = new Number(100); typeof str; // 返回 'object' typeof num; // 返回 'object' var func = new Function(); typeof func; // 返回 'function' ``` ### 其他类型判断 #### instanceof **可以判断当前实例的原型链上是否有 目标构造函数的 prototype 对象** 内部机制是通过**原型链进行判断**,可以判断引用值类型,无法对**原始值**进行判断。 手写: ```js auto instanceof Car// case let instanceof = (obj,constructor) => { if(constructor === null) return false; // 获取当前对象的原型对象 let curPrototype = Object.getPrototypeOf(obj); while(true) { // - 当obj 原型对象 === null 返回 false if(!curPrototype) return null; // - 当obj 原型对象 === const 原型对象时,返回 true if(curPrototype === constructor.prototype) return true; // 移动obj的原型对象 curPrototype = Object.getPrototypeOf(curPrototype); } } ``` **!!!细节:Object.getPrototypeOf(Object) 不是 Object.prototype** ```js JavaScript中的 Object 是构造函数(创建对象的包装器)。 一般用法是: var obj = new Object(); 所以: Object.getPrototypeOf( Object ); // ƒ () { [native code] } Object.getPrototypeOf( Function ); // ƒ () { [native code] } Object.getPrototypeOf( Object ) === Function.prototype; // true Object.getPrototypeOf( Object )是把Object这一构造函数看作对象, 返回的当然是函数对象的原型,也就是 Function.prototype。 正确的方法是,Object.prototype是构造出来的对象的原型。 var obj = new Object(); Object.prototype === Object.getPrototypeOf( obj ); // true Object.prototype === Object.getPrototypeOf( {} ); // true ``` **!!!细节:在 ES5 中,如果参数不是一个对象类型,将抛出一个TypeError异常。在 ES2015 中,参数会被强制转换为一个 Object。** ```js Object.getPrototypeOf('foo'); // TypeError: "foo" is not an object (ES5 code) Object.getPrototypeOf('foo'); // String.prototype (ES2015 code) ``` #### toString 如果此方法在自定义对象中未被覆盖,toString才会达到预想的效果进行类型判断。 >通常使用Object.toString.call 来保证该方法的纯粹性,即使被覆盖掉也能够获取正确的toString方法 ![20200805091504]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200805091504.png) ### 类型转换 因为ECMASciprt是弱语言,所以经常会发生类型转换,类型转换分为两种,**隐式转换**和**强制转换**。 **常见可能性**: ![20200805093100]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200805093100.png) **整体的判断流程**: 1. 两者类型是否相同,相同的话直接调用 `===` 2. 类型不同,进行类型转换流程: 1. 判断是否是null和undined在进行比较,是的话 -> true 2. 判断两者类型是否为string和number,string会转成number (回到1) 3. 其中一方是否为 `boolean`,是的话就会把 `boolean` 转为 `number` 再进行判断 (回到1) 4. 判断其中一方是否为 `object` 且另一方为`string`、`number` 或者 `symbol`,是的话就会把 `object` 转为原始类型再进行判断。(回到1) 3. 都没有命中返回false ![==判断流程图]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200805093124.png) #### 常见转换 ##### boolean 空对象/数组/函数/Symbol 都为真 > 为假的条件:undefined null false NaN '' +-0 ##### 四则运算 **+运算符特点:** - 其中一方为字符串,会把另一方也转换为字符串 - 其中一方不是字符串或者数字,那么会将它转换为数字或者字符串 ```javascript 1 + "abc" // "1abc" notice 1 true + true // 2 typeof:number 4 + [1,2,3] // "41,2,3" ``` **+的优先级:** `"a" + + "b"` 结果为`aNaN`可以理解为`a +(+"b")`。通常情况下`+ "1"`是用来强制转换。 `*、-、/`运算符就相对简单一些,要其中一方是数字,那么另一方就会被转为数字 ```js 4 * '3' // 12 4 * [] // 0 4 * [1, 2] // NaN [1,2] Number-> NaN; [1] Number -> 1 ``` ##### 对象转原始值(拆箱) 调用内部的 [[ ToPrimitive ]] 该规则总结如下: 0. 如果已经是原始类型了,不需要转换 1. 调用 x.valueOf(),如果转换结果为原始类型,则**返回**. 2. 调用 x.toString(),如果转换为原始类型,则**返回** 3. 没有返回原始值 -> 报错 ```javascript a == 1 && a == 2 && a == 3 如何 ==> true? 答案: const a = { value:[3,2,1], valueOf: function () {return this.value.pop(); }, } ``` ## 深浅拷贝 ### Object.assign 及手写 该方法用于从一个源对象或多个源对象中所有可枚举属性复制到目标对象,最后返回目标对象。 **特点** - **源对象为原始值时包装成对象** - **浅拷贝** - **继承属性**和**不可枚举值**无法拷贝 - **相同属性后者覆盖前者** **手写** ```javascript if(typeof Object.assign !== 'function’) { Object.defineProperty(Object,'assign',{ value: function (target,sources) { if(target === null) { throw Error(‘error’) } // 包装原始值 let to = Object(target) // 合成sources for(let i = 1; i < arguments.length;i++){ let curSource = arguments[i]; if(curSource !== null) { // in 操作符 // 筛选不可枚举 & 继承属性 for(let key in curSource) { if(Object.hasOwnProperty.call(curSource,key)){ to[key] = curSource[key] } } } } return to; }, writable: true, configurable: true }) } ``` ### 深拷贝 通过JSON.parse 和 JSON.stringfiy 实现深拷贝 ```javascript function hackClone (target) { return JSON.parse(JSON.stringify(target)); } ``` 该方案存在的问题: - 抛弃源对象的construct,克隆后变成Object - 无法解决循环引用问题 - 只能序列化JSON能够处理的数据,无法处理 函数、正则、undefined 以及Symbol 通过递归实现深拷贝 ```javascript function recursionClone(target = {},origin) { if (origin === null) { return; } for(let key in origin) { if(Object.hasOwnProperty.call(origin,key)) { let isObj = typeof (origin[key]) === ‘object’ && origin[key] !== ‘null’ if(isObj) { let isArr = Array.isArray(key); isArr ? target[key] = [] : target[key] = {} recursionClone(target[key],origin[key]) }else { target[key] = key } } } return target } ``` 存在的问题: - 当clone对象足够大,会stack overflow - [ ] 深入理解0.1 + 0.2 != 0.3 以及js 中的number bigint - [ ] 使用循环改写递归clone ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5-继承.md ================================================ ## 继承 ### 什么是面向对象?JavaScript是面向对象语言吗 面向对象语言都有一个标志就是类的概念,通过类可以**创建多个对象具有相同方法和属性**。JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数。因此他的继承和面向对象语言不太一样,是基于原型对象的一种继承方案。 ### 原型链继承 利用原型让一个引用类型"继承"另外一个引用类型的方法和属性,通过让待继承的引用类型的原型对象指向另一个类型的实例。 存在的问题: - 引用值共享问题 - 创建子类时,无法向其超类进行参数传递 - 不能用对象字面量语法向原型中添加属性(改变原型指针,会切断原型链) ```javascript function Parent() { this.name = ['1','2'] } function Son () {} Son.prototype = new Parent(); var son1 = new Son();// 无法向父类传递参数 var son2 = new Son() son1.name.push('3') //son2.name 也会跟着改变 ``` ### 借用构造函数继承 **子类的构造函数内部调用父类的构造函数,能够解决传递参数的问题。** 缺点: - 不存在原型这层关系后,导致函数无法复用 - 无法关联超类 ```javascript function Parent() { this.name = [‘foo’,’bar’]; this.funcLog = function () { console.log(‘能够使用’); } } //想要继承自Father,通过借用构造函数形式,将this绑定 function Child(...args) { Father.apply(this,args) } ``` ### 组合继承 使用原型式继承实现对原型中属性和方法的继承,使用构造函数实现对实例属性的继承。 将原型链继承和借用构造函数融为一体,能够实现向父类传递参数并且避免了引用值的问题,是最常用的一种继承方案。 缺点: - 由于重写了Child的 prototype,需要手动将constructor归位 ```javascript function Parent(name) { this.name = [‘foo’,’bar’]; } Parent.prototype.func = function () { console.log(‘继承方法成功’); } function Child(…args) { Parent.call(this,args) } Child.prototype = new Parent(); // 由于原型对象被重写,需要将constructor指针归位 Child.prototype.constructor = Child; ``` ### 原型式继承 借助原型式继承可以对已有的对象创建一个新对象(从而关联两个对象),这种实现继承的方法优点在于**不需要创建构造函数**。 前提是你必须有一个对象作为另外一个对象的继承基础,ES5对该模式封装为:`Object.create(对象,属性描述符)` 问题: - 引用值的共享 ```javascript var person = { name: 'kevin', friends: ['daisy', 'kelly'] } function myCreate (o) { // 中转函数 function F () {} F.prototype = o; return new F(); } ``` ### 圣杯继承 最完美的一种继承方案,需要注意的是重写原型后,需要将constructor 进行归位操作。 优点 - 保持原型链不变 - 避免引用值问题 - 正常使用 `instanceof` & `isPrototypeOf` instanceof vs isPrototypeof > isPrototypeOf() 与 instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。 ```javascript function _extend (sub,super) { var _prototype = _create(super.prototype); // 归位 _prototype.constructor = sub; sub.prototype = _prototype; function _create(super) { function F () {} F.prototype = super; return new F () } } ``` ================================================ FILE: todo/fe/notes-JavaScript/ES5/ES5-闭包.md ================================================ ## 闭包 ### 定义 当函数能够记住并访问当前的词法作用域,就形成了闭包。即使该函数执行时的作用域不在“记住的那个词法作用域中” ### 解释产生原因 & 执行流程 > 代码执行顺序(预编译 -> 变量对象/可执行的代码 -> 执行) -> > 执行前一刻:函数预编译(初始化`[[scope]]`:采用当前上下文的作用域 /变量对象(AO) -> 将AO 加到`[[scope]]`中 -> ) -> > call stack 执行代码 + 产生闭包(无法销毁:内存泄露) #### 执行期上下文 需要预编译的js代码,生成执行期上下文有如下三种: - 全局代码创建全局执行上下文:独一份,整个页面的生命周期。 - 函数执行创建函数执行上下文:函数体内的代码会被编译,正常情况函数执行后上下文会销毁。 - eval 的代码也会被编译,创建上下文。 对于每个执行上下文,都有三个重要属性: - 变量对象(Variable object,VO) - 作用域链(Scope chain) - this #### 函数上下文 在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问。只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活。 活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。 活动对象的初始化过程为: - 产生作用域链(scope chain) - 初始化arguments - 初始化形参,将arguments中的值赋值给形参 - 找出所有的变量声明,按照变量名加入AO,如果已存在,忽略。 - 找出所有的函数声明,按照函数名加入AO,如果已经存在同名变量或者函数,替换。 - this初始化 #### 解释调用栈溢出 **什么是调用栈?** 调用栈:函数在执行过程中会创建一个执行上下文,JavaScript 引擎 为了 便于管理函数之间的调用关系,使用栈结构管理这些上下文。 > 栈是物理(例如链表、线性表)结构进行实现的,只要满足LIFO 的逻辑关系即可。 因此会存在越界情况,当越界时会发生错误通常称之为爆栈。 **递归易爆栈:(递归是函数调函数的一种编程思想)** 函数在调用时会创建执行上下文,并且只有在函数执行完毕后才会销毁。由于编写递归不规范、边界情况判断失误 或者 调用层数过大都会导致一直在call 函数,把调用栈填爆。 函数尾调用实际在浏览器支持度并不高,基本不可用。因此还是需要通过经典的方式进行解爆栈:循环解递归 在实际开发过程中,通过IDE调试工具、浏览器调试工具可以方便的断点调试函数,查看函数调用来源、环境变量等,因此了解函数调用栈和上下文对开发调试非常有帮助。 ### 应用 - 函数防抖、函数节流 - 延长作用域(var声明语法) ```javascript var obj = {}; for(var i = 0;i < 3;i ++) { obj[i] = function () { console.log(i) } } obj[0]() obj[1]() obj[2]() // 都会打印3 ``` 利用闭包优化后为: ```javascript var obj = {}; for(var i = 0;i < 3;i ++) { obj[i] = function (i) { function () { console.log(i) } } (i) } obj[0]() obj[1]() obj[2]() // 0 、 1 、 2 ``` - 单例设计模式 ### 危害 - 内存泄露 ================================================ FILE: todo/fe/notes-JavaScript/ES6/ES6 - class 总结.md ================================================ ### 语法糖 #### class ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。 #### constructor constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。 ```javascript class Person { constructor(name) { this.name = name; } } ``` **Babel:ES5** ```javascript "use strict"; // 检查如何调用 function _classCallCheck(instance, Constructor) { // 直接调用 this 指向window if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function Person(name) { _classCallCheck(this, Person); this.name = name; }; ``` - class运行在严格模式下 - 只能通过new关键字调用 - constructer等同ES5:构造函数体内部代码 #### 原型方法,静态方法,get set属性 > 通过class 添加的原型方法/静态方法 默认不可枚举(ES5添加的方法可以枚举) ```javascript class Person { constructor(name) { this.name = name; } sayHello() { return 'hello, I am ' + this.name; } static onlySayHello() { return 'hello' } get name() { return 'kevin'; } set name(newName) { console.log('new name 为:' + newName) } } ``` **Babel:ES5** ```javascript 'use strict'; var _createClass = function() { /** * _createClass私有化的函数 * * 提供两个功能: * 1. 向原型上注入属性 * 2. 注入静态方法 * * 思路: * 循环拿到每个通过AST编译后的descriptors * 在这里需要区分存取器和属性访问器,因为他们有不同的字段:writable * 编译后的descriptors如下所示: * sayHello() { return 'hello, I am ' + this.name; } =>>>> { key: 'sayHello', value: function sayHello() { return 'hello, I am ' + this.name; } } */ function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { // 取出一个需要注入的方法 var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; // 区分存取器、属性访问器 if ("value" in descriptor) descriptor.writable = true; // 注入方法 Object.defineProperty(target, descriptor.key, descriptor); } } /** * _createClass向外暴露的入口函数 * * 需要注意参数顺序:添加到哪里、添加的属性(方法)、添加的静态属性 * * 核心是调用劫持后的defineProperties方法 * */ return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * 函数劫持,对构造函数Person进行属性、方法注入 * * 1. 在构造函数内部进行调用检查 * 2. 通过_createClass注入属性和方法 * */ var Person = function() { function Person(name) { _classCallCheck(this, Person); this.name = name; } _createClass(Person, [ { key: 'sayHello', value: function sayHello() { return 'hello, I am ' + this.name; } }, { key: 'name', get: function get() { return 'kevin'; }, set: function set(newName) { console.log('new name 为:' + newName); } }], // 静态方法 [{ key: 'onlySayHello', value: function onlySayHello() { return 'hello'; } }]); return Person; }(); ``` ### class #### 不存在变量提升 类不存在变量提升(hoist),这一点与 ES5 完全不同。 ```js new Foo(); // ReferenceError class Foo {} ``` ```js { let Foo = class {}; class Bar extends Foo { } } ``` 如果存在class的提升,上面代码就会报错,因为class会被提升到代码头部,而let命令是不提升的,所以导致Bar继承Foo的时候,Foo还没有定义。 #### 如何私有化方法 #### 私有化属性:# ```js class IncreasingCounter { #count = 0; } ``` 精简后: ```js var Test = function Test() { _classCallCheck(this, Test); _count.set(this, { writable: true, value: 0 }); }; var _count = new WeakMap(); ``` - [ ] 和 WeakMap 有什么关系? #### this指向问题 #### 静态方法和属性 箭头函数 bind函数 Symbol #### new.target 强制继承 ES6 为new命令引入了一个new.target属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。 可以用于生成只能被继承的类: ```js class Father { constructor(length, width) { console.log(new.target === Father); // ... } } ``` ================================================ FILE: todo/fe/notes-JavaScript/ES6/ES6 - extends 总结.md ================================================ ================================================ FILE: todo/fe/notes-JavaScript/ES6/Promise/Promise - 实现 Promise.race() && Promise.all() && Promise.allsettled().md ================================================ ## 手写 ### Promise.all() ```js Promise.myall = (iterators) => { let promises = Array.from(iterators); let nums = promises.length; let resolveRes = new Array(nums) let resolveResIndex = 0; return new Promise((resolve,reject) => { promises.forEach((promise,index) => { Promise.resolve(promise) .then(value => { resolveRes[index] = value if(++resolveResIndex === nums) { resolve(resolveRes) } }) .catch(reject) }) }) } ``` ### Promise.race() ```js Promise.race = iterators => { let promises = Array.from(iterators); // 谁快就是谁 retrun new Promise((resolve,reject) => { promises.forEach(promise => { Promise.resolve(promise) .then(resolve) .catch(reject) }) }) } ``` ### Promise.allSettled ```js const formate = (success,value) => ( success ? { status: "fulfilled",value } : { status: "rejected",reason: value } ) Promise.allSettled = iteraotrs => { let promises = Array.from(iteraotrs); let nums = promises.length; let settledRes = new Array(nums); let settledNums = 0; return new Promise((resolve,reject) => { promises.forEach((promise,index) => { Promise.resolve(promise) .then(value => { // todo: formate settledRes[index] = formate(true,value) if(++settledNums === nums) { resolve(settledRes) } }) .catch(error => { // formate settledRes[index] = formate(error) if(++settledNums === nums) { resolve(settledRes) } }) }) }) } ``` ### Promise.finally ```js Promise.prototype.finally = (cb) => { let p = this.constructor // 调用上一个成功或失败返回的promise的then方法,增加处理函数 return this.then( value => p.resolve(cb()).then(() => value), error => p.resolve(cb()).then(() => { throw error }) ) } ``` ================================================ FILE: todo/fe/notes-JavaScript/ES6/Promise/promiseA+.js ================================================ const RESOLVED = "RESOLVED" const REJECTED = "REJECTED" const PENDING = "PENDING" /** 1. 判断 promise === x ? reject(一个错误信息) : 向下执行 2. 判断 x === 原始值 ? resolve(x) : 向下执行 3. 判断 x拥有then函数 ? 递归处理 promise :resolve(x) @params {Promise} promise 需要被决定状态的promise实例 @params {any} x 决定pormise状态的数据 原始值 | promise对象 | then函数 @params {Function} resovle promise的状态更改函数 - 成功 @params {Function} reject promise的状态更改函数 - 失败 */ const resolvePromise = (promise,x,resovle,reject) => { if(promise === x) { reject(new Error(`xxx`)); } let called = false; if((typeof x === 'object' && x !== null) || typeof x === 'function') { try { let then = x.then; if(typeof then === 'function') { then.call(x,(y) => { if(called) return; called = true; resolvePromise(promise,y,resovle,reject) },(e) => { if(called) return; called = true; reject(e); }) }else { resovle(x) } }catch(e) { if(called) return; called = true; reject(e) } }else { resovle(x) } } class Promise { constructor(executor) { this.status = PENDING; this.value = undefined; this.reason = undefined; this.fulfilledCBS = []; this.rejectCBS = []; let resolve = (value) => { if(this.status === PENDING) { this.status = RESOLVED this.value = value fulfilledCBS.forEach(cb => cb()) } } let reject = (reason) => { if(this.status === PENDING) { this.status = PENDING this.reason = reason rejectCBS.forEach(cb => cb()) } } // 让excutor执行 try { executor(resolve,reject) }catch(e) { reject(e) } } then(onfulfilled,onrejected) { let promise2 = new Promise((resolve,reject) => { if(this.status === RESOLVED) { setTimeout(() => { try { let x = onfulfilled(this.value); resolvePromise(promise2,x,resolve,reject) }catch(e) { reject(e) } },0) } if(this.status === REJECTED) { setTimeout(() => { try { let x = onrejected(this.reason); resolvePromise(promise2,x,resolve,reject) }catch(e) { reject(e) } },0) } if(this.status === PENDING) { // 订阅事件 this.fulfilledCBS.push(() => { setTimeout(() => { try { let x = onfulfilled(this.value); resolvePromise(promise2,x,resolve,reject) }catch(e) { reject(e) } },0) }) this.rejectCBS.push(() => { setTimeout(() => { try { let x = onrejected(this.reason); resolvePromise(promise2,x,resolve,reject) }catch(e) { reject(e) } },0) }) } }) return promise2; } } ================================================ FILE: todo/fe/notes-JavaScript/ES6/Promise/promise练习.md ================================================ ### 使用Promise实现每隔1秒输出1,2,3 同步累加then函数 ```js const arr = [1,2,3] arr.reduce((memo,cur) => { return memo.then(() => { return new Promise(resolve => { setTimeout(() => resolve(console.log(cur)),1000) }) }) },Promise.resolve()) ``` ### 使用Promise实现红绿灯交替重复亮 红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现)三个亮灯函数已经存在: ```js function red() { console.log('red'); } function green() { console.log('green'); } function yellow() { console.log('yellow'); } ``` 1. 封装亮灯函数,用于控制不同秒数和颜色 2. 链式调用串起亮灯顺序,最后递归自己 ```js const light = function (wait,cb) { // 需要将其包装成一个Promise 这样方便链式调用 // 因为只有前一个状态执行,下一个then函数才会执行 return new Promise(r => { setTimeout(() => { cb() resolve() },wait) }) } const step = function () { Promise.resolve().then(() => { return light(3000,red) }).then(() => { return light(2000,green) }).then(() => { return light(1000,yellow) }).then(() => { step() }) } ``` ### 封装一个异步加载图片的方法 ### 封装ajax请求方法 ### 限制异步操作的并发个数并尽可能快的完成全部 ================================================ FILE: todo/fe/notes-JavaScript/ES6/Promise/promisify.md ================================================ ## promisify 例如现在我们有一个异步IO函数,它是基于回调函数的异步编写风格: ```js let fs = require('fs'); let read = fs.readFile; read('name.txt', 'utf8',function (err,data){ if(err) throw new Error('err is:',err); console.log(data); }) ``` 现在有一个需求是让你将read函数,转化成promise的编程风格,可以这样调用: ```js let read = promisify(fs.readFile); read('name.txt', 'utf8') .then((data) => { console.log(data) },(err) => { console.err(err) }) ``` 因此我们根据如下两条线索即可得promisify: - promisify 接收一个函数,并返回另外一个包裹后的函数 - 通过Promise 对包裹函数进行封装 ```js function promisify (fn) { return function (...args) { // 让该函数执行,并根据不同结果将promise推向已决状态 // 使得可以在then函数中获取异步结果 new Promise((resolve,rejcet) => { fn(...args,function (err,data) => { if (err) reject(err) resolve(data) } ) }) } } ``` 最终简化后的结果如下所示 ```js const promisify = fn => (...args) => new Promise((resolve,reject) => { fn(...args,function (err,data) { if(err) reject(err) resolve(data) }) }) ``` ================================================ FILE: todo/fe/notes-JavaScript/ES6/Promise/实现PromiseA+.md ================================================ ## 实现PromiseA+ 规范 ### promise 它是是一个拥有 then 方法的对象或函数,其行为符合本规范; > 因此我选择实现一个Promise类 ### Promise 的状态 一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。 #### 等待态(Pending) 处于等待态时,promise 需满足以下条件:`可以迁移至执行态或拒绝态` #### 执行态(Fulfilled) 处于执行态时,promise 需满足以下条件:`不能迁移至其他任何状态 + 必须拥有一个不可变的终值` #### 拒绝态(Rejected) 处于拒绝态时,promise 需满足以下条件::`不能迁移至其他任何状态 + 必须拥有一个不可变的终值` ### Then 方法 一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。 promise 的 then 方法接受两个参数:`promise.then(onFulfilled, onRejected)` #### 参数可选 onFulfilled 和 onRejected 都是可选参数。 - 如果 onFulfilled 不是函数,其必须被忽略 - 如果 onRejected 不是函数,其必须被忽略 #### onFulfilled 和 onRejected 特性 onFulfilled 特性,如果 onFulfilled 是函数: - 当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值 - 在 promise 执行结束前其不可被调用 - 其调用次数不可超过一次 onRejected 特性,如果 onRejected 是函数: - 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因 - 在 promise 被拒绝执行前其不可被调用 - 其调用次数不可超过一次 #### 多次调用 then 方法可以被同一个 promise 调用多次 - 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调 - 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调 #### 返回(链式调用) then 方法必须返回一个 promise 对象 `promise2 = promise1.then(onFulfilled, onRejected);` - 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x) - 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e - 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值 - 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因 > 总结来说,如果返回一个promise或原始值,那么promise2的状态会由该值决定。 > 如果在promise1的then函数执行时出错,那么promise2 状态会推向失败态,并返回错误信息 > 如果then函数没有两个fn参数,那么会出现穿透现象 #### Promise 解决过程 Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 `[[Resolve]](promise, x)`,如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。 这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。 运行 `[[Resolve]](promise, x)` 需遵循以下步骤: - x 与 promise 相等 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise - x 为 Promise 如果 x 为 Promise ,则使 promise 接受 x 的状态 ``` 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝 如果 x 处于执行态,用相同的值执行 promise 如果 x 处于拒绝态,用相同的据因拒绝 promise ``` - x 为对象或函数 ``` 如果 x 为对象或者函数: - 把 x.then 赋值给 then - 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise - 如果 then 是函数,将 x 作为函数的作用域 this 调用之。 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise: - 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y) - 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise - 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用 - 如果调用 then 方法抛出了异常 e: - 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之 - 否则以 e 为据因拒绝 promise - 如果 then 不是函数,以 x 为参数执行 promise 如果 x 不为对象或者函数,以 x 为参数执行 promise ``` ### 模仿实现 #### 基础版 根据以上规范部分实现如下: ```js //1. 实现三个状态 const RESOLVED = "RESOLVED"; // 成功 const REJECTED = "REJECTED"; // 失败 const PENDING = "PENDING"; // 等待态 // 2. 初始化Promise构造器 class Promise { constructor(executor) { this.status = PENDING; // 初始化Promise状态为PENDING this.value = undefined; // 成功的原因 this.reason = undefined; // 失败的原因 // 3. resolve 和 reject 并不属于规范,但是ES6实现中有 // 根据其用意,我们可以写出如下代码 let resolve = (value) => { // 先决条件,必须是PENDING才可以将状态推向RESOLVED if (this.status === PENDING) { this.value = value; this.status = RESOLVED; } }; let reject = (reason) => { // 先决条件,必须是PENDING才可以将状态推向REJECTED if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; } }; // 3. 这里需要捕获excutor在执行过程中可能出现的错误,规范中没有明确规范 // 但是在ES6实现中是这样的逻辑,代码如下 try { executor(resolve, reject); } catch (e) { // 捕获错误后,会将状态推向已决失败态 reject(e); } } // 4. 根据Promise规范,实现原型then方法,具体要求部分参照如上规范 then(onFulfilled, onRejected) { if (this.status === RESOLVED) { onFulfilled(this.value); } if (this.status === REJECTED) { onRejected(this.reason); } } } ``` #### 发布订阅处理异步和一个promise注册多个then函数 上面实现的基础版 Promise 无法对异步 reject/resolve 处理,如下所示: ```js let promise = new Promise((resolve, reject) => { setTimeout(() => { // 处理不了 reject("不成功"); }, 1000); }); promise.then( (data) => { console.log("success1", data); }, (err) => { console.log("faild2", err); } ); // 执行到then时,promise的状态为pending,无法进行判断,因此什么也不做然后结束执行。 ``` 因此我们要在发布订阅模式的帮助下,实现能够监听异步情况下的状态改变,对代码修改如下: ```js class Promise { constructor(executor) { this.status = PENDING; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; // 中介,按顺序存放对应的回调函数 this.onRejectedCallbacks = []; // 中介,按顺序存存放对应的回调函数 let resolve = (value) => { if (this.status === PENDING) { this.value = value; this.status = RESOLVED; // 发布 this.onReslvedCallbacks.forEach((fn) => fn()); } }; let reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; // 发布 this.onRejectedCallbacks.forEach((fn) => fn()); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { if (this.status === RESOLVED) { onFulfilled(this.value); } if (this.status === REJECTED) { onRejected(this.reason); } // 订阅异步事件 if (this.status === PENDING) { this.onResolvedCallbacks.push(() => { // XXX: 立场站不住脚? // 之所以包了一层函数是在为以后可能的AOP切片做准备 // 这样能够使得代码拓展性强(否则直接push进去的话,想在执行前做点什么就非常有难度了) // todo... onFulfilled(this.value); }); this.onRejectedCallbacks.push(() => { // todo... onRejected(this.reason); }); } } } ``` #### 实现 then 的链式调用 链式调用的特点如下: 成功/失败的结果会作为参数传递到下一层 then 函数中,每次执行完 then 函数后返回的都是一个“新的 promise"。 根据上面函数返回结果的类型不同,有如下几种情况: 1. 原始值类型:无论是成功/失败的结果,都会传递到下一层 then 函数 中的**成功处理逻辑** 2. Promise 对象:会采用这个 promise 的状态,从而决定一下层 then 函数成功还是失败 3. 异常处理:如果在当前函数中执行抛出错误,一定会走到下一层 then 函数 中的错误处理逻辑 4. 异常处理:如果离自己最近的 then 函数 没有错误处理(没有写错误函数) 会向下找 此处有一个问题,在jQuery中的链式调用实现是通过返回this,那么 then的 链式调用可以返回this实现吗? > 此时的this 是当前promise 实例,如果在当前已经变为成功态,而后在其他的then函数中,不料更改为失败态,会违背promise的设计初衷,因此选择每次then函数返回一个promise实现链式调用。 then 函数的逻辑修改如下: ```js then(onFulfilled, onRejected) { // 返回一个promise2 作为链式调用的基石 // 之所以链式调用不选择返回this // 是因为promise的特性:一旦成功或者失败就不能修改状态 // 如果是返回this,那么在后续的then函数中就有可能更改其状态,违背promiseA+规范 let promise2 = new Promise((resolve, reject) => { if (this.status === RESOLVED) { // 通过setTimeout延迟到下一个队列中执行 // 这样能够获得正确的promise2对象,因为该逻辑还处在new Promise的构造函数代码中 // 无法获得当前promise2 // 需要推迟到同步代码执行完毕后再执行 setTimeout(() => { // 尝试去执行函数并捕获异常 try { let x = onFulfilled(this.value); // x 是当前promise状态变更后所返回的内容 // 可能是一个proimise,也可能是一个原始值 // 不管怎样,都交给resolvePromise来处理 // 新的promise2作为被决定对象,x作为决定promise2的关键因素 // XXX: resolve,reject 是初始化时的公共函数,每个promise都会用到。 resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } // 同理 if (this.status === REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } // 只需要将当前参数缓存起来即可 if (this.status === PENDING) { this.onResolvedCallbacks.push(() => { // todo... setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); this.onRejectedCallbacks.push(() => { // todo... setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); } }); return promise2; } ``` resolvePromise 的实现逻辑如下: ```js const resolvePromise = (promise2, x, resolve, reject) => { // 1. 判断是否相等 if (promise2 === x) { return reject( // 用一个类型错误结束掉promise new TypeError("Chaining cycle detected for promise #") ); } let called; // 2. 判断是否为promise if ((typeof x === "object" && x != null) || typeof x === "function") { // 后续的条件要严格判断,保证代码能和别的库一起使用 // 只要满足PromiseA+就可以通过检测,但实现上会有差异性 // 满足逻辑,才有可能是一个promise,要继续判断 try { // 判断x 是否拥有then函数,如果拥有,则认为是一个promise let then = x.then; if (typeof then === "function") { then.call( x, (y) => { // 根据promise完成后状态决定promise2的状态 // 避免已经执行过一个处理函数(假设是成功处理函数) // 还会执行下一个失败的处理函数(违背PromiseA+) if (called) return; called = true; // 递归解析该过程,防止多个promise嵌套 // 例如:resolve(new Promise(... => { // resolve(new Promsie(... => { // ... // })) // })) resolvePromise(promise2, y, resolve, reject); }, (e) => { // 如果出错,直接将promise推向失败态即可,不需要递归解析 if (called) return; called = true; reject(e); } ); } else { // 如果当前刚好返回值是一个普通值 resolve(x); } } catch (e) { // 防止失败了再次进入处理成功 / 失败的函数 if (called) return; called = true; reject(e); } } // 如果是普通值,将x的结果作为下一层then的成功结果,并将其状态推向已决成功态。 else { resolve(x); } }; ``` [完整的代码](promiseA+.js) ================================================ FILE: todo/fe/notes-JavaScript/ES6/Promise/来一沓Promise.md ================================================ # Promise ## 产生背景 它是用来解决形如“回调地狱” 的**异步串行**编程风格问题,主要策略:**将一个处理函数传入到 Promise 的 then 方法中,当 Promise 构造函数中获得最终结果后,会将结果传递给此时的then函数并执行他,当then函数完成后会告诉下一个 then 方法如何进行操作。** ## 特性 ### 状态机 在 Promise 规范中,将 Promise 分为两个阶段(未决阶段和已决阶段)和三个状态(挂起、成功和失败),每个阶段和状态一经更改**即为不可变**并且他们都是**单向操作**。如: - 状态是从未决状态推向已决状态,到达已决状态后不可变。 - 阶段是从 pending 推向 resolved/rejected ,到达 resolved/rejected 后状态不能变更 ### 链式调用 利用的是这个 API 实现链式调用`Promise.prototype.then()` 在 Promise 的执行流程中,当 promise 获得结果(网络请求成功或失败)后会把该结果传递给自己的 then 方法中进行指定操作(通过resolve()或reject()), then 方法处理完用户的逻辑会**包装并返回一个 promise 对象**。 因此可以在返回的 promise 对象中继续定义 then 方法,形如链式调用的样子。其实这个 then 方法是属于一个新的 promise 对象,只不过这个 promise 对象省去了初始化构造器的流程。 事实上,是会将上一次的最终结果(手动 return 的值)传递到新的 promise 中,不过需要注意根据return的值类型区别,有如下几种差异: - 结果为原始值,那么任何情况下 promise 一定变为 resolved - 结果为 promise 对象,那么 promise 会根据这个 promise 对象来确定自己的状态(promise 为失败,promise2 就会失败,反之亦然) ### 微任务 由Promise产生的回调函数,会添加到消息队列中的微任务队列,例如then 方法 和 catch 方法。从微队列产生背景来讲,我认为这样做的好处是: 由于以前网络请求是宏任务,在宏任务队列中存在大量不同消息类型的事件回调,并且不存在“优先级”。假设当前有一个网络请求完成后,不料宏队列前有任务在执行,或者刚好堵塞,那么势必会导致“网络延迟、页面卡顿”的现象发生,导致用户体验下降。 如果将回调函数加入到微队列中,这样就能够尽可能的提高执行速度,毕竟每一次事件循环只会执行一个宏任务,而微任务队列是直接清空的(从队列中阻塞的可能性角度来讲,要低);并且微任务也是优先于宏任务执行的。 ### 异常处理 - Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。 - 跟传统的try/catch代码块不同的是,如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会影响到外层代码,即不会阻止正常代码调用。 ```js const someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行会报错,因为x没有声明 resolve(x + 2); }); }; someAsyncThing().then(function() { console.log('everything is great'); }); setTimeout(() => { console.log(123) }, 2000); // Uncaught (in promise) ReferenceError: x is not defined // 123 ``` ## 常用 API ### Promise.prototype.then() - 它的作用是为 Promise 实例添加状态改变时的回调函数(接收两个参数,但并不是必须的) - then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,上面已经介绍过。 ### Promise.resolve() 根据参数的不同,该方法时会有不同的处理情况: 1. 参数为Promise时;原封不动的将入参Promise进行返回 2. 参数为thenable对象时;会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。 ```js let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 }); ``` 3. 参数不是具有then方法的对象,或根本就不是对象,为原始值;会返回一个变为resolved状态的Promise对象,并将原始值作为成功结果。 4. 无参;会直接返回一个成功态的Promise对象 ### Promise.reject() Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。 > Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。 ### Promise.prototype.catch() - Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。 - then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。 - Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。 - 如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会影响到外层代码,上面已经介绍过。(俗称吃掉错误信息) ### Promise.all() Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。 ```js const p = Promise.all([p1, p2, p3]); // p 是一个新的promise ``` p1,p2,p3 都是 Promise 实例,如果不是,会调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。 > 参数必须提供iterator接口 该promise的状态由参数决定,分为如下两种: - 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 - 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 ### Promise.race() 和all方法基本类似,但区别在于只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。 ### Promise.prototype.finally() 用于指定不管 Promise 对象最后状态如何,都会执行的操作。 > finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。 ### Promise.allsettled() [Promise.allsettled](https://es6.ruanyifeng.com/?search=catch&x=0&y=0#docs/promise#Promise-allSettled) ## 应用 ### fetchAPI ### axios ### Vue.js的异步渲染 ### promisify ### co库 ## Promise的缺点 - 当 Promise 创建后会立即执行,无法取消 - 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成) - 如果不设置错误捕获函数,Promise 内部抛出的错误,不会反应到外部 - 代码冗余 - 兼容性问题(IE11及其以下不支持Promise) ## api实现原理 ### finally方法 finally本质上是then方法的特例,会将上一个then方法传递的参数,原封不动的传递到下一个then方法中,包括上一个抛出错误, 也会原封不动的传递下去。 > 有点中间件的意思? ```js promise .finally(() => { // 语句 }); // 等同于 promise .then( result => { // 语句 return result; }, error => { // 语句 throw error; } ); ``` ```js Promise.prototype._finally = cb => (this.then( value => Promise.resolve(cb()).then(() => value), error => Promise.resolve(cb()).then(() => throw error) )) ``` ### 并发all方法 Promise.all(iterators)返回一个新的 Promise 实例。 > iterators 中包含外界传入的多个 promise 实例 对于返回的新的 Promise 实例,有以下两种情况: - 如果传入的所有 promise 实例的状态均变为fulfilled,那么返回的 promise 实例的状态就是fulfilled,value 是 传入的所有 promise 的 value 组成的数组。 - 如果有一个 promise 实例状态变为了rejected,那么返回的 promise 实例的状态立即变为rejected。 ```js Promise._all = iterator => { // 1. 包装参数 // 2. 初始化返回结果 // 3. 初始化并发控制器 let promises = Array.form(iterator); let n = iterator.length; let promisesNums = 0; let promisesData = new Array(iterator); return Promise((resolve,reject) => { promises.forEach((promise,index) => { // 使用resolve 包装promise Promise.resolve(promise).then(data => { // 1. 加入到结果中 // 2. 控制并发数量 promisesData[idnex] = data; if(++promisesNums === n) { resolve(promisesData) } }) // 3. 如果出错,直接reject .catch(reject) }) }) } ``` ### race方法实现 ```js Promise._race = iterator => { let promises = Array.from(iterator); return new Promise((resolve,reject) => { // 只要有一个先到达,就返回其结果和他的状态 promises.forEach(promise => { Promise.resolve(promise) .then(resolve) .catch(reject) }) }) } ``` ### Promise.allSettled() 只需要在all实现的基础上, 将结果和状态进行关联,组成一个对象返回即可。 ```js const formatSettledResult = (success, value) => success ? { status: "fulfilled", value } : { status: "rejected", reason: value }; Promise.allSettled = function(iterators) { const promises = Array.from(iterators); const num = promises.length; const settledList = new Array(num); let settledNum = 0; return new Promise(resolve => { promises.forEach((promise, index) => { Promise.resolve(promise) .then(value => { settledList[index] = formatSettledResult(true, value); if (++settledNum === num) { resolve(settledList); } }) .catch(error => { settledList[index] = formatSettledResult(false, error); if (++settledNum === num) { resolve(settledList); } }); }); }); }; ``` ================================================ FILE: todo/fe/notes-JavaScript/ES6/README.MD ================================================ ## ES6 [ES6 - 数组汇总](数组常见API及实现.md) ================================================ FILE: todo/fe/notes-JavaScript/ES6/新增数据结构/map/map.md ================================================ ## map ### 特点 - 解决对象的键值对存储,只能用字符串当做键的限制,并且会有隐式类型转换 - Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构 - 除了数组,字符串,其他具有 Iterator 接口的双元素的数组的数据结构可以作为参数构建map - 对同一个键连续赋值两次,后一次的值覆盖前一次的值 - **顺序存储,遍历时保证有序性** > 注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。 ```js const map = new Map(); map.set(['a'], 555); map.get(['a']) // undefined ``` ### api #### 增删改查 和 Set 异曲同工,不过区别在于**set方法返回的是当前的Map对象,因此可以采用链式写法。** #### 遍历 和 Set基本一样,不同的在于`Map.prototype.entries()` 会返回键值对。 > 本质是set 也是返回键值对,只不过键和值相同而已。 ### 转换 #### JSON #### 对象 ### 应用 #### 解决哈希碰撞 #### 模拟 LRU 算法 该算法描述的是当空间不够时,按照最近最少使用的策略进行删除,实现对新元素的缓存。除了LRU(基于时间)策略 还有 先进先出策略 LFU(基于使用次数) ```js // ES6 Map // 该数据结构提供了iteraotr接口,遍历该数据的顺序即是插入的顺序,同时拥有map的优势:O(1)时间查找和插入 function LRUcache (capacity) { this.capacity = capacity; this.map = new Map(); } /** * * 和 set一样,需要移动到最近使用 */ LRUCache.prototype.get = function (key) { let _cacheValue = this.map.get(key) if(typeof _cacheValue === 'undefined') return -1; this.map.delete(key) this.map.set(key,_cacheValue) return _cacheValue } /** * 核心是保证设置一回元素,让其跑到最前面,可以先删除,再添加,即可。 * * 如果达到上限后,需要把最早加入进来的元素删除,所以只需要拿到最早添加的元素,删除即可 * * 可以利用this.map.keys() 返回一个新的 Iterator对象, 它按插入顺序包含了Map对象中每个元素的键,也就是上面需要的第一个元素。 * */ LRUCache.prototype.set = function (key,val) { //如果元素在缓存中 if(this.map.has(key)) { this.map.delete(key) }else if(this.map.size() >= this.capacity) { // 需要删除末尾元素 let tail = this.map.keys().next().value; this.map.delete(tail) } // 处理完毕后将需要缓存的元素缓存到map中 this.map.set(key,val); } ``` ================================================ FILE: todo/fe/notes-JavaScript/ES6/新增数据结构/set/set.md ================================================ ## set/map ### 特点 - ES6 提供了新的数据结构 Set。它类似于数组,但是**成员唯一,没有重复的值**。 - 可以接受一个数组(或者具有 iterable 接口的其他数据结构)来初始化。 - 向 Set 加入值的时候,**不会发生类型转换,所以5和"5"是两个不同的值**。 - Set 内部判断两个值是否不同,类似于精确的三等运算符,例如两个空对象不相等,但不同的是:认为NaN等于自身。 - Set的遍历顺序就是**插入顺序** ### api #### 增删查清 - `Set.prototype.add(value)`:添加某个值,返回 Set 结构本身。 - `Set.prototype.delete(value)`:删除某个值,返回一个布尔值,表示删除是否成功。 - `Set.prototype.has(value)`:返回一个布尔值,表示该值是否为Set的成员。 - `Set.prototype.clear()`: 清除所有成员,没有返回值。 #### 遍历 - `Set.prototype.keys()`:返回键名的遍历器 - `Set.prototype.values()`:返回键值的遍历器 - `Set.prototype.entries()`:返回键值对的遍历器 - `Set.prototype.forEach()`:使用回调函数遍历每个成员 - Set 结构的实例默认可遍历,可以直接用for...of循环遍历 Set > entries方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。 ```js let set = new Set(['red', 'green', 'blue']); for (let item of set.entries()) { console.log(item); } // ["red", "red"] // ["green", "green"] // ["blue", "blue"] ``` ### 数组转换 #### Array.from() Array.from() 可以接受一个迭代器接口作为参数,创建新的数组。 ```js let set = new Set([1, 2, 3]); set = new Set(Array.from(set, val => val * 2)); // set的值是2, 4, 6 ``` #### 拓展运算符 ```js let set = new Set([1, 2, 3]); set = new Set([...set].map(val => val * 2)); // set的值是2, 4, 6 ``` ### 应用 #### 数组去重 ```js function unique (arr) { return [...new Set(arr)] } ``` #### 并、交、差集 ```js let a = new Set([1, 2, 3]); let b = new Set([4, 3, 2]); // 并集 let union = new Set([...a, ...b]); // Set {1, 2, 3, 4} // 交集 let intersect = new Set([...a].filter(x => b.has(x))); // set {2, 3} // (a 相对于 b 的)差集 let difference = new Set([...a].filter(x => !b.has(x))); // Set {1} ``` ================================================ FILE: todo/fe/notes-JavaScript/README.md ================================================ ## ECMAscript ### ES5 - [x] [ES5 - 数据类型](ES5-数据类型.md) - [x] [ES5 - 变量提升](ES5-变量提升.md) - [x] [ES5 - 如何理解闭包 & 执行上下文?](ES5-闭包.md) - [x] [ES5 - this & call & apply & bind](ES5-this%20&%20call%20&%20apply%20&%20bind.md) - [x] [ES5 - 原型 & 原型链](ES5-原型原型链.md) - [x] [ES5 - new & instanceof 实现](ES5%20-%20new%20&%20instanceof%20实现.md) - [x] [ES5 - 继承实现](ES5-继承.md) ### ES6 **数据结构:** - [x] [ES - 数组总结](数组常见API及实现.md) [ES6 - 新增数据结构之set](./待迁移-ES6/新增数据结构set/map.md) [ES6 - 新增数据结构之map](./notes-JavaScript/ES6/新增数据结构/map/map.md) [ES6 - 新增数据结构之WeakSet]() [ES6 - 新增数据结构之WeakMap]() **类:** [ES6 - class](./) [ES6 - extends](https://github.com/sup-fiveyear/Notes/issues/20) [ES6 - 装饰器](https://github.com/sup-fiveyear/Notes/issues/20) **异步:** - [x] [ES6 - 总结Promise](来一沓Promise.md) - [x] [Promise - 实现 PromiseA+](实现PromiseA+.md) - [x] [Promise - 实现 Promise.race() && Promise.all() && Promise.allsettled()](Promise%20-%20实现%20Promise.race()%20&&%20Promise.all()%20&&%20Promise.allsettled().md) - [x] [ES6 - Promise 场景题汇总](/notes-JavaScript/ES6/Promise/promise练习.md) [ES6 - 即兴一下 async?](./notes-JavaScript/说说async的原理.md) [Promise - 实现 promisify?](/notes-JavaScript/promisify.md) ================================================ FILE: todo/fe/notes-JavaScript/函数式编程/README.md ================================================ - [x] [高阶函数之-函数柯里化](高阶函数之函数柯里化.md) [高阶函数之-初见 AOP](高阶函数之初见AOP.md) [高阶函数之-after 函数](高阶函数之after函数.md) - [ ] 高阶函数之-compose 函数 ================================================ FILE: todo/fe/notes-JavaScript/实现极简发布订阅.md ================================================ ## 实现极简发布订阅 发布订阅能够解决异步问题,在异步逻辑成功后,会发射一个消息,告诉我已完成。 这样在此之前订阅的内容就会被逐一执行,这是极其简单的模型,是后续 Nodejs 以及各种框架源码中的基础,如下: 需要注意的是,发布订阅是需要通过一个中介媒介来完成的,在下面的代码中就是 event 媒介。 ```js let event = { // 订阅和发布没有明显的关联, 靠中间媒介来实现 callbacks: [], on(fn) { this.callbacks.push(fn); // 进行定位行为 }, emit() { this.callbacks.forEach((fn) => fn()); // 将列表中的所有函数全部执行 }, }; event.on(function () { console.log("走一个"); }); }); let temp = {}; fs.readFile("./name.txt", "utf8", function (err, data) { temp.name = data; event.emit(); }); fs.readFile("./age.txt", "utf8", function (err, data) { temp.age = data; event.emit(); }); ``` ================================================ FILE: todo/fe/notes-JavaScript/数组常见API及实现.md ================================================ ## [数组](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array) ### 创建数组 #### Array() === new Array() [stackoverflow上的回答:](https://stackoverflow.com/questions/8205691/array-vs-new-array) > When Array is called as a function rather than as a constructor, it creates and initialises a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments. 因此从功能和参数的角度来讲,是一样的。 #### [Array.of(7)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/of) === Array(7) > Array.of(element0[, element1[, ...[, elementN]]]) Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。 ```js Array.of(7); // [7] Array(7); // [ , , , , , , ] ``` #### [ES6 Arrary.from()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from) Array.from() 方法从一个**类似数组**或**可迭代对象**创建一个新的,**浅拷贝**的数组实例。 > Array.from(arrayLike[, mapFn[, thisArg]]) > 可迭代对象:拥有迭代器接口即可,例如字符串、类数组、set、mep ```js console.log(Array.from('foo')); // expected output: Array ["f", "o", "o"] ``` `Array.from()`方法有一个可选参数 mapFn,让你可以在最后生成的数组上再执行一次 map 方法后再返回。也就是说 `Array.from(obj, mapFn, thisArg)` 就相当于 `Array.from(obj).map(mapFn, thisArg)` TODO: set map ### 改变原数组 vue 2 中 对数组进行数据劫持就需要监控所有能够改变原数组的api,以及在react 的不可变值思想中,如果对数据进行修改的话,需要首先copy一份。因此还是有必要总结一下能够改变数组的api有哪些。 #### push #### pop #### shift #### unshift #### splice 可以删除或替换现有的元素,也可以原地添加新的元素。 > array.splice(start[, deleteCount[, item1[, item2[, ...]]]]) start: 指定修改的开始位置(从0计数)。 - 如果超出了数组的长度,则从数组末尾开始添加内容; - 如果是负值,则表示从数组末位开始的第几位; - 如果负数的绝对值大于数组的长度,则表示开始位置为第0位。 deleteCount: - 如果 deleteCount 大于 **start 之后的元素的总数**,则从 start 后面的元素都将被删除 - 如果 deleteCount 被省略了,start之后数组的所有元素都会被删除。 - deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。 **返回值**: 由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有**删除元素**,则返回**空数组**。 > 由此可见,对于删除操作的需求首选splice,尽量语义化。 #### [sort](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) sort() 方法用[原地算法](https://zh.wikipedia.org/wiki/%E5%8E%9F%E5%9C%B0%E7%AE%97%E6%B3%95)对数组的元素进行排序,并返回数组。 > 不理解,在数据量大的时候V8使用quicksort进行排序,然而quicksort是需要logn的空间复杂度来支持递归的,那么v8是如何做到o(1)空间的呢? 默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。 **compareFunction:** 用来指定按某种顺序进行排列的函数,数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素: - 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前; - 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。 - 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。 - compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的(**用来数组乱序?**)。 ```js // 升序: xxx.sort((a,b) => a - b) // 降序: xxx.sort((a,b) => b - a) xxx.sort(() => Math.random() - 0.5) ``` 数组乱序进阶:[「前端进阶」数组乱序](https://juejin.im/post/5d004ad95188257c6b518056) [稳定的排序算法](https://es6.ruanyifeng.com/#docs/array#Array-prototype-sort-%E7%9A%84%E6%8E%92%E5%BA%8F%E7%A8%B3%E5%AE%9A%E6%80%A7) #### reverse 是可以颠倒类数组的,例如: ```js const a = {0: 1, 1: 2, 2: 3, length: 3}; console.log(a); // {0: 1, 1: 2, 2: 3, length: 3} Array.prototype.reverse.call(a); console.log(a); // {0: 3, 1: 2, 2: 1, length: 3} ``` #### Array.prototype.fill() ### 对数组查找 #### indexOf #### lastIndexOf #### includes 改进后的indexof方法,能够实现对NaN进行判断,并且更加语义化。 #### slice #### find()& findIndex() ### 数组拼接 #### concat #### join ### 遍历 [细节](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) #### reduce #### forEach/map #### every/some/filter #### keys/values/entries ## 实践 ### 类数组转换 类数组对象本质特征只有一个,就是拥有length属性。 #### Array.from() || 展开运算符 只要是部署了iterator接口或者可迭代的对象,都能将其转化为数组。 > 注意 展开运算符是无法转化**没有部署iterator接口**的类数组对象的。 ```js let obj = {length : 3} Array.from(obj) // Array(3) [ undefined, undefined, undefined ] [...obj] // TypeError: obj is not iterable // 因为obj没有部署iterator接口 ``` #### Array.prototype.slice.call() ```js var arr = [].slice.call(arguments) ``` es5常用的方式,还是拥抱ES6吧~ ### [unix:pipe](https://zh.wikipedia.org/wiki/%E7%AE%A1%E9%81%93_(Unix)) pipe 管道可能是我们在linux下最常用的操作符之一了, 上一个函数的计算结果传给下一个函数, 就这样一直传递,如下代码: ```js const calc1 = x => x * 2 const calc2 = x => x - 1 const calc3 = x => x * 3 const sum = calc3(calc2(calc1(10))) ``` 如果使用pipe函数后,会使得代码可读性变强,如下所示: ```js const calc = pipe(calc1, calc2, calc3) const sum = calc(10) ``` 可以使用reduce实现pipe函数,殊途同归。 ```js const pipe = (...functions) => initialVal => functions.reduce((memo,cur) => { return cur(memo) },initialVal) ``` ### compose TODO: redux 原理 compose和pipe非常的像, pipe是从左到右执行顺序, compose是从右向左的执行顺序, 所以在实现上用了reduceRight这个方法. ```js const compose = (...functions) => (initialValue) => functions.reduceRight((value, fn) => fn(value), initialValue); ``` ### 去假值 ```js const falseyArray = [0, null, 1, undefined, 2, '', 3, false, 4, NaN] const compact = (arr) => arr.filter(el => el); // 会转化成boolean,如果为false就会默认筛掉 compact(falseyArray) // [1, 2, 3, 4] // 利用reduce实现 const compact = (arr) => arr.reduce((memo,cur) => { // cur会被转化为布尔值,如果为真则会加入到原始值中,最后返回memo cur && memo.push(cur); return memo; },[]) ``` ### 简化条件 ```js const value = 'cat' if (value === 'cat' || value === 'dog' || value === 'pig'){ // statement } // 可以该写为 if(['cat', 'dog', 'pig'].includes(value)){ // statement } ``` ### deduplicate ```js const deduplicate = (items) => { const cache = {}; return items.redcue((memo,cur) => { const isIncluded = cache[cur] === true; // 如果已经存在,那么不会再加入进去。 if(!isIncluded) { cache[cur] = true; memo.push(item); } return memo },[]) } deduplicate([[1], [1], { hello: 'world' }, { hello: 'world' }]); ``` ### flatten 正则 + split 傻递归 ```js let result = []; function flatten(arr) { for(let i = 0; i < arr.length;i ++) { let item = arr[item]; if(Array.isArray(item)){ flatten(item) }else { res.push(item); } } } ``` reduce实现 ```js flatten([[1, 2], [3, 4]]); // [1, 2, 3, 4] const flatten = (arr) => arr.reduce((memo,cur) => { return memo.concat(Array.isArray(cur) ? flatten(cur) : cur) },[]) ``` ### 实现 includes includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。 > arr.includes(valueToFind[, fromIndex]) > fromIndex 的算法:如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。 #### 通过reduce实现 (**不带fromIndex功能**)利用“||” 逻辑判断,最终只要有一个为真,则会返回真,如果全假则返回false。 ```js // item: 搜寻元素,arrlist:原数组 const includes = (item,arrlist) => arrlist.reduce((memo,cur) => { return memo || cur === item },false) ``` #### 实现fromIndex功能 ```js Object.defineProperty(Array.prototype,'includes',{ value: function (valueToFind,fromIndex = 0) { let len = this.length; // 对 fromIndex 做负值兼容 let k = fromIndex >= 0 ? fromIndex : len - Math.abs(fromIndex) function sameVal (v1,v2) { // 对NaN进行兼容处理 return v1 === v2 || (typeof v1 === 'number' && typeof v2 === 'number' && isNaN(v1) && isNaN(v2)) } while(k < len) { if(sameVal(arr[k],valueToFind)) { return true; } k++ } return fasle; } }) ``` ### 实现reverse ================================================ FILE: todo/fe/notes-JavaScript/说说async的原理.md ================================================ ## async ### 本质 async 函数是 Generator 函数的语法糖(内置了执行器)。体现在写法的差异:async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await。 ### 特点(相比于generator的优势) #### 内置执行器 Generator 函数的执行必须靠执行器,所以才有了co模块,实现了自执行。而async函数自带执行器,他的执行与普通函数一模一样直接调用即可。 #### 语义化 async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。 #### 广泛用途 co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。 #### 返回Promise对象 async函数的返回值是 Promise 对象,可以用then方法指定下一步的操作。 ### 返回值 async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。 async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变 遇到return语句或者抛出错误(或者reject),会直接返回错误或者return后的内容。 ### await - 后面是一个 Promise 对象,返回该对象的结果 - 不是 Promise 对象,就直接返回对应的值 > 「紧跟着await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中」 #### 1 ```js async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); } async function async2() { console.log("async2"); } async1(); console.log('start') 'async start' 'promise' 'async1 end' 'start' ``` #### 2 ```js async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); setTimeout(() => { console.log('timer1') }, 0) } async function async2() { setTimeout(() => { console.log('timer2') }, 0) console.log("async2"); } async1(); setTimeout(() => { console.log('timer3') }, 0) console.log("start") 'async1 start' 'async2' 'start' 'async1 end' 'timer2' 'timer3' 'timer1' ``` #### 3 ```js async function async1 () { console.log('async1 start'); await new Promise(resolve => { console.log('promise1') }) console.log('async1 success'); return 'async1 end' } console.log('srcipt start') async1().then(res => console.log(res)) console.log('srcipt end') // pending 会一直await的 'script start' 'async1 start' 'promise1' 'script end' ``` ### 错误处理 当 await语句后跟随的promise rejected后(或者抛出错误),会直接结束函数的执行。有时为了能够前一个异步操作失败,也不要中断后面的异步操作,可以有如下两种方案: 1. 尾随catch函数 2. try catch 捕获异常 ```js async function f() { await Promise.reject('出错了') .catch(e => console.log(e)); return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // 出错了 // hello world ``` ```js async function f() { try { await Promise.reject('出错了'); } catch(e) { } return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // hello world ``` #### 1. ```js async function async1 () { await async2(); console.log('async1'); return 'async1 success' } async function async2 () { return new Promise((resolve, reject) => { console.log('async2') reject('error') }) } async1().then(res => console.log(res)) // 'async2' // Uncaught (in promise) error ``` #### 2. ```js async function async1 () { try { await Promise.reject('error!!!') } catch(e) { console.log(e) } console.log('async1'); return Promise.resolve('async1 success') } async1().then(res => console.log(res)) console.log('script start') 'script start' 'error!!!' 'async1' 'async1 success' ``` ### 并发和继发 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。例如下面的代码,不存在关系,还使用了异步串行,浪费时间。 ```js let foo = await getFoo(); let bar = await getBar(); ``` 可以改写如下: ```js // 写法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 写法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise; ``` ### 实现原理 是如何做到的next中参数传递给下一个参数? ```js var nums1 = await test(); // 是如何做到的呢? var nums2 = await test(nums1); ``` ```js function co (generator) { var g = generator(); function next (val) { var res = g.next(val); if(res.done) return res.value; res.value.then(val => { next(val); }) } next() } ``` ```js function co (gen) { return new Promise((resovle,reject ) => { var g = gen(); function next(val) { try{ var res = g.next(val) } catch(e) { return reject(e); } if(res.done) { return resolve(res.value); } } // 包装原始值 Promise.resolve(res.value).then( val => { next(val); } ) .catch(e => g.throw(err)) next(); }) } ``` ================================================ FILE: todo/fe/notes-JavaScript/高阶函数之after函数.md ================================================ ## 高阶函数之 after 函数 ### 异步并发控制 如何在多个异步函数中进行并发控制?例如现在有这样一个需求,希望在两个异步函数都完成后,才进行一些操作。 ```js let temp = {}; fs.readFile("./name.txt", "utf8", function (err, data) { tmep.name = data; }); fs.readFile("./age.txt", "utf8", function (err, data) { tmep.age = data; }); ``` 可以在回调函数中通过一个高阶函数来控制当前完成的并发数量,当到达上限后说明已经全部完成。 比如 name 文件和 age 文件各读取完毕后,会执行自己的回调函数,在回调函数中调用并发控制函数。 ```js function after(num, cb) { // 保证this指向,虽然用不到this return () => { // 函数执行后,会先将num减一,在进行判断 // 如果当前num === 0 说明已经满足了要求,可以执行cb if (--num === 0) { cb(); } }; } let controller = after(2, function () { console.log("并发完成数量以达到预期"); }); let temp = {}; fs.readFile("./name.txt", "utf8", function (err, data) { tmep.name = data; controller(); }); fs.readFile("./age.txt", "utf8", function (err, data) { tmep.age = data; controller(); }); ``` ================================================ FILE: todo/fe/notes-JavaScript/高阶函数之函数柯里化.md ================================================ ## 高阶函数之函数柯里化 ### 简单的例子 假设现在有一个需求,封装一个通用的判断类型的函数,用例如下: ```js isType("Array", []); // true isType("Array", {}); // false ``` 可以给出如下实现: ```js function isType(type, value) { return Object.prototype.toString.call(value) === `[object ${type}]`; } ``` 但如果我们希望缩小一个参数范围,例如这样的需求: ```js let fn = isType("Array"); fn("123"); // false fn([]); // true ``` 那么根据高阶函数的思想也不难实现,代码如下: ```js function isType(type) { reutrn function (value) { return Object.prototype.toString.call(value) === `[object ${type}]` } } ``` **其实这就是柯里化思想,将函数参数范围不断缩减。** ### 通用柯里化函数 如何将一个开发完成的函数进行柯里化操作?需求如下: ```js function sum(a, b, c, d, e, f) { return a + b + c + d + e + f; } let r = currying(sum)(1, 2)(3, 4)(5)(6); ``` 核心思路就是取到原函数的形参数量,在每层柯里化中进行判断,如果还没有达到数量,就一路柯里化下去。 实现如下: ```js let helper = (fn,...fisrtArgs) => (...secondArgs) => { fn.apply(null,fisrtArgs.concat(secondArgs))} let cur = (fn,length) => { length = length || fn.length; return (...args) => { // 判断参数长度是否达到可以执行的长度 if(args.length < length) { cur(helper.apply(null,[fn].concat(args)),length - args.length) }else { fn.apply(null,args) } } } ``` ================================================ FILE: todo/fe/notes-JavaScript/高阶函数之初见AOP.md ================================================ ## 高阶函数之初见 AOP ### 高阶函数的定义 满足如下条件即可称之为高阶函数: 1. 函数的参数是一个函数 (回调函数就是一种高阶函数) 2. 函数返回一个函数 当前这个函数也是一个高阶函数 ### 初见 AOP 假设现在我们有一个业务逻辑,里面的代码逻辑非常复杂,现在急需让你在执行该业务逻辑前先做点什么,那么我们可以通过如下的方式实现: 业务逻辑函数: ```js function say(a, b) { //... console.log("say", a, b); } ``` ```js Function.prototype.before = function (cb) { return function (...args) { cb(); this(...args); }; }; let beforeSay = say.before(function () { // 这里是你想提前做的事情 console.log("say before"); }); beforeSay("这里的参数会传递到主业务逻辑中,穿透你自己写的函数"); ``` ================================================ FILE: todo/fe/notes-nodeJs/1. NodeJs有哪些特点?应用场景又有哪些?.md ================================================ ## NodeJs 的特点有哪些 nodejs的初心是创建高性能webserver,因此开发nodejs的大哥总结了两点: - 事件驱动 - 非阻塞I/O 所以就敲定了nodejs的主要特点: - 事件驱动 + 事件回调 - 非阻塞I/O(并行处理) - 单线程(事件循环) ### 事件驱动 ### 事件回调函数 ### 单线程 > 最近在复习中,先把笔记保存好,后期会处理 ### 适合 Web 场景 1. 构建传统网站:不进行前后端分离,将视图渲染和数据库操作都放在一个项目里,这样 NodeJs 和传统后端语言没有什么区别。例如典型的 cnode 社区就是通过 NodeJs 编写的。 2. 构建 API 接口:由于一些大型工程的后端 API 接口相对复杂,因此在该基础上做一个封装代理,专门提供给前端使用还是很香的。 3. 构建 RPC 服务:微服务架构中的远程过程调用协议服务;其特点是从数据库中访问出来的数据,以 TCP 协议形式传输给调用方,相比于 HTTP 协议在传输上有明显的优势。 4. 前后端分离:前端页面服务化、服务端渲染、PWA TODO:构建 RPC 服务不是很理解 ### 高性能 1. 得益于 ChromeV8 引擎的加持,语言的执行速度非常快。 2. NodeJs 的特点:事件驱动 和 非阻塞 I/O 决定了 **天生异步** 的机制。 3. I/O 密集型网络应用的开发 TODO: IO 任务是异步的,集成到 libuv 的事件循环里才能让开发者对代码并发操作无感知? ### 简单 1. 语法简单(JavaScript 学习成本相对较低) 2. 开发简单:NodeJs 的内置模块和 npm 包都遵循“小而美”的理念,因此相对比较简单。 ### 灵活可拓展 1. 可以使用大量 npm 模块 2. 可以编写 C++代码进行拓展,实现对 CPU 密集型任务的胜任。 3. 架构互补:轻松的接入 JAVA、Rust 等基础后台,使得以业务边界进行服务拆分。 ### 应用场景 1. API 代理:为前端提供 API proxy,对后端 API 二次开发,提升开发效率。主要的框架有 koa 和 express 2. IM 即时聊天:基于 WebSocket 协议开发 3. 前端构建工具:辅助前端开发,在预编译和构建环节,提升前端开发效率。例如 webpack、Gulp 打包工具 4. 命令行工具:前端开发效率利器,例如 webpack-cli。 TODO:[npm 模块安装机制简介](http://www.ruanyifeng.com/blog/2016/01/npm-install.html) ================================================ FILE: todo/fe/notes-nodeJs/2. V8 libuv 和 Nodejs之间的关系?.md ================================================ ## V8 libuv 和 Nodejs 之间的关系 Nodejs 更像是 “站在了巨人的肩膀” 进行了一系列的拼凑... 因此他的高效离不开很多厉害的第三方类库和程序。 ![](NodeJs架构.jpg) ### V8 和 Nodejs Node 是构建在 V8 引擎之上的,V8 是通过 C 或者 C++编写的 **JavaScript 引擎**。他的作用是将 JavaScript 代码转化成底层的 C 或者 C++代码后再执行,以这种形式降低了偏底层的编程语言的学习成本。 #### V8 的特点 1. 高效:JIT 编译、垃圾回收、内敛缓存、隐藏类 2. 遵循 ECMAScript 3. 和 NodeJs 相互扶持... JIT 编译:Just-in-Time 编译,说白了就是即时编译。它编译出来的结果是机器码,而不是最终运行的字节码。以“空时间换空间”的思想,提高了 V8 在高效执行 js 代码的同时,对内存消耗尽量小。其流程如下所示: ![](JIT.jpg) XXX: 垃圾回收机制?隐藏类?(重新学浏览器时进行准备) ### libuv 和 Nodejs NodeJs 每个函数都是同步的,但 I/O 操作是异步的。所有 JavaScript 编写的异步 I/O 操作最终都会通过 libuv 库提供的事件循环来处理,简化了并发编程模型。 > libuv 是 一个专注于异步 I/O 的跨平台类库,最初专门为 Nodejs 而开发。 XXX: 解决刚需后,学习一下 C++ 和 libuv ### http-parser 和 NodeJs C 实现的 HTTP 消息解析器,主要用于解析 HTTP 协议的请求数据和返回数据。主要特点如下: - 持久化链接的流式处理(keep-alive) - 分段信息解码(chunks) 能够解析 HTTP 消息中的: - 消息头键值对(Header) - 内容长度(Content-length) - 请求方法 - 状态码 - 传输编码(transfer-encoding) - HTTP 版本 - 请求 URL - 消息体 ### OpenSSL 和 NodeJs OpenSSL 是大名鼎鼎的安全套接字层协议库,主要是通过 C 语言编写而成。实现了基本加密功能:SSL 与 TLS 协议。 NodeJs 在安全方面就是基于 OpenSSL 的一层封装,例如对 HTTPS 协议的支持。 ### zlib 和 NodeJs 在 NodeJs 中免不了对数据进行解压、压缩处理,大多数情况是使用 zlib 的一个封装而实现的。 ================================================ FILE: todo/fe/notes-nodeJs/3. 说说你对global 以及global.process的理解?.md ================================================ ## 3. 说说你对 global 以及 global.process 的理解? ### global 和 this 在 nodejs 官方网站对 global 有这样一段描述: > 这些对象在所有的模块中都可用。 以下的变量虽然看似全局的,但实际上不是。 它们仅存在于模块的作用域中 因此我们可以确定,在文件中全局代码里的 this 并不是指向 global 而是指向 model.exports ```js console.log(this === global); //false console.log(this === module.exports); // true ``` 但是在命令行中 this 则 等同于 global,原因是命令行只是执行指令,没有产生文件,因此不需要模块。 ![this](global和this.jpg) ### 全局对象 全局对象上的方法,大致可以分为:定时器类、辅助函数类、以及最重要的三个核心:模块、Buffer 和 process。 ### process 进程对象 官方有一页翻不完的介绍,在这里我捡几个我觉得比较重要的进行解释,如下: - process.platform - process.pid - process.argv - process.cwd - process.env - process.nextTick #### process.platform 和 process.pid 标识了当前运行 node 的平台,例如常见的有 darwin(mac),win32,linux 等。 用途:(太直白,但很重要)获取当前的环境... process.pid 会返回当前进程的一个唯一表示,在进程通信以及操作进程时非常有用。关于进程操作相关内容会抽出来另外一道题。 #### process.argv process.argv 属性会返回一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数。 第一个元素是 process.execPath.第二个元素是正被执行的 JavaScript 文件的路径。 其余的元素是任何额外的命令行参数。 用途:可以获取执行脚本时的参数,**写 cli**时候非常有用。例如我们可以通过如下代码收敛出用户传递的参数 ```js let userOptions = process.argv.slice(2); userOptions.reduce((memo, current, index, arr) => { if (current.startsWith("--")) { memo[current.slice(2)] = arr[index + 1]; } return memo; }, {}); ``` ![](argv.jpg) > process.execPath 属性返回启动 Node.js 进程的可执行文件的绝对路径名。 TJ 写的 commander 包 大部分的方法都是对 argv 进行封装,有兴趣的同学可以看看,写 Cli 利器。 #### process.cwd() 和 process.env `process.cwd() current working directory 的意思` 会返回返回 Node.js 进程的当前工作目录(也就是哪里运行的 node)。 用途: 默认情况下提供当前的目录列表给 http-server, 会把当前目录列表进行展示并开启一个静态服务器;**webpack 在查找配置**文件时,是在当前工作目录下进行查找的。 `process.env` 返回当前进程的环境变量,会用他来区分各种环境 (开发和线上环境) 例如我们可以设置这样的命令:`cross-env env=development && node xxxx(不同的环境变量)` 实现以不同环境变量运行 node #### nextTick `process.nextTick()` 方法将 callback 添加到下一个时间点的队列,并置于**微队列**中。 用途:通过这个方法可以在任何 I/O 操作之前,插上一脚 **详见事件循环** ================================================ FILE: todo/fe/notes-nodeJs/4. 说一下Liubv的事件循环机制.md ================================================ ## 说一下 Liubv 的事件循环机制 > 事件循环是 libuv 的核心,所有的 I/O 操作都建立在它之上。 ![20200611092438]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200611092438.png) NodeJs 最核心的两个部分:V8 引擎 和 Liubv ,V8 并不具备 I/O 操作的能力,是通过 Liubv 提供的该能力。在 V8 层面发起对文件、网络等 I/O 操作,会将该操作转化成 c++代码,并在 Liubv 事件循环中加入事件以及对应的回调处理,当 **Liubv 完成当前任务** 后,会调用注册的回调函数注入最终结果。 > Node.js 是单线程的,但 liubv 并不是单线程的,他依赖 Nodejs 启动时初始化的线程池 ### 初始化事件生命周期 Nodejs 在启动时会初始化事件循环,每次的事件循环包含如下 6 个阶段(都是宏任务): ![20200611093416]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200611093416.png) - timers:主要执行 setTimeout() 和 setInterval 设定的回调函数 - I/O callbacks:执行绝大多数的回调,除了 close、定时器回调 - idle :内部使用的,不需要关系 - poll: 获取当前 I/O 事件,适当等待定时器到达时间以及适当情况下会在这里阻塞 - check: 执行 setImmediate()设置的回调函数 - close callbacks: 执行 socket.on('close') 注册的回调函数 根据如下代码总结事件循环特点: ```js fs = require("fs"); fs.readFile(".gitignore", "utf8", () => { // poll阶段掉的文件读取操作 setImmediate(() => { console.log("setImmediate1"); }, 0); setTimeout(() => { console.log("setTimeout"); }, 0); setImmediate(() => { console.log("setImmediate2"); Promise.resolve().then(() => { console.log("promise2"); }); }); setImmediate(() => { console.log("setImmediate3"); }); setImmediate(() => { console.log("setImmediate4"); }); process.nextTick(() => { console.log("nextTick"); }); Promise.resolve().then(() => { console.log("promise1"); }); }); 结果为: nextTick promise1 setImmediate1 setImmediate2 promise2 setImmediate3 setImmediate4 setTimeout ``` **流程概述:** 当文件中脚本**执行完毕**后,会将 IO 读取操作注册到 poll 阶段,并查看当前微队列中是否存在待执行任务,由于代码中没有产生微任务因此会进行一次**新的事件循环**。 首先会检查 **timers 阶段**,由于 IO 回调还没有执行 不存在定时器。会移动到下一个阶段。 在 **poll 阶段**执行完毕 IO 操作后会执行回调函数,在回调函数中按照**先后顺序**分别向**指定的阶段**中注册了 4 个 setImmediate 定时器,一个 setTimeout,以及两个微任务:nextTick 和 Promise。执行完毕回调后,会移动到下一个阶段。 在 **check 阶段** 宏任务执行前,会先**清空微队列**中的任务。由于当前注册了两个微任务,因此会打印 nextTick 和 promise1。执行完毕后,才会查看当前 check 阶段注册的定时器,并依次执行。此时打印 setImmediate1 和 setImmediate2。 由于第二个宏任务又注册了 Promise,因此在执行 setImmediate3 这个任务前,会将该 promise 执行此时打印 promise2。 而后将 check 阶段的其他事件相继清空后,会移动到最后一个阶段**close callbacks** ,由于这个阶段没有事件,因此结束本轮循环。 再次执行循环时,在 **timers 阶段**会执行对应的定时器,此时会打印 setTimeout,这样循环往复,最后直至进程结束。 总结: - 宏任务执行完毕后检查一下当前的微任务队列是否有任务,清空微任务是一次性的(代码中没加测试,如果微任务中又注册了微任务,会在当前阶段直接干掉,不留到下一次事件循环中) > 注意是宏任务结束完毕后检查微任务,在 v8 创建执行上下文时会创建一个微队列,当全局代码执行完毕后弹出执行栈时,会检查微队列中是否有任务(检查点),如果有任务则会全部执行。 - nextTick 的优先级是高于 Promise 的 (因此先打印 nextTick) - 在每个阶段里,会将队列内的任务执行完毕后,或者执行的回调数量达到上线,才会进入到下一个阶段。 - 阶段里的**每个任务**都是一个**宏任务**,清空当前阶段任务时可能会被**微任务**插入执行 ### 底层实现 #### 观察者 在每个事件循环过程中,内部是通过观察者对事件**是否完成**进行监控。因此在每个循环中会有一个或多个观察者,通过询问这些观察者,当前是否有要处理的事件。 浏览器也是采用了类似的机制,例如当用户点击时,这些事件的产生都有对应的观察者。在 Node 中,事件来源主要有文件 I/O 和网络请求等。因此这些观察者有对应的 文件 I/O 观察者、网络 I/O 观察者等。 以简单的 fs.open()为例,Node 在执行异步 I/O 调用时的流程如下: ![](fs.open.jpg) JavaScript 调用 Node 的核心模块,核心模块调用 C++内建模块,而后通过 libuv 进行对系统的调用。 在 Liubv 中,我们封装了一个叫做**请求对象**的中间层,这个请求对象会被推入到 liubv 中 依赖的**线程池**中。在线程中如果有可用的线程,会调用对应的方法去做对应的事儿,比如会进行系统层面的文件读取操作。 到这里 JS 异步操作的第一个阶段已完成可以去做后续的操作,**不管在 I/O 线程池中是否阻塞,在 JS 层面都不会影响后面的操作,从而实现异步。** #### 执行回调函数 当线程池中 I/O 操作完毕后,会通知**事件循环**的**I/O 观察者**当前操作以完毕,并将请求对象加入到观察者的队列中,而后去做自己的事情。 当 I/O 观察者要执行回调时,会从请求对象身上获取**当时注册的事件回调处理函数**,并执行。从而达到**调用 JS 中传入的回调函数**。 整个流程如图所示: ![](liuv事件循环流程.jpg) ================================================ FILE: todo/fe/notes-nodeJs/5. 说说你对模块化的理解?.md ================================================ TODO: @阮一峰 ES6 模块和 CommonJs 模块的区别 ## 说说你对模块化的理解 模块化的好处: 1. **解决命名冲突**: 每个 js 文件都是一个模块,全局代码外面都会包装一个函数,实现私有化命名空间。 2. 高内聚低耦合 3. 便于维护和管理 ### CommonJs 规范 当 nodejs 问世时,由于 JavaScript 并没有实现模块化,对于当时 web 开发场景下没有模块化勉强接受,但对于服务器端开发没有模块化是绝逼不行的。后来由 CommonJs 社区制定了 Modules/1.0 规范,首次定义了 JavaScript 世界的模块化。 #### CommonJs 规范有哪些 - **规定了模块的标识**(书写时遵循的规则) - 定义了全局函数 require,通过传入模块的唯一标识,执行结果是其他模块暴露的 API - **require 函数中引入的模块还依赖其他模块,会依次进行加载** - 模块**加载失败**,require 函数会**抛出异常** - 模块通过变量 exports 向外暴露 API,exports 必须是一个对象,暴露的 API 作为对象的属性存在 #### Browserify VS AMD 当模块化在 nodejs 中展露头角后,CommonJs 社区想一统江湖,把前端浏览器模块化也拿下。但不料内部矛盾分裂出如下两派: - 先通过工具把现有模块化转化为浏览器上使用的模块,然后再使用 : Browserify、Webpack - 直接定义新的浏览器端 JS 规范: AMD CMD 以及统一后的 UMD > CMD 代表的是:seajs; AMD 代表的是:requirejs; umd 模块是统一模块规范 #### CommonJs 规范和 Nodejs 实现的差异性在哪里 体现在 module.exports 上: - 在 Nodejs 中 它是一个真正的对象,通过它向外进行接口暴露,而 exports 只是一个绑定在 module 身上的变量而已。 - 在 CommonJs 规范中是不存在 module.exports 这个概念的 #### CommonJs 循环引用 由于 CommonJs 模块的重要特性就是加载时执行,因此被加载脚本的代码在 require 时就会**全部执行完毕**,一旦出现某个模块被循环加载,就只能输出已执行的部分,还未执行的部分不会进行输出。 注意两点: - 输入的是被输出值的拷贝,而不是引用 - 在 CommonJs 中遇到循环加载时,返回的是以及执行部分的内容,而不是代码最终执行后的值,两者可能会导致差异的发生。 > 阮一峰《ES6》p 486, 注意模拟 a.js 和 b.js 的代码例子进行讲解,说清楚代码执行的过程 ### ES6 模块化 #### 尽量静态化 commonjs 规范静态引入 ES6 动态引入 (@阮一峰老师) 区别 node 中默认不支持 es6 模块(需要将文件后缀更改为 mjs,才可以支持模块化) > 目前开发你希望在 node 中使用 import export 需要使用 babel-node 存在动态更新,commonJs 规范 输出的是缓存值 #### import 全局单例模式,提升效果 #### export 和 export default区别 #### import() 异步加载,require 是 同步加载。 #### 循环引用 ES6 模块处理循环引用时,和 CommonJs 有本质的不同。是因为 ES6 采用的动态引用,如果使用 import 从一个模块中加载变量,该变量是一个指向被加载模块的引用。 如果加载的代码已经执行,发生循环引用时是不会像 CommonJs 一样交替进行的,而是直接走完。 > 阮一峰《ES6》 p 488 注意模拟 a.js 和 b.js 的例子,说清楚如果已经加载,不会重复执行。 #### type="module" ### Nodejs 模块 > 最新复习笔记,后期会融合到一起 在 Nodejs 中模块分为如下三类: 1. **核心模块/内置模块:** `require('fs') http、path`特点是不需要安装,引入的时候不需要增加相对路径或者绝对路径 2. **第三方模块:** `require('co')` 需要安装 3. **自定义模块:** `require('./my')` 需要通过绝对路径或者相对路径进行引入 node 中采用同步的方式读取文件,这样在项目运行时所有模块的导入已经完成,会对项目运行提供更好的性能。 #### 模块加载 1. 直接按名字加载 核心模块优先级最高,如果命名冲突时首要加载核心模块。文件模块只能按照路径形式进行加载,如果是目录路径,会首先查看是否存在 package.json 文件,按照其定义的`"main":`作为入口文件。如果不存在,则会查找 index.js 或.node 如果还没有则会报错:`Error: Cannot find module 'some-library'` 2. 查找 node_modules 目录进行加载 当 require 遇到一个既不是核心模块也不是文件路径时,会查找当前目录下 node_modules 中是否存在目标模块,如果不存在则会依次向上冒泡查找。 #### 如何区分使用 module.exports 和 exports module.exports 是真正的导出接口,exports 只是一个辅助工具用来收集,最终都会汇总到 module.exports 上。 不过需要注意的是,**如果显示的写出 module.exports 后,所有 exports 导出的都会被覆盖掉** 所有除了工具类使用 exports.xxx,绝大多数都使用 module.exports 进行导出。 #### 核心原理:函数封装器 在文件执行的过程中默认这个文件会被加一层函数,这样做的好处是: - 封闭作用域 - 提供模块特定的方法和属性 ```js exports = "hello" // 包裹后如下所示 function (exports, require, module, __filename, __dirname) { module.exports = 'hello'; return module.exports; }(exports, require, module, xxxx, xxx); ``` 由于这些参数:`exports require module __filename __direname` 是模块最外层函数的,因此可以在全局代码中直接访问使用。所以在使用层面这些参数可以称之为**全局变量**,但并不是 global 对象提供的,而是函数封装器提供的。 **\_\_filename:** 代码当前的这个文件 (绝对路径) **\_\_direname:** 代表的是当前文件运行的文件夹 (绝对路径) #### 模块化加载原理 1. 调用 require 方法,开始模块加载 2. 解析文件路径,获取对应的绝对路径 3. 创建一个模块实例,将绝对路径作为 id 传入 4. 尝试加载这个模块(根据后缀名调用不同的方法执行) 5. 加载模块时,通过 fs 读取内容,并将内容包装成一个函数 6. 通过 vm 提供的沙箱环境执行字符串获得一个函数 7. 将对应的参数传递给该函数,并执行 8. 最终可以通过`module.xxx`获取到模块暴露的 API/变量 模拟实现: **加载模块入口函数,在函数内部调用\_resolveFilename 对路径进行解析,获得一个绝对路径。** ```js function require(fileName) { _fileName = Module._resolveFilename(fileName); // _fileName 是一个绝对路径 } function Module(id) { this.id = id; // 保存的是对应模块的绝对路径 this.exports = {}; // 代表的是模块的返回结果 } Module._resolveFilename = function (fileName) { // 1. 获取绝对路径 let absPath = path.resolve(__dirname, filename); // 2. 查看路径是否存在 // 如果存在直接返回absPath // 如果不存在则增加 .js 或者.json后缀 (实际还有.node后缀) let isExists = fs.existsSync(absPath); if (isExists) { return absPath; } else { let keys = Object.keys(Module._extensions); for (let i = 0; i < keys.length; i++) { let newPath = absPath + keys[i]; // 依次拼接js/json 找到就停下 let flag = fs.existsSync(newPath); if (flag) { return newPath; } } // 都不存在则会报错 throw new Error("module not exists"); } }; Module._extensions = { '.js'(module) { }, '.json'(module) { } ``` **而后创建一个模块实例,将绝对路径作为 id 传入,并尝试加载这个模块。** ```js function require(filename) { filename = Module._resolveFilename(filename); // 2. 根据路径创建一个模块 let module = new Module(filename); // 3. 调用模块的静态方法,实现模块的加载 module.load(); return module.exports; ``` **load 方法做的事情很简单,就是根据当前绝对路径获取到后缀名后,将后缀名以及当前的 this(this 是当前 module 实例)传入到 Module.\_extensions 对象中执行即可。** ```js Module.prototype.load = function () { // 拿到对应的后缀名,调用对应的方法 let extname = path.extname(this.id); Module._extensions[extname](this); // module.exports = 'hello' }; ``` 首先我们要获取到文件的字符串内容,然后通过字符串拼接的方式,将他拼成一个字符串函数。而后调用 vm 提供的沙箱方法,将字符串执行获得一个函数 fn。 有了函数 fn 就可以执行他的同时改变 this 指向并传入指定参数,最终会将模块暴露的代码挂载到当前模块实例(也就是 this 身上),而后即可通过`module.xxx` 获取属性或 API 接口。 ```js Module._extensions = { ".js"(module) { // 1. 拿到当前文件的内容 let content = fs.readFileSync(module.id, "utf8"); // 2. 包装函数,将参数传递进去,是一个函数字符串 content = Module.wrapper[0] + content + Module.wrapper[1]; // 3. 需要让函数字符串变成真正的函数(不能使用new Function,会多一层函数) // XXX: 使用 runInThisContext 还有哪些好处? let fn = vm.runInThisContext(content); // 4. 获取当前module对象身上的exports属性 let exports = module.exports; // 5. 获取当前模块的dirname let dirname = path.dirname(module.id); // 让包装的函数执行,this就是module.exports,执行后会将属性挂载到module.exports上,在require方法结束后返回。 fn.call(exports, exports, req, module, module.id, dirname); }, ".json"(module) { let content = fs.readFileSync(module.id, "utf8"); module.exports = JSON.parse(content); }, }; Module.wrapper = [ `(function(exports,require,module,__filename,__dirname){`, `})`, ]; ``` ================================================ FILE: todo/fe/notes-nodeJs/README.md ================================================ # Node.js **介绍:** - [x] [NodeJs 有哪些特点?应用场景又有哪些?](1.%20NodeJs有哪些特点?应用场景又有哪些?.md) - [x] [NodeJs 都借助哪些第三方工具?](2.%20V8%20libuv%20和%20Nodejs之间的关系?.md) **模块化:** - [x] [说说你对模块化的理解?](5.%20说说你对模块化的理解?.md) **异步I/O:** > 这部分 之前的笔记总结的不够好,目前已手稿为准 - [x] [说一下 Nodejs 的异步I/O](说一下Nodejs的异步编程吧.md) - [x] [说一下 Liubv 的事件循环机制?](4.%20说一下Liubv的事件循环机制.md) - [ ] 重新码字,输出 --- > 未处理 [说说你对 global 以及 global.process 的理解?](notes-nodeJs/3.%20说说你对global%20以及global.process的理解?.md.md) ================================================ FILE: todo/fe/notes-nodeJs/notes-htmlcss/flex 布局.md ================================================ ## 基本术语 display: flex 属性 形成 flex container,直接子元素就此成为 flex items,如下图: ![20200613211743]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200613211743.png) 区分主轴和交叉轴的概念,当direction改变后,主轴和交叉轴随之改变。 ![20200613211759]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200613211759.png) ## 属性 ### flex container dispaly:定义一个 flex container **取值:flex || inline-flex** flex-direction:设置主轴的方向,控制 flex items 的方向 **取值:row, row-reverse, column, column-reverse 默认值row** flex-wrap:设置 flex items 是否需要换行(当一行显示不下的时候)**取值:wrap, no-wrap, wrap-reverse 默认值no-wrap** justify-content:定义 flex items 在主轴上的分布方式 **取值:flex-start, flex-end, center, space-around, space-between 默认值为flex-start** > space-around:多余空间在flex items之间平分 space-between:多余的空间平均分配到每个items的左右(理解成左右margin) align-items: 定义 flex items 在交叉轴上的对齐方式 **取值:flex-start, flex-end, center, baseline stretch 默认值为stretch** > baseline是基于文字高度;stretch是拉伸到交叉轴高度 align-content:用来定义 flex item 多行显示时,在交叉轴上的分布方式。 其取值和 align-items 相同; > 注意 strech的拉伸标准是交叉轴上剩余空间的分配 ### flex item属性 align-self:改变单个flex item 的交叉轴对齐方式,默认是继承flex container的 align-items属性 order:定义排列顺序,类比定位属性中的z-index flex-grow:用于分配剩余空间的比例 ![20200613211951]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200613211951.png) flex-shrink: 空间不足时如何缩放。只要空间不够时,会默认进行缩放 flex-shrink = 1 查看控制台 ![20200613212001]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200613212001.png) 因此计算公式为: 实际宽度 = 初始宽度 - 分配的缩放空间 分配的缩放空间 = ((all flex item - flex container) / shrink 总量 ) * 系数 flex-basis:权重高的width属性,用于标识主轴方向上的大小。 ### flex 可选值 **单值语法**: 值必须为以下其中之一: * 一个无单位数( [number](https://developer.mozilla.org/zh-CN/docs/Web/CSS/number) ): 它会被当作 [flex-grow](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-grow) 的值。 * 一个有效的宽度( [width](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width) )值: 它会被当作 [flex-basis](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-basis) 的值。 * 关键字 [none](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex$edit#none) ,auto或initial. **双值语法**: 第一个值必须为一个无单位数,并且它会被当作 [flex-grow](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-grow) 的值。第二个值必须为以下之一: * 一个无单位数:它会被当作 [flex-shrink](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-shrink) 的值。 * 一个有效的宽度值: 它会被当作 [flex-basis](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-basis) 的值。 **三值语法:** * 第一个值必须为一个无单位数,并且它会被当作 [flex-grow](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-grow) 的值。 * 第二个值必须为一个无单位数,并且它会被当作 [flex-shrink](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-shrink) 的值。 * 第三个值必须为一个有效的宽度值, 并且它会被当作 [flex-basis](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-basis) 的值。 ================================================ FILE: todo/fe/notes-nodeJs/notes-htmlcss/html汇总.md ================================================ ## html常见高频题 ### 说说你理解的语义化 所谓 web 语义化,从广义上来说,不仅要使机器(搜索引擎等)易于理解,也要使人易于理解。在团队协作开发中,对人的易于理解显得尤为重要了,一个莫名其妙的 div 会让后续的开发或者维护者一头雾水,增加了协作成本。 ### doctype 的作用 DOCTYPE标签是一种标准通用标记语言的文档类型声明,它的目的是要告诉语言解析器,它应该使用什么样的文档类型定义(DTD)来解析文档 (是混杂模式还是标准模式,两者在渲染css时呈现出差异性) ### 常见的块级、行级元素有哪些?特点是什么 常见的块级元素和行内元素 - 块级元素 (block-level) : div h1~h6 p hr form ul dl ol pre table li dd dt tr td th - 行内元素 (inline-level):em strong span a br img button input label select textarea code script - 块级元素和行内元素的区别 - 块级元素占据的是一整行 , 而行内元素占据的空间是它自身,多个行内元素可以并排排列; - 块级元素可以直接设置宽度、高度 , 而行内元素不能设置宽高(行内元素的高度由line-height决定,宽度由具体的内容来决定) - 块级元素可以包含块级和行内, 行内只能包含文本和行内 - 块级元素可以设置margin和padding, 而行内元素上下的margin、padding不生效, 只有左右生效( 此时上下padding虽不会占用空间, 但是会占用背景色) - 块级元素使用margin: 0 auto;居中,行内元素,设置text-align: center; 达到居中目的(注意:text-align:center;仅作用在块级元素上,所以text-align:center;这个属性应该设置在行内元素的block级父元素上) - 行内元素可以“感受”到浮动元素的存在 ### form常见字段有哪些? action: ``` 一个处理此表单信息的程序所在的URL。此值可以被 export default { name: 'App', data () { return { list : [1,2,3] } }, methods : { async addItem () { this.list.push(`${Date.now()}`) const ulElem = this.$refs.ul1 console.log(ulElem.childNodes.length); } } } ```
![image-20200216153637680.png](https://cdn.nlark.com/yuque/0/2020/png/481272/1581850749675-5ed9f505-524f-4918-ab13-164e1d2a68e5.png#align=left&display=inline&height=456&margin=%5Bobject%20Object%5D&name=image-20200216153637680.png&originHeight=456&originWidth=2948&size=136838&status=done&style=none&width=2948)

使用$nextTick将获取最新DOM状态推入到事件循环中;
> Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。 ```vue
  • {{item}}
export default { name: 'App', data () { return { list : [1,2,3] } }, methods : { async addItem () { this.list.push(`${Date.now()}`) const ulElem = this.$refs.ul1 await this.$nextTick () console.log(ulElem.childNodes.length); } } } ```
![image-20200216153506893.png](https://cdn.nlark.com/yuque/0/2020/png/481272/1581850778098-f5aa1df3-d7a5-41ee-a13f-a931baba5834.png#align=left&display=inline&height=820&margin=%5Bobject%20Object%5D&name=image-20200216153506893.png&originHeight=820&originWidth=2960&size=155992&status=done&style=none&width=2960)
### keep-alive 和 LRU算法 使用该技术可以将组件进行缓存,使得换组件切换时不需要重新渲染,从而提高性能;
```vue
``` > 在 2.2.0 及其更高版本中,activated 和 deactivated 将会在  树内的所有嵌套组件中触发。 ### 如何抽离相同逻辑? ### 会用指令吗?说说 #### 全局指令和自定义指令 #### 指令的钩子函数 **案例**:[实现v-click-outside](1)
## Vuex ### 简述vuex - vue 的状态管理模式 - 集中式存储组件间共享的状态 - 保证状态以可预测的方式进行更新 ### 解决的问题 维持vue的核心理念:单向数据流
![image.png](https://cdn.nlark.com/yuque/0/2020/png/481272/1582461839496-77a43f95-f83b-4229-90ea-1350348eacd3.png#align=left&display=inline&height=267&margin=%5Bobject%20Object%5D&name=image.png&originHeight=866&originWidth=1280&size=53747&status=done&style=none&width=394)

在多个组件共享状态时存在以下问题 - 多个视图共享一个状态 - 多个视图存在不同行为更改同一状态 ### 解决方案 全局单例模式,能够在任何组件中获取状态,同时触发行为
### 流程
![image.png](https://cdn.nlark.com/yuque/0/2020/png/481272/1581983107677-33fc2639-1cdb-4c86-8022-25bb87505d17.png#align=left&display=inline&height=276&margin=%5Bobject%20Object%5D&name=image.png&originHeight=551&originWidth=701&size=33979&status=done&style=none&width=350.5)
关键点 - state  - getters - mutations - actions - modules ### 原理 由于vuex是一个插件,我们通过使用install进行安装,在安装过程中,我们在beforeCreate生命周期将$options中的store属性进行挂载到this上,以便所有组件实例都可以通过this.$store进行访问。

Vuex是支持响应式的,实现响应式是通过new Vue 将store实例放入到data中进行返回。

由于vuex是支持模块化管理,因此我们需要将多个模块树最终合并成一颗模块树。因此我们最重要的是需要建立模块之间的父子关系;
在vuex内部通过递归调用register将模块进行初始化树状结构。其中关键代码就是通过使用reduce查找到该模块对应的父级。

最后我们将这些参数进行安装到最终那个课模块树上。

实现mutations actions getters等事件的处理是在内部通过发布订阅模式
## Vue Router
================================================ FILE: todo/fe/notes-vue/来,唠一下生命周期.md ================================================ ## 简述生命周期 生命周期的本质是在组件初始化过程中(数据,事件,模板编译,挂载实例,销毁实例)会运行指定的生命周期钩子函数,从而给我们在不同时机自定义代码的能力 总体分为四个阶段: ### 初始化阶段 (new Vue) 初始化属性 事件 响应式数据等 • new -> callHooks(beforeCreate): 主要工作是合并参数、初始化生命周期,初始化事件方法,以及初始化render方法(vm._c这一类,为生成真实DOM做准备) •callHooks(beforeCreate) -> callHooks(created):初始化options中的inject和provide,初始化props,对data中进行数据劫持、初始化计算属性等,但该阶段并不能获取真实DOM。 #### ```js function Vue (options) { // 入口函数 this._init(options) } ``` #### _init ```js Vue.prototype._init = function (options?: Object) { // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { // vue有$options 属性 包含了当前的用户选项 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } // 初始化的过程 initLifecycle(vm) // 初始化父子关系 组件间的 initEvents(vm) initRender(vm) // vm._c 这个方法才能创建虚拟节点 callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props // MARK 响应式原理入口 initState(vm) // 初始化状态 initProvide(vm) // resolve provide after data/props callHook(vm, 'created') if (vm.$options.el) { // 创建一个渲染watcher 进行渲染组件的操作 vm.$mount(vm.$options.el) // 要做组件的挂载 } } ``` ### 编译阶段 (不一定存在) 将模板编译成渲染函数 * [ ] 梳理模板编译demo * [ ] 补充不存在编译阶段的情况 ### 挂载阶段 将模板渲染到页面中,完成后watcher开始工作。 • callHooks(created) -> callHooks(beforeMount): 该阶段标识渲染函数生成完毕 • callHooks(beforeMount) -> callHooks(mouted): 会渲染子组件,同时能够获取真实DOM (发送AJAX比较好) ### 更新阶段 当数据发生改变时,watcher通知虚拟DOM更新视图。 • callHooks(beforeMount):更新前,触发;可以增加一些数据更新,不会导致多次触发更新 • callHooks(Updated):更新完毕,不要在内部触发进行更新,可能会导致死循环 ### 销毁阶段 -> destoryed 将自身从父组件中删除,移除事件监听,解绑watcher,防止内存泄露; • callHooks(beforeDestoryed):更新前,触发;可以增加一些数据更新,不会导致多次触发更新 • callHooks(destoryed):更新前,触发;可以增加一些数据更新,不会导致多次触发更新 * [ ] 修改 ### 源码解析 ================================================ FILE: todo/fe/notes-vue/渲染详细流程.md ================================================ ================================================ FILE: todo/fe/notes-vue/虚拟DOM.md ================================================ ## 虚拟DOM ### 钩子函数 ================================================ FILE: todo/fe/notes-vue/说一下nextTick?.md ================================================ ## 说一下\$nextTick? ### 特点 将 API 中注册的回调函数延迟到 DOM 更新后执行。 > 需要注意的是,Vue 原型上的\$nextTick 只是对全局方法 Vue.nextTick 的一个代理。 特点: - 一轮事件循环中多次调用 nextTick 只会向任务队列中添加一个任务 - 如果没有提供回调函数,在支持 Promise 的情况下会返回一个 Promise - 在不支持 Promise 的情况下,会进行优雅降级 下面对这几个关键点一一进行解释 ### DOM 更新(异步更新队列)和延迟 当状态发生改变后,会通知到组件中对应的的 watcher,而后通过虚拟 DOM 进行对整个组件的 diff,而后更新 DOM。如果在这过程中有很多数据发生改变,那么 watcher 就会接受到对应数量的通知,从而进行多次更新(频繁)。但在真正实现中虚拟 DOM 中会将所有状态修改完毕后,才会一次性的将整个组件的 DOM 进行渲染,并不会多次渲染 DOM。 #### 解决方案 在内部将通知到的 watcher 添加到队列中进行缓存,并进行去重操作,如果出现相同 watcher,则说明后面的数据变更是新的,覆盖旧 watcher 即可。 只有队列中不存在这个 watcher 才会加入到队列中,当进行到下一次事件循环中,清空该队列即可。从而保证同一个事件循环中,多次更改数据只进行一次视图更新操作。 #### 源码位置 当数据发生改变后触发 dep 的 notify 方法,该方法会执行所有依赖(watcher)的 update 方法。 ```js notify () { const subs = this.subs.slice() // ... for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } ``` 在 watcher 的 update 方法中,如果当前 watcher 的类型是组件渲染 watcher,那么会调用调度器中的 queueWatcher。 ```js update () { ... queueWatcher(this) } ``` 在调度器 queueWatcher 中,最终会将需要更新的 watcher 通过 nextTick 进行处理并加入到 nextTick 的缓存队列中。 #### 延迟 DOM 更新后执行 因此只要保证在修改完状态后再调用 nextTick,就能够保证任务添加到队列中的顺序是在下一轮事件循环时,执行任务的顺序。 ### 多次调用只会向队列中添加一个任务 在实现 nextTick 时,内部有一个队列用于存放当前事件循环中所有注册的回调函数。在本轮事件循环中只会向消息队列中注册一个任务,其实现是通过一个状态位 `pending` 实现的。 当多次调用时,只会将回调函数注册到队列中,当消息队列中的任务触发时,会依次执行队列中的回调函数。 ```js /** * 将当前缓存队列中,所有的回调一次性清空,并将pending设置false,保证下次能够正确进入 */ function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } } // 简化版的timerFunc const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) } export function nextTick(cb?: Function, ctx?: Object) { // 将回调添加到队列中 callbacks.push(() => { if (cb) { cb.call(ctx); }); // 没有向消息队列中添加任务,才可以调用timerFunc() if (!pending) { pending = true; // 在本轮中,不能再调用timerFunc() timerFunc(); // 该方法用于将 } } } ``` ### 优雅降级 根据当前环境的支持情况,会进行优雅降级,其顺序是 Promise -> MutationObserver -> setImmediate -> setTimeout setImmediate 和 setTimeout 本质上没有什么区别,只不过一个是 IE 独占的。 ```js if (typeof Promise !== "undefined" && isNative(Promise)) { const p = Promise.resolve(); timerFunc = () => { p.then(flushCallbacks); }; isUsingMicroTask = true; } else if ( !isIE && typeof MutationObserver !== "undefined" && (isNative(MutationObserver) || MutationObserver.toString() === "[object MutationObserverConstructor]") ) { let counter = 1; const observer = new MutationObserver(flushCallbacks); const textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true, }); timerFunc = () => { counter = (counter + 1) % 2; textNode.data = String(counter); }; isUsingMicroTask = true; } else if (typeof setImmediate !== "undefined" && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks); }; } else { timerFunc = () => { setTimeout(flushCallbacks, 0); }; } ``` - [ ] 对于返回 promise 这里,不明白怎么就自动将 then 方法加入到队列中去的,复习完 promise 再看看 - [ ] Vue.nextTick 不会,此时回调函数中的 this 指向谁?( 回调函数中 this 会自动绑定到调用它的实例上(Vue.nextTick 不会,此时的 this 指向谁?)) - [ ] MutationObserver 在 nextTick 中的应用,以及他的基本使用方法。 ================================================ FILE: todo/fe/notes-vue/说一下你对$nextTick的理解.md ================================================ ## 说一下\$nextTick? ### 特点 将 API 中注册的回调函数延迟到 DOM 更新后执行。 > 需要注意的是,Vue 原型上的\$nextTick 只是对全局方法 Vue.nextTick 的一个代理。 特点: - 一轮事件循环中多次调用 nextTick 只会向任务队列中添加一个任务 - 如果没有提供回调函数,在支持 Promise 的情况下会返回一个 Promise - 在不支持 Promise 的情况下,会进行优雅降级 下面对这几个关键点一一进行解释 ### DOM 更新(异步更新队列)和延迟 当状态发生改变后,会通知到组件中对应的的 watcher,而后通过虚拟 DOM 进行对整个组件的 diff,而后更新 DOM。如果在这过程中有很多数据发生改变,那么 watcher 就会接受到对应数量的通知,从而进行多次更新(频繁)。但在真正实现中虚拟 DOM 中会将所有状态修改完毕后,才会一次性的将整个组件的 DOM 进行渲染,并不会多次渲染 DOM。 #### 解决方案 在内部将通知到的 watcher 添加到队列中进行缓存,并进行去重操作,如果出现相同 watcher,则说明后面的数据变更是新的,覆盖旧 watcher 即可。 只有队列中不存在这个 watcher 才会加入到队列中,当进行到下一次事件循环中,清空该队列即可。从而保证同一个事件循环中,多次更改数据只进行一次视图更新操作。 #### 源码位置 当数据发生改变后触发 dep 的 notify 方法,该方法会执行所有依赖(watcher)的 update 方法。 ```js notify () { const subs = this.subs.slice() // ... for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } ``` 在 watcher 的 update 方法中,如果当前 watcher 的类型是组件渲染 watcher,那么会调用调度器中的 queueWatcher。 ```js update () { ... queueWatcher(this) } ``` 在调度器 queueWatcher 中,最终会将需要更新的 watcher 通过 nextTick 进行处理并加入到 nextTick 的缓存队列中。 #### 延迟 DOM 更新后执行 因此只要保证在修改完状态后再调用 nextTick,就能够保证任务添加到队列中的顺序是在下一轮事件循环时,执行任务的顺序。 ### 多次调用只会向队列中添加一个任务 在实现 nextTick 时,内部有一个队列用于存放当前事件循环中所有注册的回调函数。在本轮事件循环中只会向消息队列中注册一个任务,其实现是通过一个状态位 `pending` 实现的。 当多次调用时,只会将回调函数注册到队列中,当消息队列中的任务触发时,会依次执行队列中的回调函数。 ```js /** * 将当前缓存队列中,所有的回调一次性清空,并将pending设置false,保证下次能够正确进入 */ function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } } // 简化版的timerFunc const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) } export function nextTick(cb?: Function, ctx?: Object) { // 将回调添加到队列中 callbacks.push(() => { if (cb) { cb.call(ctx); }); // 没有向消息队列中添加任务,才可以调用timerFunc() if (!pending) { pending = true; // 在本轮中,不能再调用timerFunc() timerFunc(); // 该方法用于将 } } } ``` ### 优雅降级 根据当前环境的支持情况,会进行优雅降级,其顺序是 Promise -> MutationObserver -> setImmediate -> setTimeout setImmediate 和 setTimeout 本质上没有什么区别,只不过一个是 IE 独占的。 ```js if (typeof Promise !== "undefined" && isNative(Promise)) { const p = Promise.resolve(); timerFunc = () => { p.then(flushCallbacks); }; isUsingMicroTask = true; } else if ( !isIE && typeof MutationObserver !== "undefined" && (isNative(MutationObserver) || MutationObserver.toString() === "[object MutationObserverConstructor]") ) { let counter = 1; const observer = new MutationObserver(flushCallbacks); const textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true, }); timerFunc = () => { counter = (counter + 1) % 2; textNode.data = String(counter); }; isUsingMicroTask = true; } else if (typeof setImmediate !== "undefined" && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks); }; } else { timerFunc = () => { setTimeout(flushCallbacks, 0); }; } ``` - [ ] 对于返回 promise 这里,不明白怎么就自动将 then 方法加入到队列中去的,复习完 promise 再看看 - [ ] Vue.nextTick 不会,此时回调函数中的 this 指向谁?( 回调函数中 this 会自动绑定到调用它的实例上(Vue.nextTick 不会,此时的 this 指向谁?)) - [ ] MutationObserver 在 nextTick 中的应用,以及他的基本使用方法。 ================================================ FILE: todo/fe/notes-vue/说说指令的原理.md ================================================ ## 指令的原理 ### 指令生效流程 ### v-if / v - for ### v-on v-once ### 自定义指令 ================================================ FILE: todo/fe/notes-vue/面试题.md ================================================ ## 提纲 - [提纲](#提纲) - [感性认知](#感性认知) - [什么是命令式和声明式?](#什么是命令式和声明式) - [谈谈你对 MVC 和 MVVM 的理解?](#谈谈你对-mvc-和-mvvm-的理解) - [数据部分](#数据部分) - [说一下响应式数据的原理?](#说一下响应式数据的原理) - [数组是如何实现响应式原理的?](#数组是如何实现响应式原理的) - [\$set 和 \$delete 原理知道吗?](#set-和-delete-原理知道吗) - [\$set](#set) - [\$delete](#delete) - [vue3 对响应式做了改进,有了解吗?为什么这样做?](#vue3-对响应式做了改进有了解吗为什么这样做) - [组件中的 Data 为什么是一个函数?为什么 new Vue 中的 data 可以是一个对象?](#组件中的-data-为什么是一个函数为什么-new-vue-中的-data-可以是一个对象) - [computed 、methods 和 \$watch 之间的区别?](#computed-methods-和-watch-之间的区别) - [使用层面的区别](#使用层面的区别) - [实现层面](#实现层面) - [指令](#指令) - [v-for 中 为什么要用 key?](#v-for-中-为什么要用-key) - [v-for 和 v-if 为什么不能连用?](#v-for-和-v-if-为什么不能连用) - [v-if 和 v-show 的区别?](#v-if-和-v-show-的区别) - [v-model 原理?自定义组件的 v-model?](#v-model-原理自定义组件的-v-model) - [vue 中事件绑定的原理?和 React 有什么区别?](#vue-中事件绑定的原理和-react-有什么区别) - [自定义指令知道吗?](#自定义指令知道吗) - [指令的钩子函数](#指令的钩子函数) - [钩子函数参数](#钩子函数参数) - [请你实现一个v-click-outside功能](#请你实现一个v-click-outside功能) - [组件](#组件) - [生命周期](#生命周期) - [分为四个阶段](#分为四个阶段) - [钩子函数](#钩子函数) - [组件之间的通信方式?](#组件之间的通信方式) - [父子: props + \$emit/$on](#父子-props--math-xmlnshttpwwww3org1998mathmathmlsemanticsmrowmiemimimmimiimimitmimi-mathvariantnormalmimrowannotation-encodingapplicationx-texemitannotationsemanticsmathemiton) - [父子: \$parent \$children](#父子-parent-children) - [兄弟:通过共同祖辈组件(\$parent或$root)](#兄弟通过共同祖辈组件math-xmlnshttpwwww3org1998mathmathmlsemanticsmrowmipmimiamimirmimiemiminmimitmimtext或mtextmrowannotation-encodingapplicationx-texparent或annotationsemanticsmathparent或root) - [兄弟:Bus 总线(状态难于维护,代码可读性差,容易混乱)](#兄弟bus-总线状态难于维护代码可读性差容易混乱) - [祖先后代关系:provide inject(单向 祖先到后代)](#祖先后代关系provide-inject单向-祖先到后代) - [\$dispatch(向上传播) 和 \$broadcast (向下传播)](#dispatch向上传播-和-broadcast-向下传播) - [vuex 咋的都行](#vuex-咋的都行) - [ajax 放在那里合适?组件销毁时需要做什么?](#ajax-放在那里合适组件销毁时需要做什么) - [虚拟 DOM](#虚拟-dom) - [简述一下虚拟 DOM](#简述一下虚拟-dom) - [diff 算法的时间复杂度?它是如何优化的?](#diff-算法的时间复杂度它是如何优化的) - [简述 patching 流程?](#简述-patching-流程) - [能简单描述一下 vnode 的结构吗?](#能简单描述一下-vnode-的结构吗) - [特性](#特性) - [说说模板编译?](#说说模板编译) - [vue 为什么采用异步渲染视图?\$nextTick 有了解吗,实现原理是什么?](#vue-为什么采用异步渲染视图nexttick-有了解吗实现原理是什么) - [什么是作用域插槽?](#什么是作用域插槽) - [说说你对 keep-alive 的理解?(LRU 算法)](#说说你对-keep-alive-的理解lru-算法) - [如何抽离相同逻辑?](#如何抽离相同逻辑) - [场景题](#场景题) - [封装过组件吗?如何设计一个组件?](#封装过组件吗如何设计一个组件) - [表单验证组件是如何设计的?](#表单验证组件是如何设计的) - [轮播图组件如何设计?](#轮播图组件如何设计) - [如何设计一个购物车组件?](#如何设计一个购物车组件) Table of contents generated with markdown-toc ## 感性认知 ### 什么是命令式和声明式? 声明式操作 DOM 指的是只需要描述数据和视图的关系即可得到 DOM(vm 层帮你做了),而命令式就是一条条计算机等待执行的指令(解释的不好) ### 谈谈你对 MVC 和 MVVM 的理解? MVVM 就是将 MVC 中的 controller 层 换成 VM 层,视图层和 VM 层是双向绑定关系,视图层的变化会反应到 VM 层,VM 层的变化会反应到视图层。 [阮一峰老师讲解 MVC 和 MVVM 的关系](http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html) ## 数据部分 ### 说一下响应式数据的原理? 关键的两步: 1. 对**数据劫持**,使得能够在`getter`中收集依赖(当前组件的 watcher),在`setter`中通知依赖。 2. 将当前组件的 watcher 和`dep`进行**双向绑定**,使得状态变更时能够正确通知到依赖。 - [ ] 响应式原理实现笔记链接 - [ ] 刘博文(图片) ### 数组是如何实现响应式原理的? 使用**函数劫持**的方式,劫持了数组的方法,Vue 将**data 中的数组**进行了原型链重写,指向了自己定义的数组原型方法。这样当调用**能够改变数组**的 api 时,可以进行通知依赖。如果数组中包含着引用类型,会对数组中的引用类型进行递归响应式处理,同时对新添加的元素也会进行响应式处理。 能够改变数组的方法有: - push/pop - shift/unshift/splice - sort/reverse > 通过获取 splice 方法的 arguments[2] 既可获得新增元素 ### \$set 和 \$delete 原理知道吗? #### \$set \$set 方法用于在指定对象/数组上添加属性,如果这个对象是响应式的,那么该属性会自动转化成响应式数据,并刷新视图。从而弥补`Object.defineProperty`的缺陷(不能侦测属性被添加的限制)。 大体分为三种情况: 1. 数组元素 (通过调用 splice 方法实现响应式) 2. 已存在相同的属性 (一定是响应式的,因此直接赋值即可) 3. 对象属性 (通过 defineReactive 实现响应式处理,并手动刷新视图) 源码如下: ```js export function set(target: Array | Object, key: any, val: any): any { // 数组的splice方法会触发视图更新 Vue.set(arr,0,100) if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key); target.splice(key, 1, val); return val; } // 如果是对象属性已经存在了 直接修改即可 if (key in target && !(key in Object.prototype)) { target[key] = val; return val; } const ob = (target: any).__ob__; // 如果这个对象不是响应式的对象 那就直接赋值,不需要刷新视图 if (!ob) { target[key] = val; return val; } // 响应式的target defineReactive(ob.value, key, val); ob.dep.notify(); // 手动通知视图刷新 return val; } ``` #### \$delete 该方法主要是用来弥补无法检测`delete`关键字对数据进行删除操作。因此可以通过\$delete 实现对数据进行删除后,刷新视图。 他的实现和\$set 异曲同工,区别点已在代码中注释。 ```js export function del(target: Array | Object, key: any) { // 数组的情况,直接调用spliceAPI既可实现 if (Array.isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1); return; } const ob = (target: any).__ob__; if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== "production" && warn( "Avoid deleting properties on a Vue instance or its root $data " + "- just set it to null." ); return; } // 不存在该属性,直接返回即可 if (!hasOwn(target, key)) { return; } // 直接删除属性,如果不是响应式的target,那么直接返回即可 delete target[key]; if (!ob) { return; } // 响应式的target 需要刷新一下视图 ob.dep.notify(); } ``` ### vue3 对响应式做了改进,有了解吗?为什么这样做? Proxy 的优势:`可以直接监听对象而非属性;可以直接监听数组的变化;` Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的; Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改; Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利; Object.defineProperty 的优势如下:`兼容性好,支持 IE9` 而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。 TODO: VUE3 - proxy + ES6 proxy ### 组件中的 Data 为什么是一个函数?为什么 new Vue 中的 data 可以是一个对象? 通常组件是可以**复用**的,那么在复用组件时,我们是希望 Data 是**相互独立互不影响**的。因为 data 是一个对象,如果在复用组件时,不能隔离其作用域的话会导致多个 data 指针同时操作一个内存数据(引用值的通病)。 因此可以通过将 Data 使用函数进行封装,实现作用域的隔离。 Vue 中的 data 可以是对象是因为 Vue 通常作为“根”存在,不存在复用的情况。 ### computed 、methods 和 \$watch 之间的区别? methods 中的方法不存在缓存,每次执行函数时不管监测的响应式数据是否发生改变,都会执行。而 computed 会 diff 这个依赖的响应式数据,只有当数据发生改变时才会重新执行。 #### 使用层面的区别 **computed:** 依赖其它属性值,存在缓存机制;只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值; **\$watch:** 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作; **场景区分:** 当我们需要进行数值计算,并且依赖于其它数据时,使用 computed;(利用 computed 的缓存特性,避免重新计算,提高性能) 在数据变化时执行异步操作或较大开销的操作,可以使用 \$watch #### 实现层面 篇幅太长,直接查阅这篇笔记 [\$watch 和 computed 实现原理]($watch和computed.md) ## 指令 ### v-for 中 为什么要用 key? > key 是为 Vue 中的 vnode 标记的唯一 id,通过这个 key,我们的 diff 操作可以更准确、更快速. diff 算法的过程中,先会进行新旧节点的**双端比较**,当**双端比较算法失败后**,会用**新节点的 key 与旧节点**进行比对,找差异. - 如果不加 key,那么 vue 会选择复用节点(**就地更新策略**),导致之前节点的状态被保留下来,会产生一系列的 bug. 例如复选框勾选 bug。 - key 的唯一性可以被 Map 数据结构充分利用实现 O(1)时间查找到指定元素. ### v-for 和 v-if 为什么不能连用? v-for 的权重比 v-if 要高,例如: ```vue
  • {{ user.name }}
``` 并不会先判断逻辑,再进行渲染;其逻辑等同于: ```js this.users.map(function (user) { if (user.isActive) return user.name; }); ``` 问题在于无论逻辑是否为真,每次重新渲染都会遍历整个列表; 根据业务场景进行区分选择解决办法: - 将 v-if 移动置外层容器元素 - 使用计算属性返回筛选后的列表 ```js
  • {{ user.name }}
computed: { activeUsers: function () { return this.users.filter(function (user) { // 逻辑内置 return user.isActive }) } } ``` ### v-if 和 v-show 的区别? - v-if 是真正的条件渲染,目的在于确保条件块中的**事件监听器**和**组件**适当的进行**销毁和重建**; - v-if 惰性渲染,条件为假:什么都不做(或在 DOM 中销毁);条件为真,渲染到 DOM 中;v-show 不管逻辑真假都会渲染到页面中,通过条件控制器 css 样式 - v-if 可以用在 template 上(因为是框架层面实现控制),但 v-show 只能用在真实 DOM 中 **应用场景区分:** - v-if:更高的切换开销;适合条件切换不频繁场景; - v-show:更高的初始渲染开销;适合条件切换频繁场景; > 注意这里可能会被追问 opacity、visibility、display 三个属性的比较 以及 keep-alive ### v-model 原理?自定义组件的 v-model? v-model 本质上是语法糖,是对:value 和 @input 的一个封装, 可以通过 model 属性的 prop 和 event 属性来进行自定义。原生的 v-model,会根据标签的不同生成不同的事件和属性。 - text 和 textarea 元素使用 value 属性和 input 事件; - checkbox 和 radio 使用 checked 属性和 change 事件; - select 字段将 value 作为 prop 并将 change 作为事件; > 在 React 中通过 state 属性和事件绑定 setState 也可实现双向绑定。 自定义组件的 v-model 是通过定义 model 属性,在里面声明 v-model 的属性,和响应处理函数。 ```js Vue.component("base-checkbox", { model: { prop: "checked", event: "change", }, props: { checked: Boolean, }, template: ` `, }); ``` ```js ``` ### vue 中事件绑定的原理?和 React 有什么区别? 在 vue 中,对**原生元素**采用的是`addEventListener`进行绑定的,**组件**事件绑定是通过 Vue 自定义的\$on 实现的。 TODO: \$on 实现原理 在 react 中,事件绑定是通过 SyntheticEvent 实例传递给你事件处理函数的,它是浏览器的原生事件的**跨浏览器包装器**。除兼容所有浏览器外,它还拥有和浏览器原生事件相同的接口。 vue 中的事件修饰符: ```html
...
...
...
...
``` ### 自定义指令知道吗? 指令是对普通 DOM 元素进行底层操作,指令的注册分为全局指令和组件内部指令。全局指令是通过`Vue.directive`进行注册,组件指令的注册是通过`directive属性`。 自定义指令可以提供对操作DOM进行封装,实现可复用,利于实际开发。 #### 指令的钩子函数 - bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。(此时绑定的元素还没有插入到真实DOM中) - inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 - update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。 - componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。 - unbind:只调用一次,指令与元素解绑时调用。 #### 钩子函数参数 - el:指令所绑定的元素,可以用来直接操作 DOM - binding:一个对象,包含很多属性,常见的如下: - name:指令名,不包括 v- 前缀。 - value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。 - oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。 - expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。 - arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo" - modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。 - vnode:Vue 编译生成的虚拟节点。 - oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。 #### 请你实现一个v-click-outside功能 **需求**:input获得焦点后显示面板,点击面板以外区域隐藏面板 1. 状态和方法 ```js data() { // 用于控制面板显示 return {visible:false} }, methods: { focus() { this.visible = true; }, hidden(){ this.visible = false } } ``` 2. 设计DOM结构 对面板和input做一个层包裹元素,目的在于当点击面板和input区域时,会判定属于显示范围内,不做隐藏。 如果点击内容超出当前区域,判定为不在显示范围,隐藏即可。 因此可以将指令封装到包裹元素身上。 ```html
面板
``` 3. 设计指令 需要用到指令绑定时的钩子函数,以及解绑时(防止内存泄漏)的钩子函数。 利用事件代理思想,把事件绑定到document身上,这样使得全局页面任何地方触发click事件后,都能够根据触发位置判断是否需要显示和隐藏。 ```js directives:{ clickOutside:{ bind(el,bindings,vnode){ // 绑定在el身上是为了解绑时能够获取到正确的元素 el.handler = (e)=>{ // 如果触发的位置是范围外 if(!el.contains(e.target)){ let method = bindings.expression; // 通过context能过获取当前vnode所在的上下文,也就是组件实例 // https://github.com/vuejs/vue/blob/dev/src/core/vdom/vnode.js vnode.context[method]() } } document.addEventListener('click',el.handler) }, unbind(el){ document.removeEventListener('click',el.handler) } } }, ``` ## 组件 ### 生命周期 在组件初始化过程中(数据,事件,模板编译,挂载实例,销毁实例)会运行指定的生命周期钩子函数,从而给我们在不同时机自定义代码的能力。 #### 分为四个阶段 - 初始化阶段 new Vue -> created 初始化属性 事件 响应式数据等 - 编译阶段 created -> beforeMount 将模板编译成渲染函数 - 挂载阶段 beforeMount -> mounted 将模板渲染到页面中,挂在完成后,watcher开始工作 - 更新阶段 当数据发生改变时,watcher通知虚拟DOM更新视图。 - 销毁阶段 -> beforeDestoryed -> destoryed 将自身从父组件中删除,移除事件监听,解绑watcher,防止内存泄露; #### 钩子函数 初始化阶段: - beforeCreate: 混合一些希望在每个组件中增加的一些特定属性(发生在嵌套组件中) - created:属性,方法,响应式等初始化已完毕。但并不能获取真实DOM。 挂载阶段: - beforeMount: 该阶段标识渲染函数生成完毕; - mouted: 会渲染子组件,同时能够获取真实DOM (发送AJAX请求) 更新阶段: - beforeUpdate 更新前,触发;可以增加一些数据更新,不会导致多次触发更新 - Updated 更新完毕,不要再内部触发进行更新,可能会导致死循环 销毁阶段: - beforeDestory 事件的移除,消除定时器 - destoryed todo:说一下组件的生命周期?嵌套的呢? todo:描述一下组件渲染和更新流程 ### 组件之间的通信方式? #### 父子: props + \$emit/$on 父到子:通过 v-bind + props 向下通信;子到父:通过$emit(子) $on (父)向上通信; #### 父子: \$parent \$children 子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中;\$children 是当前实例的直接子组件。 #### 兄弟:通过共同祖辈组件(\$parent或$root) #### 兄弟:Bus 总线(状态难于维护,代码可读性差,容易混乱) #### 祖先后代关系:provide inject(单向 祖先到后代) #### \$dispatch(向上传播) 和 \$broadcast (向下传播) 核心思想:递归获取上层节点,在这条路径上把所有注册的事件全部触发一遍 ```js Vue.prototype.$dispatch = function (eventName, value) { let parent = this.$parent; while (parent) { parent.$emit(eventName, value); parent = parent.$parent; } }; ``` 核心思想:获取当前节点的所有子节点,触发当前子节点的事件,并且需要递归触发子节点事件。 ```js Vue.prototype.$broadcast = function (eventName, value) { let children = this.$children; // recursion function broad(children) { children.forEach((child) => { child.$emit(eventName, value); if (child.$children.length > 0) { broad(child.$children); } }); } broad(children); }; ``` #### vuex 咋的都行 ### ajax 放在那里合适?组件销毁时需要做什么? ## 虚拟 DOM ### 简述一下虚拟 DOM ### diff 算法的时间复杂度?它是如何优化的? ### 简述 patching 流程? ### 能简单描述一下 vnode 的结构吗? ## 特性 ### 说说模板编译? ### vue 为什么采用异步渲染视图?\$nextTick 有了解吗,实现原理是什么? ### 什么是作用域插槽? ### 说说你对 keep-alive 的理解?(LRU 算法) ### 如何抽离相同逻辑? ## 场景题 ### 封装过组件吗?如何设计一个组件? ### 表单验证组件是如何设计的? ### 轮播图组件如何设计? ### 如何设计一个购物车组件? ================================================ FILE: todo/fe/notes-性能优化/浏览器/README.md ================================================ ## 性能优化 - 浏览器 - [x] [函数防抖](函数防抖.md) - [x] [函数节流](函数节流.md) [如何高性能渲染十万条数据?](../Notes/练习-实际开发/虚拟列表/README.md) ================================================ FILE: todo/fe/notes-性能优化/浏览器/函数节流.md ================================================ ## 函数节流 ### 适用场景 和函数防抖是一样的适用场景 ### 节流思路 - 时间戳 - 定时器 ### 代码 ```js /** * 时间戳思路 * @param {Function} fn 需要节流的函数 * @param {Number} wait - (ms) 节流的时间间隔 */ function throttle (fn,wait) { let prevTime = 0, context = null; return function (...args) { let curTime = +new Date(); if(curTime - prevTime > wait) { fn.apply(context,args) prevTime = curTime; } } } /** * 定时器版本 * @param {*} fn * @param {*} wait */ function throttle (fn,wait) { let timer = null, context = null; return function (...args) { context = this; if(!timer) { timer = setTimeout(function () { fn.apply(context,args) timer = null; },wait) } } } ``` ================================================ FILE: todo/fe/notes-性能优化/浏览器/函数防抖.md ================================================ ## 函数防抖 ### 适用场景 在前端开发中会遇到一些频繁的事件触发,比如: - window 的 resize、scroll - mousedown、mousemove - keyup、keydown 通常事件回调不会很复杂,浏览器按常理能够正常处理。可是如果是复杂的回调函数或是 ajax 请求呢?假设 1 秒触发了 60 次,每个回调就必须在 1000 / 60 = 16.67ms 内完成,否则就会有卡顿出现。 同时也会浪费很多网络请求,增加服务器压力,浪费钱。 ### 防抖思路 你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行。 ### 代码 ```js /** * * @param {Function} fn 需要防抖的函数 * @param {Number} wait(ms) 防抖的时间 */ function debounce(fn,wait) { let timer = null; let _ = function(...args) { let context = this; if(timer) clearTimeout(timer) timer = setTimeout(function () { fn.apply(context,args) },wait) } return _; } ``` ================================================ FILE: todo/fe/notes-浏览器/DOM.md ================================================ ## DOM汇总 DOM的本质:树形结构,用于描述xml/html的节点关系,同时提供API进行操作。 ### nodeType常见字段 文档节点(9) 元素节点(1) text节点(3) 注释节点(8) attr节点(2) ### DOM操作 #### 创建 createElement: [cloneNode](https://developer.mozilla.org/zh-CN/docs/Web/API/Node/cloneNode) :可以克隆节点,默认浅克隆,只对当前节点内容clone。需要克隆子节点需要设置深clone #### 查找获取 注意返回值为DomList的类数组,除了通过querySelectAll获取的为静态列表,其他都为元素出现在树中的顺序构成的动态的HTML集合(快照)。 #### 插入 innerHTML: 会将设置的字符串解析成HTML 或者XML 从而替换以前容器中的节点。使用该方式需要预防XSS攻击,HTML 5 中指定不执行由 innerHTML插入的 ` ================================================ FILE: todo/fe/vue 手写/src/compiler/index.js ================================================ import { parseHTML } from './parser-html'; /** * 核心思路就是将模板转化成AST例如:下面这段字符串 *

hello {{name}}

hello
====> to AST * * AST: let root = { tag:'div', attrs:[{name:'id',value:'app'}], parent:null, type:1, children:[{ tag:'p', attrs:[], parent:root, type:1, children:[ { text:'hello', type:3 } ] }] } * AST 在转成render函数 ====> _c("div",{id:app},_c("p",undefined,_v('hello' + _s(name) )),_v('hello')) * * 最后将render函数进行字符串包装并返回字符串(模板引擎实现套路) new Function + with(){} * TODO: new Function 细节? * @param template * @returns {Function} */ export function compileToFunction (template) { let rootAST = parseHTML(template); // 1. html字符串 => AST 语法树 let code = generator(rootAST); // 2.将AST语法树 => 生成字符串包装的render函数 // TODO: 这个renderFn到底是个什么? 模板引擎包装render函数字符串,等待被执行? let renderFn = new Function(`with(this){${code}`); return renderFn; } /** * 将AST树 转化成一段字符串形式的函数 * 主要工作就是拼接字符串... * @param {*} el */ function generate(el) { // [{name:'id',value:'app'},{}] {id:app,a:1,b:2} let children = genChildren(el); // 生成孩子节点的字符串 let code = `_c("${el.tag}",${ el.attrs.length ? genProps(el.attrs) : 'undefined' }${ children ? `,${children}` : '' }) ` return code; } /** * 例如考虑style样式的处理 * style="color: red;fontSize:14px" => {style:{color:'red'},id:name,} * @param {*} attrs */ function genProps(attrs) { // 处理属性 拼接成属性的字符串 let str = ''; for (let i = 0; i < attrs.length; i++) { let attr = attrs[i]; if (attr.name === 'style') { let obj = {}; attr.value.split(';').forEach(item => { let [key, value] = item.split(':'); obj[key] = value }); attr.value = obj; } str += `${attr.name}:${JSON.stringify(attr.value)},` } return `{${str.slice(0, -1)}}` // 去除最后一个, } function genChildren(el) { let children = el.children; if (children && children.length > 0) { return `${children.map(c => gen(c)).join(',')}` } else { return false; } } /** * 对每一个孩子 生成字符串格式 * @param {*} node * 跳了这个函数 */ function gen(node) { if (node.type == 1) { // 元素标签,需要递归处理 return generate(node); } else { let text = node.text; //
a {{ name }} b{{age}} c
let tokens = []; let match, index; // 每次的偏移量 buffer.split() let lastIndex = defaultTagRE.lastIndex = 0; // 只要是全局匹配 就需要将lastIndex每次匹配的时候调到0处 while (match = defaultTagRE.exec(text)) { index = match.index; if (index > lastIndex) { tokens.push(JSON.stringify(text.slice(lastIndex, index))); } tokens.push(`_s(${match[1].trim()})`); lastIndex = index + match[0].length; } if (lastIndex < text.length) { tokens.push(JSON.stringify(text.slice(lastIndex))) } return `_v(${tokens.join('+')})`; } } ================================================ FILE: todo/fe/vue 手写/src/compiler/parser-html.js ================================================ const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; // abc-aaa const qnameCapture = `((?:${ncname}\\:)?${ncname})`; // const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名 const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的 const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配属性的 const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的 >
let rootAST = null; let currentParent; // 父节点指针 let stack = []; // 用来维护父子关系的栈,同时能够对标签规范性(是否完全闭合)进行效验。 const ELEMENT_TYPE = 1; const TEXT_TYPE = 3; /** * 对字符串进行循环截取,生成rootAST同时处理好层级关系。 * @param html * @returns {*} */ export function parseHTML(html) { let stringStart, text, startTagMatch, endTagMatch; while (html) { stringStart = html.indexOf('<'); // 开始标签和结束标签两种情况 if (stringStart === 0) {// 如果当前索引为0 肯定是一个标签,但不定就是开始标签因此要兼容结束标签 startTagMatch = parseStartTag(); // 通过这个方法获取到匹配的结果为:tagName + attrs,或者是 结束标签 if (startTagMatch) { // 调用钩子函数将tagName 和 attrs进行传递 start(startTagMatch.tagName, startTagMatch.attrs); continue; // 如果开始标签匹配完毕后 进入下一次匹配 } endTagMatch = html.match(endTag); // 匹配结束标签 if (endTagMatch) { advance(endTagMatch[0].length); // 调用钩子函数将匹配到的结束标签进行传递 end(endTagMatch[1]); continue; } } // 纯文本情况 if (stringStart >= 0) { text = html.substring(0, stringStart); // 获取纯文本内容 } // 截取的纯文本内容 if (text) { advance(text.length); // 调用钩子函数将匹配到的文本内容进行传递 chars(text); } // 后续的还有DCOTYPE、注释节点等 详情查阅源码 } /** * 用于生成匹配到的标签名和属性对象 */ function parseStartTag() { let start = html.match(startTagOpen), end, attr; const match = { tagName: start[1], attrs: [] } // 匹配到,说明是一个开始标签 if (start) { // 删除已经匹配解析后的标签名 advance(start[0].length); // 一个一个的解析属性,因此使用while循环 while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { // 删除已经解析后的属性 advance(attr[0].length); match.attrs.push({ name: attr[1], // @puzz 和书上的不太一样,但意思就是说将匹配到的属性扔进attrs数组中 value: attr[3] || attr[4] || attr[5] }); } // 如果是闭合标签,则直接返回。 if (end) { // 去掉开始标签的 > advance(end[0].length); return match } } } /** * 用于删除以匹配到的字符串内容 * @param {*} n */ function advance(n) { html = html.substring(n); } return rootAST; } /** * 根据匹配到的内容创建AST节点 * @param {*} tagName * @param {*} attrs */ function createASTElement(tagName, attrs) { return { tag: tagName, type: ELEMENT_TYPE, children: [], attrs, parent: null } } /** * 遇到开始标签 则创建一个AST元素 * @param {*} tagName * @param {*} attrs */ function start(tagName, attrs) { let element = createASTElement(tagName, attrs); if (!root) { // 设置最初的树根节点 root = element; } currentParent = element; stack.push(element); // 将开始标签存放到栈中,使得能够通过stack维护父子关系 } function chars(text) { // 处理空格内容 text = text.replace(/\s/g, ''); if (text) { // 文本内容没有子节点 currentParent.children.push({ text, type: TEXT_TYPE }) } } function end(tagName) { let element = stack.pop(); // 拿到当前节点的开始标签 // TODO: 可以校验当前弹出的ast 和 入参是否相同,如果不同则说明发生问题 currentParent = stack[stack.length - 1];// 处理节点父子关系;stack.length - 1 =>> stack.top() if (currentParent) {//为了兼容root节点,root节点的currentParent 为 null element.parent = currentParent; currentParent.children.push(element); } } ================================================ FILE: todo/fe/vue 手写/src/index.js ================================================ import {initMixin} from './init'; function Vue(options){ // 进行Vue的初始化操作 this._init(options); } initMixin(Vue); // 给Vue原型上添加一个_init方法 export default Vue ================================================ FILE: todo/fe/vue 手写/src/init.js ================================================ import {initState} from './state' import {compileToFunction} from "./compiler"; import {mountComponent} from './lifecycle' // 在原型上添加一个init方法 export function initMixin(Vue){ /** * TODO:初始化主入口对吧?所做的事情都有哪些? * @param options 用户编写的options参数 * @private */ Vue.prototype._init = function (options) { const vm = this; // 对vue实例对象做一层代理,猜测作用:写法更加优雅语义更加明确; vm.$options = options; // 对参数进行缓存到vm对象上,方便后续的操作 initState(vm); // 初始化状态:TODO: vue中initState初始化属性的顺序是什么样的?有什么逻辑关系吗? // 挂载流程 // TODO: 如果options 上没有el 属性,用户需要手动vm.$mount(el) 才会进入挂载流程 if(vm.$options.el){ vm.$mount(vm.$options.el); } } Vue.prototype.$mount = function (el) { const vm = this; const options = vm.$options; el = document.querySelector(el); // 查找模板入口的优先级(从高到低):render () => template => el // TODO:如果有render函数会进入到什么流程? if(!options.render){ let template = options.template; // 获取用户编写的template if(!template && el){ // 如果没有template 有el元素的话,就会以el元素outerHTML后的字符串作为template template = el.outerHTML; // outerHTML输出内容包含描述元素及其后代的序列化HTML片段 } /** * 我们需要将template 转化成render方法 * 之所以需要转换成render函数是为了生成vnode提供给虚拟DOM * vue1 使用的是模板解析 vue2 使用的是虚拟DOM * 核心面试难点:template -> render */ const render = compileToFunction(template); options.render = render; } // 有render方法会直接进行挂载流程 mountComponent(vm,el); } } // TODO: // 练习写AST结构,以及nodeType类型; // 栈构建AST层级结构的流程; // 解析HTMLparse的整体流程,以及代码手敲。 ================================================ FILE: todo/fe/vue 手写/src/lifecycle.js ================================================ import Watcher from './observer/watcher'; import {patch} from './vdom/patch' export function lifecycleMixin(Vue) { // 创建出真实的DOM而后更新页面 Vue.prototype._update = function (vnode) { const vm = this; // 需要用虚拟节点创建出真实节点 替换掉 上次的真实的DOM元素 vm.$el = patch(vm.$el,vnode); } } /** * * @param {*} vm vm实例,上面保存着上次真实的DOM节点 * @param {*} el 新的虚拟DOM节点 */ export function mountComponent(vm,el){ const options = vm.$options; vm.$el = el; // 真实的dom元素 // Watcher 就是用来渲染的 // vm._render 通过解析的render方法 渲染出虚拟dom _c _v _s // vm._update 通过虚拟dom 创建真实的dom // 渲染页面:无论是首次渲染还是页面更新都会调用此方法 let updateComponent = () =>{ //vm._render方法生成虚拟DOM? //_update方法拿到真实DOM? export function createElement(tag, data = {}, ...children) { let key = data.key; if(key){ delete data.key; } return vnode(tag,data,key,children,undefined); } export function createTextNode(text) { return vnode(undefined,undefined,undefined,undefined,text); } function vnode(tag, data, key, children, text) { return { tag, data, key, children, text } } // 虚拟节点 就是通过_c _v 实现用对象来描述dom的操作 (对象) // 1) 将template转换成ast语法树-> 生成render方法 -> 生成虚拟dom -> 真实的dom // 重新生成虚拟dom -> 更新dom } // 通过渲染watcher进行模板渲染 // 每个组件都有一个watcher // 渲染相关:每次数据改变,都会重新执行updateComponent new Watcher(vm,updateComponent,()=>{},true); // true表示他是一个渲染watcher } ================================================ FILE: todo/fe/vue 手写/src/observer/array.js ================================================ const methods = [ 'push', 'pop', 'shift', 'unshift', 'reverse', 'splice', 'sort' ] const _originPrototype = Array.prototype; // 构造代理原型 export const arrayMethods = Object.create(_originPrototype); // 向代理原型添加代理后的方法 methods.forEach(method => { arrayMethods[method] = function (args) { const res = _originPrototype[method].apply(this,args) let inserted = null; // 重写操作,当数组变化时进行通知 switch (method) { case 'push': case 'unshift': inserted = args; case 'splice': inserted = args.slice(2); } if(inserted) __ob__.observerArray(inserted) // 深度转化inserted属性,为了拿到ob身上的observerArray方法,在这里做一个__ob__ 属性保存当前Observer实例 return res; } }) ================================================ FILE: todo/fe/vue 手写/src/observer/dep.js ================================================ let id = 0; class Dep{ constructor(){ this.id = id++; this.subs = []; } addSub(watcher){ this.subs.push(watcher); // 观察者模式 } depend(){ // 让这个watcher记住我当前的dep,如果watcher没存过dep ,dep肯定不能存过watcher Dep.target.addDep(this); } notify(){ this.subs.forEach(watcher=>watcher.update()) } } let stack = []; // 目前可以做到 将watcher保留起来 和 移除的功能 // 下面两个方法都是在操作Dep.target export function pushTarget(watcher){ Dep.target = watcher; stack.push(watcher); } export function popTarget(){ stack.pop(); Dep.target = stack[stack.length -1]; } export default Dep ================================================ FILE: todo/fe/vue 手写/src/observer/index.js ================================================ // 把data中的数据 都使用Object.defineProperty重新定义 es5 // Object.defineProperty 不能兼容ie8 及以下 vue2 无法兼容ie8版本 import {arrayMethods} from './array.js' import { isObject,def } from "../util/index" import Dep from './dep.js'; /** * 核心API defineProperty 兼容IE9+ * TODO:Vue2 数据响应式存在的问题? * - vue如果数据的层次过多 需要递归的去解析对象中的属性,依次增加set和get方法 * @param data */ export function observe(data) { let isObj = isObject(data); if (!isObj) { return } //base case,原始值不处理 return new Observer(data); // 将data转化成响应式data } /** * */ class Observer{ constructor(originData){ // 仅仅是初始化的操作 /** * 这样是会 Stack Overflow的 ->> originData.__ob__ = this ->> 对this进行walk 发现 this 里面还有__ob__ 然后就挂掉了 * 因此需要对__ob__进行配置:不可枚举(防止递归爆栈,以及不可配置防止修改) */ def(originData,'__ob__',this); // 通过__ob__属性可以判断是否已响应式处理 if(Array.isArray(originData)){ // 并不会对索引进行观测,因为会导致性能问题 //TODO:数组响应式处理 originData.__proto__ = arrayMethods; // 重写数组的原型,采用原型链遮蔽特性 this.observerArray(originData); // 深度监听数组 }else{ this.walk(originData); // 1.遍历对象中的属性,对每一个属性进行检测、响应式转换. } } /** * 递归监听数组内容 * @param {*} originData */ observerArray(originData){ for(let i = 0; i < originData.length;i++){ observe(originData[i]); } } /** * 遍历对象,进行响应式处理 * @param {*} data */ walk(data){ let keys = Object.keys(data); // TODO: 如果这个data 不可配置 直接reurn? object.freeze 情况吗? keys.forEach((key)=>{ defineReactive(data,key,data[key]); }); } } /** * @param data 对象名称 * @param key 对象中的key值 * @param originData 对应的originData值 * @returns {*} */ function defineReactive(data,key,originData){ let dep = new Dep(); // 这个dep 是为了给对象使用的 observe(originData); // 对象嵌套情况下递归响应式处理,因此采用递归实现 Object.defineProperty(data,key,{ configurable:true, enumerable:true, get(){ // 每个属性都对应着自己的watcher,依赖收集 if(Dep.target) { // 如果当前有watcher // 建立wather 和 dep 的双向关系 dep.depend(); // 意味着我要将watcher存起来,在dep内部中右将watcher存入到dep中 if(childOb){ // *******数组的依赖收集***** childOb.dep.depend(); // 收集了数组的相关依赖 // 如果数组中还有数组 if(Array.isArray(value)){ dependArray(value); } } } return originData; }, set(newValue){ if(newValue === originData) return; observe(newValue); // 防止newValue是一个对象或者数组 originData = newValue; //TODO: notify to Wather dep.notify(); // 通知依赖 } }); } ================================================ FILE: todo/fe/vue 手写/src/observer/watcher.js ================================================ import { pushTarget, popTarget } from './dep.js'; /** * 难点:和dep 的双向关系 */ class Watcher { constructor(vm, exprOrFn, callback, options) { this.vm = vm; this.callback = callback; this.options = options; this.id = id++; this.getter = exprOrFn; // 将内部传过来的回调函数 放到getter属性上 this.depsId = new Set(); // es6中的集合 (不能放重复项) this.deps = []; this.get(); // 调用get方法 会让渲染watcher执行 } addDep(dep) { // watcher里不能放重复的dep dep里不能放重复的watcher let id = dep.id; if (!this.depsId.has(id)) { this.depsId.add(id); this.deps.push(dep); dep.addSub(this); } } get() { pushTarget(this); // 把watcher存起来 Dep.target = this this.getter(); // 渲染watcher的执行 // 执行完毕wather 要移除 popTarget(); } update() { queueWatcher(this);// 等待着 一起来更新 因为每次调用update的时候 都放入了watcher // this.get(); } run(){ this.get(); } } export default Watcher ================================================ FILE: todo/fe/vue 手写/src/render.js ================================================ import { createElement, createTextNode } from './vdom/create-element' export function renderMixin(Vue) { // _c 创建元素的虚拟节点 Vue.prototype._c = function () { return createElement(...arguments); // tag,data,children1,children2 } // _v 创建文本的虚拟节点 Vue.prototype._v = function (text) { return createTextNode(text); } // _s JSON.stringify Vue.prototype._s = function (val) { return val == null ? '' : (typeof val === 'object' ? JSON.stringify(val) : val); } /** * 执行生成的render函数,返回vnode */ Vue.prototype._render = function () { const vm = this; const { render } = vm.$options; // 去实例上 取值 let vnode = render.call(vm); return vnode; } } ================================================ FILE: todo/fe/vue 手写/src/state.js ================================================ import {observe} from "./observer/index.js" import {proxy} from './util/index' /** * TODO:初始化中的道道 * @param vm */ export function initState(vm) { const opts = vm.$options; if (opts.props) { initProps(vm); } if (opts.methods) { initMethod(vm); } if (opts.data) { initData(vm); } if (opts.computed) { initComputed(vm); } if (opts.watch) { initWatch(vm); } } function initProps() {} function initMethod() {} /** * 重要的事情: * 对用户传入的data对象进行处理 * - 数据劫持 * * @param vm */ function initData(vm) { let data = vm.$options.data; data = vm._data = typeof data === 'function' ? data.call(vm) : data;// 兼容函数和对象的情况,注意函数情况下的this指向,需要手动指向vm实例本身;vm._data挂载是为了后续操作后简便的获取data属性 for (let key in data) { proxy(vm, '_data', key); // 对原_data上所有属性代理到vm对象上,可以通过vm.xxx直接获取 } observe(data);//响应式入口 } function initComputed() {} function initWatch() {} ================================================ FILE: todo/fe/vue 手写/src/util/index.js ================================================ /** * * @param {*} data 当前数据是不是对象 */ export function isObject(data) { return typeof data === 'object' && data !== null; } export function def(data,key,value){ Object.defineProperty(data,key,{ enumerable:false, configurable:false, // 其实偷懒的话可以不写 默认值为fasle value, // 还有一个属性是 writable 标识是否可写 }) } /** * 对target做存取器,使得获取属性时进行拦截代理 * @param target 代理到x上,通过x进行访问 * @param source 被代理的y对象 * @param key y对象上的属性 */ export function proxy (target,source,key) { Object.defineProperty(target,key,{ get() { return target[source][key] }, set(v) { target[source][key] = v; } }) } ================================================ FILE: todo/fe/vue 手写/src/vdom/create-element.js ================================================ export function createElement(tag, data = {}, ...children) { let key = data.key; if(key){ delete data.key; } return vnode(tag,data,key,children,undefined); } export function createTextNode(text) { return vnode(undefined,undefined,undefined,undefined,text); } function vnode(tag, data, key, children, text) { return { tag, data, key, children, text } } // 虚拟节点 就是通过_c _v 实现用对象来描述dom的操作 (对象) // 1) 将template转换成ast语法树-> 生成render方法 -> 生成虚拟dom -> 真实的dom // 重新生成虚拟dom -> 更新dom ================================================ FILE: todo/fe/vue 手写/src/vdom/patch.js ================================================ /** * 1.判断是更新还是要渲染 * 递归创建真实节点 替换掉老的节点 * @param {*} oldVnode * @param {*} vnode */ export function patch(oldVnode,vnode){ const isRealElement = oldVnode.nodeType; if(isRealElement){ const oldElm = oldVnode; // div id="app" const parentElm = oldElm.parentNode; // body // 将虚拟节点变成真实节点 let el = createElm(vnode); // 插入到正确的位置 parentElm.insertBefore(el,oldElm.nextSibling) // 删除老节点 parentElm.removeChild(oldElm); } } /** * 创建真实DOM * @param {*} vnode */ function createElm(vnode){ // 根据虚拟节点创建真实的节点 let {tag,children,key,data,text} = vnode; // 是标签就创建元素 if(typeof tag === 'string'){ vnode.el = document.createElement(tag); // 更新属性 updateProperties(vnode); children.forEach(child=>{ // 递归创建儿子节点,将儿子节点扔到父节点中 return vnode.el.appendChild(createElm(child)) }) }else{ // 虚拟dom上映射着真实dom 方便后续更新操作 vnode.el = document.createTextNode(text) } // 如果不是标签就是文本 return vnode.el; } // 更新属性 function updateProperties(vnode){ let newProps = vnode.data; let el = vnode.el; for(let key in newProps){ if(key === 'style'){ for(let styleName in newProps.style){ el.style[styleName] = newProps.style[styleName]; } }else if(key === 'class'){ el.className = newProps.class; }else{ el.setAttribute(key,newProps[key]); } } } ================================================ FILE: todo/fe/webpack/原理 - 懒加载:单入口文件和多入口文件实现懒加载.md ================================================ ================================================ FILE: todo/fe/webpack/原理 - 模块化:如何同时支持CommonJs规范和ES module规范.md ================================================ ================================================ FILE: todo/fe/webpack/基础总结.md ================================================ ## high level ### webpack是什么? 准确的定义:webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当webpack处理该程序时,会递归地构建一个**依赖关系图**,将项目所依赖的每个模块打包成一个bundle或多个bundle。 能做的事情:代码转化,模块合并,代码校验,HMR热更新,代码分割,treeSharing,自动发布。 #### 优点 - 多模块标准的支持 - 完备的代码分割解决方案 - 可以处理多种类型资源 - 庞大的社区支持 ### 前端为什么需要打包? 已经不再是简单的一个静态页面了,常见的项目体量已经上升到App层面。 ### 前端打包工具都有哪些?应用场景是什么? ## 核心概念 ## 基础配置 ### 线上环境:抽离代码 #### 为什么线上环境需要抽离文件? - 用户体验 - 代码体积 - 缓存概率 #### 抽离css代码 核心逻辑是将开发环境的`style-loader` 调整为 `MiniCssExtractPlugin` 套件 主要流程为: ```js MiniCssExtractPlugin.loader --> new MiniCssExtractPlugin() --> optimization : minimizer: [ new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({}) ], ``` > MiniCssExtractPlugin 不具备压缩css代码的能力,需要单独配置压缩css代码 #### 抽离公共代码和第三方vendor ```js // 分割代码块 splitChunks: { chunks: 'all', /** * initial:入口 chunk,对于异步导入的文件不处理 async:异步 chunk,只对异步导入的文件处理 all:全部 chunk */ // 缓存分组 cacheGroups: { // 第三方模块 vendor: { name: 'vendor', // chunk 名称 priority: 1, // 权限更高,优先抽离,重要!!! test: /node_modules/, minSize: 0, // 大小限制 minChunks: 1 // 最少复用过几次 }, // 公共的模块 common: { name: 'common', // chunk 名称 priority: 0, // 优先级 minSize: 0, // 公共模块的大小限制 minChunks: 2 // 公共模块最少复用过几次,如果频繁抽文件,会增加网络压力 } } } ``` 注意:可以通过配置HtmlWebpackPlugin的chunks属性来选择加载抽离的文件: ```js new HtmlWebpackPlugin({ template: path.join(srcPath, 'index.html'), filename: 'index.html', // chunks 表示该页面要引用哪些 chunk // 要考虑代码分割 chunks: ['index', 'vendor', 'common'] }), ``` ## 高级配置 ## 高级特性 ### 支持懒加载 ## 调优 ## 常用loader ## 常用plugin ================================================ FILE: todo/fe/webpack 手写/实现打包器/bin/packer.js ================================================ #! /usr/bin/env node // 标识在node环境下执行代码 const path = require('path') console.log('链接成功'); const Compilar = require('../lib/Compilar.js') let webpackConfig = require(path.resolve('./webpack.config.js')) // 获取webpack.config.js 配置文件 let compilar = new Compilar(webpackConfig) compilar.run() ================================================ FILE: todo/fe/webpack 手写/实现打包器/lib/Compilar.js ================================================ const path = require('path') const fs = require('fs') // babel解析AST,修改代码 const babelType = require('@babel/types') const babelTraverse = require('@babel/traverse').default const babelGenerator = require('@babel/generator').default const babylon = require('babylon') let ejs = require('ejs'); class Compilar { constructor(options) { this._options = options // 缓存 this.entryId this.entry = options.entry // 真实入口文件 this.dependenceModules = {} // 模块依赖关系 this.workPath = process.cwd() } /** * 入口,生成模块依赖,生成打包文件 */ run() { this.buildDependenceModules( path.resolve(this.workPath, this.entry),true ) this.emitFile() } buildDependenceModules(ModulePath, isEntry) { let sourceCode = this.resolveCode(ModulePath)// 拿到源码模块中的内容 let moduleName = this.resolveName(ModulePath)// 处理源码模块的路径 isEntry ? this.entryId = moduleName : this.entryId// 保存入口id console.log('sourceCode:',sourceCode) console.log('moduleName:',moduleName) // 通过AST语法进行重构代码,替换成__webpack_require__ 以及处理模块依赖 // 最终生成准备打包处理的bundleCode以及bundleDependence let { bundleCode, bundleDependence } = this.parseCode(sourceCode,path.dirname(moduleName)) console.log('bundleCode:',bundleCode) console.log('bundleDependence:',bundleDependence) this.dependenceModules[moduleName] = bundleCode bundleDependence.forEach(dep => { // 非entry文件,设置为false this.buildDependenceModules(path.join(this.workPath,dep),false) }) } resolveCode(path) { let source = fs.readFileSync(path, 'utf8') return source } resolveName(sourcePath) { console.log('workPath:',this.workPath) console.log('sourcePath:',sourcePath) let name = './' + path.relative(this.workPath, sourcePath) return name } parseCode(source,dirPath){ let ast = babylon.parse(source)//获取ast语法树 let bundleDependence = [] babelTraverse(ast,{//遍历语法树,对源码进行重构 CallExpression(p){ let node = p.node; // 只处理是require的函数执行表达式 if(node.callee.name === 'require'){ node.callee.name = '__webpack_require__' //修改名字 // 获取函数中的参数(相对路径) let moduleName = node.arguments[0].value moduleName = moduleName + (path.extname(moduleName) ? '' : '.js') // 补全拓展名 moduleName = './' + path.join(dirPath,moduleName) // ./src/xxx.js bundleDependence.push(moduleName) node.arguments = [babelType.stringLiteral(moduleName)] } } }) // 根据新生成的语法树,生成新的代码 let bundleCode = babelGenerator(ast).code return {bundleCode,bundleDependence} } emitFile() { let outfilePath = path.join(this._options.output.path, this._options.output.filename); let templateStr = this.resolveCode(path.join(__dirname, 'template.ejs')); let tempCode = ejs.render(templateStr, { entryId: this.entryId, modules: this.dependenceModules }); // 可能存在多个入口情况,因此使用这样方式? this.assets = {} this.assets[outfilePath] = tempCode; fs.writeFileSync(outfilePath, this.assets[outfilePath]); } } module.exports = Compilar ================================================ FILE: todo/fe/webpack 手写/实现打包器/lib/template.ejs ================================================ (function (modules) { var installedModules = {}; function __webpack_require__(moduleId) { if (installedModules[moduleId]) { return installedModules[moduleId].exports; } var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); module.l = true; return module.exports; } return __webpack_require__(__webpack_require__.s = "<%-entryId%>"); }) ({ <%for(let key in modules){%> "<%-key%>": (function (module, exports, __webpack_require__) { eval(`<%-modules[key]%>`); }), <%}%> }); ================================================ FILE: todo/fe/webpack 手写/实现打包器/package.json ================================================ { "name": "my-packer", "version": "1.0.0", "description": "", "main": "index.js", "bin": { "my-pack": "./bin/packer.js" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@babel/generator": "^7.8.7", "@babel/traverse": "^7.8.6", "@babel/types": "^7.8.7", "babylon": "^6.18.0", "ejs": "^3.0.1" } } ================================================ FILE: todo/fe/待迁移-ES6/class编译/extends.js ================================================ ================================================ FILE: todo/fe/待迁移-ES6/class编译/基础设施.js ================================================ // constructor ```javascript class Person { constructor(name) { this.name = name; } } ``` // ----------------------------------Babel ```javascript "use strict"; function _classCallCheck(instance, Constructor) { // 直接调用 instance 指向window if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function Person(name) { _classCallCheck(this, Person); this.name = name; }; ``` 1. 严格模式下运行该语法 2. 只能通过new关键字调用 3. 等同ES5:构造函数体内部代码 ```javascript // 静态属性 class Person { // 实例对象属性 foo = 'foo'; // 静态属性 static bar = 'bar'; constructor(name) { this.name = name; } } ``` // ----------------------------------Babel ```javascript 'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function Person(name) { _classCallCheck(this, Person); this.foo = 'foo'; this.name = name; }; Person.bar = 'bar'; ``` 1. 直接通过this赋值的变量是一个语法糖,和constructor中的this写法并无差异 2. 静态方法会编译成构造函数的属性 ```javascript // 原型方法 + 静态方法 + get set方法 class Person { constructor(name) { this.name = name; } sayHello() { return 'hello, I am ' + this.name; } static onlySayHello() { return 'hello' } get name() { return 'kevin'; } set name(newName) { console.log('new name 为:' + newName) } } ``` // ----------------------------------Babel ```javascript 'use strict'; var _createClass = function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { // 取出一个需要注入的方法 var descriptor = props[i]; // 默认不可以枚举 descriptor.enumerable = descriptor.enumerable || false; // 默认可以配置 descriptor.configurable = true; // 如果是属性访问器,则设置writable属性 if ("value" in descriptor) descriptor.writable = true; // 注入方法 Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { // 向Constructor的原型上 添加方法 if (protoProps) defineProperties(Constructor.prototype, protoProps); // 向Constructor 自身上 添加方法 if (staticProps) defineProperties(Constructor, staticProps); // 返回Constructor本身 return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * 重写该方法,返回Constructor本身,但向他的原型和Constructor注入方法 */ var Person = function() { function Person(name) { _classCallCheck(this, Person); this.name = name; } // 向Constructor原型和Constructor分别注入方法 _createClass(Person, [ { key: 'sayHello', value: function sayHello() { return 'hello, I am ' + this.name; } }, { key: 'name', get: function get() { return 'kevin'; }, set: function set(newName) { console.log('new name 为:' + newName); } }], // 静态方法 [{ key: 'onlySayHello', value: function onlySayHello() { return 'hello'; } }]); return Person; }(); ``` 1. 通过class 添加的原型方法/静态方法 默认不可枚举 2. class语法通过内部的`_createClass`对`Object.defineProperties()`进行封装,实现对原型方法和静态方法的注入。 - 编译代码时,将原型方法和静态方法做成键值对形式,key 保存为方法名, value 保存为函数。 - 对于setter 和getter,通过判断是否存在value属性,如果不存在,则设置对应的get 方法 和 set 方法。 - 将没有static标识符的函数放置一个数组,并作为第二参数传递进`_createClass`方法,将设有static标识符的函数放置一个数组,作为第三个参数进行传递 ================================================ FILE: todo/fe/待迁移-强化代码/Observer.js ================================================ /** * * @param {object} obj 待转化响应式的对象 * @param {键} key * @param {值} val */ function defineReactive(obj, key, val) { Observer(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { return val }, set(newVal) { if (newVal === val) { return newVal } Observer(newVal) val = newVal console.log('update'); } }) } function Observer(target) { if (typeof target !== 'object' || target === null) { return target } if(Array.isArray(target)) { target.__proto__ = arrProto ArrayObserver(target) } for (const key in target) { defineReactive(target, key, target[key]) } } let arrProto = Array.prototype let proto = Object.create(arrProto)[ 'push', 'unshift', 'splice', 'reverse', 'sort', 'shift', 'pop' ].forEach(method => { proto[method] = function (...args) { let inserted switch (inserted) { case 'push': case 'unshift': inserted = args case 'splice': inserted = args.slice(2) default: break; } ArrayObserver(inserted) arrProto[method].call(this,...args) } }); /** * 将目标转化为响应式 * @param {object} target */ function ArrayObserver(target) { for (let i = 0; i < target.length; i++) { let item = target[i] Observer(item) } } ================================================ FILE: todo/fe/待迁移-强化代码/bind函数.js ================================================ let slice = Array.prototype.slice, concat = Array.prototype.concat; Function.prototype.myBind = function () { let callFunc = this, context = arguments[0], outArgs = slice.call(arguments,1); return function (...args) { let finalArgs = outArgs.concat(args) return callFunc.apply(context,finalArgs) } } function test (sex,age) { console.log(this.name,sex,age); } name = 'global' let testObj = { name : 'test_object' } let testObj2 = { name : 'test_object2' } let newTest = test.myBind(testObj,'boy') newTest(24) ================================================ FILE: todo/fe/待迁移-强化代码/deepClone.js ================================================ /** * 深拷贝 */ const obj1 = { age: 20, name: 'xxx', address: { city: 'beijing' }, arr: ['a', 'b', 'c'] } const obj2 = deepClone(obj1) obj2.address.city = 'shanghai' obj2.arr[0] = 'a1' console.log(obj1.address.city) console.log(obj1.arr[0]) function deepClone (target = {}) { //递归出口 if(target == null || typeof target !== 'object') { return target } let result = Array.isArray(target) ? [] : {} for(let key in target){ if(Object.prototype.hasOwnProperty) { result[key] = deepClone[target[key]] } } return result } ================================================ FILE: todo/fe/待迁移-强化代码/eventEmitter.js ================================================ // 测试用例 /** * 再次手写时,注意返回实例,便于链式调用该特性 */ class EventEmitter { constructor() { this.events = {} // 最大数量,可更改 this._max = 10 } $on(type, handle) { if (this._events[type]) { this.events[type].push(handle) } // 判断最大长度是否超出 if (this._max != 0 && this.events[type].length > this._max) { console.error('...') } else { // 注意是数组形式; this.events[type] = [handle] } } $emit(type, ...msg) { const listeners = this.events[type] listeners.length && listeners.forEach(l => l.apply(this, msg)); } $once(type, handle) { const wrapper = () => { handle.call(this) this.removeListener(type, wrapper) } this.$on(type, wrapper) } removeAllListeners(type) { this.events[type] && delete this.events[type] } removeListener(type, handle) { const listeners = this.events[type] listeners ? listeners.filter( (l => l != handle) ) : listeners } } const myEmit = new EventEmitter() // myEmit.$on('my-click',function () { // // dosomething... // }) myEmit.$emit('my-click', 'hello wrold') myEmit.$once('my-once', function () { // dosomething.... }) // 因此在注册事件时,尽量使用函数名称进行注册,便于销毁时进行匹对 myEmit.removeListener('my-click', function () { }) myEmit.removeAllListeners('my-click') myEmit.listeners('my-click') myEmit.setMaxListeners('') ================================================ FILE: todo/fe/待迁移-强化代码/flatten.js ================================================ const data = [ { id: 1, subItems: [ { id: 2, subItems: [ { id: 3, subItems: [], }, { id: 4, subItems: [], }, ], }, { id: 5, subItems: [], }, ], }, { id: 6, subItems: [], }, ]; const arr = [ 1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3, [1, [1, [1, 1]]]]]], 5, "string", { name: "苏鹏宇", }, ]; /** * 数组拍平这种问题,需要从以下三种方面考虑 * 1.遍历 * 2.判断是否是数组 * 3.展开该数组 */ /** * 1.能够解决arr类型的数据,但是data这样的测试数据处理不了 * 2.时间复杂度高 for循环套递归 k叉树 * @param {*} array */ // concat + 递归 function flatten(array) { let arrayResult = []; array.forEach((item) => { if (Array.isArray(item)) { arrayResult = arrayResult.concat(flatten(item)); } else { arrayResult.push(item); } }); return arrayResult; } // console.log(flatten(data)); /** * 使用reduce改写 * 和递归执行的次数一样 * 一样解决不了data这种数据; */ const flat = (arr) => { return arr.reduce((pre, cur) => { console.log(1); return pre.concat(Array.isArray(cur) ? flat(cur) : cur); }, []); }; console.log(flat(data)); ================================================ FILE: todo/fe/待迁移-强化代码/index.html ================================================ Document ================================================ FILE: todo/fe/待迁移-强化代码/lastEvent.js ================================================ class eventModule { constructor () { this.events = {} } $on (type, handle) { if(this.events[type]) { this.events[type].push(handle) } else { this.events[type] = [handle] } } $emit(type,callback) { const args = arguments.pop().shift(); // 保证有值,边界处理 this.events[type] && this.events[type].forEach(listener => { listener.apply(this,args) }); callback() } $once(type,handle) { // 当执行回调时,自动删除该监听器 const wrapper = (...args) => { handle.apply(this,args) this.$removeListener(type,wrapper) } this.$on(type,wrapper) } $removeListener(type,handle) { // 如果没有监听器删什么? if(this.events[type].length){ this.events = this.events[type].filter( l => l !== handle) return; } console.log('没有监听器'); } $getListener(type) { if(this.events[type]) { return this.events[type] } console.log('未添加'); } $removeAll() { this.events = {} } } function note () { console.log('通知成功!'); } // 测试用例 const eventModule = new MyEvent(); eventModule.$on('test',note) eventModule.$once('test',() => { console.log('我就执行一次,通知成功!'); }) eventModule.$emit('test',() => { console.log('报告:通知完毕'); }); eventModule.$getListener('test') // 会发现once 注册的事件函数以消失 eventModule.$removeListener('test', note) eventModule.$getListener('test') eventModule.$removeAll() ================================================ FILE: todo/fe/待迁移-强化代码/发布订阅.js ================================================ function EventEmitter() { this.events = {}; //会把所有的事件监听函数放在这个对象里保存 //指定给一个事件类型增加的监听函数数量最多有多少个 this._maxListeners = 10; } EventEmitter.prototype.setMaxListeners = function (maxListeners) { this._maxListeners = maxListeners; } EventEmitter.prototype.listeners = function (event) { return this.events[event]; } //给指定的事件绑定事件处理函数,1参数是事件类型 2参数是事件监听函数 EventEmitter.prototype.on = EventEmitter.prototype.addListener = function (type, listener) { if (this.events[type]) { this.events[type].push(listener); if (this._maxListeners != 0 && this.events[type].length > this._maxListeners) { console.error( `MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${this.events[type].length} ${type} listeners added. Use emitter.setMaxListeners() to increase limit`); } } else { //如果以前没有添加到此事件的监听函数,则赋一个数组 this.events[type] = [listener]; } } EventEmitter.prototype.once = function (type, listener) { //用完即焚 let wrapper = (...rest) => { listener.apply(this,rest); //先让原始的监听函数执行 this.removeListener(type, wrapper); } // 会被加入到events对象中,因此使用完毕后进行销毁,达到once效果; this.on(type, wrapper); } EventEmitter.prototype.removeListener = function (type, listener) { /** * listener = 3 * l => [1,2,3,4] * l != 1? true 保留 * l != 3? false 舍去 */ if (this.events[type]) { this.events[type] = this.events[type].filter(l => l != listener) } } //移除某个事件的所有监听函数 EventEmitter.prototype.removeAllListeners = function (type) { delete this.events[type]; } // 事件目标,以及传递的参数; EventEmitter.prototype.emit = function (type, ...rest) { this.events[type] && this.events[type].forEach(listener => listener.apply(this, rest)); } module.exports = EventEmitter; ================================================ FILE: todo/fe/正则表达式/元字符.md ================================================ ## 元字符 所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,元字符是构成正则表达式的基本元件。 ## 特殊单字符 ### . 英文的点(.)表示换行以外的任意单个字符 ![20200621114433]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621114433.png) ### \d \d 表示任意单个数字 \D 任意非数字 ![20200621114537]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621114537.png) ### \w \w 表示任意单个数字或字母或下划线 \W 任意非字母数字下划线 ![20200621114631]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621114631.png) ### \s \s 表示任意单个空白符 \S非单个空白符 ![20200621114655]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621114655.png) ## 空白符 ![20200621115724]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621115724.png) ## 量词 在正则中,英文的星号(*)代表出现 0 到多次,加号(+)代表 1 到多次,问号(?)代表 0 到 1 次,{m,n}代表 m 到 n 次。 ![20200621115027]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621115027.png) 例如可以使用\d+匹配所有数字 ![20200621115132]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621115132.png) ## 范围 学习了量词,我们就可以用 \d{11} 去匹配所有手机号,但同时也要明白,这个范围比较大,有一些不是手机号的数字也会被匹配上,需要对匹配到的内容进行范围限制。 ![20200621115451]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621115451.png) 比如某个资源可能以 http:// 开头,或者 https:// 开头,也可能以 ftp:// 开头,那么资源的协议部分,我们可以使用 (https?|ftp):// 来表示。 ![20200621115650]( https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621115650.png) ================================================ FILE: todo/fe/练习-CSS面试部分/FlexIndex.html ================================================ Document
================================================ FILE: todo/fe/练习-CSS面试部分/Grid.css ================================================ .grid{ /* 定义网格布局 */ display: grid; grid-template-columns: repeat(6,140px); grid-template-rows: repeat(3,140px); /* 表示间距,注意是间距 */ grid-gap: 20px; } .item{ background-color: #e2e2ee; } .item:nth-child(2){ grid-area: 1 / 3 / 3 /5 ; } .item:nth-child(1), .item:nth-child(5) { grid-column: 1 / 3; } .item:nth-child(7) { grid-column: 6; grid-row: 2 / 4; } .item:nth-child(9){ grid-column: 2 / 4; } ================================================ FILE: todo/fe/练习-CSS面试部分/Grid.html ================================================ Document
1
2
3
4
5
6
7
8
9
10
11
================================================ FILE: todo/fe/练习-CSS面试部分/flex.css ================================================ .container{ width: 530px; height: 100px; display: flex; background: #e2e2ee; } .item{ width: 200px; margin: 5px; background: dodgerblue; } .item:nth-of-type(3) { /*flex-shrink: 2;*/ } /*需要挤压的空间: (+flex item) - flex container 的宽度*/ /*实际宽度 = 初始宽度 - 分配的缩放空间()*/ /*630 - 530 = 100*/ /*200 - x = 175*/ /*x = 25 * 4 => 100*/ ================================================ FILE: todo/fe/练习-CSS面试部分/grid练习/index.htm.html ================================================ Title ================================================ FILE: todo/fe/练习-CSS面试部分/grid练习/九宫格.css ================================================ ================================================ FILE: todo/fe/练习-CSS面试部分/三角形.css ================================================ .trans{ width: 0; height: 0; background-color: #fff; border-right: 100px solid rgb(34, 230, 220); border-left: 100px solid rgb(202, 146, 25); border-top: 100px solid rgb(29, 156, 194); border-bottom: 100px solid rgb(16, 204, 101); } ================================================ FILE: todo/fe/练习-CSS面试部分/外边距合并.css ================================================ span{ display:grid; width: 100px; height: 100px; visibility: } ================================================ FILE: todo/fe/练习-CSS面试部分/布局方案/三栏布局.html ================================================ Document -->
main
left
right
================================================ FILE: todo/fe/练习-CSS面试部分/布局方案/两栏布局.html ================================================ Document
================================================ FILE: todo/fe/练习-CSS面试部分/水平垂直居中/index.html ================================================ Title
123
123
================================================ FILE: todo/fe/练习-CSS面试部分/水平垂直居中/定宽高.css ================================================ /*.container{*/ /* width: 300px;*/ /* height: 300px;*/ /* background: #e2e3e3;*/ /* position: relative;*/ /*}*/ /*.foo{*/ /* width: 150px;*/ /* height: 100px;*/ /* background: dodgerblue;*/ /*!* absolute + -margin*!*/ /* position: absolute;*/ /* left: 50%;*/ /* top: 50%;*/ /*!* 缺点:必须知道定位元素的尺寸*!*/ /* margin-left: -75px;*/ /* margin-top: -50px;*/ /*}*/ /*.container{*/ /* width: 300px;*/ /* height: 300px;*/ /* background: #e2e3e3;*/ /* position: relative;*/ /*}*/ /*.foo{*/ /* width: 150px;*/ /* height: 100px;*/ /* background: dodgerblue;*/ /* !* absolute + -margin*!*/ /* position: absolute;*/ /* top: 0;*/ /* left: 0;*/ /* right: 0;*/ /* bottom: 0;*/ /* margin: auto;*/ /*}*/ /*.container{*/ /* width: 300px;*/ /* height: 300px;*/ /* background: #e2e3e3;*/ /* position: relative;*/ /*}*/ /*.foo{*/ /* width: 150px;*/ /* height: 100px;*/ /* background: dodgerblue;*/ /* !* absolute + -margin*!*/ /* position: absolute;*/ /* top: calc(50% - 75px);*/ /* left: calc(50% - 75px);*/ /*}*/ .red { color: red } .blue { color: blue } ================================================ FILE: todo/fe/练习-JavaScript部分/AJAX.js ================================================ /** * 兼容IE ActiveXobject() */ const XHR = () => { let xObj = null; if (window.XMLHttpRequest) { xObj = new XMLHttpRequest(); } else { xObj = new ActiveXObject(); } return xObj; }; /** * - 过滤操作:过滤掉 不是自己的属性,是函数的数据 * * - encodeURIcomponent 编码数据 * * - 拼接 * * - * * @param {*} data 不确定的数据格式 */ const format = (data) => { if (!data) return ""; var pairs = []; for (let name in data) { // 过滤掉原型上的属性 if (!data.hasOwnProperty(name)) continue; if (typeof name === "function") continue; // 编码 let value = data[name].toString(); name = encodeURIComponent(name); value = encodeURIComponent(value); pairs.push(name + "=" + value); } return pairs.join("&"); }; function XHR() { let xobj = null; if (window.XMLHttpRequest) { xobj = new XMLHttpRequest(); } else { // IE ActiveXObject xobj = new ActiveXObject(); } return xobj; } /** * @param {Object} 需要被格式化的字符串 * * { * name : spy, * age : 24 * } * =====> name=spy&age=24 * * 遍历data * 过滤 * * 将 key value 组成一个encodeURLComponent(key)=encodeURLComponent(value) 的字符串 扔到 params中 * * * params 再 通过 join进行拼接 * * 边界: * URL中使用,所以需要对特殊字符进行转义 * * */ function format(data) { if (!data) return ""; let params = []; for (let name in data) { if (!data.hasOwnProperty(name)) continue; if (typeof data[name] === "function") continue; let value = data[name]; name = encodeURIComponent(name); value = encodeURIComponent(value); params.push(name + "=" + value); } return params.join("&"); } const ajaxPromiseify = ( method, url, data, responseType = "json", header = {} ) => { return Promise((resolve, reject) => { // 1. 创建一个XHR对象,并注册状态事件处理函数 let xhr = XHR(); let params = null; xhr.onreadystatechange = handler; // 2. 根据当前的method 去 处理data GET / POST if (method.toUpperCase() === "GET") { url = url + "?" + format(data); } else if (method.toUpperCase() === "POST") { params = data; } // 3. 调用xhr.open() 准备一个异步请求 xhr.open(method, url, true); // 4. 处理http 自定义的头部 for (let name in header) { xhr.setRequestHeader(name, header[name]); } // 5. 补充或添加content-type 类型 !xhr["Content-Type"] && (xhr["Content-Type"] = "application/json"); // 6. 发起一个网络请求 xhr.send() 并携带数据(没有就是null) xhr.send(params); /** * 可以使用this 代表xhr对象 1. readyState 不是4 就返回 2. 判断状态码 status */ function handler() { if (this.readyState !== 4) return; if (this.status === 200) { // resolve resolve(this.data); } else if (this.status === 404) { // reject reject("404 错误"); } } }); }; const method = "GET", url = "http://xxx.com:3000/list", header = { "Content-Type": "application/json", }, data = { page: 1, pageSize: 8, }; ajaxPromiseify(method, url, data, (responseType = "json"), header) .then((res) => { console.log(res); }) .catch((e) => { console.warn(e); }); function getWebData(url) { let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { switch (xhr.readyState) { case 0: console.log("请求未初始化"); break; case 1: console.log("以创建请求"); break; case 2: console.log("请求已确认"); break; case 3: console.log("正在接受数据"); break; case 4: console.log("数据全部接受完毕"); break; } }; xhr.ontimeout = function (e) { console.log("ontimeout", e); }; xhr.onerror = function (e) { console.log("onerror", e); }; xhr.open("GET", url, true); xhr.timeout = 3000; // 设置xhr请求超时事件 xhr.responseType = "text"; //设置响应返回的数据类型 // 常见的有 "document" , "json" , "text" "arraybuffer" //可以设置额外的请求头 xhr.setRequestHeader(my_header, "xxx"); xhr.send(null); } ================================================ FILE: todo/fe/练习-JavaScript部分/ES6 class编译/extends.js ================================================ class Parent { constructor(name) { this.name = name; } } class Child extends Parent { constructor(name, age) { super(name); // 调用父类的 constructor(name) this.age = age; } } var child1 = new Child('kevin', '18'); console.log(child1); ================================================ FILE: todo/fe/练习-JavaScript部分/tools.js ================================================ //取消冒泡 兼容W3C和IE function stopBubble(evnet) { if (event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } } //取消默认事件,兼容W3C和IE function cancelHandler(event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } } //获取子元素节点 function retElementChild(node) { var temp = { length: 0, push: Array.prototype.push, splice: Array.prototype.splice, }; var child = node.childNodes; var len = child.length; for (var i = 0; i < len; i++) { if (child[i].nodeType === 1) { temp.push(child[i]); } } return temp; } //返回元素e的第n曾祖先 function retParent(e, n) { while (e && n) { e = e.parentElement; n--; } return elem; } //返回元素e的第n个兄弟节点,n为正,返回后面的兄弟节点,n为负返回前面的,n为0,返回自己 function retSibling(e, n) { while (e && n) { if (n > 0) { if (e.nextElementSibling) { e = e.nextElementSibling; } else { for (e = e.nextSibling; e && e.nodeType != 1; e = e.nextSibling); } n--; } else { if (e.previousElementSibling) { e = e.previousElementSibling; } else { for ( e = e.previousSibling; e && e.nodeType != 1; e = e.previousSibling ); } n++; } } return e; } //封装children函数,解决兼容问题。 Element.prototype.myChildren = function () { var child = this.childNodes, len = child.length, arr = []; for (var i = 0; i < len; i++) { if (child[i].nodeType === 1) { arr.push(child[i]); } } return arr; }; //封装hasChildren()方法,不可使用children属性 Element.prototype.hasChildren = function () { var child = this.childNodes, len = child.length; for (var i = 0; i < len; i++) { if (child[i].nodeType === 1) { return ture; } } return false; }; //数组去重 Array.prototype.unique = function () { var temp = {}, arr = [], len = this.length; for (var i = 0; i < len; i++) { if (!temp[this[i]]) { temp[this[i]] = "abc"; arr.push(this[i]); } } return arr; }; //圣杯模型 var inherit = (function () { function F() {} return function (Target, Origin) { F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target; Target.prototype.uber = Origin.prototype; }; })(); //深度克隆 //insertAfter();功能类似inserBefore(); Element.prototype.insertAfter = function (targetNode, afterNode) { var temp = afterNode.nextElementSibling; if (temp == null) { this.appendChild(targetNode); } else { this.insertBefore(targetNode, temp); } }; //封装兼容性事件添加函数 function addEvent(elem, type, handle) { if (elem.addEventListener) { elem.addEventListener(type, handle, false); } else if (elem.attchEvent) { elem.attachEvent("on" + type, function () { handle.call(elem); }); } else { elem["on" + type] = handle; } } //封装兼容性解除事件函数 function removeEvent(elem, type, handle) { if (elem.removeEventListener) { elem.removeEventListener(type, handle, false); } else if (elem.detachEvent) { elem.datchEvent("on" + type, function () { handle.call(elem); }); } else { elem["on" + type] = null; } } //封装兼容ie的异步加载JavaScript //记得定义一个配合库来实现 var tools = { test: function () { //函数体,当作callback回调用 }, }; function loadScript(url, callback) { var script = document.createElement("script"); script.style = "text/javascript"; if (script.readyState) { script.onreadystatechange = function () { if (script.readyState == "complate" || script.readyState == "loaded") { tools[callback](); } }; } else { script.onload = function () { tools[callback](); }; } script.src = url; //能这样写吗? document.head.appendChlid(script); } // 封装type判断对象方法 template = { "[object Object]": "object", "[object Array]": "object-array", "[object String]": "object-string", "[object Boolean]": "object-boolean", "[object Number]": "object-number", }; function type(target) { var str = Object.prototype.toString.call(target); //判断原始值 和 引用值 if (target === null) { return null; } else if (typeof target == "object") { return template[str]; } else { return typeof target; } } ================================================ FILE: todo/fe/练习-JavaScript部分/功能函数汇总/index.md ================================================ ### 函数节流 ```js function throttle (fn,wait,options) { var prevTimeSpan = 0,curTimeSpan,tDiff,context; // 最后一次执行 var _trailing = function () { fn.apply(context,args); curTimeSpan = + new Date(); prevTimeSpan = curTimeSpan; timer = null; } var throttled = function (...args) { context = this; curTimeSpan = + new Date(); tDiff = wait - (curTimeSpan - prevTimeSpan); if(timer) { // 有定时器了但是事件又触发了说明不是最后一次,需要清除定时器 clearTimeout(timer); timer = null; } if(tDiff <= 0) { fn.apply(context,args); // !timer 是为了避免频繁注册 }else if(options.trailing !== false && !timer) { timer = setTimeout(_trailing,tDiff); } } return throttled; } // 5-21 (2) ``` ### 函数防抖 ```js function _debounce (fn,wait,options) { var timer = null,context,callNow; var _ = function (...args) { context = this; // 如果首次进入就需要执行,使用timer进行辅助判断 if(options.immediate) { callNow = !timer; callNow && fn.apply(context,args); } // 如果存在timer 就清空,以最后一次为主 if(timer) { clearTimeout(timer); timer = null; } timer = setTimeout(function () { fn.apply(context,args); timer = null; },wait) } return _; } // 5-21 (2) ``` ### 深度比较 ```js /** * 采用递归比较 */ function isObject(object) { return typeof object === 'object' && object !== null; } function isEqual(obj1,obj2) { if(!isObject(obj1) || !isObject(obj2)){ return obj1 === obj2 } if(obj1 === obj2) { return true; } // 首先判断数据长度是否相等 let l1 = Object.keys(obj1); let l2 = Object.keys(obj2); if(l1 !== l2) return false // 递归比较,以obj1 为基准 for (let key in obj1) { const res = isObject(obj1[key],obj2[key]) if(!res) return false } // 判断完毕,且没有return时,认为相等 return true; } ``` ### flat数组拍平 ```js var arr1 = [1, 2, [3, 4]] var arr2 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]]; const arr_ = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }] /** * 1. 只能处理一层arr * @param {}} arr */ function flat(arr) { return arr.reduce((prev, cur) => prev.concat(cur), []); } /** * 2. 对数据进行拉平操作,默认只拉平一层,如果需要全部拉平,需要传递指定的层数. * @param {*} arr * @param {*} deep */ function d_flat(arr,deep = 1){ let _helper = (arr,deep) => arr.reduce((prev,cur) => prev.concat(Array.isArray(cur) ? d_flat(cur,deep - 1) : cur),[]) // 如果还有层数没有处理,那么需要继续递归处理,但如果处理完毕,需要浅拷贝一层返回给上一层进行拼接 return deep > 0 ? _helper(arr,deep) : arr.slice() } /** * 3. 注意需要用...进行展开,因为得到的是一个遍历器对象 * @param {*} arr * @param {*} deep * 需要注意yield* 可以将控制权移交给另外一个生成器当他完成后返回回来继续干, * 和递归好像啊... */ function * gen_flat(arr,deep = 1) { // 不要使用for in 遍历数组,那都是索引... for(let item of arr) { if(Array.isArray(item) && !!deep ) { yield* flat(item,deep - 1) }else { yield item; } } } ``` ### Object.assign 和 深拷贝 ================================================ FILE: todo/fe/练习-JavaScript部分/功能函数汇总/函数深度比较.js ================================================ /** * 采用递归比较 */ function isObject(object) { return typeof object === 'object' && object !== null; } function isEqual(obj1,obj2) { if(!isObject(obj1) || !isObject(obj2)){ return obj1 === obj2 } if(obj1 === obj2) { return true; } // 首先判断数据长度是否相等 let l1 = Object.keys(obj1); let l2 = Object.keys(obj2); if(l1 !== l2) return false // 递归比较,以obj1 为基准 for (let key in obj1) { const res = isObject(obj1[key],obj2[key]) if(!res) return false } // 判断完毕,且没有return时,认为相等 return true; } ================================================ FILE: todo/fe/练习-JavaScript部分/功能函数汇总/函数节流.js ================================================ /** * 时间戳思路 * @param {Function} fn 需要节流的函数 * @param {Number} wait - (ms) 节流的时间间隔 */ function throttle(fn, wait) { let prevTime = 0, context = null; return function (...args) { let curTime = +new Date(); if (curTime - prevTime > wait) { fn.apply(context, args); prevTime = curTime; } }; } /** * 定时器版本 * @param {*} fn * @param {*} wait */ function throttle(fn, wait) { let timer = null, context = null; return function (...args) { context = this; if (!timer) { timer = setTimeout(function () { fn.apply(context, args); timer = null; }, wait); } }; } function throttle(fn, wait, options) { var prevTimeSpan = 0, curTimeSpan, tDiff, context; // 最后一次执行 var _trailing = function () { fn.apply(context, args); curTimeSpan = +new Date(); prevTimeSpan = curTimeSpan; timer = null; }; var throttled = function (...args) { context = this; curTimeSpan = +new Date(); tDiff = wait - (curTimeSpan - prevTimeSpan); if (timer) { // 有定时器了但是事件又触发了说明不是最后一次,需要清除定时器 clearTimeout(timer); timer = null; } if (tDiff <= 0) { fn.apply(context, args); // !timer 是为了避免频繁注册 } else if (options.trailing !== false && !timer) { timer = setTimeout(_trailing, tDiff); } }; return throttled; } ================================================ FILE: todo/fe/练习-JavaScript部分/功能函数汇总/函数防抖.js ================================================ /** * * @param {Function} fn 需要防抖的函数 * @param {Number} wait(ms) 防抖的时间 */ function debounce(fn,wait) { let timer = null; let _ = function(...args) { let context = this; if(timer) clearTimeout(timer) timer = setTimeout(function () { fn.apply(context,args) },wait) } return _; } function debounce (fn,wait,options) { let timer = null,context,callNow; var _debounce = function (...args) { context = this; if(options.immediate) { // 首次进入时,timer 为false callNow = !timer; callNow && fn.apply(context,args); } // 只保留最后一次定时器 if(timer) { clearTimeout(timer); } timer = setTimeout(function () { fn.apply(context,args); timer = null; },wait) } return _debounce; } // 首次点击有效果,然后开始防抖; function _debounce (fn,wait,options) { var timer = null,context,callNow; var _ = function (...args) { context = this; // 如果首次进入就需要执行,使用timer进行辅助判断 if(options.immediate) { callNow = !timer; callNow && fn.apply(context,args); } // 如果存在timer 就清空,以最后一次为主 if(timer) { clearTimeout(timer); timer = null; } timer = setTimeout(function () { fn.apply(context,args); timer = null; },wait) } return _; } 合并先跳过; ================================================ FILE: todo/fe/练习-JavaScript部分/功能函数汇总/数组平拍.js ================================================ var arr1 = [1, 2, [3, 4]] var arr2 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]]; const arr_ = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, "string", { name: "弹铁蛋同学" }] /** * 1. 只能处理一层arr * @param {}} arr */ function flat(arr) { return arr.reduce((prev, cur) => prev.concat(cur), []); } /** * 2. 对数据进行拉平操作,默认只拉平一层,如果需要全部拉平,需要传递指定的层数. * @param {*} arr * @param {*} deep */ function d_flat(arr,deep = 1){ let _helper = (arr,deep) => arr.reduce((prev,cur) => prev.concat(Array.isArray(cur) ? d_flat(cur,deep - 1) : cur),[]) // 如果还有层数没有处理,那么需要继续递归处理,但如果处理完毕,需要浅拷贝一层返回给上一层进行拼接 return deep > 0 ? _helper(arr,deep) : arr.slice() } /** * 3. 注意需要用...进行展开,因为得到的是一个遍历器对象 * @param {*} arr * @param {*} deep * 需要注意yield* 可以将控制权移交给另外一个生成器当他完成后返回回来继续干, * 和递归好像啊... */ function * gen_flat(arr,deep = 1) { // 不要使用for in 遍历数组,那都是索引... for(let item of arr) { if(Array.isArray(item) && !!deep ) { yield* flat(item,deep - 1) }else { yield item; } } } ================================================ FILE: todo/fe/练习-JavaScript部分/功能函数汇总/浅拷贝和深拷贝.js ================================================ // 浅拷贝 Object.assign实现 // 需要注意两点 // 1. 原始类型会被包装为对象 // 2. 继承属性和不可枚举属性是不能拷贝的 // 做一个属性描述符 Object.defineProperty(Object, "assign", { value: function (target, Args) { let to = Object(target); for (let i = 1; i < arguments.length; i++) { let next = arguments[i]; if (next !== null) { for (let item in next) { if (Object.prototype.hasOwnProperty.call(next, item)) { to[item] = next[item]; } } } } return to; }, writable: true, configurable: true, // enumerable :false; }); // 深拷贝 + 递归写法 + 循环写法解决递归爆栈 ================================================ FILE: todo/fe/练习-JavaScript部分/功能函数汇总/解析URL参数.js ================================================ ================================================ FILE: todo/fe/练习-JavaScript部分/图片懒加载.js ================================================ // 获取图片集合 const imgs = doucment.getElementsByTagName('img'); // 获取视口高度 const viewHeight = window.innerHeight || window.doucmentElement.clientHeight; // 图片编号 let num = 0; function lazyLoad() { for (let i = num; i < imgs.length; i++) { let distance = viewHeight - imgs[i].getBoundingClientRect().top; if (distance >= 0) { // 加载图片 imgs[i].src = imgs[i].getAttribute('data-src'); num = i + 1; } } } window.addEventListener('scroll', lazyLoad, false) const _imgs = doucment.getElementsByTagName('img'); // domList; const viewHeight = window.innerHeight || window.doucmentElement.clientHeight; todo: 浏览器距离获取 let _id = 0; for (let i = _id, len = _imgs.length; i < len; i++) { let distance = viewHeight - _imgs[0].getBoundingClientRect.top(); if (distance >= 0) { // 取attribute 并设置 imgs[i].src = imgs[i].getAttribute('data-src'); _id = _id + 1; } } ================================================ FILE: todo/fe/练习-JavaScript部分/手写assign.js ================================================ if(typeof Object.assign !== 'function') { Object.defineProperty(Object,'assign',{ value: function (target,sources) { if(target === null) { throw Error('error') } // 包装原始值 let to = Object(target) // 合成sources for(let i = 1; i < arguments.length;i++){ let curSource = arguments[i]; if(curSource !== null) { for(let key in curSource) { if(Object.hasOwnProperty.call(curSource,key)){ to[key] = curSource[key] } } } } return to; }, writable: true, configurable: true }) } ================================================ FILE: todo/fe/练习-JavaScript部分/手写create.js ================================================ if(typeof Object.create !== "function") { Object.create = function (proto) { if(typeof proto !== "object" && typeof proto !== "function") { throw new Error('error') } function F () {} F.prototype = proto.prototype; return new F(); } } ================================================ FILE: todo/fe/练习-JavaScript部分/手写instanceof.js ================================================ function _instanceof(l, r) { if (r === null) return false; var proto = Object.getPrototypeOf(l); while (true) { // 找到头 if (proto === null) return false; if (proto === r.prototype) return true; proto = Object.getPrototypeOf(proto); } } function _instanceof(l, r) { if (r === null) return false; let origin = Object.getPrototypeOf(l); while (true) { if (origin === null) return false; if (origin === r.prototype) return true; origin = Object.getPrototypeOf(origin); } } __proto__: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto 警告: 当Object.prototype.__proto__ 已被大多数浏览器厂商所支持的今天,其存在和确切行为仅在ECMAScript 2015规范中被标准化为传统功能,以确保Web浏览器的兼容性。为了更好的支持,建议只使用 Object.getPrototypeOf() 。 Object.create: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create ================================================ FILE: todo/fe/练习-JavaScript部分/手写new.js ================================================ /** * 测试用例: * function Son () { * ... * } * let son = myNew(Son,name) */ function myNew(constructor, ...args) { let to = {}; to.__proto__ = constructor.prototype; // 修改原型 /* * let F = function () {} * F.prototype = constructor.prototype; * to = new F() // 用到了new... 但是性能比__proto__好 * */ let result = constructor.apply(to, args); // 尝试执行,并拿到结果 return typeof result === "object" ? result : to; } ================================================ FILE: todo/fe/练习-JavaScript部分/手写深拷贝.js ================================================ function hackClone(target) { return JSON.parse(JSON.stringify(target)); } function recursionClone(target = {}, origin) { if (origin === null) { return; } for (let key in origin) { if (Object.hasOwnProperty.call(origin, key)) { let isObj = typeof origin[key] === "object" && origin[key] !== "null"; if (isObj) { let isArr = Object.toString.call(origin[key]) === "[object array]"; isArr ? (target[key] = []) : (target[key] = {}); recursionClone(target[key], origin[key]); } else { target[key] = key; } } } return target; } /** * - 何时需要深拷贝? * * - 如何判断是数组或者其他元素? * * - 如何遍历目标值 * * - 递归写法,出口在哪里? */ const isObject = (target) => typeof target === "object" && target !== null; const deepClone = (target, map = new Map()) => { if (map.get(target)) { // 防止循环引用处理 return; } map.set(target, true); if (isObject(target)) { const cloneTarget = Array.isArray(target) ? [] : {}; for (const prop in target) { if (target.hasOwnProperty(prop)) { cloneTarget[prop] = deepClone(target[prop], map); } } return cloneTarget; } else { return target; } }; console.log(true + 1); ================================================ FILE: todo/fe/练习-JavaScript部分/继承/1. 原型链继承.js ================================================ function Parent(name) { this.name = ['foo','bar']; } Parent.prototype.baz = function logFunc(params) { console.log('能够使用'); } function Child() { } // Child想要继承Parent 通过原型链继承 Child.prototype = new Parent(); var child1 = new Child() /** * 存在的问题: * * - 引用值共享 * - 创建子类时无法向Parent传递参数 */ ================================================ FILE: todo/fe/练习-JavaScript部分/继承/2. 借用构造函数继承.js ================================================ function Parent() { this.name = ['foo','bar']; this.funcLog = function () { console.log('能够使用'); } } //想要继承自Father,通过借用构造函数形式,将this绑定到 function Child(...args) { Father.apply(this,args) } /** * 解决:原型继承中无法向Parent传递参数的问题 * * 缺点:公共方法全部定义在构造函数中,会导致每个实例对象内存浪费 */ ================================================ FILE: todo/fe/练习-JavaScript部分/继承/3. 组合继承.js ================================================ function Parent(name) { this.name = ['foo','bar']; } Parent.prototype.func = function () { console.log('继承方法成功'); } function Child(...args) { Parent.call(this,args) } Child.prototype = new Parent(); // 由于原型对象被重写,需要将constructor指针归位 Child.prototype.constructor = Child; // 融合原型链继承和组合继承的优点,最常用的继承方式。 // 问题:需要调用两次父类构造函数 ================================================ FILE: todo/fe/练习-JavaScript部分/继承/4. 原型式继承.js ================================================ function Parent(name) { this.name = ['foo','bar']; } Parent.prototype.func = function () { console.log('继承成功'); } function myCreate (o) { function F () {} F.prototype = o; return new F(); } // 缺点: 和 原型链继承一样的问题,对引用值的共享 ================================================ FILE: todo/fe/练习-JavaScript部分/继承/5. 寄生组合式继承.js ================================================ // 解决 组合继承的两次调用构造函数 function Parent(name) { this.name = ['foo', 'bar']; } Parent.prototype.func = function () { console.log('继承成功'); } function Child() { } function prototype(child, parent) { var _prototype = myCreate(parent.prototype); child.prototype = _prototype; child.constructor = child; function myCreate(o) { function F() {}; F.prototype = o; return new F(); } } ================================================ FILE: todo/fe/练习-JavaScript部分/请你实现一个LRU缓存算法.js ================================================ //该算法描述的是当空间不够时,按照最近最少使用的策略进行删除,实现对新元素的缓存 // 除了LRU(基于时间)策略 还有 先进先出策略 LFU(基于使用次数) /** * 纯单链表: * 维护一个有序的单链表,越靠近链表尾部的元素是越早之前访问的元素(随时可能被更新掉)。 * 当有一个新的元素被get时,从链表的头部开始按顺序遍历链表 * - 如果此数据以及存在于链表中,我们遍历到该数据对应的具体节点,将其从该位置删除,并插入到链表的头部(最新) * 当在缓冲区设置一个新的值时,有如下两种情况: * - 如果缓存未满,直接插入到链表的头部 * - 如果缓存已满,将链表的尾部删除,将新的节点插入到链表的头部。 * * 觉得怪怪的,在设置新值这里 */ function LinkList(node) { this.node = node; this.next = null; } class LRU { constructor(size) { this._linkSize = size; } set() { } get() { } } // ES6 Map // 该数据结构提供了iteraotr接口,遍历该数据的顺序即是插入的顺序,同时拥有map的优势:O(1)时间查找和插入 function LRUcache(capacity) { this.capacity = capacity; this.map = new Map(); } LRUCache.prototype.get = function (key) { let _cacheValue = this.map.get(key) if (typeof _cacheValue === 'undefined') return -1; //使得让该key跑到最map的头部 this.map.delete(key) this.map.set(key, _cacheValue) return _cacheValue } LRUCache.prototype.set = function (key, val) { //如果元素在缓存中 if (this.map.has(key)) { this.map.delete(key) } else if (this.map.size() >= this.capacity) { // 需要删除末尾元素 let tail = this.map.keys.next().value; this.map.delete(tail) } // 处理完毕后将需要缓存的元素缓存到map中 this.map.set(key, val); } ================================================ FILE: todo/fe/练习-JavaScript部分/跨域.html ================================================ Document ================================================ FILE: todo/fe/练习-Nodejs/eventEmitter.js ================================================ /** *实现功能 * 1. 订阅功能 * 2. 发布功能 * * @class EventEmitter */ class EventEmitter { constructor() { // 保存所有事件 this.events = {}; } /** *向 events 对象中保存type 对应的cb *注意nodejs中的newListener默认事件机制 * @param {*} type * @param {*} cb * @memberof EventEmitter */ $on(type, cb) { if (typeof cb !== 'function') { throw new Error('xxx'); } if (eventName !== 'newListener') { // 如果当前绑定的不是newListener,看一下用户是否绑定过newListener,如果绑定过取出来依次执行 if (this._events['newListener']) { this._events['newListener'].forEach(fn => fn(eventName)) } } (this.events[type] || (this.events[type] = [])).push(cb) } /** *找到对应type 并全部执行他 * * @param {*} type * @memberof EventEmitter */ $emit(type, ...msg) { if (this.events && this.events[type]) { this.events[type].forEach(fn => fn.apply(this, msg)); } } /** *该函数只会绑定一次,执行完毕后自动销毁 *核心是做一个AOP * @param {*} type * @param {*} cb * @memberof EventEmitter */ $once(type, cb) { function once() { cb.call(this); $off(type, once) } this.$on(type, once) } /** * @param {*} type 解绑的事件名称 * @param {*} cb 对应的函数 * @memberof EventEmitter */ $off(type, cb) { const listeners = this.events[type]; if (listeners.length > 0) { listeners = listeners.filter(l => l !== cb) } } } ================================================ FILE: todo/fe/练习-Nodejs/globalObj.js ================================================ // console.log(this === global); // console.log(this === module.exports) // console.log(module.exports) // console.log(global.process) // console.log(process) let userOptions = process.argv.slice(2).reduce((memo, current, index, arr) => { if (current.startsWith('--')) { memo[current.slice(2)] = arr[index + 1] } return memo; }, {}) console.log(userOptions) ================================================ FILE: todo/fe/练习-实际开发/渲染10W节点/README.md ================================================ ## (前置知识)浏览器渲染机制 浏览器渲染进程中分为:GUI 线程、V8 线程、事件处理线程、定时器线程、ajax 线程... GUI 线程和 V8 线程是互斥关系的,在 js 脚本时会阻塞页面的渲染。 ## 傻循环 一次性将所有节点插入到页面中,关键代码如下: ```html ``` 通过下面对比 firFox 和 chrome 渲染花费的时间可以看出,一次性插入 10W 节点还是比较耗费时间的,非常影响用户体验。 > 由于浏览器的渲染优化,会一次性计算好,然后将所有缓存在 xxx 队列的节点一次性插入到页面中;但对于老浏览器就不存在这种优化。 ## 时间分片 可以利用浏览器的事件循环,每次只渲染“一部分”元素,将所有 js 计算好的内容(通过消息队列)缓存起来,当前 GUI 操作后会 loop 消息队列,取出一块渲染到页面上。递归该过程,即可完成 10W 节点的插入,这种思路简称**时间分片**。 > 实现采用的是 requestAnimationFrame,而非定时器。意图是一样的,都是将 DOM 操作拆分到小的时间节点,尽量不阻塞 DOM 渲染。但性能上还是有区别的。 ```html ``` 通过浏览器控制台可以发现首次渲染时间明显缩短,需要强调一下:打印出的时间是 js 将所有任务计算完毕后推入消息队列中 + 第一次 GUI 渲染(也就是首个 requestAnimationFrame)所用的时间。 但是这种方案存在的问题就是会导致页面中存在大量的 DOM 元素,一旦发生重绘重排,会对性能有很大的影响。 ## 虚拟列表 可以采用虚拟列表优化性能,例如 - [ ] 浏览器渲染机制? - [ ] 浏览器事件循环和渲染的关系? - [ ] requestAnimationFrame 和 定时器性能上的区别 ================================================ FILE: todo/fe/练习-实际开发/渲染10W节点/傻循环.html ================================================ Document
================================================ FILE: todo/fe/练习-实际开发/渲染10W节点/时间分片.html ================================================ Document
================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/160. 相交链表.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} headA * @param {ListNode} headB * @return {ListNode} */ /** * 让 l1 和 l2 拼接起来,保证数量上的相等 * cur1 * * 4 -> 1 -> 8 -> 4 -> 5 ->Null -> 5 -> 0 -> 1 -> 8 -> 4 -> 5 * 5 -> 0 -> 1 -> 8 -> 4 ->Null -> 5 -> 4 -> 1 -> 8 -> 4 -> 5 * * cur2 * 为了避免成环,我们需要两个移动指针,cur1 cur2 * * 当cur1 和 cur2 相等时(不是值相同),返回当前cur1(cur2也行) * * */ var getIntersectionNode = function (headA, headB) { let cur1 = headA, cur2 = headB; // 当第一次l1 + l2 遍历完毕后 // 此时 cur1 为空,本要再指向headA,此时 cur2 也为空,本要再指向headB // 但不要忘了循环条件,因此这次循环在判断相等(都为null)后,就已经结束 while (cur1 !== cur2) { cur1 = (cur1 !== null) ? cur1.next : headA; cur2 = (cur2 !== null) ? cur2.next : headB; } // cur1 === cur2 || cur1 === null === cur2 return cur1; }; ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/203. 移除链表元素.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @param {number} val * @return {ListNode} */ /** * * 构造一个新链表,已当前node头结点 * * 首先处理边界情况:如果链表头部元素值等于val * * 随后只需要让node节点扫描链表,当发现节点值等于val后,让当前runPoint 指向 node.next * * 如果当前节点值不等于val,只需要让当前runPoint 站在 node 上即可。 * * 通过runPoint 即可实现操作head 链表移除制定元素。 * * */ var removeElements = function (node, val) { var head = node; var runPoint = head; while (head != null && head.val === val) { head = head.next; } while (node != null) { if (node.val === val) { runPoint.next = node.next; } else { runPoint = node; } node = node.next; } return head; } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/4.插入有序链表.js ================================================ function insertTarget (node,target){ // 不做处理 if(node === null || target == null) { return; } // 头部情况 if(node.val > target) { target.next = node; return target; } // 中间情况 var runNode = node; while(runNode.next && runNode.next.val < target) { runNode = runNode.next; } // 找到正确位置,包含了两种情况:在中间 和直接到尾部; // 此时情况是:runNode的next节点大于target,需要在runNode和runNode.next切刀 // 并将target拼接起来 runNode.next = target; if(runNode.next !== null){ // 如果runNode已经到达链表的尾部,不需要拼接 target.next = runNode.next; } } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/445. 两数相加 II.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */ var addTwoNumbers = function (l1, l2) { let stack1 = []; let stack2 = []; while (l1) { stack1.push(l1.val); l1 = l1.next; } while (l2) { stack2.push(l2.val); l2 = l2.next; } let payload = 0; let res = null; while (stack1.length || stack2.length || payload) { let val1 = stack1.length ? stack1.pop() : 0; let val2 = stack2.length ? stack2.pop() : 0; let sum = val1 + val2 + payload; payload = sum / 10 | 0; curNode = new ListNode(sum % 10); curNode.next = res; res = curNode; } return res; } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/9. 两两交换链表中的节点.js ================================================ function swapPairs (head) { // base case if(head.next === null) { return head; } let firstNode = head; let secondeNode = head.next; firstNode.next = swapPairs(secondeNode.next); secondeNode.next = firstNode; return secondeNode; } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/x. 回文链表.js ================================================ var isPalindrome = function (head) { if (head === null || head.next === null) return true; // 快指针,慢指针找中点 let cur = head; let reversedHead = null; let prev = null; while (head !== null && head.next !== null) { prev = cur; cur = cur.next; head = head.next.next; prev.next = reversed; reversed = prev; } // 此时的cur即为中点,但是需要处理奇数的情况 if (head) { cur = cur.next } while (mid) { if (reversed.val !== mid.val) return false reversed = reversed.next mid = mid.next } return true } // 数组做法,只需要将链表存储到数组中,对数组进行出栈 和出队,判断两个元素是否相同。 var arr_isPalindrome = function (head) { var arr = []; while (head !== null) { arr.push(head.val); head = head.next; } // 进行出栈出队比较 while (arr.length > 1) { if (arr.pop() !== arr.shift) return false; } return true; } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/两数相加/2. 两数相加.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */ var addTwoNumbers = function (l1, l2) { let dummyNode = new ListNode(0); let _run = dummyNode, sum = payload = 0; while (l1 || l2) { // 遇null 补 0 操作 let x = l1 ? l1.val : 0; let y = l2 ? l2.val : 0; let sum = x + y + payload; _run.next = new ListNode(sum % 10); _run = _run.next; payload = parseInt(sum / 10) // 常规操作,保证“一对一对”进行 if (l1) l1 = l1.next; if (l2) l2 = l2.next; } // post process if (payload > 0) { _run.next = new ListNode(payload) } return dummyNode.next; }; ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/两数相加/两数相加.md ================================================ ## 两数相加 [2. 两数相加](https://leetcode-cn.com/problems/add-two-numbers/) ## 描述 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 示例: 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807 ## 思路 [合并两个有序链表](new/todo/fe/练习-数据结构算法刷题/链表/合并两个有序链表/README.md)的变种题型 我的示例: ``` 987 + 23 = 1010 7 -> 8 -> 9 3 -> 2 --> 0((null 补 0)) l1 + l2 + payload(0) = 0,payload:1 - > dummyNode -> 0 -> l1 + l2 + payload(1) = 1,payload:1 -> dummyNode -> 0 -> 1 -> l1 + l2 + payload(1) = 0,payload:1 -> dummyNode -> 0 -> 1 -> 0 -> l1(null) + l2(null) + payload(1) = 1,payload:0 -> over!!! dummyNode -> 0 -> 1 -> 0 -> 1 ``` - 构建 dummyNode,用于承载相加后的结果 - 对两个链表中的“每一对”节点进行相加操作,结果记为 sum,并且计算对应的 payload - 对 sum = sum % 10,再加入到 dummyNode 中(保证如果 sum >= 10 时,能够正确进位) - 计算 payload = sum / 10, 修改全局 payload 的值(保证进位后的值能够利用起来) - 如果链表有“错位”发生(一个还有节点,一个为 null),对 null 进行补 0 操作,保证能够相加 - 当“每一对”计算完毕后,进行后续处理,最后返回 dummyNode - 边界:如果 payload 还有值,需要继续计算(例如上面的例子:百进千) ## 代码 ```js var addTwoNumbers = function (l1, l2) { let dummyNode = new ListNode(0); let _run = dummyNode, sum = (payload = 0); while (l1 || l2) { // 遇null 补 0 操作 let x = l1 ? l1.val : 0; let y = l2 ? l2.val : 0; let sum = x + y + payload; _run.next = new ListNode(sum % 10); _run = _run.next; payload = parseInt(sum / 10); // 常规操作,保证“一对一对”进行 if (l1) l1 = l1.next; if (l2) l2 = l2.next; } // post process if (payload > 0) { _run.next = new ListNode(payload); } return dummyNode.next; }; ``` #### 复杂度 Time: 我们需要遍历“每一对”链表中的节点 => O(n) Space: 一个 虚拟节点 => O(1) ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/临时.md ================================================ | 出处 | Hint + 解: | | :-------------------------------------------------------------------------------------: | :--------------------------------------------------------------: | | [203. 移除链表元素](https://leetcode-cn.com/problems/remove-linked-list-elements/) | [思路:](./练习-数据结构算法刷题/链表/203.%20移除链表元素.js) | | [876. 链表的中间结点](https://leetcode-cn.com/problems/middle-of-the-linked-list/) | | | [141. 环形链表](https://leetcode-cn.com/problems/linked-list-cycle/) | | | 有序链表插入元素? | | | [86. 分隔链表](https://leetcode-cn.com/problems/partition-list/) | [思路:](./练习-数据结构算法刷题/链表/86.%20分隔链表.js) | | [21. 合并两个有序链表](https://leetcode-cn.com/problems/merge-two-sorted-lists/) | | | 重排链表 | | | [24. 两两交换链表中的节点](https://leetcode-cn.com/problems/swap-nodes-in-pairs/) | | | [2. 两数相加](https://leetcode-cn.com/problems/add-two-numbers/) | [思路:](./练习-数据结构算法刷题/链表/2.%20两数相加.js) | | [445. 两数相加 II](https://leetcode-cn.com/problems/add-two-numbers-ii/) | [思路:](./练习-数据结构算法刷题/链表/445.%20两数相加%20II.js) | | 难度中等217merge sort 链表?? | | | [92. 反转链表 II](https://leetcode-cn.com/problems/reverse-linked-list-ii/) | [思路:](./练习-数据结构算法刷题/链表/92.%20反转链表%20II.js) | | [25. K 个一组翻转链表](https://leetcode-cn.com/problems/reverse-nodes-in-k-group/) | [思路](./练习-数据结构算法刷题/链表/25.%20K%20个一组翻转链表.js) | | [面试题 02.06. 回文链表](https://leetcode-cn.com/problems/palindrome-linked-list-lcci/) | | | [160. 相交链表](https://leetcode-cn.com/problems/intersection-of-two-linked-lists/) | [思路:](./练习-数据结构算法刷题/链表/160.%20相交链表.js) | | 〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️ | 〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️〰️ | ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/分隔链表/86. 分隔链表.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @param {number} x * @return {ListNode} */ var partition = function (head, x) { if (head === null || head.next === null) { return head; } let dummyOne = new ListNode(-1), dummyTwo = new ListNode(-1); let dummyRun1 = dummyOne, dummyRun2 = dummyTwo; while (head !== null) { if (head.val >= x) { // append to 2 dummyRun2.next = head; dummyRun2 = dummyRun2.next } else { dummyRun1.next = head; dummyRun1 = dummyRun1.next } head = head.next } /** * post process * 1. 首先将dummyRun2的尾巴置空 * 2. 合并两个链表,注意是 dummyRun -> "dummyTwo.next" * 3. "返回dummyOne.next" * */ dummyRun2.next = null dummyRun1.next = dummyTwo.next; return dummyOne.next } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/分隔链表/README.md ================================================ ## 面试题 02.04. 分割链表 [面试题 02.04. 分割链表](https://leetcode-cn.com/problems/partition-list-lcci/) ### 描述 编写程序以 x 为基准分割链表,使得所有小于 x 的节点排在大于或等于 x 的节点之前。如果链表中包含 x,x 只需出现在小于 x 的元素之后(如下所示)。分割元素 x 只需处于“右半部分”即可,其不需要被置于左右两部分之间。 示例: 输入: head = 3->5->8->5->10->2->1, x = 5 输出: 3->1->2->10->5->5->8 ### 思路 关键点:虚拟链表 - 构造两个虚拟节点(dummy head) - 比较链表中每个节点和 x 的关系,将链表中小的值加入到 ListOne,大的值加入到 listTwo - if current.value < target : listOne.append(current) - else current.value > target : listTwo.append(current) - 合并两个链表,同时 cut 掉 listTwo 的尾结点,防止成环 - listTwo.next = null ### 代码 ```js /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @param {number} x * @return {ListNode} */ var partition = function (head, x) { if (head === null || head.next === null) { return head; } let dummyOne = new ListNode(-1), dummyTwo = new ListNode(-1); let dummyRun1 = dummyOne, dummyRun2 = dummyTwo; while (head !== null) { if (head.val >= x) { // append to 2 dummyRun2.next = head; dummyRun2 = dummyRun2.next; } else { dummyRun1.next = head; dummyRun1 = dummyRun1.next; } head = head.next; } /** * post process * 1. 首先将dummyRun2的尾巴置空 * 2. 合并两个链表,注意是 dummyRun -> "dummyTwo.next" * 3. "返回dummyOne.next" */ dummyRun2.next = null; dummyRun1.next = dummyTwo.next; return dummyOne.next; }; ``` Time:链表中有 n 个节点需要 compare => O(n) Space: 只有两个 dummyHead,没有动用其他空间 => O(1) ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/合并两个有序链表/21. 合并两个有序链表.js ================================================ /** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ /** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */ var mergeTwoLists = function (l1, l2) { if (l1 === null && l2 === null) { return null; } var dummyHead = new ListNode(-1); var runPonit = dummyHead; while (l1 && l2) { var i = l1.val; var j = l2.val; // 谁小移动谁 if (i < j) { runPonit.next = l1; l1 = l1.next; } else { runPonit.next = l2; l2 = l2.next; } runPonit = runPonit.next; } // post process if (l1 || l2) { runPonit.next = l1 ? l1 : l2; } return dummyHead.next; }; /** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ /** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */ var mergeTwoLists = function (l1, l2) { //到尾部后,处理两种后续情况 if (!l1) return l2; if (!l2) return l1; if (l1.val <= l2.val) { // 谁小移谁,并进行拼接返回 l1.next = mergeTwoLists(l1.next, l2) return l1; } else { l2.next = mergeTwoLists(l1, l2.next) return l2; } } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/合并两个有序链表/README.md ================================================ ## 合并两个有序链表 [21. 合并两个有序链表](https://leetcode-cn.com/problems/merge-two-sorted-lists/) ### 描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 ### 思路 #### 循环版 需要构建一个 dummy 链表还需要一个 runPoint 用来移动 dummy 链表,能够帮助我们避免空指针情况发生。 - dummyNode = new ListNode(-1) - 对两个链表进行 merge sort,谁小移动谁,将小的内容放入到 dummyNode.next 中 - 当两个链表操作完毕后,返回 dummyNode.next - 后续处理:如果长度不相等,需要将剩余节点拼接到 dummyNode 尾部 #### 递归 TODO: 图解 ### 代码 #### 循环版 ```js var mergeTwoLists = function (l1, l2) { if (l1 === null && l2 === null) { return null; } var dummyHead = new ListNode(-1); var runPonit = dummyHead; while (l1 && l2) { var i = l1.val; var j = l2.val; // 谁小移动谁 if (i < j) { runPonit.next = l1; l1 = l1.next; } else { runPonit.next = l2; l2 = l2.next; } runPonit = runPonit.next; } // post process if (l1 || l2) { runPonit.next = l1 ? l1 : l2; } return dummyHead.next; }; ``` #### 复杂度 Time:遍历两个链表并检查每个节点 => O(2n) => O(n) Space:一个 dummyNode 一个 runPoint 被创建,因此需要 ⇒ O(1) #### 递归 ```js var mergeTwoLists = function (l1, l2) { //到尾部后,处理两种后续情况 if (!l1) return l2; if (!l2) return l1; if (l1.val <= l2.val) { // 谁小移谁,并进行拼接返回 l1.next = mergeTwoLists(l1.next, l2); return l1; } else { l2.next = mergeTwoLists(l1, l2.next); return l2; } }; ``` ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/环形链表/141. 环形链表.js ================================================ /** * Definition for singly-linked list. * function Listhead(val) { * this.val = val; * this.next = null; * } */ /** * @param {Listhead} head * @return {boolean} */ var hasCycle = function (head) { if (head === null || head.next === null) { return false; } var fast = head.next.next; var slow = head; while (fast && fast.next) { // meet if (slow == fast) { return true; } fast = fast.next.next; slow = slow.next; } // 此时快指针已经到尾部或者出链表 return false; }; ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/环形链表/README.md ================================================ ## 环形链表 [141. 环形链表](https://leetcode-cn.com/problems/linked-list-cycle/) ### 描述 给定一个链表,判断链表中是否有环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 示例 1: 输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。 ![20200621102738](https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621102738.png) 示例 2: 输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。 ![20200621102747](https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621102747.png) 示例 3: 输入:head = [1], pos = -1 输出:false 解释:链表中没有环。 ![20200621102754](https://supyyy-1259673491.cos.ap-beijing.myqcloud.com/2020/pictures20200621102754.png) 进阶: 你能用 O(1)(即,常量)内存解决此问题吗? ### 思路 快慢指针问题 - 快指针每次移动两步,慢指针每次移动一步 - 如果链表不存在环,那么这个问题就和链表的中间节点一样,只不过返回 true 而已 - 如果链表存在环那么在某一时刻,快指针和慢指针一定会相遇。 ### 代码 ```js var hasCycle = function (head) { if (head === null || head.next === null) { return false; } var fast = head.next.next; var slow = head; while (fast && fast.next) { // meet if (slow == fast) { return true; } fast = fast.next.next; slow = slow.next; } // 此时快指针已经到尾部或者出链表 return false; }; ``` Time:链表中有 n 个节点,我们需要遍历整个列表。最坏情况下需要检查每一个节点(环出现在尾部) => O(n) Space: 两个指针变量 => O(1) ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/翻转链表/25. K 个一组翻转链表.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @param {number} k * @return {ListNode} */ var reverseKGroup = function (head, k) { if (head === null || head.next === null) { return head; } let tail = head; // 1. 找到tail指针,如果数量不足,则直接返回当前组的head也就是5(第三组的开始) for (let i = 0; i < k; i++) { if (tail === null) { return head; } tail = tail.next; } // 此时的tail指针在3,需要将[head:1,tail:3)区间元素进行翻转 let newHead = reverse(head, tail); // 此时的head指针在1,需要将1指向第二组翻转完毕的内容 head.next = reverseKGroup(tail, k); return newHead; function reverse(head, tail) { let post = null; let prev = null; // 左开右闭,到tail就可以停下了 while (head != tail) { post = head.next; head.next = prev; prev = head; head = post; } return prev; } } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/翻转链表/92. 反转链表 II.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @param {number} m * @param {number} n * @return {ListNode} */ var reverseBetween = function (head, m, n) { let post; if (m === 1) { return reverse(head, n) } head.next = reverseBetween(head.next, m - 1, n - 1); return head; function reverse(head, n) { if (n === 1) { post = head.next; return head; } let newHead = reverse(head.next, n - 1); head.next.next = head; head.next = post; return newHead; } }; ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/翻转链表/README.md ================================================ ## 出处 [206. 反转链表](https://leetcode-cn.com/problems/reverse-linked-list/) ### 描述 反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 进阶: 你可以迭代或递归地反转链表。你能否用两种方法解决这道题? ### 思路 #### 循环 - 构造三根指针,形如 prev -> cur -> post -> .... -> tail -> null - 循环检查每个节点,翻转即可。 - post = cur.next - cur.next = prev (翻转操作) - prev = cur (移动 prev 指针) - cur = post(移动 cur 指针) 边界:当 cur 指针移动到 null 指针,说明链表全部翻转完毕,此时返回 prev 指针即可。 #### 递归 - 利用分治思想将问题向下进行拆分,抽象成 cur.next 的翻转问题(如果需要翻转当前链表,我首先要将下一个节点进行翻转,翻转下一个节点,需要将下下个节点翻转...) - 所有被拆分的小问题处理完毕后(每层的翻转),最终的问题也随之解决(整个链表的翻转) - 当链表不能再向下分时,此节点就是最终的翻转后的新头部节点 - 在每层翻转时,需要切断链表原来的指针 ### 代码 #### 循环 ```js var reverseList = function (head) { if (head === null || head.next === null) { return head; } // 三指针 let prev = null, cur = head, post; while (cur !== null) { post = cur.next; cur.next = prev; prev = cur; cur = post; } // 此时cur 已经到null节点,prev指针刚好是翻转后的头节点 return prev; }; ``` #### 复杂度 Time: 如果链表长度为 n,那么需要有 n 个节点需要检查翻转 => O(n) Space: 常量空间消耗,三根指针 => O(1) #### 递归 ```js var reverseList = function (head) { /** * base case 是 tail指针 * 这样做的目的: * 1. 正确获得新链表的头部 -> tail Node * * 2. 直接从 Last but one tail Node 开始翻转 * 能够做到既控制cur.next节点的指针,以及切断当前节点的指针 * */ if (head === null || head.next === null) { return head; } let newHead = reverseList(head.next); // 在当前层做点什么事儿,就能将小问题解决? /** * 1.翻转操作 * 2.切断原指针 * */ head.next.next = head; head.next = null; return newHead; }; ``` #### 复杂度 Time: 递归树的高度为 n 层,在每层操作的时间为 O(1),所以总时间为 => O(n) Space: n 层递归树 => O(n) ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/翻转链表/面试题24. 反转链表.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {ListNode} */ var reverseList = function (head) { /** * base case 是 tail指针 * 这样做的目的: * 1. 正确获得新链表的头部 -> tail Node * * 2. 直接从 Last but one tail Node 开始翻转 * 能够做到既控制cur.next节点的指针,以及切断当前节点的指针 * */ if (head === null || head.next === null) { return head } let newHead = reverseList(head.next); // 在当前层做点什么事儿,就能将小问题解决? /** * 1.翻转操作 * 2.切断原指针 * */ head.next.next = head; head.next = null; return newHead; }; // !循环版 var reverseList = function (head) { if (head === null || head.next === null) { return head; } // 三指针 let prev = null, cur = head, post; while (cur !== null) { post = cur.next; cur.next = prev; prev = cur; cur = post; } // 此时cur 已经到null节点,prev指针刚好是翻转后的头节点 return prev; } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/重排链表/143. 重排链表.js ================================================ var reorderList = function (head) { if (head === null || head.next === null) { return head; } let first = head; // 1. 找到中点 let mid = findMiddleNode(first); let second = mid.next; // 切断first的尾巴 mid.next = null; // 2. 翻转链表 let reversedSecond = reverseList(second) // 3. merge let result = merge(first, reversedSecond); return result } function findMiddleNode(node) { if (node === null || node.next === null) { return node; } let slow = node, fast = node; while (fast !== null && fast.next !== null) { slow = slow.next; fast = fast.next.next; } return slow; } function reverseList(node) { if (node === null || node.next === null) { return node; } // let newHead = reverseList(node.next); // node.next.next = node; // node.next = null; // return newHead let prev = null, cur = node, post; while (cur !== null) { post = cur.next; cur.next = prev; prev = cur; cur = post; } return prev; } function merge(l1, l2) { if (l1 === null) { return l2 } else if (l2 === null) { return l1 } let dummyHead = new ListNode(-1); let runPoint = dummyHead; let runl1 = l1; let runl2 = l2; while (runl1 !== null && runl2 !== null) { runPoint.next = runl1; runl1 = runl1.next; runPoint = runPoint.next; runPoint.next = runl2; runl2 = runl2.next; runPoint = runPoint.next; } /** * 因为是取中间节点 * 所以 l1.size >= l2.size * 因此 有剩余一定是l1 */ if (runl1 !== null) { runPoint.next = runl1; } } ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/重排链表/README.md ================================================ ## 重排链表 [143. 重排链表](https://leetcode-cn.com/problems/reorder-list/) ### 描述 给定一个单链表 L:L0→L1→…→Ln-1→Ln , 将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→… 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例 1: 给定链表 1->2->3->4, 重新排列为 1->4->2->3. 示例 2: 给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3. ### 思路 整个问题可以分解为三步: - 找到链表的中间节点 ([快慢指针找中点](new/todo/fe/练习-数据结构算法刷题/链表/链表的中间结点/README.md)) - 链表最终会分为两部分: - head -> ... -> mid -> null - mid.next -> ... -> tail -> null - 对后一部分进行链表翻转([翻转链表](new/todo/fe/练习-数据结构算法刷题/链表/翻转链表/README.md)) - 后一部分链表变为: - tail -> ... -> mid.next -> null - 将两个链表进行合并操作([合并链表](new/todo/fe/练习-数据结构算法刷题/链表/合并两个有序链表/README.md)) - head -> tail -> ... -> null ### 代码 ```js var reorderList = function (head) { if (head === null || head.next === null) { return head; } let first = head; // 1. 找到中点 let mid = findMiddleNode(first); let second = mid.next; // 切断first的尾巴 mid.next = null; // 2. 翻转链表 let reversedSecond = reverseList(second); // 3. merge let result = merge(first, reversedSecond); return result; }; function findMiddleNode(node) { if (node === null || node.next === null) { return node; } let slow = node, fast = node; while (fast !== null && fast.next !== null) { slow = slow.next; fast = fast.next.next; } return slow; } function reverseList(node) { if (node === null || node.next === null) { return node; } // let newHead = reverseList(node.next); // node.next.next = node; // node.next = null; // return newHead let prev = null, cur = node, post; while (cur !== null) { post = cur.next; cur.next = prev; prev = cur; cur = post; } return prev; } function merge(l1, l2) { if (l1 === null) { return l2; } else if (l2 === null) { return l1; } let dummyHead = new ListNode(-1); let runPoint = dummyHead; let runl1 = l1; let runl2 = l2; while (runl1 !== null && runl2 !== null) { runPoint.next = runl1; runl1 = runl1.next; runPoint = runPoint.next; runPoint.next = runl2; runl2 = runl2.next; runPoint = runPoint.next; } /** * 因为是取中间节点 * 所以 l1.size >= l2.size * 因此 有剩余一定是l1 */ if (runl1 !== null) { runPoint.next = runl1; } } ``` Time:三步都是处理链表中的每一个节点,因此 => O(n) Space: 翻转递归 => O(n) || 翻转循环 => O(1) ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/链表的中间结点/876.链表中间节点.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {ListNode} */ var middleNode = function (head) { if (head == null) { return; } let slow = head; let fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; } return slow; }; ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/链表的中间结点/README.md ================================================ ## 链表的中间结点 [876. 链表的中间结点](https://leetcode-cn.com/problems/middle-of-the-linked-list/) ### 描述 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。 如果有两个中间结点,则返回第二个中间结点。 示例 1: 输入:[1,2,3,4,5] 输出:此列表中的结点 3 (序列化形式:[3,4,5]) 返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。 注意,我们返回了一个 ListNode 类型的对象 ans,这样: ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL. 示例 2: 输入:[1,2,3,4,5,6] 输出:此列表中的结点 4 (序列化形式:[4,5,6]) 由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。 ### 思路 典型的快慢指针问题 - 慢指针、快指针都初始化为 head 节点 - 快指针每次走两步 - 慢指针每次走一步 - 当快指针刚好走到 null 节点时,慢指针刚好为中间节点 ### 代码 题目要求在偶数情况下,返回第二个中间节点,因此我们需要将快指针初始化至 head 节点。 ```js var middleNode = function (head) { if (head == null) { return; } let slow = head; let fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; } return slow; }; ``` 如果要求返回第一个中间节点,需要将快指针初始化至 head.next 节点 ```js var middleNode = function (head) { if (head == null) { return; } let slow = head; let fast = head.next; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; } return slow; }; ``` Time:链表中有 n 个节点,我们需要遍历整个列表。快指针每次移动两步,时间为 O(n/2), 因此总时间为 => O(n) Space: 两个指针变量 => O(1) ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/面试题18. 删除链表的节点.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @param {number} val * @return {ListNode} * * 重点:考虑边界情况 * - 删除元素在头部 * - 删除元素在尾部? * * 定义两个指针,i 是 目标节点前一个元素,j 是 目标节点元素 * * 保存j -> next 的元素为tail * * 将i -> 指向 tail * */ var deleteNode = function (head, val) { if (head === null) { return; } let i = head, j = head.next; // case1: if (head.val === val) { return j; } while (j.val !== val) { i = i.next j = j.next } // 此时j一定是目标节点,切断即可 let tail = j.next; i.next = tail; return head; }; ================================================ FILE: todo/fe/练习-数据结构算法刷题/链表/面试题22. 链表中倒数第k个节点.js ================================================ /** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * 翻转链表的变形 * * 1. 压 到 tail.next (null) 时,初始化n = 0; * 2. 每返回经过一个节点,n++ * 3. 当n === k 时返回此节点 * */ var getKthFromEnd = function (head, k) { let res; helper(head, 0); function helper(head, index) { if (head.next === null) { if (k === 1) res = head; return 1; } let num = 1 + helper(head.next, index) console.log(num) if (num === k) res = head; return num; } return res; }; /** * @param {ListNode} head * @param {number} k * @return {ListNode} */ /** * 双指针走法 * * 构造两个指针,i 和 j * * j 先走,当走了k步后,i走,当j到达尾部时,i的值就是目标。 * * */ var getKthFromEnd = function (head, k) { let i = head, j = head; // 让j先走k步 for (let i = 0; i < k; i++) { j = j.next } // i和j一起走,当j到达尾部null时,返回i // 之所以让j 走到null,是因为题目给的hint:最后个非null元素,是倒数第一个。 while (j) { j = j.next; i = i.next; } return i; }; ================================================ FILE: todo/fe/组件/vue-懒加载/Note.md ================================================ ### 初始化指令 指令是通过isntall方法初始化的,因此实现指令第一步就是构造install方法。 [vue-lazyload](https://github.com/sup-fiveyear/vue-lazyload)的源码实现里,对懒加载的代码进行了封装,对外暴露一个函数作为接口,实现了高内聚。 ```js function Lazy(Vue) { // dosomethings return class LazyClass { constructor() { } // bind指令的初始化逻辑 add(el,bindings,vnode) { } } } const vueLazyLoad = { install(Vue, options) { const LazyClass = Lazy(Vue); const lazy = new LazyClass(options); Vue.directives('lazy', { bind: lazy.add.bind(this) // 还可以有4个钩子函数 // inserted // update // componentUpdated // unbind }) } } ``` ### 找到拥有scroll属性的父级元素,并监听事件 - bind 在生效时,无法拿到其父元素,其原因可以参考bind指令生效时机(patch算法结束后,插入dom前) - 可以使用 nextTick 将获取父元素延迟到DOM添加完毕后 - 调用getScrollParent(el) 获取真实的 带 “scroll” 属性的父元素 - 监听 其 scroll 事件 > ! tick:避免重复绑定事件,做一个标志位 ```js add(el, bindings, vnode) { Vue.nextTick(() => { let ownScrollParent = getScrollParent(el); // bindHandler 标志位,防止多次绑定事件 if (ownScrollParent && this.bindHandler) { this.bindHandler = true; ownScrollParent.addEventListener('scroll', this.handleLazyLoad.bind(this)) } }) } ``` ### 处理图片元素 - 先将所有绑定过指令的图片元素通过类构造出一个实例,并存储到一个数组中 - 因此可以判断数组中里面哪些需要显示,哪些不需要显示 ```js add(el, bindings, vnode) { Vue.nextTick(() => { //... const imgListener = new ReactiveImgListener({ el, src: bindings.value, options: this.options, }) this.imgListenerQueue.push(imgListener) }) } ``` ```js class ReactiveImgListener { constructor({ el, src, options }) { this.el = el; this.src = src; this.options = options; } checkInViewPosition() {} } ``` ### 校验是否显示图片 - 为图片对象增加状态属性,通过该属性能够标识三种状态:是否加载过、正在加载、加载失败 - 提供检测当前图片位置 - 提供对图片src属性处理逻辑的统一方法 ```js checkInViewPosition() { let { top } = this.el.getBoundingClientRect(); return top < window.innerHeight * (this.options.preLoad || 1.3); } ``` ```js setImgSrctoAttr(imgElement, state) { let el = imgElement.el; let src = '' switch (state) { case 'loading': src = imgElement.options.loading || '' break; case 'error': src = imgElement.options.error || ''; break; default: src = imgElement.src; break; } el.setAttribute('src', src) } ``` ### 加载图片 - 首先渲染loading态的图片 - 绑定图片onload 以及 onerror 的事件处理函数 ```js function loadImageAsync(src, resolve, reject) { let image = new Image(); image.src = src; image.onload = resolve; image.onerror = reject; } ``` ```js /** * 检查 imgListenerQueue 中 是否有需要加载的图片对象 * */ handleLazyLoad() { this.imgListenerQueue.forEach(imgListener => { if (!imgListener.state.loading) { let catIn = imgListener.checkInViewPosition(); catIn && imgListener.loadImg(); } }) } ``` ```js loadImg() { this.imgAddSrc(this, 'loading'); loadImageAsync(this.src, () => { this.state.loading = true; this.imgAddSrc(this, 'finish'); }, () => { this.imgAddSrc(this, 'error'); }); } ``` [完整代码请查阅这批文章](v-lazy.js) ================================================ FILE: todo/fe/组件/vue-懒加载/v-lazy.js ================================================ /** * 作为入口函数,执行后得到一个LazyClass实例对象 * @param {*} Vue */ function Lazy(Vue) { class ReactiveImgListener { constructor({ el, src, options }) { this.el = el; this.src = src; this.options = options; this.state = { loading: false } this.imgAddSrc = imgAddSrc } checkInViewPosition() { let { top } = this.el.getBoundingClientRect(); return top < window.innerHeight * (this.options.preLoad || 1.3); } loadImg() { this.imgAddSrc(this, 'loading'); loadImageAsync(this.src, () => { this.state.loading = true; this.imgAddSrc(this, 'finish'); }, () => { this.imgAddSrc(this, 'error'); }); } } return class LazyClass { constructor(options) { this.options = options; this.bindHandler = false; this.imgListenerQueue = []; } // TODO: 指令原理,以及钩子生效时机 /** * 1. 找到拥有scroll属性的父级元素 * - bind 在生效时,无法拿到其父亲元素,其原因可以参考生效时机 * - nextTick 延迟到DOM添加完毕后,再去获取其父元素 * - 调用getScrollParent(el) 获取真实的 带 “scroll” 属性的父级元素 * - 向真实父级元素 监听 scroll 事件 * ! tick:避免重复绑定事件,做一个标志位 * * 2. ? 判断当前元素是否在容器 “可视窗口” 中 * * 3. 存储所有绑定过指令的图片元素,将每个图片元素对应的对象,通过类进行生成一下 * - 有了存储结构后,可以判断里面哪些需要显示,哪些不需要显示 * * 4. 校验是否显示图片逻辑 * - 为图片对象增加状态属性,可以标识:是否加载过、正在加载、加载失败 * - 提供检测当前图片位置,判断是否需要加载图片 * - 提供渲染逻辑的统一方法,在构造图片对象时,挂在到对象身上。 * * 5. 加载图片 * - 通过提供的统一渲染逻辑方法,先渲染loading态的图片 * - 而后根据当前图片加载状态进行判断,是渲染正确图片还是错误图片 * * @param {*} el * @param {*} bindings * @param {*} vnode */ add(el, bindings, vnode) { Vue.nextTick(() => { let ownScrollParent = getScrollParent(el); if (ownScrollParent && this.bindHandler) { this.bindHandler = true; ownScrollParent.addEventListener('scroll', this.handleLazyLoad.bind(this)) } const imgListener = new ReactiveImgListener({ el, src: bindings.value, options: this.options, imgAddSrc: this.setImgSrctoAttr.bind(this) }) this.imgListenerQueue.push(imgListener) // 在没有触发滚动事件时,会进行一次检查 this.handleLazyLoad() }) } /** * 检查 imgListenerQueue 中 是否有需要加载的图片对象 * */ handleLazyLoad() { this.imgListenerQueue.forEach(imgListener => { if (!imgListener.state.loading) { let inView = imgListener.checkInViewPosition(); inView && imgListener.loadImg(); } }) } /** * 根据state 的不同,将图片对象的src属性进行设置 * @param {*} imgElement * @param {*} state */ setImgSrctoAttr(imgElement, state) { let el = imgElement.el; let src = '' switch (state) { case 'loading': src = imgElement.options.loading || '' break; case 'error': src = imgElement.options.error || ''; break; default: src = imgElement.src; break; } el.setAttribute('src', src) } } } /** * - 提供install方法 * * - 注册lazy指令钩子函数 */ const vueLazyLoad = { install(Vue, options) { const LazyClass = Lazy(Vue); // lazy指令的核心逻辑封装在此 const lazy = new LazyClass(options); Vue.directives('lazy', { bind: lazy.add.bind(lazy) // inserted: // update: // componentUpdated: // unbind: }) } } function getScrollParent(el) { let parent = el.parentNode; while (parent) { if (/(?:scroll)|(?:auto)/.test(getComputedStyle(parent)['overflow'])) { return parent; } parent = parent.parentNode; } return parent; } function loadImageAsync(src, resolve, reject) { let image = new Image(); image.src = src; image.onload = resolve; image.onerror = reject; } ================================================ FILE: todo/fe/记录-面试DEMO/01.md ================================================ # 百度美团1面刷题 #苏鹏宇-面试准备/面经 [百度暑期实习前端一面面经_笔经面经_牛客网](https://www.nowcoder.com/discuss/426588?type=post&order=hot&pos=&page=1&channel=&source_id=1_post) [美团前端二面凉经_笔经面经_牛客网](https://www.nowcoder.com/discuss/425614?type=post&order=hot&pos=&page=1&channel=&source_id=1_post) ### css + html #### meta标签有哪些内容,代表什么 - 字符集,规定当前页面采用的是什么编码格式,只能是UTF-8 ```js ``` - SEO搜索关键字 ```js ``` - 移动端 name 字段设置成viewport即可 @ 移动端 ``` ``` - 自动跳转 http-equiv属性定义了一个编译指示指令。这个属性叫做 http-equiv(alent) 是因为所有允许的值都是特定HTTP头部的名称 例如其中的字段:refresh - 如果 content 只包含一个正整数,则是重新载入页面的时间间隔(秒); - 如果 content 包含一个正整数并且跟着一个字符串 ';url=' 和一个合法的 URL,则是重定向到指定链接的时间间隔(秒) ```js ``` #### 什么意思? DOCTYPE是document type的简写,它并不是 HTML 标签,也没有结束标签,它是一种标记语言的文档类型声明,即告诉浏览器当前 HTML 是用什么版本编写的。 如果没有doctype会影响代码的兼容性问题(处在混杂模式,很多标准都无法实时),因此他决定了浏览器最终如何显示你的Web文档。 #### px, em, rem 区别:绝对单位和相对单位 应用场景:(2倍图) @移动端适配 #### 如何清除浮动 1. 父元素浮动 最容易想到的方案,但可读性极差,不易维护,页面健壮性不足。 2. 父元素底部包含clear空div 利用块级元素 clear:both属性清除浮动,把盒子重新撑起来,缺点:引入冗余元素 1. 父元素做BFC: BFC-overflow:hidden 有可能导致溢出元素不可见 1. after伪元素清除浮动 不需要引入冗余元素,boostrap框架采用的清除浮动方案。 ```css div::after { clear:both; display: block; content: ''; } ``` #### 如何获取DOM元素的第一个子元素,3的倍数的元素 API: firstChild 和 childNodes 进行模3 判断 考点:DOM api 5.垂直水平居中 - 定宽:绝对定位 + (-50%)宽度 6.透明度从0->1,至少说出两种实现方式 (阵亡) #### css选择器优先级,权重计算 import 行间样式 id class + 伪类 元素选择器 + 伪元素选择器 通配符选择器 ### js #### typeof和instanceof区别 typeof:主要是用来判断原始类型(string boolean undefined null symbol)的,对于引用值只能判断对象和函数 instanceof 是基于原型链的查找,只要在原型链上,判断即为true;通常只能判断引用值,但是可以通过symbol 改写判断方法实现对原始值的判断 * [ ] 神三元 实现思路:向上遍历原型链 ```js function _instanceof (l,r) { if(r === null) return false; var proto = Object.getPrototypeof(l); while(true) { // 找到头 if(proto === null) return false; if(proto === r.prototype) return true; proto = Object.getPrototypeof(proto); } } ``` #### 闭包 1. 概念:当一个函数能够访问并记住当前的词法作用域,就产生了闭包。 2. 从js编译到执行解释闭包生成的过程 js 是一门即时编译型语言,在执行前会生成执行期上下文和可执行代码,紧接着才会执行代码。在生成执行期上文时,关键环节:预编译、生成词法作用域、调用栈。 词法作用域就是描述了变量的生命周期和可见性,由于js采用的是词法作用域,其规则由书写位置决定。 函数作为js中的一等公民,在执行前一刻也会创建上下文、预编译并将执行上下文压入到调用栈。 在预编译环节中,1. 生成AO对象 2.生成[scope]属性,是复制他所在的词法作用域,并将AO对象添加到链的头部 3. 变量函数提升 4. 形参实参相统一 当函数执行完毕,会在执行栈中通过下移ESP指针删除当前的执行上文栈(垃圾回收),但是由于其内部函数(保存着当前的作用域)保存到外部导致当前执行上下文无法被销毁,这样产生了闭包。 3. 闭包的应用 - 实现块级作用域 - 防抖节流 - 私有化属性 4. 闭包的危害 - 内存泄漏 #### 类型转换和装箱拆箱 String 该函数执行后会返回一个字符串类型的数据,其内容是入参时的字符串。 new String 会返回一个对象,该对象是调用了入参元素的迭代器接口,并生成一个指定key-value键值对。 == js是一门动态类型语言,存在着类型转换。由于 判断操作符左侧是字符串格式,会将右侧转化成字符串进行判断。 右侧呢,是一个对象类型,首先会调用 x.valueOf(),如果该方法没有返回一个原始类型,会紧接着调用对象的toValue方法,同理还没有,则会调用toString,否则就会报错。 === 是完全相等判断,不会经历类型转换,因此他的结果显然为false * [ ] 类型转换流程 对象转换原始值 ```js String(’11’) == new String(’11’); String(’11’) === new String(’11’); ``` #### 原型原型链 1.流程题 首先Foo在没有执行时,其自身是没有a方法的,因此在new 之前,调用的是手动挂载的方法 因此会先打印4. 当经历了new 操作符后,Foo 被执行,函数内部的静态方法a 覆盖了之前手动挂载的方法,同时将this.a 传递给obj对象,因此obj.a 会打印2。 最后执行Foo.a 会执行覆盖后的函数,也就是1 (全程没有原型半点关系) ```js function Foo() { Foo.a = function() { console.log(1) } this.a = function() { console.log(2) } } Foo.prototype.a = function() { console.log(3) } Foo.a = function() { console.log(4) } Foo.a(); let obj = new Foo(); obj.a(); Foo.a(); ``` #### new 1. 创造继承自构造函数原型的对象 2. 执行构造函数,将_this(构造的对象)作为apply的第一个参数执行。 3. 根据函数的返回值判断是否要将_this(构造的对象返回) 4. 注意new 过程中是可以传递参数的。 ``` function new (constructor,...args) { var to = {},res; to.__proto__ = constructor.prototype res = constructor.apply(to,args) return (typeof to === 'object') ? res : to; } ``` * [ ] 转数组方法: http://47.98.159.95/my_blog/js-array/002.html#_1-array-prototype-slice-call 2.手写继承 1. 原型链继承 2. 借用构造函数式继承 3. 组合继承 4. 原型式继承 5. 圣杯继承 圣杯继承 ``` function e (c,p) { var _p = create(p); c.prototype = _p; c.constructor = c; /* 以p 创建一个原型式对象,提供给c来继承 */ function create (p) { function F () {} F.prototype = p; return new F(); } } ``` 异步编程方案 ```js async function async1() { console.log(‘async1 start’); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end’); ``` ### 浏览器 同源策略,跨域方案 概念:浏览器出于对安全的考虑,限制了非同源之间的一些操作。 1. 同源:协议 && 域名 && 端口 2. 限制:获取DOM,获取cookie webStorage 发送AJAX请求 输入url到页面展现的过程(具体) * [ ] 具体过一遍 ### 网络 tcp流量控制,拥塞控制等 阵亡 ### 操作系统 操作系统相关,进程通信,进程状态,死锁 阵亡 ### 算法编码 1. 手写快排 选取一个pivot pivot 以左: 小于, 以右:大于; 划分至不可再分,再进行一个合并。 阵亡 ```js ``` 2. 解析字符串[image:3CF4C549-06F3-450C-81BE-8E09B114770F-14289-0000E50BBDAB34EE/292551785_1589255627007_D0BF2ACDCE59ECF0289ED1730E29902E.png] 阵亡 ### vue(略) 1.vue生命周期 2.异步在vue的哪个阶段实现 3.组件间通信 ### 场景题 无限列表的优化? vue实现轮播图 ================================================ FILE: 操作系统层面的调用栈管理?.md ================================================ ================================================ FILE: 浏览器历史管理.md ================================================