redactor-rails 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 506f5e8fa3b8fe0a6672f9d9ca82821a2aff9456
4
- data.tar.gz: 12de7a67928f900a30f98897427fd5dbb94e046f
3
+ metadata.gz: e3e2f3ecba694ba0ea434016294fd47dc17188db
4
+ data.tar.gz: 22863da6e0eb0d51b48ec7d203325dba2f91bf6a
5
5
  SHA512:
6
- metadata.gz: 1c0d3c1116125b1c3ea044f7e8ba7c598a3135bbf25a4f5ea2a35c9165bfe3469ef1173a6b09dcaec9f0c522a080783b4947a8225f2fc2198359cd2f3334a9ec
7
- data.tar.gz: accce111a0a66f1fc943bc1df95c11d7175d43a5f7413b68bb0e27fe6a4701ae371e2982806934e6423a5bcd5cf4edc691f916860f8843e0e2fdb41925f6b2ee
6
+ metadata.gz: 650e89fd3bf046e8cb2838cfbd4c6b22ea1d5b1e38487cd50398e02d51ad1eb1e6416d383783e9315ff919577831ca8eb21ecf2447d109ccf482b99a6411757f
7
+ data.tar.gz: f1666d988dc35800541d2ec947bf88ea647dd62c208a4cb5e2f9c173964bb3892ea186c59bfcc4aa185da7bea8148ba4e1b020f588ccd0a2ed83053bab9d01cf
@@ -20,7 +20,7 @@ class RedactorRails::DocumentsController < ApplicationController
20
20
  if @document.save
21
21
  render :text => { :filelink => @document.url, :filename => @document.filename }.to_json
22
22
  else
23
- render :nothing => true
23
+ render json: { error: @document.errors }
24
24
  end
25
25
  end
26
26
 
@@ -1,5 +1,5 @@
1
1
  class RedactorRails::PicturesController < ApplicationController
2
- before_filter :redactor_authenticate_user!
2
+ #before_filter :redactor_authenticate_user!
3
3
 
4
4
  def index
