vendor/assets/javascripts/redactor.js in kuhsaft-1.2.10 vs vendor/assets/javascripts/redactor.js in kuhsaft-1.2.11

- old
+ new

@@ -1,17 +1,18 @@ /* - Redactor v8.2.2 - Updated: January 17, 2013 + Redactor v9.0 beta 2 + Updated: February 13, 2013 http://redactorjs.com/ Copyright (c) 2009-2013, Imperavi Inc. - License: http://redactorjs.com/license/ + License: http://imperavi.com/redactor/license/ Usage: $('#content').redactor(); */ + var rwindow, rdocument; if (typeof RELANG === 'undefined') { var RELANG = {}; @@ -80,87 +81,143 @@ horizontalrule: 'Insert Horizontal Rule', deleted: 'Deleted', anchor: 'Anchor', link_new_tab: 'Open link in new tab', underline: 'Underline', - alignment: 'Alignment' + alignment: 'Alignment', + filename: 'Name (optional)' }; +'use strict'; (function($){ // Plugin - jQuery.fn.redactor = function(option) + $.fn.redactor = function(options) { - return this.each(function() + var val = []; + var args = Array.prototype.slice.call(arguments, 1); + + if (typeof options === 'string') { - var $obj = $(this); + this.each(function() + { + var instance = $.data(this, 'redactor'); - var data = $obj.data('redactor'); - if (!data) + if (typeof instance !== 'undefined') + { + var arr = options.split('.'); + var func = arr.length == 1 ? instance[options] : instance[arr[0]][arr[1]]; + + if ($.isFunction(func)) + { + var methodVal = func.call(instance, args); + if (methodVal !== undefined && methodVal !== instance) + { + val.push(methodVal); + } + } + } + }); + } + else + { + this.each(function() { - $obj.data('redactor', (data = new Redactor(this, option))); - } - }); - }; + if (!$.data(this, 'redactor')) + { + $.data(this, 'redactor', new Redactor(this, options)); + } + }); + } + if (val.length == 0) + { + return this; + } + else if (val.length == 1) + { + return val[0]; + } + else + { + return val; + } + }; + // Initialization - var Redactor = function(element, options) + var Redactor = function(el, options) { - // Element - this.$el = $(element); + this.$element = this.$el = $(el); // Lang - if (typeof options !== 'undefined' && typeof options.lang !== 'undefined' && options.lang !== 'en' && typeof RELANG[options.lang] !== 'undefined') + if (typeof options !== 'undefined' && typeof options.lang !== 'undefined' && typeof RELANG[options.lang] !== 'undefined') { - RLANG = RELANG[options.lang]; + RLANG = $.extend({}, RLANG, RELANG[options.lang]); } // Options this.opts = $.extend({ iframe: false, + fullpage: false, css: false, // url lang: 'en', direction: 'ltr', // ltr or rtl callback: false, // function keyupCallback: false, // function keydownCallback: false, // function - execCommandCallback: false, // function + execCommandCallback: false, // function + syncBeforeCallback: false, // function + syncAfterCallback: false, // function - plugins: false, - cleanup: true, - focus: false, tabindex: false, autoresize: true, minHeight: false, - fixed: false, - fixedTop: 0, // pixels - fixedBox: false, - source: true, - shortcuts: true, + plugins: false, // array + + air: false, + airButtons: ['formatting', '|', 'bold', 'italic', 'deleted', '|', 'unorderedlist', 'orderedlist', 'outdent', 'indent', '|', 'fontcolor', 'backcolor'], + mobile: true, - air: false, // true or toolbar wym: false, + cleanup: true, + source: true, + shortcuts: true, + visual: true, + + placeholder: false, + + linebreaks: false, + paragraphy: true, + convertDivs: false, convertLinks: true, - convertDivs: true, - protocol: 'http://', // for links http or https or ftp or false + formattingPre: false, autosave: false, // false or url autosaveCallback: false, // function interval: 60, // seconds + fixed: false, + fixedTop: 0, // pixels + fixedBox: false, + toolbarExternal: false, // ID selector + + linkAnchor: false, + linkEmail: false, + imageGetJson: false, // url (ex. /folder/images.json ) or false imageUpload: false, // url imageUploadCallback: false, // function imageUploadErrorCallback: false, // function + imageDeleteCallback: false, // function fileUpload: false, // url fileUploadCallback: false, // function fileUploadErrorCallback: false, // function @@ -168,41 +225,37 @@ uploadFields: false, observeImages: true, overlay: true, // modal overlay - allowedTags: ["form", "input", "button", "select", "option", "datalist", "output", "textarea", "fieldset", "legend", - "section", "header", "hgroup", "aside", "footer", "article", "details", "nav", "progress", "time", "canvas", - "code", "span", "div", "label", "a", "br", "p", "b", "i", "del", "strike", "u", - "img", "video", "source", "track", "audio", "iframe", "object", "embed", "param", "blockquote", - "mark", "cite", "small", "ul", "ol", "li", "hr", "dl", "dt", "dd", "sup", "sub", - "big", "pre", "code", "figure", "figcaption", "strong", "em", "table", "tr", "td", - "th", "tbody", "thead", "tfoot", "h1", "h2", "h3", "h4", "h5", "h6"], + allowedTags: false, + deniedTags: false, + clearTags: ['script', 'html', 'head', 'title', 'link', 'body', 'style', 'meta', 'applet'], - toolbarExternal: false, // ID selector + boldTag: 'strong', + italicTag: 'em', buttonsCustom: {}, buttonsAdd: [], - buttons: ['html', '|', 'formatting', '|', 'bold', 'italic', 'deleted', '|', 'unorderedlist', 'orderedlist', 'outdent', 'indent', '|', + buttons: ['html', '|', 'formatting', '|', 'bold', 'italic', 'deleted', 'underline', '|', 'unorderedlist', 'orderedlist', 'outdent', 'indent', '|', 'image', 'video', 'file', 'table', 'link', '|', 'fontcolor', 'backcolor', '|', 'alignment', '|', 'horizontalrule'], // 'underline', 'alignleft', 'aligncenter', 'alignright', 'justify' - airButtons: ['formatting', '|', 'bold', 'italic', 'deleted', '|', 'unorderedlist', 'orderedlist', 'outdent', 'indent', '|', 'fontcolor', 'backcolor'], - formattingTags: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4'], - activeButtons: ['deleted', 'italic', 'bold', 'underline', 'unorderedlist', 'orderedlist'], // 'alignleft', 'aligncenter', 'alignright', 'justify' + activeButtons: ['deleted', 'italic', 'bold', 'underline', 'unorderedlist', 'orderedlist', 'table'], // 'alignleft', 'aligncenter', 'alignright', 'justify' activeButtonsStates: { b: 'bold', strong: 'bold', i: 'italic', em: 'italic', del: 'deleted', strike: 'deleted', ul: 'unorderedlist', ol: 'orderedlist', - u: 'underline' + u: 'underline', + table: 'table' }, colors: [ '#ffffff', '#000000', '#eeece1', '#1f497d', '#4f81bd', '#c0504d', '#9bbb59', '#8064a2', '#4bacc6', '#f79646', '#ffff00', '#f2f2f2', '#7f7f7f', '#ddd9c3', '#c6d9f0', '#dbe5f1', '#f2dcdb', '#ebf1dd', '#e5e0ec', '#dbeef3', '#fdeada', '#fff2ca', @@ -210,19 +263,21 @@ '#bfbfbf', '#3f3f3f', '#938953', '#548dd4', '#95b3d7', '#d99694', '#c3d69b', '#b2a2c7', '#b7dde8', '#fac08f', '#f2c314', '#a5a5a5', '#262626', '#494429', '#17365d', '#366092', '#953734', '#76923c', '#5f497a', '#92cddc', '#e36c09', '#c09100', '#7f7f7f', '#0c0c0c', '#1d1b10', '#0f243e', '#244061', '#632423', '#4f6128', '#3f3151', '#31859b', '#974806', '#7f6000'], // private - emptyHtml: '<p><br /></p>', + textareamode: false, buffer: false, - visual: true, + emptyHtml: '<p>&#x200b;</p>', + invisibleSpace: '&#x200b;', + alignmentTags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'TD', 'DIV', 'BLOCKQUOTE'], // modal windows container modal_file: String() + '<div id="redactor_modal_content">' + '<form id="redactorUploadFileForm" method="post" action="" enctype="multipart/form-data">' + - '<label>Name (optional)</label>' + + '<label>' + RLANG.filename + '</label>' + '<input type="text" id="redactor_filename" class="redactor_input" />' + '<div style="margin-top: 7px;">' + '<input type="file" id="redactor_file" name="file" />' + '</div>' + '</form><br>' + @@ -323,10 +378,12 @@ '<div id="redactor_modal_footer">' + '<a href="javascript:void(null);" class="redactor_modal_btn redactor_btn_modal_close">' + RLANG.cancel + '</a>' + '<input type="button" class="redactor_modal_btn" id="redactor_insert_video_btn" value="' + RLANG.insert + '" />' + '</div>', + + toolbar: { html: { title: RLANG.html, func: 'toggle' @@ -338,46 +395,46 @@ dropdown: { p: { title: RLANG.paragraph, - exec: 'formatblock' + func: 'format.blocks' }, blockquote: { title: RLANG.quote, - exec: 'formatblock', + func: 'quote.toggle', className: 'redactor_format_blockquote' }, pre: { title: RLANG.code, - exec: 'formatblock', + func: 'format.blocks', className: 'redactor_format_pre' }, h1: { title: RLANG.header1, - exec: 'formatblock', + func: 'format.blocks', className: 'redactor_format_h1' }, h2: { title: RLANG.header2, - exec: 'formatblock', + func: 'format.blocks', className: 'redactor_format_h2' }, h3: { title: RLANG.header3, - exec: 'formatblock', + func: 'format.blocks', className: 'redactor_format_h3' }, h4: { title: RLANG.header4, - exec: 'formatblock', + func: 'format.blocks', className: 'redactor_format_h4' } } }, bold: @@ -411,99 +468,99 @@ exec: 'insertorderedlist' }, outdent: { title: '< ' + RLANG.outdent, - exec: 'outdent' + func: 'indenting.outdent' }, indent: { title: '> ' + RLANG.indent, - exec: 'indent' + func: 'indenting.indent' }, image: { title: RLANG.image, - func: 'showImage' + func: 'image.show' }, video: { title: RLANG.video, - func: 'showVideo' + func: 'video.show' }, file: { title: RLANG.file, - func: 'showFile' + func: 'file.show' }, table: { title: RLANG.table, func: 'show', dropdown: { insert_table: { title: RLANG.insert_table, - func: 'showTable' + func: 'table.show' }, separator_drop1: { name: 'separator' }, insert_row_above: { title: RLANG.insert_row_above, - func: 'insertRowAbove' + func: 'table.insertRowAbove' }, insert_row_below: { title: RLANG.insert_row_below, - func: 'insertRowBelow' + func: 'table.insertRowBelow' }, insert_column_left: { title: RLANG.insert_column_left, - func: 'insertColumnLeft' + func: 'table.insertColumnLeft' }, insert_column_right: { title: RLANG.insert_column_right, - func: 'insertColumnRight' + func: 'table.insertColumnRight' }, separator_drop2: { name: 'separator' }, add_head: { title: RLANG.add_head, - func: 'addHead' + func: 'table.addHead' }, delete_head: { title: RLANG.delete_head, - func: 'deleteHead' + func: 'table.deleteHead' }, separator_drop3: { name: 'separator' }, delete_column: { title: RLANG.delete_column, - func: 'deleteColumn' + func: 'table.deleteColumn' }, delete_row: { title: RLANG.delete_row, - func: 'deleteRow' + func: 'table.deleteRow' }, delete_table: { title: RLANG.delete_table, - func: 'deleteTable' + func: 'table.deleteTable' } } }, link: { @@ -512,11 +569,11 @@ dropdown: { link: { title: RLANG.link_insert, - func: 'showLink' + func: 'link.show' }, unlink: { title: RLANG.unlink, exec: 'unlink' @@ -540,57 +597,56 @@ dropdown: { alignleft: { title: RLANG.align_left, - exec: 'JustifyLeft' + func: 'alignment.left' }, aligncenter: { title: RLANG.align_center, - exec: 'JustifyCenter' + func: 'alignment.center' }, alignright: { title: RLANG.align_right, - exec: 'JustifyRight' + func: 'alignment.right' }, justify: { title: RLANG.align_justify, - exec: 'JustifyFull' + func: 'alignment.justify' } } }, alignleft: { - exec: 'JustifyLeft', - title: RLANG.align_left + title: RLANG.align_left, + func: 'alignment.left' }, aligncenter: { - exec: 'JustifyCenter', - title: RLANG.align_center + title: RLANG.align_center, + func: 'alignment.center' }, alignright: { - exec: 'JustifyRight', - title: RLANG.align_right + title: RLANG.align_right, + func: 'alignment.right' }, justify: { - exec: 'JustifyFull', - title: RLANG.align_justify + title: RLANG.align_justify, + func: 'alignment.justify' }, horizontalrule: { exec: 'inserthorizontalrule', title: RLANG.horizontalrule } } - }, options, this.$el.data()); this.dropdowns = []; // Init @@ -598,34 +654,51 @@ }; // Functionality Redactor.prototype = { - // Initialization init: function() { // get dimensions this.height = this.$el.css('height'); this.width = this.$el.css('width'); + this.opts.buffer = this.opts.rebuffer = []; + this.codeactions = false; + rdocument = this.document = document; rwindow = this.window = window; - // mobile - if (this.opts.mobile === false && this.isMobile()) - { - this.build(true); - return false; - } + if (this.opts.fullpage) this.opts.iframe = true; + if (this.opts.iframe) this.opts.autoresize = false; - // iframe - if (this.opts.iframe) + // setup formatting permissions + if (this.opts.linebreaks === false) { - this.opts.autoresize = false; + if (this.opts.allowedTags !== false && $.inArray('p', this.opts.allowedTags) == '-1') + { + this.opts.allowedTags.push('p'); + } + + if (this.opts.deniedTags !== false) + { + var pos = $.inArray('p', this.opts.deniedTags); + if (pos != '-1') + { + this.opts.deniedTags.splice(pos, pos); + } + } } + // Build + this.build.start.call(this); + + }, + + afterBuild: function() + { // extend buttons if (this.opts.air) { this.opts.buttons = this.opts.airButtons; } @@ -651,595 +724,718 @@ } // formatting tags if (this.opts.toolbar !== false) { + if (this.opts.linebreaks) + { + var num = $.inArray('p', this.opts.formattingTags); + if (num === 0) + { + delete this.opts.formattingTags[0]; + } + } + $.each(this.opts.toolbar.formatting.dropdown, $.proxy(function(i,s) { if ($.inArray(i, this.opts.formattingTags) == '-1') { delete this.opts.toolbar.formatting.dropdown[i]; } }, this)); } - function afterBuild() - { - // air enable - this.enableAir(); + // air enable + this.air.enable.call(this); - // toolbar - this.buildToolbar(); + // toolbar + this.toolbar.build.call(this); - // PLUGINS - if (typeof this.opts.plugins === 'object') + // PLUGINS + if (typeof this.opts.plugins === 'object') + { + $.each(this.opts.plugins, $.proxy(function(i,s) { - $.each(this.opts.plugins, $.proxy(function(i,s) + if (typeof RedactorPlugins[s] !== 'undefined') { - if (typeof RedactorPlugins[s] !== 'undefined') - { - $.extend(this, RedactorPlugins[s]); + $.extend(this, RedactorPlugins[s]); - if (typeof RedactorPlugins[s].init !== 'undefined') - { - this.init(); - } + if (typeof RedactorPlugins[s].init !== 'undefined') + { + this.init(); } + } - }, this)); - } + }, this)); + } - // buttons response - if (this.opts.activeButtons !== false && this.opts.toolbar !== false) + // buttons response + if (this.opts.activeButtons !== false && this.opts.toolbar !== false) + { + this.$editor.on('click.redactor keyup.redactor', $.proxy(this.observe.formatting, this)); + } + + // events + if (this.opts.linebreaks === true) + { + this.$editor.on("keyup.redactor mouseup.redactor", $.proxy(function() { - var observeFormatting = $.proxy(function() { this.observeFormatting(); }, this); - this.$editor.click(observeFormatting).keyup(observeFormatting); - } + var that = this.$editor[0]; + if (this.utils.oldIE.call(this) === false && (!that.lastChild || that.lastChild.nodeName.toLowerCase() != "br")) + { + that.appendChild(document.createElement("br")); + } + }, this)); + } - // paste - var oldsafari = false; - if (this.browser('webkit') && navigator.userAgent.indexOf('Chrome') === -1) + // paste + var oldsafari = false; + if (this.utils.browser.call(this, 'webkit') && navigator.userAgent.indexOf('Chrome') === -1) + { + var arr = this.utils.browser.call(this, 'version').split('.'); + if (arr[0] < 536) { - var arr = this.browser('version').split('.'); - if (arr[0] < 536) oldsafari = true; + oldsafari = true; } + } - if (this.isMobile(true) === false && oldsafari === false) + if (this.utils.isMobile.call(this, true) === false && oldsafari === false) + { + this.$editor.bind('paste.redactor', $.proxy(function(e) { - this.$editor.bind('paste', $.proxy(function(e) - { - if (this.opts.cleanup === false) - { - return true; - } + if (this.opts.cleanup === false) return true; - this.pasteRunning = true; + this.pasteRunning = true; + this.selection.save.call(this); - this.setBuffer(); - + if (!this.selectall) + { if (this.opts.autoresize === true) { + this.$editor.height(this.$editor.height()); this.saveScroll = this.document.body.scrollTop; } else { this.saveScroll = this.$editor.scrollTop(); } + } - var frag = this.extractContent(); + var frag = this.utils.extractContent.call(this); - setTimeout($.proxy(function() + setTimeout($.proxy(function() + { + var pastedFrag = this.utils.extractContent.call(this); + this.$editor.append(frag); + + this.selection.restore.call(this); + + var html = this.utils.getFragmentHtml.call(this, pastedFrag); + this.paste.clean.call(this, html); + this.pasteRunning = false; + + if (this.opts.autoresize === true) { - var pastedFrag = this.extractContent(); - this.$editor.append(frag); + this.$editor.css('height', 'auto'); + } - this.restoreSelection(); + }, this), 1); - var html = this.getFragmentHtml(pastedFrag); - this.pasteCleanUp(html); - this.pasteRunning = false; + }, this)); + } - }, this), 1); + // formatting + this.$editor.on('keydown.redactor', $.proxy(function(e) + { + var key = e.which; + var parent = this.selection.getParent.call(this); + var current = this.selection.getElement.call(this); + var pre = false; + var ctrl = e.ctrlKey || e.metaKey; - }, this)); + // callback keydown + if (typeof this.opts.keydownCallback === 'function') + { + this.opts.keydownCallback(this, e); } - // key handlers - this.keyup(); - this.keydown(); + if ((parent && $(parent).get(0).tagName === 'PRE') || (current && $(current).get(0).tagName === 'PRE')) + { + pre = true; + if (key === 40) this.format.insertAfterLastElement.call(this, current); + } - // autosave - if (this.opts.autosave !== false) + // down + if (key === 40) { - this.autoSave(); + if (parent && $(parent).get(0).tagName === 'BLOCKQUOTE') this.format.insertAfterLastElement.call(this, parent); + if (current && $(current).get(0).tagName === 'BLOCKQUOTE') this.format.insertAfterLastElement.call(this, current); } - // observers - setTimeout($.proxy(function() + // Enter pre + if (pre === true && key === 13) { - this.observeImages(); - this.observeTables(); + this.buffer.set.call(this); + e.preventDefault(); - }, this), 1); + var html = $(current).parent().text(); + this.insert.nodeAtCaret.call(this, document.createTextNode('\n')); + if (html.search(/\s$/) == -1) + { + this.insert.nodeAtCaret.call(this, document.createTextNode('\n')); + } - // FF fix - if (this.browser('mozilla')) + this.sync(); + + return false; + } + else if (key === 13 && !e.shiftKey && !e.ctrlKey && !e.metaKey) // Enter { - this.$editor.click($.proxy(function() - { - this.saveSelection(); - }, this)); + this.buffer.set.call(this); - try + var element = this.selection.getNode.call(this); + + if (this.opts.linebreaks === false && parent === false && current === false) + { + e.preventDefault(); + var text = this.utils.getObjectText.call(this, element); + var node1 = $('<p>' + text + '</p>'); + + if (text != '') + { + var node2 = $('<p>' + this.opts.invisibleSpace + '</p>'); + $(element).replaceWith(node1); + node1.after(node2); + this.selection.start.call(this, node2); + } + else + { + $(element).replaceWith(node1); + this.selection.end.call(this, node1); + } + + this.sync(); + return false; + } + else if ($(element).closest('h1, h2, h3, h4, h5, h6, ol, ul, li, p, td', this.$editor[0]).size() == 0 && !this.utils.browser.call(this, 'mozilla')) { - this.document.execCommand('enableObjectResizing', false, false); - this.document.execCommand('enableInlineTableEditing', false, false); + // Inserting br on Enter + e.preventDefault(); + this.format.insertLineBreak.call(this); + return false; } - catch (e) {} + else + { + // Native line break for blocks elements h1, h2, h3, h4, h5, h6, ol, ul, li, p, td + setTimeout($.proxy(this.format.newLine, this), 1); + } } - - // focus - if (this.opts.focus) + else if (key === 13 && (e.ctrlKey || e.shiftKey)) // Shift+Enter or Ctrl+Enter { - setTimeout($.proxy(function(){ - this.$editor.focus(); - }, this), 1); - } + this.buffer.set.call(this); - // fixed - if (this.opts.fixed) - { - this.observeScroll(); - $(document).scroll($.proxy(this.observeScroll, this)); + e.preventDefault(); + this.format.insertLineBreak.call(this); } - // callback - if (typeof this.opts.callback === 'function') + + // SHORCTCUTS + if (ctrl) { - this.opts.callback(this); + if (key == 65) + { + this.selectall = true; + } + else if (key != 91 && key != 17) + { + this.selectall = false; + } + + + this.shortcuts.set.call(this, e, key); } - if (this.opts.toolbar !== false) + // Tab + if (key === 9 && this.opts.shortcuts) { - this.$toolbar.find('a').attr('tabindex', '-1'); + e.preventDefault(); + + if (pre === true && !e.shiftKey) + { + this.buffer.set.call(this); + this.insert.nodeAtCaret.call(this, document.createTextNode('\t')); + this.sync(); + return false; + } + else + { + if (!e.shiftKey) + { + this.indenting.indent.call(this); + return false; + } + else + { + this.indenting.outdent.call(this); + return false; + } + } } - } - // construct editor - this.build(false, afterBuild); + }, this)); - }, - shortcuts: function(e, cmd) - { - e.preventDefault(); - this.execCommand(cmd, false); - }, - keyup: function() - { - this.$editor.keyup($.proxy(function(e) + this.$editor.on('keyup.redactor', $.proxy(function(e) { - var key = e.keyCode || e.which; + var key = e.which; - if (this.browser('mozilla') && !this.pasteRunning) + // convert links + if (this.opts.convertLinks && key === 13) { - this.saveSelection(); + this.$editor.linkify(); } // callback as you type if (typeof this.opts.keyupCallback === 'function') { this.opts.keyupCallback(this, e); } // if empty - if (key === 8 || key === 46) + if (this.opts.linebreaks === false && (key === 8 || key === 46)) { - this.observeImages(); - return this.formatEmpty(e); + return this.format.empty.call(this, e); } - // new line p - if (key === 13 && !e.shiftKey && !e.ctrlKey && !e.metaKey) - { - if (this.browser('webkit')) - { - this.formatNewLine(e); - } + this.sync(); - // convert links - if (this.opts.convertLinks) - { - this.$editor.linkify(); - } - } - - this.syncCode(); - }, this)); - }, - keydown: function() - { - this.$editor.keydown($.proxy(function(e) + + // autosave + if (this.opts.autosave !== false) { - var key = e.keyCode || e.which; - var parent = this.getParentNode(); - var current = this.getCurrentNode(); - var pre = false; - var ctrl = e.ctrlKey || e.metaKey; + this.autosave(); + } - if ((parent || current) && ($(parent).get(0).tagName === 'PRE' || $(current).get(0).tagName === 'PRE')) - { - pre = true; - } + // observers + setTimeout($.proxy(this.observe.start, this), 1); - // callback keydown - if (typeof this.opts.keydownCallback === 'function') + // FF fix + if (this.utils.browser.call(this, 'mozilla')) + { + try { - this.opts.keydownCallback(this, e); + this.document.execCommand('enableObjectResizing', false, false); + this.document.execCommand('enableInlineTableEditing', false, false); } + catch (e) {} + } - if (ctrl && this.opts.shortcuts) - { - if (key === 90) - { - if (this.opts.buffer !== false) - { - e.preventDefault(); - this.getBuffer(); - } - else if (e.shiftKey) - { - this.shortcuts(e, 'redo'); // Ctrl + Shift + z - } - else - { - this.shortcuts(e, 'undo'); // Ctrl + z - } - } - else if (key === 77) - { - this.shortcuts(e, 'removeFormat'); // Ctrl + m - } - else if (key === 66) - { - this.shortcuts(e, 'bold'); // Ctrl + b - } - else if (key === 73) - { - this.shortcuts(e, 'italic'); // Ctrl + i - } - else if (key === 74) - { - this.shortcuts(e, 'insertunorderedlist'); // Ctrl + j - } - else if (key === 75) - { - this.shortcuts(e, 'insertorderedlist'); // Ctrl + k - } - else if (key === 76) - { - this.shortcuts(e, 'superscript'); // Ctrl + l - } - else if (key === 72) - { - this.shortcuts(e, 'subscript'); // Ctrl + h - } - } + // focus + if (this.opts.focus) + { + setTimeout($.proxy(this.focus.set, this), 100); + } - // clear undo buffer - if (!ctrl && key !== 90) - { - this.opts.buffer = false; - } + // fixed + if (this.opts.fixed) + { + this.observe.scroll.call(this); + $(document).scroll($.proxy(this.observe.scroll, this)); + } - // enter - if (pre === true && key === 13) + // code mode + if (this.opts.visual === false) + { + setTimeout($.proxy(function() { - e.preventDefault(); + this.opts.visual = true; + this.toggle(); - var html = $(current).parent().text(); - this.insertNodeAtCaret(this.document.createTextNode('\r\n')); - if (html.search(/\s$/) == -1) - { - this.insertNodeAtCaret(this.document.createTextNode('\r\n')); - } - this.syncCode(); + }, this), 200); + } - return false; - } + // callback + if (typeof this.opts.callback === 'function') + { + this.opts.callback(this); + } - // tab - if (this.opts.shortcuts && !e.shiftKey && key === 9) + if (this.opts.toolbar !== false) + { + this.$toolbar.find('a').attr('tabindex', '-1'); + } + + }, + + // SHORTCUTS + shortcuts: + { + set: function(e, key) + { + if (key === 90) { - if (pre === false) + if (this.opts.buffer.length != 0) { - this.shortcuts(e, 'indent'); // Tab + e.preventDefault(); + this.buffer.get.call(this); } - else + else if (e.shiftKey) { - e.preventDefault(); - this.insertNodeAtCaret(this.document.createTextNode('\t')); - this.syncCode(); - return false; + if (this.opts.rebuffer.length != 0) + { + e.preventDefault(); + this.buffer.redo.call(this); + } + else this.shortcuts.load.call(this, e, 'redo'); // Ctrl + Shift + z } + else this.shortcuts.load.call(this, e, 'undo'); // Ctrl + z } - else if (this.opts.shortcuts && e.shiftKey && key === 9 ) - { - this.shortcuts(e, 'outdent'); // Shift + tab - } - // safari shift key + enter - if (this.browser('webkit') && navigator.userAgent.indexOf('Chrome') === -1) + if (this.opts.shortcuts) { - return this.safariShiftKeyEnter(e, key); + if (key === 77) this.shortcuts.load.call(this, e, 'removeFormat'); // Ctrl + m + else if (key === 66) this.shortcuts.load.call(this, e, 'bold'); // Ctrl + b + else if (key === 73) this.shortcuts.load.call(this, e, 'italic'); // Ctrl + i + else if (key === 74) this.shortcuts.load.call(this, e, 'insertunorderedlist'); // Ctrl + j + else if (key === 75) this.shortcuts.load.call(this, e, 'insertorderedlist'); // Ctrl + k + else if (key === 76) this.shortcuts.load.call(this, e, 'superscript'); // Ctrl + l + else if (key === 72) this.shortcuts.load.call(this, e, 'subscript'); // Ctrl + h } - }, this)); + + }, + load: function(e, cmd) + { + e.preventDefault(); + this.exec.command.call(this, cmd, false); + } }, - build: function(mobile, whendone) + + // BUILD + build: { - if (mobile !== true) + start: function() { + // content + this.content = ''; + // container this.$box = $('<div class="redactor_box"></div>'); // air box if (this.opts.air) { - this.air = $('<div class="redactor_air" style="display: none;"></div>'); + this.$air = $('<div class="redactor_air" style="display: none;"></div>'); } - this.$content = null; - - function initFrame() + // mobile + if (this.utils.oldIE.call(this) || (this.opts.mobile === false && this.utils.isMobile.call(this) === true)) { - this.$editor = this.$content.contents().find("body").attr('contenteditable', true).attr('dir', this.opts.direction); - - rdocument = this.document = this.$editor[0].ownerDocument; - rwindow = this.window = this.document.defaultView || window; - - if (this.opts.css !== false) + if (this.$el.get(0).tagName === 'TEXTAREA') { - this.$content.contents().find('head').append('<link rel="stylesheet" href="' + this.opts.css + '" />'); + this.$box.insertAfter(this.$el).append(this.$el); } - - this.$editor.html(html); - - if (whendone) + else { - whendone.call(this); - whendone = null; + this.$editor = this.$el; + this.$el = $('<textarea name="' + this.$editor.attr('id') + '"></textarea>').css('height', this.height).val($.trim(this.$editor.html())); + this.$editor.hide(); + this.$box.insertAfter(this.$editor).append(this.$el); } + + return true; } - // editor - this.textareamode = true; - if (this.$el.get(0).tagName === 'TEXTAREA') + // build + if (this.opts.iframe) { - if(this.opts.iframe) - { - var me = this; - this.$content = $('<iframe style="width: 100%;" frameborder="0"></iframe>').load(function() - { - initFrame.call(me); - }); - } - else - { - this.$content = this.$editor = $('<div></div>'); - } - - var classlist = this.$el.get(0).className.split(/\s+/); - $.each(classlist, $.proxy(function(i,s) - { - this.$content.addClass('redactor_' + s); - }, this)); + this.iframe.start.call(this); } + else if (this.$el.get(0).tagName === 'TEXTAREA') + { + this.opts.textrareamode = true; + this.build.textarea.call(this); + } else { - this.textareamode = false; - this.$content = this.$editor = this.$el; - this.$el = $('<textarea name="' + this.$editor.attr('id') + '"></textarea>').css('height', this.height); + this.build.element.call(this); } - if (this.$editor) + if (!this.opts.iframe) { - this.$editor.addClass('redactor_editor').attr('contenteditable', true).attr('dir', this.opts.direction); + this.build.options.call(this); + this.afterBuild.call(this); } + }, + textarea: function() + { + this.$editor = $('<div></div>'); + this.build.addClasses.call(this, this.$editor); + this.content = $.trim(this.$el.val()); + this.$el.attr('dir', this.opts.direction); + this.$box.insertAfter(this.$el).append(this.$editor).append(this.$el); - if (this.opts.tabindex !== false) + this.build.enable.call(this); + }, + element: function() + { + this.$editor = this.$el; + this.$el = $('<textarea name="' + this.$editor.attr('id') + '"></textarea>').attr('dir', this.opts.direction).css('height', this.height); + this.content = $.trim(this.$editor.html()); + this.$box.insertAfter(this.$editor).append(this.$editor).append(this.$el); + + this.build.enable.call(this); + }, + addClasses: function(el) + { + var classlist = this.$el.get(0).className.split(/\s+/); + $.each(classlist, function(i,s) { - this.$content.attr('tabindex', this.opts.tabindex); - } + el.addClass('redactor_' + s); + }); + }, + enable: function() + { + this.$editor.addClass('redactor_editor').attr('contenteditable', true).attr('dir', this.opts.direction); + this.$el.hide(); - if (this.opts.minHeight !== false) + this.build.clean.call(this); + this.placeholder.start.call(this); + this.code.set.call(this, this.content, false); + }, + options: function() + { + var $el = this.$editor; + + if (this.opts.iframe) $el = this.$frame; + if (this.opts.tabindex) $el.attr('tabindex', this.opts.tabindex); + if (this.opts.minHeight) $el.css('min-height', this.opts.minHeight + 'px'); + if (this.opts.wym) this.$editor.addClass('redactor_editor_wym'); + if (!this.opts.autoresize) $el.css('height', this.height); + }, + clean: function(html) + { + if (typeof html === 'undefined') html = this.content; + + html = this.clean.savePreCode.call(this, html); + + if (!this.opts.fullpage) html = this.clean.stripTags.call(this, html, false); + if (this.opts.paragraphy) html = this.clean.paragraphy.call(this, html); + if (this.opts.linebreaks) html = html.replace(/\n/g, '<br>'); + + return this.content = html; + } + }, + + // IFRAME + iframe: + { + start: function() + { + this.iframe.create.call(this); + + if (this.$el.get(0).tagName === 'TEXTAREA') { - this.$content.css('min-height', this.opts.minHeight + 'px'); + this.opts.textrareamode = true; + this.content = $.trim(this.$el.val()); + this.$el.attr('dir', this.opts.direction); + this.iframe.append.call(this, this.$el); } - - if (this.opts.wym === true) + else { - this.$content.addClass('redactor_editor_wym'); + this.$elold = this.$el.hide(); + this.$el = $('<textarea name="' + this.$elold.attr('id') + '"></textarea>').attr('dir', this.opts.direction).css('height', this.height); + this.content = $.trim(this.$elold.html()); + this.iframe.append.call(this, this.$elold); } - if (this.opts.autoresize === false) + if (this.opts.fullpage) { - this.$content.css('height', this.height); + this.iframe.page.call(this); + if (this.content === '') this.content = this.opts.emptyHtml; + this.$frame.contents()[0].write(this.content); } - // hide textarea + this.iframe.load.call(this, true); + }, + append: function(el) + { + this.$box.insertAfter(el).append(this.$frame).append(this.$el); this.$el.hide(); - - // append box and frame - var html = ''; - if (this.textareamode) + }, + create: function() + { + this.$frame = null; + this.initbuild = true; + var that = this; + this.$frame = $('<iframe style="width: 100%;" frameborder="0">').load(function() { - // get html - html = this.$el.val(); - html = this.savePreCode(html); + that.iframe.load.call(that); + }); + }, + page: function() + { + var frame = this.$frame[0]; + var doc = frame.contentDocument || frame.contentWindow.document; + if (doc.documentElement) doc.removeChild(doc.documentElement); - this.$box.insertAfter(this.$el).append(this.$content).append(this.$el); - } - else + return doc; + }, + load: function(afterbuild) + { + this.$editor = this.$frame.contents().find("body").attr('contenteditable', true).attr('dir', this.opts.direction); + if (this.$editor[0]) { - // get html - html = this.$editor.html(); - html = this.savePreCode(html); - - this.$box.insertAfter(this.$content).append(this.$el).append(this.$editor); - + rdocument = this.document = this.$editor[0].ownerDocument; + rwindow = this.window = this.document.defaultView || window; } - // conver newlines to p - html = this.paragraphy(html); + var time = 1; + if (this.utils.browser.call(this, 'msie')) time = 10; - // enable - if (this.$editor) + // iframe css + if (this.opts.css !== false) { - this.$editor.html(html); + setTimeout($.proxy(function() + { + this.$frame.contents().find('head').append('<link rel="stylesheet" href="' + this.opts.css + '" />'); + }, this), time); } - if (this.textareamode === false) + if (this.opts.fullpage) { - this.syncCode(); + setTimeout($.proxy(function() + { + this.content = this.build.clean.call(this, this.$editor.html()); + this.placeholder.start.call(this); + this.$editor.html(this.content); + this.sync(); + + }, this), 10); } - } - else - { - if (this.$el.get(0).tagName !== 'TEXTAREA') + else { - var html = this.$el.val(); - var textarea = $('<textarea name="' + this.$editor.attr('id') + '"></textarea>').css('height', this.height).val(html); - this.$el.hide(); - this.$el.after(textarea); + if (this.initbuild) this.build.clean.call(this); + this.placeholder.start.call(this); + this.code.set.call(this, this.content, false); } - } - if (whendone && this.$editor) - { - whendone.call(this); - } + setTimeout($.proxy(this.build.options, this), time); + this.initbuild = false; + if (afterbuild === true) this.afterBuild.call(this); + } }, - enableAir: function() + + // PLACEHOLDER + placeholder: { - if (this.opts.air === false) + start: function() { - return false; - } + if (this.$element.attr('placeholder')) + { + this.opts.placeholder = this.$element.attr('placeholder'); + } - this.air.hide(); + var html = this.content.replace(/&#x200b;|<br>|<br \/>/gi, ''); - this.$editor.bind('textselect', $.proxy(function(e) - { - this.showAir(e); + if (html !== '' && html !== '<p></p>') return false; + if (this.opts.placeholder === false || this.opts.placeholder === '') return false; - }, this)); + this.opts.focus = false; + this.content = $('<span class="redactor_placeholder">').attr('contenteditable', false).text(this.opts.placeholder); - this.$editor.bind('textunselect', $.proxy(function() - { - this.air.hide(); + if (this.opts.linebreaks === false) + { + this.content = $('<p>').append(this.content); + } - }, this)); + this.content = this.utils.outerHtml.call(this, this.content); - }, - showAir: function(e) - { - $('.redactor_air').hide(); + this.$editor.one('focus.redactor_placeholder', $.proxy(this.placeholder.focus, this)); + this.$editor.one('blur.redactor_placeholder', $.proxy(this.placeholder.blur, this)); + }, + focus: function() + { - var width = this.air.innerWidth(); - var left = e.clientX; + var ph = this.$editor.find('.redactor_placeholder') + if (this.opts.linebreaks === false && ph.size() != 0) + { + var parent = ph.parent(); + parent.html($('<br>')); + this.$editor.focus(); + this.selection.start.call(this, parent); + } + else + { + this.format.replaceLineBreak.call(this, ph); + } - if ($(this.document).width() < (left + width)) - { - left = left - width; - } + this.sync(); - var top = e.clientY + $(document).scrollTop() + 14; - if (this.opts.iframe === true) + this.$editor.one('blur.redactor_placeholder', $.proxy(this.placeholder.blur, this)); + this.$editor.off('focus.redactor_placeholder'); + }, + blur: function() { - top = top + this.$box.position().top; - left = left + this.$box.position().left; - } + var html = this.code.get.call(this); - this.air.css({ left: left + 'px', top: top + 'px' }).show(); - }, - syncCode: function() - { - this.$el.val(this.$editor.html()); - }, + html = html.replace(/<br>|<br \/>/gi, ''); + if (html === '' || html === '<p></p>') + { + var ph = $('<span class="redactor_placeholder">').attr('contenteditable', false).text(this.opts.placeholder); - // API functions - setCode: function(html) - { - html = this.stripTags(html); - this.$editor.html(html).focus(); + if (this.opts.linebreaks === false) ph = $('<p>').append(ph); - this.syncCode(); - }, - getCode: function() - { - var html = ''; - if (this.opts.visual) + this.code.set.call(this, this.utils.outerHtml.call(this, ph)); + this.$editor.one('focus.redactor_placeholder', $.proxy(this.placeholder.focus, this)); + this.$editor.off('blur.redactor_placeholder'); + } + }, + remove: function(html, mode, events) { - html = this.$editor.html() - } - else - { - html = this.$el.val(); - } - - return this.stripTags(html); - }, - insertHtml: function(html) - { - this.$editor.focus(); - this.pasteHtmlAtCaret(html); - this.observeImages(); - this.syncCode(); - }, - - pasteHtmlAtCaret: function (html) - { - var sel, range; - if (this.document.getSelection) - { - sel = this.window.getSelection(); - if (sel.getRangeAt && sel.rangeCount) + if (this.opts.placeholder !== false) { - range = sel.getRangeAt(0); - range.deleteContents(); - var el = this.document.createElement("div"); - el.innerHTML = html; - var frag = this.document.createDocumentFragment(), node, lastNode; - while (node = el.firstChild) + if (events !== false) { - lastNode = frag.appendChild(node); + this.$editor.off('focus.redactor_placeholder'); + this.$editor.off('blur.redactor_placeholder'); } - range.insertNode(frag); - if (lastNode) + var ph = '<span class="redactor_placeholder" contenteditable="false">' + this.opts.placeholder + '</span>'; + if (this.opts.linebreaks === false && html == '<p>' + ph + '</p>') { - range = range.cloneRange(); - range.setStartAfter(lastNode); - range.collapse(true); - sel.removeAllRanges(); - sel.addRange(range); + if (mode !== false) html = this.opts.emptyHtml; + else html = ''; + } + else if (html == ph) + { + if (mode !== false) html = '<br />'; + else html = ''; + } } + + return html; } - else if (this.document.selection && this.document.selection.type != "Control") - { - this.document.selection.createRange().pasteHTML(html); - } }, + // DESTROY destroy: function() { - var html = this.getCode(); + if (!this.$editor) return false; - if (this.textareamode) + $(this.window).unbind('.redactor'); + this.$editor.unbind('.redactor'); + this.$editor.unbind('.redactor_placeholder'); + this.$editor.removeData('redactor'); + + var html = this.code.get.call(this); + + html = this.placeholder.remove.call(this, html, false); + + if (this.opts.textrareamode) { this.$box.after(this.$el); this.$box.remove(); this.$el.height(this.height).val(html).show(); } @@ -1254,10 +1450,14 @@ { $(this.opts.toolbarExternal).empty(); } $('.redactor_air').remove(); + if (typeof this.$frame !== 'undefined') + { + this.$frame.remove(); + } for (var i = 0; i < this.dropdowns.length; i++) { this.dropdowns[i].remove(); delete(this.dropdowns[i]); @@ -1266,1337 +1466,2459 @@ if (this.opts.autosave !== false) { clearInterval(this.autosaveInterval); } + this.$editor = null; + }, - // end API functions - // OBSERVERS - observeFormatting: function() + // BUFFER + buffer: { - var parent = this.getCurrentNode(); - - this.inactiveAllButtons(); - - $.each(this.opts.activeButtonsStates, $.proxy(function(i,s) + set: function() { - if ($(parent).closest(i,this.$editor.get()[0]).length != 0) + this.selection.saveDynamic.call(this); + this.opts.buffer.push(this.$editor.html()); + }, + get: function() + { + if (this.opts.buffer.length == 0) { - this.setBtnActive(s); + this.focus.set.call(this); + return false; } - }, this)); + var len = this.opts.buffer.length-1; - var tag = $(parent).closest(['p', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'td']); + // rebuffer + this.opts.rebuffer.push(this.$editor.html()); - if (typeof tag[0] !== 'undefined' && typeof tag[0].elem !== 'undefined' && $(tag[0].elem).size() != 0) - { - var align = $(tag[0].elem).css('text-align'); + this.$editor.html(this.opts.buffer[len]); + this.selection.restoreDynamic.call(this); + setTimeout($.proxy(this.observe.start, this), 1); - switch (align) + this.opts.buffer.splice(this.opts.buffer.len, 1); + }, + redo: function() + { + if (this.opts.rebuffer.length == 0) { - case 'right': - this.setBtnActive('alignright'); - break; - case 'center': - this.setBtnActive('aligncenter'); - break; - case 'justify': - this.setBtnActive('justify'); - break; - default: - this.setBtnActive('alignleft'); - break; + this.focus.set.call(this); + return false; } + + var len = this.opts.rebuffer.length-1; + + this.$editor.html(this.opts.rebuffer[len]); + this.selection.restoreDynamic.call(this); + setTimeout($.proxy(this.observe.start, this), 1); + + this.opts.rebuffer.splice(this.opts.rebuffer.len, 1); } }, - observeImages: function() + + // OBSERVERS + observe: { - if (this.opts.observeImages === false) + start: function() { - return false; - } + this.observe.images.call(this); + this.observe.tables.call(this); + }, + formatting: function() + { + var parent = this.selection.getElement.call(this); - this.$editor.find('img').each($.proxy(function(i,s) + this.button.inactiveAll.call(this); + + $.each(this.opts.activeButtonsStates, $.proxy(function(i,s) + { + if ($(parent).closest(i, this.$editor[0]).length != 0) + { + this.button.active.call(this, s); + } + + }, this)); + + var tag = $(parent).closest(['p', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'td']); + + if (typeof tag[0] !== 'undefined' && typeof tag[0].elem !== 'undefined' && $(tag[0].elem).size() != 0) + { + var align = $(tag[0].elem).css('text-align'); + + switch (align) + { + case 'right': + this.button.active.call(this, 'alignright'); + break; + case 'center': + this.button.active.call(this, 'aligncenter'); + break; + case 'justify': + this.button.active.call(this, 'justify'); + break; + default: + this.button.active.call(this, 'alignleft'); + break; + } + } + }, + images: function() { - if (this.browser('msie')) + if (this.opts.observeImages === false) { - $(s).attr('unselectable', 'on'); + return false; } - this.resizeImage(s); + this.$editor.find('img').each($.proxy(function(i,s) + { + if (this.utils.browser.call(this, 'msie')) + { + $(s).attr('unselectable', 'on'); + } - }, this)); + this.image.resize.call(this, s); - }, - observeTables: function() - { - this.$editor.find('table').click($.proxy(this.tableObserver, this)); - }, - observeScroll: function() - { - var scrolltop = $(this.document).scrollTop(); - var boxtop = this.$box.offset().top; - var left = 0; + }, this)); - if (scrolltop > boxtop) + }, + tables: function() { - var width = '100%'; - if (this.opts.fixedBox) + this.$editor.find('table').click($.proxy(this.table.observer, this)); + }, + scroll: function() + { + var scrolltop = $(this.document).scrollTop(); + var boxtop = this.$box.offset().top; + var left = 0; + + if (scrolltop > boxtop) { - left = this.$box.offset().left; - width = this.$box.innerWidth(); - } + var width = '100%'; + if (this.opts.fixedBox) + { + left = this.$box.offset().left; + width = this.$box.innerWidth(); + } - this.fixed = true; - this.$toolbar.css({ position: 'fixed', width: width, zIndex: 1005, top: this.opts.fixedTop + 'px', left: left }); + this.fixed = true; + this.$toolbar.css({ position: 'fixed', width: width, zIndex: 1005, top: this.opts.fixedTop + 'px', left: left }); + } + else + { + this.fixed = false; + this.$toolbar.css({ position: 'relative', width: 'auto', zIndex: 1, top: 0, left: left }); + } } - else - { - this.fixed = false; - this.$toolbar.css({ position: 'relative', width: 'auto', zIndex: 1, top: 0, left: left }); - } }, - // BUFFER - setBuffer: function() + // AUTOSAVE + autosave: function() { - this.saveSelection(); - this.opts.buffer = this.$editor.html(); + this.autosaveInterval = setInterval($.proxy(function() + { + $.ajax({ + url: this.opts.autosave, + type: 'post', + data: this.$el.attr('name') + '=' + escape(encodeURIComponent(this.code.get.call(this))), + success: $.proxy(function(data) + { + // callback + if (typeof this.opts.autosaveCallback === 'function') + { + this.opts.autosaveCallback(data, this); + } + + }, this) + }); + + + }, this), this.opts.interval*1000); }, - getBuffer: function() + + // AIR + air: { - if (this.opts.buffer === false) + enable: function() { - return false; - } + if (this.opts.air === false) + { + return false; + } - this.$editor.html(this.opts.buffer); + this.$air.hide(); - if (!this.browser('msie')) - { - this.restoreSelection(); - } + this.$editor.on('textselect.redactor', $.proxy(function(e) + { + this.air.show.call(this, e); - this.opts.buffer = false; - }, + }, this)); + this.$editor.on('textunselect.redactor', $.proxy(function() + { + this.$air.hide(); + }, this)); - // EXECCOMMAND - execCommand: function(cmd, param) - { - if (this.opts.visual == false) + }, + show: function(e) { - this.$el.focus(); - return false; + $('.redactor_air').hide(); + + var width = this.$air.innerWidth(); + var left = e.clientX; + + if ($(this.document).width() < (left + width)) + { + left = left - width; + } + + var top = e.clientY + $(this.document).scrollTop() + 14; + if (this.opts.iframe === true) + { + top = top + this.$box.position().top; + left = left + this.$box.position().left; + } + + this.$air.css({ left: left + 'px', top: top + 'px' }).show(); } + }, - try + // TOOLBAR + toolbar: + { + build: function() { + if (this.opts.toolbar === false) return false; - var parent; + this.$toolbar = $('<ul>').addClass('redactor_toolbar'); - if (cmd === 'inserthtml') + if (this.opts.air) { - if (this.browser('msie')) - { - this.$editor.focus(); - this.document.selection.createRange().pasteHTML(param); - } - else - { - this.pasteHtmlAtCaret(param); - //this.execRun(cmd, param); - } - - this.observeImages(); + $(this.$air).append(this.$toolbar); + $('body').append(this.$air); } - else if (cmd === 'unlink') + else { - parent = this.getParentNode(); - if ($(parent).get(0).tagName === 'A') - { - $(parent).replaceWith($(parent).text()); - } - else - { - this.execRun(cmd, param); - } + if (this.opts.toolbarExternal === false) this.$box.prepend(this.$toolbar); + else $(this.opts.toolbarExternal).html(this.$toolbar); } - else if (cmd === 'JustifyLeft' || cmd === 'JustifyCenter' || cmd === 'JustifyRight' || cmd === 'JustifyFull') + + $.each(this.opts.buttons, $.proxy(function(i,key) { - parent = this.getCurrentNode(); - var tag = $(parent).get(0).tagName; - if (this.opts.iframe === false && $(parent).parents('.redactor_editor').size() == 0) + if (key !== '|' && typeof this.opts.toolbar[key] !== 'undefined') { - return false; - } + var s = this.opts.toolbar[key]; - var tagsArray = ['P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE', 'TD']; - if ($.inArray(tag, tagsArray) != -1) - { - var align = false; - - if (cmd === 'JustifyCenter') + if (this.opts.fileUpload === false && key === 'file') { - align = 'center'; + return true; } - else if (cmd === 'JustifyRight') - { - align = 'right'; - } - else if (cmd === 'JustifyFull') - { - align = 'justify'; - } - if (align === false) - { - $(parent).css('text-align', ''); - } - else - { - $(parent).css('text-align', align); - } + this.$toolbar.append($('<li>').append(this.button.build.call(this, key, s))); } - else - { - this.execRun(cmd, param); - } + + // add separator + if (key === '|') this.$toolbar.append($('<li class="redactor_separator"></li>')); + + }, this)); + } + }, + + // COLORPICKER + picker: + { + build: function(dropdown, key) + { + $(dropdown).width(210); + + var rule = 'color'; + if (key === 'backcolor') + { + rule = 'background-color'; } - else if (cmd === 'formatblock' && param === 'blockquote') + + var len = this.opts.colors.length; + var _self = this; + for (var i = 0; i < len; ++i) { - parent = this.getCurrentNode(); - if ($(parent).get(0).tagName === 'BLOCKQUOTE') + var color = this.opts.colors[i]; + + var swatch = $('<a rel="' + color + '" href="javascript:void(null);" class="redactor_color_link"></a>').css({ 'backgroundColor': color }); + $(dropdown).append(swatch); + + $(swatch).on('click', function() { - if (this.browser('msie')) + var type = $(this).attr('rel'); + if (key === 'backcolor') { - var node = $('<p>' + $(parent).html() + '</p>'); - $(parent).replaceWith(node); + type = $(this).css('background-color'); } - else + + _self.picker.set.call(_self, rule, type); + + }); + } + + var elnone = $('<a href="javascript:void(null);" class="redactor_color_none"></a>').html(RLANG.none).on('click', function() { _self.setPickerData(rule, false); }); + $(dropdown).append(elnone); + + return dropdown; + }, + set: function(rule, type) + { + this.$editor.focus(); + this.inline.removeStyle.call(this, rule); + + if (type !== false) + { + this.inline.setStyle.call(this, rule, type); + } + + this.sync(); + } + }, + + // DROPDOWNS + dropdown: + { + build: function(dropdown, obj) + { + $.each(obj, $.proxy( + function (x, d) + { + if (typeof(d.className) === 'undefined') { - this.execRun(cmd, 'p'); + d.className = ''; } - } - else if ($(parent).get(0).tagName === 'P') - { - var parent2 = $(parent).parent(); - if ($(parent2).get(0).tagName === 'BLOCKQUOTE') + + var drop_a; + if (typeof d.name !== 'undefined' && d.name === 'separator') { - var node = $('<p>' + $(parent).html() + '</p>'); - $(parent2).replaceWith(node); - this.setSelection(node[0], 0, node[0], 0); + drop_a = $('<a class="redactor_separator_drop">'); } else { - if (this.browser('msie')) + drop_a = $('<a href="javascript:void(null);" class="' + d.className + '">' + d.title + '</a>'); + + if (typeof(d.callback) === 'function') { - var node = $('<blockquote>' + $(parent).html() + '</blockquote>'); - $(parent).replaceWith(node); + $(drop_a).click($.proxy(function(e) { d.callback(this, e, x); }, this)); } + else if (typeof(d.func) === 'undefined') + { + $(drop_a).click($.proxy(function() { this.exec.command.call(this, d.exec, x); }, this)); + } else { - this.execRun(cmd, param); + $(drop_a).click($.proxy(function(e) { + + var arr = d.func.split('.'); + if (arr.length == 1) this[s.func](x); + else this[arr[0]][arr[1]].call(this, x); + + }, this)); } } + + $(dropdown).append(drop_a); + + }, this) + ); + + return dropdown; + + }, + show: function(e, dropdown, key) + { + if (this.button.get.call(this, key).hasClass('dropact')) + { + this.dropdown.hideAll.call(this); + } + else + { + this.dropdown.hideAll.call(this); + + this.button.active.call(this, key); + this.button.get.call(this, key).addClass('dropact'); + + var left = this.button.get.call(this, key).offset().left; + + if (this.opts.air) + { + var air_top = this.$air.offset().top; + + $(dropdown).css({ position: 'absolute', left: left + 'px', top: air_top + 29 + 'px' }).show(); } + else if (this.opts.fixed && this.fixed) + { + $(dropdown).css({ position: 'fixed', left: left + 'px', top: '29px' }).show(); + } else { - this.execRun(cmd, param); + var top = this.$toolbar.offset().top + 29; + $(dropdown).css({ position: 'absolute', left: left + 'px', top: top + 'px' }).show(); } } - else if (cmd === 'formatblock' && (param === 'pre' || param === 'p')) + + var hdlHideDropDown = $.proxy(function(e) { this.dropdown.hide.call(this, e, dropdown, key); }, this); + + $(document).one('click', hdlHideDropDown); + this.$editor.one('click', hdlHideDropDown); + + e.stopPropagation(); + + }, + hideAll: function() + { + this.$toolbar.find('a.dropact').removeClass('redactor_act').removeClass('dropact'); + $('.redactor_dropdown').hide(); + }, + hide: function(e, dropdown, key) + { + if (!$(e.target).hasClass('dropact')) { - parent = this.getParentNode(); + $(dropdown).removeClass('dropact'); + this.showedDropDown = false; + this.dropdown.hideAll.call(this); + } + } + }, - if ($(parent).get(0).tagName === 'PRE') + // BUTTONS + button: + { + build: function(key, s) + { + var button = $('<a href="javascript:void(null);" title="' + s.title + '" class="redactor_btn_' + key + '"></a>'); + + if (typeof s.func === 'undefined') + { + button.click($.proxy(function() { - $(parent).replaceWith('<p>' + this.encodeEntities($(parent).text()) + '</p>'); + if ($.inArray(key, this.opts.activeButtons) != -1) + { + this.button.inactive.call(this); + + if (!button.hasClass('redactor_act')) this.button.active.call(this, key); + else this.button.inactive.call(this, key); + } + + if (this.utils.browser.call(this, 'mozilla')) this.$editor.focus(); + + this.exec.command.call(this, s.exec, key); + + }, this)); + } + else if (s.func !== 'show') + { + button.click($.proxy(function(e) { + + var arr = s.func.split('.'); + if (arr.length == 1) this[s.func](e); + else this[arr[0]][arr[1]].call(this, e); + + }, this)); + } + + if (typeof s.callback !== 'undefined' && s.callback !== false) + { + button.click($.proxy(function(e) { s.callback(this, e, key); }, this)); + } + + // dropdown + if (key === 'backcolor' || key === 'fontcolor' || typeof(s.dropdown) !== 'undefined') + { + var dropdown = $('<div class="redactor_dropdown" style="display: none;">'); + + if (key === 'backcolor' || key === 'fontcolor') + { + dropdown = this.picker.build.call(this, dropdown, key); } else { - this.execRun(cmd, param); + dropdown = this.dropdown.build.call(this, dropdown, s.dropdown); } + + this.dropdowns.push(dropdown.appendTo($(document.body))); + + // observing dropdown + this.hdlShowDropDown = $.proxy(function(e) { this.dropdown.show.call(this, e, dropdown, key); }, this); + + button.click(this.hdlShowDropDown); } - else + + return button; + }, + get: function(key) + { + if (this.opts.toolbar === false) return false; + + return $(this.$toolbar.find('a.redactor_btn_' + key)); + }, + active: function(key) + { + this.button.get.call(this, key).addClass('redactor_act'); + }, + inactive: function(key) + { + this.button.get.call(this, key).removeClass('redactor_act'); + }, + inactiveAll: function() + { + $.each(this.opts.activeButtons, $.proxy(function(i,s) { - if (cmd === 'inserthorizontalrule' && this.browser('msie')) + this.button.inactive.call(this, s); + + }, this)); + }, + changeIcon: function(key, classname) + { + this.button.get.call(this, key).addClass('redactor_btn_' + classname); + }, + removeIcon: function(key, classname) + { + this.button.get.call(this, key).removeClass('redactor_btn_' + classname); + }, + + addSeparator: function() + { + this.$toolbar.append($('<li class="redactor_separator"></li>')); + }, + addSeparatorAfter: function(key) + { + var $btn = this.button.get.call(this, key); + $btn.parent().after($('<li class="redactor_separator"></li>')); + }, + addSeparatorBefore: function(key) + { + var $btn = this.button.get.call(this, key); + $btn.parent().before($('<li class="redactor_separator"></li>')); + }, + removeSeparatorAfter: function(key) + { + var $btn = this.button.get.call(this, key); + $btn.parent().next().remove(); + }, + removeSeparatorBefore: function(key) + { + var $btn = this.button.get.call(this, key); + $btn.parent().prev().remove(); + }, + setRight: function(key) + { + if (this.opts.toolbar === false) return false; + this.button.get.call(this, key).parent().addClass('redactor_btn_right'); + }, + setLeft: function(key) + { + if (this.opts.toolbar === false) return false; + this.button.get.call(this, key).parent().removeClass('redactor_btn_right'); + }, + add: function(key, title, callback, dropdown) + { + if (this.opts.toolbar === false) return false; + var btn = this.button.build.call(this, key, { title: title, callback: callback, dropdown: dropdown }); + this.$toolbar.append($('<li>').append(btn)); + }, + addFirst: function(key, title, callback, dropdown) + { + if (this.opts.toolbar === false) return false; + var btn = this.button.build.call(this, key, { title: title, callback: callback, dropdown: dropdown }); + this.$toolbar.prepend($('<li>').append(btn)); + }, + addAfter: function(afterkey, key, title, callback, dropdown) + { + if (this.opts.toolbar === false) return false; + var btn = this.button.build.call(this, key, { title: title, callback: callback, dropdown: dropdown }); + var $btn = this.button.get.call(this, afterkey); + $btn.parent().after($('<li>').append(btn)); + }, + addBefore: function(beforekey, key, title, callback, dropdown) + { + if (this.opts.toolbar === false) return false; + var btn = this.button.build.call(this, key, { title: title, callback: callback, dropdown: dropdown }); + var $btn = this.button.get.call(this, beforekey); + $btn.parent().before($('<li>').append(btn)); + }, + remove: function(key, separator) + { + var $btn = this.button.get.call(this, key); + + if (separator === true) $btn.parent().next().remove(); + + $btn.parent().removeClass('redactor_btn_right'); + $btn.remove(); + } + }, + + // FOCUS + focus: + { + set: function() + { + if (this.opts.iframe && this.opts.linebreaks === false) + { + var contents = this.$editor.contents(); + var el = contents.eq(0); + this.$editor.focus(); + + if (el.size() !== 0 && el[0].tagName === 'HR') { - this.$editor.focus(); + el = contents.eq(1); } - if (cmd === 'formatblock' && this.browser('mozilla')) + if (el.size() !== 0) { - this.$editor.focus(); + this.selection.start.call(this, el); + return true; } - - this.execRun(cmd, param); } - if (cmd === 'inserthorizontalrule') + this.$editor.focus(); + + }, + end: function() + { + var range, selection; + if (this.document.createRange) + { + range = this.document.createRange(); + range.selectNodeContents(this.$editor[0]); + range.collapse(false); + selection = this.window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + } + else if (this.document.selection) + { + range = this.document.body.createTextRange(); + range.moveToElementText(this.$editor[0]); + range.collapse(false); + range.select(); + } + } + }, + + // TOGGLE + toggle: function() + { + var html; + + if (this.opts.visual) + { + this.selection.save.call(this); + + var height = null; + if (this.opts.iframe) { - this.$editor.find('hr').removeAttr('id'); + height = this.$frame.height(); + if (this.opts.fullpage) this.$editor.removeAttr('contenteditable'); + this.$frame.hide(); } + else + { + height = this.$editor.innerHeight(); + this.$editor.hide(); + } - this.syncCode(); + html = this.code.get.call(this); + html = $.trim(this.clean.start.call(this, html)); - if (this.oldIE()) + this.codeactions = html; + + this.$el.removeAttr('placeholder').height(height).val(html).show().focus(); + + // indenting + this.$el.on('keydown.redactor-textarea', function(e) { - this.$editor.focus(); - } + if (e.keyCode == 9) + { + var start = $(this).get(0).selectionStart; + $(this).val($(this).val().substring(0, start) + "\t" + $(this).val().substring($(this).get(0).selectionEnd)); + $(this).get(0).selectionStart = $(this).get(0).selectionEnd = start + 1; + return false; + } + }); - if (typeof this.opts.execCommandCallback === 'function') + this.button.inactiveAll.call(this); + this.button.active.call(this, 'html'); + this.opts.visual = false; + } + else + { + var html = this.$el.val(); + this.$el.hide(); + + this.codeactions = this.clean.removeSpaces.call(this, this.codeactions) == this.clean.removeSpaces.call(this, html); + + if (this.codeactions === false) { - this.opts.execCommandCallback(this, cmd); + // clean up + html = this.clean.savePreCode.call(this, html); + + if (this.opts.fullpage === false) + { + html = this.clean.stripTags.call(this, html); + html = this.clean.paragraphy.call(this, html); + } + + if (this.opts.linebreaks === false) + { + if (html === '') html = this.opts.emptyHtml; + else if (html.search(/^<hr\s?\/?>$/gi) !== -1) html = '<hr>' + this.opts.emptyHtml; + } + + this.code.set.call(this, html, false); } - if (this.opts.air) + if (this.opts.iframe) this.$frame.show() + else this.$editor.show(); + + this.$el.off('keydown.redactor-textarea'); + + if (this.codeactions === false) { - this.air.hide(); + setTimeout($.proxy(this.focus.set, this), 100); } + else + { + setTimeout($.proxy(function() + { + this.$editor.focus(); + this.selection.restore.call(this); + }, this), 100); + } + + this.observe.start.call(this); + this.button.inactive.call(this, 'html'); + this.opts.visual = true; } - catch (e) { } }, - execRun: function(cmd, param) + + // SYNC + sync: function() { - if (cmd === 'formatblock' && this.browser('msie')) + var html = this.code.get.call(this); + + if (typeof this.opts.syncBeforeCallback === 'function') { - param = '<' + param + '>'; + html = this.opts.syncBeforeCallback(this, html); } - this.document.execCommand(cmd, false, param); + this.$el.val(html); + + if (typeof this.opts.syncAfterCallback === 'function') + { + this.opts.syncAfterCallback(this, html); + } + }, - // FORMAT NEW LINE - formatNewLine: function(e) + // GET & SET CODE + code: { - var parent = this.getParentNode(); + getIframe: function() + { + this.$editor.removeAttr('contenteditable').removeAttr('dir'); + var html = this.utils.outerHtml.call(this, this.$frame.contents().children()); + this.$editor.attr('contenteditable', true).attr('dir', this.opts.direction); - if (parent.nodeName === 'DIV' && parent.className === 'redactor_editor') + return html; + }, + get: function() { - var element = $(this.getCurrentNode()); + var html = ''; - if (element.get(0).tagName === 'DIV' && (element.html() === '' || element.html() === '<br>')) + if (this.opts.fullpage) { - var newElement = $('<p>').append(element.clone().get(0).childNodes); - element.replaceWith(newElement); - newElement.html('<br />'); - this.setSelection(newElement[0], 0, newElement[0], 0); + html = $.trim(this.code.getIframe.call(this)); } - } - }, + else + { + html = this.clean.stripTags.call(this, $.trim(this.$editor.html())); + } - // SAFARI SHIFT KEY + ENTER - safariShiftKeyEnter: function(e, key) - { - if (e.shiftKey && key === 13) + // remove placeholder + html = this.placeholder.remove.call(this, html, true, false); + + // remove space + html = html.replace(/&#x200b;/gi, ''); + html = html.replace(/&#8203;/gi, ''); + + // php code fix + html = html.replace('<!--?php', '<?php'); + html = html.replace('?-->', '?>'); + + // bold, italic, del + if (this.opts.boldTag === 'strong') html = html.replace(/<b>([\w\W]*?)<\/b>/gi, '<strong>$1</strong>'); + else html = html.replace(/<strong>([\w\W]*?)<\/strong>/gi, '<b>$1</b>'); + + if (this.opts.italicTag === 'em') html = html.replace(/<i>([\w\W]*?)<\/i>/gi, '<em>$1</em>'); + else html = html.replace(/<em>([\w\W]*?)<\/em>/gi, '<i>$1</i>'); + + html = html.replace(/<strike>([\w\W]*?)<\/strike>/gi, '<del>$1</del>'); + + // script support + html = html.replace(/<title type="text\/javascript" style="display: none;" class="redactor-script-tag">([\w\W]*?)<\/title>/gi, '<script type="text/javascript">$1</script>'); + + html = this.clean.convertInlineTags.call(this, html); + + return html.replace(/<span id="(buffer|insert)-marker(.*?)">(.*?)<\/span>/gi, ''); + }, + setIframe: function(html) { - e.preventDefault(); - this.insertNodeAtCaret($('<span><br /></span>').get(0)); - this.syncCode(); - return false; - } - else - { - return true; - } - }, + var doc = this.iframe.page.call(this); + this.$frame[0].src = "about:blank" - // FORMAT EMPTY - formatEmpty: function(e) - { - var html = $.trim(this.$editor.html()); + html = this.clean.removeSpaces.call(this, html); - if (this.browser('mozilla')) + doc.open(); + doc.write(html); + doc.close(); + + this.sync(); + }, + setEditor: function(html, strip) { - html = html.replace(/<br>/i, ''); - } - var thtml = html.replace(/<(?:.|\n)*?>/gm, ''); + if (strip !== false) + { + html = this.clean.stripTags.call(this, html); + if (this.opts.linebreaks === false) + { + html = this.clean.paragraphy.call(this, html); + } + else + { + html = html.replace(/<p(.*?)>([\w\W]*?)<\/p>/gi, '$2<br>'); + } + } - if (html === '' || thtml === '') + this.$editor.html(html); + this.sync(); + }, + set: function(html, strip) { - e.preventDefault(); + html = html.toString(); - var node = $(this.opts.emptyHtml).get(0); - this.$editor.html(node); - this.setSelection(node, 0, node, 0); + // script support + html = html.replace(/<script(.*?)>([\w\W]*?)<\/script>/gi, '<title type="text/javascript" style="display: none;" class="redactor-script-tag">$2</title>'); - this.syncCode(); - return false; + if (this.opts.fullpage) this.code.setIframe.call(this, html); + else this.code.setEditor.call(this, html, strip); } - else - { - this.syncCode(); - } }, - // PARAGRAPHY - paragraphy: function (str) + // INSERT + insert: { - str = $.trim(str); - if (str === '' || str === '<p></p>') + html: function(html, sync) { - return this.opts.emptyHtml; - } + this.$editor.focus(); - // convert div to p - if (this.opts.convertDivs) + if (this.opts.linebreaks === false) + { + var current = this.selection.getBlock.call(this); + if (current !== false) + { + var blockhtml = $.trim(current.innerHTML.replace(/<br\s?\/?>/gi, '')); + } + } + + if (this.opts.linebreaks === false && current !== false && current.tagName === 'P' && blockhtml == '') + { + var tmphtml = $(html); + $(current).replaceWith(tmphtml); + this.selection.end.call(this, tmphtml.last()); + + } + else + { + var sel, range; + if (this.window.getSelection) + { + sel = this.window.getSelection(); + if (sel.getRangeAt && sel.rangeCount) + { + range = sel.getRangeAt(0); + range.deleteContents(); + + var el = this.document.createElement("div"); + el.innerHTML = html; + var frag = this.document.createDocumentFragment(), node, lastNode; + while ((node = el.firstChild)) + { + lastNode = frag.appendChild(node); + } + + range.insertNode(frag); + + if (lastNode) + { + range = range.cloneRange(); + range.setStartAfter(lastNode); + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); + } + } + } + else if (this.document.selection && this.document.selection.type != "Control") // IE < 9 + { + this.document.selection.createRange().pasteHTML(html); + } + } + + this.observe.start.call(this); + + if (sync !== false) + { + this.sync(); + } + + }, + text: function(html) { - str = str.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '<p>$2</p>'); - } + html = $(html).text(); + this.document.execCommand('inserthtml', false, html); + }, + exec: function(html) + { + this.$editor.focus(); + this.document.execCommand('inserthtml', false, html); + this.observe.start.call(this); + this.sync(); + }, + force: function(html) + { + this.$editor.focus(); - // inner functions - var X = function(x, a, b) { return x.replace(new RegExp(a, 'g'), b); }; - var R = function(a, b) { return X(str, a, b); }; + if (this.opts.linebreaks === false && /<\/(P|H[1-6]|UL|OL|DIV|TABLE|BLOCKQUOTE|PRE|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)>/gi.test(html)) + { + var current = this.selection.getBlock.call(this); + var extract = this.utils.extractBlockContentsFromCaret.call(this); - // block elements - var blocks = '(table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|blockquote|address|math|style|script|object|input|param|p|h[1-6])'; + $(current).after($('<p id="redactor-insert-id">')); - //str = '<p>' + str; - str += '\n'; + html = $(html).after('<' + current.tagName + '>' + this.utils.getFragmentHtml.call(this, extract) + '</' + current.tagName + '>'); - R('<br />\\s*<br />', '\n\n'); - R('(<' + blocks + '[^>]*>)', '\n$1'); - R('(</' + blocks + '>)', '$1\n\n'); - R('\r\n|\r', '\n'); // newlines - R('\n\n+', '\n\n'); // remove duplicates - R('\n?((.|\n)+?)$', '<p>$1</p>\n'); // including one at the end - R('<p>\\s*?</p>', ''); // remove empty p - R('<p>(<div[^>]*>\\s*)', '$1<p>'); - R('<p>([^<]+)\\s*?(</(div|address|form)[^>]*>)', '<p>$1</p>$2'); - R('<p>\\s*(</?' + blocks + '[^>]*>)\\s*</p>', '$1'); - R('<p>(<li.+?)</p>', '$1'); - R('<p>\\s*(</?' + blocks + '[^>]*>)', '$1'); - R('(</?' + blocks + '[^>]*>)\\s*</p>', '$1'); - R('(</?' + blocks + '[^>]*>)\\s*<br />', '$1'); - R('<br />(\\s*</?(p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)', '$1'); + this.$editor.find('p#redactor-insert-id').replaceWith(html); - // pre - if (str.indexOf('<pre') != -1) + if ($.trim($(current).html()) == '') + { + $(current).remove(); + } + + + this.selection.end.call(this, html); + this.observe.start.call(this) + this.sync(); + } + else + { + this.insert.html.call(this, html); + } + + }, + + // without delete contents + beforeCaret: function(node) { - R('(<pre(.|\n)*?>)((.|\n)*?)</pre>', function(m0, m1, m2, m3) + var sel, range; + if (this.window.getSelection) { - return X(m1, '\\\\([\'\"\\\\])', '$1') + X(X(X(m3, '<p>', '\n'), '</p>|<br />', ''), '\\\\([\'\"\\\\])', '$1') + '</pre>'; - }); - } + sel = this.window.getSelection(); + if (sel.getRangeAt && sel.rangeCount) + { + range = sel.getRangeAt(0); + range.insertNode(node); + } + } + }, + afterCaret: function(node) + { + if (this.window.getSelection) + { + var sel = this.window.getSelection(); + if (sel.rangeCount) + { + var range = sel.getRangeAt(0); + range.collapse(false); + range.insertNode(node); + range = range.cloneRange(); + range.selectNodeContents(node); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + } + } + }, - return R('\n</p>$', '</p>'); + // with delete contents + nodeAtCaret: function(node) + { + var sel; + if (this.window.getSelection) + { + sel = this.selection.get.call(this); + if (sel.getRangeAt && sel.rangeCount) + { + range = sel.getRangeAt(0); + range.deleteContents(); + range.insertNode(node); + range.setEndAfter(node); + range.setStartAfter(node); + sel.removeAllRanges(); + sel.addRange(range); + } + } + } }, - // REMOVE TAGS - stripTags: function(html) + // CODE CLEAN UP + clean: { - var allowed = this.opts.allowedTags; - var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi; - return html.replace(tags, function ($0, $1) + start: function(html) { - return $.inArray($1.toLowerCase(), allowed) > '-1' ? $0 : ''; - }); - }, + html = this.clean.removeSpaces.call(this, html); + html = this.clean.removeEmptyTags.call(this, html); + html = this.clean.addBefore.call(this, html); + html = this.clean.addAfter.call(this, html); + html = this.clean.setTabulation.call(this, html); + return html; + }, + removeSpaces: function(html) + { + // save pre + var prebuffer = []; + var pre = html.match(/<pre(.*?)>([\w\W]*?)<\/pre>/gi); + if (pre !== null) + { + $.each(pre, function(i,s) + { + html = html.replace(s, 'prebuffer_' + i); + prebuffer.push(s); + }); + } - savePreCode: function(html) - { - var pre = html.match(/<pre(.*?)>([\w\W]*?)<\/pre>/gi); - if (pre !== null) + // save script + var scriptbuffer = []; + var script = html.match(/<script(.*?)>([\w\W]*?)<\/script>/gi); + if (script !== null) + { + $.each(script, function(i,s) + { + html = html.replace(s, 'scriptbuffer_' + i); + scriptbuffer.push(s); + }); + } + + // save title + var titlebuffer = []; + var title = html.match(/<title(.*?)>([\w\W]*?)<\/title>/gi); + if (title !== null) + { + $.each(title, function(i,s) + { + html = html.replace(s, 'titlebuffer_' + i); + titlebuffer.push(s); + }); + } + + html = html.replace(/\s{2,}/g, ' '); + html = html.replace(/\n/g, ' '); + html = html.replace(/[\t]*/g, ''); + html = html.replace(/\n\s*\n/g, "\n"); + html = html.replace(/^[\s\n]*/g, ''); + html = html.replace(/[\s\n]*$/g, ''); + html = html.replace(/>\s+</g, '><'); + + html = this.clean.replacer.call(this, prebuffer, 'prebuffer', html); + html = this.clean.replacer.call(this, scriptbuffer, 'scriptbuffer', html); + html = this.clean.replacer.call(this, titlebuffer, 'titlebuffer', html); + + html = html.replace(/\n\n/g, "\n"); + + return html; + }, + replacer: function(arr, name, html) { - $.each(pre, $.proxy(function(i,s) + if (arr) { - var arr = s.match(/<pre(.*?)>([\w\W]*?)<\/pre>/i); - arr[2] = this.encodeEntities(arr[2]); - html = html.replace(s, '<pre' + arr[1] + '>' + arr[2] + '</pre>'); - }, this)); - } + $.each(arr, function(i,s) + { + html = html.replace(name + '_' + i, s); + }); + } - return html; - }, - encodeEntities: function(str) - { - str = String(str).replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"'); - return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); - }, - cleanupPre: function(s) - { - s = s.replace(/<br>/gi, '\n'); - s = s.replace(/<\/p>/gi, '\n'); - s = s.replace(/<\/div>/gi, '\n'); + return html; + }, + removeEmptyTags: function(html) + { + html = html.replace(/<span>([\w\W]*?)<\/span>/gi, '$1'); - var tmp = this.document.createElement("div"); - tmp.innerHTML = s; - return tmp.textContent||tmp.innerText; + var etags = ["<pre></pre>","<blockquote>\\s*</blockquote>","<em>\\s*</em>","<ul></ul>","<ol></ol>","<li></li>","<table></table>","<tr></tr>","<span>\\s*<span>", "<span>&nbsp;<span>", "<b>\\s*</b>", "<b>&nbsp;</b>", "<p>\\s*</p>", "<p>&nbsp;</p>", "<p>\\s*<br>\\s*</p>", "<div>\\s*</div>", "<div>\\s*<br>\\s*</div>"]; + for (var i = 0; i < etags.length; ++i) + { + var bbb = etags[i]; + html = html.replace(new RegExp(bbb,'gi'), ""); + } - }, + return html; + }, + addBefore: function(html) + { + var lb = '\r\n'; + var btags = ["<p", "<form","</ul>", '</ol>', "<fieldset","<legend","<object","<embed","<select","<option","<input","<textarea","<pre","<blockquote","<ul","<ol","<li","<dl","<dt","<dd","<table", "<thead","<tbody","<caption","</caption>","<th","<tr","<td","<figure", "<body", "<head>", "<meta", "<title>", "<link", "<script", "</head>"]; + for (var i = 0; i < btags.length; ++i) + { + var eee = btags[i]; + html = html.replace(new RegExp(eee,'gi'),lb+eee); + } + return html; + }, + addAfter: function(html) + { + var lb = '\r\n'; + var atags = ['</p>', '</div>', '</h1>', '</h2>', '</h3>', '</h4>', '</h5>', '</h6>', '<br>', '<br />', '</dl>', '</dt>', '</dd>', '</form>', '</blockquote>', '</pre>', '</legend>', '</fieldset>', '</object>', '</embed>', '</textarea>', '</select>', '</option>', '</table>', '</thead>', '</tbody>', '</tr>', '</td>', '</th>', '</figure>', '</script>', '</title>', '</body>']; + for (var i = 0; i < atags.length; ++i) + { + var aaa = atags[i]; + html = html.replace(new RegExp(aaa,'gi'),aaa+lb); + } - // PASTE CLEANUP - pasteCleanUp: function(html) - { - var parent = this.getParentNode(); + return html; + }, + setTabulation: function(html) + { + html = html.replace(/<li/g, "\t<li"); + html = html.replace(/<tr/g, "\t<tr"); + html = html.replace(/<td/g, "\t\t<td"); + html = html.replace(/<\/tr>/g, "\t</tr>"); - // clean up pre - if ($(parent).get(0).tagName === 'PRE') + return html; + }, + paragraphy: function(html) { - html = this.cleanupPre(html); - this.pasteCleanUpInsert(html); - return true; - } + html = $.trim(html); - // remove comments and php tags - html = html.replace(/<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi, ''); + if (this.opts.linebreaks === true) return html; + if (html === '' || html === '<p></p>') return this.opts.emptyHtml; - // remove nbsp - html = html.replace(/(&nbsp;){2,}/gi, '&nbsp;'); + // convert div to p + if (this.opts.convertDivs) + { + html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '<p>$2</p>'); + } - // remove google docs marker - html = html.replace(/<b\sid="internal-source-marker(.*?)">([\w\W]*?)<\/b>/gi, "$2"); + html = html + "\n"; - // strip tags - html = this.stripTags(html); + var safes = []; + var z = 0; - // prevert - html = html.replace(/<td><\/td>/gi, '[td]'); - html = html.replace(/<td>&nbsp;<\/td>/gi, '[td]'); - html = html.replace(/<td><br><\/td>/gi, '[td]'); - html = html.replace(/<a(.*?)href="(.*?)"(.*?)>([\w\W]*?)<\/a>/gi, '[a href="$2"]$4[/a]'); - html = html.replace(/<iframe(.*?)>([\w\W]*?)<\/iframe>/gi, '[iframe$1]$2[/iframe]'); - html = html.replace(/<video(.*?)>([\w\W]*?)<\/video>/gi, '[video$1]$2[/video]'); - html = html.replace(/<audio(.*?)>([\w\W]*?)<\/audio>/gi, '[audio$1]$2[/audio]'); - html = html.replace(/<embed(.*?)>([\w\W]*?)<\/embed>/gi, '[embed$1]$2[/embed]'); - html = html.replace(/<object(.*?)>([\w\W]*?)<\/object>/gi, '[object$1]$2[/object]'); - html = html.replace(/<param(.*?)>/gi, '[param$1]'); - html = html.replace(/<img(.*?)style="(.*?)"(.*?)>/gi, '[img$1$3]'); + if (html.search(/<(table|div|pre|object)/gi) !== -1) + { + $.each(html.match(/<(table|div|pre|object)(.*?)>([\w\W]*?)<\/(table|div|pre|object)>/gi), function(i,s) + { + z++; + safes[z] = s; + html = html.replace(s, '{replace' + z + '}\n'); + }); + } - // remove attributes - html = html.replace(/<(\w+)([\w\W]*?)>/gi, '<$1>'); + html = html.replace(/<br \/>\s*<br \/>/gi, "\n\n"); - // remove empty - html = html.replace(/<[^\/>][^>]*>(\s*|\t*|\n*|&nbsp;|<br>)<\/[^>]+>/gi, ''); - html = html.replace(/<[^\/>][^>]*>(\s*|\t*|\n*|&nbsp;|<br>)<\/[^>]+>/gi, ''); + function R(str, mod, r) + { + return html.replace(new RegExp(str, mod), r); + } - // revert - html = html.replace(/\[td\]/gi, '<td>&nbsp;</td>'); - html = html.replace(/\[a href="(.*?)"\]([\w\W]*?)\[\/a\]/gi, '<a href="$1">$2</a>'); - html = html.replace(/\[iframe(.*?)\]([\w\W]*?)\[\/iframe\]/gi, '<iframe$1>$2</iframe>'); - html = html.replace(/\[video(.*?)\]([\w\W]*?)\[\/video\]/gi, '<video$1>$2</video>'); - html = html.replace(/\[audio(.*?)\]([\w\W]*?)\[\/audio\]/gi, '<audio$1>$2</audio>'); - html = html.replace(/\[embed(.*?)\]([\w\W]*?)\[\/embed\]/gi, '<embed$1>$2</embed>'); - html = html.replace(/\[object(.*?)\]([\w\W]*?)\[\/object\]/gi, '<object$1>$2</object>'); - html = html.replace(/\[param(.*?)\]/gi, '<param$1>'); - html = html.replace(/\[img(.*?)\]/gi, '<img$1>'); + var blocks = '(html|body|head|title|meta|style|script|link|table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|option|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)'; + html = R('(<' + blocks + '[^>]*>)', 'gi', "\n$1"); + html = R('(</' + blocks + '>)', 'gi', "$1\n\n"); + html = R("\r\n", 'g', "\n"); + html = R("\r", 'g', "\n"); + html = R("/\n\n+/", 'g', "\n\n"); - // convert div to p - if (this.opts.convertDivs) - { - html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '<p>$2</p>'); - } + var htmls = html.split(new RegExp("\n\s*\n", 'g'), -1); + html = ''; - // remove span - html = html.replace(/<span>([\w\W]*?)<\/span>/gi, '$1'); + for (i in htmls) + { + if (htmls[i].search('{replace') == -1) html += '<p>' + htmls[i].replace(/^\n+|\n+$/g, "") + "</p>"; + else html += htmls[i]; + } - html = html.replace(/\n{3,}/gi, '\n'); + html = R('<p>\s*</p>', 'gi', ''); + html = R('<p>([^<]+)</(div|address|form)>', 'gi', "<p>$1</p></$2>"); + html = R('<p>\s*(</?' + blocks + '[^>]*>)\s*</p>', 'gi', "$1"); + html = R("<p>(<li.+?)</p>", 'gi', "$1"); + html = R('<p><blockquote([^>]*)>', 'gi', "<blockquote$1><p>"); + html = R('</blockquote></p>', 'gi', '</p></blockquote>'); + html = R('<p>\s*(</?' + blocks + '[^>]*>)', 'gi', "$1"); - // remove dirty p - html = html.replace(/<p><p>/gi, '<p>'); - html = html.replace(/<\/p><\/p>/gi, '</p>'); + html = R('(</?' + blocks + '[^>]*>)\s*</p>', 'gi', "$1"); + html = R('(</?' + blocks + '[^>]*>)\s*<br />', 'gi', "$1"); + html = R('<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)', 'gi', '$1'); + html = R("\n</p>", 'gi', '</p>'); - // FF fix - if (this.browser('mozilla')) - { - html = html.replace(/<br>$/gi, ''); - } + html = R('</li><p>', 'gi', '</li>'); + html = R('</ul><p>(.*?)</li>', 'gi', '</ul></li>'); + html = R('</ol><p>', 'gi', '</ol>'); - this.pasteCleanUpInsert(html); + html = this.clean.convertInlineTags.call(this, html); - }, + $.each(safes, function(i,s) + { + html = html.replace('{replace' + i + '}', s); + }); - pasteCleanUpInsert: function(html) - { - this.execCommand('inserthtml', html); + return $.trim(html); - if (this.opts.autoresize === true) + }, + convertInlineTags: function(html) { - $(this.document.body).scrollTop(this.saveScroll); - } - else - { - this.$editor.scrollTop(this.saveScroll); - } - }, + var boldTag = 'strong'; + if (this.opts.boldTag === 'b') boldTag = 'b'; + var italicTag = 'em'; + if (this.opts.italicTag === 'i') italicTag = 'i'; - // TEXTAREA CODE FORMATTING - formattingRemove: function(html) - { - // save pre - var prebuffer = []; - var pre = html.match(/<pre(.*?)>([\w\W]*?)<\/pre>/gi); - if (pre !== null) + html = html.replace(/<span style="font-style: italic;">([\w\W]*?)<\/span>/gi, '<' + italicTag + '>$1</' + italicTag + '>'); + html = html.replace(/<span style="font-weight: bold;">([\w\W]*?)<\/span>/gi, '<' + boldTag + '>$1</' + boldTag + '>'); + + return html; + }, + stripTags: function(html) { - $.each(pre, function(i,s) + var allowed = false; + if (this.opts.allowedTags !== false) { - html = html.replace(s, 'prebuffer_' + i); - prebuffer.push(s); + allowed = true; + } + + var arr = allowed === true ? this.opts.allowedTags : this.opts.deniedTags; + var cleartags = this.opts.clearTags; + + var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi; + html = html.replace(tags, function ($0, $1) + { + return $.inArray($1.toLowerCase(), cleartags) > '-1' ? '' : $0; }); - } - html = html.replace(/\s{2,}/g, ' '); - html = html.replace(/\n/g, ' '); - html = html.replace(/[\t]*/g, ''); - html = html.replace(/\n\s*\n/g, "\n"); - html = html.replace(/^[\s\n]*/g, ''); - html = html.replace(/[\s\n]*$/g, ''); - html = html.replace(/>\s+</g, '><'); + html = html.replace(tags, function ($0, $1) + { + if (allowed === true) + { + return $.inArray($1.toLowerCase(), arr) > '-1' ? $0 : ''; + } + else + { + return $.inArray($1.toLowerCase(), arr) > '-1' ? '' : $0; + } + }); - if (prebuffer) + return html; + }, + savePreCode: function(html, encode) { - $.each(prebuffer, function(i,s) + var pre = html.match(/<pre(.*?)>([\w\W]*?)<\/pre>/gi); + if (pre !== null) { - html = html.replace('prebuffer_' + i, s); - }); + $.each(pre, $.proxy(function(i,s) + { + var arr = s.match(/<pre(.*?)>([\w\W]*?)<\/pre>/i); + arr[2] = arr[2].replace(/&nbsp;/g, ' '); + arr[2] = arr[2].replace(/(<br>|<br \/>)/gi, '\n'); - prebuffer = []; - } + if (encode !== false) + { + arr[2] = this.clean.encodeEntities.call(this, arr[2]); + } - return html; - }, - formattingIndenting: function(html) - { - html = html.replace(/<li/g, "\t<li"); - html = html.replace(/<tr/g, "\t<tr"); - html = html.replace(/<td/g, "\t\t<td"); - html = html.replace(/<\/tr>/g, "\t</tr>"); + html = html.replace(s, '<pre' + arr[1] + '>' + arr[2] + '</pre>'); - return html; - }, - formattingEmptyTags: function(html) - { - var etags = ["<pre></pre>","<blockquote>\\s*</blockquote>","<em>\\s*</em>","<ul></ul>","<ol></ol>","<li></li>","<table></table>","<tr></tr>","<span>\\s*<span>", "<span>&nbsp;<span>", "<b>\\s*</b>", "<b>&nbsp;</b>", "<p>\\s*</p>", "<p>&nbsp;</p>", "<p>\\s*<br>\\s*</p>", "<div>\\s*</div>", "<div>\\s*<br>\\s*</div>"]; - for (var i = 0; i < etags.length; ++i) - { - var bbb = etags[i]; - html = html.replace(new RegExp(bbb,'gi'), ""); - } + }, this)); + } - return html; - }, - formattingAddBefore: function(html) - { - var lb = '\r\n'; - var btags = ["<p", "<form","</ul>", '</ol>', "<fieldset","<legend","<object","<embed","<select","<option","<input","<textarea","<pre","<blockquote","<ul","<ol","<li","<dl","<dt","<dd","<table", "<thead","<tbody","<caption","</caption>","<th","<tr","<td","<figure"]; - for (var i = 0; i < btags.length; ++i) + return html; + }, + encodeEntities: function(str) { - var eee = btags[i]; - html = html.replace(new RegExp(eee,'gi'),lb+eee); + str = String(str).replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"'); + return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); } - - return html; }, - formattingAddAfter: function(html) + + // PASTE + paste: { - var lb = '\r\n'; - var atags = ['</p>', '</div>', '</h1>', '</h2>', '</h3>', '</h4>', '</h5>', '</h6>', '<br>', '<br />', '</dl>', '</dt>', '</dd>', '</form>', '</blockquote>', '</pre>', '</legend>', '</fieldset>', '</object>', '</embed>', '</textarea>', '</select>', '</option>', '</table>', '</thead>', '</tbody>', '</tr>', '</td>', '</th>', '</figure>']; - for (var i = 0; i < atags.length; ++i) + clean: function(html) { - var aaa = atags[i]; - html = html.replace(new RegExp(aaa,'gi'),aaa+lb); - } + var parent = this.selection.getParent.call(this); - return html; - }, - formatting: function(html) - { - html = this.formattingRemove(html); + // clean up pre + if (parent && $(parent).get(0).tagName === 'PRE') + { + html = this.paste.pre.call(this, html); + this.paste.insert.call(this, html); + return true; + } - // empty tags - html = this.formattingEmptyTags(html); + // remove comments and php tags + html = html.replace(/<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi, ''); - // add formatting before - html = this.formattingAddBefore(html); + // remove nbsp + html = html.replace(/(&nbsp;){2,}/gi, '&nbsp;'); - // add formatting after - html = this.formattingAddAfter(html); + // remove google docs marker + html = html.replace(/<b\sid="internal-source-marker(.*?)">([\w\W]*?)<\/b>/gi, "$2"); - // indenting - html = this.formattingIndenting(html); + // strip tags + html = this.clean.stripTags.call(this, html); - return html; - }, + // prevert + html = html.replace(/<td><\/td>/gi, '[td]'); + html = html.replace(/<td>&nbsp;<\/td>/gi, '[td]'); + html = html.replace(/<td><br><\/td>/gi, '[td]'); + html = html.replace(/<a(.*?)href="(.*?)"(.*?)>([\w\W]*?)<\/a>/gi, '[a href="$2"]$4[/a]'); + html = html.replace(/<iframe(.*?)>([\w\W]*?)<\/iframe>/gi, '[iframe$1]$2[/iframe]'); + html = html.replace(/<video(.*?)>([\w\W]*?)<\/video>/gi, '[video$1]$2[/video]'); + html = html.replace(/<audio(.*?)>([\w\W]*?)<\/audio>/gi, '[audio$1]$2[/audio]'); + html = html.replace(/<embed(.*?)>([\w\W]*?)<\/embed>/gi, '[embed$1]$2[/embed]'); + html = html.replace(/<object(.*?)>([\w\W]*?)<\/object>/gi, '[object$1]$2[/object]'); + html = html.replace(/<param(.*?)>/gi, '[param$1]'); + html = html.replace(/<img(.*?)style="(.*?)"(.*?)>/gi, '[img$1$3]'); - // TOGGLE - toggle: function() - { - var html; + // remove attributes + html = html.replace(/<(\w+)([\w\W]*?)>/gi, '<$1>'); - if (this.opts.visual) - { - var height = this.$editor.innerHeight(); + // remove empty + html = html.replace(/<[^\/>][^>]*>(\s*|\t*|\n*|&nbsp;|<br>)<\/[^>]+>/gi, ''); + html = html.replace(/<[^\/>][^>]*>(\s*|\t*|\n*|&nbsp;|<br>)<\/[^>]+>/gi, ''); - this.$editor.hide(); - this.$content.hide(); + // revert + html = html.replace(/\[td\]/gi, '<td>&nbsp;</td>'); + html = html.replace(/\[a href="(.*?)"\]([\w\W]*?)\[\/a\]/gi, '<a href="$1">$2</a>'); + html = html.replace(/\[iframe(.*?)\]([\w\W]*?)\[\/iframe\]/gi, '<iframe$1>$2</iframe>'); + html = html.replace(/\[video(.*?)\]([\w\W]*?)\[\/video\]/gi, '<video$1>$2</video>'); + html = html.replace(/\[audio(.*?)\]([\w\W]*?)\[\/audio\]/gi, '<audio$1>$2</audio>'); + html = html.replace(/\[embed(.*?)\]([\w\W]*?)\[\/embed\]/gi, '<embed$1>$2</embed>'); + html = html.replace(/\[object(.*?)\]([\w\W]*?)\[\/object\]/gi, '<object$1>$2</object>'); + html = html.replace(/\[param(.*?)\]/gi, '<param$1>'); + html = html.replace(/\[img(.*?)\]/gi, '<img$1>'); - html = this.$editor.html(); - //html = $.trim(this.formatting(html)); - this.$el.height(height).val(html).show().focus(); + // convert div to p + if (this.opts.convertDivs) + { + html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '<p>$2</p>'); + } - this.setBtnActive('html'); - this.opts.visual = false; - } - else - { - this.$el.hide(); - var html = this.$el.val(); + // remove span + html = html.replace(/<span(.*?)>([\w\W]*?)<\/span>/gi, '$2'); - //html = this.savePreCode(html); + html = html.replace(/\n{3,}/gi, '\n'); - // clean up - //html = this.stripTags(html); + // remove dirty p + html = html.replace(/<p><p>/gi, '<p>'); + html = html.replace(/<\/p><\/p>/gi, '</p>'); - // set code - this.$editor.html(html).show(); - this.$content.show(); + if (this.opts.linebreaks === true) + { + html = html.replace(/<p(.*?)>([\w\W]*?)<\/p>/gi, '$2<br>'); + } - if (this.$editor.html() === '') + // FF fix + if (this.utils.browser.call(this, 'mozilla')) { - this.setCode(this.opts.emptyHtml); + html = html.replace(/<br>$/gi, ''); } - this.$editor.focus(); + this.paste.insert.call(this, html); - this.setBtnInactive('html'); - this.opts.visual = true; + }, + pre: function(s) + { + s = s.replace(/<br>/gi, '\n'); + s = s.replace(/<\/p>/gi, '\n'); + s = s.replace(/<\/div>/gi, '\n'); - this.observeImages(); - this.observeTables(); - } - }, + var tmp = this.document.createElement("div"); + tmp.innerHTML = s; + return tmp.textContent||tmp.innerText; - // AUTOSAVE - autoSave: function() - { - this.autosaveInterval = setInterval($.proxy(function() + }, + insert: function(html) { - $.ajax({ - url: this.opts.autosave, - type: 'post', - data: this.$el.attr('name') + '=' + escape(encodeURIComponent(this.getCode())), - success: $.proxy(function(data) + if (this.selectall) + { + if (this.opts.linebreaks === false) { - // callback - if (typeof this.opts.autosaveCallback === 'function') - { - this.opts.autosaveCallback(data, this); - } + this.$editor.html(this.opts.emptyHtml); + } + this.focus.set.call(this); + } - }, this) - }); + this.insert.exec.call(this, html); + this.selectall = false; - - }, this), this.opts.interval*1000); + if (this.opts.autoresize === true) $(this.document.body).scrollTop(this.saveScroll); + else this.$editor.scrollTop(this.saveScroll); + } }, - // TOOLBAR - buildToolbar: function() + // ALIGNMENT + alignment: { - if (this.opts.toolbar === false) + left: function() { - return false; - } - - this.$toolbar = $('<ul>').addClass('redactor_toolbar'); - - if (this.opts.air) + this.alignment.set.call(this, '', 'JustifyLeft'); + }, + right: function() { - $(this.air).append(this.$toolbar); - $('body').append(this.air); - } - else + this.alignment.set.call(this, 'right', 'JustifyRight'); + }, + center: function() { - if (this.opts.toolbarExternal === false) + this.alignment.set.call(this, 'center', 'JustifyCenter'); + }, + justify: function() + { + this.alignment.set.call(this, 'justify', 'JustifyFull'); + }, + set: function(type, cmd) + { + // buffer + this.buffer.set.call(this); + + if (this.utils.oldIE.call(this)) { - this.$box.prepend(this.$toolbar); + this.document.execCommand(cmd, false, false); + return true; } - else + + this.selection.save.call(this); + + var elements = this.selection.getBlocks.call(this); + var element = this.selection.getBlock.call(this); + if (elements.length === 0) { - $(this.opts.toolbarExternal).html(this.$toolbar); + elements.push(element); } + + $.each(elements, $.proxy(function(i,s) + { + var $el = false; + if ($.inArray(s.tagName, this.opts.alignmentTags) !== -1) + { + $el = $(s); + } + else + { + $el = $(s).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]); + } + + if ($el) + { + $el.css('text-align', type); + this.utils.removeEmptyStyleAttr.call(this, $el); + } + + }, this)); + + this.selection.restore.call(this); + this.sync(); } + }, - $.each(this.opts.buttons, $.proxy(function(i,key) + // INDENTING + indenting: + { + indent: function() { + this.indenting.start.call(this, 'indent'); + }, + outdent: function() + { + this.indenting.start.call(this, 'outdent'); + }, + start: function(cmd) + { + var elements = this.selection.getBlocks.call(this); + var element = this.selection.getBlock.call(this); - if (key !== '|' && typeof this.opts.toolbar[key] !== 'undefined') + if (!this.utils.isParentRedactor.call(this, element)) { - var s = this.opts.toolbar[key]; + return false; + } - if (this.opts.fileUpload === false && key === 'file') + // buffer + this.buffer.set.call(this); + + var parent = $(element).closest('h1, h2, h3, h4, h5, h6, p, li, div, blockquote'); + + if (parent[0].tagName == 'LI') + { + this.exec.command.call(this, cmd, false); + } + else + { + if (elements.length === 0) { - return true; + elements.push(element); } - this.$toolbar.append($('<li>').append(this.buildButton(key, s))); + // indent block tags + $.each(elements, $.proxy(function(i,s) + { + this.indenting.process.call(this, s, cmd); + + }, this)); } + }, + process: function(element, cmd) + { + if (!this.utils.isParentRedactor.call(this, element)) + { + return false; + } - if (key === '|') + var parent = $(element).closest('h1, h2, h3, h4, h5, h6, p, li, div, blockquote'); + + // blockquote + if (parent[0].tagName == 'P' && parent.parent()[0].tagName == 'BLOCKQUOTE') { - this.$toolbar.append($('<li class="redactor_separator"></li>')); + parent = parent.parent(); } - }, this)); + // others + if (parent.size() != 0 && !parent.hasClass('redactor_editor')) + { + // increase + if (cmd === 'indent') + { + parent.css('margin-left', (this.utils.normalize.call(this, parent.css('margin-left')) + 20) + 'px'); + } + // decrease + else + { + var marginLeft = this.utils.normalize.call(this, parent.css('margin-left')) - 20; + if (marginLeft <= 0) + { + parent.css('margin-left', ''); + if (parent.attr('style') == '') + { + parent.removeAttr('style'); + } + } + else + { + parent.css('margin-left', marginLeft + 'px'); + } + } + } + + this.sync(); + } }, - buildButton: function(key, s) - { - var button = $('<a href="javascript:void(null);" title="' + s.title + '" class="redactor_btn_' + key + '"></a>'); - if (typeof s.func === 'undefined') + // EXECCOMMAND + exec: + { + command: function(cmd, param) { - button.click($.proxy(function() + if (this.opts.visual == false) { - if ($.inArray(key, this.opts.activeButtons) != -1) + this.$el.focus(); + return false; + } + + try + { + var parentel = this.selection.getParent.call(this); + var currentel = this.selection.getElement.call(this); + var pre = false; + + if ((parentel && $(parentel).get(0).tagName === 'PRE') || (currentel && $(currentel).get(0).tagName === 'PRE')) { - this.inactiveAllButtons(); - this.setBtnActive(key); + pre = true; } - if (this.browser('mozilla')) + var parent; + + // lists + if (!this.utils.browser.call(this, 'mozilla')) { - this.$editor.focus(); - //this.restoreSelection(); + if (cmd === 'insertunorderedlist' || cmd === 'insertorderedlist') + { + this.buffer.set.call(this); + setTimeout($.proxy(function() + { + this.selection.saveDynamic.call(this); + var html = this.$editor.html(); + + html = html.replace(/<p(.*?)><ul>([\w\W]*?)<\/ul><\/p>/gi, '<ul$1>$2</ul>'); + html = html.replace(/<p(.*?)><ol>([\w\W]*?)<\/ol><\/p>/gi, '<ol$1>$2</ol>'); + + this.$editor.html(html); + this.selection.restoreDynamic.call(this); + }, this), 1); + } + + if (cmd === 'outdent' || cmd === 'indent') + { + this.buffer.set.call(this); + setTimeout($.proxy(function() + { + this.selection.saveDynamic.call(this); + var html = this.$editor.html(); + + html = html.replace(/<\/li><ul(.*?)>([\w\W]*?)<\/ul>/gi, '<ul$1>$2</ul></li>'); + html = html.replace(/<\/li><ol(.*?)>([\w\W]*?)<\/ol>/gi, '<ol$1>$2</ol></li>'); + + this.$editor.html(html); + this.selection.restoreDynamic.call(this); + }, this), 1); + } } - this.execCommand(s.exec, key); + if (cmd === 'insertunorderedlist') + { + this.button.inactive.call(this, 'orderedlist'); + } + else if (cmd === 'insertorderedlist') + { + this.button.inactive.call(this, 'unorderedlist'); + } - }, this)); - } - else if (s.func !== 'show') - { - button.click($.proxy(function(e) { + if (cmd === 'inserthtml') + { + this.insert.html.call(this, param, false); + } + else if (cmd === 'unlink') + { + parent = this.selection.getElement.call(this); + if ($(parent).get(0).tagName === 'A') $(parent).replaceWith($(parent).text()); + else this.exec.run.call(this, cmd, param); + } + else + { + if (cmd === 'inserthorizontalrule' && this.utils.browser.call(this, 'msie')) this.$editor.focus(); + if (cmd === 'formatblock' && this.utils.browser.call(this, 'mozilla')) this.$editor.focus(); - this[s.func](e); + if (pre && this.opts.formattingPre) return false; - }, this)); - } + this.exec.run.call(this, cmd, param); + } - if (typeof s.callback !== 'undefined' && s.callback !== false) + if (cmd === 'inserthorizontalrule') + { + this.$editor.find('hr').removeAttr('id'); + + if (this.opts.linebreaks === false) + { + this.$editor.focus(); + var p = $('<p>' + this.opts.invisibleSpace + '</p>'); + this.insert.nodeAtCaret.call(this, p[0]); + + this.$editor.focus(); + this.selection.start.call(this, p, 0); + } + } + + if (pre && this.opts.formattingPre && (cmd === 'italic' || cmd === 'bold' || cmd === 'strikethrough'|| cmd === 'underline')) + { + return false; + } + + setTimeout($.proxy(this.sync, this), 10); + + if (typeof this.opts.execCommandCallback === 'function') + { + this.opts.execCommandCallback(this, cmd, param); + } + + if (this.opts.air) + { + this.$air.hide(); + } + } + catch (e) { } + }, + run: function(cmd, param) { - button.click($.proxy(function(e) { s.callback(this, e, key); }, this)); + if (cmd === 'formatblock' && this.utils.browser.call(this, 'msie')) + { + param = '<' + param + '>'; + } + + this.document.execCommand(cmd, false, param); } + }, - // dropdown - if (key === 'backcolor' || key === 'fontcolor' || typeof(s.dropdown) !== 'undefined') + // QUOTE + quote: + { + toggle: function() { - var dropdown = $('<div class="redactor_dropdown" style="display: none;">'); + this.buffer.set.call(this); - if (key === 'backcolor' || key === 'fontcolor') + var html = false; + var nodes = this.selection.getBlocks.call(this); + + if (nodes.length === 0) { - dropdown = this.buildColorPicker(dropdown, key); + nodes = [this.selection.getBlock.call(this)]; } - else + + if (!nodes) { - dropdown = this.buildDropdown(dropdown, s.dropdown); + this.exec.command.call(this, 'formatblock', 'blockquote'); } - this.dropdowns.push(dropdown.appendTo($(document.body))); - - // observing dropdown - this.hdlShowDropDown = $.proxy(function(e) { this.showDropDown(e, dropdown, key); }, this); - - button.click(this.hdlShowDropDown); - } - - return button; - }, - buildDropdown: function(dropdown, obj) - { - $.each(obj, $.proxy( - function (x, d) + $.each(nodes, $.proxy(function(i, node) { - if (typeof(d.className) === 'undefined') + if (node === false || typeof node.tagName === 'undefined') { - d.className = ''; + this.exec.command.call(this, 'formatblock', 'blockquote'); } + else if (node.tagName === 'BLOCKQUOTE') + { + if (this.opts.linebreaks === true) + { + this.selection.save.call(this); + var node1 = $('<span id="insert-marker-1">')[0]; + var node2 = $('<span id="insert-marker-2">')[0]; + this.insert.beforeCaret.call(this, node1); + this.insert.afterCaret.call(this, node2); + this.selection.restore.call(this); + } - var drop_a; - if (typeof d.name !== 'undefined' && d.name === 'separator') + html = this.quote.html.call(this, node); + } + else if (node.tagName === 'PRE') { - drop_a = $('<a class="redactor_separator_drop">'); + html = '<blockquote>' + this.quote.html.call(this, node) + '</blockquote>'; } else { - drop_a = $('<a href="javascript:void(null);" class="' + d.className + '">' + d.title + '</a>'); - - if (typeof(d.callback) === 'function') + var parent = $(node).parent(); + if (parent[0].tagName === 'BLOCKQUOTE') { - $(drop_a).click($.proxy(function(e) { d.callback(this, e, x); }, this)); + html = $(parent).html(); + node = parent; } - else if (typeof(d.func) === 'undefined') + else { - $(drop_a).click($.proxy(function() { this.execCommand(d.exec, x); }, this)); + html = $('<blockquote>' + this.utils.outerHtml.call(this, node) + '</blockquote>'); } + } + + if (html !== false) + { + if (this.opts.linebreaks === false) + { + var el = $(html); + $(node).replaceWith(el); + this.selection.start.call(this, el); + } else { - $(drop_a).click($.proxy(function(e) { this[d.func](e); }, this)); + $(node).replaceWith(html); + + var node1 = $(this.$editor.find('span#insert-marker-1')); + var node2 = $(this.$editor.find('span#insert-marker-2')); + + if (node1.size() !== 0 && node2.size() !== 0) + { + this.selection.set.call(this, node1[0], 0, node2[0], 0); + node1.remove(); + node2.remove(); + } + else + { + this.focus.set.call(this); + } + } } - $(dropdown).append(drop_a); - }, this) - ); + }, this)); - return dropdown; + this.sync(); + }, + html: function(node) + { + var html = $(node).html(); + if (this.opts.linebreaks === false) + { + html = '<p>' + html + '</p>'; + } + return html; + } }, - buildColorPicker: function(dropdown, key) + + // FORMAT + format: { - var mode; - if (key === 'backcolor') + empty: function(e) { - if (this.browser('msie')) + var html = $.trim(this.$editor.html()); + + html = html.replace(/<br\s?\/?>/i, ''); + var thtml = html.replace(/<p>\s?<\/p>/gi, ''); + + if (html === '' || thtml === '') { - mode = 'BackColor'; + e.preventDefault(); + + var node = $(this.opts.emptyHtml).get(0); + this.$editor.html(node); + this.selection.set.call(this, node, 0, node, 0); + + this.sync(); + return false; } else { - mode = 'hilitecolor'; + this.sync(); } - } - else + }, + newLine: function() { - mode = 'forecolor'; - } + var parent = this.selection.getElement.call(this); - $(dropdown).width(210); - - var len = this.opts.colors.length; - for (var i = 0; i < len; ++i) - { - var color = this.opts.colors[i]; - - var swatch = $('<a rel="' + color + '" href="javascript:void(null);" class="redactor_color_link"></a>').css({ 'backgroundColor': color }); - $(dropdown).append(swatch); - - var _self = this; - $(swatch).click(function() + if (parent === false || (this.opts.iframe && parent.tagName === 'BODY') || (this.opts.iframe === false && parent.tagName === 'DIV' && $(parent).hasClass('redactor_editor'))) { - _self.execCommand(mode, $(this).attr('rel')); + var element = $(this.selection.getNode.call(this)); - if (mode === 'forecolor') + // Replace div to p + if (this.opts.linebreaks === false) { - _self.$editor.find('font').replaceWith(function() { - - return $('<span style="color: ' + $(this).attr('color') + ';">' + $(this).html() + '</span>'); - - }); + if ((element.get(0).tagName === 'DIV' || element.get(0).tagName === 'H6') && (element.html() === '' || element.html() === '<br>')) + { + // replace with paragraph + var newElement = $('<p>').append(element.clone().get(0).childNodes); + element.replaceWith(newElement); + this.selection.start.call(this, newElement); + } } - - if (_self.browser('msie') && mode === 'BackColor') + else { - _self.$editor.find('font').replaceWith(function() { + // Replace div, p to br + if (element.size() != 0 && (element[0].tagName === 'DIV' || element[0].tagName === 'P') && (element.html() === '' || element.html() === '<br>')) + { + this.format.replaceLineBreak.call(this, element); + } + } + } - return $('<span style="' + $(this).attr('style') + '">' + $(this).html() + '</span>'); + if (parent.tagName === 'P' && this.opts.linebreaks === true) + { + this.format.replaceLineBreak.call(this, parent); + } - }); + this.sync(); + + }, + insertAfterLastElement: function(element) + { + if (this.utils.isEndOfElement.call(this) && this.opts.linebreaks === false) + { + if (this.$editor.contents().last()[0] !== element) + { + return false; } - }); - } + this.buffer.set.call(this); - var elnone = $('<a href="javascript:void(null);" class="redactor_color_none"></a>').html(RLANG.none); - - if (key === 'backcolor') + var node = $(this.opts.emptyHtml); + $(element).after(node); + this.selection.start.call(this, node); + } + }, + insertLineBreak: function() { - elnone.click($.proxy(this.setBackgroundNone, this)); - } - else + var br = $('<br>' + this.opts.invisibleSpace); + this.insert.nodeAtCaret.call(this, br[0]); + }, + replaceLineBreak: function(element) { - elnone.click($.proxy(this.setColorNone, this)); - } + var node = this.document.createTextNode('\uFEFF'); + $(element).replaceWith(node); + this.selection.start.call(this, $(node)); + }, + blocks: function(tag) + { + this.buffer.set.call(this); - $(dropdown).append(elnone); + var nodes = this.selection.getBlocks.call(this); + var last; - return dropdown; - }, - setBackgroundNone: function() - { - $(this.getParentNode()).css('background-color', 'transparent'); - this.syncCode(); - }, - setColorNone: function() - { - $(this.getParentNode()).attr('color', '').css('color', ''); - this.syncCode(); - }, + if (nodes.length === 0) + { + this.format.block.call(this, tag, false, true); + } + else + { + $.each(nodes, $.proxy(function(i, node) + { + last = this.format.block.call(this, tag, node, false); - // DROPDOWNS - showDropDown: function(e, dropdown, key) - { - if (this.getBtn(key).hasClass('dropact')) - { - this.hideAllDropDown(); - } - else - { - this.hideAllDropDown(); + }, this)); - this.setBtnActive(key); - this.getBtn(key).addClass('dropact'); + this.selection.end.call(this, last); + } - var left = this.getBtn(key).offset().left; + this.sync(); + }, + block: function(tag, block, offset) + { + if (block === false) + { + block = this.selection.getBlock.call(this); + } - if (this.opts.air) + if (block === false) { - var air_top = this.air.offset().top; + if (this.opts.linebreaks === true) + { + this.exec.command.call(this, 'formatblock', tag); + } - $(dropdown).css({ position: 'absolute', left: left + 'px', top: air_top+30 + 'px' }).show(); + return true; } - else if (this.opts.fixed && this.fixed) + + if (offset !== false) { - $(dropdown).css({ position: 'fixed', left: left + 'px', top: '30px' }).show(); + var offset = this.selection.caretOffset.call(this, block); } + + var contents = ''; + if (tag !== 'pre') + { + contents = $(block).contents(); + } else { - var top = this.$toolbar.offset().top + 30; - $(dropdown).css({ position: 'absolute', left: left + 'px', top: top + 'px' }).show(); + contents = this.clean.encodeEntities.call(this, $(block).text()); } - } - var hdlHideDropDown = $.proxy(function(e) { this.hideDropDown(e, dropdown, key); }, this); + if (this.opts.linebreaks === true && tag === 'p') + { + $(block).replaceWith(contents); - $(document).one('click', hdlHideDropDown); - this.$editor.one('click', hdlHideDropDown); - this.$content.one('click', hdlHideDropDown); + return $(contents); + } + else + { + var node = $('<' + tag + '>').append(contents); + $(block).replaceWith(node); - e.stopPropagation(); + if (offset !== false) + { + this.selection.caret.call(this, node[0], offset); + } + } - }, - hideAllDropDown: function() - { - this.$toolbar.find('a.dropact').removeClass('redactor_act').removeClass('dropact'); - $('.redactor_dropdown').hide(); - }, - hideDropDown: function(e, dropdown, key) - { - if (!$(e.target).hasClass('dropact')) + return node; + }, + changeTag: function(from, to) { - $(dropdown).removeClass('dropact'); - this.showedDropDown = false; - this.hideAllDropDown(); + this.selection.save.call(this); + + var elements = this.$editor.find(from); + $.each(elements, $.proxy(function(i,s) + { + $(s).replaceWith(function() + { + return $('<' + to + '/>').append($(this).contents()); + }); + }, this)); + + setTimeout($.proxy(this.selection.restore, this), 10); + } }, - // BUTTONS MANIPULATIONS - getBtn: function(key) + // BLOCK CLASS AND STYLE + block: { - if (this.opts.toolbar === false) + removeAttr: function(attr) { - return false; - } + var nodes = this.selection.getBlocks.call(this); - return $(this.$toolbar.find('a.redactor_btn_' + key)); - }, - setBtnActive: function(key) - { - this.getBtn(key).addClass('redactor_act'); - }, - setBtnInactive: function(key) - { - this.getBtn(key).removeClass('redactor_act'); - }, - inactiveAllButtons: function() - { - $.each(this.opts.activeButtons, $.proxy(function(i,s) + if (nodes.length === 0) + { + $(this.selection.getBlock.call(this)).removeAttr(attr); + } + else + { + $(nodes).removeAttr(attr); + } + }, + setAttr: function(attr, value) { - this.setBtnInactive(s); + var nodes = this.selection.getBlocks.call(this); - }, this)); - }, - changeBtnIcon: function(key, classname) - { - this.getBtn(key).addClass('redactor_btn_' + classname); - }, - removeBtnIcon: function(key, classname) - { - this.getBtn(key).removeClass('redactor_btn_' + classname); - }, + if (nodes.length === 0) + { + $(this.selection.getBlock.call(this)).attr(attr, value); + } + else + { + $.each(nodes, function(i,s) + { + $(s).attr(attr, value); + }); + } - addBtnSeparator: function() - { - this.$toolbar.append($('<li class="redactor_separator"></li>')); - }, - addBtnSeparatorAfter: function(key) - { - var $btn = this.getBtn(key); - $btn.parent().after($('<li class="redactor_separator"></li>')); - }, - addBtnSeparatorBefore: function(key) - { - var $btn = this.getBtn(key); - $btn.parent().before($('<li class="redactor_separator"></li>')); - }, - removeBtnSeparatorAfter: function(key) - { - var $btn = this.getBtn(key); - $btn.parent().next().remove(); - }, - removeBtnSeparatorBefore: function(key) - { - var $btn = this.getBtn(key); - $btn.parent().prev().remove(); - }, - - setBtnRight: function(key) - { - if (this.opts.toolbar === false) + }, + removeStyle: function(rule) { - return false; - } + var nodes = this.selection.getBlocks.call(this); - this.getBtn(key).parent().addClass('redactor_btn_right'); - }, - setBtnLeft: function(key) - { - if (this.opts.toolbar === false) + if (nodes.length === 0) + { + var $block = $(this.selection.getBlock.call(this)); + $block.css(rule, ''); + this.utils.removeEmptyStyleAttr.call(this, $block); + } + else + { + $(nodes).css(rule, ''); + $.each(nodes, $.proxy(function(i,s) + { + this.utils.removeEmptyStyleAttr.call(this, s); + }, this)); + } + }, + setStyle: function(rule, value) { - return false; - } + var nodes = this.selection.getBlocks.call(this); - this.getBtn(key).parent().removeClass('redactor_btn_right'); - }, - addBtn: function(key, title, callback, dropdown) - { - if (this.opts.toolbar === false) - { - return false; - } + if (nodes.length === 0) + { + $(this.selection.getBlock.call(this)).css(rule, value); + } + else + { + $.each(nodes, function(i,s) + { + $(s).css(rule, value); + }); + } - var btn = this.buildButton(key, { title: title, callback: callback, dropdown: dropdown }); - this.$toolbar.append($('<li>').append(btn)); - }, - addBtnFirst: function(key, title, callback, dropdown) - { - if (this.opts.toolbar === false) + }, + removeClass: function(className) { - return false; - } + var nodes = this.selection.getBlocks.call(this); - var btn = this.buildButton(key, { title: title, callback: callback, dropdown: dropdown }); - this.$toolbar.prepend($('<li>').append(btn)); - }, - addBtnAfter: function(afterkey, key, title, callback, dropdown) - { - if (this.opts.toolbar === false) + if (nodes.length === 0) + { + $(this.selection.getBlock.call(this)).removeClass(className); + } + else + { + $(nodes).removeClass(className); + } + }, + setClass: function(className) { - return false; - } + var nodes = this.selection.getBlocks.call(this); - var btn = this.buildButton(key, { title: title, callback: callback, dropdown: dropdown }); - var $btn = this.getBtn(afterkey); - $btn.parent().after($('<li>').append(btn)); + if (nodes.length === 0) + { + $(this.selection.getBlock.call(this)).addClass(className); + } + else + { + $.each(nodes, function(i,s) + { + $(block).addClass(className); + }); + } + + } }, - addBtnBefore: function(beforekey, key, title, callback, dropdown) + + // INLINE CLASS AND STYLE + inline: { - if (this.opts.toolbar === false) + removeClass: function(className) { - return false; - } + var nodes = this.selection.getNodes.call(this); + nodes.push(this.selection.getElement.call(this)); - var btn = this.buildButton(key, { title: title, callback: callback, dropdown: dropdown }); - var $btn = this.getBtn(beforekey); - $btn.parent().before($('<li>').append(btn)); - }, - removeBtn: function(key, separator) - { - var $btn = this.getBtn(key); + $.each(nodes, function(i,s) + { + if ($(s).hasClass(className)) + { + $(s).removeClass(className); + } + }); - if (separator === true) + }, + setClass: function(className) { - $btn.parent().next().remove(); - } + var current = this.selection.getElement.call(this); + if ($(current).hasClass(className)) + { + return true; + } - $btn.parent().removeClass('redactor_btn_right'); - $btn.remove(); - }, + this.inline.methods.call(this, 'addClass', className); + }, + removeStyle: function(rule) + { + var nodes = this.selection.getNodes.call(this); + nodes.push(this.selection.getElement.call(this)); + $.each(nodes, $.proxy(function(i,s) + { + if (s.tagName === 'SPAN') + { + $(s).css(rule, '') + this.utils.removeEmptyStyleAttr.call(this, s); + } + }, this)); - // SELECTION AND NODE MANIPULATION - getFragmentHtml: function (fragment) - { - var cloned = fragment.cloneNode(true); - var div = this.document.createElement('div'); - div.appendChild(cloned); - return div.innerHTML; - }, - extractContent: function() - { - var node = this.$editor.get(0); - var frag = this.document.createDocumentFragment(), child; - while ((child = node.firstChild)) + }, + setStyle: function(rule, value) { - frag.appendChild(child); - } + this.inline.methods.call(this, 'css', rule, value); + }, + removeAttr: function(attr) + { + var nodes = this.selection.getNodes.call(this); + nodes.push(this.selection.getElement.call(this)); - return frag; - }, + $.each(nodes, function(i,s) + { + if ($(s).attr(attr)) + { + $(s).removeAttr(attr); + } + }); - // Save and Restore Selection - saveSelection: function() - { - this.$editor.focus(); + }, + setAttr: function(attr, value) + { + this.inline.methods.call(this, 'attr', attr, value); + }, + methods: function(type, attr, value) + { + this.document.execCommand("fontSize", false, 4); - this.savedSel = this.getOrigin(); - this.savedSelObj = this.getFocus(); - }, - restoreSelection: function() - { - if (typeof this.savedSel !== 'undefined' && this.savedSel !== null && this.savedSelObj !== null && this.savedSel[0].tagName !== 'BODY') + var fonts = this.$editor.find('font'); + var last; + $.each(fonts, $.proxy(function(i,s) + { + last = this.inline.setMethods.call(this, type, s, attr, value); + }, this)); + + if (last) this.selection.end.call(this, last); + }, + setMethods: function(type, s, attr, value) { - if (this.opts.iframe === false && $(this.savedSel[0]).closest('.redactor_editor').size() == 0) + var parent = $(s).parent(); + if (parent && parent[0].tagName === 'SPAN') { - this.$editor.focus(); + var el = parent; + $(s).replaceWith($(s).html()); } else { - if (this.browser('opera')) - { - this.$editor.focus(); - } + var el = $('<span/>').append($(s).contents()); + $(s).replaceWith(el); + } - this.setSelection(this.savedSel[0], this.savedSel[1], this.savedSelObj[0], this.savedSelObj[1]); + if (type === 'addClass') $(el).addClass(attr); + else if (type === 'css') $(el).css(attr, value); + else if (type === 'attr') $(el).attr(attr, value); - if (this.browser('mozilla')) + return el; + }, + format: function(tag) + { + this.document.execCommand("fontSize", false, 4); + + var fonts = this.$editor.find('font'); + var last; + $.each(fonts, function(i,s) + { + var el = $('<' + tag + '/>').append($(s).contents()); + $(s).replaceWith(el); + last = el; + }); + + if (last) this.selection.end.call(this, last); + }, + removeFormat: function(tag) + { + var utag = tag.toUpperCase(); + var nodes = this.selection.getNodes.call(this); + nodes.push(this.selection.getElement.call(this)); + + $.each(nodes, function(i,s) + { + if (s.tagName === utag) { - this.$editor.focus(); + $(s).replaceWith($(s).contents()); } - } + }); } - else - { - this.$editor.focus(); - } }, - // Selection - getSelection: function() + // SELECTION + selection: { - var doc = this.document; + save: function() + { + this.$editor.focus(); - if (this.window.getSelection) + this.savedSel = this.selection.origin.call(this); + this.savedSelObj = this.selection.focus.call(this); + }, + restore: function() { - return this.window.getSelection(); - } - else if (doc.getSelection) + if (typeof this.savedSel !== 'undefined' && this.savedSel !== null && this.savedSelObj !== null && this.savedSel[0].tagName !== 'BODY') + { + + if (this.opts.iframe === false && $(this.savedSel[0]).closest('.redactor_editor').size() == 0) + { + this.focus.set.call(this); + } + else + { + if (this.utils.browser.call(this, 'opera')) this.$editor.focus(); + + this.selection.set.call(this, this.savedSel[0], this.savedSel[1], this.savedSelObj[0], this.savedSelObj[1]); + + if (this.utils.browser.call(this, 'mozilla')) this.$editor.focus(); + } + } + else + { + this.focus.set.call(this); + } + }, + saveDynamic: function() { - return doc.getSelection(); - } - else // IE + var id = 'buffer-marker-'; + + $(this.$editor.find('span#' + id + '1')).remove(); + $(this.$editor.find('span#' + id + '2')).remove(); + + var node1 = $('<span id="' + id + '1">')[0]; + var node2 = $('<span id="' + id + '2">')[0]; + this.insert.beforeCaret.call(this, node1); + this.insert.afterCaret.call(this, node2); + + this.$editor.focus(); + this.selection.set.call(this, node1, 0, node2, 0); + }, + restoreDynamic: function() { - return doc.selection.createRange(); - } + var id = 'buffer-marker-'; + this.$editor.focus(); - return false; - }, - hasSelection: function() - { - if (!this.oldIE()) + var node1 = $(this.$editor.find('span#' + id + '1')); + var node2 = $(this.$editor.find('span#' + id + '2')); + + if (node1.size() !== 0 && node2.size() !== 0) + { + this.selection.set.call(this, node1[0], 0, node2[0], 0); + node1.remove(); + node2.remove(); + } + else + { + this.focus.set.call(this); + } + }, + all: function() { - var sel; - return (sel = this.getSelection()) && (sel.focusNode != null) && (sel.anchorNode != null); - } - else // IE8 + var sel, range; + if (this.window.getSelection && this.document.createRange) + { + range = this.document.createRange(); + range.selectNodeContents(this.$editor[0]); + sel = this.window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } + else if (this.document.body.createTextRange) + { + range = this.document.body.createTextRange(); + range.moveToElementText(this.$editor[0]); + range.select(); + } + }, + get: function() { - var node = this.$editor.get(0); + var doc = this.document; - var range; - node.focus(); - if (!node.document.selection) + if (this.window.getSelection) { - return false; + return this.window.getSelection(); } + else if (doc.getSelection) + { + return doc.getSelection(); + } + else // IE + { + return doc.selection.createRange(); + } - range = node.document.selection.createRange(); - return range && range.parentElement().document === node.document; - } - }, - getOrigin: function() - { - if (!this.oldIE()) + return false; + }, + origin: function() { var sel; - if (!((sel = this.getSelection()) && (sel.anchorNode != null))) + if (!((sel = this.selection.get.call(this)) && (sel.anchorNode != null))) { return null; } return [sel.anchorNode, sel.anchorOffset]; - } - else + }, + focus: function() { - var node = this.$editor.get(0); - - var range; - node.focus(); - if (!this.hasSelection()) + var sel; + if (!((sel = this.selection.get.call(this)) && (sel.focusNode != null))) { return null; } - range = node.document.selection.createRange(); - return this._getBoundary(node.document, range, true); - } - }, - getFocus: function() - { - if (!this.oldIE()) + return [sel.focusNode, sel.focusOffset]; + }, + getTextNodesIn: function(node) { - var sel; - if (!((sel = this.getSelection()) && (sel.focusNode != null))) + var textNodes = []; + + if (node.nodeType == 3) { - return null; + textNodes.push(node); } + else + { + var children = node.childNodes; + for (var i = 0, len = children.length; i < len; ++i) + { + textNodes.push.apply(textNodes, this.selection.getTextNodesIn.call(this, children[i])); + } + } - return [sel.focusNode, sel.focusOffset]; - } - else + return textNodes; + }, + caretOffset: function(element) { - var node = this.$editor.get(0); + var caretOffset = 0; + if (typeof this.window.getSelection != "undefined") + { + var range = this.window.getSelection().getRangeAt(0); + var preCaretRange = range.cloneRange(); + preCaretRange.selectNodeContents(element); + preCaretRange.setEnd(range.endContainer, range.endOffset); + caretOffset = $.trim(preCaretRange.toString()).length; + } + else if (typeof this.document.selection != "undefined" && this.document.selection.type != "Control") + { + var textRange = this.document.selection.createRange(); + var preCaretTextRange = this.document.body.createTextRange(); + preCaretTextRange.moveToElementText(element); + preCaretTextRange.setEndPoint("EndToEnd", textRange); + caretOffset = $.trim(preCaretTextRange.text).length; + } - var range; - node.focus(); - if (!this.hasSelection()) + return caretOffset; + }, + caret: function (el, start, end) + { + if (typeof end === 'undefined') { - return null; + end = start; } - range = node.document.selection.createRange(); - return this._getBoundary(node.document, range, false); + if (this.document.createRange && this.window.getSelection) + { + var range = this.document.createRange(); + range.selectNodeContents(el); + var textNodes = this.selection.getTextNodesIn.call(this, el); + var foundStart = false; + var charCount = 0, endCharCount; - } - }, - setSelection: function (orgn, orgo, focn, foco) - { - if (focn == null) - { - focn = orgn; - } + for (var i = 0, textNode; textNode = textNodes[i++];) + { + endCharCount = charCount + textNode.length; + if (!foundStart && start >= charCount && (start < endCharCount || (start == endCharCount && i < textNodes.length))) + { + range.setStart(textNode, start - charCount); + foundStart = true; + } - if (foco == null) - { - foco = orgo; - } + if (foundStart && end <= endCharCount) + { + range.setEnd(textNode, end - charCount); + break; + } - if (!this.oldIE()) + charCount = endCharCount; + } + + var sel = this.window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } + else if (this.document.selection && this.document.body.createTextRange) + { + var textRange = this.document.body.createTextRange(); + textRange.moveToElementText(el); + textRange.collapse(true); + textRange.moveEnd("character", end); + textRange.moveStart("character", start); + textRange.select(); + } + }, + element: function(node) { - var sel = this.getSelection(); + this.selection.set.call(this, node[0], 1, node[0], 0); + }, + start: function(node) + { + this.selection.set.call(this, node[0], 0, node[0], 0); + }, + end: function(node) + { + this.selection.set.call(this, node[0], 1, node[0], 1); + }, + set: function (orgn, orgo, focn, foco) + { + if (focn == null) + { + focn = orgn; + } + + if (foco == null) + { + foco = orgo; + } + + var sel = this.selection.get.call(this); if (!sel) { return; } @@ -2617,1456 +3939,1442 @@ } catch (e) {} sel.addRange(r); } - } - else + }, + + getNode: function() { - var node = this.$editor.get(0); - var range = node.document.body.createTextRange(); + var el = false; + if (typeof this.window.getSelection !== 'undefined') + { + var s = this.window.getSelection(); + if (s.rangeCount > 0) + { + el = this.selection.get.call(this).getRangeAt(0).startContainer; + } + } + else if (typeof this.document.selection !== 'undefined') + { + el = this.selection.get.call(this); + } - this._moveBoundary(node.document, range, false, focn, foco); - this._moveBoundary(node.document, range, true, orgn, orgo); - return range.select(); - } - }, + return this.utils.isParentRedactor.call(this, el); + }, + getElement: function() + { + var el = false; + if (typeof this.window.getSelection !== 'undefined') + { + var s = this.window.getSelection(); + if (s.rangeCount > 0) + { + el = s.getRangeAt(0).startContainer.parentNode; + } + } + else if (typeof this.document.selection !== 'undefined') + { + el = this.selection.get.call(this).parentElement(); + } - // Get elements, html and text - getCurrentNode: function() - { - if (typeof this.window.getSelection !== 'undefined') + return this.utils.isParentRedactor.call(this, el); + + }, + getParent: function() { - return this.getSelectedNode().parentNode; - } - else if (typeof this.document.selection !== 'undefined') - { - return this.getSelection().parentElement(); - } - }, - getParentNode: function() - { - return $(this.getCurrentNode()).parent()[0] - }, - getSelectedNode: function() - { - if (this.oldIE()) - { - return this.getSelection().parentElement(); - } - else if (typeof this.window.getSelection !== 'undefined') - { - var s = this.window.getSelection(); - if (s.rangeCount > 0) + var el = this.selection.getElement.call(this); + if (el) { - return this.getSelection().getRangeAt(0).commonAncestorContainer; + return this.utils.isParentRedactor.call(this, $(el).parent()[0]); } else { return false; } - } - else if (typeof this.document.selection !== 'undefined') + }, + text: function() { - return this.getSelection(); - } - }, + var text = ''; + if (this.window.getSelection) + { + text = this.window.getSelection().toString(); + } + else if (this.document.selection && this.document.selection.type != "Control") + { + text = this.document.selection.createRange().text; + } - // IE8 specific selection - _getBoundary: function(doc, textRange, bStart) - { - var cursor, cursorNode, node, offset, parent; - - cursorNode = doc.createElement('a'); - cursor = textRange.duplicate(); - cursor.collapse(bStart); - parent = cursor.parentElement(); - while (true) + return text; + }, + html: function() { - parent.insertBefore(cursorNode, cursorNode.previousSibling); - cursor.moveToElementText(cursorNode); - if (!(cursor.compareEndPoints((bStart ? 'StartToStart' : 'StartToEnd'), textRange) > 0 && (cursorNode.previousSibling != null))) + var html = ''; + + if (this.window.getSelection) { - break; + var sel = this.window.getSelection(); + if (sel.rangeCount) + { + var container = this.document.createElement("div"); + for (var i = 0, len = sel.rangeCount; i < len; ++i) + { + container.appendChild(sel.getRangeAt(i).cloneContents()); + } + + html = container.innerHTML; + } } - } + else if (this.document.selection) + { + if (this.document.selection.type === "Text") + { + html = this.document.selection.createRange().htmlText; + } + } - if (cursor.compareEndPoints((bStart ? 'StartToStart' : 'StartToEnd'), textRange) === -1 && cursorNode.nextSibling) + return html; + }, + nodeTestBlocks: function(node) { - cursor.setEndPoint((bStart ? 'EndToStart' : 'EndToEnd'), textRange); - node = cursorNode.nextSibling; - offset = cursor.text.length; - } - else + return node.nodeType == 1 && /^(P|H[1-6]|DIV|LI|BLOCKQUOTE|PRE|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)$/i.test(node.nodeName); + }, + getBlock: function(node) { - node = cursorNode.parentNode; - offset = this._getChildIndex(cursorNode); - } + if (typeof node === 'undefined') + { + node = this.selection.getNode.call(this); + } - cursorNode.parentNode.removeChild(cursorNode); - return [node, offset]; - }, - _moveBoundary: function(doc, textRange, bStart, node, offset) - { - var anchorNode, anchorParent, cursor, cursorNode, textOffset; + while (node) + { + if (this.selection.nodeTestBlocks.call(this, node)) + { - textOffset = 0; - anchorNode = this._isText(node) ? node : node.childNodes[offset]; - anchorParent = this._isText(node) ? node.parentNode : node; + if ($(node).hasClass('redactor_editor')) + { + return false; + } - if (this._isText(node)) - { - textOffset = offset; - } + return node; + } - cursorNode = doc.createElement('a'); - anchorParent.insertBefore(cursorNode, anchorNode || null); - cursor = doc.body.createTextRange(); - cursor.moveToElementText(cursorNode); - cursorNode.parentNode.removeChild(cursorNode); - textRange.setEndPoint((bStart ? 'StartToStart' : 'EndToEnd'), cursor); - return textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset); - }, - _isText: function (d) - { - return (d != null ? d.nodeType == 3 : false); - }, - _getChildIndex: function (e) - { - var k = 0; - while (e = e.previousSibling) { - k++; - } - return k; - }, + node = node.parentNode; + } - insertNodeAfterCaret: function(node) - { - this.saveSelection(); - this.insertNodeAtCaret(node); - this.restoreSelection(); - }, + return false; + }, - insertNodeAtCaret: function(node) - { - if (this.window.getSelection) + nextNode: function(node) { - var sel = this.getSelection(); - if (sel.rangeCount) + if (node.hasChildNodes()) { - var range = sel.getRangeAt(0); - range.collapse(false); - range.insertNode(node); - range = range.cloneRange(); - range.selectNodeContents(node); - range.collapse(false); - sel.removeAllRanges(); - sel.addRange(range); + return node.firstChild; } - } - else if (this.document.selection) - { - var html = (node.nodeType === 1) ? node.outerHTML : node.data; - var id = "marker_" + ("" + Math.random()).slice(2); - html += '<span id="' + id + '"></span>'; - var textRange = this.getSelection(); - textRange.collapse(false); - textRange.pasteHTML(html); - var markerSpan = this.document.getElementById(id); - textRange.moveToElementText(markerSpan); - textRange.select(); - markerSpan.parentNode.removeChild(markerSpan); - } - }, - getSelectedHtml: function() - { - var html = ''; - if (this.window.getSelection) - { - var sel = this.window.getSelection(); - if (sel.rangeCount) + else { - var container = this.document.createElement("div"); - for (var i = 0, len = sel.rangeCount; i < len; ++i) + while (node && !node.nextSibling) { - container.appendChild(sel.getRangeAt(i).cloneContents()); + node = node.parentNode; } - html = container.innerHTML; + if (!node) + { + return null; + } + return node.nextSibling; } - } - else if (this.document.selection) + }, + getRangeSelectedNodes: function(range) { - if (this.document.selection.type === "Text") + var node = range.startContainer; + var endNode = range.endContainer; + + if (node == endNode) { - html = this.document.selection.createRange().htmlText; + return [node]; } - } - return html; - }, + var rangeNodes = []; + while (node && node != endNode) + { + rangeNodes.push( node = this.selection.nextNode.call(this, node) ); + } - // RESIZE IMAGES - resizeImage: function(resize) - { - var clicked = false; - var clicker = false; - var start_x; - var start_y; - var ratio = $(resize).width()/$(resize).height(); - var min_w = 10; - var min_h = 10; + node = range.startContainer; + while (node && node != range.commonAncestorContainer) + { + rangeNodes.unshift(node); + node = node.parentNode; + } - $(resize).off('hover mousedown mouseup click mousemove'); - $(resize).hover(function() { $(resize).css('cursor', 'nw-resize'); }, function() { $(resize).css('cursor',''); clicked = false; }); - - $(resize).mousedown(function(e) + return rangeNodes; + }, + getNodes: function() { - e.preventDefault(); - - ratio = $(resize).width()/$(resize).height(); - - clicked = true; - clicker = true; - - start_x = Math.round(e.pageX - $(resize).eq(0).offset().left); - start_y = Math.round(e.pageY - $(resize).eq(0).offset().top); - }); - - $(resize).mouseup($.proxy(function(e) - { - clicked = false; - $(resize).css('cursor',''); - this.syncCode(); - - }, this)); - - $(resize).click($.proxy(function(e) - { - if (clicker) + var nodes = []; + var finalnodes = []; + if (this.window.getSelection) { - this.imageEdit(e); + var sel = this.window.getSelection(); + if (!sel.isCollapsed) + { + nodes = this.selection.getRangeSelectedNodes.call(this, sel.getRangeAt(0)); + } } - }, this)); - - $(resize).mousemove(function(e) - { - if (clicked) + $.each(nodes, $.proxy(function(i,node) { - clicker = false; + if (this.opts.iframe === false && $(node).parents('div.redactor_editor').size() == 0) + { + return false; + } - var mouse_x = Math.round(e.pageX - $(this).eq(0).offset().left) - start_x; - var mouse_y = Math.round(e.pageY - $(this).eq(0).offset().top) - start_y; + finalnodes.push(node); - var div_h = $(resize).height(); + }, this)); - var new_h = parseInt(div_h, 10) + mouse_y; - var new_w = new_h*ratio; + return finalnodes; + }, + getBlocks: function() + { + var nodes = this.selection.getNodes.call(this); + var newnodes = []; - if (new_w > min_w) + $.each(nodes, $.proxy(function(i,node) + { + if (this.opts.iframe === false && $(node).parents('div.redactor_editor').size() == 0) { - $(resize).width(new_w); + return false; } - if (new_h > min_h) + if (this.selection.nodeTestBlocks.call(this, node)) { - $(resize).height(new_h); + newnodes.push(node); } - start_x = Math.round(e.pageX - $(this).eq(0).offset().left); - start_y = Math.round(e.pageY - $(this).eq(0).offset().top); + }, this)); + + return newnodes; + }, + + remove: function() + { + if (this.window.getSelection) + { + this.window.getSelection().removeAllRanges(); } - }); + else if (document.selection) + { + this.document.selection.empty(); + } + } }, // TABLE - showTable: function() + table: { - this.saveSelection(); + show: function() + { + this.selection.save.call(this); - this.modalInit(RLANG.table, this.opts.modal_table, 300, $.proxy(function() - { - $('#redactor_insert_table_btn').click($.proxy(this.insertTable, this)); - - setTimeout(function() + this.modal.init.call(this, RLANG.table, this.opts.modal_table, 300, $.proxy(function() { - $('#redactor_table_rows').focus(); - }, 200); + $('#redactor_insert_table_btn').click($.proxy(this.table.insert, this)); - }, this) - ); - }, - insertTable: function() - { - var rows = $('#redactor_table_rows').val(); - var columns = $('#redactor_table_columns').val(); + setTimeout(function() + { + $('#redactor_table_rows').focus(); + }, 200); - var table_box = $('<div></div>'); + }, this) + ); + }, + insert: function() + { + var rows = $('#redactor_table_rows').val(); + var columns = $('#redactor_table_columns').val(); - var tableid = Math.floor(Math.random() * 99999); - var table = $('<table id="table' + tableid + '"><tbody></tbody></table>'); + var table_box = $('<div></div>'); - for (var i = 0; i < rows; i++) - { - var row = $('<tr></tr>'); - for (var z = 0; z < columns; z++) + var tableid = Math.floor(Math.random() * 99999); + var table = $('<table id="table' + tableid + '"><tbody></tbody></table>'); + + for (var i = 0; i < rows; i++) { - var column = $('<td><br></td>'); - $(row).append(column); + var row = $('<tr></tr>'); + for (var z = 0; z < columns; z++) + { + var column = $('<td>' + this.opts.invisibleSpace + '</td>'); + $(row).append(column); + } + $(table).append(row); } - $(table).append(row); - } - $(table_box).append(table); - var html = $(table_box).html() + '<p></p>'; + $(table_box).append(table); + var html = $(table_box).html(); - this.restoreSelection(); - this.execCommand('inserthtml', html); - this.modalClose(); - this.observeTables(); + this.selection.restore.call(this); + this.insert.force.call(this, html); + this.modal.close.call(this); + this.observe.tables.call(this); - }, - tableObserver: function(e) - { - this.$table = $(e.target).closest('table'); + }, + observer: function(e) + { + this.$table = $(e.target).closest('table'); - this.$table_tr = this.$table.find('tr'); - this.$table_td = this.$table.find('td'); + this.$table_tr = this.$table.find('tr'); + this.$table_td = this.$table.find('td'); - this.$tbody = $(e.target).closest('tbody'); - this.$thead = $(this.$table).find('thead'); + this.$tbody = $(e.target).closest('tbody'); + this.$thead = $(this.$table).find('thead'); - this.$current_td = $(e.target); - this.$current_tr = $(e.target).closest('tr'); - }, - deleteTable: function() - { - $(this.$table).remove(); - this.$table = false; - this.syncCode(); - }, - deleteRow: function() - { - $(this.$current_tr).remove(); - this.syncCode(); - }, - deleteColumn: function() - { - var index = $(this.$current_td).get(0).cellIndex; + this.$current_td = $(e.target); + this.$current_tr = $(e.target).closest('tr'); + }, + deleteTable: function() + { + this.buffer.set.call(this); - $(this.$table).find('tr').each(function() + $(this.$table).remove(); + this.$table = false; + this.sync(); + }, + deleteRow: function() { - $(this).find('td').eq(index).remove(); - }); + this.buffer.set.call(this); - this.syncCode(); - }, - addHead: function() - { - if ($(this.$table).find('thead').size() !== 0) + $(this.$current_tr).remove(); + this.sync(); + }, + deleteColumn: function() { - this.deleteHead(); - } - else - { - var tr = $(this.$table).find('tr').first().clone(); - tr.find('td').html('&nbsp;'); - this.$thead = $('<thead></thead>'); - this.$thead.append(tr); - $(this.$table).prepend(this.$thead); - this.syncCode(); - } - }, - deleteHead: function() - { - $(this.$thead).remove(); - this.$thead = false; - this.syncCode(); - }, - insertRowAbove: function() - { - this.insertRow('before'); - }, - insertRowBelow: function() - { - this.insertRow('after'); - }, - insertColumnLeft: function() - { - this.insertColumn('before'); - }, - insertColumnRight: function() - { - this.insertColumn('after'); - }, - insertRow: function(type) - { - var new_tr = $(this.$current_tr).clone(); - new_tr.find('td').html('&nbsp;'); - if (type === 'after') - { - $(this.$current_tr).after(new_tr); - } - else - { - $(this.$current_tr).before(new_tr); - } + this.buffer.set.call(this); - this.syncCode(); - }, - insertColumn: function(type) - { - var index = 0; + var index = $(this.$current_td).get(0).cellIndex; - this.$current_tr.find('td').each($.proxy(function(i,s) + $(this.$table).find('tr').each(function() + { + $(this).find('td').eq(index).remove(); + }); + + this.sync(); + }, + addHead: function() { - if ($(s)[0] === this.$current_td[0]) + this.buffer.set.call(this); + + if ($(this.$table).find('thead').size() !== 0) { - index = i; + this.table.deleteHead.call(this); } - }, this)); + else + { + var tr = $(this.$table).find('tr').first().clone(); + tr.find('td').html(this.opts.invisibleSpace); + this.$thead = $('<thead></thead>'); + this.$thead.append(tr); + $(this.$table).prepend(this.$thead); + this.sync(); + } + }, + deleteHead: function() + { + this.buffer.set.call(this); - this.$table_tr.each(function(i,s) + $(this.$thead).remove(); + this.$thead = false; + this.sync(); + }, + insertRowAbove: function() { - var current = $(s).find('td').eq(index); + this.table.insertRow.call(this, 'before'); + }, + insertRowBelow: function() + { + this.table.insertRow.call(this, 'after'); + }, + insertColumnLeft: function() + { + this.table.insertColumn.call(this, 'before'); + }, + insertColumnRight: function() + { + this.table.insertColumn.call(this, 'after'); + }, + insertRow: function(type) + { + this.buffer.set.call(this); - var td = current.clone(); - td.html('&nbsp;'); - + var new_tr = $(this.$current_tr).clone(); + new_tr.find('td').html(this.opts.invisibleSpace); if (type === 'after') { - $(current).after(td); + $(this.$current_tr).after(new_tr); } else { - $(current).before(td); + $(this.$current_tr).before(new_tr); } - }); + this.sync(); + }, + insertColumn: function(type) + { + this.buffer.set.call(this); - this.syncCode(); - }, + var index = 0; - // INSERT VIDEO - showVideo: function() - { - this.saveSelection(); - this.modalInit(RLANG.video, this.opts.modal_video, 600, $.proxy(function() + this.$current_tr.find('td').each($.proxy(function(i,s) { - $('#redactor_insert_video_btn').click($.proxy(this.insertVideo, this)); - - setTimeout(function() + if ($(s)[0] === this.$current_td[0]) { - $('#redactor_insert_video_area').focus(); - }, 200); + index = i; + } + }, this)); - }, this) - ); - }, - insertVideo: function() - { - var data = $('#redactor_insert_video_area').val(); - data = this.stripTags(data); - - this.restoreSelection(); - this.execCommand('inserthtml', data); - this.modalClose(); - }, - - // INSERT IMAGE - imageEdit: function(e) - { - var $el = $(e.target); - var parent = $el.parent(); - - var callback = $.proxy(function() - { - $('#redactor_file_alt').val($el.attr('alt')); - $('#redactor_image_edit_src').attr('href', $el.attr('src')); - $('#redactor_form_image_align').val($el.css('float')); - - if ($(parent).get(0).tagName === 'A') + this.$table_tr.each($.proxy(function(i,s) { - $('#redactor_file_link').val($(parent).attr('href')); - } + var current = $(s).find('td').eq(index); - $('#redactor_image_delete_btn').click($.proxy(function() { this.imageDelete($el); }, this)); - $('#redactorSaveBtn').click($.proxy(function() { this.imageSave($el); }, this)); + var td = current.clone(); + td.html(this.opts.invisibleSpace); - }, this); + if (type === 'after') $(current).after(td); + else $(current).before(td); - this.modalInit(RLANG.image, this.opts.modal_image_edit, 380, callback); + }, this)); + this.sync(); + } }, - imageDelete: function(el) + + // VIDEO + video: { - $(el).remove(); - this.modalClose(); - this.syncCode(); - }, - imageSave: function(el) - { - var parent = $(el).parent(); + show: function() + { + this.selection.save.call(this); + this.modal.init.call(this, RLANG.video, this.opts.modal_video, 600, $.proxy(function() + { + $('#redactor_insert_video_btn').click($.proxy(this.video.insert, this)); - $(el).attr('alt', $('#redactor_file_alt').val()); + setTimeout(function() + { + $('#redactor_insert_video_area').focus(); + }, 200); - var floating = $('#redactor_form_image_align').val(); - - if (floating === 'left') + }, this) + ); + }, + insert: function() { - $(el).css({ 'float': 'left', margin: '0 10px 10px 0' }); - } - else if (floating === 'right') - { - $(el).css({ 'float': 'right', margin: '0 0 10px 10px' }); - } - else - { - $(el).css({ 'float': 'none', margin: '0' }); - } + var data = $('#redactor_insert_video_area').val(); + data = this.clean.stripTags.call(this, data); - // as link - var link = $.trim($('#redactor_file_link').val()); - if (link !== '') - { - if ($(parent).get(0).tagName !== 'A') - { - $(el).replaceWith('<a href="' + link + '">' + this.outerHTML(el) + '</a>'); - } - else - { - $(parent).attr('href', link); - } + this.selection.restore.call(this); + this.exec.command.call(this, 'inserthtml', data); + this.modal.close.call(this); } - else - { - if ($(parent).get(0).tagName === 'A') - { - $(parent).replaceWith(this.outerHTML(el)); - } - } - - this.modalClose(); - this.observeImages(); - this.syncCode(); - }, - showImage: function() - { - this.saveSelection(); - var callback = $.proxy(function() + // IMAGE + image: + { + show: function() { - // json - if (this.opts.imageGetJson !== false) + this.selection.save.call(this); + + var callback = $.proxy(function() { - $.getJSON(this.opts.imageGetJson, $.proxy(function(data) { + // json + if (this.opts.imageGetJson !== false) + { + $.getJSON(this.opts.imageGetJson, $.proxy(function(data) { - var folders = {}; - var z = 0; + var folders = {}; + var z = 0; - // folders - $.each(data, $.proxy(function(key, val) - { - if (typeof val.folder !== 'undefined') + // folders + $.each(data, $.proxy(function(key, val) { - z++; - folders[val.folder] = z; - } + if (typeof val.folder !== 'undefined') + { + z++; + folders[val.folder] = z; + } - }, this)); + }, this)); - var folderclass = false; - $.each(data, $.proxy(function(key, val) - { - // title - var thumbtitle = ''; - if (typeof val.title !== 'undefined') + var folderclass = false; + $.each(data, $.proxy(function(key, val) { - thumbtitle = val.title; - } + // title + var thumbtitle = ''; + if (typeof val.title !== 'undefined') + { + thumbtitle = val.title; + } - var folderkey = 0; - if (!$.isEmptyObject(folders) && typeof val.folder !== 'undefined') - { - folderkey = folders[val.folder]; - if (folderclass === false) + var folderkey = 0; + if (!$.isEmptyObject(folders) && typeof val.folder !== 'undefined') { - folderclass = '.redactorfolder' + folderkey; + folderkey = folders[val.folder]; + if (folderclass === false) + { + folderclass = '.redactorfolder' + folderkey; + } } - } - var img = $('<img src="' + val.thumb + '" class="redactorfolder redactorfolder' + folderkey + '" rel="' + val.image + '" title="' + thumbtitle + '" />'); - $('#redactor_image_box').append(img); - $(img).click($.proxy(this.imageSetThumb, this)); + var img = $('<img src="' + val.thumb + '" class="redactorfolder redactorfolder' + folderkey + '" rel="' + val.image + '" title="' + thumbtitle + '" />'); + $('#redactor_image_box').append(img); + $(img).click($.proxy(this.image.thumb, this)); - }, this)); + }, this)); - // folders - if (!$.isEmptyObject(folders)) - { - $('.redactorfolder').hide(); - $(folderclass).show(); - - var onchangeFunc = function(e) + // folders + if (!$.isEmptyObject(folders)) { $('.redactorfolder').hide(); - $('.redactorfolder' + $(e.target).val()).show(); - } + $(folderclass).show(); - var select = $('<select id="redactor_image_box_select">'); - $.each(folders, function(k,v) - { - select.append($('<option value="' + v + '">' + k + '</option>')); - }); + var onchangeFunc = function(e) + { + $('.redactorfolder').hide(); + $('.redactorfolder' + $(e.target).val()).show(); + } - $('#redactor_image_box').before(select); - select.change(onchangeFunc); - } + var select = $('<select id="redactor_image_box_select">'); + $.each(folders, function(k,v) + { + select.append($('<option value="' + v + '">' + k + '</option>')); + }); - }, this)); - } - else - { - $('#redactor_tabs a').eq(1).remove(); - } + $('#redactor_image_box').before(select); + select.change(onchangeFunc); + } - if (this.opts.imageUpload !== false) - { + }, this)); + } + else + { + $('#redactor_tabs a').eq(1).remove(); + } - // dragupload - if (this.opts.uploadCrossDomain === false && this.isMobile() === false) + if (this.opts.imageUpload !== false) { - if ($('#redactor_file').size() !== 0) + // dragupload + if (this.opts.uploadCrossDomain === false && this.utils.isMobile.call(this) === false) { - $('#redactor_file').dragupload( + if ($('#redactor_file').size() !== 0) { - url: this.opts.imageUpload, - uploadFields: this.opts.uploadFields, - success: $.proxy(this.imageUploadCallback, this), - error: $.proxy(this.opts.imageUploadErrorCallback, this) - }); + this.dragupload.init.call(this, '#redactor_file', + { + url: this.opts.imageUpload, + uploadFields: this.opts.uploadFields, + success: $.proxy(this.image.callback, this), + error: $.proxy(this.opts.imageUploadErrorCallback, this) + }); + } } - } - // ajax upload - this.uploadInit('redactor_file', - { - auto: true, - url: this.opts.imageUpload, - success: $.proxy(this.imageUploadCallback, this), - error: $.proxy(this.opts.imageUploadErrorCallback, this) - }); - } - else - { - $('.redactor_tab').hide(); - if (this.opts.imageGetJson === false) - { - $('#redactor_tabs').remove(); - $('#redactor_tab3').show(); + // ajax upload + this.upload.init.call(this, 'redactor_file', + { + auto: true, + url: this.opts.imageUpload, + success: $.proxy(this.image.callback, this), + error: $.proxy(this.opts.imageUploadErrorCallback, this) + }); } else { - var tabs = $('#redactor_tabs a'); - tabs.eq(0).remove(); - tabs.eq(1).addClass('redactor_tabs_act'); - $('#redactor_tab2').show(); + $('.redactor_tab').hide(); + if (this.opts.imageGetJson === false) + { + $('#redactor_tabs').remove(); + $('#redactor_tab3').show(); + } + else + { + var tabs = $('#redactor_tabs a'); + tabs.eq(0).remove(); + tabs.eq(1).addClass('redactor_tabs_act'); + $('#redactor_tab2').show(); + } } - } - $('#redactor_upload_btn').click($.proxy(this.imageUploadCallbackLink, this)); + $('#redactor_upload_btn').click($.proxy(this.image.callbackLink, this)); - if (this.opts.imageUpload === false && this.opts.imageGetJson === false) - { - setTimeout(function() + if (this.opts.imageUpload === false && this.opts.imageGetJson === false) { - $('#redactor_file_link').focus(); - }, 200); + setTimeout(function() + { + $('#redactor_file_link').focus(); + }, 200); - } + } - }, this); + }, this); - this.modalInit(RLANG.image, this.opts.modal_image, 610, callback); + this.modal.init.call(this, RLANG.image, this.opts.modal_image, 610, callback); - }, - imageSetThumb: function(e) - { - this._imageSet('<img src="' + $(e.target).attr('rel') + '" alt="' + $(e.target).attr('title') + '" />', true); - }, - imageUploadCallbackLink: function() - { - if ($('#redactor_file_link').val() !== '') + }, + edit: function(e) { - var data = '<img src="' + $('#redactor_file_link').val() + '" />'; - this._imageSet(data, true); - } - else - { - this.modalClose(); - } - }, - imageUploadCallback: function(data) - { - this._imageSet(data); - }, - _imageSet: function(json, link) - { - this.restoreSelection(); + var $el = $(e.target); + var parent = $el.parent(); - if (json !== false) + var callback = $.proxy(function() + { + $('#redactor_file_alt').val($el.attr('alt')); + $('#redactor_image_edit_src').attr('href', $el.attr('src')); + $('#redactor_form_image_align').val($el.css('float')); + + if ($(parent).get(0).tagName === 'A') + { + $('#redactor_file_link').val($(parent).attr('href')); + } + + $('#redactor_image_delete_btn').click($.proxy(function() { this.image.remove.call(this, $el); }, this)); + $('#redactorSaveBtn').click($.proxy(function() { this.image.save.call(this, $el); }, this)); + + }, this); + + this.modal.init.call(this, RLANG.image, this.opts.modal_image_edit, 380, callback); + + }, + remove: function(el) { - var html = ''; - if (link !== true) + var parent = $(el).parent(); + $(el).remove(); + + if (parent.size() != 0 && parent[0].tagName === 'P') { - html = '<p><img src="' + json.filelink + '" /></p>'; + this.$editor.focus(); + this.selection.start.call(this, parent); } - else - { - html = json; - } - this.execCommand('inserthtml', html); - - // upload image callback - if (link !== true && typeof this.opts.imageUploadCallback === 'function') + // delete callback + if (typeof this.opts.imageDeleteCallback === 'function') { - this.opts.imageUploadCallback(this, json); + this.opts.imageDeleteCallback(this, el); } - } - this.modalClose(); - this.observeImages(); - }, + this.modal.close.call(this); + this.sync(); + }, + save: function(el) + { + var parent = $(el).parent(); - // INSERT LINK - showLink: function() - { - this.saveSelection(); + $(el).attr('alt', $('#redactor_file_alt').val()); - var callback = $.proxy(function() - { - this.insert_link_node = false; - var sel = this.getSelection(); - var url = '', text = '', target = ''; + var floating = $('#redactor_form_image_align').val(); - if (this.browser('msie')) + if (floating === 'left') { - var parent = this.getParentNode(); - if (parent.nodeName === 'A') + $(el).css({ 'float': 'left', margin: '0 10px 10px 0' }); + } + else if (floating === 'right') + { + $(el).css({ 'float': 'right', margin: '0 0 10px 10px' }); + } + else + { + $(el).css({ 'float': 'none', margin: '0' }); + } + + // as link + var link = $.trim($('#redactor_file_link').val()); + if (link !== '') + { + if ($(parent).get(0).tagName !== 'A') { - this.insert_link_node = $(parent); - text = this.insert_link_node.text(); - url = this.insert_link_node.attr('href'); - target = this.insert_link_node.attr('target'); + $(el).replaceWith('<a href="' + link + '">' + this.utils.outerHtml.call(this, el) + '</a>'); } else { - if (this.oldIE()) - { - text = sel.text; - } - else - { - text = sel.toString(); - } + $(parent).attr('href', link); } } else { - if (sel && sel.anchorNode && sel.anchorNode.parentNode.tagName === 'A') + if ($(parent).get(0).tagName === 'A') { - url = sel.anchorNode.parentNode.href; - text = sel.anchorNode.parentNode.text; - target = sel.anchorNode.parentNode.target; - - if (sel.toString() === '') - { - this.insert_link_node = sel.anchorNode.parentNode; - } + $(parent).replaceWith(this.utils.outerHtml.call(this, el)); } - else - { - text = sel.toString(); - } } - $('.redactor_link_text').val(text); + this.modal.close.call(this); + this.observe.images.call(this); + this.sync(); - var thref = self.location.href.replace(/\/$/i, ''); - var turl = url.replace(thref, ''); + }, + resize: function(resize) + { + var clicked = false; + var clicker = false; + var start_x; + var start_y; + var ratio = $(resize).width()/$(resize).height(); + var min_w = 10; + var min_h = 10; - if (url.search('mailto:') === 0) - { - this.setModalTab(2); + $(resize).off('hover mousedown mouseup click mousemove'); + $(resize).hover(function() { $(resize).css('cursor', 'nw-resize'); }, function() { $(resize).css('cursor',''); clicked = false; }); - $('#redactor_tab_selected').val(2); - $('#redactor_link_mailto').val(url.replace('mailto:', '')); - } - else if (turl.search(/^#/gi) === 0) + $(resize).mousedown(function(e) { - this.setModalTab(3); + e.preventDefault(); - $('#redactor_tab_selected').val(3); - $('#redactor_link_anchor').val(turl.replace(/^#/gi, '')); - } - else + ratio = $(resize).width()/$(resize).height(); + + clicked = true; + clicker = true; + + start_x = Math.round(e.pageX - $(resize).eq(0).offset().left); + start_y = Math.round(e.pageY - $(resize).eq(0).offset().top); + }); + + $(resize).mouseup($.proxy(function(e) { - $('#redactor_link_url').val(turl); - } + clicked = false; + $(resize).css('cursor',''); + this.sync(); - if (target === '_blank') + }, this)); + + $(resize).click($.proxy(function(e) { - $('#redactor_link_blank').attr('checked', true); - } + if (clicker) + { + this.image.edit.call(this, e); + } - $('#redactor_insert_link_btn').click($.proxy(this.insertLink, this)); + }, this)); - setTimeout(function() + $(resize).mousemove(function(e) { - $('#redactor_link_url').focus(); - }, 200); + if (clicked) + { + clicker = false; - }, this); + var mouse_x = Math.round(e.pageX - $(this).eq(0).offset().left) - start_x; + var mouse_y = Math.round(e.pageY - $(this).eq(0).offset().top) - start_y; - this.modalInit(RLANG.link, this.opts.modal_link, 460, callback); + var div_h = $(resize).height(); - }, - insertLink: function() - { - var tab_selected = $('#redactor_tab_selected').val(); - var link = '', text = '', target = ''; + var new_h = parseInt(div_h, 10) + mouse_y; + var new_w = new_h*ratio; - if (tab_selected === '1') // url + if (new_w > min_w) + { + $(resize).width(new_w); + } + + //if (new_h > min_h) $(resize).height(new_h); + + start_x = Math.round(e.pageX - $(this).eq(0).offset().left); + start_y = Math.round(e.pageY - $(this).eq(0).offset().top); + } + }); + }, + thumb: function(e) { - link = $('#redactor_link_url').val(); - text = $('#redactor_link_url_text').val(); + var img = '<img id="image-marker" src="' + $(e.target).attr('rel') + '" alt="' + $(e.target).attr('title') + '" />'; - if ($('#redactor_link_blank').attr('checked')) + if (this.opts.linebreaks === false) { - target = ' target="_blank"'; + img = '<p>' + img + '</p>'; } - // test url - var pattern = '/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/'; - //var pattern = '((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}'; - var re = new RegExp('^(http|ftp|https)://' + pattern,'i'); - var re2 = new RegExp('^' + pattern,'i'); - if (link.search(re) == -1 && link.search(re2) == 0 && this.opts.protocol !== false) + this.image.insert.call(this, img, true); + }, + callbackLink: function() + { + if ($('#redactor_file_link').val() !== '') { - link = this.opts.protocol + link; - } + var data = '<img id="image-marker" src="' + $('#redactor_file_link').val() + '" />'; + if (this.opts.linebreaks === false) + { + data = '<p>' + data + '</p>'; + } - } - else if (tab_selected === '2') // mailto + this.image.insert.call(this, data, true); + } + else + { + this.modal.close.call(this); + } + }, + callback: function(data) { - link = 'mailto:' + $('#redactor_link_mailto').val(); - text = $('#redactor_link_mailto_text').val(); - } - else if (tab_selected === '3') // anchor + this.image.insert.call(this, data); + }, + insert: function(json, link) { - link = '#' + $('#redactor_link_anchor').val(); - text = $('#redactor_link_anchor_text').val(); - } + this.selection.restore.call(this); - this._insertLink('<a href="' + link + '"' + target + '>' + text + '</a>', $.trim(text), link, target); - - }, - _insertLink: function(a, text, link, target) - { - this.$editor.focus(); - this.restoreSelection(); - - if (text !== '') - { - if (this.insert_link_node) + if (json !== false) { - $(this.insert_link_node).text(text); - $(this.insert_link_node).attr('href', link); - if (target !== '') + var html = ''; + if (link !== true) { - $(this.insert_link_node).attr('target', target); + html = '<img id="image-marker" src="' + json.filelink + '" />'; + if (this.opts.linebreaks === false) + { + html = '<p>' + html + '</p>'; + } } else { - $(this.insert_link_node).removeAttr('target'); + html = json; } - this.syncCode(); + this.exec.command.call(this, 'inserthtml', html); + var image = $(this.$editor.find('img#image-marker')); + + if (image.size() != 0) image.removeAttr('id'); + else image = false; + + // upload image callback + if (link !== true && typeof this.opts.imageUploadCallback === 'function') + { + this.opts.imageUploadCallback(this, image, json); + } } - else - { - this.execCommand('inserthtml', a); - } - } - this.modalClose(); + this.modal.close.call(this); + this.observe.images.call(this); + }, }, - // INSERT FILE - showFile: function() + // LINK + link: { - this.saveSelection(); - - var callback = $.proxy(function() + show: function() { - var sel = this.getSelection(); + this.selection.save.call(this); - var text = ''; - - if (this.oldIE()) + var callback = $.proxy(function() { - text = sel.text; - } - else - { - text = sel.toString(); - } + this.insert_link_node = false; + var sel = this.selection.get.call(this); + var url = '', text = '', target = ''; - $('#redactor_filename').val(text); - - // dragupload - if (this.opts.uploadCrossDomain === false && this.isMobile() === false) - { - $('#redactor_file').dragupload( + if (this.utils.browser.call(this, 'msie')) { - url: this.opts.fileUpload, - uploadFields: this.opts.uploadFields, - success: $.proxy(this.fileUploadCallback, this), - error: $.proxy(this.opts.fileUploadErrorCallback, this) - }); - } + var parent = this.selection.getElement.call(this); + if (parent.nodeName === 'A') + { + this.insert_link_node = $(parent); + text = this.insert_link_node.text(); + url = this.insert_link_node.attr('href'); + target = this.insert_link_node.attr('target'); + } + else + { + if (this.utils.oldIE.call(this)) text = sel.text; + else text = sel.toString(); + } + } + else + { + if (sel && sel.anchorNode && sel.anchorNode.parentNode.tagName === 'A') + { + url = sel.anchorNode.parentNode.href; + text = sel.anchorNode.parentNode.text; + target = sel.anchorNode.parentNode.target; - this.uploadInit('redactor_file', - { - auto: true, - url: this.opts.fileUpload, - success: $.proxy(this.fileUploadCallback, this), - error: $.proxy(this.opts.fileUploadErrorCallback, this) - }); + if (sel.toString() === '') + { + this.insert_link_node = sel.anchorNode.parentNode; + } + } + else + { + text = sel.toString(); + } + } - }, this); + $('.redactor_link_text').val(text); - this.modalInit(RLANG.file, this.opts.modal_file, 500, callback); - }, - fileUploadCallback: function(json) - { - this.restoreSelection(); + var thref = self.location.href.replace(/\/$/i, ''); + var turl = url.replace(thref, ''); - if (json !== false) - { - var text = $('#redactor_filename').val(); + var tabs = $('#redactor_tabs a'); + if (this.opts.linkEmail === false) + { + tabs.eq(1).remove(); + } - if (text === '') - { - text = json.filename; - } + if (this.opts.linkAnchor === false) + { + tabs.eq(2).remove(); + } - var link = '<a href="' + json.filelink + '">' + text + '</a>'; + if (this.opts.linkEmail === false && this.opts.linkAnchor === false) + { + $('#redactor_tabs').remove(); + $('#redactor_link_url').val(turl); + } + else + { + if (url.search('mailto:') === 0) + { + this.modal.setTab.call(this, 2); - // chrome fix - if (this.browser('webkit') && !!this.window.chrome) - { - link = link + '&nbsp;'; - } + $('#redactor_tab_selected').val(2); + $('#redactor_link_mailto').val(url.replace('mailto:', '')); + } + else if (turl.search(/^#/gi) === 0) + { + this.modal.setTab.call(this, 3); - this.execCommand('inserthtml', link); + $('#redactor_tab_selected').val(3); + $('#redactor_link_anchor').val(turl.replace(/^#/gi, '')); + } + else + { + $('#redactor_link_url').val(turl); + } + } - // file upload callback - if (typeof this.opts.fileUploadCallback === 'function') - { - this.opts.fileUploadCallback(this, json); - } - } + if (target === '_blank') + { + $('#redactor_link_blank').attr('checked', true); + } - this.modalClose(); - }, + $('#redactor_insert_link_btn').click($.proxy(this.link.process, this)); + setTimeout(function() + { + $('#redactor_link_url').focus(); + }, 200); + }, this); - // MODAL - modalInit: function(title, content, width, callback) - { - // modal overlay - if ($('#redactor_modal_overlay').size() === 0) - { - this.overlay = $('<div id="redactor_modal_overlay" style="display: none;"></div>'); - $('body').prepend(this.overlay); - } + this.modal.init.call(this, RLANG.link, this.opts.modal_link, 460, callback); - if (this.opts.overlay) + }, + process: function() { - $('#redactor_modal_overlay').show(); - $('#redactor_modal_overlay').click($.proxy(this.modalClose, this)); - } + var tab_selected = $('#redactor_tab_selected').val(); + var link = '', text = '', target = ''; - if ($('#redactor_modal').size() === 0) - { - this.modal = $('<div id="redactor_modal" style="display: none;"><div id="redactor_modal_close">&times;</div><div id="redactor_modal_header"></div><div id="redactor_modal_inner"></div></div>'); - $('body').append(this.modal); - } + if (tab_selected === '1') // url + { + link = $('#redactor_link_url').val(); + text = $('#redactor_link_url_text').val(); - $('#redactor_modal_close').click($.proxy(this.modalClose, this)); + if ($('#redactor_link_blank').attr('checked')) + { + target = ' target="_blank"'; + } - this.hdlModalClose = $.proxy(function(e) { if ( e.keyCode === 27) { this.modalClose(); return false; } }, this); + // test url + var pattern = '/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/'; + //var pattern = '((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}'; + var re = new RegExp('^(http|ftp|https)://' + pattern,'i'); + var re2 = new RegExp('^' + pattern,'i'); + if (link.search(re) == -1 && link.search(re2) == 0 && this.opts.protocol !== false) + { + link = this.opts.protocol + link; + } - $(document).keyup(this.hdlModalClose); - this.$editor.keyup(this.hdlModalClose); + } + else if (tab_selected === '2') // mailto + { + link = 'mailto:' + $('#redactor_link_mailto').val(); + text = $('#redactor_link_mailto_text').val(); + } + else if (tab_selected === '3') // anchor + { + link = '#' + $('#redactor_link_anchor').val(); + text = $('#redactor_link_anchor_text').val(); + } - // set content - if (content.indexOf('#') == 0) - { - $('#redactor_modal_inner').empty().append($(content).html()); - } - else - { - $('#redactor_modal_inner').empty().append(content); - } + this.link.insert.call(this, '<a href="' + link + '"' + target + '>' + text + '</a>', $.trim(text), link, target); - - $('#redactor_modal_header').html(title); - - // draggable - if (typeof $.fn.draggable !== 'undefined') + }, + insert: function(a, text, link, target) { - $('#redactor_modal').draggable({ handle: '#redactor_modal_header' }); - $('#redactor_modal_header').css('cursor', 'move'); - } + this.selection.restore.call(this); - // tabs - if ($('#redactor_tabs').size() !== 0) - { - var that = this; - $('#redactor_tabs a').each(function(i,s) + if (text !== '') { - i++; - $(s).click(function() + if (this.insert_link_node) { - $('#redactor_tabs a').removeClass('redactor_tabs_act'); - $(this).addClass('redactor_tabs_act'); - $('.redactor_tab').hide(); - $('#redactor_tab' + i).show(); - $('#redactor_tab_selected').val(i); - - if (that.isMobile() === false) + $(this.insert_link_node).text(text); + $(this.insert_link_node).attr('href', link); + if (target !== '') { - var height = $('#redactor_modal').outerHeight(); - $('#redactor_modal').css('margin-top', '-' + (height+10)/2 + 'px'); + $(this.insert_link_node).attr('target', target); } - }); - }); - } + else + { + $(this.insert_link_node).removeAttr('target'); + } - $('#redactor_modal .redactor_btn_modal_close').click($.proxy(this.modalClose, this)); + this.sync(); + } + else + { + this.exec.command.call(this, 'inserthtml', a); + } + } - if (this.isMobile() === false) - { - $('#redactor_modal').css({ position: 'fixed', top: '-2000px', left: '50%', width: width + 'px', marginLeft: '-' + (width+60)/2 + 'px' }).show(); - - this.modalSaveBodyOveflow = $(document.body).css('overflow'); - $(document.body).css('overflow', 'hidden'); + this.modal.close.call(this); } - else - { - $('#redactor_modal').css({ position: 'fixed', width: '100%', height: '100%', top: '0', left: '0', margin: '0', minHeight: '300px' }).show(); - } + }, - // callback - if (typeof callback === 'function') + // FILE + file: + { + show: function() { - callback(); - } + this.selection.save.call(this); - if (this.isMobile() === false) - { - setTimeout(function() + var callback = $.proxy(function() { - var height = $('#redactor_modal').outerHeight(); - $('#redactor_modal').css({ top: '50%', height: 'auto', minHeight: 'auto', marginTop: '-' + (height+10)/2 + 'px' }); + var sel = this.selection.get.call(this); - }, 20); - } + var text = ''; - }, - modalClose: function() - { - $('#redactor_modal_close').unbind('click', this.modalClose); - $('#redactor_modal').fadeOut('fast', $.proxy(function() + if (this.utils.oldIE.call(this)) + { + text = sel.text; + } + else + { + text = sel.toString(); + } + + $('#redactor_filename').val(text); + + // dragupload + if (this.opts.uploadCrossDomain === false && this.utils.isMobile.call(this) === false) + { + this.dragupload.init.call(this, '#redactor_file', + { + url: this.opts.fileUpload, + uploadFields: this.opts.uploadFields, + success: $.proxy(this.file.callback, this), + error: $.proxy(this.opts.fileUploadErrorCallback, this) + }); + } + + this.upload.init.call(this, 'redactor_file', + { + auto: true, + url: this.opts.fileUpload, + success: $.proxy(this.file.callback, this), + error: $.proxy(this.opts.fileUploadErrorCallback, this) + }); + + }, this); + + this.modal.init.call(this, RLANG.file, this.opts.modal_file, 500, callback); + }, + callback: function(json) { - $('#redactor_modal_inner').html(''); + this.selection.restore.call(this); - if (this.opts.overlay) + if (json !== false) { - $('#redactor_modal_overlay').hide(); - $('#redactor_modal_overlay').unbind('click', this.modalClose); - } + var text = $('#redactor_filename').val(); - $(document).unbind('keyup', this.hdlModalClose); - this.$editor.unbind('keyup', this.hdlModalClose); + if (text === '') + { + text = json.filename; + } - }, this)); + var link = '<a href="' + json.filelink + '" id="filelink-marker">' + text + '</a>'; + // chrome fix + if (this.utils.browser.call(this, 'webkit') && !!this.window.chrome) + { + link = link + '&nbsp;'; + } - if (this.isMobile() === false) - { - $(document.body).css('overflow', this.modalSaveBodyOveflow ? this.modalSaveBodyOveflow : 'visible'); - } + this.exec.command.call(this, 'inserthtml', link); + var linkmarker = $(this.$editor.find('a#filelink-marker')); + if (linkmarker.size() != 0) + { + linkmarker.removeAttr('id'); + } + else + { + linkmarker = false; + } - return false; + // file upload callback + if (typeof this.opts.fileUploadCallback === 'function') + { + this.opts.fileUploadCallback(this, linkmarker, json); + } + } + this.modal.close.call(this); + } }, - setModalTab: function(num) - { - $('.redactor_tab').hide(); - var tabs = $('#redactor_tabs a'); - tabs.removeClass('redactor_tabs_act'); - tabs.eq(num-1).addClass('redactor_tabs_act'); - $('#redactor_tab' + num).show(); - }, - // UPLOAD - uploadInit: function(element, options) + // MODAL + modal: { - // Upload Options - this.uploadOptions = { - url: false, - success: false, - error: false, - start: false, - trigger: false, - auto: false, - input: false - }; - - $.extend(this.uploadOptions, options); - - // Test input or form - if ($('#' + element).size() !== 0 && $('#' + element).get(0).tagName === 'INPUT') + init: function(title, content, width, callback) { - this.uploadOptions.input = $('#' + element); - this.element = $($('#' + element).get(0).form); - } - else - { - this.element = $('#' + element); - } + // modal overlay + if ($('#redactor_modal_overlay').size() === 0) + { + this.overlay = $('<div id="redactor_modal_overlay" style="display: none;"></div>'); + $('body').prepend(this.overlay); + } - this.element_action = this.element.attr('action'); + if (this.opts.overlay) + { + $('#redactor_modal_overlay').show(); + $('#redactor_modal_overlay').click($.proxy(this.modal.close, this)); + } - // Auto or trigger - if (this.uploadOptions.auto) - { - $(this.uploadOptions.input).change($.proxy(function() + if ($('#redactor_modal').size() === 0) { - this.element.submit(function(e) { return false; }); - this.uploadSubmit(); - }, this)); + this.$modal = $('<div id="redactor_modal" style="display: none;"><div id="redactor_modal_close">&times;</div><div id="redactor_modal_header"></div><div id="redactor_modal_inner"></div></div>'); + $('body').append(this.$modal); + } - } - else if (this.uploadOptions.trigger) - { - $('#' + this.uploadOptions.trigger).click($.proxy(this.uploadSubmit, this)); - } - }, - uploadSubmit : function() - { - this.uploadForm(this.element, this.uploadFrame()); - }, - uploadFrame : function() - { - this.id = 'f' + Math.floor(Math.random() * 99999); + $('#redactor_modal_close').click($.proxy(this.modal.close, this)); - var d = this.document.createElement('div'); - var iframe = '<iframe style="display:none" id="'+this.id+'" name="'+this.id+'"></iframe>'; - d.innerHTML = iframe; - $(d).appendTo("body"); + this.hdlModalClose = $.proxy(function(e) { if ( e.keyCode === 27) { this.modal.close(); return false; } }, this); - // Start - if (this.uploadOptions.start) - { - this.uploadOptions.start(); - } + $(document).keyup(this.hdlModalClose); + this.$editor.keyup(this.hdlModalClose); - $('#' + this.id).load($.proxy(this.uploadLoaded, this)); + // set content + this.modalcontent = false; + if (content.indexOf('#') == 0) + { + this.modalcontent = $(content); + $('#redactor_modal_inner').empty().append(this.modalcontent.html()); + this.modalcontent.html(''); + } + else + { + $('#redactor_modal_inner').empty().append(content); + } - return this.id; - }, - uploadForm : function(f, name) - { - if (this.uploadOptions.input) - { - var formId = 'redactorUploadForm' + this.id; - var fileId = 'redactorUploadFile' + this.id; - this.form = $('<form action="' + this.uploadOptions.url + '" method="POST" target="' + name + '" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>'); - // append hidden fields - if (this.opts.uploadFields !== false && typeof this.opts.uploadFields === 'object') + $('#redactor_modal_header').html(title); + + // draggable + if (typeof $.fn.draggable !== 'undefined') { - $.each(this.opts.uploadFields, $.proxy(function(k,v) + $('#redactor_modal').draggable({ handle: '#redactor_modal_header' }); + $('#redactor_modal_header').css('cursor', 'move'); + } + + // tabs + if ($('#redactor_tabs').size() !== 0) + { + var that = this; + $('#redactor_tabs a').each(function(i,s) { - if (v.toString().indexOf('#') === 0) + i++; + $(s).click(function() { - v = $(v).val(); - } + $('#redactor_tabs a').removeClass('redactor_tabs_act'); + $(this).addClass('redactor_tabs_act'); + $('.redactor_tab').hide(); + $('#redactor_tab' + i).show(); + $('#redactor_tab_selected').val(i); - var hidden = $('<input/>', {'type': "hidden", 'name': k, 'value': v}); - $(this.form).append(hidden); - - }, this)); + if (that.utils.isMobile.call(that) === false) + { + var height = $('#redactor_modal').outerHeight(); + $('#redactor_modal').css('margin-top', '-' + (height+10)/2 + 'px'); + } + }); + }); } - var oldElement = this.uploadOptions.input; - var newElement = $(oldElement).clone(); - $(oldElement).attr('id', fileId); - $(oldElement).before(newElement); - $(oldElement).appendTo(this.form); - $(this.form).css('position', 'absolute'); - $(this.form).css('top', '-2000px'); - $(this.form).css('left', '-2000px'); - $(this.form).appendTo('body'); + $('#redactor_modal .redactor_btn_modal_close').click($.proxy(this.modal.close, this)); - this.form.submit(); - } - else - { - f.attr('target', name); - f.attr('method', 'POST'); - f.attr('enctype', 'multipart/form-data'); - f.attr('action', this.uploadOptions.url); + if (this.utils.isMobile.call(this) === false) + { + $('#redactor_modal').css({ position: 'fixed', top: '-2000px', left: '50%', width: width + 'px', marginLeft: '-' + (width+60)/2 + 'px' }).show(); - this.element.submit(); - } + this.modalSaveBodyOveflow = $(document.body).css('overflow'); + $(document.body).css('overflow', 'hidden'); + } + else + { + $('#redactor_modal').css({ position: 'fixed', width: '100%', height: '100%', top: '0', left: '0', margin: '0', minHeight: '300px' }).show(); + } - }, - uploadLoaded : function() - { - var i = $('#' + this.id)[0]; - var d; + // callback + if (typeof callback === 'function') + { + callback(); + } - if (i.contentDocument) - { - d = i.contentDocument; - } - else if (i.contentWindow) - { - d = i.contentWindow.document; - } - else - { - d = window.frames[this.id].document; - } + if (this.utils.isMobile.call(this) === false) + { + setTimeout(function() + { + var height = $('#redactor_modal').outerHeight(); + $('#redactor_modal').css({ top: '50%', height: 'auto', minHeight: 'auto', marginTop: '-' + (height+10)/2 + 'px' }); - // Success - if (this.uploadOptions.success) + }, 20); + } + + }, + close: function() { - if (typeof d !== 'undefined') + $('#redactor_modal_close').unbind('click', this.modal.close); + $('#redactor_modal').fadeOut('fast', $.proxy(function() { - // Remove bizarre <pre> tag wrappers around our json data: - var rawString = d.body.innerHTML; - var jsonString = rawString.match(/\{(.|\n)*\}/)[0]; - var json = $.parseJSON(jsonString); - - if (typeof json.error == 'undefined') + if (this.modalcontent !== false) { - this.uploadOptions.success(json); + this.modalcontent.html($('#redactor_modal_inner').html()); + this.modalcontent = false; } - else + + $('#redactor_modal_inner').html(''); + + if (this.opts.overlay) { - this.uploadOptions.error(this, json); - this.modalClose(); + $('#redactor_modal_overlay').hide(); + $('#redactor_modal_overlay').unbind('click', this.modal.close); } - } - else - { - alert('Upload failed!'); - this.modalClose(); - } - } - this.element.attr('action', this.element_action); - this.element.attr('target', ''); + $(document).unbind('keyup', this.hdlModalClose); + this.$editor.unbind('keyup', this.hdlModalClose); - }, + }, this)); - // UTILITY - browser: function(browser) - { - var ua = navigator.userAgent.toLowerCase(); - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || []; - if (browser == 'version') - { - return match[2]; - } + if (this.utils.isMobile.call(this) === false) + { + $(document.body).css('overflow', this.modalSaveBodyOveflow ? this.modalSaveBodyOveflow : 'visible'); + } - if (browser == 'webkit') - { - return (match[1] == 'chrome' || match[1] == 'webkit'); - } + return false; - return match[1] == browser; - }, - oldIE: function() - { - if (this.browser('msie') && parseInt(this.browser('version'), 10) < 9) + }, + setTab: function(num) { - return true; + $('.redactor_tab').hide(); + var tabs = $('#redactor_tabs a'); + tabs.removeClass('redactor_tabs_act'); + tabs.eq(num-1).addClass('redactor_tabs_act'); + $('#redactor_tab' + num).show(); } - - return false; }, - outerHTML: function(s) + + // UPLOAD + upload: { - return $("<p>").append($(s).eq(0).clone()).html(); - }, - normalize: function(str) - { - return parseInt(str.replace('px',''), 10); - }, - isMobile: function(ipad) - { - if (ipad === true && /(iPhone|iPod|iPad|BlackBerry|Android)/.test(navigator.userAgent)) + init: function(el, options) { - return true; - } - else if (/(iPhone|iPod|BlackBerry|Android)/.test(navigator.userAgent)) - { - return true; - } - else - { - return false; - } - } + this.uploadOptions = { + url: false, + success: false, + error: false, + start: false, + trigger: false, + auto: false, + input: false + }; - }; + $.extend(this.uploadOptions, options); + // Test input or form + if ($('#' + el).size() !== 0 && $('#' + el).get(0).tagName === 'INPUT') + { + this.uploadOptions.input = $('#' + el); + this.el = $($('#' + el).get(0).form); + } + else + { + this.el = $('#' + el); + } - // API - $.fn.getObject = function() - { - return this.data('redactor'); - }; + this.element_action = this.el.attr('action'); - $.fn.getEditor = function() - { - return this.data('redactor').$editor; - }; + // Auto or trigger + if (this.uploadOptions.auto) + { + $(this.uploadOptions.input).change($.proxy(function() + { + this.el.submit(function(e) { return false; }); + this.upload.submit.call(this); + }, this)); - $.fn.getCode = function() - { - return $.trim(this.data('redactor').getCode()); - }; + } + else if (this.uploadOptions.trigger) + { + $('#' + this.uploadOptions.trigger).click($.proxy(this.upload.submit, this)); + } + }, + submit : function() + { + this.upload.form.call(this, this.element, this.upload.frame.call(this)); + }, + frame : function() + { + this.id = 'f' + Math.floor(Math.random() * 99999); - $.fn.getText = function() - { - return this.data('redactor').$editor.text(); - }; + var d = this.document.createElement('div'); + var iframe = '<iframe style="display:none" id="'+this.id+'" name="'+this.id+'"></iframe>'; + d.innerHTML = iframe; + $(d).appendTo("body"); - $.fn.getSelected = function() - { - return this.data('redactor').getSelectedHtml(); - }; + // Start + if (this.uploadOptions.start) + { + this.uploadOptions.start(); + } - $.fn.setCode = function(html) - { - this.data('redactor').setCode(html); - }; + $('#' + this.id).load($.proxy(this.upload.loaded, this)); - $.fn.insertHtml = function(html) - { - this.data('redactor').insertHtml(html); - }; - - $.fn.destroyEditor = function() - { - this.each(function() - { - if (typeof $(this).data('redactor') != 'undefined') + return this.id; + }, + form : function(f, name) { - $(this).data('redactor').destroy(); - $(this).removeData('redactor'); - } - }); - }; + if (this.uploadOptions.input) + { + var formId = 'redactorUploadForm' + this.id; + var fileId = 'redactorUploadFile' + this.id; + this.form = $('<form action="' + this.uploadOptions.url + '" method="POST" target="' + name + '" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>'); - $.fn.setFocus = function() - { - this.data('redactor').$editor.focus(); - }; + // append hidden fields + if (this.opts.uploadFields !== false && typeof this.opts.uploadFields === 'object') + { + $.each(this.opts.uploadFields, $.proxy(function(k,v) + { + if (v.toString().indexOf('#') === 0) + { + v = $(v).val(); + } - $.fn.execCommand = function(cmd, param) - { - this.data('redactor').execCommand(cmd, param); - }; + var hidden = $('<input/>', {'type': "hidden", 'name': k, 'value': v}); + $(this.form).append(hidden); -})(jQuery); + }, this)); + } -/* - Plugin Drag and drop Upload v1.0.2 - http://imperavi.com/ - Copyright 2012, Imperavi Inc. -*/ -(function($){ + var oldElement = this.uploadOptions.input; + var newElement = $(oldElement).clone(); + $(oldElement).attr('id', fileId); + $(oldElement).before(newElement); + $(oldElement).appendTo(this.form); + $(this.form).css('position', 'absolute'); + $(this.form).css('top', '-2000px'); + $(this.form).css('left', '-2000px'); + $(this.form).appendTo('body'); - "use strict"; + this.form.submit(); + } + else + { + f.attr('target', name); + f.attr('method', 'POST'); + f.attr('enctype', 'multipart/form-data'); + f.attr('action', this.uploadOptions.url); - // Initialization - $.fn.dragupload = function(options) - { - return this.each(function() { - var obj = new Construct(this, options); - obj.init(); - }); - }; + this.element.submit(); + } - // Options and variables - function Construct(el, options) { + }, + loaded : function() + { + var i = $('#' + this.id)[0]; + var d; - this.opts = $.extend({ + if (i.contentDocument) d = i.contentDocument; + else if (i.contentWindow) d = i.contentWindow.document; + else d = window.frames[this.id].document; - url: false, - success: false, - error: false, - preview: false, - uploadFields: false, + // Success + if (this.uploadOptions.success) + { + if (typeof d !== 'undefined') + { + // Remove bizarre <pre> tag wrappers around our json data: + var rawString = d.body.innerHTML; + var jsonString = rawString.match(/\{(.|\n)*\}/)[0]; + jsonString = jsonString.replace(/^\[/, ''); + jsonString = jsonString.replace(/\]$/, ''); + var json = $.parseJSON(jsonString); - text: RLANG.drop_file_here, - atext: RLANG.or_choose + if (typeof json.error == 'undefined') + { + this.uploadOptions.success(json); + } + else + { + this.uploadOptions.error(this, json); + this.modal.close.call(this); + } + } + else + { + this.modal.close.call(this); + alert('Upload failed!'); + } + } - }, options); + this.element.attr('action', this.element_action); + this.element.attr('target', ''); - this.$el = $(el); - } + } + }, - // Functionality - Construct.prototype = { - init: function() + // DRAGUPLOAD + dragupload: { - if (navigator.userAgent.search("MSIE") === -1) + init: function(el, options) { + this.draguploadOptions = $.extend({ + + url: false, + success: false, + error: false, + preview: false, + uploadFields: false, + + text: RLANG.drop_file_here, + atext: RLANG.or_choose + + }, options); + + if (window.FormData === undefined) + { + return false; + } + this.droparea = $('<div class="redactor_droparea"></div>'); - this.dropareabox = $('<div class="redactor_dropareabox">' + this.opts.text + '</div>'); - this.dropalternative = $('<div class="redactor_dropalternative">' + this.opts.atext + '</div>'); + this.dropareabox = $('<div class="redactor_dropareabox">' + this.draguploadOptions.text + '</div>'); + this.dropalternative = $('<div class="redactor_dropalternative">' + this.draguploadOptions.atext + '</div>'); this.droparea.append(this.dropareabox); - this.$el.before(this.droparea); - this.$el.before(this.dropalternative); + $(el).before(this.droparea); + $(el).before(this.dropalternative); // drag over - this.dropareabox.bind('dragover', $.proxy(function() { return this.ondrag(); }, this)); + this.dropareabox.bind('dragover', $.proxy(function() { return this.dragupload.ondrag.call(this); }, this)); // drag leave - this.dropareabox.bind('dragleave', $.proxy(function() { return this.ondragleave(); }, this)); + this.dropareabox.bind('dragleave', $.proxy(function() { return this.dragupload.ondragleave.call(this); }, this)); var uploadProgress = $.proxy(function(e) { var percent = parseInt(e.loaded / e.total * 100, 10); this.dropareabox.text('Loading ' + percent + '%'); @@ -4081,23 +5389,23 @@ } var provider = function () { return xhr; }; // drop - this.dropareabox.get(0).ondrop = $.proxy(function(event) + this.dropareabox.get(0).ondrop = $.proxy(function(e) { - event.preventDefault(); + e.preventDefault(); this.dropareabox.removeClass('hover').addClass('drop'); - var file = event.dataTransfer.files[0]; + var file = e.dataTransfer.files[0]; var fd = new FormData(); // append hidden fields - if (this.opts.uploadFields !== false && typeof this.opts.uploadFields === 'object') + if (this.draguploadOptions.uploadFields !== false && typeof this.draguploadOptions.uploadFields === 'object') { - $.each(this.opts.uploadFields, $.proxy(function(k,v) + $.each(this.draguploadOptions.uploadFields, $.proxy(function(k,v) { if (v.toString().indexOf('#') === 0) { v = $(v).val(); } @@ -4109,102 +5417,284 @@ // append file data fd.append('file', file); $.ajax({ - url: this.opts.url, + url: this.draguploadOptions.url, dataType: 'html', data: fd, xhr: provider, cache: false, contentType: false, processData: false, type: 'POST', success: $.proxy(function(data) { + data = data.replace(/^\[/, ''); + data = data.replace(/\]$/, ''); + var json = $.parseJSON(data); if (typeof json.error == 'undefined') { - this.opts.success(json); + this.draguploadOptions.success(json); } else { - this.opts.error(this, json); - this.opts.success(false); + this.draguploadOptions.error(this, json); + this.draguploadOptions.success(false); } }, this) }); }, this); + + }, + ondrag: function() + { + this.dropareabox.addClass('hover'); + return false; + }, + ondragleave: function() + { + this.dropareabox.removeClass('hover'); + return false; } }, - ondrag: function() + + // UTILITIES + utils: { - this.dropareabox.addClass('hover'); - return false; - }, - ondragleave: function() - { - this.dropareabox.removeClass('hover'); - return false; - } - }; + browser: function(browser) + { + var ua = navigator.userAgent.toLowerCase(); + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || []; -})(jQuery); + if (browser == 'version') + { + return match[2]; + } + if (browser == 'webkit') + { + return (match[1] == 'chrome' || match[1] == 'webkit'); + } + return match[1] == browser; + }, + outerHtml: function(s) + { + return $('<div>').append($(s).eq(0).clone()).html(); + }, + getObjectText: function(obj) + { + return obj.textContent ? obj.textContent : obj.innerText; + }, + oldIE: function() + { + if (this.utils.browser.call(this, 'msie') && parseInt(this.utils.browser.call(this, 'version'), 10) < 9) + { + return true; + } -// Define: Linkify plugin from stackoverflow -(function($){ + return false; + }, + normalize: function(str) + { + return parseInt(str.replace('px',''), 10); + }, + removeEmptyStyleAttr: function(el) + { + if ($(el).attr('style') == '') $(el).removeAttr('style'); + }, + extractBlockContentsFromCaret: function() + { + var sel = this.window.getSelection(); + if (sel.rangeCount) + { + var selRange = sel.getRangeAt(0); + var blockEl = this.selection.getBlock.call(this, selRange.endContainer); + if (blockEl) + { + var range = selRange.cloneRange(); + range.selectNodeContents(blockEl); + range.setStart(selRange.endContainer, selRange.endOffset); + return range.extractContents(); + } + } + }, + getFragmentHtml: function (fragment) + { + var cloned = fragment.cloneNode(true); + var div = this.document.createElement('div'); + div.appendChild(cloned); + return div.innerHTML; + }, + extractContent: function() + { + var node = this.$editor.get(0); + var frag = this.document.createDocumentFragment(), child; + while ((child = node.firstChild)) + { + frag.appendChild(child); + } - "use strict"; + return frag; + }, + isParentRedactor: function(el) + { + if (!el) return false; + if (this.opts.iframe) return el; - var protocol = 'http://'; - var url1 = /(^|&lt;|\s)(www\..+?\..+?)(\s|&gt;|$)/g, - url2 = /(^|&lt;|\s)(((https?|ftp):\/\/|mailto:).+?)(\s|&gt;|$)/g, + if ($(el).parents('div.redactor_editor').size() == 0 || $(el).hasClass('redactor_editor')) return false; + else return el; + }, + isEndOfElement: function() + { + var current = this.selection.getBlock.call(this); - linkifyThis = function () - { - var childNodes = this.childNodes, - i = childNodes.length; - while(i--) + var offset = this.selection.caretOffset.call(this, current); + var text = $.trim($(current).text()).replace(/\n\r\n/g, ''); + + var len = text.length; + + if (offset == len) return true; + else return false; + }, + + // Mobile + isMobile: function(ipad) { - var n = childNodes[i]; - if (n.nodeType === 3) + if (ipad === true && /(iPhone|iPod|iPad|BlackBerry|Android)/.test(navigator.userAgent)) { - var html = n.nodeValue; - if (html) - { - html = html.replace(/&/g, '&amp;') - .replace(/</g, '&lt;') - .replace(/>/g, '&gt;') - .replace(url1, '$1<a href="' + protocol + '$2">$2</a>$3') - .replace(url2, '$1<a href="$2">$2</a>$5'); - - $(n).after(html).remove(); - } + return true; } - else if (n.nodeType === 1 && !/^(a|button|textarea)$/i.test(n.tagName)) + else if (/(iPhone|iPod|BlackBerry|Android)/.test(navigator.userAgent)) { - linkifyThis.call(n); + return true; } + else + { + return false; + } } - }; + } + }; + +})(jQuery); + +(function($) { + "use strict"; + var protocol = 'http://'; + var url1 = /(^|&lt;|\s)(www\..+?\..+?)(\s|&gt;|$)/g, url2 = /(^|&lt;|\s)(((https?|ftp):\/\/|mailto:).+?)(\s|&gt;|$)/g, + linkifyThis = function () + { + var childNodes = this.childNodes, i = childNodes.length; + while (i--) + { + var n = childNodes[i]; + if (n.nodeType === 3) + { + var html = n.nodeValue; + + if (html && (html.match(url1) || html.match(url2))) + { + html = html.replace(/&/g, '&amp;') + .replace(/</g, '&lt;') + .replace(/>/g, '&gt;') + .replace(url1, '$1<a href="' + protocol + '$2">$2</a>$3') + .replace(url2, '$1<a href="$2">$2</a>$5'); + + $(n).after(html).remove(); + } + } + else if (n.nodeType === 1 && !/^(a|button|textarea)$/i.test(n.tagName)) + { + linkifyThis.call(n); + } + } + }; + $.fn.linkify = function () { this.each(linkifyThis); }; })(jQuery); + /* jQuery plugin textselect * version: 0.9 * author: Josef Moravec, josef.moravec@gmail.com * updated: Imperavi Inc. * */ -eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(5($){$.1.4.7={t:5(0,v){$(2).0("8",c);$(2).0("r",0);$(2).l(\'g\',$.1.4.7.b)},u:5(0){$(2).w(\'g\',$.1.4.7.b)},b:5(1){9 0=$(2).0("r");9 3=$.1.4.7.f(0).h();6(3!=\'\'){$(2).0("8",x);1.j="7";1.3=3;$.1.i.m(2,k)}},f:5(0){9 3=\'\';6(q.e){3=q.e()}o 6(d.e){3=d.e()}o 6(d.p){3=d.p.B().3}A 3}};$.1.4.a={t:5(0,v){$(2).0("n",0);$(2).0("8",c);$(2).l(\'g\',$.1.4.a.b);$(2).l(\'D\',$.1.4.a.s)},u:5(0){$(2).w(\'g\',$.1.4.a.b)},b:5(1){6($(2).0("8")){9 0=$(2).0("n");9 3=$.1.4.7.f(0).h();6(3==\'\'){$(2).0("8",c);1.j="a";$.1.i.m(2,k)}}},s:5(1){6($(2).0("8")){9 0=$(2).0("n");9 3=$.1.4.7.f(0).h();6((1.y=z)&&(3==\'\')){$(2).0("8",c);1.j="a";$.1.i.m(2,k)}}}}})(C);',40,40,'data|event|this|text|special|function|if|textselect|textselected|var|textunselect|handler|false|rdocument|getSelection|getSelectedText|mouseup|toString|handle|type|arguments|bind|apply|rttt|else|selection|rwindow|ttt|handlerKey|setup|teardown|namespaces|unbind|true|keyCode|27|return|createRange|jQuery|keyup'.split('|'),0,{})) +(function ($) { + $.event.special.textselect = { + setup: function (data, namespaces) { + $(this).data("textselected", false); + $(this).data("ttt", data); + $(this).bind('mouseup', $.event.special.textselect.handler) + }, + teardown: function (data) { + $(this).unbind('mouseup', $.event.special.textselect.handler) + }, + handler: function (event) { + var data = $(this).data("ttt"); + var text = $.event.special.textselect.getSelectedText(data).toString(); + if (text != '') { + $(this).data("textselected", true); + event.type = "textselect"; + event.text = text; + $.event.dispatch.apply(this, arguments) + } + }, + getSelectedText: function (data) { + var text = ''; + if (rwindow.getSelection) { + text = rwindow.getSelection() + } else if (rdocument.getSelection) { + text = rdocument.getSelection() + } else if (rdocument.selection) { + text = rdocument.selection.createRange().text + } + return text + } + }; + $.event.special.textunselect = { + setup: function (data, namespaces) { + $(this).data("rttt", data); + $(this).data("textselected", false); + $(this).bind('mouseup', $.event.special.textunselect.handler); + $(this).bind('keyup', $.event.special.textunselect.handlerKey) + }, + teardown: function (data) { + $(this).unbind('mouseup', $.event.special.textunselect.handler) + }, + handler: function (event) { + if ($(this).data("textselected")) { + var data = $(this).data("rttt"); + var text = $.event.special.textselect.getSelectedText(data).toString(); + if (text == '') { + $(this).data("textselected", false); + event.type = "textunselect"; + $.event.dispatch.apply(this, arguments) + } + } + }, + handlerKey: function (event) { + if ($(this).data("textselected")) { + var data = $(this).data("rttt"); + var text = $.event.special.textselect.getSelectedText(data).toString(); + if ((event.keyCode = 27) && (text == '')) { + $(this).data("textselected", false); + event.type = "textunselect"; + $.event.dispatch.apply(this, arguments) + } + } + } + } +})(jQuery); \ No newline at end of file