7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

citation - Evernoteに実装するアンビリバボーな機能 #2

Last updated at Posted at 2015-12-18

アンビリーバボ?アンビリバボー?

http://rashita.net/blog/?p=16722

こちらの2つ目、「拡張型ノートリンク」と書かれた機能をAppleScriptで実装しました。

使い方(基礎編)

Evernoteのノート上で引用したい箇所を選択状態に。

citation screenshot 1

スクリプトメニューからcitationを実行すると

citation screenshot 2

「Copied」と通知。

citation screenshot 3

好きなところでペーストすれば、それっぽい見た目のノートリンク。

citation screenshot 4

使い方(応用編)

スクリプトオブジェクトを使って他のアプリケーションにも拡張できます。

例えばSafariの場合。ページ上で引用部分を選択してcitationを実行。

citation screenshot 5

Evernoteにペーストで引用文を含んだリンクに。画像は反映されません。

citation screenshot 6

今のところEvernoteとSafariの2種類のみです。ソースコードをいじって他のアプリケーションを追加してみてください。

リンクの色を変えるなり、アイコンを付けるなりしないと見分けがつきませんが、それはまた別の機会に。

(2015-12-18 追記) 使い方(発展編)

HTMLを理解できる方にオススメ。

ソースコード冒頭にあるproperty citationTemplate : ...の部分で、貼り付けるリンクの見た目を決めています。

このテンプレート、見ればわかるようにHTMLで記述されているのです。

  • 元のままでは無駄な情報が多い
  • 文字やリンクの大きさや色、順番が気にくわない
  • 引用はblockquoteじゃないと気持ち悪い

なんて方はHTMLをいじって自分好みの見た目に変更しましょう。

その際に注意すべきこととして 特殊文字の取り扱い があります。

