dak ブログ

python、rubyなどのプログラミング、MySQL、サーバーの設定などの備忘録。レゴの写真も。

Quill でリッチテキストの編集

2024-05-06 23:25:09 | javascript
Quill でリッチテキストを編集する方法のメモ。

toolbar で表示するメニューを指定することができます。

■HTML
<!DOCTYPE html><html><head><link href="https://cdn.jsdelivr.net/npm/quill@2.0.1/dist/quill.snow.css" rel="stylesheet" /><script src="https://cdn.jsdelivr.net/npm/quill@2.0.1/dist/quill.js"></script><title>Quill</title></head><body><form></form>
<button onclick="show_contents()">submit</button>
<script type="text/javascript">function show_contents() {document.getElementById('editor-contents').innerHTML = quill.getSemanticHTML();console.log(JSON.stringify(quill.getContents()));}const options = {theme: 'snow',placeholder: '本文を入力してください',modules: {toolbar: [['bold', 'italic', 'link']],},};const quill = new Quill('#editor', options);</script></body></html>

■リッチテキストのデータ形式

getContents() でリッチテキストのデータを取得すできます。
{"ops": [{"insert":"aaa\n"},{"attributes":{"bold":true},"insert":"bbb"},{"insert":"\n"},{"attributes":{"italic":true,"bold":true}, "insert":"ccc"},{"insert":"\n"},{"attributes":{"link":"https://www.goo.ne.jp/"},"insert":"ddd"},{"insert":"\neee\n"}]}

■HTML での表示

getSemanticHTML() で入力内容をHTML化できます。

aaa

bbb

ccc

ddd

eee





JavaScript で画像の各ピクセルの色の取得

2022-12-25 12:17:45 | javascript
JavaScript で画像の各ピクセルの色の取得する方法のメモ。

read_image() で画像を読み込み、画像を読み込み終えたら、
raw_image() でcanvas に画像を描画します。

そして、16 ピクセル x 16 ピクセル単位で色の平均値を計算し、
その色で枠を描画しています。

各ピクセルの r, g, b, a(透明度) は以下で取得することができます。
r = img_data.data[(x + y*w)*4];
g = img_data.data[1 + (x + y*w)*4];
b = img_data.data[2 + (x + y*w)*4];
a = img_data.data[3 + (x + y*w)*4];
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>image processing</title>
  </head>
  <body>
    画像処理
    <div id="img_area"></div>
  </body>
  &;lt;script type="text/javascript">
(() => {
  function draw_image(params) {
    const img_area_obj = document.getElementById(params.img_area);
    const img_obj = params.img_obj;

    const cv_obj = document.createElement('canvas');
    cv_obj.width = img_obj.naturalWidth;
    cv_obj.height = img_obj.naturalHeight;

    const ct = cv_obj.getContext('2d');
    ct.drawImage(img_obj, 0, 0);
    img_area_obj.appendChild(cv_obj);

    const img_data = ct.getImageData(0, 0, img_obj.width, img_obj.height);

    // 枠
    draw_cell_frame(ct, img_data, 16);
  }

  function draw_cell_frame(ct, img_data, size) {
    for (let y = 0; y < img_data.height; y += size) {
      for (let x = 0; x < img_data.width; x += size) {
        let avg_col = avg_cell_color(img_data, x, y, size);
        ct.strokeStyle = `#${avg_col[0]}${avg_col[1]}${avg_col[2]}`;
        ct.strokeRect(x, y, size, size);
      }
    }
  }

  function avg_cell_color(img_data, min_x, min_y, size) {
    const w = img_data.width;
    const h = img_data.height;
    const max_x = min_x + size <= w ? min_x + size : w;
    const max_y = min_y + size <= h ? min_y + size : h;

    let sum_r = 0, sum_g = 0, sum_b = 0;
    let pixels = 0;
    for (let y = min_y; y < max_y; y++) {
      for (let x = min_x; x < max_x; x++) {
        pixels++;
        let r = img_data.data[(x + y*w)*4];
        let g = img_data.data[1 + (x + y*w)*4];
        let b = img_data.data[2 + (x + y*w)*4];

        sum_r += r;
        sum_g += g;
        sum_b += b;
      }
    }

    const avg_r = Math.floor(sum_r / pixels);
    const avg_g = Math.floor(sum_g / pixels);
    const avg_b = Math.floor(sum_b / pixels);

    return [avg_r.toString(16), avg_g.toString(16), avg_b.toString(16)];
  }

  function read_image(params) {
    const img_obj = new Image();
    params.img_obj = img_obj;
    img_obj.onload = (() => {
      draw_image(params);
    });
    img_obj.src = params.img_url;
  }

  read_image({
    img_area: 'img_area',
    img_url: 'test.png',
  });
})();
  </script>
