LoginSignup
8
8

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で引用型リンクをコピーする機能追加
8
8
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
8
8