├── .gitignore ├── assets ├── images │ └── logo.png └── template │ └── index.ejs ├── src ├── ddzy │ ├── utility │ │ ├── date │ │ │ ├── now │ │ │ │ └── index.ts │ │ │ └── README.md │ │ ├── others │ │ │ ├── isNaN │ │ │ │ └── index.ts │ │ │ ├── isString │ │ │ │ └── index.ts │ │ │ ├── isBoolean │ │ │ │ └── index.ts │ │ │ ├── isNumber │ │ │ │ └── index.ts │ │ │ ├── isSymbol │ │ │ │ └── index.ts │ │ │ ├── isUndefined │ │ │ │ └── index.ts │ │ │ ├── isNull │ │ │ │ └── index.ts │ │ │ ├── invariant │ │ │ │ └── index.ts │ │ │ ├── convertHumpToHyphen │ │ │ │ └── index.ts │ │ │ ├── gt │ │ │ │ └── index.ts │ │ │ ├── isBasicValue │ │ │ │ └── index.ts │ │ │ ├── binarySearch │ │ │ │ └── index.ts │ │ │ ├── eq │ │ │ │ └── index.ts │ │ │ ├── deepClone │ │ │ │ └── index.ts │ │ │ ├── listToTree │ │ │ │ └── index.ts │ │ │ ├── _instanceOf │ │ │ │ └── index.ts │ │ │ ├── convertURLParameterToObject │ │ │ │ └── index.ts │ │ │ ├── sizse │ │ │ │ └── index.ts │ │ │ └── sameValueZero │ │ │ │ └── index.ts │ │ ├── number │ │ │ ├── getRadian │ │ │ │ └── index.ts │ │ │ ├── getAnyRandom │ │ │ │ └── index.ts │ │ │ ├── getFullRandom │ │ │ │ └── index.ts │ │ │ ├── inRange │ │ │ │ └── index.ts │ │ │ ├── divideByThousand │ │ │ │ └── index.ts │ │ │ └── README.md │ │ ├── dom │ │ │ ├── getEle │ │ │ │ └── index.ts │ │ │ ├── getAllEle │ │ │ │ └── index.ts │ │ │ ├── getAttr │ │ │ │ └── index.ts │ │ │ ├── isDOM │ │ │ │ └── index.ts │ │ │ ├── addClass │ │ │ │ └── index.ts │ │ │ ├── removeClass │ │ │ │ └── index.ts │ │ │ ├── setAttr │ │ │ │ └── index.ts │ │ │ ├── setCss │ │ │ │ └── index.ts │ │ │ ├── convertPairToCSSText │ │ │ │ └── index.ts │ │ │ ├── throttle │ │ │ │ └── index.ts │ │ │ ├── debounce │ │ │ │ └── index.ts │ │ │ ├── _querySelector │ │ │ │ └── index.ts │ │ │ ├── traversalDOMWithBFS │ │ │ │ └── index.ts │ │ │ ├── traversalDOMWithTreeWalker │ │ │ │ └── index.ts │ │ │ ├── traversalDOMWithNodeIterator │ │ │ │ └── index.ts │ │ │ ├── traversalDOMWithDFS │ │ │ │ └── index.ts │ │ │ └── README.md │ │ ├── function │ │ │ ├── isFunction │ │ │ │ └── index.ts │ │ │ ├── pipe │ │ │ │ └── index.ts │ │ │ ├── compose │ │ │ │ └── index.ts │ │ │ ├── delay │ │ │ │ └── index.ts │ │ │ ├── _bind │ │ │ │ └── index.ts │ │ │ ├── _new │ │ │ │ └── index.ts │ │ │ ├── curry │ │ │ │ └── index.ts │ │ │ ├── _call │ │ │ │ └── index.ts │ │ │ ├── getParamNames │ │ │ │ └── index.ts │ │ │ ├── after │ │ │ │ └── index.ts │ │ │ └── README.md │ │ ├── object │ │ │ ├── isPlainObject │ │ │ │ └── index.ts │ │ │ ├── create │ │ │ │ └── index.ts │ │ │ ├── forIn │ │ │ │ └── index.ts │ │ │ ├── forOwn │ │ │ │ └── index.ts │ │ │ ├── get │ │ │ │ └── index.ts │ │ │ └── README.md │ │ ├── array │ │ │ ├── head │ │ │ │ └── index.ts │ │ │ ├── compact │ │ │ │ └── index.ts │ │ │ ├── dropRight │ │ │ │ └── index.ts │ │ │ ├── take │ │ │ │ └── index.ts │ │ │ ├── trunk │ │ │ │ └── index.ts │ │ │ ├── _join │ │ │ │ └── index.ts │ │ │ ├── pullAll │ │ │ │ └── index.ts │ │ │ ├── toStrictArray │ │ │ │ └── index.ts │ │ │ ├── difference │ │ │ │ └── index.ts │ │ │ ├── drop │ │ │ │ └── index.ts │ │ │ ├── castArray │ │ │ │ └── index.ts │ │ │ ├── toFlatArrayOutPlace │ │ │ │ └── index.ts │ │ │ ├── isStrictArray │ │ │ │ └── index.ts │ │ │ ├── fill │ │ │ │ └── index.ts │ │ │ ├── _concat │ │ │ │ └── index.ts │ │ │ ├── zip │ │ │ │ └── index.ts │ │ │ └── dropRightWhile │ │ │ │ └── index.ts │ │ ├── string │ │ │ ├── words │ │ │ │ └── index.ts │ │ │ ├── lowerCase │ │ │ │ └── index.ts │ │ │ ├── capitalize │ │ │ │ └── index.ts │ │ │ ├── trim │ │ │ │ └── index.ts │ │ │ ├── endsWith │ │ │ │ └── index.ts │ │ │ ├── getRandomStr │ │ │ │ └── index.ts │ │ │ └── README.md │ │ ├── algorithm │ │ │ ├── sort │ │ │ │ ├── insert-sort │ │ │ │ │ └── index.ts │ │ │ │ ├── bubble-sort │ │ │ │ │ └── index.ts │ │ │ │ ├── select-sort │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shell-sort │ │ │ │ │ └── index.ts │ │ │ │ └── quick-sort │ │ │ │ │ └── index.ts │ │ │ ├── double-linked-circular-list │ │ │ │ └── list-node │ │ │ │ │ └── index.ts │ │ │ ├── es6-achieve │ │ │ │ ├── startsWith │ │ │ │ │ └── index.ts │ │ │ │ ├── filter │ │ │ │ │ └── index.ts │ │ │ │ ├── some │ │ │ │ │ └── index.ts │ │ │ │ ├── find │ │ │ │ │ └── index.ts │ │ │ │ ├── findIndex │ │ │ │ │ └── index.ts │ │ │ │ ├── const │ │ │ │ │ └── index.ts │ │ │ │ ├── map │ │ │ │ │ └── index.ts │ │ │ │ ├── every │ │ │ │ │ └── index.ts │ │ │ │ ├── reduce │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── includes │ │ │ │ │ └── index.ts │ │ │ │ └── promise │ │ │ │ │ └── index.ts │ │ │ ├── binary-search-tree │ │ │ │ └── tree-node │ │ │ │ │ └── index.ts │ │ │ ├── mini-redux │ │ │ │ └── index.ts │ │ │ ├── README.md │ │ │ └── event-emitter │ │ │ │ └── index.ts │ │ └── README.md │ ├── business │ │ ├── upload │ │ │ └── index.ts │ │ ├── README.md │ │ ├── draggable │ │ │ └── index.ts │ │ └── carousel │ │ │ └── index.ts │ ├── canvas │ │ ├── README.md │ │ ├── stars-line │ │ │ ├── line │ │ │ │ └── Line.ts │ │ │ └── ball │ │ │ │ └── Ball.ts │ │ ├── jumping-characters │ │ │ └── character │ │ │ │ └── Character.ts │ │ └── colorful-bubble │ │ │ └── bubble │ │ │ └── bubble.ts │ ├── __tests__ │ │ ├── date │ │ │ └── now.test.ts │ │ ├── function │ │ │ ├── isFunction.test.ts │ │ │ ├── curry.test.ts │ │ │ ├── getParamNames.test.ts │ │ │ ├── delay.test.ts │ │ │ ├── _call.test.ts │ │ │ ├── _bind.test.ts │ │ │ ├── pipe.test.ts │ │ │ ├── compose.test.ts │ │ │ ├── after.test.ts │ │ │ └── _new.test.ts │ │ ├── dom │ │ │ ├── setAttr.test.ts │ │ │ ├── addClass.test.ts │ │ │ ├── convertPairToCSSText.test.ts │ │ │ ├── removeClass.test.ts │ │ │ ├── setCss.test.ts │ │ │ ├── getAttr.test.ts │ │ │ ├── isDOM.test.ts │ │ │ ├── getEle.test.ts │ │ │ ├── getAllEle.test.ts │ │ │ ├── traversalDOMWithBFS.test.ts │ │ │ ├── traversalDOMWithDFS.test.ts │ │ │ └── _querySelector.test.ts │ │ ├── other │ │ │ ├── invariant.test.ts │ │ │ ├── binarySearch.test.ts │ │ │ ├── isNull.test.ts │ │ │ ├── isBasicValue.test.ts │ │ │ ├── deepClone.test.ts │ │ │ ├── gt.test.ts │ │ │ ├── convertURLParameterToObject.test.ts │ │ │ ├── eq.test.ts │ │ │ ├── _instanceOf.test.ts │ │ │ ├── size.test.ts │ │ │ ├── sameValueZero.test.ts │ │ │ └── listToTree.test.ts │ │ ├── array │ │ │ ├── head.test.ts │ │ │ ├── _concat.test.ts │ │ │ ├── isStrictArray.test.ts │ │ │ ├── toFlatArrayOutPlace.test.ts │ │ │ ├── dropRight.test.ts │ │ │ ├── toStrictArray.test.ts │ │ │ ├── compact.test.ts │ │ │ ├── take.test.ts │ │ │ ├── fill.test.ts │ │ │ ├── _join.test.ts │ │ │ ├── drop.test.ts │ │ │ ├── castArray.test.ts │ │ │ ├── zip.test.ts │ │ │ ├── dropRightWhile.test.ts │ │ │ ├── difference.test.ts │ │ │ ├── trunk.test.ts │ │ │ └── pullAll.test.ts │ │ ├── number │ │ │ ├── getRadian.test.ts │ │ │ ├── getFullRandom.test.ts │ │ │ ├── inRange.test.ts │ │ │ ├── getAnyRandom.test.ts │ │ │ └── divideByThousand.test.ts │ │ ├── string │ │ │ ├── lowerCase.test.ts │ │ │ ├── endsWith.test.ts │ │ │ ├── capitalize.test.ts │ │ │ ├── words.test.ts │ │ │ ├── trim.test.ts │ │ │ └── getRandomStr.test.ts │ │ ├── object │ │ │ ├── isPlainObject.test.ts │ │ │ ├── forOwn.test.ts │ │ │ ├── get.test.ts │ │ │ ├── forIn.test.ts │ │ │ └── create.test.ts │ │ └── algorithm │ │ │ ├── miniRedux.test.ts │ │ │ ├── eventEmitter.test.ts │ │ │ └── binarySearchTree.test.ts │ └── README.md ├── qiqf │ ├── utility │ │ ├── array │ │ │ ├── IArrayInterface.ts │ │ │ └── index.ts │ │ └── test │ │ │ └── test-array.ts │ ├── CHANGELOG.md │ └── README.md └── index.ts ├── jest.config.ts ├── .babelrc ├── tsconfig.json ├── LICENSE ├── webpack.config.ts ├── tslint.json ├── package.json ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Basic 2 | node_modules 3 | package-lock.json 4 | 5 | # Extend 6 | coverage -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddzy/ts-utility-plugins/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /src/ddzy/utility/date/now/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取1970至今的毫秒数 3 | */ 4 | export function now() { 5 | return Date.now(); 6 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/isNaN/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断给定的值是否为 NaN 3 | * @param a 任意值 4 | */ 5 | export default function isNaN(a: any): boolean { 6 | return a !== a; 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/isString/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断给定的值是否为字符串 3 | * @param a 任意值 4 | */ 5 | export default function isString(a: any) { 6 | return typeof a === 'string'; 7 | } -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | testRegex: "(/ddzy\/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 3 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 4 | }; -------------------------------------------------------------------------------- /src/ddzy/utility/others/isBoolean/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断给定的值是否布尔值 3 | * @param a 任意值 4 | */ 5 | export default function isBoolean(a: any): boolean { 6 | return typeof a === 'boolean'; 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/isNumber/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断给定的值是否为数字 3 | * @param a 任意值 4 | */ 5 | export default function isNumber(a: any): boolean { 6 | return typeof a === 'number'; 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/number/getRadian/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 角度转弧度 3 | * @param angle 角度值 4 | */ 5 | export function getRadian(angle: number): number { 6 | return (Math.PI / 180) * angle; 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/isSymbol/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断给定的值是否为 Symbol 类型 3 | * @param a 任意值 4 | */ 5 | export default function isSymbol(a: any): boolean { 6 | return typeof a === 'symbol'; 7 | } -------------------------------------------------------------------------------- /src/qiqf/utility/array/IArrayInterface.ts: -------------------------------------------------------------------------------- 1 | export type IArraySize = T[]; 2 | export type IArrayAny = any[]; 3 | export interface IEveryNth { 4 | (args: number[], nth: number): number[] 5 | } 6 | -------------------------------------------------------------------------------- /src/ddzy/utility/dom/getEle/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取指定单个DOM元素 3 | * @param sign 任意选择器 4 | */ 5 | export function getEle(sign: string): HTMLElement | null { 6 | return document.querySelector(sign); 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/isUndefined/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否undefined 3 | * @param origin 任意值 4 | */ 5 | export function isUndefined(origin: any): boolean { 6 | return typeof origin === 'undefined'; 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/isFunction/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否函数(Function || Symbol) 3 | * @param ele 任意值 4 | */ 5 | export function isFunction(ele: any): boolean { 6 | return typeof ele === 'function'; 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/getAllEle/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取指定的所有DOM元素 3 | * @param sign 任意选择器 4 | */ 5 | export function getAllEle(sign: string): ArrayLike | null { 6 | return document.querySelectorAll(sign); 7 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/getAttr/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取DOM的特定属性值 3 | * @param ele 目标DOM 4 | * @param key 属性名 5 | */ 6 | export function getAttr(ele: HTMLElement, key: string): string | null { 7 | return ele.getAttribute(key); 8 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/isDOM/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 检查是否DOM元素 3 | * @param node 指定目标 4 | */ 5 | export function isDOM(node: any): boolean { 6 | return node 7 | && typeof node === 'object' 8 | && node.nodeType === 1; 9 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/addClass/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 指定DOM添加单个类名 3 | * @param el 目标DOM 4 | * @param className 类名 5 | */ 6 | export function addClass(el: HTMLElement, className: string) { 7 | el && el.classList.add(className); 8 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/removeClass/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 移出指定DOM元素的单个类名 3 | * @param el 目标DOM 4 | * @param className 类名 5 | */ 6 | export function removeClass(el: HTMLElement, className: string) { 7 | el && el.classList.remove(className); 8 | } -------------------------------------------------------------------------------- /src/ddzy/utility/number/getAnyRandom/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取指定范围内的随机任意数 3 | * @param min 小边界 4 | * @param max 大边界 5 | */ 6 | export function getAnyRandom(min: number, max: number): number { 7 | return Math.random() * (max - min) + min; 8 | } -------------------------------------------------------------------------------- /src/ddzy/utility/number/getFullRandom/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取指定范围内的随机整数 3 | * @param min 小边界 4 | * @param max 大边界 5 | */ 6 | export function getFullRandom(min: number, max: number): number { 7 | return ~~(Math.random() * (max - min) + min); 8 | } -------------------------------------------------------------------------------- /src/ddzy/utility/object/isPlainObject/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否普通的对象 3 | * @param origin 目标值 4 | */ 5 | export function isPlainObject(origin: any): boolean { 6 | return origin && ( 7 | ({}).toString.call(origin) === '[object Object]' 8 | ); 9 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/pipe/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 从左往右依次执行处理器函数 3 | * @param callbacks 处理器 4 | */ 5 | export function pipe(...callbacks: Function[]): any { 6 | return callbacks.reduce((total, current) => { 7 | return current(total); 8 | }); 9 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/compose/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 从右往左执行处理器函数 3 | * @param callbacks 处理器 4 | */ 5 | export function compose(...callbacks: Function[]): any { 6 | return callbacks.reduceRight((total, current) => { 7 | return current(total); 8 | }); 9 | } -------------------------------------------------------------------------------- /src/ddzy/business/upload/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Upload 3 | * @description 文件上传组件, 目前计划只实现`拖拽上传` 4 | */ 5 | 6 | import { 7 | DraggerUpload as Dragger, 8 | } from './dragger-upload/DraggerUpload'; 9 | 10 | export const Upload = { 11 | Dragger, 12 | }; -------------------------------------------------------------------------------- /src/ddzy/canvas/README.md: -------------------------------------------------------------------------------- 1 | # canvas 2 | 3 | 特效插件集锦, 项目主文档参考[这里](../README.md) 4 | 5 | ## 说明 6 | 7 | `2019/5/28`, 避免文档过于繁重, 故将`canvas`相关的API搬运至`Gitbook`. 8 | 9 | **Gitbook在线文档**: [https://ddzy.gitbook.io/ts-utility-plugins-docs/](https://ddzy.gitbook.io/ts-utility-plugins-docs/) -------------------------------------------------------------------------------- /src/ddzy/utility/others/isNull/index.ts: -------------------------------------------------------------------------------- 1 | import { isUndefined } from "../isUndefined"; 2 | 3 | /** 4 | * 判断是否为null 5 | * @param origin 任意值 6 | */ 7 | export function isNull(origin: any): boolean { 8 | return !(isUndefined(origin)) 9 | && origin == undefined; 10 | } -------------------------------------------------------------------------------- /src/ddzy/business/README.md: -------------------------------------------------------------------------------- 1 | # business 2 | 3 | 业务插件集锦, 项目主文档参考[这里](../README.md) 4 | 5 | ## 说明 6 | 7 | `2019/5/28`, 避免文档过于繁重, 故将`business`相关的API搬运至`Gitbook`. 8 | 9 | **Gitbook在线文档**: [https://ddzy.gitbook.io/ts-utility-plugins-docs/](https://ddzy.gitbook.io/ts-utility-plugins-docs/) -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-typescript", 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties", 8 | "@babel/plugin-proposal-object-rest-spread", 9 | "@babel/plugin-transform-runtime" 10 | ], 11 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/head/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取指定数组的第一个元素 3 | * @param origin 源数组 4 | * @example 5 | * // 'duan' 6 | * head(['duan', 'zhao', 'yang']); 7 | * // undefined 8 | * head([]); 9 | */ 10 | export function head( 11 | origin: I[] 12 | ): I { 13 | return origin[0]; 14 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/setAttr/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 设置单个DOM属性 3 | * @param ele 目标DOM 4 | * @param options 属性键值对 5 | */ 6 | export function setAttr(ele: HTMLElement, options: { 7 | [key: string]: any 8 | }) { 9 | for (const key in options) { 10 | ele.setAttribute(key, options[key]); 11 | } 12 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/invariant/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义异常处理 3 | * @param condition 判断条件 4 | * @param message 错误信息 5 | */ 6 | export function invariant(condition: boolean, message: string) { 7 | if (condition) { 8 | throw new TypeError( 9 | `Ddzy's plugin error: ${message}` 10 | ); 11 | } 12 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/convertHumpToHyphen/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 驼峰字符串转连字符 3 | * @param hump 驼峰形式字符串 4 | */ 5 | export function convertHumpToHyphen(hump: string): string { 6 | const reg: RegExp = /[A-Z]+/; 7 | 8 | return hump.replace(reg, (matched) => { 9 | return `-${matched.toLowerCase()}`; 10 | }); 11 | } -------------------------------------------------------------------------------- /src/ddzy/business/draggable/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Draggable 3 | * @description { 4 | * 1. 三种拖拽类型(拖放排序、穿梭框、范围拖拽) 5 | * } 6 | * @author ddzy 7 | * @since 19/5/11 8 | */ 9 | import { 10 | SortDraggable as Sort, 11 | } from './sort-draggable/SortDraggable'; 12 | 13 | 14 | export const Draggable = { 15 | Sort, 16 | }; -------------------------------------------------------------------------------- /src/ddzy/utility/array/compact/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 过滤指定数组中的假值(0、''、false、null、undefined、NaN) 3 | * @param origin 源数组 4 | * @returns 新数组 5 | */ 6 | export function compact(origin: I[]) { 7 | const result: I[] = []; 8 | 9 | origin.forEach((v) => { 10 | v && (result.push(v)); 11 | }); 12 | 13 | return result; 14 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/gt/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 检查x是否大于y 3 | * @param x 比数 4 | * @param y 被比数 5 | * @example 6 | * // true 7 | * gt(10, 5); 8 | * // false 9 | * gt(10, 10); 10 | * // false, 11 | * gt(10, 20); 12 | */ 13 | export function gt( 14 | x: number, 15 | y: number, 16 | ): boolean { 17 | return x > y; 18 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/dropRight/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 从源数组的右侧开始, 舍弃指定个数的值 3 | * @param origin 源数组 4 | * @param gap 需要丢弃的个数 5 | * @example 6 | * // [1, 2, 3] 7 | * dropRight([1, 2, 3, 4, 5], 2); 8 | */ 9 | export function dropRight( 10 | origin: I[], 11 | gap = 1, 12 | ): I[] { 13 | return origin.slice(0, origin.length - gap); 14 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/delay/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 延迟wait毫秒后执行处理器callback 3 | * @param callback 处理器 4 | * @param wait 等待延时 5 | * @param args 传递的参数 6 | */ 7 | export function delay( 8 | callback: (...args: any[]) => void, 9 | wait: number, 10 | ...args: any[] 11 | ): number { 12 | return window.setTimeout(callback, wait, ...args); 13 | } -------------------------------------------------------------------------------- /src/ddzy/utility/string/words/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 按照指定模式, 拆分字符串 string 中的词为数组 3 | * @param text 任意字符 4 | * @param pattern 匹配模式 5 | */ 6 | export function words( 7 | text: string, 8 | pattern?: RegExp 9 | ): string[] { 10 | const defaultPattern = pattern || /\s+/g; 11 | 12 | return text 13 | .replace(defaultPattern, '|') 14 | .split('|'); 15 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/date/now.test.ts: -------------------------------------------------------------------------------- 1 | import { now } from "../../utility/date/now"; 2 | 3 | describe('now', () => { 4 | test('now should return a timestamp like `Date.now()` and `new Date().getTime()`', () => { 5 | const expected = { 6 | type: 'number', 7 | }; 8 | 9 | const result = now(); 10 | 11 | expect(typeof result).toBe(expected.type); 12 | }) 13 | }) -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/sort/insert-sort/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 插入排序 3 | * @param arr 源数组 4 | */ 5 | export default function insertSort(arr: number[]): number[] { 6 | for (let i = 1; i < arr.length; i++) { 7 | for (let j = i; j >= 0; j--) { 8 | if (arr[j] < arr[j - 1]) { 9 | [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]; 10 | } 11 | } 12 | } 13 | 14 | return arr; 15 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/setCss/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 设置单个DOM样式 3 | * @param ele 目标DOM 4 | * @param options 样式键值对 5 | */ 6 | export function setCss(ele: HTMLElement, options: Record) { 7 | for (const key in options) { 8 | if (options.hasOwnProperty(key)) { 9 | const element = options[key]; 10 | ele.style.cssText += `${key}: ${element};`; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/sort/bubble-sort/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 冒泡排序 3 | * @param arr 源数组 4 | */ 5 | export default function bubbleSort(arr: number[]): number[] { 6 | for (let i = 0; i < arr.length - 1; i++) { 7 | for (let j = i + 1; j < arr.length; j++) { 8 | if (arr[j] < arr[i]) { 9 | [arr[i], arr[j]] = [arr[j], arr[i]]; 10 | } 11 | } 12 | } 13 | 14 | return arr; 15 | } -------------------------------------------------------------------------------- /src/ddzy/utility/number/inRange/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 检查指定值是否在 start 与 end 之间, 但是不包括 end 3 | * @param value 需要检验的值 4 | * @param start 起始位置 5 | * @param end 终止位置 6 | */ 7 | export function inRange( 8 | value: number, 9 | start: number, 10 | end: number, 11 | ) { 12 | start = start > end ? end : start; 13 | end = start > end ? start : end; 14 | 15 | return value >= start && value < end; 16 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/isBasicValue/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否基础类型的值(null、undefined、number...) 3 | * @param origin 任意值 4 | */ 5 | export function isBasicValue(origin: any): boolean { 6 | return typeof origin === 'string' 7 | || typeof origin === 'number' 8 | || typeof origin === 'undefined' 9 | || typeof origin === 'symbol' 10 | || typeof origin === 'boolean' 11 | || origin == undefined 12 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/take/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建一个数组切片,从 arr 数组的起始元素开始提取 amount 个元素 3 | * @param arr 原数组 4 | * @param amount 切片的元素个数 5 | * [1, 2, 3, 4, 5, 6, 7] 6 | */ 7 | export default function take(arr: I[], amount: number): I[] { 8 | const result = []; 9 | let count = 0; 10 | 11 | do { 12 | arr[count] && amount > 0 && result.push(arr[count]); 13 | } while ((count++ < amount - 1)); 14 | 15 | return result; 16 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/isFunction.test.ts: -------------------------------------------------------------------------------- 1 | import { isFunction } from "../../utility/function/isFunction"; 2 | 3 | describe('isFunction', () => { 4 | test('isFunction should return `true` when received a function object', () => { 5 | const origin = [ 6 | function () { }, 7 | Symbol, 8 | ]; 9 | 10 | for (const v of origin) { 11 | expect(isFunction(v)).toBeTruthy(); 12 | } 13 | }); 14 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/function/_bind/index.ts: -------------------------------------------------------------------------------- 1 | import { _call } from "../_call"; 2 | 3 | /** 4 | * 模拟实现`bind`方法 5 | * @param context this上下文 6 | * @returns {(...args: any[]) => void} 7 | */ 8 | export function _bind(context: any): (...args: any[]) => any { 9 | const that = this; 10 | Function.prototype['_call' as 'prototype'] = _call; 11 | 12 | return function (args) { 13 | that['_call'](context, ...args); 14 | } 15 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/trunk/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 将源数组根据指定大小分片 3 | * @param origin 源数组 4 | * @param size 分片大小 5 | */ 6 | export function trunk(origin: I[], size = 1) { 7 | const result: I[][] = []; 8 | let count = 0; 9 | 10 | if (!origin.length) { 11 | return result; 12 | } 13 | 14 | do { 15 | result.push(origin.slice(count, count + size)); 16 | } while ((count += size) < origin.length); 17 | 18 | return result; 19 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/_join/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 将数组中的所有元素转换为由 separator 分隔的字符串。 3 | * @param origin 源数组 4 | * @param separator 分隔符 5 | * @returns 字符串 6 | */ 7 | export function _join( 8 | origin: I[], 9 | separator = '', 10 | ): string { 11 | let result = ''; 12 | 13 | for (let i = 0; i < origin.length; i++) { 14 | result += `${origin[i]}${i === origin.length - 1 ? '' : separator}`; 15 | } 16 | 17 | return result; 18 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/sort/select-sort/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 选择排序 3 | * @param arr 源数组 4 | */ 5 | export default function selectSort(arr: number[]): number[] { 6 | for (let i = 0; i < arr.length - 1; i++) { 7 | // 最小值的索引 8 | let sign = i; 9 | 10 | for (let j = i + 1; j < arr.length; j++) { 11 | if (arr[j] < arr[sign]) { 12 | sign = j; 13 | } 14 | } 15 | [arr[i], arr[sign]] = [arr[sign], arr[i]]; 16 | } 17 | 18 | return arr; 19 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/pullAll/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 移除数组 arr 中所有和给定值相等的元素, 原地操作 3 | * @param arr 源数组 4 | * @param selector 需要剔除的元素数组 5 | */ 6 | export function pullAll( 7 | arr: I[], 8 | selector: I[], 9 | ): typeof arr { 10 | selector.forEach((v) => { 11 | let position = arr.indexOf(v); 12 | 13 | while (position !== -1) { 14 | arr.splice(position, 1); 15 | position = arr.indexOf(v); 16 | } 17 | }); 18 | 19 | return arr; 20 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/sort/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name sort 3 | * @description 实现常见的排序算法 4 | * @author ddzy 5 | * @since 2020/10/19 6 | */ 7 | import bubbleSort from './bubble-sort/index'; 8 | import insertSort from './insert-sort'; 9 | import quickSort from './quick-sort'; 10 | import selectSort from './select-sort/index'; 11 | import shellSort from './shell-sort'; 12 | 13 | export const Sort = { 14 | bubbleSort, 15 | selectSort, 16 | insertSort, 17 | shellSort, 18 | quickSort, 19 | }; -------------------------------------------------------------------------------- /src/ddzy/utility/dom/convertPairToCSSText/index.ts: -------------------------------------------------------------------------------- 1 | import { convertHumpToHyphen } from "../../others/convertHumpToHyphen"; 2 | 3 | /** 4 | * 样式组转化为内联样式(style.cssText) 5 | * @param pair CSS样式键值对 6 | */ 7 | export function convertPairToCSSText(pair: Partial): string { 8 | let text = ''; 9 | 10 | for (const key in pair) { 11 | const value = Reflect.get(pair, key); 12 | text += `${convertHumpToHyphen(key)}: ${value}; `; 13 | } 14 | 15 | return text; 16 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/toStrictArray/index.ts: -------------------------------------------------------------------------------- 1 | import { isBasicValue } from "../../others/isBasicValue"; 2 | 3 | /** 4 | * 将给定的类数组转化为严格的数组(非原地) 5 | * @param origin 需要转化的类数组对象 6 | */ 7 | export function toStrictArray(origin: ArrayLike): T[] { 8 | const result: T[] = []; 9 | 10 | if (isBasicValue(origin)) { 11 | return result; 12 | } else { 13 | for (let i = 0, every; every = origin[i++];) { 14 | result.push(every); 15 | } 16 | } 17 | 18 | return result; 19 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "module": "commonjs", 7 | "noUnusedParameters": true, 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "outDir": "lib/", 11 | "lib": [ 12 | "dom", 13 | "es2016.array.include", 14 | "es2015", 15 | "es2016" 16 | ], 17 | "sourceMap": true, 18 | "allowJs": true, 19 | "noEmit": true 20 | } 21 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/setAttr.test.ts: -------------------------------------------------------------------------------- 1 | import { setAttr } from "../../utility/dom/setAttr"; 2 | 3 | describe('setAttr', () => { 4 | test('setAttr should receive a DOM object and attributes, add attributes to DOM, and return nothing', () => { 5 | document.body.innerHTML = ` 6 | 7 | `; 8 | 9 | expect(setAttr( 10 | document.getElementById('text') as HTMLElement, 11 | { 12 | class: 'text', 13 | type: 'text', 14 | } 15 | )); 16 | }); 17 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/addClass.test.ts: -------------------------------------------------------------------------------- 1 | import { addClass } from "../../utility/dom/addClass"; 2 | 3 | describe('addClass', () => { 4 | test('addClass should add the class to a DOM object', () => { 5 | document.body.innerHTML = ` 6 |
7 | `; 8 | 9 | addClass( 10 | (document.getElementById('app') as HTMLElement), 11 | 'c2', 12 | ); 13 | 14 | expect((document.getElementById('app') as HTMLElement).classList.contains('c2')).toBeTruthy(); 15 | }); 16 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/invariant.test.ts: -------------------------------------------------------------------------------- 1 | import { invariant } from "../../utility/others/invariant"; 2 | 3 | describe('invariant', () => { 4 | 5 | // ! Test successed 6 | // test('invariant should throw error when the condition to be true', () => { 7 | // expect(utilityOthers.invariant(true, 'test error')).toThrowErrorMatchingSnapshot(); 8 | // }); 9 | 10 | test('invariant should not do anything when the condition to be false', () => { 11 | expect(invariant(false, 'test error')).toBeFalsy(); 12 | }); 13 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/sort/shell-sort/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 希尔排序(缩小增量) 3 | * @param arr 源数组 4 | */ 5 | export default function shellSort(arr: number[]): number[] { 6 | let sign = ~~(arr.length / 2); 7 | 8 | while (sign >= 1) { 9 | for (let i = 0; i < arr.length; i++) { 10 | for (let j = i + sign; j >= 0; j -= sign) { 11 | if (arr[j] < arr[j - 1]) { 12 | [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]; 13 | } 14 | } 15 | } 16 | 17 | sign = ~~(sign / 2); 18 | } 19 | 20 | return arr; 21 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/difference/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建一个具有唯一array值的数组,每个值不包含在其他给定的数组中 3 | * @param origin 源数组 4 | * @param args 任意数量的参数 5 | */ 6 | export function difference( 7 | origin: I[], 8 | ...args: any[] 9 | ): I[] { 10 | const result: I[] = []; 11 | const cache = new Map(); 12 | 13 | args.forEach((v) => { 14 | cache.set(v, 1); 15 | }); 16 | 17 | origin.forEach((v) => { 18 | if (!cache.has(v)) { 19 | result.push(v); 20 | } 21 | }); 22 | 23 | return result; 24 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/throttle/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 节流函数 3 | * @param timestamp 时间戳 4 | * @param callback 执行回调 5 | */ 6 | export function throttle(timestamp: number, callback: (...args: any[]) => void) { 7 | let lastClickTime = Date.now(); 8 | 9 | return (...args: any[]) => { 10 | const currentClickTime = Date.now(); 11 | 12 | if (currentClickTime - lastClickTime > timestamp) { 13 | callback.apply, any[], any>(globalThis, args); 14 | lastClickTime = currentClickTime; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/qiqf/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ### 2019-07-02 4 | 5 | - 加入项目 6 | - 新增数组 array 文件夹, 收集一些常用的关于数组的工具函数 7 | - 添加 `arrayMax()`、`arrayMin()` 函数 8 | 9 | 10 | ### 2019-07-04 11 | - 新增 CHANGELOG.md 变更记录、README.md 描述文档 12 | - 添加 `arrayChunk()`、`compact()` 工具函数 13 | 14 | ### 2019-07-07 15 | - 新增数组 `countOccurrences`、`deepFlatten`、`difference`、`sameArray`、`distinctValuesOfArray`等五个工具方法 16 | 17 | ### 2019-07-09 18 | - 新增数组 `dropElements`、`everyNth`、`filterNonUnique`三个工具方法 19 | 20 | ### 2019-07-16 21 | - 新增数组 `flatten`、`flattenDepth`方法 22 | -------------------------------------------------------------------------------- /src/ddzy/utility/string/lowerCase/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 转换字符串string以空格分开单词, 并转换为小写 3 | * @param text 任意字符串 4 | * @example 5 | * // duan zhao 6 | * lowerCase('--Duan-Zhao--'); 7 | * // alioeduan 8 | * lowerCase('alioeDuan'); 9 | * // duan zhao yang 10 | * lowerCase('__DUAN_ZHAO_YANG__'); 11 | */ 12 | export function lowerCase( 13 | text: string, 14 | ): string { 15 | const regSpecialCharacter = /[\W_]+/g; 16 | 17 | return text 18 | .toLocaleLowerCase() 19 | .replace(regSpecialCharacter, ' ') 20 | .trim(); 21 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/double-linked-circular-list/list-node/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 链表节点 3 | */ 4 | export interface IListNodeProps { 5 | value: V, 6 | next: ListNode | null, 7 | prev: ListNode | null, 8 | }; 9 | 10 | export class ListNode { 11 | public value: V; 12 | public next: ListNode | null; 13 | public prev: ListNode | null; 14 | 15 | public constructor(props: IListNodeProps) { 16 | this.value = props.value; 17 | this.next = props.next; 18 | this.prev = props.prev; 19 | } 20 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/curry.test.ts: -------------------------------------------------------------------------------- 1 | import { curry } from "../../utility/function/curry"; 2 | 3 | describe('curry() tests...', () => { 4 | test('method curry should works normally', () => { 5 | const received = function (a: number, b: number, c: number, d: number) { 6 | return a + b + c + d; 7 | } 8 | 9 | const curriedFunc = curry(received); 10 | 11 | expect(curriedFunc(1, 2, 3)(4)).toBe(10); 12 | expect(curriedFunc(100)(200)(300)(400)).toBe(1000); 13 | expect(curriedFunc(23)(34, 45)(56)).toBe(158); 14 | }); 15 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/array/drop/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建一个切片数组,去除array前面的n个元素。(n默认值为1。) 3 | * @param origin 源数组 4 | * @param count 丢弃的个数 5 | * @example 6 | * // [4, 5, 6] 7 | * drop([1, 2, 3, 4, 5, 6], 3); 8 | */ 9 | export function drop( 10 | origin: I[], 11 | count = 1, 12 | ): I[] { 13 | const result: I[] = []; 14 | 15 | if (!origin.length) { 16 | return result; 17 | } 18 | 19 | let sign = count; 20 | do { 21 | result.push(origin[sign]); 22 | } while ((sign++ < origin.length - 1)); 23 | 24 | return result; 25 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/startsWith/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _find 3 | * @description 模拟ES6的`array.find()` 4 | * @author ddzy 5 | * @license MIT 6 | * @since 2019-7-17 7 | */ 8 | 9 | 10 | /** 11 | * 12 | * @param origin 源字符串 13 | * @param target 需要比对的字符 14 | * @param startIndex 起始位置 15 | */ 16 | export function _startsWith( 17 | origin: string, 18 | target: string, 19 | startIndex: number = 0, 20 | ): boolean { 21 | const subStr = origin.substring(startIndex, target.length); 22 | 23 | return subStr === target; 24 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/convertPairToCSSText.test.ts: -------------------------------------------------------------------------------- 1 | import { convertPairToCSSText } from "../../utility/dom/convertPairToCSSText"; 2 | 3 | describe('convertPairToCSSText', () => { 4 | test('convertPairToCSSText should extract CSS pair object into truthy cssText.', () => { 5 | const origin: Partial = { 6 | border: '1px dotted red', 7 | backgroundColor: 'blue', 8 | }; 9 | const expected = `border: 1px dotted red; background-color: blue; `; 10 | 11 | expect(convertPairToCSSText(origin)).toBe(expected); 12 | }); 13 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/dom/debounce/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 防抖函数 3 | * @param callback 处理器 4 | * @param options 配置项 5 | */ 6 | export function debounce( 7 | callback: (...args: any[]) => void, 8 | options: { 9 | timestamp?: number, 10 | } 11 | ): (...args: any[]) => void { 12 | let timer: any = null; 13 | let { 14 | timestamp = 500, 15 | } = options; 16 | 17 | return (...args: any[]) => { 18 | clearTimeout(timer); 19 | 20 | timer = setTimeout(() => { 21 | callback.apply(this as any, args); 22 | }, timestamp); 23 | } 24 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/removeClass.test.ts: -------------------------------------------------------------------------------- 1 | import { removeClass } from "../../utility/dom/removeClass"; 2 | 3 | describe('removeClass', () => { 4 | test('removeClass should remove the class from a DOM object', () => { 5 | document.body.innerHTML = ` 6 |
7 | `; 8 | 9 | removeClass( 10 | (document.getElementById('app') as HTMLElement), 11 | 'c2', 12 | ); 13 | 14 | expect( 15 | (document.getElementById('app') as HTMLElement).classList.contains('c2') 16 | ).toBeFalsy(); 17 | }); 18 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/object/create/index.ts: -------------------------------------------------------------------------------- 1 | export interface IStaticObject { 2 | [key: string]: any; 3 | }; 4 | 5 | /** 6 | * 模拟实现 `Object.create()` 7 | * @param prototype 需要继承的原型 8 | * @param props 可枚举的属性 9 | */ 10 | export default function create(prototype: any, props: IStaticObject) { 11 | const result: IStaticObject = {}; 12 | 13 | Object.setPrototypeOf(result, prototype); 14 | for (const key in props) { 15 | if (Object.prototype.hasOwnProperty.call(props, key)) { 16 | const value = props[key]; 17 | result[key] = value; 18 | } 19 | } 20 | 21 | return result; 22 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/binarySearch/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 二分查找 3 | * @param arr 源列表 4 | * @param value 查找的值 5 | * @returns 找到则返回索引, 否则返回 -1 6 | */ 7 | export default function binarySearch(arr: number[], value: number): number { 8 | let left = 0; 9 | let right = arr.length - 1; 10 | 11 | while (left <= right) { 12 | let middle = ~~((left + right) / 2); 13 | 14 | if (arr[middle] === value) { 15 | return middle; 16 | } else if (arr[middle] < value) { 17 | left = middle + 1; 18 | } else { 19 | right = middle - 1; 20 | } 21 | } 22 | 23 | return -1; 24 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/castArray/index.ts: -------------------------------------------------------------------------------- 1 | import { isStrictArray } from "../isStrictArray"; 2 | 3 | /** 4 | * 将给定的值强制转成数组 5 | * @description 如果origin本来就是数组, 则原封不动返回 6 | * @param origin 任意值 7 | * @example 8 | * // [1] 9 | * castArray(1); 10 | * @example 11 | * // [{ a: 1 }] 12 | * castArray({ a: 1 }); 13 | * @example 14 | * // arr 15 | * const arr = []; 16 | * castArray(arr); 17 | */ 18 | export function castArray( 19 | origin: I, 20 | ): I[] | I { 21 | // 严格的数组, 直接返回 22 | if (isStrictArray(origin)) { 23 | return origin; 24 | } 25 | return [origin]; 26 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/_new/index.ts: -------------------------------------------------------------------------------- 1 | import { _reflect } from "../../algorithm/es6-achieve/reflect"; 2 | import { isFunction } from "../isFunction/index"; 3 | 4 | /** 5 | * 模拟实现`new`操作符 6 | * @param constructor 构造函数 7 | * @param args 所需参数 8 | */ 9 | export function _new(constructor: T, ...args: any[]): keyof T { 10 | if (!isFunction(constructor)) { 11 | return null; 12 | } 13 | 14 | const instance = Object.create(null); 15 | _reflect.setPrototypeOf(instance, constructor.prototype); 16 | constructor.apply(instance, args); 17 | 18 | return instance; 19 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/toFlatArrayOutPlace/index.ts: -------------------------------------------------------------------------------- 1 | import { isStrictArray } from "../isStrictArray"; 2 | 3 | /** 4 | * 数组扁平化(非原地算法) 5 | * @param origin 源数组 6 | * @returns {*} 处理后的源数组 7 | */ 8 | export function toFlatArrayOutPlace(origin: any[]) { 9 | const target: any[] = []; 10 | 11 | function _aidedToFlat(origin: any[], target: any[]) { 12 | for (const value of origin) { 13 | isStrictArray(value) 14 | ? (_aidedToFlat(value, target)) 15 | : (target.push(value)); 16 | } 17 | 18 | return target; 19 | } 20 | 21 | return _aidedToFlat(origin, target); 22 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/eq/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 比较两个值是否全等, 包括NaN 3 | * @description 与`Object.is()`具有相同的作用 4 | * @param x 第一个数值 5 | * @param y 第二个数值 6 | * @see http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero 7 | * @example 8 | * // true 9 | * eq(100, 100); 10 | * // false 11 | * eq({}, {}); 12 | */ 13 | export function eq( 14 | x: any, 15 | y: any, 16 | ): boolean { 17 | const typeX = typeof x; 18 | const typeY = typeof y; 19 | 20 | return (x === y) || ( 21 | typeX === 'number' 22 | && x !== x 23 | && typeY === 'number' 24 | && y !== y 25 | ); 26 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/sort/quick-sort/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 快速排序(递归) 3 | * @param arr 待排序数组 4 | */ 5 | export default function quickSort(arr: number[]): number[] { 6 | if (arr.length <= 1) { 7 | return arr; 8 | } 9 | 10 | // 保存比基准值小的数 11 | const left: number[] = []; 12 | // 保存比基准值大的数 13 | const right: number[] = []; 14 | const middleVal = (arr.splice(~~(arr.length / 2), 1))[0]; 15 | 16 | for (let i = 0; i < arr.length; i++) { 17 | arr[i] < middleVal 18 | ? left.push(arr[i]) 19 | : right.push(arr[i]); 20 | } 21 | 22 | return quickSort(left).concat([middleVal], quickSort(right)); 23 | } -------------------------------------------------------------------------------- /src/ddzy/utility/object/forIn/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 使用 iteratee 遍历对象的自身和继承的可枚举属性. iteratee 会传入3个参数: (value, key, object). 如果返回 false, iteratee 会提前退出遍历. 3 | * @param origin 源对象 4 | * @param callback 处理器 5 | */ 6 | export function forIn( 7 | origin: I, 8 | callback: ( 9 | v: any, 10 | i: string | number, 11 | self: typeof origin, 12 | ) => void | boolean, 13 | ) { 14 | for (const key in origin) { 15 | const value = origin[key]; 16 | const isKeep = callback.call(origin, value, key, origin); 17 | 18 | if (isKeep === false) { 19 | break; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/setCss.test.ts: -------------------------------------------------------------------------------- 1 | import { setCss } from "../../utility/dom/setCss"; 2 | 3 | describe('setCss', () => { 4 | test('setCss should receive a DOM object and CSS Rule options, add rules to DOM.', () => { 5 | document.body.innerHTML = ` 6 |
app
7 | `; 8 | 9 | setCss( 10 | document.getElementById('app') as HTMLElement, 11 | { 12 | color: 'red', 13 | 'font-size': 25, 14 | }, 15 | ); 16 | 17 | expect((document.getElementById('app') as HTMLElement).getAttribute('style')).toBe('color: red; font-size: 25;'); 18 | 19 | }); 20 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/filter/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _filter 3 | * @description 模拟实现ES6的array.filter 4 | * @author ddzy 5 | * @license MIT 6 | * @since 2019-7-11 7 | */ 8 | 9 | export function _filter( 10 | arr: I[], 11 | callback: (v: I, i: number, self: typeof arr) => boolean, 12 | thisArg?: T, 13 | ): I[] { 14 | const result: I[] = []; 15 | let count = 0; 16 | 17 | do { 18 | const isPassing = callback.call(thisArg, arr[count], count, arr); 19 | 20 | isPassing && (result.push(arr[count])); 21 | } while ((count++ < arr.length - 1)); 22 | 23 | return result; 24 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/curry/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 函数柯里化 3 | * @description 柯里化可以将一个复杂的函数解耦, 使其逻辑分离, 便于复用, 适用于函数式编程 4 | * @param fn 原函数 5 | * @param outerArgs 初始参数列表 6 | * @example 7 | * // 未柯里化的函数 8 | * curry(1, 2, 3, 4); 9 | * // 柯里化的函数 10 | * curry(1)(2)(3)(4); 11 | * curry(1)(2, 3)(4); 12 | * curry(1, 2, 3)(4); 13 | */ 14 | export function curry(this: any, fn: Function, ...outerArgs: any[]) { 15 | if (outerArgs.length >= fn.length) { 16 | return fn.apply(this, outerArgs); 17 | } 18 | 19 | return function (...innerArgs: any[]) { 20 | return curry(fn, ...outerArgs.concat(innerArgs)); 21 | } 22 | } -------------------------------------------------------------------------------- /src/ddzy/README.md: -------------------------------------------------------------------------------- 1 | # ts-utility-plugins/ddzy 2 | 3 | 一些有用的插件库集合 4 | 5 | ## 说明 6 | 7 | 插件库包括 `canvas特效`插件, `业务`插件、`工具组件`等等... 8 | 9 | 部分插件已投入使用: 10 | 11 | - [canvas-colorful-bubble](https://blog.yyge.top/) 12 | - [canvas-jumping-characters](https://blog.yyge.top/) 13 | 14 | `2019/5/28`, 已将所有的API说明, 搬运至`Gitbook`在线站点, 很舒服~ 15 | 16 | **Gitbook地址**: [https://ddzy.gitbook.io/ts-utility-plugins-docs/](https://ddzy.gitbook.io/ts-utility-plugins-docs/) 17 | 18 | ## 用法 19 | 20 | > **PS**: 重构代码中, 暂未实现对`npm引入`的支持 21 | 22 | 详细用法, 参考[这里](https://github.com/ddzy/ts-utility-plugins#usage) 23 | 24 | ## 其它 25 | 26 | 一般两到三日一更💢 27 | 28 | **Enjoy!** -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/head.test.ts: -------------------------------------------------------------------------------- 1 | import { head } from "../../utility/array/head"; 2 | 3 | describe('head tests: ', () => { 4 | test('head should return the first field that was in origin array', () => { 5 | const received: number[] = [1, 2, 3, 4, 5]; 6 | const expected = 1; 7 | 8 | const result = head(received); 9 | 10 | expect(result).toBe(expected); 11 | }); 12 | 13 | test('head should return undefined when receive an empty array', () => { 14 | const received: any[] = []; 15 | const expected = undefined; 16 | 17 | const result = head(received); 18 | 19 | expect(result).toBe(expected); 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/dom/_querySelector/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 简单的模拟querySelector 3 | * @param selector 选择器 4 | */ 5 | export function _querySelector(selector: string): Element | null { 6 | const plainMatcher: RegExp = /^(?:#([a-zA-Z]+))|(?:\.(\w+))|([a-z]+)$/; 7 | const matched = selector.match(plainMatcher); 8 | 9 | // ? 处理三种基本类型 10 | if (matched) { 11 | if (matched[1]) { 12 | return document.getElementById(matched[1]); 13 | } else if (matched[2]) { 14 | return document.getElementsByClassName(matched[2])[0] || null; 15 | } else { 16 | return document.getElementsByTagName(matched[3])[0] || null; 17 | } 18 | } 19 | 20 | return null; 21 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/some/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _find 3 | * @description 模拟ES6的`array.some()` 4 | * @author ddzy 5 | * @license MIT 6 | * @since 2019-7-25 7 | */ 8 | 9 | export function _some( 10 | arr: I[], 11 | callback: (v: I, i: number, self: typeof arr) => boolean, 12 | context?: T, 13 | ): boolean { 14 | let result = false; 15 | let count = 0; 16 | 17 | if (!arr.length) { 18 | return result; 19 | } 20 | 21 | do { 22 | result = callback.call(context, arr[count], count, arr); 23 | 24 | if (result) { 25 | break; 26 | } 27 | } while ((count++ < arr.length - 1)); 28 | 29 | return result; 30 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/isStrictArray/index.ts: -------------------------------------------------------------------------------- 1 | import { isBasicValue } from "../../others/isBasicValue"; 2 | 3 | /** 4 | * 判断是否严格的数组 5 | * @param origin 目标值 6 | */ 7 | export function isStrictArray(origin: any): boolean { 8 | if (Array.isArray(origin)) { 9 | return Array.isArray(origin); 10 | } else { 11 | // basic value 12 | if (isBasicValue(origin)) { 13 | return false; 14 | } else if (typeof origin === 'object') { 15 | if (origin.length) { 16 | // array like 17 | return Object.toString.call(() => origin) === '[object Array]'; 18 | } else { 19 | return false; 20 | } 21 | } 22 | return false; 23 | } 24 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/binarySearch.test.ts: -------------------------------------------------------------------------------- 1 | import binarySearch from "../../utility/others/binarySearch"; 2 | 3 | describe('binarySearch tests...', () => { 4 | test('The method named `binarySearch() should return a correct index value', () => { 5 | const arr = [10, 867, 4, 34, 9, 98, 45, 67]; 6 | const received = [ 7 | { 8 | arr, 9 | value: 34, 10 | }, 11 | { 12 | arr, 13 | value: 99, 14 | }, 15 | ]; 16 | const expected = [3, -1]; 17 | 18 | for (const [key, value] of received.entries()) { 19 | const result = binarySearch(value.arr, value.value); 20 | 21 | expect(result).toBe(expected[key]); 22 | } 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/isNull.test.ts: -------------------------------------------------------------------------------- 1 | import { isNull } from "util"; 2 | 3 | 4 | describe('isNull', () => { 5 | test('isNull should return `false` if received a non null value', () => { 6 | const received = [ 7 | 0, 8 | '', 9 | undefined, 10 | {}, 11 | [], 12 | function () { }, 13 | ]; 14 | 15 | for (const v of received) { 16 | expect(isNull(v)).toBeFalsy(); 17 | } 18 | }); 19 | 20 | test('isNull should return `true` if received a null value', () => { 21 | const received = [ 22 | null, 23 | ]; 24 | 25 | for (const v of received) { 26 | expect(isNull(v)).toBeTruthy(); 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/binary-search-tree/tree-node/index.ts: -------------------------------------------------------------------------------- 1 | export interface ITreeNodeProps { 2 | value: number; 3 | left?: TreeNode | null; 4 | right?: TreeNode | null; 5 | parent?: TreeNode | null; 6 | }; 7 | 8 | 9 | export class TreeNode { 10 | public constructor( 11 | props: ITreeNodeProps, 12 | ) { 13 | this.value = props.value; 14 | this.left = props.left ? props.left : null; 15 | this.right = props.right ? props.right : null; 16 | this.parent = props.parent ? props.parent : null; 17 | } 18 | 19 | public readonly value: number; 20 | public left: TreeNode | null; 21 | public right: TreeNode | null; 22 | public parent: TreeNode | null; 23 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/_call/index.ts: -------------------------------------------------------------------------------- 1 | import { isNull } from "../../others/isNull"; 2 | 3 | /** 4 | * 模拟实现`call`方法 5 | * @param context this上下文 6 | * @param args 所需参数 7 | */ 8 | export function _call(context: any, ...args: any[]) { 9 | // TODO: 使用`keyof typeof Function`来解决索引签名报错(`元素隐式具有 "any" 类型,因为类型“Function”没有索引签名`)的问题 10 | const funcName = this['name' as keyof typeof Function] as any; 11 | 12 | if (isNull(context)) { 13 | let w = window as any; 14 | 15 | w[funcName] = this; 16 | w[funcName](...args); 17 | delete w[funcName]; 18 | } else { 19 | context[funcName] = this; 20 | context[funcName](...args); 21 | delete context[funcName]; 22 | } 23 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/getParamNames/index.ts: -------------------------------------------------------------------------------- 1 | import { isFunction } from "util"; 2 | 3 | /** 4 | * 获取函数的形参名称数组 5 | * @param origin 目标函数 6 | * @returns {string[]} 函数参数名称数组 7 | */ 8 | export function getParamNames(origin: Function): string[] { 9 | if (!isFunction(origin)) { 10 | return []; 11 | } 12 | 13 | let final: string[] = []; 14 | const originToStr: string = origin.toString(); 15 | const matchParamsReg: RegExp = /(?:(?<=\()(.+)(?=\)))/; 16 | const matchedResult = originToStr.match(matchParamsReg); 17 | 18 | if (matchedResult) { 19 | let matchedParams = matchedResult[1]; 20 | final = matchedParams.split(/[,\s]+/g); 21 | } 22 | 23 | return final; 24 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/deepClone/index.ts: -------------------------------------------------------------------------------- 1 | import { isStrictArray } from "../../array/isStrictArray"; 2 | import { isBasicValue } from "../isBasicValue"; 3 | 4 | /** 5 | * 深拷贝 6 | * @param origin 需要拷贝的目标对象 7 | */ 8 | export default function deepClone(origin: any[] | object) { 9 | const target = isStrictArray(origin) 10 | ? [] 11 | : {} as { [key: string]: any }; 12 | 13 | for (const [key, value] of Object.entries(origin)) { 14 | // 普通对象或数组 15 | if (typeof value === 'object') { 16 | target[key] = deepClone(value); 17 | } 18 | // 基本值 19 | else if (isBasicValue(value)) { 20 | target[key] = value; 21 | } 22 | } 23 | 24 | return target; 25 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/traversalDOMWithBFS/index.ts: -------------------------------------------------------------------------------- 1 | import { isDOM } from "../isDOM"; 2 | 3 | /** 4 | * BFS遍历指定DOM节点 5 | * @param container 遍历的DOM容器 6 | * @param callback 回调 7 | */ 8 | export function traversalDOMWithBFS( 9 | container: HTMLElement, 10 | callback: (node: HTMLElement) => void, 11 | ) { 12 | if (!isDOM(container)) { 13 | throw new TypeError('Require a DOM element'); 14 | } 15 | 16 | const queue: Element[] = [container]; 17 | 18 | while (queue.length) { 19 | const node = (queue.shift() as HTMLElement); 20 | callback && callback(node); 21 | 22 | const children = node.children as ArrayLike; 23 | queue.push(...(Array.from(children))); 24 | } 25 | } -------------------------------------------------------------------------------- /src/ddzy/utility/number/divideByThousand/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 将数字按千位分隔 3 | * @param origin 源数字 4 | * @param separator 分隔符 5 | * @example 6 | * // 110 7 | * divideByThousand(110); 8 | * @example 9 | * // 11,000 10 | * divideByThousand(11000) 11 | */ 12 | export function divideByThousand( 13 | origin: number, 14 | separator?: string, 15 | ) { 16 | const regContinuousInterger = /(\d{3})(?=\d+)/g; 17 | const sNewSeparator = separator || ','; 18 | let sNewOrigin = ('' + origin).split('').reverse().join(''); 19 | 20 | sNewOrigin = sNewOrigin.replace(regContinuousInterger, (_, $1) => { 21 | return `${$1}${sNewSeparator}`; 22 | }); 23 | 24 | return sNewOrigin.split('').reverse().join(''); 25 | } -------------------------------------------------------------------------------- /src/ddzy/utility/object/forOwn/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name forOwn 3 | * @description 遍历对象自身的可枚举属性, 不包括继承而来的属性 4 | * @author ddzy 5 | * @since 2019/12/12 6 | * @param origin 需要遍历的源对象 7 | * @param callback 回调处理器 8 | */ 9 | export function forOwn( 10 | origin: O, 11 | callback: ( 12 | i: string | number, 13 | v: any, 14 | self: typeof origin, 15 | ) => void | boolean, 16 | ) { 17 | for (const key in origin) { 18 | if (origin.hasOwnProperty(key)) { 19 | const value = origin[key]; 20 | const isContinue = callback.call(origin, key, value, origin); 21 | 22 | // 如果处理器中返回 false, 则终止遍历 23 | if (isContinue === false) { 24 | break; 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/ddzy/utility/string/capitalize/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 将指定的字符串的首字母大写, 剩下为小写 3 | * @param text 任意字符串 4 | * @example 5 | * // Duan 6 | * capitalize('duan'); 7 | * @example 8 | * // Zhao 9 | * capitalize('ZHAO'); 10 | * @example 11 | * // Yang 12 | * capitalize('yANG'); 13 | * @example 14 | * // _College 15 | * capitalize('_college'); 16 | */ 17 | export function capitalize( 18 | text: string, 19 | ): string { 20 | if (!text) { 21 | return text; 22 | } 23 | 24 | const regLowerCaseCharacter = /[a-z]{1}/; 25 | 26 | text = text 27 | .toLocaleLowerCase() 28 | .replace(regLowerCaseCharacter, ($1) => { 29 | return $1.toLocaleUpperCase(); 30 | }); 31 | 32 | return text; 33 | } -------------------------------------------------------------------------------- /src/ddzy/utility/string/trim/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 去除字符串首尾的指定字符, 默认为空格(whitespace) 3 | * @param origin 任意字符 4 | * @param character 需要去除的字符 5 | * @example 6 | * // ddzy 7 | * const processed = trim(' ddzy '); 8 | * @example 9 | * // duanzhao 10 | * const processed = trim('duanzhaoyang', 'yang'); 11 | */ 12 | export function trim( 13 | origin: string, 14 | character = '', 15 | ): string { 16 | let result = ''; 17 | 18 | if (character === '') { 19 | const reg = /(^\s+)|(\s+$)/g; 20 | 21 | result = origin.replace(reg, ''); 22 | } else { 23 | const reg = new RegExp(`(^${character}+)|(${character}+$)`, 'g'); 24 | 25 | result = origin.replace(reg, ''); 26 | } 27 | 28 | return result; 29 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/traversalDOMWithTreeWalker/index.ts: -------------------------------------------------------------------------------- 1 | import { isDOM } from "../isDOM"; 2 | 3 | /** 4 | * TreeWalker遍历指定DOM节点 5 | * @param container 遍历的DOM容器 6 | * @param callback 执行回调 7 | */ 8 | export function traversalDOMWithTreeWalker( 9 | container: HTMLElement, 10 | callback: (node: HTMLElement) => void, 11 | ) { 12 | if (!isDOM(container)) { 13 | throw new TypeError('Require a DOM element'); 14 | } 15 | 16 | const treeWalker: TreeWalker = document.createTreeWalker( 17 | container, 18 | NodeFilter.SHOW_ELEMENT, 19 | ); 20 | let currentNode: Node | null = null; 21 | 22 | while ((currentNode = treeWalker.nextNode())) { 23 | callback && callback((currentNode as HTMLElement)); 24 | } 25 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/listToTree/index.ts: -------------------------------------------------------------------------------- 1 | export interface IList { 2 | id: number, 3 | value: any, 4 | parent: number, 5 | children?: IList[], 6 | }; 7 | 8 | /** 9 | * 将列表数据转化为树结构 10 | * @param list 需要转换的列表数据 11 | */ 12 | export default function listToTree(list: IList[]): IList[] { 13 | list = list.slice(); 14 | 15 | list.forEach((outerV) => { 16 | if (outerV.parent !== 0) { 17 | list.forEach((innerV) => { 18 | if (innerV.id === outerV.parent) { 19 | if (!innerV.children) { 20 | innerV.children = []; 21 | } 22 | innerV.children.push(outerV); 23 | } 24 | }); 25 | } 26 | }); 27 | 28 | return list.filter((v) => v.parent === 0); 29 | } -------------------------------------------------------------------------------- /src/ddzy/utility/date/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility/array 2 | 3 | ## 说明 4 | 5 | 汇集有关`Date`的工具方法. 6 | 7 | ## 目录 8 | 9 | | Name | Description | Source | Docs | 10 | | ---------- | ---------------------- | ----------------------------- | --------------------------------------------------------------------------------------- | 11 | | now | 获取`1970`至今的毫秒数 | [源码](./now/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-date/now) | 12 | | dateFormat | 格式化时间戳 | [源码](./dateFormat/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-date/dateformat) | 13 | -------------------------------------------------------------------------------- /src/ddzy/utility/dom/traversalDOMWithNodeIterator/index.ts: -------------------------------------------------------------------------------- 1 | import { isDOM } from "../isDOM"; 2 | 3 | /** 4 | * NodeIterator遍历指定DOM节点 5 | * @param container 遍历的DOM容器 6 | * @param callback 执行回调 7 | */ 8 | export function traversalDOMWithNodeIterator( 9 | container: HTMLElement, 10 | callback: (node: HTMLElement) => void, 11 | ) { 12 | if (!isDOM(container)) { 13 | throw new TypeError('Require a DOM element'); 14 | } 15 | 16 | const nodeIterator: NodeIterator = document.createNodeIterator( 17 | container, 18 | NodeFilter.SHOW_ELEMENT, 19 | ); 20 | let currentNode: Node | null = null; 21 | 22 | while ((currentNode = nodeIterator.nextNode())) { 23 | callback && callback(currentNode as HTMLElement); 24 | } 25 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/fill/index.ts: -------------------------------------------------------------------------------- 1 | import { isUndefined } from "../../others/isUndefined"; 2 | 3 | 4 | /** 5 | * 使用 value 值来填充(替换) array. 从 start 位置开始, 到 end 位置结束(但不包含 end 位置). 6 | * @param origin 源数组 7 | * @param value 需要填充的值 8 | * @param start 填充的起始位置 9 | * @param end 填充的终止位置 10 | */ 11 | export function fill( 12 | origin: I[], 13 | value: I, 14 | start?: number, 15 | end?: number, 16 | ): typeof origin { 17 | start = isUndefined(start) ? 0 : start as number; 18 | end = isUndefined(end) ? origin.length : end as number; 19 | 20 | start = start > end ? end : start; 21 | end = start > end ? start : end; 22 | 23 | for (let i = start; i < end; i++) { 24 | origin[i] = value; 25 | } 26 | 27 | return origin; 28 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/_concat/index.ts: -------------------------------------------------------------------------------- 1 | import { isStrictArray } from "../isStrictArray"; 2 | 3 | /** 4 | * 将给定的任意数量的值追加至源数组 5 | * @description Array.concat的Polyfill 6 | * @param origin 源数组 7 | * @param args 需要被连接的值 8 | * @returns 新数组 9 | */ 10 | export function _concat( 11 | origin: I[], 12 | ...args: I[] 13 | ): I[] { 14 | const result: I[] = origin.slice(0); 15 | 16 | let count = 0; 17 | do { 18 | if (isStrictArray(args[count])) { 19 | const arr = args[count] as unknown as any[]; 20 | 21 | arr.forEach((v) => { 22 | result.push(v); 23 | }); 24 | 25 | continue; 26 | } 27 | 28 | result.push(args[count]); 29 | } while ((count++ < args.length - 1)); 30 | 31 | return result; 32 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/getAttr.test.ts: -------------------------------------------------------------------------------- 1 | import { getAttr } from "../../utility/dom/getAttr"; 2 | 3 | describe('getAttr', () => { 4 | test('getAttr should return null when nothing matched ', () => { 5 | document.body.innerHTML += ` 6 | 7 | `; 8 | 9 | expect( 10 | getAttr((document.getElementById('text') as HTMLElement), 'disabled') 11 | ).toBeNull(); 12 | }); 13 | 14 | test('getAttr should return the value when matched', () => { 15 | document.body.innerHTML = ` 16 | 17 | `; 18 | 19 | expect(getAttr( 20 | (document.getElementById('text') as HTMLElement), 21 | 'type', 22 | )).toBe('text'); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/number/getRadian.test.ts: -------------------------------------------------------------------------------- 1 | import { getRadian } from "../../utility/number/getRadian"; 2 | 3 | describe('getRadian', () => { 4 | test('getRadian should receive an angle and convert it to radian', () => { 5 | const origin = [ 6 | 0, 7 | 78, 8 | 90, 9 | 180, 10 | 360 11 | ]; 12 | const expected = [ 13 | 0, 14 | 1.361356816555577, 15 | 1.5707963267948966, 16 | 3.141592653589793, 17 | 6.283185307179586, 18 | ]; 19 | const result: number[] = []; 20 | 21 | for (const v of origin) { 22 | result.push(getRadian(v)); 23 | } 24 | 25 | for (const [i, v] of result.entries()) { 26 | expect(v === expected[i]).toBeTruthy(); 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/others/_instanceOf/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 模拟实现instanceOf 3 | * @description 表达式右侧的值是否存在于左侧的原型链上 4 | * @param leftValue 任意值 5 | * @param rightValue 任意值 6 | * @example 7 | * // true 8 | * _instanceOf([], Array); 9 | * // false 10 | * _instanceOf({}, null); 11 | */ 12 | export function _instanceOf( 13 | leftValue: any, 14 | rightValue: any, 15 | ): boolean { 16 | let result = false; 17 | let oLeftValueProto: any = leftValue; 18 | 19 | try { 20 | while ((oLeftValueProto = oLeftValueProto.__proto__)) { 21 | if (oLeftValueProto === rightValue.prototype) { 22 | result = true; 23 | 24 | break; 25 | } 26 | } 27 | } catch (error) { 28 | result = false; 29 | } finally { 30 | return result; 31 | } 32 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/convertURLParameterToObject/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 提取URL中的GET请求参数 3 | * @param url 需要解析的url 4 | */ 5 | export function convertURLParameterToObject(url: string): Record { 6 | const matchURLReg: RegExp = /(?:(?<=[^\/])(\?.+))/; 7 | const matchParameterReg: RegExp = /[\?\&]{1}(?:(\w+)=(\w+))/g; 8 | const final: Record = {}; 9 | 10 | const matchedCompleteURL = url.match(matchURLReg); 11 | 12 | if (matchedCompleteURL) { 13 | const matchedPairs = matchedCompleteURL[1].match(matchParameterReg); 14 | if (matchedPairs) { 15 | matchedPairs.forEach((v) => { 16 | const [key, value] = v.split('='); 17 | 18 | final[key.substr(1)] = value; 19 | }); 20 | } 21 | } 22 | 23 | return final; 24 | } -------------------------------------------------------------------------------- /src/qiqf/README.md: -------------------------------------------------------------------------------- 1 | # ts-utility-plugins/qiqingfu 2 | 3 | 一些常用的插件库集合 4 | 5 | ## 前言: 6 | 关于目录`qiqf`目录下的 `array`中的部分工具函数参考[30-seconds-of-code](https://github.com/30-seconds/30-seconds-of-code), 主要是练习ts。 7 | 8 | > 感谢 `ddzy` 项目作者的创建此项目, 提供一个很好的练习ts的平台 9 | 10 | 11 | ## Array 12 | 13 | - arrayMax(): 返回数组中最大的数字 14 | - arrayMin(): 返回数组中最小的数字 15 | - arrayChunk(): 将数组划分指定大小的数组 16 | - compact(): 返回剔除 `null`、`' '`、`0`、`false`、`undefined`的数组 17 | - countOccurrences(): 统计数组中元素出现的次数 18 | - deepFlatten(): 扁平化数组 19 | - difference(): 两个数组不同值组成的新数组 20 | - sameArray(): 两个数组相同值组成的新数组 21 | - distinctValuesOfArray(): 数组去重 22 | - dropElements(): 返回第一个符合条件元素的后面所有元素组成的集合 23 | - everyNth(): 返回数组中每个第n个元素 24 | - filterNonUnique(): 过滤出数组中只出现一次的元素组成的集合 25 | - flatten(): 扁平化一层数组 26 | - flattenDepth(): 指定扁平化数组的层数 27 | -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/isDOM.test.ts: -------------------------------------------------------------------------------- 1 | import { isDOM } from "../../utility/dom/isDOM"; 2 | 3 | describe('isDOM', () => { 4 | test('isDOM should return `false` if received a non DOM object', () => { 5 | const origin = [ 6 | 0, Number.MAX_SAFE_INTEGER, Number.MIN_VALUE, 7 | '', 'ddzy', 8 | {}, 9 | true, false, 10 | function () { }, 11 | [], 12 | ]; 13 | 14 | for (const v of origin) { 15 | expect(isDOM(v)).toBeFalsy(); 16 | } 17 | }); 18 | 19 | test('isDOM should return `true` if received a DOM object', () => { 20 | const origin = [ 21 | document.createElement('div'), 22 | document.body 23 | ]; 24 | 25 | for (const v of origin) { 26 | expect(isDOM(v)).toBeTruthy(); 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/string/lowerCase.test.ts: -------------------------------------------------------------------------------- 1 | import { lowerCase } from "../../utility/string/lowerCase"; 2 | 3 | describe('lowerCase tests...', () => { 4 | 5 | test('lowerCase should return `` when receive an empty string', () => { 6 | const received = ''; 7 | const expected = ''; 8 | 9 | const result = lowerCase(received); 10 | 11 | expect(result).toBe(expected); 12 | }); 13 | 14 | test('lowerCase should return the converted string correctly', () => { 15 | const received = ['--Duan-Zhao--', 'alioeDuan', '__DUAN_ZHAO_YANG__']; 16 | const expected = ['duan zhao', 'alioeduan', 'duan zhao yang']; 17 | 18 | received.forEach((v, i) => { 19 | const result = lowerCase(v); 20 | 21 | expect(result).toBe(expected[i]); 22 | }); 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/find/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _find 3 | * @description 模拟ES6的`array.find()` 4 | * @author ddzy 5 | * @license MIT 6 | * @since 2019-7-17 7 | */ 8 | 9 | 10 | /** 11 | * 模拟ES6的find() 12 | * @param arr 源数组 13 | * @param callback 迭代器 14 | * @param thisArg callback上下文 15 | */ 16 | export function _find( 17 | arr: I[], 18 | callback: (v: I, i: number, self: typeof arr) => boolean, 19 | context?: T, 20 | ): I | undefined { 21 | let result = undefined; 22 | let count = 0; 23 | 24 | do { 25 | const temp = callback.call(context, arr[count], count, arr); 26 | 27 | if (temp) { 28 | result = arr[count]; 29 | break; 30 | } 31 | } while ((count++ < arr.length - 1)); 32 | 33 | return result; 34 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/findIndex/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _findIndex 3 | * @description 模拟ES6的array.findIndex 4 | * @author ddzy 5 | * @since 2019-7-31 6 | * @license MIT 7 | */ 8 | 9 | 10 | /** 11 | * 模拟array.findIndex 12 | * @param arr 源数组 13 | * @param callback 处理器 14 | * @param context callback运行时的上下文 15 | */ 16 | export function _findIndex( 17 | arr: I[], 18 | callback: (v: I, i: number, self: typeof arr) => boolean, 19 | context?: T, 20 | ): number { 21 | let result = -1; 22 | let count = 0; 23 | 24 | do { 25 | const temp = callback.call(context, arr[count], count, arr); 26 | 27 | if (temp) { 28 | result = count; 29 | break; 30 | } 31 | } while ((count++ < arr.length - 1)); 32 | 33 | return result; 34 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/getEle.test.ts: -------------------------------------------------------------------------------- 1 | import { getEle } from "../../utility/dom/getEle"; 2 | 3 | describe('getEle', () => { 4 | test('getEle should return `null` when no matched', () => { 5 | document.body.innerHTML = ` 6 |
7 | `; 8 | 9 | const origin = [ 10 | 'p', '.text' 11 | ]; 12 | 13 | for (const v of origin) { 14 | expect(getEle(v)).toBe(null); 15 | } 16 | }); 17 | 18 | test('getEle should return the `DOM object` which has been matched', () => { 19 | document.body.innerHTML = ` 20 |
21 | `; 22 | 23 | const origin = ['#app']; 24 | 25 | for (const v of origin) { 26 | expect(getEle(v)).toBe( 27 | document.getElementById('app') 28 | ) 29 | } 30 | }); 31 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/getParamNames.test.ts: -------------------------------------------------------------------------------- 1 | import { getParamNames } from "../../utility/function/getParamNames"; 2 | 3 | describe('getParamNames', () => { 4 | test("getParamsNames should receive lots of parameters and return the array of each parameter's name.", () => { 5 | const received = [ 6 | function (..._args: any[]) { }, 7 | function func1(_name: string, _age: number) { }, 8 | function func2(_name: string, _age: number, _address: string, ..._args: any[]) { }, 9 | ]; 10 | const expected = [ 11 | [], 12 | ['_name', '_age'], 13 | ['_name', '_age', '_address'], 14 | ]; 15 | 16 | for (const [i, v] of received.entries()) { 17 | const current = getParamNames(v); 18 | expect(current).toEqual(expected[i]); 19 | } 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/const/index.ts: -------------------------------------------------------------------------------- 1 | import { invariant } from "../../../others/invariant"; 2 | 3 | /** 4 | * @name _const 5 | * @description const 6 | * @author ddzy 7 | * @since 2019/5/25 8 | */ 9 | 10 | export function _const( 11 | variable: string, 12 | value: any, 13 | ) { 14 | 15 | /** 16 | * 1. 不可变 17 | * 2. 不会挂载到window | global 18 | * 3. 不可重新赋值 19 | * 4. 块级作用域(`无法实现`) 20 | */ 21 | 22 | const origin = { 23 | [variable]: value, 24 | }; 25 | 26 | return Object.defineProperty(origin, variable, { 27 | set(newValue) { 28 | invariant( 29 | newValue !== value, 30 | `Assignment to constant variable.` 31 | ); 32 | }, 33 | get() { 34 | return value; 35 | }, 36 | enumerable: true, 37 | }); 38 | } -------------------------------------------------------------------------------- /src/ddzy/utility/dom/traversalDOMWithDFS/index.ts: -------------------------------------------------------------------------------- 1 | import { isDOM } from "../isDOM"; 2 | 3 | /** 4 | * DFS遍历指定DOM节点 5 | * @param container 遍历的DOM容器 6 | * @param callback 回调 7 | */ 8 | export function traversalDOMWithDFS( 9 | container: HTMLElement, 10 | callback: (node: HTMLElement) => void, 11 | ) { 12 | if (!isDOM(container)) { 13 | throw new TypeError('Require a DOM element'); 14 | } 15 | 16 | callback && callback(container); 17 | 18 | _aidedTraversal(container.children); 19 | 20 | function _aidedTraversal(children: HTMLCollection) { 21 | if (children.length === 0) { 22 | return; 23 | } 24 | 25 | for (let i = 0, every; every = children[i++];) { 26 | callback && callback(every as HTMLElement); 27 | _aidedTraversal(every.children); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/map/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _map 3 | * @description 模拟实现ES6的`array.map()`API 4 | * @author ddzy 5 | * @since 2019-7-5 6 | * @see https://github.com/ddzy 7 | */ 8 | 9 | 10 | /** 11 | * 模拟`array.map`()方法 12 | * @param arr 源数组 13 | * @param callback 回调函数 14 | * @param thisArg callback执行时的上下文 15 | */ 16 | export function _map( 17 | arr: I[], 18 | callback: (v: I, i: number, thisArg: T | null) => O, 19 | thisArg?: T, 20 | ) { 21 | const result: O[] = []; 22 | let count = 0; 23 | 24 | do { 25 | result[count] = callback.call( 26 | thisArg ? thisArg : null, 27 | arr[count], 28 | count, 29 | thisArg ? thisArg : null, 30 | ); 31 | } while ((count++ < arr.length - 1)); 32 | 33 | return result; 34 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/every/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _every 3 | * @description 模拟ES6的`array.every()` 4 | * @author ddzy 5 | * @license MIT 6 | * @since 2019-7-14 7 | */ 8 | 9 | /** 10 | * 模拟`array.every`方法 11 | * @param arr 源数组 12 | * @param callback 处理器 13 | * @param context this上下文 14 | */ 15 | export function _every( 16 | arr: I[], 17 | callback: (v: I, i: number, self: typeof arr) => boolean, 18 | context?: T, 19 | ): boolean { 20 | let result = true; 21 | let count = 0; 22 | 23 | if (!arr.length) { 24 | return result; 25 | } 26 | 27 | do { 28 | result = callback.call(context, arr[count], count, arr); 29 | 30 | if (!result) { 31 | result = false; 32 | break; 33 | } 34 | } while ((count++ < arr.length - 1)); 35 | 36 | return result; 37 | } -------------------------------------------------------------------------------- /src/ddzy/utility/array/zip/index.ts: -------------------------------------------------------------------------------- 1 | import { isUndefined } from "../../others/isUndefined"; 2 | 3 | /** 4 | * @name zip 5 | * @author ddzy 6 | * @since 2019/12/20 7 | * @description 创建一个分组元素的数组, 数组的第一个元素包含所有给定数组的第一个元素, 数组的第二个元素包含所有给定数组的第二个元素, 以此类推. 打包后的数组的长度等于源二维数组的最大长度的项 8 | * @param args 任意数量的数组 9 | */ 10 | export function zip(...args: I[][]): I[][] { 11 | const result: typeof args = []; 12 | 13 | // 寻找最大的元素长度 14 | let maxLength = 0; 15 | args.forEach((v) => { 16 | maxLength = Math.max(v.length, maxLength); 17 | }); 18 | 19 | let count = 0; 20 | let temp: I[] = []; 21 | while (count < maxLength) { 22 | args.forEach((v) => { 23 | !isUndefined(v[count]) && (temp.push(v[count])); 24 | }); 25 | 26 | count++; 27 | result.push(temp); 28 | temp = []; 29 | } 30 | 31 | return result; 32 | } -------------------------------------------------------------------------------- /src/ddzy/utility/others/sizse/index.ts: -------------------------------------------------------------------------------- 1 | import { isPlainObject } from "../../object/isPlainObject"; 2 | 3 | /** 4 | * 返回collection(集合)的长度, 如果集合是类数组或字符串, 返回其 length; 5 | * 如果集合是对象, 返回其可枚举属性的个数. 6 | * @param origin 任意值 7 | * @example 8 | * // 4 9 | * size('ddzy'); 10 | * @example 11 | * // 0 12 | * size([]); 13 | * @example 14 | * // 2 15 | * size({ name: 'ddzy', age: 21 }); 16 | */ 17 | export function size( 18 | origin: any 19 | ): number { 20 | let result = 0; 21 | 22 | try { 23 | if (origin.length) { 24 | result = origin.length; 25 | } else if (isPlainObject(origin)) { 26 | for (const key in origin) { 27 | if (origin.hasOwnProperty(key)) { 28 | result++; 29 | } 30 | } 31 | } 32 | } catch (error) { 33 | result = 0; 34 | } finally { 35 | return result; 36 | } 37 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/delay.test.ts: -------------------------------------------------------------------------------- 1 | import { delay } from "../../utility/function/delay"; 2 | 3 | describe('delay', () => { 4 | test('delay should fire the callback after wait ms', () => { 5 | const received = function () { 6 | expect(true).toBeTruthy(); 7 | } 8 | 9 | delay(received, 500); 10 | }); 11 | 12 | test('delay should receive params that passed', () => { 13 | const received = function (name: string, age: number) { 14 | expect(name).toBe('ddzy'); 15 | expect(age).toBe(21); 16 | } 17 | 18 | delay(received, 1000, 'ddzy', 21); 19 | }); 20 | 21 | test('delay should return the timeout id', () => { 22 | const received = function () { 23 | expect(true).toBeTruthy(); 24 | } 25 | 26 | const result = delay(received, 1500); 27 | 28 | expect(typeof result).toBe('number'); 29 | }); 30 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/number/getFullRandom.test.ts: -------------------------------------------------------------------------------- 1 | import { getFullRandom } from "../../utility/number/getFullRandom"; 2 | 3 | describe('getFullRandom', () => { 4 | test('getFullRandom should receive two numbers and return a interger between these two numbers', () => { 5 | const origin = [ 6 | { 7 | min: 0, 8 | max: 10, 9 | }, 10 | { 11 | min: Number.MIN_SAFE_INTEGER, 12 | max: Number.MAX_SAFE_INTEGER, 13 | }, 14 | { 15 | min: -1, 16 | max: 1, 17 | } 18 | ]; 19 | 20 | for (const v of origin) { 21 | const { min, max } = v; 22 | 23 | const result = getFullRandom( 24 | min, 25 | max, 26 | ); 27 | 28 | expect(Number.isInteger(result)).toBeTruthy(); 29 | expect(result >= min && result <= max).toBeTruthy(); 30 | } 31 | }); 32 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/_call.test.ts: -------------------------------------------------------------------------------- 1 | import { _call } from "../../utility/function/_call"; 2 | 3 | export type FunctionKeyType = keyof typeof Function; 4 | 5 | describe('_call', () => { 6 | test('_call should change the `this` when processing', () => { 7 | const origin = { 8 | name: 'ddzy', 9 | }; 10 | const expected = { 11 | name: 'ddzy', 12 | arg0: 0, 13 | arg1: 1, 14 | }; 15 | 16 | const received = { 17 | printName(...args: any[]) { 18 | const name = this['name' as keyof typeof received]; 19 | const arg0 = args[0]; 20 | const arg1 = args[1]; 21 | 22 | expect({ name, arg0, arg1 }).toEqual(expected); 23 | }, 24 | }; 25 | 26 | Function.prototype['_call' as FunctionKeyType] = _call; 27 | 28 | received.printName['_call' as FunctionKeyType](origin, 0, 1); 29 | }); 30 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/number/inRange.test.ts: -------------------------------------------------------------------------------- 1 | import { inRange } from "../../utility/number/inRange"; 2 | 3 | 4 | describe('inRange tests...', () => { 5 | test('method inRange should compute the correct result', () => { 6 | const received = [ 7 | { 8 | value: 0, 9 | start: 100, 10 | end: 200, 11 | }, 12 | { 13 | value: 45, 14 | start: -11, 15 | end: 98, 16 | }, 17 | { 18 | value: 34, 19 | start: 34, 20 | end: 34, 21 | }, 22 | { 23 | value: 0, 24 | start: -344, 25 | end: 0, 26 | }, 27 | ]; 28 | const expected = [false, true, false, false]; 29 | 30 | received.forEach((v, i) => { 31 | const result = inRange(v.value, v.start, v.end); 32 | 33 | expect(result).toBe(expected[i]); 34 | }); 35 | }); 36 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/number/getAnyRandom.test.ts: -------------------------------------------------------------------------------- 1 | import { getAnyRandom } from "../../utility/number/getAnyRandom"; 2 | 3 | 4 | describe('getAnyRandom', () => { 5 | test('getAnyRandom should receive two numbers and return a float number or a interger between these two numbers ', () => { 6 | const origin = [ 7 | { 8 | min: 0, 9 | max: 10, 10 | }, 11 | { 12 | min: Number.MIN_SAFE_INTEGER, 13 | max: Number.MAX_SAFE_INTEGER, 14 | }, 15 | { 16 | min: -1, 17 | max: 1, 18 | } 19 | ]; 20 | 21 | for (const v of origin) { 22 | const { min, max } = v; 23 | 24 | const result = getAnyRandom( 25 | min, 26 | max, 27 | ); 28 | 29 | expect(typeof result).toBe('number'); 30 | expect(result >= min && result <= max).toBeTruthy(); 31 | } 32 | }); 33 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/isBasicValue.test.ts: -------------------------------------------------------------------------------- 1 | import { isBasicValue } from "../../utility/others/isBasicValue"; 2 | 3 | describe('isBasicValue', () => { 4 | test('isBasicValue should return `true` if received a basic value', () => { 5 | const received = [ 6 | 0, 7 | Number.MAX_SAFE_INTEGER, 8 | '', 9 | 'ddzy', 10 | Symbol('ddzy'), 11 | null, 12 | undefined, 13 | ]; 14 | 15 | for (const v of received) { 16 | expect(isBasicValue(v)).toBeTruthy(); 17 | } 18 | }); 19 | 20 | test('isBasicValue should return `false` if received a non basic value', () => { 21 | const received = [ 22 | { name: 'duan', age: 20 }, 23 | document.body, 24 | [1, 2, 3], 25 | function () { }, 26 | ]; 27 | 28 | for (const v of received) { 29 | expect(isBasicValue(v)).toBeFalsy(); 30 | } 31 | }); 32 | }) -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/deepClone.test.ts: -------------------------------------------------------------------------------- 1 | import deepClone from "../../utility/others/deepClone"; 2 | 3 | describe('deepClone', () => { 4 | test('deepClone should copy the whole object and return', () => { 5 | const received = { 6 | a: 1, 7 | b: [1, 2, 3, 4], 8 | c: { 9 | d: 1, 10 | e: 2, 11 | f: [ 12 | { 13 | g: 1, 14 | h: 2, 15 | }, 16 | { 17 | i: 1, 18 | j: 2, 19 | }, 20 | ], 21 | }, 22 | }; 23 | 24 | const result = deepClone(received); 25 | 26 | expect(result === received).toBeFalsy(); 27 | 28 | result.b.push(5); 29 | result.c.d = 2; 30 | 31 | expect(result.b.length).toBe(5); 32 | expect(result.c.d).toBe(2); 33 | expect(received.b.length).toBe(4); 34 | expect(received.c.d).toBe(1); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/ddzy/utility/function/after/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建并返回一个函数, 等待函数运行指定次数后执行处理器 3 | * @param time 等待的次数 4 | * @param callback time次数耗尽之后执行的处理器 5 | * @example 6 | * // Work completed! 7 | * function doingWorkAsync({ type: string, sign: Function }) { 8 | * setTimeout(sign, 1000); 9 | * } 10 | * 11 | * const employees = ['jack', 'rose', 'riven']; 12 | * const fragment = after(employees.length, () => { 13 | * console.log('Work completed!'); 14 | * }); 15 | * 16 | * employees.forEach((employee) => { 17 | * doingWorkAsync({ type: 'washing', sign: fragment }); 18 | * }); 19 | */ 20 | export function after( 21 | time: number, 22 | callback: (...args: any[]) => void, 23 | ): (...args: any[]) => void { 24 | let count = 0; 25 | 26 | return (...args: any[]) => { 27 | if (count === time - 1) { 28 | return callback(...args); 29 | } 30 | 31 | count++; 32 | }; 33 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/_concat.test.ts: -------------------------------------------------------------------------------- 1 | import { _concat } from "../../utility/array/_concat"; 2 | 3 | describe('_concat', () => { 4 | test('_concat should return an new array being composed of number', () => { 5 | const received = [1, 2, 3, 4, 5]; 6 | const expected = [1, 2, 3, 4, 5, 6, 7, 8]; 7 | 8 | const result = _concat(received, 6, 7, 8); 9 | 10 | for (const [i, v] of result.entries()) { 11 | expect(v).toBe(expected[i]); 12 | } 13 | 14 | // Should not change the origin array 15 | expect(received.length).toBe(5); 16 | }); 17 | 18 | test('_concat should return an new array being composed of any value', () => { 19 | const received: any[] = [1, 0, '', undefined, null, [[100]], { name: 'ddzy' }]; 20 | const expected = 10; 21 | 22 | const result = _concat(received, [[200], [300]], { age: 21 }); 23 | 24 | expect(result.length).toBe(expected); 25 | }); 26 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/gt.test.ts: -------------------------------------------------------------------------------- 1 | import { gt } from "../../utility/others/gt"; 2 | 3 | describe('gt tests: ', () => { 4 | 5 | test('gt should return `true` when the param 1 that large than param2', () => { 6 | const received = [10, 5]; 7 | const expected = true; 8 | 9 | const result = gt(received[0], received[1]); 10 | 11 | expect(result).toBe(expected); 12 | }); 13 | 14 | test('gt should return `false` when the param 1 that equal with param2', () => { 15 | const received = [10, 10]; 16 | const expected = false; 17 | 18 | const result = gt(received[0], received[1]); 19 | 20 | expect(result).toBe(expected); 21 | }); 22 | 23 | test('gt should return `false` when the param 1 that small than param2', () => { 24 | const received = [10, 20]; 25 | const expected = false; 26 | 27 | const result = gt(received[0], received[1]); 28 | 29 | expect(result).toBe(expected); 30 | }); 31 | 32 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/object/isPlainObject.test.ts: -------------------------------------------------------------------------------- 1 | import { isPlainObject } from "../../utility/object/isPlainObject"; 2 | 3 | describe('isPlainObject', () => { 4 | test('isPlainObject should return `true` if received a plain object', () => { 5 | const received = [ 6 | { name: 'duan', age: 20 }, 7 | {}, 8 | ]; 9 | 10 | for (const v of received) { 11 | expect(isPlainObject(v)).toBeTruthy(); 12 | } 13 | }); 14 | 15 | test('isPlainObject should return `false` if received a non plain object', () => { 16 | document.body.innerHTML += ` 17 |

