<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel rdf:about="https://let.hatelabo.jp/onk/rss">
    <link>https://let.hatelabo.jp/onk/rss</link>
    <description></description>
    <title>Bookmarklets from onk</title>
    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="https://let.hatelabo.jp/onk/let/g5G0uOeEqfcA"/>
        <rdf:li rdf:resource="https://let.hatelabo.jp/onk/let/krGhn6POgYAA"/>
        <rdf:li rdf:resource="https://let.hatelabo.jp/onk/let/kfnT4JiQgKAA"/>
        <rdf:li rdf:resource="https://let.hatelabo.jp/onk/let/kO_0gsqOgeAA"/>
        <rdf:li rdf:resource="https://let.hatelabo.jp/onk/let/kJmSpcv0geAA"/>
        <rdf:li rdf:resource="https://let.hatelabo.jp/onk/let/jon4w634gqAA"/>
        <rdf:li rdf:resource="https://let.hatelabo.jp/onk/let/hJmev9y2ouko"/>
      </rdf:Seq>
    </items>
  </channel>
  <item rdf:about="https://let.hatelabo.jp/onk/let/g5G0uOeEqfcA">
    <link>https://let.hatelabo.jp/onk/let/g5G0uOeEqfcA</link>
    <dc:date>2026-03-22T21:46:00Z</dc:date>
    <description>[タイトル URL]という形式でページのタイトルとURLをクリップボードにコピーします</description>
    <dc:creator>onk</dc:creator>
    <title>[Let] Scrapboxのリンク形式でタイトルとURLをコピー</title>
    <content:encoded>&lt;a href="javascript:%28%28%29%3D%3E%7B%27use%20strict%27%3Blet%20canonical%3D%28document.querySelector%28%27head%20link%5Brel%3D%22canonical%22%5D%5Bhref%5D%27%29%7C%7C%7B%7D%29.href%3Bif%28canonical%26%26location.hash%29%7Bcanonical%3Dcanonical%2Blocation.hash%7Dconst%20replacedTitle%3Ddocument.title.replaceAll%28%27%5B%27%2C%27%EF%BC%BB%27%29.replaceAll%28%27%5D%27%2C%27%EF%BC%BD%27%29.replaceAll%28%27%60%27%2C%27%EF%BD%80%27%29%3Bconst%20content%3D%22%5B%22%2BreplacedTitle%2B%22%20%22%2B%28canonical%7C%7Clocation.href%29%2B%22%5D%22%3Bnavigator.clipboard.writeText%28content%29%7D%29%28%29%3B"&gt;Scrapboxのリンク形式でタイトルとURLをコピー&lt;/a&gt;&lt;pre&gt;/*
 * @title Scrapboxのリンク形式でタイトルとURLをコピー
 * @description [タイトル URL]という形式でページのタイトルとURLをクリップボードにコピーします
 * @include *
 * @license MIT License
 * @javascript_url
 */
(() =&amp;gt; {
  'use strict';
  let canonical = (document.querySelector('head link[rel=&amp;quot;canonical&amp;quot;][href]') || {}).href
  if (canonical &amp;amp;&amp;amp; location.hash) {
    canonical = canonical + location.hash;
  }
  const replacedTitle = document.title
    .replaceAll('[', '［')
    .replaceAll(']', '］')
    .replaceAll('`', '｀');
  const content = &amp;quot;[&amp;quot; + replacedTitle + &amp;quot; &amp;quot; + (canonical || location.href) + &amp;quot;]&amp;quot;;
  navigator.clipboard.writeText(content);
})();
&lt;/pre&gt;</content:encoded>
  </item>
  <item rdf:about="https://let.hatelabo.jp/onk/let/krGhn6POgYAA">
    <link>https://let.hatelabo.jp/onk/let/krGhn6POgYAA</link>
    <dc:date>2025-03-04T18:09:43Z</dc:date>
    <description>Web サイトによって奪われたショートカットキー Cmd+K をブラウザ本来の動作に戻す</description>
    <dc:creator>onk</dc:creator>
    <title>[Let] Take back the stolen Cmd+K</title>
    <content:encoded>&lt;a href="javascript:%28%28%29%3D%3E%7Bconst%20originalPreventDefault%3DEvent.prototype.preventDefault%3BEvent.prototype.preventDefault%3Dfunction%28%29%7Bif%28%28this.ctrlKey%7C%7Cthis.metaKey%29%26%26this.key.toLowerCase%28%29%3D%3D%3D%27k%27%29%7Breturn%7Dreturn%20originalPreventDefault.apply%28this%2Carguments%29%7D%7D%29%28%29%3B"&gt;Take back the stolen Cmd+K&lt;/a&gt;&lt;pre&gt;/*
 * @title Take back the stolen Cmd+K
 * @description Web サイトによって奪われたショートカットキー Cmd+K をブラウザ本来の動作に戻す
 * @include https://x.com/*
 * @license MIT License
 * @javascript_url
 */