</html>


JavaScript で実行中の script の URL を取得する方法

2022-04-27 00:09:57 | javascript
JavaScript で実行中の script の URL を取得する方法のメモ。
document.currentScript で実行中の script を取得することができます。
そして、document.currentScript.src で JavaScript の URL を取得することができます。
■HTML
<html>
<head>

<title>script</title>
</head>
<body>
</body>
<script type="text/javascript" src="script1.js"></script>
</html>

■JavaScript (script1.js)
const script_url = document.currentScript.src;
console.log('script url:' + script_url);

const url_obj = new URL(script_url)
console.log('origin: ' + url_obj.origin);

■実行結果
script url: http://localhost/script/script1.js
origin: http://localhost


jQuery での DOM 操作

2022-03-27 13:08:03 | javascript
jQuery での DOM 操作のメモ。

以下の DOM に対して追加・削除等の操作を行います。
  <div id="d1">
    <div id="d2-1">
      <div id="d3-1">
        <p id="p4-1">p4-1 text</p>
        <p id="p4-2">p4-2 text</p>
      </div>
      <div id="d3-2"></div>
    </div>
    <div id="d2-2">
      <div id="d3-3"></div>
      <div id="d3-4"></div>
      </div>
    </div>
  </div>

■末尾に追加
  $(`<div id="d2-3"></div>`).appendTo($('#d1'));
  console.log($('#d1').html());
-->
    <div id="d2-1">
      <div id="d3-1">
        <p id="p4-1">p4-1 text</p>
        <p id="p4-2">p4-2 text</p>
      </div>
      <div id="d3-2"></div>
    </div>
    <div id="d2-2">
      <div id="d3-3"></div>
      <div id="d3-4"></div>
      </div>
    <div id="d2-3"></div>

■指定要素の前に子を追加
  $(`<div id="d2-0"></div>`).insertBefore($('#d2-1'));
  console.log($('#d1').html());
-->
    <div id="d2-0"></div><div id="d2-1">
      <div id="d3-1">
        <p id="p4-1">p4-1 text</p>
        <p id="p4-2">p4-2 text</p>
      </div>
      <div id="d3-2"></div>
    </div>
    <div id="d2-2">
      <div id="d3-3"></div>
      <div id="d3-4"></div>
      </div>

■指定要素の次に子を追加
<pre>
$(`<div id="d3-5"></div>`).insertAfter($('#d3-4'));
console.log($('#d1').html());
-->
<div id="d2-1">
<div id="d3-1">
<p id="p4-1">p4-1 text</p>
<p id="p4-2">p4-2 text</p>
</div>
<div id="d3-2"></div>
</div>
<div id="d2-2">
<div id="d3-3"></div>
<div id="d3-4"></div><div id="d3-5"></div>
</div>

■指定要素を削除
  $('#d3-1').remove();
  console.log($('#d1').html());
-->
    <div id="d2-1">
      
      <div id="d3-2"></div>
    </div>
    <div id="d2-2">
      <div id="d3-3"></div>
      <div id="d3-4"></div>
      </div>

■指定要素の子を削除(空にする)
  $('#d3-1').empty();
  console.log($('#d1').html());