18 |

19 | `; 20 | 21 | const received = [ 22 | document.querySelectorAll('p'), 23 | [1, 2, 3], 24 | null, 25 | undefined, 26 | 0, 27 | '', 28 | false, 29 | true 30 | ]; 31 | 32 | for (const v of received) { 33 | expect(isPlainObject(v)).toBeFalsy(); 34 | } 35 | }); 36 | }) 37 | -------------------------------------------------------------------------------- /src/ddzy/utility/array/dropRightWhile/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 创建一个切片数组, 去除源数组中从特定的 callback 返回假值开始到尾部的部分 3 | * @param origin 源数组 4 | * @param callback 处理器 5 | * @example 6 | * // { 'user': 'barney', 'active': true } 7 | * const users = [ 8 | * { 'user': 'barney', 'active': true }, 9 | * { 'user': 'fred', 'active': false }, 10 | * { 'user': 'pebbles', 'active': false } 11 | * ] 12 | * dropRightWhile(users, function(v) { 13 | * return !v.active; 14 | * }); 15 | */ 16 | export function dropRightWhile( 17 | origin: I[], 18 | callback: (v: I, i: number, self: typeof origin) => boolean, 19 | ): I[] { 20 | let result: I[] = []; 21 | 22 | if (!origin.length) { 23 | return result; 24 | } 25 | 26 | for (const [i, v] of origin.entries()) { 27 | const bChecker = callback(v, i, origin); 28 | 29 | if (bChecker) { 30 | result = origin.slice(0, i); 31 | break; 32 | } 33 | } 34 | 35 | return result; 36 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/_bind.test.ts: -------------------------------------------------------------------------------- 1 | import { _bind } from "../../utility/function/_bind"; 2 | 3 | export type FunctionKeyType = keyof typeof Function; 4 | 5 | describe('_bind', () => { 6 | test('_bind should return `function` which can change the `this`', () => { 7 | const origin = { 8 | name: 'ddzy is a fe programmer', 9 | }; 10 | const expected = { 11 | name: 'ddzy is a fe programmer', 12 | arg0: 20, 13 | arg1: 30, 14 | }; 15 | 16 | const received = { 17 | printName(...args: any[]) { 18 | const name = this['name' as keyof typeof received]; 19 | const arg0 = args[0]; 20 | const arg1 = args[1]; 21 | 22 | expect({ name, arg0, arg1 }).toEqual(expected); 23 | }, 24 | }; 25 | 26 | Function.prototype['_bind' as FunctionKeyType] = _bind; 27 | 28 | const _bindedFunc = received.printName['_bind' as FunctionKeyType](origin); 29 | _bindedFunc([20, 30]); 30 | }) 31 | }) -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/mini-redux/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name MiniRedux 3 | * @description 以最简单的方式实现核心 redux 4 | * @author ddzy 5 | * @since 2020/09/15 6 | */ 7 | export type IStaticReducer = (state: S, action: A) => S; 8 | export type IStaticNotificationCallback = (state: S, action: A) => void; 9 | 10 | 11 | export default function createStore(reducer: IStaticReducer, state: S) { 12 | let initialState = state; 13 | let notifications: Array> = []; 14 | 15 | function dispatch(action: A) { 16 | initialState = reducer(initialState, action) || initialState; 17 | notifications.forEach((v) => { 18 | v(initialState, action); 19 | }); 20 | } 21 | 22 | function getState() { 23 | return initialState; 24 | } 25 | 26 | function subscribe(callback: IStaticNotificationCallback) { 27 | notifications.push(callback); 28 | } 29 | 30 | return { 31 | dispatch, 32 | getState, 33 | subscribe, 34 | }; 35 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/isStrictArray.test.ts: -------------------------------------------------------------------------------- 1 | import { isStrictArray } from "../../utility/array/isStrictArray"; 2 | 3 | describe('isStrictArray', () => { 4 | test('isStrictArray should return `true` if received a strict array', () => { 5 | const received = [ 6 | [], 7 | [1, 2, 4], 8 | [{}, {}], 9 | ]; 10 | 11 | for (const v of received) { 12 | expect(isStrictArray(v)).toBeTruthy(); 13 | } 14 | }); 15 | 16 | test('isStrictArray should return `false` if received a non array', () => { 17 | document.body.innerHTML += ` 18 |