(() =&amp;gt; {
  // useCapture: true で既に preventDefault() する keydown event が登録されているので
  // preventDefault 側で無視するように……。
  // window.addEventListener('keydown', (e) =&amp;gt; {
  //   if (e.metaKey &amp;amp;&amp;amp; e.key.toLowerCase() === 'k') {
  //     e.stopImmediatePropagation();
  //   }
  // }, true);
  const originalPreventDefault = Event.prototype.preventDefault;
  Event.prototype.preventDefault = function () {
    if ((this.ctrlKey || this.metaKey) &amp;amp;&amp;amp; this.key.toLowerCase() === 'k') {
      return; // preventDefault() was skipped for Cmd+K
    }
    return originalPreventDefault.apply(this, arguments);
  };
})();
&lt;/pre&gt;</content:encoded>
  </item>
  <item rdf:about="https://let.hatelabo.jp/onk/let/kfnT4JiQgKAA">
    <link>https://let.hatelabo.jp/onk/let/kfnT4JiQgKAA</link>
    <dc:date>2024-10-09T06:45:41Z</dc:date>
    <description></description>
    <dc:creator>onk</dc:creator>
    <title>[Let] connpass のユーザ ID 一覧をクリップボードにコピー</title>
    <content:encoded>&lt;a href="javascript:%28%28%29%3D%3E%7B%27use%20strict%27%3Bconst%20userIds%3D%5B...document.querySelectorAll%28%22.display_name%20a%22%29%5D.map%28%28e%3D%3Ee.href.split%28%22%2F%22%29%5B4%5D%29%29%3Bnavigator.clipboard.writeText%28userIds.join%28%22%5Cn%22%29%29%7D%29%28%29%3B"&gt;connpass のユーザ ID 一覧をクリップボードにコピー&lt;/a&gt;&lt;pre&gt;/*
 * @title connpass のユーザ ID 一覧をクリップボードにコピー
 * @description
 * @include https://*.connpass.com
 * @license MIT License
 * @javascript_url
 *
 * 運営者やキャンセルした人も含まれているし 100 人超えていたらダメなので参考程度に
 */
