diff --git a/lib/generators/redactor/templates/config.js b/lib/generators/redactor/templates/config.js index d2255d33..27095598 100644 --- a/lib/generators/redactor/templates/config.js +++ b/lib/generators/redactor/templates/config.js @@ -1,5 +1,4 @@ -$(document).ready( - function(){ +window.init_redactor = function(){ var csrf_token = $('meta[name=csrf-token]').attr('content'); var csrf_param = $('meta[name=csrf-param]').attr('content'); var params; @@ -9,9 +8,15 @@ $(document).ready( $('.redactor').redactor( { "imageUpload":"/redactor_rails/pictures?" + params, "imageGetJson":"/redactor_rails/pictures", - "fileUpload":"/redactor_rails/documents?" + params, - "fileGetJson":"/redactor_rails/documents", "path":"/assets/redactor-rails", "css":"style.css"} ); +} + +$(document).ready(function(){ + window.init_redactor(); +}); + +$(window).bind('page:load',function(){ + window.init_redactor(); }); diff --git a/vendor/assets/javascripts/redactor-rails.js b/vendor/assets/javascripts/redactor-rails.js new file mode 100644 index 00000000..6ef0f1c4 --- /dev/null +++ b/vendor/assets/javascripts/redactor-rails.js @@ -0,0 +1 @@ +//= require redactor-rails/index diff --git a/vendor/assets/javascripts/redactor-rails/config.js b/vendor/assets/javascripts/redactor-rails/config.js index 7a2d8843..27095598 100644 --- a/vendor/assets/javascripts/redactor-rails/config.js +++ b/vendor/assets/javascripts/redactor-rails/config.js @@ -13,5 +13,10 @@ window.init_redactor = function(){ ); } -$(document).ready( window.init_redactor ); -$(window).bind('page:change', window.init_redactor); +$(document).ready(function(){ + window.init_redactor(); +}); + +$(window).bind('page:load',function(){ + window.init_redactor(); +}); diff --git a/vendor/assets/javascripts/redactor-rails/redactor.js b/vendor/assets/javascripts/redactor-rails/redactor.js index 11f98916..e628999e 100755 --- a/vendor/assets/javascripts/redactor-rails/redactor.js +++ b/vendor/assets/javascripts/redactor-rails/redactor.js @@ -1,6 +1,6 @@ /* - Redactor v9.1.4 - Updated: Sep 10, 2013 + Redactor v9.1.7 + Updated: Nov 5, 2013 http://imperavi.com/redactor/ @@ -13,7 +13,6 @@ (function($) { var uuid = 0; - var rtePaste = false; "use strict"; @@ -72,7 +71,7 @@ } $.Redactor = Redactor; - $.Redactor.VERSION = '9.1.4'; + $.Redactor.VERSION = '9.1.7'; $.Redactor.opts = { // settings @@ -94,12 +93,14 @@ pastePlainText: false, removeEmptyTags: true, templateVars: false, + xhtml: false, visual: true, focus: false, tabindex: false, autoresize: true, minHeight: false, + maxHeight: false, shortcuts: true, autosave: false, // false or url @@ -107,16 +108,19 @@ plugins: false, // array - linkAnchor: false, - linkEmail: false, + linkAnchor: true, + linkEmail: true, linkProtocol: 'http://', linkNofollow: false, + linkSize: 50, imageFloatMargin: '10px', imageGetJson: false, // url (ex. /folder/images.json ) or false imageUpload: false, // url + imageUploadParam: 'file', // input name fileUpload: false, // url + fileUploadParam: 'file', // input name clipboardUpload: true, // or false clipboardUploadUrl: false, // url dragUpload: true, // false @@ -298,6 +302,7 @@ init: function(el, options) { + this.rtePaste = false; this.$element = this.$source = $(el); this.uuid = uuid++; @@ -343,7 +348,18 @@ // setup formatting permissions if (this.opts.linebreaks === false) { - if (this.opts.allowedTags !== false && $.inArray('p', this.opts.allowedTags) === '-1') this.opts.allowedTags.push('p'); + if (this.opts.allowedTags !== false) + { + var arrSearch = ['strong', 'em', 'del']; + var arrAdd = ['b', 'i', 'strike']; + + if ($.inArray('p', this.opts.allowedTags) === '-1') this.opts.allowedTags.push('p'); + + for (i in arrSearch) + { + if ($.inArray(arrSearch[i], this.opts.allowedTags) != '-1') this.opts.allowedTags.push(arrAdd[i]); + } + } if (this.opts.deniedTags !== false) { @@ -365,7 +381,7 @@ this.buildStart(); }, - initToolbar: function(lang) + toolbarInit: function(lang) { return { html: @@ -680,7 +696,7 @@ if (this.opts.air) { - $('.redactor_air').remove(); + $('#redactor_air_' + this.uuid).remove(); } }, @@ -703,10 +719,9 @@ }, getToolbar: function() { - return this.$toolbar; + return (this.$toolbar) ? this.$toolbar : false; }, - // CODE GET & SET get: function() { @@ -736,7 +751,7 @@ html = this.cleanSavePreCode(html); html = this.cleanStripTags(html); html = this.cleanConvertProtected(html); - html = this.cleanConvertInlineTags(html); + html = this.cleanConvertInlineTags(html, true); if (this.opts.linebreaks === false) html = this.cleanConverters(html); else html = html.replace(/([\w\W]*?)<\/p>/gi, '$2
'); @@ -745,6 +760,10 @@ html = this.cleanEmpty(html); this.$editor.html(html); + + // set no editable + this.setNonEditable(); + this.setSpansVerified(); this.sync(); }, setCodeIframe: function(html) @@ -766,6 +785,9 @@ this.$editor = this.$frame.contents().find('body').attr({ 'contenteditable': true, 'dir': this.opts.direction }); } + // set no editable + this.setNonEditable(); + this.setSpansVerified(); this.sync(); }, @@ -777,8 +799,41 @@ // set code this.$editor.html(html); + + // set no editable + this.setNonEditable(); + this.setSpansVerified(); this.sync(); }, + setSpansVerified: function() + { + var spans = this.$editor.find('span'); + var replacementTag = 'inline'; + + $.each(spans, function() { + var outer = this.outerHTML; + + // Replace opening tag + var regex = new RegExp('<' + this.tagName, 'i'); + var newTag = outer.replace(regex, '<' + replacementTag); + + // Replace closing tag + regex = new RegExp('/, ''); + return html.replace(/<\/span>/, ''); + }, + setNonEditable: function() + { + this.$editor.find('.noneditable').attr('contenteditable', false); + }, // SYNC sync: function() @@ -791,7 +846,7 @@ else html = this.$editor.html(); html = this.syncClean(html); - html = this.cleanRemoveSpaces(html); + //html = this.cleanRemoveSpaces(html); html = this.cleanRemoveEmptyTags(html); // fix second level up ul, ol @@ -799,8 +854,16 @@ if ($.trim(html) === '
') html = ''; - if (html !== '' && this.opts.tidyHtml) html = this.cleanHtml(html); - html = html.replace(/
/gi, '
'); + // xhtml + if (this.opts.xhtml) + { + var xhtmlTags = ['br', 'hr', 'img', 'link', 'input', 'meta']; + $.each(xhtmlTags, function(i,s) + { + html = html.replace(new RegExp('<' + s + '(.*?[^\/$]?)>', 'gi'), '<' + s + '$1 />'); + }); + + } // before callback html = this.callback('syncBefore', false, html); @@ -841,6 +904,9 @@ html = html.replace('', '?>'); + // revert no editable + html = html.replace(/<(.*?)class="noeditable"(.*?) contenteditable="false"(.*?)>/gi, '<$1class="noeditable"$2$3>'); + html = html.replace(/ data-tagblock=""/gi, ''); html = html.replace(/\n?<\/(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)>/gi, ''); @@ -849,20 +915,16 @@ html = html.replace(/(.*?)<\/span>/i, ''); html = html.replace(/(.*?)<\/span>/i, ''); + // remove font + html = html.replace(/([\w\W]*?)<\/font>/gi, '$2'); + // remove spans - html = html.replace(/([\w\W]*?)<\/span>/gi, '$1'); - html = html.replace(/([\w\W]*?)<\/span>/gi, '$3'); - html = html.replace(/([\w\W]*?)<\/span>/gi, '$3' ); - html = html.replace(/([\w\W]*?)<\/span>/gi, '$1'); - html = html.replace(/([\w\W]*?)<\/span>/gi, ''); - html = html.replace(/([\w\W]*?)<\/span>/gi, '$1'); - html = html.replace(/([\w\W]*?)<\/span>/gi, '$3'); - html = html.replace(/([\w\W]*?)<\/span>/gi, '$3' ); - html = html.replace(/([\w\W]*?)<\/span>/gi, '$1'); + html = html.replace(/([\w\W]*?)<\/span>/gi, '$2'); + html = html.replace(/([\w\W]*?)<\/inline>/gi, '$2'); + html = html.replace(/([\w\W]*?)<\/span>/gi, ''); // amp fix - html = html.replace(/;amp;/gi, ';'); - + html = html.replace(/&/gi, '&'); html = this.cleanReConvertProtected(html); @@ -972,6 +1034,7 @@ // options if (this.opts.tabindex) $source.attr('tabindex', this.opts.tabindex); if (this.opts.minHeight) $source.css('min-height', this.opts.minHeight + 'px'); + if (this.opts.maxHeight) $source.css('max-height', this.opts.maxHeight + 'px'); if (this.opts.wym) this.$editor.addClass('redactor_editor_wym'); if (!this.opts.autoresize) $source.css('height', this.sourceHeight); }, @@ -982,7 +1045,7 @@ // load toolbar if (this.opts.toolbar) { - this.opts.toolbar = this.initToolbar(this.opts.curLang); + this.opts.toolbar = this.toolbarInit(this.opts.curLang); this.toolbarBuild(); } @@ -1029,7 +1092,7 @@ }, buildBindKeyboard: function() { - if (this.opts.dragUpload) + if (this.opts.dragUpload && this.opts.imageUpload !== false) { this.$editor.on('drop.redactor', $.proxy(this.buildEventDrop, this)); } @@ -1038,8 +1101,6 @@ this.$editor.on('keydown.redactor', $.proxy(this.buildEventKeydown, this)); this.$editor.on('keyup.redactor', $.proxy(this.buildEventKeyup, this)); - - // textarea callback if ($.isFunction(this.opts.textareaKeydownCallback)) { @@ -1052,22 +1113,27 @@ this.$editor.on('focus.redactor', $.proxy(this.opts.focusCallback, this)); } + var clickedElement; + $(document).mousedown(function(e) { + clickedElement = $(e.target); + }); + // blur callback - this.$editor.on('blur.redactor', $.proxy(function() + this.$editor.on('blur.redactor', $.proxy(function(e) { - this.selectall = false; + if (!$(clickedElement).hasClass('redactor_toolbar') && $(clickedElement).parents('.redactor_toolbar').size() == 0) + { + this.selectall = false; + if ($.isFunction(this.opts.blurCallback)) this.callback('blur', e); + } }, this)); - if ($.isFunction(this.opts.blurCallback)) - { - this.$editor.on('blur.redactor', $.proxy(this.opts.blurCallback, this)); - } }, buildEventDrop: function(e) { e = e.originalEvent || e; - if (window.FormData === undefined) return true; + if (window.FormData === undefined || !e.dataTransfer) return true; var length = e.dataTransfer.files.length; if (length == 0) return true; @@ -1086,7 +1152,14 @@ var progress = $('
'); $(document.body).append(progress); - this.dragUploadAjax(this.opts.imageUpload, file, true, progress, e); + if (this.opts.s3 === false) + { + this.dragUploadAjax(this.opts.imageUpload, file, true, progress, e, this.opts.imageUploadParam); + } + else + { + this.s3uploadFile(file); + } }, buildEventPaste: function(e) @@ -1108,7 +1181,7 @@ if (this.opts.cleanup) { - rtePaste = true; + this.rtePaste = true; this.selectionSave(); @@ -1169,7 +1242,7 @@ }, buildEventKeydown: function(e) { - if (rtePaste) return false; + if (this.rtePaste) return false; var key = e.which; var ctrl = e.ctrlKey || e.metaKey; @@ -1192,8 +1265,17 @@ // down if (key === this.keyCode.DOWN) { - if (parent && $(parent).get(0).tagName === 'BLOCKQUOTE') this.insertAfterLastElement(parent); - if (current && $(current).get(0).tagName === 'BLOCKQUOTE') this.insertAfterLastElement(current); + if (parent && $(parent)[0].tagName === 'BLOCKQUOTE') this.insertAfterLastElement(parent); + if (current && $(current)[0].tagName === 'BLOCKQUOTE') this.insertAfterLastElement(current); + + if (parent && $(parent)[0].tagName === 'P' && $(parent).parent()[0].tagName == 'BLOCKQUOTE') + { + this.insertAfterLastElement(parent, $(parent).parent()[0]); + } + if (current && $(current)[0].tagName === 'P' && parent && $(parent)[0].tagName == 'BLOCKQUOTE') + { + this.insertAfterLastElement(current, parent); + } } // shortcuts setup @@ -1224,7 +1306,7 @@ if (key == this.keyCode.ENTER && !e.shiftKey && !e.ctrlKey && !e.metaKey ) { // In ie, opera in the tables are created paragraphs, fix it. - if (parent.nodeType == 1 && (parent.tagName == 'TD' || parent.tagName == 'TH')) + if (this.browser('msie') && (parent.nodeType == 1 && (parent.tagName == 'TD' || parent.tagName == 'TH'))) { e.preventDefault(); this.bufferSet(); @@ -1234,21 +1316,7 @@ } // pre - if (pre === true) - { - e.preventDefault(); - this.bufferSet(); - var html = $(current).parent().text(); - this.insertNode(document.createTextNode('\n')); - if (html.search(/\s$/) == -1) - { - this.insertNode(document.createTextNode('\n')); - } - - this.sync(); - this.callback('enter', e); - return false; - } + if (pre === true) return this.buildEventKeydownPre(e, current); else { if (!this.opts.linebreaks) @@ -1328,57 +1396,75 @@ } // tab - if (key === this.keyCode.TAB && this.opts.shortcuts ) + if (key === this.keyCode.TAB && this.opts.shortcuts) return this.buildEventKeydownTab(e, pre); + + // delete zero-width space before the removing + if (key === this.keyCode.BACKSPACE) this.buildEventKeydownBackspace(current); + + }, + buildEventKeydownPre: function(e, current) + { + e.preventDefault(); + this.bufferSet(); + var html = $(current).parent().text(); + this.insertNode(document.createTextNode('\n')); + if (html.search(/\s$/) == -1) { - if (!this.opts.tabFocus) return true; - if (this.isEmpty(this.get())) return true; + this.insertNode(document.createTextNode('\n')); + } - e.preventDefault(); + this.sync(); + this.callback('enter', e); + return false; + }, + buildEventKeydownTab: function(e, pre) + { + if (!this.opts.tabFocus) return true; + if (this.isEmpty(this.get())) return true; - if (pre === true && !e.shiftKey) - { - this.bufferSet(); - this.insertNode(document.createTextNode('\t')); - this.sync(); - return false; + e.preventDefault(); - } - else if (this.opts.tabSpaces !== false) - { - this.bufferSet(); - this.insertNode(document.createTextNode(Array(this.opts.tabSpaces + 1).join('\u00a0'))); - this.sync(); - return false; - } - else - { - if (!e.shiftKey) this.indentingIndent(); - else this.indentingOutdent(); - } + if (pre === true && !e.shiftKey) + { + this.bufferSet(); + this.insertNode(document.createTextNode('\t')); + this.sync(); + return false; + } + else if (this.opts.tabSpaces !== false) + { + this.bufferSet(); + this.insertNode(document.createTextNode(Array(this.opts.tabSpaces + 1).join('\u00a0'))); + this.sync(); return false; } + else + { + if (!e.shiftKey) this.indentingIndent(); + else this.indentingOutdent(); + } - // delete zero-width space before the removing - if (key === this.keyCode.BACKSPACE) + return false; + }, + buildEventKeydownBackspace: function(current) + { + if (typeof current.tagName !== 'undefined' && /^(H[1-6])$/i.test(current.tagName)) { - if (typeof current.tagName !== 'undefined' && /^(H[1-6])$/i.test(current.tagName)) - { - var node; - if (this.opts.linebreaks === false) node = $('

' + this.opts.invisibleSpace + '

'); - else node = $('
' + this.opts.invisibleSpace); + var node; + if (this.opts.linebreaks === false) node = $('

' + this.opts.invisibleSpace + '

'); + else node = $('
' + this.opts.invisibleSpace); - $(current).replaceWith(node); - this.selectionStart(node); - } + $(current).replaceWith(node); + this.selectionStart(node); + } - if (typeof current.nodeValue !== 'undefined' && current.nodeValue !== null) + if (typeof current.nodeValue !== 'undefined' && current.nodeValue !== null) + { + //var value = $.trim(current.nodeValue.replace(/[^\u0000-\u1C7F]/g, '')); + if (current.remove && current.nodeType === 3 && current.nodeValue.match(/[^/\u200B]/g) == null) { - var value = $.trim(current.nodeValue.replace(/[^\u0000-\u1C7F]/g, '')); - if (current.remove && current.nodeType === 3 && current.nodeValue.charCodeAt(0) == 8203 && value == '') - { - current.remove(); - } + current.remove(); } } }, @@ -1392,7 +1478,7 @@ }, buildEventKeyup: function(e) { - if (rtePaste) return false; + if (this.rtePaste) return false; var key = e.which; var parent = this.getParent(); @@ -1414,17 +1500,11 @@ // convert links if ((this.opts.convertLinks || this.opts.convertImageLinks || this.opts.convertVideoLinks) && key === this.keyCode.ENTER) { - this.formatLinkify(this.opts.linkProtocol, this.opts.convertLinks, this.opts.convertImageLinks, this.opts.convertVideoLinks); - - setTimeout($.proxy(function() - { - if (this.opts.convertImageLinks) this.observeImages(); - if (this.opts.observeLinks) this.observeLinks(); - }, this), 5); + this.buildEventKeyupConverters(); } // if empty - if (this.opts.linebreaks === false && (key === this.keyCode.DELETE || key === this.keyCode.BACKSPACE)) + if (key === this.keyCode.DELETE || key === this.keyCode.BACKSPACE) { return this.formatEmpty(e); } @@ -1432,6 +1512,16 @@ this.callback('keyup', e); this.sync(); }, + buildEventKeyupConverters: function() + { + this.formatLinkify(this.opts.linkProtocol, this.opts.convertLinks, this.opts.convertImageLinks, this.opts.convertVideoLinks, this.opts.linkSize); + + setTimeout($.proxy(function() + { + if (this.opts.convertImageLinks) this.observeImages(); + if (this.opts.observeLinks) this.observeLinks(); + }, this), 5); + }, buildPlugins: function() { if (!this.opts.plugins ) return; @@ -1576,6 +1666,10 @@ // place the cursor inside emptyHtml this.selectionStart(this.$editor.children()[0]); } + else + { + this.focus(); + } this.sync(); }, @@ -1657,89 +1751,95 @@ sel.addRange(range); }, - // TOGGLE toggle: function(direct) { - var html; - if (this.opts.visual) + if (this.opts.visual) this.toggleCode(direct); + else this.toggleVisual(); + }, + toggleVisual: function() + { + var html = this.$source.hide().val(); + + if (typeof this.modified !== 'undefined') { - if (direct !== false) this.selectionSave(); + this.modified = this.cleanRemoveSpaces(this.modified, false) !== this.cleanRemoveSpaces(html, false); + } - var height = null; - if (this.opts.iframe) - { - height = this.$frame.height(); - if (this.opts.fullpage) this.$editor.removeAttr('contenteditable'); - this.$frame.hide(); - } + if (this.modified) + { + // don't remove the iframe even if cleared all. + if (this.opts.fullpage && html === '') this.setFullpageOnInit(html); else { - height = this.$editor.innerHeight(); - this.$editor.hide(); + this.set(html); + if (this.opts.fullpage) this.buildBindKeyboard(); } + } - html = this.$source.val(); - this.modified = html; + if (this.opts.iframe) this.$frame.show(); + else this.$editor.show(); - this.$source.height(height).show().focus(); + if (this.opts.fullpage) this.$editor.attr('contenteditable', true ); - // textarea indenting - this.$source.on('keydown.redactor-textarea-indenting', function (e) - { - if (e.keyCode === 9) - { - var $el = $(this); - var start = $el.get(0).selectionStart; - $el.val($el.val().substring(0, start) + "\t" + $el.val().substring($el.get(0).selectionEnd)); - $el.get(0).selectionStart = $el.get(0).selectionEnd = start + 1; - return false; - } - }); + this.$source.off('keydown.redactor-textarea-indenting'); + + this.$editor.focus(); + this.selectionRestore(); - this.buttonInactiveVisual(); - this.buttonActive('html'); - this.opts.visual = false; + this.observeStart(); + this.buttonActiveVisual(); + this.buttonInactive('html'); + this.opts.visual = true; + }, + toggleCode: function(direct) + { + if (direct !== false) this.selectionSave(); + var height = null; + if (this.opts.iframe) + { + height = this.$frame.height(); + if (this.opts.fullpage) this.$editor.removeAttr('contenteditable'); + this.$frame.hide(); } else { - html = this.$source.hide().val(); - - if (typeof this.modified !== 'undefined') - { - this.modified = this.cleanRemoveSpaces(this.modified, false) !== this.cleanRemoveSpaces(html, false); - } + height = this.$editor.innerHeight(); + this.$editor.hide(); + } - if (this.modified) - { - // don't remove the iframe even if cleared all. - if (this.opts.fullpage && html === '') this.setFullpageOnInit(html); - else - { - this.set(html); - if (this.opts.fullpage) this.buildBindKeyboard(); - } - } + var html = this.$source.val(); - if (this.opts.iframe) this.$frame.show(); - else this.$editor.show(); + // tidy html + if (html !== '' && this.opts.tidyHtml) + { + this.$source.val(this.cleanHtml(html)); + } - if (this.opts.fullpage ) this.$editor.attr('contenteditable', true ); + this.modified = html; - this.$source.off('keydown.redactor-textarea-indenting'); + this.$source.height(height).show().focus(); - this.$editor.focus(); - this.selectionRestore(); + // textarea indenting + this.$source.on('keydown.redactor-textarea-indenting', this.textareaIndenting); - this.observeStart(); - this.buttonActiveVisual(); - this.buttonInactive('html'); - this.opts.visual = true; + this.buttonInactiveVisual(); + this.buttonActive('html'); + this.opts.visual = false; + }, + textareaIndenting: function(e) + { + if (e.keyCode === 9) + { + var $el = $(this); + var start = $el.get(0).selectionStart; + $el.val($el.val().substring(0, start) + "\t" + $el.val().substring($el.get(0).selectionEnd)); + $el.get(0).selectionStart = $el.get(0).selectionEnd = start + 1; + return false; } }, - // AUTOSAVE autosave: function() { @@ -1904,7 +2004,6 @@ var text = this.getSelectionText(); if (e.type === 'mouseup' && text != '') this.airShow(e); - if (e.type === 'keyup' && e.shiftKey && text != '') { var $focusElem = $(this.getElement(this.getSelection().focusNode)), offset = $focusElem.offset(); @@ -2113,7 +2212,7 @@ // BUTTONS buttonBuild: function(btnName, btnObject) { - var $button = $(''); + var $button = $(''); $button.on('click', $.proxy(function(e) { @@ -2369,128 +2468,138 @@ return; } - // stop formatting pre - if (this.currentOrParentIs('PRE') && !this.opts.formattingPre) - { - return false; - } - - if (cmd === 'insertunorderedlist' || cmd === 'insertorderedlist') - { - this.bufferSet(); + // Stop formatting pre + if (this.currentOrParentIs('PRE') && !this.opts.formattingPre) return false; - var parent = this.getParent(); - var $list = $(parent).closest('ol, ul'); - var remove = false; + // Lists + if (cmd === 'insertunorderedlist' || cmd === 'insertorderedlist') return this.execLists(cmd, param); - if ($list.length) - { - remove = true; - var listTag = $list[0].tagName; - if ((cmd === 'insertunorderedlist' && listTag === 'OL') - || (cmd === 'insertorderedlist' && listTag === 'UL')) - { - remove = false; - } - } + // Unlink + if (cmd === 'unlink') return this.execUnlink(cmd, param); - this.selectionSave(); + // Usual exec + this.exec(cmd, param, sync); - // remove lists - if (remove) - { - var nodes = this.getNodes(); - var elems = this.getBlocks(nodes); + // Line + if (cmd === 'inserthorizontalrule') this.$editor.find('hr').removeAttr('id'); - if (typeof nodes[0] != 'undefined' && nodes.length > 1 && nodes[0].nodeType == 3) - { - // fix the adding the first li to the array - elems.unshift(this.getBlock()); - } + }, + execUnlink: function(cmd, param) + { + this.bufferSet(); - var data = '', replaced = ''; - $.each(elems, $.proxy(function(i,s) - { - if (s.tagName == 'LI') - { - var $s = $(s); - var cloned = $s.clone(); - cloned.find('ul', 'ol').remove(); + var link = this.currentOrParentIs('A'); + if (link) + { + $(link).replaceWith($(link).text()); - if (this.opts.linebreaks === false) data += this.outerHtml($('

