textile_rails 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/lib/generators/textile_rails/install_generator.rb +45 -0
  8. data/lib/helpers/default.rb +192 -0
  9. data/lib/textile_rails.rb +10 -0
  10. data/lib/textile_rails/version.rb +3 -0
  11. data/textile_rails.gemspec +23 -0
  12. data/vendor/README +90 -0
  13. data/vendor/assets/images/textile-editor/.DS_Store +0 -0
  14. data/vendor/assets/images/textile-editor/background.png +0 -0
  15. data/vendor/assets/images/textile-editor/blockquote.png +0 -0
  16. data/vendor/assets/images/textile-editor/bold.png +0 -0
  17. data/vendor/assets/images/textile-editor/center.png +0 -0
  18. data/vendor/assets/images/textile-editor/h1.png +0 -0
  19. data/vendor/assets/images/textile-editor/h2.png +0 -0
  20. data/vendor/assets/images/textile-editor/h3.png +0 -0
  21. data/vendor/assets/images/textile-editor/h4.png +0 -0
  22. data/vendor/assets/images/textile-editor/h5.png +0 -0
  23. data/vendor/assets/images/textile-editor/h6.png +0 -0
  24. data/vendor/assets/images/textile-editor/icon_biggrin.gif +0 -0
  25. data/vendor/assets/images/textile-editor/icon_confused.gif +0 -0
  26. data/vendor/assets/images/textile-editor/icon_cool.gif +0 -0
  27. data/vendor/assets/images/textile-editor/icon_cry.gif +0 -0
  28. data/vendor/assets/images/textile-editor/icon_eek.gif +0 -0
  29. data/vendor/assets/images/textile-editor/icon_evil.gif +0 -0
  30. data/vendor/assets/images/textile-editor/icon_lol.gif +0 -0
  31. data/vendor/assets/images/textile-editor/icon_redface.gif +0 -0
  32. data/vendor/assets/images/textile-editor/icon_rolleyes.gif +0 -0
  33. data/vendor/assets/images/textile-editor/icon_sad.gif +0 -0
  34. data/vendor/assets/images/textile-editor/icon_smile.gif +0 -0
  35. data/vendor/assets/images/textile-editor/icon_surprised.gif +0 -0
  36. data/vendor/assets/images/textile-editor/icon_toungueout.gif +0 -0
  37. data/vendor/assets/images/textile-editor/icon_twisted.gif +0 -0
  38. data/vendor/assets/images/textile-editor/icon_wink.gif +0 -0
  39. data/vendor/assets/images/textile-editor/indent.png +0 -0
  40. data/vendor/assets/images/textile-editor/italic.png +0 -0
  41. data/vendor/assets/images/textile-editor/justify.png +0 -0
  42. data/vendor/assets/images/textile-editor/left.png +0 -0
  43. data/vendor/assets/images/textile-editor/list_bullets.png +0 -0
  44. data/vendor/assets/images/textile-editor/list_numbers.png +0 -0
  45. data/vendor/assets/images/textile-editor/omega.png +0 -0
  46. data/vendor/assets/images/textile-editor/outdent.png +0 -0
  47. data/vendor/assets/images/textile-editor/paragraph.png +0 -0
  48. data/vendor/assets/images/textile-editor/right.png +0 -0
  49. data/vendor/assets/images/textile-editor/strikethrough.png +0 -0
  50. data/vendor/assets/images/textile-editor/underline.png +0 -0
  51. data/vendor/assets/javascripts/textile-editor-config.js +38 -0
  52. data/vendor/assets/javascripts/textile-editor.js +683 -0
  53. data/vendor/assets/stylesheets/textile-editor.css +53 -0
  54. metadata +139 -0
