translate and left top

常常在chrome發現一些奇怪的現象,工作上會接觸到一些地圖相關的東西,在windows7chrome拖拉map時,會發現某些marker會漂移,而且在每次會漂移的marker,都不一定是同一個,有點像是random。並且漂移時,是沒辦法click(csspointer也指標消失了),ipad上的safari也有相同問題,但是同樣的在linux上的chrome卻不會有此現象,於是就試了一下google map,發現在windows7上是正常的,於是就把lefttop,改用translate取代,發現就可以正常顯示了。

以下html結構

<div class="container" style="position:relative;overflow:hidden;">
    <div id="dragContainer" style="left:101px;top:25px;position:absolute;">
        <div class="map-image" style="position:absolute;">
            <img src="..." style="position:absolute;top:0px;left:0px;"/>
            <img src="..." style="position:absolute;top:256px;left:0px;"/>
            <img src="..." style="position:absolute;top:512px;left:0px;"/>
            ....
        <div>
        <div class="overlay" style="position::absolute;">
            <div style="width:25px;height:25px;position:absolute;left:50px;top:50px;cursor:pointer;">
                <img src="..." style="width:25px;height:25px;" />
            </div>
            ...
        </div>
    </div>
</div>

實際上在移動的是dragContainermap-image是顯示地圖圖片,overlay則是顯示相關marker

callbacks-manager(nodejs)

最近在看expresstemplate如何使用,其中使用到viewpartial,會需要傳入callback參數,如果一次需要繪製多個template,最後在傳給browser,必須要有一個監聽所有callback被呼叫的機制,所以就寫了一個簡單的plugin

安裝callback manager module

npm install callbacks-manager

example:

var callbackManager = require("callback-manager").create();
callbackManager.create("success");
setTimeout(callbackManager.getReceiver("success"),100);
setTimeout(callbackManager.getReceiver("success"),500);
callbackManager.done( "success", function(){ console.log("success"); });

這一段code主要是將create一個manager出來,將callback統一透過getReceiver回傳的介面做處理,最後callback都被呼叫後,在由done去處理。(參數可以省略->"success")

github

requirejs shim

使用同步載入,以往都是使用order plugin,在requirejs 2.0.1版,已經使用shim作為替代,可以直接定義各套件的相依性。

require.config({
    shim:{
        "sparrowApi":["module1","module2"]
    }
});

定義完後,直接require "sparrowApi" 就會自動載入module1module2了。

require.config({
    shim:{
        sparrowApi:{
            deps:["module1","module2"],
            exports:"sparrowApi"
        }
    }
});

如果使用exports這個attribute,可直接將"sparrowApi"回傳回來的值,直接定義成global變數,當然exports也可以使用function作為傳入值,然後在判斷要回傳什麼,作為global變數。

requirejs example

requirejsjavascript的套件管理library ,使用於管理套件的相依性,其中載入方式有非同步同步兩種方式。

以下是一個簡單的範例:

<!--file:index.html-->
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <script data-main="js/main" src="http://requirejs.org/docs/release/1.0.7/minified/require.js"></script> 
</body>
</html>

requirejs被載入的時候,會去讀取data-main的值,並且載入這隻script(js/main.js)。

//file:js/main.js
(function(){

    require.config({
        baseUrl:"js",
        paths:{
            order:"libs/order"
        }   
    }); 

    //require( dependencies ,callback )
    //dependencies是載入的相關套件,參數為string array
    //當載入完畢會呼叫此function,callback為function
    //先載入完js/config.js,才會載入js/module2.js
    require(["order!config","order!module2"],function( config, module2 ){
        console.log( "loaded " + module2.name );
        console.log( "version : " + config.version );
    }); 

}).call( this );
**require.config**可以配置一些設定,**baseUrl**指的是**include script**的起始路徑,而**paths**可以設定名稱,會直接對照路徑,就不需打一長串了。
其中**require**的第一個參數,是載入相關的套件,加**order!**指的會依序往下執行,第二個參數則是,當載入完成後,會呼叫的**function**。
//file:js/config.js
define([],function(){
    return {
        version:"production"
    };  
});

