2007年5月28日 星期一

批次刪除 Xuite Blog 垃圾引用 Bookmarklet   [+/-]

Ticore's Blog

接續之前的批次刪除 Xuite Blog 垃圾引用
改為 Bookmarklet 型式
也省去了修改參數的動作
只要把 Firefox 2、Firebug 裝好
新增書籤,網址輸入以下程式就好

只要跳到目標 Xuite Blog 頁面並登入
點一下 批次刪除引用書籤
便會自動從第一頁檢查到最後一頁

Read more...

2007年5月24日 星期四

批次刪除 Xuite Blog 垃圾引用   [+/-]

Ticore's Blog

廢話不多說,以下直接介紹如何利用 FireFox + FireBug + JS 小程式

批次搜索並刪除 Xuite Blog 內大量的垃圾引用

  1. 先安裝 FireFox 2
  2. 安裝 Firefox 附加元件 FireBug 1.05
  3. 使用裝好的 Firefox 進入 Xuite Blog 頁面,並登入
  4. 在 Blog 任一文章頁面下,按下 F12 啟動 Firebug,並且 Enable Firebug
  5. 切換到 Firebug Console 分頁,於 Options 選單中選擇 Larger Command Line
  6. 全選並複製下面的 JavaScript 程式,貼到 Firebug 右邊的 Larger Command 區塊內
  7. 修改程式參數
    var lid = "ticore"; // 這裡改為你的帳號
    var url = "blog2"; // 這裡改為你的 Blog 名稱
    var startPageNo = 1; // 起始頁數
    var endPageNo = 7; // 終止頁數
  8. 按下 Run 開始執行程式

※ 注意,本程式僅出於研究之用,一切後果請自行負責

JavaScript 程式碼:

//========================================================================
/*

 Xuite 批次檢查、刪除文章引用
 
 by Ticore
 http://blog.xuite.com/ticore/blog2
 
 v 0.0.2
  Fix the bug cause by disbale FireBug network monitoring.
 v 0.0.3
  Check xuite member info from cookie.
 v 0.0.4
  Check delete track result
 
*/
//
//========================================================================
// 參數設定
//========================================================================
//

// Xuite Blog Domain
var xuiteDomain = "http://blog.xuite.net/";

// Xuite Blog Parameters

var lid = "ticore"; // 這裡改為你的帳號
var url = "blog2"; // 這裡改為 Blog 名稱


// 起始頁數
var startPageNo = 1;
// 終止頁數
var endPageNo = 7;
// HTTP Request Interval Time
var interval = 500;

//
//========================================================================
// 批次檢查引用頁面
//========================================================================
//

var startCheckPage = function () {
 clear();
 console.log("\t::::::Xuite 批次檢查、刪除文章引用::::::");
 console.log("\t\tby Ticore");
 console.log("\t\thttp://blog.xuite.com/ticore/blog2");

 if (document.location.toString().indexOf(xuiteDomain) < 0) {
  console.warn("請先到 Xuite Blog 頁面!");
  return;
 }
 
 var cookieAry = document.cookie.split(";");
 var cookieObj = {};
 for (var i = 0 ; i < cookieAry.length ; ++i) {
  var pair = cookieAry[i].split("=");
  cookieObj[pair[0]] = pair[1];
 }
 
 if (cookieObj[" MemberInformation"] == null) {
  console.warn("您沒有登入 Xuite 喔!");
  return;
 }
 
 window.a_author_id = window.mid = cookieObj[" MemberSN"];


 console.log("");
 console.log("\t::::::開始檢查引用頁面::::::");
 console.log("");
 window.pageUrl = xuiteDomain + lid + "/" + url + "?p=";
 window.pageIndex = startPageNo;
 window.articleTracksAry = [];
 setTimeout(checkNextPage, interval);
};


