現在的跑者愈來愈幸福,每逄賽事總能見到馬拉松世界、運動筆記所出動或熱心自發的攝影大人,揹著沈重攝影器材,不畏日曬雨淋地在賽道旁補捉跑友身影,賽後再上傳至網站供跑友自取留念。(在此特向辛苦的攝影大哥大姐們致敬)
不過一場比賽常有成千上萬張照片,要在茫茫照片大海找尋自己的英姿有點小挑戰。照片雖然都已依拍攝時間排序,有時更會貼心註明拍攝地點,但要記下自己在何時何地遇到那家攝影師談何容易? (對我來說,記得要吸呼都快來不及了 XD) 我研究出最好的方法,是先由照片裡其他跑友的號碼布查出比賽成績,與自己的成績比較,藉以推算自己的出場時機,決定該要向前找還是向後翻,要跳過30分鐘還是一個小時,有了成績線索,找照片快多了。
手工作業過幾次,身為程式魔人手癢難耐,便用jQuery及Knockout寫了小工具網頁,配合以Greasemonkey預先由大會網站取得的成績資料,輸入號碼後立刻會帶出跑友成績。即能練功又實用,摸蛤仔兼洗褲來著~
程式即然都寫好了,不分享也是浪費,決定放上網站提供渣打馬跑友使用。網址: http://www.darkthread.net/mph/
使用方法很簡單,先選好半馬或全馬,接著輸入號碼,下方就會出現吻合的成績資料,按ESC則可清除輸入內容。
主程式不多,照例在一百行內搞定,一併補上供參:
<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>馬拉松成績速查</title>
<style>
body { font-family:'Arial Unicode MS' }
.hdr { padding: 12px; font-size: 16pt; background-color: #0094ff; color: white; }
.query { padding: 6px; }
.title { margin-right: 6px; }
.keywd { width: 100px; padding: 3px; }
</style>
</head>
<body>
<divclass="hdr">
2014渣打公益馬拉松 成績速查
</div>
<divclass="query">
<selectdata-bind="options: Groups, value: Group"></select>
<labelclass="title">號碼布</label>
<inputtype='text'class="keywd"
data-bind="value: RunnerNo, valueUpdate: 'afterkeyup', event: { keyup: onKeyUp }"/>
(按ESC清除)
</div>
<divclass="hints">
<uldata-bind="foreach: Hints">
<li><spandata-bind="text: no"></span> -
<spandata-bind="text: time"></span></li>
</ul>
</div>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js "></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js "></script>
<script src="Chartered2014-42K.js"></script>
<script src="Chartered2014-21K.js"></script>
<script>
function myViewModel() {
var self = this;
self.RunnerNo = ko.observable("");
self.onKeyUp = function (d, e) {
if (e.which == 27)
{
self.RunnerNo("");
}
};
self.Hints = ko.observableArray();
self.Groups = ko.observableArray();
self.Group = ko.observable();
self.DataGroups = {};
for (var grpName in dataSource) {
var records = dataSource[grpName];
var data = $.map(Object.keys(records).sort(), function (n) {
return { no: n, time: records[n] };
});
self.DataGroups[grpName] = data;
self.Groups.push(grpName);
}
self.Data = ko.computed(function () {
return self.DataGroups[self.Group()];
});
ko.computed(function () {
var key = self.RunnerNo();
if (key.length == 0 || isNaN(key)) {
self.Hints([]);
return;
}
var count = 0;
var data = self.Data.peek();
var res = [];
$.each(data, function (i, item) {
if (count >= 10) returnfalse;
if (item.no.indexOf(key) == 0) {
count++;
res.push(item);
}
});
self.Hints(res);
}).extend({ throttle: 100 });
}
var vm = new myViewModel();
ko.applyBindings(vm);
</script>
</body>
</html>