1: using System;
2: using System.Text.RegularExpressions;
3: using BlogEngine.Core;
4: using BlogEngine.Core.Web.Controls;
5:
6: [Extension("Adds a link to syntax highlighted code to copy it to the clipboard.", "1.0", @"<a href=""http://www.squaredroot.com"">Troy Goode</a>")]
7: public class CopyCodeToClipboard
8: {
9:
10: private ExtensionSettings settings;
11:
12: public CopyCodeToClipboard()
13: {
14:
15: Post.Serving += new EventHandler<ServingEventArgs>(Post_PostServing);
16: Post.Saving += new EventHandler<SavedEventArgs>(Post_Saving);
17:
18: ExtensionSettings initialSettings = new ExtensionSettings(GetType().Name);
19: initialSettings.Help = "This extension is written to work with the <a href="http://lvildosola.blogspot.com/2007/02/code-snippet-plugin-for-windows-live.html">Code Snippet Plugin for Windows Live Writer</a> and will not work with other syntax highlighting tools.";
20: initialSettings.AddParameter( "copyText", "Text for 'Copy to Clipboard' button?", 255, true );
21: initialSettings.AddValue( "copyText", "Copy To Clipboard" );
22: initialSettings.AddParameter( "popupText", "Text for 'View in Popup Window' button?", 255, true );
23: initialSettings.AddValue( "popupText", "View Plain" );
24: initialSettings.AddParameter("aboveBelow", "Display above or below code?", 5, true);
25: initialSettings.AddValue("aboveBelow", "Below");
26: initialSettings.AddParameter( "style", "Any additional styling?", int.MaxValue, false );
27: initialSettings.AddValue( "style", "" );
28: initialSettings.AddParameter( "flashFile", "Path to '_clipboard.swf' Flash file?", 255, true );
29: initialSettings.AddValue( "flashFile", "~/_clipboard.swf" );
30: initialSettings.IsScalar = true;
31: ExtensionManager.ImportSettings(initialSettings);
32:
33: settings = ExtensionManager.GetSettings(GetType().Name);
34:
35: }
36:
37: private void Post_Saving(object sender, SavedEventArgs e)
38: {
39: //### executing here will only run the code once per post, but will modify the post itself
40: //InsertCopyCodeLink(e);
41: }
42:
43: private void Post_PostServing(object sender, ServingEventArgs e)
44: {
45: //### executing here will execute the code on every iteration, but will not modify your post
46: InsertCopyCodeLink(e);
47: }
48:
49: private void InsertCopyCodeLink( ServingEventArgs e)
50: {
51: if( !string.IsNullOrEmpty(e.Body) )
52: {
53:
54: //### find code-div
55: string postID = Guid.NewGuid().ToString().Replace( "{", "" ).Replace( "}", "" ).Replace( "-", "" );
56: string toFind = "<div class="csharpcode-wrapper">";
57: int index = e.Body.IndexOf(toFind);
58: while( index != -1 )
59: {
60:
61: //### grab code out of code-div
62: int end = e.Body.IndexOf( "</div>", index ); //### the first </div> should be the end of "csharpcode"
63: end = e.Body.IndexOf( "</div>", end ); //### the next </div> should be the end of "csharpcode-wrapper"
64: string code = e.Body.Substring(index, end - index);
65:
66: //### parse code out of code-div
67: int codeStart = code.IndexOf("<pre ");
68: int codeEnd = code.LastIndexOf("</pre>") + 6;
69: code = code.Substring(codeStart, codeEnd - codeStart);
70: code = Regex.Replace( code, @"<(.|n)*?>", string.Empty ); //### strip html
71: code = code.Replace( " ", "" ); //### remove unnecessary  s from the blank lines
72: code = code.Replace( " ", " " ); //### convert s to spaces
73: code = Regex.Replace( code, @"^(s*)(d+): ", "" ); //### remove line numbers on first line
74: code = Regex.Replace( code, @"(n)(s*)(d+): ", "n" ); //### remove line numbers on subsequent lines
75:
76: //### create copy link
77: string insertScript = @"
78: <script type=""text/javascript"">
79: var copyToClipboard@INDEX = CopyToClipboard_Strip('@CODE');
80: </script>";
81: string insertDiv = @"<div class=""CopyToClipboard"" style=""@STYLE""><div><a href=""javascript:void(0);"" onclick=""CopyToClipboard_ViewPlain(copyToClipboard@INDEX);"">@POPUPTEXT</a> | <a href=""javascript:void(0);"" onclick=""CopyToClipboard_Copy(copyToClipboard@INDEX);"">@COPYTEXT</a></div></div>";
82:
83: //### set values for copy link and insert above/below code
84: string insert = insertDiv + OutputCommonMethods() + insertScript;
85: insert = insert.Replace( "@STYLE", settings.GetSingleValue("style") );
86: insert = insert.Replace( "@POPUPTEXT", settings.GetSingleValue("popupText") );
87: insert = insert.Replace( "@COPYTEXT", settings.GetSingleValue("copyText") );
88: insert = insert.Replace( "@INDEX", postID + "_" + index.ToString() ); //### use index of code-div as a unique ID to allow multiple code-divs on this post
89: insert = insert.Replace( "@CODE", code.Replace( "", "" ).Replace( "'", "'" ).Replace( "rn", "rn" ).Replace( "rnrnrn", "rn" ) );
90: if( settings.GetSingleValue("aboveBelow").ToLower() == "above" )
91: e.Body = e.Body.Insert( index, insert );
92: else
93: e.Body = e.Body.Insert( e.Body.IndexOf( "</div>", end + 1 ) + 6, insert );
94:
95: //### prep index to find next code-div
96: index = index + insert.Length + 1; //### ensure we don't find this same code-div again
97: if( index > e.Body.Length ) break;
98: index = e.Body.IndexOf( toFind, index ); //### find any other code divs
99:
100: }
101: }
102: }
103:
104: private string OutputCommonMethods()
105: {
106:
107: //### only output the following once per page
108: if( System.Web.HttpContext.Current.Items["CopyToClipboard_JSOutput"] == null )
109: {
110: //### add shared javascript
111: string flashPath = System.Web.VirtualPathUtility.ToAbsolute(settings.GetSingleValue("flashFile"));
112: string commonMethods = @"
113: <div id=""CopyToClipboard_Hidden"" style=""display:none;""></div>
114: <div id=""CopyToClipboard_FlashContainer""></div>
115: <script type=""text/javascript"">
116:
117: function CopyToClipboard_Strip( text ){
118: text = text.replace( / /g, ' ' );
119: text = text.replace( /"/g, '""' );
120: text = text.replace( /'/g, '""' );
121: text = text.replace( /&/g, '&' );
122: text = text.replace( /</g, String.fromCharCode(60) );
123: text = text.replace( />/g, String.fromCharCode(62) );
124: return text;
125: }
126:
127: function CopyToClipboard_Copy( text ){
128:
129: //### get reference to utility div
130: var ele = document.getElementById('CopyToClipboard_Hidden');
131:
132: //### the following taken from: http://webchicanery.com/2006/11/14/clipboard-copy-javascript/
133: if (false && window.clipboardData) {
134: window.clipboardData.setData( ""Text"", text );
135: } else {
136: document.getElementById('CopyToClipboard_FlashContainer').innerHTML = '';
137: var divinfo = '<embed id=""CopyToClipboard_FlashFile"" src=""" + flashPath + @""" FlashVars=""clipboard=' + encodeURIComponent(text) + '"" width=""0"" height=""0"" type=""application/x-shockwave-flash""></embed>';
138: document.getElementById('CopyToClipboard_FlashContainer').innerHTML = divinfo;
139: }
140:
141: }
142:
143: function CopyToClipboard_ViewPlain( text ){
144: var win = window.open( '', 'CopyToClipboard_Window', 'width=480, height=480, toolbar=no, menubar=no, scrollbars=auto, resizable=yes, location=no, directories=no, status=no' );
145: win.document.write( '<html><head><title>Code</title><body style=""margin:0;padding:0;""><textarea style=""width:100%;height:100%;border:0;"">' + text + '</textarea></body></html>' );
146: }
147:
148: </script>
149: ";
150: System.Web.HttpContext.Current.Items["CopyToClipboard_JSOutput"] = true;
151: return commonMethods;
152: }
153: else
154: return "";
155:
156: }
157:
158: }