tedshd's DevNote

Develop & Design Note by Ted

Your posts match “ Google ” tag:

Google map api - research log

Google map api - research log

Introduction

使用 Google Maps JavaScript v3
static map 嵌入地圖到網頁中以圖片方式呈現
使用限制 基本上不要一日載超過 25000 次都沒事
區域化 - 語系

Library

Init map

<!DOCTYPE html>
<!--使用 <!DOCTYPE html> 宣告,將應用程式宣告為 HTML5-->
<html>
<head>
<meta charset="utf-8">
<title>Google_map_api_basic</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<!--最新的瀏覽器會將使用此 DOCTYPE 宣告的內容以「標準模式」呈現,也就是說,您的應用程式應更具備跨瀏覽器相容性。此外,DOCTYPE 也設計成會逐漸適應,但無法理解它的瀏覽器將會忽略它,而採用「快速模式」顯示內容。請注意,有些 CSS 在快速模式中可以運作,但在標準模式中卻無效。具體來說,所有百分比的大小都必須繼承父區塊項目,但這些上階中如有任何一個無法指定大小,就會假設為 0 x 0 像素大小。基於這個理由,我們加入了下列 <style> 宣告-->
<style type="text/css">
html {
    height: 100%;
}
body {
    height: 100%;
    margin: 0px;
    padding: 0px;
}
#map_canvas {
    height: 100%;
}
</style>
<!--這個 CSS 宣告表示地圖容器 <div> (名為 map_canvas) 應使用 HTML 主體的 100% 高度。請注意,我們也必須特別宣告 <body> 和 <html> 的這些百分比-->
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=true"></script>
<!--使用 script 標記來涵蓋 Maps API JavaScript-->
<!--請注意,我們也必須設定 sensor 參數,以指出這個應用程式是否有使用感應器來判斷使用者的位置。必須明確地將這個值設為 true 或 false-->
</head>
<body onload="initialize()">
<!--從 body 標記的 onload 事件初始化地圖物件-->
  <div id="map_canvas" style="width:100%; height:100%"></div>
  <!--建立 div 元素 (名稱為「map_canvas」) 來容納「地圖」-->
