- 投稿日:2020-09-18T21:30:15+09:00
[html,js]ヒストリーバック リンク書き方
- 投稿日:2020-09-18T18:51:33+09:00
Google Maps API キーをHTMLから隠す方法
Maps JavaScript API を使うとき、APIキーが心配
Google は、公式サイトで
API キーをコードに直接埋め込まないでください
と言っている割には、[公式サンプル]のコーディングが以下の様になっていて、堂々とHTMLの中にYOUR_API_KEYが登場している。
これでwebページ作ったら世界中にAPI キーを公開してしまうことになる。まるで、堂々とフリチンで公道を歩いている様な状態。公式サンプルは裸の王様か?<script defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"> </script>一応 Google のドキュメント[API キーの制限を適用する]には「HTTP リファラー(ウェブサイト)」による制限が必須だと書いてはあるが、これだけでは非常に心許ない。
皆さんご存じの通り、ウエブページの名前なんてローカルにwebサーバー建てて hosts をいじれば簡単に詐称できるし・・・(私はそんなことやってませんよ。念のため)
もちろん、知らない間に課金されないように、[API 使用の上限設定]は実施しているが、どこかの誰かにAPIキーを勝手に使われて100万回とかアクセスされたらどうしよう?と思うと夜も眠れない・・・APIキーは見えないところに隠そう
やっぱりAPIキーは、HTMLで公開したくない!!
ということで CGI を使って API キーをサーバーの中に隠しておくことにしました。1.環境変数にセット
CGI にはAPIキーを環境変数にセットした状態で渡します。私の環境は nginx + fcgiwrap で CGI を動かしているので、/etc/nginx/fcgiwrap.conf の一番下に以下の様にセット。
fcgiwrap.conflocation /cgi-bin/ { ・・・・・・ fastcgi_param GOOGLE_MAPS_API_KEY YOUR_API_KEY; <=YOUR_API_KEYを自分のキーに置き換えてください }ここは、皆さんの環境(apacheなど)毎に設定方法が異なるので、ご自分の環境に合わせて設定してください。
2.CGI を用意
方針としては、公式サンプルの
src="https://maps.googleapis.com/maps/api/js
の部分を CGI で置き換える。
私の環境では python を使っているので、こんな感じになります。getapijs.py#!/usr/bin/python # -*- coding: utf-8 -*- import requests import os url = 'https://maps.googleapis.com/maps/api/js' # 公式サンプルの HTML が src= で読んでたurl key = os.environ['GOOGLE_MAPS_API_KEY'] # 環境変数に入っている APIキーを取り出す mysrc = url + "?key=" + key # url に APIキーを連結する response = requests.get(mysrc) # google マップのサイトにアクセスして src を持ってくる print("'Content-Type': 'text/javascript; charset=UTF-8'") # HTML に返してあげるためのヘッダー print("") print(response.text)この部分も皆さんの環境毎に異なる部分なので、ご自分の使える言語で記述してください。
要は、サーバーの環境変数からAPIキーを持ってきて、url 指定して google のサイトから javascript を頂戴するということです。ヘッダーの部分は、'Content-Type': 'text/javascript; charset=UTF-8'
にしましょう。3.javascript の window.onload で CGI を呼ぶ
main.jsfunction initMap() { // 公式サンプルの内容と同じ // Initialize and add the map // The location of Uluru var uluru = {lat: -25.344, lng: 131.036}; // The map, centered at Uluru var map = new google.maps.Map(document.getElementById('map'), {zoom: 4, center: uluru}); // The marker, positioned at Uluru var marker = new google.maps.Marker({position: uluru, map: map}); } window.onload = function() { // ページを表示した後に、実行したい処理を書く。ここからが今回のポイント! fetch("/cgi-bin/getapijs.py").then(res=>{ // CGI 実行して、結果の TEXT だけを次にパスする return res.text(); }).then(mytext => { // 受け取った javascript を EVAL で実行する。 eval(mytext); }).then(() => { // 実行後の処理。公式サンプル HTML が &callback= でコールしていた部分 initMap(); }).catch(() =>{ // お好きなエラー処理をどうぞ }); }HTML には、ヘッダー部分に
<script src="main.js"></script>
を記載してます。この辺の流儀は人によって違うところなので、お好きなスタイルで良いと思います。
要は、HTML の要素を読み込んだ後(サンプルHTMLの defer の部分。私の例では window.onload)に、CGIを実行して javascript をTEXTで受け取りEVAL で実行する、という手順です。これで安心して眠れるか?
APIキー は環境変数の中にしかないので、ソースをGITHUBで公開しても問題ないし、WEBサイトのユーザーが Chrome のデベロッパーツールで HTML や JS を見ても、APIキーは見つからない。
公式サンプルが堂々とフリチンで公道を歩いているのに比べれば、奥ゆかしい出来上がりになっています。
唯一、気持ち悪いところがあるとすると「邪悪な EVAL」を使っていることです。邪悪なwebページだと思われてしまうかもしれない・・・
EVAL が禁じ手として封じられている方は今回の手法は使えませんが、APIキーを公開の場に晒すのはEVAL使うよりも危険だと思います。止めましょう。会社でEVAL禁止されている人は諦めて JAVA で重いアプリ作ってください。私は軽くHTML + javascript + python で行きます。ご参考
index.html<!DOCTYPE html> <html> <head> <style> /* Set the size of the div element that contains the map */ #map { height: 400px; /* The height is 400 pixels */ width: 100%; /* The width is the width of the web page */ } </style> <title>Hello World</title> <script src="main.js"></script> </head> <body> <h3>My Google Maps Demo</h3> <!--The div element for the map --> <div id="map"></div> </body> </html>なお、こちらの記事を参考にさせて頂いています。
[私がよく使うJSからの外部JSの読み込み方法]
- 投稿日:2020-09-18T18:44:14+09:00
都道府県に紐づく市町村をドロップダウンで表示させる
備忘録として記録
先にコードから記載
<%= form_with(model: @current_masseur, url: store_manager_business_trip_ranges_update_path(@current_masseur), method: :patch, local: true) do |f| %> <table class="prefecture table table-hover"> <thead> <!--今は東京のみを表示しているがDBには全都道府県が入っている--> <%= collection_check_boxes :prefecture, :prefecture_ids, Prefecture.all.where(id: [12, 13, 14]), :id, :name, include_hidden: false do |prefecture| %> <tr> <th> <div id="menu<%= prefecture.object.id %>"> <span class="check_box"><%= prefecture.check_box %></span> <span class="text"><%= prefecture.text %></span> </div> </th> </tr> </thead> <tbody> <tr> <td class="city-check-boxes"> <div id="city-list<%= prefecture.object.id %>"> <%= f.collection_check_boxes :city_ids, City.all, :id, :name, include_hidden: false do |city| %> <% if prefecture.object.id == city.object.prefecture_id %> <ul class="city-check-box"> <li class="city-check-box-item"> <div id="boxes"><%= city.check_box + city.text %></div> </li> </ul> <% end %> <% end %> </div> </td> </tr> <% end %> </tbody> </table> <div class="actions text-center"> <%= f.submit "登録する", class: "btn btn-primary mt-5 w-50" %> </div> <% end %<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script> <script> $(function () { // メニュー領域外をクリックしたらメニューを閉じる $(function(){ $("#menu12 .text").on('click', function(){ $("#city-list12").slideToggle('fast'); }); }); $(function(){ $('#menu13 .text').on('click', function(){ $("#city-list13").slideToggle('fast'); }); }); $(function(){ $('#menu14 .text').on('click', function(){ $("#city-list14").slideToggle('fast'); }); }); }); </script>ポイントはjqueryのメソッドslideToggleが肝。これを使用するだけでslideDownとslideUpの両方が実装できます。
また$("#menu12 .text")の#menu12。
viewファイルの<div id="menu<%= prefecture.object.id %>">から取得したものだが、同じID名がviewファイルに複数あった場合、最初の要素しか取得できない事に注意。
またslideToggleを使用するとドロップダウンはデフォルトで開いた状態になっているので変えたい場合はcssで変更する必要がある。*eachで繰り返したID要素は最初の一つしか表示されません。
参考:
http://php.o0o0.jp/article/jquery-dropdown_list
http://www.hp-stylelink.com/news/2013/11/20131122.php
https://teratail.com/questions/21542
- 投稿日:2020-09-18T18:11:30+09:00
【Typescript】Nullish Coalescing と短絡評価
はじめに
今回はNullish Coalescing と短絡評価についてです。
Javascriptの場合
const inputData = ""; const storedData = inputData || "DEFAULT TEXT"; console.log(storedData);const inputData = null; const storedData = inputData || "DEFAULT TEXT"; console.log(storedData);const inputData = undefined; const storedData = inputData || "DEFAULT TEXT"; console.log(storedData);いずれもinputDataはfalseと評価されて"DEFAULT TEXT"が出力されます。
Typescriptの場合
Nullish Coalescingを使って
const inputData = undefined; const storedData = userInput ?? "DEFAULT"; console.log(storedData);と書けます。違いは、
const inputData = ""; const storedData = userInput ?? "DEFAULT"; console.log(storedData);の時には""がtrueと評価されて空文字が出力されることです。
最後に
また新しいことを覚えたらアウトプットしたいと思います。
- 投稿日:2020-09-18T18:00:10+09:00
Twitter投稿時のevent、callbackについて(JS)
とあるお客様から依頼があり、調査した時の備忘録です。
Twiiter投稿時、投稿完了後に〇〇がしたい
ということがありました。こちらの記事では実装工数が掛からない想定で、htmlとJavascriptだけで解決を試みたときの調査内容となります。(Twitter Javascript APIのみの話)
調査過程を載せてから結果書いているので、結果だけ見たい方は下の方見に行ってください。現状の実装と分析
React 16.8.6
react-share使用(詳しくはこちら)
ボタンデザインはオリジナルです。
Twitterの画面開くときに小ウィンドウを出しています。依頼内容がreact-shareで実現できるのか、軽く調べたらTwiiterのシェアボタンでは投稿後のcallbackは実装されてなかったので、結局Twiiterのdocumentを見に行くことに。
document確認
見るとそれっぽい内容が書いてある
tweet このイベントは、ユーザーがツイートウェブインテントを使ってツイートを投稿した時(新規投稿かリプライ)に発生します。 埋め込み型ツイートへのリプライツイートや、埋め込み型タイムライン内に表示されるツイートを含みます。 twttr.events.bind( 'tweet', function (event) { // Do something there } );これ使えそう、というかこれではないか。
よく見る記事で実装だけしてみる
https://teratail.com/questions/141755
https://qiita.com/HieroglypH/items/23252479cda93f88c227
こちらの記事を読んで参考にしてReactに入れてみました。const TwitterShareButton = memo((props) => { const { url, via, hashtags } = props const onClick = (e) => { console.log('Twiiterまで移動した!!!', e) } const onShare = useCallback((e) => { console.log('shared!!!',e) }, []) useEffect(() => { twttr.widgets.load() twttr.ready(function(twttr) { twttr.events.bind('click', onClick); twttr.events.bind('tweet', onShare); }) }, []) return ( <a href='https://twitter.com/intent/tweet' className='twitterShareButton' data-url='https://twitter.com/' target="_black"> { props.children } </a> ) })html上に
window.twttr = (function (d,s,id) { var t, js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js=d.createElement(s); js.id=id; js.src="https://platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs); return window.twttr || (t = { _e: [], ready: function(f){ t._e.push(f) } }); }(document, "script", "twitter-wjs"));こちらをscriptタグに追加しています。
target='_black'にしているのは、現実装で使っているwindow.open()ですとeventが取得できないためです。
結果
結果的に以下のような動きを起こしました。
私が取ったアクションとしてはTwitterシェアボタン(オリジナルボタン)をクリック
┗ twttr.events.bind('click', onClick);が動く想定
⇓
別タブが開き、Twitter画面になり、シェアテキストが入力できるようになる
⇓
ツイートする
┗ twttr.events.bind('tweet', onShare);が動く想定というアクションを取っていきましたが、
Twitter画面に行く時点で、
このようなlogを吐き出すこととなりました。
つまりtwttr.events.bind('tweet', onShare);は理想通りの動きをしてなかったということになります。何故機能しないか
Twitterの公式フォーラムでは2015年に以下のような修正が入ったと書いてます。
https://twittercommunity.com/t/forthcoming-change-to-web-intent-events/54718以前はTwitterシェアの投稿ボタンを押し、投稿された時点でcallbackが返ってきていたそうですが、今はindentが呼び出された時点でcallbackするように変更されていたようです。
日本語訳で抜粋するとTwitterをWebサイトで使用する場合。アクションが完了した後ではなく、ユーザーがページでアクションを呼び出したときにトリガーされるようになりました。 この変更を行っているのは、過去数年の間にモバイルへの移行により、これらのイベントがページへのやり取りを伝えるための信頼性の低い代表的な手段になったためです。2015年のフォーラムなので情報が古いだけか?と感じてますが、以降フォーラムで理想としている挙動と出会うことはありませんでしたので、実装はできないということなのでしょう。
終わりに
そしたらTwitterのdocument直してくれないかな~~~~って少し思ってしまいました。
紛らわしいなと。。何か間違いがあればご指摘お願いしたいです。
読んで頂きありがとうございました。
- 投稿日:2020-09-18T17:59:01+09:00
【Typescript】オブジェクトの要素チェック
はじめに
最近Typescriptの勉強を始めました。ちょこちょこ便利だなと思った書き方を覚書していきます。今回はOptional Chainingについてです。
Javascriptの場合
const fetchedPostData = { id: "p1", author: { name: "MIKE", age: "22" }, };こんな感じのオブジェクトの要素を取り出したいなーと思った時、エラーを回避するために
if (fetchedPostData) { if (fetchedPostData.author) { console.log(fetchedPostData.author.name); } }とか、もう少しスマートに書くなら
if (fetchedPostData && fetchedPostData.author) console.log(fetchedPostData.author.name);と書きます。
Typescriptの場合
?を使ってさらに潔にかけます。
console.log(fetchedPostData?.author?.name);最後に
美しい・・・
また新しいこと覚えたら書きます。
- 投稿日:2020-09-18T17:55:56+09:00
Javascriptの基本の書き方②
はじめに
前回に引き続いてjavascriptの基本の書き方を備忘録として残します。
繰り返し文
while文
while(条件式){ 処理; }for文
for(変数の定義;条件式;変数の更新){ 処理; }配列
[値1,値2,値3]配列も一つの値としてみなされるので定数への代入が可能
配列内の特定の値の取り出し
配列名[取得したい値のインデックス]オブジェクト
オブジェクトも一つの値としてみなされるので定数への代入が可能
{プロパティ値1:値1,プロパティ値2:値2}オブジェクトの取り出し
以下の方法で可能
オブジェクト名.プロパティ値オブジェクトを要素にもつ配列
const 変数名 = [ {プロパティ値1: 値1, プロパティ値2: 値2}, {プロパティ値1: 値1, プロパティ値2: 値2} ];取り出すときは以下のように取り出す
1.配列の中のオブジェクトを取り出す時
変数名[インデックス]2.配列の中のオブジェクト内のプロパティ値に対応する値を取り出す時
変数名[インデックス].プロパティ値
- 投稿日:2020-09-18T17:39:40+09:00
Webpack4 splitChunks の対象を一部に絞る
webpack4で新たに追加された
optimization.splitChunks
の基本的な使い方はこちらの記事に譲るとして、
splitChunks
の細かいところについて。よくある書き方
module.exports = { ... optimization: { splitChunks: { cacheGroups: { vendor: { test: /node_modules/, name: 'vendor', chunks: 'initial', } } } } };こうすることで
node_modules
配下をvendor.bundle.js
というかたちで分けることができる。対象を絞る
const moduleList = ["@babel/polyfill", "react", "react-dom"]; module.exports = { ... optimization: { splitChunks: { cacheGroups: { vendor: { test: new RegExp( `[\\/]node_modules[\\/](${moduleList.join("|")})[\\/]` ), name: 'vendor', chunks: 'initial', } } } } };こうすることで、splitしたいmoduleを一部に絞ることができる。
- 投稿日:2020-09-18T17:00:51+09:00
【Nuxt.js】Nuxt文法編:component③動的コンポーネント
? この記事はWP専用です
https://wp.me/pc9NHC-GF前置き
liを使いまわして親のulによって
中のテキストを変えたりしたい!
というような場合に便利なkey属性をご紹介?
component②の続きです?♀️ul、ol、table、selectといった要素には
子要素にできるタグli、tr、optionが
決まっていますね。
これらを分けてコンポーネント化したい場合に
活躍するのがkeyです。公式: https://jp.vuejs.org/v2/guide/components.html#DOM-テンプレートパース時の警告
keyに使う値については
こちらをご覧ください?tableでいうならthは親、
trを子コンポーネントに分けると
管理がしやすいといったメリットもあります?コード①
liタグに使うコンポーネントは
is属性で指定してあげます?ListItem.vue<template> <li>{{ text }}</li> </template> <script> export default { props: ['text'] } </script>index.vue<template> <div class="page"> <ul> <li is="ListItem" v-for="(fruit, index) in fruits" :text="fruit" :key="index" > {{ fruit }} </li> </ul> </div> </template> <script> import ListItem from '~/components/Lists/ListItem.vue' export default { data () { return { fruits: ['りんご', 'みかん', 'ぶどう'], } }, components: { ListItem, }, } </script>コード②
? 続きはWPでご覧ください?
https://wp.me/pc9NHC-GF
- 投稿日:2020-09-18T15:30:57+09:00
Javascriptの基本の書き方①
はじめに
自分の個人的勉強のまとめ
備忘録として残します変数定義(代入まで)
var 変数名=値; let 変数名=値;varとletの違いは再代入が可能かどうか
(letが可能)定数定義(代入まで)
const 定数名=値;定数と変数の違いは変数は値をあとで変更できるのに対して定数は値の変更ができない。
ターミナルでの出力方法
console.log(値);テンプレートリテラル
以下のように${変数名}と記載することで変数の値を出力することが可能
let name="たけし"; console.log(`ぼくの名前は${name}です`);出力する値は`で囲む必要がある。
条件式の書き方
if (条件式){ 処理; }}の後ろに;はいらない
if...else文
if (条件式){ 処理; }else{ 処理; }else if文
if (条件式){ 処理; }else if (条件式){ 処理; }else{ 処理; }Switch文
switch (条件の値) { case 値1: 値1と条件の値が等しい時の処理; break; case 値2: 値2と条件の値が等しい時の処理; break; case 値3: 値3と条件の値が等しい時の処理; break; default: どれにも合致しなかった時の処理; break; }caseのブロックの最後に必ずbreakを入れることで処理を終了させることが大事
- 投稿日:2020-09-18T14:42:40+09:00
【JavaScript】小数点第{n}位で四捨五入・切上げ・切捨てる方法〜10^x倍して10^xで割る儀式すればよい
※確認ののち再公開します
前提
Math.round( ) は整数値を返す
小数点第{n}位で四捨五入・切上げ・切捨てたいと思ったことはありますか.
JavaScriptには小数点第●位を四捨五入する関数がありません.
Mathクラスのメソッドでは,全て小数点以下が省かれて整数が返ります.//四捨五入 Math.round(<数値>) Math.round(123.456) // 出力:123 Math.round(123.567) // 出力:124 //切り上げ Math.ceil(<数値>) Math.ceil(123.456) // 出力:124 Math.ceil(123.567) // 出力:124 //切り捨て Math.floor(<数値>) Math.floor(123.456) // 出力:123 Math.floor(123.567) // 出力:123解決するには,10^x倍して10^xで割ればよいという話です.
ケーススタディ
【問い】小数点第2位を四捨五入するには?
シンプルなパターンです.
たとえば, 79.5714が与えられたとき,
どのように79.6 を出力してあげればよいでしょうか?【答え】
Math.round(79.5714 *10 ) / 10 // 出力:79.610倍してMath処理,のちに10で割ればよいのです
補足(ひとこと)
slice と splice 間違えがち.
配列対象と文字列対象間違えがち.
int.toString()を toString(int)って書きがち.
- 投稿日:2020-09-18T12:22:14+09:00
Developper Toolがなぜか使えない時のデバック方法
はじめに
コード書き終わった! リロード!
。。。。。
なんじゃこりゃ。
Vue.jsでフロントを開発している際にCromeブラウザ、Developper Toolの両方がエラーを起こして使えなくなってしまい、デバッグをしようにもできない状態になったのでその解決方法を手順に沿って共有しようと思います。
1.コミット履歴を遡りロールバックしてみる。
まず、Gitを使用していることが前提になりますが、問題が起こったところの手前のコミット履歴からコードの差分を見てみましょう。
正常に動作していた時がコミット履歴から分かれば、原因の範囲を特定することができます。
細かい粒度でのコミットができていれば、ここでその原因が判明することもあります。
2.コメントアウト
コミット履歴を参考に、ここ原因だと思われるメソッドなどをコメントアウトしてみましょう。
原因であるところを引き当てた場合、DevelopperToolが動作します。
3.console.log
その原因に関連するコードをひたすらにconsole.logして原因箇所を絞り込んでいきましょう。
しっかりと値が渡されているか、意図していない動きがでていないか確認していきましょう。
この段階で原因箇所が特定できると思います。
終わりに
私の場合はfor文による”無限ループ”が原因でした。
コミットの粒度や意義を考えずに適当にコミットしていたので、原因を特定するのにかなり時間がかかってしまいました。
適切な粒度でのコミットについて考えるいいきっかけになりました。
参考記事
- 投稿日:2020-09-18T10:27:10+09:00
CORS設定を行いwebserver越しにRESTでYellowfinサーバーにアクセスする
今回の目的
題名のとおりなのですが、ちょっと案件で検証したこともあって、バージョンによって挙動が違うこともありなかなか苦戦したので備忘録です。
もともと、別サービスのサーバーにYellowfinのJavascriptAPIのversion3を組み込んで、ダッシュボードを表示させたいというのがやりたいことだったのですが、間にサーバーが2つあることもあり、設定もなかなか大変でした。。結構基本的なことなのですが、少しずつ解説していきます。
※ちなみに、この方法はYellowfinのバージョン9.2.2で動作確認しており、9.2.1では動作しません。SSOに関する設定
これは組み込み元のサーバーに配置するHTMLファイルの中のRequestBodyのJSONObjectの中に設定があります。
ポイントはSSO対応のためのRESTのBodyのオブジェクトの中のnoPasswordにtrueを設定することです。
このプロパティをTrueにすることでauthUserPassの値が何であっても実行できるようになります。
逆にFalseにすると、ID/Passどちらも必須になります。リポジトリDBに以下のSQLを実行する
Yellowfinのリポジトリに対して、以下のSQLで設定を追加します。
これは簡易認証を許可する設定で、これがなければパスワードなしのSSO設定は不可能です。forSSO.sqlINSERT INTO Configuration (IpOrg, ConfigTypeCode, ConfigCode, ConfigData) VALUES (1, 'SYSTEM', 'SIMPLE_AUTHENTICATION', 'TRUE');アクセスするHTMLファイルのソース
ここでAPIのリクエストをフロントエンドwebサーバーに投げています。
RESTAPIの公式ドキュメントについてはこちら
https://developers.yellowfinbi.com/dev/api-docs/yf-api.html
ダッシュボードのロードについてはこちら
http://wiki.yellowfinbi.jp/display/yfcurrent/Base+APIrest.html<html> <head> <script src="https://code.jquery.com/jquery-3.5.1.js"></script> </head> <body> <div id="reportDiv"></div> <script> var authUserId = 'authUserId@auth.com'; var authUserPass = 'dammy for noPass=true'; var adminId = 'admin@yellowfin.com.au'; var adminPassword = 'test'; var body = { signOnUser: { userName: authUserId, password: authUserPass, clientOrgRef: "" }, noPassword: true, adminUser: { userName: adminId, password: adminPassword } }; //encode to JSON var json_text = JSON.stringify(body); $.ajax({ type: 'POST', contentType: "application/json;charset=UTF-8", data: json_text, crossDomain: true, url: "http://frontend.web.server/api/rpc/login-tokens/create-sso-token", headers: { 'Authorization': 'YELLOWFIN ts=' + new Date().getTime() + ', nonce=123', "Accept": 'application/vnd.yellowfin.api-v1+json' }, success: function (res, textStatus, jqXHR) { securityToken = res.securityToken; let url = 'http://frontend.web.server/JsAPI/v3?token=' + securityToken; let tp = 'text/javascript'; let sc = document.createElement('script'); sc.src = url; sc.type = tp; document.body.appendChild(sc); setTimeout(function () { yellowfin.showLoginPrompt = false; yellowfin.init().then(() => { yellowfin.loadDashboard({ dashboardUUID: '417a0ff3-6adf-4c92-8328-98d0eeb3fd6a', element: document.querySelector('div#reportDiv') }); }); }, 1000); }, error: function (responseData, textStatus, errorThrown) { alert("error!"); } }); </script> </body> </html>フロントエンドwebサーバーの設定(AJPでのアクセス設定用)
apacheのhttpd.confに以下を追加します。AJP経由のアクセスに変更する設定になります。
80番ポートで受け付けているアクセスをAJPの8009番ポートにリダイレクトする設定です。proxy-ajp.confProxyRequests Off ProxyPass / ajp://yellowfin.server:8009/ secret=secretPass timeout=1860 ProxyPassReverse / ajp://yellowfin.server:8009/ secret=secretPassYellowfinサーバーの設定
AJPからのアクセス設定(AJPでのアクセス設定用)
先程のAJP設定を受ける設定をserver.xmlに記載します。ついでにデフォルトの8080や不要なこれまでの接続設定はコメントアウトします。
/usr/local/yellowfin/appserver/conf/server.xml<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" maxThreads="500" minSpareThreads="25" enableLookups="false" acceptCount="100" connectionTimeout="20000" URIEncoding="UTF-8" maxPostSize="10485760" maxParameterCount="50000" address="0.0.0.0" secretRequired="true" secret="secretPass" /> <!-- comment out <Connector port="8080" protocol="HTTP/1.1" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" enableLookups="false" redirectPort="8443" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="UTF-8" compression="on" compressionMinSize="512" noCompressionUserAgents="gozilla, traviata" compressibleMimeType="text/html,application/x-javascript,text/css,application/javascript,text/javascript,text/plain,text/xml,application/json,application/vnd.ms-fontobject,application/x-font-opentype,application/x-font-truetype,application/x-font-ttf,application/xml,font/eot,font/opentype,font/otf,image/svg+xml,image/vnd.microsoft.icon,image/svg,text/comma-separated-values,application/csv,application/vnd.yellowfin.api-v1+json" useSendfile="false" /> -->YellowfinサーバーのTomcat(web.xml)に以下を追加する(CORS用設定)
これがCORS対応の肝となる部分ですね。アクセス元許可の設定・プリフライトのためのOPTIONメソッドの追加・後はYellowfinのAPIに投げるためのheaderを追加します。
/usr/local/yellowfin/appserver/conf/web.xml<filter> <filter-name>CorsFilter</filter-name> <filter-class>org.apache.catalina.filters.CorsFilter</filter-class> <init-param> <param-name>cors.allowed.origins</param-name> <param-value>*</param-value> </init-param> <init-param> <param-name>cors.allowed.methods</param-name> <param-value>GET,POST,OPTIONS</param-value> </init-param> <init-param> <param-name>cors.allowed.headers</param-name> <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value> </init-param> </filter> <filter-mapping> <filter-name>CorsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>システム構成の変更(CORS用設定)
システム構成→システムタブの許可するオリジンにブラウザでアクセスするドメインを登録する。また、マルチタブサポートをOffにする。
この一連のサーバー名の設定ですが、AJPの設定も、web.xmlの設定もドメイン名ならドメイン名・IPならIPでの表記を統一しないといけないそうです。それはそうか・・・・結果
このような感じでダッシュボードの呼び出しが成功しました!これを使用すると、組み込みや外部サイトでの参照ができるようになりより利用の幅が広がりますね。
※ライセンス系統は要確認です・・
画像はダ鳥獣戯画さんからお借りしました。https://chojugiga.com/
- 投稿日:2020-09-18T09:20:48+09:00
【javascript】配列内の要素を指定INDEXの前(後)に移動
紆余曲折を経てこの形に落ち着きそう。
(追記)delete演算子を使うのはよろしくなかったかな?
引数解説
変数 型 説明 1 arr Array 処理対象の配列 2 idx Number 移動する要素のINDEX 3 mIdx Number 移動先のINDEX 4 after Boolean true : mIdxの後方に挿入
false / 省略 : 前方に挿入コード
const moveElement = (arr, idx, mIdx, after=false) => { //事前処理は省略 arr = [...arr]; if (after) mIdx++; const elm = arr[idx]; delete arr[idx]; arr.splice(mIdx, 0, elm); return Object.values(arr); }; //連番生成 const array = [...Array(10).keys()]; moveElement(array, 2, 8, true); // [0, 1, 3, 4, 5, 6, 7, 8, 2, 9] moveElement(array, 8, 2); // [0, 1, 8, 2, 3, 4, 5, 6, 7, 9]やっている事(追記)
- 移動する要素を取得
- 取得した要素の位置を
delete
でempty状態にする- 配列の長さは変わらないので指定INDEXの前後へ
splice
で挿入Object.values()
でempty状態の要素はスルーして配列を再構成falsyな要素が消えても構わない場合は、
delete
する代わりに空文字
を代入。
Object.values()
ではなくfilter(Boolean)
でreturnするのもありでしょう。
- 投稿日:2020-09-18T08:49:51+09:00
【メモ】vue-routerを使ったアプリで画面が真っ白になる
問題
vue.jsの画面が真っ白になって表示されない。
前提
- vue-routerを使っている。
- サブディレクトリにデプロイしている
TL;DR
vue-routerにbaseプロパティでサブディレクトリ(ベースURL)を教える必要がある。
もう少し詳しく説明すると
/var/www/html/subdir
にデプロイすることで、URLがhttps://www.example.com/subdir/
がアプリケーションのトップとなる場合に、ベースURLとして、/subdir/
(前後のスラッシュ必須)をvue-routerに教える必要がある。以下のように初期化処理で
base
プロパティで教えてあげる。https://router.vuejs.org/ja/api/#mode
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) const routes = [ { path: '/', component: xxxxxx, children: [ { path: '', name: 'top', component: () => import(xxxxxxx) }, { path: '/login', name: 'login', component: () => import(xxxxxxx) }, ] } ] export default new Router({ mode: 'history', base: '/subdir/', <-これ routes })今回は、どこに設置するかわからなかったので、
base
プロパティを環境変数で設定するようにした。例えば、
base
が以下のような場合は、ベースURLの判定ができずに、ルーティングできずに画面が真っ白になる。
- /subdir :最後のスラッシュがない
- // : スラッシュが2個ある。
- https://example.com/subdir/ : URLになっている
環境変数で適当に設定するようにしちゃったので、書き方が統一されなくて画面が真っ白になっていました。
一度、設定すれば問題ないので、忘れちゃいます。
自戒をこめて記載しました。
- 投稿日:2020-09-18T08:28:50+09:00
element.tagName は readonly なので,HTML 要素のタグ名を変更する関数を作った
element.tagName
は,readonly
なので1,*someElement.tagName = 'h2'
といったかんじにタグ名を変更することはできません2.しかし,たとえば<h1>
タグを<h2>
に一括して変えたいといったことは結構あります.そういったわけで,element.tagName
を変更する関数を作ってみました3.コード
※ 公開後,戻り値を
void
からElement
に変更しました.TypeScript
replace-tag-name.ts/** * @author JuthaDDA * @see [element.tagName は readonly なので, * HTML 要素のタグ名を変更する関数を作った - Qiita]( * https://qiita.com/juthaDDA/items/974fda70945750e68120) */ const replaceTagName = ( target:Element, tagName:string ):Element => { if ( ! target.parentNode ) { return target; } const replacement = document.createElement( tagName ); Array.from( target.attributes ).forEach( ( attribute ) => { const { nodeName, nodeValue } = attribute; if ( nodeValue ) { replacement.setAttribute( nodeName, nodeValue ); } } ); target.childNodes.forEach( ( node ) => replacement.appendChild( node ) ); target.parentNode.replaceChild( replacement, target ); return replacement; };JavaScript
replace-tag-name.js/** * @author JuthaDDA * @see [element.tagName は readonly なので, * HTML 要素のタグ名を変更する関数を作った - Qiita]( * https://qiita.com/juthaDDA/items/974fda70945750e68120) * @param {Element} target * @param {string} tagName * @return {Element} */ replaceTagName = ( target, tagName ) => { if ( ! target.parentNode ) { return target; } const replacement = document.createElement( tagName ); Array.from( target.attributes ).forEach( ( attribute ) => { const { nodeName, nodeValue } = attribute; if ( nodeValue ) { replacement.setAttribute( nodeName, nodeValue ); } } ); target.childNodes.forEach( ( node ) => replacement.appendChild( node ) ); target.parentNode.replaceChild( replacement, target ); return replacement; };説明
第 1 引数にタグ名を変更したい
Element
,第 2 引数に変更後のタグ名を指定します.
Document.createElement()
で生成した要素なんかは,target.parentNode
がnull
ですが,想定しうる用途をからするとif ( ! target.parentNode ) { return; }
はほとんどオマジナイみたいなものだと思います.上述のとおり
Element.tagName
はreadonly
なので,document.createElement();
で新しい要素を生成して,最後に元のtarget
を新しく生成したreplacement
に置き換えています.replacement
はElement.tagName
以外とくに設定されていない要素なので,属性と内容を元のtarget
からコピーしてくる必要があります.まず属性ですが,
Element.attributes
もこれまたreadonly
なので,そのまま *replacement.attributes = target.attributes
とか *Object.assign( replacement.attributes, target.attributes )
とか *replacement.attributes = { ...target.attributes }
とかすることはできません.なので,target.attributes
の各要素(のうち値をもつもの)ごとに,replacement.setAttribute()
してやる必要があります.内容については,
replacement.innerHTML = target.innerHTML
とか,replacement.insertAdjacentHTML( target.innerHTML )
4とかでもおおむね問題ないですが,子要素がイベント・リスナーを持っていたり,mutationObserver
等のオブザーバー系 API の監視対象となっていた場合にも簡単に対応できるので,target.childeNodes
を.forEach()
で回して,target.appendChild()
してやっています.【追記】戻り値は,タグ名の変更が成功した場合は
replacement
,失敗した場合はtarget
です.以下のように書けば,成否を判定できます.const h1Replacement = replaceTagName( h1, 'h2' ) const hasH1BeenReplaced = h1 !== h1Replacement;補足
上では “HTML 要素” と書いていますが,厳密には,
SVGElement
やMathMLElement
といった,ほかのElement
を継承する要素にも対応しているはずです(未検証).また,
target
がイベント・リスナーを持っていたり,mutationObserver
等の監視対象となっていた場合は,それらをreplacement
に移してやることはできません5.target.onClick
等のプロパティは,おそらくそのまま移すことが可能ですが,そこだけ対応するのも中途半端かなと思い,やっていません.参考記事
たとえば
<div>
から<p>
への変換といった場合には,HTMLDivElement
からHTMLParagraphElement
へとインタフェースも変わるので,まあそう簡単には変更できないわなと思います. ↩
<hx>
タグのレベルを一括して変更する関数も作ったので,後日別途記事を立てる予定です. ↩
document.innerHTML
への代入とdocument.insertAdjacentHTML()
の違いについては,innerHTML より insertAdjacentHTML を使う - Qiita が参考になります. ↩イベント・リスナーについては,GaurangTandon/checkEventAdded GitHub を使えば,うまく移せるようにできるかもしれません.おそらく
EventTarget.prototype.addEventListener
を呼び出される前に書き換えてやる必要があるので,複数のスクリプトを呼び出しているサイトだとそれなりにめんどくさそうですが.mutationObserver
等については,いまのところよさそうな方法が見当たりません. ↩
- 投稿日:2020-09-18T07:07:20+09:00
discord.jsでapiの情報を取得する
すえきゅーですどうも
今回は discord.jsでapiから情報を取得して出力する方法をここに書こうと思います使うものApi Node fetch jsonを読む力この3つです
次にNode fetch
こいつがないとダメです
$ npm install node-fetch
こいつをターミナルで実行しましょう準備完了 コードをここに置きます
コードconst discord = require("discord.js"); const fetch = require("node-fetch"); const client = new discord.Client() client.on('ready', async () => { const msg = await client.channels.cache .get("送信したいチャンネルid") //チャンネルを取得 .send("Status"); //送信 setInterval(async () => { //任意のミリ秒ごとに繰り返す const res = await fetch("Api"); //取得 if (!res.ok) { //取得できなかったら console.log(await res.text()); //コンソールにメッセージを出力する return; } const json = await res.json(); //json msg.edit({ embed: { color: 0x00ff00, title: "thinking", description: "Api test", footer: { text: "thinkingbot", }, fields: [ { name: "fields", value: json.test.status, //Apiの情報を書く json.~~~~で行ける } ] } }) }, 1500); //1.5秒ごとに繰り返すこんな感じで出力出来ると思います
何か間違ってることや質問があるならこちらに参加して質問してください最後まで見てくれてありがとうございました
- 投稿日:2020-09-18T02:08:52+09:00
gatsbyのブログテンプレートにMaterial-UI入れて見栄えを良くする -色の統一-
今回の目的
githubにあるgatsbyのブログテンプレートにMaterial-UIを入れて見栄えを良くしていきたいと思います。
まずは基本の色を決めていきましょう。前提条件
nodeやgatsbyなどはインストールしておいてください。
Windows10の環境準備は以下で参照できます。gatsby入門 チュートリアルをこなす 0.開発環境をセットアップする
ブログテンプレートをダウンロード
好きなディレクトリにて以下コマンドを実行します。
gatsby new my-blog※任意 https://github.com/gatsbyjs/gatsby-starter-blogダウンロード後に一度ブログを起動してみましょう
以下コマンドを実行します。cd my-blog(ダウンロードすると上記で入力した任意の名前のディレクトリが出来ています。) gatsby develop
http://localhost:8000
スーパーシンブル!
これをカスタマイズしていきます。Material-UIを導入
コマンドを使用して、カレントディレクトリ(ここでいうmy-blog)にて以下コマンドを実行します。
npm install -D @material-ui/core npm install -D gatsby-plugin-material-uigatsby-plugin-material-uiはMaterial-UIをGatsbyでいい感じに利用することが出来るプラグインと考えてください。
※build時にプラグインがないとエラーになるような事をどこかにありましたが、私は再現しませんでした。インストール完了後、gatsby-config.jsに以下を追記します。
gatsby-config.jsはカレントディレクトリ直下にあります。gatsby-config.js(中略) `gatsby-plugin-react-helmet`, { resolve: `gatsby-plugin-typography`, options: { pathToConfigModule: `src/utils/typography`, }, }, `gatsby-plugin-material-ui`,←これを追記これでMaterial-UIの導入は完了です。
色の設定を統一する
現時点はシンプルですが、今後機能が増えるにつれ色の設定がばらばらになる可能性があるのでサイトで使用する色を制限するためthemeを作成します。
色決め
下記サイトでいい感じに色が決めれます。
https://material.io/resources/color/#!/
私はこんな感じにしました。
src/styles/theme.jsを作成
src/styles/theme.jsを作成し以下を追記します。
src/styles/theme.jsimport { createMuiTheme } from '@material-ui/core/styles'; export const theme = createMuiTheme({ palette: { primary: { light: '#b6ffff', main: '#81d4fa', dark: '#4ba3c7', contrastText: '#FFFFFF', }, secondary: { light: '#ff93c1', main: '#ef6091', dark: '#b92a64', contrastText: '#000000', }, }, });これでこのサイトで使用する色が決まりました。
上記はまだまだカスタマイズ可能なようですがとりあえず簡単にいきましょう。
当然ですが、個別のスタイルで色を設定することをルール決めしないといけません。
複数人で作成している場合は、話し合いましょう。色の設定を修正
それでは画面に反映していきます。
src/components/layout.jsを編集します。
src/components/layout.jsimport React from "react" import { Link } from "gatsby" import { rhythm, scale } from "../utils/typography" import {theme} from "../styles/theme"; const Layout = ({ location, title, children }) => { const rootPath = `${__PATH_PREFIX__}/` let header if (location.pathname === rootPath) { header = ( <h1 style={{ ...scale(1.5), marginBottom: rhythm(1.5), marginTop: 0, backgroundColor: theme.palette.primary.main,←追記 }} > <Link style={{ boxShadow: `none`, color: theme.palette.primary.contrastText,←修正 }} to={`/`} > (中略)
画面に色が付きました。
これで、色を付けたい場合はthemeから選ぶ(または背景色白の場合は何も指定しない。)次回は
今回は以上です。
次回はMaterial-UIを使用してヘッダを追加しようと思います。ありがとうございました。
- 投稿日:2020-09-18T01:06:34+09:00
テーブルタグからほしい情報を取り出す
新型コロナウイルス感染症に関する報道発表資料(発生状況、国内の患者発生、海外の状況、その他)
https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/0000121431_00086.htmlデータサンプル
https://www.mhlw.go.jp/stf/newpage_13640.htmlconst html =
(上のテーブルをコピペする)
// カンマ区切りの数字のカンマ削除、タブを,に変換して CSVライクにする
const csv = html.replaceAll(',', '').replaceAll('\t', ',')
// 改行ごとに配列に変換
const arr = csv.split('\n')
// ほしいデータに整形
const data = arr.map(d => {
const [name, a,b ] = d.split(',')
return {name, a: parseInt(a, 10), b: parseInt(b, 10)}
})
// 感染者数の降順にする
data.sort((a, b) => -(a.a - b.a))
- 投稿日:2020-09-18T00:02:40+09:00
Automation Anywhere A2019でQRコードを生成してみる その2 ~ 文字化け対策
はじめに
【前回の記事】
Automation Anywhere A2019でQRコードを生成してみる - Qiita現在のバージョンのQR Code Generatorは日本語未対応
前回はAutomation Anywhere A2019によるQRコード生成をご紹介しましたが、
実は現在のバージョンのQR Code Generatorアクションは、日本語などの文字コードに未対応となっています。
そのため、日本語を含む文字列をQRコード化し、読み込もうとすると以下の写真のように文字化けしてしまいます。
解決方法
このQRコード生成パッケージ自体を日本語化してくれたら話が早いんですが、
今回はこのままでも何とかする方法を考えてみます。
色々方法はあると思いますが、今回は文字列をURLエンコードして、QRコード生成を生成し、
読み取り後に複合し、日本語を含む文字列に戻す方法を取ろうと思います。
(同パッケージの日本語対応依頼も出してはいますが、対応時期は不明です。)処理の流れ
- 日本語を含む文字列を生成
- JavaScriptで文字列をURLエンコードする
- URLエンコードした文字列を元にQRコードを生成する
- QRコードを読み取る
- 読み取った文字列をJavaScriptでデコード(復号)する
- 終了
実際に組んでみる
ということで、実際にやってみましょう。
JavaScript部分
エンコードに使うJavaScriptコードfunction getEncodedText(a){ return encodeURI(a); }デコードに使うJavaScriptコードfunction getDecodedText(a){ return decodeURI(a); }大枠はこんな感じ
Stepの中を展開するとこんな感じになってます
各アクションの設定値
※デコードする部分もほぼエンコード時と設定内容は一緒で、呼び出す関数名を変えるくらいです。
結果
上記をQRコード化し、それを読み取り、デコードした結果がこちら。
まとめ
ということで、日本語対応していないQRコード生成用アクションで日本語を扱ってしまうことが出来ました。
気を付けないといけないのは、QRコードで扱える最大文字数ですが、一般的な用途であれば大丈夫なんじゃないかと思っています。参考ページ、記事
URLエンコードとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
URLエンコードについておさらいしてみた - Qiita