2016/10/17

chrome extension - long finger 瀏覽器擴充功能開發紀錄

注意:本文中有許多連結必須手動複製貼上到網址列上才能正確開啟(chrome 開頭的那些網址)

long finger 簡介

long finger 安裝網址 : https://chrome.google.com/webstore/detail/long-finger/aammiagfeadjchafpeeiacggolmfnmmo

點子以及圖片資源是由 Leggy 提供,感謝 Leggy。

這是一個會把滑鼠游標從食指改成中指的擴充功能。他沒什麼用,唯一的作用應該是舒壓吧。實作起來相當容易,程式碼也足夠短,很適合作為一個練手的作品。


擴充功能簡介


chrome 擴充功能入口:chrome://extensions/

在這個頁面可以看見目前安裝的所有擴充功能。如果只是單純嘗試開發,還沒打算上架的話,在這裡有一個「開發人員模式」的按鈕,先把他打勾。


這樣就可以安裝還沒發佈到 google 商店的擴充功能。一個擴充功能的核心檔案是manifest.json,我們可以透過特定網址去找到任一擴充功能的 manifest.json,網址格式為:

chrome-extension://擴充功能ID/manifest.json

舉例來說,在安裝好 long finger 之後開啟網址:
chrome-extension://aammiagfeadjchafpeeiacggolmfnmmo/manifest.json
會看到:
{
   "browser_action": {
      "default_icon": "longFinger.png"
   },
   "content_scripts": [ {
      "css": [ "longFinger.css" ],
      "js": [ "longFinger.js" ],
      "matches": [ "http://*/*", "https://*/*" ]
   } ],
   "description": "This extension change cursor index finger to long finger.",
   "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlWMwgzcpj1/kt1KbNVUbvQRth2wvV2WX9RKRxJ1aWAsovQjEWKytM2pkbZ2xbm/V9tSNODZW438PfS46IV12iE3aTIc0mbHDML3ScJZeBfKs8dFIt0nxWERpIVctS2xQIUPS4mfKsWdk6LckPuHQmFyz13hR13hbUoqz7DkCqWv3Sz5sbDZbqKBg9GZliWUjeafzOTjkT3rO5khVqmT8NcfJ0TYAe1sJ4u2PpHByC8+dT0iQsE6xEz2/gG66HwnpU4HibcyAujfAy4fcgOmWfL8kcm4HcvFqJ+C8s3jtlUxm8o6rzrNtw+w30r5NLLdC+GcSjw3Z9zeF4U+3swa7mQIDAQAB",
   "manifest_version": 2,
   "name": "long finger",
   "update_url": "https://clients2.google.com/service/update2/crx",
   "version": "3.0"
} 
manifest.json 是整個擴充功能的起始點,透過 manifest.json 的設定,你可以觸發其他的程式檔。我們可以看見這裡有幾個重要的檔案連結:

chrome-extension://aammiagfeadjchafpeeiacggolmfnmmo/longFinger.css
chrome-extension://aammiagfeadjchafpeeiacggolmfnmmo/longFinger.js

這兩個檔案落在 content scripts 中。content_scripts 有三個參數,分別是 matchs、css、js。當使用者開啟網頁時,若網址符合 matches 的規則,就會將 css 跟 js 附加在該網頁上。


long finger 實作


讓我們把注意力放在 longFinger.css 和 longFinger.js 上。

先來看看 longFinger.css:
a{
 cursor: url("http://etrex.tw/a/flippers3.gif"),auto !important;
}
button{
 cursor: url("http://etrex.tw/a/flippers3.gif"),auto !important;
}
.longFinger{
 cursor: url("http://etrex.tw/a/flippers3.gif"),auto !important; 
}
longFinger.css 上寫的是只要是 a 或 button 或是任何帶有 longFinger class 的 html tag 就會把游標改為 http://etrex.tw/a/flippers3.gif

接下來看看 longFinger.js:
document.querySelector('body').addEventListener("mousemove", function(e){
 var target = e.target;
 var cursor = window.getComputedStyle(target,null).getPropertyValue("cursor");
 if(cursor == "pointer"){
  target.classList.add("longFinger");
 }
});
longFinger.js 寫著,當滑鼠在整個網頁上移動時,檢查目前游標指著的元素(element)是否造成游標為食指形狀,如果是的話,就幫這個元素加上 longFinger class。

這樣就大功告成了!


擴充功能測試


我已將 long finger 放在 github 上:https://github.com/etrex/long-finger。你可以在 github 上面看到資料夾結構是由一個資料夾 long finger 下帶著四個檔案所組成:manifest.json、longFinger.js、longFinger.css、longFinger.png。

只要下載long finger 資料夾,並且在 chrome://extensions/ 選擇載入未封裝的擴充功能,然後選擇 long finger 資料夾,就可以完成擴充功能的安裝。

2016/10/7

用 C# 做一個 file upload proxy server

實現功能

讓 js 用 ajax 的方式傳檔案到跨網域的 server 上,而不會遇到跨網域問題。

核心程式(C#)


using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;

namespace Tools
{
    public class HttpCaller
    {
        public static string MultipartForm(string url, HttpRequestBase request)
        {
            var ignoreHeader = new string[] { "Content-Length", "Content-Type", "Host", "Referer" };
            string result = string.Empty;
            var mediaType = "";
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Clear();
                    foreach (var key in request.Headers.AllKeys)
                    {
                        if (key.Equals("Content-Type"))
                        {
                            mediaType = request.Headers[key];
                        }
                        if (ignoreHeader.Contains(key))
                        {
                            continue;
                        }
                        client.DefaultRequestHeaders.Add(key, request.Headers[key]);
                    }
                    HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, url);
                    message.Content = new ByteArrayContent(StreamToBytes(request.InputStream));
                    message.Content.Headers.Remove("Content-Type");
                    message.Content.Headers.Add("Content-Type", mediaType);
                    result = client.SendAsync(message).Result.Content.ReadAsStringAsync().Result;
                }
            }
            catch (Exception ex)
            {
                result = ex.Message;
            }
            return result;
        }

        private static byte[] StreamToBytes(Stream stream)
        {
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }
    }
}

使用方法(C# ASP.NET MVC 專案)


public ActionResult upload()
{
   var targetUrl = Request.Form["targetUrl"];
   var json = HttpCaller.MultipartForm(targetUrl, Request);
   return Content(json);
}

使用方法(JS)


function uploadRequest(targetUrl, requestObject, files, callback) {
    var formData = new FormData();
    for (var key in requestObject) {
        formData.append(key, requestObject[key]);
    }
    for (var i = 0 ; i < files.length ; i++){
        formData.append("file" + (i+1), files[i]);
    }
    formData.append("targetUrl", targetUrl);

    var request = new XMLHttpRequest();
    request.open("POST", "upload");
    request.onload = function (e) {
        var response = e.target.response;
        callback(response);
    };
    request.send(formData);
}