-->
    <div id="d2-1">
      <div id="d3-1"></div>
      <div id="d3-2"></div>
    </div>
    <div id="d2-2">
      <div id="d3-3"></div>
      <div id="d3-4"></div>
      </div>

■繰り返し
  $('#d3-1').children().each((i, node) => {
    console.log($(node).text());
  });
-->
p4-1 text
p4-2 text


jQuery でよく使うセレクタ

2022-03-27 12:38:42 | javascript
jQuery でよく使うセレクタの使い方のメモ。

以下の DOM に対してセレクタを試してみます。
  <div id="d1">
    <div id="d2-1">
      <div id="d3-1">
        <p id="p4-1">p4-1 text</p>
        <p id="p4-2">p4-2 text</p>
      </div>
      <div id="d3-2"></div>
    </div>
    <div id="d2-2">
      <div id="d3-3"></div>
      <div id="d3-4"></div>
      </div>
    </div>
  </div>

■id指定
  d = $('#d1');
  console.log(d.attr('id'));
  console.log(d.constructor.name);
-->
d1
S

■子要素のタグ指定
  ns = $('#d1 > div');
  console.log(ns.length);
  console.log(ns[0].constructor.name);
  console.log($(ns[0]).attr('id'));
  console.log($(ns[1]).attr('id'));
-->
2
HTMLDivElement
d2-1
d2-2

■子孫のタグ指定
  ps = $('#d1 p');
  console.log(ps.length);
  console.log(ps[0].constructor.name);
  console.log($(ps[0]).text());
  console.log($(ps[1]).text());
-->
2
HTMLParagraphElement
p4-1 text
p4-2 text

■子孫のタグ指定
  ps = $('#d1 p');
  console.log(ps.length);
  console.log($(ps[0]).text());
  console.log($(ps[1]).text());
-->
2
p4-1 text
p4-2 text

■子孫を検索
  d = $('#d1');
  ps = d.find('p');
  console.log(ps.length);
  console.log($(ps[0]).text());
  console.log($(ps[1]).text());
-->
2
selector1.html:82 p4-1 text
selector1.html:83 p4-2 text

■前の要素
  console.log('前の要素');
  d = $('#d2-2').prev();
  console.log($(d).attr('id'));
-->
d2-1

■次の要素
  d = $('#d2-1').next();
  console.log($(d).attr('id'));
-->
d2-2


jQuery でのイベント処理

2022-03-26 13:16:24 | javascript
jQuery でのイベント処理のメモ。

ボタンが押された場合のクリックイベントは、.click(() => {...}) で登録します。
以下の例では、「追加」ボタンを押下すると、その直下に行を追加します。
「削除」ボタンを押すとその行を削除します。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>test1</title>
</head>
<body>
<h3>jQuery でのイベント処理</h3>
<table border="1">
<tbody>
<tr>
<td><input type="text" name="memo"></td>
<td><input type="button" name="add" value="追加"></td>
</tr>
</tbody>
</table>
</body>
<script type="text/javascript" src="jquery-3.6.0.min.js"></script>
<script type="text/javascript">
function add_row(btn) {
  const new_row = $(`
  <tr>
  <td><input type="text" name="memo"></td>
  <td>
  <input type="button" name="add" value="追加">
  <input type="button" name="del" value="削除">
  </td>
  </tr>
  `);
  // ボタンが押された行の直後の行に追加
  const row = btn.parent().parent();
  new_row.insertAfter(row);

  // イベントハンドラ: 追加
  const add_btn = new_row.find('input[name="add"]');
  add_btn.click(() => { add_row(add_btn); });

  // イベントハンドラ: 削除
  const del_btn = new_row.find('input[name="del"]');
  del_btn.click(() => { del_row(del_btn); });
}

function del_row(btn) {
  // ボタンが押された行を削除
  const row = btn.parent().parent();
  row.remove();
}

(() => {
  const add_btn = $('input[name="add"]');
  add_btn.click(() => { add_row(add_btn); });
})();
</script>
</body>
</html>