5
5
  @pictures = RedactorRails.picture_model.where(
@@ -20,7 +20,7 @@ class RedactorRails::PicturesController < ApplicationController
20
20
  if @picture.save
21
21
  render :text => { :filelink => @picture.url }.to_json
22
22
  else
23
- render :nothing => true
23
+ render json: { error: @picture.errors }
24
24
  end
25
25
  end
26
26
 
@@ -38,7 +38,7 @@ module Redactor
38
38
  end
39
39
  end
40
40
 
41
- def create_migration
41
+ def create_redactor_migration
42
42
  if orm.to_s == "active_record"
43
43
  if ARGV.include?('--devise')
44
44
  migration_template "#{generator_dir}/devise_migration.rb", File.join('db/migrate', "create_redactor_assets.rb")
@@ -1,3 +1,3 @@
1
1
  module RedactorRails
2
- VERSION = "0.4.3"
2
+ VERSION = "0.4.4"
3
3
  end
@@ -3,7 +3,15 @@ if (!RedactorPlugins) var RedactorPlugins = {};
3
3
  RedactorPlugins.fontcolor = {
4
4
  init: function()
5
5
  {
6
- var colors = ['#ffffff', '#000000', '#eeece1', '#1f497d', '#4f81bd', '#c0504d', '#9bbb59', '#8064a2', '#4bacc6', '#f79646', '#ffff00', '#f2f2f2', '#7f7f7f', '#ddd9c3', '#c6d9f0', '#dbe5f1', '#f2dcdb', '#ebf1dd', '#e5e0ec', '#dbeef3', '#fdeada', '#fff2ca', '#d8d8d8', '#595959', '#c4bd97', '#8db3e2', '#b8cce4', '#e5b9b7', '#d7e3bc', '#ccc1d9', '#b7dde8', '#fbd5b5', '#ffe694', '#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'];
6
+ var colors = [
7
+ '#ffffff', '#000000', '#eeece1', '#1f497d', '#4f81bd', '#c0504d', '#9bbb59', '#8064a2', '#4bacc6', '#f79646', '#ffff00',
8
+ '#f2f2f2', '#7f7f7f', '#ddd9c3', '#c6d9f0', '#dbe5f1', '#f2dcdb', '#ebf1dd', '#e5e0ec', '#dbeef3', '#fdeada', '#fff2ca',
9
+ '#d8d8d8', '#595959', '#c4bd97', '#8db3e2', '#b8cce4', '#e5b9b7', '#d7e3bc', '#ccc1d9', '#b7dde8', '#fbd5b5', '#ffe694',
10
+ '#bfbfbf', '#3f3f3f', '#938953', '#548dd4', '#95b3d7', '#d99694', '#c3d69b', '#b2a2c7', '#b7dde8', '#fac08f', '#f2c314',
11
+ '#a5a5a5', '#262626', '#494429', '#17365d', '#366092', '#953734', '#76923c', '#5f497a', '#92cddc', '#e36c09', '#c09100',
12
+ '#7f7f7f', '#0c0c0c', '#1d1b10', '#0f243e', '#244061', '#632423', '#4f6128', '#3f3151', '#31859b', '#974806', '#7f6000'
13
+ ];
14
+
7
15
  var buttons = ['fontcolor', 'backcolor'];
8
16
 
9
17
  this.buttonAddSeparator();
@@ -12,7 +20,7 @@ RedactorPlugins.fontcolor = {
12
20
  {
13
21
  var name = buttons[i];
14
22
 
15
- var $dropdown = $('<div class="redactor_dropdown redactor_dropdown_box_' + name + '" style="display: none; width: 210px;">');
23
+ var $dropdown = $('<div class="redactor_dropdown redactor_dropdown_box_' + name + '" style="display: none; width: 243px;">');
16
24
 
17
25
  this.pickerBuild($dropdown, name, colors);
18
26
  $(this.$toolbar).append($dropdown);
@@ -44,7 +52,7 @@ RedactorPlugins.fontcolor = {
44
52
  {
45
53
  var color = colors[z];
46
54
 
47
- var $swatch = $('<a rel="' + color + '" data-rule="' + rule +'" href="#" style="float: left; font-size: 0; border: 2px solid #fff; padding: 0; margin: 0; width: 15px; height: 15px;"></a>');
55
+ var $swatch = $('<a rel="' + color + '" data-rule="' + rule +'" href="#" style="float: left; font-size: 0; border: 2px solid #fff; padding: 0; margin: 0; width: 20px; height: 20px;"></a>');
48
56
  $swatch.css('background-color', color);
49
57
  $dropdown.append($swatch);
50
58
  $swatch.on('click', onSwatch);
@@ -32,13 +32,11 @@ RedactorPlugins.fullscreen = {
32
32
 
33
33
  this.fsheight = this.$editor.height();
34
34
 
35
+ if (this.opts.maxHeight) this.$editor.css('max-height', '');
35
36
  if (this.opts.iframe) html = this.get();
36
37
 
37
- this.tmpspan = $('<span></span>');
38
- this.$box.addClass('redactor_box_fullscreen').after(this.tmpspan);
39
-
38
+ this.$box.addClass('redactor_box_fullscreen');
40
39
  $('body, html').css('overflow', 'hidden');
41
- $('body').prepend(this.$box);
42
40
 
43
41
  if (this.opts.iframe) this.fullscreenIframe(html);
44
42
 
@@ -62,13 +60,13 @@ RedactorPlugins.fullscreen = {
62
60
  this.$box.removeClass('redactor_box_fullscreen').css({ width: 'auto', height: 'auto' });
63
61
 
64
62
  if (this.opts.iframe) html = this.$editor.html();
65
- this.tmpspan.after(this.$box).remove();
66
63
 
67
64
  if (this.opts.iframe) this.fullscreenIframe(html);
68
65
  else this.sync();
69
66
 
70
67
  var height = this.fsheight;
71
68
  if (this.opts.autoresize) height = 'auto';
69
+ if (this.opts.maxHeight) this.$editor.css('max-height', this.opts.maxHeight);
72
70
 
73
71
  if (this.opts.toolbarExternal)
74
72
  {
@@ -90,10 +88,8 @@ RedactorPlugins.fullscreen = {
90
88
  },
91
89
  fullscreenIframe: function(html)
92
90
  {
93
- this.$editor = this.$frame.contents().find('body').attr({
94
- 'contenteditable': true,
95
- 'dir': this.opts.direction
96
- });
91
+ this.$editor = this.$frame.contents().find('body');
92
+ this.$editor.attr({ 'contenteditable': true, 'dir': this.opts.direction });
97
93
 
98
94
  // set document & window
99
95
  if (this.$editor[0])
@@ -1,6 +1,6 @@
1
1
  /*
2
- Redactor v9.1.4
3
- Updated: Sep 10, 2013
2
+ Redactor v9.1.9
3
+ Updated: Dec 12, 2013
4
4
 
5
5
  http://imperavi.com/redactor/
6
6
 
@@ -13,7 +13,6 @@
13
13
  (function($)
14
14
  {
15
15
  var uuid = 0;
16
- var rtePaste = false;
17
16
 
18
17
  "use strict";
19
18
 
@@ -72,7 +71,7 @@
72
71
  }
73
72
 
74
73
  $.Redactor = Redactor;
75
- $.Redactor.VERSION = '9.1.4';
74
+ $.Redactor.VERSION = '9.1.9';
76
75
  $.Redactor.opts = {
77
76
 
78
77
  // settings
@@ -94,29 +93,35 @@
94
93
  pastePlainText: false,
95
94
  removeEmptyTags: true,
96
95
  templateVars: false,
96
+ xhtml: false,
97
97
 
98
98
  visual: true,
99
99
  focus: false,
100
100
  tabindex: false,
101
101
  autoresize: true,
102
102
  minHeight: false,
103
+ maxHeight: false,
103
104
  shortcuts: true,
104
105
 
105
106
  autosave: false, // false or url
106
107
  autosaveInterval: 60, // seconds
108
+ autosaveFormdata: true,
107
109
 
108
110
  plugins: false, // array
109
111
 
110
- linkAnchor: false,
111
- linkEmail: false,
112
+ linkAnchor: true,
113
+ linkEmail: true,
112
114
  linkProtocol: 'http://',
113
115
  linkNofollow: false,
116
+ linkSize: 50,
114
117
 
115
118
  imageFloatMargin: '10px',
116
119
  imageGetJson: false, // url (ex. /folder/images.json ) or false
117
120
 
118
121
  imageUpload: false, // url
122
+ imageUploadParam: 'file', // input name
119
123
  fileUpload: false, // url
124
+ fileUploadParam: 'file', // input name
120
125
  clipboardUpload: true, // or false
121
126
  clipboardUploadUrl: false, // url
122
127
  dragUpload: true, // false
@@ -297,7 +302,7 @@
297
302
  // Initialization
298
303
  init: function(el, options)
299
304
  {
300
-
305
+ this.rtePaste = false;
301
306
  this.$element = this.$source = $(el);
302
307
  this.uuid = uuid++;
303
308
 
@@ -343,7 +348,18 @@
343
348
  // setup formatting permissions
344
349
  if (this.opts.linebreaks === false)
345
350
  {
346
- if (this.opts.allowedTags !== false && $.inArray('p', this.opts.allowedTags) === '-1') this.opts.allowedTags.push('p');
351
+ if (this.opts.allowedTags !== false)
352
+ {
353
+ var arrSearch = ['strong', 'em', 'del'];
354
+ var arrAdd = ['b', 'i', 'strike'];
355
+
356
+ if ($.inArray('p', this.opts.allowedTags) === '-1') this.opts.allowedTags.push('p');
357
+
358
+ for (i in arrSearch)
359
+ {
360
+ if ($.inArray(arrSearch[i], this.opts.allowedTags) != '-1') this.opts.allowedTags.push(arrAdd[i]);
361
+ }
362
+ }
347
363
 
348
364
  if (this.opts.deniedTags !== false)
349
365
  {
@@ -365,7 +381,7 @@
365
381
  this.buildStart();
366
382
 
367
383
  },
368
- initToolbar: function(lang)
384
+ toolbarInit: function(lang)
369
385
  {
370
386
  return {
371
387
  html:
@@ -680,7 +696,7 @@
680
696
 
681
697
  if (this.opts.air)
682
698
  {
683
- $('.redactor_air').remove();
699
+ $('#redactor_air_' + this.uuid).remove();
684
700
  }
685
701
  },
686
702
 
@@ -703,10 +719,9 @@
703
719
  },
704
720
  getToolbar: function()
705
721
  {
706
- return this.$toolbar;
722
+ return (this.$toolbar) ? this.$toolbar : false;
707
723
  },
708
724
 
709
-
710
725
  // CODE GET & SET
711
726
  get: function()
712
727
  {
@@ -723,28 +738,39 @@
723
738
  set: function(html, strip, placeholderRemove)
724
739
  {
725
740
  html = html.toString();
741
+ html = html.replace(/\$/g, '&#36;');
726
742
 
727
743
  if (this.opts.fullpage) this.setCodeIframe(html);
728
744
  else this.setEditor(html, strip);
729
745
 
746
+ if (html == '') placeholderRemove = false;
730
747
  if (placeholderRemove !== false) this.placeholderRemove();
731
748
  },
732
749
  setEditor: function(html, strip)
733
750
  {
751
+
734
752
  if (strip !== false)
735
753
  {
736
754
  html = this.cleanSavePreCode(html);
755
+
737
756
  html = this.cleanStripTags(html);
738
757
  html = this.cleanConvertProtected(html);
739
- html = this.cleanConvertInlineTags(html);
758
+ html = this.cleanConvertInlineTags(html, true);
740
759
 
741
- if (this.opts.linebreaks === false) html = this.cleanConverters(html);
760
+ if (this.opts.linebreaks === false) html = this.cleanConverters(html);
742
761
  else html = html.replace(/<p(.*?)>([\w\W]*?)<\/p>/gi, '$2<br>');
743
762
  }
744
763
 
764
+ // $ fix
765
+ html = html.replace(/&amp;#36;/g, '$');
766
+
745
767
  html = this.cleanEmpty(html);
746
768
 
747
769
  this.$editor.html(html);
770
+
771
+ // set no editable
772
+ this.setNonEditable();
773
+ this.setSpansVerified();
748
774
  this.sync();
749
775
  },
750
776
  setCodeIframe: function(html)
@@ -766,6 +792,9 @@
766
792
  this.$editor = this.$frame.contents().find('body').attr({ 'contenteditable': true, 'dir': this.opts.direction });
767
793
  }
768
794
 
795
+ // set no editable
796
+ this.setNonEditable();
797
+ this.setSpansVerified();
769
798
  this.sync();
770
799
 
771
800
  },
@@ -777,8 +806,41 @@
777
806
 
778
807
  // set code
779
808
  this.$editor.html(html);
809
+
810
+ // set no editable
811
+ this.setNonEditable();
812
+ this.setSpansVerified();
780
813
  this.sync();
781
814
  },
815
+ setSpansVerified: function()
816
+ {
817
+ var spans = this.$editor.find('span');
818
+ var replacementTag = 'inline';
819
+
820
+ $.each(spans, function() {
821
+ var outer = this.outerHTML;
822
+
823
+ // Replace opening tag
824
+ var regex = new RegExp('<' + this.tagName, 'gi');
825
+ var newTag = outer.replace(regex, '<' + replacementTag);
826
+
827
+ // Replace closing tag
828
+ regex = new RegExp('</' + this.tagName, 'gi');
829
+ newTag = newTag.replace(regex, '</' + replacementTag);
830
+
831
+ $(this).replaceWith(newTag);
832
+ });
833
+
834
+ },
835
+ setSpansVerifiedHtml: function(html)
836
+ {
837
+ html = html.replace(/<span(.*?)>/, '<inline$1>');
838
+ return html.replace(/<\/span>/, '</inline>');
839
+ },
840
+ setNonEditable: function()
841
+ {
842
+ this.$editor.find('.noneditable').attr('contenteditable', false);
843
+ },
782
844
 
783
845
  // SYNC
784
846
  sync: function()
@@ -791,7 +853,7 @@
791
853
  else html = this.$editor.html();
792
854
 
793
855
  html = this.syncClean(html);
794
- html = this.cleanRemoveSpaces(html);
856
+ //html = this.cleanRemoveSpaces(html);
795
857
  html = this.cleanRemoveEmptyTags(html);
796
858
 
797
859
  // fix second level up ul, ol
@@ -799,8 +861,16 @@
799
861
 
800
862
  if ($.trim(html) === '<br>') html = '';
801
863
 
802
- if (html !== '' && this.opts.tidyHtml) html = this.cleanHtml(html);
803
- html = html.replace(/<br>/gi, '<br />');
864
+ // xhtml
865
+ if (this.opts.xhtml)
866
+ {
867
+ var xhtmlTags = ['br', 'hr', 'img', 'link', 'input', 'meta'];
868
+ $.each(xhtmlTags, function(i,s)
869
+ {
870
+ html = html.replace(new RegExp('<' + s + '(.*?[^\/$]?)>', 'gi'), '<' + s + '$1 />');
871
+ });
872
+
873
+ }
804
874
 
805
875
  // before callback
806
876
  html = this.callback('syncBefore', false, html);
@@ -828,7 +898,7 @@
828
898
  // remove space
829
899
  html = html.replace(/&#x200b;/gi, '');
830
900
  html = html.replace(/&#8203;/gi, '');
831
- html = html.replace(/&nbsp;/gi, ' ');
901
+ html = html.replace(/<\/a>&nbsp;/gi, '<\/a> ');
832
902
 
833
903
  // link nofollow
834
904
  if (this.opts.linkNofollow)
@@ -841,6 +911,9 @@
841
911
  html = html.replace('<!--?php', '<?php');
842
912
  html = html.replace('?-->', '?>');
843
913
 
914
+ // revert no editable
915
+ html = html.replace(/<(.*?)class="noeditable"(.*?) contenteditable="false"(.*?)>/gi, '<$1class="noeditable"$2$3>');
916
+
844
917
  html = html.replace(/ data-tagblock=""/gi, '');
845
918
  html = html.replace(/<br\s?\/?>\n?<\/(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)>/gi, '</$1>');
846
919
 
@@ -849,19 +922,21 @@
849
922
  html = html.replace(/<span(.*?)id="redactor-image-resizer"(.*?)>(.*?)<\/span>/i, '');
850
923
  html = html.replace(/<span(.*?)id="redactor-image-editter"(.*?)>(.*?)<\/span>/i, '');
851
924
 
925
+ // remove font
926
+ html = html.replace(/<font(.*?)>([\w\W]*?)<\/font>/gi, '$2');
927
+
852
928
  // remove spans
853
- html = html.replace(/<span\s*?>([\w\W]*?)<\/span>/gi, '$1');
854
- html = html.replace(/<span(.*?)data-redactor="verified"(.*?)>([\w\W]*?)<\/span>/gi, '<span$1$2>$3</span>');
855
- html = html.replace(/<span(.*?)data-redactor-inlineMethods=""(.*?)>([\w\W]*?)<\/span>/gi, '<span$1$2>$3</span>' );
856
- html = html.replace(/<span\s*?>([\w\W]*?)<\/span>/gi, '$1');
857
- html = html.replace(/<span\s*?id="selection-marker(.*?)"(.*?)>([\w\W]*?)<\/span>/gi, '');
858
- html = html.replace(/<span\s*?>([\w\W]*?)<\/span>/gi, '$1');
859
- html = html.replace(/<span(.*?)data-redactor="verified"(.*?)>([\w\W]*?)<\/span>/gi, '<span$1$2>$3</span>');
860
- html = html.replace(/<span(.*?)data-redactor-inlineMethods=""(.*?)>([\w\W]*?)<\/span>/gi, '<span$1$2>$3</span>' );
929
+ html = html.replace(/<span(.*?)>([\w\W]*?)<\/span>/gi, '$2');
930
+ html = html.replace(/<inline>/gi, '<span>');
931
+ html = html.replace(/<inline /gi, '<span ');
932
+ html = html.replace(/<\/inline>/gi, '</span>');
933
+ html = html.replace(/<span(.*?)class="redactor_placeholder"(.*?)>([\w\W]*?)<\/span>/gi, '');
861
934
  html = html.replace(/<span>([\w\W]*?)<\/span>/gi, '$1');
862
935
 
863
- // amp fix
864
- html = html.replace(/;amp;/gi, ';');
936
+ // fixes
937
+ html = html.replace(/&amp;/gi, '&');
938
+ html = html.replace(/™/gi, '&trade;');
939
+ html = html.replace(/©/gi, '&copy;');
865
940
 
866
941
 
867
942
  html = this.cleanReConvertProtected(html);
@@ -972,6 +1047,11 @@
972
1047
  // options
973
1048
  if (this.opts.tabindex) $source.attr('tabindex', this.opts.tabindex);
974
1049
  if (this.opts.minHeight) $source.css('min-height', this.opts.minHeight + 'px');
1050
+ if (this.opts.maxHeight)
1051
+ {
1052
+ this.opts.autoresize = false;
1053
+ this.sourceHeight = this.opts.maxHeight;
1054
+ }
975
1055
  if (this.opts.wym) this.$editor.addClass('redactor_editor_wym');
976
1056
  if (!this.opts.autoresize) $source.css('height', this.sourceHeight);
977
1057
  },
@@ -982,7 +1062,7 @@
982
1062
  // load toolbar
983
1063
  if (this.opts.toolbar)
984
1064
  {
985
- this.opts.toolbar = this.initToolbar(this.opts.curLang);
1065
+ this.opts.toolbar = this.toolbarInit(this.opts.curLang);
986
1066
  this.toolbarBuild();
987
1067
  }
988
1068
 
@@ -1029,7 +1109,9 @@
1029
1109
  },
1030
1110
  buildBindKeyboard: function()
1031
1111
  {
1032
- if (this.opts.dragUpload)
1112
+ this.dblEnter = 0;
1113
+
1114
+ if (this.opts.dragUpload && this.opts.imageUpload !== false)
1033
1115
  {
1034
1116
  this.$editor.on('drop.redactor', $.proxy(this.buildEventDrop, this));
1035
1117
  }
@@ -1038,8 +1120,6 @@
1038
1120
  this.$editor.on('keydown.redactor', $.proxy(this.buildEventKeydown, this));
1039
1121
  this.$editor.on('keyup.redactor', $.proxy(this.buildEventKeyup, this));
1040
1122
 
1041
-
1042
-
1043
1123
  // textarea callback
1044
1124
  if ($.isFunction(this.opts.textareaKeydownCallback))
1045
1125
  {
@@ -1052,22 +1132,27 @@
1052
1132
  this.$editor.on('focus.redactor', $.proxy(this.opts.focusCallback, this));
1053
1133
  }
1054
1134
 
1135
+ var clickedElement;
1136
+ $(document).mousedown(function(e) {
1137
+ clickedElement = $(e.target);
1138
+ });
1139
+
1055
1140
  // blur callback
1056
- this.$editor.on('blur.redactor', $.proxy(function()
1141
+ this.$editor.on('blur.redactor', $.proxy(function(e)
1057
1142
  {
1058
- this.selectall = false;
1143
+ if (!$(clickedElement).hasClass('redactor_toolbar') && $(clickedElement).parents('.redactor_toolbar').size() == 0)
1144
+ {
1145
+ this.selectall = false;
1146
+ if ($.isFunction(this.opts.blurCallback)) this.callback('blur', e);
1147
+ }
1059
1148
  }, this));
1060
- if ($.isFunction(this.opts.blurCallback))
1061
- {
1062
- this.$editor.on('blur.redactor', $.proxy(this.opts.blurCallback, this));
1063
- }
1064
1149
 
1065
1150
  },
1066
1151
  buildEventDrop: function(e)
1067
1152
  {
1068
1153
  e = e.originalEvent || e;
1069
1154
 
1070
- if (window.FormData === undefined) return true;
1155
+ if (window.FormData === undefined || !e.dataTransfer) return true;
1071
1156
 
1072
1157
  var length = e.dataTransfer.files.length;
1073
1158
  if (length == 0) return true;
@@ -1086,7 +1171,14 @@
1086
1171
  var progress = $('<div id="redactor-progress-drag" class="redactor-progress redactor-progress-striped"><div id="redactor-progress-bar" class="redactor-progress-bar" style="width: 100%;"></div></div>');
1087
1172
  $(document.body).append(progress);
1088
1173
 
1089
- this.dragUploadAjax(this.opts.imageUpload, file, true, progress, e);
1174
+ if (this.opts.s3 === false)
1175
+ {
1176
+ this.dragUploadAjax(this.opts.imageUpload, file, true, progress, e, this.opts.imageUploadParam);
1177
+ }
1178
+ else
1179
+ {
1180
+ this.s3uploadFile(file);
1181
+ }
1090
1182
 
1091
1183
  },
1092
1184
  buildEventPaste: function(e)
@@ -1108,13 +1200,13 @@
1108
1200
 
1109
1201
  if (this.opts.cleanup)
1110
1202
  {
1111
- rtePaste = true;
1203
+ this.rtePaste = true;
1112
1204
 
1113
1205
  this.selectionSave();
1114
1206
 
1115
1207
  if (!this.selectall)
1116
1208
  {
1117
- if (this.opts.autoresize === true )
1209
+ if (this.opts.autoresize === true && this.fullscreen !== true)
1118
1210
  {
1119
1211
  this.$editor.height(this.$editor.height());
1120
1212
  this.saveScroll = this.document.body.scrollTop;
@@ -1137,7 +1229,7 @@
1137
1229
  var html = this.getFragmentHtml(pastedFrag);
1138
1230
  this.pasteClean(html);
1139
1231
 
1140
- if (this.opts.autoresize === true) this.$editor.css('height', 'auto');
1232
+ if (this.opts.autoresize === true && this.fullscreen !== true) this.$editor.css('height', 'auto');
1141
1233
 
1142
1234
  }, this), 1);
1143
1235
  }
@@ -1169,7 +1261,7 @@
1169
1261
  },
1170
1262
  buildEventKeydown: function(e)
1171
1263
  {
1172
- if (rtePaste) return false;
1264
+ if (this.rtePaste) return false;
1173
1265
 
1174
1266
  var key = e.which;
1175
1267
  var ctrl = e.ctrlKey || e.metaKey;
@@ -1192,8 +1284,17 @@
1192
1284
  // down
1193
1285
  if (key === this.keyCode.DOWN)
1194
1286
  {
1195
- if (parent && $(parent).get(0).tagName === 'BLOCKQUOTE') this.insertAfterLastElement(parent);
1196
- if (current && $(current).get(0).tagName === 'BLOCKQUOTE') this.insertAfterLastElement(current);
1287
+ if (parent && $(parent)[0].tagName === 'BLOCKQUOTE') this.insertAfterLastElement(parent);
1288
+ if (current && $(current)[0].tagName === 'BLOCKQUOTE') this.insertAfterLastElement(current);
1289
+
1290
+ if (parent && $(parent)[0].tagName === 'P' && $(parent).parent()[0].tagName == 'BLOCKQUOTE')
1291
+ {
1292
+ this.insertAfterLastElement(parent, $(parent).parent()[0]);
1293
+ }
1294
+ if (current && $(current)[0].tagName === 'P' && parent && $(parent)[0].tagName == 'BLOCKQUOTE')
1295
+ {
1296
+ this.insertAfterLastElement(current, parent);
1297
+ }
1197
1298
  }
1198
1299
 
1199
1300
  // shortcuts setup
@@ -1224,7 +1325,7 @@
1224
1325
  if (key == this.keyCode.ENTER && !e.shiftKey && !e.ctrlKey && !e.metaKey )
1225
1326
  {
1226
1327
  // In ie, opera in the tables are created paragraphs, fix it.
1227
- if (parent.nodeType == 1 && (parent.tagName == 'TD' || parent.tagName == 'TH'))
1328
+ if (this.browser('msie') && (parent.nodeType == 1 && (parent.tagName == 'TD' || parent.tagName == 'TH')))
1228
1329
  {
1229
1330
  e.preventDefault();
1230
1331
  this.bufferSet();
@@ -1233,22 +1334,49 @@
1233
1334
  return false;
1234
1335
  }
1235
1336
 
1236
- // pre
1237
- if (pre === true)
1337
+ // blockquote exit
1338
+ if (block && (block.tagName == 'BLOCKQUOTE' || $(block).parent()[0].tagName == 'BLOCKQUOTE'))
1238
1339
  {
1239
- e.preventDefault();
1240
- this.bufferSet();
1241
- var html = $(current).parent().text();
1242
- this.insertNode(document.createTextNode('\n'));
1243
- if (html.search(/\s$/) == -1)
1340
+ if (this.isEndOfElement())
1244
1341
  {
1245
- this.insertNode(document.createTextNode('\n'));
1246
- }
1342
+ if (this.dblEnter == 1)
1343
+ {
1344
+ var element;
1345
+ var last;
1346
+ if (block.tagName == 'BLOCKQUOTE')
1347
+ {
1348
+ last = 'br';
1349
+ element = block;
1350
+ }
1351
+ else
1352
+ {
1353
+ last = 'p';
1354
+ element = $(block).parent()[0];
1355
+ }
1247
1356
 
1248
- this.sync();
1249
- this.callback('enter', e);
1250
- return false;
1357
+ e.preventDefault();
1358
+ this.insertingAfterLastElement(element);
1359
+ this.dblEnter = 0;
1360
+
1361
+ if (last == 'p')
1362
+ {
1363
+ $(block).parent().find('p').last().remove();
1364
+ }
1365
+ else
1366
+ {
1367
+ var tmp = $.trim($(block).html());
1368
+ $(block).html(tmp.replace(/<br\s?\/?>$/i, ''));
1369
+ }
1370
+
1371
+ return;
1372
+ }
1373
+ else this.dblEnter++;
1374
+ }
1375
+ else this.dblEnter++;
1251
1376
  }
1377
+
1378
+ // pre
1379
+ if (pre === true) return this.buildEventKeydownPre(e, current);
1252
1380
  else
1253
1381
  {
1254
1382
  if (!this.opts.linebreaks)
@@ -1328,57 +1456,75 @@
1328
1456
  }
1329
1457
 
1330
1458
  // tab
1331
- if (key === this.keyCode.TAB && this.opts.shortcuts )
1459
+ if (key === this.keyCode.TAB && this.opts.shortcuts) return this.buildEventKeydownTab(e, pre);
1460
+
1461
+ // delete zero-width space before the removing
1462
+ if (key === this.keyCode.BACKSPACE) this.buildEventKeydownBackspace(current);
1463
+
1464
+ },
1465
+ buildEventKeydownPre: function(e, current)
1466
+ {
1467
+ e.preventDefault();
1468
+ this.bufferSet();
1469
+ var html = $(current).parent().text();
1470
+ this.insertNode(document.createTextNode('\n'));
1471
+ if (html.search(/\s$/) == -1)
1332
1472
  {
1333
- if (!this.opts.tabFocus) return true;
1334
- if (this.isEmpty(this.get())) return true;
1473
+ this.insertNode(document.createTextNode('\n'));
1474
+ }
1335
1475
 
1336
- e.preventDefault();
1476
+ this.sync();
1477
+ this.callback('enter', e);
1478
+ return false;
1479
+ },
1480
+ buildEventKeydownTab: function(e, pre)
1481
+ {
1482
+ if (!this.opts.tabFocus) return true;
1483
+ if (this.isEmpty(this.get()) && this.opts.tabSpaces === false) return true;
1337
1484
 
1338
- if (pre === true && !e.shiftKey)
1339
- {
1340
- this.bufferSet();
1341
- this.insertNode(document.createTextNode('\t'));
1342
- this.sync();
1343
- return false;
1485
+ e.preventDefault();
1344
1486
 
1345
- }
1346
- else if (this.opts.tabSpaces !== false)
1347
- {
1348
- this.bufferSet();
1349
- this.insertNode(document.createTextNode(Array(this.opts.tabSpaces + 1).join('\u00a0')));
1350
- this.sync();
1351
- return false;
1352
- }
1353
- else
1354
- {
1355
- if (!e.shiftKey) this.indentingIndent();
1356
- else this.indentingOutdent();
1357
- }
1487
+ if (pre === true && !e.shiftKey)
1488
+ {
1489
+ this.bufferSet();
1490
+ this.insertNode(document.createTextNode('\t'));
1491
+ this.sync();
1492
+ return false;
1358
1493
 
1494
+ }
1495
+ else if (this.opts.tabSpaces !== false)
1496
+ {
1497
+ this.bufferSet();
1498
+ this.insertNode(document.createTextNode(Array(this.opts.tabSpaces + 1).join('\u00a0')));
1499
+ this.sync();
1359
1500
  return false;
1360
1501
  }
1502
+ else
1503
+ {
1504
+ if (!e.shiftKey) this.indentingIndent();
1505
+ else this.indentingOutdent();
1506
+ }
1361
1507
 
1362
- // delete zero-width space before the removing
1363
- if (key === this.keyCode.BACKSPACE)
1508
+ return false;
1509
+ },
1510
+ buildEventKeydownBackspace: function(current)
1511
+ {
1512
+ if (typeof current.tagName !== 'undefined' && /^(H[1-6])$/i.test(current.tagName))
1364
1513
  {
1365
- if (typeof current.tagName !== 'undefined' && /^(H[1-6])$/i.test(current.tagName))
1366
- {
1367
- var node;
1368
- if (this.opts.linebreaks === false) node = $('<p>' + this.opts.invisibleSpace + '</p>');
1369
- else node = $('<br>' + this.opts.invisibleSpace);
1514
+ var node;
1515
+ if (this.opts.linebreaks === false) node = $('<p>' + this.opts.invisibleSpace + '</p>');
1516
+ else node = $('<br>' + this.opts.invisibleSpace);
1370
1517
 
1371
- $(current).replaceWith(node);
1372
- this.selectionStart(node);
1373
- }
1518
+ $(current).replaceWith(node);
1519
+ this.selectionStart(node);
1520
+ }
1374
1521
 
1375
- if (typeof current.nodeValue !== 'undefined' && current.nodeValue !== null)
1522
+ if (typeof current.nodeValue !== 'undefined' && current.nodeValue !== null)
1523
+ {
1524
+ //var value = $.trim(current.nodeValue.replace(/[^\u0000-\u1C7F]/g, ''));
1525
+ if (current.remove && current.nodeType === 3 && current.nodeValue.match(/[^/\u200B]/g) == null)
1376
1526
  {
1377
- var value = $.trim(current.nodeValue.replace(/[^\u0000-\u1C7F]/g, ''));
1378
- if (current.remove && current.nodeType === 3 && current.nodeValue.charCodeAt(0) == 8203 && value == '')
1379
- {
1380
- current.remove();
1381
- }
1527
+ current.remove();
1382
1528
  }
1383
1529
  }
1384
1530
  },
@@ -1392,7 +1538,7 @@
1392
1538
  },
1393
1539
  buildEventKeyup: function(e)
1394
1540
  {
1395
- if (rtePaste) return false;
1541
+ if (this.rtePaste) return false;
1396
1542
 
1397
1543
  var key = e.which;
1398
1544
  var parent = this.getParent();
@@ -1414,17 +1560,11 @@
1414
1560
  // convert links
1415
1561
  if ((this.opts.convertLinks || this.opts.convertImageLinks || this.opts.convertVideoLinks) && key === this.keyCode.ENTER)
1416
1562
  {
1417
- this.formatLinkify(this.opts.linkProtocol, this.opts.convertLinks, this.opts.convertImageLinks, this.opts.convertVideoLinks);
1418
-
1419
- setTimeout($.proxy(function()
1420
- {
1421
- if (this.opts.convertImageLinks) this.observeImages();
1422
- if (this.opts.observeLinks) this.observeLinks();
1423
- }, this), 5);
1563
+ this.buildEventKeyupConverters();
1424
1564
  }
1425
1565
 
1426
1566
  // if empty
1427
- if (this.opts.linebreaks === false && (key === this.keyCode.DELETE || key === this.keyCode.BACKSPACE))
1567
+ if (key === this.keyCode.DELETE || key === this.keyCode.BACKSPACE)
1428
1568
  {
1429
1569
  return this.formatEmpty(e);
1430
1570
  }
@@ -1432,6 +1572,16 @@
1432
1572
  this.callback('keyup', e);
1433
1573
  this.sync();
1434
1574
  },
1575
+ buildEventKeyupConverters: function()
1576
+ {
1577
+ this.formatLinkify(this.opts.linkProtocol, this.opts.convertLinks, this.opts.convertImageLinks, this.opts.convertVideoLinks, this.opts.linkSize);
1578
+
1579
+ setTimeout($.proxy(function()
1580
+ {
1581
+ if (this.opts.convertImageLinks) this.observeImages();
1582
+ if (this.opts.observeLinks) this.observeLinks();
1583
+ }, this), 5);
1584
+ },
1435
1585
  buildPlugins: function()
1436
1586
  {
1437
1587
  if (!this.opts.plugins ) return;
@@ -1576,6 +1726,10 @@
1576
1726
  // place the cursor inside emptyHtml
1577
1727
  this.selectionStart(this.$editor.children()[0]);
1578
1728
  }
1729
+ else
1730
+ {
1731
+ this.focus();
1732
+ }
1579
1733
 
1580
1734
  this.sync();
1581
1735
  },
@@ -1657,102 +1811,116 @@
1657
1811
  sel.addRange(range);
1658
1812
  },
1659
1813
 
1660
-
1661
1814
  // TOGGLE
1662
1815
  toggle: function(direct)
1663
1816
  {
1664
- var html;
1665
- if (this.opts.visual)
1817
+ if (this.opts.visual) this.toggleCode(direct);
1818
+ else this.toggleVisual();
1819
+ },
1820
+ toggleVisual: function()
1821
+ {
1822
+ var html = this.$source.hide().val();
1823
+
1824
+ if (typeof this.modified !== 'undefined')
1666
1825
  {
1667
- if (direct !== false) this.selectionSave();
1826
+ this.modified = this.cleanRemoveSpaces(this.modified, false) !== this.cleanRemoveSpaces(html, false);
1827
+ }
1668
1828
 
1669
- var height = null;
1670
- if (this.opts.iframe)
1671
- {
1672
- height = this.$frame.height();
1673
- if (this.opts.fullpage) this.$editor.removeAttr('contenteditable');
1674
- this.$frame.hide();
1675
- }
1829
+ if (this.modified)
1830
+ {
1831
+ // don't remove the iframe even if cleared all.
1832
+ if (this.opts.fullpage && html === '') this.setFullpageOnInit(html);
1676
1833
  else
1677
1834
  {
1678
- height = this.$editor.innerHeight();
1679
- this.$editor.hide();
1835
+ this.set(html);
1836
+ if (this.opts.fullpage) this.buildBindKeyboard();
1680
1837
  }
1838
+ }
1681
1839
 
1682
- html = this.$source.val();
1683
- this.modified = html;
1840
+ if (this.opts.iframe) this.$frame.show();
1841
+ else this.$editor.show();
1684
1842
 
1685
- this.$source.height(height).show().focus();
1843
+ if (this.opts.fullpage) this.$editor.attr('contenteditable', true );
1686
1844
 
1687
- // textarea indenting
1688
- this.$source.on('keydown.redactor-textarea-indenting', function (e)
1689
- {
1690
- if (e.keyCode === 9)
1691
- {
1692
- var $el = $(this);
1693
- var start = $el.get(0).selectionStart;
1694
- $el.val($el.val().substring(0, start) + "\t" + $el.val().substring($el.get(0).selectionEnd));
1695
- $el.get(0).selectionStart = $el.get(0).selectionEnd = start + 1;
1696
- return false;
1697
- }
1698
- });
1845
+ this.$source.off('keydown.redactor-textarea-indenting');
1846
+
1847
+ this.$editor.focus();
1848
+ this.selectionRestore();
1699
1849
 
1700
- this.buttonInactiveVisual();
1701
- this.buttonActive('html');
1702
- this.opts.visual = false;
1850
+ this.observeStart();
1851
+ this.buttonActiveVisual();
1852
+ this.buttonInactive('html');
1853
+ this.opts.visual = true;
1854
+ },
1855
+ toggleCode: function(direct)
1856
+ {
1857
+ if (direct !== false) this.selectionSave();
1703
1858
 
1859
+ var height = null;
1860
+ if (this.opts.iframe)
1861
+ {
1862
+ height = this.$frame.height();
1863
+ if (this.opts.fullpage) this.$editor.removeAttr('contenteditable');
1864
+ this.$frame.hide();
1704
1865
  }
1705
1866
  else
1706
1867
  {
1707
- html = this.$source.hide().val();
1708
-
1709
- if (typeof this.modified !== 'undefined')
1710
- {
1711
- this.modified = this.cleanRemoveSpaces(this.modified, false) !== this.cleanRemoveSpaces(html, false);
1712
- }
1868
+ height = this.$editor.innerHeight();
1869
+ this.$editor.hide();
1870
+ }
1713
1871
 
1714
- if (this.modified)
1715
- {
1716
- // don't remove the iframe even if cleared all.
1717
- if (this.opts.fullpage && html === '') this.setFullpageOnInit(html);
1718
- else
1719
- {
1720
- this.set(html);
1721
- if (this.opts.fullpage) this.buildBindKeyboard();
1722
- }
1723
- }
1872
+ var html = this.$source.val();
1724
1873
 
1725
- if (this.opts.iframe) this.$frame.show();
1726
- else this.$editor.show();
1874
+ // tidy html
1875
+ if (html !== '' && this.opts.tidyHtml)
1876
+ {
1877
+ this.$source.val(this.cleanHtml(html));
1878
+ }
1727
1879
 
1728
- if (this.opts.fullpage ) this.$editor.attr('contenteditable', true );
1880
+ this.modified = html;
1729
1881
 
1730
- this.$source.off('keydown.redactor-textarea-indenting');
1882
+ this.$source.height(height).show().focus();
1731
1883
 
1732
- this.$editor.focus();
1733
- this.selectionRestore();
1884
+ // textarea indenting
1885
+ this.$source.on('keydown.redactor-textarea-indenting', this.textareaIndenting);
1734
1886
 
1735
- this.observeStart();
1736
- this.buttonActiveVisual();
1737
- this.buttonInactive('html');
1738
- this.opts.visual = true;
1887
+ this.buttonInactiveVisual();
1888
+ this.buttonActive('html');
1889
+ this.opts.visual = false;
1890
+ },
1891
+ textareaIndenting: function(e)
1892
+ {
1893
+ if (e.keyCode === 9)
1894
+ {
1895
+ var $el = $(this);
1896
+ var start = $el.get(0).selectionStart;
1897
+ $el.val($el.val().substring(0, start) + "\t" + $el.val().substring($el.get(0).selectionEnd));
1898
+ $el.get(0).selectionStart = $el.get(0).selectionEnd = start + 1;
1899
+ return false;
1739
1900
  }
1740
1901
  },
1741
1902
 
1742
-
1743
1903
  // AUTOSAVE
1744
1904
  autosave: function()
1745
1905
  {
1746
1906
  var savedHtml = false;
1907
+ var html = this.get();
1908
+ var submitData = "";
1909
+ if(this.opts.autosaveFormdata == true)
1910
+ {
1911
+ submitData = this.$source.closest('form').serialize();
1912
+ }
1913
+ else{
1914
+ submitData = this.$source.attr('name') + '=' + escape(encodeURIComponent(html));
1915
+ }
1747
1916
  this.autosaveInterval = setInterval($.proxy(function()
1748
1917
  {
1749
- var html = this.get();
1750
1918
  if (savedHtml !== html)
1751
1919
  {
1752
1920
  $.ajax({
1753
1921
  url: this.opts.autosave,
1754
1922
  type: 'post',
1755
- data: this.$source.attr('name') + '=' + escape(encodeURIComponent(html)),
1923
+ data: submitData,
1756
1924
  success: $.proxy(function(data)
1757
1925
  {
1758
1926
  this.callback('autosave', false, data);
@@ -1904,7 +2072,6 @@
1904
2072
  var text = this.getSelectionText();
1905
2073
 
1906
2074
  if (e.type === 'mouseup' && text != '') this.airShow(e);
1907
-
1908
2075
  if (e.type === 'keyup' && e.shiftKey && text != '')
1909
2076
  {
1910
2077
  var $focusElem = $(this.getElement(this.getSelection().focusNode)), offset = $focusElem.offset();
@@ -2095,6 +2262,7 @@
2095
2262
  this.$editor.one('click', hdlHideDropDown);
2096
2263
 
2097
2264
  e.stopPropagation();
2265
+ this.$editor.focus();
2098
2266
  },
2099
2267
  dropdownHideAll: function()
2100
2268
  {
@@ -2113,7 +2281,7 @@
2113
2281
  // BUTTONS
2114
2282
  buttonBuild: function(btnName, btnObject)
2115
2283
  {
2116
- var $button = $('<a href="javascript:;" title="' + btnObject.title + '" class="redactor_btn redactor_btn_' + btnName + '"></a>');
2284
+ var $button = $('<a href="javascript:;" title="' + btnObject.title + '" tabindex="-1" class="redactor_btn redactor_btn_' + btnName + '"></a>');
2117
2285
 
2118
2286
  $button.on('click', $.proxy(function(e)
2119
2287
  {
@@ -2337,14 +2505,47 @@
2337
2505
  },
2338
2506
 
2339
2507
  // EXEC
2508
+ execPasteFrag: function(html)
2509
+ {
2510
+ var sel = this.getSelection();
2511
+ if (sel.getRangeAt && sel.rangeCount)
2512
+ {
2513
+ range = sel.getRangeAt(0);
2514
+ range.deleteContents();
2515
+
2516
+ var el = document.createElement("div");
2517
+ el.innerHTML = html;
2518
+ var frag = document.createDocumentFragment(), node, lastNode;
2519
+ while ((node = el.firstChild))
2520
+ {
2521
+ lastNode = frag.appendChild(node);
2522
+ }
2523
+
2524
+ var firstNode = frag.firstChild;
2525
+ range.insertNode(frag);
2526
+
2527
+ if (lastNode)
2528
+ {
2529
+ range = range.cloneRange();
2530
+ range.setStartAfter(lastNode);
2531
+ range.collapse(true);
2532
+ sel.removeAllRanges();
2533
+ sel.addRange(range);
2534
+ }
2535
+ }
2536
+ },
2340
2537
  exec: function(cmd, param, sync)
2341
2538
  {
2342
2539
  if (cmd === 'formatblock' && this.browser('msie')) param = '<' + param + '>';
2343
2540
 
2344
2541
  if (cmd === 'inserthtml' && this.browser('msie'))
2345
2542
  {
2346
- this.$editor.focus();
2347
- this.document.selection.createRange().pasteHTML(param);
2543
+ if (!this.isIe11())
2544
+ {
2545
+ this.$editor.focus();
2546
+ this.document.selection.createRange().pasteHTML(param);
2547
+ }
2548
+ else this.execPasteFrag(param);
2348
2549
  }
2349
2550
  else
2350
2551
  {
@@ -2369,128 +2570,144 @@
2369
2570
  return;
2370
2571
  }
2371
2572
 
2372
- // stop formatting pre
2373
- if (this.currentOrParentIs('PRE') && !this.opts.formattingPre)
2374
- {
2375
- return false;
2376
- }
2377
-
2378
- if (cmd === 'insertunorderedlist' || cmd === 'insertorderedlist')
2379
- {
2380
- this.bufferSet();
2573
+ // Stop formatting pre
2574
+ if (this.currentOrParentIs('PRE') && !this.opts.formattingPre) return false;
2381
2575
 
2382
- var parent = this.getParent();
2383
- var $list = $(parent).closest('ol, ul');
2384
- var remove = false;
2576
+ // Lists
2577
+ if (cmd === 'insertunorderedlist' || cmd === 'insertorderedlist') return this.execLists(cmd, param);
2385
2578
 
2386
- if ($list.length)
2387
- {
2388
- remove = true;
2389
- var listTag = $list[0].tagName;
2390
- if ((cmd === 'insertunorderedlist' && listTag === 'OL')
2391
- || (cmd === 'insertorderedlist' && listTag === 'UL'))
2392
- {
2393
- remove = false;
2394
- }
2395
- }
2579
+ // Unlink
2580
+ if (cmd === 'unlink') return this.execUnlink(cmd, param);
2396
2581
 
2397
- this.selectionSave();
2582
+ // Usual exec
2583
+ this.exec(cmd, param, sync);
2398
2584
 
2399
- // remove lists
2400
- if (remove)
2401
- {
2402
- var nodes = this.getNodes();
2403
- var elems = this.getBlocks(nodes);
2585
+ // Line
2586
+ if (cmd === 'inserthorizontalrule') this.$editor.find('hr').removeAttr('id');
2404
2587
 
2405
- if (typeof nodes[0] != 'undefined' && nodes.length > 1 && nodes[0].nodeType == 3)
2406
- {
2407
- // fix the adding the first li to the array
2408
- elems.unshift(this.getBlock());
2409
- }
2588
+ },
2589
+ execUnlink: function(cmd, param)
2590
+ {
2591
+ this.bufferSet();
2410
2592
 
2411
- var data = '', replaced = '';
2412
- $.each(elems, $.proxy(function(i,s)
2413
- {
2414
- if (s.tagName == 'LI')
2415
- {
2416
- var $s = $(s);
2417
- var cloned = $s.clone();
2418
- cloned.find('ul', 'ol').remove();
2593
+ var link = this.currentOrParentIs('A');
2594
+ if (link)
2595
+ {
2596
+ $(link).replaceWith($(link).text());
2419
2597
 
2420
- if (this.opts.linebreaks === false) data += this.outerHtml($('<p>').append(cloned.contents()));
2421
- else data += cloned.html() + '<br>';
2598
+ this.sync();
2599
+ this.callback('execCommand', cmd, param);
2600
+ return;
2601
+ }
2602
+ },
2603
+ execLists: function(cmd, param)
2604
+ {
2605
+ this.bufferSet();
2422
2606
 
2423
- if (i == 0)
2424
- {
2425
- $s.addClass('redactor-replaced').empty();
2426
- replaced = this.outerHtml($s);
2427
- }
2428
- else $s.remove();
2429
- }
2607
+ var parent = this.getParent();
2608
+ var $list = $(parent).closest('ol, ul');
2609
+ var remove = false;
2430
2610
 
2431
- }, this));
2611
+ if ($list.length)
2612
+ {
2613
+ remove = true;
2614
+ var listTag = $list[0].tagName;
2615
+ if ((cmd === 'insertunorderedlist' && listTag === 'OL')
2616
+ || (cmd === 'insertorderedlist' && listTag === 'UL'))
2617
+ {
2618
+ remove = false;
2619
+ }
2620
+ }
2432
2621
 
2433
- html = this.$editor.html().replace(replaced, '</' + listTag + '>' + data + '<' + listTag + '>');
2622
+ this.selectionSave();
2434
2623
 
2435
- this.$editor.html(html);
2436
- this.$editor.find(listTag + ':empty').remove();
2624
+ // remove lists
2625
+ if (remove)
2626
+ {
2627
+ var nodes = this.getNodes();
2628
+ var elems = this.getBlocks(nodes);
2437
2629
 
2630
+ if (typeof nodes[0] != 'undefined' && nodes.length > 1 && nodes[0].nodeType == 3)
2631
+ {
2632
+ // fix the adding the first li to the array
2633
+ elems.unshift(this.getBlock());
2438
2634
  }
2439
2635
 
2440
- // insert lists
2441
- else
2636
+ var data = '', replaced = '';
2637
+ $.each(elems, $.proxy(function(i,s)
2442
2638
  {
2443
- this.document.execCommand(cmd);
2639
+ if (s.tagName == 'LI')
2640
+ {
2641
+ var $s = $(s);
2642
+ var cloned = $s.clone();
2643
+ cloned.find('ul', 'ol').remove();
2444
2644
 
2445
- var parent = this.getParent();
2446
- var $list = $(parent).closest('ol, ul');
2645
+ if (this.opts.linebreaks === false) data += this.outerHtml($('<p>').append(cloned.contents()));
2646
+ else data += cloned.html() + '<br>';
2447
2647
 
2448
- if ($list.length)
2449
- {
2450
- if ((this.browser('msie') || this.browser('mozilla')) && parent.tagName !== 'LI')
2648
+ if (i == 0)
2451
2649
  {
2452
- $(parent).replaceWith($(parent).html());
2453
- }
2454
-
2455
- var $listParent = $list.parent();
2456
- if (this.isParentRedactor($listParent) && this.nodeTestBlocks($listParent[0]))
2457
- {
2458
- $listParent.replaceWith($listParent.contents());
2650
+ $s.addClass('redactor-replaced').empty();
2651
+ replaced = this.outerHtml($s);
2459
2652
  }
2653
+ else $s.remove();
2460
2654
  }
2461
2655
 
2462
- if (this.browser('mozilla')) this.$editor.focus();
2656
+ }, this));
2463
2657
 
2464
- }
2658
+ html = this.$editor.html().replace(replaced, '</' + listTag + '>' + data + '<' + listTag + '>');
2465
2659
 
2466
- this.selectionRestore();
2660
+ this.$editor.html(html);
2661
+ this.$editor.find(listTag + ':empty').remove();
2467
2662
 
2468
- this.sync();
2469
- this.callback('execCommand', cmd, param);
2470
- return;
2471
2663
  }
2472
2664
 
2473
- if (cmd === 'unlink')
2665
+ // insert lists
2666
+ else
2474
2667
  {
2475
- this.bufferSet();
2668
+ var firstParent = this.getParent();
2669
+
2670
+ this.document.execCommand(cmd);
2476
2671
 
2477
- var link = this.currentOrParentIs('A');
2478
- if (link)
2672
+ var parent = this.getParent();
2673
+ var $list = $(parent).closest('ol, ul');
2674
+
2675
+ if (firstParent && firstParent.tagName == 'TD')
2479
2676
  {
2480
- $(link).replaceWith($(link).text());
2677
+ $list.wrapAll('<td>');
2678
+ }
2481
2679
 
2482
- this.sync();
2483
- this.callback('execCommand', cmd, param);
2484
- return;
2680
+ if ($list.length)
2681
+ {
2682
+ // focus fix
2683
+ var li = $list.children().first();
2684
+ if ($.trim($(li).text()) == '')
2685
+ {
2686
+ var marker = $('<span id="selection-marker-1"></span>');
2687
+ $(li).prepend(marker);
2688
+ }
2689
+
2690
+ if ((this.browser('msie') || this.browser('mozilla')) && parent.tagName !== 'LI')
2691
+ {
2692
+ $(parent).replaceWith($(parent).html());
2693
+ }
2694
+
2695
+ var $listParent = $list.parent();
2696
+ if (this.isParentRedactor($listParent) && this.nodeTestBlocks($listParent[0]))
2697
+ {
2698
+ $listParent.replaceWith($listParent.contents());
2699
+ }
2485
2700
  }
2701
+
2702
+ if (this.browser('mozilla')) this.$editor.focus();
2486
2703
  }
2487
2704
 
2488
- this.exec(cmd, param, sync);
2705
+ this.selectionRestore();
2706
+ $(marker).remove();
2489
2707
 
2490
- if (cmd === 'inserthorizontalrule')
2491
- {
2492
- this.$editor.find('hr').removeAttr('id');
2493
- }
2708
+ this.sync();
2709
+ this.callback('execCommand', cmd, param);
2710
+ return;
2494
2711
  },
2495
2712
 
2496
2713
  // INDENTING
@@ -2512,7 +2729,6 @@
2512
2729
 
2513
2730
  this.selectionSave();
2514
2731
 
2515
-
2516
2732
  if (block && block.tagName == 'LI')
2517
2733
  {
2518
2734
  // li
@@ -2637,6 +2853,8 @@
2637
2853
  this.selectionRestore();
2638
2854
  }
2639
2855
 
2856
+ this.sync();
2857
+
2640
2858
  },
2641
2859
  insideOutdent: function (li, index, elems)
2642
2860
  {
@@ -2664,6 +2882,82 @@
2664
2882
  }
2665
2883
  },
2666
2884
 
2885
+ // ALIGNMENT
2886
+ alignmentLeft: function()
2887
+ {
2888
+ this.alignmentSet('', 'JustifyLeft');
2889
+ },
2890
+ alignmentRight: function()
2891
+ {
2892
+ this.alignmentSet('right', 'JustifyRight');
2893
+ },
2894
+ alignmentCenter: function()
2895
+ {
2896
+ this.alignmentSet('center', 'JustifyCenter');
2897
+ },
2898
+ alignmentJustify: function()
2899
+ {
2900
+ this.alignmentSet('justify', 'JustifyFull');
2901
+ },
2902
+ alignmentSet: function(type, cmd)
2903
+ {
2904
+ this.bufferSet();
2905
+
2906
+ if (this.oldIE())
2907
+ {
2908
+ this.document.execCommand(cmd, false, false);
2909
+ return true;
2910
+ }
2911
+
2912
+ this.selectionSave();
2913
+
2914
+ var block = this.getBlock();
2915
+ if (!block && this.opts.linebreaks)
2916
+ {
2917
+ // one element
2918
+ this.exec('formatBlock', 'blockquote');
2919
+ var newblock = this.getBlock();
2920
+ var block = $('<div data-tagblock="">').html($(newblock).html());
2921
+ $(newblock).replaceWith(block);
2922
+
2923
+ $(block).css('text-align', type);
2924
+ this.removeEmptyAttr(block, 'style');
2925
+
2926
+ if (type == '' && typeof($(block).data('tagblock')) !== 'undefined')
2927
+ {
2928
+ $(block).replaceWith($(block).html());
2929
+ }
2930
+ }
2931
+ else
2932
+ {
2933
+ var elements = this.getBlocks();
2934
+ $.each(elements, $.proxy(function(i, elem)
2935
+ {
2936
+ var $el = false;
2937
+
2938
+ if ($.inArray(elem.tagName, this.opts.alignmentTags) !== -1)
2939
+ {
2940
+ $el = $(elem);
2941
+ }
2942
+ else
2943
+ {
2944
+ $el = $(elem).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]);
2945
+ }
2946
+
2947
+ if ($el)
2948
+ {
2949
+ $el.css('text-align', type);
2950
+ this.removeEmptyAttr($el, 'style');
2951
+ }
2952
+
2953
+ }, this));
2954
+ }
2955
+
2956
+ this.selectionRestore();
2957
+
2958
+ this.sync();
2959
+ },
2960
+
2667
2961
  // CLEAN
2668
2962
  cleanEmpty: function(html)
2669
2963
  {
@@ -2802,9 +3096,10 @@
2802
3096
 
2803
3097
  html = html + "\n";
2804
3098
 
3099
+
2805
3100
  var safes = [];
2806
3101
  var matches = html.match(/<(table|div|pre|object)(.*?)>([\w\W]*?)<\/(table|div|pre|object)>/gi);
2807
- if (matches === null) matches = [];
3102
+ if (!matches) matches = [];
2808
3103
 
2809
3104
  var commentsMatches = html.match(/<!--([\w\W]*?)-->/gi);
2810
3105
  if (commentsMatches) matches = $.merge(matches, commentsMatches);
@@ -2812,10 +3107,7 @@
2812
3107
  if (this.opts.phpTags)
2813
3108
  {
2814
3109
  var phpMatches = html.match(/<section(.*?)rel="redactor-php-tag">([\w\W]*?)<\/section>/gi);
2815
- if (phpMatches)
2816
- {
2817
- matches = $.merge(matches, phpMatches);
2818
- }
3110
+ if (phpMatches) matches = $.merge(matches, phpMatches);
2819
3111
  }
2820
3112
 
2821
3113
  if (matches)
@@ -2851,24 +3143,18 @@
2851
3143
  {
2852
3144
  if (htmls[i].search('{replace') == -1)
2853
3145
  {
2854
- html += '<p>' + htmls[i].replace(/^\n+|\n+$/g, "") + "</p>";
3146
+ htmls[i] = htmls[i].replace(/<p>\n\t<\/p>/gi, '');
3147
+ htmls[i] = htmls[i].replace(/<p><\/p>/gi, '');
3148
+
3149
+ if (htmls[i] != '')
3150
+ {
3151
+ html += '<p>' + htmls[i].replace(/^\n+|\n+$/g, "") + "</p>";
3152
+ }
2855
3153
  }
2856
3154
  else html += htmls[i];
2857
3155
  }
2858
3156
  }
2859
3157
 
2860
- // blockquote
2861
- if (html.search(/<blockquote/gi) !== -1)
2862
- {
2863
- $.each(html.match(/<blockquote(.*?)>([\w\W]*?)<\/blockquote>/gi), function(i,s)
2864
- {
2865
- var str = '';
2866
- str = s.replace('<p>', '');
2867
- str = str.replace('</p>', '<br>');
2868
- html = html.replace(s, str);
2869
- });
2870
- }
2871
-
2872
3158
  html = R('<p>\s*</p>', 'gi', '');
2873
3159
  html = R('<p>([^<]+)</(div|address|form)>', 'gi', "<p>$1</p></$2>");
2874
3160
  html = R('<p>\s*(</?' + blocks + '[^>]*>)\s*</p>', 'gi', "$1");
@@ -2891,7 +3177,6 @@
2891
3177
  html = R('<br></p></blockquote>', 'gi', '</blockquote>');
2892
3178
  html = R('<p>\t*</p>', 'gi', '');
2893
3179
 
2894
-
2895
3180
  // restore safes
2896
3181
  $.each(safes, function(i,s)
2897
3182
  {
@@ -2900,7 +3185,7 @@
2900
3185
 
2901
3186
  return $.trim(html);
2902
3187
  },
2903
- cleanConvertInlineTags: function(html)
3188
+ cleanConvertInlineTags: function(html, set)
2904
3189
  {
2905
3190
  var boldTag = 'strong';
2906
3191
  if (this.opts.boldTag === 'b') boldTag = 'b';
@@ -2918,11 +3203,13 @@
2918
3203
  if (this.opts.italicTag === 'em') html = html.replace(/<i>([\w\W]*?)<\/i>/gi, '<em>$1</em>');
2919
3204
  else html = html.replace(/<em>([\w\W]*?)<\/em>/gi, '<i>$1</i>');
2920
3205
 
2921
- html = html.replace(/<strike>([\w\W]*?)<\/strike>/gi, '<del>$1</del>');
2922
-
2923
- if (!/<span(.*?)data-redactor="verified"(.*?)>([\w\W]*?)<\/span>/gi.test(html))
3206
+ if (set !== true)
3207
+ {
3208
+ html = html.replace(/<strike>([\w\W]*?)<\/strike>/gi, '<del>$1</del>');
3209
+ }
3210
+ else
2924
3211
  {
2925
- html = html.replace(/<span(.*?)(?!data-redactor="verified")(.*?)>([\w\W]*?)<\/span>/gi, '<span$1 data-redactor="verified"$2>$3</span>');
3212
+ html = html.replace(/<del>([\w\W]*?)<\/del>/gi, '<strike>$1</strike>');
2926
3213
  }
2927
3214
 
2928
3215
  return html;
@@ -2958,8 +3245,12 @@
2958
3245
  var arr = s.match(/<(pre|code)(.*?)>([\w\W]*?)<\/(pre|code)>/i);
2959
3246
 
2960
3247
  arr[3] = arr[3].replace(/&nbsp;/g, ' ');
3248
+
2961
3249
  if (encode !== false) arr[3] = this.cleanEncodeEntities(arr[3]);
2962
3250
 
3251
+ // $ fix
3252
+ arr[3] = arr[3].replace(/\$/g, '&#36;');
3253
+
2963
3254
  html = html.replace(s, '<' + arr[1] + arr[2] + '>' + arr[3] + '</' + arr[1] + '>');
2964
3255
 
2965
3256
  }, this));
@@ -2970,26 +3261,21 @@
2970
3261
  cleanEncodeEntities: function(str)
2971
3262
  {
2972
3263
  str = String(str).replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"');
2973
- return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
3264
+ return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
2974
3265
  },
2975
3266
  cleanUnverified: function()
2976
3267
  {
2977
3268
  // label, abbr, mark, meter, code, q, dfn, ins, time, kbd, var
2978
-
2979
3269
  var $elem = this.$editor.find('li, img, a, b, strong, sub, sup, i, em, u, small, strike, del, span, cite');
2980
3270
 
2981
- $elem.not('[data-redactor="verified"]').filter('[style*="font-size"][style*="line-height"]')
2982
- .css('font-size', '')
2983
- .css('line-height', '');
2984
-
2985
- $elem.not('[data-redactor="verified"]').filter('[style*="background-color: transparent;"][style*="line-height"]')
3271
+ $elem.filter('[style*="background-color: transparent;"][style*="line-height"]')
2986
3272
  .css('background-color', '')
2987
3273
  .css('line-height', '');
2988
3274
 
2989
- $elem.not('[data-redactor="verified"]').filter('[style*="background-color: transparent;"]')
3275
+ $elem.filter('[style*="background-color: transparent;"]')
2990
3276
  .css('background-color', '');
2991
3277
 
2992
- $elem.not('[data-redactor="verified"]').css('line-height', '');
3278
+ $elem.css('line-height', '');
2993
3279
 
2994
3280
  $.each($elem, $.proxy(function(i,s)
2995
3281
  {
@@ -3181,97 +3467,33 @@
3181
3467
  return out;
3182
3468
  },
3183
3469
 
3184
- // ALIGNMENT
3185
- alignmentLeft: function()
3186
- {
3187
- this.alignmentSet('', 'JustifyLeft');
3188
- },
3189
- alignmentRight: function()
3190
- {
3191
- this.alignmentSet('right', 'JustifyRight');
3192
- },
3193
- alignmentCenter: function()
3194
- {
3195
- this.alignmentSet('center', 'JustifyCenter');
3196
- },
3197
- alignmentJustify: function()
3198
- {
3199
- this.alignmentSet('justify', 'JustifyFull');
3200
- },
3201
- alignmentSet: function(type, cmd)
3470
+ // FORMAT
3471
+ formatEmpty: function(e)
3202
3472
  {
3203
- this.bufferSet();
3204
-
3205
- if (this.oldIE())
3206
- {
3207
- this.document.execCommand(cmd, false, false);
3208
- return true;
3209
- }
3210
-
3211
- this.selectionSave();
3473
+ var html = $.trim(this.$editor.html());
3212
3474
 
3213
- var block = this.getBlock();
3214
- if (!block && this.opts.linebreaks)
3475
+ if (this.opts.linebreaks)
3215
3476
  {
3216
- // one element
3217
- this.exec('formatBlock', 'blockquote');
3218
- var newblock = this.getBlock();
3219
- var block = $('<div data-tagblock="">').html($(newblock).html());
3220
- $(newblock).replaceWith(block);
3221
-
3222
- $(block).css('text-align', type);
3223
- this.removeEmptyAttr(block, 'style');
3224
-
3225
- if (type == '' && typeof($(block).data('tagblock')) !== 'undefined')
3477
+ if (html == '')
3226
3478
  {
3227
- $(block).replaceWith($(block).html());
3479
+ e.preventDefault();
3480
+ this.$editor.html('');
3481
+ this.focus();
3228
3482
  }
3229
3483
  }
3230
3484
  else
3231
3485
  {
3232
- var elements = this.getBlocks();
3233
- $.each(elements, $.proxy(function(i, elem)
3234
- {
3235
- var $el = false;
3236
-
3237
- if ($.inArray(elem.tagName, this.opts.alignmentTags) !== -1)
3238
- {
3239
- $el = $(elem);
3240
- }
3241
- else
3242
- {
3243
- $el = $(elem).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]);
3244
- }
3245
-
3246
- if ($el)
3247
- {
3248
- $el.css('text-align', type);
3249
- this.removeEmptyAttr($el, 'style');
3250
- }
3486
+ html = html.replace(/<br\s?\/?>/i, '');
3487
+ var thtml = html.replace(/<p>\s?<\/p>/gi, '');
3251
3488
 
3252
- }, this));
3253
- }
3254
-
3255
- this.selectionRestore();
3256
-
3257
- this.sync();
3258
- },
3259
-
3260
- // FORMAT
3261
- formatEmpty: function(e)
3262
- {
3263
- var html = $.trim(this.$editor.html());
3264
-
3265
- html = html.replace(/<br\s?\/?>/i, '');
3266
- var thtml = html.replace(/<p>\s?<\/p>/gi, '');
3267
-
3268
- if (html === '' || thtml === '')
3269
- {
3270
- e.preventDefault();
3489
+ if (html === '' || thtml === '')
3490
+ {
3491
+ e.preventDefault();
3271
3492
 
3272
- var node = $(this.opts.emptyHtml).get(0);
3273
- this.$editor.html(node);
3274
- this.focus();
3493
+ var node = $(this.opts.emptyHtml).get(0);
3494
+ this.$editor.html(node);
3495
+ this.focus();
3496
+ }
3275
3497
  }
3276
3498
 
3277
3499
  this.sync();
@@ -3287,7 +3509,29 @@
3287
3509
  {
3288
3510
  if (node.tagName !== 'LI')
3289
3511
  {
3290
- this.formatBlock(tag, node);
3512
+ var parent = $(node).parent();
3513
+
3514
+ if (tag === 'p')
3515
+ {
3516
+ if ((node.tagName === 'P'
3517
+ && parent.size() != 0
3518
+ && parent[0].tagName === 'BLOCKQUOTE')
3519
+ ||
3520
+ node.tagName === 'BLOCKQUOTE')
3521
+ {
3522
+ this.formatQuote();
3523
+ return;
3524
+ }
3525
+ else if (this.opts.linebreaks) return;
3526
+ else
3527
+ {
3528
+ this.formatBlock(tag, node);
3529
+ }
3530
+ }
3531
+ else
3532
+ {
3533
+ this.formatBlock(tag, node);
3534
+ }
3291
3535
  }
3292
3536
 
3293
3537
  }, this));
@@ -3327,8 +3571,15 @@
3327
3571
  }
3328
3572
  else
3329
3573
  {
3574
+ var parent = this.getParent();
3575
+
3330
3576
  var node = $('<' + tag + '>').append(contents);
3331
3577
  $(block).replaceWith(node);
3578
+
3579
+ if (parent && parent.tagName == 'TD')
3580
+ {
3581
+ $(node).wrapAll('<td>');
3582
+ }
3332
3583
  }
3333
3584
  },
3334
3585
  formatChangeTag: function(fromElement, toTagName, save)
@@ -3348,25 +3599,110 @@
3348
3599
  {
3349
3600
  this.bufferSet();
3350
3601
 
3602
+ // paragraphy
3351
3603
  if (this.opts.linebreaks === false)
3352
3604
  {
3353
3605
  this.selectionSave();
3354
3606
 
3355
3607
  var blocks = this.getBlocks();
3608
+
3609
+ var blockquote = false;
3610
+ var blocksLen = blocks.length;
3356
3611
  if (blocks)
3357
3612
  {
3613
+ var data = '';
3614
+ var replaced = '';
3615
+ var replace = false;
3616
+ var paragraphsOnly = true;
3617
+
3618
+ $.each(blocks, function(i,s)
3619
+ {
3620
+ if (s.tagName !== 'P') paragraphsOnly = false;
3621
+ });
3622
+
3358
3623
  $.each(blocks, $.proxy(function(i,s)
3359
3624
  {
3360
3625
  if (s.tagName === 'BLOCKQUOTE')
3361
3626
  {
3362
3627
  this.formatBlock('p', s, false);
3363
3628
  }
3629
+ else if (s.tagName === 'P')
3630
+ {
3631
+ blockquote = $(s).parent();
3632
+ // from blockquote
3633
+ if (blockquote[0].tagName == 'BLOCKQUOTE')
3634
+ {
3635
+ var count = $(blockquote).children('p').size();
3636
+
3637
+ // one
3638
+ if (count == 1)
3639
+ {
3640
+ $(blockquote).replaceWith(s);
3641
+ }
3642
+ // all
3643
+ else if (count == blocksLen)
3644
+ {
3645
+ replace = 'blockquote';
3646
+ data += this.outerHtml(s);
3647
+ }
3648
+ // some
3649
+ else
3650
+ {
3651
+ replace = 'html';
3652
+ data += this.outerHtml(s);
3653
+
3654
+ if (i == 0)
3655
+ {
3656
+ $(s).addClass('redactor-replaced').empty();
3657
+ replaced = this.outerHtml(s);
3658
+ }
3659
+ else $(s).remove();
3660
+ }
3661
+ }
3662
+ // to blockquote
3663
+ else
3664
+ {
3665
+ if (paragraphsOnly === false || blocks.length == 1)
3666
+ {
3667
+ this.formatBlock('blockquote', s, false);
3668
+ }
3669
+ else
3670
+ {
3671
+ replace = 'paragraphs';
3672
+ data += this.outerHtml(s);
3673
+ }
3674
+ }
3675
+
3676
+ }
3364
3677
  else if (s.tagName !== 'LI')
3365
3678
  {
3366
3679
  this.formatBlock('blockquote', s, false);
3367
3680
  }
3368
3681
 
3369
3682
  }, this));
3683
+
3684
+ if (replace)
3685
+ {
3686
+ if (replace == 'paragraphs')
3687
+ {
3688
+ $(blocks[0]).replaceWith('<blockquote>' + data + '</blockquote>');
3689
+ $(blocks).remove();
3690
+ }
3691
+ else if (replace == 'blockquote')
3692
+ {
3693
+ $(blockquote).replaceWith(data);
3694
+ }
3695
+ else if (replace == 'html')
3696
+ {
3697
+ var html = this.$editor.html().replace(replaced, '</blockquote>' + data + '<blockquote>');
3698
+
3699
+ this.$editor.html(html);
3700
+ this.$editor.find('blockquote').each(function()
3701
+ {
3702
+ if ($.trim($(this).html()) == '') $(this).remove();
3703
+ })
3704
+ }
3705
+ }
3370
3706
  }
3371
3707
 
3372
3708
  this.selectionRestore();
@@ -3379,9 +3715,34 @@
3379
3715
  {
3380
3716
  this.selectionSave();
3381
3717
 
3382
- $(block).replaceWith($(block).html() + '<br>');
3718
+ var html = $.trim($(block).html());
3719
+ var selection = $.trim(this.getSelectionHtml());
3720
+
3721
+ html = html.replace(/<span(.*?)id="selection-marker(.*?)<\/span>/gi, '');
3722
+
3723
+ if (html == selection)
3724
+ {
3725
+ $(block).replaceWith($(block).html() + '<br>');
3726
+ }
3727
+ else
3728
+ {
3729
+ // replace
3730
+ this.inlineFormat('tmp');
3731
+ var tmp = this.$editor.find('tmp');
3732
+ tmp.empty();
3733
+
3734
+ var newhtml = this.$editor.html().replace('<tmp></tmp>', '</blockquote><span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>' + selection + '<blockquote>');
3735
+
3736
+ this.$editor.html(newhtml);
3737
+ tmp.remove();
3738
+ this.$editor.find('blockquote').each(function()
3739
+ {
3740
+ if ($.trim($(this).html()) == '') $(this).remove();
3741
+ })
3742
+ }
3383
3743
 
3384
3744
  this.selectionRestore();
3745
+ this.$editor.find('span#selection-marker-1').attr('id', false);
3385
3746
  }
3386
3747
  else
3387
3748
  {
@@ -3416,8 +3777,7 @@
3416
3777
  this.sync();
3417
3778
  },
3418
3779
 
3419
-
3420
- // BLOCK CLASS AND STYLE
3780
+ // BLOCK
3421
3781
  blockRemoveAttr: function(attr, value)
3422
3782
  {
3423
3783
  var nodes = this.getBlocks();
@@ -3463,7 +3823,7 @@
3463
3823
  this.sync();
3464
3824
  },
3465
3825
 
3466
- // INLINE CLASS AND STYLE
3826
+ // INLINE
3467
3827
  inlineRemoveClass: function(className)
3468
3828
  {
3469
3829
  this.selectionSave();
@@ -3544,27 +3904,32 @@
3544
3904
  this.inlineSetMethods(type, s, attr, value);
3545
3905
 
3546
3906
  }, this));
3907
+
3547
3908
  }
3548
3909
 
3549
3910
  this.selectionRestore();
3550
-
3551
3911
  this.sync();
3552
3912
  },
3553
3913
  inlineSetMethods: function(type, s, attr, value)
3554
3914
  {
3555
3915
  var parent = $(s).parent(), el;
3556
3916
 
3557
- if (parent && parent[0].tagName === 'SPAN' && parent[0].attributes.length != 0)
3917
+ var selectionHtml = this.getSelectionText();
3918
+ var parentHtml = $(parent).text();
3919
+ var selected = selectionHtml == parentHtml;
3920
+
3921
+ if (selected && parent && parent[0].tagName === 'INLINE' && parent[0].attributes.length != 0)
3558
3922
  {
3559
3923
  el = parent;
3560
3924
  $(s).replaceWith($(s).html());
3561
3925
  }
3562
3926
  else
3563
3927
  {
3564
- el = $('<span data-redactor="verified" data-redactor-inlineMethods>').append($(s).contents());
3928
+ el = $('<inline>').append($(s).contents());
3565
3929
  $(s).replaceWith(el);
3566
3930
  }
3567
3931
 
3932
+
3568
3933
  $(el)[type](attr, value);
3569
3934
 
3570
3935
  return el;
@@ -3585,9 +3950,13 @@
3585
3950
 
3586
3951
  $.each(nodes, $.proxy(function(i, node)
3587
3952
  {
3588
- if (!collapsed && node.tagName !== 'SPAN')
3953
+ if (!collapsed && node.tagName !== 'INLINE')
3589
3954
  {
3590
- if (node.parentNode.tagName === 'SPAN' && !$(node.parentNode).hasClass('redactor_editor'))
3955
+ var selectionHtml = this.getSelectionText();
3956
+ var parentHtml = $(node).parent().text();
3957
+ var selected = selectionHtml == parentHtml;
3958
+
3959
+ if (selected && node.parentNode.tagName === 'INLINE' && !$(node.parentNode).hasClass('redactor_editor'))
3591
3960
  {
3592
3961
  node = node.parentNode;
3593
3962
  }
@@ -3599,7 +3968,7 @@
3599
3968
  },
3600
3969
  inlineUnwrapSpan: function()
3601
3970
  {
3602
- var $spans = this.$editor.find('span[data-redactor-inlineMethods]');
3971
+ var $spans = this.$editor.find('inline');
3603
3972
 
3604
3973
  $.each($spans, $.proxy(function(i, span)
3605
3974
  {
@@ -3664,13 +4033,13 @@
3664
4033
 
3665
4034
  this.bufferSet();
3666
4035
 
3667
- var $html = $('<div>').append($.parseHTML(html));
4036
+ var $html = $('<div>').append(html);
3668
4037
  html = $html.html();
3669
4038
 
3670
4039
  html = this.cleanRemoveEmptyTags(html);
3671
4040
 
3672
4041
  // Update value
3673
- $html = $('<div>').append($.parseHTML(html));
4042
+ $html = $('<div>').append(html);
3674
4043
 
3675
4044
  var currBlock = this.getBlock();
3676
4045
 
@@ -3693,18 +4062,22 @@
3693
4062
  html = '<p>' + html + '</p>';
3694
4063
  }
3695
4064
 
3696
- // in the quote, we can insert text or links only
3697
- /*
3698
- if (currBlock.tagName == 'BLOCKQUOTE' && html.indexOf('<a') === -1)
3699
- {
3700
- this.insertText(html);
3701
- }
3702
- else
3703
- */
4065
+ html = this.setSpansVerifiedHtml(html);
4066
+
3704
4067
  if ($html.contents().length > 1 && currBlock
3705
- || $html.contents().is('p, :header, ul, ol, div, table, blockquote, pre, address, section, header, footer, aside, article'))
4068
+ || $html.contents().is('p, :header, ul, ol, li, div, table, td, blockquote, pre, address, section, header, footer, aside, article'))
3706
4069
  {
3707
- if (this.browser('msie')) this.document.selection.createRange().pasteHTML(html);
4070
+ if (this.browser('msie'))
4071
+ {
4072
+ if (!this.isIe11())
4073
+ {
4074
+ this.document.selection.createRange().pasteHTML(html);
4075
+ }
4076
+ else
4077
+ {
4078
+ this.execPasteFrag(html);
4079
+ }
4080
+ }
3708
4081
  else this.document.execCommand('inserthtml', false, html);
3709
4082
  }
3710
4083
  else this.insertHtmlAdvanced(html, false);
@@ -3721,10 +4094,15 @@
3721
4094
 
3722
4095
  this.observeStart();
3723
4096
 
4097
+ // set no editable
4098
+ this.setNonEditable();
4099
+
3724
4100
  if (sync !== false) this.sync();
3725
4101
  },
3726
4102
  insertHtmlAdvanced: function(html, sync)
3727
4103
  {
4104
+ html = this.setSpansVerifiedHtml(html);
4105
+
3728
4106
  var sel = this.getSelection();
3729
4107
 
3730
4108
  if (sel.getRangeAt && sel.rangeCount)
@@ -3758,15 +4136,35 @@
3758
4136
  }
3759
4137
 
3760
4138
  },
4139
+ insertBeforeCursor: function(html)
4140
+ {
4141
+ html = this.setSpansVerifiedHtml(html);
4142
+
4143
+ var node = $(html);
4144
+
4145
+ var space = document.createElement("span");
4146
+ space.innerHTML = "\u200B";
4147
+
4148
+ var range = this.getRange();
4149
+ range.insertNode(space);
4150
+ range.insertNode(node[0]);
4151
+ range.collapse(false);
4152
+
4153
+ var sel = this.getSelection();
4154
+ sel.removeAllRanges();
4155
+ sel.addRange(range);
4156
+
4157
+ this.sync();
4158
+ },
3761
4159
  insertText: function(html)
3762
4160
  {
3763
- var $html = $($.parseHTML(html));
4161
+ var $html = $(html);
3764
4162
 
3765
4163
  if ($html.length) html = $html.text();
3766
4164
 
3767
4165
  this.$editor.focus();
3768
4166
 
3769
- if (this.browser('msie')) this.document.selection.createRange().pasteHTML(html);
4167
+ if (this.browser('msie') && !this.isIe11()) this.document.selection.createRange().pasteHTML(html);
3770
4168
  else this.document.execCommand('inserthtml', false, html);
3771
4169
 
3772
4170
  this.sync();
@@ -3774,6 +4172,23 @@
3774
4172
  insertNode: function(node)
3775
4173
  {
3776
4174
  node = node[0] || node;
4175
+
4176
+ if (node.tagName == 'SPAN')
4177
+ {
4178
+ var replacementTag = 'inline';
4179
+
4180
+ var outer = node.outerHTML;
4181
+
4182
+ // Replace opening tag
4183
+ var regex = new RegExp('<' + node.tagName, 'i');
4184
+ var newTag = outer.replace(regex, '<' + replacementTag);
4185
+
4186
+ // Replace closing tag
4187
+ regex = new RegExp('</' + node.tagName, 'i');
4188
+ newTag = newTag.replace(regex, '</' + replacementTag);
4189
+ node = $(newTag)[0];
4190
+ }
4191
+
3777
4192
  var sel = this.getSelection();
3778
4193
  if (sel.getRangeAt && sel.rangeCount)
3779
4194
  {
@@ -3815,29 +4230,48 @@
3815
4230
  }
3816
4231
 
3817
4232
  },
3818
- insertAfterLastElement: function(element)
4233
+ insertAfterLastElement: function(element, parent)
3819
4234
  {
4235
+ if (typeof(parent) != 'undefined') element = parent;
4236
+
3820
4237
  if (this.isEndOfElement())
3821
4238
  {
3822
-
3823
- if ($($.trim(this.$editor.html())).get(0) != $.trim(element)
3824
- && this.$editor.contents().last()[0] !== element) return false;
3825
-
3826
- this.bufferSet();
3827
-
3828
- if (this.opts.linebreaks === false)
4239
+ if (this.opts.linebreaks)
3829
4240
  {
3830
- var node = $(this.opts.emptyHtml);
3831
- $(element).after(node);
3832
- this.selectionStart(node);
4241
+ var contents = $('<div>').append($.trim(this.$editor.html())).contents();
4242
+ if (this.outerHtml(contents.last()[0]) != this.outerHtml(element))
4243
+ {
4244
+ return false;
4245
+ }
3833
4246
  }
3834
4247
  else
3835
4248
  {
3836
- var node = $('<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>', this.document)[0];
3837
- $(element).after(node);
3838
- $(node).after(this.opts.invisibleSpace);
3839
- this.selectionRestore();
4249
+ if (this.$editor.contents().last()[0] !== element)
4250
+ {
4251
+ return false;
4252
+ }
3840
4253
  }
4254
+
4255
+ this.insertingAfterLastElement(element);
4256
+ }
4257
+ },
4258
+ insertingAfterLastElement: function(element)
4259
+ {
4260
+ this.bufferSet();
4261
+
4262
+ if (this.opts.linebreaks === false)
4263
+ {
4264
+ var node = $(this.opts.emptyHtml);
4265
+ $(element).after(node);
4266
+ this.selectionStart(node);
4267
+ }
4268
+ else
4269
+ {
4270
+ var node = $('<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>', this.document)[0];
4271
+ $(element).after(node);
4272
+ $(node).after(this.opts.invisibleSpace);
4273
+ this.selectionRestore();
4274
+ this.$editor.find('span#selection-marker-1').removeAttr('id');
3841
4275
  }
3842
4276
  },
3843
4277
  insertLineBreak: function()
@@ -3865,6 +4299,16 @@
3865
4299
  {
3866
4300
  html = this.callback('pasteBefore', false, html);
3867
4301
 
4302
+ // ie10 fix paste links
4303
+ if (this.browser('msie'))
4304
+ {
4305
+ var tmp = $.trim(html);
4306
+ if (tmp.search(/^<a(.*?)>(.*?)<\/a>$/i) == 0)
4307
+ {
4308
+ html = html.replace(/^<a(.*?)>(.*?)<\/a>$/i, "$2");
4309
+ }
4310
+ }
4311
+
3868
4312
  if (this.opts.pastePlainText)
3869
4313
  {
3870
4314
  var tmp = this.document.createElement('div');
@@ -3874,6 +4318,7 @@
3874
4318
  tmp.innerHTML = html;
3875
4319
  html = tmp.textContent || tmp.innerText;
3876
4320
 
4321
+ html = $.trim(html);
3877
4322
  html = html.replace('\n', '<br>');
3878
4323
  html = this.cleanParagraphy(html);
3879
4324
 
@@ -3890,8 +4335,13 @@
3890
4335
  }
3891
4336
 
3892
4337
  // ms word list
3893
- html = html.replace(/<p(.*?)class="MsoListParagraphCxSpFirst"([\w\W]*?)<\/p>/gi, '<ul><p$2</p>');
3894
- html = html.replace(/<p(.*?)class="MsoListParagraphCxSpLast"([\w\W]*?)<\/p>/gi, '<p$2</p></ul>');
4338
+ html = html.replace(/<p(.*?)class="MsoListParagraphCxSpFirst"([\w\W]*?)<\/p>/gi, '<ul><li$2</li>');
4339
+ html = html.replace(/<p(.*?)class="MsoListParagraphCxSpMiddle"([\w\W]*?)<\/p>/gi, '<li$2</li>');
4340
+ html = html.replace(/<p(.*?)class="MsoListParagraphCxSpLast"([\w\W]*?)<\/p>/gi, '<li$2</li></ul>');
4341
+ // one line
4342
+ html = html.replace(/<p(.*?)class="MsoListParagraph"([\w\W]*?)<\/p>/gi, '<ul><li$2</li></ul>');
4343
+ // remove ms word's bullet
4344
+ html = html.replace(/·/g, '');
3895
4345
 
3896
4346
  // remove comments and php tags
3897
4347
  html = html.replace(/<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi, '');
@@ -3911,6 +4361,8 @@
3911
4361
  html = html.replace(/<td><\/td>/gi, '[td]');
3912
4362
  html = html.replace(/<td>&nbsp;<\/td>/gi, '[td]');
3913
4363
  html = html.replace(/<td><br><\/td>/gi, '[td]');
4364
+ html = html.replace(/<td(.*?)colspan="(.*?)"(.*?)>([\w\W]*?)<\/td>/gi, '[td colspan="$2"]$4[/td]');
4365
+ html = html.replace(/<td(.*?)rowspan="(.*?)"(.*?)>([\w\W]*?)<\/td>/gi, '[td rowspan="$2"]$4[/td]');
3914
4366
  html = html.replace(/<a(.*?)href="(.*?)"(.*?)>([\w\W]*?)<\/a>/gi, '[a href="$2"]$4[/a]');
3915
4367
  html = html.replace(/<iframe(.*?)>([\w\W]*?)<\/iframe>/gi, '[iframe$1]$2[/iframe]');
3916
4368
  html = html.replace(/<video(.*?)>([\w\W]*?)<\/video>/gi, '[video$1]$2[/video]');
@@ -3918,7 +4370,6 @@
3918
4370
  html = html.replace(/<embed(.*?)>([\w\W]*?)<\/embed>/gi, '[embed$1]$2[/embed]');
3919
4371
  html = html.replace(/<object(.*?)>([\w\W]*?)<\/object>/gi, '[object$1]$2[/object]');
3920
4372
  html = html.replace(/<param(.*?)>/gi, '[param$1]');
3921
- html = html.replace(/<img(.*?)style="(.*?)"(.*?)>/gi, '[img$1$3]');
3922
4373
  html = html.replace(/<img(.*?)>/gi, '[img$1]');
3923
4374
 
3924
4375
  // remove classes
@@ -3929,10 +4380,11 @@
3929
4380
 
3930
4381
  // remove empty
3931
4382
  html = html.replace(/<[^\/>][^>]*>(\s*|\t*|\n*|&nbsp;|<br>)<\/[^>]+>/gi, '');
3932
-
3933
4383
  html = html.replace(/<div>\s*?\t*?\n*?(<ul>|<ol>|<p>)/gi, '$1');
3934
4384
 
3935
4385
  // revert
4386
+ html = html.replace(/\[td colspan="(.*?)"\]([\w\W]*?)\[\/td\]/gi, '<td colspan="$1">$2</td>');
4387
+ html = html.replace(/\[td rowspan="(.*?)"\]([\w\W]*?)\[\/td\]/gi, '<td rowspan="$1">$2</td>');
3936
4388
  html = html.replace(/\[td\]/gi, '<td>&nbsp;</td>');
3937
4389
  html = html.replace(/\[a href="(.*?)"\]([\w\W]*?)\[\/a\]/gi, '<a href="$1">$2</a>');
3938
4390
  html = html.replace(/\[iframe(.*?)\]([\w\W]*?)\[\/iframe\]/gi, '<iframe$1>$2</iframe>');
@@ -3960,7 +4412,6 @@
3960
4412
  html = this.cleanParagraphy(html);
3961
4413
  }
3962
4414
 
3963
-
3964
4415
  // remove span
3965
4416
  html = html.replace(/<span(.*?)>([\w\W]*?)<\/span>/gi, '$2');
3966
4417
 
@@ -3988,6 +4439,13 @@
3988
4439
  // remove safari local images
3989
4440
  html = html.replace(/<img src="webkit-fake-url\:\/\/(.*?)"(.*?)>/gi, '');
3990
4441
 
4442
+ // remove p in td
4443
+ html = html.replace(/<td(.*?)>(\s*|\t*|\n*)<p>([\w\W]*?)<\/p>(\s*|\t*|\n*)<\/td>/gi, '<td$1>$3</td>');
4444
+
4445
+ // remove divs
4446
+ html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '$2');
4447
+ html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '$2');
4448
+
3991
4449
  // FF specific
3992
4450
  this.pasteClipboardMozilla = false;
3993
4451
  if (this.browser('mozilla'))
@@ -4013,7 +4471,7 @@
4013
4471
  }
4014
4472
  }
4015
4473
 
4016
- // bullets
4474
+ // bullets again
4017
4475
  html = html.replace(/<p>•([\w\W]*?)<\/p>/gi, '<li>$1</li>');
4018
4476
 
4019
4477
  // ie inserts a blank font tags when pasting
@@ -4022,6 +4480,12 @@
4022
4480
  html = html.replace(/<font>([\w\W]*?)<\/font>/gi, '$1');
4023
4481
  }
4024
4482
 
4483
+ // ms word lists breakline
4484
+ html = html.replace(/<p>\n?<li>/gi, '<li>');
4485
+ if (this.browser('msie') && !this.isIe11())
4486
+ {
4487
+ html = html.replace(/\n/g, '');
4488
+ }
4025
4489
 
4026
4490
  this.pasteInsert(html);
4027
4491
 
@@ -4051,7 +4515,7 @@
4051
4515
  this.selectall = false;
4052
4516
  setTimeout($.proxy(function()
4053
4517
  {
4054
- rtePaste = false;
4518
+ this.rtePaste = false;
4055
4519
 
4056
4520
  // FF specific
4057
4521
  if (this.browser('mozilla'))
@@ -4065,10 +4529,9 @@
4065
4529
 
4066
4530
  }, this), 100);
4067
4531
 
4068
- if (this.opts.autoresize) $(this.document.body).scrollTop(this.saveScroll);
4532
+ if (this.opts.autoresize && this.fullscreen !== true) $(this.document.body).scrollTop(this.saveScroll);
4069
4533
  else this.$editor.scrollTop(this.saveScroll);
4070
4534
  },
4071
-
4072
4535
  pasteClipboardUploadMozilla: function()
4073
4536
  {
4074
4537
  var imgs = this.$editor.find('img[data-mozilla-paste-image]');
@@ -4085,7 +4548,7 @@
4085
4548
  },
4086
4549
  $.proxy(function(data)
4087
4550
  {
4088
- var json = $.parseJSON(data);
4551
+ var json = (typeof data === 'string' ? $.parseJSON(data) : data);
4089
4552
  $s.attr('src', json.filelink);
4090
4553
  $s.removeAttr('data-mozilla-paste-image');
4091
4554
 
@@ -4113,7 +4576,7 @@
4113
4576
  },
4114
4577
  $.proxy(function(data)
4115
4578
  {
4116
- var json = $.parseJSON(data);
4579
+ var json = (typeof data === 'string' ? $.parseJSON(data) : data);
4117
4580
 
4118
4581
  var html = '<img src="' + json.filelink + '" id="clipboard-image-marker" />';
4119
4582
  this.execCommand('inserthtml', html, false);
@@ -4187,12 +4650,10 @@
4187
4650
  setTimeout($.proxy(this.observeStart, this), 4);
4188
4651
  },
4189
4652
 
4190
-
4191
4653
  // OBSERVE
4192
4654
  observeStart: function()
4193
4655
  {
4194
4656
  this.observeImages();
4195
- this.observeTables();
4196
4657
 
4197
4658
  if (this.opts.observeLinks) this.observeLinks();
4198
4659
  },
@@ -4204,10 +4665,11 @@
4204
4665
  this.linkObserverTooltipClose(e);
4205
4666
 
4206
4667
  }, this));
4207
- },
4208
- observeTables: function()
4209
- {
4210
- this.$editor.find('table').on('click', $.proxy(this.tableObserver, this));
4668
+ $(document).on('click.redactor', $.proxy(function(e)
4669
+ {
4670
+ this.linkObserverTooltipClose(e);
4671
+
4672
+ }, this));
4211
4673
  },
4212
4674
  observeImages: function()
4213
4675
  {
@@ -4277,7 +4739,6 @@
4277
4739
  $('.redactor-link-tooltip').remove();
4278
4740
  },
4279
4741
 
4280
-
4281
4742
  // SELECTION
4282
4743
  getSelection: function()
4283
4744
  {
@@ -4294,7 +4755,7 @@
4294
4755
  {
4295
4756
  if (this.document.getSelection)
4296
4757
  {
4297
- var sel = this.document.getSelection();
4758
+ var sel = this.getSelection();
4298
4759
  if (sel.getRangeAt && sel.rangeCount) return sel.getRangeAt(0);
4299
4760
  }
4300
4761
 
@@ -4397,172 +4858,62 @@
4397
4858
 
4398
4859
  var textNodes = this.getTextNodesIn(el);
4399
4860
  var foundStart = false;
4400
- var charCount = 0, endCharCount;
4401
-
4402
- if (textNodes.length == 1 && start)
4403
- {
4404
- range.setStart(textNodes[0], start);
4405
- range.setEnd(textNodes[0], end);
4406
- }
4407
- else
4408
- {
4409
- for (var i = 0, textNode; textNode = textNodes[i++];)
4410
- {
4411
- endCharCount = charCount + textNode.length;
4412
- if (!foundStart && start >= charCount && (start < endCharCount || (start == endCharCount && i < textNodes.length)))
4413
- {
4414
- range.setStart(textNode, start - charCount);
4415
- foundStart = true;
4416
- }
4417
-
4418
- if (foundStart && end <= endCharCount)
4419
- {
4420
- range.setEnd( textNode, end - charCount );
4421
- break;
4422
- }
4423
-
4424
- charCount = endCharCount;
4425
- }
4426
- }
4427
-
4428
- var sel = this.getSelection();
4429
- sel.removeAllRanges();
4430
- sel.addRange( range );
4431
- },
4432
- getTextNodesIn: function (node)
4433
- {
4434
- var textNodes = [];
4435
-
4436
- if (node.nodeType == 3) textNodes.push(node);
4437
- else
4438
- {
4439
- var children = node.childNodes;
4440
- for (var i = 0, len = children.length; i < len; ++i)
4441
- {
4442
- textNodes.push.apply(textNodes, this.getTextNodesIn(children[i]));
4443
- }
4444
- }
4445
-
4446
- return textNodes;
4447
- },
4448
-
4449
- // SAVE & RESTORE
4450
- selectionSave: function()
4451
- {
4452
- if (!this.isFocused()) this.$editor.focus();
4453
-
4454
- if (!this.opts.rangy)
4455
- {
4456
- this.selectionCreateMarker(this.getRange());
4457
- }
4458
- // rangy
4459
- else
4460
- {
4461
- this.savedSel = rangy.saveSelection();
4462
- }
4463
- },
4464
- selectionCreateMarker: function(range, remove)
4465
- {
4466
- if (!range) return;
4467
-
4468
- var node1 = $('<span id="selection-marker-1" class="redactor-selection-marker">' + this.opts.invisibleSpace + '</span>', this.document)[0];
4469
- var node2 = $('<span id="selection-marker-2" class="redactor-selection-marker">' + this.opts.invisibleSpace + '</span>', this.document)[0];
4470
-
4471
- if (range.collapsed === true)
4472
- {
4473
- this.selectionSetMarker(range, node1, true);
4474
- }
4475
- else
4476
- {
4477
- this.selectionSetMarker(range, node1, true);
4478
- this.selectionSetMarker(range, node2, false);
4479
- }
4480
-
4481
- this.savedSel = this.$editor.html();
4482
-
4483
- this.selectionRestore(false, false);
4484
- },
4485
- selectionSetMarker: function(range, node, type)
4486
- {
4487
- var boundaryRange = range.cloneRange();
4488
-
4489
- boundaryRange.collapse(type);
4490
-
4491
- boundaryRange.insertNode(node);
4492
- boundaryRange.detach();
4493
- },
4494
- selectionRestore: function(replace, remove)
4495
- {
4496
- if (!this.opts.rangy)
4497
- {
4498
- if (replace === true && this.savedSel)
4499
- {
4500
- this.$editor.html(this.savedSel);
4501
- }
4502
-
4503
- var node1 = this.$editor.find('span#selection-marker-1');
4504
- var node2 = this.$editor.find('span#selection-marker-2');
4505
-
4506
- if (this.browser('mozilla'))
4507
- {
4508
- this.$editor.focus();
4509
- }
4510
- else if (!this.isFocused())
4511
- {
4512
- this.$editor.focus();
4513
- }
4514
-
4515
- if (node1.length != 0 && node2.length != 0)
4516
- {
4517
- this.selectionSet(node1[0], 0, node2[0], 0);
4518
- }
4519
- else if (node1.length != 0)
4520
- {
4521
- this.selectionSet(node1[0], 0, null, 0);
4522
- }
4861
+ var charCount = 0, endCharCount;
4523
4862
 
4524
- if (remove !== false)
4525
- {
4526
- this.selectionRemoveMarkers();
4527
- this.savedSel = false;
4528
- }
4529
- }
4530
- // rangy
4531
- else
4863
+ if (textNodes.length == 1 && start)
4532
4864
  {
4533
- rangy.restoreSelection(this.savedSel);
4865
+ range.setStart(textNodes[0], start);
4866
+ range.setEnd(textNodes[0], end);
4534
4867
  }
4535
- },
4536
- selectionRemoveMarkers: function(type)
4537
- {
4538
- if (!this.opts.rangy)
4868
+ else
4539
4869
  {
4540
- $.each(this.$editor.find('span.redactor-selection-marker'), function()
4870
+ for (var i = 0, textNode; textNode = textNodes[i++];)
4541
4871
  {
4542
- var html = $.trim($(this).html().replace(/[^\u0000-\u1C7F]/g, ''));
4543
- if (html == '')
4872
+ endCharCount = charCount + textNode.length;
4873
+ if (!foundStart && start >= charCount && (start < endCharCount || (start == endCharCount && i < textNodes.length)))
4544
4874
  {
4545
- $(this).remove();
4875
+ range.setStart(textNode, start - charCount);
4876
+ foundStart = true;
4546
4877
  }
4547
- else
4878
+
4879
+ if (foundStart && end <= endCharCount)
4548
4880
  {
4549
- $(this).removeAttr('class').removeAttr('id');
4881
+ range.setEnd( textNode, end - charCount );
4882
+ break;
4550
4883
  }
4551
- });
4884
+
4885
+ charCount = endCharCount;
4886
+ }
4552
4887
  }
4553
- // rangy
4888
+
4889
+ var sel = this.getSelection();
4890
+ sel.removeAllRanges();
4891
+ sel.addRange( range );
4892
+ },
4893
+ getTextNodesIn: function (node)
4894
+ {
4895
+ var textNodes = [];
4896
+
4897
+ if (node.nodeType == 3) textNodes.push(node);
4554
4898
  else
4555
4899
  {
4556
- rangy.removeMarkers(this.savedSel);
4900
+ var children = node.childNodes;
4901
+ for (var i = 0, len = children.length; i < len; ++i)
4902
+ {
4903
+ textNodes.push.apply(textNodes, this.getTextNodesIn(children[i]));
4904
+ }
4557
4905
  }
4906
+
4907
+ return textNodes;
4558
4908
  },
4909
+
4559
4910
  // GET ELEMENTS
4560
4911
  getCurrent: function()
4561
4912
  {
4562
4913
  var el = false;
4563
4914
  var sel = this.getSelection();
4564
4915
 
4565
- if (sel.rangeCount > 0) el = sel.getRangeAt(0).startContainer;
4916
+ if (sel && sel.rangeCount > 0) el = sel.getRangeAt(0).startContainer;
4566
4917
 
4567
4918
  return this.isParentRedactor(el);
4568
4919
  },
@@ -4618,47 +4969,8 @@
4618
4969
  {
4619
4970
  return this.rTestBlock.test(tag);
4620
4971
  },
4621
- getSelectedNodes: function(range)
4622
- {
4623
- if (typeof range == 'undefined' || range == false) var range = this.getRange()
4624
- if (range && range.collapsed === true)
4625
- {
4626
- return [this.getCurrent()];
4627
- }
4628
-
4629
- var sel = this.getSelection();
4630
- try {
4631
- var frag = sel.getRangeAt(0).cloneContents();
4632
- }
4633
- catch(e)
4634
- {
4635
- return(false);
4636
- }
4637
-
4638
- var tempspan = this.document.createElement("span");
4639
- tempspan.appendChild(frag);
4640
-
4641
- window.selnodes = tempspan.childNodes;
4642
-
4643
- var len = selnodes.length;
4644
- var output = [];
4645
- for(var i = 0, u = len; i<u; i++)
4646
- {
4647
- output.push(selnodes[i]);
4648
- }
4649
-
4650
- if (output.length == 0) output.push(this.getCurrent());
4651
-
4652
- return output;
4653
- },
4654
4972
  getNodes: function(range, tag)
4655
4973
  {
4656
- if (this.opts.linebreaks)
4657
- {
4658
- return this.getSelectedNodes(range);
4659
- }
4660
-
4661
-
4662
4974
  if (typeof range == 'undefined' || range == false) var range = this.getRange();
4663
4975
  if (range && range.collapsed === true)
4664
4976
  {
@@ -4711,6 +5023,13 @@
4711
5023
  }
4712
5024
  }
4713
5025
 
5026
+ // last element filtering
5027
+ var last = finalnodes[finalnodes.length-1];
5028
+ if (this.nodeTestBlocks(last))
5029
+ {
5030
+ finalnodes = finalnodes.slice(0, -1);
5031
+ }
5032
+
4714
5033
  return finalnodes;
4715
5034
  },
