rtextile 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.project +26 -0
  2. data/Gemfile +7 -0
  3. data/LICENSE +20 -0
  4. data/README.textile +44 -0
  5. data/Rakefile +8 -0
  6. data/lib/rails/generators/rtextile/USAGE +18 -0
  7. data/lib/rails/generators/rtextile/rtextile_generator.rb +80 -0
  8. data/lib/rails/generators/rtextile/templates/_form.html.erb +12 -0
  9. data/lib/rails/generators/rtextile/templates/_show.html.erb +3 -0
  10. data/lib/rails/generators/rtextile/templates/controller.rb +64 -0
  11. data/lib/rails/generators/rtextile/templates/edit.html.erb +10 -0
  12. data/lib/rails/generators/rtextile/templates/index.html.erb +8 -0
  13. data/lib/rails/generators/rtextile/templates/migration.rb +9 -0
  14. data/lib/rails/generators/rtextile/templates/model.rb +4 -0
  15. data/lib/rails/generators/rtextile/templates/new.html.erb +3 -0
  16. data/lib/rails/generators/rtextile/templates/schema.rb +14 -0
  17. data/lib/rails/generators/rtextile/templates/show.html.erb +8 -0
  18. data/lib/rails/generators/textile_editor_helper/USAGE +23 -0
  19. data/lib/rails/generators/textile_editor_helper/templates/background.png +0 -0
  20. data/lib/rails/generators/textile_editor_helper/templates/blockquote.png +0 -0
  21. data/lib/rails/generators/textile_editor_helper/templates/bold.png +0 -0
  22. data/lib/rails/generators/textile_editor_helper/templates/center.png +0 -0
  23. data/lib/rails/generators/textile_editor_helper/templates/h1.png +0 -0
  24. data/lib/rails/generators/textile_editor_helper/templates/h2.png +0 -0
  25. data/lib/rails/generators/textile_editor_helper/templates/h3.png +0 -0
  26. data/lib/rails/generators/textile_editor_helper/templates/h4.png +0 -0
  27. data/lib/rails/generators/textile_editor_helper/templates/h5.png +0 -0
  28. data/lib/rails/generators/textile_editor_helper/templates/h6.png +0 -0
  29. data/lib/rails/generators/textile_editor_helper/templates/indent.png +0 -0
  30. data/lib/rails/generators/textile_editor_helper/templates/italic.png +0 -0
  31. data/lib/rails/generators/textile_editor_helper/templates/justify.png +0 -0
  32. data/lib/rails/generators/textile_editor_helper/templates/left.png +0 -0
  33. data/lib/rails/generators/textile_editor_helper/templates/list_bullets.png +0 -0
  34. data/lib/rails/generators/textile_editor_helper/templates/list_numbers.png +0 -0
  35. data/lib/rails/generators/textile_editor_helper/templates/omega.png +0 -0
  36. data/lib/rails/generators/textile_editor_helper/templates/outdent.png +0 -0
  37. data/lib/rails/generators/textile_editor_helper/templates/paragraph.png +0 -0
  38. data/lib/rails/generators/textile_editor_helper/templates/right.png +0 -0
  39. data/lib/rails/generators/textile_editor_helper/templates/strikethrough.png +0 -0
  40. data/lib/rails/generators/textile_editor_helper/templates/textile-editor-config.js +22 -0
  41. data/lib/rails/generators/textile_editor_helper/templates/textile-editor.css +53 -0
  42. data/lib/rails/generators/textile_editor_helper/templates/textile-editor.js +676 -0
  43. data/lib/rails/generators/textile_editor_helper/templates/underline.png +0 -0
  44. data/lib/rails/generators/textile_editor_helper/textile_editor_helper_generator.rb +39 -0
  45. data/lib/rtextile/version.rb +3 -0
  46. data/lib/rtextile.rb +3 -0
  47. data/lib/textile_editor_helper.rb +185 -0
  48. data/rtextile.gemspec +22 -0
  49. metadata +126 -0