').append(cloned.contents())); - else data += cloned.html() + '
'; + this.sync(); + this.callback('execCommand', cmd, param); + return; + } + }, + execLists: function(cmd, param) + { + this.bufferSet(); - if (i == 0) - { - $s.addClass('redactor-replaced').empty(); - replaced = this.outerHtml($s); - } - else $s.remove(); - } + var parent = this.getParent(); + var $list = $(parent).closest('ol, ul'); + var remove = false; - }, this)); + if ($list.length) + { + remove = true; + var listTag = $list[0].tagName; + if ((cmd === 'insertunorderedlist' && listTag === 'OL') + || (cmd === 'insertorderedlist' && listTag === 'UL')) + { + remove = false; + } + } - html = this.$editor.html().replace(replaced, '' + data + '<' + listTag + '>'); + this.selectionSave(); - this.$editor.html(html); - this.$editor.find(listTag + ':empty').remove(); + // remove lists + if (remove) + { + var nodes = this.getNodes(); + var elems = this.getBlocks(nodes); + if (typeof nodes[0] != 'undefined' && nodes.length > 1 && nodes[0].nodeType == 3) + { + // fix the adding the first li to the array + elems.unshift(this.getBlock()); } - // insert lists - else + var data = '', replaced = ''; + $.each(elems, $.proxy(function(i,s) { - this.document.execCommand(cmd); - - var parent = this.getParent(); - var $list = $(parent).closest('ol, ul'); - - if ($list.length) + if (s.tagName == 'LI') { - if ((this.browser('msie') || this.browser('mozilla')) && parent.tagName !== 'LI') - { - $(parent).replaceWith($(parent).html()); - } + var $s = $(s); + var cloned = $s.clone(); + cloned.find('ul', 'ol').remove(); - var $listParent = $list.parent(); - if (this.isParentRedactor($listParent) && this.nodeTestBlocks($listParent[0])) + if (this.opts.linebreaks === false) data += this.outerHtml($('