4716
5035
  getElement: function(node)
@@ -4746,7 +5065,7 @@
4746
5065
  node = range.startContainer;
4747
5066
  while (node && node != range.commonAncestorContainer)
4748
5067
  {
4749
- rangeNodes.unshift( node );
5068
+ rangeNodes.unshift(node);
4750
5069
  node = node.parentNode;
4751
5070
  }
4752
5071
 
@@ -4767,7 +5086,6 @@
4767
5086
  }
4768
5087
  },
4769
5088
 
4770
-
4771
5089
  // GET SELECTION HTML OR TEXT
4772
5090
  getSelectionText: function()
4773
5091
  {
@@ -4793,6 +5111,117 @@
4793
5111
  return this.syncClean(html);
4794
5112
  },
4795
5113
 
5114
+ // SAVE & RESTORE
5115
+ selectionSave: function()
5116
+ {
5117
+ if (!this.isFocused()) this.$editor.focus();
5118
+
5119
+ if (!this.opts.rangy)
5120
+ {
5121
+ this.selectionCreateMarker(this.getRange());
5122
+ }
5123
+ // rangy
5124
+ else
5125
+ {
5126
+ this.savedSel = rangy.saveSelection();
5127
+ }
5128
+ },
5129
+ selectionCreateMarker: function(range, remove)
5130
+ {
5131
+ if (!range) return;
5132
+
5133
+ var node1 = $('<span id="selection-marker-1" class="redactor-selection-marker">' + this.opts.invisibleSpace + '</span>', this.document)[0];
5134
+ var node2 = $('<span id="selection-marker-2" class="redactor-selection-marker">' + this.opts.invisibleSpace + '</span>', this.document)[0];
5135
+
5136
+ if (range.collapsed === true)
5137
+ {
5138
+ this.selectionSetMarker(range, node1, true);
5139
+ }
5140
+ else
5141
+ {
5142
+ this.selectionSetMarker(range, node1, true);
5143
+ this.selectionSetMarker(range, node2, false);
5144
+ }
5145
+
5146
+ this.savedSel = this.$editor.html();
5147
+
5148
+ this.selectionRestore(false, false);
5149
+ },
5150
+ selectionSetMarker: function(range, node, type)
5151
+ {
5152
+ var boundaryRange = range.cloneRange();
5153
+
5154
+ boundaryRange.collapse(type);
5155
+
5156
+ boundaryRange.insertNode(node);
5157
+ boundaryRange.detach();
5158
+ },
5159
+ selectionRestore: function(replace, remove)
5160
+ {
5161
+ if (!this.opts.rangy)
5162
+ {
5163
+ if (replace === true && this.savedSel)
5164
+ {
5165
+ this.$editor.html(this.savedSel);
5166
+ }
5167
+
5168
+ var node1 = this.$editor.find('span#selection-marker-1');
5169
+ var node2 = this.$editor.find('span#selection-marker-2');
5170
+
5171
+ if (this.browser('mozilla'))
5172
+ {
5173
+ this.$editor.focus();
5174
+ }
5175
+ else if (!this.isFocused())
5176
+ {
5177
+ this.$editor.focus();
5178
+ }
5179
+
5180
+ if (node1.length != 0 && node2.length != 0)
5181
+ {
5182
+ this.selectionSet(node1[0], 0, node2[0], 0);
5183
+ }
5184
+ else if (node1.length != 0)
5185
+ {
5186
+ this.selectionSet(node1[0], 0, null, 0);
5187
+ }
5188
+
5189
+ if (remove !== false)
5190
+ {
5191
+ this.selectionRemoveMarkers();
5192
+ this.savedSel = false;
5193
+ }
5194
+ }
5195
+ // rangy
5196
+ else
5197
+ {
5198
+ rangy.restoreSelection(this.savedSel);
5199
+ }
5200
+ },
5201
+ selectionRemoveMarkers: function(type)
5202
+ {
5203
+ if (!this.opts.rangy)
5204
+ {
5205
+ $.each(this.$editor.find('span.redactor-selection-marker'), function()
5206
+ {
5207
+ var html = $.trim($(this).html().replace(/[^\u0000-\u1C7F]/g, ''));
5208
+ if (html == '')
5209
+ {
5210
+ $(this).remove();
5211
+ }
5212
+ else
5213
+ {
5214
+ $(this).removeAttr('class').removeAttr('id');
5215
+ }
5216
+ });
5217
+ }
5218
+ // rangy
5219
+ else
5220
+ {
5221
+ rangy.removeMarkers(this.savedSel);
5222
+ }
5223
+ },
5224
+
4796
5225
  // TABLE