define function的第一個參數與require function相同,都是用來載入相關package用,而第二個參數當載入完後,return的直將會傳給requirecallback function

//file:js/module1.js
define([],function(){
    return {
        name:"module1"
    };  
});
//file:js/module2.js
define(["module1"],function( module1 ){

    console.log("loaded " + module1.name);

    return {
        name:"module2"
    };  
});

js/lib/order.js是當載入需要同步時,需要使用到的requirejs plugin,其他plugin可參考官方。

當要將js壓縮和合併成同一隻時,可以執行以下指令,他會參考build.js,這個配置檔(需先裝node.js和requirejs):

./build.sh

github example

canvas event

在canvas上監聽event,最簡單的方式,就是將event綁在canvas,但是當canvas畫上各種不同的shape時,這時就必須另外實作監聽各個shape的event。另一種方式,則是在canvas上,在疊加一塊image或html5svg(利用現成html的tag),用來監聽不規則的形狀。

用image的方式,則是搭配map和area tag,這種方式再大部份的瀏覽器都支援。

以下是個map tag搭配canvas的example:
<canvas id="canvas" width="200" height="200"></canvas>
<img src="" width="200" height="200" usemap="#map" style="border:0px;position:absolute;z-index:1;opacity:0;" id="mapImage" />

<map name="map" id="map">
  <area id="areaCircle" shape="circle" coords="75,75,10"  href="#" />
</map>

image上的usemap屬性,是用來參照map tag的名稱,而area則是設定監聽的範圍,和綁定event的tag,範例都以圓形為例。圓的coords(x,y,r),r為半徑

var $canvas = $("#canvas");

$("#mapImage").offset({
    top:$canvas.offset().top,
    left:$canvas.offset().left
});
$("#areaCircle").on("click",function(){
    alert("click");
});

//get a reference to the canvas
var ctx = $canvas[0].getContext("2d");