(() =&amp;gt; {
  'use strict';
  const userIds = [...document.querySelectorAll(&amp;quot;.display_name a&amp;quot;)].map(e=&amp;gt;e.href.split(&amp;quot;/&amp;quot;)[4]);
  navigator.clipboard.writeText(userIds.join(&amp;quot;\n&amp;quot;));
})();
&lt;/pre&gt;</content:encoded>
  </item>
  <item rdf:about="https://let.hatelabo.jp/onk/let/kO_0gsqOgeAA">
    <link>https://let.hatelabo.jp/onk/let/kO_0gsqOgeAA</link>
    <dc:date>2024-05-20T03:12:37Z</dc:date>
    <description>個別 Tweet の URL から前後 1 時間の投稿を RT を含めて表示する</description>
    <dc:creator>onk</dc:creator>
    <title>[Let] Twitter(現X)の前後ポストを表示する</title>
    <content:encoded>&lt;a href="javascript:%28%28%29%3D%3E%7Bconst%20TWITTER_EPOCH%3D1288834974657%3Bconst%20formatTwitterDate%3Ddate%3D%3E%7Bconst%20pad2%3Dn%3D%3En.toString%28%29.padStart%282%2C%270%27%29%3Bconst%20year%3Ddate.getFullYear%28%29%3Bconst%20month%3Dpad2%28date.getMonth%28%29%2B1%29%3Bconst%20day%3Dpad2%28date.getDate%28%29%29%3Bconst%20hour%3Dpad2%28date.getHours%28%29%29%3Bconst%20minute%3Dpad2%28date.getMinutes%28%29%29%3Bconst%20second%3Dpad2%28date.getSeconds%28%29%29%3Breturn%60%24%7Byear%7D-%24%7Bmonth%7D-%24%7Bday%7D_%24%7Bhour%7D%3A%24%7Bminute%7D%3A%24%7Bsecond%7D_JST%60%7D%3Bconst%20getSearchRange%3DsnowflakeStr%3D%3E%7Bconst%20snowflake%3DBigInt%28snowflakeStr%29%3Bconst%20timestamp%3DNumber%28snowflake%3E%3E22n%29%2BTWITTER_EPOCH%3Breturn%7Bsince%3AformatTwitterDate%28new%20Date%28timestamp-60%2A60%2A1e3%29%29%2Cuntil%3AformatTwitterDate%28new%20Date%28timestamp%2B60%2A60%2A1e3%29%29%7D%7D%3Bconst%20main%3D%28%29%3D%3E%7Bconst%20match%3Dlocation.href.match%28%2Fhttps%3A%5C%2F%5C%2F%28twitter%7Cx%29%5C.com%5C%2F%28%3F%3Cname%3E%5B%5E%2F%5D%2B%29%5C%2Fstatus%5C%2F%28%3F%3Csnowflake%3E%5Cd%2B%29%2F%29%3Bif%28%21match%29%7Balert%28%27%E3%81%93%E3%81%AE%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AF%20Twitter%20%E3%81%AE%E5%80%8B%E5%88%A5%20Tweet%20%E3%81%A7%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93%27%29%3Breturn%7Dconst%20range%3DgetSearchRange%28match.groups.snowflake%29%3Bconst%20query%3D%60from%3A%24%7Bmatch.groups.name%7D%20since%3A%24%7Brange.since%7D%20until%3A%24%7Brange.until%7D%20include%3Anativeretweets%60%3Blocation.href%3D%60https%3A%2F%2Fx.com%2Fsearch%3Ff%3Dlive%26q%3D%24%7BencodeURIComponent%28query%29%7D%60%7D%3Bmain%28%29%7D%29%28%29%3B"&gt;Twitter(現X)の前後ポストを表示する&lt;/a&gt;&lt;pre&gt;/*
 * @title Twitter(現X)の前後ポストを表示する
 * @description 個別 Tweet の URL から前後 1 時間の投稿を RT を含めて表示する
 * @include https://x.com/*
 * @license MIT License
 * @javascript_url
 */

(() =&amp;gt; {
const TWITTER_EPOCH = 1288834974657;

// 'YYYY-MM-DD_HH:mm:ss_JST'形式で日付をフォーマット
const formatTwitterDate = (date) =&amp;gt; {
  const pad2 = (n) =&amp;gt; n.toString().padStart(2, '0');

  const year = date.getFullYear();
  const month = pad2(date.getMonth() + 1);
  const day = pad2(date.getDate());
  const hour = pad2(date.getHours());
  const minute = pad2(date.getMinutes());
  const second = pad2(date.getSeconds());
  return `${year}-${month}-${day}_${hour}:${minute}:${second}_JST`;
}

// Snowflake ID 文字列を受け取り、前後 60 分の since, until を返す
const getSearchRange = (snowflakeStr) =&amp;gt; {
  const snowflake = BigInt(snowflakeStr);
  // 41 bits: millisec from TWITTER_EPOCH
  // 10 bits: worker_id
  // 12 bits: sequence number
  // なので 22 bit 右シフトする
  const timestamp = Number(snowflake &amp;gt;&amp;gt; 22n) + TWITTER_EPOCH;

  return {
    since: formatTwitterDate(new Date(timestamp - 60 * 60 * 1000)), // 60 分前
    until: formatTwitterDate(new Date(timestamp + 60 * 60 * 1000)), // 60 分後
  };
}

const main = () =&amp;gt; {
  const match = location.href.match(/https:\/\/(twitter|x)\.com\/(?&amp;lt;name&amp;gt;[^/]+)\/status\/(?&amp;lt;snowflake&amp;gt;\d+)/);
  if (!match) {
    alert('このページは Twitter の個別 Tweet ではありません');
    return;
  }
  const range = getSearchRange(match.groups.snowflake);
  const query = `from:${match.groups.name} since:${range.since} until:${range.until} include:nativeretweets`;
  location.href = `https://x.com/search?f=live&amp;amp;q=${encodeURIComponent(query)}`;
}

main();

})();
&lt;/pre&gt;</content:encoded>
  </item>
  <item rdf:about="https://let.hatelabo.jp/onk/let/kJmSpcv0geAA">
    <link>https://let.hatelabo.jp/onk/let/kJmSpcv0geAA</link>
    <dc:date>2023-10-26T12:22:27Z</dc:date>
    <description>はてなブックマークのコメント一覧ページに遷移する</description>
    <dc:creator>onk</dc:creator>
    <title>[Let] b</title>
    <content:encoded>&lt;a href="javascript:%28%28%29%3D%3E%7Blocation.href%3D%60https%3A%2F%2Fb.hatena.ne.jp%2Fentry%2F%24%7Blocation.href.replace%28%27%23%27%2C%27%2523%27%29%7D%60%7D%29%28%29%3B"&gt;b&lt;/a&gt;&lt;pre&gt;/*
 * @title b
 * @description はてなブックマークのコメント一覧ページに遷移する
 * @include http://*
 * @license MIT License
 * @javascript_url
 */