4797
5226
  tableShow: function()
4798
5227
  {
@@ -4843,20 +5272,13 @@
4843
5272
  this.selectionRestore();
4844
5273
 
4845
5274
  var current = this.getBlock() || this.getCurrent();
4846
- if (current && current.tagName != 'BODY')
4847
- {
4848
- $(current).after(html)
4849
- }
4850
- else
4851
- {
4852
- this.insertHtmlAdvanced(html, false);
4853
5275
 
4854
- }
5276
+ if (current && current.tagName != 'BODY') $(current).after(html)
5277
+ else this.insertHtmlAdvanced(html, false);
4855
5278
 
4856
5279
  this.selectionRestore();
4857
5280
 
4858
5281
  var table = this.$editor.find('#table' + tableId);
4859
- this.tableObserver(table);
4860
5282
  this.buttonActiveObserver();
4861
5283
 
4862
5284
  table.find('span#selection-marker-1').remove();
@@ -4864,92 +5286,98 @@
4864
5286
 
4865
5287
  this.sync();
4866
5288
  },
4867
- tableObserver: function(e)
4868
- {
4869
- this.$table = $(e.target || e).closest('table');
4870
-
4871
- this.$tbody = $(e.target).closest('tbody');
4872
- this.$thead = this.$table.find('thead');
4873
-
4874
- this.$current_td = $(e.target || this.$table.find('td').first());
4875
- this.$current_tr = $(e.target || this.$table.find('tr').first()).closest('tr');
4876
- },
4877
5289
  tableDeleteTable: function()