19 |

20 |

21 | `; 22 | 23 | const received = [ 24 | { a: 1, b: 2, length: 2 }, 25 | document.querySelectorAll('p'), 26 | { name: 'duan', age: 20 }, 27 | null, 28 | undefined, 29 | 0, 30 | '', 31 | ]; 32 | 33 | for (const v of received) { 34 | expect(isStrictArray(v)).toBeFalsy(); 35 | } 36 | }); 37 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/pipe.test.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "../../utility/function/pipe"; 2 | 3 | describe('pipe', () => { 4 | test('pipe should invoke function from left to right', () => { 5 | function func1() { 6 | return 2; 7 | } 8 | function func2() { 9 | return func1() * 3; 10 | } 11 | function func3() { 12 | return func2() * 4; 13 | } 14 | 15 | function func4() { 16 | return 'duan' 17 | } 18 | function func5() { 19 | return func4() + '*' + 'zhao'; 20 | } 21 | function func6() { 22 | return func5() + '*' + 'yang'; 23 | } 24 | 25 | const received = { 26 | s1: [func1, func2, func3], 27 | s2: [func4, func5, func6], 28 | }; 29 | const expected = { 30 | p1: 24, 31 | p2: 'duan*zhao*yang', 32 | }; 33 | 34 | const r1 = pipe(...received.s1); 35 | const r2 = pipe(...received.s2); 36 | 37 | expect(r1).toBe(expected.p1); 38 | expect(r2).toBe(expected.p2); 39 | }); 40 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/reduce/index.ts: -------------------------------------------------------------------------------- 1 | import { isUndefined } from "../../../others/isUndefined"; 2 | 3 | /** 4 | * @name _reduce 5 | * @description 模拟实现ES6的`array.reduce()`API 6 | * @author ddzy 7 | * @since 2019-7-8 8 | * @see https://github.com/ddzy/ts-utility-plugins 9 | */ 10 | 11 | 12 | /** 13 | * 模拟es6的`array.reduce` 14 | * @param arr 源数组 15 | * @param callback 迭代函数 16 | * @param [initialValue] 初始值 17 | */ 18 | export function _reduce( 19 | arr: I[], 20 | callback: (total: O, current: I, index: number, self: typeof arr) => typeof total, 21 | initialValue?: O, 22 | ): typeof total { 23 | let dummy = !isUndefined(initialValue) ? initialValue : arr[0]; 24 | let total: I | O | undefined = dummy; 25 | let count = !isUndefined(initialValue) ? 0 : 1; 26 | 27 | if (!arr.length) { 28 | return total; 29 | } 30 | 31 | do { 32 | total = callback(total as O, arr[count], count, arr); 33 | } while ((count++ < arr.length - 1)); 34 | 35 | return total; 36 | } -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name es6-archive 3 | * @description 简单实现es6的API 4 | * @author ddzy 5 | * @since 2019/5/25 6 | */ 7 | 8 | import { _const } from './const/index'; 9 | import { _reflect } from './reflect/index'; 10 | import { _map } from './map/index'; 11 | import { _reduce } from './reduce/index'; 12 | import { _filter } from './filter/index'; 13 | import { _every } from './every/index'; 14 | import { _find } from './find/index'; 15 | import { _startsWith } from './startsWith/index'; 16 | import { _some } from './some/index'; 17 | import { _includes } from './includes/index'; 18 | import { _findIndex } from './findIndex/index'; 19 | import { Dictionary } from './dictionary/index'; 20 | import _Promise from './promise'; 21 | 22 | 23 | export const ES6Achieve = { 24 | _const, 25 | _reflect, 26 | _map, 27 | _reduce, 28 | _filter, 29 | _every, 30 | _find, 31 | _startsWith, 32 | _some, 33 | _includes, 34 | _findIndex, 35 | Dictionary, 36 | _Promise, 37 | }; 38 | -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/compose.test.ts: -------------------------------------------------------------------------------- 1 | import { compose } from "../../utility/function/compose"; 2 | 3 | describe('compose', () => { 4 | test('compose should invoke function from right to left', () => { 5 | function func1() { 6 | return func2() * 2; 7 | } 8 | function func2() { 9 | return func3() * 3; 10 | } 11 | function func3() { 12 | return 4; 13 | } 14 | 15 | function func4() { 16 | return func5() + '*' + 'yang'; 17 | } 18 | function func5() { 19 | return func6() + '*' + 'zhao'; 20 | } 21 | function func6() { 22 | return 'duan'; 23 | } 24 | 25 | const received = { 26 | s1: [func1, func2, func3], 27 | s2: [func4, func5, func6], 28 | }; 29 | const expected = { 30 | p1: 24, 31 | p2: 'duan*zhao*yang', 32 | }; 33 | 34 | const r1 = compose(...received.s1); 35 | const r2 = compose(...received.s2); 36 | 37 | expect(r1).toBe(expected.p1); 38 | expect(r2).toBe(expected.p2); 39 | }); 40 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/string/endsWith/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 检查字符串text是否以给定的target字符串结尾 3 | * @param text 源字符串 4 | * @param target 需要检索的字符 5 | * @param [position] 检索的位置 6 | * @example 7 | * // false 8 | * endsWith('ddzy', 'z'); 9 | * // true 10 | * endsWith('duanzhaoyang', 'g'); 11 | * // true 12 | * endsWith('alioe', 'o', 2); 13 | */ 14 | export function endsWith( 15 | text: string, 16 | target: string, 17 | position = text.length, 18 | ): boolean { 19 | const regSubStr = new RegExp(`${target}`, 'g'); 20 | 21 | let matchedStrArr = regSubStr.exec(text); 22 | 23 | while (matchedStrArr !== null) { 24 | const foundIndex = matchedStrArr['index']; 25 | 26 | if (position === text.length) { 27 | if (foundIndex === position - 1 || foundIndex === text.length - position) { 28 | return true; 29 | } 30 | } else { 31 | if (text.length - foundIndex === position) { 32 | return true; 33 | } 34 | } 35 | 36 | matchedStrArr = regSubStr.exec(text); 37 | } 38 | 39 | return false; 40 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/toFlatArrayOutPlace.test.ts: -------------------------------------------------------------------------------- 1 | import { toFlatArrayOutPlace } from "../../utility/array/toFlatArrayOutPlace"; 2 | 3 | describe('toFlatArrayOutPlace', () => { 4 | test('toFlatArrayOutPlace should flatten the origin array', () => { 5 | const received = [ 6 | 'duan', 7 | 1998, 8 | [ 9 | 'a', 10 | 'b', 11 | [ 12 | 'c', 13 | 'd', 14 | [ 15 | 'e', 16 | 'f', 17 | 123 18 | ], 19 | ], 20 | ], 21 | { 22 | name: 'duan', 23 | age: 20, 24 | }, 25 | ]; 26 | const expected = [ 27 | 'duan', 28 | 1998, 29 | 'a', 30 | 'b', 31 | 'c', 32 | 'd', 33 | 'e', 34 | 'f', 35 | 123, 36 | { name: 'duan', age: 20 }, 37 | ]; 38 | 39 | const result = toFlatArrayOutPlace(received); 40 | 41 | for (const [outerI, outerV] of result.entries()) { 42 | expect(outerV).toEqual(expected[outerI]); 43 | } 44 | }); 45 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/dropRight.test.ts: -------------------------------------------------------------------------------- 1 | import { dropRight } from "../../utility/array/dropRight"; 2 | 3 | describe('dropRight tests...', () => { 4 | test('dropRight should return an empty array when receive an empty array', () => { 5 | const received: number[] = []; 6 | const expected = []; 7 | 8 | const result = dropRight(received); 9 | 10 | expect(result.length).toBe(expected.length); 11 | }); 12 | 13 | test('dropRight should return the processed array correctly', () => { 14 | const received: number[] = [1, 2, 3, 4, 5]; 15 | const expected = [1, 2, 3]; 16 | 17 | const result = dropRight(received, 2); 18 | 19 | result.forEach((v, i) => { 20 | expect(v).toBe(expected[i]); 21 | }); 22 | }); 23 | 24 | test('dropRight should not change the origin array', () => { 25 | const received: number[] = [1, 2, 3, 4, 5, 6]; 26 | 27 | const result = dropRight(received); 28 | 29 | result.push(100, 200); 30 | 31 | expect(result.length).toBe(7); 32 | expect(received.length).toBe(6); 33 | }); 34 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/toStrictArray.test.ts: -------------------------------------------------------------------------------- 1 | import { toStrictArray } from "../../utility/array/toStrictArray"; 2 | 3 | describe('toStrictArray', () => { 4 | test('toStrictArray should receive an `ArrayLike` and return the `Array`', () => { 5 | document.body.innerHTML += ` 6 |
7 |
8 | `; 9 | const received1 = { length: 0 }; 10 | const received2 = { 11 | 0: 'ddzy', 12 | 1: 20, 13 | length: 2, 14 | }; 15 | const received3 = document.querySelectorAll('div'); 16 | const received4 = [1, 2, 3, 4]; 17 | 18 | const result1 = toStrictArray(received1); 19 | const result2 = toStrictArray(received2); 20 | const result3 = toStrictArray(received3); 21 | const result4 = toStrictArray(received4); 22 | 23 | expect(result1.length).toBe(0); 24 | expect(result2.length).toBe(2); 25 | expect(result3.length).toBe(2); 26 | expect(result4.length).toBe(4); 27 | 28 | expect(result2[0]).toBe(received2[0]); 29 | expect(result3[0]).toBe(received3[0]); 30 | expect(result4[0]).toBe(received4[0]); 31 | }); 32 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 yyge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/getAllEle.test.ts: -------------------------------------------------------------------------------- 1 | import { getAllEle } from "../../utility/dom/getAllEle"; 2 | import { getEle } from "../../utility/dom/getEle"; 3 | 4 | describe('getAllEle', () => { 5 | test('getEle should return `null` when no matched', () => { 6 | document.body.innerHTML = ` 7 |
8 |