(() =&amp;gt; {
location.href = `https://b.hatena.ne.jp/entry/${location.href.replace('#', '%23')}`;
})();&lt;/pre&gt;</content:encoded>
  </item>
  <item rdf:about="https://let.hatelabo.jp/onk/let/jon4w634gqAA">
    <link>https://let.hatelabo.jp/onk/let/jon4w634gqAA</link>
    <dc:date>2022-08-31T05:18:42Z</dc:date>
    <description>scrapboxで特定行にスクロールする</description>
    <dc:creator>onk</dc:creator>
    <title>[Let] ScrollToHighlightLine</title>
    <content:encoded>&lt;a href="javascript:%28%28%29%3D%3E%7Bdocument.getElementById%28%22L%22%2Blocation.hash.substr%281%29%29.scrollIntoView%28%29%7D%29%28%29%3B"&gt;ScrollToHighlightLine&lt;/a&gt;&lt;pre&gt;/*
 * @title ScrollToHighlightLine
 * @description scrapboxで特定行にスクロールする
 * @include https://scrapbox.io/
 * @license MIT License
 * @javascript_url
 */

// #L${lineId} を使ったが、.permalink を使ってもいいかもしれない

(()=&amp;gt;{
document.getElementById(&amp;quot;L&amp;quot; + location.hash.substr(1)).scrollIntoView()
})();
&lt;/pre&gt;</content:encoded>
  </item>
  <item rdf:about="https://let.hatelabo.jp/onk/let/hJmev9y2ouko">
    <link>https://let.hatelabo.jp/onk/let/hJmev9y2ouko</link>
    <dc:date>2019-04-08T16:28:52Z</dc:date>
    <description>utf8mb4</description>
    <dc:creator>onk</dc:creator>
    <title>[Let] sob</title>
    <content:encoded>&lt;a href="javascript:%22https%3A%2F%2Flet.st-hatelabo.com%2Fonk%2Flet%2FhJmev9y2ouko.bookmarklet.js%20%28arg%29%22.replace%28%2F%28%5CS%2B%29%5Cs%2B%28%5CS%2A%29%2F%2Cfunction%28s%2Curl%2Carg%29%7Bs%3Ddocument.createElement%28%22script%22%29%3Bs.charset%3D%22utf-8%22%3Bs.src%3Durl%2B%22%3Fs%3D%22%2BencodeURIComponent%28arg%29%3Bdocument.body.appendChild%28s%29%7D%29%3Bvoid%280%29%3B"&gt;sob&lt;/a&gt;&lt;pre&gt;/*
 * @title sob
 * @description utf8mb4
 * @include http://*
 * @license MIT License
 * @require 
 */

// 下書きです
var sob = '?';&lt;/pre&gt;</content:encoded>
  </item>
</rdf:RDF>
