パターン指定型パスワード ジェネレーター (JS版)

JavaScript を思い出す & 訓練すべく作ってみる。

IE8 でも動作するようにしたら、かなりアレな感じになった。
(追記: IE6 は OK で IE5 はダメだった)

**パターン指定型パスワード ジェネレーターの目的は、好みのパスワードを得ることなので、生成されたパスワードを、そのまま使う必要はありません。**

<details><summary>HTMLファイル</summary><div>

```html:ppwgen.html
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
    <title>パターン指定型パスワード生成</title>
  </head>
  <body onload="cpwSample();">
    <h1>パターン指定型パスワード生成</h1>
    <hr>
    <p>
      <input type="text" id="cpwinp1" size="50" placeholder="ここに生成パターンを入力" style="font-size: large;" /><br/>
      <input id="cpwgen" type="button" value="クリックすると生成します" onclick="cpwStart('1');" />
    </p>
    <p>パスワード（JavaScript によるローカル生成です）：</p>
    <blockquote><pre id="cpwout1">なし</pre></blockquote>
    <hr>
    <p>文字の選択は、'%' の後に</p>
    <blockquote>
      <div>% 種類 </div>
      <div>% 文字数 種類 </div>
    </blockquote>
    <p>の形式で指定します。文字数を省略すると１文字になります。</p>
    <blockquote>
      <table border="1" style="border-collapse: collapse; border-spacing: 0px;">
        <tr><th>&nbsp;種類&nbsp;</th><th>内容</th><th>文字</th></tr>
        <tr><td style="text-align: center;">b</td><td>&nbsp; 2進数の数字&nbsp;</td><td>&nbsp;01&nbsp;</td></tr>
        <tr><td style="text-align: center;">o</td><td>&nbsp; 8進数の数字&nbsp;</td><td>&nbsp;01234567&nbsp;</td></tr>
        <tr><td style="text-align: center;">d</td><td>&nbsp;10進数の数字&nbsp;</td><td>&nbsp;0123456789&nbsp;</td></tr>
        <tr><td style="text-align: center;">X</td><td>&nbsp;16進数の数字(大)&nbsp;</td><td>&nbsp;0123456789ABCDEF&nbsp;</td></tr>
        <tr><td style="text-align: center;">x</td><td>&nbsp;16進数の数字(小)&nbsp;</td><td>&nbsp;0123456789abcdef&nbsp;</td></tr>
        <tr><td style="text-align: center;">A</td><td>&nbsp;英文字&nbsp;</td><td>&nbsp;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&nbsp;</td></tr>
        <tr><td style="text-align: center;">C</td><td>&nbsp;英文字(大)&nbsp;</td><td>&nbsp;ABCDEFGHIJKLMNOPQRSTUVWXYZ&nbsp;</td></tr>
        <tr><td style="text-align: center;">c</td><td>&nbsp;英文字(小)&nbsp;</td><td>&nbsp;abcdefghijklmnopqrstuvwxyz&nbsp;</td></tr>
        <tr><td style="text-align: center;">B</td><td>&nbsp;英文字の母音&nbsp;</td><td>&nbsp;AEIOUaeiou&nbsp;</td></tr>
        <tr><td style="text-align: center;">V</td><td>&nbsp;英文字の母音(大)&nbsp;</td><td>&nbsp;AEIOU&nbsp;</td></tr>
        <tr><td style="text-align: center;">v</td><td>&nbsp;英文字の母音(小)&nbsp;</td><td>&nbsp;aeiou&nbsp;</td></tr>
        <tr><td style="text-align: center;">D</td><td>&nbsp;英文字の子音&nbsp;</td><td>&nbsp;BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz&nbsp;</td></tr>
        <tr><td style="text-align: center;">Q</td><td>&nbsp;英文字の子音(大)&nbsp;</td><td>&nbsp;BCDFGHJKLMNPQRSTVWXYZ&nbsp;</td></tr>
        <tr><td style="text-align: center;">q</td><td>&nbsp;英文字の子音(小)&nbsp;</td><td>&nbsp;bcdfghjklmnpqrstvwxyz&nbsp;</td></tr>
        <tr><td style="text-align: center;">Y</td><td>&nbsp;英数&nbsp;</td><td>&nbsp;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&nbsp;</td></tr>
        <tr><td style="text-align: center;">Z</td><td>&nbsp;英数(大)&nbsp;</td><td>&nbsp;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ&nbsp;</td></tr>
        <tr><td style="text-align: center;">z</td><td>&nbsp;英数(小)&nbsp;</td><td>&nbsp;0123456789abcdefghijklmnopqrstuvwxyz&nbsp;</td></tr>
        <tr><td style="text-align: center;">W</td><td>&nbsp;英数と&nbsp;'_'&nbsp;</td><td>&nbsp;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_&nbsp;</td></tr>
        <tr><td style="text-align: center;">L</td><td>&nbsp;英数(大)と&nbsp;'_'&nbsp;</td><td>&nbsp;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_&nbsp;</td></tr>
        <tr><td style="text-align: center;">l</td><td>&nbsp;英数(小)と&nbsp;'_'&nbsp;</td><td>&nbsp;0123456789abcdefghijklmnopqrstuvwxyz_&nbsp;</td></tr>
      </table>
    </blockquote>
    <p>複数の連続指定</p>
    <blockquote><div>%2c%3x%4d</div></blockquote>
    <p>を</p>
    <blockquote><div id="cpwinp2">%{2c3x4d}</div></blockquote>
    <p>として、{..} で簡略化できます。</p>
    <blockquote><pre id="cpwout2" style="color: gray;"></pre></blockquote>
    <p>さらに</p>
    <blockquote><div id="cpwinp3">%3{2c3x4d}</div></blockquote>
    <p>とすると、{..} を３回の繰り返しにできます。</p>
    <blockquote><pre id="cpwout3" style="color: gray;"></pre></blockquote>
    <p>省略形の途中に文字を入れるには、バックスラッシュ "\" を使って</p>
    <blockquote><div id="cpwinp4">%{2c\-3x\-4d}</div></blockquote>
    <p>とします。(バックスラッシュなしでも可能な場合があります)</p>
    <blockquote><pre id="cpwout4" style="color: gray;"></pre></blockquote>
    <p>選択する文字を限定するには [..] を使って</p>
    <blockquote><div id="cpwinp5">%20[ABHKO]</div></blockquote>
    <p>とします。例では ABHKO のどれかが出てきます。</p>
    <blockquote><pre id="cpwout5" style="color: gray;"></pre></blockquote>
    <p>選択文字を [..] の中に連続した文字の順番で並べる場合</p>
    <blockquote><div>%10[ABCDEF]</div></blockquote>
    <p>は<p>
      <blockquote><div id="cpwinp6">%10[A-F]</div></blockquote>
    <p>として、[ 開始 - 終了 ] で簡略化できます。</p>
    <blockquote><pre id="cpwout6" style="color: gray;"></pre></blockquote>
    <hr>
    <p>サンプル</p>


    <p><span>書式を列挙</span><span>&nbsp;:&nbsp;</span>
      " <span id="cpwinp11">%b %o %d %X %x %A %C %c %B %V %v %D %Q %q %Y %Z %z %W %L %l</span> "
    </p><blockquote><pre id="cpwout11"></pre></blockquote>

    <p><span>書式を列挙(空白なし)</span><span>&nbsp;:&nbsp;</span>
      " <span id="cpwinp12">%b%o%d%X%x%A%C%c%B%V%v%D%Q%q%Y%Z%z%W%L%l</span> "
    </p><blockquote><pre id="cpwout12"></pre></blockquote>

    <p><span>書式を列挙('{..}' による省略形)</span><span>&nbsp;:&nbsp;</span>
      " <span id="cpwinp13">%{bodXxACcBVvDQqYZzWLl}</span> "
    </p><blockquote><pre id="cpwout13"></pre></blockquote>

    <p><span>10桁の数字</span><span>&nbsp;:&nbsp;</span>
      " <span id="cpwinp14">%10d</span> "
    </p><blockquote><pre id="cpwout14"></pre></blockquote>

    <p><span>ライセンス キー風</span><span>&nbsp;:&nbsp;</span>
      " <span id="cpwinp15">LK%5{\-C2Z3d}</span> "
    </p><blockquote><pre id="cpwout15"></pre></blockquote>

    <hr>
    <script type="text/javascript">
      function cpwSample()
      {
          for (var n = 2; n <= 20; n++)
          {
              var inp = document.getElementById("cpwinp"+n);
              var out = document.getElementById("cpwout"+n);
              if (inp && out) cpwMake(out, inp.innerText, 3);
          }
      }
    </script>
    <script type="text/javascript">
    <!--

      var cpwPatternMap = cpwGetPatternMap();

      function newScriptingDictionary()
      {
          try { return new ActiveXObject("Scripting.Dictionary"); } catch (e) {}
          try { return new cpwScriptingDictionary(); } catch (e) {}
      }

      function cpwScriptingDictionary()
      {
          this.map = new Map();
          this.add = function(k, v) { this.map.set(k, v); }
          this.Exists = function(k) { return this.map.has(k); }
          this.Item = function(k) { return this.map.get(k); }
      }

      function cpwGetPatternMap()
      {
          var dict = newScriptingDictionary();
          dict.add('b', '01');
          dict.add('o', '01234567');
          dict.add('d', '0123456789');
          dict.add('X', '0123456789ABCDEF');
          dict.add('x', '0123456789abcdef');
          dict.add('A', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
          dict.add('C', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
          dict.add('c', 'abcdefghijklmnopqrstuvwxyz');
          dict.add('B', 'AEIOUaeiou');
          dict.add('V', 'AEIOU');
          dict.add('v', 'aeiou');
          dict.add('D', 'BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz');
          dict.add('Q', 'BCDFGHJKLMNPQRSTVWXYZ');
          dict.add('q', 'bcdfghjklmnpqrstvwxyz');
          dict.add('Y', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
          dict.add('Z', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ');
          dict.add('z', '0123456789abcdefghijklmnopqrstuvwxyz');
          dict.add('W', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_');
          dict.add('L', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_');
          dict.add('l', '0123456789abcdefghijklmnopqrstuvwxyz_');
          return dict;
      }

      var cpwRandom = newRandom();

      function newRandom()
      {
          try { return new cpwRandomSource(); } catch (e) {}
          try { return new cpwOldRandom(); } catch (e) {}
      }

      function cpwRandomSource()
      {
          var arraybuffer = new Uint32Array(1);
          window.crypto.getRandomValues(arraybuffer); // check
          this.random = function()
          {
              return window.crypto.getRandomValues(arraybuffer)[0] / 4294967295;
          }
      }

      function cpwOldRandom()
      {
          this.random = Math.random;
      }

      function cpwChoicer()
      {
          this.choice = function(tab)
          {
              return tab.charAt(Math.floor(cpwRandom.random() * tab.length));
          }
      }

      function cpwMake(out, pattern, count)
      {
          var res = cpwGenerator(pattern, count);
          while (out.firstChild)
              out.removeChild(out.firstChild);
          out.appendChild(res);
      }

      function cpwStart(suffix)
      {
          var inp = document.getElementById("cpwinp" + suffix);
          var out = document.getElementById("cpwout" + suffix);
          cpwMake(out, inp.value, 10);

          cpwSample();
      }

      function cpwGenCharSet(charset)
      {
          this.charset = charset;
          this.generate = function(rnd)
          {
              return rnd.choice(this.charset);
          }
      }

      function cpwGenCount(count, generator)
      {
          this.generator = generator;
          this.count = count;
          this.generate = function(rnd)
          {
              var r = '';
              for (var n = 0; n < this.count; n++)
                  r += this.generator.generate(rnd);
              return r;
          }
      }

      function cpwGenList(generators)
      {
          this.generators = generators;
          this.generate = function(rnd)
          {
              var r = '';
              for (var n in this.generators)
                  r += this.generators[n].generate(rnd);
              return r;
          }
      }

      function cpwPatternCharList(pattern)
      {
          var clist = new Array();
          var p = '';

          for (var cp = 0; cp < pattern.length; cp++)
          {
              var c = pattern.charAt(cp);

              if (p != '\\')
              {
                  clist.push(c);
                  p = c;
              }
              else
              {
                  clist.push(clist.pop() + c);
                  p = '';
              }
          }
          return clist;
      }

      function cpwPatternIterator(pattern)
      {
          this.clist = cpwPatternCharList(pattern);
          this.pointer = 0;
          this.done = (this.pointer >= this.clist.length);

          this.next = function()
          {
              var c = '';
              if (!this.done)
              {
                  c = this.clist[this.pointer++];
                  this.done = (this.pointer >= this.clist.length);
              }
              return c;
          };

          this.rewind = function()
          {
              --this.pointer;
              return this;
          }
      }

      function cpwUnescape(s)
      {
          return ((s.length > 1 && s[0] == '\\') ? s[1] : s);
      }

      function cpwParsePat(ptr, nest)
      {
          var cnt = 0;
          var len = -1
          var c = '';

          while (!ptr.done)
          {
              c = ptr.next();
              if (!('0' <= c && c <= '9')) break;
              cnt = (cnt * 10) + parseInt(c, 10);
              len = cnt;
          }
          if (len < 0) len = 1;

          switch (c)
          {
              case '': throw undefined;
              case '{': return new cpwGenCount(len, cpwParseSubPat(ptr));
              case '[': return new cpwGenCount(len, cpwParseSubSet(ptr));
              default: break;
          }
          if (cpwPatternMap.Exists(c))
              return new cpwGenCount(len, new cpwGenCharSet(cpwPatternMap.Item(c)));
          if (nest)
          {
              if (c.length > 1 && c[0] == '\\')
                  return new cpwGenCharSet(cpwUnescape(c));

              var cu = c.toUpperCase();
              if (c != '}' && !('A' <= cu && cu <= 'Z'))
                  return new cpwGenCharSet(c);
          }
          throw undefined;
      }

      function cpwParseSubPat(ptr)
      {
          var r = new Array();

          while (!ptr.done)
          {
              var c = ptr.next();
              if (c == '}') break;
              r.push(cpwParsePat(ptr.rewind(), true));
          }
          return new cpwGenList(r);
      }

      function cpwParseSubSet(ptr)
      {
          var r = new Array();
          var s = new Array();
          while (!ptr.done)
          {
              var c = ptr.next();
              if (c == ']') break;
              if (s.length < 2)
              {
                  s.push(c);
                  continue;
              }
              a = cpwUnescape(s[0]);
              if (s[1] != '-')
              {
                  r.push(a);
                  s.shift();
                  s.push(c);
                  continue;
              }
              b = cpwUnescape(c);

              var cs = a.charCodeAt(0);
              var ce = b.charCodeAt(0);
              for (var c = cs; c <= ce; c++)
                  r.push(String.fromCharCode(c));
              s = new Array();
          }
          p = '';
          for (var n in r) p += r[n];
          for (var n in s) p += s[n];
          return new cpwGenCharSet(p);
      }

      function cpwParse(pattern)
      {
          var ptr = new cpwPatternIterator(pattern);
          var r = new Array();

          while (!ptr.done)
          {
              var c = ptr.next();
              if (c != '%')
                  g = new cpwGenCharSet(c);
              else
                  g = cpwParsePat(ptr, false);
              r.push(g);
          }
          return new cpwGenList(r);
      }

      function cpwGenerator(pattern, count)
      {
          var parent = document.createElement('div');

          try
          {
              var g = cpwParse(pattern);
              var rnd = new cpwChoicer();

              for (var i = 0; i < count; i++)
              {
                  var el = document.createElement('div');
                  el.innerText = g.generate(rnd);
                  parent.appendChild(el);
              }
          }
          catch (e)
          {
              var el = document.createElement('div');
              el.innerText = 'Error: invalid pattern: ' + pattern;
              parent.appendChild(el);
          }

          return parent;
      }

      // -->
    </script>
  </body>
</html>
```

</div></details>

HTML を開くと、こんな感じ。

<img width="563" alt="スクリーン ショット" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226677/dfcee83e-9981-a755-5a40-dc5de20eb18c.png">