d3.js で折れ線グラフを描画

2022-01-27 20:45:17 | javascript
d3.js で折れ線グラフを描画する方法のメモ。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;utf-8">
<title>graph</title>
</head>
<body>
<div id="graph"></div>
<script type="text/javascript" src="../d3.min.js"></script>
<script type="text/javascript">
const org_dataset = [
    ['01/01', 4],
    ['01/02', 5],
    ['01/03', 8],
    ['01/04', 10],
    ['01/05', 9],
    ['01/06', 12],
    ['01/07', 8],
    ['01/08', 7],
    ['01/09', 6],
    ['01/10', 8],
  ];

const dataset = [];
for (let i = 0; i < org_dataset.length; i++) {
  let d = org_dataset[i];
  dataset.push({date: d[0], x: i, y: d[1]});
}

const width = 600;
const height = 400;
const margin = { "top": 40, "bottom": 40, "right": 40, "left": 40 };
 
const svg =
	d3.select("body")
	.append("svg")
	.attr("width", margin.left+width+margin.right)
	.attr("height", margin.top+height+margin.bottom)
;

var xScale =
        d3.scaleLinear()
	.domain([0, d3.max(dataset, function(d) { return d.x; })])
	.range([margin.left, margin.left+width])
;

var yScale =
        d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d.y; })])
        .range([margin.top+height, margin.top])
;

var xAxis =
        d3.axisBottom(xScale)
        .ticks(5)
        .tickFormat((d) => { return dataset[d].date; })
;

var yAxis =
        d3.axisLeft(yScale)
        .ticks(5);
 
svg.append("g")
    .attr("transform",
          "translate(" +
          [0, margin.top+height].join(",")
          + ")")
    .call(xAxis)
    .append("text")
    .attr("fill", "black")
    .attr("x", (margin.left+width+margin.right)/2)
    .attr("y", margin.bottom/2)
    .attr("text-anchor", "middle")
    .attr("font-size", "10pt")
    .text("日付");
 
svg.append("g")
    .attr("transform",
          "translate(" +
          [margin.left, 0].join(",")
          + ")")
    .call(yAxis)
    .append("text")
    .attr("fill", "black")
    .attr("text-anchor", "middle")
    .attr("x", -(height+margin.top+margin.bottom)/2)
    .attr("y", -margin.left/2)
    .attr("transform", "rotate(-90)")
    .attr("font-size", "10pt")
    .text("値");
 
svg.append("path")
	.datum(dataset)
	.attr("fill", "none")
	.attr("stroke", "steelblue")
	.attr("stroke-width", 1.5)
	.attr("d", d3.line()
		.x(function(d) { return xScale(d.x); })
		.y(function(d) { return yScale(d.y); })
	);
</script>
</body>
</html>


d3.js で折れ線グラフを描画

2022-01-27 20:35:28 | javascript
d3.js で折れ線グラフを描画する方法のメモ。
<html>
<head>

