20200117のHTMLに関する記事は7件です。

反復で復習html(haml)の機能

hamlで今日までで学んだこと
以下、

html
haml

の順で書き換えをどんどん書く。

<h1>Messages#index</h1>
%h1 Messages#index
<div class="font-box">
.font-box
<span class="class_value1"></span>
.span.class_value1

fontAwesomeからアイコンを取得したい場合。

<i class="far fa-image"></i>
%i.far.fa-image

link_toメソッド

<a class="yahoo_link_btn" href="http://www.yahoo.co.jp/">Yahoo</a>
= link_to "http://www.yahoo.co.jp/", class: "yahoo_link_btn" do
  Yahoo

hamlはインデントがとても大事。
学んだらこれからも追記する予定。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Apache2.4でSSIを動かすときの後方参照でハマった話

はじめに

SSI(Server Side Include)を利用して、画面の表示をそこそこディープに制御する必要があった。
その際、後方参照を用いた手法に難儀した話である。
枯れた技術かもしれないが、目をつむってほしい。

環境

  • Apache2.4
  • httpd.conf には IncludesNoExec

後方参照という言葉について

正直はっきり理解できていないが、ここでは正規表現等を用いて条件マッチした際、特定の箇所を抜くときに利用する特殊変数(\$1、$2...)のことを指す。

$1がうまく機能しない?

さて、ハマった記述は以下の形である。

false.shtml
<!--#If expr="%{QUERY_STRING} =~ /age=([0-9]+)/" -->
<!--#set var="age" value="$1" -->
<!--#Endif -->

期待する$1の返却値はクエリストリング内のage=に記載される数値だが、これで変数ageをechoすると(none)が返される。
Apacheの公式ドキュメントにサンプルとしてほぼ同一の記載があるのに、だ。

$0にはなにがある?

上記コードの\$1を\$0にしechoすると、age=数値が表示される。
つまり、まったく後方参照が利用できないというわけではなさそうだ。

結論

以下の記述であれば、期待する値を取得できることが分かった。

true.shtml
<!--#If expr="%{QUERY_STRING} =~ /age=([0-9]+)/" && $1 =~ /([0-9]+)/" -->
<!--#set var="age" value="$0" -->
<!--#Endif -->

http://~~~/?age=123

123

読解してみると、If文に合致する箇所は数値の部分であり、
\$0は条件合致した内容をすべて表示するため、これで数値箇所のみを取り出せることが分かった。

なぜこうなるか(考察)

\$1以降の後方参照は、If文と同列でないと取り扱えないようである。
※濁した書き方なのは、正式な仕様がどこにも見つからないからだ。

調査にだいぶ時間がかかったのは、日本語のドキュメントで本問題を取り扱ったものが見当たらなかったからである。
(stackoverflow には似たような質問が散見された)
何らかの事由で、SSIを利用し条件マッチによる制御を行う必要がある諸兄の一助になれば幸いである。

補足

Apache2.2では、If文と同列でなくとも後方参照は動作するようである。
そもそもApache2.4になってからSSIにおける変数の記述や正規表現マッチの書き方が微妙に変わっており、
日本語のナレッジも少ない為、ディープに利用している諸兄は是非発信していって欲しい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTMLマイテンプレート

自分用です。

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="utf-8">
  <script src="javascript.js"></script>
  <link rel="stylesheet" type="text/css" media="screen, tv" href="browse.css"></link>
 <script></script>
  <style></style>

  <title>マイテンプレート</title>
</head>
<body>

</body>
</html>

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M5StickCのディスプレイに現在の天気を表示する。

はじめに

obnizOSを搭載したM5StickCのディスプレイに現在の天気を表示したいと思います。

HTMLとJavaScriptしか使わない簡単なコードなので、プログラミング初心者にも優しくなっています。

天気を取得するにはOpenWeatherMapという無料のAPIを使用します。

完成品

StickC_weather.jpg

用意するもの

作成手順

1. OpenWeatherMapにサインアップする

OpenWeatherMapにアクセスし、サインアップをしてください。

その後、サインアップ完了のメールが届きます。メールに記載してあるAPI Keyを控えておいてください。
API Keyは、Webサイトにログイン後、API Keysというページからも確認できます。

2. プログラムを書く

最終プログラムは以下になります。

<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@3.2.0/obniz.js" crossorigin="anonymous"></script>
  <script src="https://unpkg.com/m5stickcjs/m5stickc.js"></script>