9 |

10 |

11 |
12 | `; 13 | 14 | const origin = [ 15 | 'span', '.link', 16 | ]; 17 | 18 | for (const v of origin) { 19 | expect(getEle(v)).toBe(null); 20 | } 21 | }); 22 | 23 | test('getEle should return all of the `DOM object` which have been matched', () => { 24 | document.body.innerHTML = ` 25 |
26 |

27 |

28 |

29 |
30 | `; 31 | 32 | const origin = ['.text']; 33 | 34 | for (const v of origin) { 35 | expect(getAllEle(v)).toBe( 36 | document.querySelectorAll('.text') 37 | ); 38 | } 39 | }); 40 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/string/endsWith.test.ts: -------------------------------------------------------------------------------- 1 | import { endsWith } from "../../utility/string/endsWith"; 2 | 3 | describe('endsWith tests...', () => { 4 | 5 | test('endsWith should return the correct value', () => { 6 | interface IReceivedParams { 7 | text: string; 8 | target: string; 9 | position: number | undefined; 10 | }; 11 | 12 | const received: IReceivedParams[] = [ 13 | { 14 | text: 'duanzhaoyang', 15 | target: 'a', 16 | position: 3, 17 | }, 18 | { 19 | text: 'duanzhaoyang', 20 | target: 'ao', 21 | position: undefined, 22 | }, 23 | { 24 | text: 'ddzy', 25 | target: 'y', 26 | position: undefined, 27 | }, 28 | { 29 | text: 'duan', 30 | target: 'd', 31 | position: 4, 32 | }, 33 | ]; 34 | const expected = [true, false, true, true]; 35 | 36 | received.forEach((v, i) => { 37 | const result = endsWith(v.text, v.target, v.position); 38 | 39 | expect(result).toBe(expected[i]); 40 | }); 41 | }); 42 | 43 | }); -------------------------------------------------------------------------------- /webpack.config.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import htmlWebpackPlugin from 'html-webpack-plugin'; 4 | import { CleanWebpackPlugin } from 'clean-webpack-plugin'; 5 | 6 | 7 | export default { 8 | entry: './src/index.ts', 9 | output: { 10 | filename: 'index.js', 11 | path: path.resolve(__dirname, 'lib'), 12 | }, 13 | resolve: { 14 | extensions: ['.ts', '.js'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.(ts|js)?$/, 20 | use: 'babel-loader', 21 | exclude: /node_modules/, 22 | }, 23 | { 24 | test: /.html?$/, 25 | use: 'html-loader', 26 | }, 27 | { 28 | test: /.ejs?$/, 29 | use: 'ejs-loader', 30 | } 31 | ], 32 | }, 33 | devServer: { 34 | contentBase: './lib', 35 | hot: true, 36 | port: 3000, 37 | open: true, 38 | }, 39 | plugins: [ 40 | new htmlWebpackPlugin({ 41 | title: 'ts插件库测试', 42 | template: './assets/template/index.ejs', 43 | }), 44 | new CleanWebpackPlugin(), 45 | ], 46 | devtool: 'inline-source-map', 47 | }; -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/traversalDOMWithBFS.test.ts: -------------------------------------------------------------------------------- 1 | import { traversalDOMWithBFS } from "../../utility/dom/traversalDOMWithBFS"; 2 | 3 | describe('traversalDOMWithBFS', () => { 4 | test('traversalDOMWithBFS should traversal DOM Tree by BFS', () => { 5 | document.body.innerHTML = ` 6 |
7 |

title

8 |
9 |