<title>graph</title>
</head>
<body>
<script type="text/javascript" src="../d3.min.js"></script> <script type="text/javascript"> const org_dataset = [ ['01/01', 4], ['01/02', 5], ['01/03', 8], ['01/04', 10], ['01/05', 9], ['01/06', 12], ['01/07', 8], ['01/08', 7], ['01/09', 6], ['01/10', 8], ]; const dataset = []; for (let i = 0; i < org_dataset.length; i++) { let d = org_dataset[i]; dataset.push({date: d[0], x: i, y: d[1]}); } const width = 600; const height = 400; const margin = { "top": 40, "bottom": 40, "right": 40, "left": 40 }; const svg = d3.select("body") .append("svg") .attr("width", margin.left+width+margin.right) .attr("height", margin.top+height+margin.bottom) ; var xScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d.x; })]) .range([margin.left, margin.left+width]) ; var yScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d.y; })]) .range([margin.top+height, margin.top]) ; var xAxis = d3.axisBottom(xScale) .ticks(5) .tickFormat((d) => { return dataset[d].date; }) ; var yAxis = d3.axisLeft(yScale) .ticks(5); svg.append("g") .attr("transform", "translate(" + [0, margin.top+height].join(",") + ")") .call(xAxis) .append("text") .attr("fill", "black") .attr("x", (margin.left+width+margin.right)/2) .attr("y", margin.bottom/2) .attr("text-anchor", "middle") .attr("font-size", "10pt") .text("日付"); svg.append("g") .attr("transform", "translate(" + [margin.left, 0].join(",") + ")") .call(yAxis) .append("text") .attr("fill", "black") .attr("text-anchor", "middle") .attr("x", -(height+margin.top+margin.bottom)/2) .attr("y", -margin.left/2) .attr("transform", "rotate(-90)") .attr("font-size", "10pt") .text("値"); svg.append("path") .datum(dataset) .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-width", 1.5) .attr("d", d3.line() .x(function(d) { return xScale(d.x); }) .y(function(d) { return yScale(d.y); }) ); </script> </body> </html>


d3.js の散布図でのクリックイベントの設定方法

2022-01-04 21:08:42 | javascript
d3.js の散布図でクリックイベントを設定する方法のメモ。

「d3.js の散布図でマウスオーバーでメッセージを表示」の例で、以下のように .on("click", function (e) {}) を指定するとクリックイベントを処理することができます。
svg.append("g")
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function (d) { return xScale(d[0]); })
.attr("cy", function (d) { return yScale(d[1]); })
.attr("r", 10)
.attr("fill", function (d) {
   return "rgba(0, 0, " + Math.round(colorScale(d[2])) + ", 0.8)";
})
.on("mouseover", function (e) {
    const d = e.target.__data__;
    console.log(e);
    tooltip
    .style("left", e.x + 30 + 'px')
    .style("top", e.y + 'px')
    .html(
        "id: " + d[3] + ""
        + "[" + d[0] + ", " + d[1] + "]" + ""
        + "value: " + d[2]
    )
    .style("opacity", 1)
    ;
})
.on("mouseout", function (e) {
    tooltip
    .style("opacity", 0)
    ;
})
.on("click", function (e) {
    const d = e.target.__data__;
    console.log(e);
})
;


実行中の script タグの要素を取得する方法

2022-01-01 23:23:58 | javascript
実行中の script タグの要素を取得する方法のメモ。

実行中の script タグの要素は document.currentScript で取得できます。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>script</title>
</head>
<body>
<div>123</div>
<script type="text/javascript">
var e = document.currentScript;
var c = document.createElement('div')
c.innerText = 'def';
e.parentNode.append(c);
</script>
456
</body> </html>



d3.js の散布図でマウスオーバーでメッセージを表示

2021-12-03 19:41:38 | javascript
d3.js で散布図でマウスオーバーでメッセージを表示する方法のメモ。
on("mouseover", function (e) { ... }) でマウスオーバー時のイベントを処理します。

メッセージは div タグで作成しておき、マウスオーバー時に内部の html を変更して表示するようにします。
以下のようなイメージです。
const tooltip = d3.select("#scatter")
    .append("div")
    .style("position", "absolute")
    .style("text-align", "left")
    .style("background", "#ffffff")
    .style("border", "1px solid #000000")
    .style("padding", "10px")
    .style("opacity", 0)
    ;

svg.append("g")
.selectAll("circle")
.data(dataset)
.enter()
....
.on("mouseover", function (e) {
    const d = e.target.__data__;
    tooltip
    .style("left", e.x + 30 + 'px')
    .style("top", e.y + 'px')
    .html(
        "id: " + d[3] + ""
        + "[" + d[0] + ", " + d[1] + "]" + ""
        + "value: " + d[2]
    )
    .style("opacity", 1)
    ;
})
.on("mouseout", function (d) {
    tooltip
    .style("opacity", 0)
    ;
})
;

HTML 全体は以下の通りです。
<html>
<head>

