scrivito_editors 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/app/assets/fonts/redactor-font.eot +0 -0
  4. data/app/assets/javascripts/redactor.js +844 -415
  5. data/app/assets/javascripts/scrivito_editors/date_editor.js.coffee +1 -1
  6. data/app/assets/javascripts/scrivito_editors/enum_editor.js.coffee +1 -1
  7. data/app/assets/javascripts/scrivito_editors/html_editor.js.coffee +1 -1
  8. data/app/assets/javascripts/scrivito_editors/image_editor.js.coffee +72 -0
  9. data/app/assets/javascripts/scrivito_editors/link_editor.js.coffee +124 -0
  10. data/app/assets/javascripts/scrivito_editors/linklist_editor.js.coffee +1 -1
  11. data/app/assets/javascripts/scrivito_editors/multienum_editor.js.coffee +1 -1
  12. data/app/assets/javascripts/scrivito_editors/reference_editor.js.coffee +1 -1
  13. data/app/assets/javascripts/scrivito_editors/referencelist_editor.js.coffee +1 -1
  14. data/app/assets/javascripts/scrivito_editors/slider_editor.js.coffee +1 -1
  15. data/app/assets/javascripts/scrivito_editors/string_editor.js.coffee +5 -3
  16. data/app/assets/javascripts/scrivito_editors/text_editor.js.coffee +2 -2
  17. data/app/assets/javascripts/scrivito_editors.js +2 -7
  18. data/app/assets/javascripts/scrivito_editors_addons.js +4 -0
  19. data/app/assets/javascripts/scrivito_editors_core.js +3 -0
  20. data/app/assets/stylesheets/redactor.css.erb +66 -53
  21. data/app/assets/stylesheets/scrivito_editors/editors/image_editor.css +7 -0
  22. data/app/assets/stylesheets/scrivito_editors/editors/link_editor.css +80 -0
  23. data/app/assets/stylesheets/scrivito_editors.css +2 -2
  24. data/lib/scrivito_editors/version.rb +1 -1
  25. metadata +23 -4
  26. data/app/assets/javascripts/jquery_additions/jquery_center.js.coffee +0 -6
@@ -1,6 +1,6 @@
1
1
  /*
2
- Redactor v9.2.1
3
- Updated: Mar 19, 2014
2
+ Redactor v9.2.6
3
+ Updated: Jul 19, 2014
4
4
 
5
5
  http://imperavi.com/redactor/
6
6
 
@@ -9,7 +9,6 @@
9
9
 
10
10
  Usage: $('#content').redactor();
11
11
  */