var checkNextPage = function () {
 if (pageIndex <= endPageNo) {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", pageUrl + pageIndex , false);
  xhr.send(null);
  if (xhr.readyState == 4) {
   var tmp = checkTrackNo(xhr.responseText);
   articleTracksAry = articleTracksAry.concat(tmp);
   console.log("讀取頁面 page ", pageIndex, ", 被引用頁面數量 : ", tmp.length);
   if (pageIndex + 1 <= endPageNo) {
    pageIndex++;
    setTimeout(checkNextPage, interval);
   } else {
    console.log("頁面檢查完畢,讀取頁面數量 : ", pageIndex
       , ", 全部被引用文章數量 : ", articleTracksAry.length);
    startCheckArticle();
   }
  } else {
   console.warn("Http State Error : ",  xhr.readyState);
  }
 }
};


// 檢查引用數量
var checkTrackNo = function(txt) {
 var reg = /([0-9]+)#trackback">引用\(([0-9]+)\)<\/a>/g;
 var matchs = [];
 while(match = reg.exec(txt)) {
  //console.log(match);
  if(match[2] != "0") {
   matchs.push(match);
  }
 }
 return matchs;
};

//
//========================================================================
// 批次擷取引用ID
//========================================================================
//


var startCheckArticle = function () {
 console.log("");
 console.log("\t::::::開始檢查引用 ID::::::");
 console.log("");
 window.articleTrackUrl = xuiteDomain + "_theme/TrackBackButtonExp.php?a_author_id="
  + a_author_id + "&mid=" + mid + "&aid=";
 window.articleIndex = 0;
 window.trackIdAry = [];
 setTimeout(checkNextArticle, interval);
};


var checkNextArticle = function () {
 if (articleIndex < articleTracksAry.length) {
  
  var articleId = articleTracksAry[articleIndex][1];
  
  var xhr = new XMLHttpRequest();
  xhr.open("GET", articleTrackUrl + articleId , false);
  xhr.send(null);
  
  if (xhr.readyState == 4) {
   //console.log(xhr.responseText);
   articleIndex++;
   var tmp = checkTrackId(xhr.responseText);
   trackIdAry = trackIdAry.concat(tmp);
   console.log("讀取 ",  articleId,  " 文章引用 ID, 引用 ID 數量 : ",  tmp.length);
   
   if (articleIndex < articleTracksAry.length) {
    setTimeout(checkNextArticle, interval);
   } else {
    console.log("文章引用 ID 檢查完畢,引用文章數量 : ",  articleIndex
      , ", 引用 ID 數量 : ",  trackIdAry.length);
    startDeleteTrack();
   }
  } else {
   console.warn("Http State Error : ",  xhr.readyState);
  }
 } else {
  console.log("文章引用 ID 檢查完畢,引用文章數量 : ",  articleIndex
    , ", 引用 ID 數量 : ",  trackIdAry.length);
  startDeleteTrack();
 }
};


var checkTrackId = function(txt) {
 //[{"id":"203490"},{"id":"203489"}]
 var reg = /\{"id":"([0-9]+)"\}/g;
 var matchs = [];
 while(match = reg.exec(txt)) {
  //console.log(match);
  matchs.push(match[1]);
 }
 return matchs;
};

//
//========================================================================
// 批次刪除引用
//========================================================================
//

var startDeleteTrack = function () {
 console.log("");
 console.log("\t::::::開始刪除引用 ID::::::");
 console.log("");
 window.deleteTrackUrl = xuiteDomain + "_theme/trackback/track_delete.php?lid="
  + lid + "&url=" + url + "&mid=" + mid + "&act=delete&tid=";
 window.trackIndex = 0;
 setTimeout(deleteNextTrack, interval);
};


