biola_wcms_components 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);