10 |
11 |
12 | `; 13 | 14 | const expected = [ 15 | (document.getElementById('app') as HTMLDivElement), 16 | (document.querySelector('.title') as HTMLTitleElement), 17 | (document.querySelector('.post') as HTMLDivElement), 18 | (document.querySelector('.text') as HTMLParagraphElement), 19 | ]; 20 | const result: HTMLElement[] = []; 21 | 22 | traversalDOMWithBFS( 23 | (document.getElementById('app') as HTMLElement), 24 | (node) => { 25 | result.push(node); 26 | } 27 | ); 28 | 29 | for (const [index, node] of result.entries()) { 30 | expect(node).toBe(expected[index]); 31 | } 32 | }); 33 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/includes/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name _includes 3 | * @description 模拟ES6的`array.includes()` 4 | * @author ddzy 5 | * @license MIT 6 | * @since 2019-7-29 7 | */ 8 | 9 | /** 10 | * 判断数组中是否存在指定值 11 | * @param origin 源数组 12 | * @param target 目标值 13 | * @param fromIndex 起始位置 14 | */ 15 | export function _includes( 16 | origin: I[], 17 | target: any, 18 | fromIndex?: number, 19 | ): boolean { 20 | let result = false; 21 | 22 | fromIndex = fromIndex 23 | ? fromIndex < 0 24 | ? origin.length + fromIndex 25 | : fromIndex 26 | : 0; 27 | 28 | do { 29 | if (Object.is) { 30 | if (Object.is(target, origin[fromIndex])) { 31 | result = true; 32 | break; 33 | } 34 | } else { 35 | if (String(target) === 'NaN' && String(origin[fromIndex]) === 'NaN') { 36 | result = true; 37 | break; 38 | } 39 | if (target === origin[fromIndex]) { 40 | result = true; 41 | break; 42 | } 43 | } 44 | } while ((fromIndex++ < origin.length - 1)); 45 | 46 | return result; 47 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/compact.test.ts: -------------------------------------------------------------------------------- 1 | import { compact } from "../../utility/array/compact"; 2 | 3 | describe('compact', () => { 4 | test('compact should return an empty array when receive an empty array', () => { 5 | const received: any[] = []; 6 | const expected = 0; 7 | 8 | const result = compact(received); 9 | 10 | expect(result.length).toBe(0); 11 | }); 12 | 13 | test('compact should return the array only contains truthy value', () => { 14 | const received: any[] = [true, false, 0, '', undefined, NaN, null, 22]; 15 | const expected = [true, 22]; 16 | 17 | const result = compact(received); 18 | 19 | for (const [i, v] of result.entries()) { 20 | expect(v).toBe(expected[i]); 21 | } 22 | }); 23 | 24 | test('compact should return a new array', () => { 25 | const received: number[] = [0, 1, 2, 3, 4]; 26 | const expected: number[] = [1, 2, 3, 4]; 27 | 28 | const result = compact(received); 29 | 30 | received.push(5); 31 | 32 | for (const [i, v] of result.entries()) { 33 | expect(v).toBe(expected[i]); 34 | } 35 | }); 36 | }) -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/take.test.ts: -------------------------------------------------------------------------------- 1 | import take from "../../utility/array/take"; 2 | 3 | describe('take', () => { 4 | test('Method `take()` should return the correct result', () => { 5 | const received = [ 6 | { 7 | arr: [1, 2, 3, 4, 5, 6, 7, 8], 8 | amount: 5, 9 | }, 10 | { 11 | arr: [], 12 | amount: 3, 13 | }, 14 | { 15 | arr: ['a', 'b', 'c', 'd', 'e'], 16 | amount: 8, 17 | }, 18 | { 19 | arr: [[], [], [], [1, 2, 3]], 20 | amount: 0, 21 | }, 22 | ]; 23 | const expected = [ 24 | [1, 2, 3, 4, 5], 25 | [], 26 | ['a', 'b', 'c', 'd', 'e'], 27 | [], 28 | ]; 29 | 30 | received.forEach((outerV, outerI) => { 31 | const result = take(outerV.arr, outerV.amount); 32 | 33 | if (result.length > 0) { 34 | result.forEach((innerV, innerI) => { 35 | expect(innerV).toBe(expected[outerI][innerI]); 36 | }); 37 | } else { 38 | if (outerI % 2 !== 0) { 39 | expect(result.length).toBe(expected[outerI].length); 40 | } 41 | } 42 | }); 43 | }); 44 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/fill.test.ts: -------------------------------------------------------------------------------- 1 | import { fill } from "../../utility/array/fill"; 2 | 3 | 4 | describe('fill tests...', () => { 5 | 6 | test('method fill should return an empty array when receive an empty array', () => { 7 | const received: number[] = []; 8 | const expected = 0; 9 | 10 | const result = fill(received, 100); 11 | 12 | expect(result.length).toBe(expected); 13 | }); 14 | 15 | test('method fill should return the correct array when receive an array composed of number', () => { 16 | const received: number[] = [1, 2, 3, 4, 5]; 17 | const expected = [1, 2, 200, 200, 200, 200, 200, 200]; 18 | 19 | const result = fill(received, 200, 2); 20 | 21 | result.forEach((v, i) => { 22 | expect(v).toBe(expected[i]); 23 | }); 24 | }); 25 | 26 | test('method fill will modify the origin array', () => { 27 | const received: any[] = [false, 0, '', NaN, true, function () { }]; 28 | const expected = received; 29 | 30 | const result = fill(received, 'duanzhaoyang'); 31 | 32 | expect(result).toBe(expected); 33 | expect(result).toBe(received); 34 | }); 35 | 36 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/string/getRandomStr/index.ts: -------------------------------------------------------------------------------- 1 | import { getFullRandom } from "../../number/getFullRandom"; 2 | 3 | /** 4 | * 获取随机的乱序字符串 5 | * @param origin 需要乱序的源字符串数组 6 | * @param length 乱序后的字符串长度 7 | * @param enableSpecialCharacter 是否允许出现特殊字符 8 | * @returns {string} 乱序字符串 9 | */ 10 | export function getRandomStr( 11 | origin?: string[], 12 | length = 16, 13 | enableSpecialCharacter = true, 14 | ): string { 15 | if (!origin || origin.length === 0) { 16 | origin = []; 17 | for (let i = 97; i <= 122; i++) { 18 | origin.push(String.fromCharCode(i)); 19 | } 20 | for (let i = 65; i <= 90; i++) { 21 | origin.push(String.fromCharCode(i)); 22 | } 23 | for (let i = 48; i <= 57; i++) { 24 | origin.push(String.fromCharCode(i)); 25 | } 26 | enableSpecialCharacter && (origin.push('_', '-', '&', '$', '@', '^')); 27 | } 28 | 29 | const OPTIONS = { 30 | origin, 31 | length, 32 | }; 33 | let result = ''; 34 | 35 | let i = 0; 36 | do { 37 | result += OPTIONS.origin[getFullRandom(0, OPTIONS.origin.length)]; 38 | } while ((i++) < OPTIONS.length - 1); 39 | 40 | return result; 41 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/_join.test.ts: -------------------------------------------------------------------------------- 1 | import { _join } from "../../utility/array/_join"; 2 | 3 | describe('Method _join test:', () => { 4 | test('_join() should return the empty string when receive an empty array', () => { 5 | const received = { 6 | arr: [], 7 | separator: '', 8 | }; 9 | const expected = ''; 10 | 11 | const result = _join(received.arr, received.separator); 12 | 13 | expect(result).toBe(expected); 14 | }); 15 | 16 | test('_join() should return the composed string when receive an array maked by number', () => { 17 | const received = { 18 | arr: [1, 2, 3, 4, 5], 19 | separator: '', 20 | }; 21 | const expected = '12345'; 22 | 23 | const result = _join(received.arr, received.separator); 24 | 25 | expect(result).toBe(expected); 26 | }); 27 | 28 | test('_join() can config your own separator', () => { 29 | const received = { 30 | arr: ['a', 'b', 'c', 'd', 'e'], 31 | separator: '-', 32 | }; 33 | const expected = 'a-b-c-d-e'; 34 | 35 | const result = _join(received.arr, received.separator); 36 | 37 | expect(result).toBe(expected); 38 | }); 39 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/convertURLParameterToObject.test.ts: -------------------------------------------------------------------------------- 1 | import { convertURLParameterToObject } from "../../utility/others/convertURLParameterToObject"; 2 | 3 | describe('convertURLParameterToObject', () => { 4 | test('convertURLParameterToObject should return the truthy key-value-pair when received a URL string', () => { 5 | const received = [ 6 | 'https://github.com/ddzy?username=duan&age=20&token=kslgjadg', 7 | 'https://github.com/ddzy??username=duan&&age=20', 8 | 'https://github.com/ddzy/username=duan?age=20&token=askndg', 9 | 'https://github.com/ddzy&username=duan?age=20?token=askndg', 10 | ]; 11 | const expected = [ 12 | { 13 | username: 'duan', 14 | age: '20', 15 | token: 'kslgjadg', 16 | }, 17 | { 18 | username: 'duan', 19 | age: '20', 20 | }, 21 | { 22 | age: '20', 23 | token: 'askndg', 24 | }, 25 | { 26 | age: '20', 27 | token: 'askndg', 28 | }, 29 | ]; 30 | 31 | for (const [i, v] of received.entries()) { 32 | expect(convertURLParameterToObject(v)).toEqual(expected[i]); 33 | } 34 | }); 35 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/drop.test.ts: -------------------------------------------------------------------------------- 1 | import { drop } from "../../utility/array/drop"; 2 | 3 | describe('drop tests', () => { 4 | test('drop should return an empty array when receive an empty array', () => { 5 | const received: number[] = []; 6 | const expected = []; 7 | 8 | const result = drop(received); 9 | 10 | expect(result.length).toBe(expected.length); 11 | }); 12 | 13 | test('drop should return the droped array by using customized number when receive an array being composed of number', () => { 14 | const received: number[] = [1, 2, 3, 4, 5, 6]; 15 | const expected: number[] = [4, 5, 6]; 16 | 17 | const result = drop(received, 3); 18 | 19 | result.forEach((v, i) => { 20 | expect(v).toBe(expected[i]); 21 | }); 22 | }); 23 | 24 | test('drop should not change the origin array', () => { 25 | const received: number[] = [100, 300, 500]; 26 | const expected: number[] = [100, 300, 500]; 27 | 28 | const result = drop(received, 0); 29 | result.push(700, 900); 30 | 31 | expect(result.length).toBe(5); 32 | received.forEach((v, i) => { 33 | expect(v).toBe(expected[i]); 34 | }); 35 | }); 36 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/string/capitalize.test.ts: -------------------------------------------------------------------------------- 1 | import { capitalize } from "../../utility/string/capitalize"; 2 | 3 | describe('capitalize tests...', () => { 4 | test('capitalize should return the origin text when receive an empty string', () => { 5 | const received = ''; 6 | const expected = ''; 7 | 8 | const result = capitalize(received); 9 | 10 | expect(result).toBe(expected); 11 | }); 12 | 13 | test('capitalize should return the converted string correctly', () => { 14 | const received = ['ZHAO', 'duan']; 15 | const expected = ['Zhao', 'Duan']; 16 | 17 | const result = received.map((v) => { 18 | return capitalize(v); 19 | }); 20 | 21 | result.forEach((v, i) => { 22 | expect(v).toBe(expected[i]); 23 | }); 24 | }); 25 | 26 | test('capitalize should return the converted string when receive a text contains special characters', () => { 27 | const received = ['_DDZY', '___&*duanzhaoYANg']; 28 | const expected = ['_Ddzy', '___&*Duanzhaoyang']; 29 | 30 | const result = received.map((v) => { 31 | return capitalize(v); 32 | }); 33 | 34 | result.forEach((v, i) => { 35 | expect(v).toBe(expected[i]); 36 | }); 37 | }); 38 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/string/words.test.ts: -------------------------------------------------------------------------------- 1 | import { words } from "../../utility/string/words"; 2 | 3 | describe('Method words() test: ', () => { 4 | test('words() should use the default pattern while receive none pattern', () => { 5 | const received = { 6 | str: 'duan zhao _*$*$ y & ang a p g', 7 | reg: /\W+/, 8 | }; 9 | const expected = ["duan", "zhao _*$*$ y & ang a p g"]; 10 | 11 | const result = words(received.str, received.reg); 12 | 13 | result.forEach((v, i) => { 14 | expect(v).toBe(expected[i]); 15 | }); 16 | }); 17 | 18 | test('words() can use custom pattern', () => { 19 | const received = [ 20 | { 21 | str: 'duan zhao yang', 22 | reg: /\s+/g, 23 | }, 24 | { 25 | str: 'duan ### zhaoyang |%# s d h dan', 26 | reg: /#+/g, 27 | }, 28 | ]; 29 | const expected = [ 30 | ["duan", "zhao", "yang"], 31 | ["duan ", " zhaoyang ", "%", " s d h dan"], 32 | ]; 33 | 34 | received.forEach((outerV, outerI) => { 35 | const result = words(outerV.str, outerV.reg); 36 | 37 | result.forEach((innerV, innerI) => { 38 | expect(innerV).toBe(expected[outerI][innerI]); 39 | }); 40 | }); 41 | }); 42 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/traversalDOMWithDFS.test.ts: -------------------------------------------------------------------------------- 1 | import { traversalDOMWithDFS } from "../../utility/dom/traversalDOMWithDFS"; 2 | 3 | describe('traversalDOMWithDFS', () => { 4 | test('traversalDOMWithDFS should traversal DOM Tree by DFS', () => { 5 | document.body.innerHTML = ` 6 |
7 |

8 | 9 |

10 |
11 |

12 |
13 |
14 | `; 15 | 16 | const expected = [ 17 | (document.getElementById('app') as HTMLDivElement), 18 | (document.querySelector('.title') as HTMLTitleElement), 19 | (document.querySelector('.link') as HTMLAnchorElement), 20 | (document.querySelector('.post') as HTMLDivElement), 21 | (document.querySelector('.text') as HTMLParagraphElement), 22 | ]; 23 | const result: HTMLElement[] = []; 24 | 25 | traversalDOMWithDFS( 26 | (document.getElementById('app') as HTMLElement), 27 | (node) => { 28 | result.push(node); 29 | } 30 | ); 31 | 32 | for (const [index, node] of result.entries()) { 33 | expect(node).toBe(expected[index]); 34 | } 35 | }) 36 | }) -------------------------------------------------------------------------------- /src/ddzy/__tests__/number/divideByThousand.test.ts: -------------------------------------------------------------------------------- 1 | import { divideByThousand } from "../../utility/number/divideByThousand"; 2 | 3 | 4 | describe('divideByThousand tests...', () => { 5 | 6 | test('method divideByThousand should return the correct value', () => { 7 | const received: number[] = [ 8 | // 小于千位的数字 9 | 11, 45, -56, 0, 10 | // 刚好等于千位的数字 11 | 110, -323, 12 | // 大于千位的数字 13 | 1100, 3456687, -4534236423642364, 14 | ]; 15 | const expected = [ 16 | '11', '45', '-56', '0', 17 | '110', '-323', 18 | '1,100', '3,456,687', '-4,534,236,423,642,364' 19 | ]; 20 | 21 | received.forEach((v, i) => { 22 | const result = divideByThousand(v); 23 | 24 | expect(result).toBe(expected[i]); 25 | }); 26 | }); 27 | 28 | test('method divideByThousand should use customized separator that passed', () => { 29 | const received = [ 30 | { 31 | num: 12345, 32 | separator: '_', 33 | }, 34 | ]; 35 | const expected = ['12_345']; 36 | 37 | received.forEach((v, i) => { 38 | const result = divideByThousand(v.num, v.separator); 39 | 40 | expect(result).toBe(expected[i]); 41 | }); 42 | }); 43 | 44 | }) -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import sameValueZero from "./ddzy/utility/others/sameValueZero"; 2 | 3 | let a = Symbol('a'); 4 | let b = Symbol('b'); 5 | let c = [1, 2, 3]; 6 | let d = { a, b, c }; 7 | 8 | const received = [ 9 | { 10 | a: 10, 11 | b: 'duan', 12 | }, 13 | { 14 | a: undefined, 15 | b: undefined, 16 | }, 17 | { 18 | a: null, 19 | b: null, 20 | }, 21 | { 22 | a: NaN, 23 | b: NaN, 24 | }, 25 | { 26 | a: +0, 27 | b: -0, 28 | }, 29 | { 30 | a: -0, 31 | b: +0, 32 | }, 33 | { 34 | a: 999, 35 | b: 999, 36 | }, 37 | { 38 | a: 99999, 39 | b: 11111, 40 | }, 41 | { 42 | a: 'duan', 43 | b: 'duan', 44 | }, 45 | { 46 | a: 'duan', 47 | b: 'duanzhaoyang', 48 | }, 49 | { 50 | a: true, 51 | b: true, 52 | }, 53 | { 54 | a: true, 55 | b: false, 56 | }, 57 | { 58 | a: a, 59 | b: b, 60 | }, 61 | { 62 | a: a, 63 | b: a, 64 | }, 65 | { 66 | a: c, 67 | b: d, 68 | }, 69 | { 70 | a: c, 71 | b: c, 72 | }, 73 | ]; 74 | 75 | const expected = received.map((v) => { 76 | return sameValueZero(v.a, v.b); 77 | }); 78 | 79 | console.log(expected); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/castArray.test.ts: -------------------------------------------------------------------------------- 1 | import { castArray } from "../../utility/array/castArray"; 2 | 3 | describe('castArray', () => { 4 | test('castArray should convert the passed value to array forcibly', () => { 5 | interface IObjParams { 6 | age: number, 7 | }; 8 | const obj: IObjParams = { 9 | age: 21, 10 | }; 11 | 12 | const received = [ 13 | 0, 14 | undefined, 15 | null, 16 | 'ddzy', 17 | obj, 18 | ]; 19 | const expected = [ 20 | [0], 21 | [undefined], 22 | [null], 23 | ['ddzy'], 24 | [obj], 25 | ]; 26 | 27 | for (const [i, v] of received.entries()) { 28 | const result = castArray(v) as any[]; 29 | 30 | expect(result[0]).toBe(expected[i][0]); 31 | } 32 | }); 33 | 34 | test('castArray should return it immediatly when receive an array', () => { 35 | const arr: number[] = []; 36 | 37 | const received = [ 38 | arr, 39 | ]; 40 | const expected = [ 41 | arr, 42 | ]; 43 | 44 | for (const [i, v] of received.entries()) { 45 | const result = castArray(v) as number[]; 46 | 47 | expect(result).toBe(expected[i]); 48 | } 49 | }); 50 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/eq.test.ts: -------------------------------------------------------------------------------- 1 | import { eq } from "../../utility/others/eq"; 2 | 3 | describe('eq tests: ', () => { 4 | test('eq should return true or false when receive two basic value that equals', () => { 5 | const received = [100, 100]; 6 | const expected = true; 7 | 8 | const result = eq(received[0], received[1]); 9 | 10 | expect(result).toBe(expected); 11 | }); 12 | 13 | test('eq should return false when receive two object but point at different memory', () => { 14 | const received = [{}, {}]; 15 | const expected = false; 16 | 17 | const result = eq(received[0], received[1]); 18 | 19 | expect(result).toBe(expected); 20 | }); 21 | 22 | test('eq should return true when receive two object point at same memory', () => { 23 | const obj = {}; 24 | 25 | const received = [obj, obj]; 26 | const expected = true; 27 | 28 | const result = eq(received[0], received[1]); 29 | 30 | expect(result).toBe(expected); 31 | }); 32 | 33 | test('eq should return true when receive two NaN value', () => { 34 | const received = [NaN, NaN]; 35 | const expected = true; 36 | 37 | const result = eq(received[0], received[1]); 38 | 39 | expect(result).toBe(expected); 40 | }); 41 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/string/trim.test.ts: -------------------------------------------------------------------------------- 1 | import { trim } from "../../utility/string/trim"; 2 | 3 | describe('trim() tests', () => { 4 | test('trim should return a new processed string by using the default replacer when receive an empty string', () => { 5 | const received = ''; 6 | const expected = ''; 7 | 8 | const result = trim(received); 9 | 10 | expect(result).toBe(expected); 11 | }); 12 | 13 | test('trim should return a new processed string that throw away the whitespace by using the default replacer', () => { 14 | const received = ' ddzy '; 15 | const expected = 'ddzy'; 16 | 17 | const result = trim(received); 18 | 19 | expect(result).toBe(expected); 20 | }); 21 | 22 | test('trim should return a new processed string by using a special replacer', () => { 23 | const received = [ 24 | 'wow, i like program!', 25 | 'oh... i love you oh', 26 | ]; 27 | const expected = [ 28 | ', i like program!', 29 | '... i love you ', 30 | ]; 31 | 32 | const replacer = ['wow', 'oh']; 33 | 34 | const result = received.map((v, i) => { 35 | return trim(v, replacer[i]); 36 | }); 37 | 38 | result.forEach((v, i) => { 39 | expect(v).toBe(expected[i]); 40 | }); 41 | }); 42 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/object/forOwn.test.ts: -------------------------------------------------------------------------------- 1 | import { forOwn } from "../../utility/object/forOwn"; 2 | 3 | describe('forOwn tests...', () => { 4 | test('method forOwn should traversal the object with its own property', () => { 5 | const received = { 6 | name: 'duanzhaoyang', 7 | age: 21, 8 | address: 'Dongguan', 9 | }; 10 | const expected = { 11 | name: 'duanzhaoyang', 12 | age: 21, 13 | address: 'Dongguan', 14 | }; 15 | 16 | forOwn(received, (key, value, origin) => { 17 | expect(expected[key as keyof typeof expected]).toBe(value); 18 | expect(origin).toBe(received); 19 | }); 20 | }); 21 | 22 | test('method forOwn should traversal the object without the property which were inherite by prototype', () => { 23 | class Received { 24 | public name: string = ''; 25 | public age: number = 0; 26 | } 27 | Received.prototype.isCorrect = false; 28 | const expected = { 29 | name: '', 30 | age: 0, 31 | }; 32 | 33 | const received = new Received(); 34 | 35 | forOwn(received, (key, value, origin) => { 36 | expect(expected[key as keyof typeof expected]).toBe(value); 37 | expect(key !== 'isCorrect').toBeTruthy(); 38 | expect(origin).toBe(received); 39 | }); 40 | }); 41 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/object/get/index.ts: -------------------------------------------------------------------------------- 1 | import { isStrictArray } from "../../array/isStrictArray"; 2 | import { isUndefined } from "../../others/isUndefined"; 3 | import { trim } from "../../string/trim"; 4 | 5 | /** 6 | * 根据 object 对象的 path 路径获取值 7 | * @param origin 提供的源对象 8 | * @param path 路径信息 9 | * @param defaultValue 路径对应的值为 undefined 时, 使用该值替代 10 | */ 11 | export function get( 12 | origin: object, 13 | path: string | string[], 14 | defaultValue?: any, 15 | ): any { 16 | const formatedOrigin = origin; 17 | const formatedPath = typeof path === 'string' 18 | ? path.split('.') 19 | : isStrictArray(path) 20 | ? path 21 | : []; 22 | const formatedDefaultValue = defaultValue || undefined; 23 | 24 | const regAttrPicker = /^(\w+)\[(\d{1})\]$/; 25 | let tempValue: any = formatedOrigin; 26 | 27 | for (let i = 0, every; every = formatedPath[i++];) { 28 | try { 29 | const matchedAttr = every.match(regAttrPicker); 30 | 31 | if (matchedAttr) { 32 | tempValue = tempValue[matchedAttr[1]][matchedAttr[2]]; 33 | } else { 34 | tempValue = tempValue[every]; 35 | } 36 | } catch (error) { 37 | return formatedDefaultValue; 38 | } 39 | } 40 | 41 | return isUndefined(tempValue) ? defaultValue : tempValue; 42 | } -------------------------------------------------------------------------------- /src/ddzy/utility/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility 2 | 3 | 详细罗列出所有的工具函数, 主文档参考[这里](../README.md) 4 | 5 | ## 说明 6 | 7 | `utility`总共分为`n`多个大区块——`dom`相关、`Array`相关...诸如此类. 8 | 9 | `2019/5/28`, 避免文档过于繁重, 故将utility相关的API搬运至`Gitbook`. 10 | 11 | **Gitbook在线文档**: [https://ddzy.gitbook.io/ts-utility-plugins-docs/](https://ddzy.gitbook.io/ts-utility-plugins-docs/) 12 | 13 | ## 目录 14 | 15 | | Name | Description | Link | 16 | | --------- | --------------------------------------------------- | ----------------------------- | 17 | | algorithm | 汇集有关`算法`、`数据结构`、`模拟原生API`的工具插件 | [地址](./algorithm/README.md) | 18 | | array | 汇集有关`数组`的工具方法 | [地址](./array/README.md) | 19 | | dom | 汇集有关`DOM`的工具方法 | [地址](./dom/README.md) | 20 | | function | 汇集有关`函数`的工具方法 | [地址](./function/README.md) | 21 | | number | 汇集有关`数值`的工具方法 | [地址](./number/README.md) | 22 | | object | 汇集有关`对象`的工具方法 | [地址](./object/README.md) | 23 | | string | 汇集有关`字符串`的工具方法 | [地址](./string/README.md) | 24 | | date | 汇集有关`日期`的工具方法 | [地址](./date/README.md) | 25 | | others | 汇集其它有用的工具方法 | [地址](./others/README.md) | -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/after.test.ts: -------------------------------------------------------------------------------- 1 | import { after } from "../../utility/function/after"; 2 | 3 | describe('after', () => { 4 | test('after should fire the handler with no params after the special times of function call', () => { 5 | function doingWorkAsync( 6 | props: { type: string, sign: (...args: any[]) => void }, 7 | ) { 8 | setTimeout(() => { 9 | props.sign(); 10 | }, 0); 11 | } 12 | 13 | const employees = ['jack', 'rose', 'riven']; 14 | const fragment = after(employees.length, () => { 15 | expect(true).toBeTruthy(); 16 | }); 17 | 18 | for (const employee of employees) { 19 | doingWorkAsync({ type: employee, sign: fragment }); 20 | } 21 | }); 22 | 23 | test('after should fire the handler with any params after special times of function call', () => { 24 | function doingWorkAsyncWithParams( 25 | props: { type: string, sign: (...args: any[]) => void }, 26 | ) { 27 | setTimeout(() => { 28 | props.sign(props.type); 29 | }, 0); 30 | } 31 | 32 | const employees = ['jack', 'rose', 'riven']; 33 | const fragment = after(employees.length, (type) => { 34 | expect(type).toBe('riven'); 35 | }); 36 | 37 | for (const employee of employees) { 38 | doingWorkAsyncWithParams({ type: employee, sign: fragment }); 39 | } 40 | }); 41 | }) -------------------------------------------------------------------------------- /src/ddzy/business/carousel/index.ts: -------------------------------------------------------------------------------- 1 | import { Scroll } from './carousel-scroll/carouselScroll'; 2 | import { Fade } from './carousel-fade/carouselFade'; 3 | 4 | 5 | export interface ICarouselConfigProps { 6 | container?: string, 7 | dataSource?: { 8 | text: string, 9 | img: { 10 | url: string, 11 | target: string, 12 | }, 13 | }[]; 14 | autoPlay?: boolean; 15 | showDots?: boolean; 16 | showArrows?: boolean; 17 | easing?: string; 18 | effect?: string; 19 | vertical?: boolean; 20 | duringTime?: number; 21 | delayTime?: number; 22 | isHoverPause?: boolean; 23 | beforeChange?: () => void; 24 | afterChange?: () => void; 25 | } 26 | 27 | export class Carousel { 28 | public constructor( 29 | config: ICarouselConfigProps, 30 | ) { 31 | this.__init(config); 32 | } 33 | 34 | private __init( 35 | config: ICarouselConfigProps, 36 | ): void { 37 | this.__initWhichEffect(config); 38 | } 39 | 40 | private __initWhichEffect( 41 | config: ICarouselConfigProps, 42 | ): void { 43 | switch (config.effect) { 44 | case 'scroll': { 45 | new Scroll(config); 46 | return; 47 | } 48 | case 'fade': { 49 | new Fade(config); 50 | return; 51 | } 52 | default: { 53 | new Scroll(config); 54 | return; 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/qiqf/utility/test/test-array.ts: -------------------------------------------------------------------------------- 1 | import * as arrayUtil from '../array' 2 | import * as InterfaceUtil from "../array/IArrayInterface"; 3 | // const numberArrays = [1,2,3,4,5]; 4 | 5 | // console.log(arrayUtil.arrayMax(numberArrays)); // 5 6 | // console.log(arrayUtil.arrayMin(numberArrays)); // 1 7 | // console.log(arrayUtil.arrayChunk(numberArrays, 3)); // [[1,2,3], [4,5]] 8 | // console.log(arrayUtil.compact(numberArrays)); // [1,2,3,4,5] 9 | 10 | // const testSecondArrays = [1,2,1,3,4,5,1]; 11 | // console.log(arrayUtil.countOccurrences(testSecondArrays, 1)); // 3 12 | 13 | // const testDeepArrays = [1,2,[2,3], [4,5,[7,8]]]; 14 | // console.log(arrayUtil.deepFlatten(testDeepArrays)); // [1, 2, 2, 3, 4, 5, 7, 8] 15 | 16 | // console.log(arrayUtil.difference([1,2,3], [3,'4','1',5])); // ["4", "1", 5] 17 | 18 | // console.log(arrayUtil.sameArray([1,2,3], [3,'4','1',5])); // [3] 19 | 20 | // console.log(arrayUtil.distinctValuesOfArray([1,2,1,3,4,2,3])); // [1,2,3,4] 21 | 22 | // console.log(arrayUtil.dropElements([1,2,3,4], e => e === 3)); // [3,4] 23 | 24 | // console.log(arrayUtil.everyNth([1,2,3,4,5], 2)); [1,3,5] 25 | 26 | // console.log(arrayUtil.filterNonUnique([1,2,3,1,2,4,5,5,1])); [3,4] 27 | // console.log(arrayUtil.flatten([1,2,[3,4,5], 6,7])); [1,2,3,4,5,6,7] 28 | 29 | // const flattenArrays = [1,2,[3,4,[5,6,7,[8,9,[10]]]],11,12,13]; 30 | // console.log(arrayUtil.flattenDepth(flattenArrays, 1)); 31 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "linterOptions": { 3 | "exclude": [ 4 | "config/**/*.js", 5 | "node_modules/**/*.ts" 6 | ] 7 | }, 8 | "rules": { 9 | "object-literal-sort-keys": false, 10 | "jsx-no-lambda": false, 11 | "eofline": false, 12 | "no-consecutive-blank-lines": false, 13 | "no-var-requires": false, 14 | "quotemark": false, 15 | "space-within-parens": false, 16 | "no-submodule-imports": false, 17 | "no-implicit-dependencies": false, 18 | "ordered-imports": [ 19 | false 20 | ], 21 | "jsx-boolean-value": false, 22 | "no-trailing-whitespace": false, 23 | "semicolon": false, 24 | "trailing-comma": false, 25 | "space-in-parents": false, 26 | "typedef-whitespace": false, 27 | "whitespace": false, 28 | "interface-over-type-literal": true, 29 | "no-duplicate-imports": false, 30 | "no-namespace": true, 31 | "no-internal-module": true, 32 | "no-unused-expression": [false], 33 | "space-before-function-paren": [ 34 | "ignore" 35 | ], 36 | "no-console": [ 37 | "error", 38 | { 39 | "allow": [ 40 | "log", 41 | "error" 42 | ] 43 | } 44 | ], 45 | "no-empty-interface": false, 46 | "no-string-literal": false, 47 | "no-bitwise": false, 48 | "variable-name": [ 49 | false 50 | ] 51 | } 52 | } -------------------------------------------------------------------------------- /assets/template/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 19 | 25 | 26 | 27 | 28 | 29 | 51 | 52 |
53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/_instanceOf.test.ts: -------------------------------------------------------------------------------- 1 | import { _instanceOf } from "../../utility/others/_instanceOf"; 2 | 3 | describe('_instanceOf tests...', () => { 4 | 5 | test('_instanceOf should return the correct result', () => { 6 | interface PersonConstructor { 7 | new(): PersonInterface; 8 | }; 9 | interface PersonInterface { 10 | }; 11 | 12 | class Person implements PersonInterface { 13 | constructor() { 14 | } 15 | } 16 | 17 | const received: Array<{ 18 | left: any, 19 | right: any, 20 | }> = [ 21 | { 22 | left: new Person(), 23 | right: Person, 24 | }, 25 | { 26 | left: {}, 27 | right: Object, 28 | }, 29 | { 30 | left: [], 31 | right: Array, 32 | }, 33 | { 34 | left: [], 35 | right: Object, 36 | }, 37 | { 38 | left: 2, 39 | right: 8, 40 | }, 41 | { 42 | left: 'ddzy', 43 | right: {}, 44 | }, 45 | { 46 | left: {}, 47 | right: null, 48 | }, 49 | ]; 50 | const expected = [true, true, true, true, false, false, false,]; 51 | 52 | received.forEach((v, i) => { 53 | const result = _instanceOf(v.left, v.right); 54 | 55 | expect(result).toBe(expected[i]); 56 | }); 57 | }); 58 | 59 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/others/sameValueZero/index.ts: -------------------------------------------------------------------------------- 1 | import isBoolean from "../isBoolean"; 2 | import isNaN from "../isNaN"; 3 | import { isNull } from "../isNull"; 4 | import isNumber from "../isNumber"; 5 | import isString from "../isString"; 6 | import isSymbol from "../isSymbol"; 7 | 8 | /** 9 | * 使用 SameValueZero 策略比较两个值是否相等 10 | * @see http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero 11 | * @description SameValueZero 和 SameValue 的区别在于前者: SameValueZero(0, -0) => true 12 | * @param a 需要对比的第一个值 13 | * @param b 需要对比的第二个值 14 | */ 15 | export default function sameValueZero(a: any, b: any): boolean { 16 | if (typeof a !== typeof b) { 17 | return false; 18 | } 19 | if (typeof a === 'undefined' && typeof b === 'undefined') { 20 | return true; 21 | } 22 | if (isNull(a) && isNull(b)) { 23 | return true; 24 | } 25 | if (isNumber(a) && isNumber(b)) { 26 | if (isNaN(a) && isNaN(b)) { 27 | return true; 28 | } 29 | if (a === 0 && b === 0 && (1 / a > 0) && (1 / b) < 0) { 30 | return true; 31 | } 32 | if (a === 0 && b === 0 && (1 / a < 0) && (1 / b) > 0) { 33 | return true; 34 | } 35 | if (a == b) { 36 | return true; 37 | } 38 | return false; 39 | } 40 | if (isString(a) && isString(b)) { 41 | return a === b; 42 | } 43 | if (isBoolean(a) && isBoolean(b)) { 44 | return a === b; 45 | } 46 | if (isSymbol(a) && isSymbol(b)) { 47 | return a === b; 48 | } 49 | return a === b; 50 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/size.test.ts: -------------------------------------------------------------------------------- 1 | import { size } from "../../utility/others/sizse"; 2 | 3 | describe('size tests...', () => { 4 | 5 | test('size() should return zero when receive a special character', () => { 6 | const received = [0, 100, undefined, null, Symbol('ddzy'), function () { }]; 7 | const expected = [0, 0, 0, 0, 0, 0]; 8 | 9 | received.forEach((v, i) => { 10 | const result = size(v); 11 | 12 | expect(result).toBe(expected[i]); 13 | }); 14 | }); 15 | 16 | test('size() should return the correct length of string', () => { 17 | const received = ['', 'ddzy']; 18 | const expected = [0, 4]; 19 | 20 | received.forEach((v, i) => { 21 | const result = size(v); 22 | 23 | expect(result).toBe(expected[i]); 24 | }); 25 | }); 26 | 27 | test('size() should return the correct length of array', () => { 28 | const received = [[], [0, 1, 2, 3, 4, 5]]; 29 | const expected = [0, 6]; 30 | 31 | received.forEach((v, i) => { 32 | const result = size(v); 33 | 34 | expect(result).toBe(expected[i]); 35 | }); 36 | }); 37 | 38 | test('size() should return the correct length of array', () => { 39 | const received = [{}, { name: 'duanzhaoyang', age: 21 }]; 40 | const expected = [0, 2]; 41 | 42 | received.forEach((v, i) => { 43 | const result = size(v); 44 | 45 | expect(result).toBe(expected[i]); 46 | }); 47 | }); 48 | 49 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/object/get.test.ts: -------------------------------------------------------------------------------- 1 | import { get } from "../../utility/object/get"; 2 | 3 | describe('get() tests...', () => { 4 | 5 | test('method get should return the correct value', () => { 6 | const received: Array<{ 7 | origin: any, 8 | path: string | string[], 9 | defaultValue: any, 10 | }> = [ 11 | { 12 | origin: {}, 13 | path: 'a.b.c', 14 | defaultValue: undefined, 15 | }, 16 | { 17 | origin: {}, 18 | path: 'a[0].b[1].c', 19 | defaultValue: 'default', 20 | }, 21 | { 22 | origin: { 23 | a: [ 24 | { 25 | b: { 26 | c: 3, 27 | }, 28 | }, 29 | ], 30 | }, 31 | path: 'a[0].b.c', 32 | defaultValue: undefined, 33 | }, 34 | { 35 | origin: [ 36 | { 37 | a: [ 38 | { 39 | b: 1998, 40 | }, 41 | ], 42 | }, 43 | ], 44 | path: ['0', 'a', '0', 'b'], 45 | defaultValue: undefined, 46 | }, 47 | ]; 48 | const expected = [undefined, 'default', 3, 1998]; 49 | 50 | received.forEach((v, i) => { 51 | const result = get(v.origin, v.path, v.defaultValue); 52 | 53 | expect(result).toBe(expected[i]); 54 | }); 55 | }); 56 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/dom/_querySelector.test.ts: -------------------------------------------------------------------------------- 1 | import { _querySelector } from "../../utility/dom/_querySelector"; 2 | 3 | 4 | describe('_querySelector', () => { 5 | test('_querySelector should return `null` when no matched', () => { 6 | document.body.innerHTML = ` 7 |
8 |

