2009年3月31日火曜日

JavaScriptでWYSIWYGエディタへの道

JavaScriptのWYSIWYGエディタは世の中にたくさんあるけれど、いったいどうやって実現しているんだろう?
…と気になってしまって夜も眠れなくなりそうになったのでちょっと調べてみた。

最初に見つけたのは、iframeを作ってcontentEditable = trueにする方法だった。

contentEditable.html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<title>contentEditable</title>
<script type="text/javascript">
//<![CDATA[
var o = null;
var f = null;
var d = null;
var b = null;
var la = null;
function findBody(doc){
for(var i = 0; i < doc.childNodes.length; ++i){
if(doc.childNodes[i].nodeName == 'HTML'){
for(var j = 0; j < doc.childNodes[i].childNodes.length; ++j){
if(doc.childNodes[i].childNodes[j].nodeName == 'BODY'){
return doc.childNodes[i].childNodes[j];
}
}
}
}
return null;
}
function onLoad1(){
o = document.getElementById('o');

f = document.getElementById('f');
d = f.contentDocument;
b = findBody(d);
d.charset = 'utf-8';
b.contentEditable = true;
b.innerHTML = '<div>HELLO WORLD</div>'
+ '<font style="color:red;">red string</font> '
+ '<a href="http://www.google.com/">google</a> '
+ '<font style="color:blue;">blue string</font>'
+ '<div>editable</div>';
b.focus();
}
function test1(){
var s = f.contentWindow.getSelection();
for(var i = 0; i < s.rangeCount; ++i){
var r = s.getRangeAt(i);
la = r;
var fragment = d.createDocumentFragment();
var div = d.createElement('div');
div.innerHTML = '<font style="color:green;font-size:large;font-weight:bold;">'
+ r + '</font>';
while(div.firstChild){
fragment.appendChild(div.firstChild);
}
var container = r.startContainer;
var offset = r.startOffset;
r.deleteContents();
switch(container.nodeType){
case 1:
container.insertBefore(fragment, container.clidNodes[offset]);
break;
case 3:
var node = container.splitText(offset);
node.parentNode.insertBefore(fragment, node);
break;
}
}
}
//]]>
</script>
</head>
<body onload="javascript:onLoad1()">
hello
world<br/>
<iframe name="f" id="f" src="about:blank" width="200" height="150"></iframe><br/>
<input type="button" value="test1" onclick="javascript:test1()"/><br/>
<span name="o" id="o">...</span>
</body>
</html>

※注意※IEでは動きません。

2009年3月30日月曜日

Tour de Flex

Tour de Flexも気になる~。時間つくらなくちゃ~。
もとねたはadakoda

Flex Core ComponentsやサードパーティのComponentsや、FlickrなどのCloud APIsも収録されていて、ソースコードも閲覧できるらしい。

2009年3月24日火曜日

SubversionのWindows版 Ruby Bindings バイナリでsvn info

相変わらずドキュメント少ないなぁ。
http://svn.collab.net/viewvc/svn/trunk/subversion/bindings/swig/ruby/test/ ここを参考にして、とりあえず svn info もどきを作ってみた。
svn_info.rb
#!/usr/local/bin/ruby -Ku
# -*- encoding: utf-8 -*-

require 'svn/core'
require 'svn/client'

def make_context(user, pass)
raise 'no block given' unless block_given?

ctx = Svn::Client::Context.new

ctx.add_simple_prompt_provider(0) do |cred, realm, username, may_save|
cred.username = user
cred.password = pass
cred.may_save = false
end

yield ctx
end

def svn_info(repos_url, user, pass)
make_context(user, pass) do |ctx|
ctx.info(repos_url, 'head') do |path, info|
puts <<INFO
Path: #{path}
URL: #{info.url}
Repository Root: #{info.repos_root_url}
Repository UUID: #{info.repos_UUID}
Revision: #{info.rev}
Node Kind: #{info.kind}
Last Changed Rev: #{info.last_changed_rev}
Last Changed Date: #{info.last_changed_date}
INFO
end
end
end