12
-
13
12
  (function($)
14
13
  {
15
14
  var uuid = 0;
@@ -31,6 +30,9 @@
31
30
  return this[0] === this[1];
32
31
  };
33
32
 
33
+ var reUrlYoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig;
34
+ var reUrlVimeo = /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/;
35
+
34
36
  // Plugin
35
37
  $.fn.redactor = function(options)
36
38
  {
@@ -71,7 +73,7 @@
71
73
  }
72
74
 
73
75
  $.Redactor = Redactor;
74
- $.Redactor.VERSION = '9.2.1';
76
+ $.Redactor.VERSION = '9.2.6';
75
77
  $.Redactor.opts = {
76
78
 
77
79
  // settings
@@ -84,7 +86,7 @@
84
86
  lang: 'en',
85
87
  direction: 'ltr', // ltr or rtl
86
88
 
87
- placeholder: '',
89
+ placeholder: false,
88
90
 
89
91
  typewriter: false,
90
92
  wym: false,
@@ -104,7 +106,17 @@
104
106
  autoresize: true,
105
107
  minHeight: false,
106
108
  maxHeight: false,
107
- shortcuts: true,
109
+ shortcuts: {
110
+ 'ctrl+m, meta+m': "this.execCommand('removeFormat', false)",
111
+ 'ctrl+b, meta+b': "this.execCommand('bold', false)",
112
+ 'ctrl+i, meta+i': "this.execCommand('italic', false)",
113
+ 'ctrl+h, meta+h': "this.execCommand('superscript', false)",
114
+ 'ctrl+l, meta+l': "this.execCommand('subscript', false)",
115
+ 'ctrl+k, meta+k': "this.linkShow()",
116
+ 'ctrl+shift+7': "this.execCommand('insertorderedlist', false)",
117
+ 'ctrl+shift+8': "this.execCommand('insertunorderedlist', false)"
118
+ },
119
+ shortcutsAdd: false,
108
120
 
109
121
  autosave: false, // false or url
110
122
  autosaveInterval: 60, // seconds
@@ -116,14 +128,16 @@
116
128
  linkProtocol: 'http://',
117
129
  linkNofollow: false,
118
130
  linkSize: 50,
131
+ predefinedLinks: false, // json url (ex. /some-url.json ) or false
119
132
 
120
133
  imageFloatMargin: '10px',
121
- imageGetJson: false, // url (ex. /folder/images.json ) or false
134
+ imageGetJson: false, // json url (ex. /some-images.json ) or false
122
135
 
123
136
  dragUpload: true, // false
124
137
  imageTabLink: true,
125
138
  imageUpload: false, // url
126
139
  imageUploadParam: 'file', // input name
140
+ imageResizable: true,
127
141
 
128
142
  fileUpload: false, // url
129
143
  fileUploadParam: 'file', // input name
@@ -212,6 +226,7 @@
212
226
  'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'PRE', 'ADDRESS', 'SECTION',
213
227
  'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE', 'TD'],
214
228
 
229
+
215
230
  // lang
216
231
  langs: {
217
232
  en: {
@@ -383,6 +398,12 @@
383
398
  // load lang
384
399
  this.opts.curLang = this.opts.langs[this.opts.lang];
385
400
 
401
+ // extend shortcuts
402
+ $.extend(this.opts.shortcuts, this.opts.shortcutsAdd);
403
+
404
+ // init placeholder
405
+ this.placeholderInit();
406
+
386
407
  // Build
387
408
  this.buildStart();
388
409
 
@@ -802,6 +823,12 @@
802
823
  },
803
824
  setFullpageOnInit: function(html)
804
825
  {
826
+ this.fullpageDoctype = html.match(/^<\!doctype[^>]*>/i);
827
+ if (this.fullpageDoctype && this.fullpageDoctype.length == 1)
828
+ {
829
+ html = html.replace(/^<\!doctype[^>]*>/i, '');
830
+ }
831
+
805
832
  html = this.cleanSavePreCode(html, true);
806
833
  html = this.cleanConverters(html);
807
834
  html = this.cleanEmpty(html);
@@ -814,6 +841,14 @@
814
841
  this.setSpansVerified();
815
842
  this.sync();
816
843
  },
844
+ setFullpageDoctype: function()
845
+ {
846
+ if (this.fullpageDoctype && this.fullpageDoctype.length == 1)
847
+ {
848
+ var source = this.fullpageDoctype[0] + '\n' + this.$source.val();
849
+ this.$source.val(source);
850
+ }
851
+ },
817
852
  setSpansVerified: function()
818
853
  {
819
854
  var spans = this.$editor.find('span');
@@ -866,8 +901,6 @@
866
901
  // do not sync
867
902
  return false;
868
903
  }
869
-
870
-
871
904
  // fix second level up ul, ol
872
905
  html = html.replace(/<\/li><(ul|ol)>([\w\W]*?)<\/(ul|ol)>/gi, '<$1>$2</$1></li>');
873
906
 
@@ -888,6 +921,7 @@
888
921
  html = this.callback('syncBefore', false, html);
889
922
 
890
923
  this.$source.val(html);
924
+ this.setFullpageDoctype();
891
925
 
892
926
  // onchange & after callback
893
927
  this.callback('syncAfter', false, html);
@@ -972,20 +1006,26 @@
972
1006
 
973
1007
  // remove spans
974
1008
  html = html.replace(/<span(.*?)>([\w\W]*?)<\/span>/gi, '$2');
1009
+ html = html.replace(/<inline>([\w\W]*?)<\/inline>/gi, '$1');
975
1010
  html = html.replace(/<inline>/gi, '<span>');
976
1011
  html = html.replace(/<inline /gi, '<span ');
977
1012
  html = html.replace(/<\/inline>/gi, '</span>');
1013
+
1014
+ if (this.opts.removeEmptyTags)
1015
+ {
1016
+ html = html.replace(/<span>([\w\W]*?)<\/span>/gi, '$1');
1017
+ }
1018
+
978
1019
  html = html.replace(/<span(.*?)class="redactor_placeholder"(.*?)>([\w\W]*?)<\/span>/gi, '');
979
- html = html.replace(/<span>([\w\W]*?)<\/span>/gi, '$1');
1020
+ html = html.replace(/<img(.*?)contenteditable="false"(.*?)>/gi, '<img$1$2>');
980
1021
 
981
1022
  // special characters
982
- html = html.replace(/&amp;/gi, '&');
983
- html = html.replace(/™/gi, '&trade;');
984
- html = html.replace(/©/gi, '&copy;');
985
- html = html.replace(/…/gi, '&hellip;');
986
- html = html.replace(/—/gi, '&mdash;');
987
- html = html.replace(/‐/gi, '&dash;');
988
-
1023
+ html = html.replace(/&/gi, '&');
1024
+ html = html.replace(/\u2122/gi, '&trade;');
1025
+ html = html.replace(/\u00a9/gi, '&copy;');
1026
+ html = html.replace(/\u2026/gi, '&hellip;');
1027
+ html = html.replace(/\u2014/gi, '&mdash;');
1028
+ html = html.replace(/\u2010/gi, '&dash;');
989
1029
 
990
1030
  html = this.cleanReConvertProtected(html);
991
1031
 
@@ -993,6 +1033,7 @@
993
1033
  },
994
1034
 
995
1035
 
1036
+
996
1037
  // BUILD
997
1038
  buildStart: function()
998
1039
  {
@@ -1001,7 +1042,6 @@
1001
1042
 
1002
1043
  // container
1003
1044
  this.$box = $('<div class="redactor_box" />');
1004
- this.$box.css('z-index', 100-this.uuid);
1005
1045
 
1006
1046
  // textarea test
1007
1047
  if (this.$source[0].tagName === 'TEXTAREA') this.opts.textareamode = true;
@@ -1175,11 +1215,17 @@
1175
1215
  {
1176
1216
  this.dblEnter = 0;
1177
1217
 
1178
- if (this.opts.dragUpload && this.opts.imageUpload !== false)
1218
+ if (this.opts.dragUpload && (this.opts.imageUpload !== false || this.opts.s3 !== false))
1179
1219
  {
1180
1220
  this.$editor.on('drop.redactor', $.proxy(this.buildEventDrop, this));
1181
1221
  }
1182
1222
 
1223
+ this.$editor.on('click.redactor', $.proxy(function()
1224
+ {
1225
+ this.selectall = false;
1226
+
1227
+ }, this));
1228
+
1183
1229
  this.$editor.on('input.redactor', $.proxy(this.sync, this));
1184
1230
  this.$editor.on('paste.redactor', $.proxy(this.buildEventPaste, this));
1185
1231
  this.$editor.on('keydown.redactor', $.proxy(this.buildEventKeydown, this));
@@ -1233,12 +1279,11 @@
1233
1279
 
1234
1280
  this.bufferSet();
1235
1281
 
1236
- var progress = $('<div id="redactor-progress"><span></span></div>');
1237
- $(document.body).append(progress);
1282
+ this.showProgressBar();
1238
1283
 
1239
1284
  if (this.opts.s3 === false)
1240
1285
  {
1241
- this.dragUploadAjax(this.opts.imageUpload, file, true, progress, e, this.opts.imageUploadParam);
1286
+ this.dragUploadAjax(this.opts.imageUpload, file, true, e, this.opts.imageUploadParam);
1242
1287
  }
1243
1288
  else
1244
1289
  {
@@ -1305,6 +1350,7 @@
1305
1350
  var event = e.originalEvent || e;
1306
1351
  this.clipboardFilePaste = false;
1307
1352
 
1353
+
1308
1354
  if (typeof(event.clipboardData) === 'undefined') return false;
1309
1355
  if (event.clipboardData.items)
1310
1356
  {
@@ -1338,13 +1384,38 @@
1338
1384
 
1339
1385
  this.callback('keydown', e);
1340
1386
 
1341
- // disabling cmd|ctrl + left
1342
- if (this.browser('mozilla') && ctrl && key === 37)
1387
+ /*
1388
+ firefox cmd+left/Cmd+right browser back/forward fix -
1389
+ http://joshrhoderick.wordpress.com/2010/05/05/how-firefoxs-command-key-bug-kills-usability-on-the-mac/
1390
+ */
1391
+ if (this.browser('mozilla') && "modify" in window.getSelection())
1343
1392
  {
1344
- e.preventDefault();
1345
- return false;
1393
+ if ((ctrl) && (e.keyCode===37 || e.keyCode===39))
1394
+ {
1395
+ var selection = this.getSelection();
1396
+ var lineOrWord = (e.metaKey ? "line" : "word");
1397
+ if (e.keyCode===37)
1398
+ {
1399
+ selection.modify("extend","left",lineOrWord);
1400
+ if (!e.shiftKey)
1401
+ {
1402
+ selection.collapseToStart();
1403
+ }
1404
+ }
1405
+ if (e.keyCode===39)
1406
+ {
1407
+ selection.modify("extend","right",lineOrWord);
1408
+ if (!e.shiftKey)
1409
+ {
1410
+ selection.collapseToEnd();
1411
+ }
1412
+ }
1413
+
1414
+ e.preventDefault();
1415
+ }
1346
1416
  }
1347
1417
 
1418
+
1348
1419
  this.imageResizeHide(false);
1349
1420
 
1350
1421
  // pre & down
@@ -1371,7 +1442,7 @@
1371
1442
  }
1372
1443
 
1373
1444
  // shortcuts setup
1374
- if (ctrl && !e.shiftKey) this.shortcuts(e, key);
1445
+ this.shortcuts(e, key);
1375
1446
 
1376
1447
  // buffer setup
1377
1448
  if (ctrl && key === 90 && !e.shiftKey && !e.altKey) // z key
@@ -1408,9 +1479,9 @@
1408
1479
  }
1409
1480
 
1410
1481
  // enter
1411
- if (key == this.keyCode.ENTER && !e.shiftKey && !e.ctrlKey && !e.metaKey )
1482
+ if (key == this.keyCode.ENTER && !e.shiftKey && !e.ctrlKey && !e.metaKey)
1412
1483
  {
1413
- //
1484
+ // remove selected content on enter
1414
1485
  var range = this.getRange();
1415
1486
  if (range && range.collapsed === false)
1416
1487
  {
@@ -1473,11 +1544,43 @@
1473
1544
  }
1474
1545
 
1475
1546
  // pre
1476
- if (pre === true) return this.buildEventKeydownPre(e, current);
1547
+ if (pre === true)
1548
+ {
1549
+ return this.buildEventKeydownPre(e, current);
1550
+ }
1477
1551
  else
1478
1552
  {
1479
1553
  if (!this.opts.linebreaks)
1480
1554
  {
1555
+ // lists exit
1556
+ if (block && block.tagName == 'LI')
1557
+ {
1558
+ var listCurrent = this.getBlock();
1559
+ if (listCurrent !== false || listCurrent.tagName === 'LI')
1560
+ {
1561
+ var listText = $.trim($(block).text());
1562
+ var listCurrentText = $.trim($(listCurrent).text());
1563
+ if (listText == ''
1564
+ && listCurrentText == ''
1565
+ && $(listCurrent).next('li').size() == 0
1566
+ && $(listCurrent).parents('li').size() == 0)
1567
+ {
1568
+ this.bufferSet();
1569
+
1570
+ var $list = $(listCurrent).closest('ol, ul');
1571
+ $(listCurrent).remove();
1572
+ var node = $('<p>' + this.opts.invisibleSpace + '</p>');
1573
+ $list.after(node);
1574
+ this.selectionStart(node);
1575
+
1576
+ this.sync();
1577
+ this.callback('enter', e);
1578
+ return false;
1579
+ }
1580
+ }
1581
+
1582
+ }
1583
+
1481
1584
  // replace div to p
1482
1585
  if (block && this.opts.rBlockTest.test(block.tagName))
1483
1586
  {
@@ -1500,7 +1603,6 @@
1500
1603
  {
1501
1604
  // hit enter
1502
1605
  this.bufferSet();
1503
-
1504
1606
  var node = $('<p>' + this.opts.invisibleSpace + '</p>');
1505
1607
  this.insertNode(node[0]);
1506
1608
  this.selectionStart(node);
@@ -1559,7 +1661,7 @@
1559
1661
  }
1560
1662
 
1561
1663
  // delete zero-width space before the removing
1562
- if (key === this.keyCode.BACKSPACE) this.buildEventKeydownBackspace(current);
1664
+ if (key === this.keyCode.BACKSPACE) this.buildEventKeydownBackspace(e, current, parent);
1563
1665
 
1564
1666
  },
1565
1667
  buildEventKeydownPre: function(e, current)
@@ -1607,8 +1709,23 @@
1607
1709
 
1608
1710
  return false;
1609
1711
  },
1610
- buildEventKeydownBackspace: function(current)
1712
+ buildEventKeydownBackspace: function(e, current, parent)
1611
1713
  {
1714
+ // remove empty list in table
1715
+ if (parent && current && parent.parentNode.tagName == 'TD'
1716
+ && parent.tagName == 'UL' && current.tagName == 'LI' && $(parent).children('li').size() == 1)
1717
+ {
1718
+ var text = $(current).text().replace(/[\u200B-\u200D\uFEFF]/g, '');
1719
+ if (text == '')
1720
+ {
1721
+ var node = parent.parentNode;
1722
+ $(parent).remove();
1723
+ this.selectionStart(node);
1724
+ this.sync();
1725
+ return false;
1726
+ }
1727
+ }
1728
+
1612
1729
  if (typeof current.tagName !== 'undefined' && /^(H[1-6])$/i.test(current.tagName))
1613
1730
  {
1614
1731
  var node;
@@ -1617,15 +1734,15 @@
1617
1734
 
1618
1735
  $(current).replaceWith(node);
1619
1736
  this.selectionStart(node);
1737
+ this.sync();
1620
1738
  }
1621
1739
 
1622
1740
  if (typeof current.nodeValue !== 'undefined' && current.nodeValue !== null)
1623
1741
  {
1624
-
1625
- //var value = $.trim(current.nodeValue.replace(/[^\u0000-\u1C7F]/g, ''));
1626
1742
  if (current.remove && current.nodeType === 3 && current.nodeValue.match(/[^\u200B]/g) == null)
1627
1743
  {
1628
- current.remove();
1744
+ $(current).prev().remove();
1745
+ this.sync();
1629
1746
  }
1630
1747
  }
1631
1748
  },
@@ -1787,7 +1904,10 @@
1787
1904
  // iframe css
1788
1905
  this.iframeAddCss();
1789
1906
 
1790
- if (this.opts.fullpage) this.setFullpageOnInit(this.$editor.html());
1907
+ if (this.opts.fullpage)
1908
+ {
1909
+ this.setFullpageOnInit(this.$source.val());
1910
+ }
1791
1911
  else this.set(this.content, true, false);
1792
1912
 
1793
1913
  this.buildOptions();
@@ -1795,24 +1915,41 @@
1795
1915
  },
1796
1916
 
1797
1917
  // PLACEHOLDER
1798
- placeholderStart: function(html)
1918
+ placeholderInit: function()
1799
1919
  {
1800
- if (this.isEmpty(html))
1920
+ if (this.opts.placeholder !== false)
1921
+ {
1922
+ this.placeholderText = this.opts.placeholder;
1923
+ this.opts.placeholder = true;
1924
+ }
1925
+ else
1801
1926
  {
1802
- if (this.$element.attr('placeholder'))
1927
+ if (typeof this.$element.attr('placeholder') == 'undefined' || this.$element.attr('placeholder') == '')
1803
1928
  {
1804
- this.opts.placeholder = this.$element.attr('placeholder');
1929
+ this.opts.placeholder = false;
1805
1930
  }
1806
-
1807
- if (this.opts.placeholder !== '')
1931
+ else
1808
1932
  {
1809
- this.opts.focus = false;
1810
- this.placeholderOnFocus();
1811
- this.placeholderOnBlur();
1812
-
1813
- return this.placeholderGet();
1933
+ this.placeholderText = this.$element.attr('placeholder');
1934
+ this.opts.placeholder = true;
1814
1935
  }
1815
1936
  }
1937
+ },
1938
+ placeholderStart: function(html)
1939
+ {
1940
+ if (this.opts.placeholder === false)
1941
+ {
1942
+ return false;
1943
+ }
1944
+
1945
+ if (this.isEmpty(html))
1946
+ {
1947
+ this.opts.focus = false;
1948
+ this.placeholderOnFocus();
1949
+ this.placeholderOnBlur();
1950
+
1951
+ return this.placeholderGet();
1952
+ }
1816
1953
  else
1817
1954
  {
1818
1955
  this.placeholderOnBlur();
@@ -1830,7 +1967,14 @@
1830
1967
  },
1831
1968
  placeholderGet: function()
1832
1969
  {
1833
- return $('<span class="redactor_placeholder">').data('redactor', 'verified').attr('contenteditable', false).text(this.opts.placeholder);
1970
+ var ph = $('<span class="redactor_placeholder">').data('redactor', 'verified')
1971
+ .attr('contenteditable', false).text(this.placeholderText);
1972
+
1973
+ if (this.opts.linebreaks === false)
1974
+ {
1975
+ return $('<p>').append(ph);
1976
+ }
1977
+ else return ph;
1834
1978
  },
1835
1979
  placeholderBlur: function()
1836
1980
  {
@@ -1880,32 +2024,98 @@
1880
2024
  shortcuts: function(e, key)
1881
2025
  {
1882
2026
 
2027
+ // disable browser's hot keys for bold and italic
1883
2028
  if (!this.opts.shortcuts)
1884
2029
  {
1885
- if (key === 66 || key === 73) e.preventDefault();
2030
+ if ((e.ctrlKey || e.metaKey) && (key === 66 || key === 73))
2031
+ {
2032
+ e.preventDefault();
2033
+ }
2034
+
1886
2035
  return false;
1887
2036
  }
1888
2037
 
1889
- if (key === 77) this.shortcutsLoad(e, 'removeFormat'); // Ctrl + m
1890
- else if (key === 66) this.shortcutsLoad(e, 'bold'); // Ctrl + b
1891
- else if (key === 73) this.shortcutsLoad(e, 'italic'); // Ctrl + i
2038
+ $.each(this.opts.shortcuts, $.proxy(function(str, command)
2039
+ {
2040
+ var keys = str.split(',');
2041
+ for (var i in keys)
2042
+ {
2043
+ if (typeof keys[i] === 'string')
2044
+ {
2045
+ this.shortcutsHandler(e, $.trim(keys[i]), $.proxy(function()
2046
+ {
2047
+ eval(command);
2048
+ }, this));
2049
+ }
2050
+
2051
+ }
1892
2052
 
1893
- else if (key === 74) this.shortcutsLoad(e, 'insertunorderedlist'); // Ctrl + j
1894
- else if (key === 75) this.shortcutsLoad(e, 'insertorderedlist'); // Ctrl + k
2053
+ }, this));
1895
2054
 
1896
- else if (key === 72) this.shortcutsLoad(e, 'superscript'); // Ctrl + h
1897
- else if (key === 76) this.shortcutsLoad(e, 'subscript'); // Ctrl + l
1898
2055
 
1899
2056
  },
1900
- shortcutsLoad: function(e, cmd)
1901
- {
1902
- e.preventDefault();
1903
- this.execCommand(cmd, false);
1904
- },
1905
- shortcutsLoadFormat: function(e, cmd)
2057
+ shortcutsHandler: function(e, keys, origHandler)
1906
2058
  {
1907
- e.preventDefault();
1908
- this.formatBlocks(cmd);
2059
+ // based on https://github.com/jeresig/jquery.hotkeys
2060
+ var hotkeysSpecialKeys =
2061
+ {
2062
+ 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
2063
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
2064
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", 59: ";", 61: "=",
2065
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
2066
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
2067
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
2068
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 173: "-", 186: ";", 187: "=",
2069
+ 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'"
2070
+ };
2071
+
2072
+
2073
+ var hotkeysShiftNums =
2074
+ {
2075
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
2076
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
2077
+ ".": ">", "/": "?", "\\": "|"
2078
+ };
2079
+
2080
+ keys = keys.toLowerCase().split(" ");
2081
+ var special = hotkeysSpecialKeys[e.keyCode],
2082
+ character = String.fromCharCode( e.which ).toLowerCase(),
2083
+ modif = "", possible = {};
2084
+
2085
+ $.each([ "alt", "ctrl", "meta", "shift"], function(index, specialKey)
2086
+ {
2087
+ if (e[specialKey + 'Key'] && special !== specialKey)
2088
+ {
2089
+ modif += specialKey + '+';
2090
+ }
2091
+ });
2092
+
2093
+
2094
+ if (special)
2095
+ {
2096
+ possible[modif + special] = true;
2097
+ }
2098
+
2099
+ if (character)
2100
+ {
2101
+ possible[modif + character] = true;
2102
+ possible[modif + hotkeysShiftNums[character]] = true;
2103
+
2104
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
2105
+ if (modif === "shift+")
2106
+ {
2107
+ possible[hotkeysShiftNums[character]] = true;
2108
+ }
2109
+ }
2110
+
2111
+ for (var i = 0, l = keys.length; i < l; i++)
2112
+ {
2113
+ if (possible[keys[i]])
2114
+ {
2115
+ e.preventDefault();
2116
+ return origHandler.apply(this, arguments);
2117
+ }
2118
+ }
1909
2119
  },
1910
2120
 
1911
2121
  // FOCUS
@@ -1984,21 +2194,33 @@
1984
2194
  toggleVisual: function()
1985
2195
  {
1986
2196
  var html = this.$source.hide().val();
1987
-
1988
2197
  if (typeof this.modified !== 'undefined')
1989
2198
  {
1990
- this.modified = this.cleanRemoveSpaces(this.modified, false) !== this.cleanRemoveSpaces(html, false);
2199
+ var modified = this.modified.replace(/\n/g, '');
2200
+
2201
+ var thtml = html.replace(/\n/g, '');
2202
+ thtml = this.cleanRemoveSpaces(thtml, false);
2203
+
2204
+ this.modified = this.cleanRemoveSpaces(modified, false) !== thtml;
1991
2205
  }
1992
2206
 
1993
2207
  if (this.modified)
1994
2208
  {
1995
2209
  // don't remove the iframe even if cleared all.
1996
- if (this.opts.fullpage && html === '') this.setFullpageOnInit(html);
2210
+ if (this.opts.fullpage && html === '')
2211
+ {
2212
+ this.setFullpageOnInit(html);
2213
+ }
1997
2214
  else
1998
2215
  {
1999
2216
  this.set(html);
2000
- if (this.opts.fullpage) this.buildBindKeyboard();
2217
+ if (this.opts.fullpage)
2218
+ {
2219
+ this.buildBindKeyboard();
2220
+ }
2001
2221
  }
2222
+
2223
+ this.callback('change', false, html);
2002
2224
  }
2003
2225
 
2004
2226
  if (this.opts.iframe) this.$frame.show();
@@ -2015,6 +2237,8 @@
2015
2237
  this.buttonActiveVisual();
2016
2238
  this.buttonInactive('html');
2017
2239
  this.opts.visual = true;
2240
+
2241
+
2018
2242
  },
2019
2243
  toggleCode: function(direct)
2020
2244
  {
@@ -2292,6 +2516,8 @@
2292
2516
  {
2293
2517
  if (!this.opts.air) return;
2294
2518
 
2519
+ this.selectionSave();
2520
+
2295
2521
  var left, top;
2296
2522
  $('.redactor_air').hide();
2297
2523
 
@@ -2397,6 +2623,11 @@
2397
2623
  $item = $('<a href="#" class="' + btnObject.className + ' redactor_dropdown_' + btnName + '">' + btnObject.title + '</a>');
2398
2624
  $item.on('click', $.proxy(function(e)
2399
2625
  {
2626
+ if (this.opts.air)
2627
+ {
2628
+ this.selectionRestore();
2629
+ }
2630
+
2400
2631
  if (e.preventDefault) e.preventDefault();
2401
2632
  if (this.browser('msie')) e.returnValue = false;
2402
2633
 
@@ -2407,6 +2638,7 @@
2407
2638
  this.buttonActiveObserver();
2408
2639
  if (this.opts.air) this.$air.fadeOut(100);
2409
2640
 
2641
+
2410
2642
  }, this));
2411
2643
  }
2412
2644
 
@@ -2422,9 +2654,11 @@
2422
2654
  return false;
2423
2655
  }
2424
2656
 
2425
- var $dropdown = this.$toolbar.find('.redactor_dropdown_box_' + key);
2426
2657
  var $button = this.buttonGet(key);
2427
2658
 
2659
+ // Always re-append it to the end of <body> so it always has the highest sub-z-index.
2660
+ var $dropdown = $button.data('dropdown').appendTo(document.body);
2661
+
2428
2662
  if ($button.hasClass('dropact')) this.dropdownHideAll();
2429
2663
  else
2430
2664
  {
@@ -2434,11 +2668,7 @@
2434
2668
  this.buttonActive(key);
2435
2669
  $button.addClass('dropact');
2436
2670
 
2437
- var keyPosition = $button.position();
2438
- if (this.toolbarFixed)
2439
- {
2440
- keyPosition = $button.offset();
2441
- }
2671
+ var keyPosition = $button.offset();
2442
2672
 
2443
2673
  // fix right placement
2444
2674
  var dropdownWidth = $dropdown.width();
@@ -2451,10 +2681,10 @@
2451
2681
  var btnHeight = $button.innerHeight();
2452
2682
 
2453
2683
  var position = 'absolute';
2454
- var top = btnHeight + 'px';
2684
+ var top = (btnHeight + this.opts.toolbarFixedTopOffset) + 'px';
2455
2685
 
2456
2686
  if (this.opts.toolbarFixed && this.toolbarFixed) position = 'fixed';
2457
- else if (!this.opts.air) top = keyPosition.top + btnHeight + 'px';
2687
+ else top = keyPosition.top + btnHeight + 'px';
2458
2688
 
2459
2689
  $dropdown.css({ position: position, left: left, top: top }).show();
2460
2690
  this.callback('dropdownShown', { dropdown: $dropdown, key: key, button: $button });
@@ -2469,6 +2699,8 @@
2469
2699
 
2470
2700
  $(document).one('click', hdlHideDropDown);
2471
2701
  this.$editor.one('click', hdlHideDropDown);
2702
+ this.$editor.one('touchstart', hdlHideDropDown);
2703
+
2472
2704
 
2473
2705
  e.stopPropagation();
2474
2706
  this.focusWithSaveScroll();
@@ -2543,7 +2775,7 @@
2543
2775
  if (btnObject.dropdown)
2544
2776
  {
2545
2777
  var $dropdown = $('<div class="redactor_dropdown redactor_dropdown_box_' + btnName + '" style="display: none;">');
2546
- $dropdown.appendTo(this.$toolbar);
2778
+ $button.data('dropdown', $dropdown);
2547
2779
  this.dropdownBuild($dropdown, btnObject.dropdown);
2548
2780
  }
2549
2781
 
@@ -2681,22 +2913,12 @@
2681
2913
  if ($parent.length)
2682
2914
  {
2683
2915
  var align = $parent.css('text-align');
2684
-
2685
- switch (align)
2916
+ if (align == '')
2686
2917
  {
2687
- case 'right':
2688
- this.buttonActive('alignright');
2689
- break;
2690
- case 'center':
2691
- this.buttonActive('aligncenter');
2692
- break;
2693
- case 'justify':
2694
- this.buttonActive('alignjustify');
2695
- break;
2696
- default:
2697
- this.buttonActive('alignleft');
2698
- break;
2918
+ align = 'left';
2699
2919
  }
2920
+
2921
+ this.buttonActive('align' + align);
2700
2922
  }
2701
2923
  },
2702
2924
 
@@ -2873,7 +3095,8 @@
2873
3095
  }
2874
3096
  else
2875
3097
  {
2876
- data += cloned.html() + '<br>';
3098
+ var clonedHtml = cloned.html().replace(/<br\s?\/?>$/i, '');
3099
+ data += clonedHtml + '<br>';
2877
3100
  }
2878
3101
 
2879
3102
  if (i == 0)
@@ -2899,11 +3122,49 @@
2899
3122
  {
2900
3123
  var firstParent = $(this.getParent()).closest('td');
2901
3124
 
2902
- this.document.execCommand(cmd);
3125
+ if (this.browser('msie') && !this.isIe11() && this.opts.linebreaks)
3126
+ {
3127
+ var wrapper = this.selectionWrap('div');
3128
+ var wrapperHtml = $(wrapper).html();
3129
+ var tmpList = $('<ul>');
3130
+ if (cmd == 'insertorderedlist')
3131
+ {
3132
+ tmpList = $('<ol>');
3133
+ }
3134
+
3135
+ var tmpLi = $('<li>');
3136
+
3137
+ if ($.trim(wrapperHtml) == '')
3138
+ {
3139
+ tmpLi.append(wrapperHtml + '<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>');
3140
+ tmpList.append(tmpLi);
3141
+ this.$editor.find('#selection-marker-1').replaceWith(tmpList);
3142
+ }
3143
+ else
3144
+ {
3145
+ tmpLi.append(wrapperHtml);
3146
+ tmpList.append(tmpLi);
3147
+ $(wrapper).replaceWith(tmpList);
3148
+ }
3149
+ }
3150
+ else
3151
+ {
3152
+ this.document.execCommand(cmd);
3153
+ }
2903
3154
 
2904
3155
  var parent = this.getParent();
2905
3156
  var $list = $(parent).closest('ol, ul');
2906
3157
 
3158
+ if (this.opts.linebreaks === false)
3159
+ {
3160
+ var listText = $.trim($list.text());
3161
+ if (listText == '')
3162
+ {
3163
+ $list.children('li').find('br').remove();
3164
+ $list.children('li').append('<span id="selection-marker-1">' + this.opts.invisibleSpace + '</span>');
3165
+ }
3166
+ }
3167
+
2907
3168
  if (firstParent.size() != 0)
2908
3169
  {
2909
3170
  $list.wrapAll('<td>');
@@ -2926,7 +3187,7 @@
2926
3187
  }
2927
3188
 
2928
3189
  this.selectionRestore();
2929
-
3190
+ this.$editor.find('#selection-marker-1').removeAttr('id');
2930
3191
  this.sync();
2931
3192
  this.callback('execCommand', cmd, param);
2932
3193
  return;
@@ -3054,7 +3315,7 @@
3054
3315
  // linebreaks
3055
3316
  if (this.opts.linebreaks === true && typeof($el.data('tagblock')) !== 'undefined')
3056
3317
  {
3057
- $el.replaceWith($el.html());
3318
+ $el.replaceWith($el.html() + '<br>');
3058
3319
  }
3059
3320
  // all block tags
3060
3321
  else
@@ -3177,7 +3438,6 @@
3177
3438
  }
3178
3439
 
3179
3440
  this.selectionRestore();
3180
-
3181
3441
  this.sync();
3182
3442
  },
3183
3443
 
@@ -3198,7 +3458,11 @@
3198
3458
  cleanConverters: function(html)
3199
3459
  {
3200
3460
  // convert div to p
3201
- if (this.opts.convertDivs) html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '<p$1>$2</p>');
3461
+ if (this.opts.convertDivs && !this.opts.gallery)
3462
+ {
3463
+ html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '<p$1>$2</p>');
3464
+ }
3465
+
3202
3466
  if (this.opts.paragraphy) html = this.cleanParagraphy(html);
3203
3467
 
3204
3468
  return html;
@@ -3288,8 +3552,6 @@
3288
3552
  },
3289
3553
  cleanRemoveEmptyTags: function(html)
3290
3554
  {
3291
- html = html.replace(/<span>([\w\W]*?)<\/span>/gi, '$1');
3292
-
3293
3555
  // remove zero width-space
3294
3556
  html = html.replace(/[\u200B-\u200D\uFEFF]/g, '');
3295
3557
 
@@ -3347,6 +3609,7 @@
3347
3609
  }
3348
3610
 
3349
3611
  html = html.replace(/<br \/>\s*<br \/>/gi, "\n\n");
3612
+ html = html.replace(/<br><br>/gi, "\n\n");
3350
3613
 
3351
3614
  function R(str, mod, r)
3352
3615
  {
@@ -3370,7 +3633,7 @@
3370
3633
  {
3371
3634
  if (htmls[i].search('{replace') == -1)
3372
3635
  {
3373
- htmls[i] = htmls[i].replace(/<p>\n\t<\/p>/gi, '');
3636
+ htmls[i] = htmls[i].replace(/<p>\n\t?<\/p>/gi, '');
3374
3637
  htmls[i] = htmls[i].replace(/<p><\/p>/gi, '');
3375
3638
 
3376
3639
  if (htmls[i] != '')
@@ -3435,14 +3698,10 @@
3435
3698
  if (this.opts.italicTag === 'em') html = html.replace(/<i>([\w\W]*?)<\/i>/gi, '<em>$1</em>');
3436
3699
  else html = html.replace(/<em>([\w\W]*?)<\/em>/gi, '<i>$1</i>');
3437
3700
 
3438
- if (set !== true)
3439
- {
3440
- html = html.replace(/<strike>([\w\W]*?)<\/strike>/gi, '<del>$1</del>');
3441
- }
3442
- else
3443
- {
3444
- html = html.replace(/<del>([\w\W]*?)<\/del>/gi, '<strike>$1</strike>');
3445
- }
3701
+ html = html.replace(/<span style="text-decoration: underline;">([\w\W]*?)<\/span>/gi, '<u>$1</u>');
3702
+
3703
+ if (set !== true) html = html.replace(/<strike>([\w\W]*?)<\/strike>/gi, '<del>$1</del>');
3704
+ else html = html.replace(/<del>([\w\W]*?)<\/del>/gi, '<strike>$1</strike>');
3446
3705
 
3447
3706
  return html;
3448
3707
  },
@@ -3514,6 +3773,14 @@
3514
3773
  this.removeEmptyAttr(s, 'style');
3515
3774
  }, this));
3516
3775
 
3776
+ var $elem2 = this.$editor.find('b, strong, i, em, u, strike, del');
3777
+ $elem2.css('font-size', '');
3778
+
3779
+ $.each($elem2, $.proxy(function(i,s)
3780
+ {
3781
+ this.removeEmptyAttr(s, 'style');
3782
+ }, this));
3783
+
3517
3784
  // When we paste text in Safari is wrapping inserted div (remove it)
3518
3785
  this.$editor.find('div[style="text-align: -webkit-auto;"]').contents().unwrap();
3519
3786
 
@@ -3632,7 +3899,7 @@
3632
3899
  }