</head>
<body>
  <canvas id="canvas" width="80" height="160"></canvas>
  <script>
    const m5 = new M5StickC('OBNIZ_ID_HERE');
    m5.onconnect = async function () {
      console.log("connected");
      await m5.m5display.onWait();

      let ctx = document.getElementById("canvas").getContext("2d");
      let baseURL = "http://api.openweathermap.org";
      let area = "/data/2.5/weather?q=Tokyo,jp";
      let key = "&APPID=APPID_HERE";
      let url = baseURL + area + key;
      let weatherListEn = ["Clouds","Rain","Clear","Snow","Drizzle","Thunderstorm"];
      let weatherListJa = ["くもり","あめ","はれ","ゆき","きりさめ","かみなり"];

      $.ajax({
        url: url,
        type: 'GET',
        dataType: "json",
      })
      .done(function(response) {
        weather = response.weather[0].main;
        for(let i = 0; i < weatherListEn.length; i++){
          if(weather === weatherListEn[i]){
            weather = weatherListJa[i];
            break;
          }
        }
        console.log(weather);
        ctx.clearRect(0, 0, m5.m5display.width, m5.m5display.height);
        ctx.fillStyle = "#fff";
        ctx.font = "30px sans-serif";
        ctx.translate(m5.m5display.width - 30, m5.m5display.height - 10) ;  
        ctx.rotate(-90 * Math.PI / 180);
        ctx.fillText(weather, 0, 0);
        m5.m5display.draw(ctx);
      });
    };
  </script>
</body>
</html>


コードの解説

obniz.js, m5stickc.jsをそれぞれ読み込みます。
obniz.jsを読み込んだ後に、m5stickc.jsを読み込むようにしてください。

<script src="https://aframe.io/releases/latest/aframe.min.js"></script>
<script src="https://unpkg.com/obniz@3.2.0/obniz.js" crossorigin="anonymous"></script>
<script src="https://unpkg.com/m5stickcjs/m5stickc.js"></script>


次に、M5StickCのインスタンスを作成します。OBNIZ_ID_HEREにはあなたのobniz_idを入れてください。

const m5 = new M5StickC('OBNIZ_ID_HERE');


このコードではディスプレイの初期化処理をしています。

await m5.m5display.onWait();


次に、URLを指定します。baseURLはこのままで大丈夫です。
areaは「Tokyo,jp」の部分に調べたい都市の名前を入れてください。今回は東京の天気を取得します。
keyには「APPID_HERE」の部分に先ほど控えたOpenWeatherMapのAPI Keyを入力してください。

let baseURL = "http://api.openweathermap.org";
let area = "/data/2.5/weather?q=Tokyo,jp";
let key = "&APPID=APPID_HERE";


以下のコードでは、APIが返す天気の一覧とそれに対応する日本語を配列に格納しています。
本当はもう少し天気の種類は多いのですが、頻度が少ないので割愛しています。

let weatherListEn = ["Clouds","Rain","Clear","Snow","Drizzle","Thunderstorm"];
let weatherListJa = ["くもり","あめ","はれ","ゆき","きりさめ","かみなり"];


その後、AjaxでHTTP通信を行なっています。戻り値のresponse.weather[0].mainに天気が格納されているので変数weatherに代入します。
戻り値は英語なので日本語への変換を以下のコードで行います。

for(let i = 0; i < weatherListEn.length; i++){
  if(weather === weatherListEn[i]){
     weather = weatherListJa[i];
     break;
  }
}


今回はcanvasを利用して文字を表示します。canvasを使用することで文字を横向きに表示することが可能です。
ctx.translate()とctx.rotate()で文字を90度回転させています。
m5.m5display.draw()の引数にはcanvasオブジェクトのみ指定できます。

ctx.clearRect(0, 0, m5.m5display.width, m5.m5display.height);
ctx.fillStyle = "#fff";
ctx.font = "30px sans-serif";
ctx.translate(m5.m5display.width - 30, m5.m5display.height - 10) ;  
ctx.rotate(-90 * Math.PI / 180);
ctx.fillText(weather, 0, 0);
m5.m5display.draw(ctx);

3. M5StickCを電源に繋ぐ

電源が入ってもディスプレイには何も表示されません。起動したのかわかりづらいですが、電源ボタンを何度も押さないようにしましょう。

4. 実行

上手くいくと、このように現在の天気が表示されます。

StickC_weather.jpg

最後に

このコードにobnizのdeveloper's consoleからサーバレスイベントを使用すれば、毎日朝8時に天気を自動で取得して表示したり、30分毎に表示したりすることが可能です。

わざわざ、アプリやWebで天気を調べる必要もなくなるので是非作ってみてはいかがでしょうか。

最後まで読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

materializeを使ったformのselectタグで、iOS13だと正しく選択できない

materializeとは?

Googleが作ったWeb用のフロントエンドフレームワークです。
レスポンシブデザインに対応していて、BootStrapみたいな感じで使えるので使いやすいです。
何よりいいのが、Bootstrapみたいに使えるのにBootstrapっぽくないので「あ、こいつフロントエンドはお手軽に済ましたな!」みたいに思われにくく、作り込んだ感が出ます

