顯示具有 javascript 標籤的文章。 顯示所有文章
顯示具有 javascript 標籤的文章。 顯示所有文章

2017年2月19日 星期日

gmail 碎紙機

電子郵件是個讓人又愛又恨的東西,基於工作的理由你實在不能不用它,各網站服務也都是用電子郵件帳號註冊跟認證,可是,一但加入會員就會不斷收到宣傳信,像是什麼<XX購物網>一直倒宣傳信過來,啊我沒錢不然你給我錢我買你的東西這樣可以嗎?然後像什麼求職廣告、防詐騙提醒,封鎖要是鎖掉了真正重要的信件又很麻煩。工作上討論一次CC群組,每個人回信信就一直湧進來,多到要爆炸……嗯爆炸是不至於,畢竟我用的gmail 空間很大。

可是呢,每天刪信刪到手軟也很煩,突發奇想就寫了一個chrome extension,原始碼在這裡,可以載下來之後用<載入未封裝之擴充功能>來安裝:
https://github.com/yodalee/shred-my-gmail

我不但要刪掉信,在刪的同時還要把他給攪掉,用下面這個 extension boilerplate:
https://github.com/KartikTalwar/gmail-chrome-extension-boilerplate
搭配最新的 gmail.js
http://github.com/kartiktalwar/gmail.js

首先是在文件中插入一個 audio tag,並連結到我extension 中的mp3 檔
var a = document.createElement('audio');
a.id = "sound";
a.src = chrome.extension.getURL('shredder.mp3');
(document.head || document.documentElement).appendChild(a);
然後在main.js 裡面,載入gmail.js 之後,用gmail.js 的observe
gmail.observe.on("delete", playShredSound);
就能監看刪除事件,並用getElementById 取得audio element 並播放,就能做出信被攪掉的效果,聲音的來源是這裡:
https://www.youtube.com/watch?v=ECstQgQZWXc
下載下來之後用audacity 把切削的部分取出來。

雖然信其實只是一樣進到垃圾桶裡而已,不過把垃圾信攪一攪就有一種爽感:「X的你們這些王八蛋垃圾信,看我把你們攪成碎片~~~」;我本來想說如果把信永久刪除的話,就放個燒掉的音效好了,最後覺得燒掉的聲音太難表現還是算了。

總之寫了一個自己爽的專案,感謝各位看倌貢獻的時間,文章最後附上示範影片連結:

2017年1月19日 星期四

使用webpack打包React.js 專案