4878
5290
  {
4879
- this.bufferSet();
5291
+ var $table = $(this.getParent()).closest('table');
5292
+ if (!this.isParentRedactor($table)) return false;
5293
+ if ($table.size() == 0) return false;
4880
5294
 
4881
- if (!this.$table) return;
5295
+ this.bufferSet();
4882
5296
 
4883
- this.$table.remove();
4884
- this.$table = false;
5297
+ $table.remove();
4885
5298
  this.sync();
4886
-
4887
5299
  },
4888
5300
  tableDeleteRow: function()
4889
5301
  {
4890
- this.bufferSet();
5302
+ var $table = $(this.getParent()).closest('table');
5303
+ if (!this.isParentRedactor($table)) return false;
5304
+ if ($table.size() == 0) return false;
4891
5305
 
4892
- if (!this.$current_tr) return;
5306
+ this.bufferSet();
4893
5307
 
4894
- // Set the focus correctly
4895
- var $focusTR = this.$current_tr.prev().length ? this.$current_tr.prev() : this.$current_tr.next();
4896
- if ($focusTR.length)
5308
+ var $current_tr = $(this.getParent()).closest('tr');
5309
+ var $focus_tr = $current_tr.prev().length ? $current_tr.prev() : $current_tr.next();
5310
+ if ($focus_tr.length)
4897
5311
  {
4898
- var $focusTD = $focusTR.children('td' ).first();
4899
- if ($focusTD.length)
5312
+ var $focus_td = $focus_tr.children('td' ).first();
5313
+ if ($focus_td.length)
4900
5314
  {
4901
- $focusTD.prepend('<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>');
4902
- this.selectionRestore();
5315
+ $focus_td.prepend('<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>');
4903
5316
  }
4904
5317
  }
4905
5318
 
4906
- this.$current_tr.remove();
5319
+ $current_tr.remove();
5320
+ this.selectionRestore();
4907
5321
  this.sync();
4908
5322
  },
