biola_wcms_components 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  /*
2
- Redactor v10.0.7
3
- Updated: January 31, 2015
2
+ Redactor v10.1.1
3
+ Updated: April 28, 2015
4
4
 
5
5
  http://imperavi.com/redactor/
6
6
 
@@ -28,9 +28,6 @@
28
28
 
29
29
  var uuid = 0;
30
30
 
31
- var reUrlYoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig;
32
- var reUrlVimeo = /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/;
33
-
34
31
  // Plugin
35
32
  $.fn.redactor = function(options)
36
33
  {
@@ -94,13 +91,13 @@
94
91
 
95
92
  // Functionality
96
93
  $.Redactor = Redactor;
97
- $.Redactor.VERSION = '10.0.7';
94
+ $.Redactor.VERSION = '10.1.1';
98
95
  $.Redactor.modules = ['alignment', 'autosave', 'block', 'buffer', 'build', 'button',
99
96
  'caret', 'clean', 'code', 'core', 'dropdown', 'file', 'focus',
100
97
  'image', 'indent', 'inline', 'insert', 'keydown', 'keyup',
101
98
  'lang', 'line', 'link', 'list', 'modal', 'observe', 'paragraphize',
102
99
  'paste', 'placeholder', 'progress', 'selection', 'shortcuts',
103
- 'tabifier', 'tidy', 'toolbar', 'upload', 'utils'];
100
+ 'tabifier', 'tidy', 'toolbar', 'upload', 'utils', 'linkify'];
104
101
 
105
102
  $.Redactor.opts = {
106
103
 
@@ -135,6 +132,7 @@
135
132
  autosaveName: false,
136
133
  autosaveInterval: 60, // seconds
137
134
  autosaveOnChange: false,
135
+ autosaveFields: false,
138
136
 
139
137
  linkTooltip: true,
140
138
  linkProtocol: 'http',
@@ -191,9 +189,13 @@
191
189
 
192
190
  tabifier: true,
193
191
 
194
- deniedTags: ['html', 'head', 'link', 'body', 'meta', 'script', 'style', 'applet'],
192
+ deniedTags: ['script', 'style'],
195
193
  allowedTags: false, // or array
196
194
 
195
+ paragraphizeBlocks: ['table', 'div', 'pre', 'form', 'ul', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dl', 'blockquote', 'figcaption',
196
+ 'address', 'section', 'header', 'footer', 'aside', 'article', 'object', 'style', 'script', 'iframe', 'select', 'input', 'textarea',
197
+ 'button', 'option', 'map', 'area', 'math', 'hr', 'fieldset', 'legend', 'hgroup', 'nav', 'figure', 'details', 'menu', 'summary', 'p'],
198
+
197
199
  removeComments: false,
198
200
  replaceTags: [
199
201
  ['strike', 'del']
@@ -323,9 +325,22 @@
323
325
  underline: 'Underline',
324
326
  alignment: 'Alignment',
325
327
  filename: 'Name (optional)',
326
- edit: 'Edit'
328
+ edit: 'Edit',
329
+ upload_label: 'Drop file here or '
330
+
327
331
  }
328
- }
332
+ },
333
+
334
+ linkify: {
335
+ regexps: {
336
+ youtube: /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig,
337
+ vimeo: /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/,
338
+ image: /((https?|www)[^\s]+\.)(jpe?g|png|gif)(\?[^\s-]+)?/ig,
339
+ url: /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/ig,
340
+ }
341
+ },
342
+
343
+ codemirror: false
329
344
  };
330
345
 
331
346
  // Functionality
@@ -371,6 +386,16 @@
371
386
  // setup allowed and denied tags
372
387
  this.tidy.setupAllowed();
373
388
 
389
+ // setup denied tags
390
+ if (this.opts.deniedTags !== false)
391
+ {
392
+ var tags = ['html', 'head', 'link', 'body', 'meta', 'applet'];
393
+ for (var i = 0; i < tags.length; i++)
394
+ {
395
+ this.opts.deniedTags.push(tags[i]);
396
+ }
397
+ }
398
+
374
399
  // load lang
375
400
  this.lang.load();
376
401
 
@@ -516,11 +541,11 @@
516
541
  autosave: function()
517
542
  {
518
543
  return {
544
+ html: false,
519
545
  enable: function()
520
546
  {
521
547
  if (!this.opts.autosave) return;
522
548
 
523
- this.autosave.html = false;
524
549
  this.autosave.name = (this.opts.autosaveName) ? this.opts.autosaveName : this.$textarea.attr('name');
525
550
 
526
551
  if (this.opts.autosaveOnChange) return;
@@ -536,12 +561,13 @@
536
561
  this.autosave.source = this.code.get();
537
562
 
538
563
  if (this.autosave.html === this.autosave.source) return;
539
- if (this.utils.isEmpty(this.autosave.source)) return;
564
+ //if (this.utils.isEmpty(this.autosave.source)) return;
540
565
 
541
566
  // data
542
567
  var data = {};
543
568
  data['name'] = this.autosave.name;
544
- data[this.autosave.name] = escape(encodeURIComponent(this.autosave.source));
569
+ data[this.autosave.name] = this.autosave.source;
570
+ data = this.autosave.getHiddenFields(data);
545
571
 
546
572
  // ajax
547
573
  var jsxhr = $.ajax({
@@ -552,6 +578,23 @@
552
578
 
553
579
  jsxhr.done(this.autosave.success);
554
580
  },
581
+ getHiddenFields: function(data)
582
+ {
583
+ if (this.opts.autosaveFields === false || typeof this.opts.autosaveFields !== 'object')
584
+ {
585
+ return data;
586
+ }
587
+
588
+ $.each(this.opts.autosaveFields, $.proxy(function(k, v)
589
+ {
590
+ if (v !== null && v.toString().indexOf('#') === 0) v = $(v).val();
591
+ data[k] = v;
592
+
593
+ }, this));
594
+
595
+ return data;
596
+
597
+ },
555
598
  success: function(data)
556
599
  {
557
600
  var json;
@@ -586,7 +629,7 @@
586
629
 
587
630
  if (typeof this.formatting[name].data != 'undefined') type = 'data';
588
631
  else if (typeof this.formatting[name].attr != 'undefined') type = 'attr';
589
- else if (typeof this.formatting[name].class != 'undefined') type = 'class';
632
+ else if (typeof this.formatting[name]['class'] != 'undefined') type = 'class';
590
633
 
591
634
  if (typeof this.formatting[name].clear != 'undefined')
592
635
  {
@@ -701,6 +744,11 @@
701
744
  this.block.toggle($(block));
702
745
  }
703
746
 
747
+ if (typeof this.block.type == 'undefined' && typeof this.block.value == 'undefined')
748
+ {
749
+ $(block).removeAttr('class').removeAttr('style');
750
+ }
751
+
704
752
  },
705
753
  setMultiple: function(tag)
706
754
  {
@@ -777,14 +825,20 @@
777
825
  {
778
826
  $.each(this.block.blocks, $.proxy(function(i,s)
779
827
  {
828
+ var $formatted = false;
780
829
  if (this.opts.linebreaks)
781
830
  {
782
831
  $(s).prepend('<br>').append('<br>');
783
- this.utils.replaceWithContents(s);
832
+ $formatted = this.utils.replaceWithContents(s);
784
833
  }
785
834
  else
786
835
  {
787
- this.utils.replaceToTag(s, 'p');
836
+ $formatted = this.utils.replaceToTag(s, 'p');
837
+ }
838
+
839
+ if ($formatted && typeof this.block.type == 'undefined' && typeof this.block.value == 'undefined')
840
+ {
841
+ $formatted.removeAttr('class').removeAttr('style');
788
842
  }
789
843
 
790
844
  }, this));
@@ -831,6 +885,11 @@
831
885
  if (this.block.isRemoveInline) this.utils.removeInlineTags($formatted);
832
886
  if (tag == 'p' || this.block.headTag) $formatted.find('p').contents().unwrap();
833
887
 
888
+ if (typeof this.block.type == 'undefined' && typeof this.block.value == 'undefined')
889
+ {
890
+ $formatted.removeAttr('class').removeAttr('style');
891
+ }
892
+
834
893
 
835
894
  }, this));
836
895
  }
@@ -893,7 +952,7 @@
893
952
  },
894
953
  formatListToBlockquote: function()
895
954
  {
896
- var block = $(this.block.blocks[0]).closest('ul, ol');
955
+ var block = $(this.block.blocks[0]).closest('ul, ol', this.$editor[0]);
897
956
 
898
957
  $(block).find('ul, ol').contents().unwrap();
899
958
  $(block).find('li').append($('<br>')).contents().unwrap();
@@ -986,10 +1045,10 @@
986
1045
  },
987
1046
  formatTableWrapping: function($formatted)
988
1047
  {
989
- if ($formatted.closest('table').length === 0) return;
1048
+ if ($formatted.closest('table', this.$editor[0]).length === 0) return;
990
1049
 
991
- if ($formatted.closest('tr').length === 0) $formatted.wrap('<tr>');
992
- if ($formatted.closest('td').length === 0 && $formatted.closest('th').length === 0)
1050
+ if ($formatted.closest('tr', this.$editor[0]).length === 0) $formatted.wrap('<tr>');
1051
+ if ($formatted.closest('td', this.$editor[0]).length === 0 && $formatted.closest('th').length === 0)
993
1052
  {
994
1053
  $formatted.wrap('<td>');
995
1054
  }
@@ -1302,6 +1361,9 @@
1302
1361
  // paste
1303
1362
  this.$editor.on('paste.redactor', $.proxy(this.paste.init, this));
1304
1363
 
1364
+ // cut
1365
+ this.$editor.on('cut.redactor', $.proxy(this.code.sync, this));
1366
+
1305
1367
  // keydown
1306
1368
  this.$editor.on('keydown.redactor', $.proxy(this.keydown.init, this));
1307
1369
 
@@ -1348,8 +1410,11 @@
1348
1410
  },
1349
1411
  setHelpers: function()
1350
1412
  {
1351
- // autosave
1352
- this.autosave.enable();
1413
+ // linkify
1414
+ if (this.linkify.isEnabled())
1415
+ {
1416
+ this.linkify.format();
1417
+ }
1353
1418
 
1354
1419
  // placeholder
1355
1420
  this.placeholder.enable();
@@ -1422,7 +1487,7 @@
1422
1487
  // dropdown
1423
1488
  if (btnObject.dropdown)
1424
1489
  {
1425
- var $dropdown = $('<div class="redactor-dropdown redactor-dropdown-box-' + btnName + '" style="display: none;">');
1490
+ var $dropdown = $('<div class="redactor-dropdown redactor-dropdown-' + this.uuid + ' redactor-dropdown-box-' + btnName + '" style="display: none;">');
1426
1491
  $button.data('dropdown', $dropdown);
1427
1492
  this.dropdown.build(btnName, $dropdown, btnObject.dropdown);
1428
1493
  }
@@ -1525,7 +1590,7 @@
1525
1590
  },
1526
1591
  setInactiveAll: function(key)
1527
1592
  {
1528
- if (typeof key == 'undefined')
1593
+ if (typeof key === 'undefined')
1529
1594
  {
1530
1595
  this.$toolbar.find('a.re-icon').removeClass('redactor-act');
1531
1596
  }
@@ -1572,7 +1637,7 @@
1572
1637
  var key = $btn.attr('rel');
1573
1638
  this.button.addCallback($btn, 'dropdown');
1574
1639
 
1575
- var $dropdown = $('<div class="redactor-dropdown redactor-dropdown-box-' + key + '" style="display: none;">');
1640
+ var $dropdown = $('<div class="redactor-dropdown redactor-dropdown-' + this.uuid + ' redactor-dropdown-box-' + key + '" style="display: none;">');
1576
1641
  $btn.data('dropdown', $dropdown);
1577
1642
 
1578
1643
  // build dropdown
@@ -1596,6 +1661,7 @@
1596
1661
  if (!this.opts.toolbar) return;
1597
1662
 
1598
1663
  var btn = this.button.build(key, { title: title });
1664
+ btn.addClass('redactor-btn-image');
1599
1665
  this.$toolbar.prepend($('<li>').append(btn));
1600
1666
 
1601
1667
  return btn;
@@ -1605,6 +1671,7 @@
1605
1671
  if (!this.opts.toolbar) return;
1606
1672
 
1607
1673
  var btn = this.button.build(key, { title: title });
1674
+ btn.addClass('redactor-btn-image');
1608
1675
  var $btn = this.button.get(afterkey);
1609
1676
 
1610
1677
  if ($btn.length !== 0) $btn.parent().after($('<li>').append(btn));
@@ -1617,6 +1684,7 @@
1617
1684
  if (!this.opts.toolbar) return;
1618
1685
 
1619
1686
  var btn = this.button.build(key, { title: title });
1687
+ btn.addClass('redactor-btn-image');
1620
1688
  var $btn = this.button.get(beforekey);
1621
1689
 
1622
1690
  if ($btn.length !== 0) $btn.parent().before($('<li>').append(btn));
@@ -1840,9 +1908,9 @@
1840
1908
 
1841
1909
  // replace dollar sign to entity
1842
1910
  html = html.replace(/\$/g, '&#36;');
1843
- html = html.replace(/”/g, '"');
1844
- html = html.replace(/‘/g, '\'');
1845
- html = html.replace(/’/g, '\'');
1911
+
1912
+ // replace special characters in links
1913
+ html = html.replace(/<a href="(.*?[^>]?)®(.*?[^>]?)">/gi, '<a href="$1&reg$2">');
1846
1914
 
1847
1915
  if (this.opts.replaceDivs) html = this.clean.replaceDivs(html);
1848
1916
  if (this.opts.linebreaks) html = this.clean.replaceParagraphsToBr(html);
@@ -1924,13 +1992,13 @@
1924
1992
  html = html.replace(new RegExp('<br\\s?/?></li>', 'gi'), '</li>');
1925
1993
  html = html.replace(new RegExp('</li><br\\s?/?>', 'gi'), '</li>');
1926
1994
  // remove verified
1927
- html = html.replace(new RegExp('<div(.*?[^>]) data-tagblock="redactor"(.*?[^>])>', 'gi'), '<div$1$2>');
1928
- html = html.replace(new RegExp('<(.*?) data-verified="redactor"(.*?[^>])>', 'gi'), '<$1$2>');
1929
- html = html.replace(new RegExp('<span(.*?[^>])\srel="(.*?[^>])"(.*?[^>])>', 'gi'), '<span$1$3>');
1930
- html = html.replace(new RegExp('<img(.*?[^>])\srel="(.*?[^>])"(.*?[^>])>', 'gi'), '<img$1$3>');
1931
- html = html.replace(new RegExp('<img(.*?[^>])\sstyle="" (.*?[^>])>', 'gi'), '<img$1 $2>');
1932
- html = html.replace(new RegExp('<img(.*?[^>])\sstyle (.*?[^>])>', 'gi'), '<img$1 $2>');
1933
- html = html.replace(new RegExp('<span class="redactor-invisible-space">(.*?)</span>', 'gi'), '$1');
1995
+ html = html.replace(/<div(.*?[^>]) data-tagblock="redactor"(.*?[^>])>/gi, '<div$1$2>');
1996
+ html = html.replace(/<(.*?) data-verified="redactor"(.*?[^>])>/gi, '<$1$2>');
1997
+ html = html.replace(/<span(.*?[^>])\srel="(.*?[^>])"(.*?[^>])>/gi, '<span$1$3>');
1998
+ html = html.replace(/<img(.*?[^>])\srel="(.*?[^>])"(.*?[^>])>/gi, '<img$1$3>');
1999
+ html = html.replace(/<img(.*?[^>])\sstyle="" (.*?[^>])>'/gi, '<img$1 $2>');
2000
+ html = html.replace(/<img(.*?[^>])\sstyle (.*?[^>])>'/gi, '<img$1 $2>');
2001
+ html = html.replace(/<span class="redactor-invisible-space">(.*?)<\/span>/gi, '$1');
1934
2002
  html = html.replace(/ data-save-url="(.*?[^>])"/gi, '');
1935
2003
 
1936
2004
  // remove image resize
@@ -1964,8 +2032,6 @@
1964
2032
  html = $.trim(html);
1965
2033
 
1966
2034
  html = html.replace(/\$/g, '&#36;');
1967
- html = html.replace(/‘/g, '\'');
1968
- html = html.replace(/’/g, '\'');
1969
2035
 
1970
2036
  // convert dirty spaces
1971
2037
  html = html.replace(/<span class="s1">/gi, '<span>');
@@ -1989,6 +2055,8 @@
1989
2055
  {
1990
2056
  html = html.replace(/”/g, '"');
1991
2057
  html = html.replace(/“/g, '"');
2058
+ html = html.replace(/‘/g, '\'');
2059
+ html = html.replace(/’/g, '\'');
1992
2060
 
1993
2061
  return this.clean.getPreCode(html);
1994
2062
  }
@@ -2055,6 +2123,7 @@
2055
2123
  html = this.clean.onPasteRemoveSpans(html);
2056
2124
  html = this.clean.onPasteRemoveEmpty(html);
2057
2125
 
2126
+
2058
2127
  html = this.clean.convertInline(html);
2059
2128
 
2060
2129
  return html;
@@ -2199,23 +2268,16 @@
2199
2268
  }
2200
2269
 
2201
2270
  var options = {
2202
- deniedTags: false,
2203
- allowedTags: tags,
2271
+ deniedTags: (this.opts.deniedTags) ? this.opts.deniedTags : false,
2272
+ allowedTags: (this.opts.allowedTags) ? this.opts.allowedTags : tags,
2204
2273
  removeComments: true,
2205
2274
  removePhp: true,
2206
- removeAttr: false,
2207
- allowedAttr: attrAllowed,
2275
+ removeAttr: (this.opts.removeAttr) ? this.opts.removeAttr : false,
2276
+ allowedAttr: (this.opts.allowedAttr) ? this.opts.allowedAttr : attrAllowed,
2208
2277
  removeEmpty: tagsEmpty
2209
2278
  };
2210
2279
 
2211
- // denied tags
2212
- if (this.opts.deniedTags)
2213
- {
2214
- options.deniedTags = this.opts.deniedTags;
2215
- }
2216
-
2217
2280
  return this.tidy.load(html, options);
2218
-
2219
2281
  },
2220
2282
  onPasteRemoveEmpty: function(html)
2221
2283
  {
@@ -2281,27 +2343,56 @@
2281
2343
  },
2282
2344
  savePreCode: function(html)
2283
2345
  {
2284
- var pre = html.match(/<(pre|code)(.*?)>([\w\W]*?)<\/(pre|code)>/gi);
2346
+ html = this.clean.savePreFormatting(html);
2347
+ html = this.clean.saveCodeFormatting(html);
2348
+
2349
+ return html;
2350
+ },
2351
+ savePreFormatting: function(html)
2352
+ {
2353
+ var pre = html.match(/<pre(.*?)>([\w\W]*?)<\/pre>/gi);
2285
2354
  if (pre !== null)
2286
2355
  {
2287
2356
  $.each(pre, $.proxy(function(i,s)
2288
2357
  {
2289
- var arr = s.match(/<(pre|code)(.*?)>([\w\W]*?)<\/(pre|code)>/i);
2358
+ var arr = s.match(/<pre(.*?)>([\w\W]*?)<\/pre>/i);
2290
2359
 
2291
- arr[3] = arr[3].replace(/<br\s?\/?>/g, '\n');
2292
- arr[3] = arr[3].replace(/&nbsp;/g, ' ');
2360
+ arr[2] = arr[2].replace(/<br\s?\/?>/g, '\n');
2361
+ arr[2] = arr[2].replace(/&nbsp;/g, ' ');
2293
2362
 
2294
2363
  if (this.opts.preSpaces)
2295
2364
  {
2296
- arr[3] = arr[3].replace(/\t/g, Array(this.opts.preSpaces + 1).join(' '));
2365
+ arr[2] = arr[2].replace(/\t/g, Array(this.opts.preSpaces + 1).join(' '));
2297
2366
  }
2298
2367
 
2299
- arr[3] = this.clean.encodeEntities(arr[3]);
2368
+ arr[2] = this.clean.encodeEntities(arr[2]);
2369
+
2370
+ // $ fix
2371
+ arr[2] = arr[2].replace(/\$/g, '&#36;');
2372
+
2373
+ html = html.replace(s, '<pre' + arr[1] + '>' + arr[2] + '</pre>');
2374
+
2375
+ }, this));
2376
+ }
2377
+
2378
+ return html;
2379
+ },
2380
+ saveCodeFormatting: function(html)
2381
+ {
2382
+ var code = html.match(/<code(.*?[^>])>(.*?)<\/code>/gi);
2383
+ if (code !== null)
2384
+ {
2385
+ $.each(code, $.proxy(function(i,s)
2386
+ {
2387
+ var arr = s.match(/<code(.*?[^>])>(.*?)<\/code>/i);
2388
+
2389
+ arr[2] = arr[2].replace(/&nbsp;/g, ' ');
2390
+ arr[2] = this.clean.encodeEntities(arr[2]);
2300
2391
 
2301
2392
  // $ fix
2302
- arr[3] = arr[3].replace(/\$/g, '&#36;');
2393
+ arr[2] = arr[2].replace(/\$/g, '&#36;');
2303
2394
 
2304
- html = html.replace(s, '<' + arr[1] + arr[2] + '>' + arr[3] + '</' + arr[1] + '>');
2395
+ html = html.replace(s, '<code' + arr[1] + '>' + arr[2] + '</code>');
2305
2396
 
2306
2397
  }, this));
2307
2398
  }
@@ -2323,7 +2414,7 @@
2323
2414
  html = this.clean.getTextFromHtml(html);
2324
2415
  html = html.replace(/\n/g, '<br />');
2325
2416
 
2326
- if (this.opts.paragraphize && typeof paragraphize == 'undefined')
2417
+ if (this.opts.paragraphize && typeof paragraphize == 'undefined' && !this.utils.browser('mozilla'))
2327
2418
  {
2328
2419
  html = this.paragraphize.load(html);
2329
2420
  }
@@ -2412,22 +2503,34 @@
2412
2503
  });
2413
2504
 
2414
2505
  },
2506
+ cleanEmptyParagraph: function()
2507
+ {
2508
+ var p = this.$editor.find("p").first();
2509
+
2510
+ if (this.utils.isEmpty(p.html()))
2511
+ {
2512
+ p.remove();
2513
+ }
2514
+ },
2415
2515
  setVerified: function(html)
2416
2516
  {
2417
2517
  if (this.utils.browser('msie')) return html;
2418
2518
 
2419
2519
  html = html.replace(new RegExp('<img(.*?[^>])>', 'gi'), '<img$1 data-verified="redactor">');
2420
- html = html.replace(new RegExp('<span(.*?)>', 'gi'), '<span$1 data-verified="redactor">');
2520
+ html = html.replace(new RegExp('<span(.*?[^>])>', 'gi'), '<span$1 data-verified="redactor">');
2421
2521
 
2422
2522
  var matches = html.match(new RegExp('<(span|img)(.*?)style="(.*?)"(.*?[^>])>', 'gi'));
2523
+
2423
2524
  if (matches)
2424
2525
  {
2425
2526
  var len = matches.length;
2426
2527
  for (var i = 0; i < len; i++)
2427
2528
  {
2428
2529
  try {
2530
+
2429
2531
  var newTag = matches[i].replace(/style="(.*?)"/i, 'style="$1" rel="$1"');
2430
- html = html.replace(new RegExp(matches[i], 'gi'), newTag);
2532
+ html = html.replace(matches[i], newTag);
2533
+
2431
2534
  }
2432
2535
  catch (e) {}
2433
2536
  }
@@ -2498,6 +2601,9 @@
2498
2601
  html = html.replace(/<div(.*?)>([\w\W]*?)<\/div>/gi, '<p$1>$2</p>');
2499
2602
  }
2500
2603
 
2604
+ html = html.replace(/<div(.*?[^>])>/gi, '');
2605
+ html = html.replace(/<\/div>/gi, '');
2606
+
2501
2607
  return html;
2502
2608
  },
2503
2609
  replaceDivsToBr: function(html)
@@ -2540,6 +2646,8 @@
2540
2646
  this.$editor.html(html);
2541
2647
  this.code.sync();
2542
2648
 
2649
+ if (html !== '') this.placeholder.remove();
2650
+
2543
2651
  setTimeout($.proxy(this.buffer.add, this), 15);
2544
2652
  if (this.start === false) this.observe.load();
2545
2653
 
@@ -2590,8 +2698,22 @@
2590
2698
 
2591
2699
  this.start = false;
2592
2700
 
2593
- // autosave on change
2701
+ if (this.autosave.html == false)
2702
+ {
2703
+ this.autosave.html = this.code.get();
2704
+ }
2705
+
2706
+ if (this.opts.codemirror)
2707
+ {
2708
+ this.$textarea.next('.CodeMirror').each(function(i, el)
2709
+ {
2710
+ el.CodeMirror.setValue(html);
2711
+ });
2712
+ }
2713
+
2714
+ //autosave
2594
2715
  this.autosave.onChange();
2716
+ this.autosave.enable();
2595
2717
  },
2596
2718
  toggle: function()
2597
2719
  {
@@ -2609,7 +2731,8 @@
2609
2731
  this.code.offset = this.caret.getOffset();
2610
2732
  var scroll = $(window).scrollTop();
2611
2733
 
2612
- var height = this.$editor.innerHeight();
2734
+ var width = this.$editor.innerWidth(),
2735
+ height = this.$editor.innerHeight();
2613
2736
 
2614
2737
  this.$editor.hide();
2615
2738
 
@@ -2619,17 +2742,33 @@
2619
2742
  // indent code
2620
2743
  html = this.tabifier.get(html);
2621
2744
 
2622
- this.$textarea.val(html).height(height).show().focus();
2623
- this.$textarea.on('keydown.redactor-textarea-indenting', this.code.textareaIndenting);
2624
-
2625
- $(window).scrollTop(scroll);
2745
+ this.$textarea.val(html);
2626
2746
 
2627
- if (this.$textarea[0].setSelectionRange)
2747
+ if (this.opts.codemirror)
2628
2748
  {
2629
- this.$textarea[0].setSelectionRange(0, 0);
2749
+ this.$textarea.next('.CodeMirror').each(function(i, el)
2750
+ {
2751
+ $(el).show();
2752
+ el.CodeMirror.setValue(html);
2753
+ el.CodeMirror.setSize(width, height);
2754
+ el.CodeMirror.refresh();
2755
+ el.CodeMirror.focus();
2756
+ });
2630
2757
  }
2758
+ else
2759
+ {
2760
+ this.$textarea.height(height).show().focus();
2761
+ this.$textarea.on('keydown.redactor-textarea-indenting', this.code.textareaIndenting);
2762
+
2763
+ $(window).scrollTop(scroll);
2631
2764
 
2632
- this.$textarea[0].scrollTop = 0;
2765
+ if (this.$textarea[0].setSelectionRange)
2766
+ {
2767
+ this.$textarea[0].setSelectionRange(0, 0);
2768
+ }
2769
+
2770
+ this.$textarea[0].scrollTop = 0;
2771
+ }
2633
2772
 
2634
2773
  this.opts.visual = false;
2635
2774
 
@@ -2639,15 +2778,32 @@
2639
2778
  },
2640
2779
  showVisual: function()
2641
2780
  {
2781
+ var html;
2782
+
2642
2783
  if (this.opts.visual) return;
2643
2784
 
2644
- var html = this.$textarea.hide().val();
2785
+ if (this.opts.codemirror)
2786
+ {
2787
+ this.$textarea.next('.CodeMirror').each(function(i, el)
2788
+ {
2789
+ html = el.CodeMirror.getValue();
2790
+ });
2791
+ }
2792
+ else
2793
+ {
2794
+ html = this.$textarea.hide().val();
2795
+ }
2645
2796
 
2646
2797
  if (this.modified !== this.clean.removeSpaces(html))
2647
2798
  {
2648
2799
  this.code.set(html);
2649
2800
  }
2650
2801
 
2802
+ if (this.opts.codemirror)
2803
+ {
2804
+ this.$textarea.next('.CodeMirror').hide();
2805
+ }
2806
+
2651
2807
  this.$editor.show();
2652
2808
 
2653
2809
  if (!this.utils.isEmpty(html))
@@ -2664,6 +2820,7 @@
2664
2820
 
2665
2821
  this.observe.load();
2666
2822
  this.opts.visual = true;
2823
+ this.core.setCallback('visual', html);
2667
2824
  },
2668
2825
  textareaIndenting: function(e)
2669
2826
  {
@@ -2733,12 +2890,30 @@
2733
2890
  this.$element.off('.redactor').removeData('redactor');
2734
2891
  this.$editor.off('.redactor');
2735
2892
 
2893
+ $(document).off('click.redactor-image-delete.' + this.uuid);
2894
+ $(document).off('click.redactor-image-resize-hide.' + this.uuid);
2895
+ $(document).off('touchstart.redactor.' + this.uuid + ' click.redactor.' + this.uuid);
2896
+ $("body").off('scroll.redactor.' + this.uuid);
2897
+ $(this.opts.toolbarFixedTarget).off('scroll.redactor.' + this.uuid);
2898
+
2736
2899
  // common
2737
2900
  this.$editor.removeClass('redactor-editor redactor-linebreaks redactor-placeholder');
2738
2901
  this.$editor.removeAttr('contenteditable');
2739
2902
 
2740
2903
  var html = this.code.get();
2741
2904
 
2905
+ // dropdowns off
2906
+ this.$toolbar.find('a').each(function()
2907
+ {
2908
+ var $el = $(this);
2909
+ if ($el.data('dropdown'))
2910
+ {
2911
+ $el.data('dropdown').remove();
2912
+ $el.data('dropdown', {});
2913
+ }
2914
+ });
2915
+
2916
+
2742
2917
  if (this.build.isTextarea())
2743
2918
  {
2744
2919
  this.$box.after(this.$element);
@@ -2777,21 +2952,31 @@
2777
2952
  {
2778
2953
  $.each(this.opts.formattingAdd, $.proxy(function(i,s)
2779
2954
  {
2780
- var name = s.tag;
2781
- if (typeof s.class != 'undefined')
2955
+ var name = s.tag,
2956
+ func;
2957
+
2958
+ if (typeof s['class'] != 'undefined')
2782
2959
  {
2783
- name = name + '-' + s.class;
2960
+ name = name + '-' + s['class'];
2784
2961
  }
2785
2962
 
2786
2963
  s.type = (this.utils.isBlockTag(s.tag)) ? 'block' : 'inline';
2787
- var func = (s.type == 'inline') ? 'inline.formatting' : 'block.formatting';
2964
+
2965
+ if (typeof s.func !== "undefined")
2966
+ {
2967
+ func = s.func;
2968
+ }
2969
+ else
2970
+ {
2971
+ func = (s.type == 'inline') ? 'inline.formatting' : 'block.formatting';
2972
+ }
2788
2973
 
2789
2974
  if (this.opts.linebreaks && s.type == 'block' && s.tag == 'p') return;
2790
2975
 
2791
2976
  this.formatting[name] = {
2792
2977
  tag: s.tag,
2793
2978
  style: s.style,
2794
- 'class': s.class,
2979
+ 'class': s['class'],
2795
2980
  attr: s.attr,
2796
2981
  data: s.data,
2797
2982
  clear: s.clear
@@ -2898,7 +3083,6 @@
2898
3083
  $dropdown.css({ position: 'absolute', left: left, top: top }).show();
2899
3084
  }
2900
3085
 
2901
-
2902
3086
  this.core.setCallback('dropdownShown', { dropdown: $dropdown, key: key, button: $button });
2903
3087
  }
2904
3088
 
@@ -2930,7 +3114,7 @@
2930
3114
  this.$toolbar.find('a.dropact').removeClass('redactor-act').removeClass('dropact');
2931
3115
 
2932
3116
  $(document.body).removeClass('body-redactor-hidden').css('margin-right', 0);
2933
- $('.redactor-dropdown').hide();
3117
+ $('.redactor-dropdown-' + this.uuid).hide();
2934
3118
  this.core.setCallback('dropdownHide');
2935
3119
  },
2936
3120
  hide: function (e)
@@ -3032,8 +3216,7 @@
3032
3216
 
3033
3217
  if (first[0].tagName == 'UL' || first[0].tagName == 'OL')
3034
3218
  {
3035
- first = first.find('li').first();
3036
- var child = first.children().first();
3219
+ var child = first.find('li').first();
3037
3220
  if (!this.utils.isBlock(child) && child.text() === '')
3038
3221
  {
3039
3222
  // empty inline tag in li
@@ -3060,6 +3243,8 @@
3060
3243
  if (this.utils.browser('mozilla') || this.utils.browser('msie'))
3061
3244
  {
3062
3245
  var last = this.$editor.children().last();
3246
+
3247
+ this.$editor.focus();
3063
3248
  this.caret.setEnd(last);
3064
3249
  }
3065
3250
  else
@@ -3102,7 +3287,7 @@
3102
3287
  },
3103
3288
  showEdit: function($image)
3104
3289
  {
3105
- var $link = $image.closest('a');
3290
+ var $link = $image.closest('a', this.$editor[0]);
3106
3291
 
3107
3292
  this.modal.load('imageEdit', this.lang.get('edit'), 705);
3108
3293
 
@@ -3179,7 +3364,7 @@
3179
3364
  this.image.hideResize();
3180
3365
  this.buffer.set();
3181
3366
 
3182
- var $link = $image.closest('a');
3367
+ var $link = $image.closest('a', this.$editor[0]);
3183
3368
 
3184
3369
  $image.attr('alt', $('#redactor-image-title').val());
3185
3370
 
@@ -3241,7 +3426,7 @@
3241
3426
  }
3242
3427
 
3243
3428
  $image.on('mousedown', $.proxy(this.image.hideResize, this));
3244
- $image.on('click touchstart', $.proxy(function(e)
3429
+ $image.on('click.redactor touchstart', $.proxy(function(e)
3245
3430
  {
3246
3431
  this.observe.image = $image;
3247
3432
 
@@ -3249,8 +3434,8 @@
3249
3434
 
3250
3435
  this.image.resizer = this.image.loadEditableControls($image);
3251
3436
 
3252
- $(document).on('click.redactor-image-resize-hide', $.proxy(this.image.hideResize, this));
3253
- this.$editor.on('click.redactor-image-resize-hide', $.proxy(this.image.hideResize, this));
3437
+ $(document).on('click.redactor-image-resize-hide.' + this.uuid, $.proxy(this.image.hideResize, this));
3438
+ this.$editor.on('click.redactor-image-resize-hide.' + this.uuid, $.proxy(this.image.hideResize, this));
3254
3439
 
3255
3440
  // resize
3256
3441
  if (!this.opts.imageResizable) return;
@@ -3307,8 +3492,11 @@
3307
3492
 
3308
3493
  if (height < 50 || width < 100) return;
3309
3494
 
3495
+ var height = Math.round(this.image.resizeHandle.el.width() / this.image.resizeHandle.ratio);
3496
+
3497
+ this.image.resizeHandle.el.attr({width: width, height: height});
3310
3498
  this.image.resizeHandle.el.width(width);
3311
- this.image.resizeHandle.el.height(this.image.resizeHandle.el.width()/this.image.resizeHandle.ratio);
3499
+ this.image.resizeHandle.el.height(height);
3312
3500
 
3313
3501
  this.code.sync();
3314
3502
  },
@@ -3352,7 +3540,7 @@
3352
3540
  },
3353
3541
  hideResize: function(e)
3354
3542
  {
3355
- if (e && $(e.target).closest('#redactor-image-box').length !== 0) return;
3543
+ if (e && $(e.target).closest('#redactor-image-box', this.$editor[0]).length !== 0) return;
3356
3544
  if (e && e.target.tagName == 'IMG')
3357
3545
  {
3358
3546
  var $image = $(e.target);
@@ -3383,8 +3571,8 @@
3383
3571
  return $(this).contents();
3384
3572
  });
3385
3573
 
3386
- $(document).off('click.redactor-image-resize-hide');
3387
- this.$editor.off('click.redactor-image-resize-hide');
3574
+ $(document).off('click.redactor-image-resize-hide.' + this.uuid);
3575
+ this.$editor.off('click.redactor-image-resize-hide.' + this.uuid);
3388
3576
 
3389
3577
  if (typeof this.image.resizeHandle !== 'undefined')
3390
3578
  {
@@ -3464,8 +3652,8 @@
3464
3652
  remove: function(image)
3465
3653
  {
3466
3654
  var $image = $(image);
3467
- var $link = $image.closest('a');
3468
- var $figure = $image.closest('figure');
3655
+ var $link = $image.closest('a', this.$editor[0]);
3656
+ var $figure = $image.closest('figure', this.$editor[0]);
3469
3657
  var $parent = $image.parent();
3470
3658
  if ($('#redactor-image-box').length !== 0)
3471
3659
  {
@@ -3650,7 +3838,7 @@
3650
3838
 
3651
3839
  var current = this.selection.getCurrent();
3652
3840
 
3653
- var $item = $(current).closest('li');
3841
+ var $item = $(current).closest('li', this.$editor[0]);
3654
3842
  var $parent = $item.parent();
3655
3843
  if ($item.length !== 0 && $parent.length !== 0 && $parent[0].tagName == 'LI')
3656
3844
  {
@@ -3714,7 +3902,7 @@
3714
3902
  var type, value;
3715
3903
 
3716
3904
  if (typeof this.formatting[name].style != 'undefined') type = 'style';
3717
- else if (typeof this.formatting[name].class != 'undefined') type = 'class';
3905
+ else if (typeof this.formatting[name]['class'] != 'undefined') type = 'class';
3718
3906
 
3719
3907
  if (type) value = this.formatting[name][type];
3720
3908
 
@@ -3734,11 +3922,24 @@
3734
3922
  if (tag == tags[i]) tag = replaced[i];
3735
3923
  }
3736
3924
 
3925
+ if (this.opts.allowedTags)
3926
+ {
3927
+ if ($.inArray(tag, this.opts.allowedTags) == -1) return;
3928
+ }
3929
+ else
3930
+ {
3931
+ if ($.inArray(tag, this.opts.deniedTags) !== -1) return;
3932
+ }
3933
+
3737
3934
  this.inline.type = type || false;
3738
3935
  this.inline.value = value || false;
3739
3936
 
3740
3937
  this.buffer.set();
3741
- this.$editor.focus();
3938
+
3939
+ if (!this.utils.browser('msie'))
3940
+ {
3941
+ this.$editor.focus();
3942
+ }
3742
3943
 
3743
3944
  this.selection.get();
3744
3945
 
@@ -3754,19 +3955,23 @@
3754
3955
  formatCollapsed: function(tag)
3755
3956
  {
3756
3957
  var current = this.selection.getCurrent();
3757
- var $parent = $(current).closest(tag + '[data-redactor-tag=' + tag + ']');
3958
+ var $parent = $(current).closest(tag + '[data-redactor-tag=' + tag + ']', this.$editor[0]);
3758
3959
 
3759
3960
  // inline there is
3760
3961
  if ($parent.length !== 0 && (this.inline.type != 'style' && $parent[0].tagName != 'SPAN'))
3761
3962
  {
3762
- this.caret.setAfter($parent[0]);
3763
-
3764
3963
  // remove empty
3765
- if ( this.utils.isEmpty($parent.text()))
3964
+ if (this.utils.isEmpty($parent.text()))
3766
3965
  {
3966
+ this.caret.setAfter($parent[0]);
3967
+
3767
3968
  $parent.remove();
3768
3969
  this.code.sync();
3769
3970
  }
3971
+ else if (this.utils.isEndOfElement($parent))
3972
+ {
3973
+ this.caret.setAfter($parent[0]);
3974
+ }
3770
3975
 
3771
3976
  return;
3772
3977
  }
@@ -4358,6 +4563,8 @@
4358
4563
  }
4359
4564
 
4360
4565
  this.range.insertNode(frag);
4566
+ this.range.collapse(false);
4567
+ this.selection.addRange();
4361
4568
  }
4362
4569
  };
4363
4570
  },
@@ -4404,7 +4611,7 @@
4404
4611
  var $table = false;
4405
4612
  if (this.keydown.block && this.keydown.block.tagName === 'TD')
4406
4613
  {
4407
- $table = $(this.keydown.block).closest('table');
4614
+ $table = $(this.keydown.block).closest('table', this.$editor[0]);
4408
4615
  }
4409
4616
 
4410
4617
  if ($table && $table.find('td').last()[0] === this.keydown.block)
@@ -4478,21 +4685,26 @@
4478
4685
  current = this.selection.getCurrent();
4479
4686
  $next = $(this.keydown.current).next();
4480
4687
 
4481
- if (current !== false && $(current).hasClass('redactor-invisible-space'))
4688
+ if ($next.length !== 0 && $next[0].tagName == 'BR')
4689
+ {
4690
+ return this.keydown.insertBreakLine(e);
4691
+ }
4692
+ else if (current !== false && $(current).hasClass('redactor-invisible-space'))
4482
4693
  {
4483
4694
  this.caret.setAfter(current);
4484
4695
  $(current).contents().unwrap();
4696
+
4485
4697
  return this.keydown.insertDblBreakLine(e);
4486
4698
  }
4487
4699
  else
4488
4700
  {
4489
- if ($next.length === 0 && current === false && typeof $next.context != 'undefined')
4701
+ if (this.utils.isEndOfEditor())
4490
4702
  {
4491
4703
  return this.keydown.insertDblBreakLine(e);
4492
4704
  }
4493
- else if (this.utils.isEndOfEditor())
4705
+ else if ($next.length === 0 && current === false && typeof $next.context != 'undefined')
4494
4706
  {
4495
- return this.keydown.insertDblBreakLine(e);
4707
+ return this.keydown.insertBreakLine(e);
4496
4708
  }
4497
4709
 
4498
4710
  return this.keydown.insertBreakLine(e);
@@ -4503,16 +4715,34 @@
4503
4715
  setTimeout($.proxy(this.keydown.replaceDivToBreakLine, this), 1);
4504
4716
  }
4505
4717
  // paragraphs
4506
- else if (!this.opts.linebreaks && this.keydown.block && this.keydown.block.tagName !== 'LI')
4718
+ else if (!this.opts.linebreaks && this.keydown.block)
4507
4719
  {
4508
- setTimeout($.proxy(this.keydown.replaceDivToParagraph, this), 1);
4720
+ if (this.keydown.block.tagName !== 'LI')
4721
+ {
4722
+ setTimeout($.proxy(this.keydown.replaceDivToParagraph, this), 1);
4723
+ }
4724
+ else
4725
+ {
4726
+ current = this.selection.getCurrent();
4727
+ var $parent = $(current).closest('li', this.$editor[0]);
4728
+ var $list = $parent.closest('ul,ol', this.$editor[0]);
4729
+
4730
+ if ($parent.length !== 0 && this.utils.isEmpty($parent.html()) && $list.next().length === 0 && this.utils.isEmpty($list.find("li").last().html()))
4731
+ {
4732
+ $list.find("li").last().remove();
4733
+
4734
+ var node = $(this.opts.emptyHtml);
4735
+ $list.after(node);
4736
+ this.caret.setStart(node);
4737
+
4738
+ return false;
4739
+ }
4740
+ }
4509
4741
  }
4510
4742
  else if (!this.opts.linebreaks && !this.keydown.block)
4511
4743
  {
4512
4744
  return this.keydown.insertParagraph(e);
4513
4745
  }
4514
-
4515
-
4516
4746
  }
4517
4747
 
4518
4748
  // Shift+Enter or Ctrl+Enter
@@ -4531,6 +4761,12 @@
4531
4761
  // image delete and backspace
4532
4762
  if (key === this.keyCode.BACKSPACE || key === this.keyCode.DELETE)
4533
4763
  {
4764
+ if (this.utils.browser('mozilla') && this.keydown.current && this.keydown.current.tagName === 'TD')
4765
+ {
4766
+ e.preventDefault();
4767
+ return false;
4768
+ }
4769
+
4534
4770
  var nodes = this.selection.getNodes();
4535
4771
  if (nodes)
4536
4772
  {
@@ -4841,26 +5077,66 @@
4841
5077
  e.stopPropagation();
4842
5078
 
4843
5079
  this.selection.get();
5080
+
4844
5081
  var br1 = document.createElement('br');
4845
5082
 
4846
- this.range.deleteContents();
5083
+ if (this.utils.browser('msie'))
5084
+ {
5085
+ this.range.collapse(false);
5086
+ this.range.setEnd(this.range.endContainer, this.range.endOffset);
5087
+ }
5088
+ else
5089
+ {
5090
+ this.range.deleteContents();
5091
+ }
5092
+
4847
5093
  this.range.insertNode(br1);
4848
5094
 
5095
+ // move br outside A tag
5096
+ var $parentA = $(br1).parent("a");
5097
+
5098
+ if ($parentA.length > 0)
5099
+ {
5100
+ $parentA.find(br1)
5101
+ .remove();
5102
+
5103
+ $parentA.after(br1);
5104
+ }
5105
+
4849
5106
  if (dbl === true)
4850
5107
  {
5108
+ var $next = $(br1).next();
5109
+ if ($next.length !== 0 && $next[0].tagName === 'BR' && this.utils.isEndOfEditor())
5110
+ {
5111
+ this.caret.setAfter(br1);
5112
+ this.code.sync();
5113
+ return false;
5114
+ }
5115
+
4851
5116
  var br2 = document.createElement('br');
5117
+
4852
5118
  this.range.insertNode(br2);
4853
5119
  this.caret.setAfter(br2);
4854
5120
  }
4855
5121
  else
4856
5122
  {
4857
- this.caret.setAfter(br1);
5123
+ this.keydown.insertBreakLineProcessingAfter(br1);
4858
5124
  }
4859
5125
 
4860
5126
  this.code.sync();
4861
-
4862
5127
  return false;
4863
5128
  },
5129
+ insertBreakLineProcessingAfter: function(node)
5130
+ {
5131
+ var space = this.utils.createSpaceElement();
5132
+ $(node).after(space);
5133
+ this.selection.selectElement(space);
5134
+
5135
+ $(space).replaceWith(function()
5136
+ {
5137
+ return $(this).contents();
5138
+ });
5139
+ },
4864
5140
  removeInvisibleSpace: function()
4865
5141
  {
4866
5142
  var $current = $(this.keydown.current);
@@ -4873,9 +5149,9 @@
4873
5149
  {
4874
5150
  var $current = $(this.keydown.current);
4875
5151
  var $parent = $(this.keydown.parent);
4876
- var td = $current.closest('td');
5152
+ var td = $current.closest('td', this.$editor[0]);
4877
5153
 
4878
- if (td.length !== 0 && $current.closest('li') && $parent.children('li').length === 1)
5154
+ if (td.length !== 0 && $current.closest('li', this.$editor[0]) && $parent.children('li').length === 1)
4879
5155
  {
4880
5156
  if (!this.utils.isEmpty($current.text())) return;
4881
5157
 
@@ -4902,7 +5178,6 @@
4902
5178
  this.keyup.parent = this.selection.getParent();
4903
5179
  var $parent = this.utils.isRedactorParent($(this.keyup.parent).parent());
4904
5180
 
4905
-
4906
5181
  // callback
4907
5182
  var keyupStop = this.core.setCallback('keyup', e);
4908
5183
  if (keyupStop === false)
@@ -4931,13 +5206,8 @@
4931
5206
  }
4932
5207
 
4933
5208
  // linkify
4934
- if (this.keyup.isLinkify(key))
4935
- {
4936
- this.formatLinkify(this.opts.linkProtocol, this.opts.convertLinks, this.opts.convertUrlLinks, this.opts.convertImageLinks, this.opts.convertVideoLinks, this.opts.linkSize);
4937
-
4938
- this.observe.load();
4939
- this.code.sync();
4940
- }
5209
+ if (this.linkify.isEnabled() && this.linkify.isKey(key))
5210
+ this.linkify.format();
4941
5211
 
4942
5212
  if (key === this.keyCode.DELETE || key === this.keyCode.BACKSPACE)
4943
5213
  {
@@ -4958,7 +5228,10 @@
4958
5228
  }
4959
5229
 
4960
5230
  // remove empty paragraphs
4961
- this.$editor.find('p').each($.proxy(this.utils.removeEmpty, this));
5231
+ this.$editor.find('p').each($.proxy(function(i, s)
5232
+ {
5233
+ this.utils.removeEmpty(i, $(s).html());
5234
+ }, this));
4962
5235
 
4963
5236
  // remove invisible space
4964
5237
  if (this.opts.linebreaks && this.keyup.current && this.keyup.current.tagName == 'DIV' && this.utils.isEmpty(this.keyup.current.innerHTML))
@@ -4972,10 +5245,6 @@
4972
5245
  return this.keyup.formatEmpty(e);
4973
5246
  }
4974
5247
  },
4975
- isLinkify: function(key)
4976
- {
4977
- return this.opts.convertLinks && (this.opts.convertUrlLinks || this.opts.convertImageLinks || this.opts.convertVideoLinks) && key === this.keyCode.ENTER && !this.utils.isCurrentOrParent('PRE');
4978
- },
4979
5248
  replaceToParagraph: function(clone)
4980
5249
  {
4981
5250
  var $current = $(this.keyup.current);
@@ -5156,23 +5425,26 @@
5156
5425
  cleanUrl: function()
5157
5426
  {
5158
5427
  var thref = self.location.href.replace(/\/$/i, '');
5159
- this.link.url = this.link.url.replace(thref, '');
5160
- this.link.url = this.link.url.replace(/^\/#/, '#');
5161
- this.link.url = this.link.url.replace('mailto:', '');
5162
5428
 
5163
- // remove host from href
5164
- if (!this.opts.linkProtocol)
5429
+ if (typeof this.link.url !== "undefined")
5165
5430
  {
5166
- var re = new RegExp('^(http|ftp|https)://' + self.location.host, 'i');
5167
- this.link.url = this.link.url.replace(re, '');
5168
- }
5431
+ this.link.url = this.link.url.replace(thref, '');
5432
+ this.link.url = this.link.url.replace(/^\/#/, '#');
5433
+ this.link.url = this.link.url.replace('mailto:', '');
5169
5434
 
5435
+ // remove host from href
5436
+ if (!this.opts.linkProtocol)
5437
+ {
5438
+ var re = new RegExp('^(http|ftp|https)://' + self.location.host, 'i');
5439
+ this.link.url = this.link.url.replace(re, '');
5440
+ }
5441
+ }
5170
5442
  },
5171
5443
  getData: function()
5172
5444
  {
5173
5445
  this.link.$node = false;
5174
5446
 
5175
- var $el = $(this.selection.getCurrent()).closest('a');
5447
+ var $el = $(this.selection.getCurrent()).closest('a', this.$editor[0]);
5176
5448
  if ($el.length !== 0 && $el[0].tagName === 'A')
5177
5449
  {
5178
5450
  this.link.$node = $el;
@@ -5191,6 +5463,8 @@
5191
5463
  },
5192
5464
  insert: function()
5193
5465
  {
5466
+ this.placeholder.remove();
5467
+
5194
5468
  var target = '';
5195
5469
  var link = this.link.$inputUrl.val();
5196
5470
  var text = this.link.$inputText.val();
@@ -5247,16 +5521,37 @@
5247
5521
  {
5248
5522
  this.buffer.set();
5249
5523
 
5250
- this.link.$node.text(text).attr('href', link);
5524
+ var $link = this.link.$node,
5525
+ $el = $link.children();
5526
+
5527
+ if ($el.length > 0)
5528
+ {
5529
+ while ($el.length)
5530
+ {
5531
+ $el = $el.children();
5532
+ }
5533
+
5534
+ $el = $el.end();
5535
+ }
5536
+ else
5537
+ {
5538
+ $el = $link;
5539
+ }
5540
+
5541
+ $link.attr('href', link);
5542
+ $el.text(text);
5543
+
5251
5544
  if (target !== '')
5252
5545
  {
5253
- this.link.$node.attr('target', target);
5546
+ $link.attr('target', target);
5254
5547
  }
5255
5548
  else
5256
5549
  {
5257
- this.link.$node.removeAttr('target');
5550
+ $link.removeAttr('target');
5258
5551
  }
5259
5552
 
5553
+ this.selection.selectElement($link);
5554
+
5260
5555
  this.code.sync();
5261
5556
  }
5262
5557
  else
@@ -5278,20 +5573,36 @@
5278
5573
  if (target !== '') $a.attr('target', target);
5279
5574
 
5280
5575
  $a = $(this.insert.node($a));
5576
+
5577
+ if (this.selection.getText().match(/\s$/))
5578
+ {
5579
+ $a.after(" ");
5580
+ }
5581
+
5281
5582
  this.selection.selectElement($a);
5282
5583
  }
5283
5584
  else
5284
5585
  {
5285
5586
  document.execCommand('createLink', false, link);
5286
5587
 
5287
- $a = $(this.selection.getCurrent()).closest('a');
5588
+ $a = $(this.selection.getCurrent()).closest('a', this.$editor[0]);
5589
+ if (this.utils.browser('mozilla'))
5590
+ {
5591
+ $a = $('a[_moz_dirty=""]');
5592
+ }
5288
5593
 
5289
5594
  if (target !== '') $a.attr('target', target);
5290
- $a.removeAttr('style');
5595
+ $a.removeAttr('style').removeAttr('_moz_dirty');
5596
+
5597
+ if (this.selection.getText().match(/\s$/))
5598
+ {
5599
+ $a.after(" ");
5600
+ }
5291
5601
 
5292
- if (this.link.text === '' || this.link.text != text)
5602
+ if (this.link.text !== '' || this.link.text != text)
5293
5603
  {
5294
5604
  $a.text(text);
5605
+
5295
5606
  this.selection.selectElement($a);
5296
5607
  }
5297
5608
  }
@@ -5311,7 +5622,10 @@
5311
5622
  },
5312
5623
  unlink: function(e)
5313
5624
  {
5314
- if (typeof e != 'undefined' && e.preventDefault) e.preventDefault();
5625
+ if (typeof e != 'undefined' && e.preventDefault)
5626
+ {
5627
+ e.preventDefault();
5628
+ }
5315
5629
 
5316
5630
  var nodes = this.selection.getNodes();
5317
5631
  if (!nodes) return;
@@ -5321,11 +5635,8 @@
5321
5635
  var len = nodes.length;
5322
5636
  for (var i = 0; i < len; i++)
5323
5637
  {
5324
- if (nodes[i].tagName == 'A')
5325
- {
5326
- var $node = $(nodes[i]);
5327
- $node.replaceWith($node.contents());
5328
- }
5638
+ var $node = $(nodes[i]).closest('a', this.$editor[0]);
5639
+ $node.replaceWith($node.contents());
5329
5640
  }
5330
5641
 
5331
5642
  // hide link's tooltip
@@ -5333,6 +5644,28 @@
5333
5644
 
5334
5645
  this.code.sync();
5335
5646
 
5647
+ },
5648
+ toggleClass: function(className)
5649
+ {
5650
+ this.link.setClass(className, 'toggleClass');
5651
+ },
5652
+ addClass: function(className)
5653
+ {
5654
+ this.link.setClass(className, 'addClass');
5655
+ },
5656
+ removeClass: function(className)
5657
+ {
5658
+ this.link.setClass(className, 'removeClass');
5659
+ },
5660
+ setClass: function(className, func)
5661
+ {
5662
+ var links = this.selection.getInlinesTags(['a']);
5663
+ if (links === false) return;
5664
+
5665
+ $.each(links, function()
5666
+ {
5667
+ $(this)[func](className);
5668
+ });
5336
5669
  }
5337
5670
  };
5338
5671
  },
@@ -5348,7 +5681,7 @@
5348
5681
  this.selection.save();
5349
5682
 
5350
5683
  var parent = this.selection.getParent();
5351
- var $list = $(parent).closest('ol, ul');
5684
+ var $list = $(parent).closest('ol, ul', this.$editor[0]);
5352
5685
 
5353
5686
  if (!this.utils.isRedactorParent($list) && $list.length !== 0)
5354
5687
  {
@@ -5386,7 +5719,6 @@
5386
5719
  }
5387
5720
  }
5388
5721
 
5389
-
5390
5722
  this.selection.restore();
5391
5723
  this.code.sync();
5392
5724
  },
@@ -5394,7 +5726,7 @@
5394
5726
  {
5395
5727
  var parent = this.selection.getParent();
5396
5728
  var current = this.selection.getCurrent();
5397
- var $td = $(current).closest('td, th');
5729
+ var $td = $(current).closest('td, th', this.$editor[0]);
5398
5730
 
5399
5731
  if (this.utils.browser('msie') && this.opts.linebreaks)
5400
5732
  {
@@ -5405,7 +5737,7 @@
5405
5737
  document.execCommand('insert' + cmd);
5406
5738
  }
5407
5739
 
5408
- var $list = $(this.selection.getParent()).closest('ol, ul');
5740
+ var $list = $(this.selection.getParent()).closest('ol, ul', this.$editor[0]);
5409
5741
 
5410
5742
  if ($td.length !== 0)
5411
5743
  {
@@ -5492,13 +5824,13 @@
5492
5824
 
5493
5825
  this.indent.fixEmptyIndent();
5494
5826
 
5495
- if (!this.opts.linebreaks && $current.closest('li, th, td').length === 0)
5827
+ if (!this.opts.linebreaks && $current.closest('li, th, td', this.$editor[0]).length === 0)
5496
5828
  {
5497
5829
  document.execCommand('formatblock', false, 'p');
5498
5830
  this.$editor.find('ul, ol, blockquote').each($.proxy(this.utils.removeEmpty, this));
5499
5831
  }
5500
5832
 
5501
- var $table = $(this.selection.getCurrent()).closest('table');
5833
+ var $table = $(this.selection.getCurrent()).closest('table', this.$editor[0]);
5502
5834
  var $prev = $table.prev();
5503
5835
  if (!this.opts.linebreaks && $table.length !== 0 && $prev.length !== 0 && $prev[0].tagName == 'BR')
5504
5836
  {
@@ -5859,7 +6191,14 @@
5859
6191
  var current = this.selection.getCurrent();
5860
6192
  var parent = this.selection.getParent();
5861
6193
 
5862
- this.button.setInactiveAll(btnName);
6194
+ if (e !== false)
6195
+ {
6196
+ this.button.setInactiveAll();
6197
+ }
6198
+ else
6199
+ {
6200
+ this.button.setInactiveAll(btnName);
6201
+ }
5863
6202
 
5864
6203
  if (e === false && btnName !== 'html')
5865
6204
  {
@@ -5872,19 +6211,19 @@
5872
6211
 
5873
6212
  $.each(this.opts.activeButtonsStates, $.proxy(function(key, value)
5874
6213
  {
5875
- var parentEl = $(parent).closest(key);
5876
- var currentEl = $(current).closest(key);
6214
+ var parentEl = $(parent).closest(key, this.$editor[0]);
6215
+ var currentEl = $(current).closest(key, this.$editor[0]);
5877
6216
 
5878
6217
  if (parentEl.length !== 0 && !this.utils.isRedactorParent(parentEl)) return;
5879
6218
  if (!this.utils.isRedactorParent(currentEl)) return;
5880
- if (parentEl.length !== 0 || currentEl.closest(key).length !== 0)
6219
+ if (parentEl.length !== 0 || currentEl.closest(key, this.$editor[0]).length !== 0)
5881
6220
  {
5882
6221
  this.button.setActive(value);
5883
6222
  }
5884
6223
 
5885
6224
  }, this));
5886
6225
 
5887
- var $parent = $(parent).closest(this.opts.alignmentTags.toString().toLowerCase());
6226
+ var $parent = $(parent).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]);
5888
6227
  if (this.utils.isRedactorParent(parent) && $parent.length)
5889
6228
  {
5890
6229
  var align = ($parent.css('text-align') === '') ? 'left' : $parent.css('text-align');
@@ -5903,7 +6242,7 @@
5903
6242
  var $img = $(img);
5904
6243
 
5905
6244
  // IE fix (when we clicked on an image and then press backspace IE does goes to image's url)
5906
- $img.closest('a').on('click', function(e) { e.preventDefault(); });
6245
+ $img.closest('a', this.$editor[0]).on('click', function(e) { e.preventDefault(); });
5907
6246
 
5908
6247
  if (this.utils.browser('msie')) $img.attr('unselectable', 'on');
5909
6248
 
@@ -5911,7 +6250,7 @@
5911
6250
 
5912
6251
  }, this));
5913
6252
 
5914
- $(document).on('click.redactor-image-delete', $.proxy(function(e)
6253
+ $(document).on('click.redactor-image-delete.' + this.uuid, $.proxy(function(e)
5915
6254
  {
5916
6255
  this.observe.image = false;
5917
6256
  if (e.target.tagName == 'IMG' && this.utils.isRedactorParent(e.target))
@@ -5926,9 +6265,9 @@
5926
6265
  {
5927
6266
  if (!this.opts.linkTooltip) return;
5928
6267
 
5929
- this.$editor.find('a').on('touchstart click', $.proxy(this.observe.showTooltip, this));
5930
- this.$editor.on('touchstart click.redactor', $.proxy(this.observe.closeTooltip, this));
5931
- $(document).on('touchstart click.redactor', $.proxy(this.observe.closeTooltip, this));
6268
+ this.$editor.find('a').on('touchstart.redactor.' + this.uuid + ' click.redactor.' + this.uuid, $.proxy(this.observe.showTooltip, this));
6269
+ this.$editor.on('touchstart.redactor.' + this.uuid + ' click.redactor.' + this.uuid, $.proxy(this.observe.closeTooltip, this));
6270
+ $(document).on('touchstart.redactor.' + this.uuid + ' click.redactor.' + this.uuid, $.proxy(this.observe.closeTooltip, this));
5932
6271
  },
5933
6272
  getTooltipPosition: function($link)
5934
6273
  {
@@ -5936,20 +6275,18 @@
5936
6275
  },
5937
6276
  showTooltip: function(e)
5938
6277
  {
5939
- var $link = $(e.target);
5940
- var $parent = $link.closest('a');
5941
- var tag = ($link.length !== 0) ? $link[0].tagName : false;
6278
+ var $el = $(e.target);
5942
6279
 
5943
- if ($parent[0].tagName === 'A')
5944
- {
5945
- if (tag === 'IMG') return;
5946
- else if (tag !== 'A') $link = $parent;
5947
- }
6280
+ if ($el[0].tagName == 'IMG')
6281
+ return;
5948
6282
 
5949
- if (tag !== 'A')
5950
- {
6283
+ if ($el[0].tagName !== 'A')
6284
+ $el = $el.closest('a', this.$editor[0]);
6285
+
6286
+ if ($el[0].tagName !== 'A')
5951
6287
  return;
5952
- }
6288
+
6289
+ var $link = $el;
5953
6290
 
5954
6291
  var pos = this.observe.getTooltipPosition($link);
5955
6292
  var tooltip = $('<span class="redactor-link-tooltip"></span>');
@@ -5980,7 +6317,7 @@
5980
6317
  e = e.originalEvent || e;
5981
6318
 
5982
6319
  var target = e.target;
5983
- var $parent = $(target).closest('a');
6320
+ var $parent = $(target).closest('a', this.$editor[0]);
5984
6321
  if ($parent.length !== 0 && $parent[0].tagName === 'A' && target.tagName !== 'A')
5985
6322
  {
5986
6323
  return;
@@ -6003,10 +6340,6 @@
6003
6340
  if (this.opts.linebreaks) return html;
6004
6341
  if (html === '' || html === '<p></p>') return this.opts.emptyHtml;
6005
6342
 
6006
- this.paragraphize.blocks = ['table', 'div', 'pre', 'form', 'ul', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dl', 'blockquote', 'figcaption',
6007
- 'address', 'section', 'header', 'footer', 'aside', 'article', 'object', 'style', 'script', 'iframe', 'select', 'input', 'textarea',
6008
- 'button', 'option', 'map', 'area', 'math', 'hr', 'fieldset', 'legend', 'hgroup', 'nav', 'figure', 'details', 'menu', 'summary', 'p'];
6009
-
6010
6343
  html = html + "\n";
6011
6344
 
6012
6345
  this.paragraphize.safes = [];
@@ -6021,7 +6354,7 @@
6021
6354
  html = this.paragraphize.clear(html);
6022
6355
  html = this.paragraphize.restoreSafes(html);
6023
6356
 
6024
- html = html.replace(new RegExp('<br\\s?/?>\n?<(' + this.paragraphize.blocks.join('|') + ')(.*?[^>])>', 'gi'), '<p><br /></p>\n<$1$2>');
6357
+ html = html.replace(new RegExp('<br\\s?/?>\n?<(' + this.opts.paragraphizeBlocks.join('|') + ')(.*?[^>])>', 'gi'), '<p><br /></p>\n<$1$2>');
6025
6358
 
6026
6359
  return $.trim(html);
6027
6360
  },
@@ -6037,7 +6370,7 @@
6037
6370
 
6038
6371
  html = $div.html();
6039
6372
 
6040
- $div.find(this.paragraphize.blocks.join(', ')).each($.proxy(function(i,s)
6373
+ $div.find(this.opts.paragraphizeBlocks.join(', ')).each($.proxy(function(i,s)
6041
6374
  {
6042
6375
  this.paragraphize.z++;
6043
6376
  this.paragraphize.safes[this.paragraphize.z] = s.outerHTML;
@@ -6066,7 +6399,9 @@
6066
6399
  {
6067
6400
  $.each(this.paragraphize.safes, function(i,s)
6068
6401
  {
6402
+ s = (typeof s !== 'undefined') ? s.replace(/\$/g, '&#36;') : s;
6069
6403
  html = html.replace('{replace' + i + '}', s);
6404
+
6070
6405
  });
6071
6406
 
6072
6407
  return html;
@@ -6134,7 +6469,11 @@
6134
6469
  return {
6135
6470
  init: function(e)
6136
6471
  {
6137
- if (!this.opts.cleanOnPaste) return;
6472
+ if (!this.opts.cleanOnPaste)
6473
+ {
6474
+ setTimeout($.proxy(this.code.sync, this), 1);
6475
+ return;
6476
+ }
6138
6477
 
6139
6478
  this.rtePaste = true;
6140
6479
 
@@ -6163,14 +6502,24 @@
6163
6502
 
6164
6503
  $(window).off('scroll.redactor-freeze');
6165
6504
 
6166
- }, this), 1);
6505
+ if (this.linkify.isEnabled())
6506
+ this.linkify.format();
6167
6507
 
6508
+ }, this), 1);
6168
6509
  },
6169
6510
  createPasteBox: function()
6170
6511
  {
6171
6512
  this.$pasteBox = $('<div>').html('').attr('contenteditable', 'true').css({ position: 'fixed', width: 0, top: 0, left: '-9999px' });
6172
6513
 
6173
- this.$box.parent().append(this.$pasteBox);
6514
+ if (this.utils.browser('msie'))
6515
+ {
6516
+ this.$box.append(this.$pasteBox);
6517
+ }
6518
+ else
6519
+ {
6520
+ $('body').append(this.$pasteBox);
6521
+ }
6522
+
6174
6523
  this.$pasteBox.focus();
6175
6524
  },
6176
6525
  insert: function(html)
@@ -6327,7 +6676,7 @@
6327
6676
 
6328
6677
  return false;
6329
6678
  },
6330
- getInlines: function(nodes)
6679
+ getInlines: function(nodes, tags)
6331
6680
  {
6332
6681
  this.selection.get();
6333
6682
 
@@ -6337,9 +6686,18 @@
6337
6686
  }
6338
6687
 
6339
6688
  var inlines = [];
6340
- nodes = (typeof nodes == 'undefined') ? this.selection.getNodes() : nodes;
6689
+ nodes = (typeof nodes == 'undefined' || nodes === false) ? this.selection.getNodes() : nodes;
6341
6690
  var inlineTags = this.opts.inlineTags;
6342
6691
  inlineTags.push('span');
6692
+
6693
+ if (typeof tags !== 'undefined')
6694
+ {
6695
+ for (var i = 0; i < tags.length; i++)
6696
+ {
6697
+ inlineTags.push(tags[i]);
6698
+ }
6699
+ }
6700
+
6343
6701
  $.each(nodes, $.proxy(function(i,node)
6344
6702
  {
6345
6703
  if ($.inArray(node.tagName.toLowerCase(), inlineTags) != -1)
@@ -6351,6 +6709,28 @@
6351
6709
 
6352
6710
  return (inlines.length === 0) ? false : inlines;
6353
6711
  },
6712
+ getInlinesTags: function(tags)
6713
+ {
6714
+ this.selection.get();
6715
+
6716
+ if (this.range && this.range.collapsed)
6717
+ {
6718
+ return false;
6719
+ }
6720
+
6721
+ var inlines = [];
6722
+ var nodes = this.selection.getNodes();
6723
+ $.each(nodes, $.proxy(function(i,node)
6724
+ {
6725
+ if ($.inArray(node.tagName.toLowerCase(), tags) != -1)
6726
+ {
6727
+ inlines.push(node);
6728
+ }
6729
+
6730
+ }, this));
6731
+
6732
+ return (inlines.length === 0) ? false : inlines;
6733
+ },
6354
6734
  getBlocks: function(nodes)
6355
6735
  {
6356
6736
  this.selection.get();
@@ -6567,7 +6947,12 @@
6567
6947
  },
6568
6948
  removeMarkers: function()
6569
6949
  {
6570
- this.$editor.find('span.redactor-selection-marker').remove();
6950
+ this.$editor.find('span.redactor-selection-marker').each(function(i,s)
6951
+ {
6952
+ var text = $(s).text().replace(/[\u200B-\u200D\uFEFF]/g, '');
6953
+ if (text === '') $(s).remove();
6954
+ else $(s).replaceWith(function() { return $(this).contents(); });
6955
+ });
6571
6956
  },
6572
6957
  getText: function()
6573
6958
  {
@@ -6593,6 +6978,34 @@
6593
6978
  }
6594
6979
 
6595
6980
  return this.clean.onSync(html);
6981
+ },
6982
+ replaceWithHtml: function(html)
6983
+ {
6984
+ html = this.selection.getMarkerAsHtml(1) + html + this.selection.getMarkerAsHtml(2);
6985
+
6986
+ this.selection.get();
6987
+
6988
+ if (window.getSelection && window.getSelection().getRangeAt)
6989
+ {
6990
+ this.range.deleteContents();
6991
+ var div = document.createElement("div");
6992
+ div.innerHTML = html;
6993
+
6994
+ var frag = document.createDocumentFragment(), child;
6995
+ while ((child = div.firstChild))
6996
+ {
6997
+ frag.appendChild(child);
6998
+ }
6999
+
7000
+ this.range.insertNode(frag);
7001
+ }
7002
+ else if (document.selection && document.selection.createRange)
7003
+ {
7004
+ this.range.pasteHTML(html);
7005
+ }
7006
+
7007
+ this.selection.restore();
7008
+ this.code.sync();
6596
7009
  }
6597
7010
  };
6598
7011
  },
@@ -6710,7 +7123,7 @@
6710
7123
  // clean setup
6711
7124
  var ownLine = ['area', 'body', 'head', 'hr', 'i?frame', 'link', 'meta', 'noscript', 'style', 'script', 'table', 'tbody', 'thead', 'tfoot'];
6712
7125
  var contOwnLine = ['li', 'dt', 'dt', 'h[1-6]', 'option', 'script'];
6713
- var newLevel = ['blockquote', 'div', 'dl', 'fieldset', 'form', 'frameset', 'map', 'ol', 'p', 'pre', 'select', 'td', 'th', 'tr', 'ul'];
7126
+ var newLevel = ['p', 'blockquote', 'div', 'dl', 'fieldset', 'form', 'frameset', 'map', 'ol', 'pre', 'select', 'td', 'th', 'tr', 'ul'];
6714
7127
 
6715
7128
  this.tabifier.lineBefore = new RegExp('^<(/?' + ownLine.join('|/?' ) + '|' + contOwnLine.join('|') + ')[ >]');
6716
7129
  this.tabifier.lineAfter = new RegExp('^<(br|/?' + ownLine.join('|/?' ) + '|/' + contOwnLine.join('|/') + ')[ >]');
@@ -6875,6 +7288,7 @@
6875
7288
  placeTag: function (tag, out)
6876
7289
  {
6877
7290
  var nl = tag.match(this.tabifier.newLevel);
7291
+
6878
7292
  if (tag.match(this.tabifier.lineBefore) || nl)
6879
7293
  {
6880
7294
  out = out.replace(/\s*$/, '');
@@ -6890,7 +7304,7 @@
6890
7304
  if (tag.match(this.tabifier.lineAfter) || tag.match(this.tabifier.newLevel))
6891
7305
  {
6892
7306
  out = out.replace(/ *$/, '');
6893
- out += '\n';
7307
+ //out += '\n';
6894
7308
  }
6895
7309
 
6896
7310
  return out;
@@ -6991,24 +7405,13 @@
6991
7405
  replacement.push(this.tidy.settings.replaceTags[i][0]);
6992
7406
  }
6993
7407
 
6994
- this.tidy.$div.find(replacement.join(',')).each($.proxy(function(n,s)
7408
+ $.each(replacement, $.proxy(function(key, value)
6995
7409
  {
6996
- var tag = rTags[n];
6997
- $(s).replaceWith(function()
7410
+ this.tidy.$div.find(value).replaceWith(function()
6998
7411
  {
6999
- var replaced = $('<' + tag + ' />').append($(this).contents());
7000
-
7001
- for (var i = 0; i < this.attributes.length; i++)
7002
- {
7003
- replaced.attr(this.attributes[i].name, this.attributes[i].value);
7004
- }
7005
-
7006
- return replaced;
7412
+ return $("<" + rTags[key] + " />", {html: $(this).html()});
7007
7413
  });
7008
-
7009
7414
  }, this));
7010
-
7011
- return html;
7012
7415
  },
7013
7416
  replaceStyles: function()
7014
7417
  {
@@ -7437,7 +7840,7 @@
7437
7840
  if (!this.opts.toolbarFixed) return;
7438
7841
 
7439
7842
  this.toolbar.observeScroll();
7440
- $(this.opts.toolbarFixedTarget).on('scroll.redactor', $.proxy(this.toolbar.observeScroll, this));
7843
+ $(this.opts.toolbarFixedTarget).on('scroll.redactor.' + this.uuid, $.proxy(this.toolbar.observeScroll, this));
7441
7844
 
7442
7845
  },
7443
7846
  setOverflow: function()
@@ -7502,7 +7905,7 @@
7502
7905
  {
7503
7906
  var top = this.opts.toolbarFixedTopOffset + scrollTop - boxTop;
7504
7907
  var left = 0;
7505
- var end = boxTop + this.$box.height() + 30;
7908
+ var end = boxTop + this.$box.height() - 32;
7506
7909
  var width = this.$box.innerWidth();
7507
7910
 
7508
7911
  this.$toolbar.addClass('toolbar-fixed-box');
@@ -7513,6 +7916,9 @@
7513
7916
  left: left
7514
7917
  });
7515
7918
 
7919
+ if (scrollTop > end)
7920
+ $('.redactor-dropdown-' + this.uuid + ':visible').hide();
7921
+
7516
7922
  this.toolbar.setDropdownsFixed();
7517
7923
  this.$toolbar.css('visibility', (scrollTop < end) ? 'visible' : 'hidden');
7518
7924
  },
@@ -7528,7 +7934,6 @@
7528
7934
 
7529
7935
  this.toolbar.unsetDropdownsFixed();
7530
7936
  this.$toolbar.removeClass('toolbar-fixed-box');
7531
-
7532
7937
  },
7533
7938
  setDropdownsFixed: function()
7534
7939
  {
@@ -7540,7 +7945,7 @@
7540
7945
  position = 'absolute';
7541
7946
  }
7542
7947
 
7543
- $('.redactor-dropdown').each(function()
7948
+ $('.redactor-dropdown-' + this.uuid).each(function()
7544
7949
  {
7545
7950
  $(this).css({ position: position, top: top + 'px' });
7546
7951
  });
@@ -7548,7 +7953,7 @@
7548
7953
  unsetDropdownsFixed: function()
7549
7954
  {
7550
7955
  var top = (this.$toolbar.innerHeight() + this.$toolbar.offset().top);
7551
- $('.redactor-dropdown').each(function()
7956
+ $('.redactor-dropdown-' + this.uuid).each(function()
7552
7957
  {
7553
7958
  $(this).css({ position: 'absolute', top: top + 'px' });
7554
7959
  });
@@ -7566,7 +7971,7 @@
7566
7971
  this.upload.$el = $(id);
7567
7972
  this.upload.$droparea = $('<div id="redactor-droparea" />');
7568
7973
 
7569
- this.upload.$placeholdler = $('<div id="redactor-droparea-placeholder" />').text('Drop file here or ');
7974
+ this.upload.$placeholdler = $('<div id="redactor-droparea-placeholder" />').text(this.lang.get('upload_label'));
7570
7975
  this.upload.$input = $('<input type="file" name="file" />');
7571
7976
 
7572
7977
  this.upload.$placeholdler.append(this.upload.$input);
@@ -7627,6 +8032,7 @@
7627
8032
  }
7628
8033
 
7629
8034
  this.progress.show();
8035
+ this.core.setCallback('uploadStart', e, formData);
7630
8036
  this.upload.sendData(formData, e);
7631
8037
  },
7632
8038
  setConfig: function(file)
@@ -7677,6 +8083,7 @@
7677
8083
 
7678
8084
  var xhr = new XMLHttpRequest();
7679
8085
  xhr.open('POST', this.upload.url);
8086
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
7680
8087
 
7681
8088
  // complete
7682
8089
  xhr.onreadystatechange = $.proxy(function()
@@ -7914,6 +8321,7 @@
7914
8321
  html = html.replace(/\s/g, '');
7915
8322
  html = html.replace(/^<p>[^\W\w\D\d]*?<\/p>$/i, '');
7916
8323
  html = html.replace(/<iframe(.*?[^>])>$/i, 'iframe');
8324
+ html = html.replace(/<source(.*?[^>])>$/i, 'source');
7917
8325
 
7918
8326
  // remove empty tags
7919
8327
  if (removeEmptyTags !== false)
@@ -7978,12 +8386,13 @@
7978
8386
  },
7979
8387
  removeEmpty: function(i, s)
7980
8388
  {
7981
- var $s = $(s);
8389
+ var $s = $($.parseHTML(s));
7982
8390
 
7983
8391
  $s.find('.redactor-invisible-space').removeAttr('style').removeAttr('class');
7984
8392
 
7985
- if ($s.find('hr, br, img, iframe').length !== 0) return;
8393
+ if ($s.find('hr, br, img, iframe, source').length !== 0) return;
7986
8394
  var text = $.trim($s.text());
8395
+
7987
8396
  if (this.utils.isEmpty(text, false))
7988
8397
  {
7989
8398
  $s.remove();
@@ -7993,8 +8402,6 @@
7993
8402
  // save and restore scroll
7994
8403
  saveScroll: function()
7995
8404
  {
7996
- if (this.utils.isSelectAll()) return;
7997
-
7998
8405
  this.saveEditorScroll = this.$editor.scrollTop();
7999
8406
  this.saveBodyScroll = $(window).scrollTop();
8000
8407
 
@@ -8039,6 +8446,8 @@
8039
8446
 
8040
8447
  return $(this).contents();
8041
8448
  });
8449
+
8450
+ return $(node);
8042
8451
  },
8043
8452
  replaceToTag: function(node, tag, removeInlineTags)
8044
8453
  {
@@ -8071,13 +8480,16 @@
8071
8480
 
8072
8481
  return (offset === 0) ? true : false;
8073
8482
  },
8074
- isEndOfElement: function()
8483
+ isEndOfElement: function(element)
8075
8484
  {
8076
- var block = this.selection.getBlock();
8077
- if (!block) return false;
8485
+ if (typeof element == 'undefined')
8486
+ {
8487
+ var element = this.selection.getBlock();
8488
+ if (!element) return false;
8489
+ }
8078
8490
 
8079
- var offset = this.caret.getOffsetOfElement(block);
8080
- var text = $.trim($(block).text()).replace(/\n\r\n/g, '');
8491
+ var offset = this.caret.getOffsetOfElement(element);
8492
+ var text = $.trim($(element).text()).replace(/\n\r\n/g, '');
8081
8493
 
8082
8494
  return (offset == text.length) ? true : false;
8083
8495
  },
@@ -8086,7 +8498,7 @@
8086
8498
  var block = this.$editor[0];
8087
8499
 
8088
8500
  var offset = this.caret.getOffsetOfElement(block);
8089
- var text = $.trim($(block).text()).replace(/\n\r\n/g, '');
8501
+ var text = $.trim($(block).html().replace(/(<([^>]+)>)/gi,''));
8090
8502
 
8091
8503
  return (offset == text.length) ? true : false;
8092
8504
  },
@@ -8108,7 +8520,7 @@
8108
8520
  // tag detection
8109
8521
  isTag: function(current, tag)
8110
8522
  {
8111
- var element = $(current).closest(tag);
8523
+ var element = $(current).closest(tag, this.$editor[0]);
8112
8524
  if (element.length == 1)
8113
8525
  {
8114
8526
  return element[0];
@@ -8209,104 +8621,148 @@
8209
8621
 
8210
8622
  if (browser == 'safari') return (typeof match[3] != 'undefined') ? match[3] == 'safari' : false;
8211
8623
  if (browser == 'version') return match[2];
8212
- if (browser == 'webkit') return (match[1] == 'chrome' || match[1] == 'webkit');
8624
+ if (browser == 'webkit') return (match[1] == 'chrome' || match[1] == 'opr' || match[1] == 'webkit');
8213
8625
  if (match[1] == 'rv') return browser == 'msie';
8214
8626
  if (match[1] == 'opr') return browser == 'webkit';
8215
8627
 
8216
8628
  return browser == match[1];
8217
8629
  }
8218
8630
  };
8219
- }
8220
- };
8221
-
8222
- $(window).on('load.tools.redactor', function()
8223
- {
8224
- $('[data-tools="redactor"]').redactor();
8225
- });
8226
-
8227
- // constructor
8228
- Redactor.prototype.init.prototype = Redactor.prototype;
8631
+ },
8632
+ linkify: function()
8633
+ {
8634
+ return {
8635
+ isKey: function(key)
8636
+ {
8637
+ return key == this.keyCode.ENTER || key == this.keyCode.SPACE;
8638
+ },
8639
+ isEnabled: function()
8640
+ {
8641
+ return this.opts.convertLinks && (this.opts.convertUrlLinks || this.opts.convertImageLinks || this.opts.convertVideoLinks) && !this.utils.isCurrentOrParent('PRE');
8642
+ },
8643
+ format: function()
8644
+ {
8645
+ var linkify = this.linkify,
8646
+ opts = this.opts;
8229
8647
 
8230
- // LINKIFY
8231
- $.Redactor.fn.formatLinkify = function(protocol, convertLinks, convertUrlLinks, convertImageLinks, convertVideoLinks, linkSize)
8232
- {
8233
- var urlCheck = '((?:http[s]?:\\/\\/(?:www\\.)?|www\\.){1}(?:[0-9A-Za-z\\-%_]+\\.)+[a-zA-Z]{2,}(?::[0-9]+)?(?:(?:/[0-9A-Za-z\\-#\\.%\+_]*)+)?(?:\\?(?:[0-9A-Za-z\\-\\.%_]+(?:=[0-9A-Za-z\\-\\.%_\\+]*)?)?(?:&(?:[0-9A-Za-z\\-\\.%_]+(?:=[0-9A-Za-z\\-\\.%_\\+]*)?)?)*)?(?:#[0-9A-Za-z\\-\\.%_\\+=\\?&;]*)?)';
8234
- var regex = new RegExp(urlCheck, 'gi');
8235
- var rProtocol = /(https?|ftp):\/\//i;
8236
- var urlImage = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/gi;
8648
+ this.$editor
8649
+ .find(":not(iframe,img,a,pre)")
8650
+ .addBack()
8651
+ .contents()
8652
+ .filter(function()
8653
+ {
8654
+ return this.nodeType === 3 && $.trim(this.nodeValue) != "" && !$(this).parent().is("pre") && (this.nodeValue.match(opts.linkify.regexps.youtube) || this.nodeValue.match(opts.linkify.regexps.vimeo) || this.nodeValue.match(opts.linkify.regexps.image) || this.nodeValue.match(opts.linkify.regexps.url));
8655
+ })
8656
+ .each(function()
8657
+ {
8658
+ var text = $(this).text(),
8659
+ html = text;
8237
8660
 
8238
- var childNodes = (this.$editor ? this.$editor[0] : this).childNodes, i = childNodes.length;
8239
- while (i--)
8240
- {
8241
- var n = childNodes[i];
8661
+ if (opts.convertVideoLinks && (html.match(opts.linkify.regexps.youtube) || html.match(opts.linkify.regexps.vimeo)) )
8662
+ {
8663
+ html = linkify.convertVideoLinks(html);
8664
+ }
8665
+ else if (opts.convertImageLinks && html.match(opts.linkify.regexps.image))
8666
+ {
8667
+ html = linkify.convertImages(html);
8668
+ }
8669
+ else if (opts.convertUrlLinks)
8670
+ {
8671
+ html = linkify.convertLinks(html);
8672
+ }
8242
8673
 
8243
- if (n.nodeType === 3 && n.parentNode !== 'PRE')
8244
- {
8245
- var html = n.nodeValue;
8674
+ $(this).before(text.replace(text, html))
8675
+ .remove();
8676
+ });
8246
8677
 
8247
- // youtube & vimeo
8248
- if (convertVideoLinks && html)
8678
+ this.linkify.after();
8679
+ },
8680
+ convertVideoLinks: function(html)
8249
8681
  {
8250
8682
  var iframeStart = '<iframe width="500" height="281" src="',
8251
8683
  iframeEnd = '" frameborder="0" allowfullscreen></iframe>';
8252
8684
 
8253
- if (html.match(reUrlYoutube))
8685
+ if (html.match(this.opts.linkify.regexps.youtube))
8254
8686
  {
8255
- html = html.replace(reUrlYoutube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd);
8256
- $(n).after(html).remove();
8687
+ html = html.replace(this.opts.linkify.regexps.youtube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd);
8257
8688
  }
8258
- else if (html.match(reUrlVimeo))
8689
+
8690
+ if (html.match(this.opts.linkify.regexps.vimeo))
8259
8691
  {
8260
- html = html.replace(reUrlVimeo, iframeStart + '//player.vimeo.com/video/$2' + iframeEnd);
8261
- $(n).after(html).remove();
8692
+ html = html.replace(this.opts.linkify.regexps.vimeo, iframeStart + '//player.vimeo.com/video/$2' + iframeEnd);
8262
8693
  }
8263
- }
8264
8694
 
8265
- // image
8266
- if (convertImageLinks && html && html.match(urlImage))
8695
+ return html;
8696
+ },
8697
+ convertImages: function(html)
8267
8698
  {
8268
- html = html.replace(urlImage, '<img src="$1" />');
8699
+ var matches = html.match(this.opts.linkify.regexps.image);
8269
8700
 
8270
- $(n).after(html).remove();
8271
- return;
8272
- }
8273
-
8274
- // link
8275
- if (html.search(/\$/g) != -1) html = html.replace(/\$/g, '&#36;');
8701
+ if (matches)
8702
+ {
8703
+ html = html.replace(html, '<img src="' + matches + '" />');
8704
+ }
8276
8705
 
8277
- var matches = html.match(regex);
8278
- if (convertUrlLinks && html && matches)
8706
+ return html;
8707
+ },
8708
+ convertLinks: function(html)
8279
8709
  {
8710
+ var matches = html.match(this.opts.linkify.regexps.url);
8280
8711
 
8281
- var len = matches.length;
8282
- for (var z = 0; z < len; z++)
8712
+ if (matches)
8283
8713
  {
8284
- // remove dot in the end
8285
- if (matches[z].match(/\.$/) !== null) matches[z] = matches[z].replace(/\.$/, '');
8714
+ matches = $.grep(matches, function(v, k) { return $.inArray(v, matches) === k; });
8286
8715
 
8287
- var href = matches[z];
8288
- var text = href;
8716
+ var length = matches.length;
8289
8717
 
8290
- var space = '';
8291
- if (href.match(/\s$/) !== null) space = ' ';
8718
+ for (var i = 0; i < length; i++)
8719
+ {
8720
+ var href = matches[i],
8721
+ text = href,
8722
+ linkProtocol = this.opts.linkProtocol + '://';
8292
8723
 
8293
- var addProtocol = protocol + '://';
8294
- if (href.match(rProtocol) !== null) addProtocol = '';
8724
+ if (href.match(/(https?|ftp):\/\//i) !== null)
8725
+ {
8726
+ linkProtocol = "";
8727
+ }
8295
8728
 
8296
- if (text.length > linkSize) text = text.substring(0, linkSize) + '...';
8297
- text = text.replace(/&#36;/g, '$').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
8729
+ if (text.length > this.opts.linkSize)
8730
+ {
8731
+ text = text.substring(0, this.opts.linkSize) + '...';
8732
+ }
8298
8733
 
8299
- html = html.replace(href, '<a href=\"' + addProtocol + $.trim(href) + '\">' + $.trim(text) + '</a>' + space);
8734
+ text = decodeURIComponent(text);
8735
+
8736
+ var regexB = "\\b";
8737
+
8738
+ if ($.inArray(href.slice(-1), ["/", "&", "="]) != -1)
8739
+ {
8740
+ regexB = "";
8741
+ }
8742
+
8743
+ // escaping url
8744
+ var regexp = new RegExp('(' + href.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + regexB + ')', 'g');
8745
+
8746
+ html = html.replace(regexp, '<a href="' + linkProtocol + $.trim(href) + '">' + $.trim(text) + '</a>');
8747
+ }
8300
8748
  }
8301
8749
 
8302
- $(n).after(html).remove();
8750
+ return html;
8751
+ },
8752
+ after: function()
8753
+ {
8754
+ this.observe.load();
8755
+ this.code.sync();
8303
8756
  }
8304
8757
  }
8305
- else if (n.nodeType === 1 && !/^(pre|a|button|textarea)$/i.test(n.tagName))
8306
- {
8307
- $.Redactor.fn.formatLinkify.call(n, protocol, convertLinks, convertUrlLinks, convertImageLinks, convertVideoLinks, linkSize);
8308
- }
8309
8758
  }
8310
8759
  };
8311
8760
 
8761
+ $(window).on('load.tools.redactor', function()
8762
+ {
8763
+ $('[data-tools="redactor"]').redactor();
8764
+ });
8765
+
8766
+ // constructor
8767
+ Redactor.prototype.init.prototype = Redactor.prototype;
8312
8768
  })(jQuery);