2010年8月24日火曜日

IE対策 - クリックEventの取得について

JavaScriptを使用して、イベント(Event)を取得し、そのイベントの動作を
制御したい場合に以下の点でハマることがあります。

・確認画面(windows.confirm)やモーダルウィンドウ($.modal)などを使用して
ダイアログを開いた状態のまま処理を停止したい時に、停止すべき"Event"を取得することができない

実際のソースコードでは以下のような場合にInternet Explorer 7および8で上手く動きません。

【例1】

            function showDetail( event , kwds , argv ){
                event.preventDefault();    //イベントを取得してそのイベントを一時的に止めておくための処理
            }

            <button onclick="showDetail(event,message,value)">クリック</button>


上記のソースで上手く動かないポイントは「event.preventDefault()」を実行した際に、
引数で「event」が渡っているにも関わらず、IEのJavaScript判定ではこのeventを取得
出来ない、というところです。

これを正しく動作するようにするためには以下のように書き換える必要があります。


【例1改】

            function showDetail( event , kwds , argv ){
                $("#clickme").click(function(event){
                    event.preventDefault();    //イベントを取得してそのイベントを一時的に止めておくための処理
                });
            }

            <button id="clickme" onclick="showDetail(event,message,value)">クリック</button>


上記のように「あるidをクリックした」という事実を「.click」や「.submit」を使用して
その引数にeventを渡してあげることでIE7&8でもSafariでもFirefoxでもChromeでも正しく動くようになります。

2010年8月13日金曜日

Google Calender API | feedの取得

Google Calendar の予定などをApp Engine上に読み込むためには
Google Data Library(GDATA)というものを使用します。

【GDATA Python Client Library】のダウンロード
http://code.google.com/p/gdata-python-client/
2010年8月13日現在、最新版は2.0.11です。

→ファイルをダウンロードしたら展開し、「src」フォルダ内の
「gdata」「atom」の2つのフォルダを、自身のアプリケーションの
プロジェクトフォルダのルートにドラッグ&ドロップで移動してください。


このデータの取得方法において、まず始めにやらなくてはならないことが、
「ユーザの許可を得る」ということです。

このユーザの許可を得る方法としては

・アクセスするユーザのアカウント名/パスワードをあらかじめ
    ソースにハードコーディングで仕込んでおく「Client Login」

・ユーザのデータへアクセスする必要がある場合にユーザに対して
    直接得る「AuthSub」

の2つの認証方法があります。※1

アクセスするユーザが特定されている場合には「Client Login」を使用したほうが
実装は簡単です。
しかし複数のユーザが存在することが前提となるGoogle Apps環境などに合わせて開発を
行う場合には「AuthSub」を使用しなくてはなりません。

ここでは
「AuthSubによる認証トークンの取得とデータ(Feed)の取得」
について説明します。




【AuthSubによる認証トークンの取得とデータの取得】


# -*- coding: utf_8 -*-
#!/usr/bin/env python

############################# Import  Library ##############################
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp import util
try:
  from xml.etree import ElementTree # for Python 2.5 users
except ImportError:
  from elementtree import ElementTree
import gdata.calendar.service
import gdata.service
import atom.service
import gdata.calendar
import gdata.auth
import gdata.alt.appengine
import atom
import getopt
import sys
import string
import time
import cgi
import logging
import codecs
import os
############################# Import Library ##############################


################################ Class #################################
#url:/
class MainHandler(webapp.RequestHandler):
    def get(self):
        # Googleサービス(Scope)への接続許可を求めるためのリンクを生成する。
        authSubUrl = GetAuthSubUrl();
        a_url = u'Googleサービスへアクセス' % authSubUrl
        self.response.out.write(str(a_url.encode('utf-8')))

