bhf 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2440 +0,0 @@
1
- var Attacklab = Attacklab || {};
2
-
3
- Attacklab.wmdBase = function(){
4
-
5
- // A few handy aliases for readability.
6
- var wmd = top.Attacklab;
7
- var doc = top.document;
8
- var re = top.RegExp;
9
- var nav = top.navigator;
10
-
11
- // Some namespaces.
12
- wmd.Util = {};
13
- wmd.Position = {};
14
- wmd.Command = {};
15
- wmd.Global = {};
16
-
17
- var util = wmd.Util;
18
- var position = wmd.Position;
19
- var command = wmd.Command;
20
- var global = wmd.Global;
21
-
22
-
23
- // Used to work around some browser bugs where we can't use feature testing.
24
- global.isIE = /msie/.test(nav.userAgent.toLowerCase());
25
- global.isIE_5or6 = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase());
26
- global.isIE_7plus = global.isIE && !global.isIE_5or6;
27
- global.isOpera = /opera/.test(nav.userAgent.toLowerCase());
28
- global.isKonqueror = /konqueror/.test(nav.userAgent.toLowerCase());
29
-
30
-
31
- // -------------------------------------------------------------------
32
- // YOUR CHANGES GO HERE
33
- //
34
- // I've tried to localize the things you are likely to change to
35
- // this area.
36
- // -------------------------------------------------------------------
37
-
38
- // The text that appears on the upper part of the dialog box when
39
- // entering links.
40
- var imageDialogText = "<p style='margin-top: 0px'><b>Enter the image URL.</b></p><p>You can also add a title, which will be displayed as a tool tip.</p><p>Example:<br />http://wmd-editor.comcloud1.jpg \"Optional title\"</p>";
41
- var linkDialogText = "<p style='margin-top: 0px'><b>Enter the web address.</b></p><p>You can also add a title, which will be displayed as a tool tip.</p><p>Example:<br />http://wmd-editor.com/ \"Optional title\"</p>";
42
-
43
- // The default text that appears in the dialog input box when entering
44
- // links.
45
- var imageDefaultText = "http://";
46
- var linkDefaultText = "http://";
47
-
48
- // The location of your button images relative to the base directory.
49
- var imageDirectory = "images/";
50
-
51
- // Some intervals in ms. These can be adjusted to reduce the control's load.
52
- var previewPollInterval = 500;
53
- var pastePollInterval = 100;
54
-
55
- // The link and title for the help button
56
- var helpLink = "http://wmd-editor.com/";
57
- var helpHoverTitle = "WMD website";
58
- var helpTarget = "_blank";
59
-
60
- // -------------------------------------------------------------------
61
- // END OF YOUR CHANGES
62
- // -------------------------------------------------------------------
63
-
64
- // A collection of the important regions on the page.
65
- // Cached so we don't have to keep traversing the DOM.
66
- wmd.PanelCollection = function(){
67
- this.buttonBar = doc.getElementById("wmd-button-bar");
68
- this.preview = doc.getElementById("wmd-preview");
69
- this.output = doc.getElementById("wmd-output");
70
- this.input = doc.getElementById("wmd-input");
71
- };
72
-
73
- // This PanelCollection object can't be filled until after the page
74
- // has loaded.
75
- wmd.panels = undefined;
76
-
77
- // Internet explorer has problems with CSS sprite buttons that use HTML
78
- // lists. When you click on the background image "button", IE will
79
- // select the non-existent link text and discard the selection in the
80
- // textarea. The solution to this is to cache the textarea selection
81
- // on the button's mousedown event and set a flag. In the part of the
82
- // code where we need to grab the selection, we check for the flag
83
- // and, if it's set, use the cached area instead of querying the
84
- // textarea.
85
- //
86
- // This ONLY affects Internet Explorer (tested on versions 6, 7
87
- // and 8) and ONLY on button clicks. Keyboard shortcuts work
88
- // normally since the focus never leaves the textarea.
89
- wmd.ieCachedRange = null; // cached textarea selection
90
- wmd.ieRetardedClick = false; // flag
91
-
92
- // Returns true if the DOM element is visible, false if it's hidden.
93
- // Checks if display is anything other than none.
94
- util.isVisible = function (elem) {
95
-
96
- if (window.getComputedStyle) {
97
- // Most browsers
98
- return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none";
99
- }
100
- else if (elem.currentStyle) {
101
- // IE
102
- return elem.currentStyle["display"] !== "none";
103
- }
104
- };
105
-
106
-
107
- // Adds a listener callback to a DOM element which is fired on a specified
108
- // event.
109
- util.addEvent = function(elem, event, listener){
110
- if (elem.attachEvent) {
111
- // IE only. The "on" is mandatory.
112
- elem.attachEvent("on" + event, listener);
113
- }
114
- else {
115
- // Other browsers.
116
- elem.addEventListener(event, listener, false);
117
- }
118
- };
119
-
120
-
121
- // Removes a listener callback from a DOM element which is fired on a specified
122
- // event.
123
- util.removeEvent = function(elem, event, listener){
124
- if (elem.detachEvent) {
125
- // IE only. The "on" is mandatory.
126
- elem.detachEvent("on" + event, listener);
127
- }
128
- else {
129
- // Other browsers.
130
- elem.removeEventListener(event, listener, false);
131
- }
132
- };
133
-
134
- // Converts \r\n and \r to \n.
135
- util.fixEolChars = function(text){
136
- text = text.replace(/\r\n/g, "\n");
137
- text = text.replace(/\r/g, "\n");
138
- return text;
139
- };
140
-
141
- // Extends a regular expression. Returns a new RegExp
142
- // using pre + regex + post as the expression.
143
- // Used in a few functions where we have a base
144
- // expression and we want to pre- or append some
145
- // conditions to it (e.g. adding "$" to the end).
146
- // The flags are unchanged.
147
- //
148
- // regex is a RegExp, pre and post are strings.
149
- util.extendRegExp = function(regex, pre, post){
150
-
151
- if (pre === null || pre === undefined)
152
- {
153
- pre = "";
154
- }
155
- if(post === null || post === undefined)
156
- {
157
- post = "";
158
- }
159
-
160
- var pattern = regex.toString();
161
- var flags = "";
162
-
163
- // Replace the flags with empty space and store them.
164
- // Technically, this can match incorrect flags like "gmm".
165
- var result = pattern.match(/\/([gim]*)$/);
166
- if (result === null) {
167
- flags = result[0];
168
- }
169
- else {
170
- flags = "";
171
- }
172
-
173
- // Remove the flags and slash delimiters from the regular expression.
174
- pattern = pattern.replace(/(^\/|\/[gim]*$)/g, "");
175
- pattern = pre + pattern + post;
176
-
177
- return new RegExp(pattern, flags);
178
- }
179
-
180
-
181
- // Sets the image for a button passed to the WMD editor.
182
- // Returns a new element with the image attached.
183
- // Adds several style properties to the image.
184
- util.createImage = function(img){
185
-
186
- var imgPath = imageDirectory + img;
187
-
188
- var elem = doc.createElement("img");
189
- elem.className = "wmd-button";
190
- elem.src = imgPath;
191
-
192
- return elem;
193
- };
194
-
195
-
196
- // This simulates a modal dialog box and asks for the URL when you
197
- // click the hyperlink or image buttons.
198
- //
199
- // text: The html for the input box.
200
- // defaultInputText: The default value that appears in the input box.
201
- // makeLinkMarkdown: The function which is executed when the prompt is dismissed, either via OK or Cancel
202
- util.prompt = function(text, defaultInputText, makeLinkMarkdown){
203
-
204
- // These variables need to be declared at this level since they are used
205
- // in multiple functions.
206
- var dialog; // The dialog box.
207
- var background; // The background beind the dialog box.
208
- var input; // The text box where you enter the hyperlink.
209
-
210
-
211
- if (defaultInputText === undefined) {
212
- defaultInputText = "";
213
- }
214
-
215
- // Used as a keydown event handler. Esc dismisses the prompt.
216
- // Key code 27 is ESC.
217
- var checkEscape = function(key){
218
- var code = (key.charCode || key.keyCode);
219
- if (code === 27) {
220
- close(true);
221
- }
222
- };
223
-
224
- // Dismisses the hyperlink input box.
225
- // isCancel is true if we don't care about the input text.
226
- // isCancel is false if we are going to keep the text.
227
- var close = function(isCancel){
228
- util.removeEvent(doc.body, "keydown", checkEscape);
229
- var text = input.value;
230
-
231
- if (isCancel){
232
- text = null;
233
- }
234
- else{
235
- // Fixes common pasting errors.
236
- text = text.replace('http://http://', 'http://');
237
- text = text.replace('http://https://', 'https://');
238
- text = text.replace('http://ftp://', 'ftp://');
239
-
240
- if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('https://') === -1) {
241
- text = 'http://' + text;
242
- }
243
- }
244
-
245
- dialog.parentNode.removeChild(dialog);
246
- background.parentNode.removeChild(background);
247
- makeLinkMarkdown(text);
248
- return false;
249
- };
250
-
251
- // Creates the background behind the hyperlink text entry box.
252
- // Most of this has been moved to CSS but the div creation and
253
- // browser-specific hacks remain here.
254
- var createBackground = function(){
255
-
256
- background = doc.createElement("div");
257
- background.className = "wmd-prompt-background";
258
- style = background.style;
259
- style.position = "absolute";
260
- style.top = "0";
261
-
262
- style.zIndex = "1000";
263
-
264
- // Some versions of Konqueror don't support transparent colors
265
- // so we make the whole window transparent.
266
- //
267
- // Is this necessary on modern konqueror browsers?
268
- if (global.isKonqueror){
269
- style.backgroundColor = "transparent";
270
- }
271
- else if (global.isIE){
272
- style.filter = "alpha(opacity=50)";
273
- }
274
- else {
275
- style.opacity = "0.5";
276
- }
277
-
278
- var pageSize = position.getPageSize();
279
- style.height = pageSize[1] + "px";
280
-
281
- if(global.isIE){
282
- style.left = doc.documentElement.scrollLeft;
283
- style.width = doc.documentElement.clientWidth;
284
- }
285
- else {
286
- style.left = "0";
287
- style.width = "100%";
288
- }
289
-
290
- doc.body.appendChild(background);
291
- };
292
-
293
- // Create the text input box form/window.
294
- var createDialog = function(){
295
-
296
- // The main dialog box.
297
- dialog = doc.createElement("div");
298
- dialog.className = "wmd-prompt-dialog";
299
- dialog.style.padding = "10px;";
300
- dialog.style.position = "fixed";
301
- dialog.style.width = "400px";
302
- dialog.style.zIndex = "1001";
303
-
304
- // The dialog text.
305
- var question = doc.createElement("div");
306
- question.innerHTML = text;
307
- question.style.padding = "5px";
308
- dialog.appendChild(question);
309
-
310
- // The web form container for the text box and buttons.
311
- var form = doc.createElement("form");
312
- form.onsubmit = function(){ return close(false); };
313
- style = form.style;
314
- style.padding = "0";
315
- style.margin = "0";
316
- style.cssFloat = "left";
317
- style.width = "100%";
318
- style.textAlign = "center";
319
- style.position = "relative";
320
- dialog.appendChild(form);
321
-
322
- // The input text box
323
- input = doc.createElement("input");
324
- input.type = "text";
325
- input.value = defaultInputText;
326
- style = input.style;
327
- style.display = "block";
328
- style.width = "80%";
329
- style.marginLeft = style.marginRight = "auto";
330
- form.appendChild(input);
331
-
332
- // The ok button
333
- var okButton = doc.createElement("input");
334
- okButton.type = "button";
335
- okButton.onclick = function(){ return close(false); };
336
- okButton.value = "OK";
337
- style = okButton.style;
338
- style.margin = "10px";
339
- style.display = "inline";
340
- style.width = "7em";
341
-
342
-
343
- // The cancel button
344
- var cancelButton = doc.createElement("input");
345
- cancelButton.type = "button";
346
- cancelButton.onclick = function(){ return close(true); };
347
- cancelButton.value = "Cancel";
348
- style = cancelButton.style;
349
- style.margin = "10px";
350
- style.display = "inline";
351
- style.width = "7em";
352
-
353
- // The order of these buttons is different on macs.
354
- if (/mac/.test(nav.platform.toLowerCase())) {
355
- form.appendChild(cancelButton);
356
- form.appendChild(okButton);
357
- }
358
- else {
359
- form.appendChild(okButton);
360
- form.appendChild(cancelButton);
361
- }
362
-
363
- util.addEvent(doc.body, "keydown", checkEscape);
364
- dialog.style.top = "50%";
365
- dialog.style.left = "50%";
366
- dialog.style.display = "block";
367
- if(global.isIE_5or6){
368
- dialog.style.position = "absolute";
369
- dialog.style.top = doc.documentElement.scrollTop + 200 + "px";
370
- dialog.style.left = "50%";
371
- }
372
- doc.body.appendChild(dialog);
373
-
374
- // This has to be done AFTER adding the dialog to the form if you
375
- // want it to be centered.
376
- dialog.style.marginTop = -(position.getHeight(dialog) / 2) + "px";
377
- dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + "px";
378
-
379
- };
380
-
381
- createBackground();
382
-
383
- // Why is this in a zero-length timeout?
384
- // Is it working around a browser bug?
385
- top.setTimeout(function(){
386
-
387
- createDialog();
388
-
389
- var defTextLen = defaultInputText.length;
390
- if (input.selectionStart !== undefined) {
391
- input.selectionStart = 0;
392
- input.selectionEnd = defTextLen;
393
- }
394
- else if (input.createTextRange) {
395
- var range = input.createTextRange();
396
- range.collapse(false);
397
- range.moveStart("character", -defTextLen);
398
- range.moveEnd("character", defTextLen);
399
- range.select();
400
- }
401
-
402
- input.focus();
403
- }, 0);
404
- };
405
-
406
-
407
- // UNFINISHED
408
- // The assignment in the while loop makes jslint cranky.
409
- // I'll change it to a better loop later.
410
- position.getTop = function(elem, isInner){
411
- var result = elem.offsetTop;
412
- if (!isInner) {
413
- while (elem = elem.offsetParent) {
414
- result += elem.offsetTop;
415
- }
416
- }
417
- return result;
418
- };
419
-
420
- position.getHeight = function (elem) {
421
- return elem.offsetHeight || elem.scrollHeight;
422
- };
423
-
424
- position.getWidth = function (elem) {
425
- return elem.offsetWidth || elem.scrollWidth;
426
- };
427
-
428
- position.getPageSize = function(){
429
-
430
- var scrollWidth, scrollHeight;
431
- var innerWidth, innerHeight;
432
-
433
- // It's not very clear which blocks work with which browsers.
434
- if(self.innerHeight && self.scrollMaxY){
435
- scrollWidth = doc.body.scrollWidth;
436
- scrollHeight = self.innerHeight + self.scrollMaxY;
437
- }
438
- else if(doc.body.scrollHeight > doc.body.offsetHeight){
439
- scrollWidth = doc.body.scrollWidth;
440
- scrollHeight = doc.body.scrollHeight;
441
- }
442
- else{
443
- scrollWidth = doc.body.offsetWidth;
444
- scrollHeight = doc.body.offsetHeight;
445
- }
446
-
447
- if(self.innerHeight){
448
- // Non-IE browser
449
- innerWidth = self.innerWidth;
450
- innerHeight = self.innerHeight;
451
- }
452
- else if(doc.documentElement && doc.documentElement.clientHeight){
453
- // Some versions of IE (IE 6 w/ a DOCTYPE declaration)
454
- innerWidth = doc.documentElement.clientWidth;
455
- innerHeight = doc.documentElement.clientHeight;
456
- }
457
- else if(doc.body){
458
- // Other versions of IE
459
- innerWidth = doc.body.clientWidth;
460
- innerHeight = doc.body.clientHeight;
461
- }
462
-
463
- var maxWidth = Math.max(scrollWidth, innerWidth);
464
- var maxHeight = Math.max(scrollHeight, innerHeight);
465
- return [maxWidth, maxHeight, innerWidth, innerHeight];
466
- };
467
-
468
- // Watches the input textarea, polling at an interval and runs
469
- // a callback function if anything has changed.
470
- wmd.inputPoller = function(callback, interval){
471
-
472
- var pollerObj = this;
473
- var inputArea = wmd.panels.input;
474
-
475
- // Stored start, end and text. Used to see if there are changes to the input.
476
- var lastStart;
477
- var lastEnd;
478
- var markdown;
479
-
480
- var killHandle; // Used to cancel monitoring on destruction.
481
- // Checks to see if anything has changed in the textarea.
482
- // If so, it runs the callback.
483
- this.tick = function(){
484
-
485
- if (!util.isVisible(inputArea)) {
486
- return;
487
- }
488
-
489
- // Update the selection start and end, text.
490
- if (inputArea.selectionStart || inputArea.selectionStart === 0) {
491
- var start = inputArea.selectionStart;
492
- var end = inputArea.selectionEnd;
493
- if (start != lastStart || end != lastEnd) {
494
- lastStart = start;
495
- lastEnd = end;
496
-
497
- if (markdown != inputArea.value) {
498
- markdown = inputArea.value;
499
- return true;
500
- }
501
- }
502
- }
503
- return false;
504
- };
505
-
506
-
507
- var doTickCallback = function(){
508
-
509
- if (!util.isVisible(inputArea)) {
510
- return;
511
- }
512
-
513
- // If anything has changed, call the function.
514
- if (pollerObj.tick()) {
515
- callback();
516
- }
517
- };
518
-
519
- // Set how often we poll the textarea for changes.
520
- var assignInterval = function(){
521
- // previewPollInterval is set at the top of the namespace.
522
- killHandle = top.setInterval(doTickCallback, interval);
523
- };
524
-
525
- this.destroy = function(){
526
- top.clearInterval(killHandle);
527
- };
528
-
529
- assignInterval();
530
- };
531
-
532
- // Handles pushing and popping TextareaStates for undo/redo commands.
533
- // I should rename the stack variables to list.
534
- wmd.undoManager = function(callback){
535
-
536
- var undoObj = this;
537
- var undoStack = []; // A stack of undo states
538
- var stackPtr = 0; // The index of the current state
539
- var mode = "none";
540
- var lastState; // The last state
541
- var poller;
542
- var timer; // The setTimeout handle for cancelling the timer
543
- var inputStateObj;
544
-
545
- // Set the mode for later logic steps.
546
- var setMode = function(newMode, noSave){
547
-
548
- if (mode != newMode) {
549
- mode = newMode;
550
- if (!noSave) {
551
- saveState();
552
- }
553
- }
554
-
555
- if (!global.isIE || mode != "moving") {
556
- timer = top.setTimeout(refreshState, 1);
557
- }
558
- else {
559
- inputStateObj = null;
560
- }
561
- };
562
-
563
- var refreshState = function(){
564
- inputStateObj = new wmd.TextareaState();
565
- poller.tick();
566
- timer = undefined;
567
- };
568
-
569
- this.setCommandMode = function(){
570
- mode = "command";
571
- saveState();
572
- timer = top.setTimeout(refreshState, 0);
573
- };
574
-
575
- this.canUndo = function(){
576
- return stackPtr > 1;
577
- };
578
-
579
- this.canRedo = function(){
580
- if (undoStack[stackPtr + 1]) {
581
- return true;
582
- }
583
- return false;
584
- };
585
-
586
- // Removes the last state and restores it.
587
- this.undo = function(){
588
-
589
- if (undoObj.canUndo()) {
590
- if (lastState) {
591
- // What about setting state -1 to null or checking for undefined?
592
- lastState.restore();
593
- lastState = null;
594
- }
595
- else {
596
- undoStack[stackPtr] = new wmd.TextareaState();
597
- undoStack[--stackPtr].restore();
598
-
599
- if (callback) {
600
- callback();
601
- }
602
- }
603
- }
604
-
605
- mode = "none";
606
- wmd.panels.input.focus();
607
- refreshState();
608
- };
609
-
610
- // Redo an action.
611
- this.redo = function(){
612
-
613
- if (undoObj.canRedo()) {
614
-
615
- undoStack[++stackPtr].restore();
616
-
617
- if (callback) {
618
- callback();
619
- }
620
- }
621
-
622
- mode = "none";
623
- wmd.panels.input.focus();
624
- refreshState();
625
- };
626
-
627
- // Push the input area state to the stack.
628
- var saveState = function(){
629
-
630
- var currState = inputStateObj || new wmd.TextareaState();
631
-
632
- if (!currState) {
633
- return false;
634
- }
635
- if (mode == "moving") {
636
- if (!lastState) {
637
- lastState = currState;
638
- }
639
- return;
640
- }
641
- if (lastState) {
642
- if (undoStack[stackPtr - 1].text != lastState.text) {
643
- undoStack[stackPtr++] = lastState;
644
- }
645
- lastState = null;
646
- }
647
- undoStack[stackPtr++] = currState;
648
- undoStack[stackPtr + 1] = null;
649
- if (callback) {
650
- callback();
651
- }
652
- };
653
-
654
- var handleCtrlYZ = function(event){
655
-
656
- var handled = false;
657
-
658
- if (event.ctrlKey || event.metaKey) {
659
-
660
- // IE and Opera do not support charCode.
661
- var keyCode = event.charCode || event.keyCode;
662
- var keyCodeChar = String.fromCharCode(keyCode);
663
-
664
- switch (keyCodeChar) {
665
-
666
- case "y":
667
- undoObj.redo();
668
- handled = true;
669
- break;
670
-
671
- case "z":
672
- if (!event.shiftKey) {
673
- undoObj.undo();
674
- }
675
- else {
676
- undoObj.redo();
677
- }
678
- handled = true;
679
- break;
680
- }
681
- }
682
-
683
- if (handled) {
684
- if (event.preventDefault) {
685
- event.preventDefault();
686
- }
687
- if (top.event) {
688
- top.event.returnValue = false;
689
- }
690
- return;
691
- }
692
- };
693
-
694
- // Set the mode depending on what is going on in the input area.
695
- var handleModeChange = function(event){
696
-
697
- if (!event.ctrlKey && !event.metaKey) {
698
-
699
- var keyCode = event.keyCode;
700
-
701
- if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) {
702
- // 33 - 40: page up/dn and arrow keys
703
- // 63232 - 63235: page up/dn and arrow keys on safari
704
- setMode("moving");
705
- }
706
- else if (keyCode == 8 || keyCode == 46 || keyCode == 127) {
707
- // 8: backspace
708
- // 46: delete
709
- // 127: delete
710
- setMode("deleting");
711
- }
712
- else if (keyCode == 13) {
713
- // 13: Enter
714
- setMode("newlines");
715
- }
716
- else if (keyCode == 27) {
717
- // 27: escape
718
- setMode("escape");
719
- }
720
- else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) {
721
- // 16-20 are shift, etc.
722
- // 91: left window key
723
- // I think this might be a little messed up since there are
724
- // a lot of nonprinting keys above 20.
725
- setMode("typing");
726
- }
727
- }
728
- };
729
-
730
- var setEventHandlers = function(){
731
-
732
- util.addEvent(wmd.panels.input, "keypress", function(event){
733
- // keyCode 89: y
734
- // keyCode 90: z
735
- if ((event.ctrlKey || event.metaKey) && (event.keyCode == 89 || event.keyCode == 90)) {
736
- event.preventDefault();
737
- }
738
- });
739
-
740
- var handlePaste = function(){
741
- if (global.isIE || (inputStateObj && inputStateObj.text != wmd.panels.input.value)) {
742
- if (timer == undefined) {
743
- mode = "paste";
744
- saveState();
745
- refreshState();
746
- }
747
- }
748
- };
749
-
750
- // pastePollInterval is specified at the beginning of this namespace.
751
- poller = new wmd.inputPoller(handlePaste, pastePollInterval);
752
-
753
- util.addEvent(wmd.panels.input, "keydown", handleCtrlYZ);
754
- util.addEvent(wmd.panels.input, "keydown", handleModeChange);
755
-
756
- util.addEvent(wmd.panels.input, "mousedown", function(){
757
- setMode("moving");
758
- });
759
- wmd.panels.input.onpaste = handlePaste;
760
- wmd.panels.input.ondrop = handlePaste;
761
- };
762
-
763
- var init = function(){
764
- setEventHandlers();
765
- refreshState();
766
- saveState();
767
- };
768
-
769
- this.destroy = function(){
770
- if (poller) {
771
- poller.destroy();
772
- }
773
- };
774
-
775
- init();
776
- };
777
-
778
- // I think my understanding of how the buttons and callbacks are stored in the array is incomplete.
779
- wmd.editor = function(previewRefreshCallback){
780
-
781
- if (!previewRefreshCallback) {
782
- previewRefreshCallback = function(){};
783
- }
784
-
785
- var inputBox = wmd.panels.input;
786
-
787
- var offsetHeight = 0;
788
-
789
- var editObj = this;
790
-
791
- var mainDiv;
792
- var mainSpan;
793
-
794
- var div; // This name is pretty ambiguous. I should rename this.
795
-
796
- // Used to cancel recurring events from setInterval.
797
- var creationHandle;
798
-
799
- var undoMgr; // The undo manager
800
-
801
- // Perform the button's action.
802
- var doClick = function(button){
803
-
804
- inputBox.focus();
805
-
806
- if (button.textOp) {
807
-
808
- if (undoMgr) {
809
- undoMgr.setCommandMode();
810
- }
811
-
812
- var state = new wmd.TextareaState();
813
-
814
- if (!state) {
815
- return;
816
- }
817
-
818
- var chunks = state.getChunks();
819
-
820
- // Some commands launch a "modal" prompt dialog. Javascript
821
- // can't really make a modal dialog box and the WMD code
822
- // will continue to execute while the dialog is displayed.
823
- // This prevents the dialog pattern I'm used to and means
824
- // I can't do something like this:
825
- //
826
- // var link = CreateLinkDialog();
827
- // makeMarkdownLink(link);
828
- //
829
- // Instead of this straightforward method of handling a
830
- // dialog I have to pass any code which would execute
831
- // after the dialog is dismissed (e.g. link creation)
832
- // in a function parameter.
833
- //
834
- // Yes this is awkward and I think it sucks, but there's
835
- // no real workaround. Only the image and link code
836
- // create dialogs and require the function pointers.
837
- var fixupInputArea = function(){
838
-
839
- inputBox.focus();
840
-
841
- if (chunks) {
842
- state.setChunks(chunks);
843
- }
844
-
845
- state.restore();
846
- previewRefreshCallback();
847
- };
848
-
849
- var useDefaultText = true;
850
- var noCleanup = button.textOp(chunks, fixupInputArea, useDefaultText);
851
-
852
- if(!noCleanup) {
853
- fixupInputArea();
854
- }
855
-
856
- }
857
-
858
- if (button.execute) {
859
- button.execute(editObj);
860
- }
861
- };
862
-
863
- var setUndoRedoButtonStates = function(){
864
- if(undoMgr){
865
- setupButton(document.getElementById("wmd-undo-button"), undoMgr.canUndo());
866
- setupButton(document.getElementById("wmd-redo-button"), undoMgr.canRedo());
867
- }
868
- };
869
-
870
- var setupButton = function(button, isEnabled) {
871
-
872
- var normalYShift = "0px";
873
- var disabledYShift = "-20px";
874
- var highlightYShift = "-40px";
875
-
876
- if(isEnabled) {
877
- button.style.backgroundPosition = button.XShift + " " + normalYShift;
878
- button.onmouseover = function(){
879
- this.style.backgroundPosition = this.XShift + " " + highlightYShift;
880
- };
881
-
882
- button.onmouseout = function(){
883
- this.style.backgroundPosition = this.XShift + " " + normalYShift;
884
- };
885
-
886
- // IE tries to select the background image "button" text (it's
887
- // implemented in a list item) so we have to cache the selection
888
- // on mousedown.
889
- if(global.isIE) {
890
- button.onmousedown = function() {
891
- wmd.ieRetardedClick = true;
892
- wmd.ieCachedRange = document.selection.createRange();
893
- };
894
- }
895
-
896
- if (!button.isHelp)
897
- {
898
- button.onclick = function() {
899
- if (this.onmouseout) {
900
- this.onmouseout();
901
- }
902
- doClick(this);
903
- return false;
904
- }
905
- }
906
- }
907
- else {
908
- button.style.backgroundPosition = button.XShift + " " + disabledYShift;
909
- button.onmouseover = button.onmouseout = button.onclick = function(){};
910
- }
911
- }
912
-
913
- var makeSpritedButtonRow = function(){
914
-
915
- var buttonBar = document.getElementById("wmd-button-bar");
916
-
917
- var normalYShift = "0px";
918
- var disabledYShift = "-20px";
919
- var highlightYShift = "-40px";
920
-
921
- var buttonRow = document.createElement("ul");
922
- buttonRow.id = "wmd-button-row";
923
- buttonRow = buttonBar.appendChild(buttonRow);
924
-
925
-
926
- var boldButton = document.createElement("li");
927
- boldButton.className = "wmd-button";
928
- boldButton.id = "wmd-bold-button";
929
- boldButton.title = "Strong <strong> Ctrl+B";
930
- boldButton.XShift = "0px";
931
- boldButton.textOp = command.doBold;
932
- setupButton(boldButton, true);
933
- buttonRow.appendChild(boldButton);
934
-
935
- var italicButton = document.createElement("li");
936
- italicButton.className = "wmd-button";
937
- italicButton.id = "wmd-italic-button";
938
- italicButton.title = "Emphasis <em> Ctrl+I";
939
- italicButton.XShift = "-20px";
940
- italicButton.textOp = command.doItalic;
941
- setupButton(italicButton, true);
942
- buttonRow.appendChild(italicButton);
943
-
944
- var spacer1 = document.createElement("li");
945
- spacer1.className = "wmd-spacer";
946
- spacer1.id = "wmd-spacer1";
947
- buttonRow.appendChild(spacer1);
948
-
949
- var linkButton = document.createElement("li");
950
- linkButton.className = "wmd-button";
951
- linkButton.id = "wmd-link-button";
952
- linkButton.title = "Hyperlink <a> Ctrl+L";
953
- linkButton.XShift = "-40px";
954
- linkButton.textOp = function(chunk, postProcessing, useDefaultText){
955
- return command.doLinkOrImage(chunk, postProcessing, false);
956
- };
957
- setupButton(linkButton, true);
958
- buttonRow.appendChild(linkButton);
959
-
960
- var quoteButton = document.createElement("li");
961
- quoteButton.className = "wmd-button";
962
- quoteButton.id = "wmd-quote-button";
963
- quoteButton.title = "Blockquote <blockquote> Ctrl+Q";
964
- quoteButton.XShift = "-60px";
965
- quoteButton.textOp = command.doBlockquote;
966
- setupButton(quoteButton, true);
967
- buttonRow.appendChild(quoteButton);
968
-
969
- var codeButton = document.createElement("li");
970
- codeButton.className = "wmd-button";
971
- codeButton.id = "wmd-code-button";
972
- codeButton.title = "Code Sample <pre><code> Ctrl+K";
973
- codeButton.XShift = "-80px";
974
- codeButton.textOp = command.doCode;
975
- setupButton(codeButton, true);
976
- buttonRow.appendChild(codeButton);
977
-
978
- var imageButton = document.createElement("li");
979
- imageButton.className = "wmd-button";
980
- imageButton.id = "wmd-image-button";
981
- imageButton.title = "Image <img> Ctrl+G";
982
- imageButton.XShift = "-100px";
983
- imageButton.textOp = function(chunk, postProcessing, useDefaultText){
984
- return command.doLinkOrImage(chunk, postProcessing, true);
985
- };
986
- setupButton(imageButton, true);
987
- buttonRow.appendChild(imageButton);
988
-
989
- var spacer2 = document.createElement("li");
990
- spacer2.className = "wmd-spacer";
991
- spacer2.id = "wmd-spacer2";
992
- buttonRow.appendChild(spacer2);
993
-
994
- var olistButton = document.createElement("li");
995
- olistButton.className = "wmd-button";
996
- olistButton.id = "wmd-olist-button";
997
- olistButton.title = "Numbered List <ol> Ctrl+O";
998
- olistButton.XShift = "-120px";
999
- olistButton.textOp = function(chunk, postProcessing, useDefaultText){
1000
- command.doList(chunk, postProcessing, true, useDefaultText);
1001
- };
1002
- setupButton(olistButton, true);
1003
- buttonRow.appendChild(olistButton);
1004
-
1005
- var ulistButton = document.createElement("li");
1006
- ulistButton.className = "wmd-button";
1007
- ulistButton.id = "wmd-ulist-button";
1008
- ulistButton.title = "Bulleted List <ul> Ctrl+U";
1009
- ulistButton.XShift = "-140px";
1010
- ulistButton.textOp = function(chunk, postProcessing, useDefaultText){
1011
- command.doList(chunk, postProcessing, false, useDefaultText);
1012
- };
1013
- setupButton(ulistButton, true);
1014
- buttonRow.appendChild(ulistButton);
1015
-
1016
- var headingButton = document.createElement("li");
1017
- headingButton.className = "wmd-button";
1018
- headingButton.id = "wmd-heading-button";
1019
- headingButton.title = "Heading <h1>/<h2> Ctrl+H";
1020
- headingButton.XShift = "-160px";
1021
- headingButton.textOp = command.doHeading;
1022
- setupButton(headingButton, true);
1023
- buttonRow.appendChild(headingButton);
1024
-
1025
- var hrButton = document.createElement("li");
1026
- hrButton.className = "wmd-button";
1027
- hrButton.id = "wmd-hr-button";
1028
- hrButton.title = "Horizontal Rule <hr> Ctrl+R";
1029
- hrButton.XShift = "-180px";
1030
- hrButton.textOp = command.doHorizontalRule;
1031
- setupButton(hrButton, true);
1032
- buttonRow.appendChild(hrButton);
1033
-
1034
- var spacer3 = document.createElement("li");
1035
- spacer3.className = "wmd-spacer";
1036
- spacer3.id = "wmd-spacer3";
1037
- buttonRow.appendChild(spacer3);
1038
-
1039
- var undoButton = document.createElement("li");
1040
- undoButton.className = "wmd-button";
1041
- undoButton.id = "wmd-undo-button";
1042
- undoButton.title = "Undo - Ctrl+Z";
1043
- undoButton.XShift = "-200px";
1044
- undoButton.execute = function(manager){
1045
- manager.undo();
1046
- };
1047
- setupButton(undoButton, true);
1048
- buttonRow.appendChild(undoButton);
1049
-
1050
- var redoButton = document.createElement("li");
1051
- redoButton.className = "wmd-button";
1052
- redoButton.id = "wmd-redo-button";
1053
- redoButton.title = "Redo - Ctrl+Y";
1054
- if (/win/.test(nav.platform.toLowerCase())) {
1055
- redoButton.title = "Redo - Ctrl+Y";
1056
- }
1057
- else {
1058
- // mac and other non-Windows platforms
1059
- redoButton.title = "Redo - Ctrl+Shift+Z";
1060
- }
1061
- redoButton.XShift = "-220px";
1062
- redoButton.execute = function(manager){
1063
- manager.redo();
1064
- };
1065
- setupButton(redoButton, true);
1066
- buttonRow.appendChild(redoButton);
1067
-
1068
- var helpButton = document.createElement("li");
1069
- helpButton.className = "wmd-button";
1070
- helpButton.id = "wmd-help-button";
1071
- helpButton.XShift = "-240px";
1072
- helpButton.isHelp = true;
1073
-
1074
- var helpAnchor = document.createElement("a");
1075
- helpAnchor.href = helpLink;
1076
- helpAnchor.target = helpTarget
1077
- helpAnchor.title = helpHoverTitle;
1078
- helpButton.appendChild(helpAnchor);
1079
-
1080
- setupButton(helpButton, true);
1081
- buttonRow.appendChild(helpButton);
1082
-
1083
- setUndoRedoButtonStates();
1084
- }
1085
-
1086
- var setupEditor = function(){
1087
-
1088
- if (/\?noundo/.test(doc.location.href)) {
1089
- wmd.nativeUndo = true;
1090
- }
1091
-
1092
- if (!wmd.nativeUndo) {
1093
- undoMgr = new wmd.undoManager(function(){
1094
- previewRefreshCallback();
1095
- setUndoRedoButtonStates();
1096
- });
1097
- }
1098
-
1099
- makeSpritedButtonRow();
1100
-
1101
-
1102
- var keyEvent = "keydown";
1103
- if (global.isOpera) {
1104
- keyEvent = "keypress";
1105
- }
1106
-
1107
- util.addEvent(inputBox, keyEvent, function(key){
1108
-
1109
- // Check to see if we have a button key and, if so execute the callback.
1110
- if (key.ctrlKey || key.metaKey) {
1111
-
1112
- var keyCode = key.charCode || key.keyCode;
1113
- var keyCodeStr = String.fromCharCode(keyCode).toLowerCase();
1114
-
1115
- switch(keyCodeStr) {
1116
- case "b":
1117
- doClick(document.getElementById("wmd-bold-button"));
1118
- break;
1119
- case "i":
1120
- doClick(document.getElementById("wmd-italic-button"));
1121
- break;
1122
- case "l":
1123
- doClick(document.getElementById("wmd-link-button"));
1124
- break;
1125
- case "q":
1126
- doClick(document.getElementById("wmd-quote-button"));
1127
- break;
1128
- case "k":
1129
- doClick(document.getElementById("wmd-code-button"));
1130
- break;
1131
- case "g":
1132
- doClick(document.getElementById("wmd-image-button"));
1133
- break;
1134
- case "o":
1135
- doClick(document.getElementById("wmd-olist-button"));
1136
- break;
1137
- case "u":
1138
- doClick(document.getElementById("wmd-ulist-button"));
1139
- break;
1140
- case "h":
1141
- doClick(document.getElementById("wmd-heading-button"));
1142
- break;
1143
- case "r":
1144
- doClick(document.getElementById("wmd-hr-button"));
1145
- break;
1146
- case "y":
1147
- doClick(document.getElementById("wmd-redo-button"));
1148
- break;
1149
- case "z":
1150
- if(key.shiftKey) {
1151
- doClick(document.getElementById("wmd-redo-button"));
1152
- }
1153
- else {
1154
- doClick(document.getElementById("wmd-undo-button"));
1155
- }
1156
- break;
1157
- default:
1158
- return;
1159
- }
1160
-
1161
-
1162
- if (key.preventDefault) {
1163
- key.preventDefault();
1164
- }
1165
-
1166
- if (top.event) {
1167
- top.event.returnValue = false;
1168
- }
1169
- }
1170
- });
1171
-
1172
- // Auto-continue lists, code blocks and block quotes when
1173
- // the enter key is pressed.
1174
- util.addEvent(inputBox, "keyup", function(key){
1175
- if (!key.shiftKey && !key.ctrlKey && !key.metaKey) {
1176
- var keyCode = key.charCode || key.keyCode;
1177
- // Key code 13 is Enter
1178
- if (keyCode === 13) {
1179
- fakeButton = {};
1180
- fakeButton.textOp = command.doAutoindent;
1181
- doClick(fakeButton);
1182
- }
1183
- }
1184
- });
1185
-
1186
- // Disable ESC clearing the input textarea on IE
1187
- if (global.isIE) {
1188
- util.addEvent(inputBox, "keydown", function(key){
1189
- var code = key.keyCode;
1190
- // Key code 27 is ESC
1191
- if (code === 27) {
1192
- return false;
1193
- }
1194
- });
1195
- }
1196
-
1197
- if (inputBox.form) {
1198
- var submitCallback = inputBox.form.onsubmit;
1199
- // inputBox.form.onsubmit = function(){
1200
- // convertToHtml();
1201
- // if (submitCallback) {
1202
- // return submitCallback.apply(this, arguments);
1203
- // }
1204
- // };
1205
- }
1206
- };
1207
-
1208
- // Convert the contents of the input textarea to HTML in the output/preview panels.
1209
- var convertToHtml = function(){
1210
-
1211
- if (wmd.showdown) {
1212
- var markdownConverter = new wmd.showdown.converter();
1213
- }
1214
- var text = inputBox.value;
1215
-
1216
- var callback = function(){
1217
- inputBox.value = text;
1218
- };
1219
-
1220
- if (!/markdown/.test(wmd.wmd_env.output.toLowerCase())) {
1221
- if (markdownConverter) {
1222
- inputBox.value = markdownConverter.makeHtml(text);
1223
- top.setTimeout(callback, 0);
1224
- }
1225
- }
1226
- return true;
1227
- };
1228
-
1229
-
1230
- this.undo = function(){
1231
- if (undoMgr) {
1232
- undoMgr.undo();
1233
- }
1234
- };
1235
-
1236
- this.redo = function(){
1237
- if (undoMgr) {
1238
- undoMgr.redo();
1239
- }
1240
- };
1241
-
1242
- // This is pretty useless. The setupEditor function contents
1243
- // should just be copied here.
1244
- var init = function(){
1245
- setupEditor();
1246
- };
1247
-
1248
- this.destroy = function(){
1249
- if (undoMgr) {
1250
- undoMgr.destroy();
1251
- }
1252
- if (div.parentNode) {
1253
- div.parentNode.removeChild(div);
1254
- }
1255
- if (inputBox) {
1256
- inputBox.style.marginTop = "";
1257
- }
1258
- top.clearInterval(creationHandle);
1259
- };
1260
-
1261
- init();
1262
- };
1263
-
1264
- // The input textarea state/contents.
1265
- // This is used to implement undo/redo by the undo manager.
1266
- wmd.TextareaState = function(){
1267
-
1268
- // Aliases
1269
- var stateObj = this;
1270
- var inputArea = wmd.panels.input;
1271
-
1272
- this.init = function() {
1273
-
1274
- if (!util.isVisible(inputArea)) {
1275
- return;
1276
- }
1277
-
1278
- this.setInputAreaSelectionStartEnd();
1279
- this.scrollTop = inputArea.scrollTop;
1280
- if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) {
1281
- this.text = inputArea.value;
1282
- }
1283
-
1284
- }
1285
-
1286
- // Sets the selected text in the input box after we've performed an
1287
- // operation.
1288
- this.setInputAreaSelection = function(){
1289
-
1290
- if (!util.isVisible(inputArea)) {
1291
- return;
1292
- }
1293
-
1294
- if (inputArea.selectionStart !== undefined && !global.isOpera) {
1295
-
1296
- inputArea.focus();
1297
- inputArea.selectionStart = stateObj.start;
1298
- inputArea.selectionEnd = stateObj.end;
1299
- inputArea.scrollTop = stateObj.scrollTop;
1300
- }
1301
- else if (doc.selection) {
1302
-
1303
- if (doc.activeElement && doc.activeElement !== inputArea) {
1304
- return;
1305
- }
1306
-
1307
- inputArea.focus();
1308
- var range = inputArea.createTextRange();
1309
- range.moveStart("character", -inputArea.value.length);
1310
- range.moveEnd("character", -inputArea.value.length);
1311
- range.moveEnd("character", stateObj.end);
1312
- range.moveStart("character", stateObj.start);
1313
- range.select();
1314
- }
1315
- };
1316
-
1317
- this.setInputAreaSelectionStartEnd = function(){
1318
-
1319
- if (inputArea.selectionStart || inputArea.selectionStart === 0) {
1320
-
1321
- stateObj.start = inputArea.selectionStart;
1322
- stateObj.end = inputArea.selectionEnd;
1323
- }
1324
- else if (doc.selection) {
1325
-
1326
- stateObj.text = util.fixEolChars(inputArea.value);
1327
-
1328
- // IE loses the selection in the textarea when buttons are
1329
- // clicked. On IE we cache the selection and set a flag
1330
- // which we check for here.
1331
- var range;
1332
- if(wmd.ieRetardedClick && wmd.ieCachedRange) {
1333
- range = wmd.ieCachedRange;
1334
- wmd.ieRetardedClick = false;
1335
- }
1336
- else {
1337
- range = doc.selection.createRange();
1338
- }
1339
-
1340
- var fixedRange = util.fixEolChars(range.text);
1341
- var marker = "\x07";
1342
- var markedRange = marker + fixedRange + marker;
1343
- range.text = markedRange;
1344
- var inputText = util.fixEolChars(inputArea.value);
1345
-
1346
- range.moveStart("character", -markedRange.length);
1347
- range.text = fixedRange;
1348
-
1349
- stateObj.start = inputText.indexOf(marker);
1350
- stateObj.end = inputText.lastIndexOf(marker) - marker.length;
1351
-
1352
- var len = stateObj.text.length - util.fixEolChars(inputArea.value).length;
1353
-
1354
- if (len) {
1355
- range.moveStart("character", -fixedRange.length);
1356
- while (len--) {
1357
- fixedRange += "\n";
1358
- stateObj.end += 1;
1359
- }
1360
- range.text = fixedRange;
1361
- }
1362
-
1363
- this.setInputAreaSelection();
1364
- }
1365
- };
1366
-
1367
- // Restore this state into the input area.
1368
- this.restore = function(){
1369
-
1370
- if (stateObj.text != undefined && stateObj.text != inputArea.value) {
1371
- inputArea.value = stateObj.text;
1372
- }
1373
- this.setInputAreaSelection();
1374
- inputArea.scrollTop = stateObj.scrollTop;
1375
- };
1376
-
1377
- // Gets a collection of HTML chunks from the inptut textarea.
1378
- this.getChunks = function(){
1379
-
1380
- var chunk = new wmd.Chunks();
1381
-
1382
- chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start));
1383
- chunk.startTag = "";
1384
- chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end));
1385
- chunk.endTag = "";
1386
- chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end));
1387
- chunk.scrollTop = stateObj.scrollTop;
1388
-
1389
- return chunk;
1390
- };
1391
-
1392
- // Sets the TextareaState properties given a chunk of markdown.
1393
- this.setChunks = function(chunk){
1394
-
1395
- chunk.before = chunk.before + chunk.startTag;
1396
- chunk.after = chunk.endTag + chunk.after;
1397
-
1398
- if (global.isOpera) {
1399
- chunk.before = chunk.before.replace(/\n/g, "\r\n");
1400
- chunk.selection = chunk.selection.replace(/\n/g, "\r\n");
1401
- chunk.after = chunk.after.replace(/\n/g, "\r\n");
1402
- }
1403
-
1404
- this.start = chunk.before.length;
1405
- this.end = chunk.before.length + chunk.selection.length;
1406
- this.text = chunk.before + chunk.selection + chunk.after;
1407
- this.scrollTop = chunk.scrollTop;
1408
- };
1409
-
1410
- this.init();
1411
- };
1412
-
1413
- // before: contains all the text in the input box BEFORE the selection.
1414
- // after: contains all the text in the input box AFTER the selection.
1415
- wmd.Chunks = function(){
1416
- };
1417
-
1418
- // startRegex: a regular expression to find the start tag
1419
- // endRegex: a regular expresssion to find the end tag
1420
- wmd.Chunks.prototype.findTags = function(startRegex, endRegex){
1421
-
1422
- var chunkObj = this;
1423
- var regex;
1424
-
1425
- if (startRegex) {
1426
-
1427
- regex = util.extendRegExp(startRegex, "", "$");
1428
-
1429
- this.before = this.before.replace(regex,
1430
- function(match){
1431
- chunkObj.startTag = chunkObj.startTag + match;
1432
- return "";
1433
- });
1434
-
1435
- regex = util.extendRegExp(startRegex, "^", "");
1436
-
1437
- this.selection = this.selection.replace(regex,
1438
- function(match){
1439
- chunkObj.startTag = chunkObj.startTag + match;
1440
- return "";
1441
- });
1442
- }
1443
-
1444
- if (endRegex) {
1445
-
1446
- regex = util.extendRegExp(endRegex, "", "$");
1447
-
1448
- this.selection = this.selection.replace(regex,
1449
- function(match){
1450
- chunkObj.endTag = match + chunkObj.endTag;
1451
- return "";
1452
- });
1453
-
1454
- regex = util.extendRegExp(endRegex, "^", "");
1455
-
1456
- this.after = this.after.replace(regex,
1457
- function(match){
1458
- chunkObj.endTag = match + chunkObj.endTag;
1459
- return "";
1460
- });
1461
- }
1462
- };
1463
-
1464
- // If remove is false, the whitespace is transferred
1465
- // to the before/after regions.
1466
- //
1467
- // If remove is true, the whitespace disappears.
1468
- wmd.Chunks.prototype.trimWhitespace = function(remove){
1469
-
1470
- this.selection = this.selection.replace(/^(\s*)/, "");
1471
-
1472
- if (!remove) {
1473
- this.before += re.$1;
1474
- }
1475
-
1476
- this.selection = this.selection.replace(/(\s*)$/, "");
1477
-
1478
- if (!remove) {
1479
- this.after = re.$1 + this.after;
1480
- }
1481
- };
1482
-
1483
-
1484
- wmd.Chunks.prototype.addBlankLines = function(nLinesBefore, nLinesAfter, findExtraNewlines){
1485
-
1486
- if (nLinesBefore === undefined) {
1487
- nLinesBefore = 1;
1488
- }
1489
-
1490
- if (nLinesAfter === undefined) {
1491
- nLinesAfter = 1;
1492
- }
1493
-
1494
- nLinesBefore++;
1495
- nLinesAfter++;
1496
-
1497
- var regexText;
1498
- var replacementText;
1499
-
1500
- this.selection = this.selection.replace(/(^\n*)/, "");
1501
- this.startTag = this.startTag + re.$1;
1502
- this.selection = this.selection.replace(/(\n*$)/, "");
1503
- this.endTag = this.endTag + re.$1;
1504
- this.startTag = this.startTag.replace(/(^\n*)/, "");
1505
- this.before = this.before + re.$1;
1506
- this.endTag = this.endTag.replace(/(\n*$)/, "");
1507
- this.after = this.after + re.$1;
1508
-
1509
- if (this.before) {
1510
-
1511
- regexText = replacementText = "";
1512
-
1513
- while (nLinesBefore--) {
1514
- regexText += "\\n?";
1515
- replacementText += "\n";
1516
- }
1517
-
1518
- if (findExtraNewlines) {
1519
- regexText = "\\n*";
1520
- }
1521
- this.before = this.before.replace(new re(regexText + "$", ""), replacementText);
1522
- }
1523
-
1524
- if (this.after) {
1525
-
1526
- regexText = replacementText = "";
1527
-
1528
- while (nLinesAfter--) {
1529
- regexText += "\\n?";
1530
- replacementText += "\n";
1531
- }
1532
- if (findExtraNewlines) {
1533
- regexText = "\\n*";
1534
- }
1535
-
1536
- this.after = this.after.replace(new re(regexText, ""), replacementText);
1537
- }
1538
- };
1539
-
1540
- // The markdown symbols - 4 spaces = code, > = blockquote, etc.
1541
- command.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";
1542
-
1543
- // Remove markdown symbols from the chunk selection.
1544
- command.unwrap = function(chunk){
1545
- var txt = new re("([^\\n])\\n(?!(\\n|" + command.prefixes + "))", "g");
1546
- chunk.selection = chunk.selection.replace(txt, "$1 $2");
1547
- };
1548
-
1549
- command.wrap = function(chunk, len){
1550
- command.unwrap(chunk);
1551
- var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm");
1552
-
1553
- chunk.selection = chunk.selection.replace(regex, function(line, marked){
1554
- if (new re("^" + command.prefixes, "").test(line)) {
1555
- return line;
1556
- }
1557
- return marked + "\n";
1558
- });
1559
-
1560
- chunk.selection = chunk.selection.replace(/\s+$/, "");
1561
- };
1562
-
1563
- command.doBold = function(chunk, postProcessing, useDefaultText){
1564
- return command.doBorI(chunk, 2, "strong text");
1565
- };
1566
-
1567
- command.doItalic = function(chunk, postProcessing, useDefaultText){
1568
- return command.doBorI(chunk, 1, "emphasized text");
1569
- };
1570
-
1571
- // chunk: The selected region that will be enclosed with */**
1572
- // nStars: 1 for italics, 2 for bold
1573
- // insertText: If you just click the button without highlighting text, this gets inserted
1574
- command.doBorI = function(chunk, nStars, insertText){
1575
-
1576
- // Get rid of whitespace and fixup newlines.
1577
- chunk.trimWhitespace();
1578
- chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n");
1579
-
1580
- // Look for stars before and after. Is the chunk already marked up?
1581
- chunk.before.search(/(\**$)/);
1582
- var starsBefore = re.$1;
1583
-
1584
- chunk.after.search(/(^\**)/);
1585
- var starsAfter = re.$1;
1586
-
1587
- var prevStars = Math.min(starsBefore.length, starsAfter.length);
1588
-
1589
- // Remove stars if we have to since the button acts as a toggle.
1590
- if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) {
1591
- chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), "");
1592
- chunk.after = chunk.after.replace(re("^[*]{" + nStars + "}", ""), "");
1593
- }
1594
- else if (!chunk.selection && starsAfter) {
1595
- // It's not really clear why this code is necessary. It just moves
1596
- // some arbitrary stuff around.
1597
- chunk.after = chunk.after.replace(/^([*_]*)/, "");
1598
- chunk.before = chunk.before.replace(/(\s?)$/, "");
1599
- var whitespace = re.$1;
1600
- chunk.before = chunk.before + starsAfter + whitespace;
1601
- }
1602
- else {
1603
-
1604
- // In most cases, if you don't have any selected text and click the button
1605
- // you'll get a selected, marked up region with the default text inserted.
1606
- if (!chunk.selection && !starsAfter) {
1607
- chunk.selection = insertText;
1608
- }
1609
-
1610
- // Add the true markup.
1611
- var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ?
1612
- chunk.before = chunk.before + markup;
1613
- chunk.after = markup + chunk.after;
1614
- }
1615
-
1616
- return;
1617
- };
1618
-
1619
- command.stripLinkDefs = function(text, defsToAdd){
1620
-
1621
- text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,
1622
- function(totalMatch, id, link, newlines, title){
1623
- defsToAdd[id] = totalMatch.replace(/\s*$/, "");
1624
- if (newlines) {
1625
- // Strip the title and return that separately.
1626
- defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, "");
1627
- return newlines + title;
1628
- }
1629
- return "";
1630
- });
1631
-
1632
- return text;
1633
- };
1634
-
1635
- command.addLinkDef = function(chunk, linkDef){
1636
-
1637
- var refNumber = 0; // The current reference number
1638
- var defsToAdd = {}; //
1639
- // Start with a clean slate by removing all previous link definitions.
1640
- chunk.before = command.stripLinkDefs(chunk.before, defsToAdd);
1641
- chunk.selection = command.stripLinkDefs(chunk.selection, defsToAdd);
1642
- chunk.after = command.stripLinkDefs(chunk.after, defsToAdd);
1643
-
1644
- var defs = "";
1645
- var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;
1646
-
1647
-
1648
-
1649
- var addDefNumber = function(def){
1650
- refNumber++;
1651
- def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, " [" + refNumber + "]:");
1652
- defs += "\n" + def;
1653
- };
1654
-
1655
- // note that
1656
- // a) the recursive call to getLink cannot go infinite, because by definition
1657
- // of regex, inner is always a proper substring of wholeMatch, and
1658
- // b) more than one level of nesting is neither supported by the regex
1659
- // nor making a lot of sense (the only use case for nesting is a linked image)
1660
- var getLink = function (wholeMatch, before, inner, afterInner, id, end) {
1661
- inner = inner.replace(regex, getLink);
1662
- if (defsToAdd[id]) {
1663
- addDefNumber(defsToAdd[id]);
1664
- return before + inner + afterInner + refNumber + end;
1665
-
1666
- }
1667
- return wholeMatch;
1668
- };
1669
-
1670
- chunk.before = chunk.before.replace(regex, getLink);
1671
-
1672
- if (linkDef) {
1673
- addDefNumber(linkDef);
1674
- }
1675
- else {
1676
- chunk.selection = chunk.selection.replace(regex, getLink);
1677
- }
1678
-
1679
- var refOut = refNumber;
1680
-
1681
- chunk.after = chunk.after.replace(regex, getLink);
1682
-
1683
- if (chunk.after) {
1684
- chunk.after = chunk.after.replace(/\n*$/, "");
1685
- }
1686
- if (!chunk.after) {
1687
- chunk.selection = chunk.selection.replace(/\n*$/, "");
1688
- }
1689
-
1690
- chunk.after += "\n\n" + defs;
1691
-
1692
- return refOut;
1693
- };
1694
-
1695
- command.doLinkOrImage = function(chunk, postProcessing, isImage){
1696
-
1697
- chunk.trimWhitespace();
1698
- chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/);
1699
-
1700
- if (chunk.endTag.length > 1) {
1701
-
1702
- chunk.startTag = chunk.startTag.replace(/!?\[/, "");
1703
- chunk.endTag = "";
1704
- command.addLinkDef(chunk, null);
1705
-
1706
- }
1707
- else {
1708
-
1709
- if (/\n\n/.test(chunk.selection)) {
1710
- command.addLinkDef(chunk, null);
1711
- return;
1712
- }
1713
-
1714
- // The function to be executed when you enter a link and press OK or Cancel.
1715
- // Marks up the link and adds the ref.
1716
- var makeLinkMarkdown = function(link){
1717
-
1718
- if (link !== null) {
1719
-
1720
- chunk.startTag = chunk.endTag = "";
1721
- var linkDef = " [999]: " + link;
1722
-
1723
- var num = command.addLinkDef(chunk, linkDef);
1724
- chunk.startTag = isImage ? "![" : "[";
1725
- chunk.endTag = "][" + num + "]";
1726
-
1727
- if (!chunk.selection) {
1728
- if (isImage) {
1729
- chunk.selection = "alt text";
1730
- }
1731
- else {
1732
- chunk.selection = "link text";
1733
- }
1734
- }
1735
- }
1736
- postProcessing();
1737
- };
1738
-
1739
- if (isImage) {
1740
- util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown);
1741
- }
1742
- else {
1743
- util.prompt(linkDialogText, linkDefaultText, makeLinkMarkdown);
1744
- }
1745
- return true;
1746
- }
1747
- };
1748
-
1749
- util.makeAPI = function(){
1750
- wmd.wmd = {};
1751
- wmd.wmd.editor = wmd.editor;
1752
- wmd.wmd.previewManager = wmd.previewManager;
1753
- };
1754
-
1755
- util.startEditor = function(){
1756
-
1757
- if (wmd.wmd_env.autostart === false) {
1758
- util.makeAPI();
1759
- return;
1760
- }
1761
-
1762
- var edit; // The editor (buttons + input + outputs) - the main object.
1763
- var previewMgr; // The preview manager.
1764
-
1765
- // Fired after the page has fully loaded.
1766
- var loadListener = function(){
1767
-
1768
- wmd.panels = new wmd.PanelCollection();
1769
-
1770
- previewMgr = new wmd.previewManager();
1771
- var previewRefreshCallback = previewMgr.refresh;
1772
-
1773
- edit = new wmd.editor(previewRefreshCallback);
1774
-
1775
- previewMgr.refresh(true);
1776
-
1777
- };
1778
-
1779
- util.addEvent(top, "load", loadListener);
1780
- };
1781
-
1782
- wmd.previewManager = function(){
1783
-
1784
- var managerObj = this;
1785
- var converter;
1786
- var poller;
1787
- var timeout;
1788
- var elapsedTime;
1789
- var oldInputText;
1790
- var htmlOut;
1791
- var maxDelay = 3000;
1792
- var startType = "delayed"; // The other legal value is "manual"
1793
-
1794
- // Adds event listeners to elements and creates the input poller.
1795
- var setupEvents = function(inputElem, listener){
1796
-
1797
- util.addEvent(inputElem, "input", listener);
1798
- inputElem.onpaste = listener;
1799
- inputElem.ondrop = listener;
1800
-
1801
- util.addEvent(inputElem, "keypress", listener);
1802
- util.addEvent(inputElem, "keydown", listener);
1803
- // previewPollInterval is set at the top of this file.
1804
- poller = new wmd.inputPoller(listener, previewPollInterval);
1805
- };
1806
-
1807
- var getDocScrollTop = function(){
1808
-
1809
- var result = 0;
1810
-
1811
- if (top.innerHeight) {
1812
- result = top.pageYOffset;
1813
- }
1814
- else
1815
- if (doc.documentElement && doc.documentElement.scrollTop) {
1816
- result = doc.documentElement.scrollTop;
1817
- }
1818
- else
1819
- if (doc.body) {
1820
- result = doc.body.scrollTop;
1821
- }
1822
-
1823
- return result;
1824
- };
1825
-
1826
- var makePreviewHtml = function(){
1827
-
1828
- // If there are no registered preview and output panels
1829
- // there is nothing to do.
1830
- if (!wmd.panels.preview && !wmd.panels.output) {
1831
- return;
1832
- }
1833
-
1834
- var text = wmd.panels.input.value;
1835
- if (text && text == oldInputText) {
1836
- return; // Input text hasn't changed.
1837
- }
1838
- else {
1839
- oldInputText = text;
1840
- }
1841
-
1842
- var prevTime = new Date().getTime();
1843
-
1844
- if (!converter && wmd.showdown) {
1845
- converter = new wmd.showdown.converter();
1846
- }
1847
-
1848
- if (converter) {
1849
- text = converter.makeHtml(text);
1850
- }
1851
-
1852
- // Calculate the processing time of the HTML creation.
1853
- // It's used as the delay time in the event listener.
1854
- var currTime = new Date().getTime();
1855
- elapsedTime = currTime - prevTime;
1856
-
1857
- pushPreviewHtml(text);
1858
- htmlOut = text;
1859
- };
1860
-
1861
- // setTimeout is already used. Used as an event listener.
1862
- var applyTimeout = function(){
1863
-
1864
- if (timeout) {
1865
- top.clearTimeout(timeout);
1866
- timeout = undefined;
1867
- }
1868
-
1869
- if (startType !== "manual") {
1870
-
1871
- var delay = 0;
1872
-
1873
- if (startType === "delayed") {
1874
- delay = elapsedTime;
1875
- }
1876
-
1877
- if (delay > maxDelay) {
1878
- delay = maxDelay;
1879
- }
1880
- timeout = top.setTimeout(makePreviewHtml, delay);
1881
- }
1882
- };
1883
-
1884
- var getScaleFactor = function(panel){
1885
- if (panel.scrollHeight <= panel.clientHeight) {
1886
- return 1;
1887
- }
1888
- return panel.scrollTop / (panel.scrollHeight - panel.clientHeight);
1889
- };
1890
-
1891
- var setPanelScrollTops = function(){
1892
-
1893
- if (wmd.panels.preview) {
1894
- wmd.panels.preview.scrollTop = (wmd.panels.preview.scrollHeight - wmd.panels.preview.clientHeight) * getScaleFactor(wmd.panels.preview);
1895
- ;
1896
- }
1897
-
1898
- if (wmd.panels.output) {
1899
- wmd.panels.output.scrollTop = (wmd.panels.output.scrollHeight - wmd.panels.output.clientHeight) * getScaleFactor(wmd.panels.output);
1900
- ;
1901
- }
1902
- };
1903
-
1904
- this.refresh = function(requiresRefresh){
1905
-
1906
- if (requiresRefresh) {
1907
- oldInputText = "";
1908
- makePreviewHtml();
1909
- }
1910
- else {
1911
- applyTimeout();
1912
- }
1913
- };
1914
-
1915
- this.processingTime = function(){
1916
- return elapsedTime;
1917
- };
1918
-
1919
- // The output HTML
1920
- this.output = function(){
1921
- return htmlOut;
1922
- };
1923
-
1924
- // The mode can be "manual" or "delayed"
1925
- this.setUpdateMode = function(mode){
1926
- startType = mode;
1927
- managerObj.refresh();
1928
- };
1929
-
1930
- var isFirstTimeFilled = true;
1931
-
1932
- var pushPreviewHtml = function(text){
1933
-
1934
- var emptyTop = position.getTop(wmd.panels.input) - getDocScrollTop();
1935
-
1936
- // Send the encoded HTML to the output textarea/div.
1937
- if (wmd.panels.output) {
1938
- // The value property is only defined if the output is a textarea.
1939
- if (wmd.panels.output.value !== undefined) {
1940
- wmd.panels.output.value = text;
1941
- wmd.panels.output.readOnly = true;
1942
- }
1943
- // Otherwise we are just replacing the text in a div.
1944
- // Send the HTML wrapped in <pre><code>
1945
- else {
1946
- var newText = text.replace(/&/g, "&amp;");
1947
- newText = newText.replace(/</g, "&lt;");
1948
- wmd.panels.output.innerHTML = "<pre><code>" + newText + "</code></pre>";
1949
- }
1950
- }
1951
-
1952
- if (wmd.panels.preview) {
1953
- wmd.panels.preview.innerHTML = text;
1954
- }
1955
-
1956
- setPanelScrollTops();
1957
-
1958
- if (isFirstTimeFilled) {
1959
- isFirstTimeFilled = false;
1960
- return;
1961
- }
1962
-
1963
- var fullTop = position.getTop(wmd.panels.input) - getDocScrollTop();
1964
-
1965
- if (global.isIE) {
1966
- top.setTimeout(function(){
1967
- top.scrollBy(0, fullTop - emptyTop);
1968
- }, 0);
1969
- }
1970
- else {
1971
- top.scrollBy(0, fullTop - emptyTop);
1972
- }
1973
- };
1974
-
1975
- var init = function(){
1976
-
1977
- setupEvents(wmd.panels.input, applyTimeout);
1978
- makePreviewHtml();
1979
-
1980
- if (wmd.panels.preview) {
1981
- wmd.panels.preview.scrollTop = 0;
1982
- }
1983
- if (wmd.panels.output) {
1984
- wmd.panels.output.scrollTop = 0;
1985
- }
1986
- };
1987
-
1988
- this.destroy = function(){
1989
- if (poller) {
1990
- poller.destroy();
1991
- }
1992
- };
1993
-
1994
- init();
1995
- };
1996
-
1997
- // Moves the cursor to the next line and continues lists, quotes and code.
1998
- command.doAutoindent = function(chunk, postProcessing, useDefaultText){
1999
-
2000
- chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n");
2001
- chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n");
2002
- chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n");
2003
-
2004
- useDefaultText = false;
2005
-
2006
- if(/(\n|^)[ ]{0,3}([*+-])[ \t]+.*\n$/.test(chunk.before)){
2007
- if(command.doList){
2008
- command.doList(chunk, postProcessing, false, true);
2009
- }
2010
- }
2011
- if(/(\n|^)[ ]{0,3}(\d+[.])[ \t]+.*\n$/.test(chunk.before)){
2012
- if(command.doList){
2013
- command.doList(chunk, postProcessing, true, true);
2014
- }
2015
- }
2016
- if(/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(chunk.before)){
2017
- if(command.doBlockquote){
2018
- command.doBlockquote(chunk, postProcessing, useDefaultText);
2019
- }
2020
- }
2021
- if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)){
2022
- if(command.doCode){
2023
- command.doCode(chunk, postProcessing, useDefaultText);
2024
- }
2025
- }
2026
- };
2027
-
2028
- command.doBlockquote = function(chunk, postProcessing, useDefaultText){
2029
-
2030
- chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,
2031
- function(totalMatch, newlinesBefore, text, newlinesAfter){
2032
- chunk.before += newlinesBefore;
2033
- chunk.after = newlinesAfter + chunk.after;
2034
- return text;
2035
- });
2036
-
2037
- chunk.before = chunk.before.replace(/(>[ \t]*)$/,
2038
- function(totalMatch, blankLine){
2039
- chunk.selection = blankLine + chunk.selection;
2040
- return "";
2041
- });
2042
-
2043
- var defaultText = useDefaultText ? "Blockquote" : "";
2044
- chunk.selection = chunk.selection.replace(/^(\s|>)+$/ ,"");
2045
- chunk.selection = chunk.selection || defaultText;
2046
-
2047
- // The original code uses a regular expression to find out how much of the
2048
- // text *directly before* the selection already was a blockquote:
2049
- /*
2050
- if(chunk.before){
2051
- chunk.before = chunk.before.replace(/\n?$/,"\n");
2052
- }
2053
- chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,
2054
- function (totalMatch) {
2055
- chunk.startTag = totalMatch;
2056
- return "";
2057
- });
2058
- */
2059
- // This comes down to:
2060
- // Go backwards as many lines a possible, such that each line
2061
- // a) starts with ">", or
2062
- // b) is almost empty, except for whitespace, or
2063
- // c) is preceeded by an unbroken chain of non-empty lines
2064
- // leading up to a line that starts with ">" and at least one more character
2065
- // and in addition
2066
- // d) at least one line fulfills a)
2067
- //
2068
- // Since this is essentially a backwards-moving regex, it's susceptible to
2069
- // catstrophic backtracking and can cause the browser to hang;
2070
- // see e.g. http://meta.stackoverflow.com/questions/9807.
2071
- //
2072
- // Hence we replaced this by a simple state machine that just goes through the
2073
- // lines and checks for a), b), and c).
2074
-
2075
- var match = "";
2076
- var leftOver = "";
2077
- if (chunk.before) {
2078
- var lines = chunk.before.replace(/\n$/, "").split("\n");
2079
- var inChain = false;
2080
- for (var i in lines) {
2081
- var good = false;
2082
- line = lines[i];
2083
- inChain = inChain && line.length > 0; // c) any non-empty line continues the chain
2084
- if (/^>/.test(line)) { // a)
2085
- good = true;
2086
- if (!inChain && line.length > 1) // c) any line that starts with ">" and has at least one more character starts the chain
2087
- inChain = true;
2088
- } else if (/^[ \t]*$/.test(line)) { // b)
2089
- good = true;
2090
- } else {
2091
- good = inChain; // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain
2092
- }
2093
- if (good) {
2094
- match += line + "\n";
2095
- } else {
2096
- leftOver += match + line;
2097
- match = "\n";
2098
- }
2099
- }
2100
- if (!/(^|\n)>/.test(match)) { // d)
2101
- leftOver += match;
2102
- match = "";
2103
- }
2104
- }
2105
-
2106
- chunk.startTag = match;
2107
- chunk.before = leftOver;
2108
-
2109
- // end of change
2110
-
2111
- if(chunk.after){
2112
- chunk.after = chunk.after.replace(/^\n?/,"\n");
2113
- }
2114
-
2115
- chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,
2116
- function(totalMatch){
2117
- chunk.endTag = totalMatch;
2118
- return "";
2119
- });
2120
-
2121
- var replaceBlanksInTags = function(useBracket){
2122
-
2123
- var replacement = useBracket ? "> " : "";
2124
-
2125
- if(chunk.startTag){
2126
- chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/,
2127
- function(totalMatch, markdown){
2128
- return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n";
2129
- });
2130
- }
2131
- if(chunk.endTag){
2132
- chunk.endTag = chunk.endTag.replace(/^\n((>|\s)*)\n/,
2133
- function(totalMatch, markdown){
2134
- return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n";
2135
- });
2136
- }
2137
- };
2138
-
2139
- if(/^(?![ ]{0,3}>)/m.test(chunk.selection)){
2140
- command.wrap(chunk, wmd.wmd_env.lineLength - 2);
2141
- chunk.selection = chunk.selection.replace(/^/gm, "> ");
2142
- replaceBlanksInTags(true);
2143
- chunk.addBlankLines();
2144
- }
2145
- else{
2146
- chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, "");
2147
- command.unwrap(chunk);
2148
- replaceBlanksInTags(false);
2149
-
2150
- if(!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag){
2151
- chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n");
2152
- }
2153
-
2154
- if(!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag){
2155
- chunk.endTag=chunk.endTag.replace(/^\n{0,2}/, "\n\n");
2156
- }
2157
- }
2158
-
2159
- if(!/\n/.test(chunk.selection)){
2160
- chunk.selection = chunk.selection.replace(/^(> *)/,
2161
- function(wholeMatch, blanks){
2162
- chunk.startTag += blanks;
2163
- return "";
2164
- });
2165
- }
2166
- };
2167
-
2168
- command.doCode = function(chunk, postProcessing, useDefaultText){
2169
-
2170
- var hasTextBefore = /\S[ ]*$/.test(chunk.before);
2171
- var hasTextAfter = /^[ ]*\S/.test(chunk.after);
2172
-
2173
- // Use 'four space' markdown if the selection is on its own
2174
- // line or is multiline.
2175
- if((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)){
2176
-
2177
- chunk.before = chunk.before.replace(/[ ]{4}$/,
2178
- function(totalMatch){
2179
- chunk.selection = totalMatch + chunk.selection;
2180
- return "";
2181
- });
2182
-
2183
- var nLinesBefore = 1;
2184
- var nLinesAfter = 1;
2185
-
2186
-
2187
- if(/\n(\t|[ ]{4,}).*\n$/.test(chunk.before) || chunk.after === ""){
2188
- nLinesBefore = 0;
2189
- }
2190
- if(/^\n(\t|[ ]{4,})/.test(chunk.after)){
2191
- nLinesAfter = 0; // This needs to happen on line 1
2192
- }
2193
-
2194
- chunk.addBlankLines(nLinesBefore, nLinesAfter);
2195
-
2196
- if(!chunk.selection){
2197
- chunk.startTag = " ";
2198
- chunk.selection = useDefaultText ? "enter code here" : "";
2199
- }
2200
- else {
2201
- if(/^[ ]{0,3}\S/m.test(chunk.selection)){
2202
- chunk.selection = chunk.selection.replace(/^/gm, " ");
2203
- }
2204
- else{
2205
- chunk.selection = chunk.selection.replace(/^[ ]{4}/gm, "");
2206
- }
2207
- }
2208
- }
2209
- else{
2210
- // Use backticks (`) to delimit the code block.
2211
-
2212
- chunk.trimWhitespace();
2213
- chunk.findTags(/`/, /`/);
2214
-
2215
- if(!chunk.startTag && !chunk.endTag){
2216
- chunk.startTag = chunk.endTag="`";
2217
- if(!chunk.selection){
2218
- chunk.selection = useDefaultText ? "enter code here" : "";
2219
- }
2220
- }
2221
- else if(chunk.endTag && !chunk.startTag){
2222
- chunk.before += chunk.endTag;
2223
- chunk.endTag = "";
2224
- }
2225
- else{
2226
- chunk.startTag = chunk.endTag="";
2227
- }
2228
- }
2229
- };
2230
-
2231
- command.doList = function(chunk, postProcessing, isNumberedList, useDefaultText){
2232
-
2233
- // These are identical except at the very beginning and end.
2234
- // Should probably use the regex extension function to make this clearer.
2235
- var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;
2236
- var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;
2237
-
2238
- // The default bullet is a dash but others are possible.
2239
- // This has nothing to do with the particular HTML bullet,
2240
- // it's just a markdown bullet.
2241
- var bullet = "-";
2242
-
2243
- // The number in a numbered list.
2244
- var num = 1;
2245
-
2246
- // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list.
2247
- var getItemPrefix = function(){
2248
- var prefix;
2249
- if(isNumberedList){
2250
- prefix = " " + num + ". ";
2251
- num++;
2252
- }
2253
- else{
2254
- prefix = " " + bullet + " ";
2255
- }
2256
- return prefix;
2257
- };
2258
-
2259
- // Fixes the prefixes of the other list items.
2260
- var getPrefixedItem = function(itemText){
2261
-
2262
- // The numbering flag is unset when called by autoindent.
2263
- if(isNumberedList === undefined){
2264
- isNumberedList = /^\s*\d/.test(itemText);
2265
- }
2266
-
2267
- // Renumber/bullet the list element.
2268
- itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,
2269
- function( _ ){
2270
- return getItemPrefix();
2271
- });
2272
-
2273
- return itemText;
2274
- };
2275
-
2276
- chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null);
2277
-
2278
- if(chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)){
2279
- chunk.before += chunk.startTag;
2280
- chunk.startTag = "";
2281
- }
2282
-
2283
- if(chunk.startTag){
2284
-
2285
- var hasDigits = /\d+[.]/.test(chunk.startTag);
2286
- chunk.startTag = "";
2287
- chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n");
2288
- command.unwrap(chunk);
2289
- chunk.addBlankLines();
2290
-
2291
- if(hasDigits){
2292
- // Have to renumber the bullet points if this is a numbered list.
2293
- chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem);
2294
- }
2295
- if(isNumberedList == hasDigits){
2296
- return;
2297
- }
2298
- }
2299
-
2300
- var nLinesBefore = 1;
2301
-
2302
- chunk.before = chunk.before.replace(previousItemsRegex,
2303
- function(itemText){
2304
- if(/^\s*([*+-])/.test(itemText)){
2305
- bullet = re.$1;
2306
- }
2307
- nLinesBefore = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0;
2308
- return getPrefixedItem(itemText);
2309
- });
2310
-
2311
- if(!chunk.selection){
2312
- chunk.selection = useDefaultText ? "List item" : " ";
2313
- }
2314
-
2315
- var prefix = getItemPrefix();
2316
-
2317
- var nLinesAfter = 1;
2318
-
2319
- chunk.after = chunk.after.replace(nextItemsRegex,
2320
- function(itemText){
2321
- nLinesAfter = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0;
2322
- return getPrefixedItem(itemText);
2323
- });
2324
-
2325
- chunk.trimWhitespace(true);
2326
- chunk.addBlankLines(nLinesBefore, nLinesAfter, true);
2327
- chunk.startTag = prefix;
2328
- var spaces = prefix.replace(/./g, " ");
2329
- command.wrap(chunk, wmd.wmd_env.lineLength - spaces.length);
2330
- chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces);
2331
-
2332
- };
2333
-
2334
- command.doHeading = function(chunk, postProcessing, useDefaultText){
2335
-
2336
- // Remove leading/trailing whitespace and reduce internal spaces to single spaces.
2337
- chunk.selection = chunk.selection.replace(/\s+/g, " ");
2338
- chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, "");
2339
-
2340
- // If we clicked the button with no selected text, we just
2341
- // make a level 2 hash header around some default text.
2342
- if(!chunk.selection){
2343
- chunk.startTag = "## ";
2344
- chunk.selection = "Heading";
2345
- chunk.endTag = " ##";
2346
- return;
2347
- }
2348
-
2349
- var headerLevel = 0; // The existing header level of the selected text.
2350
-
2351
- // Remove any existing hash heading markdown and save the header level.
2352
- chunk.findTags(/#+[ ]*/, /[ ]*#+/);
2353
- if(/#+/.test(chunk.startTag)){
2354
- headerLevel = re.lastMatch.length;
2355
- }
2356
- chunk.startTag = chunk.endTag = "";
2357
-
2358
- // Try to get the current header level by looking for - and = in the line
2359
- // below the selection.
2360
- chunk.findTags(null, /\s?(-+|=+)/);
2361
- if(/=+/.test(chunk.endTag)){
2362
- headerLevel = 1;
2363
- }
2364
- if(/-+/.test(chunk.endTag)){
2365
- headerLevel = 2;
2366
- }
2367
-
2368
- // Skip to the next line so we can create the header markdown.
2369
- chunk.startTag = chunk.endTag = "";
2370
- chunk.addBlankLines(1, 1);
2371
-
2372
- // We make a level 2 header if there is no current header.
2373
- // If there is a header level, we substract one from the header level.
2374
- // If it's already a level 1 header, it's removed.
2375
- var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1;
2376
-
2377
- if(headerLevelToCreate > 0){
2378
-
2379
- // The button only creates level 1 and 2 underline headers.
2380
- // Why not have it iterate over hash header levels? Wouldn't that be easier and cleaner?
2381
- var headerChar = headerLevelToCreate >= 2 ? "-" : "=";
2382
- var len = chunk.selection.length;
2383
- if(len > wmd.wmd_env.lineLength){
2384
- len = wmd.wmd_env.lineLength;
2385
- }
2386
- chunk.endTag = "\n";
2387
- while(len--){
2388
- chunk.endTag += headerChar;
2389
- }
2390
- }
2391
- };
2392
-
2393
- command.doHorizontalRule = function(chunk, postProcessing, useDefaultText){
2394
- chunk.startTag = "----------\n";
2395
- chunk.selection = "";
2396
- chunk.addBlankLines(2, 1, true);
2397
- }
2398
- };
2399
-
2400
-
2401
- Attacklab.wmd_env = {};
2402
- Attacklab.account_options = {};
2403
- Attacklab.wmd_defaults = {version:1, output:"HTML", lineLength:40, delayLoad:false};
2404
-
2405
- if(!Attacklab.wmd)
2406
- {
2407
- Attacklab.wmd = function()
2408
- {
2409
- Attacklab.loadEnv = function()
2410
- {
2411
- var mergeEnv = function(env)
2412
- {
2413
- if(!env)
2414
- {
2415
- return;
2416
- }
2417
-
2418
- for(var key in env)
2419
- {
2420
- Attacklab.wmd_env[key] = env[key];
2421
- }
2422
- };
2423
-
2424
- mergeEnv(Attacklab.wmd_defaults);
2425
- mergeEnv(Attacklab.account_options);
2426
- mergeEnv(top["wmd_options"]);
2427
- Attacklab.full = true;
2428
-
2429
- var defaultButtons = "bold italic link blockquote code image ol ul heading hr";
2430
- Attacklab.wmd_env.buttons = Attacklab.wmd_env.buttons || defaultButtons;
2431
- };
2432
- Attacklab.loadEnv();
2433
-
2434
- };
2435
-
2436
- Attacklab.wmd();
2437
- Attacklab.wmdBase();
2438
- Attacklab.Util.startEditor();
2439
- };
2440
-