4909
5323
  tableDeleteColumn: function()
4910
5324
  {
5325
+ var $table = $(this.getParent()).closest('table');
5326
+ if (!this.isParentRedactor($table)) return false;
5327
+ if ($table.size() == 0) return false;
5328
+
4911
5329
  this.bufferSet();
4912
- var index = this.$current_td.get(0).cellIndex;
5330
+
5331
+ var $current_td = $(this.getParent()).closest('td');
5332
+ var index = $current_td.get(0).cellIndex;
4913
5333
 
4914
5334
  // Set the focus correctly
4915
- this.$table.find('tr').each($.proxy(function(i, elem)
5335
+ $table.find('tr').each($.proxy(function(i, elem)
4916
5336
  {
4917
5337
  var focusIndex = index - 1 < 0 ? index + 1 : index - 1;
4918
5338
  if (i === 0)
4919
5339
  {
4920
5340
  $(elem).find('td').eq(focusIndex).prepend('<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>');
4921
- this.selectionRestore();
4922
5341
  }
4923
5342
 
4924
5343
  $(elem).find('td').eq(index).remove();
4925
5344
 
4926
5345
  }, this));
4927
5346
 
5347
+ this.selectionRestore();
4928
5348
  this.sync();
4929
5349
  },
4930
5350
  tableAddHead: function()
4931
5351
  {
5352
+ var $table = $(this.getParent()).closest('table');
5353
+ if (!this.isParentRedactor($table)) return false;
5354
+ if ($table.size() == 0) return false;
5355
+
4932
5356
  this.bufferSet();
4933
5357
 
4934
- if (this.$table.find('thead').size() !== 0) this.tableDeleteHead();
5358
+ if ($table.find('thead').size() !== 0) this.tableDeleteHead();
4935
5359
  else
4936
5360
  {
4937
- var tr = this.$table.find('tr').first().clone();
4938
- tr.find('td').html( this.opts.invisibleSpace );
4939
- this.$thead = $('<thead></thead>');
4940
- this.$thead.append(tr);
4941
- this.$table.prepend(this.$thead);
5361
+ var tr = $table.find('tr').first().clone();
5362
+ tr.find('td').html(this.opts.invisibleSpace);
5363
+ $thead = $('<thead></thead>');
5364
+ $thead.append(tr);
5365
+ $table.prepend($thead);
4942
5366
 
4943
5367
  this.sync();
4944
5368
  }
4945
5369
  },
4946
5370
  tableDeleteHead: function()
4947
5371
  {
4948
- this.bufferSet();
5372
+ var $table = $(this.getParent()).closest('table');
5373
+ if (!this.isParentRedactor($table)) return false;
5374
+ var $thead = $table.find('thead');
5375
+
5376
+ if ($thead.size() == 0) return false;
4949
5377
 
4950
- $(this.$thead).remove();
4951
- this.$thead = false;
5378
+ this.bufferSet();
4952
5379
 
5380
+ $thead.remove();
4953
5381
  this.sync();
4954
5382
  },
4955
5383
  tableAddRowAbove: function()
@@ -4970,29 +5398,41 @@
4970
5398
  },
4971
5399
  tableAddRow: function(type)
4972
5400
  {
5401
+ var $table = $(this.getParent()).closest('table');
5402
+ if (!this.isParentRedactor($table)) return false;
5403
+ if ($table.size() == 0) return false;
5404
+
4973
5405
  this.bufferSet();
4974
5406
 
4975
- var new_tr = this.$current_tr.clone();
5407
+ var $current_tr = $(this.getParent()).closest('tr');
5408
+ var new_tr = $current_tr.clone();
4976
5409
  new_tr.find('td').html(this.opts.invisibleSpace);
4977
5410
 
4978
- if (type === 'after') this.$current_tr.after(new_tr);
4979
- else this.$current_tr.before(new_tr);
5411
+ if (type === 'after') $current_tr.after(new_tr);
5412
+ else $current_tr.before(new_tr);
4980
5413
 
4981
5414
  this.sync();
4982
5415
  },
4983
5416
  tableAddColumn: function (type)
4984
5417
  {
5418
+ var $table = $(this.getParent()).closest('table');
5419
+ if (!this.isParentRedactor($table)) return false;
5420
+ if ($table.size() == 0) return false;
5421
+
4985
5422
  this.bufferSet();
4986
5423
 
4987
5424
  var index = 0;
4988
5425
 
4989
- this.$current_tr.find('td').each($.proxy(function(i, elem)
5426
+ var $current_tr = $(this.getParent()).closest('tr');
5427
+ var $current_td = $(this.getParent()).closest('td');
5428
+
5429
+ $current_tr.find('td').each($.proxy(function(i, elem)
4990
5430
  {
4991
- if ($(elem)[0] === this.$current_td[0]) index = i;
5431
+ if ($(elem)[0] === $current_td[0]) index = i;
4992
5432
 
4993
5433
  }, this));
4994
5434
 
4995
- this.$table.find('tr').each($.proxy(function(i, elem)
5435
+ $table.find('tr').each($.proxy(function(i, elem)
4996
5436
  {
4997
5437
  var $current = $(elem).find('td').eq(index);
4998
5438
 
@@ -5189,10 +5629,24 @@
5189
5629
  }
5190
5630
  else
5191
5631
  {
5192
- this.exec('inserthtml', a);
5632
+ var $a = $(a).addClass('redactor-added-link');
5633
+ this.exec('inserthtml', this.outerHtml($a), false);
5634
+ this.$editor.find('a.redactor-added-link').removeAttr('style').removeClass('redactor-added-link').each(function()
5635
+ {
5636
+ if (this.className == '') $(this).removeAttr('class');
5637
+ });
5638
+ this.sync();
5193
5639
  }
5194
5640
  }
5195
5641
 
5642
+ // link tooltip
5643
+ setTimeout($.proxy(function()
5644
+ {
5645
+
5646
+ if (this.opts.observeLinks) this.observeLinks();
5647
+
5648
+ }, this), 5);
5649
+
5196
5650
  this.modalClose();
5197
5651
  },
5198
5652
 
@@ -5223,7 +5677,8 @@
5223
5677
  {
5224
5678
  this.callback('fileUploadError', json);
5225
5679
 
5226
- }, this)
5680
+ }, this),
5681
+ uploadParam: this.opts.fileUploadParam
5227
5682
  });
5228
5683
  }
