Test Case: Twitter Developers: Using OAuthThis snippet should reproduce the request signature for the 'request_token' example request found
here.
url := "https://api.twitter.com/oauth/request_token"
oauth_consumersecret( "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98" )
params =
( LTRIM JOIN&
oauth_consumer_key=GDdmIQH6jhtmLUypg82g
oauth_nonce=QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk
oauth_signature_method=HMAC-SHA1
oauth_timestamp=1272323042
oauth_version=1.0
oauth_callback=
)
params .= uriencode( "http://localhost:3005/the_dance/process_callback?service_provider_id=11" )
msgbox % oauth_headerauth( url, params, "POST" )
exitapp
Example: Imgur's Authenticated APIYou will need an Imgur account to use this demo script.
Please understand, this script is ONLY to demonstrate OAuth authenticated requests and is NOT meant as a professional release. Its functionality is LIMITED.
This script contains a
consumer_key and
consumer_secret. Please do not hijack these for your own Imgur scripts. It is very easy to register for your own keys (and you get to choose a cool name for your script, unlike 'OAuth Example: Imgur API', which is the name you should see when authorising this script).
/*
####################################################################################################
## Demo: Imgur Authenticated API ##
####################################################################################################
AutoHotkey Version: 1.0.48.05
Language: English
Encoding: ANSI
Developed Using: WIN_XP
Created On: 2011/07/11
Author: [VxE]
Description: Authenticated Imgur API. Upload and view images on your Imgur account.
*/
#NoEnv
#NoTrayIcon
Menu, Tray, Icon, Shell32.dll, 128
Menu, Tray, Icon
; Use a config.ini file to store the OAuth urls and credentials (create with defaults if it doesn't exist)
IfNotExist, % IniFile := A_ScriptDir "\config.ini"
FileAppend, [Imgur]`n
( LTRIM
Request_Token_Endpoint=https://api.imgur.com/oauth/request_token
Authorize_Endpoint=https://api.imgur.com/oauth/authorize
Access_Token_Endpoint=https://api.imgur.com/oauth/access_token
[Imgur]
Consumer_Key=160dbabd9359f35a9eed2c2757ec0dc404e12324d
Consumer_Secret=7ad540780e8f7cc68a1d46cc1a25823f
Access_Token=
Token_Secret=
[Script Settings]
), % IniFile
OAuth_ConsumerKey( IniRead( IniFile, "Imgur: OAuth Tokens", "Consumer_Key" ) )
OAuth_ConsumerSecret( IniRead( IniFile, "Imgur: OAuth Tokens", "Consumer_Secret" ) )
OAuth_TokenSecret( IniRead( IniFile, "Imgur: OAuth Tokens", "Token_Secret" ) )
OAuth_Token( IniRead( IniFile, "Imgur: OAuth Tokens", "Access_Token" ) )
; If there IS an OAuth token already available, use a simple GET function to test it.
If !OAuth_Token() || !( Account_Name := Imgur_AccountName() )
{
; Either there is no existing token, or it's invalid. Either way, we have to get a new token.
; Acquire a request token.
If !( Request_Token := OAuth_RequestToken( IniRead( IniFile, "Imgur: OAuth Endpoints", "Request_Token_Endpoint" ) ) )
{
MsgBox, 16, Imgur API: Critical Error, % ""
. "For some reason`, a request token could not be obtained. This script will exit."
. "`nLast Response: " OAuth_LastResponse()
Exitapp
}
; Open the authorization page in the user's default browser.
Run, % IniRead( IniFile, "Imgur: OAuth Endpoints", "Authorize_Endpoint" ) "?oauth_token=" Request_Token
; Display a custom prompt for the user to copy/paste the verifier code after authorizing the script.
If !( verifier := OAuth_PromptUserForOOBVerifier() )
Exitapp ; user cancelled... we're done
; Acquire an access token
Access_Token := OAuth_AccessToken( IniRead( IniFile, "Imgur: OAuth Endpoints", "Access_Token_Endpoint" ), verifier )
If !( Account_Name := Imgur_AccountName() )
{
MsgBox, 16, Imgur API: Critical Error, % ""
. "There was a problem verifying the access token. This script will exit."
. "`nLast Response: " OAuth_LastResponse()
Exitapp
}
; Store the token and secret in the config.ini for future use. NOTE: tokens should be encrypted for storage.
IniWrite, % Access_Token, % IniFile, Imgur: OAuth Tokens, Access_Token
IniWrite, % OAuth_TokenSecret(), % IniFile, Imgur: OAuth Tokens, Token_Secret
}
StringUpper, Account_Name, Account_Name, T
; Build the main gui.
Gui, +LastFound +Resize +MinSize212x131 +LabelImgur_API_DemoScript_Gui_
GroupAdd, MainWindowHotkeys, % "AHK_ID " ( 1gui_hwnd := WinExist() )
Gui, Margin, 3, 3
Gui, Font, S11, Arial
Gui, Font,, Lucida Sans
Gui, Add, ListView, x3 y3 Grid R7 -Multi vImages, Title|Size Kb|Size Px|Hash
LV_ModifyCol( 2, "Float" )
Gui, Add, Button, xm gImgur_API_DemoScript_Gui_Upload DEFAULT, &Upload
Gui, Add, Button, yp gImgur_API_DemoScript_Gui_Options hwndGui_OptsBtn_hwnd, Image &Options
Gui, Add, Button, yp gImgur_API_DemoScript_Gui_Close, &Quit
ControlGetPos,,,, Btn_H,, % "AHK_ID " Gui_OptsBtn_hwnd
Gui, Add, Progress, x3 yp h%Btn_H% vUploadProgress c22FFDD HIDDEN
Gui, Show, y-99999, % Account_Name " - Imgur API Demo"
WinMove, % "AHK_ID " 1gui_hwnd,,,
, % IniRead( IniFile, "Script Settings", "Gui.W", 434 )
, % IniRead( IniFile, "Script Settings", "Gui.H", 268 )
Gui, Show, % "X" IniRead( IniFile, "Script Settings", "Gui.X", "Center" )
. " Y" IniRead( IniFile, "Script Settings", "Gui.Y", "Center" )
Gosub, Imgur_API_DemoScript_Gui_Refresh
LV_ModifyCol( ), LV_ModifyCol( 4, "Autohdr" )
WinWaitClose, % "AHK_ID " 1gui_hwnd
; Wait for the main gui to close, then exit the script
ExitApp
Imgur_API_DemoScript_Gui_ContextMenu:
; Build and show the cntextmenu for the appropriate listview item
If ( A_GuiControl != "Images" )
Return
Imgur_API_DemoScript_Gui_Options:
If !LV_GetCount()
Gosub, Imgur_API_DemoScript_Gui_Refresh
If !( Chosen_Row := 0 | ( A_ThisLabel = "Imgur_API_DemoScript_Gui_Options" ? LV_GetNext( 0 ) : A_EventInfo ) )
Return
LV_GetText( Image_Hash, Chosen_Row, 4 )
Menu, Imgur_API_DemoScript_Menu, UseErrorLevel
Menu, Imgur_API_DemoScript_Menu, Add, &Copy URL (original), Imgur_API_DemoScript_Menu_CopyUrlOrg
Menu, Imgur_API_DemoScript_Menu, Add, Copy URL (Lg Thum&b), Imgur_API_DemoScript_Menu_CopyUrlLgt
Menu, Imgur_API_DemoScript_Menu, Add, Copy URL (Sq Thu&mb), Imgur_API_DemoScript_Menu_CopyUrlSmt
Menu, Imgur_API_DemoScript_Menu, Add
Menu, Imgur_API_DemoScript_Menu, Add, &Refresh Image List, Imgur_API_DemoScript_Gui_Refresh
Menu, Imgur_API_DemoScript_Menu, Add
Menu, Imgur_API_DemoScript_Menu, Add, About This Demo, Imgur_API_DemoScript_Menu_HelpAbout
Menu, Imgur_API_DemoScript_Menu, Add
Menu, Imgur_API_DemoScript_Menu, Add, Open De&lete Page, Imgur_API_DemoScript_Menu_OpenDelUrl
If ( A_ThisLabel = "Imgur_API_DemoScript_Gui_Options" )
{
ControlGetPos, wx, wy,,,, % "AHK_ID " Gui_OptsBtn_hwnd
Menu, Imgur_API_DemoScript_Menu, Show, % wx, % wy
}
Else Menu, Imgur_API_DemoScript_Menu, Show
Menu, Imgur_API_DemoScript_Menu, Delete
SetTimer, RemoveTooltip, -2880
Return
Imgur_API_DemoScript_Gui_Close:
; Close the gui after saving the window's position to the ini file
WinGetPos, wx, wy,,, AHK_ID %1gui_hwnd%
If wx IS INTEGER
IniWrite, % wx, % IniFile, Script Settings, Gui.X
If wy IS INTEGER
IniWrite, % wy, % IniFile, Script Settings, Gui.Y
Gui, Cancel
Return
Imgur_API_DemoScript_Gui_Refresh:
; Update the gui's listview with the data in the Account_Images table
Account_Images := Imgur_TableFromAccountXML( Imgur_AccountImages() )
Images := "`n"
Loop, % LV_GetCount()
If LV_GetText( Image_Hash, A_Index, 4 )
IfInString, Account_Images, % "`n" Image_Hash "`t"
Images .= Image_Hash "`n"
Else LV_Delete( A_Index )
Loop, Parse, Account_Images, `n
If ( A_Index = 1 )
url := A_LoopField "`n"
Else
{
StringLeft, Image_Hash, A_LoopField, InStr( A_LoopField, "`t" ) - 1
StringGetPos, pos, Images, % "`n" Image_Hash "`n"
If !( ErrorLevel )
{
StringLeft, Chosen_Row, Images, pos + 1
StringReplace, Chosen_Row, Chosen_Row, `n, `n, UseErrorLevel
Chosen_Row := ErrorLevel
}
Else Chosen_Row := LV_Add( "Col4", Image_Hash )
LV_Modify( Chosen_Row, "Col1", HTML_Decode( TbGet( url A_LoopField, Image_Hash, "Title" ) ) )
LV_Modify( Chosen_Row, "Col2", Round( TbGet( url A_LoopField, Image_Hash, "Size" ) / 1024, 1 ) )
LV_Modify( Chosen_Row, "Col3", TbGet( url A_LoopField, Image_Hash, "Width" ) "x" TbGet( url A_LoopField, Image_Hash, "Height" ) )
}
Return
Imgur_API_DemoScript_Gui_Size:
Loop 3
GuiControl, MoveDraw, Button%A_Index%
, % "x" Round( 3 + ( ( 1 << A_Index - 1 ) - 1 ) * ( A_GuiWidth / 4 - 1.5 ) )
. " y" A_GuiHeight - 3 - Btn_H
. " w" Round( ( A_Index + A_GuiWidth - 14 ) / ( 4 >> ( A_Index = 2 ) ) )
GuiControl, MoveDraw, Images, % "w" A_GuiWidth - 6 " h" A_GuiHeight - 9 - Btn_H
GuiControl, MoveDraw, UploadProgress, % "y" A_GuiHeight - 3 - Btn_H " w" A_GuiWidth - 6
SetTimer, Imgur_API_DemoScript_Gui_SaveSize, -250
Return
Imgur_API_DemoScript_Gui_SaveSize:
WinGetPos,,, wx, wy, AHK_ID %1gui_hwnd%
If wx IS INTEGER
IniWrite, % wx, % IniFile, Script Settings, Gui.W
If wy IS INTEGER
IniWrite, % wy, % IniFile, Script Settings, Gui.H
Return
Imgur_API_DemoScript_Gui_Upload:
Gui, +OwnDialogs
Gui, 1:+Disabled
FileSelectFile, image_file, 2, ::{450d8fba-ad25-11d0-98a8-0800361b1103}
, Upload Image To Imgur, Image Files (*.jpg; *.jpeg; *.gif; *.png; *.bmp)
If !( ErrorLevel )
{
FileGetSize, image_size, % image_file
GuiControl,, UploadProgress, 0
GuiControl, +Range0-%image_size%, UploadProgress
GuiControl, Show, UploadProgress
Loop 3
GuiControl, Hide, Button%A_Index%
Image_Hash := Imgur_Upload( image_file )
Images := Imgur_SetImageTitle( Image_Hash, RegexReplace( image_file, ".*\\" ) )
Account_Images := Imgur_TableFromAccountXML( Images, Account_Images )
GuiControl, Hide, UploadProgress
Loop 3
GuiControl, Show, Button%A_Index%
Gosub, Imgur_API_DemoScript_Gui_Refresh
}
Gui, 1:-Disabled
Return
Imgur_API_DemoScript_Menu_HelpAbout:
MsgBox, 64, Imgur API Demo - by [VxE],
( LTRIM
This script is a demonstration of OAuth 1.0 using Imgur's Authenticated API.
Using this script, you can:
- View a list of images in your account.
- Upload images from your computer to your account.
- Copy the URLs for linking to your images.
)
Return
Imgur_API_DemoScript_Menu_CopyUrlOrg:
Clipboard := url := TbGet( Account_Images, Image_Hash, "original" )
Tooltip, Copied '%url%' to the clipboard.
Return
Imgur_API_DemoScript_Menu_CopyUrlLgt:
Clipboard := url := TbGet( Account_Images, Image_Hash, "large_thumbnail" )
Tooltip, Copied '%url%' to the clipboard.
Return
Imgur_API_DemoScript_Menu_CopyUrlSmt:
Clipboard := url := TbGet( Account_Images, Image_Hash, "small_square" )
Tooltip, Copied '%url%' to the clipboard.
Return
Imgur_API_DemoScript_Menu_OpenDelUrl:
Run, % TbGet( Account_Images, Image_Hash, "delete_page" )
Return
RemoveTooltip:
Tooltip
Return
; Hotkeys
#IfWinActive, AHK_Group MainWindowHotkeys
F1::SetTimer, Imgur_API_DemoScript_Menu_HelpAbout
Del::
BS::
LV_GetText( Image_Hash, Chosen_Row := LV_GetNext(0), 4 )
SetTimer, Imgur_API_DemoScript_Menu_OpenDelUrl, -20
Return
; ------------------- Imgur API Functions: These invoke specific API functions. --------------------
Imgur_AccountImages() { ; --------------------------------------------------------------------------
; Returns the XML feed that describes the images currently in the user's account.
Static EndPoint := "http://api.imgur.com/2/account/images.xml?"
headers := OAuth_HeaderAuth( EndPoint, "", "GET" )
HTTPRequest( EndPoint, data := "", headers )
OAuth_LastResponse( EndPoint ), OAuth_LastResponse( data ), OAuth_LastResponse( headers )
IfInString, data, <images>
Return data
Return ""
} ; Imgur_AccountImages() --------------------------------------------------------------------------
Imgur_AccountName() { ; ----------------------------------------------------------------------------
; Returns the name (url) of the account that has authorized this script.
Static EndPoint := "http://api.imgur.com/2/account.xml"
headers := OAuth_HeaderAuth( EndPoint, "", "GET" )
HTTPRequest( EndPoint, data := "", headers )
OAuth_LastResponse( EndPoint ), OAuth_LastResponse( data ), OAuth_LastResponse( headers )
StringGetPos, pos, data, <url>
If ( ErrorLevel )
Return "" ; failure
StringTrimLeft, data, data, pos + 5
Return SubStr( data, 1, InStr( data, "<" ) - 1 )
} ; Imgur_AccountName() ----------------------------------------------------------------------------
/*
; BROKEN. I could not get Imgur to recognize the '_method' parameter required for the delete operaition
Imgur_DeleteImage( image_hash ) { ; ----------------------------------------------------------------
; Deletes the indicated image from the user's Imgur account
Static EndPoint := "http://api.imgur.com/2/account/images/%image_hash%.xml?_method=DELETE"
StringReplace, url, EndPoint, % "%image_hash%", % image_hash
data := "_method=DELETE"
headers := OAuth_HeaderAuth( url, data, "POST" )
. "`n" . "Content-Type: application/x-www-form-urlencoded; charset=UTF-8"
HTTPRequest( url, data, headers )
OAuth_LastResponse( url ), OAuth_LastResponse( data ), OAuth_LastResponse( headers )
IfInString, data, Success
Return 1
Return ""
} ; Imgur_DeleteImage( image_hash ) ----------------------------------------------------------------
*/
Imgur_SetImageTitle( image_hash, title ) { ; -------------------------------------------------------
; Changes the title of an image in the user's Imgur account.
Static EndPoint := "http://api.imgur.com/2/account/images/%image_hash%.xml"
StringReplace, url, EndPoint, % "%image_hash%", % image_hash
data := "title=" uriencode( title )
headers := OAuth_HeaderAuth( url, data, "POST" )
. "`n" . "Content-Type: application/x-www-form-urlencoded; charset=UTF-8"
HTTPRequest( url, data, headers )
OAuth_LastResponse( url ), OAuth_LastResponse( data ), OAuth_LastResponse( headers )
Return data
} ; Imgur_SetImageTitle( image_hash, title ) -------------------------------------------------------
Imgur_Upload( image_file ) { ; ---------------------------------------------------------------------
; Uploads one image to the user's Imgur account and returns the URL of the image.
Static EndPoint := "http://api.imgur.com/2/upload.xml"
FileGetSize, size, % image_file
FileRead, data, % "*c " image_file
headers := OAuth_HeaderAuth( EndPoint, "", "POST" )
. "`n" . "Content-Length: " size
. "`n" . "Content-Type: application/octet-stream"
HTTPRequest( EndPoint, data, headers, "Callback: Imgur_Progress" )
OAuth_LastResponse( EndPoint ), OAuth_LastResponse( data ), OAuth_LastResponse( headers )
StringGetPos, pos, data, <hash>
If !( ErrorLevel )
Return SubStr( data, pos + 7, Instr( data, "</hash>", 0, pos + 6 ) - pos - 7 )
Else Return "" ; error: see response
} ; Imgur_Upload( image_file ) ---------------------------------------------------------------------
; --------------------------------- Accessory and GUI functions. -----------------------------------
Imgur_TableFromAccountXML( XML, Prior_Table="" ) { ; -----------------------------------------------
; Parses the XML response that contains Imgur image info into a 9/10 table, optionally appending or
; updating an existing table.
Static nodes := "
( LTRIM JOIN,
name,title,caption,hash,deletehash,datetime,type,animated,width,height,size
views,bandwidth,original,imgur_page,delete_page,small_square,large_thumbnail
)"
; If the prior table is not blank, chop off its header, otherwise initialize both.
Prior_Table .= "`n"
If ( Prior_Table != "`n" )
{
StringGetPos, pos, Prior_Table, `n
StringLeft, hd, Prior_Table, pos
hd := "`t" hd "`t"
StringTrimLeft, Prior_Table, Prior_Table, pos
}
Else hd := "`thash`t"
; parse the XML by lessthan signs.
Loop, Parse, XML, <
If ( A_Index != 1 )
Loop, Parse, A_LoopField, >, % "`t`n`r "
If ( A_Index = 1 )
StringLeft, node, A_LoopField, InStr( A_LoopField " ", " " ) - 1
Else If node IN %nodes%
Imgur_TFAXML_%node% := A_LoopField
Else If ( node = "/links" ) ; Use </links> as end-of-item
{
; check for a duplicate
StringGetPos, pos, Prior_Table, % "`n" Imgur_TFAXML_hash "`t"
If !( ErrorLevel )
{
StringGetPos, node, Prior_Table, % "`n",, pos + 1
StringTrimLeft, XML, Prior_Table, node + 1
StringLeft, Prior_Table, Prior_Table, pos + 1
Prior_Table .= XML
}
; Build the table row in order by existing headers
node := "," nodes ","
Loop, Parse, hd, % "`t", % "`r`n"
If ( A_LoopField != "" )
{
Prior_Table .= ( A_Index = 2 ? "" : "`t" ) Imgur_TFAXML_%A_LoopField%
StringReplace, node, node, % "," A_LoopField ",", `,
}
; add any extra columns to the entire table
Loop, Parse, node, `,
If ( A_LoopField != "" )
{
Prior_Table .= "`t" Imgur_TFAXML_%A_LoopField%
StringReplace, Prior_Table, Prior_Table, `n, % "`t`n", A
hd .= A_LoopField "`t"
}
; wipe the array to prevent bleeding
Loop, Parse, nodes, `,
Imgur_TFAXML_%A_LoopField% := ""
Prior_Table .= "`n"
}
StringGetPos, pos, Prior_Table, `n
Return SubStr( hd, 2, -1 ) SubStr( Prior_Table, pos + 1, -1 )
} ; Imgur_TableFromAccountXML( XML, Prior_Table="" ) -----------------------------------------------
Imgur_Progress( p, t ) { ; -------------------------------------------------------------------------
; Update the progress bar in the gui
GuiControl, 1:, UploadProgress, % Round( ( 1 + p ) * t )
} ; Imgur_Progress( p, t ) -------------------------------------------------------------------------
IniRead( Filename, Section, Key, Default=" " ) {
IniRead, OutputVar, % Filename, % Section, % Key, % Default
Return OutputVar
}
TbGet( data, row, col ) {
; This is a stripped down version of 'Table_GetCell()' from my Table Manipulation Library.
oel := ErrorLevel, data .= "`n"
StringGetPos, pos, data, % "`n"
StringLeft, hd, data, pos
hd := "`t" hd "`t"
StringGetPos, pos, hd, % "`t" col "`t"
If ( ErrorLevel )
Return "", ErrorLevel := oel
StringLeft, hd, hd, pos
StringGetPos, pos, data, % "`n" row "`t"
If ( ErrorLevel )
Return "", ErrorLevel := oel
StringTrimLeft, data, data, pos + 1
StringGetPos, pos, data, % "`n"
StringLeft, data, data, pos
If ( hd != "" )
{
StringReplace, hd, hd, % "`t", % "`t", UseErrorLevel
StringGetPos, pos, data, % "`t", L%ErrorLevel%
StringTrimLeft, data, data, pos + 1
}
StringLeft, data, data, InStr( data "`t", "`t" ) - 1
Return data, ErrorLevel := oel
}
HTML_Decode( string ) { ; --------------------------------------------------------------------------
; Function by [VxE]. Transforms HTML entities (like {, or «,) into their actual characters.
; NOTE: unless you are using a unicode version of AHK, characters with a character code above 255
; will appear in UTF-8 encoding and will look like '™'. Named entity codes were ripped from here:
; http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
Static NamedEntities := "
( LTRIM JOIN
",22&,26&apos,27<,3C>,3E ,A0¡,A1¢,A2£,A3¤,A4¥,A5
¦,A6§,A7¨,A8©,A9ª,AA«,AB¬,AC­,AD®,AE¯,AF°,B0
±,B1²,B2³,B3´,B4µ,B5¶,B6·,B7¸,B8¹,B9º,BA
»,BB¼,BC½,BD¾,BE¿,BFÀ,C0Á,C1Â,C2Ã,C3
Ä,C4Å,C5Æ,C6Ç,C7È,C8É,C9Ê,CAË,CBÌ,CC
Í,CDÎ,CEÏ,CFÐ,D0Ñ,D1Ò,D2Ó,D3Ô,D4Õ,D5Ö,D6
×,D7Ø,D8Ù,D9Ú,DAÛ,DBÜ,DCÝ,DDÞ,DEß,DF
à,E0á,E1â,E2ã,E3ä,E4å,E5æ,E6ç,E7è,E8
é,E9ê,EAë,EBì,ECí,EDî,EEï,EFð,F0ñ,F1ò,F2
ó,F3ô,F4õ,F5ö,F6÷,F7ø,F8ù,F9ú,FAû,FB
ü,FCý,FDþ,FEÿ,FF&OElig,152&oelig,153&Scaron,160&scaron,161&Yuml,178
&fnof,192&circ,2C6&tilde,2DC&Alpha,391&Beta,392&Gamma,393&Delta,394&Epsilon,395&Zeta,396
&Eta,397&Theta,398&Iota,399&Kappa,39A&Lambda,39B&Mu,39C&Nu,39D&Xi,39E&Omicron,39F&Pi,3A0
&Rho,3A1&Sigma,3A3&Tau,3A4&Upsilon,3A5&Phi,3A6&Chi,3A7&Psi,3A8&Omega,3A9&alpha,3B1&beta,3B2
&gamma,3B3&delta,3B4&epsilon,3B5&zeta,3B6&eta,3B7&theta,3B8&iota,3B9&kappa,3BA&lambda,3BB
&mu,3BC&nu,3BD&xi,3BE&omicron,3BF&pi,3C0&rho,3C1&sigmaf,3C2&sigma,3C3&tau,3C4&upsilon,3C5
&phi,3C6&chi,3C7&psi,3C8&omega,3C9&thetasym,3D1&upsih,3D2&piv,3D6&ensp,2002&emsp,2003
&thinsp,2009&zwnj,200C&zwj,200D&lrm,200E&rlm,200F&ndash,2013&mdash,2014&lsquo,2018
&rsquo,2019&sbquo,201A&ldquo,201C&rdquo,201D&bdquo,201E&dagger,2020&Dagger,2021&bull,2022
&hellip,2026&permil,2030&prime,2032&Prime,2033&lsaquo,2039&rsaquo,203A&oline,203E
&frasl,2044&euro,20AC&image,2111&weierp,2118&real,211C&trade,2122&alefsym,2135&larr,2190
&uarr,2191&rarr,2192&darr,2193&harr,2194&crarr,21B5&lArr,21D0&uArr,21D1&rArr,21D2&dArr,21D3
&hArr,21D4&forall,2200&part,2202&exist,2203&empty,2205&nabla,2207&isin,2208¬in,2209
&ni,220B&prod,220F&sum,2211&minus,2212&lowast,2217&radic,221A&prop,221D&infin,221E&ang,2220
&and,2227&or,2228&cap,2229&cup,222A&int,222B&there4,2234&sim,223C&cong,2245&asymp,2248
&ne,2260&equiv,2261&le,2264&ge,2265&sub,2282&sup,2283&nsub,2284&sube,2286&supe,2287
&oplus,2295&otimes,2297&perp,22A5&sdot,22C5&lceil,2308&rceil,2309&lfloor,230A&rfloor,230B
&lang,27E8&rang,27E9&loz,25CA&spades,2660&clubs,2663&hearts,2665&diams,2666&
)"
oel := ErrorLevel, oscs := A_StringCaseSense
StringCaseSense, On
Loop, Parse, string, & ; parse the string by ampersands, because they neatly delimit entities
If ( A_Index = 1 )
string := A_LoopField ; the part before the first ampersand can't contain an entity
Else
{
StringGetPos, pos, A_LoopField, % ";" ; find the ending ';' of the entity
If !( ErrorLevel )
{
StringLeft, entity, A_LoopField, pos
StringGetPos, np, NamedEntities, &%entity%, ; see if the entity is a named entity
If !( ErrorLevel )
{
; Use the static lookup list to convert the entity name to a number
np += 3 + StrLen( entity )
StringGetPos, cp, NamedEntities, &,, np
StringMid, entity, NamedEntities, np, 1 + cp - np
entity := "0x" entity
}
Else StringReplace, entity, entity, #, 0
If entity IS INTEGER ; convert the numeric entity to character(s)
{
If ( A_IsUnicode ) || ( entity < 0x100 ) ; normal character conversion
string .= Chr( entity )
Else If ( entity < 0x800 ) ; UTF-8 character conversion
string .= Chr( 0xC0 | ( entity >> 6 ) ) Chr( 0x80 | ( entity & 63 ) )
Else If ( entity < 0x10000 )
string .= Chr( 0xE0 | ( entity >> 12 ) ) Chr( 0x80 | ( ( entity >> 6 ) & 63 ) ) Chr( 0x80 | ( entity & 63 ) )
Else If ( entity < 0x110000 )
string .= Chr( 0xF0 | ( entity >> 18 ) ) Chr( 0x80 | ( ( entity >> 12 ) & 63 ) ) Chr( 0x80 | ( ( entity >> 6 ) & 63 ) ) Chr( 0x80 | ( entity & 63 ) )
Else string .= "" SubStr( entity, 2 ) ";" ; unknown character code ?
}
Else string .= "&" SubStr( A_LoopField, 1, pos + 1 ) ; unrecognized entity
string .= SubStr( A_LoopField, pos + 2 ) ; append the rest of the substring
}
Else string .= "&" A_LoopField ; so a lone '&' was found... just move along.
}
StringCaseSense, % oscs
Return string, ErrorLevel := oel
} ; HTML_Decode( string ) --------------------------------------------------------------------------