if $0 == __FILE__
svn_info(*ARGV)
end

SubversionのWindows版 Ruby Bindings バイナリ

今更だけどSubversionのWindows版 Ruby Bindings バイナリが配布されているのを見つけた。

普段はTortoiseSVNで十分なので、コマンドライン版のアップデートを怠っていたのだけれど、久しぶりにアップデートを思い立ってsubversion.tigris.orgに立ち寄ってようやく気がついた。2008年10月末頃からバイナリが配布されていたようだ。

苦労しながらRuby Bindingsをビルドしていた頃が懐かしい。2007年末頃だったかな。

たまにリポジトリやワーキングコピーの操作をプログラムから行いたい時があって、svn.exeをパイプでつないでお茶を濁していたのともサヨウナラできてしまうかも。

早速、多くのマシンにインストールしたOne Click Rubyのruby 1.8.6 p111 i386-mswin32で使ってみることに。

とにかく動かしてみたいので、tigrisからsvn-win32-1.5.6.zipとsvn-win32-1.5.6_rb.zipをダウンロードしてlib/ruby/site_ruby/1.8/svnにぶち込んでirbしてみたが・・・

エラーを示すアラートダイアログボックス。

序数3866がダイナミックライブラリ LIBEAY32.dllから見つかりませんでした

C:\Temp>irb -r svn/client
c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/svn/ext/core.dll: 182: このオペレーテ
ィング システムでは %1 は実行されません。 - c:/App/ruby-186-26/lib/ruby/site_r
uby/1.8/svn/ext/core.dll (LoadError)
from c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/rubygems/custom_require.r
b:31:in `require'
from c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/svn/error.rb:2
from c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/rubygems/custom_require.r
b:31:in `gem_original_require'
from c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/rubygems/custom_require.r
b:31:in `require'
from c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/svn/client.rb:3
from c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/rubygems/custom_require.r
b:31:in `gem_original_require'
from c:/App/ruby-186-26/lib/ruby/site_ruby/1.8/rubygems/custom_require.r
b:31:in `require'
from c:/App/ruby-186-26/lib/ruby/1.8/irb/init.rb:252:in `load_modules'
from c:/App/ruby-186-26/lib/ruby/1.8/irb/init.rb:250:in `each'
from c:/App/ruby-186-26/lib/ruby/1.8/irb/init.rb:250:in `load_modules'
from c:/App/ruby-186-26/lib/ruby/1.8/irb/init.rb:21:in `setup'
from c:/App/ruby-186-26/lib/ruby/1.8/irb.rb:54:in `start'
from c:/App/ruby-186-26/bin/irb:13

C:\Temp>

どうやらdllが古いらしい。svn-win32-1.5.6のbinにPATHを通し、ruby-186-26/binのlibeay32.dllとssleay32.dllのファイル名を適当に変えて、再挑戦・・・

D:\Temp>irb -r svn/core
irb(main):001:0>
D:\Temp>

動いた~♪

EclipseNSISを試してみる

NSISでインストーラを作ってみることを思い立ち、久しぶりにしらべてみたところ、EclipseNSISという便利そうなプラグインが開発されていることを発見。早速試してみることにする。

手元にあったEclipseはEuropa(3.3.2 M20080221-1800)で、EclipseNSISは3.4以上でないと動かないとのこと。早速eclipse.orgからeclipse-jee-ganymede-SR2-win32.zipをダウンロード&インストール&アップデート。(3.4.2 M20090211-1700)
※jeeは163Mもある。Eclipse IDE for C/C++ Developers (68 MB)こっちでも良かったかも。

そして、EclipseのSoftware Updates and Add-onsでhttp://eclipsensis.sf.net/updateからインストールする。EclipseNSIS(0.9.7.2)とEclipseNSIS InstallOptions Editor(0.9.7)の2つを選択した。