@@ -0,0 +1,676 @@
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 = '/images/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
+ // no clue, i'm sure it made sense at the time i wrote it
270
+ if (followupText.match(/^\n/)) {
271
+ newlineFollowup = '';
272
+ }
273
+ else {
274
+ newlineFollowup = '\n\n';
275
+ }
276
+
277
+ // first off let's check if the user is trying to mess with lists
278
+ if ((button.tagStart == ' * ') || (button.tagStart == ' # ')) {
279
+
280
+ listItems = 0; // sets up a default to be able to properly manipulate final selection
281
+
282
+ // set-up all of the regex's
283
+ re_start = new RegExp('^ (\\*|\\#) ','g');
284
+ if (button.tagStart == ' # ') {
285
+ 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
286
+ }
287
+ else {
288
+ re_tag = new RegExp(' \\* ','g');
289
+ }
290
+ re_replace = new RegExp(' (\\*|\\#) ','g');
291
+
292
+ // try to remove bullets in text copied from ms word **Mac Only!**
293
+ re_word_bullet_m_s = new RegExp('• ','g'); // mac/safari
294
+ re_word_bullet_m_f = new RegExp('∑ ','g'); // mac/firefox
295
+ selectedText = selectedText.replace(re_word_bullet_m_s,'').replace(re_word_bullet_m_f,'');
296
+
297
+ // if the selected text starts with one of the tags we're working with...
298
+ if (selectedText.match(re_start)) {
299
+
300
+ // if tag that begins the selection matches the one clicked, remove them all
301
+ if (selectedText.match(re_tag)) {
302
+ finalText = beginningText
303
+ + newlineStart
304
+ + selectedText.replace(re_replace,'')
305
+ + newlineEnd
306
+ + followupText;
307
+ if (matches = selectedText.match(/ (\*|\#) /g)) {
308
+ listItems = matches.length;
309
+ }
310
+ posDiffNeg = listItems*3; // how many list items were there because that's 3 spaces to remove from final selection
311
+ }
312
+
313
+ // else replace the current tag type with the selected tag type
314
+ else {
315
+ finalText = beginningText
316
+ + newlineStart
317
+ + selectedText.replace(re_replace,button.tagStart)
318
+ + newlineEnd
319
+ + followupText;
320
+ }
321
+ }
322
+
323
+ // else try to create the list type
324
+ // NOTE: the items in a list will only be replaced if a newline starts with some character, not a space
325
+ else {
326
+ finalText = beginningText
327
+ + newlineStart
328
+ + button.tagStart
329
+ + selectedText.replace(newlineReplaceRegexClean,newlineReplaceClean + button.tagStart).replace(/\n(\S)/g,'\n' + button.tagStart + '$1')
330
+ + newlineEnd
331
+ + followupText;
332
+ if (matches = selectedText.match(/\n(\S)/g)) {
333
+ listItems = matches.length;
334
+ }
335
+ posDiffPos = 3 + listItems*3;
336
+ }
337
+ }
338
+
339
+ // now lets look and see if the user is trying to muck with a block or block modifier
340
+ else if (button.tagStart.match(/^(h1|h2|h3|h4|h5|h6|bq|p|\>|\<\>|\<|\=|\(|\))/g)) {
341
+
342
+ var insertTag = '';
343
+ var insertModifier = '';
344
+ var tagPartBlock = '';
345
+ var tagPartModifier = '';
346
+ var tagPartModifierOrig = ''; // ugly hack but it's late
347
+ var drawSwitch = '';
348
+ var captureIndentStart = false;
349
+ var captureListStart = false;
350
+ var periodAddition = '\\. ';
351
+ var periodAdditionClean = '. ';
352
+ var listItemsAddition = 0;
353
+
354
+ var re_list_items = new RegExp('(\\*+|\\#+)','g'); // need this regex later on when checking indentation of lists
355
+
356
+ var re_block_modifier = new RegExp('^(h1|h2|h3|h4|h5|h6|bq|p| [\\*]{1,} | [\\#]{1,} |)(\\>|\\<\\>|\\<|\\=|[\\(]{1,}|[\\)]{1,6}|)','g');
357
+ if (tagPartMatches = re_block_modifier.exec(selectedText)) {
358
+ tagPartBlock = tagPartMatches[1];
359
+ tagPartModifier = tagPartMatches[2];
360
+ tagPartModifierOrig = tagPartMatches[2];
361
+ tagPartModifierOrig = tagPartModifierOrig.replace(/\(/g,"\\(");
362
+ }
363
+
364
+ // if tag already up is the same as the tag provided replace the whole tag
365
+ if (tagPartBlock == button.tagStart) {
366
+ insertTag = tagPartBlock + tagPartModifierOrig; // use Orig because it's escaped for regex
367
+ drawSwitch = 0;
368
+ }
369
+ // else if let's check to add/remove block modifier
370
+ else if ((tagPartModifier == button.tagStart) || (newm = tagPartModifier.match(/[\(]{2,}/g))) {
371
+ if ((button.tagStart == '(') || (button.tagStart == ')')) {
372
+ var indentLength = tagPartModifier.length;
373
+ if (button.tagStart == '(') {
374
+ indentLength = indentLength + 1;
375
+ }
376
+ else {
377
+ indentLength = indentLength - 1;
378
+ }
379
+ for (var i = 0; i < indentLength; i++) {
380
+ insertModifier = insertModifier + '(';
381
+ }
382
+ insertTag = tagPartBlock + insertModifier;
383
+ }
384
+ else {
385
+ if (button.tagStart == tagPartModifier) {
386
+ insertTag = tagPartBlock;
387
+ } // going to rely on the default empty insertModifier
388
+ else {
389
+
390
+ if (button.tagStart.match(/(\>|\<\>|\<|\=)/g)) {
391
+ insertTag = tagPartBlock + button.tagStart;
392
+ }
393
+ else {
394
+ insertTag = button.tagStart + tagPartModifier;
395
+ }
396
+ }
397
+
398
+ }
399
+ drawSwitch = 1;
400
+ }
401
+ // indentation of list items
402
+ else if (listPartMatches = re_list_items.exec(tagPartBlock)) {
403
+ var listTypeMatch = listPartMatches[1];
404
+ var indentLength = tagPartBlock.length - 2;
405
+ var listInsert = '';
406
+ if (button.tagStart == '(') {
407
+ indentLength = indentLength + 1;
408
+ }
409
+ else {
410
+ indentLength = indentLength - 1;
411
+ }
412
+ if (listTypeMatch.match(/[\*]{1,}/g)) {
413
+ var listType = '*';
414
+ var listReplace = '\\*';
415
+ }
416
+ else {
417
+ var listType = '#';
418
+ var listReplace = '\\#';
419
+ }
420
+ for (var i = 0; i < indentLength; i++) {
421
+ listInsert = listInsert + listType;
422
+ }
423
+ if (listInsert != '') {
424
+ insertTag = ' ' + listInsert + ' ';
425
+ }
426
+ else {
427
+ insertTag = '';
428
+ }
429
+ tagPartBlock = tagPartBlock.replace(/(\*|\#)/g,listReplace);
430
+ drawSwitch = 1;
431
+ captureListStart = true;
432
+ periodAddition = '';
433
+ periodAdditionClean = '';
434
+ if (matches = selectedText.match(/\n\s/g)) {
435
+ listItemsAddition = matches.length;
436
+ }
437
+ }
438
+ // must be a block modification e.g. p>. to p<.
439
+ else {
440
+
441
+ // if this is a block modification/addition
442
+ if (button.tagStart.match(/(h1|h2|h3|h4|h5|h6|bq|p)/g)) {
443
+ if (tagPartBlock == '') {
444
+ drawSwitch = 2;
445
+ }
446
+ else {
447
+ drawSwitch = 1;
448
+ }
449
+
450
+ insertTag = button.tagStart + tagPartModifier;
451
+ }
452
+
453
+ // else this is a modifier modification/addition
454
+ else {
455
+ if ((tagPartModifier == '') && (tagPartBlock != '')) {
456
+ drawSwitch = 1;
457
+ }
458
+ else if (tagPartModifier == '') {
459
+ drawSwitch = 2;
460
+ }
461
+ else {
462
+ drawSwitch = 1;
463
+ }
464
+
465
+ // if no tag part block but a modifier we need at least the p tag
466
+ if (tagPartBlock == '') {
467
+ tagPartBlock = 'p';
468
+ }
469
+
470
+ //make sure to swap out outdent
471
+ if (button.tagStart == ')') {
472
+ tagPartModifier = '';
473
+ }
474
+ else {
475
+ tagPartModifier = button.tagStart;
476
+ captureIndentStart = true; // ugly hack to fix issue with proper selection handling
477
+ }
478
+
479
+ insertTag = tagPartBlock + tagPartModifier;
480
+ }
481
+ }
482
+
483
+ mplier = 0;
484
+ if (captureListStart || (tagPartModifier.match(/[\(\)]{1,}/g))) {
485
+ re_start = new RegExp(insertTag.escape + periodAddition,'g'); // for tags that mimic regex properties, parens + list tags
486
+ }
487
+ else {
488
+ re_start = new RegExp(insertTag + periodAddition,'g'); // for tags that don't, why i can't just escape everything i have no clue
489
+ }
490
+ re_old = new RegExp(tagPartBlock + tagPartModifierOrig + periodAddition,'g');
491
+ re_middle = new RegExp(newlineReplaceRegexDirty + insertTag.escape + periodAddition.escape,'g');
492
+ re_tag = new RegExp(insertTag.escape + periodAddition.escape,'g');
493
+
494
+ // *************************************************************************************************************************
495
+ // this is where everything gets swapped around or inserted, bullets and single options have their own if/else statements
496
+ // *************************************************************************************************************************
497
+ if ((drawSwitch == 0) || (drawSwitch == 1)) {
498
+ if (drawSwitch == 0) { // completely removing a tag
499
+ finalText = beginningText
500
+ + newlineStart
501
+ + selectedText.replace(re_start,'').replace(re_middle,newlineReplaceClean)
502
+ + newlineEnd
503
+ + followupText;
504
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
505
+ mplier = mplier + matches.length;
506
+ }
507
+ posDiffNeg = insertTag.length + 2 + (mplier*4);
508
+ }
509
+ else { // modifying a tag, though we do delete bullets here
510
+ finalText = beginningText
511
+ + newlineStart
512
+ + selectedText.replace(re_old,insertTag + periodAdditionClean)
513
+ + newlineEnd
514
+ + followupText;
515
+
516
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
517
+ mplier = mplier + matches.length;
518
+ }
519
+ // figure out the length of various elements to modify the selection position
520
+ if (captureIndentStart) { // need to double-check that this wasn't the first indent
521
+ tagPreviousLength = tagPartBlock.length;
522
+ tagCurrentLength = insertTag.length;
523
+ }
524
+ else if (captureListStart) { // if this is a list we're manipulating
525
+ if (button.tagStart == '(') { // if indenting
526
+ tagPreviousLength = listTypeMatch.length + 2;
527
+ tagCurrentLength = insertTag.length + listItemsAddition;
528
+ }
529
+ else if (insertTag.match(/(\*|\#)/g)) { // if removing but still has bullets
530
+ tagPreviousLength = insertTag.length + listItemsAddition;
531
+ tagCurrentLength = listTypeMatch.length;
532
+ }
533
+ else { // if removing last bullet
534
+ tagPreviousLength = insertTag.length + listItemsAddition;
535
+ tagCurrentLength = listTypeMatch.length - (3*listItemsAddition) - 1;
536
+ }
537
+ }
538
+ else { // everything else
539
+ tagPreviousLength = tagPartBlock.length + tagPartModifier.length;
540
+ tagCurrentLength = insertTag.length;
541
+ }
542
+ if (tagCurrentLength > tagPreviousLength) {
543
+ posDiffPos = (tagCurrentLength - tagPreviousLength) + (mplier*(tagCurrentLength - tagPreviousLength));
544
+ }
545
+ else {
546
+ posDiffNeg = (tagPreviousLength - tagCurrentLength) + (mplier*(tagPreviousLength - tagCurrentLength));
547
+ }
548
+ }
549
+ }
550
+ else { // for adding tags other then bullets (have their own statement)
551
+ finalText = beginningText
552
+ + newlineStart
553
+ + insertTag + '. '
554
+ + selectedText.replace(newlineReplaceRegexClean,button.tagEnd + '\n' + insertTag + '. ')
555
+ + newlineFollowup
556
+ + newlineEnd
557
+ + followupText;
558
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
559
+ mplier = mplier + matches.length;
560
+ }
561
+ posDiffPos = insertTag.length + 2 + (mplier*4);
562
+ }
563
+ }
564
+
565
+ // swap in and out the simple tags around a selection like bold
566
+ else {
567
+
568
+ mplier = 1; // the multiplier for the tag length
569
+ re_start = new RegExp('^\\' + button.tagStart,'g');
570
+ re_end = new RegExp('\\' + button.tagEnd + '$','g');
571
+ re_middle = new RegExp('\\' + button.tagEnd + newlineReplaceRegexDirty + '\\' + button.tagStart,'g');
572
+ if (selectedText.match(re_start) && selectedText.match(re_end)) {
573
+ finalText = beginningText
574
+ + newlineStart
575
+ + selectedText.replace(re_start,'').replace(re_end,'').replace(re_middle,newlineReplaceClean)
576
+ + newlineEnd
577
+ + followupText;
578
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
579
+ mplier = mplier + matches.length;
580
+ }
581
+ posDiffNeg = button.tagStart.length*mplier + button.tagEnd.length*mplier;
582
+ }
583
+ else {
584
+ finalText = beginningText
585
+ + newlineStart
586
+ + button.tagStart
587
+ + selectedText.replace(newlineReplaceRegexClean,button.tagEnd + newlineReplaceClean + button.tagStart)
588
+ + button.tagEnd
589
+ + newlineEnd
590
+ + followupText;
591
+ if (matches = selectedText.match(newlineReplaceRegexClean)) {
592
+ mplier = mplier + matches.length;
593
+ }
594
+ posDiffPos = (button.tagStart.length*mplier) + (button.tagEnd.length*mplier);
595
+ }
596
+ }
597
+
598
+ cursorPos += button.tagStart.length + button.tagEnd.length;
599
+
600
+ }
601
+
602
+ // just swap in and out single values, e.g. someone clicks b they'll get a *
603
+ else {
604
+ var buttonStart = '';
605
+ var buttonEnd = '';
606
+ var re_p = new RegExp('(\\<|\\>|\\=|\\<\\>|\\(|\\))','g');
607
+ var re_h = new RegExp('^(h1|h2|h3|h4|h5|h6|p|bq)','g');
608
+ if (!this.checkOpenTags(button) || button.tagEnd == '') { // opening tag
609
+
610
+ if (button.tagStart.match(re_h)) {
611
+ buttonStart = button.tagStart + '. ';
612
+ }
613
+ else {
614
+ buttonStart = button.tagStart;
615
+ }
616
+ if (button.tagStart.match(re_p)) { // make sure that invoking block modifiers don't do anything
617
+ finalText = beginningText
618
+ + followupText;
619
+ cursorPos = startPos;
620
+ }
621
+ else {
622
+ finalText = beginningText
623
+ + buttonStart
624
+ + followupText;
625
+ this.addTag(button);
626
+ cursorPos = startPos + buttonStart.length;
627
+ }
628
+
629
+ }
630
+ else { // closing tag
631
+ if (button.tagStart.match(re_p)) {
632
+ buttonEnd = '\n\n';
633
+ }
634
+ else if (button.tagStart.match(re_h)) {
635
+ buttonEnd = '\n\n';
636
+ }
637
+ else {
638
+ buttonEnd = button.tagEnd
639
+ }
640
+ finalText = beginningText
641
+ + button.tagEnd
642
+ + followupText;
643
+ this.removeTag(button);
644
+ cursorPos = startPos + button.tagEnd.length;
645
+ }
646
+ }
647
+
648
+ // set the appropriate DOM value with the final text
649
+ if (FF == true) {
650
+ myField.value = finalText;
651
+ myField.scrollTop = scrollTop;
652
+ }
653
+ else {
654
+ sel.text = finalText;
655
+ }
656
+
657
+ // build up the selection capture, doesn't work in IE
658
+ if (textSelected) {
659
+ myField.selectionStart = startPos + newlineStartPos;
660
+ myField.selectionEnd = endPos + posDiffPos - posDiffNeg - newlineEndPos;
661
+ //alert('s: ' + myField.selectionStart + ' e: ' + myField.selectionEnd + ' sp: ' + startPos + ' ep: ' + endPos + ' pdp: ' + posDiffPos + ' pdn: ' + posDiffNeg)
662
+ }
663
+ else {
664
+ myField.selectionStart = cursorPos;
665
+ myField.selectionEnd = cursorPos;
666
+ }
667
+ } // end insertTag
668
+ }; // end class
669
+
670
+ // add class methods
671
+ // Object.extend(TextileEditor, TextileEditor.Methods);
672
+ destination = TextileEditor
673
+ source = TextileEditor.Methods
674
+ for(var property in source) destination[property] = source[property];
675
+
676
+ document.write('<script src="/javascripts/textile-editor-config.js" type="text/javascript"></script>');