').append(cloned.contents())); + else data += cloned.html() + '
'; + + if (i == 0) { - $listParent.replaceWith($listParent.contents()); + $s.addClass('redactor-replaced').empty(); + replaced = this.outerHtml($s); } + else $s.remove(); } - if (this.browser('mozilla')) this.$editor.focus(); + }, this)); - } + html = this.$editor.html().replace(replaced, '' + data + '<' + listTag + '>'); - this.selectionRestore(); + this.$editor.html(html); + this.$editor.find(listTag + ':empty').remove(); - this.sync(); - this.callback('execCommand', cmd, param); - return; } - if (cmd === 'unlink') + // insert lists + else { - this.bufferSet(); + var firstParent = this.getParent(); + + this.document.execCommand(cmd); + + var parent = this.getParent(); + var $list = $(parent).closest('ol, ul'); - var link = this.currentOrParentIs('A'); - if (link) + if (firstParent && firstParent.tagName == 'TD') { - $(link).replaceWith($(link).text()); + $list.wrapAll(''); + } - this.sync(); - this.callback('execCommand', cmd, param); - return; + + + if ($list.length) + { + if ((this.browser('msie') || this.browser('mozilla')) && parent.tagName !== 'LI') + { + $(parent).replaceWith($(parent).html()); + } + + var $listParent = $list.parent(); + if (this.isParentRedactor($listParent) && this.nodeTestBlocks($listParent[0])) + { + $listParent.replaceWith($listParent.contents()); + } } - } - this.exec(cmd, param, sync); + if (this.browser('mozilla')) this.$editor.focus(); - if (cmd === 'inserthorizontalrule') - { - this.$editor.find('hr').removeAttr('id'); } + + this.selectionRestore(); + + this.sync(); + this.callback('execCommand', cmd, param); + return; }, // INDENTING @@ -2512,7 +2621,6 @@ this.selectionSave(); - if (block && block.tagName == 'LI') { // li @@ -2637,6 +2745,8 @@ this.selectionRestore(); } + this.sync(); + }, insideOutdent: function (li, index, elems) { @@ -2664,8 +2774,84 @@ } }, - // CLEAN - cleanEmpty: function(html) + // ALIGNMENT + alignmentLeft: function() + { + this.alignmentSet('', 'JustifyLeft'); + }, + alignmentRight: function() + { + this.alignmentSet('right', 'JustifyRight'); + }, + alignmentCenter: function() + { + this.alignmentSet('center', 'JustifyCenter'); + }, + alignmentJustify: function() + { + this.alignmentSet('justify', 'JustifyFull'); + }, + alignmentSet: function(type, cmd) + { + this.bufferSet(); + + if (this.oldIE()) + { + this.document.execCommand(cmd, false, false); + return true; + } + + this.selectionSave(); + + var block = this.getBlock(); + if (!block && this.opts.linebreaks) + { + // one element + this.exec('formatBlock', 'blockquote'); + var newblock = this.getBlock(); + var block = $('