var deleteNextTrack = function () {
 if (trackIndex < trackIdAry.length) {
  
  var trackId = trackIdAry[trackIndex];
  
  var xhr = new XMLHttpRequest();
  xhr.open("GET", deleteTrackUrl + trackId, false);
  xhr.send(null);
  
  if (xhr.readyState == 4) {
   trackIndex++;
   
   if(xhr.responseText.indexOf("刪除成功!!") >= 0) {
    console.log("刪除引用 ",  trackId,  "成功, 剩餘引用數量 : ",  (trackIdAry.length - trackIndex));
   } else {
    console.log("刪除引用 ",  trackId,  "失敗!, 剩餘引用數量 : ",  (trackIdAry.length - trackIndex));
   }
      
   
   if (trackIndex < trackIdAry.length) {
    setTimeout(deleteNextTrack, interval);
   } else {
    console.log("刪除完畢, 剩餘引用數量 : ",  (trackIdAry.length - trackIndex));
   }

  } else {
   console.warn("Http State Error : ",  xhr.readyState);
  }
 } else {
  console.log("刪除完畢, 剩餘引用數量 : ",  (trackIdAry.length - trackIndex));
 }
};


//
//========================================================================
//

//setInterval(startCheckPage, 10 * 60 * 1000);
startCheckPage();

//
//========================================================================
//

執行畫面:

Read more...

2007年5月16日 星期三

網頁錨點造成 Flash Preloader 失效問題   [+/-]

Ticore's Blog

看到討論 從 SWFAddress 找到的詭異的bug