5229
5684
 
@@ -5366,7 +5821,8 @@
5366
5821
  {
5367
5822
  this.callback('imageUploadError', json);
5368
5823
 
5369
- }, this)
5824
+ }, this),
5825
+ uploadParam: this.opts.imageUploadParam
5370
5826
  });
5371
5827
  }
5372
5828
  }
@@ -5460,7 +5916,7 @@
5460
5916
 
5461
5917
  }, this);
5462
5918
 
5463
- this.modalInit(this.opts.curLang.image, this.opts.modal_image_edit, 380, callback);
5919
+ this.modalInit(this.opts.curLang.edit, this.opts.modal_image_edit, 380, callback);
5464
5920
 
5465
5921
  },
5466
5922
  imageRemove: function(el)
@@ -5491,14 +5947,17 @@
5491
5947
 
5492
5948
  if (floating === 'left')
5493
5949
  {
5494
- $el.css({ 'float': 'left', 'margin': '0 ' + this.opts.imageFloatMargin + ' ' + this.opts.imageFloatMargin + ' 0' });
5950
+ this.imageMargin = '0 ' + this.opts.imageFloatMargin + ' ' + this.opts.imageFloatMargin + ' 0';
5951
+ $el.css({ 'float': 'left', 'margin': this.imageMargin });
5495
5952
  }
5496
5953
  else if (floating === 'right')
5497
5954
  {
5498
- $el.css({ 'float': 'right', 'margin': '0 0 ' + this.opts.imageFloatMargin + ' ' + this.opts.imageFloatMargin + '' });
5955
+ this.imageMargin = '0 0 ' + this.opts.imageFloatMargin + ' ' + this.opts.imageFloatMargin + '';
5956
+ $el.css({ 'float': 'right', 'margin': this.imageMargin });
5499
5957
  }
5500
5958
  else