在上一篇我們終於在server 上把React.js 跑起來之後,就能繼續寫下去,結果很快的我們就遇到另一個問題,那就是怎麼所有code 都擠在一塊了(yay,
我們上一篇解法的問題,在於我們template render 這個網頁之後,相關的檔案全塞在用<script>引入的javascript 檔案裡,然後這個文件就引不入其他文件了,而React 提倡的模組化該是能把各元件分別歸檔才是。
幸好,我們還是有解法的,利用webpack 讓我們把檔案分開,然後用webpack 打包成單一的 javascript 檔案,據說這種寫法就是<入坑>,因為一用了webpack 就脫不了身,之後都會綁死在webpack 上面。
網路上有些相關的文件,就來一步步跟著做:
https://rhadow.github.io/2015/04/02/webpack-workflow/

首先先準備我們的front-end/main.jsx,因為使用到react跟react-dom,因此在檔案開頭用require 引入,完成的main.jsx 像這樣:
'use strict'

var React = require('react')
var ReactDOM = require('react-dom')

var hello = React.createClass({
  render: function() {
    return (<h2>Hello World!</h2>);
  }
});

ReactDOM.render(
  React.createElement(hello, null),
  document.getElementById('content')
);
接著使用npm 安裝webpack :
npm install --save-dev webpack

並編輯webpack 的設定檔webpack.config.js:
var path = require('path');

var config = {
  entry: [path.resolve(__dirname, './front-end/main.jsx')],
  output: {
    path: path.resolve(__dirname, './static'),
    filename: 'bundle.js'
  },
};

module.exports = config;
這時候使用webpack 來打包,會出現錯誤訊息:
ERROR in ./front-end/main.jsx
Module parse failed: /home/yodalee/website/message-downloader/front-end/main.jsx Unexpected token (8:12)
You may need an appropriate loader to handle this file type.

明顯它看不懂react 的jsx 語法,我們需要設定webpack.config.js 遇到 jsx 檔案就使用babel-loader,還要排除本地的node_modules 裡面的檔案,與output 同層加上:
module: {
  loaders: [{
    test: /\.jsx?$/,
    loader: 'babel-loader',
    exclude: path.join(__dirname, 'node_modules')
  }]
},
並編輯babel的設定檔 .babelrc:
{
  "presets": ["react"],
}
並且使用npm 安裝babel-loader和preset react:
npm install --save-dev babel-loader babel-core babel-preset-react react react-dom

這樣就能用webpack 幫忙打包所有檔案了,有了這個我們就能…嗯…把bower 給刪掉啦(・∀・)。

現在bundle.js 產生在static 裡面,我們可以把template/view.html 清得乾乾淨淨只剩下這樣就會動了:
<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8">
    <title>View Test</title>
  </head>
  <body>
    <div id="content"></div>
    <script src="/static/bundle.js"></script>
  </body>
</html>
有沒有覺得超級神奇,我也是這麼覺得的。
老實說你要說我一知半解我也認了,前端真的是各種古怪離奇,我看網路上那堆stack overflow的答案怎麼每個都不太一樣,然後試著做不能動的居多(yay,這篇要不是強者我同學人生勝利組一哥冠霖大神的指導,根本要試超久才試得出來。

下面整理一些相關的錯誤訊息:
Unexpected token
well 它看不懂 jsx 的檔案,所以需要用babel-loader 來處理 jsx 檔

Module not found: Error: Cannot resolve module 'react'
表示它在node_modules 裡面找不到require 的module,使用npm 安裝即可

ERROR in ./front-end/main.jsx
Module build failed: Error: Couldn't find preset "react" relative to directory "/home/yodalee/website/message-downloader"
這個是指 babel-loader 沒有相關的……套件?像上面裝了babel-preset-react 就解掉了。

本文感謝強者我同學人生勝利組一哥冠霖大神的指導

2017年1月14日 星期六

使用bower安裝react 前端環境

最近寫message-viewer ,想在bottle.py 執行的server 上面跑React.js,於是就小找了一下,基本上排除了使用 bottle-react 這種懶人套件,我想要的就是能直接寫,同時react jsx 也能在我的管控之下的設定。
後來找到這篇文章,照著它的步驟、跟留言的回覆做就成功了,在這邊整理一下:
https://realpython.com/blog/python/the-ultimate-flask-front-end/

這裡就不介紹React.js 的運作原理了,筆者到目前也還在學,總之我們就跟裡面的一樣,先寫個 view.html,裡面沒什麼,就是用React 寫一個Hello World,直接使用cdnjs 提供的library:https://cdnjs.com/libraries/react/:
<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8">
    <title>View Test</title>
  </head>
  <body>
    <div id="content"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.38/browser.min.js"></script>

    <script type="text/babel">
var hello = React.createClass({
  render: function() {
    return (<h2>Hello World!</h2>);
  }
});

ReactDOM.render(
  React.createElement(hello, null),
  document.getElementById('content')
);
    </script>
  </body>
</html>

接著我們使用任何一種server 的template engine ,我這裡用的是Jinja2就能把網頁跑起來,因為script 來自CDN,所以不必特別設定就能直接使用,打開來應該會出現h1 的Hello World。
app.route('/view', 'GET', MessageViewHandler)
@route('/view')
def MessageViewHandler():
    template = JINJA_ENVIRONMENT.get_template('view.html')
    return template.render()

下一步我們要在自己的電腦上面裝上React,我們使用的是管理前端的管理程式bower,像是bootstrap, React 什麼的都可以裝,

因為我是用archlinux ,本身就提供了bower 套件,所以可以用 pacman 裝bower;非archlinux 的發行版就要用npm 裝bower:
$ npm install -g bower
使用-g 是設定global ,因為在其他project 中八成也會用到bower,但另外,為了服務載了你的project 卻沒有bower 的使用者,我們也需要設定一個npm 的文件:
$ npm init
$ npm install --save-dev bower

有了bower之後,在project 中初始一個bower:
$ bower init
設定直接接受預設設定即可,它會產生一個 bower.json 檔案,我們另外要指定bower 安裝檔案的路徑為static,這要編輯 .bowerrc 並加入下列內容:
{
  "directory": "./static/bower_components"
}
並使用bower 安裝套件,可以用 bower install <package_name> --save 或是在bower.json 中加入套件名之後,再呼叫 bower install;我們這裡用第二種方法,在bower.json 的dependency 下面加上:
"dependencies": {
  "bootstrap": "^3.3.6",
  "react": "^15.1.0",
  "babel": "^5.8.38"
}
並執行 bower install,就能安裝好所需的套件,這時project 中的檔案應該差不多是這樣:
.bowerrc
.gitignore
bower.json
package.json
static/bower_components
template/

現在可以把上面的cdnjs 換成本地資料夾的static link:
<script src="/static/bower_components/react/react.min.js"></script>
<script src="/static/bower_components/react/react-dom.min.js"></script>
<script src="/static/bower_components/babel/browser.min.js"></script>
同時在server 要加上static handler來處理所有對static 的連結:
@route('/static/<path:path>')
def callback(path):
    return static_file(path, root='static')

這樣應該就能在python server 上面寫react.js 的網頁前端了,把剛剛的view.html 打開來跑跑看吧。

2016年6月6日 星期一

字幕產生器 Subtitle Generator

六月初參加PyCon ,因為第一天開幕睡過頭了,用One Punch Man 超商大特賣的畫面做了兩張搞笑圖,用gimp 做不熟練,花了不少時間,決定來寫個:「字幕產生器」

成品在此:
http://yodalee.github.io/subtitle-gen.html

這個網頁基於之前qcl 大神所寫的「大師語錄產生器」,大體架構沒什麼改:
http://qcl.github.io/master-quote-gen.html

同時我還參考了以下的stack overflow,老實說要是沒有網路跟stack overflow,我應該寫得出一點C跟python,可是html 和javascript 我大概會完全卡住,一行都寫不出來:

上傳圖片到canvas裡面:
http://stackoverflow.com/questions/22255580/javascript-upload-image-file-and-draw-it-into-a-canvas
canvas 裡面大圖縮小:
http://stackoverflow.com/questions/2303690/resizing-an-image-in-an-html5-canvas
canvas裡寫字有邊框:
http://stackoverflow.com/questions/13627111/drawing-text-with-an-outer-stroke-with-html5s-canvas
指定input/color 預設顏色:
http://stackoverflow.com/questions/14943074/html5-input-colors-default-color

其實寫出來覺得有點對不起qcl 大神,其實就是「大師語錄產生器」的變種,應該要貢獻回qcl 大神的github 的,只是我不知道怎麼調和自行上傳圖片和預設圖片的衝突,也不知道要怎麼處理使用者決定文字在圖內或圖下時,canvas要怎麼處理,乾脆算了。

不過既然做出來了,表示好處多多,可以好好利用:
除了本來PyCon 兩張可以快速做出來

還可以玩一些其他的:


Future Work:等我想到再來寫

2015年7月12日 星期日

使用Google App Engine 處理前端ajax request

最近在學用GAE寫一個簡單的服務,結果一直鬼打牆,這時候就要來跟我念一遍:前.端.超.難.

這次是用了google app engine來處理ajax post,送一些base64 encode後的字串把資料送到server去,用的是ajax 來達成,ajax 其實跟一般的post, get沒什麼兩樣,只是它不需要重新整理網頁,可以做到網頁內容即時的變換。

使用時先產生一個XMLHttpRequest物件,如果是IE5跟IE6就直接放棄支援(其實要用舊語法,不過…算了管它去死),並用它開啟一個GET, POST的要求,指定server 的URL跟是否同步傳送,並用send()發送
還可以用setReqeustHeader來指定post 的內容,總之有許多的設定可以選用,我用的就很直接,一個非同步的post 把資料送去server 就是
xmlhttp = new XMLHttpRequest()
xmlhttp.open(“POST”, “upload?data=” + data, true);
xmlhttp.send();

建立python handler,其實就是post handler,GAE已經把post分解為request物件,直接取裡面的內容就好了:
class UploadHandler(webapp2.RequestHandler):
    def post(self):
        data = self.request.get("data")

註:後來發現這裡的內容有錯,這樣寫會動沒錯,但因為data 仍然在open() 的URL 裡面,為GET method,會視瀏覽器遇到幾KB 的長度限制,data 很長的話應該要放在send() 裡面,才是POST method ,理論上的長度上限是 GB 等級。
上面應該要改成:
xmlhttp = new XMLHttpRequest()
xmlhttp.open(“POST”, “upload", true);
xmlhttp.send(data);

class UploadHandler(webapp2.RequestHandler):
    def post(self):
        data = self.request.body

如果要回應什麼東西給ajax,用return送回去就是了,並在routing rule 建立對應的規則:
app = webapp2.WSGIApplication([
('upload', UploadHandler),
], debug=True)

這樣一個極簡單的ajax post handler就完成了,雖然說我python後端無法解開前端javascript編碼的base64字串,不知道又是哪裡有問題,反正前端超難我什麼都不會

其他ajax請見w3schools
http://www.w3schools.com/ajax/default.asp

本文感謝世恩大神的指導。

2015年5月10日 星期日

Javascript Worker

最近在寫一些前端的東西,深覺前.端.超.難.(yay)

接觸了HTM5 javascript 的worker ,在這裡筆記一下:

簡單來說,一般javascript的指令,都是存在一個執行緒裡執行,而worker 則可以開另一個執行緒在背景執行,就不會block住前端的UI的script,一般。
使用方法也很簡單,宣告一個worker並傳入一個檔案路徑,這個worker 就會負責執行檔案內的javascript。
要和worker 溝通,一律要經過postmessage跟onmessage 介面,postmessage會代入一個參數,可以用json寫得超級複雜,包括File, Blob, ArrayBuffer都可以傳遞;這時在對方的onmessage就會被呼叫,並包含這個物件的複製版本。
我看到的設計是這樣,在主要文件內 main.js 中,宣告worker
var worker = new Worker(“workerFile”);

在workerFile 內要定義onmessage 函式,並且用參數的command判斷執行哪個對應的函式:
this.onmessage = function(e) {
switch(e.data.command) {
case 'act1':
fun_act1(e.data.payload);
break;
}

這時候main.js就可以利用postmessage 叫worker 作事:
worker.postMessage({
command: 'act1',
payload: blahblahblah
})
worker.onmessage = function(e) {
data = e.data;
}

同樣我們在worker 裡面也可以呼叫postmessage,對方接口就是main.js 裡的onmessage,這樣就能做到雙方互相傳遞資料。
如果不想要以複製的方式傳遞資料,可以改用transferable object 的方式,減少複製成本,另外因為是多執行緒的關係,在worker 裡面,不應該去動到DOM,window, document, parent,這些都不是thread-safe。

至於更多詳細的內容,可見:
http://www.html5rocks.com/en/tutorials/workers/basics/

或者可以看worker 的spec:
https://html.spec.whatwg.org/multipage/workers.html