diff --git a/examples/tippytippytepee/static/codepress.css b/examples/tippytippytepee/static/codepress.css new file mode 100644 index 0000000..3ccf4f5 --- /dev/null +++ b/examples/tippytippytepee/static/codepress.css @@ -0,0 +1,5 @@ +html {border:none;} /*remove ie frameborder */ +body {margin-top:13px;_margin-top:14px;background:white;font-family:monospace;font-size:13px;margin-left:32px;white-space:pre;background-image:url("/static/linenumbers.png");background-repeat:repeat-y;background-position:0 3px;line-height:16px;} +html>body{background-position:0 2px;} +P {margin:0;padding:0;border:0;outline:0;display:block;white-space:pre;} +b, i, s, u, a, em, tt, ins, big, cite, strong {text-decoration:none;font-weight:normal;font-style:normal;font-size:13px;} diff --git a/examples/tippytippytepee/static/codepress.js b/examples/tippytippytepee/static/codepress.js new file mode 100644 index 0000000..1cc56d3 --- /dev/null +++ b/examples/tippytippytepee/static/codepress.js @@ -0,0 +1,240 @@ +/* + * CodePress - Real Time Syntax Highlighting Editor written in JavaScript - http://codepress.fermads.net/ + * + * Copyright (C) 2006 Fernando M.A.d.S. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation. + * + * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php + * + * This is a slightly modified copy of version 0.8.15 (13 january 2007). + * A few lines here and in the CSS were modified to use 'static' as a prefix. + */ + +CodePress = { + range : null, + language : null, + scrolling : false, + + // set initial vars and start sh + initialize : function() { + if(typeof(editor)=='undefined'&&!arguments[0]) return; + this.detect(); + chars = '|13|32|191|57|48|187|188|'; // charcodes that trigger syntax highlighting + cc = '\u2009'; // control char + if(browser.ff) { + editor = document.getElementById('ffedt'); + document.designMode = 'on'; + document.addEventListener('keydown', this.keyHandler, true); + window.addEventListener('scroll', function() { if(!CodePress.scrolling) CodePress.syntaxHighlight('scroll') }, false); + } + else if(browser.ie) { + editor = document.getElementById('ieedt'); + editor.contentEditable = 'true'; + document.attachEvent('onkeydown', this.keyHandler); + window.attachEvent('onscroll', function() { if(!CodePress.scrolling) CodePress.syntaxHighlight('scroll') }); + } + else { + // TODO: textarea without syntax highlighting for non supported browsers + alert('your browser is not supported at the moment'); + return; + } + this.syntaxHighlight('init'); + setTimeout(function() { window.scroll(0,0) },50); // scroll IE to top + }, + + // detect browser, for now IE and FF + detect : function() { + browser = { ie:false, ff:false }; + if(navigator.appName.indexOf("Microsoft") != -1) browser.ie = true; + else if(navigator.appName == "Netscape") browser.ff = true; + }, + + // treat key bindings + keyHandler : function(evt) { + evt = (evt) ? evt : (window.event) ? event : null; + if(evt) { + charCode = (evt.charCode) ? evt.charCode : ((evt.keyCode) ? evt.keyCode : ((evt.which) ? evt.which : 0)); + + if((charCode==34||charCode==33)&&browser.ie) { // handle page up/down for IE + parent.codepress.scrollBy(0, (charCode==34) ? 200 : -200); + evt.returnValue = false; + } + if((chars.indexOf('|'+charCode+'|')!=-1) && (!evt.ctrlKey && !evt.altKey)) { // syntax highlighting + CodePress.syntaxHighlight('generic'); + } + else if(charCode==46||charCode==8) { // save to history when delete or backspace pressed + CodePress.actions.history[CodePress.actions.next()] = editor.innerHTML; + } + else if((charCode==90||charCode==89) && evt.ctrlKey) { // undo and redo + (charCode==89||evt.shiftKey) ? CodePress.actions.redo() : CodePress.actions.undo() ; + evt.returnValue = false; + if(browser.ff)evt.preventDefault(); + } + else if(charCode==86 && evt.ctrlKey) { // paste + // TODO: pasted text should be parsed and highlighted + } + + } + }, + + // put cursor back to its original position after every parsing + findString : function() { + if(browser.ff) { + if(self.find(cc)) + window.getSelection().getRangeAt(0).deleteContents(); + } + else if(browser.ie) { + range = self.document.body.createTextRange(); + if(range.findText(cc)){ + range.select(); + range.text = ''; + } + } + }, + + // split big files, highlighting parts of it + split : function(code,flag) { + if(flag=='scroll') { + this.scrolling = true; + return code; + } + else { + this.scrolling = false; + mid = code.indexOf(cc); + if(mid-2000<0) {ini=0;end=4000;} + else if(mid+2000>code.length) {ini=code.length-4000;end=code.length;} + else {ini=mid-2000;end=mid+2000;} + code = code.substring(ini,end); + if(browser.ff) return code; + else return code.substring(code.indexOf('

'),code.lastIndexOf('

')+4); + } + }, + + // syntax highlighting parser + syntaxHighlight : function(flag) { + if(browser.ff) { + if(flag!='init') window.getSelection().getRangeAt(0).insertNode(document.createTextNode(cc)); + o = editor.innerHTML; + o = o.replace(/
/g,'\n'); + o = o.replace(/<.*?>/g,''); + x = z = this.split(o,flag); + x = x.replace(/\n/g,'
'); + } + else if(browser.ie) { + if(flag!='init') document.selection.createRange().text = cc; + o = editor.innerHTML; + o = o.replace(/

/g,'\n'); + o = o.replace(/<\/P>/g,'\r'); + o = o.replace(/<.*?>/g,''); + o = o.replace(/ /g,''); + o = '

