rtextile 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.project +26 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.textile +44 -0
- data/Rakefile +8 -0
- data/lib/rails/generators/rtextile/USAGE +18 -0
- data/lib/rails/generators/rtextile/rtextile_generator.rb +80 -0
- data/lib/rails/generators/rtextile/templates/_form.html.erb +12 -0
- data/lib/rails/generators/rtextile/templates/_show.html.erb +3 -0
- data/lib/rails/generators/rtextile/templates/controller.rb +64 -0
- data/lib/rails/generators/rtextile/templates/edit.html.erb +10 -0
- data/lib/rails/generators/rtextile/templates/index.html.erb +8 -0
- data/lib/rails/generators/rtextile/templates/migration.rb +9 -0
- data/lib/rails/generators/rtextile/templates/model.rb +4 -0
- data/lib/rails/generators/rtextile/templates/new.html.erb +3 -0
- data/lib/rails/generators/rtextile/templates/schema.rb +14 -0
- data/lib/rails/generators/rtextile/templates/show.html.erb +8 -0
- data/lib/rails/generators/textile_editor_helper/USAGE +23 -0
- data/lib/rails/generators/textile_editor_helper/templates/background.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/blockquote.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/bold.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/center.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/h1.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/h2.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/h3.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/h4.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/h5.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/h6.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/indent.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/italic.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/justify.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/left.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/list_bullets.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/list_numbers.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/omega.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/outdent.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/paragraph.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/right.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/strikethrough.png +0 -0
- data/lib/rails/generators/textile_editor_helper/templates/textile-editor-config.js +22 -0
- data/lib/rails/generators/textile_editor_helper/templates/textile-editor.css +53 -0
- data/lib/rails/generators/textile_editor_helper/templates/textile-editor.js +676 -0
- data/lib/rails/generators/textile_editor_helper/templates/underline.png +0 -0
- data/lib/rails/generators/textile_editor_helper/textile_editor_helper_generator.rb +39 -0
- data/lib/rtextile/version.rb +3 -0
- data/lib/rtextile.rb +3 -0
- data/lib/textile_editor_helper.rb +185 -0
- data/rtextile.gemspec +22 -0
- 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>');
|
Binary file
|