{"id":20059229,"url":"https://github.com/qddegtya/ajs","last_synced_at":"2025-05-05T15:31:12.776Z","repository":{"id":57400940,"uuid":"171503354","full_name":"qddegtya/ajs","owner":"qddegtya","description":"🍩 A collection of utility libraries used by @qddegtya with PURE-JS.","archived":false,"fork":false,"pushed_at":"2024-08-27T05:30:15.000Z","size":142,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-01T12:36:49.983Z","etag":null,"topics":["ajs","pure-javascript","pure-js","utility","utility-library","zero-dependency"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qddegtya.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-19T15:54:51.000Z","updated_at":"2024-09-03T06:36:24.000Z","dependencies_parsed_at":"2024-06-21T19:07:53.971Z","dependency_job_id":"fa10f311-c09a-4d4c-bc8e-bad36804253e","html_url":"https://github.com/qddegtya/ajs","commit_stats":{"total_commits":65,"total_committers":2,"mean_commits":32.5,"dds":0.03076923076923077,"last_synced_commit":"be3160fc0b221eaa057b2d125a161d80cbe1cdf7"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qddegtya%2Fajs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qddegtya%2Fajs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qddegtya%2Fajs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qddegtya%2Fajs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qddegtya","download_url":"https://codeload.github.com/qddegtya/ajs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224452828,"owners_count":17313668,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ajs","pure-javascript","pure-js","utility","utility-library","zero-dependency"],"created_at":"2024-11-13T13:06:33.340Z","updated_at":"2025-05-05T15:31:12.766Z","avatar_url":"https://github.com/qddegtya.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cbr\u003e\n\t\u003cimg width=\"400\" src=\"media/ajs.svg\" alt=\"ajs\"\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n\u003c/h1\u003e\n\n![NPM Version](https://img.shields.io/npm/v/xajs)\n![NPM Downloads](https://img.shields.io/npm/dw/xajs?style=flat)\n![GitHub contributors from allcontributors.org](https://img.shields.io/github/all-contributors/qddegtya/ajs)\n![GitHub License](https://img.shields.io/github/license/qddegtya/ajs)\n\n# 关于\n\n\u003c!--ABOUT_START--\u003e\n🪄 Just another javascript utility library.\n\u003c!--ABOUT_END--\u003e\n\n# ✨ 概述\n\n- 🌐 **主流 Javascript 运行时支持**\n  - 完整支持 Node.js、Browser、Deno 等主流 JavaScript 运行时\n  - 提供 UMD/CommonJS/ES Module 多模块规范支持\n  - 零依赖实现，确保最大兼容性\n\n- 📦 **模块化设计**\n  - 支持按需引入\n  - 完整的 Tree-shaking 支持\n  - 独立的子模块发布支持\n\n- 🛡️ **可靠性保证**\n  - 完整的单元测试覆盖\n\n- 🔄 **工程自动化**\n  - 自动化的文档生成和同步\n  - 自动化的版本管理和发布流程\n\n- 💡 **开发者友好**\n  - 详尽的 API 文档和使用示例\n  - 完善的 Contributing 指南\n  - 活跃的社区维护\n\n- 📈 **持续维护**\n  - 语义化版本控制\n  - 及时的安全补丁\n\n# 🌰 快速开始\n\n\u003c!--QUICK_START_START--\u003e\n## 示例\n\n### 基础模块\n\n```javascript\nimport { core, dom } from 'xajs';\n\nconst Component = core.base.Class({\n  $extends: BaseComponent,\n\n  $static: {\n    defaultConfig: { theme: 'light' }\n  },\n\n  $ctor(config) {\n    this.$super();\n    this.config = { ...this.constructor.defaultConfig, ...config };\n    this.handler = dom.E.delegate('.menu a', {\n      click: (e, target) =\u003e {\n        e.preventDefault();\n        this.navigate(target.getAttribute('href'));\n      }\n    });\n  }\n});\n```\n\n### 高级模块\n\n```javascript\nimport { future, functional } from 'xajs';\n\n// Create atomic state\nconst todoAtom = future.TR.atom({\n  key: 'todoAtom',\n  default: { items: [], filter: 'all' }\n});\n\n// Create derived state\nconst filteredTodos = future.TR.selector({\n  key: 'filteredTodos',\n  get: ({ get }) =\u003e {\n    const state = get(todoAtom);\n    return state.items.filter(item =\u003e\n      state.filter === 'all'\n        ? true\n        : state.filter === 'completed'\n          ? item.completed\n          : !item.completed\n    );\n  }\n});\n\n// Create pub/sub communication\nconst { Puber, Suber } = functional.PS;\nconst todoService = new Puber('todos');\nconst todoView = new Suber('view');\n\ntodoView.rss(todoService, [\n  {\n    msg: 'todos:updated',\n    handler: todos =\u003e filteredTodos.observe(renderTodos)\n  }\n]);\n```\n\n### 浏览器运行时相关\n\n```javascript\nimport { mobile, dom } from 'xajs';\n\n// Device detection\nconst ua = new mobile.UserAgent(navigator.userAgent);\nif (ua.isMobile()) {\n  if (ua.isOS('iOS')) {\n    enableIOSFeatures();\n  }\n}\n\n// DOM manipulation\nconst { div, nav, a } = dom.tags;\nconst menu = div({ className: 'menu' }, [\n  nav(null, [a({ href: '#home' }, 'Home'), a({ href: '#about' }, 'About')])\n]);\n\n// URL parsing\nconst url = new dom.UrlParser(location.href);\nconsole.log(url.query.page);\n```\n\u003c!--QUICK_START_END--\u003e\n\n# 🎉 特性一览\n\n\u003c!--FEATURES_START--\u003e\n- Core\n  - base: 基础核心模块，自定义类，支持类的私有属性、继承等特性\n  - decorators: 常用装饰器，deprecate、mixin 等\n  - Deferred: Deferred，维护异步模型状态\n- DOM\n  - h: DOM 创建操作的优雅封装\n  - tags: 基于 h 封装常用 html tag 的快速创建方法\n  - E: 增强版事件管理\n  - UrlParser: 兼容性良好的 URL 解析器\n- FP\n  - compose: 同步组合\n  - composeAsync: 异步组合\n- Functional\n  - intercepter: 支持同步、异步两种模式的通用函数拦截器\n  - tryNext: 链式调用风格的 try\n  - PS: 优雅的发布订阅实现\n  - di: 简单实用的 DI 实现\n- Future\n  - TR: 支持依赖追踪，计算定义、组合的创新响应式\n  - atom: 基于 TR 封装的、类似 Recoil Atom 的原子状态\n  - selector: 基于 TR 封装的、类似 Recoil selector 的派生状态\n  - eff: 基于迭代器特性实现的代数效应\n  - tpl: 基于标签函数实现的简单实用模板引擎\n- Mobile\n  - UserAgent: User Agent 解析\n  - device: 便捷的 UA 对象「设备属性」访问器\n  - browser: 便捷的 UA 对象「浏览器属性」访问器\n- Lang\n  - MagicString: 支持链式调用的字符串不可变操作类\n- Internal\n  - is: 对象的类型运行时检查 (isArray, isObject, etc.)\n  - assign: 安全的对象属性分配，可实现对象深拷贝等特性\n  - hasOwnProp: 安全的对象自持属性嗅探\n\u003c!--FEATURES_END--\u003e\n\n# 📦 模块列表\n\n\u003c!--MODULES_START--\u003e\n## 可用模块\n\n| 名称 | 描述 | 可按需引入的模块名 |\n|---------|-------------|-------------|\n| core | 核心基础包 | `xajs/core` |\n| dom | 浏览器运行时相关包 | `xajs/dom` |\n| fp | 函数式编程相关包 | `xajs/fp` |\n| functional | 实用主义相关包 | `xajs/functional` |\n| future | 高级及实验特性包 | `xajs/future` |\n| internal | AJS 内部都在使用的实用工具包 | `xajs/internal` |\n| lang | Javascript 语言特性扩展包 | `xajs/lang` |\n| mobile | 移动端相关包 | `xajs/mobile` |\n\n## 模块详情\n\n\u003cdetails\u003e\n\u003csummary\u003ecore\u003c/summary\u003e\n\n## core\n\n核心基础包\n\n### 示例\n\n**Class Definition with Static Members**\n\n```javascript\nimport { base } from 'xajs/core';\n\nconst MyComponent = base.Class({\n  $extends: BaseComponent,\n\n  // Static properties and methods\n  $static: {\n    defaultConfig: {\n      theme: 'light'\n    },\n    create(config) {\n      return new this({ ...this.defaultConfig, ...config });\n    }\n  },\n\n  // Constructor\n  $ctor(config) {\n    this.$super(); // Call parent constructor\n    this.config = config;\n    this.state = { count: 0 };\n  },\n\n  // Instance methods\n  increment() {\n    this.state.count++;\n    this.emit('change', this.state.count);\n  }\n});\n```\n\n**Mixin and Deprecation**\n\n```javascript\nimport { decorators } from 'xajs/core';\n\n// Define a mixin\nconst LoggerMixin = {\n  log(msg) {\n    console.log(`[${this.constructor.name}] ${msg}`);\n  }\n};\n\n// Apply mixin and deprecate old methods\n@decorators.mixin(LoggerMixin)\nclass MyService {\n  @decorators.deprecate('Use newMethod() instead', { since: '2.0.0' })\n  oldMethod() {\n    return this.newMethod();\n  }\n\n  newMethod() {\n    this.log('Operation started');\n    return this.processData();\n  }\n}\n```\n\n**Async Operations with Deferred**\n\n```javascript\nimport { base } from 'xajs/core';\n\nclass DataLoader {\n  async loadData(retryCount = 3) {\n    const deferred = new base.Deferred();\n\n    try {\n      // Attempt to load data with retry\n      for (let i = 0; i \u003c retryCount; i++) {\n        try {\n          const response = await fetch('/api/data');\n          if (!response.ok) throw new Error('API Error');\n          const data = await response.json();\n          return deferred.resolve(data);\n        } catch (err) {\n          if (i === retryCount - 1) throw err;\n          await new Promise(r =\u003e setTimeout(r, 1000 * (i + 1)));\n        }\n      }\n    } catch (err) {\n      deferred.reject(err);\n    }\n\n    return deferred.done(); // Ensures unhandled rejections are thrown\n  }\n\n  isDataLoaded() {\n    return this.loadData.isDone();\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003edom\u003c/summary\u003e\n\n## dom\n\n浏览器运行时相关包\n\n### 示例\n\n**DOM with Tags Helpers**\n\n```javascript\nimport { h, tags } from 'xajs/dom';\n\n// Using h function directly\nconst vnode = h('div', { className: 'container' }, [\n  h('header', { key: 'header' }, [h('h1', null, 'Welcome')])\n]);\n\n// Using tags helpers (more concise)\nconst { div, header, h1, nav, a } = tags;\n\nconst menu = div({ className: 'container' }, [\n  header({ key: 'header' }, [\n    h1(null, 'Welcome'),\n    nav({ className: 'menu' }, [\n      a({ href: '#home' }, 'Home'),\n      a({ href: '#about' }, 'About')\n    ])\n  ])\n]);\n```\n\n**Advanced Event Handling**\n\n```javascript\nimport { E } from 'xajs/dom';\n\n// One-time event handling\nE.once('window.load', () =\u003e {\n  console.log('App loaded');\n});\n\n// Event sequence handling\nE.once(\n  'window.mouseover',\n  'window.click',\n  e =\u003e {\n    console.log('Mouse over then clicked');\n  },\n  { capture: true }\n);\n```\n\n**URL Parsing and Manipulation**\n\n```javascript\nimport { UrlParser } from 'xajs/dom';\n\n// Create parser instance\nconst parser = new UrlParser(\n  'https://example.com/path?q=search\u0026tags[]=js\u0026tags[]=dom'\n);\n\n// Basic URL parts\nconsole.log(parser.protocol); // 'https:'\nconsole.log(parser.hostname); // 'example.com'\nconsole.log(parser.pathname); // '/path'\n\n// Advanced query handling\nconst query = parser.query;\nconsole.log(query.q); // 'search'\nconsole.log(query.tags); // ['js', 'dom']\n\n// URL manipulation\nparser.setQueryParam('page', '2');\n// 'https://example.com/new-path?q=search\u0026tags[]=js\u0026tags[]=dom\u0026page=2'\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003efp\u003c/summary\u003e\n\n## fp\n\n函数式编程相关包\n\n### 示例\n\n**Function Composition**\n\n```javascript\nimport { compose, composeAsync } from 'xajs/fp';\n\n// Synchronous composition\nconst enhance = compose(addTimestamp, validate, normalize);\n\n// With type checking\nconst result = enhance({ name: 'example' });\n\n// Async composition with error handling\nconst pipeline = composeAsync(\n  async data =\u003e {\n    const validated = await validate(data);\n    if (!validated.success) {\n      throw new ValidationError(validated.errors);\n    }\n    return validated.data;\n  },\n  async record =\u003e {\n    const normalized = await normalize(record);\n    return {\n      ...normalized,\n      timestamp: Date.now()\n    };\n  }\n);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003efunctional\u003c/summary\u003e\n\n## functional\n\n实用主义相关包\n\n### 示例\n\n**Function Interception (Sync \u0026 Async)**\n\n```javascript\nimport { helper } from 'xajs/functional';\n\n// Synchronous interception\nconst loggedFetch = helper\n  .intercepter(fetch)\n  .before(url =\u003e {\n    console.log(`Fetching: ${url}`);\n  })\n  .after((url, response) =\u003e {\n    console.log(`Completed: ${url} (${response.status})`);\n  }).$runner;\n\n// Async interception\nconst cachedFetch = helper\n  .intercepter(fetch)\n  .before(async url =\u003e {\n    const cached = await cache.get(url);\n    if (cached) return cached;\n  })\n  .after(async (url, response) =\u003e {\n    await cache.set(url, response.clone());\n  }).$asyncRunner;\n```\n\n**Pub/Sub System**\n\n```javascript\nimport { helper } from 'xajs/functional';\n\nconst {\n  PS: { Puber, Suber }\n} = helper;\n\n// Create publisher and subscriber\nclass DataService extends Puber {\n  constructor() {\n    super('data-service', {});\n  }\n\n  async fetchData() {\n    const data = await fetch('/api/data');\n    this.pub('data:updated', await data.json());\n  }\n}\n\nclass DataView extends Suber {\n  constructor(service) {\n    super('data-view', {});\n    this.rss(service, [\n      {\n        msg: 'data:updated',\n        handler: this.onDataUpdate.bind(this)\n      }\n    ]);\n  }\n\n  onDataUpdate(data) {\n    this.render(data);\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003efuture\u003c/summary\u003e\n\n## future\n\n高级及实验特性包\n\n### 示例\n\n**Reactive State Management**\n\n```javascript\nimport { TR } from 'xajs/future';\n\n// Create atomic states\nconst count1 = TR(1);\nconst count2 = TR(2);\n\n// Create computed value\nconst sum = TR.compute((a, b) =\u003e a + b)(count1, count2);\n\n// Create derived computation\nconst doubled = TR.compute(s =\u003e s * 2)(sum);\n\n// Observe changes\nsum.observe(val =\u003e console.log('Sum:', val)); // 3\ndoubled.observe(val =\u003e console.log('Double:', val)); // 6\n\n// Update source values\ncount1(v =\u003e v + 1); // Sum: 4, Double: 8\ncount2(6); // Sum: 8, Double: 16\n\n// Cleanup\nsum.dispose();\ndoubled.dispose();\n```\n\n**Advanced State with Atoms**\n\n```javascript\nimport { TR } from 'xajs/future';\nconst { atom, selector } = TR;\n\n// Create base atom\nconst todoAtom = atom({\n  key: 'todoAtom',\n  default: {\n    items: [],\n    filter: 'all'\n  }\n});\n\n// Create derived selectors\nconst filteredTodos = selector({\n  key: 'filteredTodos',\n  get: ({ get }) =\u003e {\n    const state = get(todoAtom);\n    switch (state.filter) {\n      case 'completed':\n        return state.items.filter(item =\u003e item.completed);\n      case 'active':\n        return state.items.filter(item =\u003e !item.completed);\n      default:\n        return state.items;\n    }\n  }\n});\n\nconst todoStats = selector({\n  key: 'todoStats',\n  get: ({ get }) =\u003e {\n    const items = get(todoAtom).items;\n    return {\n      total: items.length,\n      completed: items.filter(item =\u003e item.completed).length,\n      active: items.filter(item =\u003e !item.completed).length\n    };\n  }\n});\n\n// Use selectors\nfilteredTodos.observe(todos =\u003e {\n  renderTodoList(todos);\n});\n\ntodoStats.observe(stats =\u003e {\n  updateStatusBar(stats);\n});\n```\n\n**Template Engine**\n\n```javascript\nimport { tpl } from 'xajs/future';\n\n// template\nconst template = tpl.exec(`\u003cdiv\u003e${name}\u003cdiv\u003e`, { name: 'AJS' });\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003einternal\u003c/summary\u003e\n\n## internal\n\nAJS 内部都在使用的实用工具包\n\n### 示例\n\n**Type Checking**\n\n```javascript\nimport { is } from 'xajs/internal';\n\n// Array checks\nconsole.log(is.isArray([1, 2, 3])); // true\nconsole.log(is.isArray({})); // false\n\n// Object checks\nconsole.log(is.isObject({})); // true\nconsole.log(is.isObject([])); // false\n\n// Function checks\nconsole.log(is.isFunction(() =\u003e {})); // true\nconsole.log(is.isFunction(async () =\u003e {})); // true\nconsole.log(is.isFunction(function* () {})); // true\n\n// Boolean checks\nconsole.log(is.isBoolean(true)); // true\nconsole.log(is.isBoolean(1)); // false\n```\n\n**Safe Object Operations**\n\n```javascript\nimport { assign, hasOwnProp } from 'xajs/internal';\n\n// Safe object assignment\nconst base = { a: 1, b: { c: 2 } };\nconst extension = { b: { d: 3 }, e: 4 };\n\nconst result = assign({}, base, extension);\nconsole.log(result);\n// {\n//   a: 1,\n//   b: { c: 2, d: 3 },\n//   e: 4\n// }\n\n// Safe property checks\nif (hasOwnProp(result, 'b')) {\n  console.log('Property exists:', result.b);\n}\n```\n\n**Type-Safe Operations**\n\n```javascript\nimport { is, assign } from 'xajs/internal';\n\nfunction safeUpdate(target, source) {\n  // Type validation\n  if (!is.isObject(target) || !is.isObject(source)) {\n    throw new TypeError('Both arguments must be objects');\n  }\n\n  // Safe assignment with type checking\n  const result = assign({}, target);\n\n  for (const key in source) {\n    if (hasOwnProp(source, key)) {\n      const value = source[key];\n\n      // Type-specific handling\n      if (is.isArray(value)) {\n        result[key] = [...value];\n      } else if (is.isObject(value)) {\n        result[key] = safeUpdate({}, value);\n      } else {\n        result[key] = value;\n      }\n    }\n  }\n\n  return result;\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003elang\u003c/summary\u003e\n\n## lang\n\nJavascript 语言特性扩展包\n\n### 示例\n\n**Basic String Operations**\n\n```javascript\nimport { MagicString } from 'xajs/lang';\n\nconst str = MagicString('hello world');\n\n// Chain multiple operations\nconst result = str.trim().capitalize().replace(/world/, 'AJS');\n\nconsole.log(result); // 'Hello AJS'\nconsole.log(str); // Original string unchanged\n```\n\n**Advanced Pattern Matching**\n\n```javascript\nimport { MagicString } from 'xajs/lang';\n\nconst text = MagicString('user.name.first');\n\n// Replace with callback\nconst result = text.replace(/\\w+/g, (match, index) =\u003e {\n  if (index === 0) return match;\n  return match.toUpperCase();\n});\n\nconsole.log(result); // 'user.NAME.FIRST'\n```\n\n**String Transformation**\n\n```javascript\nimport { MagicString } from 'xajs/lang';\n\nconst template = MagicString('Hello, ${name}!');\n\n// Interpolate values\nconst greeting = template.replace(\n  /\\${(\\w+)}/g,\n  (_, key) =\u003e\n    ({\n      name: 'AJS User'\n    })[key] || ''\n);\n\nconsole.log(greeting); // 'Hello, AJS User!'\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003emobile\u003c/summary\u003e\n\n## mobile\n\n移动端相关包\n\n### 示例\n\n**Device Detection and Feature Support**\n\n```javascript\nimport { UserAgent } from 'xajs/mobile';\n\n// Initialize with current user agent\nconst ua = new UserAgent(navigator.userAgent);\n\n// Comprehensive device check\nif (ua.isMobile()) {\n  // Mobile-specific optimizations\n  if (ua.isOS('iOS')) {\n    // iOS specific features\n    if (parseFloat(ua.getResult().os.version) \u003e= 14.5) {\n      enableModernIOSFeatures();\n    } else {\n      enableLegacyIOSSupport();\n    }\n  } else if (ua.isOS('Android')) {\n    const version = parseFloat(ua.getResult().os.version);\n    if (version \u003e= 10) {\n      enableModernAndroidFeatures();\n    } else {\n      enableLegacyAndroidSupport();\n    }\n  }\n} else if (ua.isTablet()) {\n  // Tablet optimizations\n  const { device } = ua.getResult();\n  if (device.vendor === 'Apple' \u0026\u0026 device.model === 'iPad') {\n    enableIPadFeatures();\n  }\n} else if (ua.isDesktop()) {\n  // Desktop optimizations\n  enableDesktopFeatures();\n}\n```\n\n**Browser and Engine Detection**\n\n```javascript\nimport { UserAgent } from 'xajs/mobile';\n\nconst ua = new UserAgent(navigator.userAgent);\nconst { browser, engine } = ua.getResult();\n\n// Comprehensive browser checks\nif (ua.isBrowser('Chrome')) {\n  const version = parseFloat(browser.version);\n  if (version \u003e= 90) {\n    // Modern Chrome features\n    enableProgressiveFeatures();\n  } else if (version \u003e= 80) {\n    // Legacy but stable Chrome features\n    enableBasicFeatures();\n  } else {\n    // Very old Chrome\n    showBrowserUpdateNotice();\n  }\n} else if (ua.isBrowser('Safari')) {\n  if (parseFloat(browser.version) \u003e= 14) {\n    if (engine.name === 'Webkit') {\n      // Modern Safari + Webkit\n      enableWebkitOptimizations();\n    }\n  } else {\n    // Legacy Safari support\n    enableLegacySafariSupport();\n  }\n}\n```\n\n\u003c/details\u003e\n\u003c!--MODULES_END--\u003e\n\n# 🤝 Contributing\n\nPlease read our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request to the project.\n\n# 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n# ✨ Contributors\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore --\u003e\n| [\u003cimg src=\"https://avatars2.githubusercontent.com/u/773248?v=4\" width=\"100px;\" alt=\"Archer (炽宇)\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eArcher (炽宇)\u003c/b\u003e\u003c/sub\u003e](http://xiaoa.name)\u003cbr /\u003e[💻](https://github.com/qddegtya/ajs/commits?author=qddegtya \"Code\") [🚇](#infra-qddegtya \"Infrastructure (Hosting, Build-Tools, etc)\") [🚧](#maintenance-qddegtya \"Maintenance\") |\n| :---: |\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqddegtya%2Fajs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqddegtya%2Fajs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqddegtya%2Fajs/lists"}