//draw a circle
ctx.beginPath();
ctx.arc(75, 75, 10, 0, Math.PI*2, true); 
ctx.closePath();
ctx.fill();
上面程式做的事情,先將image疊在canvas上,綁定好event,接著用canvas畫一個圓。[map tag可參考w3c](http://www.w3schools.com/tags/tag_area.asp)
 
##### svg搭配canvas的example:
<canvas id="canvas" width="200" height="200"></canvas>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg" style="position:absolute;z-index:1;opacity:0;">
   <circle id="circle" cx="75" cy="75" r="10" stroke="black" stroke-width="0" style="cursor:pointer;opacity:0;"/>
</svg>

svgmap tag不同的是,map tag必須指定image作為參照,而svg可以自己產生不同的shape,這裡的範例是用來搭配canvas使用,所以會設定為透明。

$("circle").on("click",function(){
alert("click");
});

var $canvas = $("#canvas");

$("#svg").offset({
top:$canvas.offset().top,
left:$canvas.offset().left
});

//get a reference to the canvas
var ctx = $canvas[0].getContext("2d");

//draw a circle
ctx.beginPath();
ctx.arc(75, 75, 10, 0, Math.PI2, true);
ctx.closePath();
ctx.fill();

*svg的應用方式還有多種,可以參考此頁面

GITkit API(Identity Toolkit)

gitkit是一個整合各家登入機制的api,可直接透過它去取得gmail、yahoo、hotmail…等。在使用前必須先申請api access,然後開啟Identity Toolkit服務。(經由api console)

1.申請Api Access,你會取得Client IDClient secret

2.到Service,開啟Identity Toolkit Api。( api使用次數,每天有最大限制。 )

3.申請Identity Toolkit Api,最下面的script,可貼到你需要登入的頁面。

4.在你需要登入的page,貼上script。

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/googleapis/0.0.4/googleapis.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/jsapi"></script>
<script type="text/javascript">
   google.load("identitytoolkit", "1.0", {packages: ["ac"]});
</script>
<script type="text/javascript">
$(function(){
  window.google.identitytoolkit.setConfig({
    developerKey: "{your developer key}",
    companyName: "Your company",
    callbackUrl: "https://localhost/callback", // must be a full URL
    userStatusUrl: "/userStatus", // these can just be partial paths
    loginUrl: "/login",
    signupUrl: "/signup",
    homeUrl: "/home",
    logoutUrl: "/logout",
    realm: "", // optional
    language: "en",
    idps: ["Gmail", "AOL", "Hotmail", "Yahoo"],
    tryFederatedFirst: true,
    useCachedUserStatus: false
  });
  $('#navbar').accountChooser();
});
</script>

5.接著開啟,會出現一個登入Button。

6.點擊後,會出現一個登入視窗。(導向各家的第三方驗證,最後導回callbackUrl

7.此時callback的page,將可經由第三方驗證,取得post回來的參數。然後在callback page執行以下script。registeredtrue時,會導向homeUrl,否則將會導向signupuUrl

<script type='text/javascript' src='https://ajax.googleapis.com/jsapi'></script>
<script type='text/javascript'> 
  google.load("identitytoolkit", "1.0", {packages: ["notify"]});
  window.google.identitytoolkit.notifyFederatedSuccess({ "email": "name@email.com", "registered": true });
</script>

8.最後從callback page導向回來後,在執行以下script,就會自動將登入button的ui,改成登入狀態了。

<script>
  window.google.identitytoolkit.setConfig({
    developerKey: "{your developer key}",
    companyName: "Your company",
    callbackUrl: "https://localhost/callback", // must be a full URL
    userStatusUrl: "/userStatus", // these can just be partial paths
    loginUrl: "/login",
    signupUrl: "/signup",
    homeUrl: "/home",
    logoutUrl: "/logout",
    realm: "", // optional
    language: "en",
    idps: ["Gmail", "AOL", "Hotmail", "Yahoo"],
    tryFederatedFirst: true,
    useCachedUserStatus: false
  });
  $('#navbar').accountChooser();

  //將狀態改為登入
  setTimeout(function(){ 
   window.google.identitytoolkit.showSavedAccount("login@domain.com"); 
},100); 
</script>

example for node.js

使用此api每天有最大使用限制,和許多資訊都是經過google去取得,隱私上可能會有些疑慮,但對於只要取得一個登入的識別key,可以參考看看。(關於api更多資訊)

ajax cross domain

之前有介紹用iframe做跨網域方式,那如果想直接對其他網域發送呢?我們可透過callback的機制,來實現跨網域,簡單來說就是,就是依照參數,include一隻script

例如:
function test( result ){
   alert(result);
}
$.getScript("http://localhost/jsonp.php?callback=test");
jsonp.php
echo $_GET["callback"]."(\"Hello!\")";

 

那如果想直接發送ajax,可透過實作server proxy的方式或直接在server端加入一行header,來加入可以發送網域。(可參考此link)

Access-Control-Allow-Origin:http://localhost/
ajax cross domain範例:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<script>
$.get("http://localhost:8080",function(){
    console.log(arguments);
});
</script>
server side(node.js):
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {
        'Content-Type': 'text/plain',
        "Access-Control-Allow-Origin": "http://localhost/"
  }); 
  res.end('Hello World\n');
}).listen(8080, "127.0.0.1");