'+o+'

'; + o = o.replace(/\n/g,'

'); + o = o.replace(/\r/g,'<\/P>'); + o = o.replace(/

(

)+/,'

'); + o = o.replace(/<\/P>(<\/P>)+/,'

'); + o = o.replace(/

<\/P>/g,'


<\/P>'); + x = z = this.split(o,flag); + } + + for(i=0;i20) this.history[this.pos-21] = undefined; + return ++this.pos; + } + }, + + // transform syntax highlighted code to original code + getCode : function() { + code = editor.innerHTML; + code = code.replace(/
/g,'\n'); + code = code.replace(/<\/p>/gi,'\r'); + code = code.replace(/

/i,''); // IE first line fix + code = code.replace(/

/gi,'\n'); + code = code.replace(/ /gi,''); + code = code.replace(/\u2009/g,''); + code = code.replace(/<.*?>/g,''); + code = code.replace(/</g,'<'); + code = code.replace(/>/g,'>'); + code = code.replace(/&/gi,'&'); + return code; + }, + + // put some code inside editor + setCode : function() { + if(typeof(arguments[1])=='undefined') { + language = top.document.getElementById(arguments[0]).lang.toLowerCase(); + code = top.document.getElementById(arguments[0]).value; + } + else { + language = arguments[0]; + code = arguments[1]; + } + document.designMode = 'off'; + head = document.getElementsByTagName('head')[0]; + script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = '/static/languages/codepress-'+language+'.js'; + head.appendChild(script) + document.getElementById('cp-lang-style').href = '/static/languages/codepress-'+language+'.css'; + code = code.replace(/\u2009/gi,''); + code = code.replace(/&/gi,'&'); + code = code.replace(//g,'>'); + editor.innerHTML = "