大意是說當網址有出現指向錨點的 URL (Eg: index.html#anchor) 會造成該頁內 SWF Preloader 失效
真是很有意思的發現~

先稍微找一下 Google,似乎沒有相關的文章
於是自己寫測試程式

開一空白文件 (AS 2.0)
建立四個關鍵影格
前三影格設立影格標籤,分別為 A、B、C (Label Type: Name or Anchor)
在 A、B、C 三個影格加入靜態文字,分別標示 Fame Label A、B、C,與一個動態文字欄位,變數名稱為 precent
在 A、B、C 三個影格都加上以下程式:

this.onEnterFrame = function():Void  {
 percent = Math.floor(getBytesLoaded() / getBytesTotal() * 10000) / 100 + "%";
};
trace("_currentFrame : " + _currentFrame);
stop();

在第四個影格放入一張大圖用以突顯 Preloader 效果
將輸出的 SWF 與相關的 HTML、JS 部署到 Local Apache 上進行測試
使用 Fx 與 Charles 模擬網路頻寬

分別用以下網址進行測試

http://localhost/AnchorTest/AnchorTest.html
http://localhost/AnchorTest/AnchorTest.html#A
http://localhost/AnchorTest/AnchorTest.html#B
http://localhost/AnchorTest/AnchorTest.html#C
http://localhost/AnchorTest/AnchorTest.html#
http://localhost/AnchorTest/AnchorTest.html#UndefinedAnchor

Flash Player Plugin 是具有偵測 HTML Acnhor 的功能的
不過功能太陽春,所以才會有 SWFAddress 這類東西出現

經過測試發現,Label Type 甚至不需要選擇 Anchor,HTML 輸出設定也不用改
Flash Player Plugin 就會對 URL Anchor 有所反應
這未免也太自動了一些

Flash Player Plugin 對於 Anchor 的撥放行為比較特殊
它會一邊下載一邊搜尋是否有對應的 Frame Label
並不會馬上立即進行撥放,直到找到對應的 Frame Label
或是整個 SWF 下載完仍找不到,才會開始撥放

所以當遇到了像這樣的錨點 AnchorTest.html#
Flash 撥放器根本不可能在 SWF 內找到空字串 "" 的影格標籤
也沒有辦法在 Flash IDE 內設置這樣的標籤 ""

不過我們可以使用 UltraEdit 修改 SWF
找到 Frame Label A 之後
將 A 改為 0x00
再次使用 AnchorTest.html# 進行測試
就會發現 Preloader 可以正常執行了

這或許不算是一個 Bug
而是 Flash Anchor 功能延伸出來的行為
不過誰會想得到呢?
或許 Adobe 應該要提供取消 Flash Anchor 功能的方式

Read more...

2007年5月15日 星期二

Silverlight 透明背景測試   [+/-]

Ticore's Blog

以下是 Silverlight 透明背景 HTML Tag
只要設定 windowless=true
然後設定 background 的 ARGB 色碼
同時可以兼容 IE、Fx 的寫法,非常簡潔

在 Flash HTML Tag 也可以使用類似的方式
只是與 JavaScript 溝通上會出現問題~~


Scene.xaml:


Screenshot:
Read more...

Flash 技巧 - 預先編譯嵌入字體   [+/-]

Ticore's Blog

Flash 不知道第幾版開始加入的嵌入字體的功能 可以讓 SWF 呈現出用戶端電腦上沒有安裝的字體

不過編譯含有嵌入字體 SWF 是非常耗時的一件事情
重複編譯 fla 時,Flash 本身似乎會做某種程度的快取
一旦 Flash 關閉,快取就會消失~
下次重開文件,仍需要大量的編譯時間

那如何可以節省編譯時間呢?
或許你會聯想到 Flash 8 新加入的功能 - 預先編譯 MovieClip
只答對了一半,Font 元件是沒有辦法直接預先編譯的

以下就使用 Flash CS3、ActionScript 2.0 示範
(Flash 8 也是可以這樣用的)
預先將編譯字體編譯
省去重複編譯字體的時間
缺點是字體容量會直接反應在 *.fla 原始檔案的大小

首先,在 Flash 元件庫內新增想要嵌入的 Font 元件
名稱隨意~

設定 Font 元件 Linkage 屬性,勾選『Export for ActionScript』

建立一個 MovieClip,並於該元件內加入 Dynamic TextField,設定字體為之前建立的 Font 元件

將含有文字欄位的 MovieClip 轉為編譯過的Clip
你會發現這個動作比較耗時,因為它會連字體一併進行編譯

最後將 Font Linkage 屬性,取消勾選『Export for ActionScript』
而將編譯過的 MovieClip 勾選『Export for ActionScript』
以下用 ActionScript 2.0 方式示範如何使用剛才預先編譯的嵌入字體

影格 1:
var txt:TextField = createTextField("txt", 100, 100, 50, 100, 22);
txt.embedFonts = true;
var tf:TextFormat = txt.getTextFormat();
tf.font = "Arial";
txt._rotation = 10;
txt.text = "123456";
txt.setTextFormat(tf);

要注意的是字體名稱是 "Arial" 不是自行定義的 "Arial_Regular"

相關連結:
Flash 內嵌動態文字與樣式的問題

Read more...

2007年5月14日 星期一

Flash TextField Font Size Bug   [+/-]

Ticore's Blog

最近工作上需要用到動態建立不同尺寸的文字
結果發現到動態文字欄位最大尺寸的顯示居然有問題
不只是如此,連靜態文字欄位 Use Device Font 也有類似的問題

以下是簡單的 Bug 示範

建立兩個 160pt Static TextField
左邊是使用 Anti-alias for Animation
右邊是使用 Use device fonts

測試影片的結果可以發現 Use Device Font 的文字尺寸最大只能到 127 pt 左右
而且位置稍微有點偏移~

這問題似乎在 Flash 7、8 就已經存在了

問題表現程度略有差異而已

最後解決方式使用 MovieClip 多包一層
由外層 MovieClip 做放大兩倍動作才解決

Read more...

2007年5月13日 星期日

Flash CS3 小技巧 - 替 AS 檔案選擇版本   [+/-]

Ticore's Blog

Flash 現在出到 CS3 了
ActionScript 版本也增加到 1.0、2.0、3.0 三種
真是令人眼花撩亂
好在 *.fla 檔案可以設定使用的 ActionScript 版本
Action Script 編輯器便會依據設定的版本給予合適的程式碼提示
Auto-Format 功能也給依據設定來檢查程式碼

假如遇到 *.as 檔案怎麼辦? 因為它並不像 *.fla 可以設定 ActionSctipt 版本

Flash CS3 新增一個好用的小功能~
當編輯 *.as 檔案的時候,在右上角有一個 『Target:』下拉選單
平常是不能選擇的

只要在編輯 *.as 檔案的同時隨便開一個 *.fla 的檔案
此時 Target 選項便可以用來選擇 *.fla
而編輯 *.as 檔案時也會依照指定 *.fla 檔案內設定的 ActionScript 版本

Read more...

2007年5月7日 星期一

Flash CS3 - AS3 影格程式重複觸發問題   [+/-]

Ticore's Blog

承接上一篇的問題~

會造成影格程式重複觸發的命令不只是 addFrameScript 而已

任何讓 MovieClip 跳格的命令也都會

舉一個實際的測試範例

首先用 Flash CS3 開一個空白文件
建立一個 含有 3 個影格的 MovieClip
3 個影格程式都加上

trace("MC Frame " + currentFrame + " Script.");
stop();

放一個實體於 root 下,命名為 mc
最後在 root 影格 1 加上程式

mc.nextFrame();
mc.prevFrame();
mc.gotoAndStop(3);
mc.addFrameScript(0, null);

測試影片,輸出結果

MC Frame 3 Script.
MC Frame 3 Script.
MC Frame 3 Script.
MC Frame 3 Script.
MC Frame 3 Script.

可以發現到在影格程式內,對目標 MC 作影格控制動作與 addFrameScript
都會造成目標 MC 最後停留影格上的程式重複被觸發
觸發次數與呼叫次數相關

我也不知道為什麼會這樣
目前只能避免在影格程式執行階段直接呼叫這些動作

Read more...

2007年5月6日 星期日

一行 AS3 程式讓 Flash Player 9 死機   [+/-]

Ticore's Blog

你能相信只要一行 ActionScript 3 程式
就能讓 Flash Player 9 產生無窮迴圈而死機嗎?

不要懷疑!
那行程式就是 addFrameScript(1, null);

可以參考 ActionScript 3.0 未公開的函式 MovieClip.addFrameScript

按下 Ctrl + Enter 測試影片後~

假如在前後加上追蹤碼
便可以發現 addFrameScript 會造成 frame script 再度被觸發
而這個例子的 frame script 就是 addFrameScript
結果形成無窮迴圈

除此之外,還發現一個小Bug
就是錯誤訊息裡面的『15 秒』

Error: Error #1502: script 已經執行超過預設的 15 秒逾時時段。
   at Untitled_fla::MainTimeline/Untitled_fla::frame1()

PS. 在非 Debug 版本的 Flash Player 上無法直接看到錯誤訊息

那個執行逾時秒數在 SWF 發布設定裡是可以修改的
可是不管怎樣改
錯誤訊息都會顯示『15 秒』

實際測試過會掛掉的版本:
Flash Player 9.0.45.0
Flash Player 9.0.47.0
Flash Player 9.0.60.120
Flash Player 9.0.60.184
Flash Player 9.0.60.235
Flash Player 9.0.64.0
Flash Player 9.0.115.0
Flash Player 9.0.124.0
Flash Player 10.0.0.525
Flash Player 10.0.1.218
Flash Player 10.0.2.54
Flash Player 10.0.12.10
Flash Player 10.0.12.29
Flash Player 10.0.12.36
Flash Player 10.0.15.3 LNX
Flash Player 10.0.22.87

相關連結:
一行 AS3 程式讓 Flash Player 10 死機 Part 5
一行 AS3 程式讓 Flash Player 9 死機 Part 4
一行 AS3 程式讓 Flash Player 9 死機 Part 3
一行 AS3 程式讓 Flash Player 9 死機 Part 2

Read more...

2007年5月3日 星期四

Flex Metadata Tag - Frame FactoryClass   [+/-]

Ticore's Blog

在介紹這個 Tag 之前,先說說 Flash SWF 檔案 Streaming 與 Preloading
很早之前 Flash 為了要做到一邊下載一邊撥放的功能
把資料分別散佈到主時間軸的影格內
只要該影格所需的資料下載完畢,該影格便可進行撥放
我記得以前是號稱 SWF Streaming,不過現在與 RTMP Streaming 相比
恐怕只能稱作 SWF 漸進式下載 (Progressive Download) 而已了

最有代表性的應該就是 Flash Test Movie 時候,上面哪個長條圖了

結果漸進式下載常常會發生停頓的現象
要另外寫 Preloading 程式...
結果大多數情況下又回到了,全部下載完才能撥放的情況~~
只是可以看到下載進度而已....

要 Preloading 程式能夠正常執行
便需要能夠讓 Preloading 程式在主時間軸影格 1 輸出
其它的內容放在後面的影格
讓 Preloading 程式儘量小可以快速被下載完畢
好執行它的工作

在 Flash 開發工具內有好用的時間軸面板
很容易把 Preloading 程式與其它內容用影格分開來
但是在 Flex Builder 裡面並沒有時間軸面板啊
連怎樣作出多影格的 SWF 都不知道
那要怎樣管理 Preloading 呢?

答案就是標題 Flex Metadata Tag - Frame FactoryClass

先看一個簡單的使用例子:

Main.as
package {
 import flash.display.*;
 [Frame(factoryClass="Factory")]
    
 public class Main extends MovieClip {
  public function Main():*{
   
  }
 }
}

Factory.as

package {
 import flash.display.*; 
 public class Factory1 extends MovieClip {
  public function Factory():*{
   
  }
 }
}

Frame FactoryClass 標籤只對 Application Class (Document Class) 有作用
功能是用指定的 Class 取代作為 Application Class
並且在指定的 Class 增加影格數量

從上面的例子來看 Factory Class 便成為新的 Application Class
且具有兩個影格,影格標籤分別為 ["Factory", "Main"]
同時也兼具 Preloading、初始化 Main Insatnce 的任務

接下來,利用 FactoryClass 作一個完整的 Preloading

Main.as
package {
 import flash.display.*;
 [Frame(factoryClass="Factory")]
    
 public class Main extends MovieClip {
     // 隨便嵌入一張大圖,以突顯 Preloading 作用
  [Embed(source="assets/1600.jpg")]
   public var img:Class;
  public function Main():*{
   this.addChild(new img());
  }
 }
}

Factory.as

package{
 import flash.display.*;
 import flash.events.*;
 import flash.text.*;
 import flash.utils.*;
 
 public class Factory extends MovieClip {
  
  public var percentTxt:TextField;
  
  public function Factory():*{
   init();
  }
  private function init():*{
   
   stage.scaleMode = StageScaleMode.NO_SCALE;
   stage.align = StageAlign.TOP_LEFT;
   
   percentTxt = new TextField();
   percentTxt.width = 100;
   percentTxt.height = 20;
   percentTxt.x = (stage.stageWidth - 100) / 2;
   percentTxt.y = (stage.stageHeight - 20) / 2;
   percentTxt.border = true;
   
   var tf:TextFormat = percentTxt.getTextFormat();
   tf.align = TextFormatAlign.CENTER;
   percentTxt.defaultTextFormat = tf;
   
   this.addChild(percentTxt);
   
   this.addEventListener(Event.ENTER_FRAME, doEnterFrame);
   doEnterFrame();
   stop();
   
  }
  
  public function doEnterFrame(event:Event = null):void{
   var percent:Number =
    Math.round(loaderInfo.bytesLoaded / loaderInfo.bytesTotal * 100);
   percentTxt.text = percent + "%";
   if (loaderInfo.bytesLoaded == loaderInfo.bytesTotal) {
    this.removeEventListener(Event.ENTER_FRAME, doEnterFrame);
    this.removeChildAt(0);
    this.gotoAndStop(2);
    
    // Main Class 初始化
    var main:Class = Class(getDefinitionByName("Main"));
    this.addChild(new main());
   }
  }
 }
}
// Ticore's Blog - http://ticore.blogspot.com/

 

※FactoryClass 使用要點:

1. 不可以在 Factory Class 內明確引用其它非 Preloading 期間需要用到的 Class,
否則會造成引用的 Class 在影格 1 輸出,失去 Preloading 作用;
應該待讀取完畢後,使用反射 (Reflection) flash.utils.getDefinitionByName 方式取得 Class。

2. SWF 載入完畢後,仍要讓撥放頭 (playhead) 前進,Main Class 才可以被使用。

相關連結:
Multiple Frames Flex Application
BIT-101 Blog - Preloaders in AS3

Read more...