headタグに要素を追加できない。
JavaScriptでheadタグの中にscriptタグを追加しようとしたのですが、jQueryを使うとなぜか上手くいかないみたいです。
こんな感じでscript要素を作って
var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.setAttribute("src", "unko.js");
jQueryのappendメソッドでheadタグに追加したのですが、なぜか追加されません。
$("head").append(script); // ダメ
jQueryを使わずに追加したら上手くいきました。
document.getElementsByTagName("head")[0].appendChild(script); // できた
なんでー?
実験したソース
<html> <head> </head> <body> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script> <script type="text/javascript"> $(function(){ // scriptを作る var make_script = function(url){ var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.setAttribute("src", url); return script; }; var plain_js = make_script("plain.js"); var jquery_js = make_script("jquery.js"); $("head").append(jquery_js); document.getElementsByTagName("head")[0].appendChild(plain_js); var headHTML = document.getElementsByTagName("head")[0].innerHTML; puts(headHTML); function puts(str){ var text = document.createTextNode(str); document.body.appendChild(text); var br = document.createElement("br"); document.body.appendChild(br); } }); </script> </body> </html>
追記
下のページを読んで謎がとけました。
jQueryのappend,prepend,before,afterメソッドにscriptタグを含んだDOM要素なり文字列なりを渡したときは、script以外の部分はちゃんと追加されますが、scriptタグの部分は追加されずに、スクリプトがその場で実行(eval)されてしまうのだそうです。
anything from here jQuery の挙動を解読する(32):jQuery.clean() メソッド解読──jQuery解読(49)
firebugで見てもheadタグに変化はないけど、scriptはちゃんと実行されているから心配しなくてもいいよ。ということみたいです。ほんとうにありがとうございました。
Greasemonkeyメモ
いつの間にかgreasemonkeyの中でjQueryとかのライブラリが使えるようになってました。
他にも色々と知らないことがあったのでメモしておきます。
@require
スクリプトの先頭に色々書くあそこに「@require」って書くと、外部JavaScriptをincludeして使うことができます。
例:
// ==UserScript== // @name test // @include http://localhost/* // @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js // ==/UserScript== (function($){ $('body').click(function(){ alert('クリックしました。'); }); })(jQuery);
思ったこと
なぜか「$」が使えない。「jQuery」は使えるのに。なのでスクリプト全体を関数で囲んで「jQuery」を渡してあげるのがセオリーみたい。
ライブラリのURLは、 AJAX Libraries API - Google Codeにあるのを使うのが良いです。
@requireを使うスクリプトを作成する場合、右下のサルを右クリックして「新規ユーザースクリプト」で作るとうまくいきませんでした。「@require」って書かれた状態のスクリプトファイルをFirefoxにドラッグ&ドロップして、ユーザースクリプトのインストールをすることで、@requireのファイルがダウンロードされてローカルに保存されてスクリプトから利用できるようになるみたいです。私はこれを知らずに小一時間ほど頭を抱えることになったので要注意です。
@resouce
「@resouce」を使うと、スクリプトの中でCSSや画像ファイルを扱えるようになります。
// @resource name url
って指定して
GM_getResourceText('name');
って書くと、urlで指定したファイルのテキストデータが取得できます。
バイナリファイルの場合は
GM_getResourceURL('name');
って書くと、urlで指定したファイルの中身を示す「data:」形式のURLが取得できます。
例:
// ==UserScript== // @name test // @include http://localhost/* // @resource style http://gist.github.com/raw/99181/9d511e468af600a93df53e17c0ab1de1e2f17a90/gistfile1.css // @resource image http://img.f.hatena.ne.jp/images/fotolife/s/syttru/20080221/20080221011637.png // ==/UserScript== (function(){ // スタイルシートを読み込む var style = GM_getResourceText('style'); // style -> ".test_style{ color ... }" GM_addStyle(style); // div要素を作ってスタイルシートを適用してbodyに追加する var div = document.createElement('div'); div.setAttribute('class', 'test_style'); div.innerHTML = 'そうなのかー'; document.body.appendChild(div); // bodyの末尾に画像を追加する var img = document.createElement('img'); var image = GM_getResourceURL('image'); // image -> "data:image/png;base64,iVBORw0K..." img.setAttribute('src', image); img.setAttribute('alt', 'ソーナンス'); document.body.appendChild(img); })();
Amazonの書籍ページにGoogleブックのリンクをはるぐりもん
会社の人からGoogleブック検索がいろんな意味ですごいっていうページを教えてもらい、「これはすごいなあ」と思い、勢いにまかせてAmazonの書籍ページにプレビューページへのリンクをはるGreaseMonkeyを作りました。
使い方
Googleブック検索でプレビューが読める本の場合、下のようにボタンが表示されます。
プレビューが読めない本だと何も起こりません。
ボタンをクリックするとGoogleブック検索のプレビューページに飛びます。
Google App Engine Javaで遊んでみる
Google App Engineが、Javaにも対応したというニュースを帰りの電車の中で見ました。
Google App Engine Blog: Seriously this time, the new language on App Engine: Java™
上の記事は英語なので何が書かれてるのかよくわかりませんが、Dukeが飛行機に乗ってたので間違いないです。よく見るとGWTの箱も持ってますね。
Getting Started: Java - Google App Engine - Google Code
Getting Startを見ながら遊んでみます。
開発環境をダウンロードする
Downloads - Google App Engine - Google Codeから開発環境をダウンロードして
適当なフォルダに解凍します。
Eclipseのプラグインもあったのですが、インストールしようとしたらeclipseが固まってしまったので諦めました。残念です。
デモを動かしてみる
開発環境にデモがついてきました。これを動かしてみます。
コマンドラインから「bin/dev_appserver.cmd」を起動するとサーバーが動くみたいです。パラメータにwarフォルダのパスを渡します。
cd C:\work\download\appengine-java-sdk-1.2.0 bin\dev_appserver.cmd demos\guestbook\war
The server is running at http://localhost:8080/
おー!
動いたような気がする。
ブラウザでhttp://localhost:8080/を開いてみます。
おおおー!
挨拶を書き込むだけのアプリケーションですが、Googleアカウントでの認証ができたり、書き込んだデータがサーバーを再起動しても残ってたりとか、ちゃんとWebアプリケーションとして動いてるみたいです。すごい!
プロジェクトを作る
Creating a Project - Google App Engine - Google Code
Google App EngineはServlet標準にのっとった作りになってるそうです。いつものwarプロジェクトと同じフォルダ構成にして、いくつかの独自設定ファイルを置いておけばいいよー。みたいな事が書いてる気がしました。英語が不自由なので確かなことは何一つわかりませんが、多分そんな感じでしょう。
チュートリアルでは「Guestbook」というプロジェクトを作りながら進むようです。
「ecilpseのプラグインを入れた人は簡単にできるよー」と書いてましたが、私はインストールに失敗したのでコツコツと手作りでがんばるしかありません。格差社会です。
「demos/new_project_template」というフォルダがあったので、それをコピーしてフォルダ名を「Guestbook」にしました。
フォルダの中身はこんな感じです。
Guestbook/ +build.xml +COPYING +html/ | +index.html +src/ +log4j.properties +logging.properties +META-INF/ | +jdoconfig.xml +WEB-INF/ | +appengine-web.xml | +web.xml +org/ +example/ +HelloAppEngineServlet.java
上の2つは見慣れないファイルですが、それ以外はいつもどおりです。あのファイルにGoogle App Engineの設定を色々と書いていくのでしょう。そういえばpython版にも似たような名前のファイルがあったような気がする。確かyamlだったような。。。
htmlフォルダの中に静的なファイルを入れてビルドするときに直下にコピーするって感じなのだと思います。
- Guestbook/src/org/example/HelloAppEngineServlet.java
中身を見てみます。
package org.example; import java.io.IOException; import javax.servlet.http.*; public class HelloAppEngineServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("text/plain"); resp.getWriter().println("Hello, world"); } }
普通のサーブレットクラスでした。
コンパイル
テンプレートフォルダの直下にbuild.xmlがあったので、antを実行してみます。
cd C:\work\download\appengine-java-sdk-1.2.0\workspace\guestbook ant
Buildfile: build.xml compile: [mkdir] Created dir: C:\work\download\appengine-java-sdk-1.2.0\workspace\guestbook\www\WEB-INF\classes [mkdir] Created dir: C:\work\download\appengine-java-sdk-1.2.0\workspace\guestbook\www\WEB-INF\lib [javac] Compiling 1 source file to C:\work\download\appengine-java-sdk-1.2.0\workspace\guestbook\www\WEB-INF\classes enhance: [enhance] DataNucleus Enhancer (version 1.1.0) : Enhancement of classes [enhance] [enhance] DataNucleus Enhancer completed with success for 0 classes. Timings : input=16 ms, enhance=0 ms, total=16 ms. Consult the log for full details [enhance] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details war: [copy] Copying 1 file to C:\work\download\appengine-java-sdk-1.2.0\workspace\guestbook\www [copy] Copying 2 files to C:\work\download\appengine-java-sdk-1.2.0\workspace\guestbook\www\WEB-INF [enhance] DataNucleus Enhancer (version 1.1.0) : Enhancement of classes [enhance] [enhance] DataNucleus Enhancer completed with success for 0 classes. Timings : input=16 ms, enhance=0 ms, total=16 ms. Consult the log for full details [enhance] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details BUILD SUCCESSFUL Total time: 1 second
おおお。成功したみたいです。(enhanceってなんだろう…)
wwwというフォルダができていて、その中にコンパイルされたクラスと設定ファイルとhtmlファイルがwar形式で収まっていました。
サーバー起動
antからサーバーの起動もできるみたいです。
ant runserver
runserver Buildfile: build.xml BUILD FAILED Target "runserver" does not exist in the project "myproject". Total time: 0 seconds
あれ…?
失敗してしまいました。
build.xmlの中を見たら確かに「runserver」というtargetはなくて、代わりに「dev_appserver」という名前のtargetがありました。こっちを使うみたいですね。
ant dev_appserver
Buildfile: build.xml compile: enhance: [enhance] DataNucleus Enhancer (version 1.1.0) : Enhancement of classes [enhance] [enhance] DataNucleus Enhancer completed with success for 0 classes. Timings : input=31 ms, enhance=0 ms, total=31 ms. Consult the log for full details [enhance] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details war: [enhance] DataNucleus Enhancer (version 1.1.0) : Enhancement of classes [enhance] [enhance] DataNucleus Enhancer completed with success for 0 classes. Timings : input=15 ms, enhance=0 ms, total=15 ms. Consult the log for full details [enhance] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details dev_appserver: [java] The server is running at http://localhost:8080/
無事に動きました。
ブラウザでhttp://localhost:8080/を開いてみたらこんな画面が表示されました。
ハローハロー!
Users Service
Using the Users Service - Google App Engine - Google Code
Google App EngineではUserServiceというクラスを使って、現在ログインしているGoogleアカウントの情報を使うことができるみたいです。
UserService userService = UserServiceFactory.getUserService();
UserServiceのインスタンスを取得して、
User user = userService.getCurrentUser();
現在のユーザーを取得。簡単ですね。
ログインユーザーのニックネームやEmailが取得できるようです。「AuthDomain」ってのはなんだろう…
試しにやってみます。
package org.example; import java.io.IOException; import javax.servlet.http.*; import com.google.appengine.api.users.User; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; public class HelloAppEngineServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); if (user != null) { resp.setContentType("text/plain"); resp.getWriter().println("Hello, " + user.getNickname()); resp.getWriter().println(" Email: " + user.getEmail()); resp.getWriter().println(" AuthDomain: " + user.getAuthDomain()); } else { resp.sendRedirect(userService.createLoginURL(req.getRequestURI())); } } }
HTTP ERROR: 500 com/google/appengine/api/users/UserServiceFactory RequestURI=/helloappengine Caused by: java.lang.NoClassDefFoundError: com/google/appengine/api/users/UserServiceFactory
ギャー!
Google App Engineが提供するクラスを使う場合は、src/WEB-INF/libフォルダにjarファイルを入れておかないといけないようです。
「lib/user/appengine-api-1.0-sdk-1.2.0.jar」をプロジェクトフォルダの「src/WEB-INF/lib」にコピーしたらちゃんと動きました。
http://localhost:8080/helloappengine
ログインページにリダイレクトされます。
パスワードを入れる欄がないのはローカルの開発環境だからなのかな?
ログインしたらニックネームとメールアドレスが表示されました。
ローカルなので上手くいってるのかどうかわかりませんが、きっと大丈夫でしょう。
アップロード
夜も遅くなってきましたが、せっかくなのでアップロードして公開するところまでやりたいです。
データストアは飛ばして、アプリケーションのアップロードをやってみます。
うまくいけば***.appspot.comのドメインで、自分の書いたJavaアプリケーションが世界中に公開されるわけです。
楽しみだー。
・・・
cd C:\work\download\appengine-java-sdk-1.2.0 bin\appcfg.cmd update workspace\java-helloworld\www
Reading application configuration data... Beginning server interaction for java-helloworld... 0% Creating staging directory 5% Scanning for jsp files. 20% Scanning files on local disk. 25% Initiating update. java.io.IOException: Error posting to URL: http://appengine.google.com/api/appversion/create?app_id=java-helloworld&version=1& 400 Bad Request Invalid runtime specified. Unable to upload app: Error posting to URL: http://appengine.google.com/api/appversion/create?app_id=java-helloworld&version=1& 400 Bad Request Invalid runtime specified. Please see the logs [C:\DOCUME~1\m_ishida\LOCALS~1\Temp\appcfg7220633167556510812.log] for further information.
ぎゃー!
「Invalid runtime specified」
「Javaのランタイムが不正です」ってことなのかな。
うーん。わからない。残念だ。寝よう。
追記
他にもアップロードできない人がいました。
もう少し待てばできるようになるのかなー。
さらに追記
一晩寝て起きたらGoogleからメールが届いていて、全文英語で書かれていたので何がなんだかわからなかったのですが、試しにアップロードしてみたら成功しました!
http://java-helloworld.appspot.com/
やったー!
コメントとかブコメで色々と教えてくれた人たち、ありがとうございます。
scalaの練習問題やってみた
プログラミング言語 Scala Wiki - Scala練習問題
// x + y ただし「+」を使わない def add(x: Int, y: Int): Int = y match { case 0 => x case _ => add(succ(x), pred(y)) } // リストの合計 def sum(x: List[Int]): Int = x match { case x :: xs => add(x, sum(xs)) case Nil => 0 } // リストの長さ def length[A](x: List[A]): Int = x match { case x :: xs => 1 + length(xs) case Nil => 0 } // リストの要素全部に関数を適用したリストを返す def map[A, B](x: List[A], f: A => B): List[B] = x match { case x :: xs => f(x) :: map(xs, f) case Nil => Nil } // リストから条件に当てはまるものだけ抜き出す def filter[A](x: List[A], f: A => Boolean): List[A] = x match { case x :: xs => if(f(x)) x :: filter(xs, f) else filter(xs, f) case Nil => Nil } // リストとリストをガッチャンコする def append[A](x: List[A], y: List[A]): List[A] = x match { case x :: xs => x :: append(xs, y) case Nil => y } // リストの中のリストを全部ガッチャンコする def concat[A](x: List[List[A]]): List[A] = x match { case x :: xs => append(x, concat(xs)) case Nil => Nil } // リストの要素全部に関数を適用して全部ガッチャンコする def concatMap[A, B](x: List[A], f: A => List[B]): List[B] = concat(map(x, f)) // 一番でかいものを返す def maximum(x: List[Int]): Int = x match { case x :: Nil => x case x :: y :: xs if(x > y) => maximum(x :: xs) case x :: y :: xs => maximum(y :: xs) case Nil => throw new IllegalArgumentException } // リストを逆順にする def reverse[A](x: List[A]): List[A] = x match { case x :: xs => append(reverse(xs), x :: Nil) case Nil => Nil }
難しかったー><
maximumだけどうしても分からなかったのでカンニングしてしまいました。
OCaml vs. Scala パターンマッチ:Rainy Day Codings:So-net blog
↑のページ「ガード条件」のところ