9 |

10 | 11 |

12 |
13 |

14 |
15 |
16 | `; 17 | 18 | const received = ['.ddzy', 'ddzy', '#ddzy', '']; 19 | 20 | for (const v of received) { 21 | expect(_querySelector(v)).toBeNull(); 22 | } 23 | }); 24 | 25 | test('_querySelector should return `a single Element` when matched', () => { 26 | document.body.innerHTML = ` 27 |
28 |

29 |

30 | 31 |

32 |
33 |

34 |
35 |
36 | `; 37 | 38 | const received = ['#app', '.title', '.text', 'a']; 39 | const expected = [ 40 | document.querySelector('#app'), 41 | document.querySelector('.title'), 42 | document.querySelector('.text'), 43 | document.querySelector('a'), 44 | ]; 45 | 46 | for (const [i, v] of received.entries()) { 47 | expect(_querySelector(v)).toBe(expected[i]); 48 | } 49 | }); 50 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/number/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility/number 2 | 3 | ## 说明 4 | 5 | 汇集有关`数值`的工具方法. 6 | 7 | ## 目录 8 | 9 | | Name | Description | Source | Docs | 10 | | ---------------- | -------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------- | 11 | | getRadian | `角度`转`弧度` | [源码](./getRadian/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-number/getradian) | 12 | | getFullRandom | 获取指定范围内的随机`整数` | [源码](./getFullRandom/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-number/getfullrandom) | 13 | | getAnyRandom | 获取指定范围内的随机`自然数` | [源码](./getAnyRandom/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-number/getanyrandom) | 14 | | inRange | 检查指定值是否在 `start` 与 `end` 之间, 但是不包括 `end` | [源码](./inRange/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-number/inrange) | 15 | | divideByThousand | 将数字按千位分隔 | [源码](./divideByThousand/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-number/dividebythousand) | -------------------------------------------------------------------------------- /src/ddzy/utility/string/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility/string 2 | 3 | ## 说明 4 | 5 | 汇集有关`字符串`的工具方法. 6 | 7 | ## 目录 8 | 9 | | Name | Description | Source | Docs | 10 | | ------------ | -------------------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------- | 11 | | getRandomStr | 获取`随机`的`乱序`字符串 | [源码](./getRandomStr/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-string-1/getrandomstr) | 12 | | trim | 去除字符串首尾的指定字符, 默认为空格(`whitespace`) | [源码](./trim/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-string-1/trim) | 13 | | capitalize | 将指定的字符串的`首字母`大写, 剩下为小写 | [源码](./capitalize/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-string-1/capitalize) | 14 | | endsWith | 检查字符串`text`是否以给定的`target`字符串结尾 | [源码](./endsWith/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-string-1/endswith) | 15 | | lowerCase | 转换字符串`string`以空格分开单词, 并转换为`小写` | [源码](./lowerCase/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-string-1/lowercase) | 16 | | words | 按照指定模式, 拆分字符串 `string` 中的词为数组 | [源码](./words/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-string-1/words) | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-utility-plugins", 3 | "version": "1.0.0", 4 | "description": "ts构建的学习类库, 囊括canvas、business、utility等多个区块", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage", 8 | "build": "webpack --config webpack.config.ts", 9 | "start": "webpack-dev-server", 10 | "check": "tsc -w" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ddzy/ts-utility-plugins.git" 15 | }, 16 | "author": "ddzy", 17 | "license": "Apache", 18 | "bugs": { 19 | "url": "https://github.com/ddzy/ts-utility-plugins/issues" 20 | }, 21 | "homepage": "https://ddzy.gitbook.io/ts-utility-plugins-docs", 22 | "devDependencies": { 23 | "@babel/plugin-proposal-class-properties": "^7.4.4", 24 | "@babel/plugin-proposal-object-rest-spread": "^7.4.4", 25 | "@babel/plugin-transform-runtime": "^7.4.4", 26 | "@babel/preset-env": "^7.4.4", 27 | "@babel/preset-typescript": "^7.3.3", 28 | "@types/clean-webpack-plugin": "^0.1.3", 29 | "@types/html-webpack-plugin": "^3.2.0", 30 | "@types/jest": "^24.0.12", 31 | "@types/node": "^10.12.2", 32 | "@types/webpack": "^4.4.27", 33 | "babel-loader": "^8.0.5", 34 | "clean-webpack-plugin": "^3.0.0", 35 | "ejs-loader": "^0.3.3", 36 | "html-webpack-plugin": "^3.2.0", 37 | "jest": "^24.8.0", 38 | "ts-loader": "^5.4.3", 39 | "ts-node": "^8.1.0", 40 | "typescript": "^3.4.5", 41 | "webpack": "^4.30.0", 42 | "webpack-cli": "^3.3.1", 43 | "webpack-dev-server": "^3.3.1" 44 | }, 45 | "dependencies": { 46 | "@babel/runtime": "^7.4.5" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/zip.test.ts: -------------------------------------------------------------------------------- 1 | import { zip } from "../../utility/array/zip"; 2 | 3 | describe('Method zip tests...', () => { 4 | test('zip should return the zipped array correctly when receive an array composed of number', () => { 5 | const received = [ 6 | [1, 2, 3], 7 | [4, 5, 6], 8 | [7, 8, 9, 10, 11], 9 | ]; 10 | const expected = [ 11 | [1, 4, 7], 12 | [2, 5, 8], 13 | [3, 6, 9], 14 | [10], 15 | [11] 16 | ]; 17 | 18 | const result = zip(...received); 19 | 20 | expect(result.length).toBe(expected.length); 21 | 22 | result.forEach((outerV, outerI) => { 23 | outerV.forEach((innerV, innerI) => { 24 | expect(innerV).toBe(expected[outerI][innerI]); 25 | }); 26 | }); 27 | }); 28 | 29 | test('zip should return the zipped array correctly when receive an mixed array', () => { 30 | const arr1: any[] = []; 31 | const obj1 = { 32 | name: 'duanzhaoyang', 33 | age: 21, 34 | } 35 | const func1 = function () { }; 36 | 37 | const received = [ 38 | [998, 899, 989, 898], 39 | [false, true], 40 | [arr1, obj1], 41 | [func1] 42 | ]; 43 | const expected = [ 44 | [998, false, arr1, func1], 45 | [899, true, obj1], 46 | [989], 47 | [898], 48 | ]; 49 | 50 | const result = zip(...received); 51 | 52 | expect(result.length).toBe(expected.length); 53 | 54 | result.forEach((outerV, outerI) => { 55 | outerV.forEach((innerV, innerI) => { 56 | expect(innerV).toBe(expected[outerI][innerI]); 57 | }); 58 | }); 59 | }); 60 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/object/forIn.test.ts: -------------------------------------------------------------------------------- 1 | import { forIn } from "../../utility/object/forIn"; 2 | 3 | describe('forIn tests...', () => { 4 | 5 | test('method forIn should not exit', () => { 6 | interface IReceivedProps { 7 | name: string, 8 | age: number, 9 | skill: string, 10 | }; 11 | 12 | const received: IReceivedProps = { 13 | name: 'duanzhaoyang', 14 | age: 21, 15 | skill: 'program', 16 | }; 17 | const expected = received; 18 | 19 | forIn(received, (value, key) => { 20 | expect(value).toBe(expected[key as keyof typeof expected]); 21 | }); 22 | }); 23 | 24 | test('method forIn should exit when return `false`', () => { 25 | interface IReceivedProps { 26 | city: string[]; 27 | position: { 28 | x: number, 29 | y: number, 30 | }; 31 | continue: boolean; 32 | nextOne: number; 33 | nextTwo: number; 34 | }; 35 | 36 | const received: IReceivedProps = { 37 | city: ['Dongguan', 'Foshan', 'Guangzhou'], 38 | position: { 39 | x: 100, 40 | y: 200, 41 | }, 42 | continue: false, 43 | nextOne: 1, 44 | nextTwo: 2, 45 | }; 46 | 47 | const expected = received; 48 | let count = 0; 49 | 50 | forIn(received, function (value, key, origin) { 51 | count++; 52 | expect(value).toBe(expected[key as keyof typeof expected]); 53 | expect(this).toBe(origin); 54 | 55 | if (key === 'continue') { 56 | return false; 57 | } 58 | }); 59 | 60 | expect(count).toBe(3); 61 | }); 62 | 63 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/dropRightWhile.test.ts: -------------------------------------------------------------------------------- 1 | import { dropRightWhile } from "../../utility/array/dropRightWhile"; 2 | 3 | describe('dropRightWhile tests...', () => { 4 | 5 | test('dropRightWhile should return an empty array when receve an empty array', () => { 6 | const received: number[] = []; 7 | const expected = 0; 8 | 9 | const result = dropRightWhile(received, function (v) { 10 | return !!v; 11 | }); 12 | 13 | expect(result.length).toBe(expected); 14 | }); 15 | 16 | test('dropRightWhile should return the filtered array when receive an array being composed of number', () => { 17 | const received: number[] = [23, 34, -1, -5, 54, 22, 0]; 18 | const expected: number[] = [23, 34]; 19 | 20 | const result = dropRightWhile(received, function (v) { 21 | return v < 0; 22 | }) 23 | 24 | for (const [i, v] of result.entries()) { 25 | expect(v).toBe(expected[i]); 26 | } 27 | }); 28 | 29 | test('dropRightWhile should return the filtered array when receive an array being composed of object', () => { 30 | interface IReceivedParams { 31 | name: string; 32 | age: number; 33 | }; 34 | const received: IReceivedParams[] = [ 35 | { name: 'duan', age: 20 }, 36 | { name: 'zhao', age: 30 }, 37 | { name: 'yang', age: 40 }, 38 | ]; 39 | const expected = [received[0]]; 40 | 41 | 42 | const result = dropRightWhile(received, function (v) { 43 | return v.age === 30; 44 | }); 45 | 46 | for (const [i, v] of result.entries()) { 47 | expect(v).toBe(expected[i]); 48 | } 49 | }); 50 | 51 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/difference.test.ts: -------------------------------------------------------------------------------- 1 | import { difference } from "../../utility/array/difference"; 2 | 3 | describe('difference tests', () => { 4 | test('difference should receive an array being composed of number and return the processed array', () => { 5 | const received = { 6 | origin: [1, 2, 3, 4, 5], 7 | filter: [1, 4], 8 | }; 9 | const expected = [2, 3, 5]; 10 | 11 | const result = difference(received.origin, ...received.filter); 12 | 13 | for (const [i, v] of result.entries()) { 14 | expect(v).toBe(expected[i]); 15 | } 16 | }); 17 | 18 | test('difference should return a new array', () => { 19 | const received = { 20 | origin: [100, 200, 300, 300, 500], 21 | filter: [300], 22 | }; 23 | const expected = [100, 200, 500]; 24 | 25 | const result = difference(received.origin, ...received.filter); 26 | 27 | for (const [i, v] of result.entries()) { 28 | expect(v).toBe(expected[i]); 29 | } 30 | 31 | received.origin.push(800); 32 | 33 | for (const [i, v] of result.entries()) { 34 | expect(v).toBe(expected[i]); 35 | } 36 | }); 37 | 38 | test('difference should receive an array being composed of any value and return a new array', () => { 39 | const received = { 40 | origin: [null, undefined, NaN, '', 0, false, function () { }, {}, []], 41 | filter: [function () { }, [], null], 42 | }; 43 | const expected = [undefined, NaN, '', 0, false, function () { }, {}, []]; 44 | 45 | const result = difference(received.origin, ...received.filter); 46 | 47 | expect(result.length).toBe(expected.length); 48 | }); 49 | }); -------------------------------------------------------------------------------- /src/ddzy/canvas/stars-line/line/Line.ts: -------------------------------------------------------------------------------- 1 | export interface ILineProps { 2 | startPoint: { 3 | x: number, 4 | y: number, 5 | }; 6 | endPoint: { 7 | x: number, 8 | y: number, 9 | }; 10 | lineColor: string; 11 | lineWidth: number; 12 | pen: CanvasRenderingContext2D; 13 | }; 14 | 15 | 16 | export class Line { 17 | 18 | private readonly startPoint: { 19 | x: number, 20 | y: number, 21 | } 22 | private readonly endPoint: { 23 | x: number, 24 | y: number, 25 | } 26 | private readonly lineColor: string; 27 | private readonly lineWidth: number ; 28 | private readonly pen: CanvasRenderingContext2D; 29 | 30 | public constructor( 31 | props: ILineProps, 32 | ) { 33 | const { 34 | startPoint, 35 | endPoint, 36 | lineColor, 37 | lineWidth, 38 | pen, 39 | } = props; 40 | 41 | this.startPoint = startPoint; 42 | this.endPoint = endPoint; 43 | this.lineColor = lineColor 44 | this.lineWidth = lineWidth; 45 | this.pen = pen; 46 | 47 | this.draw(); 48 | } 49 | 50 | public draw(): void { 51 | const { 52 | pen, 53 | startPoint, 54 | endPoint, 55 | lineColor, 56 | lineWidth, 57 | } = this; 58 | 59 | pen.save(); 60 | pen.beginPath(); 61 | pen.moveTo( 62 | startPoint.x, 63 | startPoint.y, 64 | ); 65 | pen.lineTo( 66 | endPoint.x, 67 | endPoint.y, 68 | ); 69 | pen.lineCap = 'round'; 70 | pen.lineWidth = lineWidth; 71 | pen.strokeStyle = '#1890ff'; 72 | pen.strokeStyle = lineColor; 73 | pen.stroke(); 74 | pen.closePath(); 75 | pen.restore(); 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/function/_new.test.ts: -------------------------------------------------------------------------------- 1 | import { _new } from "../../utility/function/_new"; 2 | 3 | describe('_new', () => { 4 | test('_new should return the instance of target constructor and received non parameter', () => { 5 | function Person() { } 6 | Person.prototype = { 7 | constructor: Person, 8 | say() { 9 | return 'Person'; 10 | }, 11 | }; 12 | 13 | const received = [ 14 | Person, 15 | ]; 16 | const expected = [ 17 | { 18 | constructor: Person, 19 | say: 'Person', 20 | }, 21 | ]; 22 | 23 | for (const [i, v] of received.entries()) { 24 | const result: any = _new(v); 25 | 26 | expect(result.constructor).toBe(expected[i]['constructor']); 27 | expect(result.say()).toBe(expected[i]['say']); 28 | } 29 | }); 30 | 31 | test('_new should return the instance of target constructor and received many of parameters', () => { 32 | function Person(name: string, age: number) { 33 | this.name = name; 34 | this.age = age; 35 | } 36 | Person.prototype = { 37 | constructor: Person, 38 | say() { 39 | return 'Person'; 40 | }, 41 | }; 42 | 43 | const received = [ 44 | Person, 45 | ]; 46 | const expected = [ 47 | { 48 | name: 'ddzy', 49 | age: 21, 50 | constructor: Person, 51 | say: 'Person', 52 | }, 53 | ]; 54 | 55 | for (const [i, v] of received.entries()) { 56 | const result: any = _new(v, 'ddzy', 21); 57 | 58 | expect(result.name).toBe(expected[i]['name']); 59 | expect(result.age).toBe(expected[i]['age']); 60 | expect(result.constructor).toBe(expected[i]['constructor']); 61 | expect(result.say()).toBe(expected[i]['say']); 62 | } 63 | }); 64 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/algorithm/miniRedux.test.ts: -------------------------------------------------------------------------------- 1 | import createStore from '../../utility/algorithm/mini-redux/index'; 2 | 3 | describe('MiniRedux', () => { 4 | interface IStaticState { 5 | count: number; 6 | }; 7 | interface IStaticAction { 8 | type: string; 9 | }; 10 | 11 | const initialState: IStaticState = { 12 | count: 0, 13 | }; 14 | const reducer = (state: IStaticState, action: IStaticAction) => { 15 | switch (action.type) { 16 | case 'INCREMENT': { 17 | return { 18 | ...state, 19 | count: ++state.count, 20 | }; 21 | }; 22 | case 'DECREMENT': { 23 | return { 24 | ...state, 25 | count: --state.count, 26 | }; 27 | }; 28 | default: { 29 | return state 30 | }; 31 | } 32 | } 33 | const store = createStore(reducer, initialState); 34 | 35 | test('The method `store.getState()` should work fine', () => { 36 | const state = store.getState(); 37 | 38 | expect(state.count).toBe(0); 39 | }); 40 | test('The method `store.dispatch()` should work fine`', () => { 41 | store.dispatch({ 42 | type: 'INCREMENT', 43 | }); 44 | store.dispatch({ 45 | type: 'INCREMENT', 46 | }); 47 | store.dispatch({ 48 | type: 'INCREMENT', 49 | }); 50 | store.dispatch({ 51 | type: 'DECREMENT', 52 | }); 53 | 54 | expect(store.getState().count).toBe(2); 55 | }); 56 | test('The method `store.subscribe()` should work fine', () => { 57 | let index = 0; 58 | 59 | store.subscribe(() => { 60 | ++index; 61 | }); 62 | store.dispatch({ 63 | type: 'DECREMENT', 64 | }); 65 | store.dispatch({ 66 | type: 'DECREMENT', 67 | }); 68 | 69 | expect(index).toBe(2); 70 | expect(store.getState().count).toBe(0); 71 | }); 72 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/algorithm/eventEmitter.test.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "../../utility/algorithm/event-emitter"; 2 | 3 | // ? EventEmitter 4 | describe('EventEmitter tests', () => { 5 | test("EventEmitter.handleOn should put handlers into events and return the handler's own index", () => { 6 | const event = new EventEmitter({}); 7 | const fun1 = function () { 8 | console.log('fun1'); 9 | } 10 | const fun2 = function () { 11 | console.log('fun2'); 12 | } 13 | const received = [ 14 | { 15 | type: 'click', 16 | handler: fun1, 17 | }, 18 | { 19 | type: 'click', 20 | handler: fun2, 21 | } 22 | ]; 23 | const expected = [ 24 | { 25 | type: 'click', 26 | index: 0, 27 | }, 28 | { 29 | type: 'click', 30 | index: 1, 31 | } 32 | ]; 33 | 34 | received.forEach(function (v, i) { 35 | const result = event.handleOn(v.type, v.handler); 36 | expect(result).toEqual(expected[i]); 37 | }); 38 | }); 39 | 40 | test('EventEmitter.handleRemove should remove the `dest handler` from events', () => { 41 | const event = new EventEmitter({}); 42 | const fun1 = function () { 43 | console.log('fun1'); 44 | } 45 | const fun2 = function () { 46 | console.log('fun2'); 47 | } 48 | 49 | const r1 = event.handleOn('click', fun1); 50 | const r2 = event.handleOn('click', fun2); 51 | 52 | event.handleRemove(r2); 53 | 54 | let result: boolean = false; 55 | const events = event.events; 56 | events['click'].forEach(function (v, i) { 57 | if (i === r2.index) { 58 | if (!v) { 59 | result = true; 60 | } 61 | } 62 | }); 63 | 64 | expect(result).toBeTruthy(); 65 | }); 66 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/string/getRandomStr.test.ts: -------------------------------------------------------------------------------- 1 | import { getRandomStr } from "../../utility/string/getRandomStr"; 2 | 3 | describe('getRandomStr', () => { 4 | test('getRandomStr should return default string when not received parameters', () => { 5 | const expected: [string, number] = [ 6 | 'string', 7 | 16, 8 | ]; 9 | const result: string = getRandomStr(); 10 | 11 | expect(typeof result).toBe(expected[0]); 12 | expect(result.length).toBe(expected[1]); 13 | }); 14 | 15 | test('getRandomStr should return default string when received a empty array', () => { 16 | const received: string[] = []; 17 | const expected: [string, number] = [ 18 | 'string', 19 | 16, 20 | ]; 21 | const result: string = getRandomStr(received); 22 | 23 | expect(typeof result).toBe(expected[0]); 24 | expect(result.length).toBe(expected[1]); 25 | }); 26 | 27 | test('getRandomStr should return assignment length of string when received the length parameter', () => { 28 | const received: [string[], number] = [ 29 | ['a', 'b', 'c', '1', '2', '3', '_', '-'], 30 | 8, 31 | ]; 32 | const expected: [string, number] = [ 33 | 'string', 34 | 8, 35 | ]; 36 | const result: string = getRandomStr(...received); 37 | 38 | expect(typeof result).toBe(expected[0]); 39 | expect(result.length).toBe(expected[1]); 40 | }); 41 | 42 | test('getRandomStr should return a string that not contains special characters when received the truthy parameter names `enableSpecialCharacter`', () => { 43 | const received: [ 44 | undefined, 45 | undefined, 46 | boolean, 47 | ] = [undefined, undefined, false]; 48 | const expected: RegExp = /[_\-&$@^]+/g; 49 | const result: string = getRandomStr(...received); 50 | 51 | expect(expected.test(result)).toBeFalsy(); 52 | }); 53 | }); -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | The format is based on [Keep a Changelog](http://keepachangelog.com/). 4 | 5 | ## [2018-9-30] 6 | 7 | ### Added 8 | 9 | - Initial release 10 | 11 | ## [2019-4-25] 12 | 13 | ### Added 14 | 15 | - 新增`tslint.json` 16 | - 新增`webpack`开发环境 17 | 18 | ### Changed 19 | 20 | - 更新`README` 21 | 22 | ## [2019-4-27] 23 | 24 | ### Added 25 | 26 | - 配置完成基本的`webpack + ts`开发环境 27 | 28 | ## [2019-4-30] 29 | 30 | ### Added 31 | 32 | - 增加`bucket`, 对应github用户名, 方便贡献代码 33 | 34 | ### Changed 35 | 36 | - 重构整个项目目录, 移至对应`bucket`下方 37 | 38 | ## [2019-5-2] 39 | 40 | ### Added 41 | 42 | - 初步构建`dist` 43 | - 添加`script`引入的支持 44 | 45 | ## [2019-5-9] 46 | 47 | ### Added 48 | 49 | - 增加`babel`构建 50 | 51 | ### Removed 52 | 53 | - 移除初始的`ts-loader`构建方式 54 | 55 | ## [2019-5-10] 56 | 57 | ### Added 58 | 59 | - 集成`jest`单测环境 60 | 61 | ## [2019-5-27] 62 | 63 | ### Marked 64 | 65 | - 项目被[@HelloGithub](https://github.com/521xueweihan/HelloGitHub/issues/595)团队收录 66 | 67 | ## [2019-6-1] 68 | 69 | ### Added 70 | 71 | - 引入`jest`测试覆盖率 72 | 73 | ## [2019-6-3] 74 | 75 | ### Changed 76 | 77 | - 重构`package.json` 78 | - 更改了`repository`、`author`、`bugs`、`license`等一系列配置项 79 | 80 | ## [2019-6-11] 81 | 82 | ### Added 83 | 84 | - `贡献者列表`下新增`Docs`条目, 在线文档地址 85 | 86 | ## [2019-7-4] 87 | 88 | ### Added 89 | 90 | - 新的贡献者: [@qiqingfu](https://github.com/qiqingfu) 91 | 92 | ## [2019-7-8] 93 | 94 | ### Added 95 | 96 | - 项目`README`增加 97 | - logo 98 | - `npm`徽章 99 | 100 | ## [2019-8-19] 101 | 102 | ### Marked 103 | 104 | - 发布[v1.0](https://github.com/ddzy/ts-utility-plugins/tree/v1.0), 重构之前的最后一个版本 105 | 106 | ## [2019-12-20] 107 | 108 | ### Marked 109 | 110 | - 发布[v1.1](https://github.com/ddzy/ts-utility-plugins/tree/v1.1) 111 | 112 | ### Changed 113 | 114 | - 更改 [2019-8-19](#2019-8-19) 的标签链接 -------------------------------------------------------------------------------- /src/ddzy/utility/object/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility/object 2 | 3 | ## 说明 4 | 5 | 汇集有关`对象`的工具方法. 6 | 7 | ## 目录 8 | 9 | | Name | Description | Source | Docs | 10 | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------- | 11 | | isPlainObject | 判断是否普通的`键值对`对象 | [源码](./isPlainObject/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-object/isplainobject) | 12 | | get | 根据`object`对象的`path`路径获取值 | [源码](./get/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-object/get) | 13 | | forIn | 使用 `iteratee` 遍历对象的自身和继承的可枚举属性. `iteratee` 会传入3个参数: (value, key, object). 如果返回 `false`, iteratee 会提前退出遍历. | [源码](./forIn/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-object/forin) | 14 | | forOwn | 遍历对象自身的可枚举属性, 不包括继承而来的属性 | [源码](./forOwn/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-object/forown) | 15 | | create | 模拟实现 `Object.create()` | [源码](./create/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-object/create) | -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility/algorithm 2 | 3 | ## 说明 4 | 5 | 汇集有关`算法`、`数据结构`、`模拟原生API`的工具插件. 6 | 7 | ## 目录 8 | 9 | | Name | Description | Source | Docs | 10 | | -------------------------------- | ------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------- | 11 | | BST(`BinarySearchTree`) | 二叉搜索树 | [源码](./binary-search-tree/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-algorithm/bst) | 12 | | DLCL(`DoubleLinkedCircularList`) | 双向循环链表 | [源码](./double-linked-circular-list/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-algorithm/dlcl) | 13 | | EventEmitter | 模拟实现简单的`EventEmitter` | [源码](./event-emitter/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-algorithm/eventemitter) | 14 | | URLSearchParams | 模拟实现简单的`URLSearchParams` | [源码](./url-search-params/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-algorithm/urlsearchparams) | 15 | | ES6Achieve | 旨在模拟实现`ES6`的API | [源码](./es6-achieve/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-algorithm/es6achieve) | 16 | | MiniRedux | 模拟`redux`核心实现 | [源码](./mini-redux/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-algorithm/miniredux) | 17 | | Sort | 经典排序算法实现 | [源码](./sort/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-algorithm/sort) | -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/sameValueZero.test.ts: -------------------------------------------------------------------------------- 1 | import sameValueZero from "../../utility/others/sameValueZero"; 2 | 3 | 4 | describe('sameValueZero tests()...', () => { 5 | test('sameValueZero should compare the value that passed', () => { 6 | let a = Symbol('a'); 7 | let b = Symbol('b'); 8 | let c = [1, 2, 3]; 9 | let d = { a, b, c }; 10 | 11 | const received = [ 12 | { 13 | a: 10, 14 | b: 'duan', 15 | }, 16 | { 17 | a: undefined, 18 | b: undefined, 19 | }, 20 | { 21 | a: null, 22 | b: null, 23 | }, 24 | { 25 | a: NaN, 26 | b: NaN, 27 | }, 28 | { 29 | a: +0, 30 | b: -0, 31 | }, 32 | { 33 | a: -0, 34 | b: +0, 35 | }, 36 | { 37 | a: 999, 38 | b: 999, 39 | }, 40 | { 41 | a: 99999, 42 | b: 11111, 43 | }, 44 | { 45 | a: 'duan', 46 | b: 'duan', 47 | }, 48 | { 49 | a: 'duan', 50 | b: 'duanzhaoyang', 51 | }, 52 | { 53 | a: true, 54 | b: true, 55 | }, 56 | { 57 | a: true, 58 | b: false, 59 | }, 60 | { 61 | a: a, 62 | b: b, 63 | }, 64 | { 65 | a: a, 66 | b: a, 67 | }, 68 | { 69 | a: c, 70 | b: d, 71 | }, 72 | { 73 | a: c, 74 | b: c, 75 | }, 76 | ]; 77 | const expected = [ 78 | false, 79 | true, 80 | true, 81 | true, 82 | true, 83 | true, 84 | true, 85 | false, 86 | true, 87 | false, 88 | true, 89 | false, 90 | false, 91 | true, 92 | false, 93 | true 94 | ]; 95 | const result = received.map((v) => { 96 | return sameValueZero(v.a, v.b); 97 | }); 98 | 99 | result.forEach((v, i) => { 100 | expect(v).toBe(expected[i]); 101 | }); 102 | }); 103 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/object/create.test.ts: -------------------------------------------------------------------------------- 1 | import create from "../../utility/object/create"; 2 | 3 | export interface IStaticObject { 4 | [key: string]: any; 5 | }; 6 | describe('create tests...', () => { 7 | test('The method `create()` should return the correct value while receiving a plain object', () => { 8 | const received: IStaticObject[] = [ 9 | { 10 | prototype: { 11 | name: 'duanzhaoyang', 12 | age: 22, 13 | }, 14 | props: {}, 15 | }, 16 | { 17 | prototype: {}, 18 | props: { 19 | skill: 'running', 20 | }, 21 | }, 22 | ]; 23 | const expected: IStaticObject[] = [ 24 | {}, 25 | { 26 | skill: 'running', 27 | }, 28 | ]; 29 | 30 | received.forEach((v, i) => { 31 | const result = create(v.prototype, v.props); 32 | 33 | for (const key in result) { 34 | if (Object.prototype.hasOwnProperty.call(result, key)) { 35 | const value = result[key]; 36 | 37 | expect(expected[i].hasOwnProperty(key)).toBeTruthy(); 38 | expect(expected[i][key]).toBe(value); 39 | } 40 | } 41 | }); 42 | }); 43 | test('The method `create()` should return the correct value while receiving `null`', () => { 44 | const received: IStaticObject[] = [ 45 | { 46 | prototype: null, 47 | props: {}, 48 | }, 49 | { 50 | prototype: null, 51 | props: { 52 | a: 1, 53 | b: 2, 54 | }, 55 | }, 56 | ]; 57 | const expected: IStaticObject[] = [ 58 | {}, 59 | { 60 | a: 1, 61 | b: 2, 62 | }, 63 | ]; 64 | 65 | received.forEach((v, i) => { 66 | const result = create(v.prototype, v.props); 67 | 68 | for (const key in result) { 69 | if (Object.prototype.hasOwnProperty.call(result, key)) { 70 | const value = result[key]; 71 | 72 | expect(expected[i].hasOwnProperty(key)).toBeTruthy(); 73 | expect(expected[i][key]).toBe(value); 74 | } 75 | } 76 | }); 77 | }); 78 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/es6-achieve/promise/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Promise 的几大标准 3 | * 1. 一个 Promise 对象有三种状态(pending: 默认状态、fulfilled: 成功状态、rejected: 失败状态), 状态的改变只能从 pending -> fulfilled 或 pending -> rejected, 并且是不可逆的 4 | * 2. promise.then 方法接收两个参数(onFulfilled、onRejected), 分别当状态变为(fulfilled、rejected)时执行 5 | * 3. promise.then 方法返回一个新的 Promise 实例, 借此来实现链式调用 6 | */ 7 | export type IPromiseCallback = ( 8 | onFulfilled: IFulfilledCallback, 9 | onRejected: (value: IPromiseValue) => void, 10 | ) => void; 11 | export type IFulfilledCallback = (value: IPromiseValue) => void; 12 | export type IResolveCallback = (value: IPromiseValue) => void; 13 | export interface IFulfilledParams { 14 | currentFulfilled: IFulfilledCallback; 15 | nextResolve: IResolveCallback; 16 | }; 17 | export type IPromiseValue = any; 18 | 19 | const STATUS = { 20 | pending: 'pending', 21 | fulfilled: 'fulfilled', 22 | rejected: 'rejected', 23 | }; 24 | 25 | export default class _Promise { 26 | public status: string = STATUS.pending; 27 | public value: IPromiseValue = null; 28 | public error: any = null; 29 | public fulfilledList: IFulfilledParams[] = []; 30 | 31 | constructor(callback: IPromiseCallback) { 32 | callback(this.resolve.bind(this), this.reject.bind(this)); 33 | } 34 | 35 | public resolve(value: IPromiseValue) { 36 | if (this.status === STATUS.pending) { 37 | this.status = STATUS.fulfilled; 38 | this.value = value; 39 | this.fulfilledList.forEach((callback) => { 40 | callback.nextResolve(callback.currentFulfilled(this.value)); 41 | }); 42 | } 43 | } 44 | 45 | public reject() { 46 | // TODO 47 | } 48 | 49 | public then(onFulfilled: IFulfilledCallback) { 50 | return new _Promise((nextResolve) => { 51 | // 如果当前异步操作已经执行完毕 52 | if (this.status === STATUS.fulfilled) { 53 | nextResolve(onFulfilled(this.value)); 54 | } 55 | // 反之, 加入到回调队列 56 | else if (this.status === STATUS.pending) { 57 | this.fulfilledList.push({ 58 | currentFulfilled: onFulfilled, 59 | nextResolve, 60 | }); 61 | } 62 | }); 63 | } 64 | } -------------------------------------------------------------------------------- /src/ddzy/utility/function/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility/function 2 | 3 | ## 说明 4 | 5 | 汇集有关`函数`的工具方法. 6 | 7 | ## 目录 8 | 9 | | Name | Description | Source | Docs | 10 | | ------------- | ------------------------------------------------------ | -------------------------------- | ---------------------------------------------------------------------------------------------- | 11 | | isFunction | 检查是否`函数` | [源码](./isFunction/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/isfunction) | 12 | | _call | 模拟实现`call`方法 | [源码](./_call/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/_call) | 13 | | _bind | 模拟实现`bind`方法 | [源码](./_bind/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/_bind) | 14 | | _new | 模拟实现`new`操作符 | [源码](./_new/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/_new) | 15 | | getParamNames | 获取函数的`形参`名称数组 | [源码](./getParamNames/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/getparamnames) | 16 | | compose | 从右往左执行处理器函数 | [源码](./compose/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/compose) | 17 | | pipe | 从左往右执行处理器函数, 与`compose`相反 | [源码](./pipe/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/pipe) | 18 | | delay | 延迟`wait`毫秒后执行处理器`callback` | [源码](./delay/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/delay) | 19 | | after | 创建并返回一个函数, 等待函数运行指定`次数`后执行处理器 | [源码](./after/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/after) | 20 | | curry | 函数柯里化 | [源码](./curry/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/utility-function/curry) | -------------------------------------------------------------------------------- /src/ddzy/__tests__/other/listToTree.test.ts: -------------------------------------------------------------------------------- 1 | import listToTree from "../../utility/others/listToTree"; 2 | 3 | describe('listToTree tests()...', () => { 4 | test('listToTree should transform a list to a tree', () => { 5 | const received = [ 6 | { 7 | id: 2, 8 | value: 2, 9 | parent: 1, 10 | }, 11 | { 12 | id: 3, 13 | value: 3, 14 | parent: 0, 15 | }, 16 | { 17 | id: 4, 18 | value: 4, 19 | parent: 3, 20 | }, 21 | { 22 | id: 5, 23 | value: 5, 24 | parent: 3, 25 | }, 26 | { 27 | id: 1, 28 | value: 1, 29 | parent: 0, 30 | }, 31 | { 32 | id: 6, 33 | value: 6, 34 | parent: 4, 35 | }, 36 | { 37 | id: 7, 38 | value: 7, 39 | parent: 4, 40 | }, 41 | { 42 | id: 8, 43 | value: 8, 44 | parent: 7, 45 | }, 46 | ]; 47 | const expected = [ 48 | { 49 | "id": 3, 50 | "value": 3, 51 | "parent": 0, 52 | "children": [ 53 | { 54 | "id": 4, 55 | "value": 4, 56 | "parent": 3, 57 | "children": [ 58 | { 59 | "id": 6, 60 | "value": 6, 61 | "parent": 4 62 | }, 63 | { 64 | "id": 7, 65 | "value": 7, 66 | "parent": 4, 67 | "children": [ 68 | { 69 | "id": 8, 70 | "value": 8, 71 | "parent": 7 72 | } 73 | ] 74 | } 75 | ] 76 | }, 77 | { 78 | "id": 5, 79 | "value": 5, 80 | "parent": 3 81 | } 82 | ] 83 | }, 84 | { 85 | "id": 1, 86 | "value": 1, 87 | "parent": 0, 88 | "children": [ 89 | { 90 | "id": 2, 91 | "value": 2, 92 | "parent": 1 93 | } 94 | ] 95 | } 96 | ]; 97 | 98 | const result = listToTree(received); 99 | 100 | expect(result).toStrictEqual(expected); 101 | }); 102 | }); -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/trunk.test.ts: -------------------------------------------------------------------------------- 1 | import { trunk } from "../../utility/array/trunk"; 2 | 3 | describe('trunk', () => { 4 | test('trunk should return an empty array when receive an empty array', () => { 5 | const received: number[] = []; 6 | const expected = 0; 7 | 8 | const result = trunk(received); 9 | 10 | expect(result.length).toBe(expected); 11 | }); 12 | 13 | test('trunk should return the divided array by default size', () => { 14 | const received: number[] = [1, 2, 3, 4, 5, 6, 7]; 15 | const expected = { 16 | length: 7, 17 | value: [[1], [2], [3], [4], [5], [6], [7]], 18 | }; 19 | 20 | const result = trunk(received); 21 | 22 | for (const [i, v] of result.entries()) { 23 | for (const [ii, vv] of v.entries()) { 24 | expect(vv).toBe(expected.value[i][ii]); 25 | } 26 | expect(v.length).toBe(expected.value[i].length); 27 | } 28 | expect(result.length).toBe(expected.length); 29 | }); 30 | 31 | test('trunk should return the divided array by customized size', () => { 32 | const received: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 33 | const expected = { 34 | s2: [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], 35 | s5: [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]], 36 | s7: [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10]], 37 | s10: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], 38 | }; 39 | 40 | const p2 = trunk(received, 2); 41 | const p5 = trunk(received, 5); 42 | const p7 = trunk(received, 7); 43 | const p10 = trunk(received, 10); 44 | 45 | for (const [i, v] of p2.entries()) { 46 | for (const [ii, vv] of v.entries()) { 47 | expect(vv).toBe(expected.s2[i][ii]); 48 | } 49 | expect(v.length).toBe(expected.s2[i].length); 50 | } 51 | 52 | for (const [i, v] of p5.entries()) { 53 | for (const [ii, vv] of v.entries()) { 54 | expect(vv).toBe(expected.s5[i][ii]); 55 | } 56 | expect(v.length).toBe(expected.s5[i].length); 57 | } 58 | 59 | for (const [i, v] of p7.entries()) { 60 | for (const [ii, vv] of v.entries()) { 61 | expect(vv).toBe(expected.s7[i][ii]); 62 | } 63 | expect(v.length).toBe(expected.s7[i].length); 64 | } 65 | 66 | for (const [i, v] of p10.entries()) { 67 | for (const [ii, vv] of v.entries()) { 68 | expect(vv).toBe(expected.s10[i][ii]); 69 | } 70 | expect(v.length).toBe(expected.s10[i].length); 71 | } 72 | }); 73 | }); -------------------------------------------------------------------------------- /src/ddzy/canvas/jumping-characters/character/Character.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Character 3 | * @description 单个字符 4 | */ 5 | 6 | export interface ICharacterProps { 7 | pen: CanvasRenderingContext2D; 8 | 9 | text: string; 10 | textSize: number; 11 | textColor: string; 12 | initialOpacity: number; 13 | safeDistance: number; 14 | speed: number; 15 | 16 | mousePoint: { 17 | x: number; 18 | y: number; 19 | }; 20 | 21 | handleCancelTimer: () => void; 22 | }; 23 | 24 | 25 | export class Character { 26 | 27 | public constructor( 28 | config: ICharacterProps, 29 | ) { 30 | const { 31 | initialOpacity, 32 | textSize, 33 | textColor, 34 | text, 35 | speed, 36 | safeDistance, 37 | pen, 38 | mousePoint, 39 | handleCancelTimer, 40 | } = config; 41 | 42 | this.pen = pen; 43 | this.text = text; 44 | this.textColor = textColor; 45 | this.safeDistance = safeDistance; 46 | this.mousePoint = mousePoint; 47 | this.opacity = initialOpacity; 48 | this.textSize = textSize; 49 | this.speed = speed; 50 | this.handleCancelTimer = handleCancelTimer; 51 | 52 | this.__init__(); 53 | } 54 | 55 | private readonly handleCancelTimer: () => void; 56 | private readonly pen: CanvasRenderingContext2D; 57 | private readonly text: string; 58 | private readonly textColor: string; 59 | private readonly safeDistance: number; 60 | private readonly speed: number; 61 | private readonly mousePoint: { 62 | x: number; 63 | y: number; 64 | }; 65 | private opacity: number; 66 | private textSize: number; 67 | 68 | 69 | private __init__(): void { 70 | this.handleDraw(); 71 | } 72 | 73 | public handleDraw(): void { 74 | const { 75 | pen, 76 | text, 77 | textColor, 78 | textSize, 79 | mousePoint, 80 | } = this; 81 | 82 | if (this.opacity <= 0) { 83 | this.opacity = 0; 84 | this.handleCancelTimer(); 85 | } 86 | 87 | pen.save(); 88 | pen.beginPath(); 89 | pen.fillStyle = textColor; 90 | pen.font = `${textSize}px 'Fira Code Regular'`; 91 | pen.textAlign = 'center'; 92 | pen.textBaseline = 'middle'; 93 | pen.globalAlpha = this.opacity; 94 | pen.fillText( 95 | text, 96 | mousePoint.x, 97 | mousePoint.y, 98 | ); 99 | 100 | pen.closePath(); 101 | pen.restore(); 102 | } 103 | 104 | public handleMove(): void { 105 | const { 106 | mousePoint, 107 | } = this; 108 | 109 | mousePoint.y -= this.speed; 110 | this.opacity -= .01; 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | 7 | 8 | 9 |