#url:/calendar_view
class CalendarViewer(webapp.RequestHandler):
    layout_file = 'calendar.html'
    
    def __init__(self):
        # calendar_clientにGoogleカレンダーサービスをセットし、初期値の設定を行う。
        self.calendar_client = gdata.calendar.service.CalendarService()
        gdata.alt.appengine.run_on_appengine(
            self.calendar_client,store_tokens = True, single_user_mode = True
        )
        
    def get(self):
        # Googleサービス(Scope)への接続許可を求めるためのリンクを生成する。
        authSubUrl = GetAuthSubUrl();
        a_url = u'Googleサービスへアクセス' % authSubUrl
        
        token_request_url = None
        cal_list          = []
        cal_list_detail   = []
        # URLからトークンを取得する
        auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
        if auth_token:
            try:
                # 取得したトークンをセッショントークンにアップグレードして
                # AuthSubTokenとしてセットする
                self.calendar_client.SetAuthSubToken(
                    self.calendar_client.upgrade_to_session_token(auth_token)
                )
            except gdata.service.TokenUpgradeFailed:
                self.redirect('/')
                return
            
            # アップデートされたトークンがセットされたcalendar_clientで
            # カレンダーの予定を取得するGetCalendarEventFeed()を実行する
            feed = self.calendar_client.GetCalendarEventFeed()
            
            # feed.entryにカレンダーのデータの配列が入っているのでループで取得する
            # ここでは例としてキー・バリューを取り出します。
            #(予定のタイトル開始日時・終了日時が含まれた配列がcal_listにセットされます。    
            cal_list = [[i.title.text,i.when[0].start_time,i.when[0].end_time] for i in feed.entry]
     
        ValueList = {
        'a_url':a_url,
        'cal_list':cal_list        # html上でループして値を取得・表示できる
        }

        fpath = os.path.join(os.path.dirname(__file__),'layouts',self.layout_file)
        html = template.render(fpath,ValueList)
        self.response.out.write(html)

################################ Class #################################



############################# Custom Method ############################

# Googleサービス(Scope)への接続許可を求めるためのリンクを生成する。
def GetAuthSubUrl():
    next = 'http://calender-retrieve-sample.appspot.com/calendar_view'
    scope = 'http://www.google.com/calendar/feeds/default/allcalendars/full'
    secure = False
    session = True
    calendar_service = gdata.calendar.service.CalendarService()
    return calendar_service.GenerateAuthSubURL(next, scope, secure, session);

############################# Custom Method ############################


################################# main #################################
def main():
    application = webapp.WSGIApplication([
        ('/', MainHandler),
        ('/calendar_view',CalendarViewer)
    ],debug=True)
    util.run_wsgi_app(application)

if __name__ == '__main__':
    main()
################################# main #################################

2010年8月8日日曜日

iPhone/iPad向けWebアプリ開発時のローカルファイルキャッシュ

iPhone/iPad向けのWebアプリケーションを開発する際、SenchaやjQTouchなどの
ライブラリーを活用してWebアプリケーションをあたかもネイティブアプリのように
振舞わせるわけだが、その際に避けて通れないのが
「ライブラリ・CSSなどのロード時の速度低下」です。

ライブラリやCSSなどのファイルのロードがかなり時間を食ってしまい、
利用者に「このページ遅いな」と思われてしまいます。

それを防ぐためには
「iPhone/iPadのフラッシュメモリ内にJavaScriptやCSSなどのファイルを保存させる」
という設定を行ないます。

この設定には「manifest」ファイルをサーバ側で用意し、manifestファイルには、
「どのファイルをローカルキャッシュさせるか」を記述してあげる必要があります。


以下に記述例を記載します。

使用するファイルの構成は以下の通りとなります。

【プロジェクトフォルダ内】
CSSファイル
/css/js/sencha_icons/mystyle.css
(元々app.yamlに以下の
- url: /css
  static_dir: css
という静的ファイルディレクトリ指定の設定をしています。)

JavaScirptファイル
/css/js/sencha_icons/ext-touch.js
/css/js/sencha_icons/index.js

作成したmanifestファイル
/css/js/sencha_icons/sitefile.manifest


1.まずはじめに、使用するHTMLファイル内のソースに


と記述します。
(example.manifestはexampleという名前のマニフェストファイルとなります。)


2.app.yamlに以下の内容を記述します。




- url: /css/js/sencha_icons/sitefile.manifest                        →所在URL
  static_files: /css/js/sencha_icons/sitefile.manifest          →ファイルの在処
  mime_type: text/cache-manifest                                        →mime_typeの指定
(↑これがないとマニフェストファイルとして認識されません。)
  upload: /css/js/sencha_icons/sitefile.manifest                →ファイルのアップロード先



3.sitefile.manifestファイル(ファイル名はsitefileでなくてもOK)を作成し、以下の内容を記述します。

CACHE:
mystyle.css
ext-touch.js
index.js

4.デプロイしてiPhone/iPadのブラウザで再読込する。


これで上記の3つのファイルがiPhone/iPadのローカルに保存され、
ページのロードが高速になります。



参考URL: http://www.html5rocks.com/tutorials/appcache/beginner/