<title>d3.js test</title>
</head>
<body>
d3.js test
<script src="../d3.min.js"></script> <script type="text/javascript"> const dataset = [ [0.0, 0.0, 0.0, "p00"], [1.0, 2.0, 0.1, "p01"], [1.5, 4.0, 0.2, "p02"], [2.0, 1.5, 0.3, "p03"], [1.5, 4.0, 0.4, "p04"], [2.5, 3.5, 0.5, "p05"], [1.0, 4.0, 0.6, "p06"], [1.5, 3.5, 0.7, "p07"], [0.5, 2.0, 0.8, "p08"], [1.0, 2.5, 0.9, "p09"], [1.5, 1.0, 1.0, "p10"], ]; const width = 800; const height = 600; const margin = {"top": 30, "bottom": 30, "right": 30, "left": 30}; const svg = d3.select("#scatter") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) ; const xScale = d3.scaleLinear() .domain([0, d3.max(dataset, function (d) { return d[0]; })]) .range([margin.left, margin.left + width]) ; const yScale = d3.scaleLinear() .domain([d3.max(dataset, function (d) { return d[1]; }), 0]) .range([margin.bottom, margin.bottom + height]) ; svg.append("g") .attr("class", "x_axis") .attr("transform", "translate(" + [0, height + margin.top].join(",") + ")" ) .call(d3.axisBottom(xScale).ticks(0.5) ); svg.append("g") .attr("class", "y_axis") .attr("transform", "translate(" + [margin.left, 0].join(",") + ")" ) .call(d3.axisLeft(yScale).ticks(0.5) ); const colorMax = 255; const colorMin = 80; const colorScale = d3.scaleLinear() .domain([d3.min(dataset, function (d) { return d[2]; }) , d3.max(dataset, function (d) { return d[2]; })]) .range([colorMin, colorMax]) ; const tooltip = d3.select("#scatter") .append("div") .style("position", "absolute") .style("text-align", "left") .style("background", "#ffffff") .style("border", "1px solid #000000") .style("padding", "10px") .style("opacity", 0) ; svg.append("g") .selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function (d) { return xScale(d[0]); }) .attr("cy", function (d) { return yScale(d[1]); }) .attr("r", 10) .attr("fill", function (d) { return "rgba(0, 0, " + Math.round(colorScale(d[2])) + ", 0.8)"; }) .on("mouseover", function (e) { const d = e.target.__data__; console.log(e); tooltip .style("left", e.x + 30 + 'px') .style("top", e.y + 'px') .html( "id: " + d[3] + "" + "[" + d[0] + ", " + d[1] + "]" + "" + "value: " + d[2] ) .style("opacity", 1) ; }) .on("mouseout", function (d) { tooltip .style("opacity", 0) ; }) ; </script> </body> </html>


d3.js でシンプルな散布図を描画

2021-12-03 17:24:26 | javascript
d3.js でシンプルな散布図を描画する方法のメモ。
d3.js では左上が [0, 0] となるため、yScale では range を[margin.top, margin.top + height] にしています。
<html>
<head>