flash也是透過類似的方式,當發送不同網域的request時,會先在所在網域,去讀取crossdomain.xml。(http://localhost/crossdomain.xml)

<!--?xml version="1.0"?-->
<cross-domain-policy>
<allow-access-from domain="*">
</allow-access-from></cross-domain-policy>

firefox add-on

add-on是用來開發firefox的附加元件,其中開發方式有兩種,一種是採用browser線上開發,另一種則是在本機端開發,利用command line去對執行。在開發之前,需要先下載add-on sdk。(1.0版本只能運行在firefox4以上)

sdk下載完後執行:

source bin/activate (linux環境,若是window直接執行activate即可,接著就可以執行cfx。)

接著建立一個目錄,到目錄底下執行:

cfx init   (將會建立及配置基本的目錄檔案)

如果要執行可以使用:

cfx run

匯出檔案:

cfx xpi

以下是一個簡單範例:

main.js

const widgets = require("widget");
const pageMod = require('page-mod');
const data = require('self').data;
const tabs = require("tabs");
var workers = [];
var selector = pageMod.PageMod({
  include: ['*'],
  contentScriptWhen: 'ready',
  contentScriptFile: [data.url('page.js')],
  onAttach: function(worker) {
    worker.on("message",function(data){
        console.log(data);
    }); 
    workers.push(worker);
  }   
});   

var widget = widgets.Widget({
  id: "mozilla-link",
  label: "Mozilla website",
  contentURL: "http://www.mozilla.org/favicon.ico",
  onClick: function() {
    for(var i in workers){
        workers[i].postMessage("test");
    }   
  } 
}); 

console.log("The add-on is running."); 

data目錄的page.js

self.on("message",function(data){

    console.log(data);
    self.postMessage(document.title);
}); 

附加元件,會被顯示在browser的右下角,點選後即可觸發click事件,在終端機上即可看到console.log

add-on api

chrome extension

chrome extensionchrome的擴充元件,只需要有配置manifest.json,搭配一個background_pagepopup,就可以完成一個簡單的擴充元件。

以下是一個與頁面溝通example:

manifest.json的配置:

{
  "name": "test",
  "version": "1.0",
  "background_page": "background.html",
  "permissions": [
    "tabs", "http://*/*"
  ]
}
其中background_page是主要運行在extension的程式,permissions則是可以使用的權限。
[manifest的參數配置](http://code.google.com/chrome/extensions/contextMenus.html)
 
**background.html:**
<html>
<head>
<script>

  function click() {

    chrome.tabs.executeScript(null,{file:"page.js"});
  }
  //監聽page request的event
  chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
   console.log(arguments);
  });
   //配置icon
  chrome.browserAction.setIcon({path:"icon" + current + ".png"});
  chrome.browserAction.onClicked.addListener(click);
</script>
</head>
</html>

page.js:

    chrome.extension.sendRequest({page:document.title});

接著只要將此目錄載入到擴充元件,即可使用。(擴充元件console.log,要在點選擴充元件裡"檢查運作中的檢視"的頁面,才看的到喔~!)

chrome api 和配置

example

讀取file的內容(FileReader)

讀取file的內容,可以使用FileReader,這功能是html5才加入的,依照不同瀏覽器,支援file相關物件的method,實作完整度不同。

example:
<script type="text/javascript">
  function load() {
    var finput = document.getElementById("file");
    //取得file
    var f = finput.files[0];

    if( f ){
      var r = new FileReader();
      //讀取完畢執行function
      r.onload = function(e) { console.log(e.target.result); };
        //使用utf8邊碼讀取
        //r.readAsText(f);
        //用base64格式讀取
        //r.readAsDataURL(f);
        //使用二進位方式讀取file
        r.readAsBinaryString(f);

       }
    }
</script>

<input type="file" id="file" />
<a href="#" onclick="load()">Load</a>

FileReader還有一些event可以使用,例如onloadstartonprogressonerror….等,可以用來監聽讀取的進度,另外使用dropdragover也可以利用div,製作一個框框,搭配FileReader,讓使用者方便拖拉檔案,直接載入檔案內容。

如果要支援IE8、IE9可直接使用現成的flash FileReader

w3c的定義格式