Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial import (WIP)

git-svn-id: http://svn.webtest.wvu.edu/repos/rails/plugins/textile_editor@56 6731b596-9f2e-9c42-8d5b-c76208a4698c
  • Loading branch information...
commit 749195ce3915ff49651575cdb21a5d24f78b5486 1 parent 7533806
cbscharf authored
Showing with 861 additions and 0 deletions.
  1. +4 −0 README
  2. +22 −0 Rakefile
  3. BIN  assets/images/toolbar/background.png
  4. BIN  assets/images/toolbar/blockquote.png
  5. BIN  assets/images/toolbar/bold.png
  6. BIN  assets/images/toolbar/center.png
  7. BIN  assets/images/toolbar/h1.png
  8. BIN  assets/images/toolbar/h2.png
  9. BIN  assets/images/toolbar/h3.png
  10. BIN  assets/images/toolbar/h4.png
  11. BIN  assets/images/toolbar/h5.png
  12. BIN  assets/images/toolbar/h6.png
  13. BIN  assets/images/toolbar/indent.png
  14. BIN  assets/images/toolbar/italic.png
  15. BIN  assets/images/toolbar/justify.png
  16. BIN  assets/images/toolbar/left.png
  17. BIN  assets/images/toolbar/list_bullets.png
  18. BIN  assets/images/toolbar/list_numbers.png
  19. BIN  assets/images/toolbar/omega.png
  20. BIN  assets/images/toolbar/outdent.png
  21. BIN  assets/images/toolbar/paragraph.png
  22. BIN  assets/images/toolbar/right.png
  23. BIN  assets/images/toolbar/strikethrough.png
  24. BIN  assets/images/toolbar/underline.png
  25. +182 −0 assets/javascripts/text-tags-config.js
  26. +603 −0 assets/javascripts/text-tags.js
  27. +2 −0  init.rb
  28. +1 −0  install.rb
  29. +24 −0 lib/textile_editor.rb
  30. +15 −0 tasks/textile_editor_tasks.rake
  31. +8 −0 test/textile_editor_test.rb