property citationTemplate : ...の部分ではHTML全体を文字列としてcitationTemplateという変数に入れるため、ダブルクオーテーション(")で囲っています。

HTML内でダブルクオーテーション(")を使うと、そこが文字列の終わりと判断されて「残りの文字列はなんだ?」ってエラーが出てしまいます。

そこで、エスケープのためにバックスラッシュ(\)を前に置いて、\"と記述してください。こうすればエラーは出ないはず。

他にもつまずくことがあるかもしれませんが、Googleに頼りながら試してみてください。

動作環境

  • Mac OS X: 10.11
  • Evernote: ver 6.3 (452832 Direct)

ソースコード

citation.scpt
(*
* [[Clipping]]: 引用内容 - getClipping()
* [[Link]]: リンクURL - getLink()
* [[Title]]: リンクタイトル - getTitle()
* [[Author]]: 作者 - getAuthor()
* [[Publisher]]: 出版社、発行元 - getPublisher()
* [[Date]]: 日付 - getDate()
*)
property citationTemplate : "<br>
<div class='citation' style='padding: 0px 30px;'>
	<div class='clipping' style='line-height:18px; padding: 8px 15px; border-left:2px solid #dedede; color: #919191; font-family: Helvetica, sans-serif; font-style: italic; font-size: 14px;'>[[Clipping]]</div>
	<div style='padding-left: 17px;'>
		<div class='title' style='color: #000000; font-family: Helvetica, sans-serif; font-size: 14px; margin-top: 19px;'>
			<a style='text-decoration: none; color: rgba(45,190,96,1.0);' href='[[Link]]'>
				&quot;[[Title]]&quot;
			</a>
		</div>
		<div style='color: #919191; font-size: 12px; margin-top: 4px; font-family: 'CaeciliaLTStd', serif; font-style: italic;'>
			<span class='author'>[[Author]]</span>
			<span class='publisher'>[[Publisher]].</span>
			<span class='date'>[[Date]]</span>
		</div>
	</div>
</div>
<br>"

on run
	
	--アプリケーション判別
	if application "Evernote" is running and application "Evernote" is frontmost then
		set curApp to make Evernote
	else if application "Safari" is running and application "Safari" is frontmost then
		set curApp to make Safari
	else
		--set curApp to make some_application
		error
	end if
	
	--citationのHTML作成
	copy citationTemplate to citation
	repeat with replacement in {¬
		{"[[Clipping]]", getClipping() of curApp}, ¬
		{"[[Link]]", getLink() of curApp}, ¬
		{"[[Title]]", getTitle() of curApp}, ¬
		{"[[Author]]", getAuthor() of curApp}, ¬
		{"[[Publisher]]", getPublisher() of curApp}, ¬
		{"[[Date]]", getDate() of curApp} ¬
			}
		set {f, r} to replacement
		set r to my escapeHTMLSpecialChars(r)
		set r to my replace(r, {return, linefeed}, "<br>")
		set citation to my replace(citation, f, r)
	end repeat
	
	--HTMLをコピー
	my copyHTML(citation)
	display notification getTitle() of curApp with title "Copied"
end run

script some_application
	
	on getClipping()
		repeat until application id (my appID) is frontmost
			activate application id (my appID)
		end repeat
		set the clipboard to ""
		set tmpClipboard to the clipboard as record
		tell application "System Events"
			keystroke "c" using {command down}
			set timestamp to current date
			repeat while (the clipboard as record) = tmpClipboard
				if (current date) > timestamp + 10 then
					error
				end if
				delay 1
			end repeat
		end tell
		return the clipboard as text
	end getClipping
	
	on getLink()
		return ""
	end getLink
	
	on getTitle()
		return ""
	end getTitle
	
	on getAuthor()
		return ""
	end getAuthor
	
	on getPublisher()
		return ""
	end getPublisher
	
	on getDate()
		return ""
	end getDate
	
	on make
		set a_class to me
		script Instance
			property parent : a_class
			property appID : run script "tell application \"System Events\" to return bundle identifier of process 1 whose frontmost = true and visible = true"
		end script
	end make
end script

script Evernote
	property parent : some_application
	
	--on getClipping()
	--	continue getClipping()
	--end getClipping
	
	on getLink()
		tell application "Evernote"
			return note link of (note id (my noteID)) of notebook 1
		end tell
	end getLink
	
	on getTitle()
		tell application "Evernote"
			return title of (note id (my noteID)) of notebook 1
		end tell
	end getTitle
	
	on getAuthor()
		tell application "Evernote"
			set noteAuthor to author of (note id (my noteID)) of notebook 1
		end tell
		if noteAuthor = missing value then
			set noteAuthor to ""
		end if
		return noteAuthor
	end getAuthor
	
	on getPublisher()
		tell application "Evernote"
			return name of notebook of (note id (my noteID)) of notebook 1
		end tell
	end getPublisher
	
	on getDate()
		tell application "Evernote"
			tell creation date of (note id (my noteID)) of notebook 1 as date
				return "" & year & "年" & (month of it as number) & "月" & day & "日"
			end tell
		end tell
	end getDate
	
	on make
		set self to continue make
		script EvernoteInstance
			property parent : self
			property noteID : run script "tell application \"Evernote\" to return local id of item 1 of (selection as list)"
		end script
	end make
end script

script Safari
	property parent : some_application
	
	on getClipping()
		tell application "Safari"
			set clipping to do JavaScript "document.getSelection().toString();" in current tab of window id (my windowID)
		end tell
		if clipping = "" then
			try
				set clipping to my extractText(my metas, linefeed & "og:description,", linefeed)
			on error number -2700
			end try
		end if
		return clipping
	end getClipping
	
	on getLink()
		try
			return my extractText(my metas, linefeed & "og:url,", linefeed)
		on error number -2700
		end try
		tell application "Safari"
			return URL of current tab of window id (my windowID)
		end tell
	end getLink
	
	on getTitle()
		try
			return my extractText(my metas, linefeed & "og:title,", linefeed)
		on error number -2700
		end try
		tell application "Safari"
			return name of current tab of window id (my windowID)
		end tell
	end getTitle
	
	on getAuthor()
		try
			return my extractText(my metas, linefeed & "twitter:creator,", linefeed)
		on error number -2700
		end try
		return ""
	end getAuthor
	
	on getPublisher()
		try
			return my extractText(my metas, linefeed & "og:site_name,", linefeed)
		on error number -2700
		end try
		return ""
	end getPublisher
	
	on getDate()
		try
			set ISODate to my extractText(my metas, linefeed & "article:published_time,", linefeed)
			tell my ISODateToDate(ISODate)
				return "" & year & "年" & (month of it as number) & "月" & day & "日"
			end tell
		on error number -2700
		end try
		return ""
	end getDate
	
	on make
		set self to continue make
		script SafariInstance
			property parent : self
			property windowID : run script "tell application \"Safari\" to return id of window 1 whose current tab ≠ missing value"
			property metas : run script "tell application \"Safari\" to return do JavaScript \"var ary=[]; var elements=document.getElementsByTagName('meta'); for(i=0; i<elements.length; i++) {content=elements[i].getAttribute('content'); if(elements[i].getAttribute('property')) {ary.push(elements[i].getAttribute('property') + ',' + elements[i].getAttribute('content'));} else if(elements[i].getAttribute('name')) {ary.push(elements[i].getAttribute('name') + ',' + elements[i].getAttribute('content'));}} '\\\\n' + ary.join('\\\\n') + '\\\\n';\" in current tab of window id " & windowID
		end script
	end make
end script

on escapeHTMLSpecialChars(args)
	if class of args = list then
		repeat with i from 1 to count args
			set item i of args to my escapeHTMLSpecialChars(item i of args)
		end repeat
	else
		set args to my replace(args, "&", "&amp;")
		set args to my replace(args, "<", "&lt;")
		set args to my replace(args, ">", "&gt;")
		set args to my replace(args, quote, "&quot;")
		set args to my replace(args, "'", "&#039;")
	end if
	return args
end escapeHTMLSpecialChars

on replace(str as text, find, replace as text)
	set oldDel to AppleScript's text item delimiters
	set AppleScript's text item delimiters to find
	set replaceList to text items of str
	set AppleScript's text item delimiters to replace
	set str to replaceList as string
	set AppleScript's text item delimiters to oldDel
	return str
end replace

on copyHTML(HTML as text)
	set the clipboard to {«class utf8»:HTML, string:HTML, Unicode text:HTML, «class HTML»:my htmlToRawData(HTML)}
end copyHTML

on extractText(theText, beginText, endText)
	--beginText, endText例: {text:"a", forwardSearch:false, containText:true}
	set beginText to (beginText as record) & {forwardSearch:true, containText:false}
	set endText to (endText as record) & {forwardSearch:true, containText:false}
	
	if theText does not contain text of beginText or theText does not contain text of endText then error "引数の文字列が含まれません"
	
	set scraps to my split(theText, text of beginText)
	if forwardSearch of beginText then
		set theText to my join(rest of scraps, text of beginText)
	else
		set {theText} to reverse of scraps
	end if
	set scraps to my split(theText, text of endText)
	if forwardSearch of endText then
		set {theText} to scraps
	else
		set theText to my join(reverse of rest of reverse of scraps, text of endText)
	end if
	if containText of beginText then
		set theText to "" & text of beginText & theText
	end if
	if containText of endText then
		set theText to "" & theText & text of endText
	end if
	return theText
end extractText

on ISODateToDate(ISODate)
	if class of ISODate = «class isot» then
		set aDate to ISODate as date
	else
		set aDate to (do shell script "echo " & quoted form of ISODate as «class isot») as date
		if ISODate ends with "Z" then
			set aDate to my shiftDateFromGMT(aDate)
		end if
	end if
	return aDate
end ISODateToDate

on htmlToRawData(HTML as text)
	return run script "«data HTML" & (do shell script "echo " & quoted form of HTML & " | hexdump -v -e '/1 \"%02X\"'") & "»"
end htmlToRawData

on split(str as text, delim)
	set oldDel to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delim
	set aList to text items of str
	set AppleScript's text item delimiters to oldDel
	return aList
end split

on join(textList, term as text)
	set oldDel to AppleScript's text item delimiters
	set AppleScript's text item delimiters to term
	set aText to textList as string
	set AppleScript's text item delimiters to oldDel
	return aText
end join

on shiftDateFromGMT(aDate)
	global timeToGMT
	try
		timeToGMT
	on error number -2753
		set timeToGMT to time to GMT
	end try
	return aDate + timeToGMT
end shiftDateFromGMT

普段AppleScriptを使わない方へ

メニューバーにスクリプトメニューを表示する方法

このスクリプトはスクリプトメニューから実行することを想定しています。スクリプトエディタから実行してもエラーになってしまうので、次に示す設定をお願いします。

  • スクリプトエディタ.app (/Applications/Utilities/Script Editor.app) 起動
  • メニューバー
  • 「スクリプトエディタ」
  • 「環境設定…」
  • 「一般」
  • 「スクリプトメニュー」
  • 「メニューバーにスクリプトメニューを表示」 にチェック

メニューバーにスクリプトメニューのアイコンが表示されればOK。

GUIスクリプティングを有効に

スクリプト中に「GUIスクリプティング」と呼ばれる機能を使っています。スクリプトメニューから実行する前に以下の設定が必要です。

  • システム環境設定.app (/Applications/System Preferences.app) 起動
  • 「セキュリティとプライバシー」
  • 「プライバシー」
  • 鍵マーク 「変更するにはカギをクリックします。」をクリック
  • パスワード入力
  • 「ロックを解除」
  • 「アクセシビリティ」
  • 「+」
  • /System/Library/CoreServices/SystemUIServer.app を選択
  • 「開く」

(2015-12-18 追記) スクリプトファイルの保存方法

スクリプトファイルの保存場所を書いていませんでした。スクリプトメニューからAppleScriptを実行するには特定の場所にファイルがなければいけません。

下の方法でスクリプトファイルを保存してください。

  • スクリプトエディタ.app (/Applications/Utilities/Script Editor.app) 起動
  • ファイル選択ダイアログが表示された場合は「新規書類」
  • このページ中部の「ソースコード」にある「citation.scpt」全体をスクリプトエディタ.appにコピペ
  • ⌘+Kでコンパイル
  • ⌘+Sで保存
  • 名前を「citation.scpt」に
  • 保存場所を「/Users/(ユーザ名)/Library/Scripts」に
  • ファイルフォーマットは「スクリプト」のままで
  • 「保存」

保存が完了しました。スクリプトエディタ.appを終了してください。

スクリプトの実行方法は冒頭の「使い方(基礎編)・(応用編)」に書いたとおり。きちんと動くか確認してみてください。

更新履歴

  • 2015-12-16: Evernoteのノート上で引用型ノートリンクをコピーする機能実装
  • 2015-12-17: Safariで引用型リンクをコピーする機能追加
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?