tinymce-rails 4.4.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +71 -22
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/paste/plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/themes/inlite/scratch/compile/theme.js +204 -199
  9. data/vendor/assets/javascripts/tinymce/themes/inlite/scratch/inline/theme.js +204 -199
  10. data/vendor/assets/javascripts/tinymce/themes/inlite/scratch/inline/theme.raw.js +204 -199
  11. data/vendor/assets/javascripts/tinymce/themes/inlite/src/demo/html/demo.html +51 -24
  12. data/vendor/assets/javascripts/tinymce/themes/inlite/src/main/js/tinymce/inlite/Theme.js +24 -17
  13. data/vendor/assets/javascripts/tinymce/themes/inlite/src/main/js/tinymce/inlite/core/Measure.js +14 -23
  14. data/vendor/assets/javascripts/tinymce/themes/inlite/src/main/js/tinymce/inlite/core/UrlType.js +6 -1
  15. data/vendor/assets/javascripts/tinymce/themes/inlite/src/main/js/tinymce/inlite/ui/Buttons.js +3 -3
  16. data/vendor/assets/javascripts/tinymce/themes/inlite/src/main/js/tinymce/inlite/ui/Forms.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/themes/inlite/src/main/js/tinymce/inlite/ui/Panel.js +162 -160
  18. data/vendor/assets/javascripts/tinymce/themes/inlite/src/test/js/atomic/core/UrlTypeTest.js +8 -0
  19. data/vendor/assets/javascripts/tinymce/themes/inlite/src/test/js/browser/alien/BookmarkTest.js +4 -4
  20. data/vendor/assets/javascripts/tinymce/themes/inlite/src/test/js/browser/core/MeasureTest.js +4 -4
  21. data/vendor/assets/javascripts/tinymce/themes/inlite/src/test/js/browser/core/SelectionMatcherTest.js +3 -3
  22. data/vendor/assets/javascripts/tinymce/themes/inlite/src/test/js/browser/file/ConversionsTest.js +1 -1
  23. data/vendor/assets/javascripts/tinymce/themes/inlite/theme.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/themes/modern/theme.js +1 -1
  25. data/vendor/assets/javascripts/tinymce/tinymce.js +13 -13
  26. metadata +2 -2
@@ -592,8 +592,13 @@ define("p", [
592
592
  return /^www\.|\.(com|org|edu|gov|uk|net|ca|de|jp|fr|au|us|ru|ch|it|nl|se|no|es|mil)$/i.test(href.trim());
593
593
  };
594
594
 
595
+ var isAbsolute = function (href) {
596
+ return /^https?:\/\//.test(href.trim());
597
+ };
598
+
595
599
  return {
596
- isDomainLike: isDomainLike
600
+ isDomainLike: isDomainLike,
601
+ isAbsolute: isAbsolute
597
602
  };
598
603
  });
599
604
 
