diff --git a/Uservoice bugs.txt b/Uservoice bugs.txt deleted file mode 100644 index 4d5335db..00000000 --- a/Uservoice bugs.txt +++ /dev/null @@ -1,66 +0,0 @@ -Opera post preview bug - -I can't seem to get to the cause of this bug, so here is the complete post: - - -Here's an idea: - Just before you create the alert window, stop the DragEvent -....event.stopImmediatePropagation(); -store the event so we can resume if the user clicks the Yes button -....queuedEvent = event as DragEvent; -show the alert window -if the user clicks the yes button, resume the queued event -....dispatchEvent(queuedEvent); - - -(dots are spaces) -The problem here is that only the first indented piece of text is converted to code and has syntax highlighting. The other 2 are just indented, but no formatting applied to them. - -I'm using Opera 9.61, XP SP3. - - --------------------------------------------------------------------------- - -List button - -Highlighting multiple lines of text and clicking the list button should -create multiple list items instead of one item. - --------------------------------------------------------------------------- - -More AltGr issues - -Bug: Can't type '@' in answer - -I just found that AltGr+Q (=@) toggles Blockquote mode., which makes TSQL code in Markups extremely uncomfortable. ALT+064 works though. - -Guess this must be new behavior, as I never noticed it before - - sztyopek - -The same problem occurs for me: it is impossible to enter some essential characters on my keyboard layout: \ ] l L > { -since these can only be accessed by pressing AltGr + Q, U, G, K, L, Y and B, respectively. - - - devio - -AltGr = right ALT key. -I am referring to German keyboard layout. - --------------------------------------------------------------------------- - -Help button link is off. - -If you look at the help button image and link in Firebug they are a little -bit offset. - --------------------------------------------------------------------------- - -Fix Markdown to allow underscores in picture URLs - -Underscores in image URLs get interpreted as Markdown and then don't show the picture properly. - -BUT they do show the picture properly in the post preview. - - -Read more: "fix Markdown to allow underscores in picture URLs" - http://stackoverflow.uservoice.com/pages/general/suggestions/131034-fix-markdown-to-allow-underscores-in-picture-urls#ixzz08aKPzrOv - diff --git a/django/markedit/__init__.py b/django/markedit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/django/markedit/admin.py b/django/markedit/admin.py new file mode 100644 index 00000000..d1cfc539 --- /dev/null +++ b/django/markedit/admin.py @@ -0,0 +1,22 @@ +from django.contrib import admin +from markedit.widgets import AdminMarkEdit + +class MarkEditAdmin(admin.ModelAdmin): + + class MarkEdit: + fields = ['text',] + options = {} + + class Media: + from markedit import settings + css = getattr(settings, 'MARKEDIT_CSS', {}) + js = getattr(settings, 'MARKEDIT_JS', []) + + def formfield_for_dbfield(self, db_field, **kwargs): + formfield = super(MarkEditAdmin, self).formfield_for_dbfield(db_field, **kwargs) + if db_field.name in self.MarkEdit.fields: + formfield.widget = AdminMarkEdit(attrs = { + 'options': self.MarkEdit.options, + }) + return formfield + diff --git a/django/markedit/settings.py b/django/markedit/settings.py new file mode 100644 index 00000000..25c95767 --- /dev/null +++ b/django/markedit/settings.py @@ -0,0 +1,12 @@ +from django.conf import settings +media_url = getattr(settings, 'MEDIA_URL', '/media/') + +# Default settings (as python dict) +MARKEDIT_DEFAULT_SETTINGS = { } + +# Media +MARKEDIT_CSS = { 'all': (media_url + '/js/jquery-markedit/jquery.markedit.css',) } +MARKEDIT_JS = ( + media_url + '/js/jquery-markedit/showdown.js', + media_url + '/js/jquery-markedit/jquery.markedit.js', +) \ No newline at end of file diff --git a/django/markedit/templates/markedit/ui.html b/django/markedit/templates/markedit/ui.html new file mode 100644 index 00000000..743cbc8b --- /dev/null +++ b/django/markedit/templates/markedit/ui.html @@ -0,0 +1,4 @@ + + diff --git a/django/markedit/views.py b/django/markedit/views.py new file mode 100644 index 00000000..60f00ef0 --- /dev/null +++ b/django/markedit/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/django/markedit/widgets.py b/django/markedit/widgets.py new file mode 100644 index 00000000..cdad9dc6 --- /dev/null +++ b/django/markedit/widgets.py @@ -0,0 +1,52 @@ +from django import forms +from django.contrib.admin import widgets as admin_widgets +from django.template import loader, Context +from django.utils.html import conditional_escape +from django.utils.encoding import force_unicode + +class MarkEdit(forms.Textarea): + + def render(self, name, value, attrs=None): + + # Prepare values + attrs = self.build_attrs(attrs, name=name) + if not value: + value = '' + + # Extract or load options + from markedit import settings + options = getattr(settings, 'MARKEDIT_DEFAULT_SETTINGS', {}) + + if 'options' in attrs: + options = self._eval_value(attrs['options'], {}) + del attrs['options'] + + # Render widget to HTML + t = loader.get_template('markedit/ui.html') + c = Context({ + 'attributes' : self._render_attrs(attrs), + 'value' : conditional_escape(force_unicode(value)), + 'id' : attrs['id'], + 'options': options, + }) + + return t.render(c) + + def _eval_value(self, value, default_value): + v = None + try: + v = value() + except: + v = value + if v == None: + v = default_value + return v + + def _render_attrs(self, attrs): + atts = u'' + for key,value in attrs.items(): + atts += u'%s="%s" ' % (key, value) + return atts[:-1] + +class AdminMarkEdit(admin_widgets.AdminTextareaWidget, MarkEdit): + pass diff --git a/examples/basic.html b/examples/basic.html new file mode 100644 index 00000000..61482b7c --- /dev/null +++ b/examples/basic.html @@ -0,0 +1,48 @@ + + + + MarkEdit Basic Example + + + + + + + + + + + + + + + diff --git a/examples/dark-theme.html b/examples/dark-theme.html new file mode 100644 index 00000000..5cd23156 --- /dev/null +++ b/examples/dark-theme.html @@ -0,0 +1,52 @@ + + + + MarkEdit Dark Theme Example + + + + + + + + + + + + + + + diff --git a/examples/flickr-image-gallery.html b/examples/flickr-image-gallery.html new file mode 100644 index 00000000..894168dd --- /dev/null +++ b/examples/flickr-image-gallery.html @@ -0,0 +1,97 @@ + + + + MarkEdit Basic Example + + + + + + + + + + + + + + + diff --git a/examples/in-chinese.html b/examples/in-chinese.html new file mode 100644 index 00000000..01a2d996 --- /dev/null +++ b/examples/in-chinese.html @@ -0,0 +1,51 @@ + + + + MarkEdit国际语言演示 + + + + + + + + + + + + + + + + + + diff --git a/examples/in-place-editing-with-preview.html b/examples/in-place-editing-with-preview.html new file mode 100644 index 00000000..9aab43ac --- /dev/null +++ b/examples/in-place-editing-with-preview.html @@ -0,0 +1,74 @@ + + + + MarkEdit In Place Editing Example + + + + + + + + + + + + + +

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

+ +

It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Whatever

+ +

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.

+ + + diff --git a/examples/in-place-editing.html b/examples/in-place-editing.html new file mode 100644 index 00000000..5d93e135 --- /dev/null +++ b/examples/in-place-editing.html @@ -0,0 +1,71 @@ + + + + MarkEdit In Place Editing Example + + + + + + + + + + + + + +

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

+ +

It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Whatever

+ +

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.

+ + + diff --git a/examples/multiple-editors.html b/examples/multiple-editors.html new file mode 100644 index 00000000..3f7757de --- /dev/null +++ b/examples/multiple-editors.html @@ -0,0 +1,60 @@ + + + + MarkEdit Basic Example + + + + + + + + + + + + + + + + + + + + diff --git a/examples/multiple-preview-panes.html b/examples/multiple-preview-panes.html new file mode 100644 index 00000000..927e0a6e --- /dev/null +++ b/examples/multiple-preview-panes.html @@ -0,0 +1,62 @@ + + + + MarkEdit Basic Example + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ + + diff --git a/images/Thumbs.db b/images/Thumbs.db new file mode 100644 index 00000000..eb9dc6e5 Binary files /dev/null and b/images/Thumbs.db differ diff --git a/images/wmd-buttons-dark.png b/images/wmd-buttons-dark.png new file mode 100644 index 00000000..d0510fcc Binary files /dev/null and b/images/wmd-buttons-dark.png differ diff --git a/images/wmd-buttons-dark.psd b/images/wmd-buttons-dark.psd new file mode 100644 index 00000000..75330cc7 Binary files /dev/null and b/images/wmd-buttons-dark.psd differ diff --git a/images/wmd-buttons.png b/images/wmd-buttons.png index 50b37090..34e25ef2 100644 Binary files a/images/wmd-buttons.png and b/images/wmd-buttons.png differ diff --git a/images/wmd-buttons.psd b/images/wmd-buttons.psd index e61ff377..c69e6cbb 100644 Binary files a/images/wmd-buttons.psd and b/images/wmd-buttons.psd differ diff --git a/jQuery/jquery-1.2.6.js b/jQuery/jquery-1.2.6.js deleted file mode 100644 index 88e661ee..00000000 --- a/jQuery/jquery-1.2.6.js +++ /dev/null @@ -1,3549 +0,0 @@ -(function(){ -/* - * jQuery 1.2.6 - New Wave Javascript - * - * Copyright (c) 2008 John Resig (jquery.com) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ - * $Rev: 5685 $ - */ - -// Map over jQuery in case of overwrite -var _jQuery = window.jQuery, -// Map over the $ in case of overwrite - _$ = window.$; - -var jQuery = window.jQuery = window.$ = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); -}; - -// A simple way to check for HTML strings or ID strings -// (both of which we optimize for) -var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/, - -// Is it a simple selector - isSimple = /^.[^:#\[\.]*$/, - -// Will speed up references to undefined, and allows munging its name. - undefined; - -jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { - // Make sure that a selection was provided - selector = selector || document; - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this[0] = selector; - this.length = 1; - return this; - } - // Handle HTML strings - if ( typeof selector == "string" ) { - // Are we dealing with HTML string or an ID? - var match = quickExpr.exec( selector ); - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) - selector = jQuery.clean( [ match[1] ], context ); - - // HANDLE: $("#id") - else { - var elem = document.getElementById( match[3] ); - - // Make sure an element was located - if ( elem ){ - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id != match[3] ) - return jQuery().find( selector ); - - // Otherwise, we inject the element directly into the jQuery object - return jQuery( elem ); - } - selector = []; - } - - // HANDLE: $(expr, [context]) - // (which is just equivalent to: $(content).find(expr) - } else - return jQuery( context ).find( selector ); - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) - return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector ); - - return this.setArray(jQuery.makeArray(selector)); - }, - - // The current version of jQuery being used - jquery: "1.2.6", - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - // The number of elements contained in the matched element set - length: 0, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == undefined ? - - // Return a 'clean' array - jQuery.makeArray( this ) : - - // Return just the object - this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - // Build a new jQuery matched element set - var ret = jQuery( elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Force the current matched set of elements to become - // the specified array of elements (destroying the stack in the process) - // You should use pushStack() in order to do this, but maintain the stack - setArray: function( elems ) { - // Resetting the length to 0, then using the native Array push - // is a super-fast way to populate an object with array-like properties - this.length = 0; - Array.prototype.push.apply( this, elems ); - - return this; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - var ret = -1; - - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem && elem.jquery ? elem[0] : elem - , this ); - }, - - attr: function( name, value, type ) { - var options = name; - - // Look for the case where we're accessing a style value - if ( name.constructor == String ) - if ( value === undefined ) - return this[0] && jQuery[ type || "attr" ]( this[0], name ); - - else { - options = {}; - options[ name ] = value; - } - - // Check to see if we're setting style values - return this.each(function(i){ - // Set all the styles - for ( name in options ) - jQuery.attr( - type ? - this.style : - this, - name, jQuery.prop( this, options[ name ], type, i, name ) - ); - }); - }, - - css: function( key, value ) { - // ignore negative width and height values - if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) - value = undefined; - return this.attr( key, value, "curCSS" ); - }, - - text: function( text ) { - if ( typeof text != "object" && text != null ) - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - - var ret = ""; - - jQuery.each( text || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - ret += this.nodeType != 1 ? - this.nodeValue : - jQuery.fn.text( [ this ] ); - }); - }); - - return ret; - }, - - wrapAll: function( html ) { - if ( this[0] ) - // The elements to wrap the target around - jQuery( html, this[0].ownerDocument ) - .clone() - .insertBefore( this[0] ) - .map(function(){ - var elem = this; - - while ( elem.firstChild ) - elem = elem.firstChild; - - return elem; - }) - .append(this); - - return this; - }, - - wrapInner: function( html ) { - return this.each(function(){ - jQuery( this ).contents().wrapAll( html ); - }); - }, - - wrap: function( html ) { - return this.each(function(){ - jQuery( this ).wrapAll( html ); - }); - }, - - append: function() { - return this.domManip(arguments, true, false, function(elem){ - if (this.nodeType == 1) - this.appendChild( elem ); - }); - }, - - prepend: function() { - return this.domManip(arguments, true, true, function(elem){ - if (this.nodeType == 1) - this.insertBefore( elem, this.firstChild ); - }); - }, - - before: function() { - return this.domManip(arguments, false, false, function(elem){ - this.parentNode.insertBefore( elem, this ); - }); - }, - - after: function() { - return this.domManip(arguments, false, true, function(elem){ - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - }, - - end: function() { - return this.prevObject || jQuery( [] ); - }, - - find: function( selector ) { - var elems = jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - }); - - return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ? - jQuery.unique( elems ) : - elems ); - }, - - clone: function( events ) { - // Do the clone - var ret = this.map(function(){ - if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { - // IE copies events bound via attachEvent when - // using cloneNode. Calling detachEvent on the - // clone will also remove the events from the orignal - // In order to get around this, we use innerHTML. - // Unfortunately, this means some modifications to - // attributes in IE that are actually only stored - // as properties will not be copied (such as the - // the name attribute on an input). - var clone = this.cloneNode(true), - container = document.createElement("div"); - container.appendChild(clone); - return jQuery.clean([container.innerHTML])[0]; - } else - return this.cloneNode(true); - }); - - // Need to set the expando to null on the cloned set if it exists - // removeData doesn't work here, IE removes it from the original as well - // this is primarily for IE but the data expando shouldn't be copied over in any browser - var clone = ret.find("*").andSelf().each(function(){ - if ( this[ expando ] != undefined ) - this[ expando ] = null; - }); - - // Copy the events from the original to the clone - if ( events === true ) - this.find("*").andSelf().each(function(i){ - if (this.nodeType == 3) - return; - var events = jQuery.data( this, "events" ); - - for ( var type in events ) - for ( var handler in events[ type ] ) - jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); - }); - - // Return the cloned set - return ret; - }, - - filter: function( selector ) { - return this.pushStack( - jQuery.isFunction( selector ) && - jQuery.grep(this, function(elem, i){ - return selector.call( elem, i ); - }) || - - jQuery.multiFilter( selector, this ) ); - }, - - not: function( selector ) { - if ( selector.constructor == String ) - // test special case where just one selector is passed in - if ( isSimple.test( selector ) ) - return this.pushStack( jQuery.multiFilter( selector, this, true ) ); - else - selector = jQuery.multiFilter( selector, this ); - - var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; - return this.filter(function() { - return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; - }); - }, - - add: function( selector ) { - return this.pushStack( jQuery.unique( jQuery.merge( - this.get(), - typeof selector == 'string' ? - jQuery( selector ) : - jQuery.makeArray( selector ) - ))); - }, - - is: function( selector ) { - return !!selector && jQuery.multiFilter( selector, this ).length > 0; - }, - - hasClass: function( selector ) { - return this.is( "." + selector ); - }, - - val: function( value ) { - if ( value == undefined ) { - - if ( this.length ) { - var elem = this[0]; - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specifc value for the option - value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value; - - // We don't need an array for one selects - if ( one ) - return value; - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - - // Everything else, we just grab the value - } else - return (this[0].value || "").replace(/\r/g, ""); - - } - - return undefined; - } - - if( value.constructor == Number ) - value += ''; - - return this.each(function(){ - if ( this.nodeType != 1 ) - return; - - if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) - this.checked = (jQuery.inArray(this.value, value) >= 0 || - jQuery.inArray(this.name, value) >= 0); - - else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(value); - - jQuery( "option", this ).each(function(){ - this.selected = (jQuery.inArray( this.value, values ) >= 0 || - jQuery.inArray( this.text, values ) >= 0); - }); - - if ( !values.length ) - this.selectedIndex = -1; - - } else - this.value = value; - }); - }, - - html: function( value ) { - return value == undefined ? - (this[0] ? - this[0].innerHTML : - null) : - this.empty().append( value ); - }, - - replaceWith: function( value ) { - return this.after( value ).remove(); - }, - - eq: function( i ) { - return this.slice( i, i + 1 ); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function(elem, i){ - return callback.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - data: function( key, value ){ - var parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value === undefined ) { - var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - if ( data === undefined && this.length ) - data = jQuery.data( this[0], key ); - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } else - return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ - jQuery.data( this, key, value ); - }); - }, - - removeData: function( key ){ - return this.each(function(){ - jQuery.removeData( this, key ); - }); - }, - - domManip: function( args, table, reverse, callback ) { - var clone = this.length > 1, elems; - - return this.each(function(){ - if ( !elems ) { - elems = jQuery.clean( args, this.ownerDocument ); - - if ( reverse ) - elems.reverse(); - } - - var obj = this; - - if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) - obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); - - var scripts = jQuery( [] ); - - jQuery.each(elems, function(){ - var elem = clone ? - jQuery( this ).clone( true )[0] : - this; - - // execute all scripts after the elements have been injected - if ( jQuery.nodeName( elem, "script" ) ) - scripts = scripts.add( elem ); - else { - // Remove any inner scripts for later evaluation - if ( elem.nodeType == 1 ) - scripts = scripts.add( jQuery( "script", elem ).remove() ); - - // Inject the elements into the document - callback.call( obj, elem ); - } - }); - - scripts.each( evalScript ); - }); - } -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -function evalScript( i, elem ) { - if ( elem.src ) - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild( elem ); -} - -function now(){ - return +new Date; -} - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; - - // Handle a deep copy situation - if ( target.constructor == Boolean ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target != "object" && typeof target != "function" ) - target = {}; - - // extend jQuery itself if only one argument is passed - if ( length == i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) - // Extend the base object - for ( var name in options ) { - var src = target[ name ], copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) - continue; - - // Recurse if we're merging object values - if ( deep && copy && typeof copy == "object" && !copy.nodeType ) - target[ name ] = jQuery.extend( deep, - // Never move original objects, clone them - src || ( copy.length != null ? [ ] : { } ) - , copy ); - - // Don't bring in undefined values - else if ( copy !== undefined ) - target[ name ] = copy; - - } - - // Return the modified object - return target; -}; - -var expando = "jQuery" + now(), uuid = 0, windowData = {}, - // exclude the following css properties to add px - exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, - // cache defaultView - defaultView = document.defaultView || {}; - -jQuery.extend({ - noConflict: function( deep ) { - window.$ = _$; - - if ( deep ) - window.jQuery = _jQuery; - - return jQuery; - }, - - // See test/unit/core.js for details concerning this function. - isFunction: function( fn ) { - return !!fn && typeof fn != "string" && !fn.nodeName && - fn.constructor != Array && /^[\s[]?function/.test( fn + "" ); - }, - - // check if an element is in a (or is an) XML document - isXMLDoc: function( elem ) { - return elem.documentElement && !elem.body || - elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; - }, - - // Evalulates a script in a global context - globalEval: function( data ) { - data = jQuery.trim( data ); - - if ( data ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.getElementsByTagName("head")[0] || document.documentElement, - script = document.createElement("script"); - - script.type = "text/javascript"; - if ( jQuery.browser.msie ) - script.text = data; - else - script.appendChild( document.createTextNode( data ) ); - - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709). - head.insertBefore( script, head.firstChild ); - head.removeChild( script ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); - }, - - cache: {}, - - data: function( elem, name, data ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // Compute a unique ID for the element - if ( !id ) - id = elem[ expando ] = ++uuid; - - // Only generate the data cache if we're - // trying to access or manipulate it - if ( name && !jQuery.cache[ id ] ) - jQuery.cache[ id ] = {}; - - // Prevent overriding the named cache with undefined values - if ( data !== undefined ) - jQuery.cache[ id ][ name ] = data; - - // Return the named cache data, or the ID for the element - return name ? - jQuery.cache[ id ][ name ] : - id; - }, - - removeData: function( elem, name ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( jQuery.cache[ id ] ) { - // Remove the section of cache data - delete jQuery.cache[ id ][ name ]; - - // If we've removed all the data, remove the element's cache - name = ""; - - for ( name in jQuery.cache[ id ] ) - break; - - if ( !name ) - jQuery.removeData( elem ); - } - - // Otherwise, we want to remove all of the element's data - } else { - // Clean up the element expando - try { - delete elem[ expando ]; - } catch(e){ - // IE has trouble directly removing the expando - // but it's ok with using removeAttribute - if ( elem.removeAttribute ) - elem.removeAttribute( expando ); - } - - // Completely remove the data cache - delete jQuery.cache[ id ]; - } - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, length = object.length; - - if ( args ) { - if ( length == undefined ) { - for ( name in object ) - if ( callback.apply( object[ name ], args ) === false ) - break; - } else - for ( ; i < length; ) - if ( callback.apply( object[ i++ ], args ) === false ) - break; - - // A special, fast, case for the most common use of each - } else { - if ( length == undefined ) { - for ( name in object ) - if ( callback.call( object[ name ], name, object[ name ] ) === false ) - break; - } else - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} - } - - return object; - }, - - prop: function( elem, value, type, i, name ) { - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, i ); - - // Handle passing in a number to a CSS property - return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, classNames ) { - jQuery.each((classNames || "").split(/\s+/), function(i, className){ - if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) - elem.className += (elem.className ? " " : "") + className; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, classNames ) { - if (elem.nodeType == 1) - elem.className = classNames != undefined ? - jQuery.grep(elem.className.split(/\s+/), function(className){ - return !jQuery.className.has( classNames, className ); - }).join(" ") : - ""; - }, - - // internal only, use hasClass("class") - has: function( elem, className ) { - return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; - } - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var old = {}; - // Remember the old values, and insert the new ones - for ( var name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - callback.call( elem ); - - // Revert the old values - for ( var name in options ) - elem.style[ name ] = old[ name ]; - }, - - css: function( elem, name, force ) { - if ( name == "width" || name == "height" ) { - var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; - - function getWH() { - val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - var padding = 0, border = 0; - jQuery.each( which, function() { - padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; - }); - val -= Math.round(padding + border); - } - - if ( jQuery(elem).is(":visible") ) - getWH(); - else - jQuery.swap( elem, props, getWH ); - - return Math.max(0, val); - } - - return jQuery.curCSS( elem, name, force ); - }, - - curCSS: function( elem, name, force ) { - var ret, style = elem.style; - - // A helper method for determining if an element's values are broken - function color( elem ) { - if ( !jQuery.browser.safari ) - return false; - - // defaultView is cached - var ret = defaultView.getComputedStyle( elem, null ); - return !ret || ret.getPropertyValue("color") == ""; - } - - // We need to handle opacity special in IE - if ( name == "opacity" && jQuery.browser.msie ) { - ret = jQuery.attr( style, "opacity" ); - - return ret == "" ? - "1" : - ret; - } - // Opera sometimes will give the wrong display answer, this fixes it, see #2037 - if ( jQuery.browser.opera && name == "display" ) { - var save = style.outline; - style.outline = "0 solid black"; - style.outline = save; - } - - // Make sure we're using the right name for getting the float value - if ( name.match( /float/i ) ) - name = styleFloat; - - if ( !force && style && style[ name ] ) - ret = style[ name ]; - - else if ( defaultView.getComputedStyle ) { - - // Only "float" is needed here - if ( name.match( /float/i ) ) - name = "float"; - - name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); - - var computedStyle = defaultView.getComputedStyle( elem, null ); - - if ( computedStyle && !color( elem ) ) - ret = computedStyle.getPropertyValue( name ); - - // If the element isn't reporting its values properly in Safari - // then some display: none elements are involved - else { - var swap = [], stack = [], a = elem, i = 0; - - // Locate all of the parent display: none elements - for ( ; a && color(a); a = a.parentNode ) - stack.unshift(a); - - // Go through and make them visible, but in reverse - // (It would be better if we knew the exact display type that they had) - for ( ; i < stack.length; i++ ) - if ( color( stack[ i ] ) ) { - swap[ i ] = stack[ i ].style.display; - stack[ i ].style.display = "block"; - } - - // Since we flip the display style, we have to handle that - // one special, otherwise get the value - ret = name == "display" && swap[ stack.length - 1 ] != null ? - "none" : - ( computedStyle && computedStyle.getPropertyValue( name ) ) || ""; - - // Finally, revert the display styles back - for ( i = 0; i < swap.length; i++ ) - if ( swap[ i ] != null ) - stack[ i ].style.display = swap[ i ]; - } - - // We should always get a number back from opacity - if ( name == "opacity" && ret == "" ) - ret = "1"; - - } else if ( elem.currentStyle ) { - var camelCase = name.replace(/\-(\w)/g, function(all, letter){ - return letter.toUpperCase(); - }); - - ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { - // Remember the original values - var left = style.left, rsLeft = elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - elem.runtimeStyle.left = elem.currentStyle.left; - style.left = ret || 0; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - elem.runtimeStyle.left = rsLeft; - } - } - - return ret; - }, - - clean: function( elems, context ) { - var ret = []; - context = context || document; - // !context.createElement fails in IE with an error but returns typeof 'object' - if (typeof context.createElement == 'undefined') - context = context.ownerDocument || context[0] && context[0].ownerDocument || document; - - jQuery.each(elems, function(i, elem){ - if ( !elem ) - return; - - if ( elem.constructor == Number ) - elem += ''; - - // Convert html string into DOM nodes - if ( typeof elem == "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div"); - - var wrap = - // option or optgroup - !tags.indexOf("" ] || - - !tags.indexOf("
", "
" ] || - - tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [ 1, "", "
" ] || - - !tags.indexOf("", "
" ] || - - // matched above - (!tags.indexOf("", "
" ] || - - !tags.indexOf("", "
" ] || - - // IE can't serialize and - -3. Add wmd.js right before your closing body tag: - - - -4. Put the wmd-buttons.png someplace. You can modify this image if you don't - like the visual look. The .psd file contains easily-edited layers. Changing - the size of the buttons might cause problems where you have to edit the wmd - source. Please message me on github (link below) if this sucks for you so - I can improve support for various button sizes. - -5. Pop open the source to change the variables at the top that require - changing (image paths, dialogs, etc.). - -6. I'd minify the source. It'll get a LOT smaller. Eventually I'll provide a - minified version. - - - -You need to create: -------------------- - -0. For all of these, I use the CSS class "wmd-panel" but this isn't required. - The ids given below are very important as the wmd code uses them to find - the panels. - -1. A button bar div - - This will contain WMD's buttons. id is "wmd-button-bar". - -2. An input textarea - - This is where you'll enter markdown. id is "wmd-input". - -3. A preview div (optional but recommended) - - This will give you a live preview of your markdown. id is "wmd-preview". - -4. An HTML preview div (optional and you probably don't need this) - - This will show the raw HTML that the markdown will produce. Not so - useful for most web pages but useful for troubleshooting WMD :) id - is "wmd-output". - -Example: - - - - - Test WMD Page - - - - - - -
-
- -
-
-
-
- - - - - - -Support -------- - -If you're having trouble getting WMD up and running, feel free to -message me on github: http://github.com/derobins +http://wiki.github.com/tstone/jquery-markedit \ No newline at end of file diff --git a/wmd-test.html b/wmd-test.html deleted file mode 100644 index 37ad051f..00000000 --- a/wmd-test.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - Test WMD Page - - - - - - - -
-
- -
-
-
- -

To test that page up/down and arrow keys work, copy this above the WMD - control.

- -

- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
- Scroll Down!
-

- - - - diff --git a/wmd.css b/wmd.css deleted file mode 100644 index ee8380f1..00000000 --- a/wmd.css +++ /dev/null @@ -1,139 +0,0 @@ -body -{ - background-color: White -} - -.wmd-panel -{ - margin-left: 25%; - margin-right: 25%; - width: 50%; - min-width: 500px; -} - -#wmd-editor -{ - background-color: Aquamarine; -} - -#wmd-button-bar -{ - width: 100%; - background-color: Silver; -} - -#wmd-input -{ - height: 500px; - width: 100%; - background-color: Gainsboro; - border: 1px solid DarkGray; -} - -#wmd-preview -{ - background-color: LightSkyBlue; -} - -#wmd-output -{ - background-color: Pink; -} - -#wmd-button-row -{ - position: relative; - margin-left: 5px; - margin-right: 5px; - margin-bottom: 5px; - margin-top: 10px; - padding: 0px; - height: 20px; -} - -.wmd-spacer -{ - width: 1px; - height: 20px; - margin-left: 14px; - - position: absolute; - background-color: Silver; - display: inline-block; - list-style: none; -} - -.wmd-button -{ - width: 20px; - height: 20px; - margin-left: 5px; - margin-right: 5px; - - position: absolute; - background-image: url(images/wmd-buttons.png); - background-repeat: no-repeat; - background-position: 0px 0px; - display: inline-block; - list-style: none; -} - -.wmd-button > a -{ - width: 20px; - height: 20px; - margin-left: 5px; - margin-right: 5px; - - position: absolute; - display: inline-block; -} - - -/* sprite button slicing style information */ -#wmd-button-bar #wmd-bold-button {left: 0px; background-position: 0px 0;} -#wmd-button-bar #wmd-italic-button {left: 25px; background-position: -20px 0;} -#wmd-button-bar #wmd-spacer1 {left: 50px;} -#wmd-button-bar #wmd-link-button {left: 75px; background-position: -40px 0;} -#wmd-button-bar #wmd-quote-button {left: 100px; background-position: -60px 0;} -#wmd-button-bar #wmd-code-button {left: 125px; background-position: -80px 0;} -#wmd-button-bar #wmd-image-button {left: 150px; background-position: -100px 0;} -#wmd-button-bar #wmd-spacer2 {left: 175px;} -#wmd-button-bar #wmd-olist-button {left: 200px; background-position: -120px 0;} -#wmd-button-bar #wmd-ulist-button {left: 225px; background-position: -140px 0;} -#wmd-button-bar #wmd-heading-button {left: 250px; background-position: -160px 0;} -#wmd-button-bar #wmd-hr-button {left: 275px; background-position: -180px 0;} -#wmd-button-bar #wmd-spacer3 {left: 300px;} -#wmd-button-bar #wmd-undo-button {left: 325px; background-position: -200px 0;} -#wmd-button-bar #wmd-redo-button {left: 350px; background-position: -220px 0;} -#wmd-button-bar #wmd-help-button {right: 0px; background-position: -240px 0;} - - -.wmd-prompt-background -{ - background-color: Black; -} - -.wmd-prompt-dialog -{ - border: 1px solid #999999; - background-color: #F5F5F5; -} - -.wmd-prompt-dialog > div { - font-size: 0.8em; - font-family: arial, helvetica, sans-serif; -} - - -.wmd-prompt-dialog > form > input[type="text"] { - border: 1px solid #999999; - color: black; -} - -.wmd-prompt-dialog > form > input[type="button"]{ - border: 1px solid #888888; - font-family: trebuchet MS, helvetica, sans-serif; - font-size: 0.8em; - font-weight: bold; -} diff --git a/wmd.js b/wmd.js deleted file mode 100644 index a8e691b5..00000000 --- a/wmd.js +++ /dev/null @@ -1,2378 +0,0 @@ -var Attacklab = Attacklab || {}; - -Attacklab.wmdBase = function(){ - - // A few handy aliases for readability. - var wmd = top.Attacklab; - var doc = top.document; - var re = top.RegExp; - var nav = top.navigator; - - // Some namespaces. - wmd.Util = {}; - wmd.Position = {}; - wmd.Command = {}; - wmd.Global = {}; - - var util = wmd.Util; - var position = wmd.Position; - var command = wmd.Command; - var global = wmd.Global; - - - // Used to work around some browser bugs where we can't use feature testing. - global.isIE = /msie/.test(nav.userAgent.toLowerCase()); - global.isIE_5or6 = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase()); - global.isIE_7plus = global.isIE && !global.isIE_5or6; - global.isOpera = /opera/.test(nav.userAgent.toLowerCase()); - global.isKonqueror = /konqueror/.test(nav.userAgent.toLowerCase()); - - - // ------------------------------------------------------------------- - // YOUR CHANGES GO HERE - // - // I've tried to localize the things you are likely to change to - // this area. - // ------------------------------------------------------------------- - - // The text that appears on the upper part of the dialog box when - // entering links. - var imageDialogText = "

Enter the image URL.

You can also add a title, which will be displayed as a tool tip.

Example:
http://wmd-editor.com/images/cloud1.jpg \"Optional title\"

"; - var linkDialogText = "

Enter the web address.

You can also add a title, which will be displayed as a tool tip.

Example:
http://wmd-editor.com/ \"Optional title\"

"; - - // The default text that appears in the dialog input box when entering - // links. - var imageDefaultText = "http://"; - var linkDefaultText = "http://"; - - // The location of your button images relative to the base directory. - var imageDirectory = "images/"; - - // Some intervals in ms. These can be adjusted to reduce the control's load. - var previewPollInterval = 500; - var pastePollInterval = 100; - - // The link and title for the help button - var helpLink = "http://wmd-editor.com/"; - var helpHoverTitle = "WMD website"; - var helpTarget = "_blank"; - - // ------------------------------------------------------------------- - // END OF YOUR CHANGES - // ------------------------------------------------------------------- - - // A collection of the important regions on the page. - // Cached so we don't have to keep traversing the DOM. - wmd.PanelCollection = function(){ - this.buttonBar = doc.getElementById("wmd-button-bar"); - this.preview = doc.getElementById("wmd-preview"); - this.output = doc.getElementById("wmd-output"); - this.input = doc.getElementById("wmd-input"); - }; - - // This PanelCollection object can't be filled until after the page - // has loaded. - wmd.panels = undefined; - - // Internet explorer has problems with CSS sprite buttons that use HTML - // lists. When you click on the background image "button", IE will - // select the non-existent link text and discard the selection in the - // textarea. The solution to this is to cache the textarea selection - // on the button's mousedown event and set a flag. In the part of the - // code where we need to grab the selection, we check for the flag - // and, if it's set, use the cached area instead of querying the - // textarea. - // - // This ONLY affects Internet Explorer (tested on versions 6, 7 - // and 8) and ONLY on button clicks. Keyboard shortcuts work - // normally since the focus never leaves the textarea. - wmd.ieCachedRange = null; // cached textarea selection - wmd.ieRetardedClick = false; // flag - - // Returns true if the DOM element is visible, false if it's hidden. - // Checks if display is anything other than none. - util.isVisible = function (elem) { - - if (window.getComputedStyle) { - // Most browsers - return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none"; - } - else if (elem.currentStyle) { - // IE - return elem.currentStyle["display"] !== "none"; - } - }; - - - // Adds a listener callback to a DOM element which is fired on a specified - // event. - util.addEvent = function(elem, event, listener){ - if (elem.attachEvent) { - // IE only. The "on" is mandatory. - elem.attachEvent("on" + event, listener); - } - else { - // Other browsers. - elem.addEventListener(event, listener, false); - } - }; - - - // Removes a listener callback from a DOM element which is fired on a specified - // event. - util.removeEvent = function(elem, event, listener){ - if (elem.detachEvent) { - // IE only. The "on" is mandatory. - elem.detachEvent("on" + event, listener); - } - else { - // Other browsers. - elem.removeEventListener(event, listener, false); - } - }; - - // Converts \r\n and \r to \n. - util.fixEolChars = function(text){ - text = text.replace(/\r\n/g, "\n"); - text = text.replace(/\r/g, "\n"); - return text; - }; - - // Extends a regular expression. Returns a new RegExp - // using pre + regex + post as the expression. - // Used in a few functions where we have a base - // expression and we want to pre- or append some - // conditions to it (e.g. adding "$" to the end). - // The flags are unchanged. - // - // regex is a RegExp, pre and post are strings. - util.extendRegExp = function(regex, pre, post){ - - if (pre === null || pre === undefined) - { - pre = ""; - } - if(post === null || post === undefined) - { - post = ""; - } - - var pattern = regex.toString(); - var flags = ""; - - // Replace the flags with empty space and store them. - // Technically, this can match incorrect flags like "gmm". - var result = pattern.match(/\/([gim]*)$/); - if (result === null) { - flags = result[0]; - } - else { - flags = ""; - } - - // Remove the flags and slash delimiters from the regular expression. - pattern = pattern.replace(/(^\/|\/[gim]*$)/g, ""); - pattern = pre + pattern + post; - - return new RegExp(pattern, flags); - } - - - // Sets the image for a button passed to the WMD editor. - // Returns a new element with the image attached. - // Adds several style properties to the image. - util.createImage = function(img){ - - var imgPath = imageDirectory + img; - - var elem = doc.createElement("img"); - elem.className = "wmd-button"; - elem.src = imgPath; - - return elem; - }; - - - // This simulates a modal dialog box and asks for the URL when you - // click the hyperlink or image buttons. - // - // text: The html for the input box. - // defaultInputText: The default value that appears in the input box. - // makeLinkMarkdown: The function which is executed when the prompt is dismissed, either via OK or Cancel - util.prompt = function(text, defaultInputText, makeLinkMarkdown){ - - // These variables need to be declared at this level since they are used - // in multiple functions. - var dialog; // The dialog box. - var background; // The background beind the dialog box. - var input; // The text box where you enter the hyperlink. - - - if (defaultInputText === undefined) { - defaultInputText = ""; - } - - // Used as a keydown event handler. Esc dismisses the prompt. - // Key code 27 is ESC. - var checkEscape = function(key){ - var code = (key.charCode || key.keyCode); - if (code === 27) { - close(true); - } - }; - - // Dismisses the hyperlink input box. - // isCancel is true if we don't care about the input text. - // isCancel is false if we are going to keep the text. - var close = function(isCancel){ - util.removeEvent(doc.body, "keydown", checkEscape); - var text = input.value; - - if (isCancel){ - text = null; - } - else{ - // Fixes common pasting errors. - text = text.replace('http://http://', 'http://'); - text = text.replace('http://https://', 'https://'); - text = text.replace('http://ftp://', 'ftp://'); - - if (text.indexOf('http://') === -1 && text.indexOf('ftp://') === -1 && text.indexOf('https://') === -1) { - text = 'http://' + text; - } - } - - dialog.parentNode.removeChild(dialog); - background.parentNode.removeChild(background); - makeLinkMarkdown(text); - return false; - }; - - // Creates the background behind the hyperlink text entry box. - // Most of this has been moved to CSS but the div creation and - // browser-specific hacks remain here. - var createBackground = function(){ - - background = doc.createElement("div"); - background.className = "wmd-prompt-background"; - style = background.style; - style.position = "absolute"; - style.top = "0"; - - style.zIndex = "1000"; - - // Some versions of Konqueror don't support transparent colors - // so we make the whole window transparent. - // - // Is this necessary on modern konqueror browsers? - if (global.isKonqueror){ - style.backgroundColor = "transparent"; - } - else if (global.isIE){ - style.filter = "alpha(opacity=50)"; - } - else { - style.opacity = "0.5"; - } - - var pageSize = position.getPageSize(); - style.height = pageSize[1] + "px"; - - if(global.isIE){ - style.left = doc.documentElement.scrollLeft; - style.width = doc.documentElement.clientWidth; - } - else { - style.left = "0"; - style.width = "100%"; - } - - doc.body.appendChild(background); - }; - - // Create the text input box form/window. - var createDialog = function(){ - - // The main dialog box. - dialog = doc.createElement("div"); - dialog.className = "wmd-prompt-dialog"; - dialog.style.padding = "10px;"; - dialog.style.position = "fixed"; - dialog.style.width = "400px"; - dialog.style.zIndex = "1001"; - - // The dialog text. - var question = doc.createElement("div"); - question.innerHTML = text; - question.style.padding = "5px"; - dialog.appendChild(question); - - // The web form container for the text box and buttons. - var form = doc.createElement("form"); - form.onsubmit = function(){ return close(false); }; - style = form.style; - style.padding = "0"; - style.margin = "0"; - style.cssFloat = "left"; - style.width = "100%"; - style.textAlign = "center"; - style.position = "relative"; - dialog.appendChild(form); - - // The input text box - input = doc.createElement("input"); - input.type = "text"; - input.value = defaultInputText; - style = input.style; - style.display = "block"; - style.width = "80%"; - style.marginLeft = style.marginRight = "auto"; - form.appendChild(input); - - // The ok button - var okButton = doc.createElement("input"); - okButton.type = "button"; - okButton.onclick = function(){ return close(false); }; - okButton.value = "OK"; - style = okButton.style; - style.margin = "10px"; - style.display = "inline"; - style.width = "7em"; - - - // The cancel button - var cancelButton = doc.createElement("input"); - cancelButton.type = "button"; - cancelButton.onclick = function(){ return close(true); }; - cancelButton.value = "Cancel"; - style = cancelButton.style; - style.margin = "10px"; - style.display = "inline"; - style.width = "7em"; - - // The order of these buttons is different on macs. - if (/mac/.test(nav.platform.toLowerCase())) { - form.appendChild(cancelButton); - form.appendChild(okButton); - } - else { - form.appendChild(okButton); - form.appendChild(cancelButton); - } - - util.addEvent(doc.body, "keydown", checkEscape); - dialog.style.top = "50%"; - dialog.style.left = "50%"; - dialog.style.display = "block"; - if(global.isIE_5or6){ - dialog.style.position = "absolute"; - dialog.style.top = doc.documentElement.scrollTop + 200 + "px"; - dialog.style.left = "50%"; - } - doc.body.appendChild(dialog); - - // This has to be done AFTER adding the dialog to the form if you - // want it to be centered. - dialog.style.marginTop = -(position.getHeight(dialog) / 2) + "px"; - dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + "px"; - - }; - - createBackground(); - - // Why is this in a zero-length timeout? - // Is it working around a browser bug? - top.setTimeout(function(){ - - createDialog(); - - var defTextLen = defaultInputText.length; - if (input.selectionStart !== undefined) { - input.selectionStart = 0; - input.selectionEnd = defTextLen; - } - else if (input.createTextRange) { - var range = input.createTextRange(); - range.collapse(false); - range.moveStart("character", -defTextLen); - range.moveEnd("character", defTextLen); - range.select(); - } - - input.focus(); - }, 0); - }; - - - // UNFINISHED - // The assignment in the while loop makes jslint cranky. - // I'll change it to a better loop later. - position.getTop = function(elem, isInner){ - var result = elem.offsetTop; - if (!isInner) { - while (elem = elem.offsetParent) { - result += elem.offsetTop; - } - } - return result; - }; - - position.getHeight = function (elem) { - return elem.offsetHeight || elem.scrollHeight; - }; - - position.getWidth = function (elem) { - return elem.offsetWidth || elem.scrollWidth; - }; - - position.getPageSize = function(){ - - var scrollWidth, scrollHeight; - var innerWidth, innerHeight; - - // It's not very clear which blocks work with which browsers. - if(self.innerHeight && self.scrollMaxY){ - scrollWidth = doc.body.scrollWidth; - scrollHeight = self.innerHeight + self.scrollMaxY; - } - else if(doc.body.scrollHeight > doc.body.offsetHeight){ - scrollWidth = doc.body.scrollWidth; - scrollHeight = doc.body.scrollHeight; - } - else{ - scrollWidth = doc.body.offsetWidth; - scrollHeight = doc.body.offsetHeight; - } - - if(self.innerHeight){ - // Non-IE browser - innerWidth = self.innerWidth; - innerHeight = self.innerHeight; - } - else if(doc.documentElement && doc.documentElement.clientHeight){ - // Some versions of IE (IE 6 w/ a DOCTYPE declaration) - innerWidth = doc.documentElement.clientWidth; - innerHeight = doc.documentElement.clientHeight; - } - else if(doc.body){ - // Other versions of IE - innerWidth = doc.body.clientWidth; - innerHeight = doc.body.clientHeight; - } - - var maxWidth = Math.max(scrollWidth, innerWidth); - var maxHeight = Math.max(scrollHeight, innerHeight); - return [maxWidth, maxHeight, innerWidth, innerHeight]; - }; - - // Watches the input textarea, polling at an interval and runs - // a callback function if anything has changed. - wmd.inputPoller = function(callback, interval){ - - var pollerObj = this; - var inputArea = wmd.panels.input; - - // Stored start, end and text. Used to see if there are changes to the input. - var lastStart; - var lastEnd; - var markdown; - - var killHandle; // Used to cancel monitoring on destruction. - // Checks to see if anything has changed in the textarea. - // If so, it runs the callback. - this.tick = function(){ - - if (!util.isVisible(inputArea)) { - return; - } - - // Update the selection start and end, text. - if (inputArea.selectionStart || inputArea.selectionStart === 0) { - var start = inputArea.selectionStart; - var end = inputArea.selectionEnd; - if (start != lastStart || end != lastEnd) { - lastStart = start; - lastEnd = end; - - if (markdown != inputArea.value) { - markdown = inputArea.value; - return true; - } - } - } - return false; - }; - - - var doTickCallback = function(){ - - if (!util.isVisible(inputArea)) { - return; - } - - // If anything has changed, call the function. - if (pollerObj.tick()) { - callback(); - } - }; - - // Set how often we poll the textarea for changes. - var assignInterval = function(){ - // previewPollInterval is set at the top of the namespace. - killHandle = top.setInterval(doTickCallback, interval); - }; - - this.destroy = function(){ - top.clearInterval(killHandle); - }; - - assignInterval(); - }; - - // Handles pushing and popping TextareaStates for undo/redo commands. - // I should rename the stack variables to list. - wmd.undoManager = function(callback){ - - var undoObj = this; - var undoStack = []; // A stack of undo states - var stackPtr = 0; // The index of the current state - var mode = "none"; - var lastState; // The last state - var poller; - var timer; // The setTimeout handle for cancelling the timer - var inputStateObj; - - // Set the mode for later logic steps. - var setMode = function(newMode, noSave){ - - if (mode != newMode) { - mode = newMode; - if (!noSave) { - saveState(); - } - } - - if (!global.isIE || mode != "moving") { - timer = top.setTimeout(refreshState, 1); - } - else { - inputStateObj = null; - } - }; - - var refreshState = function(){ - inputStateObj = new wmd.TextareaState(); - poller.tick(); - timer = undefined; - }; - - this.setCommandMode = function(){ - mode = "command"; - saveState(); - timer = top.setTimeout(refreshState, 0); - }; - - this.canUndo = function(){ - return stackPtr > 1; - }; - - this.canRedo = function(){ - if (undoStack[stackPtr + 1]) { - return true; - } - return false; - }; - - // Removes the last state and restores it. - this.undo = function(){ - - if (undoObj.canUndo()) { - if (lastState) { - // What about setting state -1 to null or checking for undefined? - lastState.restore(); - lastState = null; - } - else { - undoStack[stackPtr] = new wmd.TextareaState(); - undoStack[--stackPtr].restore(); - - if (callback) { - callback(); - } - } - } - - mode = "none"; - wmd.panels.input.focus(); - refreshState(); - }; - - // Redo an action. - this.redo = function(){ - - if (undoObj.canRedo()) { - - undoStack[++stackPtr].restore(); - - if (callback) { - callback(); - } - } - - mode = "none"; - wmd.panels.input.focus(); - refreshState(); - }; - - // Push the input area state to the stack. - var saveState = function(){ - - var currState = inputStateObj || new wmd.TextareaState(); - - if (!currState) { - return false; - } - if (mode == "moving") { - if (!lastState) { - lastState = currState; - } - return; - } - if (lastState) { - if (undoStack[stackPtr - 1].text != lastState.text) { - undoStack[stackPtr++] = lastState; - } - lastState = null; - } - undoStack[stackPtr++] = currState; - undoStack[stackPtr + 1] = null; - if (callback) { - callback(); - } - }; - - var handleCtrlYZ = function(event){ - - var handled = false; - - if (event.ctrlKey || event.metaKey) { - - // IE and Opera do not support charCode. - var keyCode = event.charCode || event.keyCode; - var keyCodeChar = String.fromCharCode(keyCode); - - switch (keyCodeChar) { - - case "y": - undoObj.redo(); - handled = true; - break; - - case "z": - if (!event.shiftKey) { - undoObj.undo(); - } - else { - undoObj.redo(); - } - handled = true; - break; - } - } - - if (handled) { - if (event.preventDefault) { - event.preventDefault(); - } - if (top.event) { - top.event.returnValue = false; - } - return; - } - }; - - // Set the mode depending on what is going on in the input area. - var handleModeChange = function(event){ - - if (!event.ctrlKey && !event.metaKey) { - - var keyCode = event.keyCode; - - if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) { - // 33 - 40: page up/dn and arrow keys - // 63232 - 63235: page up/dn and arrow keys on safari - setMode("moving"); - } - else if (keyCode == 8 || keyCode == 46 || keyCode == 127) { - // 8: backspace - // 46: delete - // 127: delete - setMode("deleting"); - } - else if (keyCode == 13) { - // 13: Enter - setMode("newlines"); - } - else if (keyCode == 27) { - // 27: escape - setMode("escape"); - } - else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) { - // 16-20 are shift, etc. - // 91: left window key - // I think this might be a little messed up since there are - // a lot of nonprinting keys above 20. - setMode("typing"); - } - } - }; - - var setEventHandlers = function(){ - - util.addEvent(wmd.panels.input, "keypress", function(event){ - // keyCode 89: y - // keyCode 90: z - if ((event.ctrlKey || event.metaKey) && (event.keyCode == 89 || event.keyCode == 90)) { - event.preventDefault(); - } - }); - - var handlePaste = function(){ - if (global.isIE || (inputStateObj && inputStateObj.text != wmd.panels.input.value)) { - if (timer == undefined) { - mode = "paste"; - saveState(); - refreshState(); - } - } - }; - - // pastePollInterval is specified at the beginning of this namespace. - poller = new wmd.inputPoller(handlePaste, pastePollInterval); - - util.addEvent(wmd.panels.input, "keydown", handleCtrlYZ); - util.addEvent(wmd.panels.input, "keydown", handleModeChange); - - util.addEvent(wmd.panels.input, "mousedown", function(){ - setMode("moving"); - }); - wmd.panels.input.onpaste = handlePaste; - wmd.panels.input.ondrop = handlePaste; - }; - - var init = function(){ - setEventHandlers(); - refreshState(); - saveState(); - }; - - this.destroy = function(){ - if (poller) { - poller.destroy(); - } - }; - - init(); - }; - - // I think my understanding of how the buttons and callbacks are stored in the array is incomplete. - wmd.editor = function(previewRefreshCallback){ - - if (!previewRefreshCallback) { - previewRefreshCallback = function(){}; - } - - var inputBox = wmd.panels.input; - - var offsetHeight = 0; - - var editObj = this; - - var mainDiv; - var mainSpan; - - var div; // This name is pretty ambiguous. I should rename this. - - // Used to cancel recurring events from setInterval. - var creationHandle; - - var undoMgr; // The undo manager - - // Perform the button's action. - var doClick = function(button){ - - inputBox.focus(); - - if (button.textOp) { - - if (undoMgr) { - undoMgr.setCommandMode(); - } - - var state = new wmd.TextareaState(); - - if (!state) { - return; - } - - var chunks = state.getChunks(); - - // Some commands launch a "modal" prompt dialog. Javascript - // can't really make a modal dialog box and the WMD code - // will continue to execute while the dialog is displayed. - // This prevents the dialog pattern I'm used to and means - // I can't do something like this: - // - // var link = CreateLinkDialog(); - // makeMarkdownLink(link); - // - // Instead of this straightforward method of handling a - // dialog I have to pass any code which would execute - // after the dialog is dismissed (e.g. link creation) - // in a function parameter. - // - // Yes this is awkward and I think it sucks, but there's - // no real workaround. Only the image and link code - // create dialogs and require the function pointers. - var fixupInputArea = function(){ - - inputBox.focus(); - - if (chunks) { - state.setChunks(chunks); - } - - state.restore(); - previewRefreshCallback(); - }; - - var useDefaultText = true; - var noCleanup = button.textOp(chunks, fixupInputArea, useDefaultText); - - if(!noCleanup) { - fixupInputArea(); - } - - } - - if (button.execute) { - button.execute(editObj); - } - }; - - var setUndoRedoButtonStates = function(){ - if(undoMgr){ - setupButton(document.getElementById("wmd-undo-button"), undoMgr.canUndo()); - setupButton(document.getElementById("wmd-redo-button"), undoMgr.canRedo()); - } - }; - - var setupButton = function(button, isEnabled) { - - var normalYShift = "0px"; - var disabledYShift = "-20px"; - var highlightYShift = "-40px"; - - if(isEnabled) { - button.style.backgroundPosition = button.XShift + " " + normalYShift; - button.onmouseover = function(){ - this.style.backgroundPosition = this.XShift + " " + highlightYShift; - }; - - button.onmouseout = function(){ - this.style.backgroundPosition = this.XShift + " " + normalYShift; - }; - - // IE tries to select the background image "button" text (it's - // implemented in a list item) so we have to cache the selection - // on mousedown. - if(global.isIE) { - button.onmousedown = function() { - wmd.ieRetardedClick = true; - wmd.ieCachedRange = document.selection.createRange(); - }; - } - - if (!button.isHelp) - { - button.onclick = function() { - if (this.onmouseout) { - this.onmouseout(); - } - doClick(this); - return false; - } - } - } - else { - button.style.backgroundPosition = button.XShift + " " + disabledYShift; - button.onmouseover = button.onmouseout = button.onclick = function(){}; - } - } - - var makeSpritedButtonRow = function(){ - - var buttonBar = document.getElementById("wmd-button-bar"); - - var normalYShift = "0px"; - var disabledYShift = "-20px"; - var highlightYShift = "-40px"; - - var buttonRow = document.createElement("ul"); - buttonRow.id = "wmd-button-row"; - buttonRow = buttonBar.appendChild(buttonRow); - - - var boldButton = document.createElement("li"); - boldButton.className = "wmd-button"; - boldButton.id = "wmd-bold-button"; - boldButton.title = "Strong Ctrl+B"; - boldButton.XShift = "0px"; - boldButton.textOp = command.doBold; - setupButton(boldButton, true); - buttonRow.appendChild(boldButton); - - var italicButton = document.createElement("li"); - italicButton.className = "wmd-button"; - italicButton.id = "wmd-italic-button"; - italicButton.title = "Emphasis Ctrl+I"; - italicButton.XShift = "-20px"; - italicButton.textOp = command.doItalic; - setupButton(italicButton, true); - buttonRow.appendChild(italicButton); - - var spacer1 = document.createElement("li"); - spacer1.className = "wmd-spacer"; - spacer1.id = "wmd-spacer1"; - buttonRow.appendChild(spacer1); - - var linkButton = document.createElement("li"); - linkButton.className = "wmd-button"; - linkButton.id = "wmd-link-button"; - linkButton.title = "Hyperlink Ctrl+L"; - linkButton.XShift = "-40px"; - linkButton.textOp = function(chunk, postProcessing, useDefaultText){ - return command.doLinkOrImage(chunk, postProcessing, false); - }; - setupButton(linkButton, true); - buttonRow.appendChild(linkButton); - - var quoteButton = document.createElement("li"); - quoteButton.className = "wmd-button"; - quoteButton.id = "wmd-quote-button"; - quoteButton.title = "Blockquote
Ctrl+Q"; - quoteButton.XShift = "-60px"; - quoteButton.textOp = command.doBlockquote; - setupButton(quoteButton, true); - buttonRow.appendChild(quoteButton); - - var codeButton = document.createElement("li"); - codeButton.className = "wmd-button"; - codeButton.id = "wmd-code-button"; - codeButton.title = "Code Sample
 Ctrl+K";
-			codeButton.XShift = "-80px";
-			codeButton.textOp = command.doCode;
-			setupButton(codeButton, true);
-			buttonRow.appendChild(codeButton);
-
-			var imageButton = document.createElement("li");
-			imageButton.className = "wmd-button";
-			imageButton.id = "wmd-image-button";
-			imageButton.title = "Image  Ctrl+G";
-			imageButton.XShift = "-100px";
-			imageButton.textOp = function(chunk, postProcessing, useDefaultText){
-				return command.doLinkOrImage(chunk, postProcessing, true);
-			};
-			setupButton(imageButton, true);
-			buttonRow.appendChild(imageButton);
-
-			var spacer2 = document.createElement("li");
-			spacer2.className = "wmd-spacer";
-			spacer2.id = "wmd-spacer2";
-			buttonRow.appendChild(spacer2); 
-
-			var olistButton = document.createElement("li");
-			olistButton.className = "wmd-button";
-			olistButton.id = "wmd-olist-button";
-			olistButton.title = "Numbered List 
    Ctrl+O"; - olistButton.XShift = "-120px"; - olistButton.textOp = function(chunk, postProcessing, useDefaultText){ - command.doList(chunk, postProcessing, true, useDefaultText); - }; - setupButton(olistButton, true); - buttonRow.appendChild(olistButton); - - var ulistButton = document.createElement("li"); - ulistButton.className = "wmd-button"; - ulistButton.id = "wmd-ulist-button"; - ulistButton.title = "Bulleted List
      Ctrl+U"; - ulistButton.XShift = "-140px"; - ulistButton.textOp = function(chunk, postProcessing, useDefaultText){ - command.doList(chunk, postProcessing, false, useDefaultText); - }; - setupButton(ulistButton, true); - buttonRow.appendChild(ulistButton); - - var headingButton = document.createElement("li"); - headingButton.className = "wmd-button"; - headingButton.id = "wmd-heading-button"; - headingButton.title = "Heading

      /

      Ctrl+H"; - headingButton.XShift = "-160px"; - headingButton.textOp = command.doHeading; - setupButton(headingButton, true); - buttonRow.appendChild(headingButton); - - var hrButton = document.createElement("li"); - hrButton.className = "wmd-button"; - hrButton.id = "wmd-hr-button"; - hrButton.title = "Horizontal Rule
      Ctrl+R"; - hrButton.XShift = "-180px"; - hrButton.textOp = command.doHorizontalRule; - setupButton(hrButton, true); - buttonRow.appendChild(hrButton); - - var spacer3 = document.createElement("li"); - spacer3.className = "wmd-spacer"; - spacer3.id = "wmd-spacer3"; - buttonRow.appendChild(spacer3); - - var undoButton = document.createElement("li"); - undoButton.className = "wmd-button"; - undoButton.id = "wmd-undo-button"; - undoButton.title = "Undo - Ctrl+Z"; - undoButton.XShift = "-200px"; - undoButton.execute = function(manager){ - manager.undo(); - }; - setupButton(undoButton, true); - buttonRow.appendChild(undoButton); - - var redoButton = document.createElement("li"); - redoButton.className = "wmd-button"; - redoButton.id = "wmd-redo-button"; - redoButton.title = "Redo - Ctrl+Y"; - if (/win/.test(nav.platform.toLowerCase())) { - redoButton.title = "Redo - Ctrl+Y"; - } - else { - // mac and other non-Windows platforms - redoButton.title = "Redo - Ctrl+Shift+Z"; - } - redoButton.XShift = "-220px"; - redoButton.execute = function(manager){ - manager.redo(); - }; - setupButton(redoButton, true); - buttonRow.appendChild(redoButton); - - var helpButton = document.createElement("li"); - helpButton.className = "wmd-button"; - helpButton.id = "wmd-help-button"; - helpButton.XShift = "-240px"; - helpButton.isHelp = true; - - var helpAnchor = document.createElement("a"); - helpAnchor.href = helpLink; - helpAnchor.target = helpTarget - helpAnchor.title = helpHoverTitle; - helpButton.appendChild(helpAnchor); - - setupButton(helpButton, true); - buttonRow.appendChild(helpButton); - - setUndoRedoButtonStates(); - } - - var setupEditor = function(){ - - if (/\?noundo/.test(doc.location.href)) { - wmd.nativeUndo = true; - } - - if (!wmd.nativeUndo) { - undoMgr = new wmd.undoManager(function(){ - previewRefreshCallback(); - setUndoRedoButtonStates(); - }); - } - - makeSpritedButtonRow(); - - - var keyEvent = "keydown"; - if (global.isOpera) { - keyEvent = "keypress"; - } - - util.addEvent(inputBox, keyEvent, function(key){ - - // Check to see if we have a button key and, if so execute the callback. - if (key.ctrlKey || key.metaKey) { - - var keyCode = key.charCode || key.keyCode; - var keyCodeStr = String.fromCharCode(keyCode).toLowerCase(); - - switch(keyCodeStr) { - case "b": - doClick(document.getElementById("wmd-bold-button")); - break; - case "i": - doClick(document.getElementById("wmd-italic-button")); - break; - case "l": - doClick(document.getElementById("wmd-link-button")); - break; - case "q": - doClick(document.getElementById("wmd-quote-button")); - break; - case "k": - doClick(document.getElementById("wmd-code-button")); - break; - case "g": - doClick(document.getElementById("wmd-image-button")); - break; - case "o": - doClick(document.getElementById("wmd-olist-button")); - break; - case "u": - doClick(document.getElementById("wmd-ulist-button")); - break; - case "h": - doClick(document.getElementById("wmd-heading-button")); - break; - case "r": - doClick(document.getElementById("wmd-hr-button")); - break; - case "y": - doClick(document.getElementById("wmd-redo-button")); - break; - case "z": - if(key.shiftKey) { - doClick(document.getElementById("wmd-redo-button")); - } - else { - doClick(document.getElementById("wmd-undo-button")); - } - break; - default: - return; - } - - - if (key.preventDefault) { - key.preventDefault(); - } - - if (top.event) { - top.event.returnValue = false; - } - } - }); - - // Auto-continue lists, code blocks and block quotes when - // the enter key is pressed. - util.addEvent(inputBox, "keyup", function(key){ - if (!key.shiftKey && !key.ctrlKey && !key.metaKey) { - var keyCode = key.charCode || key.keyCode; - // Key code 13 is Enter - if (keyCode === 13) { - fakeButton = {}; - fakeButton.textOp = command.doAutoindent; - doClick(fakeButton); - } - } - }); - - // Disable ESC clearing the input textarea on IE - if (global.isIE) { - util.addEvent(inputBox, "keydown", function(key){ - var code = key.keyCode; - // Key code 27 is ESC - if (code === 27) { - return false; - } - }); - } - - if (inputBox.form) { - var submitCallback = inputBox.form.onsubmit; - inputBox.form.onsubmit = function(){ - convertToHtml(); - if (submitCallback) { - return submitCallback.apply(this, arguments); - } - }; - } - }; - - // Convert the contents of the input textarea to HTML in the output/preview panels. - var convertToHtml = function(){ - - if (wmd.showdown) { - var markdownConverter = new wmd.showdown.converter(); - } - var text = inputBox.value; - - var callback = function(){ - inputBox.value = text; - }; - - if (!/markdown/.test(wmd.wmd_env.output.toLowerCase())) { - if (markdownConverter) { - inputBox.value = markdownConverter.makeHtml(text); - top.setTimeout(callback, 0); - } - } - return true; - }; - - - this.undo = function(){ - if (undoMgr) { - undoMgr.undo(); - } - }; - - this.redo = function(){ - if (undoMgr) { - undoMgr.redo(); - } - }; - - // This is pretty useless. The setupEditor function contents - // should just be copied here. - var init = function(){ - setupEditor(); - }; - - this.destroy = function(){ - if (undoMgr) { - undoMgr.destroy(); - } - if (div.parentNode) { - div.parentNode.removeChild(div); - } - if (inputBox) { - inputBox.style.marginTop = ""; - } - top.clearInterval(creationHandle); - }; - - init(); - }; - - // The input textarea state/contents. - // This is used to implement undo/redo by the undo manager. - wmd.TextareaState = function(){ - - // Aliases - var stateObj = this; - var inputArea = wmd.panels.input; - - this.init = function() { - - if (!util.isVisible(inputArea)) { - return; - } - - this.setInputAreaSelectionStartEnd(); - this.scrollTop = inputArea.scrollTop; - if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) { - this.text = inputArea.value; - } - - } - - // Sets the selected text in the input box after we've performed an - // operation. - this.setInputAreaSelection = function(){ - - if (!util.isVisible(inputArea)) { - return; - } - - if (inputArea.selectionStart !== undefined && !global.isOpera) { - - inputArea.focus(); - inputArea.selectionStart = stateObj.start; - inputArea.selectionEnd = stateObj.end; - inputArea.scrollTop = stateObj.scrollTop; - } - else if (doc.selection) { - - if (doc.activeElement && doc.activeElement !== inputArea) { - return; - } - - inputArea.focus(); - var range = inputArea.createTextRange(); - range.moveStart("character", -inputArea.value.length); - range.moveEnd("character", -inputArea.value.length); - range.moveEnd("character", stateObj.end); - range.moveStart("character", stateObj.start); - range.select(); - } - }; - - this.setInputAreaSelectionStartEnd = function(){ - - if (inputArea.selectionStart || inputArea.selectionStart === 0) { - - stateObj.start = inputArea.selectionStart; - stateObj.end = inputArea.selectionEnd; - } - else if (doc.selection) { - - stateObj.text = util.fixEolChars(inputArea.value); - - // IE loses the selection in the textarea when buttons are - // clicked. On IE we cache the selection and set a flag - // which we check for here. - var range; - if(wmd.ieRetardedClick && wmd.ieCachedRange) { - range = wmd.ieCachedRange; - wmd.ieRetardedClick = false; - } - else { - range = doc.selection.createRange(); - } - - var fixedRange = util.fixEolChars(range.text); - var marker = "\x07"; - var markedRange = marker + fixedRange + marker; - range.text = markedRange; - var inputText = util.fixEolChars(inputArea.value); - - range.moveStart("character", -markedRange.length); - range.text = fixedRange; - - stateObj.start = inputText.indexOf(marker); - stateObj.end = inputText.lastIndexOf(marker) - marker.length; - - var len = stateObj.text.length - util.fixEolChars(inputArea.value).length; - - if (len) { - range.moveStart("character", -fixedRange.length); - while (len--) { - fixedRange += "\n"; - stateObj.end += 1; - } - range.text = fixedRange; - } - - this.setInputAreaSelection(); - } - }; - - // Restore this state into the input area. - this.restore = function(){ - - if (stateObj.text != undefined && stateObj.text != inputArea.value) { - inputArea.value = stateObj.text; - } - this.setInputAreaSelection(); - inputArea.scrollTop = stateObj.scrollTop; - }; - - // Gets a collection of HTML chunks from the inptut textarea. - this.getChunks = function(){ - - var chunk = new wmd.Chunks(); - - chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start)); - chunk.startTag = ""; - chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end)); - chunk.endTag = ""; - chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end)); - chunk.scrollTop = stateObj.scrollTop; - - return chunk; - }; - - // Sets the TextareaState properties given a chunk of markdown. - this.setChunks = function(chunk){ - - chunk.before = chunk.before + chunk.startTag; - chunk.after = chunk.endTag + chunk.after; - - if (global.isOpera) { - chunk.before = chunk.before.replace(/\n/g, "\r\n"); - chunk.selection = chunk.selection.replace(/\n/g, "\r\n"); - chunk.after = chunk.after.replace(/\n/g, "\r\n"); - } - - this.start = chunk.before.length; - this.end = chunk.before.length + chunk.selection.length; - this.text = chunk.before + chunk.selection + chunk.after; - this.scrollTop = chunk.scrollTop; - }; - - this.init(); - }; - - // before: contains all the text in the input box BEFORE the selection. - // after: contains all the text in the input box AFTER the selection. - wmd.Chunks = function(){ - }; - - // startRegex: a regular expression to find the start tag - // endRegex: a regular expresssion to find the end tag - wmd.Chunks.prototype.findTags = function(startRegex, endRegex){ - - var chunkObj = this; - var regex; - - if (startRegex) { - - regex = util.extendRegExp(startRegex, "", "$"); - - this.before = this.before.replace(regex, - function(match){ - chunkObj.startTag = chunkObj.startTag + match; - return ""; - }); - - regex = util.extendRegExp(startRegex, "^", ""); - - this.selection = this.selection.replace(regex, - function(match){ - chunkObj.startTag = chunkObj.startTag + match; - return ""; - }); - } - - if (endRegex) { - - regex = util.extendRegExp(endRegex, "", "$"); - - this.selection = this.selection.replace(regex, - function(match){ - chunkObj.endTag = match + chunkObj.endTag; - return ""; - }); - - regex = util.extendRegExp(endRegex, "^", ""); - - this.after = this.after.replace(regex, - function(match){ - chunkObj.endTag = match + chunkObj.endTag; - return ""; - }); - } - }; - - // If remove is false, the whitespace is transferred - // to the before/after regions. - // - // If remove is true, the whitespace disappears. - wmd.Chunks.prototype.trimWhitespace = function(remove){ - - this.selection = this.selection.replace(/^(\s*)/, ""); - - if (!remove) { - this.before += re.$1; - } - - this.selection = this.selection.replace(/(\s*)$/, ""); - - if (!remove) { - this.after = re.$1 + this.after; - } - }; - - - wmd.Chunks.prototype.addBlankLines = function(nLinesBefore, nLinesAfter, findExtraNewlines){ - - if (nLinesBefore === undefined) { - nLinesBefore = 1; - } - - if (nLinesAfter === undefined) { - nLinesAfter = 1; - } - - nLinesBefore++; - nLinesAfter++; - - var regexText; - var replacementText; - - this.selection = this.selection.replace(/(^\n*)/, ""); - this.startTag = this.startTag + re.$1; - this.selection = this.selection.replace(/(\n*$)/, ""); - this.endTag = this.endTag + re.$1; - this.startTag = this.startTag.replace(/(^\n*)/, ""); - this.before = this.before + re.$1; - this.endTag = this.endTag.replace(/(\n*$)/, ""); - this.after = this.after + re.$1; - - if (this.before) { - - regexText = replacementText = ""; - - while (nLinesBefore--) { - regexText += "\\n?"; - replacementText += "\n"; - } - - if (findExtraNewlines) { - regexText = "\\n*"; - } - this.before = this.before.replace(new re(regexText + "$", ""), replacementText); - } - - if (this.after) { - - regexText = replacementText = ""; - - while (nLinesAfter--) { - regexText += "\\n?"; - replacementText += "\n"; - } - if (findExtraNewlines) { - regexText = "\\n*"; - } - - this.after = this.after.replace(new re(regexText, ""), replacementText); - } - }; - - // The markdown symbols - 4 spaces = code, > = blockquote, etc. - command.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)"; - - // Remove markdown symbols from the chunk selection. - command.unwrap = function(chunk){ - var txt = new re("([^\\n])\\n(?!(\\n|" + command.prefixes + "))", "g"); - chunk.selection = chunk.selection.replace(txt, "$1 $2"); - }; - - command.wrap = function(chunk, len){ - command.unwrap(chunk); - var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm"); - - chunk.selection = chunk.selection.replace(regex, function(line, marked){ - if (new re("^" + command.prefixes, "").test(line)) { - return line; - } - return marked + "\n"; - }); - - chunk.selection = chunk.selection.replace(/\s+$/, ""); - }; - - command.doBold = function(chunk, postProcessing, useDefaultText){ - return command.doBorI(chunk, 2, "strong text"); - }; - - command.doItalic = function(chunk, postProcessing, useDefaultText){ - return command.doBorI(chunk, 1, "emphasized text"); - }; - - // chunk: The selected region that will be enclosed with */** - // nStars: 1 for italics, 2 for bold - // insertText: If you just click the button without highlighting text, this gets inserted - command.doBorI = function(chunk, nStars, insertText){ - - // Get rid of whitespace and fixup newlines. - chunk.trimWhitespace(); - chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n"); - - // Look for stars before and after. Is the chunk already marked up? - chunk.before.search(/(\**$)/); - var starsBefore = re.$1; - - chunk.after.search(/(^\**)/); - var starsAfter = re.$1; - - var prevStars = Math.min(starsBefore.length, starsAfter.length); - - // Remove stars if we have to since the button acts as a toggle. - if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) { - chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), ""); - chunk.after = chunk.after.replace(re("^[*]{" + nStars + "}", ""), ""); - } - else if (!chunk.selection && starsAfter) { - // It's not really clear why this code is necessary. It just moves - // some arbitrary stuff around. - chunk.after = chunk.after.replace(/^([*_]*)/, ""); - chunk.before = chunk.before.replace(/(\s?)$/, ""); - var whitespace = re.$1; - chunk.before = chunk.before + starsAfter + whitespace; - } - else { - - // In most cases, if you don't have any selected text and click the button - // you'll get a selected, marked up region with the default text inserted. - if (!chunk.selection && !starsAfter) { - chunk.selection = insertText; - } - - // Add the true markup. - var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ? - chunk.before = chunk.before + markup; - chunk.after = markup + chunk.after; - } - - return; - }; - - command.stripLinkDefs = function(text, defsToAdd){ - - text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm, - function(totalMatch, id, link, newlines, title){ - defsToAdd[id] = totalMatch.replace(/\s*$/, ""); - if (newlines) { - // Strip the title and return that separately. - defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, ""); - return newlines + title; - } - return ""; - }); - - return text; - }; - - command.addLinkDef = function(chunk, linkDef){ - - var refNumber = 0; // The current reference number - var defsToAdd = {}; // - // Start with a clean slate by removing all previous link definitions. - chunk.before = command.stripLinkDefs(chunk.before, defsToAdd); - chunk.selection = command.stripLinkDefs(chunk.selection, defsToAdd); - chunk.after = command.stripLinkDefs(chunk.after, defsToAdd); - - var defs = ""; - var regex = /(\[(?:\[[^\]]*\]|[^\[\]])*\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g; - - var addDefNumber = function(def){ - refNumber++; - def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, " [" + refNumber + "]:"); - defs += "\n" + def; - }; - - var getLink = function(wholeMatch, link, id, end){ - - if (defsToAdd[id]) { - addDefNumber(defsToAdd[id]); - return link + refNumber + end; - - } - return wholeMatch; - }; - - chunk.before = chunk.before.replace(regex, getLink); - - if (linkDef) { - addDefNumber(linkDef); - } - else { - chunk.selection = chunk.selection.replace(regex, getLink); - } - - var refOut = refNumber; - - chunk.after = chunk.after.replace(regex, getLink); - - if (chunk.after) { - chunk.after = chunk.after.replace(/\n*$/, ""); - } - if (!chunk.after) { - chunk.selection = chunk.selection.replace(/\n*$/, ""); - } - - chunk.after += "\n\n" + defs; - - return refOut; - }; - - command.doLinkOrImage = function(chunk, postProcessing, isImage){ - - chunk.trimWhitespace(); - chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/); - - if (chunk.endTag.length > 1) { - - chunk.startTag = chunk.startTag.replace(/!?\[/, ""); - chunk.endTag = ""; - command.addLinkDef(chunk, null); - - } - else { - - if (/\n\n/.test(chunk.selection)) { - command.addLinkDef(chunk, null); - return; - } - - // The function to be executed when you enter a link and press OK or Cancel. - // Marks up the link and adds the ref. - var makeLinkMarkdown = function(link){ - - if (link !== null) { - - chunk.startTag = chunk.endTag = ""; - var linkDef = " [999]: " + link; - - var num = command.addLinkDef(chunk, linkDef); - chunk.startTag = isImage ? "![" : "["; - chunk.endTag = "][" + num + "]"; - - if (!chunk.selection) { - if (isImage) { - chunk.selection = "alt text"; - } - else { - chunk.selection = "link text"; - } - } - } - postProcessing(); - }; - - if (isImage) { - util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown); - } - else { - util.prompt(linkDialogText, linkDefaultText, makeLinkMarkdown); - } - return true; - } - }; - - util.makeAPI = function(){ - wmd.wmd = {}; - wmd.wmd.editor = wmd.editor; - wmd.wmd.previewManager = wmd.previewManager; - }; - - util.startEditor = function(){ - - if (wmd.wmd_env.autostart === false) { - util.makeAPI(); - return; - } - - var edit; // The editor (buttons + input + outputs) - the main object. - var previewMgr; // The preview manager. - - // Fired after the page has fully loaded. - var loadListener = function(){ - - wmd.panels = new wmd.PanelCollection(); - - previewMgr = new wmd.previewManager(); - var previewRefreshCallback = previewMgr.refresh; - - edit = new wmd.editor(previewRefreshCallback); - - previewMgr.refresh(true); - - }; - - util.addEvent(top, "load", loadListener); - }; - - wmd.previewManager = function(){ - - var managerObj = this; - var converter; - var poller; - var timeout; - var elapsedTime; - var oldInputText; - var htmlOut; - var maxDelay = 3000; - var startType = "delayed"; // The other legal value is "manual" - - // Adds event listeners to elements and creates the input poller. - var setupEvents = function(inputElem, listener){ - - util.addEvent(inputElem, "input", listener); - inputElem.onpaste = listener; - inputElem.ondrop = listener; - - util.addEvent(inputElem, "keypress", listener); - util.addEvent(inputElem, "keydown", listener); - // previewPollInterval is set at the top of this file. - poller = new wmd.inputPoller(listener, previewPollInterval); - }; - - var getDocScrollTop = function(){ - - var result = 0; - - if (top.innerHeight) { - result = top.pageYOffset; - } - else - if (doc.documentElement && doc.documentElement.scrollTop) { - result = doc.documentElement.scrollTop; - } - else - if (doc.body) { - result = doc.body.scrollTop; - } - - return result; - }; - - var makePreviewHtml = function(){ - - // If there are no registered preview and output panels - // there is nothing to do. - if (!wmd.panels.preview && !wmd.panels.output) { - return; - } - - var text = wmd.panels.input.value; - if (text && text == oldInputText) { - return; // Input text hasn't changed. - } - else { - oldInputText = text; - } - - var prevTime = new Date().getTime(); - - if (!converter && wmd.showdown) { - converter = new wmd.showdown.converter(); - } - - if (converter) { - text = converter.makeHtml(text); - } - - // Calculate the processing time of the HTML creation. - // It's used as the delay time in the event listener. - var currTime = new Date().getTime(); - elapsedTime = currTime - prevTime; - - pushPreviewHtml(text); - htmlOut = text; - }; - - // setTimeout is already used. Used as an event listener. - var applyTimeout = function(){ - - if (timeout) { - top.clearTimeout(timeout); - timeout = undefined; - } - - if (startType !== "manual") { - - var delay = 0; - - if (startType === "delayed") { - delay = elapsedTime; - } - - if (delay > maxDelay) { - delay = maxDelay; - } - timeout = top.setTimeout(makePreviewHtml, delay); - } - }; - - var getScaleFactor = function(panel){ - if (panel.scrollHeight <= panel.clientHeight) { - return 1; - } - return panel.scrollTop / (panel.scrollHeight - panel.clientHeight); - }; - - var setPanelScrollTops = function(){ - - if (wmd.panels.preview) { - wmd.panels.preview.scrollTop = (wmd.panels.preview.scrollHeight - wmd.panels.preview.clientHeight) * getScaleFactor(wmd.panels.preview); - ; - } - - if (wmd.panels.output) { - wmd.panels.output.scrollTop = (wmd.panels.output.scrollHeight - wmd.panels.output.clientHeight) * getScaleFactor(wmd.panels.output); - ; - } - }; - - this.refresh = function(requiresRefresh){ - - if (requiresRefresh) { - oldInputText = ""; - makePreviewHtml(); - } - else { - applyTimeout(); - } - }; - - this.processingTime = function(){ - return elapsedTime; - }; - - // The output HTML - this.output = function(){ - return htmlOut; - }; - - // The mode can be "manual" or "delayed" - this.setUpdateMode = function(mode){ - startType = mode; - managerObj.refresh(); - }; - - var isFirstTimeFilled = true; - - var pushPreviewHtml = function(text){ - - var emptyTop = position.getTop(wmd.panels.input) - getDocScrollTop(); - - // Send the encoded HTML to the output textarea/div. - if (wmd.panels.output) { - // The value property is only defined if the output is a textarea. - if (wmd.panels.output.value !== undefined) { - wmd.panels.output.value = text; - wmd.panels.output.readOnly = true; - } - // Otherwise we are just replacing the text in a div. - // Send the HTML wrapped in
      
      -				else {
      -					var newText = text.replace(/&/g, "&");
      -					newText = newText.replace(/
      "; - } - } - - if (wmd.panels.preview) { - wmd.panels.preview.innerHTML = text; - } - - setPanelScrollTops(); - - if (isFirstTimeFilled) { - isFirstTimeFilled = false; - return; - } - - var fullTop = position.getTop(wmd.panels.input) - getDocScrollTop(); - - if (global.isIE) { - top.setTimeout(function(){ - top.scrollBy(0, fullTop - emptyTop); - }, 0); - } - else { - top.scrollBy(0, fullTop - emptyTop); - } - }; - - var init = function(){ - - setupEvents(wmd.panels.input, applyTimeout); - makePreviewHtml(); - - if (wmd.panels.preview) { - wmd.panels.preview.scrollTop = 0; - } - if (wmd.panels.output) { - wmd.panels.output.scrollTop = 0; - } - }; - - this.destroy = function(){ - if (poller) { - poller.destroy(); - } - }; - - init(); - }; - - // Moves the cursor to the next line and continues lists, quotes and code. - command.doAutoindent = function(chunk, postProcessing, useDefaultText){ - - chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n"); - chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n"); - chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n"); - - useDefaultText = false; - - if(/(\n|^)[ ]{0,3}([*+-])[ \t]+.*\n$/.test(chunk.before)){ - if(command.doList){ - command.doList(chunk, postProcessing, false, true); - } - } - if(/(\n|^)[ ]{0,3}(\d+[.])[ \t]+.*\n$/.test(chunk.before)){ - if(command.doList){ - command.doList(chunk, postProcessing, true, true); - } - } - if(/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(chunk.before)){ - if(command.doBlockquote){ - command.doBlockquote(chunk, postProcessing, useDefaultText); - } - } - if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)){ - if(command.doCode){ - command.doCode(chunk, postProcessing, useDefaultText); - } - } - }; - - command.doBlockquote = function(chunk, postProcessing, useDefaultText){ - - chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/, - function(totalMatch, newlinesBefore, text, newlinesAfter){ - chunk.before += newlinesBefore; - chunk.after = newlinesAfter + chunk.after; - return text; - }); - - chunk.before = chunk.before.replace(/(>[ \t]*)$/, - function(totalMatch, blankLine){ - chunk.selection = blankLine + chunk.selection; - return ""; - }); - - var defaultText = useDefaultText ? "Blockquote" : ""; - chunk.selection = chunk.selection.replace(/^(\s|>)+$/ ,""); - chunk.selection = chunk.selection || defaultText; - - if(chunk.before){ - chunk.before = chunk.before.replace(/\n?$/,"\n"); - } - if(chunk.after){ - chunk.after = chunk.after.replace(/^\n?/,"\n"); - } - - chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/, - function(totalMatch){ - chunk.startTag = totalMatch; - return ""; - }); - - chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/, - function(totalMatch){ - chunk.endTag = totalMatch; - return ""; - }); - - var replaceBlanksInTags = function(useBracket){ - - var replacement = useBracket ? "> " : ""; - - if(chunk.startTag){ - chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/, - function(totalMatch, markdown){ - return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; - }); - } - if(chunk.endTag){ - chunk.endTag = chunk.endTag.replace(/^\n((>|\s)*)\n/, - function(totalMatch, markdown){ - return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; - }); - } - }; - - if(/^(?![ ]{0,3}>)/m.test(chunk.selection)){ - command.wrap(chunk, wmd.wmd_env.lineLength - 2); - chunk.selection = chunk.selection.replace(/^/gm, "> "); - replaceBlanksInTags(true); - chunk.addBlankLines(); - } - else{ - chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, ""); - command.unwrap(chunk); - replaceBlanksInTags(false); - - if(!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag){ - chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n"); - } - - if(!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag){ - chunk.endTag=chunk.endTag.replace(/^\n{0,2}/, "\n\n"); - } - } - - if(!/\n/.test(chunk.selection)){ - chunk.selection = chunk.selection.replace(/^(> *)/, - function(wholeMatch, blanks){ - chunk.startTag += blanks; - return ""; - }); - } - }; - - command.doCode = function(chunk, postProcessing, useDefaultText){ - - var hasTextBefore = /\S[ ]*$/.test(chunk.before); - var hasTextAfter = /^[ ]*\S/.test(chunk.after); - - // Use 'four space' markdown if the selection is on its own - // line or is multiline. - if((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)){ - - chunk.before = chunk.before.replace(/[ ]{4}$/, - function(totalMatch){ - chunk.selection = totalMatch + chunk.selection; - return ""; - }); - - var nLinesBefore = 1; - var nLinesAfter = 1; - - - if(/\n(\t|[ ]{4,}).*\n$/.test(chunk.before) || chunk.after === ""){ - nLinesBefore = 0; - } - if(/^\n(\t|[ ]{4,})/.test(chunk.after)){ - nLinesAfter = 0; // This needs to happen on line 1 - } - - chunk.addBlankLines(nLinesBefore, nLinesAfter); - - if(!chunk.selection){ - chunk.startTag = " "; - chunk.selection = useDefaultText ? "enter code here" : ""; - } - else { - if(/^[ ]{0,3}\S/m.test(chunk.selection)){ - chunk.selection = chunk.selection.replace(/^/gm, " "); - } - else{ - chunk.selection = chunk.selection.replace(/^[ ]{4}/gm, ""); - } - } - } - else{ - // Use backticks (`) to delimit the code block. - - chunk.trimWhitespace(); - chunk.findTags(/`/, /`/); - - if(!chunk.startTag && !chunk.endTag){ - chunk.startTag = chunk.endTag="`"; - if(!chunk.selection){ - chunk.selection = useDefaultText ? "enter code here" : ""; - } - } - else if(chunk.endTag && !chunk.startTag){ - chunk.before += chunk.endTag; - chunk.endTag = ""; - } - else{ - chunk.startTag = chunk.endTag=""; - } - } - }; - - command.doList = function(chunk, postProcessing, isNumberedList, useDefaultText){ - - // These are identical except at the very beginning and end. - // Should probably use the regex extension function to make this clearer. - var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/; - var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/; - - // The default bullet is a dash but others are possible. - // This has nothing to do with the particular HTML bullet, - // it's just a markdown bullet. - var bullet = "-"; - - // The number in a numbered list. - var num = 1; - - // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list. - var getItemPrefix = function(){ - var prefix; - if(isNumberedList){ - prefix = " " + num + ". "; - num++; - } - else{ - prefix = " " + bullet + " "; - } - return prefix; - }; - - // Fixes the prefixes of the other list items. - var getPrefixedItem = function(itemText){ - - // The numbering flag is unset when called by autoindent. - if(isNumberedList === undefined){ - isNumberedList = /^\s*\d/.test(itemText); - } - - // Renumber/bullet the list element. - itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm, - function( _ ){ - return getItemPrefix(); - }); - - return itemText; - }; - - chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null); - - if(chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)){ - chunk.before += chunk.startTag; - chunk.startTag = ""; - } - - if(chunk.startTag){ - - var hasDigits = /\d+[.]/.test(chunk.startTag); - chunk.startTag = ""; - chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n"); - command.unwrap(chunk); - chunk.addBlankLines(); - - if(hasDigits){ - // Have to renumber the bullet points if this is a numbered list. - chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem); - } - if(isNumberedList == hasDigits){ - return; - } - } - - var nLinesBefore = 1; - - chunk.before = chunk.before.replace(previousItemsRegex, - function(itemText){ - if(/^\s*([*+-])/.test(itemText)){ - bullet = re.$1; - } - nLinesBefore = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; - return getPrefixedItem(itemText); - }); - - if(!chunk.selection){ - chunk.selection = useDefaultText ? "List item" : " "; - } - - var prefix = getItemPrefix(); - - var nLinesAfter = 1; - - chunk.after = chunk.after.replace(nextItemsRegex, - function(itemText){ - nLinesAfter = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; - return getPrefixedItem(itemText); - }); - - chunk.trimWhitespace(true); - chunk.addBlankLines(nLinesBefore, nLinesAfter, true); - chunk.startTag = prefix; - var spaces = prefix.replace(/./g, " "); - command.wrap(chunk, wmd.wmd_env.lineLength - spaces.length); - chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces); - - }; - - command.doHeading = function(chunk, postProcessing, useDefaultText){ - - // Remove leading/trailing whitespace and reduce internal spaces to single spaces. - chunk.selection = chunk.selection.replace(/\s+/g, " "); - chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, ""); - - // If we clicked the button with no selected text, we just - // make a level 2 hash header around some default text. - if(!chunk.selection){ - chunk.startTag = "## "; - chunk.selection = "Heading"; - chunk.endTag = " ##"; - return; - } - - var headerLevel = 0; // The existing header level of the selected text. - - // Remove any existing hash heading markdown and save the header level. - chunk.findTags(/#+[ ]*/, /[ ]*#+/); - if(/#+/.test(chunk.startTag)){ - headerLevel = re.lastMatch.length; - } - chunk.startTag = chunk.endTag = ""; - - // Try to get the current header level by looking for - and = in the line - // below the selection. - chunk.findTags(null, /\s?(-+|=+)/); - if(/=+/.test(chunk.endTag)){ - headerLevel = 1; - } - if(/-+/.test(chunk.endTag)){ - headerLevel = 2; - } - - // Skip to the next line so we can create the header markdown. - chunk.startTag = chunk.endTag = ""; - chunk.addBlankLines(1, 1); - - // We make a level 2 header if there is no current header. - // If there is a header level, we substract one from the header level. - // If it's already a level 1 header, it's removed. - var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1; - - if(headerLevelToCreate > 0){ - - // The button only creates level 1 and 2 underline headers. - // Why not have it iterate over hash header levels? Wouldn't that be easier and cleaner? - var headerChar = headerLevelToCreate >= 2 ? "-" : "="; - var len = chunk.selection.length; - if(len > wmd.wmd_env.lineLength){ - len = wmd.wmd_env.lineLength; - } - chunk.endTag = "\n"; - while(len--){ - chunk.endTag += headerChar; - } - } - }; - - command.doHorizontalRule = function(chunk, postProcessing, useDefaultText){ - chunk.startTag = "----------\n"; - chunk.selection = ""; - chunk.addBlankLines(2, 1, true); - } -}; - - -Attacklab.wmd_env = {}; -Attacklab.account_options = {}; -Attacklab.wmd_defaults = {version:1, output:"HTML", lineLength:40, delayLoad:false}; - -if(!Attacklab.wmd) -{ - Attacklab.wmd = function() - { - Attacklab.loadEnv = function() - { - var mergeEnv = function(env) - { - if(!env) - { - return; - } - - for(var key in env) - { - Attacklab.wmd_env[key] = env[key]; - } - }; - - mergeEnv(Attacklab.wmd_defaults); - mergeEnv(Attacklab.account_options); - mergeEnv(top["wmd_options"]); - Attacklab.full = true; - - var defaultButtons = "bold italic link blockquote code image ol ul heading hr"; - Attacklab.wmd_env.buttons = Attacklab.wmd_env.buttons || defaultButtons; - }; - Attacklab.loadEnv(); - - }; - - Attacklab.wmd(); - Attacklab.wmdBase(); - Attacklab.Util.startEditor(); -}; -