<title>d3.js test</title>
</head>
<body>
d3.js test
<script src="../d3.min.js"></script> <script type="text/javascript"> const dataset = [ // x, y, 値, ID [0.0, 0.0, 0.0, "p00"], [1.0, 2.0, 0.1, "p01"], [1.5, 4.0, 0.2, "p02"], [2.0, 1.5, 0.3, "p03"], [1.5, 4.0, 0.4, "p04"], [2.5, 3.5, 0.5, "p05"], [1.0, 4.0, 0.6, "p06"], [1.5, 3.5, 0.7, "p07"], [0.5, 2.0, 0.8, "p08"], [1.0, 2.5, 0.9, "p09"], [1.5, 1.0, 1.0, "p10"], ]; const width = 800; const height = 600; const margin = {"top": 60, "bottom": 30, "right": 30, "left": 60}; const xScale = d3.scaleLinear() .domain([0, d3.max(dataset, function (d) { return d[0]; })]) .range([margin.left, margin.left + width]) ; const yScale = d3.scaleLinear() .domain([d3.max(dataset, function (d) { return d[1]; }), 0]) .range([margin.top, margin.top + height]) ; const svg = d3.select("#scatter") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) ; svg.append("g") .attr("class", "x_axis") .attr("transform", "translate(" + [0, margin.top + height].join(",") + ")" ) .call(d3.axisBottom(xScale)) ; svg.append("g") .attr("class", "y_axis") .attr("transform", "translate(" + [margin.left, 0].join(",") + ")" ) .call(d3.axisLeft(yScale)) ; const colorMax = 255; const colorMin = 80; const colorScale = d3.scaleLinear() .domain([d3.min(dataset, function (d) { return d[2]; }) , d3.max(dataset, function (d) { return d[2]; })]) .range([colorMin, colorMax]) ; svg.append("g") .selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function (d) { return xScale(d[0]); }) .attr("cy", function (d) { return yScale(d[1]); }) .attr("r", 7) .attr("fill", function (d) { return "rgba(" + Math.round(colorScale(d[2])) + ", 0, 0, 0.8)"; }) ; </script> </body> </html>


JSON データの型の判定方法

2021-10-08 23:02:41 | javascript
JSON データの型の判定方法のメモ。

判定したいデータを obj とした場合、配列は obj instanceof(Array) で判定できます。
ハッシュは obj.instanceof(Array) が false の場合に、obj instanceof(Object) で判定できます。
文字列は typeof(obj) == 'string' で判定できます。

以下は JSON のデータをコピーする関数の実行例です。
function copy_json(obj) {
    if (obj instanceof(Array)) {
        let ret = [];
        for (let i = 0; i < obj.length; i++) {
            ret[i] = obj[i];
        }
        return ret;
    }
    else if (obj instanceof(Object)) {
        let ret = {};
        for (let [key, val] of Object.entries(obj)) {
            ret[key] = val;
        }
        return ret;
    }
    else if (typeof(obj) == 'string') {
        return obj;
    }
    else {
        return obj;
    }
}

let obj = {'abc': '123', 'def': 123, 'ghi': [0, 1, 2]};
let new_obj = copy_json(obj);
console.log(JSON.stringify(new_obj));
> {"abc":"123","def":123,"ghi":[0,1,2]}


JavaScript で数値を桁数指定、桁区切りつきで表示する方法

2021-09-17 21:13:26 | javascript
JavaScript で数値を桁数指定、桁区切りつきで表示する方法のメモ。

JavaScript で数値を桁数指定して表示するには、toPrecision(桁数) を使います。
123.456.toPrecision(4);
> '123.5'

toPrecision() の結果は文字列です。

数値を桁区切りつきで表示するには、toLocaleString() を使います。
123456.789.toLocaleString();
> '123,456.789'

入力された数値を何らかの式で計算を行い、その結果を単純に表示すると、
桁数が長くなってしまうことがあります。
そのような場合、以下のようにすると桁数を抑えて表示することができます。
var org_val = 123.45;          // 元の値
var res_val = org_val * 0.777; // 計算後の値
console.log(res_val);
> 95.92065000000001            // そのまま出力すると桁数が多い

var size = String(org_val).length + 2;  // 元の値の桁数+2桁を表示
var cnv_val = res_val.toPrecision(size);
cnv_val = Number(cnv_val);
cnv_val.toLocaleString();
console.log(cnv_val);
> 95.92065                     // 桁数を抑えて出力


PDF.js でテキストをハイライトする方法

2021-06-12 14:47:51 | javascript
PDF.js で特定の文字列を含むPDF要素をハイライトする方法のメモ。

ここでは、PDF.js と PDF描画用の html ファイルは以下のようなディレクトリに配置しています。
www
├── pdfjs  # pdf.js
│     ├── build
│     ├── LICENSE
│     └── web
│           └── compressed.tracemonkey-pldi-09.pdf  # このPDFを描画
└── test
     └── test_viewer.html  # PDF描画用の html ファイル

PDF から getTextContent() で PDF のテキスト要素を取得して、
テキストが特定の条件を満たす場合(ここでは JavaScript を含む場合)に
ハイライトするようにします。
page.getTextContent().then(function (textContent) {
  ...
  textContent.items.forEach(function (item) {
    if (item['str'].match(/JavaScript/)) {
      ...
    }
  });
);

ハイライトは、PDFのテキスト要素と同じ位置・サイズに半透明の div タグを描画します。
位置、サイズは以下のように設定します。
        var item_left = item['transform'][4];
        var item_top = item['transform'][5];

        var rect = [item_left, item_top,
                    item_left + item['width'], item_top + item['height']];

        var view_rect = viewport.convertToViewportRectangle(rect);
        var abs_width = Math.abs(view_rect[0] - view_rect[2]);
        var abs_height = Math.abs(view_rect[1] - view_rect[3]);
        var abs_left = canvas_left + Math.min(view_rect[0], view_rect[2]);
        var abs_top = canvas_top + Math.min(view_rect[1], view_rect[3]);

また、ハイライト用の div タグを hl_parent に追加しておき、ページ遷移した際には前のページの div タグが残らないように、
hl_parent 内の div タグを削除します。
  while (hl_parent.firstChild) {
    hl_parent.removeChild(hl_parent.firstChild);
  }

全ソースは以下の通り。

<html>
<head>

<title>highlight</title>
</head>
<body>

highlight



<canvas id="the-canvas" style="border: solid;1px;"></canvas>



<button id="prev">Previous</button>
<button id="next">Next</button>
   


Page: /



<script src="/www/pdfjs/build/pdf.js"></script>
<script type="text/javascript">
var url = '/www/pdfjs/web/compressed.tracemonkey-pldi-09.pdf';

var pdfjsLib = window['pdfjs-dist/build/pdf'];

pdfjsLib.GlobalWorkerOptions.workerSrc = '/www/pdfjs/build/pdf.worker.js';

var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 1.2,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');

// ハイライト表示用のタグ
var hl_parent = document.getElementById('highlight_parent');

// canvas の位置
var canvas_rect = canvas.getBoundingClientRect();

// ページ内の特定テキストをハイライト
function highlightTexts(page) { while (hl_parent.firstChild) { hl_parent.removeChild(hl_parent.firstChild); }
page.getTextContent().then(function (textContent) {
var pageElement = canvas.parentElement;

var viewport = page.getViewport({'scale': scale});

// highlight
textContent.items.forEach(function (item) {
if (item['str'].match(/JavaScript/)) { var item_left = item['transform'][4]; var item_top = item['transform'][5]; var rect = [item_left, item_top, item_left + item['width'], item_top + item['height']]; var view_rect = viewport.convertToViewportRectangle(rect); var abs_width = Math.abs(view_rect[0] - view_rect[2]); var abs_height = Math.abs(view_rect[1] - view_rect[3]); var abs_left = canvas_left + Math.min(view_rect[0], view_rect[2]); var abs_top = canvas_top + Math.min(view_rect[1], view_rect[3]);
var style = 'position: absolute;'
+ ' opacity: 0.50;'
+ ' background-color: yellow;'
+ ' left:' + String(abs_left) + 'px;'
+ ' top:' + String(abs_top) + 'px;'
+ ' width:' + String(abs_width) + 'px;'
+ ' height: ' + String(abs_height) + 'px;';

var e = document.createElement('div');
e.setAttribute('style', style);
hl_parent.appendChild(e);
}
});
});
}

function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;

// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);

// Wait for rendering to finish
renderTask.promise.then(function() {
// highlight
highlightTexts(page);

pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});

document.getElementById('page_num').textContent = num;
}

function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}

function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);


function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);

pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;

// Initial/first page rendering
renderPage(pageNum);
});
</script>
</body>
</html>