iconも豊富でカラーバリエーションも多いので、作り込まれたサイト感が出しやすいです。
またフラットなデザインなので、UnDrawのイメージともなじみやすくよきです。
最近流行りの感じのサイトをお手軽に作ることができます。

https://materializecss.com/

Bootstrapから抜け出して次に行きたい!という人、ぜひmaterializeCSSを使いましょう。
日本語の情報がまだ少ないっぽいので、もっとやれるひとを増やしていきたいのです。

問題の詳細

さて、そんなすばらしいmaterializeですが、ちょっと不具合があったのです。
formのselectはおしゃれで使いやすいんですが、iOS13では正しく選択できません。
(正確にはiOS13のChromeで正しく動きませんでした、Safariはみていません)

materializeのselectタグは、JavaScriptで動かしているみたいなんですが、それに不具合があったようです。
githubのisueに問題が報告されています。
https://github.com/Dogfalo/materialize/issues/6444

解決方法

「じゃあどうすりゃいいんだよ!」ってなるとおもいますが、こちらの不具合はすでに修正されています。
ただリリースはされていないので、githubからソースをダウンロードして、js/select.jsだけを追加で読み込んでください。

手順

  1. materializeCSSのソースを一式落とす。https://github.com/Dogfalo/materialize
  2. 自分のプロジェクトの所定の場所に、js/selec.jsを移す。
  3. 自分のプロジェクトで、materializeCSSを読み込んだあとに、select.jsを読み込む。
index.html
    <script type="text/javascript" src="{% static 'materialize/js/select.js'%}"></script>
    <script type="text/javascript" src="{% static 'user/js/user.js'%}"></script>

確認

iOS13で試してみる。ちゃんと選べる!かんぺき!

おわりに

たぶん次のリリースで修正されるとは思いますが、それまでは悩む人も多そうなので記事にしました。
繰り返しになりますがmaterializeCSSを使おう!そして日本語情報を増やしていきましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゲーム2を作ってみた

タイトルはyui2となっています。

https://syui.cf/imgame2

まずロゴを作りました。数字の部分、最初はフォントを使おうと思ったんですが、めんどくさいですしね、でもしっくりこなかったので、自分で作る羽目になりました、2です。

今回もアイテムから背景、キャラクター、シナリオなどを設計しました。

例えば、モンスターボックスというアイテムが登場するのですが、以下のようなデザインから構成されています。なんとなく線を引いたり、消したり、塗りつぶしてみたり。

シナリオに関しては、最初のシリーズで謎だった部分の伏線がいくつか回収されていると思います。

イラストで描いている背景、アイテム、キャラクターは方向性がそれぞれに決まっているのでやりやすいです。

やらないこととやることを決めていて、やらないことはやりません。それは面白くないことだったり、めんどくさいことだったり、やたら時間がかかることだったり。

シナリオに関しては、やはり非公開で書いていた小説が原案になっています。シリーズ1では暗くなってしまうので、バッサリと切り捨てた部分を少し拾いました。

キャラクターに関しては、キョウスケ、ポンタ、オクトカットはゲームからの参加で、それによって原案キャラも若干影響を受けていると思います。

ここから少しキャラクターに関する話をします。

例えば、yuiに出てくるキャラで私はどれに当てはまるのかってよく聞かれるんですが...うそですきかれたことありません!で、自分がどのキャラに当たるかと言うとそりゃもちろん、アイですよ!...という冗談は置いといて、私はおそらく、誰でもあって、誰でもないのだと思います。

私は以前、こんなコメントを見たことがあって、作者が自分のキャラのセリフにはっとさせられたという話に「作者は自分の思いをキャラAに語らせていて感動的」とコメントがついていました。

これですね、大半の人がコメントと同じような意見を持つと思うんですけど、私が思ったのはおそらくそうではないなと。

作者が作ったキャラでも、あいつらは勝手に動いて勝手にしゃべるので、特に作者がこうしゃべらせているみたいなことって少ないです。作者は単にキャラが勝手に喋ったそれをですね、メモしたり、記録したりですね、するだけですから、おそらく、本当に自分が作ったキャラのセリフにはっとさせられたんだろうなーって思ったんです。

一方で、そうは言っても作者が作ってんでしょと言われると、確かにそうで、それはやはり、どこかにあるからだと思うんです。

なので、私は、どのキャラでもないし、どのキャラでもあるなあと今の所はそう思ってたり。

話を変えて、次はモンスターの話です。

(本作ではまだ未登場)