NSISを使うためには当然ながらNSISをインストールしなければいけない。NSISのウェブサイトからNSIS 2.44 Feb 21, 2009(現時点での最新版)をダウンロード&インストール。

以上で環境構築完了。

プロジェクトの作成は、EclipseのFile>New>Other...からEclipseNSIS>NSIS Scriptを選択するとWizardが起動するので必要な情報を入力していくだけ。

驚くほど簡単だ。

2009年3月23日月曜日

Bloggerにコードを載せる(2)

スタイルを変更してみた。

CSS
/* MyCode
----------------------------------------------- */
.mycodeT, .mycodeB {
font-family: consolas, "Courier New", courier, monospace;
padding: 3px 3px 3px;
}
.mycodeT {
margin: 1em 1em 0;
color: black;
background: #ffc080;
font-weight: bold;
}
.mycodeB {
margin: 0 1em 1em;
color: black;
background: papayawhip;
border-width: 1px;
border-color: #ffc080;
border-style: solid;
overflow-x: auto;
word-wrap: normal;
}

Xmlを便利に扱えるXmlSimple

いままでXMLはREXMLで扱っていたけど、XmlSimpleの方が便利そうだ。

Bloggerにコードを載せる

以前のものを改良してみた。
CSS
/* MyCode
----------------------------------------------- */
.mycodeT, .mycodeB {
font-family: consolas, "Courier New", courier, monospace;
padding: 3px 3px 3px;
}
.mycodeT {
margin: 1em 1em 0;
color: #e04000;
background: #ffc080;
font-weight: bold;
}
.mycodeB {
margin: 0 1em 1em;
color: black;
background: papayawhip;
border-width: 1px;
border-style: dashed;
border-color: maroon;
overflow: scroll;
word-wrap: normal;
}
ついでにAIRで成型用のツールを作ってみた。
BloggerStyle
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
title="Sample"
invoke="invokeHandler()">
<mx:Style>
WindowedApplication
{
background-color:"0xffffff";
font-size:"12";
}
</mx:Style>
<mx:Script>
<![CDATA[
private static var TEMPLATE_TEXT:String = '<pre class="mycodeT">%name%</pre><pre class="mycodeB">%text%</pre>';

private function invokeHandler():void {
_srcName.setFocus();
}
private function changeHandler(event:Event):void
{
_dstText.text = format();
}
private function format():String
{
var name:String = escape(_srcName.text);
var text:String = escape(_srcText.text);
return TEMPLATE_TEXT.replace(/%name%/, name).replace(/%text%/, text);
}
private function escape(x:String):String
{
x = x.replace(/&/g, '&amp;');
x = x.replace(/</g, '&lt;');
x = x.replace(/>/g, '&gt;');
x = x.replace(/"/g, '&quot;');
return x;
}
]]>
</mx:Script>
<mx:HDividedBox width="100%" height="100%">
<mx:VBox width="50%" height="100%">
<mx:TextInput id="_srcName" width="100%" change="changeHandler(event)"/>
<mx:TextArea id="_srcText" width="100%" height="100%" change="changeHandler(event)"/>
</mx:VBox>
<mx:TextArea id="_dstText" width="50%" height="100%" editable="false"/>
</mx:HDividedBox>
</mx:WindowedApplication>

2009年3月19日木曜日

Flex_SDKでAIRアプリを作る

AIRアプリは無料で作れる。
手順は次の通り。

1. Javaをダウンロード&インストールする。
2. Adobe® Flex® 3 SDKをダウンロード&インストールする。
3. Sample.mxmlを書いてコンパイルする。
$ amxmlc Sample.mxml
4. Sample-app.xmlを書いて実行してみる。
$ adl Sample-app.xml
5. パッケージングする。
$ adt -certificate -cn SelfSigned 1024-RSA Sample.pfx password
※自己署名証明書は1回作れば使い回せる
$ adt -package -storetype pkcs12 -keystore Sample.pfx -storepass password Sample.air Sample-app.xml Sample.swf icons
※この例ではアプリケーションにアイコンを設定しているのでパッケージにiconsディレクトリを含めている。

Sample.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
title="Sample"
invoke="invokeHandler()">
<mx:Style>
WindowedApplication
{
background-color:"0xffffff";
font-size:"12";
}
</mx:Style>
<mx:Script>
<![CDATA[
private static var PROLOGUE:String = '<div class="mycode1"><pre class="mycode2">';
private static var EPILOGUE:String = '</pre></div>';

private function invokeHandler():void {
_srcText.setFocus();
}
private function changeHandler(event:Event):void
{
_dstText.text = toMyCode(_srcText.text);
}
private function toMyCode(x:String):String
{
x = x.replace(/&/g, '&amp;');
x = x.replace(/</g, '&lt;');
x = x.replace(/>/g, '&gt;');
x = x.replace(/"/g, '&quot;');
return PROLOGUE + x + EPILOGUE;
}
]]>
</mx:Script>
<mx:HDividedBox width="100%" height="100%">
<mx:TextArea id="_srcText" width="50%" height="100%" change="changeHandler(event)"/>
<mx:TextArea id="_dstText" width="50%" height="100%" editable="false"/>
</mx:HDividedBox>
</mx:WindowedApplication>

Sample-app.xml
<?xml version="1.0" encoding="utf-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.5">
<id>com.blogspot.takumakei.Sample</id>
<version>0.1</version>
<filename>Sample</filename>
<initialWindow>
<content>Sample.swf</content>
<visible>true</visible>
<!--<systemChrome>none</systemChrome>-->
<!--<transparent>true</transparent>-->
<width>320</width>
<height>240</height>
</initialWindow>
<icon>
<image16x16>icons/icon_016.png</image16x16>
<image32x32>icons/icon_032.png</image32x32>
<image48x48>icons/icon_048.png</image48x48>
<image128x128>icons/icon_128.png</image128x128>
</icon>
</application>


Adobe Developer Box

気になる。あ~気になる。でも今は時間がないから試せない。気になるぅ~。

Adobe Developer Box

成型用スクリプト

Bloggerは記事をHTMLで編集することができる。それに独自のCSSを編集することもできる。

記事にコードを書く時に使うスタイルを定義してみたので、ついでに、コードを投稿する時に整形するためのスクリプトも作ってみた。

カスタマイズのHTMLの編集のテンプレートの編集で定義したスタイルと成型用スクリプト。

/* MyCode
----------------------------------------------- */
.mycode1 {
border-width: 1px;
border-style: dashed;
border-color: maroon;
background: papayawhip;
}
.mycode2 {
color: black;
padding: 0 0 0 5px;
}

#!/usr/local/bin/ruby -Ku
print '<div class="mycode1"><pre class="mycode2">'
while line = gets
line.gsub!(/&/, '&amp;')
line.gsub!(/</, '&lt;')
line.gsub!(/>/, '&gt;')
line.gsub!(/"/, '&quot;')
print line
end
puts '</pre></div>'

Bluffでグラフ描画

以下、配布サイト(http://bluff.jcoglan.com/)からの抜粋。

BluffはGruff graphing library for RubyをJavaScriptに移植したもの。
他のライブラリへの依存を最低限にしつつ、Gruffの全ての機能をサポートするようにデザインした。
必要なサードパーティーのスクリプトはJS.Class(約2kb gzip圧縮時)と、Internet Explorerでcanvasをを使えるようにするGoogleのExCanvasだ。
これら2つのスクリプトはBluffと共に提供されている。Bluff自身はgzip圧縮時に約8kb程度。

とのこと。

で試しに書いたスクリプトが以下。

<html>
<head>
<script language="javascript" src="js-class.js" type="text/javascript"></script>
<script language="javascript" src="bluff-min.js" type="text/javascript"></script>
<script language="javascript" src="excanvas.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
function drawSample(){
var g = new Bluff.Line('example', 800);
//g.theme_keynote();
//g.theme_37signals();
//g.theme_rails_keynote();
//g.theme_odeo();
//g.theme_pastel();
//g.theme_greyscale();
//g.hide_line_markers = true;
g.hide_dots = true;
//g.hide_legend = true;
g.minimum_value = 0;
g.maximum_value = 200;
g.x_axis_label = 'Year';
g.y_axis_increment = 25;
g.zero_degree = 90;
g.title = 'グラフ描画サンプル';
g.data('Apples', [1,2,3,4,4,3,4, 1,2,3,4,4,3,4]);
g.data('マンゴー',[3,4,5,6,7,8,9, 10,12,13,140,15,16]);
g.data('Oranges', [4,8,7,7,3,1,4, 4,8,7,7,3,1,4]);
g.data('なにか', [100, 120, 100, 119, 98, 102, 100, 100, 120, 100, 119, 98, 102, 100]);
g.labels = {0:'2003', 4:'2004', 8:'2005', 12:'2006' };
g.draw();
}
</script>
</head>
<body onload="drawSample()">
<canvas id="example" width="100%"></canvas>
</body>
</html>

2009年3月13日金曜日

Windowsでwhich

whichがないと何かと不便。
whichはとっても便利なのになんで標準装備されていないのだろう・・・

#! ruby

def search_dir(result, target_path)
puts " #{target_path}" if $DEBUG
Dir[target_path].each do |file|
unless result.index(file)
puts(file.gsub(/\//, '\\'))
result << file
end
end
end

def main(args)
if args.size == 0
puts "Usage error: which keyword"
exit
end

target = args[0]
result = []

(['.'] + ENV['PATH'].split(';')).each do |path|
path.sub!(/^"*/, '').sub!(/"*$/, '')
search_dir(result, File.expand_path(File.join(path, "#{target}")))
search_dir(result, File.expand_path(File.join(path, "#{target}.*")))
end
end

main(ARGV)

2009年3月5日木曜日

Win32APIでDLLの関数を呼ぶ

DLL
extern "C" {
struct P3 {
char* a;
char* b;
wchar_t* c;
};

void __declspec(dllexport) study2(P3* ptr)
{
ptr = ptr;
ptr->c[0] = L'希';
}
}
Ruby
#!/usr/local/bin/ruby -Ku
# -*- encoding: utf-8 -*-
require 'Win32API'
require 'nkf'

def sjis(x)
NKF.nkf('-m0 --ic=utf8 --oc=cp932', x)
end

def utf16le(x)
NKF.nkf('-m0 --ic=utf8 --oc=utf-16le', x)
end

fn_study2 = Win32API.new('Study.dll', 'study2', %w(p), 'v')
x = [ sjis("hello"), sjis("world"), utf16le("あかさた") ]
fn_study2.call(x.pack("ppp"))
puts(NKF.nkf('-m0 --ic=utf-16le --oc=cp932', x[2]))

2009年3月2日月曜日

RubyのWin32APIとUNICODE

Ruby-1.9.1でWin32APIを使ってUNICODE文字列を渡す例。
#ほとんどRubyリファレンスマニュアルのコピペ。

#!/usr/local/bin/ruby
# -*- encoding: utf-8 -*-

require 'iconv'
require 'Win32API'

class Win32API
MB_OK = 0

@@messagebox = nil

def self.MessageBox(wnd, text, caption, type = MB_OK)
@@messagebox ||= Win32API.new('user32', 'MessageBoxW', %W(p p p i), 'i')
@@messagebox.call(wnd, text, caption, type)
end
end

a = Iconv.iconv('utf-16le', 'utf-8', 'やぁ')[0]
b = Iconv.iconv('utf-16le', 'utf-8', 'うあは??')[0]
Win32API.MessageBox(0, a, b)