"+code+"
"; + this.language = language; + } +} + +onload = function() { + cpWindow = top.document.getElementById('codepress'); + if(cpWindow!=null) { + cpWindow.style.border = '1px solid gray'; + cpWindow.style.frameBorder = '0'; + } + + top.CodePress = CodePress; + CodePress.initialize('new'); + + cpOnload = top.document.getElementById('codepress-onload'); + cpOndemand = top.document.getElementById('codepress-ondemand'); + + if(cpOnload!=null) { + cpOnload.style.display = 'none'; + cpOnload.id = 'codepress-loaded'; + CodePress.setCode('codepress-loaded'); + } + if(cpOndemand!=null) cpOndemand.style.display = 'none'; +} diff --git a/examples/tippytippytepee/static/languages/codepress-ruby.css b/examples/tippytippytepee/static/languages/codepress-ruby.css new file mode 100644 index 0000000..edb9028 --- /dev/null +++ b/examples/tippytippytepee/static/languages/codepress-ruby.css @@ -0,0 +1,10 @@ +/* + * CodePress color styles for Ruby syntax highlighting + */ + +b {color:#7F0055;font-weight:bold;} /* reserved words */ +i, i b, i s, i em, i a, i u {color:gray;font-weight:normal;} /* comments */ +s, s b, s a, s em, s u {color:#2A00FF;font-weight:normal;} /* strings */ +a {color:#006700;font-weight:bold;} /* variables */ +em {color:darkblue;font-weight:bold;} /* functions */ +u {font-weight:bold;} /* special chars */ \ No newline at end of file diff --git a/examples/tippytippytepee/static/languages/codepress-ruby.js b/examples/tippytippytepee/static/languages/codepress-ruby.js new file mode 100644 index 0000000..195579e --- /dev/null +++ b/examples/tippytippytepee/static/languages/codepress-ruby.js @@ -0,0 +1,16 @@ +/* + * CodePress regular expressions for Ruby syntax highlighting + */ + +syntax = [ // Ruby + /\"(.*?)(\"|
|<\/P>)/g,'"$1$2', // strings double quote + /\'(.*?)(\'|
|<\/P>)/g,'\'$1$2', // strings single quote + /([\$\@\%]+)([\w\.]*)/g,'$1$2', // vars + /(def\s+)([\w\.]*)/g,'$1$2', // functions + /\b(alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|false|for|if|in|module|next|nil|not|or|redo|rescue|retry|return|self|super|then|true|undef|unless|until|when|while|yield)\b/g,'$1', // reserved words + /([\(\){}])/g,'$1', // special chars + /#(.*?)(
|<\/P>)/g,'#$1$2', // comments +]; + +CodePress.initialize(); + diff --git a/examples/tippytippytepee/static/linenumbers.png b/examples/tippytippytepee/static/linenumbers.png new file mode 100644 index 0000000..ffea4e6 Binary files /dev/null and b/examples/tippytippytepee/static/linenumbers.png differ diff --git a/examples/tippytippytepee/tepee.rb b/examples/tippytippytepee/tepee.rb index 3fefe0e..89977c1 100755 --- a/examples/tippytippytepee/tepee.rb +++ b/examples/tippytippytepee/tepee.rb @@ -115,8 +115,30 @@ def get class Stylesheet < R '/css/tepee.css' def get -@headers['Content-Type'] = 'text/css' -File.read(__FILE__).gsub(/.*__END__/m, '') + @headers['Content-Type'] = 'text/css' + File.read(__FILE__).gsub(/.*__END__/m, '') + end + end + + class Editor < R '/chunky/bacon/editor' + def get + @no_layout = true + render :edit_code + end + end + + class Static < R '(/static/.+)' + MIME_TYPES = {'.css' => 'text/css', '.js' => 'text/javascript', + '.jpg' => 'image/jpeg', '.png' => 'image/png'} + PATH = File.expand_path('.') + + def get(path) + @headers['Content-Type'] = MIME_TYPES[path[/\.\w+$/, 0]] || "text/plain" + if path.include? '..' || Path.expand_path(PATH+path) !~ /^#{PATH}/ + "404 - Invalid path" + else + @headers['X-Sendfile'] = PATH+path + end end end end @@ -132,12 +154,12 @@ def layout style <<-END, :type => 'text/css' body { font-family: verdana, arial, sans-serif; - min-width: 800px; + min-width: 900px; background:#d7d7d7; text-align: center; } #doc { - width: 800px; + width: 900px; background:#ffffff; margin-left: auto; margin-right: auto; @@ -217,16 +239,38 @@ def show def edit h1 @page.title form :method => 'post', :action => R(Edit, @page.title) do - input :type => 'submit', :value=>'save' + input :type => 'submit', :value=>'save', :onclick=>'copyCode()' p do - textarea @page.body, :name => 'post_body', :rows => 30, :cols => 100 + iframe :id=>'codepress', :name=>'codepress', :src=>'/chunky/bacon/editor', :width=>850, :height=>400 + br + textarea @page.body, :id=>'codepress-onload', :name => 'post_body', :lang=>'ruby' end - input :type => 'submit', :value=>'save' + script 'function copyCode() { + var txt = document.getElementsByName("post_body")[0]; + txt.value = CodePress.getCode(); } ', :type => 'text/javascript' + input :type => 'submit', :value=>'save', :onclick=>'copyCode()' end _button 'cancel', R(Show, @page.title, @page.version) a 'syntax', :href => 'http://pub.cozmixng.org/~the-rwiki/?cmd=view;name=ERbMemo.en', :target=>'_blank' end + def edit_code + html do + head do + link :href=>'/static/codepress.css', :rel=>'stylesheet', :type=>'text/css' + link :href=>'/static/languages/codepress-ruby.css', :rel=>'stylesheet', + :type=>'text/css', :id=>'cp-lang-style' + script :type=>'text/javascript', :src=>'/static/codepress.js' + script :type=>'text/javascript', :src=>'/static/languages/codepress-ruby.js' + script "CodePress.language = 'ruby';", :type=>'text/javascript' + end + body :id=>'ffedt' do + pre :id=>'ieedt' do + end + end + end + end + def list h1 'all pages' ul { @pages.each { |p| @@ -306,12 +350,15 @@ def Tepee.create Tepee::Models::Session.create_schema end -require 'mongrel/camping' -Tepee::Models::Base.establish_connection :adapter => 'sqlite3', :database => ENV['HOME'] + '/.camping.db' -Tepee::Models::Base.threaded_connections=false +if __FILE__ == $0 + require 'mongrel/camping' + Tepee::Models::Base.establish_connection :adapter => 'sqlite3', :database => ENV['HOME'] + '/.camping.db' + Tepee::Models::Base.logger = Logger.new('tepee.log') + Tepee::Models::Base.threaded_connections=false -s = Mongrel::Camping.start('0.0.0.0', 3300, '/', Tepee) -s.run.join + s = Mongrel::Camping.start('0.0.0.0', 3300, '/', Tepee) + s.run.join +end __END__ /** focus **/ diff --git a/examples/tippytippytepee/webdev.rb b/examples/tippytippytepee/webdev.rb index ad28f6b..c628d0e 100644 --- a/examples/tippytippytepee/webdev.rb +++ b/examples/tippytippytepee/webdev.rb @@ -13,6 +13,7 @@ def self.get_code(uri) end def self.import(wiki_node) - eval self.get_code(wiki_node.to_s) + src = self.get_code(wiki_node.to_s) + eval src end end