今回は、モンスターが登場しますが、モンスターを作るの結構大変でした。特に、アイテムの方向性でデザインするのか、キャラクターの方向性でデザインするのかで迷いました。なので、その中間をとって、基本は四角や三角の図形で構成するものの、やはり躍動的に表現する部分も可という方向で行くことにしました。各々の部品もシンプルにすることを心がけ、あまり複雑にしてしまうと、あとで描くの大変ですからね...。

あと、配色ですが、私は1キャラ、1フィールド、1アイテムにつき3,4色と決めているところがありますので、モンスターも同じ。それでも配色は、難しかったし、悩みます。ただ、鳥なら鳥、ネコならネコ、ドラゴンならドラゴンで、一番に思いつく色ってあるじゃないですか。私の場合、それを基本色にしました。

最後にキャラクター紹介ページを作りました。

https://syui.cf/gamechara/

これは、漫画とか読む人はわかると思うんですが、冒頭にありますよね。私は全く見ないんですけど、というかゲームも説明書すら読まなかったり...でも作る側からすると、やっぱり「わかりにくいかな、こいつ誰ってなったりしない、途中から入って大丈夫?」みたいなことをちょっとだけ気にします...うそ、すごく気にします。なので、作りました。

ゲーム作るのとか、面倒でしたけど、でも、面白かったですよ、楽しかった。

次はどんなお話にしましょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

あえてseleniumを使わずにフォームに入力してみる

はじめに

「あえて」というほどでもないのですが、Pythonでフォームの編集を調べているとseleniumが良く出てくるのでRequests+BeautifulSoupで実装してみました。

以前投稿した記事の発展編としてちょうどいいのでQiitaのプロフィール文を編集してみました。

編集用関数

編集可能な多くの項目はtypeが['text', 'url', 'checkbox']のいずれかであることがソースから判明したため、ほとんど手打ちする必要なく取得できました。
取得もれである'user[desctiption]]'を追加するついでに編集すればpost用データの完成です。なぜかemail公開のチェックボックスがデフォルトでチェック済みになっていたため0に修正しています。データの作成ができたらログイン時のセッションを使ってPostすれば完成です。

def edit_profile(session):
    profile_page_data = get_page_data(session, profile_url)
    bs_profile_page = BeautifulSoup(profile_page_data.text, 'html.parser')
    authenticity_token = str(bs_profile_page.find(attrs={'name':'authenticity_token'}).get('value'))
    post_data = {
        'utf-8':'✓',
        '_method': 'put',
        'authenticity_token':authenticity_token
    }
    response = bs_profile_page.find_all(attrs={'type':['text', 'url', 'checkbox']})
    for i in response:
        value = i.get('value')
        post_data[i.get('name')] = value
    post_data['user[public_email]'] = 0 # なぜかデフォルトで1
    post_data['user[description]'] = '編集したよ!!!!'
    print(post_data)
    response = session.post(profile_url, post_data)
    print(response)

いざ実行

前回作成したプログラムに上で記載したedit_profile()を追加すればプログラムの完成です。

import requests
import os
from bs4 import BeautifulSoup
import json


user_name = 'user_name'
user_password = 'user_password'
login_url = 'https://qiita.com/login'
profile_url = 'https://qiita.com/settings/profile'


def get_authenticity_token(session, login_url):
    page_data = get_page_data(session, login_url)
    bs_page_data = BeautifulSoup(page_data.text, 'html.parser')
    authenticity_token = str(bs_login_page.find(attrs={'name':'authenticity_token'}).get('value'))
    return bs_page_data, authenticity_token


def get_page_data(session, url):
    response = session.get(url)
    response.encoding = response.apparent_encoding
    return response


def login(session):
    login_form = {
        'utf-8':'✓',
        'authenticity_token':'token',
        'identity':user_name,
        'password':user_password
    }
    bs_login_page, authenticity_token = get_authenticity_token(session, login_url)
    login_form['authenticity_token'] = authenticity_token
    session.post(login_url, login_form)


def edit_profile(session):
    bs_profile_page, authenticity_token = get_authenticity_token(session, profile_url)
    post_data = {
        'utf-8':'✓',
        '_method': 'put',
        'authenticity_token':authenticity_token
    }
    response = bs_profile_page.find_all(attrs={'type':['text', 'url', 'checkbox', 'textarea']})
    for i in response:
        value = i.get('value')
        post_data[i.get('name')] = value
    post_data['user[public_email]'] = 0 # なぜかデフォルトで1
    post_data['user[description]'] = '編集したよ!!!!'
    print(post_data)
    response = session.post(profile_url, post_data)
    print(response)

if __name__ == '__main__':
    session = requests.Session()
    login(session)
    edit_profile(session)

実行してResponse [200]が返ってくれば成功です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む