gucheen 发布的文章

一直以来,MAC 下鼠标的功能按键(比如侧键)都不能正常工作,罗技自己的驱动对鼠标型号的支持都非常有限,比如我手中的 G500s 和 G502 就不能正常支持,尝试过很多方案,都不能完美解决,最近发现了 SensibleSideButtons 这个应用,完美的解决了这个问题。

根据作者的描述,他一直以来也被这个问题困扰,知道后来研究了 Logitech MX Master 的技术方案,最终采用模拟 Trackpad 三指手势的方式,完美的解决了这个问题。

具体可以看作者写的技术原理和实现 http://sensible-side-buttons.archagon.net/

1. Tray(托盘图片)消失

假如你把 tray 的实例变量定义在了函数内部(比如 app.on('ready', createWindow) 的回调函数内),那么当 JavaScript 引擎进行垃圾回收的时候,这个变量就会被销毁,这样 tray 的实例就没了,托盘图标就消失了。

2. 无法进行复制、粘贴之类的操作

由于默认没有绑定任何快捷键,当然操作不了,使用官方提供的下面这段代码,将各平台所有常见的菜单(快捷键)绑定好。

const template = [
  {
    label: 'Edit',
    submenu: [
      {role: 'undo'},
      {role: 'redo'},
      {type: 'separator'},
      {role: 'cut'},
      {role: 'copy'},
      {role: 'paste'},
      {role: 'pasteandmatchstyle'},
      {role: 'delete'},
      {role: 'selectall'}
    ]
  },
  {
    label: 'View',
    submenu: [
      {role: 'reload'},
      {role: 'forcereload'},
      {role: 'toggledevtools'},
      {type: 'separator'},
      {role: 'resetzoom'},
      {role: 'zoomin'},
      {role: 'zoomout'},
      {type: 'separator'},
      {role: 'togglefullscreen'}
    ]
  },
  {
    role: 'window',
    submenu: [
      {role: 'minimize'},
      {role: 'close'}
    ]
  },
  {
    role: 'help',
    submenu: [
      {
        label: 'Learn More',
        click () { require('electron').shell.openExternal('https://electron.atom.io') }
      }
    ]
  }
]

if (process.platform === 'darwin') {
  template.unshift({
    label: app.getName(),
    submenu: [
      {role: 'about'},
      {type: 'separator'},
      {role: 'services', submenu: []},
      {type: 'separator'},
      {role: 'hide'},
      {role: 'hideothers'},
      {role: 'unhide'},
      {type: 'separator'},
      {role: 'quit'}
    ]
  })

  // Edit menu
  template[1].submenu.push(
    {type: 'separator'},
    {
      label: 'Speech',
      submenu: [
        {role: 'startspeaking'},
        {role: 'stopspeaking'}
      ]
    }
  )

  // Window menu
  template[3].submenu = [
    {role: 'close'},
    {role: 'minimize'},
    {role: 'zoom'},
    {type: 'separator'},
    {role: 'front'}
  ]
}

最近处理一些用户录入数据时,发现有一些明明是纯数字的数据,却通过不了类型校验(无法解析成 Int/BigInt)类型,由于一直都是通过日志查看错误,觉得很疑惑,后来找到原始数据,发现因为这个『字符串』带了一个 zero width space,导致程序异常。

zero width space 在大多数文本输出的地方都是不可见的(顾名思义,没有宽度的空格),因此通过类似页面、日志排查都很难发现这个问题。

当把这个字符串手动复制粘贴到 Chrome 的控制台时,可以看到,这个 zero width space 表现成了一个·,这应该是 Chrome 为了方便调试设计的。

zero width space

而粘贴到 iTerm 终端,则可以直接看到 Unicode 值(但是当作为日志输出到终端时,是无法显示的)

zero width space in iTerm

这个字符一般来源都是用户从一些特定的平台复制而来,因此建议在类似的用户数据录入处进行一下相应处理,这里需要注意,一般语言的标准库的 trim 处理都是不会处理 zero width space 的,所以只能手动移除\u200B( 正则下)。

事实上,还有很多不可见字符,具体请参照这个回答:

https://stackoverflow.com/a/11305926/2530524

Control character in ASCII

引言

在一年多之前,我写了一篇文章《The way to Angular 2 | Part.1》介绍 Angular,最初打算持续完成一个系列,然后在我写完文章之后,Angular 和 TypeScript 都发生了相当大的变化,整个系列也搁浅了。 如今,我打算换一个角度,继续这个系列。

为什么要使用 Angular

angular.io_features

为什么要使用 TypeScript

www.typescriptlang.org_index.htm

- 阅读剩余部分 -

随机数与随机字符串

早前我都使用Math.random()来生成随机数,然后转成16进制字符串来生成随机字符串,后来看到了这个回答(stackoverflow)Math.random() 确实不是一个好的选择,因此我需要更加科学的方法。

window.crypto (Web Cryptography API)

Web Cryptography API 已经是 W3C Recommendation 级别,常见浏览器的近期版本也都已经实现了。 0ivS.png 使用的时候也可以不用担心兼容性问题。

crypto.getRandomValues

crypto.getRandomValues 是我们用来实现随机字符串的主要方法,这个方法从实现和随机性的角度来说,更加高效、可靠。 10.2.1. The getRandomValues method The getRandomValues method generates cryptographically random values. It must act as follows:

  1. If array is not of an integer type (i.e., Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array or UInt8ClampedArray), throw a TypeMismatchError and terminate the algorithm.
  2. If the byteLength of array is greater than 65536, throw a QuotaExceededError and terminate the algorithm.
  3. Overwrite all elements of array with cryptographically random values of the appropriate type.
  4. Return array.
Note Do not generate keys using the getRandomValues method. Use the generateKey method instead.

使用方法就是传入一个 Int 的数组,然后返回被加密之后的数组(覆盖原始数组)。

科学的随机字符串生成方法

知道了crypto.getRandomValues的使用方法之后,我们就可以来实现一个科学的随机字符串生成方法了。 首先毫无疑问我们需要一个 UintArray,这比我们手动生成随机数的数组要简单多了。

let len = 64;
const arr = new Uint8Array(len / 2);

假设我们要生成长度为 64 的随机字符串,len 就是 64,之所以在生成 Uint8Array 时候,要将长度除以2,是因为最终我们是通过16进制字符串的形式来输出的,并且在头部补0取末尾两位,所以只需要一半长度的 Uint8Array 就可以了。 用crypto.getRandomValues()加密这个数据

window.crypto.getRandomValues(arr);

把这个 Uint8Array 转换成普通数组,并且把每一个值转换成16进制字符串(首位补0),取末尾2位,最后拼接到一起,就是最终的随机字符串了。

// dec2hex :: Integer -> String
function dec2hex (dec) {
  return ('0' + dec.toString(16)).substr(-2);
}
    
// generateId :: Integer -> String
function generateId (len = 40) {
  const arr = new Uint8Array(len / 2);
  window.crypto.getRandomValues(arr);
  return Array.from(arr, dec2hex).join('');
}

使用这个方法来生成随机字符串,更加高效、科学、可靠。 gist: Generate random string/characters in JavaScript