5501
5959
  {
5960
+ this.imageMargin = '0px';
5502
5961
  var imageBox = $el.closest('#redactor-image-box');
5503
5962
  if (imageBox.size() != 0) imageBox.css({ 'float': '', 'margin': '' });
5504
5963
  $el.css({ 'float': '', 'margin': '' });
@@ -5566,11 +6025,12 @@
5566
6025
 
5567
6026
  this.$editor.find('#redactor-image-editter, #redactor-image-resizer').remove();
5568
6027
 
5569
- var margin = imageBox.css('margin');
5570
- if (margin != '0px')
6028
+
6029
+ if (this.imageMargin != '0px')
5571
6030
  {
5572
- imageBox.find('img').css('margin', margin);
6031
+ imageBox.find('img').css('margin', this.imageMargin);
5573
6032
  imageBox.css('margin', '');
6033
+ this.imageMargin = '0px';
5574
6034
  }
5575
6035
 
5576
6036
  imageBox.find('img').css('opacity', '');
@@ -5693,6 +6153,7 @@
5693
6153
 
5694
6154
  if (this.keyCode.BACKSPACE == key || this.keyCode.DELETE == key)
5695
6155
  {
6156
+ this.bufferSet();
5696
6157
  this.imageResizeHide(false);
5697
6158
  this.imageRemove($image);
5698
6159
  }
@@ -5717,10 +6178,10 @@
5717
6178
  });
5718
6179
  imageBox.attr('contenteditable', false);
5719
6180
 
5720
- var margin = $image.css('margin');
5721
- if (margin != '0px')
6181
+ this.imageMargin = $image[0].style.margin;
6182
+ if (this.imageMargin != '0px')
5722
6183
  {
5723
- imageBox.css('margin', margin);
6184
+ imageBox.css('margin', this.imageMargin);
5724
6185
  $image.css('margin', '');
5725
6186
  }
5726
6187
 
@@ -5774,7 +6235,8 @@
5774
6235
  {
5775
6236
  var img = '<img id="image-marker" src="' + $(e.target).attr('rel') + '" alt="' + $(e.target).attr('title') + '" />';
5776
6237
 
5777
- if (this.opts.paragraphy) img = '<p>' + img + '</p>';
6238
+ var parent = this.getParent();
6239
+ if (this.opts.paragraphy && $(parent).closest('li').size() == 0) img = '<p>' + img + '</p>';
5778
6240
 
5779
6241
  this.imageInsert(img, true);
5780
6242
  },
@@ -5806,7 +6268,12 @@
5806
6268
  if (link !== true)
5807
6269
  {
5808
6270
  html = '<img id="image-marker" src="' + json.filelink + '" />';
5809
- if (this.opts.paragraphy) html = '<p>' + html + '</p>';
6271
+
6272
+ var parent = this.getParent();
6273
+ if (this.opts.paragraphy && $(parent).closest('li').size() == 0)
6274
+ {
6275
+ html = '<p>' + html + '</p>';
6276
+ }
5810
6277
  }
5811
6278
  else
5812
6279
  {
@@ -5844,7 +6311,7 @@
5844
6311
  + '<label>' + this.opts.curLang.filename + '</label>'
5845
6312
  + '<input type="text" id="redactor_filename" class="redactor_input" />'
5846
6313
  + '<div style="margin-top: 7px;">'
5847
- + '<input type="file" id="redactor_file" name="file" />'
6314
+ + '<input type="file" id="redactor_file" name="' + this.opts.fileUploadParam + '" />'
5848
6315
  + '</div>'
5849
6316
  + '</form>'
5850
6317
  + '</section>',
@@ -5864,9 +6331,9 @@
5864
6331
  + '</select>'
5865
6332
  + '</section>'
5866
6333
  + '<footer>'
5867
- + '<button id="redactor_image_delete_btn" class="redactor_modal_btn">' + this.opts.curLang._delete + '</button>&nbsp;&nbsp;&nbsp;'
6334
+ + '<button id="redactor_image_delete_btn" class="redactor_modal_btn redactor_modal_delete_btn">' + this.opts.curLang._delete + '</button>&nbsp;&nbsp;&nbsp;'
5868
6335
  + '<button class="redactor_modal_btn redactor_btn_modal_close">' + this.opts.curLang.cancel + '</button>'
5869
- + '<input type="button" name="save" class="redactor_modal_btn" id="redactorSaveBtn" value="' + this.opts.curLang.save + '" />'
6336
+ + '<input type="button" name="save" class="redactor_modal_btn redactor_modal_action_btn" id="redactorSaveBtn" value="' + this.opts.curLang.save + '" />'
5870
6337
  + '</footer>',
5871
6338
 
5872
6339
  modal_image: String()
@@ -5881,7 +6348,7 @@
5881
6348
  + '</div>'
5882
6349
  + '<form id="redactorInsertImageForm" method="post" action="" enctype="multipart/form-data">'
5883
6350
  + '<div id="redactor_tab1" class="redactor_tab">'
5884
- + '<input type="file" id="redactor_file" name="file" />'
6351
+ + '<input type="file" id="redactor_file" name="' + this.opts.imageUploadParam + '" />'
5885
6352
  + '</div>'
5886
6353
  + '<div id="redactor_tab2" class="redactor_tab" style="display: none;">'
5887
6354
  + '<div id="redactor_image_box"></div>'
@@ -5894,7 +6361,7 @@
5894
6361
  + '</section>'
5895
6362
  + '<footer>'
5896
6363
  + '<button class="redactor_modal_btn redactor_btn_modal_close">' + this.opts.curLang.cancel + '</button>'
5897
- + '<input type="button" name="upload" class="redactor_modal_btn" id="redactor_upload_btn" value="' + this.opts.curLang.insert + '" />'
6364
+ + '<input type="button" name="upload" class="redactor_modal_btn redactor_modal_action_btn" id="redactor_upload_btn" value="' + this.opts.curLang.insert + '" />'
5898
6365
  + '</footer>',
5899
6366
 
5900
6367
  modal_link: String()
@@ -5929,7 +6396,7 @@
5929
6396
  + '</section>'
5930
6397
  + '<footer>'
5931
6398
  + '<button class="redactor_modal_btn redactor_btn_modal_close">' + this.opts.curLang.cancel + '</button>'
5932
- + '<input type="button" class="redactor_modal_btn" id="redactor_insert_link_btn" value="' + this.opts.curLang.insert + '" />'
6399
+ + '<input type="button" class="redactor_modal_btn redactor_modal_action_btn" id="redactor_insert_link_btn" value="' + this.opts.curLang.insert + '" />'
5933
6400
  + '</footer>',
5934
6401
 
5935
6402
  modal_table: String()
@@ -5941,7 +6408,7 @@
5941
6408
  + '</section>'
5942
6409
  + '<footer>'
5943
6410
  + '<button class="redactor_modal_btn redactor_btn_modal_close">' + this.opts.curLang.cancel + '</button>'
5944
- + '<input type="button" name="upload" class="redactor_modal_btn" id="redactor_insert_table_btn" value="' + this.opts.curLang.insert + '" />'
6411
+ + '<input type="button" name="upload" class="redactor_modal_btn redactor_modal_action_btn" id="redactor_insert_table_btn" value="' + this.opts.curLang.insert + '" />'
5945
6412
  + '</footer>',
5946
6413
 
5947
6414
  modal_video: String()
@@ -5953,7 +6420,7 @@
5953
6420
  + '</section>'
5954
6421
  + '<footer>'
5955
6422
  + '<button class="redactor_modal_btn redactor_btn_modal_close">' + this.opts.curLang.cancel + '</button>'
5956
- + '<input type="button" class="redactor_modal_btn" id="redactor_insert_video_btn" value="' + this.opts.curLang.insert + '" />'
6423
+ + '<input type="button" class="redactor_modal_btn redactor_modal_action_btn" id="redactor_insert_video_btn" value="' + this.opts.curLang.insert + '" />'
5957
6424
  + '</footer>'
5958
6425
 
5959
6426
  });
@@ -6051,7 +6518,14 @@
6051
6518
  $redactorModal.find('.redactor_btn_modal_close').on('click', $.proxy(this.modalClose, this));
6052
6519
 
6053
6520
  // save scroll
6054
- if (this.opts.autoresize === true) this.saveModalScroll = this.document.body.scrollTop;
6521
+ if (this.opts.autoresize === true)
6522
+ {
6523
+ this.saveModalScroll = this.document.body.scrollTop;
6524
+ }
6525
+ else
6526
+ {
6527
+ this.saveModalScroll = this.$editor.scrollTop();
6528
+ }
6055
6529
 
6056
6530
  if (this.isMobile() === false)
6057
6531
  {
@@ -6124,7 +6598,14 @@
6124
6598
  this.selectionRestore();
6125
6599
 
6126
6600
  // restore scroll
6127
- if (this.opts.autoresize && this.saveModalScroll) $(this.document.body).scrollTop(this.saveModalScroll);
6601
+ if (this.opts.autoresize && this.saveModalScroll)
6602
+ {
6603
+ $(this.document.body).scrollTop(this.saveModalScroll);
6604
+ }
6605
+ else if (this.opts.autoresize === false && this.saveModalScroll)
6606
+ {
6607
+ this.$editor.scrollTop(this.saveModalScroll);
6608
+ }
6128
6609
 
6129
6610
  }, this));
6130
6611
 
@@ -6143,7 +6624,6 @@
6143
6624
  $('#redactor_tab' + num).show();
6144
6625
  },
6145
6626
 
6146
-
6147
6627
  // S3
6148
6628
  s3handleFileSelect: function(e)
6149
6629
  {
@@ -6164,10 +6644,14 @@
6164
6644
  s3executeOnSignedUrl: function(file, callback)
6165
6645
  {
6166
6646
  var xhr = new XMLHttpRequest();
6167
- xhr.open('GET', this.opts.s3 + '?name=' + file.name + '&type=' + file.type, true);
6647
+
6648
+ var mark = '?';
6649
+ if (this.opts.s3.search(/\?/) != '-1') mark = '&';
6650
+
6651
+ xhr.open('GET', this.opts.s3 + mark + 'name=' + file.name + '&type=' + file.type, true);
6168
6652
 
6169
6653
  // Hack to pass bytes through unprocessed.
6170
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
6654
+ if (xhr.overrideMimeType) xhr.overrideMimeType('text/plain; charset=x-user-defined');
6171
6655
 
6172
6656
  xhr.onreadystatechange = function(e)
6173
6657
  {
@@ -6218,7 +6702,7 @@
6218
6702
  {
6219
6703
  //setProgress(100, 'Upload completed.');
6220
6704
 
6221
- $('#redactor-progress').hide();
6705
+ $('#redactor-progress, #redactor-progress-drag').hide();
6222
6706
 
6223
6707
  var s3image = url.split('?');
6224
6708
 
@@ -6279,7 +6763,6 @@
6279
6763
  }
6280
6764
  },
6281
6765
 
6282
-
6283
6766
  // UPLOAD
6284
6767
  uploadInit: function(el, options)
6285
6768
  {
@@ -6451,7 +6934,8 @@
6451
6934
  preview: false,
6452
6935
  uploadFields: false,
6453
6936
  text: this.opts.curLang.drop_file_here,
6454
- atext: this.opts.curLang.or_choose
6937
+ atext: this.opts.curLang.or_choose,
6938
+ uploadParam: false
6455
6939
  }, options);
6456
6940
 
6457
6941
  if (window.FormData === undefined) return false;
@@ -6486,11 +6970,11 @@
6486
6970
 
6487
6971
  this.dropareabox.removeClass('hover').addClass('drop');
6488
6972
 
6489
- this.dragUploadAjax(this.draguploadOptions.url, e.dataTransfer.files[0], false);
6973
+ this.dragUploadAjax(this.draguploadOptions.url, e.dataTransfer.files[0], false, false, false, this.draguploadOptions.uploadParam);
6490
6974
 
6491
6975
  }, this );
6492
6976
  },
6493
- dragUploadAjax: function(url, file, directupload, progress, e)
6977
+ dragUploadAjax: function(url, file, directupload, progress, e, uploadParam)
6494
6978
  {
6495
6979
 
6496
6980
 
@@ -6510,7 +6994,14 @@
6510
6994
  var fd = new FormData();
6511
6995
 
6512
6996
  // append file data
6513
- fd.append('file', file);
6997
+ if (uploadParam !== false)
6998
+ {
6999
+ fd.append(uploadParam, file);
7000
+ }
7001
+ else
7002
+ {
7003
+ fd.append('file', file);
7004
+ }
6514
7005
 
6515
7006
  // append hidden fields
6516
7007
  if (this.opts.uploadFields !== false && typeof this.opts.uploadFields === 'object')
@@ -6536,7 +7027,7 @@
6536
7027
  data = data.replace(/^\[/, '');
6537
7028
  data = data.replace(/\]$/, '');
6538
7029
 
6539
- var json = $.parseJSON(data);
7030
+ var json = (typeof data === 'string' ? $.parseJSON(data) : data);
6540
7031
 
6541
7032
  if (directupload)
6542
7033
  {
@@ -6595,7 +7086,6 @@
6595
7086
  this.dropareabox.text('Loading ' + percent + '% ' + (text || ''));
6596
7087
  },
6597
7088
 
6598
-
6599
7089
  // UTILS
6600
7090
  isMobile: function()
6601
7091
  {
@@ -6623,20 +7113,30 @@
6623
7113
 
6624
7114
  return html == '';
6625
7115
  },
7116
+ isIe11: function()
7117
+ {
7118
+ return !!navigator.userAgent.match(/Trident\/7\./);
7119
+ },
6626
7120
  browser: function(browser)
6627
7121
  {
6628
7122
  var ua = navigator.userAgent.toLowerCase();
6629
- var match = /(chrome)[ \/]([\w.]+)/.exec(ua)
6630
- || /(webkit)[ \/]([\w.]+)/.exec(ua)
6631
- || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua)
6632
- || /(msie) ([\w.]+)/.exec(ua)
6633
- || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua)
6634
- || [];
7123
+ var match = /(opr)[\/]([\w.]+)/.exec( ua ) ||
7124
+ /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
7125
+ /(webkit)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) ||
7126
+ /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
7127
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
7128
+ /(msie) ([\w.]+)/.exec( ua ) ||
7129
+ ua.indexOf("trident") >= 0 && /(rv)(?::| )([\w.]+)/.exec( ua ) ||
7130
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
7131
+ [];
6635
7132
 
6636
7133
  if (browser == 'version') return match[2];
6637
7134
  if (browser == 'webkit') return (match[1] == 'chrome' || match[1] == 'webkit');
7135
+ if (match[1] == 'rv') return browser == 'msie';
7136
+ if (match[1] == 'opr') return browser == 'webkit';
7137
+
7138
+ return browser == match[1];
6638
7139
 
6639
- return match[1] == browser;
6640
7140
  },
6641
7141
  oldIE: function()
6642
7142
  {
@@ -6718,19 +7218,20 @@
6718
7218
 
6719
7219
  return array;
6720
7220
  }
7221
+
6721
7222
  };
6722
7223
 
6723
7224
  // constructor
6724
7225
  Redactor.prototype.init.prototype = Redactor.prototype;
6725
7226
 
6726
7227
  // LINKIFY
6727
- $.Redactor.fn.formatLinkify = function(protocol, convertLinks, convertImageLinks, convertVideoLinks)
7228
+ $.Redactor.fn.formatLinkify = function(protocol, convertLinks, convertImageLinks, convertVideoLinks, linkSize)
6728
7229
  {
6729
7230
  var url1 = /(^|&lt;|\s)(www\..+?\..+?)(\s|&gt;|$)/g,
6730
7231
  url2 = /(^|&lt;|\s)(((https?|ftp):\/\/|mailto:).+?)(\s|&gt;|$)/g,
6731
7232
  urlImage = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/gi,
6732
- urlYoutube = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/,
6733
- urlVimeo = /http:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/;
7233
+ urlYoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig,
7234
+ urlVimeo = /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/;
6734
7235
 
6735
7236
  var childNodes = (this.$editor ? this.$editor.get(0) : this).childNodes, i = childNodes.length;
6736
7237
  while (i--)
@@ -6748,7 +7249,7 @@
6748
7249
 
6749
7250
  if (html.match(urlYoutube))
6750
7251
  {
6751
- html = html.replace(urlYoutube, iframeStart + '//www.youtube.com/embed/$2' + iframeEnd);
7252
+ html = html.replace(urlYoutube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd);
6752
7253
  $(n).after(html).remove();
6753
7254
  }
6754
7255
  else if (html.match(urlVimeo))
@@ -6771,13 +7272,13 @@
6771
7272
  {
6772
7273
  var href = (html.match(url1) || html.match(url2));
6773
7274
  href = href[0];
6774
- if (href.length > 50) href = href.substring(0, 50) + '...';
7275
+ if (href.length > linkSize) href = href.substring(0, linkSize) + '...';
6775
7276
 
6776
7277
  html = html.replace(/&/g, '&amp;')
6777
7278
  .replace(/</g, '&lt;')
6778
7279
  .replace(/>/g, '&gt;')
6779
- .replace(url1, '$1<a href="' + protocol + '$2">' + href + '</a>$3')
6780
- .replace(url2, '$1<a href="$2">' + href + '</a>$5');
7280
+ .replace(url1, '$1<a href="' + protocol + '$2">' + $.trim(href) + '</a>$3')
7281
+ .replace(url2, '$1<a href="$2">' + $.trim(href) + '</a>$5');
6781
7282
 
6782
7283
 
6783
7284
  $(n).after(html).remove();
@@ -6785,7 +7286,7 @@
6785
7286
  }
6786
7287
  else if (n.nodeType === 1 && !/^(a|button|textarea)$/i.test(n.tagName))
6787
7288
  {
6788
- $.Redactor.fn.formatLinkify.call(n, protocol, convertLinks, convertImageLinks, convertVideoLinks);
7289
+ $.Redactor.fn.formatLinkify.call(n, protocol, convertLinks, convertImageLinks, convertVideoLinks, linkSize);
6789
7290
  }
6790
7291
  }
6791
7292
  };