').html($(newblock).html()); + $(newblock).replaceWith(block); + + $(block).css('text-align', type); + this.removeEmptyAttr(block, 'style'); + + if (type == '' && typeof($(block).data('tagblock')) !== 'undefined') + { + $(block).replaceWith($(block).html()); + } + } + else + { + var elements = this.getBlocks(); + $.each(elements, $.proxy(function(i, elem) + { + var $el = false; + + if ($.inArray(elem.tagName, this.opts.alignmentTags) !== -1) + { + $el = $(elem); + } + else + { + $el = $(elem).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]); + } + + if ($el) + { + $el.css('text-align', type); + this.removeEmptyAttr($el, 'style'); + } + + }, this)); + } + + this.selectionRestore(); + + this.sync(); + }, + + // CLEAN + cleanEmpty: function(html) { var ph = this.placeholderStart(html); if (ph !== false) return ph; @@ -2802,9 +2988,10 @@ html = html + "\n"; + var safes = []; var matches = html.match(/<(table|div|pre|object)(.*?)>([\w\W]*?)<\/(table|div|pre|object)>/gi); - if (matches === null) matches = []; + if (!matches) matches = []; var commentsMatches = html.match(//gi); if (commentsMatches) matches = $.merge(matches, commentsMatches); @@ -2812,10 +2999,7 @@ if (this.opts.phpTags) { var phpMatches = html.match(/([\w\W]*?)<\/section>/gi); - if (phpMatches) - { - matches = $.merge(matches, phpMatches); - } + if (phpMatches) matches = $.merge(matches, phpMatches); } if (matches) @@ -2857,18 +3041,6 @@ } } - // blockquote - if (html.search(/
([\w\W]*?)<\/blockquote>/gi), function(i,s) - { - var str = ''; - str = s.replace('

', ''); - str = str.replace('

', '
'); - html = html.replace(s, str); - }); - } - html = R('

\s*

', 'gi', ''); html = R('

([^<]+)', 'gi', "

$1

"); html = R('

\s*(]*>)\s*

', 'gi', "$1"); @@ -2900,7 +3072,7 @@ return $.trim(html); }, - cleanConvertInlineTags: function(html) + cleanConvertInlineTags: function(html, set) { var boldTag = 'strong'; if (this.opts.boldTag === 'b') boldTag = 'b'; @@ -2918,11 +3090,13 @@ if (this.opts.italicTag === 'em') html = html.replace(/([\w\W]*?)<\/i>/gi, '$1'); else html = html.replace(/([\w\W]*?)<\/em>/gi, '$1'); - html = html.replace(/([\w\W]*?)<\/strike>/gi, '$1'); - - if (!/([\w\W]*?)<\/span>/gi.test(html)) + if (set !== true) + { + html = html.replace(/([\w\W]*?)<\/strike>/gi, '$1'); + } + else { - html = html.replace(/([\w\W]*?)<\/span>/gi, '$3'); + html = html.replace(/([\w\W]*?)<\/del>/gi, '$1'); } return html; @@ -2975,21 +3149,16 @@ cleanUnverified: function() { // label, abbr, mark, meter, code, q, dfn, ins, time, kbd, var - var $elem = this.$editor.find('li, img, a, b, strong, sub, sup, i, em, u, small, strike, del, span, cite'); - $elem.not('[data-redactor="verified"]').filter('[style*="font-size"][style*="line-height"]') - .css('font-size', '') - .css('line-height', ''); - - $elem.not('[data-redactor="verified"]').filter('[style*="background-color: transparent;"][style*="line-height"]') + $elem.filter('[style*="background-color: transparent;"][style*="line-height"]') .css('background-color', '') .css('line-height', ''); - $elem.not('[data-redactor="verified"]').filter('[style*="background-color: transparent;"]') + $elem.filter('[style*="background-color: transparent;"]') .css('background-color', ''); - $elem.not('[data-redactor="verified"]').css('line-height', ''); + $elem.css('line-height', ''); $.each($elem, $.proxy(function(i,s) { @@ -3181,97 +3350,33 @@ return out; }, - // ALIGNMENT - alignmentLeft: function() - { - this.alignmentSet('', 'JustifyLeft'); - }, - alignmentRight: function() - { - this.alignmentSet('right', 'JustifyRight'); - }, - alignmentCenter: function() - { - this.alignmentSet('center', 'JustifyCenter'); - }, - alignmentJustify: function() - { - this.alignmentSet('justify', 'JustifyFull'); - }, - alignmentSet: function(type, cmd) + // FORMAT + formatEmpty: function(e) { - this.bufferSet(); - - if (this.oldIE()) - { - this.document.execCommand(cmd, false, false); - return true; - } - - this.selectionSave(); + var html = $.trim(this.$editor.html()); - var block = this.getBlock(); - if (!block && this.opts.linebreaks) + if (this.opts.linebreaks) { - // one element - this.exec('formatBlock', 'blockquote'); - var newblock = this.getBlock(); - var block = $('
').html($(newblock).html()); - $(newblock).replaceWith(block); - - $(block).css('text-align', type); - this.removeEmptyAttr(block, 'style'); - - if (type == '' && typeof($(block).data('tagblock')) !== 'undefined') + if (html == '') { - $(block).replaceWith($(block).html()); + e.preventDefault(); + this.$editor.html(''); + this.focus(); } } else { - var elements = this.getBlocks(); - $.each(elements, $.proxy(function(i, elem) - { - var $el = false; - - if ($.inArray(elem.tagName, this.opts.alignmentTags) !== -1) - { - $el = $(elem); - } - else - { - $el = $(elem).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]); - } - - if ($el) - { - $el.css('text-align', type); - this.removeEmptyAttr($el, 'style'); - } - - }, this)); - } - - this.selectionRestore(); - - this.sync(); - }, - - // FORMAT - formatEmpty: function(e) - { - var html = $.trim(this.$editor.html()); + html = html.replace(//i, ''); + var thtml = html.replace(/

\s?<\/p>/gi, ''); - html = html.replace(//i, ''); - var thtml = html.replace(/

\s?<\/p>/gi, ''); - - if (html === '' || thtml === '') - { - e.preventDefault(); + if (html === '' || thtml === '') + { + e.preventDefault(); - var node = $(this.opts.emptyHtml).get(0); - this.$editor.html(node); - this.focus(); + var node = $(this.opts.emptyHtml).get(0); + this.$editor.html(node); + this.focus(); + } } this.sync(); @@ -3287,7 +3392,26 @@ { if (node.tagName !== 'LI') { - this.formatBlock(tag, node); + var parent = $(node).parent(); + + if (tag === 'p') + { + if ((node.tagName === 'P' + && parent.size() != 0 + && parent[0].tagName === 'BLOCKQUOTE') + || + node.tagName === 'BLOCKQUOTE') + { + this.formatQuote(); + return; + } + else if (this.opts.linebreaks) return; + + } + else + { + this.formatBlock(tag, node); + } } }, this)); @@ -3348,25 +3472,109 @@ { this.bufferSet(); + // paragraphy if (this.opts.linebreaks === false) { this.selectionSave(); var blocks = this.getBlocks(); + var blockquote = false; + var blocksLen = blocks.length; if (blocks) { + var data = ''; + var replaced = ''; + var replace = false; + var paragraphsOnly = true; + + $.each(blocks, function(i,s) + { + if (s.tagName !== 'P') paragraphsOnly = false; + }); + $.each(blocks, $.proxy(function(i,s) { if (s.tagName === 'BLOCKQUOTE') { this.formatBlock('p', s, false); } + else if (s.tagName === 'P') + { + blockquote = $(s).parent(); + // from blockquote + if (blockquote[0].tagName == 'BLOCKQUOTE') + { + var count = $(blockquote).children('p').size(); + + // one + if (count == 1) + { + $(blockquote).replaceWith(s); + } + // all + else if (count == blocksLen) + { + replace = 'blockquote'; + data += this.outerHtml(s); + } + // some + else + { + replace = 'html'; + data += this.outerHtml(s); + + if (i == 0) + { + $(s).addClass('redactor-replaced').empty(); + replaced = this.outerHtml(s); + } + else $(s).remove(); + } + } + // to blockquote + else + { + if (paragraphsOnly === false || blocks.length == 1) + { + this.formatBlock('blockquote', s, false); + } + else + { + replace = 'paragraphs'; + data += this.outerHtml(s); + } + } + + } else if (s.tagName !== 'LI') { this.formatBlock('blockquote', s, false); } }, this)); + + if (replace) + { + if (replace == 'paragraphs') + { + $(blocks[0]).replaceWith('

' + data + '
'); + $(blocks).remove(); + } + else if (replace == 'blockquote') + { + $(blockquote).replaceWith(data); + } + else if (replace == 'html') + { + var html = this.$editor.html().replace(replaced, '
' + data + '
'); + + this.$editor.html(html); + this.$editor.find('blockquote').each(function() + { + if ($.trim($(this).html()) == '') $(this).remove(); + }) + } + } } this.selectionRestore(); @@ -3379,9 +3587,34 @@ { this.selectionSave(); - $(block).replaceWith($(block).html() + '
'); + var html = $.trim($(block).html()); + var selection = $.trim(this.getSelectionHtml()); + + html = html.replace(//gi, ''); + + if (html == selection) + { + $(block).replaceWith($(block).html() + '
'); + } + else + { + // replace + this.inlineFormat('tmp'); + var tmp = this.$editor.find('tmp'); + tmp.empty(); + + var newhtml = this.$editor.html().replace('', '
' + this.opts.invisibleSpace + '' + selection + '
'); + + this.$editor.html(newhtml); + tmp.remove(); + this.$editor.find('blockquote').each(function() + { + if ($.trim($(this).html()) == '') $(this).remove(); + }) + } this.selectionRestore(); + this.$editor.find('span#selection-marker-1').attr('id', false); } else { @@ -3416,8 +3649,7 @@ this.sync(); }, - - // BLOCK CLASS AND STYLE + // BLOCK blockRemoveAttr: function(attr, value) { var nodes = this.getBlocks(); @@ -3463,7 +3695,7 @@ this.sync(); }, - // INLINE CLASS AND STYLE + // INLINE inlineRemoveClass: function(className) { this.selectionSave(); @@ -3554,14 +3786,14 @@ { var parent = $(s).parent(), el; - if (parent && parent[0].tagName === 'SPAN' && parent[0].attributes.length != 0) + if (parent && parent[0].tagName === 'INLINE' && parent[0].attributes.length != 0) { el = parent; $(s).replaceWith($(s).html()); } else { - el = $('').append($(s).contents()); + el = $('').append($(s).contents()); $(s).replaceWith(el); } @@ -3585,9 +3817,9 @@ $.each(nodes, $.proxy(function(i, node) { - if (!collapsed && node.tagName !== 'SPAN') + if (!collapsed && node.tagName !== 'INLINE') { - if (node.parentNode.tagName === 'SPAN' && !$(node.parentNode).hasClass('redactor_editor')) + if (node.parentNode.tagName === 'INLINE' && !$(node.parentNode).hasClass('redactor_editor')) { node = node.parentNode; } @@ -3599,7 +3831,7 @@ }, inlineUnwrapSpan: function() { - var $spans = this.$editor.find('span[data-redactor-inlineMethods]'); + var $spans = this.$editor.find('inline'); $.each($spans, $.proxy(function(i, span) { @@ -3693,16 +3925,10 @@ html = '

' + html + '

'; } - // in the quote, we can insert text or links only -/* - if (currBlock.tagName == 'BLOCKQUOTE' && html.indexOf(' 1 && currBlock - || $html.contents().is('p, :header, ul, ol, div, table, blockquote, pre, address, section, header, footer, aside, article')) + || $html.contents().is('p, :header, ul, ol, li, div, table, td, blockquote, pre, address, section, header, footer, aside, article')) { if (this.browser('msie')) this.document.selection.createRange().pasteHTML(html); else this.document.execCommand('inserthtml', false, html); @@ -3721,6 +3947,9 @@ this.observeStart(); + // set no editable + this.setNonEditable(); + if (sync !== false) this.sync(); }, insertHtmlAdvanced: function(html, sync) @@ -3815,13 +4044,27 @@ } }, - insertAfterLastElement: function(element) + insertAfterLastElement: function(element, parent) { + if (typeof(parent) != 'undefined') element = parent; + if (this.isEndOfElement()) { - - if ($($.trim(this.$editor.html())).get(0) != $.trim(element) - && this.$editor.contents().last()[0] !== element) return false; + if (this.opts.linebreaks) + { + var contents = $('
').append($.trim(this.$editor.html())).contents(); + if (this.outerHtml(contents.last()[0]) != this.outerHtml(element)) + { + return false; + } + } + else + { + if (this.$editor.contents().last()[0] !== element) + { + return false; + } + } this.bufferSet(); @@ -3837,6 +4080,7 @@ $(element).after(node); $(node).after(this.opts.invisibleSpace); this.selectionRestore(); + this.$editor.find('span#selection-marker-1').removeAttr('id'); } } }, @@ -3874,6 +4118,7 @@ tmp.innerHTML = html; html = tmp.textContent || tmp.innerText; + html = $.trim(html); html = html.replace('\n', '
'); html = this.cleanParagraphy(html); @@ -3889,9 +4134,15 @@ return true; } + // ms word list - html = html.replace(//gi, '
    '); - html = html.replace(//gi, '
'); + html = html.replace(//gi, '
    '); + html = html.replace(//gi, ''); + html = html.replace(//gi, '
'); + // one line + html = html.replace(//gi, '
'); + // remove ms word's bullet + html = html.replace(/·/g, ''); // remove comments and php tags html = html.replace(/|<\?(?:php)?[\s\S]*?\?>/gi, ''); @@ -3911,6 +4162,7 @@ html = html.replace(/<\/td>/gi, '[td]'); html = html.replace(/ <\/td>/gi, '[td]'); html = html.replace(/
<\/td>/gi, '[td]'); + html = html.replace(/([\w\W]*?)<\/td>/gi, '[td colspan="$2"]$4[/td]'); html = html.replace(/([\w\W]*?)<\/a>/gi, '[a href="$2"]$4[/a]'); html = html.replace(/([\w\W]*?)<\/iframe>/gi, '[iframe$1]$2[/iframe]'); html = html.replace(/([\w\W]*?)<\/video>/gi, '[video$1]$2[/video]'); @@ -3918,7 +4170,6 @@ html = html.replace(/([\w\W]*?)<\/embed>/gi, '[embed$1]$2[/embed]'); html = html.replace(/([\w\W]*?)<\/object>/gi, '[object$1]$2[/object]'); html = html.replace(//gi, '[param$1]'); - html = html.replace(//gi, '[img$1$3]'); html = html.replace(//gi, '[img$1]'); // remove classes @@ -3929,10 +4180,10 @@ // remove empty html = html.replace(/<[^\/>][^>]*>(\s*|\t*|\n*| |
)<\/[^>]+>/gi, ''); - html = html.replace(/
\s*?\t*?\n*?(
    |
      |

      )/gi, '$1'); // revert + html = html.replace(/\[td colspan="(.*?)"\]([\w\W]*?)\[\/td\]/gi, '$2'); html = html.replace(/\[td\]/gi, ' '); html = html.replace(/\[a href="(.*?)"\]([\w\W]*?)\[\/a\]/gi, '$2'); html = html.replace(/\[iframe(.*?)\]([\w\W]*?)\[\/iframe\]/gi, '$2'); @@ -3988,6 +4239,13 @@ // remove safari local images html = html.replace(//gi, ''); + // remove p in td + html = html.replace(/(\s*|\t*|\n*)

      ([\w\W]*?)<\/p>(\s*|\t*|\n*)<\/td>/gi, '$3'); + + // remove divs + html = html.replace(/([\w\W]*?)<\/div>/gi, '$2'); + html = html.replace(/([\w\W]*?)<\/div>/gi, '$2'); + // FF specific this.pasteClipboardMozilla = false; if (this.browser('mozilla')) @@ -4013,8 +4271,8 @@ } } - // bullets - html = html.replace(/

      •([\w\W]*?)<\/p>/gi, '

    1. $1
    2. '); + // bullets again + html = html.replace(/

      •([\w\W]*?)<\/p>/gi, '

    3. $1
    4. '); // ie inserts a blank font tags when pasting while (/([\w\W]*?)<\/font>/gi.test(html)) @@ -4051,7 +4309,7 @@ this.selectall = false; setTimeout($.proxy(function() { - rtePaste = false; + this.rtePaste = false; // FF specific if (this.browser('mozilla')) @@ -4068,7 +4326,6 @@ if (this.opts.autoresize) $(this.document.body).scrollTop(this.saveScroll); else this.$editor.scrollTop(this.saveScroll); }, - pasteClipboardUploadMozilla: function() { var imgs = this.$editor.find('img[data-mozilla-paste-image]'); @@ -4085,7 +4342,7 @@ }, $.proxy(function(data) { - var json = $.parseJSON(data); + var json = (typeof data === 'string' ? $.parseJSON(data) : data); $s.attr('src', json.filelink); $s.removeAttr('data-mozilla-paste-image'); @@ -4113,7 +4370,7 @@ }, $.proxy(function(data) { - var json = $.parseJSON(data); + var json = (typeof data === 'string' ? $.parseJSON(data) : data); var html = ''; this.execCommand('inserthtml', html, false); @@ -4187,12 +4444,10 @@ setTimeout($.proxy(this.observeStart, this), 4); }, - // OBSERVE observeStart: function() { this.observeImages(); - this.observeTables(); if (this.opts.observeLinks) this.observeLinks(); }, @@ -4205,10 +4460,6 @@ }, this)); }, - observeTables: function() - { - this.$editor.find('table').on('click', $.proxy(this.tableObserver, this)); - }, observeImages: function() { if (this.opts.observeImages === false) return false; @@ -4277,7 +4528,6 @@ $('.redactor-link-tooltip').remove(); }, - // SELECTION getSelection: function() { @@ -4294,7 +4544,7 @@ { if (this.document.getSelection) { - var sel = this.document.getSelection(); + var sel = this.getSelection(); if (sel.getRangeAt && sel.rangeCount) return sel.getRangeAt(0); } @@ -4385,177 +4635,67 @@ }, getCaretOffsetRange: function() { - return new Range(this.getSelection().getRangeAt(0)); - }, - setCaret: function (el, start, end) - { - if (typeof end === 'undefined') end = start; - el = el[0] || el; - - var range = this.getRange(); - range.selectNodeContents(el); - - var textNodes = this.getTextNodesIn(el); - var foundStart = false; - var charCount = 0, endCharCount; - - if (textNodes.length == 1 && start) - { - range.setStart(textNodes[0], start); - range.setEnd(textNodes[0], end); - } - else - { - 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 (foundStart && end <= endCharCount) - { - range.setEnd( textNode, end - charCount ); - break; - } - - charCount = endCharCount; - } - } - - var sel = this.getSelection(); - sel.removeAllRanges(); - sel.addRange( range ); - }, - getTextNodesIn: function (node) - { - var textNodes = []; - - if (node.nodeType == 3) textNodes.push(node); - else - { - var children = node.childNodes; - for (var i = 0, len = children.length; i < len; ++i) - { - textNodes.push.apply(textNodes, this.getTextNodesIn(children[i])); - } - } - - return textNodes; - }, - - // SAVE & RESTORE - selectionSave: function() - { - if (!this.isFocused()) this.$editor.focus(); - - if (!this.opts.rangy) - { - this.selectionCreateMarker(this.getRange()); - } - // rangy - else - { - this.savedSel = rangy.saveSelection(); - } - }, - selectionCreateMarker: function(range, remove) - { - if (!range) return; - - var node1 = $('' + this.opts.invisibleSpace + '', this.document)[0]; - var node2 = $('' + this.opts.invisibleSpace + '', this.document)[0]; - - if (range.collapsed === true) - { - this.selectionSetMarker(range, node1, true); - } - else - { - this.selectionSetMarker(range, node1, true); - this.selectionSetMarker(range, node2, false); - } - - this.savedSel = this.$editor.html(); - - this.selectionRestore(false, false); - }, - selectionSetMarker: function(range, node, type) - { - var boundaryRange = range.cloneRange(); - - boundaryRange.collapse(type); - - boundaryRange.insertNode(node); - boundaryRange.detach(); - }, - selectionRestore: function(replace, remove) - { - if (!this.opts.rangy) - { - if (replace === true && this.savedSel) - { - this.$editor.html(this.savedSel); - } - - var node1 = this.$editor.find('span#selection-marker-1'); - var node2 = this.$editor.find('span#selection-marker-2'); - - if (this.browser('mozilla')) - { - this.$editor.focus(); - } - else if (!this.isFocused()) - { - this.$editor.focus(); - } + return new Range(this.getSelection().getRangeAt(0)); + }, + setCaret: function (el, start, end) + { + if (typeof end === 'undefined') end = start; + el = el[0] || el; - if (node1.length != 0 && node2.length != 0) - { - this.selectionSet(node1[0], 0, node2[0], 0); - } - else if (node1.length != 0) - { - this.selectionSet(node1[0], 0, null, 0); - } + var range = this.getRange(); + range.selectNodeContents(el); - if (remove !== false) - { - this.selectionRemoveMarkers(); - this.savedSel = false; - } - } - // rangy - else + var textNodes = this.getTextNodesIn(el); + var foundStart = false; + var charCount = 0, endCharCount; + + if (textNodes.length == 1 && start) { - rangy.restoreSelection(this.savedSel); + range.setStart(textNodes[0], start); + range.setEnd(textNodes[0], end); } - }, - selectionRemoveMarkers: function(type) - { - if (!this.opts.rangy) + else { - $.each(this.$editor.find('span.redactor-selection-marker'), function() + for (var i = 0, textNode; textNode = textNodes[i++];) { - var html = $.trim($(this).html().replace(/[^\u0000-\u1C7F]/g, '')); - if (html == '') + endCharCount = charCount + textNode.length; + if (!foundStart && start >= charCount && (start < endCharCount || (start == endCharCount && i < textNodes.length))) { - $(this).remove(); + range.setStart(textNode, start - charCount); + foundStart = true; } - else + + if (foundStart && end <= endCharCount) { - $(this).removeAttr('class').removeAttr('id'); + range.setEnd( textNode, end - charCount ); + break; } - }); + + charCount = endCharCount; + } } - // rangy + + var sel = this.getSelection(); + sel.removeAllRanges(); + sel.addRange( range ); + }, + getTextNodesIn: function (node) + { + var textNodes = []; + + if (node.nodeType == 3) textNodes.push(node); else { - rangy.removeMarkers(this.savedSel); + var children = node.childNodes; + for (var i = 0, len = children.length; i < len; ++i) + { + textNodes.push.apply(textNodes, this.getTextNodesIn(children[i])); + } } + + return textNodes; }, + // GET ELEMENTS getCurrent: function() { @@ -4618,47 +4758,8 @@ { return this.rTestBlock.test(tag); }, - getSelectedNodes: function(range) - { - if (typeof range == 'undefined' || range == false) var range = this.getRange() - if (range && range.collapsed === true) - { - return [this.getCurrent()]; - } - - var sel = this.getSelection(); - try { - var frag = sel.getRangeAt(0).cloneContents(); - } - catch(e) - { - return(false); - } - - var tempspan = this.document.createElement("span"); - tempspan.appendChild(frag); - - window.selnodes = tempspan.childNodes; - - var len = selnodes.length; - var output = []; - for(var i = 0, u = len; i' + this.opts.invisibleSpace + '', this.document)[0]; + var node2 = $('' + this.opts.invisibleSpace + '', this.document)[0]; + + if (range.collapsed === true) + { + this.selectionSetMarker(range, node1, true); + } + else + { + this.selectionSetMarker(range, node1, true); + this.selectionSetMarker(range, node2, false); + } + + this.savedSel = this.$editor.html(); + + this.selectionRestore(false, false); + }, + selectionSetMarker: function(range, node, type) + { + var boundaryRange = range.cloneRange(); + + boundaryRange.collapse(type); + + boundaryRange.insertNode(node); + boundaryRange.detach(); + }, + selectionRestore: function(replace, remove) + { + if (!this.opts.rangy) + { + if (replace === true && this.savedSel) + { + this.$editor.html(this.savedSel); + } + + var node1 = this.$editor.find('span#selection-marker-1'); + var node2 = this.$editor.find('span#selection-marker-2'); + + if (this.browser('mozilla')) + { + this.$editor.focus(); + } + else if (!this.isFocused()) + { + this.$editor.focus(); + } + + if (node1.length != 0 && node2.length != 0) + { + this.selectionSet(node1[0], 0, node2[0], 0); + } + else if (node1.length != 0) + { + this.selectionSet(node1[0], 0, null, 0); + } + + if (remove !== false) + { + this.selectionRemoveMarkers(); + this.savedSel = false; + } + } + // rangy + else + { + rangy.restoreSelection(this.savedSel); + } + }, + selectionRemoveMarkers: function(type) + { + if (!this.opts.rangy) + { + $.each(this.$editor.find('span.redactor-selection-marker'), function() + { + var html = $.trim($(this).html().replace(/[^\u0000-\u1C7F]/g, '')); + if (html == '') + { + $(this).remove(); + } + else + { + $(this).removeAttr('class').removeAttr('id'); + } + }); + } + // rangy + else + { + rangy.removeMarkers(this.savedSel); + } + }, + // TABLE tableShow: function() { @@ -4843,20 +5054,13 @@ this.selectionRestore(); var current = this.getBlock() || this.getCurrent(); - if (current && current.tagName != 'BODY') - { - $(current).after(html) - } - else - { - this.insertHtmlAdvanced(html, false); - } + if (current && current.tagName != 'BODY') $(current).after(html) + else this.insertHtmlAdvanced(html, false); this.selectionRestore(); var table = this.$editor.find('#table' + tableId); - this.tableObserver(table); this.buttonActiveObserver(); table.find('span#selection-marker-1').remove(); @@ -4864,92 +5068,98 @@ this.sync(); }, - tableObserver: function(e) - { - this.$table = $(e.target || e).closest('table'); - - this.$tbody = $(e.target).closest('tbody'); - this.$thead = this.$table.find('thead'); - - this.$current_td = $(e.target || this.$table.find('td').first()); - this.$current_tr = $(e.target || this.$table.find('tr').first()).closest('tr'); - }, tableDeleteTable: function() { - this.bufferSet(); + var $table = $(this.getParent()).closest('table'); + if (!this.isParentRedactor($table)) return false; + if ($table.size() == 0) return false; - if (!this.$table) return; + this.bufferSet(); - this.$table.remove(); - this.$table = false; + $table.remove(); this.sync(); - }, tableDeleteRow: function() { - this.bufferSet(); + var $table = $(this.getParent()).closest('table'); + if (!this.isParentRedactor($table)) return false; + if ($table.size() == 0) return false; - if (!this.$current_tr) return; + this.bufferSet(); - // Set the focus correctly - var $focusTR = this.$current_tr.prev().length ? this.$current_tr.prev() : this.$current_tr.next(); - if ($focusTR.length) + var $current_tr = $(this.getParent()).closest('tr'); + var $focus_tr = $current_tr.prev().length ? $current_tr.prev() : $current_tr.next(); + if ($focus_tr.length) { - var $focusTD = $focusTR.children('td' ).first(); - if ($focusTD.length) + var $focus_td = $focus_tr.children('td' ).first(); + if ($focus_td.length) { - $focusTD.prepend('' + this.opts.invisibleSpace + ''); - this.selectionRestore(); + $focus_td.prepend('' + this.opts.invisibleSpace + ''); } } - this.$current_tr.remove(); + $current_tr.remove(); + this.selectionRestore(); this.sync(); }, tableDeleteColumn: function() { + var $table = $(this.getParent()).closest('table'); + if (!this.isParentRedactor($table)) return false; + if ($table.size() == 0) return false; + this.bufferSet(); - var index = this.$current_td.get(0).cellIndex; + + var $current_td = $(this.getParent()).closest('td'); + var index = $current_td.get(0).cellIndex; // Set the focus correctly - this.$table.find('tr').each($.proxy(function(i, elem) + $table.find('tr').each($.proxy(function(i, elem) { var focusIndex = index - 1 < 0 ? index + 1 : index - 1; if (i === 0) { $(elem).find('td').eq(focusIndex).prepend('' + this.opts.invisibleSpace + ''); - this.selectionRestore(); } $(elem).find('td').eq(index).remove(); }, this)); + this.selectionRestore(); this.sync(); }, tableAddHead: function() { + var $table = $(this.getParent()).closest('table'); + if (!this.isParentRedactor($table)) return false; + if ($table.size() == 0) return false; + this.bufferSet(); - if (this.$table.find('thead').size() !== 0) this.tableDeleteHead(); + if ($table.find('thead').size() !== 0) this.tableDeleteHead(); else { - var tr = this.$table.find('tr').first().clone(); - tr.find('td').html( this.opts.invisibleSpace ); - this.$thead = $(''); - this.$thead.append(tr); - this.$table.prepend(this.$thead); + var tr = $table.find('tr').first().clone(); + tr.find('td').html(this.opts.invisibleSpace); + $thead = $(''); + $thead.append(tr); + $table.prepend($thead); this.sync(); } }, tableDeleteHead: function() { - this.bufferSet(); + var $table = $(this.getParent()).closest('table'); + if (!this.isParentRedactor($table)) return false; + var $thead = $table.find('thead'); + + if ($thead.size() == 0) return false; - $(this.$thead).remove(); - this.$thead = false; + this.bufferSet(); + $thead.remove(); this.sync(); }, tableAddRowAbove: function() @@ -4970,29 +5180,41 @@ }, tableAddRow: function(type) { + var $table = $(this.getParent()).closest('table'); + if (!this.isParentRedactor($table)) return false; + if ($table.size() == 0) return false; + this.bufferSet(); - var new_tr = this.$current_tr.clone(); + var $current_tr = $(this.getParent()).closest('tr'); + var new_tr = $current_tr.clone(); new_tr.find('td').html(this.opts.invisibleSpace); - if (type === 'after') this.$current_tr.after(new_tr); - else this.$current_tr.before(new_tr); + if (type === 'after') $current_tr.after(new_tr); + else $current_tr.before(new_tr); this.sync(); }, tableAddColumn: function (type) { + var $table = $(this.getParent()).closest('table'); + if (!this.isParentRedactor($table)) return false; + if ($table.size() == 0) return false; + this.bufferSet(); var index = 0; - this.$current_tr.find('td').each($.proxy(function(i, elem) + var $current_tr = $(this.getParent()).closest('tr'); + var $current_td = $(this.getParent()).closest('td'); + + $current_tr.find('td').each($.proxy(function(i, elem) { - if ($(elem)[0] === this.$current_td[0]) index = i; + if ($(elem)[0] === $current_td[0]) index = i; }, this)); - this.$table.find('tr').each($.proxy(function(i, elem) + $table.find('tr').each($.proxy(function(i, elem) { var $current = $(elem).find('td').eq(index); @@ -5193,6 +5415,12 @@ } } + // link tooltip + setTimeout($.proxy(function() + { + if (this.opts.observeLinks) this.observeLinks(); + }, this), 5); + this.modalClose(); }, @@ -5223,7 +5451,8 @@ { this.callback('fileUploadError', json); - }, this) + }, this), + uploadParam: this.opts.fileUploadParam }); } @@ -5366,7 +5595,8 @@ { this.callback('imageUploadError', json); - }, this) + }, this), + uploadParam: this.opts.imageUploadParam }); } } @@ -5460,7 +5690,7 @@ }, this); - this.modalInit(this.opts.curLang.image, this.opts.modal_image_edit, 380, callback); + this.modalInit(this.opts.curLang.edit, this.opts.modal_image_edit, 380, callback); }, imageRemove: function(el) @@ -5566,7 +5796,7 @@ this.$editor.find('#redactor-image-editter, #redactor-image-resizer').remove(); - var margin = imageBox.css('margin'); + var margin = imageBox[0].style.margin; if (margin != '0px') { imageBox.find('img').css('margin', margin); @@ -5717,7 +5947,7 @@ }); imageBox.attr('contenteditable', false); - var margin = $image.css('margin'); + var margin = $image[0].style.margin; if (margin != '0px') { imageBox.css('margin', margin); @@ -5844,7 +6074,7 @@ + '' + '' + '
      ' - + '' + + '' + '
      ' + '' + '', @@ -5864,9 +6094,9 @@ + '' + '' + '
      ' - + '   ' + + '   ' + '' - + '' + + '' + '
      ', modal_image: String() @@ -5881,7 +6111,7 @@ + '
' + '
' + '
' - + '' + + '' + '
' + '