@@ -658,7 +663,7 @@ define("f", [
658
663
  };
659
664
 
660
665
  var convertLinkToAbsolute = function (editor, href) {
661
- return UrlType.isDomainLike(href) ? askAboutPrefix(editor, href) : Promise.resolve(href);
666
+ return !UrlType.isAbsolute(href) && UrlType.isDomainLike(href) ? askAboutPrefix(editor, href) : Promise.resolve(href);
662
667
  };
663
668
 
664
669
  var createQuickLinkForm = function (editor, hide) {
@@ -767,36 +772,27 @@ define("g", [
767
772
  };
768
773
  };
769
774
 
770
- var getElementRect = function (editor, elm) {
771
- var pos, targetRect, root;
772
-
773
- pos = DOM.getPos(editor.getContentAreaContainer());
774
- targetRect = editor.dom.getRect(elm);
775
- root = editor.dom.getRoot();
776
-
777
- // Adjust targetPos for scrolling in the editor
778
- if (root.nodeName == 'BODY') {
779
- targetRect.x -= root.ownerDocument.documentElement.scrollLeft || root.scrollLeft;
780
- targetRect.y -= root.ownerDocument.documentElement.scrollTop || root.scrollTop;
781
- }
782
-
783
- targetRect.x += pos.x;
784
- targetRect.y += pos.y;
775
+ var measureElement = function (elm) {
776
+ var clientRect = elm.getBoundingClientRect();
785
777
 
786
- // We need to use these instead of the rect values since the style
787
- // size properites might not be the same as the real size for a table
788
- targetRect.w = elm.clientWidth > 0 ? elm.clientWidth : elm.offsetWidth;
789
- targetRect.h = elm.clientHeight > 0 ? elm.clientHeight : elm.offsetHeight;
778
+ return toAbsolute({
779
+ x: clientRect.left,
780
+ y: clientRect.top,
781
+ w: Math.max(elm.clientWidth, elm.offsetWidth),
782
+ h: Math.max(elm.clientHeight, elm.offsetHeight)
783
+ });
784
+ };
790
785
 
791
- return targetRect;
786
+ var getElementRect = function (editor, elm) {
787
+ return measureElement(elm);
792
788
  };
793
789
 
794
790
  var getPageAreaRect = function (editor) {
795
- return DOM.getRect(editor.getElement().ownerDocument.body);
791
+ return measureElement(editor.getElement().ownerDocument.body);
796
792
  };
797
793
 
798
794
  var getContentAreaRect = function (editor) {
799
- return toAbsolute(DOM.getRect(editor.getContentAreaContainer() || editor.getBody()));
795
+ return measureElement(editor.getContentAreaContainer() || editor.getBody());
800
796
  };
801
797
 
802
798
  var getSelectionRect = function (editor) {
@@ -927,138 +923,103 @@ define("3", [
927
923
  "g",
928
924
  "h"
929
925
  ], function (Tools, Factory, DOM, Toolbar, Forms, Measure, Layout) {
930
- var DEFAULT_TEXT_SELECTION_ITEMS = 'bold italic | quicklink h2 h3 blockquote';
931
- var DEFAULT_INSERT_TOOLBAR_ITEMS = 'quickimage quicktable';
932
- var panel, currentRect;
926
+ return function () {
927
+ var DEFAULT_TEXT_SELECTION_ITEMS = 'bold italic | quicklink h2 h3 blockquote';
928
+ var DEFAULT_INSERT_TOOLBAR_ITEMS = 'quickimage quicktable';
929
+ var panel, currentRect;
930
+
931
+ var createToolbars = function (editor, toolbars) {
932
+ return Tools.map(toolbars, function (toolbar) {
933
+ return Toolbar.create(editor, toolbar.id, toolbar.items);
934
+ });
935
+ };
933
936
 
934
- var createToolbars = function (editor, toolbars) {
935
- return Tools.map(toolbars, function (toolbar) {
936
- return Toolbar.create(editor, toolbar.id, toolbar.items);
937
- });
938
- };
937
+ var getTextSelectionToolbarItems = function (settings) {
938
+ var value = settings.selection_toolbar;
939
+ return value ? value : DEFAULT_TEXT_SELECTION_ITEMS;
940
+ };
939
941
 
940
- var getTextSelectionToolbarItems = function (settings) {
941
- var value = settings.selection_toolbar;
942
- return value ? value : DEFAULT_TEXT_SELECTION_ITEMS;
943
- };
942
+ var getInsertToolbarItems = function (settings) {
943
+ var value = settings.insert_toolbar;
944
+ return value ? value : DEFAULT_INSERT_TOOLBAR_ITEMS;
945
+ };
944
946
 
945
- var getInsertToolbarItems = function (settings) {
946
- var value = settings.insert_toolbar;
947
- return value ? value : DEFAULT_INSERT_TOOLBAR_ITEMS;
948
- };
947
+ var create = function (editor, toolbars) {
948
+ var items, settings = editor.settings;
949
949
 
950
- var create = function (editor, toolbars) {
951
- var items, settings = editor.settings;
950
+ items = createToolbars(editor, toolbars);
951
+ items = items.concat([
952
+ Toolbar.create(editor, 'text', getTextSelectionToolbarItems(settings)),
953
+ Toolbar.create(editor, 'insert', getInsertToolbarItems(settings)),
954
+ Forms.createQuickLinkForm(editor, hide)
955
+ ]);
952
956
 
953
- items = createToolbars(editor, toolbars);
954
- items = items.concat([
955
- Toolbar.create(editor, 'text', getTextSelectionToolbarItems(settings)),
956
- Toolbar.create(editor, 'insert', getInsertToolbarItems(settings)),
957
- Forms.createQuickLinkForm(editor, hide)
958
- ]);
957
+ return Factory.create({
958
+ type: 'floatpanel',
959
+ role: 'dialog',
960
+ classes: 'tinymce tinymce-inline arrow',
961
+ ariaLabel: 'Inline toolbar',
962
+ layout: 'flex',
963
+ direction: 'column',
964
+ align: 'stretch',
965
+ autohide: false,
966
+ autofix: true,
967
+ fixed: true,
968
+ border: 1,
969
+ items: items,
970
+ oncancel: function() {
971
+ editor.focus();
972
+ }
973
+ });
974
+ };
959
975
 
960
- return Factory.create({
961
- type: 'floatpanel',
962
- role: 'dialog',
963
- classes: 'tinymce tinymce-inline arrow',
964
- ariaLabel: 'Inline toolbar',
965
- layout: 'flex',
966
- direction: 'column',
967
- align: 'stretch',
968
- autohide: false,
969
- autofix: true,
970
- fixed: true,
971
- border: 1,
972
- items: items,
973
- oncancel: function() {
974
- editor.focus();
976
+ var showPanel = function (panel) {
977
+ if (panel) {
978
+ panel.show();
975
979
  }
976
- });
977
- };
978
-
979
- var showPanel = function (panel) {
980
- if (panel) {
981
- panel.show();
982
- }
983
- };
984
-
985
- var movePanelTo = function (panel, pos) {
986
- panel.moveTo(pos.x, pos.y);
987
- };
980
+ };
988
981
 
989
- var togglePositionClass = function (panel, relPos) {
990
- relPos = relPos ? relPos.substr(0, 2) : '';
982
+ var movePanelTo = function (panel, pos) {
983
+ panel.moveTo(pos.x, pos.y);
984
+ };
991
985
 
992
- Tools.each({
993
- t: 'down',
994
- b: 'up',
995
- c: 'center'
996
- }, function(cls, pos) {
997
- panel.classes.toggle('arrow-' + cls, pos === relPos.substr(0, 1));
998
- });
986
+ var togglePositionClass = function (panel, relPos) {
987
+ relPos = relPos ? relPos.substr(0, 2) : '';
999
988
 
1000
- if (relPos === 'cr') {
1001
- panel.classes.toggle('arrow-left', true);
1002
- panel.classes.toggle('arrow-right', false);
1003
- } else if (relPos === 'cl') {
1004
- panel.classes.toggle('arrow-left', true);
1005
- panel.classes.toggle('arrow-right', true);
1006
- } else {
1007
989
  Tools.each({
1008
- l: 'left',
1009
- r: 'right'
990
+ t: 'down',
991
+ b: 'up',
992
+ c: 'center'
1010
993
  }, function(cls, pos) {
1011
- panel.classes.toggle('arrow-' + cls, pos === relPos.substr(1, 1));
994
+ panel.classes.toggle('arrow-' + cls, pos === relPos.substr(0, 1));
1012
995
  });
1013
- }
1014
- };
1015
-
1016
- var showToolbar = function (panel, id) {
1017
- var toolbars = panel.items().filter('#' + id);
1018
996
 
1019
- if (toolbars.length > 0) {
1020
- toolbars[0].show();
1021
- panel.reflow();
1022
- }
1023
- };
1024
-
1025
- var showPanelAt = function (panel, id, editor, targetRect) {
1026
- var contentAreaRect, panelRect, result, userConstainHandler;
1027
-
1028
- showPanel(panel);
1029
- panel.items().hide();
1030
- showToolbar(panel, id);
1031
-
1032
- userConstainHandler = editor.settings.inline_toolbar_position_handler;
1033
- contentAreaRect = Measure.getContentAreaRect(editor);
1034
- panelRect = DOM.getRect(panel.getEl());
1035
-
1036
- if (id === 'insert') {
1037
- result = Layout.calcInsert(targetRect, contentAreaRect, panelRect);
1038
- } else {
1039
- result = Layout.calc(targetRect, contentAreaRect, panelRect);
1040
- }
1041
-
1042
- if (result) {
1043
- panelRect = result.rect;
1044
- currentRect = targetRect;
1045
- movePanelTo(panel, Layout.userConstrain(userConstainHandler, targetRect, contentAreaRect, panelRect));
1046
-
1047
- togglePositionClass(panel, result.position);
1048
- } else {
1049
- hide(panel);
1050
- }
1051
- };
997
+ if (relPos === 'cr') {
998
+ panel.classes.toggle('arrow-left', true);
999
+ panel.classes.toggle('arrow-right', false);
1000
+ } else if (relPos === 'cl') {
1001
+ panel.classes.toggle('arrow-left', true);
1002
+ panel.classes.toggle('arrow-right', true);
1003
+ } else {
1004
+ Tools.each({
1005
+ l: 'left',
1006
+ r: 'right'
1007
+ }, function(cls, pos) {
1008
+ panel.classes.toggle('arrow-' + cls, pos === relPos.substr(1, 1));
1009
+ });
1010
+ }
1011
+ };
1052
1012
 
1053
- var hasFormVisible = function () {
1054
- return panel.items().filter('form:visible').length > 0;
1055
- };
1013
+ var showToolbar = function (panel, id) {
1014
+ var toolbars = panel.items().filter('#' + id);
1056
1015
 
1057
- var showForm = function (editor, id) {
1058
- if (panel) {
1059
- panel.items().hide();
1060
- showToolbar(panel, id);
1016
+ if (toolbars.length > 0) {
1017
+ toolbars[0].show();
1018
+ panel.reflow();
1019
+ }
1020
+ };
1061
1021
 
1022
+ var showPanelAt = function (panel, id, editor, targetRect) {
1062
1023
  var contentAreaRect, panelRect, result, userConstainHandler;
1063
1024
 
1064
1025
  showPanel(panel);
@@ -1069,59 +1030,96 @@ define("3", [
1069
1030
  contentAreaRect = Measure.getContentAreaRect(editor);
1070
1031
  panelRect = DOM.getRect(panel.getEl());
1071
1032
 
1072
- result = Layout.calc(currentRect, contentAreaRect, panelRect);
1033
+ if (id === 'insert') {
1034
+ result = Layout.calcInsert(targetRect, contentAreaRect, panelRect);
1035
+ } else {
1036
+ result = Layout.calc(targetRect, contentAreaRect, panelRect);
1037
+ }
1073
1038
 
1074
1039
  if (result) {
1075
1040
  panelRect = result.rect;
1076
- movePanelTo(panel, Layout.userConstrain(userConstainHandler, currentRect, contentAreaRect, panelRect));
1041
+ currentRect = targetRect;
1042
+ movePanelTo(panel, Layout.userConstrain(userConstainHandler, targetRect, contentAreaRect, panelRect));
1077
1043
 
1078
1044
  togglePositionClass(panel, result.position);
1045
+ } else {
1046
+ hide(panel);
1079
1047
  }
1080
- }
1081
- };
1048
+ };
1082
1049
 
1083
- var show = function (editor, id, targetRect, toolbars) {
1084
- if (!panel) {
1085
- panel = create(editor, toolbars);
1086
- panel.renderTo(document.body).reflow().moveTo(targetRect.x, targetRect.y);
1087
- editor.nodeChanged();
1088
- }
1050
+ var hasFormVisible = function () {
1051
+ return panel.items().filter('form:visible').length > 0;
1052
+ };
1089
1053
 
1090
- showPanelAt(panel, id, editor, targetRect);
1091
- };
1054
+ var showForm = function (editor, id) {
1055
+ if (panel) {
1056
+ panel.items().hide();
1057
+ showToolbar(panel, id);
1092
1058
 
1093
- var hide = function () {
1094
- if (panel) {
1095
- panel.hide();
1096
- }
1097
- };
1059
+ var contentAreaRect, panelRect, result, userConstainHandler;
1098
1060
 
1099
- var focus = function () {
1100
- if (panel) {
1101
- panel.find('toolbar:visible').eq(0).each(function (item) {
1102
- item.focus(true);
1103
- });
1104
- }
1105
- };
1061
+ showPanel(panel);
1062
+ panel.items().hide();
1063
+ showToolbar(panel, id);
1106
1064
 
1107
- var remove = function () {
1108
- if (panel) {
1109
- panel.remove();
1110
- panel = null;
1111
- }
1112
- };
1065
+ userConstainHandler = editor.settings.inline_toolbar_position_handler;
1066
+ contentAreaRect = Measure.getContentAreaRect(editor);
1067
+ panelRect = DOM.getRect(panel.getEl());
1113
1068
 
1114
- var inForm = function () {
1115
- return panel && panel.visible() && hasFormVisible();
1116
- };
1069
+ result = Layout.calc(currentRect, contentAreaRect, panelRect);
1117
1070
 
1118
- return {
1119
- show: show,
1120
- showForm: showForm,
1121
- inForm: inForm,
1122
- hide: hide,
1123
- focus: focus,
1124
- remove: remove
1071
+ if (result) {
1072
+ panelRect = result.rect;
1073
+ movePanelTo(panel, Layout.userConstrain(userConstainHandler, currentRect, contentAreaRect, panelRect));
1074
+
1075
+ togglePositionClass(panel, result.position);
1076
+ }
1077
+ }
1078
+ };
1079
+
1080
+ var show = function (editor, id, targetRect, toolbars) {
1081
+ if (!panel) {
1082
+ panel = create(editor, toolbars);
1083
+ panel.renderTo(document.body).reflow().moveTo(targetRect.x, targetRect.y);
1084
+ editor.nodeChanged();
1085
+ }
1086
+
1087
+ showPanelAt(panel, id, editor, targetRect);
1088
+ };
1089
+
1090
+ var hide = function () {
1091
+ if (panel) {
1092
+ panel.hide();
1093
+ }
1094
+ };
1095
+
1096
+ var focus = function () {
1097
+ if (panel) {
1098
+ panel.find('toolbar:visible').eq(0).each(function (item) {
1099
+ item.focus(true);
1100
+ });
1101
+ }
1102
+ };
1103
+
1104
+ var remove = function () {
1105
+ if (panel) {
1106
+ panel.remove();
1107
+ panel = null;
1108
+ }
1109
+ };
1110
+
1111
+ var inForm = function () {
1112
+ return panel && panel.visible() && hasFormVisible();
1113
+ };
1114
+
1115
+ return {
1116
+ show: show,
1117
+ showForm: showForm,
1118
+ inForm: inForm,
1119
+ hide: hide,
1120
+ focus: focus,
1121
+ remove: remove
1122
+ };
1125
1123
  };
1126
1124
  });
1127
1125
 
@@ -1238,13 +1236,13 @@ define("4", [
1238
1236
  }
1239
1237
  };
1240
1238
 
1241
- var addToEditor = function (editor) {
1239
+ var addToEditor = function (editor, panel) {
1242
1240
  editor.addButton('quicklink', {
1243
1241
  icon: 'link',
1244
1242
  tooltip: 'Insert/Edit link',
1245
1243
  stateSelector: 'a[href]',
1246
1244
  onclick: function () {
1247
- Panel.showForm(editor, 'quicklink');
1245
+ panel.showForm(editor, 'quicklink');
1248
1246
  }
1249
1247
  });
1250
1248
 
@@ -1266,7 +1264,7 @@ define("4", [
1266
1264
  icon: 'table',
1267
1265
  tooltip: 'Insert table',
1268
1266
  onclick: function () {
1269
- Panel.hide();
1267
+ panel.hide();
1270
1268
  Actions.insertTable(editor, 2, 2);
1271
1269
  }
1272
1270
  });
@@ -1591,11 +1589,16 @@ define("0", [
1591
1589
  return result && result.rect ? result : null;
1592
1590
  };
1593
1591
 
1594
- var togglePanel = function (editor) {
1592
+ var togglePanel = function (editor, panel) {
1595
1593
  var toggle = function () {
1596
1594
  var toolbars = getToolbars(editor);
1597
1595
  var result = findMatchResult(editor, toolbars);
1598
- result ? Panel.show(editor, result.id, result.rect, toolbars) : Panel.hide();
1596
+
1597
+ if (result) {
1598
+ panel.show(editor, result.id, result.rect, toolbars);
1599
+ } else {
1600
+ panel.hide();
1601
+ }
1599
1602
  };
1600
1603
 
1601
1604
  return function () {
@@ -1605,28 +1608,28 @@ define("0", [
1605
1608
  };
1606
1609
  };
1607
1610
 
1608
- var ignoreWhenFormIsVisible = function (f) {
1611
+ var ignoreWhenFormIsVisible = function (panel, f) {
1609
1612
  return function () {
1610
- if (!Panel.inForm()) {
1613
+ if (!panel.inForm()) {
1611
1614
  f();
1612
1615
  }
1613
1616
  };
1614
1617
  };
1615
1618
 
1616
- var bindContextualToolbarsEvents = function (editor) {
1617
- var throttledTogglePanel = Delay.throttle(togglePanel(editor), 0);
1618
- var throttledTogglePanelWhenNotInForm = Delay.throttle(ignoreWhenFormIsVisible(togglePanel(editor)), 0);
1619
+ var bindContextualToolbarsEvents = function (editor, panel) {
1620
+ var throttledTogglePanel = Delay.throttle(togglePanel(editor, panel), 0);
1621
+ var throttledTogglePanelWhenNotInForm = Delay.throttle(ignoreWhenFormIsVisible(panel, togglePanel(editor, panel)), 0);
1619
1622
 
1620
- editor.on('blur hide ObjectResizeStart', Panel.hide);
1623
+ editor.on('blur hide ObjectResizeStart', panel.hide);
1621
1624
  editor.on('click', throttledTogglePanel);
1622
1625
  editor.on('nodeChange mouseup', throttledTogglePanelWhenNotInForm);
1623
1626
  editor.on('ResizeEditor ResizeWindow keyup', throttledTogglePanel);
1624
- editor.on('remove', Panel.remove);
1627
+ editor.on('remove', panel.remove);
1625
1628
 
1626
- editor.shortcuts.add('Alt+F10', '', Panel.focus);
1629
+ editor.shortcuts.add('Alt+F10', '', panel.focus);
1627
1630
  };
1628
1631
 
1629
- var overrideLinkShortcut = function (editor) {
1632
+ var overrideLinkShortcut = function (editor, panel) {
1630
1633
  editor.shortcuts.remove('meta+k');
1631
1634
  editor.shortcuts.add('meta+k', '', function () {
1632
1635
  var toolbars = getToolbars(editor);
@@ -1635,17 +1638,17 @@ define("0", [
1635
1638
  ]);
1636
1639
 
1637
1640
  if (result) {
1638
- Panel.show(editor, result.id, result.rect, toolbars);
1641
+ panel.show(editor, result.id, result.rect, toolbars);
1639
1642
  }
1640
1643
  });
1641
1644
  };
1642
1645
 
1643
- var renderInlineUI = function (editor) {
1646
+ var renderInlineUI = function (editor, panel) {
1644
1647
  var skinName = editor.settings.skin || 'lightgray';
1645
1648
 
1646
1649
  SkinLoader.load(editor, skinName, function () {
1647
- bindContextualToolbarsEvents(editor);
1648
- overrideLinkShortcut(editor);
1650
+ bindContextualToolbarsEvents(editor, panel);
1651
+ overrideLinkShortcut(editor, panel);
1649
1652
  });
1650
1653
 
1651
1654
  return {};
@@ -1656,10 +1659,12 @@ define("0", [
1656
1659
  };
1657
1660
 
1658
1661
  ThemeManager.add('inlite', function (editor) {
1659
- Buttons.addToEditor(editor);
1662
+ var panel = new Panel();
1663
+
1664
+ Buttons.addToEditor(editor, panel);
1660
1665
 
1661
1666
  var renderUI = function () {
1662
- return editor.inline ? renderInlineUI(editor) : fail('inlite theme only supports inline mode.');
1667
+ return editor.inline ? renderInlineUI(editor, panel) : fail('inlite theme only supports inline mode.');
1663
1668
  };
1664
1669
 
1665
1670
  return {