redactor-rails 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
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
  };