3633
3900
  }
3634
3901
 
3635
- return this.cleanFinish( out );
3902
+ return this.cleanFinish(out);
3636
3903
  },
3637
3904
  cleanGetTabs: function()
3638
3905
  {
@@ -3646,10 +3913,10 @@
3646
3913
  },
3647
3914
  cleanFinish: function(code)
3648
3915
  {
3649
- code = code.replace( /\n\s*\n/g, '\n' );
3650
- code = code.replace( /^[\s\n]*/, '' );
3651
- code = code.replace( /[\s\n]*$/, '' );
3652
- code = code.replace( /<script(.*?)>\n<\/script>/gi, '<script$1></script>' );
3916
+ code = code.replace(/\n\s*\n/g, '\n');
3917
+ code = code.replace(/^[\s\n]*/, '');
3918
+ code = code.replace(/[\s\n]*$/, '');
3919
+ code = code.replace(/<script(.*?)>\n<\/script>/gi, '<script$1></script>');
3653
3920
 
3654
3921
  this.cleanlevel = 0;
3655
3922
 
@@ -3738,6 +4005,11 @@
3738
4005
  },
3739
4006
  formatBlocks: function(tag)
3740
4007
  {
4008
+ if (this.browser('mozilla') && this.isFocused())
4009
+ {
4010
+ this.$editor.focus();
4011
+ }
4012
+
3741
4013
  this.bufferSet();
3742
4014
 
3743
4015
  var nodes = this.getBlocks();
@@ -3842,6 +4114,11 @@
3842
4114
  // QUOTE
3843
4115
  formatQuote: function()
3844
4116
  {
4117
+ if (this.browser('mozilla') && this.isFocused())
4118
+ {
4119
+ this.$editor.focus();
4120
+ }
4121
+
3845
4122
  this.bufferSet();
3846
4123
 
3847
4124
  // paragraphy
@@ -4081,7 +4358,6 @@
4081
4358
  this.selectionRestore();
4082
4359
  this.sync();
4083
4360
  },
4084
-
4085
4361
  inlineSetClass: function(className)
4086
4362
  {
4087
4363
  var current = this.getCurrent();
@@ -4140,7 +4416,25 @@
4140
4416
  }
4141
4417
  else
4142
4418
  {
4143
- this.document.execCommand('fontSize', false, 4 );
4419
+ var cmd, arg = value;
4420
+ switch (attr)
4421
+ {
4422
+ case 'font-size':
4423
+ cmd = 'fontSize';
4424
+ arg = 4;
4425
+ break;
4426
+ case 'font-family':
4427
+ cmd = 'fontName';
4428
+ break;
4429
+ case 'color':
4430
+ cmd = 'foreColor';
4431
+ break;
4432
+ case 'background-color':
4433
+ cmd = 'backColor';
4434
+ break;
4435
+ }
4436
+
4437
+ this.document.execCommand(cmd, false, arg);
4144
4438
 
4145
4439
  var fonts = this.$editor.find('font');
4146
4440
  $.each(fonts, $.proxy(function(i, s)
@@ -4267,6 +4561,7 @@
4267
4561
  $(el).replaceWith($(el).contents());
4268
4562
  },
4269
4563
 
4564
+
4270
4565
  // INSERT
4271
4566
  insertHtml: function (html, sync)
4272
4567
  {
@@ -4361,9 +4656,9 @@
4361
4656
  var range = sel.getRangeAt(0);
4362
4657
  range.deleteContents();
4363
4658
 
4364
- var el = this.document.createElement('div');
4659
+ var el = document.createElement('div');
4365
4660
  el.innerHTML = html;
4366
- var frag = this.document.createDocumentFragment(), node, lastNode;
4661
+ var frag = document.createDocumentFragment(), node, lastNode;
4367
4662
  while ((node = el.firstChild))
4368
4663
  {
4369
4664
  lastNode = frag.appendChild(node);
@@ -4415,8 +4710,21 @@
4415
4710
 
4416
4711
  this.focusWithSaveScroll();
4417
4712
 
4418
- if (this.browser('msie') && !this.isIe11()) this.document.selection.createRange().pasteHTML(html);
4419
- else this.document.execCommand('inserthtml', false, html);
4713
+ if (this.browser('msie'))
4714
+ {
4715
+ if (!this.isIe11())
4716
+ {
4717
+ this.document.selection.createRange().pasteHTML(html);
4718
+ }
4719
+ else
4720
+ {
4721
+ this.execPasteFrag(html);
4722
+ }
4723
+ }
4724
+ else
4725
+ {
4726
+ this.document.execCommand('inserthtml', false, html);
4727
+ }
4420
4728
 
4421
4729
  this.sync();
4422
4730
  },
@@ -4452,6 +4760,8 @@
4452
4760
  sel.removeAllRanges();
4453
4761
  sel.addRange(range);
4454
4762
  }
4763
+
4764
+ return node;
4455
4765
  },
4456
4766
  insertNodeToCaretPositionFromPoint: function(e, node)
4457
4767
  {
@@ -4671,8 +4981,16 @@
4671
4981
  html = html.replace(/<b\sid="internal-source-marker(.*?)">([\w\W]*?)<\/b>/gi, "$2");
4672
4982
  html = html.replace(/<b(.*?)id="docs-internal-guid(.*?)">([\w\W]*?)<\/b>/gi, "$3");
4673
4983
 
4984
+
4985
+ html = html.replace(/<span[^>]*(font-style: italic; font-weight: bold|font-weight: bold; font-style: italic)[^>]*>/gi, '<span style="font-weight: bold;"><span style="font-style: italic;">');
4986
+ html = html.replace(/<span[^>]*font-style: italic[^>]*>/gi, '<span style="font-style: italic;">');
4987
+ html = html.replace(/<span[^>]*font-weight: bold[^>]*>/gi, '<span style="font-weight: bold;">');
4988
+ html = html.replace(/<span[^>]*text-decoration: underline[^>]*>/gi, '<span style="text-decoration: underline;">');
4989
+
4674
4990
  // strip tags
4675
- html = this.cleanStripTags(html);
4991
+ //html = this.cleanStripTags(html);
4992
+
4993
+
4676
4994
 
4677
4995
  // prevert
4678
4996
  html = html.replace(/<td>\u200b*<\/td>/gi, '[td]');
@@ -4687,6 +5005,7 @@
4687
5005
  html = html.replace(/<embed(.*?)>([\w\W]*?)<\/embed>/gi, '[embed$1]$2[/embed]');
4688
5006
  html = html.replace(/<object(.*?)>([\w\W]*?)<\/object>/gi, '[object$1]$2[/object]');
4689
5007
  html = html.replace(/<param(.*?)>/gi, '[param$1]');
5008
+
4690
5009
  html = html.replace(/<img(.*?)>/gi, '[img$1]');
4691
5010
 
4692
5011
  // remove classes
@@ -4742,6 +5061,9 @@
4742
5061
  html = html.replace(/<div><\/div>/gi, '<br />');
4743
5062
  }
4744
5063
 
5064
+ // strip tags
5065
+ html = this.cleanStripTags(html);
5066
+
4745
5067
  if (this.currentOrParentIs('LI'))
4746
5068
  {
4747
5069
  html = html.replace(/<p>([\w\W]*?)<\/p>/gi, '$1<br>');
@@ -4986,19 +5308,20 @@
4986
5308
  },
4987
5309
 
4988
5310
  // BUFFER
4989
- bufferSet: function(html, selectionSave)
5311
+ bufferSet: function(selectionSave)
4990
5312
  {
4991
- if (html !== undefined || html === false) this.opts.buffer.push(html);
4992
- else
5313
+ if (selectionSave !== false)
4993
5314
  {
4994
- if (selectionSave !== false)
4995
- {
4996
- this.selectionSave();
4997
- }
5315
+ this.selectionSave();
5316
+ }
5317
+
5318
+ this.opts.buffer.push(this.$editor.html());
4998
5319
 
4999
- this.opts.buffer.push(this.$editor.html());
5320
+ if (selectionSave !== false)
5321
+ {
5000
5322
  this.selectionRemoveMarkers('buffer');
5001
5323
  }
5324
+
5002
5325
  },
5003
5326
  bufferUndo: function()
5004
5327
  {
@@ -5046,11 +5369,13 @@
5046
5369
  observeLinks: function()
5047
5370
  {
5048
5371
  this.$editor.find('a').on('click', $.proxy(this.linkObserver, this));
5372
+
5049
5373
  this.$editor.on('click.redactor', $.proxy(function(e)
5050
5374
  {
5051
5375
  this.linkObserverTooltipClose(e);
5052
5376
 
5053
5377
  }, this));
5378
+
5054
5379
  $(document).on('click.redactor', $.proxy(function(e)
5055
5380
  {
5056
5381
  this.linkObserverTooltipClose(e);
@@ -5064,14 +5389,29 @@
5064
5389
  this.$editor.find('img').each($.proxy(function(i, elem)
5065
5390
  {
5066
5391
  if (this.browser('msie')) $(elem).attr('unselectable', 'on');
5067
- this.imageResize(elem);
5392
+
5393
+ var parent = $(elem).parent();
5394
+ if (!parent.hasClass('royalSlider') && !parent.hasClass('fotorama'))
5395
+ {
5396
+ this.imageResize(elem);
5397
+ }
5068
5398
 
5069
5399
  }, this));
5400
+
5401
+ // royalSlider and fotorama
5402
+ this.$editor.find('.fotorama, .royalSlider').on('click', $.proxy(this.editGallery, this));
5403
+
5070
5404
  },
5071
5405
  linkObserver: function(e)
5072
5406
  {
5073
5407
  var $link = $(e.target);
5074
5408
 
5409
+ var parent = $(e.target).parent();
5410
+ if (parent.hasClass('royalSlider') || parent.hasClass('fotorama'))
5411
+ {
5412
+ return;
5413
+ }
5414
+
5075
5415
  if ($link.size() == 0 || $link[0].tagName !== 'A') return;
5076
5416
 
5077
5417
  var pos = $link.offset();
@@ -5399,6 +5739,12 @@
5399
5739
 
5400
5740
  return newnodes;
5401
5741
  },
5742
+ isInlineNode: function(node)
5743
+ {
5744
+ if (node.nodeType != 1) return false;
5745
+
5746
+ return !this.rTestBlock.test(node.nodeName);
5747
+ },
5402
5748
  nodeTestBlocks: function(node)
5403
5749
  {
5404
5750
  return node.nodeType == 1 && this.rTestBlock.test(node.nodeName);
@@ -5552,7 +5898,10 @@
5552
5898
  // SAVE & RESTORE
5553
5899
  selectionSave: function()
5554
5900
  {
5555
- if (!this.isFocused()) this.focusWithSaveScroll();
5901
+ if (!this.isFocused())
5902
+ {
5903
+ this.focusWithSaveScroll();
5904
+ }
5556
5905
 
5557
5906
  if (!this.opts.rangy)
5558
5907
  {
@@ -5589,10 +5938,19 @@
5589
5938
  {
5590
5939
  var boundaryRange = range.cloneRange();
5591
5940
 
5592
- boundaryRange.collapse(type);
5941
+ try {
5942
+ boundaryRange.collapse(type);
5943
+ boundaryRange.insertNode(node);
5944
+ boundaryRange.detach();
5945
+ }
5946
+ catch (e)
5947
+ {
5948
+ var html = this.opts.emptyHtml;
5949
+ if (this.opts.linebreaks) html = '<br>';
5593
5950
 
5594
- boundaryRange.insertNode(node);
5595
- boundaryRange.detach();
5951
+ this.$editor.prepend(html);
5952
+ this.focus();
5953
+ }
5596
5954
  },
5597
5955
  selectionRestore: function(replace, remove)
5598
5956
  {
@@ -5617,6 +5975,7 @@
5617
5975
 
5618
5976
  if (node1.length != 0 && node2.length != 0)
5619
5977
  {
5978
+
5620
5979
  this.selectionSet(node1[0], 0, node2[0], 0);
5621
5980
  }
5622
5981
  else if (node1.length != 0)
@@ -5679,7 +6038,7 @@
5679
6038
  },
5680
6039
  tableInsert: function()
5681
6040
  {
5682
- this.bufferSet(false, false);
6041
+ this.bufferSet(false);
5683
6042
 
5684
6043
  var rows = $('#redactor_table_rows').val(),
5685
6044
  columns = $('#redactor_table_columns').val(),
@@ -5711,6 +6070,11 @@
5711
6070
  $table_box.append($table);
5712
6071
  var html = $table_box.html();
5713
6072
 
6073
+ if (this.opts.linebreaks === false && this.browser('mozilla'))
6074
+ {
6075
+ html += '<p>' + this.opts.invisibleSpace + '</p>';
6076
+ }
6077
+
5714
6078
  this.modalClose();
5715
6079
  this.selectionRestore();
5716
6080
 
@@ -5727,6 +6091,7 @@
5727
6091
  }
5728
6092
  else
5729
6093
  {
6094
+
5730
6095
  this.insertHtmlAdvanced(html, false);
5731
6096
  }
5732
6097
 
@@ -5775,6 +6140,7 @@
5775
6140
 
5776
6141
  $current_tr.remove();
5777
6142
  this.selectionRestore();
6143
+ $table.find('span#selection-marker-1').remove();
5778
6144
  this.sync();
5779
6145
  },
5780
6146
  tableDeleteColumn: function()
@@ -5809,6 +6175,7 @@
5809
6175
  }, this));
5810
6176
 
5811
6177
  this.selectionRestore();
6178
+ $table.find('span#selection-marker-1').remove();
5812
6179
  this.sync();
5813
6180
  },
5814
6181
  tableAddHead: function()
@@ -5935,6 +6302,19 @@
5935
6302
  var data = $('#redactor_insert_video_area').val();
5936
6303
  data = this.cleanStripTags(data);
5937
6304
 
6305
+ // parse if it is link on youtube & vimeo
6306
+ var iframeStart = '<iframe width="500" height="281" src="',
6307
+ iframeEnd = '" frameborder="0" allowfullscreen></iframe>';
6308
+
6309
+ if (data.match(reUrlYoutube))
6310
+ {
6311
+ data = data.replace(reUrlYoutube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd);
6312
+ }
6313
+ else if (data.match(reUrlVimeo))
6314
+ {
6315
+ data = data.replace(reUrlVimeo, iframeStart + '//player.vimeo.com/video/$2' + iframeEnd);
6316
+ }
6317
+
5938
6318
  this.selectionRestore();
5939
6319
 
5940
6320
  var current = this.getBlock() || this.getCurrent();
@@ -5954,6 +6334,40 @@
5954
6334
 
5955
6335
  var callback = $.proxy(function()
5956
6336
  {
6337
+ // Predefined links
6338
+ if (this.opts.predefinedLinks !== false)
6339
+ {
6340
+ this.predefinedLinksStorage = {};
6341
+ var that = this;
6342
+ $.getJSON(this.opts.predefinedLinks, function(data)
6343
+ {
6344
+ var $select = $('#redactor-predefined-links');
6345
+ $select .html('');
6346
+ $.each(data, function(key, val)
6347
+ {
6348
+ that.predefinedLinksStorage[key] = val;
6349
+ $select.append($('<option>').val(key).html(val.name));
6350
+ });
6351
+
6352
+ $select.on('change', function()
6353
+ {
6354
+ var key = $(this).val();
6355
+ var name = '', url = '';
6356
+ if (key != 0)
6357
+ {
6358
+ name = that.predefinedLinksStorage[key].name;
6359
+ url = that.predefinedLinksStorage[key].url;
6360
+ }
6361
+
6362
+ $('#redactor_link_url').val(url);
6363
+ $('#redactor_link_url_text').val(name);
6364
+
6365
+ });
6366
+
6367
+ $select.show();
6368
+ });
6369
+ }
6370
+
5957
6371
  this.insert_link_node = false;
5958
6372
 
5959
6373
  var sel = this.getSelection();
@@ -5999,7 +6413,8 @@
5999
6413
  }
6000
6414
 
6001
6415
  this.linkInsertPressed = false;
6002
- $('#redactor_insert_link_btn').click($.proxy(this.linkProcess, this));
6416
+ $('#redactor_insert_link_btn').on('click', $.proxy(this.linkProcess, this));
6417
+
6003
6418
 
6004
6419
  setTimeout(function()
6005
6420
  {
@@ -6026,7 +6441,7 @@
6026
6441
  var text = $('#redactor_link_url_text').val();
6027
6442
 
6028
6443
  // mailto
6029
- if (link.search('@') != -1)
6444
+ if (link.search('@') != -1 && /(http|ftp|https):\/\//i.test(link) === false)
6030
6445
  {
6031
6446
  link = 'mailto:' + link;
6032
6447
  }
@@ -6200,6 +6615,7 @@
6200
6615
  // json
6201
6616
  if (this.opts.imageGetJson)
6202
6617
  {
6618
+
6203
6619
  $.getJSON(this.opts.imageGetJson, $.proxy(function(data)
6204
6620
  {
6205
6621
  var folders = {}, count = 0;
@@ -6261,7 +6677,7 @@
6261
6677
  }
6262
6678
  else
6263
6679
  {
6264
- $('#redactor-modal-tab-2').remove();
6680
+ $('#redactor-tab-control-2').remove();
6265
6681
  }
6266
6682
 
6267
6683
  if (this.opts.imageUpload || this.opts.s3)
@@ -6316,8 +6732,8 @@
6316
6732
  }
6317
6733
  else
6318
6734
  {
6319
- $('#redactor-modal-tab-1').remove();
6320
- $('#redactor-modal-tab-2').addClass('redactor_tabs_act');
6735
+ $('#redactor-tab-control-1').remove();
6736
+ $('#redactor-tab-control-2').addClass('redactor_tabs_act');
6321
6737
  $('#redactor_tab2').show();
6322
6738
  }
6323
6739
  }
@@ -6425,6 +6841,8 @@
6425
6841
  },
6426
6842
  imageSave: function(el)
6427
6843
  {
6844
+ this.imageResizeHide(false);
6845
+
6428
6846
  var $el = $(el);
6429
6847
  var parent = $el.parent();
6430
6848
 
@@ -6433,8 +6851,6 @@
6433
6851
  var floating = $('#redactor_form_image_align').val();
6434
6852
  var margin = '';
6435
6853
 
6436
- this.imageResizeHide(false);
6437
-
6438
6854
  if (floating === 'left')
6439
6855
  {
6440
6856
  margin = '0 ' + this.opts.imageFloatMargin + ' ' + this.opts.imageFloatMargin + ' 0';
@@ -6581,63 +6997,66 @@
6581
6997
 
6582
6998
  // resize
6583
6999
  var isResizing = false;
6584
- imageResizer.on('mousedown', function(e)
7000
+ if (imageResizer !== false)
6585
7001
  {
6586
- isResizing = true;
6587
- e.preventDefault();
7002
+ imageResizer.on('mousedown', function(e)
7003
+ {
7004
+ isResizing = true;
7005
+ e.preventDefault();
6588
7006
 
6589
- ratio = $image.width() / $image.height();
7007
+ ratio = $image.width() / $image.height();
6590
7008
 
6591
- start_x = Math.round(e.pageX - $image.eq(0).offset().left);
6592
- start_y = Math.round(e.pageY - $image.eq(0).offset().top);
7009
+ start_x = Math.round(e.pageX - $image.eq(0).offset().left);
7010
+ start_y = Math.round(e.pageY - $image.eq(0).offset().top);
6593
7011
 
6594
- });
7012
+ });
6595
7013
 
6596
- $(this.document.body).on('mousemove', $.proxy(function(e)
6597
- {
6598
- if (isResizing)
7014
+ $(this.document.body).on('mousemove', $.proxy(function(e)
6599
7015
  {
6600
- var mouse_x = Math.round(e.pageX - $image.eq(0).offset().left) - start_x;
6601
- var mouse_y = Math.round(e.pageY - $image.eq(0).offset().top) - start_y;
6602
-
6603
- var div_h = $image.height();
7016
+ if (isResizing)
7017
+ {
7018
+ var mouse_x = Math.round(e.pageX - $image.eq(0).offset().left) - start_x;
7019
+ var mouse_y = Math.round(e.pageY - $image.eq(0).offset().top) - start_y;
6604
7020
 
6605
- var new_h = parseInt(div_h, 10) + mouse_y;
6606
- var new_w = Math.round(new_h * ratio);
7021
+ var div_h = $image.height();
6607
7022
 
6608
- if (new_w > min_w)
6609
- {
6610
- $image.width(new_w);
7023
+ var new_h = parseInt(div_h, 10) + mouse_y;
7024
+ var new_w = Math.round(new_h * ratio);
6611
7025
 
6612
- if (new_w < 100)
6613
- {
6614
- this.imageEditter.css({
6615
- marginTop: '-7px',
6616
- marginLeft: '-13px',
6617
- fontSize: '9px',
6618
- padding: '3px 5px'
6619
- });
6620
- }
6621
- else
7026
+ if (new_w > min_w)
6622
7027
  {
6623
- this.imageEditter.css({
6624
- marginTop: '-11px',
6625
- marginLeft: '-18px',
6626
- fontSize: '11px',
6627
- padding: '7px 10px'
6628
- });
7028
+ $image.width(new_w);
7029
+
7030
+ if (new_w < 100)
7031
+ {
7032
+ this.imageEditter.css({
7033
+ marginTop: '-7px',
7034
+ marginLeft: '-13px',
7035
+ fontSize: '9px',
7036
+ padding: '3px 5px'
7037
+ });
7038
+ }
7039
+ else
7040
+ {
7041
+ this.imageEditter.css({
7042
+ marginTop: '-11px',
7043
+ marginLeft: '-18px',
7044
+ fontSize: '11px',
7045
+ padding: '7px 10px'
7046
+ });
7047
+ }
6629
7048
  }
6630
- }
6631
7049
 
6632
- start_x = Math.round(e.pageX - $image.eq(0).offset().left);
6633
- start_y = Math.round(e.pageY - $image.eq(0).offset().top);
7050
+ start_x = Math.round(e.pageX - $image.eq(0).offset().left);
7051
+ start_y = Math.round(e.pageY - $image.eq(0).offset().top);
6634
7052
 
6635
- this.sync()
6636
- }
6637
- }, this)).on('mouseup', function()
6638
- {
6639
- isResizing = false;
6640
- });
7053
+ this.sync()
7054
+ }
7055
+ }, this)).on('mouseup', function()
7056
+ {
7057
+ isResizing = false;
7058
+ });
7059
+ }
6641
7060
 
6642
7061
 
6643
7062
  this.$editor.on('keydown.redactor-image-delete', $.proxy(function(e)
@@ -6646,7 +7065,7 @@
6646
7065
 
6647
7066
  if (this.keyCode.BACKSPACE == key || this.keyCode.DELETE == key)
6648
7067
  {
6649
- this.bufferSet(false, false);
7068
+ this.bufferSet(false);
6650
7069
  this.imageResizeHide(false);
6651
7070
  this.imageRemove($image);
6652
7071
  }
@@ -6713,25 +7132,34 @@
6713
7132
  imageBox.append(this.imageEditter);
6714
7133
 
6715
7134
  // resizer
6716
- var imageResizer = $('<span id="redactor-image-resizer" data-redactor="verified"></span>');
6717
- imageResizer.css({
6718
- position: 'absolute',
6719
- zIndex: 2,
6720
- lineHeight: 1,
6721
- cursor: 'nw-resize',
6722
- bottom: '-4px',
6723
- right: '-5px',
6724
- border: '1px solid #fff',
6725
- backgroundColor: '#000',
6726
- width: '8px',
6727
- height: '8px'
6728
- });
6729
- imageResizer.attr('contenteditable', false);
6730
- imageBox.append(imageResizer);
7135
+ if (this.opts.imageResizable)
7136
+ {
7137
+ var imageResizer = $('<span id="redactor-image-resizer" data-redactor="verified"></span>');
7138
+ imageResizer.css({
7139
+ position: 'absolute',
7140
+ zIndex: 2,
7141
+ lineHeight: 1,
7142
+ cursor: 'nw-resize',
7143
+ bottom: '-4px',
7144
+ right: '-5px',
7145
+ border: '1px solid #fff',
7146
+ backgroundColor: '#000',
7147
+ width: '8px',
7148
+ height: '8px'
7149
+ });
7150
+ imageResizer.attr('contenteditable', false);
7151
+ imageBox.append(imageResizer);
7152
+
7153
+ imageBox.append($image);
6731
7154
 
6732
- imageBox.append($image);
7155
+ return imageResizer;
7156
+ }
7157
+ else
7158
+ {
7159
+ imageBox.append($image);
6733
7160
 
6734
- return imageResizer;
7161
+ return false;
7162
+ }
6735
7163
  },
6736
7164
  imageThumbClick: function(e)
6737
7165
  {
@@ -6799,6 +7227,24 @@
6799
7227
  this.observeImages();
6800
7228
  },
6801
7229
 
7230
+ // PROGRESS BAR
7231
+ buildProgressBar: function()
7232
+ {
7233
+ if ($('#redactor-progress').size() != 0) return;
7234
+
7235
+ this.$progressBar = $('<div id="redactor-progress"><span></span></div>');
7236
+ $(document.body).append(this.$progressBar);
7237
+ },
7238
+ showProgressBar: function()
7239
+ {
7240
+ this.buildProgressBar();
7241
+ $('#redactor-progress').fadeIn();
7242
+ },
7243
+ hideProgressBar: function()
7244
+ {
7245
+ $('#redactor-progress').fadeOut(1500);
7246
+ },
7247
+
6802
7248
  // MODAL
6803
7249
  modalTemplatesInit: function()
6804
7250
  {
@@ -6806,7 +7252,6 @@
6806
7252
  {
6807
7253
  modal_file: String()
6808
7254
  + '<section id="redactor-modal-file-insert">'
6809
- + '<div id="redactor-progress" class="redactor-progress-inline" style="display: none;"><span></span></div>'
6810
7255
  + '<form id="redactorUploadFileForm" method="post" action="" enctype="multipart/form-data">'
6811
7256
  + '<label>' + this.opts.curLang.filename + '</label>'
6812
7257
  + '<input type="text" id="redactor_filename" class="redactor_input" />'
@@ -6844,7 +7289,6 @@
6844
7289
  + '<a href="#" id="redactor-tab-control-2">' + this.opts.curLang.choose + '</a>'
6845
7290
  + '<a href="#" id="redactor-tab-control-3">' + this.opts.curLang.link + '</a>'
6846
7291
  + '</div>'
6847
- + '<div id="redactor-progress" class="redactor-progress-inline" style="display: none;"><span></span></div>'
6848
7292
  + '<form id="redactorInsertImageForm" method="post" action="" enctype="multipart/form-data">'
6849
7293
  + '<div id="redactor_tab1" class="redactor_tab">'
6850
7294
  + '<input type="file" id="redactor_file" name="' + this.opts.imageUploadParam + '" />'
@@ -6865,6 +7309,7 @@
6865
7309
 
6866
7310
  modal_link: String()
6867
7311
  + '<section id="redactor-modal-link-insert">'
7312
+ + '<select id="redactor-predefined-links" style="width: 99.5%; display: none;"></select>'
6868
7313
  + '<label>URL</label>'
6869
7314
  + '<input type="text" class="redactor_input" id="redactor_link_url" />'
6870
7315
  + '<label>' + this.opts.curLang.text + '</label>'
@@ -6904,44 +7349,107 @@
6904
7349
  },
6905
7350
  modalInit: function(title, content, width, callback)
6906
7351
  {
6907
- var $redactorModalOverlay = $('#redactor_modal_overlay');
7352
+ this.modalSetOverlay();
6908
7353
 
6909
- // modal overlay
6910
- if (!$redactorModalOverlay.length)
7354
+ this.$redactorModalWidth = width;
7355
+ this.$redactorModal = $('#redactor_modal');
7356
+
7357
+ if (!this.$redactorModal.length)
6911
7358
  {
6912
- this.$overlay = $redactorModalOverlay = $('<div id="redactor_modal_overlay" style="display: none;"></div>');
6913
- $('body').prepend(this.$overlay);
7359
+ this.$redactorModal = $('<div id="redactor_modal" style="display: none;" />');
7360
+ this.$redactorModal.append($('<div id="redactor_modal_close">&times;</div>'));
7361
+ this.$redactorModal.append($('<header id="redactor_modal_header" />'));
7362
+ this.$redactorModal.append($('<div id="redactor_modal_inner" />'));
7363
+ this.$redactorModal.appendTo(document.body);
6914
7364
  }
6915
7365
 
6916
- if (this.opts.modalOverlay)
7366
+ $('#redactor_modal_close').on('click', $.proxy(this.modalClose, this));
7367
+ $(document).on('keyup', $.proxy(this.modalCloseHandler, this));
7368
+ this.$editor.on('keyup', $.proxy(this.modalCloseHandler, this));
7369
+
7370
+ this.modalSetContent(content);
7371
+ this.modalSetTitle(title);
7372
+ this.modalSetDraggable();
7373
+ this.modalLoadTabs();
7374
+ this.modalOnCloseButton();
7375
+ this.modalSetButtonsWidth();
7376
+
7377
+ this.saveModalScroll = this.document.body.scrollTop;
7378
+ if (this.opts.autoresize === false)
6917
7379
  {
6918
- $redactorModalOverlay.show().on('click', $.proxy(this.modalClose, this));
7380
+ this.saveModalScroll = this.$editor.scrollTop();
6919
7381
  }
6920
7382
 
6921
- var $redactorModal = $('#redactor_modal');
7383
+ if (this.isMobile() === false) this.modalShowOnDesktop();
7384
+ else this.modalShowOnMobile();
6922
7385
 
6923
- if (!$redactorModal.length)
7386
+ // modal actions callback
7387
+ if (typeof callback === 'function')
6924
7388
  {
6925
- this.$modal = $redactorModal = $('<div id="redactor_modal" style="display: none;"><div id="redactor_modal_close">&times;</div><header id="redactor_modal_header"></header><div id="redactor_modal_inner"></div></div>');
6926
- $('body').append(this.$modal);
7389
+ callback();
6927
7390
  }
6928
7391
 
6929
- $('#redactor_modal_close').on('click', $.proxy(this.modalClose, this));
7392
+ // modal shown callback
7393
+ setTimeout($.proxy(function()
7394
+ {
7395
+ this.callback('modalOpened', this.$redactorModal);
7396
+
7397
+ }, this), 11);
7398
+
7399
+ // fix bootstrap modal focus
7400
+ $(document).off('focusin.modal');
6930
7401
 
6931
- this.hdlModalClose = $.proxy(function(e)
7402
+ // enter
7403
+ this.$redactorModal.find('input[type=text]').on('keypress', $.proxy(function(e)
6932
7404
  {
6933
- if (e.keyCode === this.keyCode.ESC)
7405
+ if (e.which === 13)
6934
7406
  {
6935
- this.modalClose();
6936
- return false;
7407
+ this.$redactorModal.find('.redactor_modal_action_btn').click();
7408
+ e.preventDefault();
6937
7409
  }
7410
+ }, this));
6938
7411
 
6939
- }, this);
7412
+ return this.$redactorModal;
6940
7413
 
6941
- $(document).keyup(this.hdlModalClose);
6942
- this.$editor.keyup(this.hdlModalClose);
7414
+ },
7415
+ modalShowOnDesktop: function()
7416
+ {
7417
+ this.$redactorModal.css({
7418
+ position: 'fixed',
7419
+ top: '-2000px',
7420
+ left: '50%',
7421
+ width: this.$redactorModalWidth + 'px',
7422
+ marginLeft: '-' + (this.$redactorModalWidth / 2) + 'px'
7423
+ }).show();
6943
7424
 
6944
- // set content
7425
+ this.modalSaveBodyOveflow = $(document.body).css('overflow');
7426
+ $(document.body).css('overflow', 'hidden');
7427
+
7428
+ setTimeout($.proxy(function()
7429
+ {
7430
+ var height = this.$redactorModal.outerHeight();
7431
+ this.$redactorModal.css({
7432
+ top: '50%',
7433
+ height: 'auto',
7434
+ minHeight: 'auto',
7435
+ marginTop: '-' + (height + 10) / 2 + 'px'
7436
+ });
7437
+ }, this), 15);
7438
+ },
7439
+ modalShowOnMobile: function()
7440
+ {
7441
+ this.$redactorModal.css({
7442
+ position: 'fixed',
7443
+ width: '100%',
7444
+ height: '100%',
7445
+ top: '0',
7446
+ left: '0',
7447
+ margin: '0',
7448
+ minHeight: '300px'
7449
+ }).show();
7450
+ },
7451
+ modalSetContent: function(content)
7452
+ {
6945
7453
  this.modalcontent = false;
6946
7454
  if (content.indexOf('#') == 0)
6947
7455
  {
@@ -6954,129 +7462,81 @@
6954
7462
  {
6955
7463
  $('#redactor_modal_inner').empty().append(content);
6956
7464
  }
6957
-
6958
- $redactorModal.find('#redactor_modal_header').html(title);
6959
-
6960
- // draggable
6961
- if (typeof $.fn.draggable !== 'undefined')
6962
- {
6963
- $redactorModal.draggable({ handle: '#redactor_modal_header' });
6964
- $redactorModal.find('#redactor_modal_header').css('cursor', 'move');
6965
- }
6966
-
6967
- var $redactor_tabs = $('#redactor_tabs');
6968
-
6969
- // tabs
6970
- if ($redactor_tabs.length )
6971
- {
6972
- var that = this;
6973
- $redactor_tabs.find('a').each(function(i, s)
6974
- {
6975
- i++;
6976
- $(s).on('click', function(e)
6977
- {
6978
- e.preventDefault();
6979
-
6980
- $redactor_tabs.find('a').removeClass('redactor_tabs_act');
6981
- $(this).addClass('redactor_tabs_act');
6982
- $('.redactor_tab').hide();
6983
- $('#redactor_tab' + i ).show();
6984
- $('#redactor_tab_selected').val(i);
6985
-
6986
- if (that.isMobile() === false)
6987
- {
6988
- var height = $redactorModal.outerHeight();
6989
- $redactorModal.css('margin-top', '-' + (height + 10) / 2 + 'px');
6990
- }
6991
- });
6992
- });
6993
- }
6994
-
6995
- $redactorModal.find('.redactor_btn_modal_close').on('click', $.proxy(this.modalClose, this));
6996
-
6997
- var buttons = $redactorModal.find('footer button');
7465
+ },
7466
+ modalSetTitle: function(title)
7467
+ {
7468
+ this.$redactorModal.find('#redactor_modal_header').html(title);
7469
+ },
7470
+ modalSetButtonsWidth: function()
7471
+ {
7472
+ var buttons = this.$redactorModal.find('footer button').not('.redactor_modal_btn_hidden');
6998
7473
  var buttonsSize = buttons.size();
6999
7474
  if (buttonsSize > 0)
7000
7475
  {
7001
- $(buttons).css('width', (width/buttonsSize) + 'px')
7002
- }
7003
-
7004
- // save scroll
7005
- if (this.opts.autoresize === true)
7006
- {
7007
- this.saveModalScroll = this.document.body.scrollTop;
7008
- }
7009
- else
7010
- {
7011
- this.saveModalScroll = this.$editor.scrollTop();
7476
+ $(buttons).css('width', (this.$redactorModalWidth/buttonsSize) + 'px')
7012
7477
  }
7013
-
7014
- if (this.isMobile() === false)
7478
+ },
7479
+ modalOnCloseButton: function()
7480
+ {
7481
+ this.$redactorModal.find('.redactor_btn_modal_close').on('click', $.proxy(this.modalClose, this));
7482
+ },
7483
+ modalSetOverlay: function()
7484
+ {
7485
+ if (this.opts.modalOverlay)
7015
7486
  {
7016
- $redactorModal.css({
7017
- position: 'fixed',
7018
- top: '-2000px',
7019
- left: '50%',
7020
- width: width + 'px',
7021
- marginLeft: '-' + (width / 2) + 'px'
7022
- }).show();
7023
-
7024
- this.modalSaveBodyOveflow = $(document.body).css('overflow');
7025
- $(document.body).css('overflow', 'hidden');
7487
+ this.$redactorModalOverlay = $('#redactor_modal_overlay');
7488
+ if (!this.$redactorModalOverlay.length)
7489
+ {
7490
+ this.$redactorModalOverlay = $('<div id="redactor_modal_overlay" style="display: none;"></div>');
7491
+ $('body').prepend(this.$redactorModalOverlay);
7492
+ }
7026
7493
 
7494
+ this.$redactorModalOverlay.show().on('click', $.proxy(this.modalClose, this));
7027
7495
  }
7028
- else
7496
+ },
7497
+ modalSetDraggable: function()
7498
+ {
7499
+ if (typeof $.fn.draggable !== 'undefined')
7029
7500
  {
7030
- $redactorModal.css({
7031
- position: 'fixed',
7032
- width: '100%',
7033
- height: '100%',
7034
- top: '0',
7035
- left: '0',
7036
- margin: '0',
7037
- minHeight: '300px'
7038
- }).show();
7501
+ this.$redactorModal.draggable({ handle: '#redactor_modal_header' });
7502
+ this.$redactorModal.find('#redactor_modal_header').css('cursor', 'move');
7039
7503
  }
7504
+ },
7505
+ modalLoadTabs: function()
7506
+ {
7507
+ var $redactor_tabs = $('#redactor_tabs');
7508
+ if (!$redactor_tabs.length) return false;
7040
7509
 
7041
- // modal actions callback
7042
- if (typeof callback === 'function')
7510
+ var that = this;
7511
+ $redactor_tabs.find('a').each(function(i, s)
7043
7512
  {
7044
- callback();
7045
- }
7046
-
7047
- // modal shown callback
7048
- setTimeout($.proxy(function()
7049
- {
7050
- this.callback('modalOpened');
7513
+ i++;
7514
+ $(s).on('click', function(e)
7515
+ {
7516
+ e.preventDefault();
7051
7517
 
7052
- }, this), 11);
7518
+ $redactor_tabs.find('a').removeClass('redactor_tabs_act');
7519
+ $(this).addClass('redactor_tabs_act');
7520
+ $('.redactor_tab').hide();
7521
+ $('#redactor_tab' + i ).show();
7522
+ $('#redactor_tab_selected').val(i);
7053
7523
 
7054
- // fix bootstrap modal focus
7055
- $(document).off('focusin.modal');
7524
+ if (that.isMobile() === false)
7525
+ {
7526
+ var height = that.$redactorModal.outerHeight();
7527
+ that.$redactorModal.css('margin-top', '-' + (height + 10) / 2 + 'px');
7528
+ }
7529
+ });
7530
+ });
7056
7531
 
7057
- if (this.isMobile() === false)
7532
+ },
7533
+ modalCloseHandler: function(e)
7534
+ {
7535
+ if (e.keyCode === this.keyCode.ESC)
7058
7536
  {
7059
- setTimeout(function()
7060
- {
7061
- var height = $redactorModal.outerHeight();
7062
- $redactorModal.css({
7063
- top: '50%',
7064
- height: 'auto',
7065
- minHeight: 'auto',
7066
- marginTop: '-' + (height + 10) / 2 + 'px'
7067
- });
7068
- }, 10);
7537
+ this.modalClose();
7538
+ return false;
7069
7539
  }
7070
-
7071
- $redactorModal.find('input[type=text]').keypress(function(e)
7072
- {
7073
- if (e.which === 13 )
7074
- {
7075
- $redactorModal.find('.redactor_modal_action_btn').click();
7076
- e.preventDefault();
7077
- }
7078
- });
7079
-
7080
7540
  },
7081
7541
  modalClose: function()
7082
7542
  {
@@ -7098,8 +7558,8 @@
7098
7558
  $('#redactor_modal_overlay').hide().off('click', this.modalClose);
7099
7559
  }
7100
7560
 
7101
- $(document).unbind('keyup', this.hdlModalClose);
7102
- this.$editor.unbind('keyup', this.hdlModalClose);
7561
+ $(document).off('keyup', this.modalCloseHandler);
7562
+ this.$editor.off('keyup', this.modalCloseHandler);
7103
7563
 
7104
7564
  this.selectionRestore();
7105
7565
 
@@ -7161,11 +7621,12 @@
7161
7621
  // Hack to pass bytes through unprocessed.
7162
7622
  if (xhr.overrideMimeType) xhr.overrideMimeType('text/plain; charset=x-user-defined');
7163
7623
 
7624
+ var that = this;
7164
7625
  xhr.onreadystatechange = function(e)
7165
7626
  {
7166
7627
  if (this.readyState == 4 && this.status == 200)
7167
7628
  {
7168
- $('#redactor-progress').fadeIn();
7629
+ that.showProgressBar();
7169
7630
  callback(decodeURIComponent(this.responseText));
7170
7631
  }
7171
7632
  else if(this.readyState == 4 && this.status != 200)
@@ -7210,7 +7671,7 @@
7210
7671
  {
7211
7672
  //setProgress(100, 'Upload completed.');
7212
7673
 
7213
- $('#redactor-progress, #redactor-progress-drag').hide();
7674
+ this.hideProgressBar();
7214
7675
 
7215
7676
  var s3image = url.split('?');
7216
7677
 
@@ -7320,7 +7781,7 @@
7320
7781
  },
7321
7782
  uploadSubmit: function(e)
7322
7783
  {
7323
- $('#redactor-progress').fadeIn();
7784
+ this.showProgressBar();
7324
7785
  this.uploadForm(this.element, this.uploadFrame());
7325
7786
  },
7326
7787
  uploadFrame: function()
@@ -7401,7 +7862,7 @@
7401
7862
  // Success
7402
7863
  if (this.uploadOptions.success)
7403
7864
  {
7404
- $('#redactor-progress').hide();
7865
+ this.hideProgressBar();
7405
7866
 
7406
7867
  if (typeof d !== 'undefined')
7407
7868
  {
@@ -7477,12 +7938,12 @@
7477
7938
  e.preventDefault();
7478
7939
 
7479
7940
  this.dropareabox.removeClass('hover').addClass('drop');
7480
-
7481
- this.dragUploadAjax(this.draguploadOptions.url, e.dataTransfer.files[0], false, false, false, this.draguploadOptions.uploadParam);
7941
+ this.showProgressBar();
7942
+ this.dragUploadAjax(this.draguploadOptions.url, e.dataTransfer.files[0], false, e, this.draguploadOptions.uploadParam);
7482
7943
 
7483
7944
  }, this );
7484
7945
  },
7485
- dragUploadAjax: function(url, file, directupload, progress, e, uploadParam)
7946
+ dragUploadAjax: function(url, file, directupload, e, uploadParam)
7486
7947
  {
7487
7948
  if (!directupload)
7488
7949
  {
@@ -7538,13 +7999,10 @@
7538
7999
 
7539
8000
  var json = (typeof data === 'string' ? $.parseJSON(data) : data);
7540
8001
 
8002
+ this.hideProgressBar();
8003
+
7541
8004
  if (directupload)
7542
8005
  {
7543
- progress.fadeOut('slow', function()
7544
- {
7545
- $(this).remove();
7546
- });
7547
-
7548
8006
  var $img = $('<img>');
7549
8007
  $img.attr('src', json.filelink).attr('id', 'drag-image-marker');
7550
8008
 
@@ -7631,6 +8089,21 @@
7631
8089
 
7632
8090
  return html == '';
7633
8091
  },
8092
+ getInternetExplorerVersion: function()
8093
+ {
8094
+ var rv = false;
8095
+ if (navigator.appName == 'Microsoft Internet Explorer')
8096
+ {
8097
+ var ua = navigator.userAgent;
8098
+ var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
8099
+ if (re.exec(ua) != null)
8100
+ {
8101
+ rv = parseFloat(RegExp.$1);
8102
+ }
8103
+ }
8104
+
8105
+ return rv;
8106
+ },
7634
8107
  isIe11: function()
7635
8108
  {
7636
8109
  return !!navigator.userAgent.match(/Trident\/7\./);
@@ -7745,11 +8218,9 @@
7745
8218
  // LINKIFY
7746
8219
  $.Redactor.fn.formatLinkify = function(protocol, convertLinks, convertImageLinks, convertVideoLinks, linkSize)
7747
8220
  {
7748
- var url1 = /(^|&lt;|\s)(www\..+?\..+?)([.),]?)(\s|\.\s+|\)|&gt;|$)/,
7749
- url2 = /(^|&lt;|\s)(((https?|ftp):\/\/|mailto:).+?)([.),]?)(\s|\.\s+|\)|&gt;|$)/,
7750
- urlImage = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/gi,
7751
- urlYoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig,
7752
- urlVimeo = /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/;
8221
+ var url = /(((https?|ftps?):\/\/)|www[.][^\s])(.+?\..+?)([.),]?)(\s|\.\s+|\)|$)/gi,
8222
+ rProtocol = /(https?|ftp):\/\//i,
8223
+ urlImage = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/gi;
7753
8224
 
7754
8225
  var childNodes = (this.$editor ? this.$editor.get(0) : this).childNodes, i = childNodes.length;
7755
8226
  while (i--)
@@ -7765,14 +8236,14 @@
7765
8236
  var iframeStart = '<iframe width="500" height="281" src="',
7766
8237
  iframeEnd = '" frameborder="0" allowfullscreen></iframe>';
7767
8238
 
7768
- if (html.match(urlYoutube))
8239
+ if (html.match(reUrlYoutube))
7769
8240
  {
7770
- html = html.replace(urlYoutube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd);
8241
+ html = html.replace(reUrlYoutube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd);
7771
8242
  $(n).after(html).remove();
7772
8243
  }
7773
- else if (html.match(urlVimeo))
8244
+ else if (html.match(reUrlVimeo))
7774
8245
  {
7775
- html = html.replace(urlVimeo, iframeStart + '//player.vimeo.com/video/$2' + iframeEnd);
8246
+ html = html.replace(reUrlVimeo, iframeStart + '//player.vimeo.com/video/$2' + iframeEnd);
7776
8247
  $(n).after(html).remove();
7777
8248
  }
7778
8249
  }
@@ -7786,74 +8257,32 @@
7786
8257
  }
7787
8258
 
7788
8259
  // link
7789
- if (convertLinks && html && (html.match(url1) || html.match(url2)))
8260
+ if (convertLinks && html && html.match(url))
7790
8261
  {
7791
- var found = true;
7792
- var first = true;
8262
+ var matches = html.match(url);
7793
8263
 
7794
- while (found)
8264
+ for (var i in matches)
7795
8265
  {
7796
- var href;
7797
- var url = url1;
7798
- var href1 = url1.exec(html);
7799
- var href2 = url2.exec(html);
8266
+ var href = matches[i];
8267
+ var text = href;
7800
8268
 
7801
- if (href1 && href1[2] && href2 && href2[2])
7802
- {
7803
- //process whichever came first sequentially *first*
7804
- var index1 = html.indexOf(href1[2]);
7805
- var index2 = html.indexOf(href2[2]);
7806
- if (index1 < index2)
7807
- {
7808
- href = href1;
7809
- url = url1;
7810
- }
7811
- else
7812
- {
7813
- href = href2;
7814
- url = url2
7815
- }
7816
- }
7817
- else if (href1 && href1[2])
7818
- {
7819
- href = href1;
7820
- url = url1
7821
- }
7822
- else if (href2 && href2[2])
7823
- {
7824
- href = href2;
7825
- url = url2
7826
- }
8269
+ var space = '';
8270
+ if (href.match(/\s$/) !== null) space = ' ';
7827
8271
 
7828
- found = (href && href.length);
7829
- if (found)
7830
- {
7831
- href = href[2];
7832
- }
8272
+ var addProtocol = protocol;
8273
+ if (href.match(rProtocol) !== null) addProtocol = '';
7833
8274
 
7834
- if (found && href && href.length > linkSize)
7835
- {
7836
- href = href.substring(0, linkSize) + '...';
7837
- }
8275
+ if (text.length > linkSize) text = text.substring(0, linkSize) + '...';
7838
8276
 
7839
- if (first)
7840
- {
7841
- html = html.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
7842
- }
8277
+ text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
7843
8278
 
7844
- if (found && href)
7845
- {
7846
- if (url == url1)
7847
- {
7848
- html = html.replace(url1, '$1<a href="' + protocol + '$2">' + $.trim(href) + '</a>$3$4')
7849
- }
7850
- else
7851
- {
7852
- html = html.replace(url2, '$1<a href="$2">' + $.trim(href) + '</a>$5$6');
7853
- }
7854
- }
8279
+ /*
8280
+ To handle URLs which may have $ characters in them, need to escape $ -> $$ to prevent $1 from getting treated as a backreference.
8281
+ See http://gotofritz.net/blog/code-snippets/escaping-in-replace-strings-in-javascript/
8282
+ */
8283
+ var escapedBackReferences = text.replace('$', '$$$');
7855
8284
 
7856
- first = false;
8285
+ html = html.replace(href, '<a href=\"' + addProtocol + $.trim(href) + '\">' + $.trim(escapedBackReferences) + '</a>' + space);
7857
8286
  }
7858
8287
 
7859
8288
  $(n).after(html).remove();