4 README
View
@@ -0,0 +1,4 @@
+TextileEditor
+=============
+
+Description goes here
22 Rakefile
View
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the textile_editor plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the textile_editor plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'TextileEditor'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
BIN  assets/images/toolbar/background.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/blockquote.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/bold.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/center.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/h1.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/h2.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/h3.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/h4.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/h5.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/h6.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/indent.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/italic.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/justify.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/left.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/list_bullets.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/list_numbers.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/omega.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/outdent.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/paragraph.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/right.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/strikethrough.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  assets/images/toolbar/underline.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
182 assets/javascripts/text-tags-config.js
View
@@ -0,0 +1,182 @@
+// Define Button Object
+function edButton(id, display, tagStart, tagEnd, access, title, sve, open) {
+ this.id = id; // used to name the toolbar button
+ this.display = display; // label on button
+ this.tagStart = tagStart; // open tag
+ this.tagEnd = tagEnd; // close tag
+ this.access = access; // set to -1 if tag does not need to be closed
+ this.title = title;
+ this.sve = sve;
+ this.open = open; // set to -1 if tag does not need to be closed
+}
+
+var theButtons = new Array();
+var edOpenTags = new Array();
+
+theButtons[theButtons.length] = new edButton('ed_strong'
+ ,'str'
+ ,'*'
+ ,'*'
+ ,'b'
+ ,'Bold'
+ ,'s'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_emphasis'
+ ,'em'
+ ,'_'
+ ,'_'
+ ,'i'
+ ,'Italicize'
+ ,'s'
+ );
+
+
+
+theButtons[theButtons.length] = new edButton('ed_underline'
+ ,'u'
+ ,'+'
+ ,'+'
+ ,'u'
+ ,'Underline'
+ ,'s'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_strike'
+ ,'S'
+ ,'-'
+ ,'-'
+ ,'s'
+ ,'Strikethrough'
+ ,'s'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_ol'
+ ,'OL'
+ ,' # '
+ ,'\n'
+ ,'o'
+ ,'Numbered List'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_ul'
+ ,'UL'
+ ,' * '
+ ,'\n'
+ ,'l'
+ ,'Bulleted List'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_p'
+ ,'P'
+ ,'p'
+ ,'\n'
+ ,'p'
+ ,'Paragraph'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_h1'
+ ,'h1'
+ ,'h1'
+ ,'\n'
+ ,'1'
+ ,'Header 1'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_h2'
+ ,'h2'
+ ,'h2'
+ ,'\n'
+ ,'2'
+ ,'Header 2'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_h3'
+ ,'h3'
+ ,'h3'
+ ,'\n'
+ ,'3'
+ ,'Header 3'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_h4'
+ ,'h4'
+ ,'h4'
+ ,'\n'
+ ,'4'
+ ,'Header 4'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_block'
+ ,'b-quote'
+ ,'bq'
+ ,'\n'
+ ,'q'
+ ,'Blockquote'
+ );
+
+
+theButtons[theButtons.length] = new edButton('ed_outdent'
+ ,'OUT'
+ ,')'
+ ,'\n'
+ ,''
+ ,'Outdent'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_indent'
+ ,'IN'
+ ,'('
+ ,'\n'
+ ,''
+ ,'Indent'
+ );
+
+
+theButtons[theButtons.length] = new edButton('ed_justifyl'
+ ,'JL'
+ ,'<'
+ ,''
+ ,''
+ ,'Left Justify'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_justifyc'
+ ,'JC'
+ ,'='
+ ,''
+ ,''
+ ,'Center Text'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_justifyr'
+ ,'JR'
+ ,'>'
+ ,''
+ ,''
+ ,'Right Justify'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_justify'
+ ,'J'
+ ,'<>'
+ ,''
+ ,''
+ ,'Justify'
+ );
+
+
+/*theButtons[theButtons.length] = new edButton('ed_code'
+ ,'code'
+ ,'@'
+ ,'@'
+ ,'c'
+ );
+
+theButtons[theButtons.length] = new edButton('ed_close'
+ ,'Close Tags'
+ ,''
+ ,''
+ ,'t'
+ ,'Close All Tags (doesnt work)'
+ );*/ // special case
603 assets/javascripts/text-tags.js
View
@@ -0,0 +1,603 @@
+/*
+
+Textile Editor v0.1
+created by: dave olsen, wvu web services
+created on: march 17, 2007
+project page: slateinfo.blogs.wvu.edu
+
+inspired by:
+ - Patrick Woods, http://www.hakjoon.com/code/38/textile-quicktags-redirect &
+ - Alex King, http://alexking.org/projects/js-quicktags
+
+features:
+ - supports: IE7, FF2, Safari2
+ - ability to use "simple" vs. "extended" editor
+ - supports all block elements in textile except footnote
+ - supports all block modifier elements in textile
+ - supports simple ordered and unordered lists
+ - supports most of the phrase modifiers, very easy to add the missing ones
+ - supports multiple-paragraph modification
+ - can have multiple "editors" on one page, access key use in this environment is flaky
+ - access key support
+ - select text to add and remove tags, selection stays highlighted
+ - seamlessly change between tags and modifiers
+ - doesn't need to be in the body onload tag
+ - can supply your own, custom IDs for the editor to be drawn around
+
+todo:
+ - a clean way of providing image and link inserts
+ - get the selection to properly show in IE
+
+more on textile:
+ - Textism, http://www.textism.com/tools/textile/index.php
+ - Textile Reference, http://hobix.com/textile/
+
+*/
+
+document.write('<script src="text-tags-config.js" type="text/javascript"></script>');
+
+// initiliaze the quicktags
+function initQuicktags(canvasi) {
+ for (var i = 0; i < canvasi.length; i++) {
+ edToolbar(document.getElementById(canvasi[i]),canvasi[i]);
+ }
+}
+
+// draw the toolbar
+function edToolbar(canvas,canvas_name) {
+ var view = '';
+ if (canvas_name.match(/^s\_/)) {
+ view = 's';
+ }
+ var toolbar = document.createElement("div");
+ toolbar.id = "toolbar";
+ canvas.parentNode.insertBefore(toolbar, canvas);
+ // Create the local Button array by assigning theButtons array to edButtons
+ var edButtons = new Array();
+ edButtons = theButtons;
+ for (var i = 0; i < edButtons.length; i++) {
+ var thisButton = edShowButton(edButtons[i], canvas);
+ if (view == 's') {
+ if (edButtons[i].sve == 's') {
+ toolbar.appendChild(thisButton);
+ }
+ }
+ else {
+ toolbar.appendChild(thisButton);
+ }
+ }
+}
+
+// draw individual buttons
+function edShowButton(button, edCanvas) {
+ var theButton = document.createElement("button");
+ theButton.id = button.id;
+ theButton.className = 'ed_button';
+ theButton.className += 'selected';
+ theButton.tagStart = button.tagStart;
+ theButton.tagEnd = button.tagEnd;
+ theButton.accessKey = button.access;
+ theButton.title = button.title;
+ theButton.open = button.open;
+ theButton.onclick = function() { edInsertTag(edCanvas,this); return false; }
+ return theButton;
+}
+
+// if clicked, no selected text, tag not open highlight button
+function edAddTag(button) {
+ if (button.tagEnd != '') {
+ edOpenTags[edOpenTags.length] = button;
+ var el = document.getElementById(button.id);
+ el.className = 'selected';
+ }
+}
+
+// if clicked, no selected text, tag open lowlight button
+function edRemoveTag(button) {
+ for (i = 0; i < edOpenTags.length; i++) {
+ if (edOpenTags[i] == button) {
+ edOpenTags.splice(button, 1);
+ var el = document.getElementById(button.id);
+ el.className = 'unselected';
+ }
+ }
+}
+
+// see if there are open tags. for the remove tag bit...
+function edCheckOpenTags(button) {
+ var tag = 0;
+ for (i = 0; i < edOpenTags.length; i++) {
+ if (edOpenTags[i] == button) {
+ tag++;
+ }
+ }
+ if (tag > 0) {
+ return true; // tag found
+ }
+ else {
+ return false; // tag not found
+ }
+}
+
+// insert the tag. this is the bulk of the code.
+function edInsertTag(myField, button) {
+
+ myField.focus();
+
+ var textSelected = false;
+ var finalText = '';
+ var FF = false;
+
+ // grab the text that's going to be manipulated, by browser
+ if (document.selection) { // IE support
+ sel = document.selection.createRange();
+
+ // set-up the text vars
+ var beginningText = '';
+ var followupText = '';
+ var selectedText = sel.text;
+
+ // check if text has been selected
+ if (sel.text.length > 0) {
+ textSelected = true;
+ }
+
+ // set-up newline regex's so we can swap tags across multiple paragraphs
+ var newlineReplaceRegexClean = /\r\n\s\n/g;
+ var newlineReplaceRegexDirty = '\\r\\n\\s\\n';
+ var newlineReplaceClean = '\r\n\n';
+ }
+ else if (myField.selectionStart || myField.selectionStart == '0') { // MOZ/FF/NS/S support
+
+ // figure out cursor and selection positions
+ var startPos = myField.selectionStart;
+ var endPos = myField.selectionEnd;
+ var cursorPos = endPos;
+ var scrollTop = myField.scrollTop;
+ FF = true; // note that is is a FF/MOZ/NS/S browser
+
+ // set-up the text vars
+ var beginningText = myField.value.substring(0, startPos);
+ var followupText = myField.value.substring(endPos, myField.value.length);
+
+ // check if text has been selected
+ if (startPos != endPos) {
+ textSelected = true;
+ var selectedText = myField.value.substring(startPos, endPos);
+ }
+
+ // set-up newline regex's so we can swap tags across multiple paragraphs
+ var newlineReplaceRegexClean = /\n\n/g;
+ var newlineReplaceRegexDirty = '\\n\\n';
+ var newlineReplaceClean = '\n\n';
+ }
+
+
+ // if there is text that has been highlighted...
+ if (textSelected) {
+
+ // set-up some defaults for how to handle bad new line characters
+ var newlineStart = '';
+ var newlineStartPos = 0;
+ var newlineEnd = '';
+ var newlineEndPos = 0;
+ var newlineFollowup = '';
+
+ // set-up some defaults for how to handle placing the beginning and end of selection
+ var posDiffPos = 0;
+ var posDiffNeg = 0;
+ var mplier = 1;
+
+ // remove newline from the beginning of the selectedText.
+ if (selectedText.match(/^\n/)) {
+ selectedText = selectedText.replace(/^\n/,'');
+ newlineStart = '\n';
+ newlineStartpos = 1;
+ }
+
+ // remove newline from the end of the selectedText.
+ if (selectedText.match(/\n$/g)) {
+ selectedText = selectedText.replace(/\n$/g,'');
+ newlineEnd = '\n';
+ newlineEndPos = 1;
+ }
+
+ // no clue, i'm sure it made sense at the time i wrote it
+ if (followupText.match(/^\n/)) {
+ newlineFollowup = '';
+ }
+ else {
+ newlineFollowup = '\n\n';
+ }
+
+ // first off let's check if the user is trying to mess with lists
+ if ((button.tagStart == ' * ') || (button.tagStart == ' # ')) {
+
+ listItems = 0; // sets up a default to be able to properly manipulate final selection
+
+ // set-up all of the regex's
+ re_start = new RegExp('^ (\\*|\\#) ','g');
+ if (button.tagStart == ' # ') {
+ re_tag = new RegExp(' \\# ','g'); // because of JS regex stupidity i need an if/else to properly set it up, could have done it with a regex replace though
+ }
+ else {
+ re_tag = new RegExp(' \\* ','g');
+ }
+ re_replace = new RegExp(' (\\*|\\#) ','g');
+
+ // try to remove bullets in text copied from ms word **Mac Only!**
+ re_word_bullet_m_s = new RegExp('','g'); // mac/safari
+ re_word_bullet_m_f = new RegExp('','g'); // mac/firefox
+ selectedText = selectedText.replace(re_word_bullet_m_s,'').replace(re_word_bullet_m_f,'');
+
+ // if the selected text starts with one of the tags we're working with...
+ if (selectedText.match(re_start)) {
+
+ // if tag that begins the selection matches the one clicked, remove them all
+ if (selectedText.match(re_tag)) {
+ finalText = beginningText
+ + newlineStart
+ + selectedText.replace(re_replace,'')
+ + newlineEnd
+ + followupText;
+ if (matches = selectedText.match(/ (\*|\#) /g)) {
+ listItems = matches.length;
+ }
+ posDiffNeg = listItems*3; // how many list items were there because that's 3 spaces to remove from final selection
+ }
+
+ // else replace the current tag type with the selected tag type
+ else {
+ finalText = beginningText
+ + newlineStart
+ + selectedText.replace(re_replace,button.tagStart)
+ + newlineEnd
+ + followupText;
+ }
+ }
+
+ // else try to create the list type
+ // NOTE: the items in a list will only be replaced if a newline starts with some character, not a space
+ else {
+ finalText = beginningText
+ + newlineStart
+ + button.tagStart
+ + selectedText.replace(newlineReplaceRegexClean,newlineReplaceClean + button.tagStart).replace(/\n(\w)/g,'\n' + button.tagStart + '$1')
+ + newlineEnd
+ + followupText;
+ if (matches = selectedText.match(/\n(\w)/g)) {
+ listItems = matches.length;
+ }
+ posDiffPos = 3 + listItems*3;
+ }
+ }
+
+ // now lets look and see if the user is trying to muck with a block or block modifier
+ else if (button.tagStart.match(/^(h1|h2|h3|h4|bq|p|\>|\<\>|\<|\=|\(|\))/g)) {
+
+ var insertTag = '';
+ var insertModifier = '';
+ var tagPartBlock = '';
+ var tagPartModifier = '';
+ var tagPartModifierOrig = ''; // ugly hack but it's late
+ var drawSwitch = '';
+ var captureIndentStart = false;
+ var captureListStart = false;
+ var periodAddition = '\\. ';
+ var periodAdditionClean = '. ';
+ var listItemsAddition = 0;
+
+ var re_list_items = new RegExp('(\\*+|\\#+)','g'); // need this regex later on when checking indentation of lists
+
+ var re_block_modifier = new RegExp('^(h1|h2|h3|h4|bq|p| [\\*]{1,} | [\\#]{1,} |)(\\>|\\<\\>|\\<|\\=|[\\(]{1,}|[\\)]{1,6}|)','g');
+ if (tagPartMatches = re_block_modifier.exec(selectedText)) {
+ tagPartBlock = tagPartMatches[1];
+ tagPartModifier = tagPartMatches[2];
+ tagPartModifierOrig = tagPartMatches[2];
+ tagPartModifierOrig = tagPartModifierOrig.replace(/\(/g,"\\(");
+ }
+
+ // if tag already up is the same as the tag provided replace the whole tag
+ if (tagPartBlock == button.tagStart) {
+ insertTag = tagPartBlock + tagPartModifierOrig; // use Orig because it's escaped for regex
+ drawSwitch = 0;
+ }
+ // else if let's check to add/remove block modifier
+ else if ((tagPartModifier == button.tagStart) || (newm = tagPartModifier.match(/[\(]{2,}/g))) {
+ if ((button.tagStart == '(') || (button.tagStart == ')')) {
+ var indentLength = tagPartModifier.length;
+ if (button.tagStart == '(') {
+ indentLength = indentLength + 1;
+ }
+ else {
+ indentLength = indentLength - 1;
+ }
+ for (var i = 0; i < indentLength; i++) {
+ insertModifier = insertModifier + '(';
+ }
+ insertTag = tagPartBlock + insertModifier;
+ }
+ else {
+ if (button.tagStart == tagPartModifier) {
+ insertTag = tagPartBlock;
+ } // going to rely on the default empty insertModifier
+ else {
+
+ if (button.tagStart.match(/(\>|\<\>|\<|\=)/g)) {
+ insertTag = tagPartBlock + button.tagStart;
+ }
+ else {
+ insertTag = button.tagStart + tagPartModifier;
+ }
+ }
+
+ }
+ drawSwitch = 1;
+ }
+ // indentation of list items
+ else if (listPartMatches = re_list_items.exec(tagPartBlock)) {
+ var listTypeMatch = listPartMatches[1];
+ var indentLength = tagPartBlock.length - 2;
+ var listInsert = '';
+ if (button.tagStart == '(') {
+ indentLength = indentLength + 1;
+ }
+ else {
+ indentLength = indentLength - 1;
+ }
+ if (listTypeMatch.match(/[\*]{1,}/g)) {
+ var listType = '*';
+ var listReplace = '\\*';
+ }
+ else {
+ var listType = '#';
+ var listReplace = '\\#';
+ }
+ for (var i = 0; i < indentLength; i++) {
+ listInsert = listInsert + listType;
+ }
+ if (listInsert != '') {
+ insertTag = ' ' + listInsert + ' ';
+ }
+ else {
+ insertTag = '';
+ }
+ tagPartBlock = tagPartBlock.replace(/(\*|\#)/g,listReplace);
+ drawSwitch = 1;
+ captureListStart = true;
+ periodAddition = '';
+ periodAdditionClean = '';
+ if (matches = selectedText.match(/\n\s/g)) {
+ listItemsAddition = matches.length;
+ }
+ }
+ // must be a block modification e.g. p>. to p<.
+ else {
+
+ // if this is a block modification/addition
+ if (button.tagStart.match(/(h1|h2|h3|h4|bq|p)/g)) {
+ if (tagPartBlock == '') {
+ drawSwitch = 2;
+ }
+ else {
+ drawSwitch = 1;
+ }
+
+ insertTag = button.tagStart + tagPartModifier;
+ }
+
+ // else this is a modifier modification/addition
+ else {
+ if ((tagPartModifier == '') && (tagPartBlock != '')) {
+ drawSwitch = 1;
+ }
+ else if (tagPartModifier == '') {
+ drawSwitch = 2;
+ }
+ else {
+ drawSwitch = 1;
+ }
+
+ // if no tag part block but a modifier we need at least the p tag
+ if (tagPartBlock == '') {
+ tagPartBlock = 'p';
+ }
+
+ //make sure to swap out outdent
+ if (button.tagStart == ')') {
+ tagPartModifier = '';
+ }
+ else {
+ tagPartModifier = button.tagStart;
+ captureIndentStart = true; // ugly hack to fix issue with proper selection handling
+ }
+
+ insertTag = tagPartBlock + tagPartModifier;
+ }
+ }
+
+ mplier = 0;
+ if (captureListStart || (tagPartModifier.match(/[\(\)]{1,}/g))) {
+ re_start = new RegExp(insertTag.escape + periodAddition,'g'); // for tags that mimic regex properties, parens + list tags
+ }
+ else {
+ re_start = new RegExp(insertTag + periodAddition,'g'); // for tags that don't, why i can't just escape everything i have no clue
+ }
+ re_old = new RegExp(tagPartBlock + tagPartModifierOrig + periodAddition,'g');
+ re_middle = new RegExp(newlineReplaceRegexDirty + insertTag.escape + periodAddition.escape,'g');
+ re_tag = new RegExp(insertTag.escape + periodAddition.escape,'g');
+
+ // *************************************************************************************************************************
+ // this is where everything gets swapped around or inserted, bullets and single options have their own if/else statements
+ // *************************************************************************************************************************
+ if ((drawSwitch == 0) || (drawSwitch == 1)) {
+ if (drawSwitch == 0) { // completely removing a tag
+ finalText = beginningText
+ + newlineStart
+ + selectedText.replace(re_start,'').replace(re_middle,newlineReplaceClean)
+ + newlineEnd
+ + followupText;
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
+ mplier = mplier + matches.length;
+ }
+ posDiffNeg = insertTag.length + 2 + (mplier*4);
+ }
+ else { // modifying a tag, though we do delete bullets here
+ finalText = beginningText
+ + newlineStart
+ + selectedText.replace(re_old,insertTag + periodAdditionClean)
+ + newlineEnd
+ + followupText;
+
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
+ mplier = mplier + matches.length;
+ }
+ // figure out the length of various elements to modify the selection position
+ if (captureIndentStart) { // need to double-check that this wasn't the first indent
+ tagPreviousLength = tagPartBlock.length;
+ tagCurrentLength = insertTag.length;
+ }
+ else if (captureListStart) { // if this is a list we're manipulating
+ if (button.tagStart == '(') { // if indenting
+ tagPreviousLength = listTypeMatch.length + 2;
+ tagCurrentLength = insertTag.length + listItemsAddition;
+ }
+ else if (insertTag.match(/(\*|\#)/g)) { // if removing but still has bullets
+ tagPreviousLength = insertTag.length + listItemsAddition;
+ tagCurrentLength = listTypeMatch.length;
+ }
+ else { // if removing last bullet
+ tagPreviousLength = insertTag.length + listItemsAddition;
+ tagCurrentLength = listTypeMatch.length - (3*listItemsAddition) - 1;
+ }
+ }
+ else { // everything else
+ tagPreviousLength = tagPartBlock.length + tagPartModifier.length;
+ tagCurrentLength = insertTag.length;
+ }
+ if (tagCurrentLength > tagPreviousLength) {
+ posDiffPos = (tagCurrentLength - tagPreviousLength) + (mplier*(tagCurrentLength - tagPreviousLength));
+ }
+ else {
+ posDiffNeg = (tagPreviousLength - tagCurrentLength) + (mplier*(tagPreviousLength - tagCurrentLength));
+ }
+ }
+ }
+ else { // for adding tags other then bullets (have their own statement)
+ finalText = beginningText
+ + newlineStart
+ + insertTag + '. '
+ + selectedText.replace(newlineReplaceRegexClean,button.tagEnd + '\n' + insertTag + '. ')
+ + newlineFollowup
+ + newlineEnd
+ + followupText;
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
+ mplier = mplier + matches.length;
+ }
+ posDiffPos = insertTag.length + 2 + (mplier*4);
+ }
+ }
+
+ // swap in and out the simple tags around a selection like bold
+ else {
+
+ mplier = 1; // the multiplier for the tag length
+ re_start = new RegExp('^\\' + button.tagStart,'g');
+ re_end = new RegExp('\\' + button.tagEnd + '$','g');
+ re_middle = new RegExp('\\' + button.tagEnd + newlineReplaceRegexDirty + '\\' + button.tagStart,'g');
+ if (selectedText.match(re_start) && selectedText.match(re_end)) {
+ finalText = beginningText
+ + newlineStart
+ + selectedText.replace(re_start,'').replace(re_end,'').replace(re_middle,newlineReplaceClean)
+ + newlineEnd
+ + followupText;
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
+ mplier = mplier + matches.length;
+ }
+ posDiffNeg = button.tagStart.length*mplier + button.tagEnd.length*mplier;
+ }
+ else {
+ finalText = beginningText
+ + newlineStart
+ + button.tagStart
+ + selectedText.replace(newlineReplaceRegexClean,button.tagEnd + newlineReplaceClean + button.tagStart)
+ + button.tagEnd
+ + newlineEnd
+ + followupText;
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
+ mplier = mplier + matches.length;
+ }
+ posDiffPos = (button.tagStart.length*mplier) + (button.tagEnd.length*mplier);
+ }
+ }
+
+ cursorPos += button.tagStart.length + button.tagEnd.length;
+
+ }
+
+ // just swap in and out single values, e.g. someone clicks b they'll get a *
+ else {
+ var buttonStart = '';
+ var buttonEnd = '';
+ var re_p = new RegExp('(\\<|\\>|\\=|\\<\\>|\\(|\\))','g');
+ var re_h = new RegExp('^(h1|h2|h3|h4|p|bq)','g');
+ if (!edCheckOpenTags(button) || button.tagEnd == '') { // opening tag
+
+ if (button.tagStart.match(re_h)) {
+ buttonStart = button.tagStart + '. ';
+ }
+ else {
+ buttonStart = button.tagStart;
+ }
+ if (button.tagStart.match(re_p)) { // make sure that invoking block modifiers don't do anything
+ finalText = beginningText
+ + followupText;
+ cursorPos = startPos;
+ }
+ else {
+ finalText = beginningText
+ + buttonStart
+ + followupText;
+ edAddTag(button);
+ cursorPos = startPos + buttonStart.length;
+ }
+
+ }
+ else { // closing tag
+ if (button.tagStart.match(re_p)) {
+ buttonEnd = '\n\n';
+ }
+ else if (button.tagStart.match(re_h)) {
+ buttonEnd = '\n\n';
+ }
+ else {
+ buttonEnd = button.tagEnd
+ }
+ finalText = beginningText
+ + button.tagEnd
+ + followupText;
+ edRemoveTag(button);
+ cursorPos = startPos + button.tagEnd.length;
+ }
+ }
+
+ // set the appropriate DOM value with the final text
+ if (FF == true) {
+ myField.value = finalText;
+ }
+ else {
+ sel.text = finalText;
+ }
+
+ // build up the selection capture, doesn't work in IE
+ if (textSelected) {
+ myField.selectionStart = startPos + newlineStartPos;
+ myField.selectionEnd = endPos + posDiffPos - posDiffNeg - newlineEndPos;
+ //alert('s: ' + myField.selectionStart + ' e: ' + myField.selectionEnd + ' sp: ' + startPos + ' ep: ' + endPos + ' pdp: ' + posDiffPos + ' pdn: ' + posDiffNeg)
+ }
+ else {
+ myField.selectionStart = cursorPos;
+ myField.selectionEnd = cursorPos;
+ }
+ //myField.scrollTop = scrollTop;
+}
2  init.rb
View
@@ -0,0 +1,2 @@
+# Include hook code here
+ActionView::Base.send :include, TextileEditorHelper
1  install.rb
View
@@ -0,0 +1 @@
+# Install hook code here
24 lib/textile_editor.rb
View
@@ -0,0 +1,24 @@
+# TextileEditor
+module TextileEditorHelper
+ def textile_editor(object, field, options={})
+ editor_id = '%s_%s' % [object, field]
+ mode = options.delete(:simple) ? 'simple' : 'extended'
+ (@textile_editor_ids ||= []) << [editor_id, mode]
+ text_area(object, field, options)
+ end
+
+ def textile_editor_initialize
+ editor_ids = @textile_editor_ids || []
+ output = []
+ output << stylesheet_link_tag('button_styles', 'editor_styles')
+ output << javascript_include_tag('text-tags')
+ output << '<script type="text/javascript">'
+ output << 'addLoadEvent(initTextileEditors);'
+ output << 'function initTextileEditors() {'
+ editor_ids.each do |editor_id, mode|
+ output << "edToolbar('%s', '%s');" % [editor_id, mode]
+ end
+ output << '}'
+ output << '</script>'
+ end
+end
15 tasks/textile_editor_tasks.rake
View
@@ -0,0 +1,15 @@
+namespace :textile_editor do
+ PLUGIN_ROOT = File.dirname(__FILE__) + '/../'
+
+ desc 'Installs required javascript files to the public/javascripts directory.'
+ task :install do
+ %w{javascripts stylesheets images}.each do |dir|
+ FileUtils.cp Dir[PLUGIN_ROOT + "/assets/#{dir}/*.*"], RAILS_ROOT + "/public/#{dir}"
+ end
+ end
+
+ # desc 'Removes the javascripts for the plugin.'
+ # task :remove do
+ # FileUtils.rm %{lowpro.js}.collect { |f| RAILS_ROOT + "/public/javascripts/" + f }
+ # end
+end
8 test/textile_editor_test.rb
View
@@ -0,0 +1,8 @@
+require 'test/unit'
+
+class TextileEditorTest < Test::Unit::TestCase
+ # Replace this with your real tests.
+ def test_this_plugin
+ flunk
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.