</body>
<!--建立 JavaScript 物件實字以存放多個地圖屬性-->
<!--JavaScript 函式以建立「地圖」物件-->
<script type="text/javascript">
function initialize() {
    var myOptions = {
        center: new google.maps.LatLng(25.05060, 121.51870),
        zoom: 8,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    var map = new google.maps.Map(
        document.getElementById("map_canvas"),
        myOptions
    );
}

</script>
</html>

JavaScript map object

先建立地圖的設定
地圖選項

var myOptions = {
    center: new google.maps.LatLng(25.05060, 121.51870),
    zoom: 8,
    mapTypeId: google.maps.MapTypeId.ROADMAP
};
  • center 表示一開始地圖的中心位置
  • zoom 地圖縮放(0 ~ 18)
  • mapTypeId 地圖類型
    • ROADMAP 顯示 Google 地圖的正常、預設 2D 地圖方塊。
    • SATELLITE 可顯示攝影地圖方塊。
    • HYBRID 可顯示混合攝影地圖方塊與重要地圖項 (道路、城市名稱) 的地圖方塊圖層。
    • TERRAIN 可顯示實際起伏的地圖方塊,以呈現海拔高度和水域圖徵 (山嶽、河流等)。

初始化地圖
地圖物件

var map = new google.maps.Map(
    document.getElementById("map_canvas"),
    myOptions
);

Mark Google map

標記
標記在地圖上的位置

// list marks

var arr = new Array(
    {
        position: new google.maps.LatLng(25.05060, 121.51870),
        title: 'Hello World!'
    },
    {
        position: new google.maps.LatLng(25.056304, 121.522079),
        title: '春漾咖啡'
    }
);

// put marks in map

for (var i = 0; arr.length-1 >= i; i++) {
    var marker = new google.maps.Marker(arr[i]);
    marker.setMap(map);
};

Info windows

infowindows

標記上的 pop tooltip

var infowindow = new google.maps.InfoWindow();
Method
  • setContent()
  • open()
  • close()
Properties
  • content
  • disableAutoPan
  • maxWidth
  • pixelOffset
  • position
  • zIndex

Event

事件
Google map api 提供許多與 map 有關的事件

Style

樣式標記地圖

JavaScript - YouTube API note

JavaScript - YouTube API note

player demo
YouTube JavaScript Player API Reference
YouTube 嵌入式播放器参数
YouTube Embedded Players and Player Parameters
YouTube Player API Reference for iframe Embeds

Use YouTube video must init a youtube player
There have 2 player we can use

  • flash player
  • iframe player

I suggest use iframe player because it can support flash and HTML5
If use Apple mobile devices, they can't support flash
Use iframe player can solve this problem
We can use javascript control player

This is a note to iframe API

embed script

<script src="https://www.youtube.com/iframe_api"></script>

JavaScript generate

var tag = document.createElement('script'),
    firstScriptTag = document.getElementsByTagName('script')[0];
tag.src = 'https://www.youtube.com/iframe_api';
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

1.First check load iframe API

function onYouTubeIframeAPIReady() {
    // do something

}

2.Init YouTube player

var player;
player = new YT.Player(
    'player',
    {
        width: '1280', // player width

        height: '720', // player height

        videoId: videoList[0], // youtube id ex: '0lfDNu-k6oo'

        playerVars: {
            rel: 1,
            autoplay: 0,
            disablekb: 0,
            showsearch: 0,
            showinfo: 0,
            controls: 1,
            wmode: 'opaque',
            hd: 1,
            html5: 1,
            iv_load_policy: 3
        },
        events: {
            'onReady'        : onPlayerReady,
            'onStateChange'  : onPlayerStateChange,
            'onError'        : error
        }
    }
);

playerVars can refer YouTube 嵌入式播放器参数

3.Handle onPlayerReady, onPlayerStateChange, error

function onPlayerReady(e) {
    // do something

}

function onPlayerStateChange(e) {
    // do something

}

function error(e) {
    // do something

}

Sample code

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>youtube_iframe_api</title>
</head>
<body>
    <div id="player"></div>
</body>
<script>
    var player;

    function loadPlayer() {
        if (document.querySelector('#player')) {
            // get API

            var tag = document.createElement('script'),
            firstScriptTag = document.getElementsByTagName('script')[0];
            tag.src = 'https://www.youtube.com/iframe_api';
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            var videoList = [],
                playCount = 0;

            var video_ID = {
                'v_2': 'V8BTsiMxyaQ',
                'v_0': '7wvNwOPprBE',
                'v_1': 'mTSuiGubCHE',
                'v_8': 'DDs5bXh4erM',
                'v_3': 'n-BXNXvTvV4',
                'v_4': 'CRJDQQXS4uE',
                'v_5': 'ASO_zypdnsQ',
                'v_6': 'IZkYdqRWKaY',
                'v_7': '9Y15es8OY0U',
                'v_9': 'LWV-f6dMN3Q'
            };

            // videoList array

            for(var key in video_ID) {
                var value = video_ID[key];
                videoList.push(value);
            }

            function playChannel() {
                // init player

                player = new YT.Player(
                    'player',
                    {
                        width: '1280',
                        height: '720',
                        videoId: videoList[0],
                        playerVars: {
                            rel: 1,
                            autoplay: 1,
                            disablekb: 0,
                            showsearch: 0,
                            showinfo: 0,
                            controls: 1,
                            autohide: 0,
                            modestbranding: 0,
                            wmode: 'opaque',
                            hd: 1,
                            html5: 1,
                            iv_load_policy: 3
                        },
                        events: {
                            'onReady'        : onPlayerReady,
                            'onStateChange'  : onPlayerStateChange,
                            'onError'        : error
                        }
                    }
                );

                // play video

                function onPlayerReady(e) {
                    console.log('onPlayerReady');
                }

                function error(e) {
                    console.log('Error');
                    console.log(e);
                }
            }

            function onPlayerStateChange(e) {
                // get state

                console.log(e);

                // play list loop when video end play next video

                if (e.data === 0) {
                    playCount++;
                    if (playCount > (videoList.length -1)) {
                        playCount = 0;
                    }
                    player.loadVideoById(videoList[playCount]);
                    player.playVideo();

                }
            }

            function onYouTubeIframeAPIReady() {
                playChannel();
            }

            setTimeout(function() {
                onYouTubeIframeAPIReady();
            }, 1200);
        }
    }

    loadPlayer();
</script>
</html>

Refer - Force HTML5 youtube video

onError error code

  • 2 – The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.
  • 5 – The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.
  • 100 – The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.
  • 101 – The owner of the requested video does not allow it to be played in embedded players.
  • 150 – This error is the same as 101. It's just a 101 error in disguise!

Youtube Data api

Get video data api

http://gdata.youtube.com/feeds/api/videos/<youtube_id>

It return format is XML

http://gdata.youtube.com/feeds/api/videos/<youtube_id>?alt=json

It return format is json

参考指南:Data API 协议
Developer's Guide: JSON / JavaScript
Refer- How to ensure YouTube API only returns videos that are streamable on iPhone?

Something note

Player API 其中有個參數允許強制使用 HTML5 player 這建議有個好處與壞處
好處是不會有廣告
壞處是如果遇到訊源只有 flash 格式的影片 player 會重啟為 flash player
用 gData 抓影片資訊可抓到其中一個 content 的 key 其中就有提供此影片所帶有的格式
test page

  • 5 - HTTP URL to the embeddable player (SWF) for this video. This format is not available for a video that is not embeddable. Developers commonly add &format=5 to their queries to restrict results to videos that can be embedded on their sites.
  • 1 - RTSP streaming URL for mobile video playback. H.263 video (up to 176x144) and AMR audio.
  • 6 - RTSP streaming URL for mobile video playback. MPEG-4 SP video (up to 176x144) and AAC audio.

Refer - Reference Guide: Data API Protocol

實測在 mobile 裝置上(Android, iOS), 參數設定 autoplay 是無效的, 利用 JavaScript 控制讓 youtube 播放也是無效的, 必須用手去點擊播放

HTML5 - Full Screen Mode

HTML5 - Full Screen Mode

It can control full screen the browser
IE if it is to be more than IE11

Code

function toggleFullScreen() {
  if (!document.fullscreenElement &&    // alternative standard method

      !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {  // current working methods

    if (document.documentElement.requestFullscreen) {
      document.documentElement.requestFullscreen();
    } else if (document.documentElement.msRequestFullscreen) {
      document.documentElement.msRequestFullscreen();
    } else if (document.documentElement.mozRequestFullScreen) {
      document.documentElement.mozRequestFullScreen();
    } else if (document.documentElement.webkitRequestFullscreen) {
      document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
    }
  } else {
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    }
  }
}

Example

HTML

<button id="full">Full Screen</button>

JavaScript

document.querySelector('#full').addEventListener('click', function() {
    toggleFullScreen();
});

function toggleFullScreen() {
  if (!document.fullscreenElement &&    // alternative standard method

      !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {  // current working methods

    if (document.documentElement.requestFullscreen) {
      document.documentElement.requestFullscreen();
    } else if (document.documentElement.msRequestFullscreen) {
      document.documentElement.msRequestFullscreen();
    } else if (document.documentElement.mozRequestFullScreen) {
      document.documentElement.mozRequestFullScreen();
    } else if (document.documentElement.webkitRequestFullscreen) {
      document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
    }
  } else {
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    }
  }
}

CodePen

See the Pen full screen mode by Ted (@tedshd) on CodePen.

Other use

Use it make youtube api player full screen
If render player on <div id="player"></div>

document.querySelector('#full').addEventListener('click', function () {
  toggleFullScreen(document.querySelector('#player'));
});

function toggleFullScreen(el) {
  if (!document.fullscreenElement &&    // alternative standard method

      !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {  // current working methods

    if (document.documentElement.requestFullscreen) {
      el.requestFullscreen();
    } else if (document.documentElement.msRequestFullscreen) {
      el.msRequestFullscreen();
    } else if (document.documentElement.mozRequestFullScreen) {
      el.mozRequestFullScreen();
    } else if (document.documentElement.webkitRequestFullscreen) {
      el.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
    }
  } else {
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    }
  }
}

Refer - Using fullscreen mode - Web developer guide | MDN

Refer - 全螢幕 API (Windows)

Refer - How to Use the HTML5 Full-Screen API (Again) - SitePoint

JavaScript - Google Closure Compiler V.S. Gulp uglify

JavaScript - Google Closure Compiler V.S. Gulp uglify

Intro

I have two choice

  1. Google Closure Compiler

  2. UglifyJs

Google Closure Compiler

Google Closure Compiler

Google Closure Compiler is a tool for minify and uglify js.

  • From Google

  • Java

  • Command Line

  • Slow

UglifyJs

mishoo/UglifyJS2

  • More option setting

  • Node.js

  • Command Line

  • Fast

Test

Google Closure Compiler

When i run command line, it used lot of cpu source.

Can feel it lock some seconds.

If compiler error,it can show error message.

UglifyJs

It's so fast.

It can fix some small error when it compiler.

So i recommendation this tool.

Finally i use Gulp handle uglifyJs.

Google Cloud Platform - Study Log

Google Cloud Platform - Study Log

Intro

之前有幸參加亞太區 Google Cloud Platform 發表會, 聽完後就有點心動(因為價錢低於 AWS), 但ㄧ直沒時間研究怎麼用就這樣拖了很久...

加上之前用的網路空間不穩又常常換硬體主機, 換完就要重建帳號很麻煩(雖然是免費的, 但去常常在假日跟我一起休息...)

所以打算把 demo code 和一些小東西丟到 GCP 試試, 改用 GCP(開始要花錢了...)

Service type

Compute Engine

Google Compute Engine

簡單說就是 AWS 的 EC2

就是在上面 run VM 開 web service 的服務

Usage for me

f1-micro (vCPUs: shared, RAM: 0.60 GB) * does not support local SSD - 10GB storage

這樣 USD 5, 不含流量

但基本上對外流量是 1GB USD 0.01

所以還好

GCP 還有個優勢就是利用它呼叫 Google service 的 API(Map, youtube...) 是不算流量的(free!)

  1. 先開個 VM

  1. 設定 SSH key

  1. 設定成靜態 IP, 這跟 AWS 一樣有組靜態 IP, 設定綁完給 VM 用就不會再算錢

  1. 如果別人要 SSH 進來的話, 就到中繼資料加 key

Some help data

首頁

Google Cloud Computing, Hosting Services & Cloud Support — Google Cloud Platform

價錢計算機

Google Cloud Platform Pricing Calculator - Google Cloud Platform

Compute Engine 計價方式

Google Compute Engine Pricing - Compute Engine - Google Cloud Platform

流量計價方式

Google Adsense domain 設置

Google Adsense domain 設置

一點設定 Google adsense 的心得

因為有時會寫一寫筆記或部落格記錄人生

然後因為工作進到跟廣告有點關係的公司, 遇到裡面的一些專家

有人提議說, 既然我有寫文章的習慣, 那也可以放個廣告賺一點錢

但我個人是不太喜歡放廣告, 畢竟大家都知道廣告是拖累網站速度的元凶

但最近想說試試可不可以在自己的網站放個一個版位的廣告, 看能不能抵掉 GCP 維持的費用(應該是不太可能...

所以就在自己寫的 blog 和自己做的一些 service 放上去試試

這紀錄一下一些在用 Google adsense 比較特別的行為與情況

US 100 才能領出來

我這要等到幾時啊...

Google adsense 是動態 CPC + CPM 計算價錢

所以沒一定的價格, 似乎有受流量影響

眾所皆知, 廣告通常是 by domain 申請, 現在用 subdomain 是不行的

很久以前依據在 subdomain service 的話似乎可以, 但現在不行了

因為我用 logdown 申請說不能用 subdomain 申請...(明明約半年前還可以, 只是當時不知為何一直說我違反他們的協議, 所以一直申請不過

但 Google 自己的 blogger 卻可以, 我試過了因為我有個 blogger, 但 Pixnet 我沒試過(不過 Pixnet 可以跟他們自己申請廣告分潤

Google Adsense 的網站審查蠻嚴謹的

嚴謹到我這 logdown 之前都過不了(靠, 搞得我都認為是 XXX 的錯...

把自己的站用 submain 掛到申請過 doamin 下去可行

我本身是這樣試出來可以的

How to do?

我申請 tedshd.io 放 Google adsense

在 tedshd.io 開個 subdomain blog.tedshd.io

設定 blog.tedshd.io CNAME tedshd.logdown.com

這樣 blog.tedshd.io 就是 tedshd.logdown.com

這樣 Google adsense 就會有了

且去 Google adsense 後台看也有數據進來了

但這不知道是漏洞還是 Google 有意為之

個人覺得這算黑的做法

所以哪天被鎖就 GG 了...

Chrome headless 研究筆記

Chrome headless 研究筆記

最近要用到 chrome headless 的部分功能

且得在 ubuntu or debian server 實踐

所以筆記一下

Intro

headless 大致分兩種用法

  1. CLI

  2. libary

Refer - Getting Started with Headless Chrome

CLI 用法簡單但是受限很多且沒有找到具體詳細完整的文件, 就連用 man 查都查不到...

也有可能是我自己沒有認真找

libary 就相對容易, 連 Google 自己都有提供部分語言的版本, 且有前端工程師很熟的 Node 版本

on ubuntu server

sudo apt-get install chromium-browser

ex:

chromium-browser --headless --screenshot=test.png --disable-gpu --window-size=320,480 --user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53" http://tedshd.logdown.com

這指令下了截圖, 設定寬高, 模擬 UserAgent

當然也有 --dump-dom 可以看 source code

使用 puppeteer 處理

puppeteer

puppeteer 是 Google 維護的用 node 處理 headless 的套件

用了一下真是感到清爽好用

放一段最簡單的 smaple code

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:64.0) Gecko/20100101 Firefox/64.0');
        await page.goto('https://tysh310246.blogspot.com');

    await page.screenshot({path: 'example.png', fullPage: true});
    await browser.close();

})();

就是如此清爽

且有很多包好的 function 可以用

看了一下文件根本可以用 headless 取代 selenium + webdriver.io 了

但是目前的需求也不需要用到太多其他的功能

有空再慢慢研究

Puppeteer API Tip-Of-Tree

字型

在一台 server 上除非要截的網站是用 webfont 不然就會發現截的圖因為沒有字型所以中文會是方塊字

這部分就得處理一下

可以直接裝 Google 的 noto 字型

wget -c https://noto-website.storage.googleapis.com/pkgs/Noto-hinted.zip

把字型檔載下來放在

/usr/local/share/fonts/

設定 owner

設定權限

安裝字型

sudo fc-cache -fv

搞定

這邊建議直接裝全部語系的字型較為完整, 以免會遇到其他語系還是會有問題

Refer - Ubuntu環境下,手動安裝思源字型