10 | 11 |

12 | 13 | 14 | 15 |

16 | 17 |

18 | 19 | 20 | 21 |

22 | 23 | ## 前言 24 | 25 | 框架流行的大背景下, 是否已经缺失了基本的原生`js/ts`能力? 没关系, 本项目就是你的舞台(`注: 本项目仅供学习`). 26 | 27 | `2019/5/27`, 很荣幸本项目被[@HelloGithub](https://github.com/521xueweihan/HelloGitHub/)团队收录! 28 | 29 | 详情参见HelloGithub: [第38期月报](https://hellogithub.com/periodical/volume/38/#ts-utility-plugins). 30 | 31 | ## 说明 32 | 33 | 好玩、有趣的插件库, 使用纯原生`ts`构建, 不参杂其它框架. 目前囊括特效(`canvas`)、业务(`business`)、工具(`utility`)三个大区块, 本项目仅供参考学习~ 34 | 35 | 项目的主要受众: 36 | 37 | - 想实战`ts`的小白 38 | - 想重拾原生coding能力的框架搬运工 39 | - 想提升插件编写能力的工程师 40 | - 想互相借鉴优秀代码的`sharer` 41 | 42 | 欢迎[贡献代码](#如何贡献)! 43 | 44 | ## 用法 45 | 46 | ### Step 1 47 | 48 | 进入对应[Bucket](#贡献者列表) 49 | 50 | ### step 2 51 | 52 | 观阅`README`, 查看对应的插件介绍 53 | 54 | ### Step 3 55 | 56 | > 重构代码中, 部分导入功能暂未实现 57 | 58 | 于[项目dist文件夹](https://github.com/ddzy/ts-utility-plugins/dist)下找到同名`Bucket`的对应插件. 在项目中引入即可: 59 | 60 | - [x] `script`导入 61 | - [ ] `CDN`引入 62 | - [ ] `NPM`包 63 | - [ ] `React`组件 64 | - [ ] `Vue`组件 65 | 66 | ## 贡献者列表 67 | 68 | | Bucket | Github | Project | Docs | 69 | | -------- | --------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------ | 70 | | ddzy | [ddzy](https://github.com/ddzy) | [ts-utility-plugins/ddzy](https://github.com/ddzy/ts-utility-plugins/tree/master/src/ddzy) | [文档地址](https://ddzy.gitbook.io/ts-utility-plugins-docs/) | 71 | | qiqingfu | [qiqingfu](https://github.com/qiqingfu) | [ts-utility-plugins/qiqf](https://github.com/ddzy/ts-utility-plugins/tree/master/src/qiqf) | * | 72 | 73 | ## 如何贡献? 74 | 75 | 关于如何贡献, 已移至[Wiki](https://github.com/ddzy/ts-utility-plugins/wiki/%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE%3F). 76 | 77 | ## CHANGELOG 78 | 79 | @see: [CHANGELOG](./CHANGELOG.md) 80 | 81 | ## License 82 | 83 | @see: [MIT](./LICENSE) 84 | 85 | ## Others 86 | 87 | 持续更新中... 88 | 89 | **Enjoy!** -------------------------------------------------------------------------------- /src/ddzy/__tests__/array/pullAll.test.ts: -------------------------------------------------------------------------------- 1 | import { pullAll } from "../../utility/array/pullAll"; 2 | 3 | 4 | describe('pullAll tests...', () => { 5 | test('Method pullAll should receive an array composed of number and return the filtered array', () => { 6 | const received = { 7 | origin: [1, 2, 3, 1, 2, 3], 8 | selector: [2, 3], 9 | }; 10 | const expected = { 11 | isEqual: true, 12 | result: [1, 1], 13 | }; 14 | 15 | const result = pullAll(received.origin, received.selector); 16 | 17 | expect(result === received.origin).toBe(expected.isEqual); 18 | 19 | result.forEach((v, i) => { 20 | expect(v).toBe(expected.result[i]); 21 | }); 22 | }); 23 | 24 | test('Method pullAll should receive an array composed of plain object and return the filtered array', () => { 25 | interface IOriginParams { 26 | name: string, 27 | age: number, 28 | }; 29 | 30 | const received = { 31 | origin: [ 32 | { 33 | name: 'duan', 34 | age: 21, 35 | }, 36 | { 37 | name: 'zhao', 38 | age: 22, 39 | }, 40 | { 41 | name: 'duan', 42 | age: 21, 43 | }, 44 | ], 45 | selector: [ 46 | { 47 | name: 'duan', 48 | age: 21, 49 | }, 50 | ], 51 | }; 52 | const expected = { 53 | isEqual: true, 54 | result: [ 55 | { 56 | name: 'duan', 57 | age: 21, 58 | }, 59 | { 60 | name: 'zhao', 61 | age: 22, 62 | }, 63 | { 64 | name: 'duan', 65 | age: 21, 66 | }, 67 | ], 68 | }; 69 | 70 | const result = pullAll(received.origin, received.selector); 71 | 72 | expect(result === received.origin).toBe(expected.isEqual); 73 | 74 | expect(result.length).toBe(expected.result.length); 75 | }); 76 | 77 | test('Method pullAll should receive an array composed of mixed value and return the filtered array', () => { 78 | const received = { 79 | origin: [ 80 | 0, 81 | 19980808, 82 | 'duanzhaoyang', 83 | false, 84 | null, 85 | undefined, 86 | Symbol('a'), 87 | function () { }, 88 | {}, 89 | [], 90 | ], 91 | selector: [false, undefined, 19980808, []], 92 | }; 93 | const expected = { 94 | isEqual: true, 95 | result: [ 96 | 0, 97 | 'duanzhaoyang', 98 | null, 99 | Symbol('a'), 100 | function () { }, 101 | {}, 102 | [], 103 | ], 104 | }; 105 | 106 | const result = pullAll(received.origin, received.selector); 107 | 108 | expect(result === received.origin).toBe(expected.isEqual); 109 | 110 | expect(result.length).toBe(expected.result.length); 111 | }); 112 | }); -------------------------------------------------------------------------------- /src/ddzy/utility/algorithm/event-emitter/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name event-emitter 3 | * @description 模拟实现EventEmitter 4 | * @author ddzy 5 | * @since 2019/6/14 6 | */ 7 | 8 | /** 9 | * @param on 放置事件 10 | * @param emit 发射事件 11 | * @param remove 移除事件 12 | */ 13 | 14 | export interface IEventEmitterProps { 15 | 16 | }; 17 | export interface IEventEmitterState { 18 | events: EventParams; 19 | }; 20 | export type EventParams = Record; 21 | export type EventHandler = ((...args: any[]) => void) | null; 22 | 23 | 24 | export class EventEmitter { 25 | public static readonly defaultProps: IEventEmitterProps = {}; 26 | 27 | public constructor( 28 | props: IEventEmitterProps, 29 | ) { 30 | this.__init__(props); 31 | } 32 | 33 | private readonly state: IEventEmitterState = { 34 | events: {}, 35 | }; 36 | 37 | private __init__( 38 | props: IEventEmitterProps, 39 | ): void { 40 | this._initProps(props); 41 | } 42 | 43 | private _initProps( 44 | props: IEventEmitterProps, 45 | ): void { 46 | const { 47 | defaultProps, 48 | } = EventEmitter; 49 | 50 | for (const key in props) { 51 | const value = props[ 52 | key as keyof typeof EventEmitter.defaultProps 53 | ]; 54 | defaultProps[ 55 | key as keyof typeof EventEmitter.defaultProps 56 | ] = value; 57 | } 58 | } 59 | 60 | public get events(): EventParams { 61 | return this.state.events; 62 | } 63 | 64 | /** 65 | * 放置事件 66 | * @param type 事件类型 67 | * @param handler 处理函数 68 | */ 69 | public handleOn( 70 | type: string, 71 | handler: EventHandler, 72 | ): { 73 | type: string, 74 | index: number, 75 | } { 76 | const { 77 | events, 78 | } = this.state; 79 | let index: number = 0; 80 | 81 | if (!events[type]) { 82 | events[type] = [handler]; 83 | } 84 | else { 85 | events[type].push(handler); 86 | index = events[type].findIndex((v) => { 87 | return v === handler; 88 | }); 89 | } 90 | 91 | return { 92 | type, 93 | index, 94 | }; 95 | } 96 | 97 | /** 98 | * 发射事件 99 | * @param type 发射的事件类型 100 | */ 101 | public handleEmit( 102 | type: string 103 | ): EventEmitter { 104 | const { events } = this.state; 105 | 106 | if (events[type]) { 107 | events[type].forEach(function (handler) { 108 | handler && handler.call(function(this: any) { return this }); 109 | }); 110 | } 111 | 112 | return this; 113 | } 114 | 115 | /** 116 | * 移除某type的某个handler 117 | * @param options 移除的配置项 118 | */ 119 | public handleRemove( 120 | options: { 121 | type: string, 122 | index: number, 123 | }, 124 | ): EventEmitter { 125 | const { events } = this.state; 126 | const { type, index } = options; 127 | 128 | if (events[type]) { 129 | events[type].splice(index, 1, null); 130 | } 131 | 132 | return this; 133 | } 134 | } -------------------------------------------------------------------------------- /src/qiqf/utility/array/index.ts: -------------------------------------------------------------------------------- 1 | import * as InterfaceUtil from './IArrayInterface' 2 | 3 | export function arrayMax(args: InterfaceUtil.IArraySize):number { 4 | return Math.max.apply(null, args) 5 | } 6 | 7 | export function arrayMin(args: InterfaceUtil.IArraySize): number { 8 | return Math.min.apply(null, args); 9 | } 10 | 11 | /** 12 | * 将数组划分为指定大小的数组 13 | * @param args 14 | * @param size 15 | */ 16 | export function arrayChunk(args: InterfaceUtil.IArrayAny, size: number): InterfaceUtil.IArrayAny { 17 | return Array.from({length: Math.ceil(args.length / size)}, (_v: any, index: number) => { 18 | return args.slice(index * size, index * size + size); 19 | }) 20 | } 21 | 22 | /** 23 | * 从数组中移除 [false, null, '', 0, undefined] 值 24 | * @param args 25 | */ 26 | export function compact(args: InterfaceUtil.IArrayAny): InterfaceUtil.IArrayAny { 27 | return args.filter(Boolean) 28 | } 29 | 30 | /** 31 | * 计算数组中值出现的次数 32 | */ 33 | const INITIAL_NUMBER = 0 34 | export function countOccurrences(args: InterfaceUtil.IArrayAny, value: number) { 35 | return args.reduce((count, arg) => { 36 | return arg === value ? count + 1 : count 37 | }, INITIAL_NUMBER) 38 | } 39 | 40 | /** 41 | * 扁平化数组 42 | * @param args 43 | */ 44 | export function deepFlatten(args: InterfaceUtil.IArrayAny): InterfaceUtil.IArrayAny[] { 45 | return [].concat(...args.map(arg => Array.isArray(arg) ? deepFlatten(arg) : arg)) 46 | } 47 | 48 | /** 49 | * 返回两个数组之间的差异部分组成的数组 50 | * @param arg1 51 | * @param arg2 52 | */ 53 | export function difference(arg1: InterfaceUtil.IArrayAny, arg2: InterfaceUtil.IArrayAny): InterfaceUtil.IArrayAny[] { 54 | return arg2.filter(v2 => !arg1.includes(v2)) 55 | } 56 | 57 | /** 58 | * 返回两个数组中相同的部分组成的数组 59 | * @param arg1 60 | * @param arg2 61 | */ 62 | export function sameArray(arg1: InterfaceUtil.IArrayAny, arg2: InterfaceUtil.IArrayAny): InterfaceUtil.IArrayAny[] { 63 | return arg2.filter(v2 => arg1.includes(v2)) 64 | } 65 | 66 | /** 67 | * 数组去重 68 | * @param args 69 | */ 70 | export function distinctValuesOfArray(args: InterfaceUtil.IArrayAny): InterfaceUtil.IArrayAny[] { 71 | return [...new Set(args)]; 72 | } 73 | 74 | /** 75 | * 返回第一个符合条件元素的后面所有元素组成的集合 76 | * @param args 77 | * @param callback 78 | */ 79 | export function dropElements(args: InterfaceUtil.IArrayAny, callback: (arg: any) => boolean): InterfaceUtil.IArrayAny[] { 80 | while (args.length && !callback(args[0])) args.shift() 81 | 82 | return args 83 | } 84 | 85 | /** 86 | * 返回数组中每个第n个元素 87 | * @param args 88 | * @param nth 89 | */ 90 | export const everyNth: InterfaceUtil.IEveryNth = (args: number[], nth: number): number[] => { 91 | return args.filter((_arg, i) => i % nth === 0) 92 | }; 93 | 94 | /** 95 | * 筛选出数组中非唯一值 96 | * @param args 97 | */ 98 | export function filterNonUnique(args: InterfaceUtil.IArrayAny): InterfaceUtil.IArrayAny[] { 99 | return args.filter(arg => args.indexOf(arg) === args.lastIndexOf(arg)) 100 | } 101 | 102 | /** 103 | * 拼合数组 104 | * @param args 105 | */ 106 | export function flatten(args: InterfaceUtil.IArrayAny): InterfaceUtil.IArrayAny { 107 | return args.reduce((a, v) => a.concat(v) ,[]) 108 | } 109 | 110 | /** 111 | * 将数组向上拼合到指定深度。 112 | * @param args 113 | * @param depth 114 | */ 115 | export function flattenDepth(args: InterfaceUtil.IArrayAny, depth: number = 1): InterfaceUtil.IArrayAny[] { 116 | if (depth < 1) throw new Error(`The second parameter minimum value cannot be less than 1`); 117 | return depth != 1 ? 118 | args.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v) ,[]) 119 | : flatten(args) 120 | } 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/ddzy/utility/dom/README.md: -------------------------------------------------------------------------------- 1 | # ddzy/utility/dom 2 | 3 | ## 说明 4 | 5 | 汇集有关`DOM`的工具方法. 6 | 7 | ## 目录 8 | 9 | | Name | Description | Source | Docs | 10 | | ---------------------------- | -------------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------- | 11 | | getEle | 获取指定单个`DOM`元素 | [源码](./getEle/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/getele) | 12 | | getAllEle | 获取指定的所有`DOM`元素 | [源码](./getAllEle/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/getallele) | 13 | | setAttr | 设置单个`DOM`属性 | [源码](./setAttr/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/setattr) | 14 | | setCss | 设置单个`DOM`样式 | [源码](./setCss/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/setcss) | 15 | | getAttr | 获取`DOM`的特定属性值 | [源码](./getAttr/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/getattr) | 16 | | addClass | 指定`DOM`添加单个类名 | [源码](./addClass/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/addclass) | 17 | | removeClass | 移除指定`DOM`的单个类名 | [源码](./removeClass/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/removeclass) | 18 | | throttle | 节流 | [源码](./throttle/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/throttle) | 19 | | debounce | 防抖 | [源码](./debounce/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/debounce) | 20 | | isDOM | 检查是否`DOM`元素 | [源码](./isDOM/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/isdom) | 21 | | traversalDOMWithBFS | `BFS`遍历指定`DOM`节点 | [源码](./traversalDOMWithBFS/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/traversaldomwithbfs) | 22 | | traversalDOMWithDFS | `DFS`遍历指定`DOM`节点 | [源码](./traversalDOMWithDFS/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/traversaldomwithdfs) | 23 | | traversalDOMWithNodeIterator | `NodeIterator`遍历指定`DOM`节点 | [源码](./traversalDOMWithNodeIterator/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/traversaldomwithnodeiterator) | 24 | | traversalDOMWithTreeWalker | `TreeWalker`遍历指定`DOM`节点 | [源码](./traversalDOMWithTreeWalker/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/traversaldomwithtreewalker) | 25 | | convertPairToCSSText | 将给定的`CSS样式键值对`转化为`cssText`字符串 | [源码](./convertPairToCSSText/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/convertpairtocsstext) | 26 | | _querySelector | 模拟实现简单的`querySelector` | [源码](./_querySelector/index.ts) | [文档](https://ddzy.gitbook.io/ts-utility-plugins-docs/utility/untitled/_queryselector) | -------------------------------------------------------------------------------- /src/ddzy/canvas/stars-line/ball/Ball.ts: -------------------------------------------------------------------------------- 1 | import { Line } from '../line/Line'; 2 | import { getAnyRandom } from '../../../utility/number/getAnyRandom'; 3 | import { getRadian } from '../../../utility/number/getRadian'; 4 | 5 | 6 | export interface IBallProps { 7 | cvsWidth: number; 8 | cvsHeight: number; 9 | pen: CanvasRenderingContext2D; 10 | color: string; 11 | radius: number; 12 | speed: number; 13 | ballArr: Ball[]; 14 | safeDistance: number; 15 | lineColor: string; 16 | lineWidth: number; 17 | centerPoint: { 18 | x: number, 19 | y: number, 20 | }; 21 | }; 22 | 23 | 24 | export class Ball { 25 | 26 | private readonly cvsWidth: number; 27 | private readonly cvsHeight: number; 28 | private readonly pen: CanvasRenderingContext2D; 29 | private readonly color: string; 30 | private readonly radius: number; 31 | // 球移动步数 32 | private readonly speed: number; 33 | // 球中心坐标 34 | private readonly centerPoint: { 35 | x: number, 36 | y: number, 37 | } 38 | private readonly lineColor: string; 39 | private readonly lineWidth: number; 40 | // 球移动速率 41 | private readonly distance: { 42 | x: number, 43 | y: number, 44 | } 45 | private readonly ballArr: Ball[]; 46 | private readonly safeDistance: number; 47 | 48 | public constructor( 49 | config: IBallProps, 50 | ) { 51 | const { 52 | pen, 53 | color, 54 | radius, 55 | speed, 56 | cvsWidth, 57 | cvsHeight, 58 | ballArr, 59 | safeDistance, 60 | lineWidth, 61 | lineColor, 62 | centerPoint, 63 | } = config; 64 | 65 | this.pen = pen; 66 | this.color = color; 67 | this.radius = radius; 68 | this.speed = speed; 69 | this.centerPoint = centerPoint; 70 | this.lineWidth = lineWidth; 71 | this.lineColor = lineColor; 72 | this.distance = { 73 | x: getAnyRandom(-this.speed, this.speed), 74 | y: getAnyRandom(-this.speed, this.speed), 75 | }; 76 | this.cvsWidth = cvsWidth; 77 | this.cvsHeight = cvsHeight; 78 | this.ballArr = ballArr; 79 | this.safeDistance = safeDistance; 80 | } 81 | 82 | public draw(): void { 83 | const { 84 | pen, 85 | centerPoint, 86 | color, 87 | radius, 88 | } = this; 89 | 90 | pen.save(); 91 | pen.beginPath(); 92 | pen.fillStyle = color; 93 | pen.arc( 94 | centerPoint.x, 95 | centerPoint.y, 96 | radius, 97 | 0, 98 | getRadian(360) 99 | ); 100 | pen.fill(); 101 | pen.closePath(); 102 | pen.restore(); 103 | } 104 | 105 | public move(): void { 106 | const { 107 | distance, 108 | centerPoint, 109 | cvsWidth, 110 | cvsHeight, 111 | } = this; 112 | 113 | centerPoint.x += distance.x; 114 | centerPoint.y += distance.y; 115 | 116 | // 碰撞检测 117 | distance.x = (centerPoint.x > cvsWidth 118 | || centerPoint.x < 0) 119 | ? -distance.x 120 | : distance.x; 121 | distance.y = (centerPoint.y > cvsHeight 122 | || centerPoint.y < 0) 123 | ? -distance.y 124 | : distance.y; 125 | } 126 | 127 | public drawLine( 128 | outerItem: Ball | false, 129 | ): void { 130 | const { 131 | ballArr, 132 | safeDistance, 133 | pen, 134 | lineColor, 135 | lineWidth, 136 | } = this; 137 | 138 | if (outerItem) { 139 | for (const innerItem of ballArr) { 140 | if ( 141 | outerItem !== innerItem && Math.sqrt( 142 | Math.pow(( 143 | outerItem.centerPoint.x - innerItem.centerPoint.x), 144 | 2) + Math.pow(( 145 | outerItem.centerPoint.y - innerItem.centerPoint.y), 146 | 2) 147 | ) < safeDistance 148 | ) { 149 | new Line({ 150 | lineColor, 151 | lineWidth, 152 | startPoint: { 153 | x: outerItem.centerPoint.x, 154 | y: outerItem.centerPoint.y, 155 | }, 156 | endPoint: { 157 | x: innerItem.centerPoint.x, 158 | y: innerItem.centerPoint.y, 159 | }, 160 | pen, 161 | }); 162 | } 163 | } 164 | } 165 | } 166 | 167 | } -------------------------------------------------------------------------------- /src/ddzy/__tests__/algorithm/binarySearchTree.test.ts: -------------------------------------------------------------------------------- 1 | import { BinarySearchTree } from "../../utility/algorithm/binary-search-tree"; 2 | 3 | describe('BST', () => { 4 | const bst = new BinarySearchTree({ 5 | nodes: [2, 5, 3, 8, 7, 4, 9, 12, 23, 10, 1], 6 | }); 7 | 8 | test('bst.handleHasValue should receive a number and return true or false if the value was exist', () => { 9 | const received = [2, 5, 3, 8, 7, 4, 9, 12, 23, 10, 1]; 10 | 11 | for (const v of received) { 12 | expect(bst.handleHasValue(v)).toBeTruthy(); 13 | } 14 | }); 15 | 16 | test('bst.handleInsert should receive a number and insert it to root', () => { 17 | const received = [100, 200, 300]; 18 | 19 | for (const v of received) { 20 | bst.handleInsert(v); 21 | expect(bst.handleHasValue(v)).toBeTruthy(); 22 | } 23 | }); 24 | 25 | test('bst.handleRemove should receive a number and remove it from root', () => { 26 | const received = [100, 200, 300]; 27 | 28 | for (const v of received) { 29 | bst.handleRemove(v); 30 | expect(bst.handleHasValue(v)).toBeFalsy(); 31 | } 32 | }); 33 | 34 | test('bst.handleGetDepth should return the whole tree depth when received undefined', () => { 35 | const expected = 6; 36 | 37 | expect(bst.handleGetDepth()).toBe(expected); 38 | }); 39 | 40 | test('bst.handleGetDepth should receive a number and return its depth which was in tree', () => { 41 | const received = [5, 7, 23]; 42 | const expected = [2, 4, 6]; 43 | 44 | for (const [i, v] of received.entries()) { 45 | expect(bst.handleGetDepth(v)).toBe(expected[i]); 46 | } 47 | }); 48 | 49 | test('bst.handleGetHeight should return whe whole tree height when received undefined', () => { 50 | const expected = 6; 51 | 52 | expect(bst.handleGetHeight()).toBe(expected); 53 | }); 54 | 55 | test('bst.handleGetHeight should receive a number and return its height which was in tree', () => { 56 | const received = [5, 7, 23]; 57 | const expected = [5, 1, 1]; 58 | 59 | for (const [i, v] of received.entries()) { 60 | expect(bst.handleGetHeight(v)).toBe(expected[i]); 61 | } 62 | }); 63 | 64 | test('bst.handleGetLeaves should return the collection of leaves node', () => { 65 | const expected = [1, 4, 7, 10, 23]; 66 | const result = bst.handleGetLeaves(); 67 | 68 | for (const [i, v] of result.entries()) { 69 | expect(v.value).toBe(expected[i]); 70 | } 71 | }); 72 | 73 | test('bst.handleFrontOrderTraversal should ergodic the whole tree by using front-order and execute callback parameter', () => { 74 | const expected = [2, 1, 5, 3, 4, 8, 7, 9, 12, 10, 23]; 75 | let count = 0; 76 | 77 | bst.handleFrontOrderTraversal((node) => { 78 | expect(node.value).toBe(expected[count]); 79 | 80 | count++; 81 | }); 82 | }); 83 | 84 | test('bst.handleMiddleOrderTraversal should ergodic the whole tree by using middle-order and execute callback parameter', () => { 85 | const expected = [1, 2, 5, 3, 4, 8, 7, 9, 12, 10, 23]; 86 | let count = 0; 87 | 88 | bst.handleMiddleOrderTraversal((node) => { 89 | expect(node.value).toBe(expected[count]); 90 | 91 | count++; 92 | }) 93 | }); 94 | 95 | test('bst.handleBackOrderTraversal should ergodic the whole tree by using back-order and execute callback parameter', () => { 96 | const expected = [1, 5, 3, 4, 8, 7, 9, 12, 10, 23, 2]; 97 | let count = 0; 98 | 99 | bst.handlBackOrderTraversal((node) => { 100 | expect(node.value).toBe(expected[count]); 101 | 102 | count++; 103 | }) 104 | }); 105 | 106 | test('bst.handleGetRoot should return the whole tree', () => { 107 | const expected = [2, 1, 5, 3, 4, 8, 7, 9, 12, 10, 23]; 108 | let count = 0; 109 | 110 | bst.handleFrontOrderTraversal((node) => { 111 | expect(node.value).toBe(expected[count]); 112 | 113 | count++; 114 | }); 115 | }); 116 | 117 | test('bst.handleGetMaxValue should return the maximum node which was in tree', () => { 118 | const expected = 23; 119 | const result = bst.handleGetMaxValue(); 120 | 121 | expect(result).toBe(expected); 122 | }); 123 | 124 | test('bst.handleGetMinValue should return the minimum node which was in tree', () => { 125 | const expected = 1; 126 | const result = bst.handleGetMinValue(); 127 | 128 | expect(result).toBe(expected); 129 | }); 130 | }); -------------------------------------------------------------------------------- /src/ddzy/canvas/colorful-bubble/bubble/bubble.ts: -------------------------------------------------------------------------------- 1 | import { getAnyRandom } from "../../../utility/number/getAnyRandom"; 2 | import { getFullRandom } from "../../../utility/number/getFullRandom"; 3 | import { getRadian } from "../../../utility/number/getRadian"; 4 | 5 | export interface IBubbleProps { 6 | pen: CanvasRenderingContext2D, 7 | cvsWidth: number; 8 | cvsHeight: number; 9 | bubbleSpeed: number; 10 | bubbleColorArr: string[]; 11 | bubbleScaleRange: { 12 | min: number, 13 | max: number, 14 | }; 15 | bubbleOpacity: number; 16 | bubbleExpandRange: number; 17 | mousePoint: { 18 | x: number; 19 | y: number; 20 | }; 21 | }; 22 | 23 | export class Bubble { 24 | // 气泡初始半径 25 | private static BUBBLE_INITIAL_RADIUS: number; 26 | 27 | private readonly pen: CanvasRenderingContext2D; 28 | 29 | private readonly cvsWidth: number; 30 | private readonly cvsHeight: number; 31 | 32 | // 中心点坐标 33 | private readonly centerPoint: { 34 | x: number, 35 | y: number, 36 | } 37 | // 移动距离 38 | private readonly distance: { 39 | x: number, 40 | y: number, 41 | } 42 | // 气泡颜色 43 | private readonly color: string 44 | // 气泡半径 45 | private radius: number 46 | // 气泡透明度 47 | private readonly opacity: number 48 | // 气泡伸缩范围 49 | private readonly bubbleExpandRange: number; 50 | // 鼠标当前坐标 51 | // ! TODO 考虑提取至全局变量 52 | private readonly mousePoint: { 53 | x: number, 54 | y: number, 55 | }; 56 | 57 | public constructor( 58 | config: IBubbleProps, 59 | ) { 60 | const { 61 | pen, 62 | cvsWidth, 63 | cvsHeight, 64 | bubbleSpeed, 65 | bubbleColorArr, 66 | bubbleScaleRange, 67 | bubbleOpacity, 68 | bubbleExpandRange, 69 | mousePoint, 70 | } = config; 71 | 72 | this.pen = pen; 73 | this.cvsWidth = cvsWidth; 74 | this.cvsHeight = cvsHeight; 75 | 76 | this.centerPoint = { 77 | x: getAnyRandom( 78 | 0, 79 | cvsWidth, 80 | ), 81 | y: getAnyRandom( 82 | 0, 83 | cvsHeight, 84 | ), 85 | }; 86 | this.distance = { 87 | x: getAnyRandom(-bubbleSpeed, bubbleSpeed), 88 | y: getAnyRandom(-bubbleSpeed, bubbleSpeed), 89 | }; 90 | this.color = bubbleColorArr[ 91 | getFullRandom(0, bubbleColorArr.length) 92 | ]; 93 | this.radius = getAnyRandom( 94 | bubbleScaleRange && bubbleScaleRange.min, 95 | bubbleScaleRange && bubbleScaleRange.max, 96 | ); 97 | this.opacity = bubbleOpacity; 98 | Bubble.BUBBLE_INITIAL_RADIUS = this.radius; 99 | this.bubbleExpandRange = bubbleExpandRange; 100 | this.mousePoint = mousePoint; 101 | } 102 | 103 | public draw(): void { 104 | const { 105 | pen, 106 | centerPoint, 107 | color, 108 | radius, 109 | opacity, 110 | } = this; 111 | 112 | pen.save(); 113 | pen.beginPath(); 114 | pen.globalAlpha = opacity; 115 | pen.fillStyle = color; 116 | pen.arc( 117 | centerPoint.x, 118 | centerPoint.y, 119 | radius, 120 | 0, 121 | getRadian(360), 122 | ); 123 | pen.fill(); 124 | pen.closePath(); 125 | pen.restore(); 126 | } 127 | 128 | public move(): void { 129 | const { 130 | cvsWidth, 131 | cvsHeight, 132 | mousePoint, 133 | bubbleExpandRange, 134 | centerPoint, 135 | distance, 136 | radius, 137 | } = this; 138 | 139 | // 鼠标 - 球中心 距离 140 | const mouseToBubbleDistanceX: number = Math 141 | .pow(centerPoint.x - mousePoint.x, 2); 142 | const mouseToBubbleDistanceY: number = Math 143 | .pow(centerPoint.y - mousePoint.y, 2); 144 | const mouseToBubbleDistance: number = Math 145 | .pow(mouseToBubbleDistanceX + mouseToBubbleDistanceY, .5) 146 | 147 | centerPoint.x += distance.x; 148 | centerPoint.y += distance.y; 149 | 150 | // 碰撞检测 151 | this.distance.x = centerPoint.x < radius 152 | || centerPoint.x > cvsWidth - radius 153 | ? -this.distance.x 154 | : this.distance.x; 155 | this.distance.y = centerPoint.y < radius 156 | || centerPoint.y > cvsHeight - radius 157 | ? -this.distance.y 158 | : this.distance.y; 159 | 160 | // 缩放检测 161 | if (mouseToBubbleDistance <= bubbleExpandRange) { 162 | this.radius += 1; 163 | } else if ( 164 | mouseToBubbleDistance > bubbleExpandRange 165 | && this.radius > Bubble.BUBBLE_INITIAL_RADIUS 166 | ) { 167 | this.radius -= 1; 168 | } 169 | } 170 | } 171 | --------------------------------------------------------------------------------