textile_editor_helper 0.0.12

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