@@ -0,0 +1,38 @@
1
+ var teButtons = TextileEditor.buttons;
2
+
3
+ teButtons.push(new TextileEditorButton('ed_strong', 'bold.png', '*', '*', 'b', 'Bold','s'));
4
+ teButtons.push(new TextileEditorButton('ed_emphasis', 'italic.png', '_', '_', 'i', 'Italicize','s'));
5
+ teButtons.push(new TextileEditorButton('ed_underline', 'underline.png', '+', '+', 'u', 'Underline','s'));
6
+ teButtons.push(new TextileEditorButton('ed_strike', 'strikethrough.png', '-', '-', 's', 'Strikethrough','s'));
7
+ teButtons.push(new TextileEditorButton('ed_ol', 'list_numbers.png', ' # ', '\n', ',', 'Numbered List'));
8
+ teButtons.push(new TextileEditorButton('ed_ul', 'list_bullets.png', ' * ', '\n', '.', 'Bulleted List'));
9
+ teButtons.push(new TextileEditorButton('ed_p', 'paragraph.png', 'p', '\n', 'p', 'Paragraph'));
10
+ teButtons.push(new TextileEditorButton('ed_h1', 'h1.png', 'h1', '\n', '1', 'Header 1'));
11
+ teButtons.push(new TextileEditorButton('ed_h2', 'h2.png', 'h2', '\n', '2', 'Header 2'));
12
+ teButtons.push(new TextileEditorButton('ed_h3', 'h3.png', 'h3', '\n', '3', 'Header 3'));
13
+ teButtons.push(new TextileEditorButton('ed_h4', 'h4.png', 'h4', '\n', '4', 'Header 4'));
14
+ teButtons.push(new TextileEditorButton('ed_block', 'blockquote.png', 'bq', '\n', 'q', 'Blockquote'));
15
+ teButtons.push(new TextileEditorButton('ed_outdent', 'outdent.png', ')', '\n', ']', 'Outdent'));
16
+ teButtons.push(new TextileEditorButton('ed_indent', 'indent.png', '(', '\n', '[', 'Indent'));
17
+ teButtons.push(new TextileEditorButton('ed_justifyl', 'left.png', '<', '\n', 'l', 'Left Justify'));
18
+ teButtons.push(new TextileEditorButton('ed_justifyc', 'center.png', '=', '\n', 'e', 'Center Text'));
19
+ teButtons.push(new TextileEditorButton('ed_justifyr', 'right.png', '>', '\n', 'r', 'Right Justify'));
20
+ teButtons.push(new TextileEditorButton('ed_justify', 'justify.png', '<>', '\n', 'j', 'Justify'));
21
+ teButtons.push(new TextileEditorButton('ed_smile', 'icon_smile.gif', ':-} ', ' ', '10', 'Smile'));
22
+ teButtons.push(new TextileEditorButton('ed_sad', 'icon_sad.gif', ':-{ ', ' ', '11', 'Sad'));
23
+ teButtons.push(new TextileEditorButton('ed_wink', 'icon_wink.gif', ';-} ', ' ', '12', 'Wink'));
24
+ teButtons.push(new TextileEditorButton('ed_biggrin', 'icon_biggrin.gif', ':-D ', ' ', '13', 'Biggrin'));
25
+ teButtons.push(new TextileEditorButton('ed_surprised', 'icon_surprised.gif', ':-o ', ' ', '14', 'Surprised'));
26
+ teButtons.push(new TextileEditorButton('ed_toungueout', 'icon_toungueout.gif', ':-P ', ' ', '24', 'Toungueout'));
27
+ teButtons.push(new TextileEditorButton('ed_eek', 'icon_eek.gif', ':shock: ', ' ', '15', 'Eek'));
28
+ teButtons.push(new TextileEditorButton('ed_confused', 'icon_confused.gif', ':-? ', ' ', '16', 'Confused'));
29
+ teButtons.push(new TextileEditorButton('ed_cool', 'icon_cool.gif', ':cool: ', ' ', '17', 'Cool'));
30
+ teButtons.push(new TextileEditorButton('ed_lol', 'icon_lol.gif', ':lol: ', ' ', '18', 'Lol'));
31
+ teButtons.push(new TextileEditorButton('ed_evil', 'icon_evil.gif', ':evil: ', ' ', '19', 'Evil'));
32
+ teButtons.push(new TextileEditorButton('ed_twisted', 'icon_twisted.gif', ':twisted: ', ' ', '20', 'Twisted'));
33
+ teButtons.push(new TextileEditorButton('ed_roll', 'icon_rolleyes.gif', ':roll: ', ' ', '21', 'Roll'));
34
+ teButtons.push(new TextileEditorButton('ed_oops', 'icon_redface.gif', ':oops: ', ' ', '22', 'Redface'));
35
+ teButtons.push(new TextileEditorButton('ed_cry', 'icon_cry.gif', ':cry: ', ' ', '23', 'Cry'));
36
+
37
+
38
+ // teButtons.push(new TextileEditorButton('ed_code','code','@','@','c','Code'));
@@ -0,0 +1,683 @@
1
+ /*
2
+
3
+ Textile Editor v0.2
4
+ created by: dave olsen, wvu web services
5
+ created on: march 17, 2007
6
+ project page: slateinfo.blogs.wvu.edu
7
+
8
+ inspired by:
9
+ - Patrick Woods, http://www.hakjoon.com/code/38/textile-quicktags-redirect &
10
+ - Alex King, http://alexking.org/projects/js-quicktags
11
+
12
+ features:
13
+ - supports: IE7, FF2, Safari2
14
+ - ability to use "simple" vs. "extended" editor
15
+ - supports all block elements in textile except footnote
16
+ - supports all block modifier elements in textile
17
+ - supports simple ordered and unordered lists
18
+ - supports most of the phrase modifiers, very easy to add the missing ones
19
+ - supports multiple-paragraph modification
20
+ - can have multiple "editors" on one page, access key use in this environment is flaky
21
+ - access key support
22
+ - select text to add and remove tags, selection stays highlighted
23
+ - seamlessly change between tags and modifiers
24
+ - doesn't need to be in the body onload tag
25
+ - can supply your own, custom IDs for the editor to be drawn around
26
+
27
+ todo:
28
+ - a clean way of providing image and link inserts
29
+ - get the selection to properly show in IE
30
+
31
+ more on textile:
32
+ - Textism, http://www.textism.com/tools/textile/index.php
33
+ - Textile Reference, http://hobix.com/textile/
34
+
35
+ */
36
+
37
+ // Define Button Object
38
+ function TextileEditorButton(id, display, tagStart, tagEnd, access, title, sve, open) {
39
+ this.id = id; // used to name the toolbar button
40
+ this.display = display; // label on button
41
+ this.tagStart = tagStart; // open tag
42
+ this.tagEnd = tagEnd; // close tag
43
+ this.access = access; // set to -1 if tag does not need to be closed
44
+ this.title = title; // sets the title attribute of the button to give 'tool tips'
45
+ this.sve = sve; // sve = simple vs. extended. add an 's' to make it show up in the simple toolbar
46
+ this.open = open; // set to -1 if tag does not need to be closed
47
+ this.standard = true; // this is a standard button
48
+ // this.framework = 'prototype'; // the JS framework used
49
+ }
50
+
51
+ function TextileEditorButtonSeparator(sve) {
52
+ this.separator = true;
53
+ this.sve = sve;
54
+ }
55
+
56
+ var TextileEditor = function() {};
57
+ TextileEditor.buttons = new Array();
58
+ TextileEditor.Methods = {
59
+ // class methods
60
+
61
+ // create the toolbar (edToolbar)
62
+ initialize: function(canvas, view) {
63
+ var toolbar = document.createElement("div");
64
+ toolbar.id = "textile-toolbar-" + canvas;
65
+ toolbar.className = 'textile-toolbar';
66
+ this.canvas = document.getElementById(canvas);
67
+ this.canvas.parentNode.insertBefore(toolbar, this.canvas);
68
+ this.openTags = new Array();
69
+
70
+ // Create the local Button array by assigning theButtons array to edButtons
71
+ var edButtons = new Array();
72
+ edButtons = this.buttons;
73
+
74
+ var standardButtons = new Array();
75
+ for(var i = 0; i < edButtons.length; i++) {
76
+ var thisButton = this.prepareButton(edButtons[i]);
77
+ if (view == 's') {
78
+ if (edButtons[i].sve == 's') {
79
+ toolbar.appendChild(thisButton);
80
+ standardButtons.push(thisButton);
81
+ }
82
+ } else {
83
+ if (typeof thisButton == 'string') {
84
+ toolbar.innerHTML += thisButton;
85
+ } else {
86
+ toolbar.appendChild(thisButton);
87
+ standardButtons.push(thisButton);
88
+ }
89
+ }
90
+ } // end for
91
+
92
+ var te = this;
93
+ var buttons = toolbar.getElementsByTagName('button');
94
+ for(var i = 0; i < buttons.length; i++) {
95
+ //$A(toolbar.getElementsByTagName('button')).each(function(button) {
96
+ if (!buttons[i].onclick) {
97
+ buttons[i].onclick = function() { te.insertTag(this); return false; }
98
+ } // end if
99
+
100
+ buttons[i].tagStart = buttons[i].getAttribute('tagStart');
101
+ buttons[i].tagEnd = buttons[i].getAttribute('tagEnd');
102
+ buttons[i].open = buttons[i].getAttribute('open');
103
+ buttons[i].textile_editor = te;
104
+ buttons[i].canvas = te.canvas;
105
+ // console.log(buttons[i].canvas);
106
+ //});
107
+ }
108
+ }, // end initialize
109
+
110
+ // draw individual buttons (edShowButton)
111
+ prepareButton: function(button) {
112
+ if (button.separator) {
113
+ var theButton = document.createElement('span');
114
+ theButton.className = 'ed_sep';
115
+ return theButton;
116
+ }
117
+
118
+ if (button.standard) {
119
+ var theButton = document.createElement("button");
120
+ theButton.id = button.id;
121
+ theButton.setAttribute('class', 'standard');
122
+ theButton.setAttribute('tagStart', button.tagStart);
123
+ theButton.setAttribute('tagEnd', button.tagEnd);
124
+ theButton.setAttribute('open', button.open);
125
+
126
+ var img = document.createElement('img');
127
+ img.src = '/assets/textile-editor/' + button.display;
128
+ theButton.appendChild(img);
129
+ } else {
130
+ return button;
131
+ } // end if !custom
132
+
133
+ theButton.accessKey = button.access;
134
+ theButton.title = button.title;
135
+ return theButton;
136
+ }, // end prepareButton
137
+
138
+ // if clicked, no selected text, tag not open highlight button
139
+ // (edAddTag)
140
+ addTag: function(button) {
141
+ if (button.tagEnd != '') {
142
+ this.openTags[this.openTags.length] = button;
143
+ //var el = document.getElementById(button.id);
144
+ //el.className = 'selected';
145
+ button.className = 'selected';
146
+ }
147
+ }, // end addTag
148
+
149
+ // if clicked, no selected text, tag open lowlight button
150
+ // (edRemoveTag)
151
+ removeTag: function(button) {
152
+ for (i = 0; i < this.openTags.length; i++) {
153
+ if (this.openTags[i] == button) {
154
+ this.openTags.splice(button, 1);
155
+ //var el = document.getElementById(button.id);
156
+ //el.className = 'unselected';
157
+ button.className = 'unselected';
158
+ }
159
+ }
160
+ }, // end removeTag
161
+
162
+ // see if there are open tags. for the remove tag bit...
163
+ // (edCheckOpenTags)
164
+ checkOpenTags: function(button) {
165
+ var tag = 0;
166
+ for (i = 0; i < this.openTags.length; i++) {
167
+ if (this.openTags[i] == button) {
168
+ tag++;
169
+ }
170
+ }
171
+ if (tag > 0) {
172
+ return true; // tag found
173
+ }
174
+ else {
175
+ return false; // tag not found
176
+ }
177
+ }, // end checkOpenTags
178
+
179
+ // insert the tag. this is the bulk of the code.
180
+ // (edInsertTag)
181
+ insertTag: function(button, tagStart, tagEnd) {
182
+ // console.log(button);
183
+ var myField = button.canvas;
184
+ myField.focus();
185
+
186
+ if (tagStart) {
187
+ button.tagStart = tagStart;
188
+ button.tagEnd = tagEnd ? tagEnd : '\n';
189
+ }
190
+
191
+ var textSelected = false;
192
+ var finalText = '';
193
+ var FF = false;
194
+
195
+ // grab the text that's going to be manipulated, by browser
196
+ if (document.selection) { // IE support
197
+ sel = document.selection.createRange();
198
+
199
+ // set-up the text vars
200
+ var beginningText = '';
201
+ var followupText = '';
202
+ var selectedText = sel.text;
203
+
204
+ // check if text has been selected
205
+ if (sel.text.length > 0) {
206
+ textSelected = true;
207
+ }
208
+
209
+ // set-up newline regex's so we can swap tags across multiple paragraphs
210
+ var newlineReplaceRegexClean = /\r\n\s\n/g;
211
+ var newlineReplaceRegexDirty = '\\r\\n\\s\\n';
212
+ var newlineReplaceClean = '\r\n\n';
213
+ }
214
+ else if (myField.selectionStart || myField.selectionStart == '0') { // MOZ/FF/NS/S support
215
+
216
+ // figure out cursor and selection positions
217
+ var startPos = myField.selectionStart;
218
+ var endPos = myField.selectionEnd;
219
+ var cursorPos = endPos;
220
+ var scrollTop = myField.scrollTop;
221
+ FF = true; // note that is is a FF/MOZ/NS/S browser
222
+
223
+ // set-up the text vars
224
+ var beginningText = myField.value.substring(0, startPos);
225
+ var followupText = myField.value.substring(endPos, myField.value.length);
226
+
227
+ // check if text has been selected
228
+ if (startPos != endPos) {
229
+ textSelected = true;
230
+ var selectedText = myField.value.substring(startPos, endPos);
231
+ }
232
+
233
+ // set-up newline regex's so we can swap tags across multiple paragraphs
234
+ var newlineReplaceRegexClean = /\n\n/g;
235
+ var newlineReplaceRegexDirty = '\\n\\n';
236
+ var newlineReplaceClean = '\n\n';
237
+ }
238
+
239
+
240
+ // if there is text that has been highlighted...
241
+ if (textSelected) {
242
+
243
+ // set-up some defaults for how to handle bad new line characters
244
+ var newlineStart = '';
245
+ var newlineStartPos = 0;
246
+ var newlineEnd = '';
247
+ var newlineEndPos = 0;
248
+ var newlineFollowup = '';
249
+
250
+ // set-up some defaults for how to handle placing the beginning and end of selection
251
+ var posDiffPos = 0;
252
+ var posDiffNeg = 0;
253
+ var mplier = 1;
254
+
255
+ // remove newline from the beginning of the selectedText.
256
+ if (selectedText.match(/^\n/)) {
257
+ selectedText = selectedText.replace(/^\n/,'');
258
+ newlineStart = '\n';
259
+ newlineStartpos = 1;
260
+ }
261
+
262
+ // remove newline from the end of the selectedText.
263
+ if (selectedText.match(/\n$/g)) {
264
+ selectedText = selectedText.replace(/\n$/g,'');
265
+ newlineEnd = '\n';
266
+ newlineEndPos = 1;
267
+ }
268
+
269
+ // remove space from the end of the selectedText.
270
+ // Fixes a bug that causes any browser running under Microsoft Internet Explorer
271
+ // to append an additional space before the closing element.
272
+ // *Bold text *here => *Bold text*
273
+ if (selectedText.match(/\s$/g)) {
274
+ selectedText = selectedText.replace(/\s$/g,'');
275
+ followupText = ' ';
276
+ }
277
+
278
+ // no clue, i'm sure it made sense at the time i wrote it
279
+ if (followupText.match(/^\n/)) {
280
+ newlineFollowup = '';
281
+ }
282
+ else {
283
+ newlineFollowup = '\n\n';
284
+ }
285
+
286
+ // first off let's check if the user is trying to mess with lists
287
+ if ((button.tagStart == ' * ') || (button.tagStart == ' # ')) {
288
+
289
+ listItems = 0; // sets up a default to be able to properly manipulate final selection
290
+
291
+ // set-up all of the regex's
292
+ re_start = new RegExp('^ (\\*|\\#) ','g');
293
+ if (button.tagStart == ' # ') {
294
+ 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
295
+ }
296
+ else {
297
+ re_tag = new RegExp(' \\* ','g');
298
+ }
299
+ re_replace = new RegExp(' (\\*|\\#) ','g');
300
+
301
+ // try to remove bullets in text copied from ms word **Mac Only!**
302
+ re_word_bullet_m_s = new RegExp('• ','g'); // mac/safari
303
+ re_word_bullet_m_f = new RegExp('∑ ','g'); // mac/firefox
304
+ selectedText = selectedText.replace(re_word_bullet_m_s,'').replace(re_word_bullet_m_f,'');
305
+
306
+ // if the selected text starts with one of the tags we're working with...
307
+ if (selectedText.match(re_start)) {
308
+
309
+ // if tag that begins the selection matches the one clicked, remove them all
310
+ if (selectedText.match(re_tag)) {
311
+ finalText = beginningText
312
+ + newlineStart
313
+ + selectedText.replace(re_replace,'')
314
+ + newlineEnd
315
+ + followupText;
316
+ if (matches = selectedText.match(/ (\*|\#) /g)) {
317
+ listItems = matches.length;
318
+ }
319
+ posDiffNeg = listItems*3; // how many list items were there because that's 3 spaces to remove from final selection
320
+ }
321
+
322
+ // else replace the current tag type with the selected tag type
323
+ else {
324
+ finalText = beginningText
325
+ + newlineStart
326
+ + selectedText.replace(re_replace,button.tagStart)
327
+ + newlineEnd
328
+ + followupText;
329
+ }
330
+ }
331
+
332
+ // else try to create the list type
333
+ // NOTE: the items in a list will only be replaced if a newline starts with some character, not a space
334
+ else {
335
+ finalText = beginningText
336
+ + newlineStart
337
+ + button.tagStart
338
+ + selectedText.replace(newlineReplaceRegexClean,newlineReplaceClean + button.tagStart).replace(/\n(\S)/g,'\n' + button.tagStart + '$1')
339
+ + newlineEnd
340
+ + followupText;
341
+ if (matches = selectedText.match(/\n(\S)/g)) {
342
+ listItems = matches.length;
343
+ }
344
+ posDiffPos = 3 + listItems*3;
345
+ }
346
+ }
347
+
348
+ // now lets look and see if the user is trying to muck with a block or block modifier
349
+ else if (button.tagStart.match(/^(h1|h2|h3|h4|h5|h6|bq|p|\>|\<\>|\<|\=|\(|\))/g)) {
350
+
351
+ var insertTag = '';
352
+ var insertModifier = '';
353
+ var tagPartBlock = '';
354
+ var tagPartModifier = '';
355
+ var tagPartModifierOrig = ''; // ugly hack but it's late
356
+ var drawSwitch = '';
357
+ var captureIndentStart = false;
358
+ var captureListStart = false;
359
+ var periodAddition = '\\. ';
360
+ var periodAdditionClean = '. ';
361
+ var listItemsAddition = 0;
362
+
363
+ var re_list_items = new RegExp('(\\*+|\\#+)','g'); // need this regex later on when checking indentation of lists
364
+
365
+ var re_block_modifier = new RegExp('^(h1|h2|h3|h4|h5|h6|bq|p| [\\*]{1,} | [\\#]{1,} |)(\\>|\\<\\>|\\<|\\=|[\\(]{1,}|[\\)]{1,6}|)','g');
366
+ if (tagPartMatches = re_block_modifier.exec(selectedText)) {
367
+ tagPartBlock = tagPartMatches[1];
368
+ tagPartModifier = tagPartMatches[2];
369
+ tagPartModifierOrig = tagPartMatches[2];
370
+ tagPartModifierOrig = tagPartModifierOrig.replace(/\(/g,"\\(");
371
+ }
372
+
373
+ // if tag already up is the same as the tag provided replace the whole tag
374
+ if (tagPartBlock == button.tagStart) {
375
+ insertTag = tagPartBlock + tagPartModifierOrig; // use Orig because it's escaped for regex
376
+ drawSwitch = 0;
377
+ }
378
+ // else if let's check to add/remove block modifier
379
+ else if ((tagPartModifier == button.tagStart) || (newm = tagPartModifier.match(/[\(]{2,}/g))) {
380
+ if ((button.tagStart == '(') || (button.tagStart == ')')) {
381
+ var indentLength = tagPartModifier.length;
382
+ if (button.tagStart == '(') {
383
+ indentLength = indentLength + 1;
384
+ }
385
+ else {
386
+ indentLength = indentLength - 1;
387
+ }
388
+ for (var i = 0; i < indentLength; i++) {
389
+ insertModifier = insertModifier + '(';
390
+ }
391
+ insertTag = tagPartBlock + insertModifier;
392
+ }
393
+ else {
394
+ if (button.tagStart == tagPartModifier) {
395
+ insertTag = tagPartBlock;
396
+ } // going to rely on the default empty insertModifier
397
+ else {
398
+
399
+ if (button.tagStart.match(/(\>|\<\>|\<|\=)/g)) {
400
+ insertTag = tagPartBlock + button.tagStart;
401
+ }
402
+ else {
403
+ insertTag = button.tagStart + tagPartModifier;
404
+ }
405
+ }
406
+
407
+ }
408
+ drawSwitch = 1;
409
+ }
410
+ // indentation of list items
411
+ else if (listPartMatches = re_list_items.exec(tagPartBlock)) {
412
+ var listTypeMatch = listPartMatches[1];
413
+ var indentLength = tagPartBlock.length - 2;
414
+ var listInsert = '';
415
+ if (button.tagStart == '(') {
416
+ indentLength = indentLength + 1;
417
+ }
418
+ else {
419
+ indentLength = indentLength - 1;
420
+ }
421
+ if (listTypeMatch.match(/[\*]{1,}/g)) {
422
+ var listType = '*';
423
+ var listReplace = '\\*';
424
+ }
425
+ else {
426
+ var listType = '#';
427
+ var listReplace = '\\#';
428
+ }
429
+ for (var i = 0; i < indentLength; i++) {
430
+ listInsert = listInsert + listType;
431
+ }
432
+ if (listInsert != '') {
433
+ insertTag = ' ' + listInsert + ' ';
434
+ }
435
+ else {
436
+ insertTag = '';
437
+ }
438
+ tagPartBlock = tagPartBlock.replace(/(\*|\#)/g,listReplace);
439
+ drawSwitch = 1;
440
+ captureListStart = true;
441
+ periodAddition = '';
442
+ periodAdditionClean = '';
443
+ if (matches = selectedText.match(/\n\s/g)) {
444
+ listItemsAddition = matches.length;
445
+ }
446
+ }
447
+ // must be a block modification e.g. p>. to p<.
448
+ else {
449
+
450
+ // if this is a block modification/addition
451
+ if (button.tagStart.match(/(h1|h2|h3|h4|h5|h6|bq|p)/g)) {
452
+ if (tagPartBlock == '') {
453
+ drawSwitch = 2;
454
+ }
455
+ else {
456
+ drawSwitch = 1;
457
+ }
458
+
459
+ insertTag = button.tagStart + tagPartModifier;
460
+ }
461
+
462
+ // else this is a modifier modification/addition
463
+ else {
464
+ if ((tagPartModifier == '') && (tagPartBlock != '')) {
465
+ drawSwitch = 1;
466
+ }
467
+ else if (tagPartModifier == '') {
468
+ drawSwitch = 2;
469
+ }
470
+ else {
471
+ drawSwitch = 1;
472
+ }
473
+
474
+ // if no tag part block but a modifier we need at least the p tag
475
+ if (tagPartBlock == '') {
476
+ tagPartBlock = 'p';
477
+ }
478
+
479
+ //make sure to swap out outdent
480
+ if (button.tagStart == ')') {
481
+ tagPartModifier = '';
482
+ }
483
+ else {
484
+ tagPartModifier = button.tagStart;
485
+ captureIndentStart = true; // ugly hack to fix issue with proper selection handling
486
+ }
487
+
488
+ insertTag = tagPartBlock + tagPartModifier;
489
+ }
490
+ }
491
+
492
+ mplier = 0;
493
+ if (captureListStart || (tagPartModifier.match(/[\(\)]{1,}/g))) {
494
+ re_start = new RegExp(insertTag.escape + periodAddition,'g'); // for tags that mimic regex properties, parens + list tags
495
+ }
496
+ else {
497
+ re_start = new RegExp(insertTag + periodAddition,'g'); // for tags that don't, why i can't just escape everything i have no clue
498
+ }
499
+ re_old = new RegExp(tagPartBlock + tagPartModifierOrig + periodAddition,'g');
500
+ re_middle = new RegExp(newlineReplaceRegexDirty + insertTag.escape + periodAddition.escape,'g');
501
+ re_tag = new RegExp(insertTag.escape + periodAddition.escape,'g');
502
+
503
+ // *************************************************************************************************************************
504
+ // this is where everything gets swapped around or inserted, bullets and single options have their own if/else statements
505
+ // *************************************************************************************************************************
506
+ if ((drawSwitch == 0) || (drawSwitch == 1)) {
507
+ if (drawSwitch == 0) { // completely removing a tag
508
+ finalText = beginningText
509
+ + newlineStart
510
+ + selectedText.replace(re_start,'').replace(re_middle,newlineReplaceClean)
511
+ + newlineEnd
512
+ + followupText;
513
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
514
+ mplier = mplier + matches.length;
515
+ }
516
+ posDiffNeg = insertTag.length + 2 + (mplier*4);
517
+ }
518
+ else { // modifying a tag, though we do delete bullets here
519
+ finalText = beginningText
520
+ + newlineStart
521
+ + selectedText.replace(re_old,insertTag + periodAdditionClean)
522
+ + newlineEnd
523
+ + followupText;
524
+
525
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
526
+ mplier = mplier + matches.length;
527
+ }
528
+ // figure out the length of various elements to modify the selection position
529
+ if (captureIndentStart) { // need to double-check that this wasn't the first indent
530
+ tagPreviousLength = tagPartBlock.length;
531
+ tagCurrentLength = insertTag.length;
532
+ }
533
+ else if (captureListStart) { // if this is a list we're manipulating
534
+ if (button.tagStart == '(') { // if indenting
535
+ tagPreviousLength = listTypeMatch.length + 2;
536
+ tagCurrentLength = insertTag.length + listItemsAddition;
537
+ }
538
+ else if (insertTag.match(/(\*|\#)/g)) { // if removing but still has bullets
539
+ tagPreviousLength = insertTag.length + listItemsAddition;
540
+ tagCurrentLength = listTypeMatch.length;
541
+ }
542
+ else { // if removing last bullet
543
+ tagPreviousLength = insertTag.length + listItemsAddition;
544
+ tagCurrentLength = listTypeMatch.length - (3*listItemsAddition) - 1;
545
+ }
546
+ }
547
+ else { // everything else
548
+ tagPreviousLength = tagPartBlock.length + tagPartModifier.length;
549
+ tagCurrentLength = insertTag.length;
550
+ }
551
+ if (tagCurrentLength > tagPreviousLength) {
552
+ posDiffPos = (tagCurrentLength - tagPreviousLength) + (mplier*(tagCurrentLength - tagPreviousLength));
553
+ }
554
+ else {
555
+ posDiffNeg = (tagPreviousLength - tagCurrentLength) + (mplier*(tagPreviousLength - tagCurrentLength));
556
+ }
557
+ }
558
+ }
559
+ else { // for adding tags other then bullets (have their own statement)
560
+ finalText = beginningText
561
+ + newlineStart
562
+ + insertTag + '. '
563
+ + selectedText.replace(newlineReplaceRegexClean,button.tagEnd + '\n' + insertTag + '. ')
564
+ + newlineFollowup
565
+ + newlineEnd
566
+ + followupText;
567
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
568
+ mplier = mplier + matches.length;
569
+ }
570
+ posDiffPos = insertTag.length + 2 + (mplier*4);
571
+ }
572
+ }
573
+
574
+ // swap in and out the simple tags around a selection like bold
575
+ else {
576
+
577
+ mplier = 1; // the multiplier for the tag length
578
+ re_start = new RegExp('^\\' + button.tagStart,'g');
579
+ re_end = new RegExp('\\' + button.tagEnd + '$','g');
580
+ re_middle = new RegExp('\\' + button.tagEnd + newlineReplaceRegexDirty + '\\' + button.tagStart,'g');
581
+ if (selectedText.match(re_start) && selectedText.match(re_end)) {
582
+ finalText = beginningText
583
+ + newlineStart
584
+ + selectedText.replace(re_start,'').replace(re_end,'').replace(re_middle,newlineReplaceClean)
585
+ + newlineEnd
586
+ + followupText;
587
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
588
+ mplier = mplier + matches.length;
589
+ }
590
+ posDiffNeg = button.tagStart.length*mplier + button.tagEnd.length*mplier;
591
+ }
592
+ else {
593
+ finalText = beginningText
594
+ + newlineStart
595
+ + button.tagStart
596
+ + selectedText.replace(newlineReplaceRegexClean,button.tagEnd + newlineReplaceClean + button.tagStart)
597
+ + button.tagEnd
598
+ + newlineEnd
599
+ + followupText;
600
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
601
+ mplier = mplier + matches.length;
602
+ }
603
+ posDiffPos = (button.tagStart.length*mplier) + (button.tagEnd.length*mplier);
604
+ }
605
+ }
606
+
607
+ cursorPos += button.tagStart.length + button.tagEnd.length;
608
+
609
+ }
610
+
611
+ // just swap in and out single values, e.g. someone clicks b they'll get a *
612
+ else {
613
+ var buttonStart = '';
614
+ var buttonEnd = '';
615
+ var re_p = new RegExp('(\\<|\\>|\\=|\\<\\>|\\(|\\))','g');
616
+ var re_h = new RegExp('^(h1|h2|h3|h4|h5|h6|p|bq)','g');
617
+ if (!this.checkOpenTags(button) || button.tagEnd == '') { // opening tag
618
+
619
+ if (button.tagStart.match(re_h)) {
620
+ buttonStart = button.tagStart + '. ';
621
+ }
622
+ else {
623
+ buttonStart = button.tagStart;
624
+ }
625
+ if (button.tagStart.match(re_p)) { // make sure that invoking block modifiers don't do anything
626
+ finalText = beginningText
627
+ + followupText;
628
+ cursorPos = startPos;
629
+ }
630
+ else {
631
+ finalText = beginningText
632
+ + buttonStart
633
+ + followupText;
634
+ this.addTag(button);
635
+ cursorPos = startPos + buttonStart.length;
636
+ }
637
+
638
+ }
639
+ else { // closing tag
640
+ if (button.tagStart.match(re_p)) {
641
+ buttonEnd = '\n\n';
642
+ }
643
+ else if (button.tagStart.match(re_h)) {
644
+ buttonEnd = '\n\n';
645
+ }
646
+ else {
647
+ buttonEnd = button.tagEnd
648
+ }
649
+ finalText = beginningText
650
+ + button.tagEnd
651
+ + followupText;
652
+ this.removeTag(button);
653
+ cursorPos = startPos + button.tagEnd.length;
654
+ }
655
+ }
656
+
657
+ // set the appropriate DOM value with the final text
658
+ if (FF == true) {
659
+ myField.value = finalText;
660
+ myField.scrollTop = scrollTop;
661
+ }
662
+ else {
663
+ sel.text = finalText;
664
+ }
665
+
666
+ // build up the selection capture, doesn't work in IE
667
+ if (textSelected) {
668
+ myField.selectionStart = startPos + newlineStartPos;
669
+ myField.selectionEnd = endPos + posDiffPos - posDiffNeg - newlineEndPos;
670
+ //alert('s: ' + myField.selectionStart + ' e: ' + myField.selectionEnd + ' sp: ' + startPos + ' ep: ' + endPos + ' pdp: ' + posDiffPos + ' pdn: ' + posDiffNeg)
671
+ }
672
+ else {
673
+ myField.selectionStart = cursorPos;
674
+ myField.selectionEnd = cursorPos;
675
+ }
676
+ } // end insertTag
677
+ }; // end class
678
+
679
+ // add class methods
680
+ // Object.extend(TextileEditor, TextileEditor.Methods);
681
+ destination = TextileEditor
682
+ source = TextileEditor.Methods
683
+ for(var property in source) destination[property] = source[property];