can be break into more than one
browser line.
*/
-var caretPosition = require('/caretPosition');
+var caretPosition = require('./caretPosition');
function Scroll(outerWin) {
// scroll settings
diff --git a/src/static/js/skin_variants.js b/src/static/js/skin_variants.js
new file mode 100644
index 00000000000..8fc056acf87
--- /dev/null
+++ b/src/static/js/skin_variants.js
@@ -0,0 +1,55 @@
+// Specific hash to display the skin variants builder popup
+if (window.location.hash.toLowerCase() == "#skinvariantsbuilder") {
+ $('#skin-variants').addClass('popup-show');
+
+ $('.skin-variant').change(function() {
+ updateSkinVariantsClasses();
+ });
+
+ var containers = ['editor', 'background', 'toolbar'];
+ var colors = ['super-light', 'light', 'dark', 'super-dark'];
+
+ updateCheckboxFromSkinClasses();
+ updateSkinVariantsClasses();
+
+ // add corresponding classes when config change
+ function updateSkinVariantsClasses() {
+ var domsToUpdate = [
+ $('html'),
+ $('iframe[name=ace_outer]').contents().find('html'),
+ $('iframe[name=ace_outer]').contents().find('iframe[name=ace_inner]').contents().find('html')
+ ];
+ colors.forEach(function(color) {
+ containers.forEach(function(container) {
+ domsToUpdate.forEach(function(el) { el.removeClass(color + '-' + container); });
+ })
+ })
+
+ domsToUpdate.forEach(function(el) { el.removeClass('full-width-editor'); });
+
+ var new_classes = [];
+ $('select.skin-variant-color').each(function() {
+ new_classes.push($(this).val() + "-" + $(this).data('container'));
+ })
+ if ($('#skin-variant-full-width').is(':checked')) new_classes.push("full-width-editor");
+
+ domsToUpdate.forEach(function(el) { el.addClass(new_classes.join(" ")); });
+
+ $('#skin-variants-result').val('"skinVariants": "' + new_classes.join(" ") + '",');
+ }
+
+ // run on init
+ function updateCheckboxFromSkinClasses() {
+ $('html').attr('class').split(' ').forEach(function(classItem) {
+ var container = classItem.split('-').slice(-1);
+
+ var container = classItem.substring(classItem.lastIndexOf("-") + 1, classItem.length);
+ if (containers.indexOf(container) > -1) {
+ var color = classItem.substring(0, classItem.lastIndexOf("-"));
+ $('.skin-variant-color[data-container="' + container + '"').val(color);
+ }
+ })
+
+ $('#skin-variant-full-width').prop('checked', $('html').hasClass('full-width-editor'));
+ }
+}
diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js
index 3cdff91a951..5ac56bea11a 100644
--- a/src/static/js/timeslider.js
+++ b/src/static/js/timeslider.js
@@ -165,33 +165,9 @@ function handleClientVars(message)
// font family change
$("#viewfontmenu").change(function(){
- var font = $("#viewfontmenu").val();
- switch (font) {
- case "monospace": setFont("Courier new");break;
- case "opendyslexic": setFont("OpenDyslexic");break;
- case "comicsans": setFont("Comic Sans MS");break;
- case "georgia": setFont("Georgia");break;
- case "impact": setFont("Impact");break;
- case "lucida": setFont("Lucida");break;
- case "lucidasans": setFont("Lucida Sans Unicode");break;
- case "palatino": setFont("Palatino Linotype");break;
- case "tahoma": setFont("Tahoma");break;
- case "timesnewroman": setFont("Times New Roman");break;
- case "trebuchet": setFont("Trebuchet MS");break;
- case "verdana": setFont("Verdana");break;
- case "symbol": setFont("Symbol");break;
- case "webdings": setFont("Webdings");break;
- case "wingdings": setFont("Wingdings");break;
- case "sansserif": setFont("MS Sans Serif");break;
- case "serif": setFont("MS Serif");break;
- default: setFont("");break;
- }
+ $('#innerdocbody').css("font-family", $(this).val() || "");
});
}
-function setFont(font){
- $('#padcontent').css("font-family", font);
-}
-
exports.baseURL = '';
exports.init = init;
diff --git a/src/static/js/undomodule.js b/src/static/js/undomodule.js
index 6610224fefe..7bb47a62560 100644
--- a/src/static/js/undomodule.js
+++ b/src/static/js/undomodule.js
@@ -253,7 +253,15 @@ var undoModule = (function()
}
if (!merged)
{
- stack.pushEvent(event);
+ /*
+ * Push the event on the undo stack only if it exists, and if it's
+ * not a "clearauthorship". This disallows undoing the removal of the
+ * authorship colors, but is a necessary stopgap measure against
+ * https://github.com/ether/etherpad-lite/issues/2802
+ */
+ if (event && (event.eventType !== "clearauthorship")) {
+ stack.pushEvent(event);
+ }
}
undoPtr = 0;
}
diff --git a/src/static/js/vendors/nice-select.js b/src/static/js/vendors/nice-select.js
new file mode 100644
index 00000000000..cbd978ac7b1
--- /dev/null
+++ b/src/static/js/vendors/nice-select.js
@@ -0,0 +1,209 @@
+/* jQuery Nice Select - v1.1.0
+ https://github.com/hernansartorio/jquery-nice-select
+ Made by Hernán Sartorio */
+
+(function($) {
+
+ $.fn.niceSelect = function(method) {
+
+ // Methods
+ if (typeof method == 'string') {
+ if (method == 'update') {
+ this.each(function() {
+ var $select = $(this);
+ var $dropdown = $(this).next('.nice-select');
+ var open = $dropdown.hasClass('open');
+
+ if ($dropdown.length) {
+ $dropdown.remove();
+ create_nice_select($select);
+
+ if (open) {
+ $select.next().trigger('click');
+ }
+ }
+ });
+ } else if (method == 'destroy') {
+ this.each(function() {
+ var $select = $(this);
+ var $dropdown = $(this).next('.nice-select');
+
+ if ($dropdown.length) {
+ $dropdown.remove();
+ $select.css('display', '');
+ }
+ });
+ if ($('.nice-select').length == 0) {
+ $(document).off('.nice_select');
+ }
+ } else {
+ console.log('Method "' + method + '" does not exist.')
+ }
+ return this;
+ }
+
+ // Hide native select
+ this.hide();
+
+ // Create custom markup
+ this.each(function() {
+ var $select = $(this);
+
+ if (!$select.next().hasClass('nice-select')) {
+ create_nice_select($select);
+ }
+ });
+
+ function create_nice_select($select) {
+ $select.after($('
')
+ .addClass('nice-select')
+ .addClass($select.attr('class') || '')
+ .addClass($select.attr('disabled') ? 'disabled' : '')
+ .attr('tabindex', $select.attr('disabled') ? null : '0')
+ .html('
')
+ );
+
+ var $dropdown = $select.next();
+ var $options = $select.find('option');
+ var $selected = $select.find('option:selected');
+
+ $dropdown.find('.current').html($selected.data('display') || $selected.text());
+
+ $options.each(function(i) {
+ var $option = $(this);
+ var display = $option.data('display');
+
+ $dropdown.find('ul').append($('
')
+ .attr('data-value', $option.val())
+ .attr('data-display', (display || null))
+ .addClass('option' +
+ ($option.is(':selected') ? ' selected' : '') +
+ ($option.is(':disabled') ? ' disabled' : ''))
+ .html($option.text())
+ );
+ });
+ }
+
+ /* Event listeners */
+
+ // Unbind existing events in case that the plugin has been initialized before
+ $(document).off('.nice_select');
+
+ // Open/close
+ $(document).on('click.nice_select', '.nice-select', function(event) {
+ var $dropdown = $(this);
+
+ $('.nice-select').not($dropdown).removeClass('open');
+
+ $dropdown.toggleClass('open');
+
+ if ($dropdown.hasClass('open')) {
+ $dropdown.find('.option');
+ $dropdown.find('.focus').removeClass('focus');
+ $dropdown.find('.selected').addClass('focus');
+ if ($dropdown.closest('.toolbar').length > 0) {
+ $dropdown.find('.list').css('left', $dropdown.offset().left);
+ $dropdown.find('.list').css('top', $dropdown.offset().top + $dropdown.outerHeight());
+ $dropdown.find('.list').css('min-width', $dropdown.outerWidth() + 'px');
+ }
+
+ $listHeight = $dropdown.find('.list').outerHeight();
+ $top = $dropdown.parent().offset().top;
+ $bottom = $('body').height() - $top;
+ $maxListHeight = $bottom - $dropdown.outerHeight() - 20;
+ if ($maxListHeight < 200) {
+ $dropdown.addClass('reverse');
+ $maxListHeight = 250;
+ } else {
+ $dropdown.removeClass('reverse')
+ }
+ $dropdown.find('.list').css('max-height', $maxListHeight + 'px');
+
+ } else {
+ $dropdown.focus();
+ }
+ });
+
+ // Close when clicking outside
+ $(document).on('click.nice_select', function(event) {
+ if ($(event.target).closest('.nice-select').length === 0) {
+ $('.nice-select').removeClass('open').find('.option');
+ }
+ });
+
+ // Option click
+ $(document).on('click.nice_select', '.nice-select .option:not(.disabled)', function(event) {
+ var $option = $(this);
+ var $dropdown = $option.closest('.nice-select');
+
+ $dropdown.find('.selected').removeClass('selected');
+ $option.addClass('selected');
+
+ var text = $option.data('display') || $option.text();
+ $dropdown.find('.current').text(text);
+
+ $dropdown.prev('select').val($option.data('value')).trigger('change');
+ });
+
+ // Keyboard events
+ $(document).on('keydown.nice_select', '.nice-select', function(event) {
+ var $dropdown = $(this);
+ var $focused_option = $($dropdown.find('.focus') || $dropdown.find('.list .option.selected'));
+
+ // Space or Enter
+ if (event.keyCode == 32 || event.keyCode == 13) {
+ if ($dropdown.hasClass('open')) {
+ $focused_option.trigger('click');
+ } else {
+ $dropdown.trigger('click');
+ }
+ return false;
+ // Down
+ } else if (event.keyCode == 40) {
+ if (!$dropdown.hasClass('open')) {
+ $dropdown.trigger('click');
+ } else {
+ var $next = $focused_option.nextAll('.option:not(.disabled)').first();
+ if ($next.length > 0) {
+ $dropdown.find('.focus').removeClass('focus');
+ $next.addClass('focus');
+ }
+ }
+ return false;
+ // Up
+ } else if (event.keyCode == 38) {
+ if (!$dropdown.hasClass('open')) {
+ $dropdown.trigger('click');
+ } else {
+ var $prev = $focused_option.prevAll('.option:not(.disabled)').first();
+ if ($prev.length > 0) {
+ $dropdown.find('.focus').removeClass('focus');
+ $prev.addClass('focus');
+ }
+ }
+ return false;
+ // Esc
+ } else if (event.keyCode == 27) {
+ if ($dropdown.hasClass('open')) {
+ $dropdown.trigger('click');
+ }
+ // Tab
+ } else if (event.keyCode == 9) {
+ if ($dropdown.hasClass('open')) {
+ return false;
+ }
+ }
+ });
+
+ // Detect CSS pointer-events support, for IE <= 10. From Modernizr.
+ var style = document.createElement('a').style;
+ style.cssText = 'pointer-events:auto';
+ if (style.pointerEvents !== 'auto') {
+ $('html').addClass('no-csspointerevents');
+ }
+
+ return this;
+
+ };
+
+}(jQuery));
\ No newline at end of file
diff --git a/src/static/skins/colibris/pad.css b/src/static/skins/colibris/pad.css
index ad17132f874..c1cec179c7a 100644
--- a/src/static/skins/colibris/pad.css
+++ b/src/static/skins/colibris/pad.css
@@ -1,6 +1,8 @@
+@import url("src/general.css");
@import url("src/layout.css");
@import url("src/pad-editor.css");
+@import url("src/components/scrollbars.css");
@import url("src/components/buttons.css");
@import url("src/components/popup.css");
@@ -14,114 +16,60 @@
@import url("src/components/import-export.css");
@import url("src/plugins/brightcolorpicker.css");
-@import url("src/plugins/comments_page.css");
@import url("src/plugins/font_color.css");
-@import url("src/plugins/set_title_on_pad.css");
@import url("src/plugins/tables2.css");
-@import url("src/plugins/embedded_hyperlinks.css");
@import url("src/plugins/author_hover.css");
+@import url("src/plugins/comments.css");
-/* NEUTRAL COLOR */
-body,
-#innerdocbody
-#users,
-#chattext,
-#chatinput,
-#chatlabel,
-#toc,
-#tocItems a,
-.toolbar ul li a:hover .buttonicon,
-.toolbar ul li a,
-.toolbar ul li select,
-#mycolorpickercancel,
-.btn-default,
-.color\:black,
-[data-color=black],
-#chattext.authorColors p, #chattext.authorColors span,
-#chattext .time,
-#tbl_context_menu ul .yuimenuitemlabel,
-.yui-skin-sam .yui-split-button button em:not(.color-picker-button),
-#yui-picker-panel .button-group .yui-button:first-child button,
-#newComment .sidebar-comment input[type=reset], #newComment .sidebar-comment input[type=reset]:hover,
-#newComment .sidebar-comment input[type=submit]:hover,
-.suggestion, .comment-reply-input, .reply-suggestion p:not(.reply-comment-suggest-from-p), .comment-text,
-.sidebar-comment textarea, .reply-comment-suggest label, .comment-suggest label, .comment-reply-input
-#comments, #newComments, .reply-comment-suggest-from-p,
-.comment-changeFrom-value,
-.comment-changeTo-value,
-.reply-suggestion .reply-comment-suggest-from,
-.suggestion .comment-suggest-from,
-.hyperlink-dialog>.hyperlink-url,
-.timeslider #padcontent span ,
-.exporttype, .timeslider #export > p
-{ color: #2E3338 !important; }
+@import url("src/pad-variants.css");
-/* MENUS ICONS */
-#edit_title:before,
-#tbl-menu:before
-{ color: #767676 !important; }
-/* MENU BUTTONS */
-.timeslider #editbar .buttontext
-{ background-color: #767676 !important; }
+/* -----------------------------------------------------------------
+ * COLORS
+ * If you want to change main colors, please replace following CSS variables
+ * -----------------------------------------------------------------
+ */
-/* PRIMARY BUTTONS */
-#mycolorpickersave,
-.btn-primary,
-#tbl_btn_close,
-#save_title button,
-#yui-picker-panel .button-group .yui-button:last-child button,
-#newComment.sidebar-comment input[type=submit],
-.comment-changeTo-approve input[type=submit],
-.hyperlink-dialog>.hyperlink-save,
-#importsubmitinput, #forcereconnect
-{
- background-color: #64d29b;
- color: white;
-}
+:root {
+ --super-dark-color: #485365; /*#374256;*/
+ --dark-color: #576273; /*#4d5d77*/
-/* PRIMARY COLOR */
-h1,
-#titlelabel,
-.yui-skin-sam .yui-panel .hd,
-p[data-l10n-id="ep_comments_page.comment"],
-.comment-reply-input-label span,
-.stepper, #importmessageabiword, #importmessageabiword > a
-{ color: #64d29b; }
-#ui-slider-handle, #playpause_button_icon {
- background-color: #64d29b;
-}
-.stepper {
- border-color: #64d29b;
-}
+ --primary-color: #64d29b;
+ --middle-color: #d2d2d2; /* kind of grey, use for border for examples */
+
+ --light-color: #f2f3f4; /*#f9f9f9;*/
+ --super-light-color: white;
+
+ --text-color: var(--super-dark-color);
+ --text-soft-color: var(--dark-color);
+ --border-color: var(--middle-color);
+ --bg-soft-color: var(--light-color);
+ --bg-color: var(--super-light-color);
-/* BACKGROUND COLOR */
-#editorcontainer, #padmain {
- background-color: #f9f9f9 !important;
+ --toolbar-border: none;
+
+ --main-font-family: Quicksand, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
+
+ --editor-horizontal-padding: 55px;
+ --editor-vertical-padding: 40px;
}
+@media (max-width:1000px) {
+ :root {
+ --editor-horizontal-padding: 15px;
+ --editor-vertical-padding: 15px;
+ }
+}
-/* NEUTRAL FONT */
-body,
-#innerdocbody,
-#chatinput,
-.toolbar ul li select,
-button, input, select, textarea,
-td[name=tData],
-#comments, #newComments,
-#sidediv,
-#comments .sidebar-comment,
-#timeslider #timer, .exporttype
-{ font-family: Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; }
+@media (max-width:600px) {
+ :root {
+ --editor-horizontal-padding: 15px;
+ --editor-vertical-padding: 15px;
+ }
+}
-/* PRIMARY FONT */
-h1,
-#titlelabel,
-#chatlabel,
-.btn,
-.yui-skin-sam .yui-panel .hd,
-.reply-suggestion p:not(.reply-comment-suggest-from-p),
-p[data-l10n-id="ep_comments_page.comment"],
-#newComment.sidebar-comment input[type=submit],
-.comment-changeTo-approve input[type=submit],
-.hyperlink-dialog>.hyperlink-save
-{ font-family: Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif !important; }
+/* Default scrollbar values */
+body {
+ --scrollbar-bg: var(--light-color);
+ --scrollbar-track: var(--super-light-color);
+ --scrollbar-thumb: var(--dark-color);
+}
diff --git a/src/static/skins/colibris/pad.js b/src/static/skins/colibris/pad.js
index 88ddce4c38a..1a7bd938a8c 100644
--- a/src/static/skins/colibris/pad.js
+++ b/src/static/skins/colibris/pad.js
@@ -1,4 +1,6 @@
function customStart()
{
$('#pad_title').show();
+ $('.buttonicon').mousedown(function() { $(this).parent().addClass('pressed'); })
+ $('.buttonicon').mouseup(function() { $(this).parent().removeClass('pressed'); })
}
diff --git a/src/static/skins/colibris/src/components/buttons.css b/src/static/skins/colibris/src/components/buttons.css
index 93879023ec8..ccaea9d4d9a 100644
--- a/src/static/skins/colibris/src/components/buttons.css
+++ b/src/static/skins/colibris/src/components/buttons.css
@@ -1,21 +1,31 @@
-.btn, #mycolorpickercancel, #mycolorpickersave, #save_title button, #yui-picker-panel .button-group .yui-button button, .hyperlink-dialog>.hyperlink-save, .timeslider #editbar .buttontext,
-#importsubmitinput, #forcereconnect
+button, .btn
{
- margin-right: 10px;
padding: 5px 20px;
border-radius: 4px;
- font-size: 13px;
line-height: 1.5;
width: auto;
- border: none !important;
+ border: none;
font-weight: bold;
text-transform: uppercase;
position: relative;
background: none;
- top: 0;
- left: 0;
+ cursor: pointer;
}
-.btn:hover, #mycolorpickercancel:hover, #mycolorpickersave:hover, #save_title button:hover, .hyperlink-dialog>.hyperlink-save:hover, #importsubmitinput:hover, #forcereconnect:hover {
- cursor: pointer;
+.btn-primary
+{
+ background-color: #64d29b;
+ background-color: var(--primary-color);
+ color: #ffffff;
+ color: var(--bg-color);
+}
+.btn-default {
+ color: #485365;
+ color: var(--text-color);
}
+
+.buttonicon:before, [class^="buttonicon-"]:before, [class*=" buttonicon-"]:before {
+ line-height: 18px;
+}
+
+
diff --git a/src/static/skins/colibris/src/components/chat.css b/src/static/skins/colibris/src/components/chat.css
index 7a2979e02c1..61d992c5f21 100644
--- a/src/static/skins/colibris/src/components/chat.css
+++ b/src/static/skins/colibris/src/components/chat.css
@@ -1,33 +1,43 @@
#chatbox {
+ background-color: transparent !important;
+ color: var(--text-color);
+}
+.chat-content {
background: none;
padding: 0;
- background-color: white;
- border: none;
- box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
width: 400px;
height: 300px;
- background-color: #f9f9f9 !important;
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
}
-#chatbox.stickyChat {
- width: 193px !important;
- border: none !important;
+.chat-content, #chaticon {
+ box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
+ border: none;
}
-#chatbox.stickyChat.chatAndUsersChat{
- margin-top: 181px;
+#chaticon {
+ padding: 10px 20px;
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+ color: #485365;
+ color: var(--text-color);
+ right: 30px;
+}
+
+#chatbox.stickyChat .chat-content {
+ border: none;
box-shadow: none;
- border-top: 1px solid #d2d2d2 !important;
- padding: 0!important;
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
}
#titlebar {
bottom: 0;
- line-height: 39px;
+ line-height: 44px;
height: 44px;
padding: 0 7px;
z-index: 20000;
- border-bottom: 1px solid #d2d2d2;
}
#titlelabel, #chatlabel {
@@ -35,80 +45,40 @@
font-weight: bold;
}
-#titlelabel { font-size: 16px; }
+#titlebar #titlelabel { font-size: 16px; }
#chatlabel { margin-right: 15px; }
#chattext {
- top: 45px;
- font-size: 13px;
- bottom: 52px;
- overflow-y: auto;
padding: 0;
- border: none;
- border-bottom: 1px solid #cccccc;
-}
-
-.plugin-ep_author_neat #chattext {
- padding: 10px;
-}
-
-.plugin-ep_author_neat #chattext.authorColors p,
-.plugin-ep_author_neat #chattext.authorColors span {
- background-color: transparent !important;
-}
-
-#chattext p b {
- color: #4c4c4c;
+ border-top: 1px solid #ffffff;
+ border-top: 1px solid var(--bg-color);
+ border-bottom: 1px solid #ffffff;
+ border-bottom: 1px solid var(--bg-color);
+ background-color: inherit;
+ color: inherit;
}
-
-#chattext::-webkit-scrollbar-track {
- background-color: #f6f6f6;
- border: 1px solid #f0f0f0;
+#chattext p {
+ padding: 4px 10px;
}
-
-#chattext::-webkit-scrollbar {
- width: 7px;
-}
-
-#chattext::-webkit-scrollbar-thumb {
- border-radius: 10px;
- background-color: #C5C5C5;
+#chattext:not(.authorColors) p:first-child {
+ padding-top: 10px;
}
-
-#chatbox.stickyChat #chattext {
- padding: 0px;
+#chattext:not(.authorColors) p:last-child {
+ padding-bottom: 10px;
}
#chatinputbox {
padding: 8px;
}
-
-#chatinput {
- width: calc(100% - 20px);
- float: right;
-}
-
-.plugin-ep_author_neat #chatbox.stickyChat #chattext {
- padding: 5px 3px;
-}
-
-#chaticon {
- box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
- border: none;
- padding: 10px 20px;
- right: 30px;
+#chatinputbox #chatinput {
+ background-color: #ffffff;
+ background-color: var(--bg-color);
}
@media (max-width: 720px) {
#chaticon {
right: 0;
- bottom: 44px;
- }
-
- #chatbox {
- width: 100%;
- right: 0;
}
- #titlesticky { display: none; }
+ .stick-to-screen-btn { display: none; }
}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/components/form.css b/src/static/skins/colibris/src/components/form.css
index 5d34fc865d3..aa82218c2af 100644
--- a/src/static/skins/colibris/src/components/form.css
+++ b/src/static/skins/colibris/src/components/form.css
@@ -1,9 +1,114 @@
-#input_title, #chatinput, .hyperlink-dialog>.hyperlink-url {
- border: 1px solid #d2d2d2;
- height: 18px;
+input[type="text"], select, textarea, .nice-select {
border-radius: 3px;
+ box-shadow: none;
+ border: none;
+ outline: 0;
+}
+
+input[type="text"], textarea {
padding: 8px 10px;
- background: none !important;
- background-color: white !important;
- box-shadow: none !important;
-}
\ No newline at end of file
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+ border: none;
+ color: #485365;
+ color: var(--text-color);
+}
+input[type="text"]::placeholder, textarea::placeholder {
+ color: #576273;
+ color: var(--text-soft-color);
+}
+select, .nice-select {
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+ border: none;
+ padding: 4px 10px;
+ padding-right: 25px;
+ font-weight: bold;
+ line-height: inherit;
+}
+.nice-select .list {
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+}
+.nice-select .option:hover,.nice-select .option.focus,.nice-select .option.selected.focus {
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+}
+.nice-select .option {
+ padding: 0 15px;
+}
+.popup .nice-select .list {
+ right: 0;
+ left: auto;
+}
+
+
+/* Checkboxes
+ ========================================================================== */
+/* Remove default checkbox */
+[type="checkbox"]:not(:checked),
+[type="checkbox"]:checked {
+ position: absolute;
+ opacity: 0;
+ pointer-events: none;
+}
+
+[type="checkbox"] + label {
+ position: relative;
+ padding-left: 2.5rem;
+ cursor: pointer;
+ display: inline-block;
+ height: 1.4rem;
+ line-height: 1.4rem;
+ font-size: 1rem;
+}
+
+[type="checkbox"] + label:before,
+[type="checkbox"] + label:after {
+ content: '';
+ position: absolute;
+ margin-top: 4px;
+ transition: all .2s ease-in-out;
+}
+
+/* BEFORE, the container*/
+[type="checkbox"] + label:before {
+ width: 24px;
+ height: 14px;
+ top: 0;
+ left: 0;
+ border-radius: 6px;
+ border: 2px solid #576273;
+ border: 2px solid var(--text-soft-color);
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+ opacity: .7;
+}
+[type="checkbox"]:checked + label:before {
+ background-color: transparent;
+ border-color: #64d29b;
+ border-color: var(--primary-color);
+}
+
+/* AFTER, the circle moving */
+[type="checkbox"] + label:after {
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ background-color: #576273;
+ background-color: var(--text-soft-color);
+ top: -1px;
+ left: -3px;
+}
+[type="checkbox"]:checked + label:after {
+ background-color: #64d29b;
+ background-color: var(--primary-color);
+ transform: translateX(14px);
+}
+
+[type="checkbox"]:checked:disabled + label,
+[type="checkbox"]:checked:disabled + label:before,
+[type="checkbox"]:checked:disabled + label:after {
+ cursor: not-allowed;
+ opacity: .4;
+}
diff --git a/src/static/skins/colibris/src/components/gritter.css b/src/static/skins/colibris/src/components/gritter.css
index 7c514010c64..fa255fe4a22 100644
--- a/src/static/skins/colibris/src/components/gritter.css
+++ b/src/static/skins/colibris/src/components/gritter.css
@@ -1,28 +1,65 @@
-/* Popups at the bottom of the page to indicate when the pad expires, and others stuff */
+.gritter-item:not(.error) .popup-content{
+ background-color: #64d29b;
+ background-color: var(--primary-color);
+ color: #ffffff;
+ color: var(--bg-color);
+}
+.gritter-item .popup-content {
+ padding: 15px;
+ box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
+}
+#gritter-container.bottom .gritter-item .popup-content {
+ margin-top: 10px;
+}
+#gritter-container.top .gritter-item .popup-content {
+ margin-bottom: 10px;
+}
+.gritter-item p {
+ margin: 0 !important;
+}
+.gritter-item .gritter-title {
+ margin-bottom: 10px;
+}
+.gritter-item .gritter-close {
+ margin-left: 15px;
+ margin-right: 0px;
+}
+.gritter-item:not(.error) .gritter-close .buttonicon {
+ color: #ffffff;
+ color: var(--bg-color);
+}
-.gritter-title {
- text-shadow: none;
+/* CHAT GRIITER ITEM */
+.gritter-item.chat-gritter-msg:not(.error) .popup-content {
+ background-color: white;
+ background-color: var(--bg-color);
+ color: #485365;
+ color: var(--text-color);
+}
+.gritter-item.chat-gritter-msg .gritter-content {
+ text-align: left;
+}
+.gritter-item.chat-gritter-msg .author-name {
+ font-weight: bold;
+ margin-right: 5px;
+}
+.gritter-item.chat-gritter-msg:not(.error) .gritter-close .buttonicon {
+ color: #485365;
+ color: var(--text-color);
}
-#gritter-notice-wrapper {
- background-color: #fff;
- box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
- border-radius: 3px;
- padding: 15px 20px 5px 30px;
- bottom: 50px !important;
- right: 30px !important;
+.gritter-item.saved-revision {
+ max-width: 600px;
}
-@media (max-width: 1100px) {
- #gritter-notice-wrapper {
- display: none;
- }
+#gritter-container.top .gritter-item.popup > .popup-content {
+ transform: scale(0.8) translateY(-100px);
+}
+#gritter-container.bottom .gritter-item.popup > .popup-content {
+ transform: scale(0.8) translateY(0px);
}
-.gritter-item {
- font-family: Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif !important;
- color: #4e545b;
- font-size: 12px;
- line-height: 20px;
- padding: 0;
+.gritter-item.popup.popup-show > .popup-content {
+ transform: scale(1) translateY(0) !important;
+ transition: all 0.4s cubic-bezier(0.74, -0.05, 0.27, 1.75) !important;
}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/components/import-export.css b/src/static/skins/colibris/src/components/import-export.css
index bdaa184e9c2..d8425d89529 100644
--- a/src/static/skins/colibris/src/components/import-export.css
+++ b/src/static/skins/colibris/src/components/import-export.css
@@ -1,32 +1,16 @@
-#exportColumn {
- margin-top: 0;
- padding-left: 20px;
- width: calc(50% - 20px);
-}
-
#importmessageabiword {
font-style: italic;
- font-size: 13px;
+ color: #64d29b;
+ color: var(--primary-color);
}
#importmessageabiword > a {
font-weight: bold;
text-decoration: underline;
-}
-
-.exportlink {
- font-size: 14px;
- margin: 0;
- margin-bottom: 7px;
- margin-left: 10px;
-}
-
-.exporttype {
- margin-left: 5px;
- font-size: 14px;
+ color: #64d29b;
+ color: var(--primary-color);
}
#importmessagefail {
- font-size: 13px;
margin-top: 10px;
}
diff --git a/src/static/skins/colibris/src/components/popup.css b/src/static/skins/colibris/src/components/popup.css
index 7b3c0381b7a..0a50c9bbe15 100644
--- a/src/static/skins/colibris/src/components/popup.css
+++ b/src/static/skins/colibris/src/components/popup.css
@@ -1,30 +1,70 @@
-#users,
-#mycolorpicker,
-.popup,
-.hyperlink-dialog {
- border-radius: 3px;
- padding: 20px 20px;
+.popup-content {
+ border-radius: 5px;
+ padding: 25px;
background: none;
- background-color: white;
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+ color: #576273;
+ color: var(--text-soft-color);
border: none;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
}
-#users input[type=text],
-.popup input[type=text] {
- border: none !important;
- border-bottom: 1px solid #d7d8da !important;
+#mycolorpicker, #users {
+ min-width: 0;
}
.popup h1 {
- margin-bottom: 15px;
+ margin-bottom: 20px;
+ font-size: 1.6rem;
}
.popup h2 {
margin-bottom: 15px;
+ margin-top: 20px;
+ color: #485365;
+ color: var(--text-color);
}
-.popup p {
+.popup:not(.comment-modal) p {
margin: 10px 0;
}
+.popup .dropdowns-container .dropdown-line {
+ margin-top: 15px;
+}
+.popup .dropdowns-container label {
+ width: 120px;
+ display: inline-block;
+}
+.popup .dropdowns-container .nice-select {
+ min-width: 180px;
+}
+
+@media (max-width: 720px) {
+ .popup-content {
+ padding: 1rem;
+ box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), -1px 1px 16px 3px rgba(27, 39, 51, 0.12);
+ }
+ .popup .dropdowns-container select {
+ min-width: 0;
+ }
+}
+
+/* SKIN Variants Popup */
+#skin-variants {
+ bottom: 0;
+ left: 0;
+ right: auto;
+ top: auto;
+}
+#skin-variants .popup-content > p {
+ margin-top: 25px;
+}
+#skin-variants-result{
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+}
+.skin-variant-container {
+ text-transform: capitalize;
+}
diff --git a/src/static/skins/colibris/src/components/scrollbars.css b/src/static/skins/colibris/src/components/scrollbars.css
new file mode 100644
index 00000000000..cd4ea08b59a
--- /dev/null
+++ b/src/static/skins/colibris/src/components/scrollbars.css
@@ -0,0 +1,39 @@
+@media (min-width: 721px) {
+ ::-webkit-scrollbar-track {
+ background-color: white;
+ background-color: var(--scrollbar-track);
+ border-radius: 10px;
+ border: 7px solid #f2f3f4;
+ border: 7px solid var(--scrollbar-bg);
+ }
+
+ ::-webkit-scrollbar {
+ width: 22px;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background-color: #576273;
+ background-color: var(--scrollbar-thumb);
+ border: 7px solid #f2f3f4;
+ border: 7px solid var(--scrollbar-bg);
+ }
+}
+
+.thin-scrollbar::-webkit-scrollbar-track {
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+ border-radius: 0px;
+ border: none;
+}
+
+.thin-scrollbar::-webkit-scrollbar {
+ width: 6px;
+}
+
+.thin-scrollbar::-webkit-scrollbar-thumb {
+ border-radius: 0px;
+ background-color: #d2d2d2;
+ background-color: var(--middle-color);
+ border: none;
+}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/components/sidediv.css b/src/static/skins/colibris/src/components/sidediv.css
index 2fb8377476f..63eee2dd526 100644
--- a/src/static/skins/colibris/src/components/sidediv.css
+++ b/src/static/skins/colibris/src/components/sidediv.css
@@ -1,14 +1,16 @@
#sidediv {
background-color: transparent;
border: none;
+ opacity: .8;
}
#sidedivinner>div:before {
- font-size: 13px;
+ font-family: var(--main-font-family); /* the parent div have font-family monospace (line number) */
+ color: #485365;
+ color: var(--text-color);
padding-right: 18px !important;
- color: #6a6a6b;
- text-transform: uppercase;
- font-size: 11px !important;
+ text-transform: capitalize;
+ font-size: 12px !important;
font-weight: bold;
}
@@ -21,8 +23,10 @@
#sidedivinner>div {
line-height: 24px;
- font-size: 10px !important;
- color: #a0a0a0;
+ font-size: 9px !important;
+ font-family: RobotoMono;
+ color: #576273;
+ color: var(--text-soft-color);
}
#sidedivinner.authorColors>div, #sidedivinner.authorColors>div.primary-none, #sidedivinner>div {
diff --git a/src/static/skins/colibris/src/components/table-of-content.css b/src/static/skins/colibris/src/components/table-of-content.css
index b2f26c4a220..fd6c5ea2bef 100644
--- a/src/static/skins/colibris/src/components/table-of-content.css
+++ b/src/static/skins/colibris/src/components/table-of-content.css
@@ -1,11 +1,21 @@
#toc {
- background: none !important;
- background-color: rgb(249, 249, 249) !important;
- padding: 20px !important;
- width: 146px !important;
- padding-left: 15px !important;
+ padding: 20px 20px 10px 10px !important;
+ min-width: 146px !important;
+ background-color: transparent !important;
+ border: none !important;
+ order: -2;
}
#tocItems {
line-height: 40px !important;
+}
+
+.plugin-ep_resizable_bars #toc {
+ min-width: 186px !important;
+}
+
+@media (max-width: 1200px) {
+ #toc {
+ padding-top: 10px !important
+ }
}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/components/toolbar.css b/src/static/skins/colibris/src/components/toolbar.css
index 78d774c3083..354e1324965 100644
--- a/src/static/skins/colibris/src/components/toolbar.css
+++ b/src/static/skins/colibris/src/components/toolbar.css
@@ -1,26 +1,50 @@
.toolbar {
- height: 39px !important;
- padding-top: 0;
- margin: 0;
- background-color: white !important;
- background: none;
- border: 1px solid #d2d2d2
+ border-bottom: none;
+ padding: 0;
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+ color: #576273;
+ color: var(--text-soft-color);
+ border-bottom: none;
+}
+
+#editbar.editor-scrolled {
+ border-bottom: 1px solid #d2d2d2;
+ border-bottom: var(--toolbar-border);
+}
+
+.toolbar ul {
+ align-items: center;
+}
+
+.toolbar ul.menu_left {
+ padding-left: 5px;
+}
+
+.toolbar ul li {
+ margin: 7px 1px;
+}
+
+.toolbar ul li a, .toolbar .buttonicon {
+ color: inherit;
}
.toolbar .buttonicon {
background-color: transparent;
- font-size: 14px;
- color: #767676;
+ font-size: 15px;
+}
+.buttonicon-insertorderedlist:before,
+.buttonicon-insertunorderedlist:before,
+.buttonicon-indent:before,
+.buttonicon-outdent:before {
+ font-size: 16px !important;
}
.toolbar ul li.separator {
- padding: 0;
- visibility: visible;
+ visibility: hidden;
width: 1px;
margin: 0 10px;
- margin-right: 6px;
- height: 39px;
- background-color: rgba(78, 85, 92, 0.22);
+ position: relative;
}
.toolbar.condensed ul li {
@@ -35,111 +59,95 @@
background-color: transparent;
background: none;
border: none;
- margin-top: 6px;
+ border-radius: 3px !important;
+ transition: background-color .1s;
}
-.toolbar ul li a:hover,
-.toolbar ul li a.selected,
-.toolbar ul li a:focus {
- background: none !important;
- border-radius: 0;
+.toolbar ul li a:hover, .toolbar ul li select:hover {
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+ color: #485365;
+ color: var(--text-color);
}
-
-.toolbar ul li a:hover {
- background:#f7f7f7!important
+.toolbar ul li a.selected {
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
}
-
-.toolbar ul li a.selected,
-.toolbar ul li a:focus {
- background: #eaeaea!important;
+.toolbar ul li a.pressed,
+.toolbar ul li select:active {
+ color: #64d29b;
+ color: var(--primary-color);
}
-.toolbar ul li select {
- border: none;
- border-bottom: 1px solid #d7d8da;
- border-radius: 0;
- width: 90px;
- margin-top: 5px;
+.toolbar ul li select:active option {
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+ color: #576273;
+ color: var(--text-soft-color);
+ padding: 5px;
}
-.toolbar ul {
- height: 39px;
+.toolbar .menu_right li a.selected {
+ background-color: #576273;
+ background-color: var(--text-soft-color);
+ color: #ffffff;
+ color: var(--bg-color);
}
-.toolbar ul.menu_left {
- left: 8px;
+.toolbar ul li[data-key=showusers] {
+ margin: 0;
+ margin-left: 15px;
+ width: 45px;
+ height: 100%;
+}
+.toolbar ul li[data-key=showusers] > a {
+ width: 100%;
+ height: 100%;
+ border-radius: 0 !important;
}
-.toolbar ul.menu_right {
- right: 0;
- padding: 0;
+.toolbar .menu_right .separator {
+ display: none;
+}
+.toolbar .menu_right li {
+ margin-left: 10px;
}
-.toolbar ul li[data-key=showusers] a {
- margin: 0;
- height: 59px;
- line-height: 25px;
- width: 45px;
- margin-left: -10px;
- border-radius: 0;
+.toolbar.cropped .menu_left {
+ height: 39px;
+ padding-top: 1px;
+}
+.toolbar .show-more-icon-btn {
+ font-size: 1.8rem;
+ color: #64d29b;
+ color: var(--primary-color);
}
@media (max-width: 1000px) {
.toolbar ul li.separator {
- margin: 0 2px;
+ margin: 0 5px;
background: none;
display: block;
}
-
- .toolbar ul li[data-key=showusers] a {
- margin-left: 0 !important;
- }
}
@media (max-width: 720px) {
-.toolbar ul {
- height: 39px;
- background: none;
- background-color: transparent;
- border: none !important;
- padding: 0 !important;
- overflow-x: visible;
- }
- .toolbar ul.menu_left {
- padding-top: 2px !important;
- }
- .toolbar ul.menu_right {
- left: 0;
- padding-left: 8px !important;
- padding-top: 8px !important;
- height: 35px !important;
- border-top: 1px solid #d2d2d2 !important;
- background-color: white;
- }
- .toolbar ul li a {
- padding: 4px 5px !important;
- }
- .toolbar ul li[data-key=showusers] {
- position: absolute;
- right: 0;
- top: 0;
+ .toolbar ul li {
+ margin: 5px 2px;
}
- .toolbar ul li[data-key=showusers] a {
- padding-top: 9px !important;
- }
- .toolbar ul li a:hover { background-color: transparent; }
-
- #connectivity, #embed, #import_export, #settings { bottom: 42px; }
- li.superscript,
- li.subscript,
- li[data-key="undo"],
- li[data-key="redo"] {
- display: none;
+ .toolbar .menu_right {
+ border-top: 1px solid #d2d2d2;
+ border-top: var(--toolbar-border);
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+ padding: 0;
}
- .toolbar ul li.separator { margin: 0; }
+ .toolbar ul li a:hover { background-color: transparent; }
+
+ .toolbar ul li.separator { margin: 0; display: none; }
}
diff --git a/src/static/skins/colibris/src/components/users.css b/src/static/skins/colibris/src/components/users.css
index bb5fbb49500..beb498e3b2d 100644
--- a/src/static/skins/colibris/src/components/users.css
+++ b/src/static/skins/colibris/src/components/users.css
@@ -1,5 +1,18 @@
table#otheruserstable {
- margin-top: 10px !important;
+ margin-top: 20px;
+}
+
+.popup#users.chatAndUsers > .popup-content {
+ padding: 20px 10px;
+ height: 250px;
+ border-left: none;
+ transition: none;
+ border-bottom-color: #d2d2d2;
+ border-bottom-color: var(--border-color);
+}
+.popup#users.chatAndUsers #mycolorpicker.popup {
+ right: calc(100% + 30px);
+ top: 15px;
}
#otheruserstable .swatch {
@@ -16,80 +29,24 @@ table#otheruserstable {
margin-left: 35px;
}
-#myusernameedit {
- width: 110px;
-}
-
-#myswatchbox {
- background: none;
- float: left;
- position: relative;
- left: 0;
- top: 0;
+input#myusernameedit {
+ min-width: 110px;
border: none !important;
+ background-color: transparent !important;
+ border-bottom: 1px solid #d2d2d2 !important;
+ border-bottom: 1px solid var(--border-color) !important;
+ border-radius: 0;
+ padding-bottom: 5px;
}
#myswatch {
border-radius: 50%;
}
-#nootherusers {
- padding: 0;
-}
-
-#mycolorpicker {
- width: auto;
- height: auto;
- left: -280px;
-}
-
#colorpicker {
margin-bottom: 25px;
}
-#mycolorpickercancel {
- padding-left: 3px;
-}
-
-#mycolorpickersave {
- color: #fff;
-}
-
#mycolorpickerpreview {
- float: right;
- top: 0;
- left: 0;
- position: relative;
border-radius: 50%;
- height: 20px;
- width: 20px;
-}
-
-@media (max-width: 720px) {
- #users {
- bottom: 42px;
- top: initial !important;
- height: auto !important;
- }
-
- #mycolorpicker {
- width: auto;
- height: auto;
- right: 0;
- bottom: 42px;
- left: initial;
- top: initial !important;
- }
-}
-
-#users.chatAndUsers {
- width: 172px!important;
- box-shadow: none;
- border: none !important;
- padding: 10px;
- padding-top: 15px;
-}
-
-#users.chatAndUsers #myusernameedit {
- width: calc(100% - 10px);
}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/general.css b/src/static/skins/colibris/src/general.css
new file mode 100644
index 00000000000..f51d46ce5aa
--- /dev/null
+++ b/src/static/skins/colibris/src/general.css
@@ -0,0 +1,15 @@
+html {
+ font-size: 15px;
+}
+
+body {
+ color: #485365;
+ color: var(--text-color);
+ font-family: Quicksand, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
+ font-family: var(--main-font-family);
+}
+
+h1 {
+ color: #64d29b;
+ color: var(--primary-color);
+}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/layout.css b/src/static/skins/colibris/src/layout.css
index 9a32962cf09..a6dd784d7c5 100644
--- a/src/static/skins/colibris/src/layout.css
+++ b/src/static/skins/colibris/src/layout.css
@@ -1,76 +1,48 @@
-#connectivity,
-#embed,
-#import_export,
-#settings,
-#users {
- top: 38px;
- right: 30px;
-}
-
-#editorcontainer {
- top: 41px !important;
- padding-top: 0 !important;
-}
-
-#outerdocbody, .timeslider #editorcontainerbox {
- max-width: 900px;
+#outerdocbody {
margin: 0 auto;
padding-top: 20px;
+ width: 100%;
}
-#outerdocbody {
- overflow-y: auto;
- position: relative;
- background-color: transparent;
- padding-left: 40px; /* space for side div */
+#editorcontainerbox {
+ background-color: #f2f3f4;
+ background-color: var(--bg-color);
}
-#outerdocbody.plugin-ep_author_neat {
- padding-left: 120px; /* more space for sidediv */
-}
-@media (max-width:600px) {
- #outerdocbody.plugin-ep_author_neat { padding-left: 0; }
- #options-linenoscheck { display:none; }
- #options-linenoscheck ~ label { display:none; }
+#editorcontainerbox .sticky-container {
+ width: 250px;
}
#outerdocbody.sidediv-hidden {
padding-left: 0; /* sidediv hidden */
}
-#outerdocbody iframe {
- display: block;
- position: relative;
- left: 0 !important;
- top: 0;
-}
-
-#outerdocbody iframe, .timeslider #editorcontainerbox {
- padding: 55px;
- box-shadow: 0 0 0 0.5px rgba(209, 209, 209, 0.32), 0 0 7pt 0pt rgba(204, 204, 204, 0.52);
+#outerdocbody iframe, #outerdocbody > #innerdocbody {
+ max-width: 900px;
+ padding: 40px 55px;
+ padding-left: var(--editor-horizontal-padding);
+ padding-right: var(--editor-horizontal-padding);
+ padding-top: var(--editor-vertical-padding);
+ padding-bottom: var(--editor-vertical-padding);
+ box-shadow: none;
border: 0;
- border-radius: 5px;
- background-color: white;
- width: calc(100% - 110px) !important; /* 100% - padding */
+ border-radius: 8px 8px 0 0;
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+ color: #485365;
+ color: var(--text-color);
}
-
#sidediv {
- position: absolute;
- right: calc(100% - 35px);
- left: initial;
- top: 74px !important;
- padding: 0;
+ /* Padding must be the same than editor, otherwise it creates problem */
+ padding-top: 40px; /* = #innerdocbody iframe vertical padding */
+ padding-bottom: 40px;
+ padding-top: var(--editor-vertical-padding);
+ padding-bottom: var(--editor-vertical-padding);
}
-#outerdocbody.plugin-ep_author_neat #sidediv {
- right: calc(100% - 113px);
-}
-
-/* Fixs comments_page & author_hover does not take in account the document padding */
-.comment-modal { margin-top: 75px !important; margin-left: 45px; }
+/* Fixs author_hover does not take in account the document padding */
.authortooltip { margin-top: 65px !important; margin-left: 60px; }
.caretindicator { margin-top: 61px!important; margin-left: 52px; }
-
#outerdocbody.plugin-ep_author_neat .authortooltip{ margin-left: 145px; }
#outerdocbody.plugin-ep_author_neat .caretindicator{ margin-left: 52px; margin-top: 65px!important;}
@media (max-width:1000px) {
@@ -79,33 +51,16 @@
#outerdocbody.plugin-ep_author_neat .caretindicator{ margin-left: 17px; }
}
-@media (min-width: 1381px) {
- #outerdocbody.plugin-ep_comments_page { padding-right: 150px; } }
- #outerdocbody.plugin-ep_comments_page #comments { left: calc(100% - 150px) }
-@media (max-width: 1380px) {
- #outerdocbody.plugin-ep_comments_page #comments { left: calc(100% - 220px) }
- #outerdocbody.plugin-ep_comments_page { padding-right: 220px; }
-}
-@media (max-width: 1278px) {
- #outerdocbody.plugin-ep_comments_page #comments { display: none; }
- #outerdocbody.plugin-ep_comments_page { padding-right: 0px; }
-}
-
@media (max-width:1000px) {
#outerdocbody {
- max-width: none;
padding-top: 0;
}
- #outerdocbody iframe, .timeslider #editorcontainerbox {
- padding: 20px !important;
+ #outerdocbody iframe, #outerdocbody > #innerdocbody {
+ max-width: none;
border-radius: 0;
- width: calc(100% - 40px) !important; /* 100% - padding */
- }
- #sidediv {
- top: 20px !important; /* = #outerdocbody iframe padding-top */
}
- .comment-modal, .authortooltip { margin-top: 20px !important; }
+ .authortooltip { margin-top: 20px !important; }
.caretindicator { margin-top: 0px !important; }
#outerdocbody.plugin-ep_author_neat .caretindicator { margin-top: 10px !important; }
@@ -116,18 +71,9 @@
#outerdocbody.plugin-ep_author_neat #sidediv { padding-right: 0 !important; }
}
-@media (max-width:600px) {
- html { overflow: scroll; }
- #outerdocbody {
- width: 100%;
- }
- #outerdocbody iframe, .timeslider #editorcontainerbox {
- padding: 15px !important;
- width: calc(100% - 30px) !important; /* 100% - padding */
- }
- #sidediv {
- display: none;
- top: 15px !important; /* = #outerdocbody iframe padding-top */
+@media only screen and (max-width: 720px) {
+ #editorcontainerbox {
+ margin-bottom: 39px; /* margin for bottom toolbar */
}
}
diff --git a/src/static/skins/colibris/src/pad-editor.css b/src/static/skins/colibris/src/pad-editor.css
index aae79bf844c..1d50ed0f9c9 100644
--- a/src/static/skins/colibris/src/pad-editor.css
+++ b/src/static/skins/colibris/src/pad-editor.css
@@ -1,43 +1,34 @@
-#innerdocbody, #padcontent {
+#innerdocbody {
font-size: 15px;
line-height: 25px;
padding: 0;
+ background: transparent;
+ color: #485365;
+ color: var(--text-color);
}
-#innerdocbody span, #padcontent span {
+#innerdocbody span, #innerdocbody span {
padding: 4px 0 !important;
}
-#innerdocbody h1 span, #padcontent h1 span {
+#innerdocbody h1 span, #innerdocbody h1 span {
padding: 0;
}
-body {
- overflow: hidden;
-}
-
option {
text-transform: capitalize;
}
-h1 {
+#innerdocbody h1 {
font-size: 2.5em !important;
}
-h3 {
+#innerdocbody h3 {
font-size: 1.15em;
letter-spacing: 1px;
}
-a {
- color: #3f51b5;
-}
-
-h1, h2, h3, h4, h5, h6 {
- line-height: 120%;
-}
-
-
-
-
-
+#innerdocbody a {
+ color: #64d29b;
+ color: var(--primary-color);
+}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/pad-variants.css b/src/static/skins/colibris/src/pad-variants.css
new file mode 100644
index 00000000000..212acdfcf9d
--- /dev/null
+++ b/src/static/skins/colibris/src/pad-variants.css
@@ -0,0 +1,228 @@
+/* =========================== */
+/* === Super Light Toolbar === */
+/* =========================== */
+.super-light-toolbar .toolbar, .super-light-toolbar .popup-content {
+ --text-color: var(--super-dark-color);
+ --text-soft-color: var(--dark-color);
+ --border-color: #e4e6e9;
+ --bg-soft-color: var(--light-color);
+ --bg-color: var(--super-light-color);
+}
+/* ===================== */
+/* === Light Toolbar === */
+/* ===================== */
+.light-toolbar .toolbar, .light-toolbar .popup-content {
+ --text-color: var(--super-dark-color);
+ --text-soft-color: var(--dark-color);
+ --border-color: var(--middle-color);
+ --bg-soft-color: var(--super-light-color);
+ --bg-color: var(--light-color);
+}
+/* ========================== */
+/* === Super Dark Toolbar === */
+/* ========================== */
+.super-dark-toolbar .toolbar, .super-dark-toolbar .popup-content {
+ --text-color: var(--super-light-color);
+ --text-soft-color: var(--light-color);
+ --border-color: var(--dark-color);
+ --bg-soft-color: var(--dark-color);
+ --bg-color: var(--super-dark-color);
+}
+.super-dark-toolbar.super-dark-editor .popup-content {
+ border: 1px solid var(--dark-color);
+ box-shadow: none;
+}
+/* ==================== */
+/* === Dark Toolbar === */
+/* ==================== */
+.dark-toolbar .toolbar, .dark-toolbar .popup-content {
+ --text-color: var(--super-light-color);
+ --text-soft-color: var(--light-color);
+ --border-color: var(--super-dark-color);
+ --bg-soft-color: var(--super-dark-color);
+ --bg-color: var(--dark-color);
+}
+
+
+
+
+
+/* ============================ */
+/* == Super Light Background == */
+/* ============================ */
+.super-light-background #editorcontainerbox, .super-light-background #sidediv,
+.super-light-background #chatbox, .super-light-background #outerdocbody, .super-light-background {
+ --text-color: var(--super-dark-color);
+ --text-soft-color: var(--dark-color);
+ --border-color: #e4e6e9;
+ --bg-soft-color: var(--light-color);
+ --bg-color: var(--super-light-color);
+}
+.super-light-background body, .full-width-editor.super-light-editor body:not(.comments-active) {
+ --scrollbar-bg: var(--super-light-color);
+ --scrollbar-track: var(--light-color);
+ --scrollbar-thumb: var(--dark-color);
+}
+.super-light-background .compact-display-content {
+ background-color: var(--super-light-color);
+}
+/* ====================== */
+/* == Light Background == */
+/* ====================== */
+.light-background #editorcontainerbox, .light-background #sidediv,
+.light-background #chatbox, .light-background #outerdocbody, .light-background {
+ --text-color: var(--super-dark-color);
+ --text-soft-color: var(--dark-color);
+ --border-color: var(--middle-color);
+ --bg-soft-color: var(--super-light-color);
+ --bg-color: var(--light-color);
+}
+.light-background body, .full-width-editor.light-editor body:not(.comments-active) {
+ --scrollbar-bg: var(--light-color);
+ --scrollbar-track: var(--super-light-color);
+ --scrollbar-thumb: var(--dark-color);
+}
+.light-background .compact-display-content {
+ background-color: var(--light-color);
+}
+/* =========================== */
+/* == Super Dark Background == */
+/* =========================== */
+.super-dark-background #editorcontainerbox, .super-dark-background #sidediv,
+.super-dark-background #chatbox, .super-dark-background #outerdocbody, .super-dark-background {
+ --text-color: var(--super-light-color);
+ --text-soft-color: var(--light-color);
+ --border-color: var(--dark-color);
+ --bg-soft-color: var(--dark-color);
+ --bg-color: var(--super-dark-color);
+}
+.super-dark-background body, .full-width-editor.super-dark-editor body:not(.comments-active) {
+ --scrollbar-bg: var(--super-dark-color);
+ --scrollbar-track: var(--dark-color);
+ --scrollbar-thumb: var(--light-color);
+}
+.super-dark-background .compact-display-content {
+ background-color: var(--super-dark-color);
+}
+/* Special combinaison with toolbar */
+.super-dark-background.super-dark-toolbar .popup-content {
+ border: 1px solid var(--dark-color);
+ box-shadow: none;
+}
+/* ===================== */
+/* == Dark Background == */
+/* ===================== */
+.dark-background #editorcontainerbox, .dark-background #sidediv,
+.dark-background #chatbox, .dark-background #outerdocbody, .dark-background {
+ --text-color: var(--super-light-color);
+ --text-soft-color: var(--light-color);
+ --border-color: var(--super-dark-color);
+ --bg-soft-color: var(--super-dark-color);
+ --bg-color: var(--dark-color);
+}
+.dark-background body, .full-width-editor.dark-editor body:not(.comments-active) {
+ --scrollbar-bg: var(--dark-color);
+ --scrollbar-track: var(--super-dark-color);
+ --scrollbar-thumb: var(--light-color);
+}
+.dark-background .compact-display-content {
+ background-color: var(--dark-color);
+}
+/* Special combinaison with toolbar */
+.dark-background.dark-toolbar .popup-content, .dark-editor.dark-toolbar .popup-content {
+ box-shadow: 0 0 14px 0px var(--super-dark-color);
+}
+
+
+
+
+
+/* ======================== */
+/* == Super Light Editor == */
+/* ======================== */
+.super-light-editor #outerdocbody iframe, .super-light-editor #outerdocbody > #innerdocbody {
+ --bg-color: var(--super-light-color);
+}
+.super-light-editor #innerdocbody {
+ --text-color: var(--super-dark-color);
+}
+/* ================== */
+/* == Light Editor == */
+/* ================== */
+.light-editor #outerdocbody iframe, .light-editor #outerdocbody > #innerdocbody {
+ --bg-color: var(--light-color);
+}
+.light-editor #innerdocbody {
+ --text-color: var(--super-dark-color);
+}
+/* ======================= */
+/* == Super Dark Editor == */
+/* ======================= */
+.super-dark-editor #outerdocbody iframe, .super-dark-editor #outerdocbody > #innerdocbody {
+ --bg-color: var(--super-dark-color);
+}
+.super-dark-editor #innerdocbody {
+ --text-color: var(--super-light-color);
+}
+/* ================= */
+/* == Dark Editor == */
+/* ================= */
+.dark-editor #outerdocbody iframe, .dark-editor #outerdocbody > #innerdocbody {
+ --bg-color: var(--dark-color);
+}
+.dark-editor #innerdocbody {
+ --text-color: var(--super-light-color);
+}
+
+
+/* ======================================== */
+/* == Combinaison with background/editor == */
+/* ======================================== */
+.super-light-editor.super-light-background #outerdocbody,
+.light-editor.light-background #outerdocbody,
+.super-dark-editor.super-dark-background #outerdocbody,
+.dark-editor.dark-background #outerdocbody {
+ padding-top: 0;
+}
+@media (min-width: 1001px) {
+ .super-light-editor.super-light-background,
+ .light-editor.light-background,
+ .super-dark-editor.super-dark-background,
+ .dark-editor.dark-background {
+ --editor-horizontal-padding: 35px;
+ --editor-vertical-padding: 20px;
+ }
+}
+
+/* ===================================== */
+/* == Combinaison with toolbar/editor == */
+/* ===================================== */
+.super-light-editor.super-light-toolbar .toolbar,
+.light-editor.light-toolbar .toolbar,
+.super-dark-editor.super-dark-toolbar .toolbar,
+.dark-editor.dark-toolbar .toolbar {
+ --toolbar-border: 1px solid var(--border-color);
+}
+
+
+/* ======================= */
+/* == Full Width Editor == */
+/* ======================= */
+.full-width-editor #outerdocbody iframe, .full-width-editor #outerdocbody > #innerdocbody {
+ max-width: none !important;
+ border-radius: 0;
+}
+.full-width-editor #outerdocbody {
+ padding: 0;
+ margin: 0;
+}
+@media (min-width: 1001px) {
+ .full-width-editor {
+ --editor-horizontal-padding: 35px !important;
+ --editor-vertical-padding: 20px !important;
+ }
+}
+.full-width-editor ::-webkit-scrollbar-track,
+.full-width-editor ::-webkit-scrollbar-thumb {
+ border-radius: 0px;
+}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/plugins/brightcolorpicker.css b/src/static/skins/colibris/src/plugins/brightcolorpicker.css
index 32480cc9c5f..8db2880c12c 100644
--- a/src/static/skins/colibris/src/plugins/brightcolorpicker.css
+++ b/src/static/skins/colibris/src/plugins/brightcolorpicker.css
@@ -1,13 +1,7 @@
-#colorpicker {
- left: -200px !important;
- top: 0px !important;
-}
-
#colorpicker a.brightColorPicker-cancelButton {
background: none;
padding: 0;
padding-top: 10px;
- font-family: Arial, sans-serif;
font-weight: bold;
border: none;
}
diff --git a/src/static/skins/colibris/src/plugins/comments.css b/src/static/skins/colibris/src/plugins/comments.css
new file mode 100644
index 00000000000..005890ef484
--- /dev/null
+++ b/src/static/skins/colibris/src/plugins/comments.css
@@ -0,0 +1,112 @@
+.sidebar-comment .btn {
+ margin-top: 10px;
+ padding: 3px 8px;
+ font-size: .9rem;
+ margin: 10px 0 5px 0;
+}
+.sidebar-comment .btn.btn-primary:not(#comment-create-btn) {
+ background-color: #576273;
+ background-color: var(--text-soft-color);
+}
+.sidebar-comment .suggestion-create {
+ margin-top: 10px;
+}
+.suggestion-display .from-value, .suggestion-display .to-value {
+ color: #64d29b;
+ color: var(--primary-color);
+ font-weight: bold;
+ opacity: 1;
+}
+.suggestion-display .from-value {
+ margin-right: 5px;
+}
+.comment-actions-wrapper .buttonicon {
+ opacity: .8;
+}
+.comment-actions-wrapper .buttonicon:hover {
+ opacity: 1;
+}
+.comment-actions-wrapper .comment-edit {
+ margin-right: 5px;
+}
+[type="checkbox"] + label.label-suggestion-checkbox {
+ margin-left: 5px;
+ padding-left: 2.4rem;
+}
+.sidebar-comment .full-display-content {
+ margin-left: -10px;
+ box-shadow: none;
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+ border: 1px solid #ffffff;
+ border: 1px solid var(--bg-color);
+}
+.comment-reply {
+ border-top: 1px solid #ffffff;
+ border-top: 1px solid var(--bg-color);
+ background-color: inherit;
+}
+.comment-reply textarea, .comment-reply input[type="text"] {
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+}
+.btn.revert-suggestion-btn {
+ padding-left: 0;
+}
+.comment-edit-form {
+ margin-top: 15px;
+}
+
+/* MODAL */
+.comment-modal .full-display-content {
+ box-shadow: none;
+ margin: 0 !important;
+ border: none;
+ background-color: #ffffff;
+ background-color: var(--bg-color);
+}
+.comment-modal .comment-modal-comment {
+ padding: 0;
+}
+.comment-modal .comment-reply textarea, .comment-modal .comment-reply input[type="text"] {
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+}
+.comment-modal .comment-reply {
+ border-top: 1px solid #f2f3f4;
+ border-top: 1px solid var(--bg-soft-color);
+}
+.comment-modal .full-display-content .comment-title-wrapper,
+.comment-modal .full-display-content .comment-reply {
+ padding: 15px;
+}
+
+
+/* NEW COMMENT POPUP */
+.new-comment-popup textarea {
+ background-color: #f2f3f4;
+ background-color: var(--bg-soft-color);
+}
+.new-comment-popup .suggestion {
+ margin-bottom: 10px;
+}
+
+
+/* EDITOR COMMENTEED LINE */
+#innerdocbody .ace-line .comment {
+ background-color: #fffacc;
+ color: var(--super-dark-color);
+}
+
+
+@media (min-width: 1200px) {
+ #comments {
+ width: 300px;
+ }
+ .sidebar-comment .full-display-content {
+ margin-left: 10px;
+ }
+ .compact-display-content {
+ padding-left: 20px;
+ }
+}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/plugins/comments_page.css b/src/static/skins/colibris/src/plugins/comments_page.css
deleted file mode 100644
index f11fa61c8a4..00000000000
--- a/src/static/skins/colibris/src/plugins/comments_page.css
+++ /dev/null
@@ -1,290 +0,0 @@
-.comment.selected {
- color: #a28239;
-}
-
-#newComment {
- box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
- border: none;
- padding: 15px;
-}
-
-p[data-l10n-id="ep_comments_page.comment"] {
- font-weight: 900;
- font-size: 16px;
- text-transform: uppercase;
- margin-bottom: 10px;
-}
-
-#newComment.sidebar-comment textarea:not(.comment-suggest-from) {
- border: 1px solid #d2d2d2;
- border-radius: 3px;
- padding: 8px 10px;
- height: 80px;
- padding: 8px;
- font-size: 14px;
- width: calc(100% - 15px);
-}
-
-#newComment.sidebar-comment .comment-suggest-from {
- height: auto !important;
- font-size: 14px;
- padding: 8px;
- width: calc(100% - 15px) !important;
- background-color: #f9f9f9;
- margin-top: 8px;
- border: 1px solid #f3eeee;
- border-left: 4px solid #a7a7a7;
- border-radius: 3px;
-}
-
-.comment-suggest {
- margin-bottom: 8px;
-}
-
-.reply-comment-suggest input,
-.comment-suggest input {
- vertical-align: -2px;
- margin-right: 5px;
-}
-
-.reply-comment-suggest label,
-.comment-suggest label {
- font-style: italic;
- font-weight: normal;
-}
-
-.sidebar-comment input,
-.comment-buttons input {
- border-radius: 4px;
- text-transform: uppercase;
- font-weight: 900;
- font-size: 13px;
- line-height: 1.5;
- width: auto;
- border: none;
-}
-
-#newComment.sidebar-comment input[type=submit] {
- color: white;
- margin-right: 10px;
- padding: 0 10px;
- border: none;
-}
-
-#newComment.sidebar-comment input[type=reset] {
- background-color: white;
- border: none;
-}
-
-.comment-content:focus {
- border: 2px solid rgba(33, 150, 243, 0.51);
-}
-
-.comment-changeTo-approve input[type=submit] { width: 100%; font-size: 12px;}
-
-.reply-comment-suggest-from-p,
-.comment-changeFrom-value,
-.comment-changeTo-value,
-.reply-suggestion .reply-comment-suggest-from,
-.suggestion .comment-suggest-from {
- border: none;
- font-weight: normal;
- font-style: italic;
- padding-left: 0;
- width: 100% !important;
- padding: 0;
- padding-top: 8px;
- /* width: inherit !important; */
- /*color: rgba(41, 125, 191, 0.85);*/
-}
-
-textarea.comment-suggest-to {
- margin-bottom: 8px;
-}
-
-.suggestion {
- font-weight: bold;
- font-size: 11px;
- text-transform: uppercase;
-}
-
-#comments {
- top: 82px;
-}
-
-#comments .sidebar-comment {
- background-color: transparent;
- font-size: 13px;
- border: none;
- box-shadow: none;
-}
-
-@media (min-width: 1200px) {
- #comments .sidebar-comment:hover,
- #comments .sidebar-comment.mouseover,
- .comment-modal {
- margin-right: -30px;
- }
-}
-
-#comments .sidebar-comment:hover,
-#comments .sidebar-comment.mouseover,
-.comment-modal {
- background-color: #fff;
- padding: 0;
- padding-top: 10px;
- margin-top: -8px;
- width: 250px;
- box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
-}
-
-#comments .sidebar-comment:hover time,
-#comments .sidebar-comment.mouseover time,
-.comment-modal-comment>time {
- display: block !important;
-}
-
-#comments .sidebar-comment:hover>section,
-#comments .sidebar-comment.mouseover>section,
-.comment-modal-comment>section {
- padding: 0 15px;
- padding-bottom: 10px;
- margin-top: 5px;
- display: block;
- line-height: 20px;
- margin-top: 8px;
-}
-
-.comment-delete-container {
- float: right;
- margin: 0;
- height: 15px;
- font-size: 15px;
- background-color: transparent;
- padding: 6px 15px 4px 8px;
- color: #7b7777;
-}
-
-.comment-reply {
- border-top: 1px solid #d2d2d2;
- background-color: #f9f9f9;
- margin: 0;
- padding: 10px 15px 3px;
-}
-
-.comment-reply-input-label span {
- font-weight: bold;
-}
-
-.comment-reply-input {
- border: 1px solid #d2d2d2 !important;
- width: calc(100% - 20px) !important;
- padding: 8px 10px;
- text-transform: none !important;
- margin-top: 5px;
- margin-bottom: 10px;
- font-weight: normal;
-}
-
-.reply-suggestion p:not(.reply-comment-suggest-from-p) {
- text-transform: uppercase;
- font-size: 11px;
- font-weight: 400;
- text-transform: uppercase;
-}
-
-.comment-changeTo-approve input {
- height: 25px;
- margin: 0;
- margin-top: 5px;
-}
-
-.reply-suggestion {
- margin-top: 8px;
-}
-
-.reply-comment-suggest-from-p {
- padding: 0
-}
-
-.comment-text {
- font-weight: normal;
- line-height: 20px;
-}
-
-.sidebar-comment-reply:nth-child(even) {
- background-color: transparent;
-}
-
-.sidebar-comment-reply {
- padding: 0;
- margin-bottom: 10px;
-}
-
-.comment-author-name {
- font-style: normal;
-}
-
-.comment-changeFrom-label,
-.comment-changeTo-label {
- text-transform: uppercase;
- font-size: 11px;
-}
-
-.comment-reply note {
- line-height: 16px;
-}
-
-.reply-comment-suggest {
- display: none !important
-}
-
-.comment-changeTo-form {
- margin-bottom: 5px;
- padding: 0 15px;
-}
-
-.sidebar-comment>.comment-author-name,
-.comment-modal-comment>.comment-author-name {
- padding-left: 0px;
-}
-
-#comments .sidebar-comment:hover >.comment-author-name,
-#comments .sidebar-comment.mouseover >.comment-author-name {
- margin-top: 2px;
- display: inline-block;
- padding-left: 15px;
-}
-
-.sidebar-comment>time,
-.comment-modal-comment>time {
- position: absolute;
- top: 11px;
- font-size: 11px;
- right: 36px;
- color: #555555;
- font-style: italic;
-}
-
-.comment-changeTo-approve {
- margin-bottom: 14px;
- margin-top: 5px;
-}
-
-.comment-reply note:first-child {
- margin-top: 8px;
-}
-
-.comment-modal {
- padding: 0;
- border: none;
- width: 350px !important;
- margin-top: 0;
- padding-top: 10px;
-}
-
-.comment-modal-comment>.comment-author-name {
- padding-left: 14px;
-}
-
-
diff --git a/src/static/skins/colibris/src/plugins/cursortrace.css b/src/static/skins/colibris/src/plugins/cursortrace.css
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/src/static/skins/colibris/src/plugins/embedded_hyperlinks.css b/src/static/skins/colibris/src/plugins/embedded_hyperlinks.css
deleted file mode 100644
index b5ec7e61cb8..00000000000
--- a/src/static/skins/colibris/src/plugins/embedded_hyperlinks.css
+++ /dev/null
@@ -1,6 +0,0 @@
-.hyperlink-dialog>.hyperlink-save{
- height: 34px;
- margin-top: 1px;
- margin-right: 0;
- margin-left: 2px;
-}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/plugins/font_color.css b/src/static/skins/colibris/src/plugins/font_color.css
index 4ecd5c1cd01..95f81cf31a2 100644
--- a/src/static/skins/colibris/src/plugins/font_color.css
+++ b/src/static/skins/colibris/src/plugins/font_color.css
@@ -1,32 +1,38 @@
-li.acl-write.font-color-icon.plugin-ep_font_color {
- display: none;
+.font-color-icon {
+ display: none !important;
}
#font-color {
display: list-item !important;
}
+.color\:black,
+[data-color=black] {
+ color: #485365;
+ color: var(--text-color);
+}
+
.color\:red,
[data-color=red] {
- color: #F44336;
+ color: #F44336;
}
.color\:green,
[data-color=green] {
- color: #66d29c;
+ color: #66d29c;
}
.color\:blue,
[data-color=blue] {
- color: #2196f3;
+ color: #2196f3;
}
.color\:yellow,
[data-color=yellow] {
- color: #e0d776;
+ color: #ffeb3b;
}
.color\:orange,
[data-color=orange] {
- color: #d2a564;
+ color: #FF9800;
}
\ No newline at end of file
diff --git a/src/static/skins/colibris/src/plugins/set_title_on_pad.css b/src/static/skins/colibris/src/plugins/set_title_on_pad.css
deleted file mode 100644
index 94b19520318..00000000000
--- a/src/static/skins/colibris/src/plugins/set_title_on_pad.css
+++ /dev/null
@@ -1,34 +0,0 @@
-#pad_title {
- margin-bottom: 15px !important;
- margin-top: 5px !important;
- display: none; /* display only when page is loaded via javascript */
-}
-
-@media (max-width:720px) {
- #pad_title { display: none !important; }
-}
-
-#edit_title {
- color: white;
-}
-#edit_title:before {
- font-family: fontawesome-etherpad;
- position: absolute;
- top: 20px;
- font-size: 14px;
- content: "\E839";
- margin-left: 10px;
-}
-
-#input_title {
- background-color: #f9f9f9 !important;
- height: auto !important;
- margin-top: 3px;
- width: calc(100% - 110px) !important;
- padding: 8px 10px !important;
-}
-
-#save_title button {
- height: 30px !important;
- padding: 5px 20px !important;
-}
\ No newline at end of file
diff --git a/src/static/skins/colibris/timeslider.css b/src/static/skins/colibris/timeslider.css
index ba5199baa97..b7b9f3e1045 100644
--- a/src/static/skins/colibris/timeslider.css
+++ b/src/static/skins/colibris/timeslider.css
@@ -1,142 +1,98 @@
-@media (max-width: 600px) { html { overflow: hidden } }
-
-@media (max-width: 1100px) {
- .timeslider #padeditor {
- padding: 0 !important;
- }
-}
-
-.timeslider #import_export, .timeslider #settings{
- top: 108px !important;
- right: 55px;
-}
-
-.timeslider #export > p {
- font-size: 15px;
- margin-top: 0;
- margin-bottom: 20px;
+#timeslider-slider #ui-slider-handle {
+ border-radius: 3px;
+ width: 12px;
+ height: 28px;
+ background-color: #64d29b;
+ background-color: var(--primary-color);
}
-.timeslider #padpage {
- display: flex;
- flex-direction: column;
+#timeslider-slider #ui-slider-bar {
+ border-radius: 3px;
+ background-color: #d2d2d2;
+ background-color: var(--border-color);
}
-
-.timeslider #timeslider-top {
- position: relative;
- border-bottom: 1px solid #e4e4e4;
+#slider-btn-container {
+ margin: -18px 15px 0 20px;
}
-
-.timeslider-bar { background: none; }
-.timeslider-bar p { margin: 8px; font-size: 12px;}
-
-.timeslider-bar #editbar {
+#slider-btn-container #playpause_button_icon {
+ color: #ffffff;
+ color: var(--bg-color);
+ background-color: #64d29b;
+ background-color: var(--primary-color);
border: none;
- background: none !important;
- margin-right: 10px;
-}
-
-.timeslider #padmain {
- position: relative;
- top: 0 !important;
- flex: 1 auto;
- overflow: auto;
-}
-
-.timeslider #padeditor {
- height: 100%;
- padding-top: 30px;
-}
-
-.timeslider #editorcontainerbox {
- height: 100%;
- overflow: visible;
- margin-top: 0 !important;
+ margin-right: 5px;
+ padding-top: 3px;
+ width: 45px;
+ height: 45px;
+}
+#slider-btn-container #playpause_button_icon:not(.pause) {
+ padding-left: 4px;
+}
+#slider-btn-container .stepper {
+ border: 2px solid !important;
+ height: 30px;
+ width: 30px;
+ line-height: 28px;
+ margin-left: 5px;
+ font-size: 13px;
+ color: #64d29b;
+ color: var(--primary-color);
+ border-color: #64d29b;
+ border-color: var(--primary-color);
}
-
-#timeslider {
- margin-top: -20px;
- margin-left: 0;
- background-color: transparent;
+#slider-btn-container .stepper.disabled {
+ opacity: .5;
}
-#timeslider, #timeslider-left, #timeslider-right {
- height: 57px;
- background-color: transparent;
-}
-
-#timeslider #timer {
- opacity: 0.8;
- font-style: italic;
- right: 158px;
- top: -3px;
- left: initial;
- background: none;
+.timeslider #editbar .buttontext {
+ background-color: #576273;
+ background-color: var(--text-soft-color);
+ color: #ffffff;
+ color: var(--bg-color);
+ margin: 0;
}
-#timeslider #timeslider-slider { margin-left: 4px; }
-
-#ui-slider-handle {
- z-index: 5;
- border-radius: 3px;
- height: 28px;
- top: 19px;
+#editbar {
+ display: block;
+ padding-bottom: 5px;
}
-#ui-slider-bar {
- height: 10px;
- margin-top: 28px;
- margin-right: 180px;
+#editbar li > a {
border-radius: 3px;
- background-color: #e4e4e4;
-}
-
-#timeslider .star {
- top: 25px;
-}
-#timeslider .star:before {
- color: #da9700;
- content: "\e836";
}
-.timeslider #editbar .buttontext {
- color: white;
- margin: 0;
-}
-.timeslider #editbar .grouped-right {
- margin: 0; padding: 0;
- margin-top: 5px;
- margin-left: 5px;
+#timeslider-slider #timer {
+ opacity: .7;
+ top: -12px;
+ font-size: .8em;
}
-.timeslider #playpause_button {
- right: 95px;
- top: 1px;
-}
-#playpause_button_icon {
- border:none;
+.timeslider #authorsList .author {
+ padding: 2px 5px;
+ border-radius: 3px;
+ margin-right: 4px;
+ margin-bottom: 4px;
}
-#playpause_button_icon:before { color: white; }
-.timeslider #leftstep {
- right: 60px;
- top: 12px;
+.timeslider-title {
+ font-size: 1.8rem !important;
}
-
-.timeslider #rightstep {
- top: 12px;
- right: 30px;
+.timeslider-subtitle {
+ margin-top: 6px;
+ font-size: .9em;
}
-.stepper {
- border: 2px solid;
- text-align: center;
- border-radius: 50%;
- height: 25px;
- padding-top: 2px;
-}
+@media (max-width: 720px) {
-.timeslider #authorsList .author {
- padding: 2px 5px;
- border-radius: 3px;
+ #slider-btn-container {
+ margin-top: 0;
+ margin-right: 5px;
+ }
+ #slider-btn-container #playpause_button_icon {
+ width: 30px;
+ height: 30px;
+ }
+ #slider-btn-container #playpause_button_icon:before {
+ font-size: 18px;
+ }
}
\ No newline at end of file
diff --git a/src/static/skins/colibris/timeslider.js b/src/static/skins/colibris/timeslider.js
index c94a55778df..1322ec4c8ef 100644
--- a/src/static/skins/colibris/timeslider.js
+++ b/src/static/skins/colibris/timeslider.js
@@ -1,6 +1,3 @@
function customStart()
{
- console.log("custom start", $('#timeslider-wrapper').length);
- // inverse display order betwwen slidebar and titles
- $('#timeslider-wrapper').appendTo('#timeslider-top');
}
diff --git a/src/static/skins/no-skin/pad.css b/src/static/skins/no-skin/pad.css
index b8cdb4642b6..e69de29bb2d 100644
--- a/src/static/skins/no-skin/pad.css
+++ b/src/static/skins/no-skin/pad.css
@@ -1,5 +0,0 @@
-@media (max-width:600px) {
- #sidediv {
- display: none !important;
- }
-}
\ No newline at end of file
diff --git a/src/templates/pad.html b/src/templates/pad.html
index 34bcf05997d..45f9a7d3dab 100644
--- a/src/templates/pad.html
+++ b/src/templates/pad.html
@@ -5,346 +5,377 @@
;
%>
+
<% e.begin_block("htmlHead"); %>
-
+
<% e.end_block(); %>
-
<%=settings.title%>
-
+ |@licend The above is the entire license notice
+ for the JavaScript code in this page.|
+ */
+
-
-
-
-
-
+
+
+
+
+
- <% e.begin_block("styles"); %>
-
+ <% e.begin_block("styles"); %>
+
- <% e.begin_block("customStyles"); %>
-
- <% e.end_block(); %>
+ <% e.begin_block("customStyles"); %>
+
+ <% e.end_block(); %>
-
- <% e.end_block(); %>
+
+ <% e.end_block(); %>
-
-
-
+
+
+
-
+
- <% e.begin_block("body"); %>
-
- <% e.begin_block("afterEditbar"); %><% e.end_block(); %>
+
+
+
+
+
+ <% e.begin_block("afterEditbar"); %><% e.end_block(); %>
+
+
+
+ <% e.begin_block("editorContainerBox"); %>
-
+
+
+
+
+
+
+
+
+
You need a password to access this pad
+
+
+
+
You do not have permission to access this pad
+
+
+
Your password was wrong
+
+
+
Cookie could not be found. Please allow cookies in your browser!
+
+ <% e.begin_block("loading"); %>
+
Loading...
+ <% e.end_block(); %>
+
Sorry, you have to enable Javascript in order to use this.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
+
0
+
+
+
+
+ <% if (settings.skinName == 'colibris') { %>
+
+ <% } %>
+
+ <% e.end_block(); %>
+
+
+
+ <% e.end_block(); %>
+
+
+
+
+
+
+ <% e.begin_block("scripts"); %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <% e.begin_block("customScripts"); %>
+
+ <% e.end_block(); %>
+
+
+
+
+ <% e.end_block(); %>
diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html
index 60194af12da..2368f0ea1fd 100644
--- a/src/templates/timeslider.html
+++ b/src/templates/timeslider.html
@@ -3,7 +3,7 @@
, langs = require("ep_etherpad-lite/node/hooks/i18n").availableLangs
%>
-
+
<%=settings.title%> Timeslider
@@ -273,5 +297,4 @@
<% e.end_block(); %>
-
diff --git a/tests/backend/loadSettings.js b/tests/backend/loadSettings.js
deleted file mode 100644
index f208fe3d04e..00000000000
--- a/tests/backend/loadSettings.js
+++ /dev/null
@@ -1,17 +0,0 @@
-var jsonminify = require(__dirname+"/../../src/node_modules/jsonminify");
-
-function loadSettings(){
- var settingsStr = fs.readFileSync(__dirname+"/../../settings.json").toString();
- // try to parse the settings
- var settings;
- try {
- if(settingsStr) {
- settingsStr = jsonminify(settingsStr).replace(",]","]").replace(",}","}");
- return JSON.parse(settingsStr);
- }
- }catch(e){
- console.error("whoops something is bad with settings");
- }
-}
-
-exports.loadSettings = loadSettings;
diff --git a/tests/backend/specs/api/api.js b/tests/backend/specs/api/api.js
new file mode 100644
index 00000000000..0b372de852b
--- /dev/null
+++ b/tests/backend/specs/api/api.js
@@ -0,0 +1,79 @@
+/**
+ * API specs
+ *
+ * Tests for generic overarching HTTP API related features not related to any
+ * specific part of the data model or domain. For example: tests for versioning
+ * and openapi definitions.
+ */
+
+const assert = require('assert');
+const supertest = require(__dirname + '/../../../../src/node_modules/supertest');
+const fs = require('fs');
+const settings = require(__dirname + '/../../../../src/node/utils/Settings');
+const api = supertest('http://' + settings.ip + ':' + settings.port);
+const path = require('path');
+
+var validateOpenAPI = require(__dirname + '/../../../../src/node_modules/openapi-schema-validation').validate;
+
+var filePath = path.join(__dirname, '../../../../APIKEY.txt');
+
+var apiKey = fs.readFileSync(filePath, { encoding: 'utf-8' });
+apiKey = apiKey.replace(/\n$/, '');
+var apiVersion = 1;
+
+var testPadId = makeid();
+
+describe('API Versioning', function() {
+ it('errors if can not connect', function(done) {
+ api
+ .get('/api/')
+ .expect(function(res) {
+ apiVersion = res.body.currentVersion;
+ if (!res.body.currentVersion) throw new Error('No version set in API');
+ return;
+ })
+ .expect(200, done);
+ });
+});
+
+describe('OpenAPI definition', function() {
+ it('generates valid openapi definition document', function(done) {
+ api
+ .get('/api/openapi.json')
+ .expect(function(res) {
+ const { valid, errors } = validateOpenAPI(res.body, 3);
+ if (!valid) {
+ const prettyErrors = JSON.stringify(errors, null, 2);
+ throw new Error(`Document is not valid OpenAPI. ${errors.length} validation errors:\n${prettyErrors}`);
+ }
+ return;
+ })
+ .expect(200, done);
+ });
+});
+
+describe('jsonp support', function() {
+ it('supports jsonp calls', function(done) {
+ api
+ .get(endPoint('createPad') + '&jsonp=jsonp_1&padID=' + testPadId)
+ .expect(function(res) {
+ if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen');
+ })
+ .expect('Content-Type', /javascript/)
+ .expect(200, done);
+ });
+});
+
+var endPoint = function(point) {
+ return '/api/' + apiVersion + '/' + point + '?apikey=' + apiKey;
+};
+
+function makeid() {
+ var text = '';
+ var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+ for (var i = 0; i < 5; i++) {
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
+ }
+ return text;
+}
diff --git a/tests/backend/specs/api/characterEncoding.js b/tests/backend/specs/api/characterEncoding.js
new file mode 100644
index 00000000000..83145089f49
--- /dev/null
+++ b/tests/backend/specs/api/characterEncoding.js
@@ -0,0 +1,113 @@
+/*
+ * This file is copied & modified from
/tests/backend/specs/api/pad.js
+ *
+ * TODO: maybe unify those two files and merge in a single one.
+ */
+
+const assert = require('assert');
+const supertest = require(__dirname+'/../../../../src/node_modules/supertest');
+const fs = require('fs');
+const settings = require(__dirname + '/../../../../src/node/utils/Settings');
+const api = supertest('http://'+settings.ip+":"+settings.port);
+const path = require('path');
+const async = require(__dirname+'/../../../../src/node_modules/async');
+
+var filePath = path.join(__dirname, '../../../../APIKEY.txt');
+
+var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
+apiKey = apiKey.replace(/\n$/, "");
+var apiVersion = 1;
+var testPadId = makeid();
+
+describe('Connectivity For Character Encoding', function(){
+ it('can connect', function(done) {
+ api.get('/api/')
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
+describe('API Versioning', function(){
+ it('finds the version tag', function(done) {
+ api.get('/api/')
+ .expect(function(res){
+ apiVersion = res.body.currentVersion;
+ if (!res.body.currentVersion) throw new Error("No version set in API");
+ return;
+ })
+ .expect(200, done)
+ });
+})
+
+describe('Permission', function(){
+ it('errors with invalid APIKey', function(done) {
+ // This is broken because Etherpad doesn't handle HTTP codes properly see #2343
+ // If your APIKey is password you deserve to fail all tests anyway
+ var permErrorURL = '/api/'+apiVersion+'/createPad?apikey=password&padID=test';
+ api.get(permErrorURL)
+ .expect(401, done)
+ });
+})
+
+describe('createPad', function(){
+ it('creates a new Pad', function(done) {
+ api.get(endPoint('createPad')+"&padID="+testPadId)
+ .expect(function(res){
+ if(res.body.code !== 0) throw new Error("Unable to create new Pad");
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
+describe('setHTML', function(){
+ it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function(done) {
+ fs.readFile('../tests/backend/specs/api/emojis.html', 'utf8', function(err, html) {
+ api.post(endPoint('setHTML'))
+ .send({
+ "padID": testPadId,
+ "html": html,
+ })
+ .expect(function(res){
+ if(res.body.code !== 0) throw new Error("Can't set HTML properly");
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done);
+ });
+ });
+})
+
+describe('getHTML', function(){
+ it('get the HTML of Pad with emojis', function(done) {
+ api.get(endPoint('getHTML')+"&padID="+testPadId)
+ .expect(function(res){
+ if (res.body.data.html.indexOf("🇼") === -1) {
+ throw new Error("Unable to get the HTML");
+ }
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
+/*
+
+ End of test
+
+*/
+
+var endPoint = function(point, version){
+ version = version || apiVersion;
+ return '/api/'+version+'/'+point+'?apikey='+apiKey;
+}
+
+function makeid()
+{
+ var text = "";
+ var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+ for( var i=0; i < 10; i++ ){
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
+ }
+ return text;
+}
diff --git a/tests/backend/specs/api/chat.js b/tests/backend/specs/api/chat.js
index 2bcd9783add..e2ce59b1c3b 100644
--- a/tests/backend/specs/api/chat.js
+++ b/tests/backend/specs/api/chat.js
@@ -1,7 +1,7 @@
var assert = require('assert')
supertest = require(__dirname+'/../../../../src/node_modules/supertest'),
fs = require('fs'),
- settings = require(__dirname+'/../../loadSettings').loadSettings(),
+ settings = require(__dirname + '/../../../../src/node/utils/Settings'),
api = supertest('http://'+settings.ip+":"+settings.port),
path = require('path');
diff --git a/tests/backend/specs/api/emojis.html b/tests/backend/specs/api/emojis.html
new file mode 100644
index 00000000000..a0e3d2752f6
--- /dev/null
+++ b/tests/backend/specs/api/emojis.html
@@ -0,0 +1,10 @@
+
+
+
+ foo
+
+
+
+😀 😁 😂 🤣 😃 😄 😅 😆 😉 😊 😋 😎 😍 😘 🥰 😗 😙 😚 ☺️ 🙂 🤗 🤩 🤔 🤨 😐 😑 😶 🙄 😏 😣 😥 😮 🤐 😯 😪 😫 😴 😌 😛 😜 😝 🤤 😒 😓 😔 😕 🙃 🤑 😲 ☹️ 🙁 😖 😞 😟 😤 😢 😭 😦 😧 😨 😩 🤯 😬 😰 😱 🥵 🥶 😳 🤪 😵 😡 😠 🤬 😷 🤒 🤕 🤢 🤮 🤧 😇 🤠 🤡 🥳 🥴 🥺 🤥 🤫 🤭 🧐 🤓 😈 👿 👹 👺 💀 👻 👽 🤖 💩 😺 😸 😹 😻 😼 😽 🙀 😿 😾 👶 👧 🧒 👦 👩 🧑 👨 👵 🧓 👴 👲 👳♀️ 👳♂️ 🧕 🧔 👱♂️ 👱♀️ 👨🦰 👩🦰 👨🦱 👩🦱 👨🦲 👩🦲 👨🦳 👩🦳 🦸♀️ 🦸♂️ 🦹♀️ 🦹♂️ 👮♀️ 👮♂️ 👷♀️ 👷♂️ 💂♀️ 💂♂️ 🕵️♀️ 🕵️♂️ 👩⚕️ 👨⚕️ 👩🌾 👨🌾 👩🍳 👨🍳 👩🎓 👨🎓 👩🎤 👨🎤 👩🏫 👨🏫 👩🏭 👨🏭 👩💻 👨💻 👩💼 👨💼 👩🔧 👨🔧 👩🔬 👨🔬 👩🎨 👨🎨 👩🚒 👨🚒 👩✈️ 👨✈️ 👩🚀 👨🚀 👩⚖️ 👨⚖️ 👰 🤵 👸 🤴 🤶 🎅 🧙♀️ 🧙♂️ 🧝♀️ 🧝♂️ 🧛♀️ 🧛♂️ 🧟♀️ 🧟♂️ 🧞♀️ 🧞♂️ 🧜♀️ 🧜♂️ 🧚♀️ 🧚♂️ 👼 🤰 🤱 🙇♀️ 🙇♂️ 💁♀️ 💁♂️ 🙅♀️ 🙅♂️ 🙆♀️ 🙆♂️ 🙋♀️ 🙋♂️ 🤦♀️ 🤦♂️ 🤷♀️ 🤷♂️ 🙎♀️ 🙎♂️ 🙍♀️ 🙍♂️ 💇♀️ 💇♂️ 💆♀️ 💆♂️ 🧖♀️ 🧖♂️ 💅 🤳 💃 🕺 👯♀️ 👯♂️ 🕴 🚶♀️ 🚶♂️ 🏃♀️ 🏃♂️ 👫 👭 👬 💑 👩❤️👩 👨❤️👨 💏 👩❤️💋👩 👨❤️💋👨 👪 👨👩👧 👨👩👧👦 👨👩👦👦 👨👩👧👧 👩👩👦 👩👩👧 👩👩👧👦 👩👩👦👦 👩👩👧👧 👨👨👦 👨👨👧 👨👨👧👦 👨👨👦👦 👨👨👧👧 👩👦 👩👧 👩👧👦 👩👦👦 👩👧👧 👨👦 👨👧 👨👧👦 👨👦👦 👨👧👧 🤲 👐 🙌 👏 🤝 👍 👎 👊 ✊ 🤛 🤜 🤞 ✌️ 🤟 🤘 👌 👈 👉 👆 👇 ☝️ ✋ 🤚 🖐 🖖 👋 🤙 💪 🦵 🦶 🖕 ✍️ 🙏 💍 💄 💋 👄 👅 👂 👃 👣 👁 👀 🧠 🦴 🦷 🗣 👤 👥🧥 👚 👕 👖 👔 👗 👙 👘 👠 👡 👢 👞 👟 🥾 🥿 🧦 🧤 🧣 🎩 🧢 👒 🎓 ⛑ 👑 👝 👛 👜 💼 🎒 👓 🕶 🥽 🥼 🌂 🧵 🧶👶🏻 👦🏻 👧🏻 👨🏻 👩🏻 👱🏻♀️ 👱🏻 👴🏻 👵🏻 👲🏻 👳🏻♀️ 👳🏻 👮🏻♀️ 👮🏻 👷🏻♀️ 👷🏻 💂🏻♀️ 💂🏻 🕵🏻♀️ 🕵🏻 👩🏻⚕️ 👨🏻⚕️ 👩🏻🌾 👨🏻🌾 👩🏻🍳 👨🏻🍳 👩🏻🎓 👨🏻🎓 👩🏻🎤 👨🏻🎤 👩🏻🏫 👨🏻🏫 👩🏻🏭 👨🏻🏭 👩🏻💻 👨🏻💻 👩🏻💼 👨🏻💼 👩🏻🔧 👨🏻🔧 👩🏻🔬 👨🏻🔬 👩🏻🎨 👨🏻🎨 👩🏻🚒 👨🏻🚒 👩🏻✈️ 👨🏻✈️ 👩🏻🚀 👨🏻🚀 👩🏻⚖️ 👨🏻⚖️ 🤶🏻 🎅🏻 👸🏻 🤴🏻 👰🏻 🤵🏻 👼🏻 🤰🏻 🙇🏻♀️ 🙇🏻 💁🏻 💁🏻♂️ 🙅🏻 🙅🏻♂️ 🙆🏻 🙆🏻♂️ 🙋🏻 🙋🏻♂️ 🤦🏻♀️ 🤦🏻♂️ 🤷🏻♀️ 🤷🏻♂️ 🙎🏻 🙎🏻♂️ 🙍🏻 🙍🏻♂️ 💇🏻 💇🏻♂️ 💆🏻 💆🏻♂️ 🕴🏻 💃🏻 🕺🏻 🚶🏻♀️ 🚶🏻 🏃🏻♀️ 🏃🏻 🤲🏻 👐🏻 🙌🏻 👏🏻 🙏🏻 👍🏻 👎🏻 👊🏻 ✊🏻 🤛🏻 🤜🏻 🤞🏻 ✌🏻 🤟🏻 🤘🏻 👌🏻 👈🏻 👉🏻 👆🏻 👇🏻 ☝🏻 ✋🏻 🤚🏻 🖐🏻 🖖🏻 👋🏻 🤙🏻 💪🏻 🖕🏻 ✍🏻 🤳🏻 💅🏻 👂🏻 👃🏻👶🏼 👦🏼 👧🏼 👨🏼 👩🏼 👱🏼♀️ 👱🏼 👴🏼 👵🏼 👲🏼 👳🏼♀️ 👳🏼 👮🏼♀️ 👮🏼 👷🏼♀️ 👷🏼 💂🏼♀️ 💂🏼 🕵🏼♀️ 🕵🏼 👩🏼⚕️ 👨🏼⚕️ 👩🏼🌾 👨🏼🌾 👩🏼🍳 👨🏼🍳 👩🏼🎓 👨🏼🎓 👩🏼🎤 👨🏼🎤 👩🏼🏫 👨🏼🏫 👩🏼🏭 👨🏼🏭 👩🏼💻 👨🏼💻 👩🏼💼 👨🏼💼 👩🏼🔧 👨🏼🔧 👩🏼🔬 👨🏼🔬 👩🏼🎨 👨🏼🎨 👩🏼🚒 👨🏼🚒 👩🏼✈️ 👨🏼✈️ 👩🏼🚀 👨🏼🚀 👩🏼⚖️ 👨🏼⚖️ 🤶🏼 🎅🏼 👸🏼 🤴🏼 👰🏼 🤵🏼 👼🏼 🤰🏼 🙇🏼♀️ 🙇🏼 💁🏼 💁🏼♂️ 🙅🏼 🙅🏼♂️ 🙆🏼 🙆🏼♂️ 🙋🏼 🙋🏼♂️ 🤦🏼♀️ 🤦🏼♂️ 🤷🏼♀️ 🤷🏼♂️ 🙎🏼 🙎🏼♂️ 🙍🏼 🙍🏼♂️ 💇🏼 💇🏼♂️ 💆🏼 💆🏼♂️ 🕴🏼 💃🏼 🕺🏼 🚶🏼♀️ 🚶🏼 🏃🏼♀️ 🏃🏼 🤲🏼 👐🏼 🙌🏼 👏🏼 🙏🏼 👍🏼 👎🏼 👊🏼 ✊🏼 🤛🏼 🤜🏼 🤞🏼 ✌🏼 🤟🏼 🤘🏼 👌🏼 👈🏼 👉🏼 👆🏼 👇🏼 ☝🏼 ✋🏼 🤚🏼 🖐🏼 🖖🏼 👋🏼 🤙🏼 💪🏼 🖕🏼 ✍🏼 🤳🏼 💅🏼 👂🏼 👃🏼👶🏽 👦🏽 👧🏽 👨🏽 👩🏽 👱🏽♀️ 👱🏽 👴🏽 👵🏽 👲🏽 👳🏽♀️ 👳🏽 👮🏽♀️ 👮🏽 👷🏽♀️ 👷🏽 💂🏽♀️ 💂🏽 🕵🏽♀️ 🕵🏽 👩🏽⚕️ 👨🏽⚕️ 👩🏽🌾 👨🏽🌾 👩🏽🍳 👨🏽🍳 👩🏽🎓 👨🏽🎓 👩🏽🎤 👨🏽🎤 👩🏽🏫 👨🏽🏫 👩🏽🏭 👨🏽🏭 👩🏽💻 👨🏽💻 👩🏽💼 👨🏽💼 👩🏽🔧 👨🏽🔧 👩🏽🔬 👨🏽🔬 👩🏽🎨 👨🏽🎨 👩🏽🚒 👨🏽🚒 👩🏽✈️ 👨🏽✈️ 👩🏽🚀 👨🏽🚀 👩🏽⚖️ 👨🏽⚖️ 🤶🏽 🎅🏽 👸🏽 🤴🏽 👰🏽 🤵🏽 👼🏽 🤰🏽 🙇🏽♀️ 🙇🏽 💁🏽 💁🏽♂️ 🙅🏽 🙅🏽♂️ 🙆🏽 🙆🏽♂️ 🙋🏽 🙋🏽♂️ 🤦🏽♀️ 🤦🏽♂️ 🤷🏽♀️ 🤷🏽♂️ 🙎🏽 🙎🏽♂️ 🙍🏽 🙍🏽♂️ 💇🏽 💇🏽♂️ 💆🏽 💆🏽♂️ 🕴🏼 💃🏽 🕺🏽 🚶🏽♀️ 🚶🏽 🏃🏽♀️ 🏃🏽 🤲🏽 👐🏽 🙌🏽 👏🏽 🙏🏽 👍🏽 👎🏽 👊🏽 ✊🏽 🤛🏽 🤜🏽 🤞🏽 ✌🏽 🤟🏽 🤘🏽 👌🏽 👈🏽 👉🏽 👆🏽 👇🏽 ☝🏽 ✋🏽 🤚🏽 🖐🏽 🖖🏽 👋🏽 🤙🏽 💪🏽 🖕🏽 ✍🏽 🤳🏽 💅🏽 👂🏽 👃🏽👶🏾 👦🏾 👧🏾 👨🏾 👩🏾 👱🏾♀️ 👱🏾 👴🏾 👵🏾 👲🏾 👳🏾♀️ 👳🏾 👮🏾♀️ 👮🏾 👷🏾♀️ 👷🏾 💂🏾♀️ 💂🏾 🕵🏾♀️ 🕵🏾 👩🏾⚕️ 👨🏾⚕️ 👩🏾🌾 👨🏾🌾 👩🏾🍳 👨🏾🍳 👩🏾🎓 👨🏾🎓 👩🏾🎤 👨🏾🎤 👩🏾🏫 👨🏾🏫 👩🏾🏭 👨🏾🏭 👩🏾💻 👨🏾💻 👩🏾💼 👨🏾💼 👩🏾🔧 👨🏾🔧 👩🏾🔬 👨🏾🔬 👩🏾🎨 👨🏾🎨 👩🏾🚒 👨🏾🚒 👩🏾✈️ 👨🏾✈️ 👩🏾🚀 👨🏾🚀 👩🏾⚖️ 👨🏾⚖️ 🤶🏾 🎅🏾 👸🏾 🤴🏾 👰🏾 🤵🏾 👼🏾 🤰🏾 🙇🏾♀️ 🙇🏾 💁🏾 💁🏾♂️ 🙅🏾 🙅🏾♂️ 🙆🏾 🙆🏾♂️ 🙋🏾 🙋🏾♂️ 🤦🏾♀️ 🤦🏾♂️ 🤷🏾♀️ 🤷🏾♂️ 🙎🏾 🙎🏾♂️ 🙍🏾 🙍🏾♂️ 💇🏾 💇🏾♂️ 💆🏾 💆🏾♂️ 🕴🏾 💃🏾 🕺🏾 🚶🏾♀️ 🚶🏾 🏃🏾♀️ 🏃🏾 🤲🏾 👐🏾 🙌🏾 👏🏾 🙏🏾 👍🏾 👎🏾 👊🏾 ✊🏾 🤛🏾 🤜🏾 🤞🏾 ✌🏾 🤟🏾 🤘🏾 👌🏾 👈🏾 👉🏾 👆🏾 👇🏾 ☝🏾 ✋🏾 🤚🏾 🖐🏾 🖖🏾 👋🏾 🤙🏾 💪🏾 🖕🏾 ✍🏾 🤳🏾 💅🏾 👂🏾 👃🏾👶🏿 👦🏿 👧🏿 👨🏿 👩🏿 👱🏿♀️ 👱🏿 👴🏿 👵🏿 👲🏿 👳🏿♀️ 👳🏿 👮🏿♀️ 👮🏿 👷🏿♀️ 👷🏿 💂🏿♀️ 💂🏿 🕵🏿♀️ 🕵🏿 👩🏿⚕️ 👨🏿⚕️ 👩🏿🌾 👨🏿🌾 👩🏿🍳 👨🏿🍳 👩🏿🎓 👨🏿🎓 👩🏿🎤 👨🏿🎤 👩🏿🏫 👨🏿🏫 👩🏿🏭 👨🏿🏭 👩🏿💻 👨🏿💻 👩🏿💼 👨🏿💼 👩🏿🔧 👨🏿🔧 👩🏿🔬 👨🏿🔬 👩🏿🎨 👨🏿🎨 👩🏿🚒 👨🏿🚒 👩🏿✈️ 👨🏿✈️ 👩🏿🚀 👨🏿🚀 👩🏿⚖️ 👨🏿⚖️ 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿♀️ 🙇🏿 💁🏿 💁🏿♂️ 🙅🏿 🙅🏿♂️ 🙆🏿 🙆🏿♂️ 🙋🏿 🙋🏿♂️ 🤦🏿♀️ 🤦🏿♂️ 🤷🏿♀️ 🤷🏿♂️ 🙎🏿 🙎🏿♂️ 🙍🏿 🙍🏿♂️ 💇🏿 💇🏿♂️ 💆🏿 💆🏿♂️ 🕴🏿 💃🏿 🕺🏿 🚶🏿♀️ 🚶🏿 🏃🏿♀️ 🏃🏿 🤲🏿 👐🏿 🙌🏿 👏🏿 🙏🏿 👍🏿 👎🏿 👊🏿 ✊🏿 🤛🏿 🤜🏿 🤞🏿 ✌🏿 🤟🏿 🤘🏿 👌🏿 👈🏿 👉🏿 👆🏿 👇🏿 ☝🏿 ✋🏿 🤚🏿 🖐🏿 🖖🏿 👋🏿 🤙🏿 💪🏿 🖕🏿 ✍🏿 🤳🏿 💅🏿 👂🏿 👃🏿🐶 🐱 🐭 🐹 🐰 🦊 🦝 🐻 🐼 🦘 🦡 🐨 🐯 🦁 🐮 🐷 🐽 🐸 🐵 🙈 🙉 🙊 🐒 🐔 🐧 🐦 🐤 🐣 🐥 🦆 🦢 🦅 🦉 🦚 🦜 🦇 🐺 🐗 🐴 🦄 🐝 🐛 🦋 🐌 🐚 🐞 🐜 🦗 🕷 🕸 🦂 🦟 🦠 🐢 🐍 🦎 🦖 🦕 🐙 🦑 🦐 🦀 🐡 🐠 🐟 🐬 🐳 🐋 🦈 🐊 🐅 🐆 🦓 🦍 🐘 🦏 🦛 🐪 🐫 🦙 🦒 🐃 🐂 🐄 🐎 🐖 🐏 🐑 🐐 🦌 🐕 🐩 🐈 🐓 🦃 🕊 🐇 🐁 🐀 🐿 🦔 🐾 🐉 🐲 🌵 🎄 🌲 🌳 🌴 🌱 🌿 ☘️ 🍀 🎍 🎋 🍃 🍂 🍁 🍄 🌾 💐 🌷 🌹 🥀 🌺 🌸 🌼 🌻 🌞 🌝 🌛 🌜 🌚 🌕 🌖 🌗 🌘 🌑 🌒 🌓 🌔 🌙 🌎 🌍 🌏 💫 ⭐️ 🌟 ✨ ⚡️ ☄️ 💥 🔥 🌪 🌈 ☀️ 🌤 ⛅️ 🌥 ☁️ 🌦 🌧 ⛈ 🌩 🌨 ❄️ ☃️ ⛄️ 🌬 💨 💧 💦 ☔️ ☂️ 🌊 🌫🍏 🍎 🍐 🍊 🍋 🍌 🍉 🍇 🍓 🍈 🍒 🍑 🍍 🥭 🥥 🥝 🍅 🍆 🥑 🥦 🥒 🥬 🌶 🌽 🥕 🥔 🍠 🥐 🍞 🥖 🥨 🥯 🧀 🥚 🍳 🥞 🥓 🥩 🍗 🍖 🌭 🍔 🍟 🍕 🥪 🥙 🌮 🌯 🥗 🥘 🥫 🍝 🍜 🍲 🍛 🍣 🍱 🥟 🍤 🍙 🍚 🍘 🍥 🥮 🥠 🍢 🍡 🍧 🍨 🍦 🥧 🍰 🎂 🍮 🍭 🍬 🍫 🍿 🧂 🍩 🍪 🌰 🥜 🍯 🥛 🍼 ☕️ 🍵 🥤 🍶 🍺 🍻 🥂 🍷 🥃 🍸 🍹 🍾 🥄 🍴 🍽 🥣 🥡 🥢⚽️ 🏀 🏈 ⚾️ 🥎 🏐 🏉 🎾 🥏 🎱 🏓 🏸 🥅 🏒 🏑 🥍 🏏 ⛳️ 🏹 🎣 🥊 🥋 🎽 ⛸ 🥌 🛷 🛹 🎿 ⛷ 🏂 🏋️♀️ 🏋🏻♀️ 🏋🏼♀️ 🏋🏽♀️ 🏋🏾♀️ 🏋🏿♀️ 🏋️♂️ 🏋🏻♂️ 🏋🏼♂️ 🏋🏽♂️ 🏋🏾♂️ 🏋🏿♂️ 🤼♀️ 🤼♂️ 🤸♀️ 🤸🏻♀️ 🤸🏼♀️ 🤸🏽♀️ 🤸🏾♀️ 🤸🏿♀️ 🤸♂️ 🤸🏻♂️ 🤸🏼♂️ 🤸🏽♂️ 🤸🏾♂️ 🤸🏿♂️ ⛹️♀️ ⛹🏻♀️ ⛹🏼♀️ ⛹🏽♀️ ⛹🏾♀️ ⛹🏿♀️ ⛹️♂️ ⛹🏻♂️ ⛹🏼♂️ ⛹🏽♂️ ⛹🏾♂️ ⛹🏿♂️ 🤺 🤾♀️ 🤾🏻♀️ 🤾🏼♀️ 🤾🏾♀️ 🤾🏾♀️ 🤾🏿♀️ 🤾♂️ 🤾🏻♂️ 🤾🏼♂️ 🤾🏽♂️ 🤾🏾♂️ 🤾🏿♂️ 🏌️♀️ 🏌🏻♀️ 🏌🏼♀️ 🏌🏽♀️ 🏌🏾♀️ 🏌🏿♀️ 🏌️♂️ 🏌🏻♂️ 🏌🏼♂️ 🏌🏽♂️ 🏌🏾♂️ 🏌🏿♂️ 🏇 🏇🏻 🏇🏼 🏇🏽 🏇🏾 🏇🏿 🧘♀️ 🧘🏻♀️ 🧘🏼♀️ 🧘🏽♀️ 🧘🏾♀️ 🧘🏿♀️ 🧘♂️ 🧘🏻♂️ 🧘🏼♂️ 🧘🏽♂️ 🧘🏾♂️ 🧘🏿♂️ 🏄♀️ 🏄🏻♀️ 🏄🏼♀️ 🏄🏽♀️ 🏄🏾♀️ 🏄🏿♀️ 🏄♂️ 🏄🏻♂️ 🏄🏼♂️ 🏄🏽♂️ 🏄🏾♂️ 🏄🏿♂️ 🏊♀️ 🏊🏻♀️ 🏊🏼♀️ 🏊🏽♀️ 🏊🏾♀️ 🏊🏿♀️ 🏊♂️ 🏊🏻♂️ 🏊🏼♂️ 🏊🏽♂️ 🏊🏾♂️ 🏊🏿♂️ 🤽♀️ 🤽🏻♀️ 🤽🏼♀️ 🤽🏽♀️ 🤽🏾♀️ 🤽🏿♀️ 🤽♂️ 🤽🏻♂️ 🤽🏼♂️ 🤽🏽♂️ 🤽🏾♂️ 🤽🏿♂️ 🚣♀️ 🚣🏻♀️ 🚣🏼♀️ 🚣🏽♀️ 🚣🏾♀️ 🚣🏿♀️ 🚣♂️ 🚣🏻♂️ 🚣🏼♂️ 🚣🏽♂️ 🚣🏾♂️ 🚣🏿♂️ 🧗♀️ 🧗🏻♀️ 🧗🏼♀️ 🧗🏽♀️ 🧗🏾♀️ 🧗🏿♀️ 🧗♂️ 🧗🏻♂️ 🧗🏼♂️ 🧗🏽♂️ 🧗🏾♂️ 🧗🏿♂️ 🚵♀️ 🚵🏻♀️ 🚵🏼♀️ 🚵🏽♀️ 🚵🏾♀️ 🚵🏿♀️ 🚵♂️ 🚵🏻♂️ 🚵🏼♂️ 🚵🏽♂️ 🚵🏾♂️ 🚵🏿♂️ 🚴♀️ 🚴🏻♀️ 🚴🏼♀️ 🚴🏽♀️ 🚴🏾♀️ 🚴🏿♀️ 🚴♂️ 🚴🏻♂️ 🚴🏼♂️ 🚴🏽♂️ 🚴🏾♂️ 🚴🏿♂️ 🏆 🥇 🥈 🥉 🏅 🎖 🏵 🎗 🎫 🎟 🎪 🤹♀️ 🤹🏻♀️ 🤹🏼♀️ 🤹🏽♀️ 🤹🏾♀️ 🤹🏿♀️ 🤹♂️ 🤹🏻♂️ 🤹🏼♂️ 🤹🏽♂️ 🤹🏾♂️ 🤹🏿♂️ 🎭 🎨 🎬 🎤 🎧 🎼 🎹 🥁 🎷 🎺 🎸 🎻 🎲 🧩 ♟ 🎯 🎳 🎮 🎰🚗 🚕 🚙 🚌 🚎 🏎 🚓 🚑 🚒 🚐 🚚 🚛 🚜 🛴 🚲 🛵 🏍 🚨 🚔 🚍 🚘 🚖 🚡 🚠 🚟 🚃 🚋 🚞 🚝 🚄 🚅 🚈 🚂 🚆 🚇 🚊 🚉 ✈️ 🛫 🛬 🛩 💺 🛰 🚀 🛸 🚁 🛶 ⛵️ 🚤 🛥 🛳 ⛴ 🚢 ⚓️ ⛽️ 🚧 🚦 🚥 🚏 🗺 🗿 🗽 🗼 🏰 🏯 🏟 🎡 🎢 🎠 ⛲️ ⛱ 🏖 🏝 🏜 🌋 ⛰ 🏔 🗻 🏕 ⛺️ 🏠 🏡 🏘 🏚 🏗 🏭 🏢 🏬 🏣 🏤 🏥 🏦 🏨 🏪 🏫 🏩 💒 🏛 ⛪️ 🕌 🕍 🕋 ⛩ 🛤 🛣 🗾 🎑 🏞 🌅 🌄 🌠 🎇 🎆 🌇 🌆 🏙 🌃 🌌 🌉 🌁⌚️ 📱 📲 💻 ⌨️ 🖥 🖨 🖱 🖲 🕹 🗜 💽 💾 💿 📀 📼 📷 📸 📹 🎥 📽 🎞 📞 ☎️ 📟 📠 📺 📻 🎙 🎚 🎛 ⏱ ⏲ ⏰ 🕰 ⌛️ ⏳ 📡 🔋 🔌 💡 🔦 🕯 🗑 🛢 💸 💵 💴 💶 💷 💰 💳 🧾 💎 ⚖️ 🔧 🔨 ⚒ 🛠 ⛏ 🔩 ⚙️ ⛓ 🔫 💣 🔪 🗡 ⚔️ 🛡 🚬 ⚰️ ⚱️ 🏺 🧭 🧱 🔮 🧿 🧸 📿 💈 ⚗️ 🔭 🧰 🧲 🧪 🧫 🧬 🧯 🔬 🕳 💊 💉 🌡 🚽 🚰 🚿 🛁 🛀 🛀🏻 🛀🏼 🛀🏽 🛀🏾 🛀🏿 🧴 🧵 🧶 🧷 🧹 🧺 🧻 🧼 🧽 🛎 🔑 🗝 🚪 🛋 🛏 🛌 🖼 🛍 🧳 🛒 🎁 🎈 🎏 🎀 🎊 🎉 🧨 🎎 🏮 🎐 🧧 ✉️ 📩 📨 📧 💌 📥 📤 📦 🏷 📪 📫 📬 📭 📮 📯 📜 📃 📄 📑 📊 📈 📉 🗒 🗓 📆 📅 📇 🗃 🗳 🗄 📋 📁 📂 🗂 🗞 📰 📓 📔 📒 📕 📗 📘 📙 📚 📖 🔖 🔗 📎 🖇 📐 📏 📌 📍 ✂️ 🖊 🖋 ✒️ 🖌 🖍 📝 ✏️ 🔍 🔎 🔏 🔐 🔒 🔓❤️ 🧡 💛 💚 💙 💜 🖤 💔 ❣️ 💕 💞 💓 💗 💖 💘 💝 💟 ☮️ ✝️ ☪️ 🕉 ☸️ ✡️ 🔯 🕎 ☯️ ☦️ 🛐 ⛎ ♈️ ♉️ ♊️ ♋️ ♌️ ♍️ ♎️ ♏️ ♐️ ♑️ ♒️ ♓️ 🆔 ⚛️ 🉑 ☢️ ☣️ 📴 📳 🈶 🈚️ 🈸 🈺 🈷️ ✴️ 🆚 💮 🉐 ㊙️ ㊗️ 🈴 🈵 🈹 🈲 🅰️ 🅱️ 🆎 🆑 🅾️ 🆘 ❌ ⭕️ 🛑 ⛔️ 📛 🚫 💯 💢 ♨️ 🚷 🚯 🚳 🚱 🔞 📵 🚭 ❗️ ❕ ❓ ❔ ‼️ ⁉️ 🔅 🔆 〽️ ⚠️ 🚸 🔱 ⚜️ 🔰 ♻️ ✅ 🈯️ 💹 ❇️ ✳️ ❎ 🌐 💠 Ⓜ️ 🌀 💤 🏧 🚾 ♿️ 🅿️ 🈳 🈂️ 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 ℹ️ 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 🔢 #️⃣ *️⃣ ⏏️ ▶️ ⏸ ⏯ ⏹ ⏺ ⏭ ⏮ ⏩ ⏪ ⏫ ⏬ ◀️ 🔼 🔽 ➡️ ⬅️ ⬆️ ⬇️ ↗️ ↘️ ↙️ ↖️ ↕️ ↔️ ↪️ ↩️ ⤴️ ⤵️ 🔀 🔁 🔂 🔄 🔃 🎵 🎶 ➕ ➖ ➗ ✖️ ♾ 💲 💱 ™️ ©️ ®️ 〰️ ➰ ➿ 🔚 🔙 🔛 🔝 🔜 ✔️ ☑️ 🔘 ⚪️ ⚫️ 🔴 🔵 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 ▪️ ▫️ ◾️ ◽️ ◼️ ◻️ ⬛️ ⬜️ 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁🗨 💬 💭 🗯 ♠️ ♣️ ♥️ ♦️ 🃏 🎴 🀄️ 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 🕟 🕠 🕡 🕢 🕣 🕤 🕥 🕦 🕧🏳️ 🏴 🏁 🚩 🏳️🌈 🏴☠️ 🇦🇫 🇦🇽 🇦🇱 🇩🇿 🇦🇸 🇦🇩 🇦🇴 🇦🇮 🇦🇶 🇦🇬 🇦🇷 🇦🇲 🇦🇼 🇦🇺 🇦🇹 🇦🇿 🇧🇸 🇧🇭 🇧🇩 🇧🇧 🇧🇾 🇧🇪 🇧🇿 🇧🇯 🇧🇲 🇧🇹 🇧🇴 🇧🇦 🇧🇼 🇧🇷 🇮🇴 🇻🇬 🇧🇳 🇧🇬 🇧🇫 🇧🇮 🇰🇭 🇨🇲 🇨🇦 🇮🇨 🇨🇻 🇧🇶 🇰🇾 🇨🇫 🇹🇩 🇨🇱 🇨🇳 🇨🇽 🇨🇨 🇨🇴 🇰🇲 🇨🇬 🇨🇩 🇨🇰 🇨🇷 🇨🇮 🇭🇷 🇨🇺 🇨🇼 🇨🇾 🇨🇿 🇩🇰 🇩🇯 🇩🇲 🇩🇴 🇪🇨 🇪🇬 🇸🇻 🇬🇶 🇪🇷 🇪🇪 🇪🇹 🇪🇺 🇫🇰 🇫🇴 🇫🇯 🇫🇮 🇫🇷 🇬🇫 🇵🇫 🇹🇫 🇬🇦 🇬🇲 🇬🇪 🇩🇪 🇬🇭 🇬🇮 🇬🇷 🇬🇱 🇬🇩 🇬🇵 🇬🇺 🇬🇹 🇬🇬 🇬🇳 🇬🇼 🇬🇾 🇭🇹 🇭🇳 🇭🇰 🇭🇺 🇮🇸 🇮🇳 🇮🇩 🇮🇷 🇮🇶 🇮🇪 🇮🇲 🇮🇱 🇮🇹 🇯🇲 🇯🇵 🎌 🇯🇪 🇯🇴 🇰🇿 🇰🇪 🇰🇮 🇽🇰 🇰🇼 🇰🇬 🇱🇦 🇱🇻 🇱🇧 🇱🇸 🇱🇷 🇱🇾 🇱🇮 🇱🇹 🇱🇺 🇲🇴 🇲🇰 🇲🇬 🇲🇼 🇲🇾 🇲🇻 🇲🇱 🇲🇹 🇲🇭 🇲🇶 🇲🇷 🇲🇺 🇾🇹 🇲🇽 🇫🇲 🇲🇩 🇲🇨 🇲🇳 🇲🇪 🇲🇸 🇲🇦 🇲🇿 🇲🇲 🇳🇦 🇳🇷 🇳🇵 🇳🇱 🇳🇨 🇳🇿 🇳🇮 🇳🇪 🇳🇬 🇳🇺 🇳🇫 🇰🇵 🇲🇵 🇳🇴 🇴🇲 🇵🇰 🇵🇼 🇵🇸 🇵🇦 🇵🇬 🇵🇾 🇵🇪 🇵🇭 🇵🇳 🇵🇱 🇵🇹 🇵🇷 🇶🇦 🇷🇪 🇷🇴 🇷🇺 🇷🇼 🇼🇸 🇸🇲 🇸🇦 🇸🇳 🇷🇸 🇸🇨 🇸🇱 🇸🇬 🇸🇽 🇸🇰 🇸🇮 🇬🇸 🇸🇧 🇸🇴 🇿🇦 🇰🇷 🇸🇸 🇪🇸 🇱🇰 🇧🇱 🇸🇭 🇰🇳 🇱🇨 🇵🇲 🇻🇨 🇸🇩 🇸🇷 🇸🇿 🇸🇪 🇨🇭 🇸🇾 🇹🇼 🇹🇯 🇹🇿 🇹🇭 🇹🇱 🇹🇬 🇹🇰 🇹🇴 🇹🇹 🇹🇳 🇹🇷 🇹🇲 🇹🇨 🇹🇻 🇻🇮 🇺🇬 🇺🇦 🇦🇪 🇬🇧 🏴 🏴 🏴 🇺🇳 🇺🇸 🇺🇾 🇺🇿 🇻🇺 🇻🇦 🇻🇪 🇻🇳 🇼🇫 🇪🇭 🇾🇪 🇿🇲 🇿🇼🥱 🤏 🦾 🦿 🦻 🧏 🧏♂️ 🧏♀️ 🧍 🧍♂️ 🧍♀️ 🧎 🧎♂️ 🧎♀️ 👨🦯 👩🦯 👨🦼 👩🦼 👨🦽 👩🦽 🦧 🦮 🐕🦺 🦥 🦦 🦨 🦩 🧄 🧅 🧇 🧆 🧈 🦪 🧃 🧉 🧊 🛕 🦽 🦼 🛺 🪂 🪐 🤿 🪀 🪁 🦺 🥻 🩱 🩲 🩳 🩰 🪕 🪔 🪓 🦯 🩸 🩹 🩺 🪑 🪒 🤎 🤍 🟠 🟡 🟢 🟣 🟤 🟥 🟧 🟨 🟩 🟦 🟪 🟫
+
+
diff --git a/tests/backend/specs/api/instance.js b/tests/backend/specs/api/instance.js
new file mode 100644
index 00000000000..4849c6507af
--- /dev/null
+++ b/tests/backend/specs/api/instance.js
@@ -0,0 +1,54 @@
+/*
+ * Tests for the instance-level APIs
+ *
+ * Section "GLOBAL FUNCTIONS" in src/node/db/API.js
+ */
+const assert = require('assert');
+const supertest = require(__dirname+'/../../../../src/node_modules/supertest');
+const fs = require('fs');
+const settings = require(__dirname+'/../../../../src/node/utils/Settings');
+const api = supertest('http://'+settings.ip+":"+settings.port);
+const path = require('path');
+
+var filePath = path.join(__dirname, '../../../../APIKEY.txt');
+
+var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
+apiKey = apiKey.replace(/\n$/, "");
+
+var apiVersion = '1.2.14';
+
+describe('Connectivity for instance-level API tests', function() {
+ it('can connect', function(done) {
+ api.get('/api/')
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+});
+
+describe('getStats', function(){
+ it('Gets the stats of a running instance', function(done) {
+ api.get(endPoint('getStats'))
+ .expect(function(res){
+ if (res.body.code !== 0) throw new Error("getStats() failed");
+
+ if (!(('totalPads' in res.body.data) && (typeof res.body.data.totalPads === 'number'))) {
+ throw new Error(`Response to getStats() does not contain field totalPads, or it's not a number: ${JSON.stringify(res.body.data)}`);
+ }
+
+ if (!(('totalSessions' in res.body.data) && (typeof res.body.data.totalSessions === 'number'))) {
+ throw new Error(`Response to getStats() does not contain field totalSessions, or it's not a number: ${JSON.stringify(res.body.data)}`);
+ }
+
+ if (!(('totalActivePads' in res.body.data) && (typeof res.body.data.totalActivePads === 'number'))) {
+ throw new Error(`Response to getStats() does not contain field totalActivePads, or it's not a number: ${JSON.stringify(res.body.data)}`);
+ }
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done);
+ });
+});
+
+var endPoint = function(point, version){
+ version = version || apiVersion;
+ return '/api/'+version+'/'+point+'?apikey='+apiKey;
+}
diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js
index 4a0c6b6a216..c1b8d49df17 100644
--- a/tests/backend/specs/api/pad.js
+++ b/tests/backend/specs/api/pad.js
@@ -1,10 +1,17 @@
-var assert = require('assert')
- supertest = require(__dirname+'/../../../../src/node_modules/supertest'),
- fs = require('fs'),
- settings = require(__dirname+'/../../loadSettings').loadSettings(),
- api = supertest('http://'+settings.ip+":"+settings.port),
- path = require('path'),
- async = require(__dirname+'/../../../../src/node_modules/async');
+/*
+ * ACHTUNG: there is a copied & modified version of this file in
+ * /tests/container/specs/api/pad.js
+ *
+ * TODO: unify those two files, and merge in a single one.
+ */
+
+const assert = require('assert');
+const supertest = require(__dirname+'/../../../../src/node_modules/supertest');
+const fs = require('fs');
+const settings = require(__dirname + '/../../../../src/node/utils/Settings');
+const api = supertest('http://'+settings.ip+":"+settings.port);
+const path = require('path');
+const async = require(__dirname+'/../../../../src/node_modules/async');
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
@@ -29,7 +36,7 @@ var ulHtml = 'one two<
var expectedHtml = 'itemitem1 item2 ';
describe('Connectivity', function(){
- it('errors if can not connect', function(done) {
+ it('can connect', function(done) {
api.get('/api/')
.expect('Content-Type', /json/)
.expect(200, done)
@@ -37,7 +44,7 @@ describe('Connectivity', function(){
})
describe('API Versioning', function(){
- it('errors if can not connect', function(done) {
+ it('finds the version tag', function(done) {
api.get('/api/')
.expect(function(res){
apiVersion = res.body.currentVersion;
@@ -49,7 +56,7 @@ describe('API Versioning', function(){
})
describe('Permission', function(){
- it('errors if can connect without correct APIKey', function(done) {
+ it('errors with invalid APIKey', function(done) {
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343
// If your APIKey is password you deserve to fail all tests anyway
var permErrorURL = '/api/'+apiVersion+'/createPad?apikey=password&padID=test';
@@ -104,7 +111,7 @@ describe('deletePad', function(){
it('deletes a Pad', function(done) {
api.get(endPoint('deletePad')+"&padID="+testPadId)
.expect('Content-Type', /json/)
- .expect(200, done)
+ .expect(200, done) // @TODO: we shouldn't expect 200 here since the pad may not exist
});
})
@@ -166,6 +173,19 @@ describe('getHTML', function(){
});
})
+describe('listAllPads', function () {
+ it('list all pads', function (done) {
+ api.get(endPoint('listAllPads'))
+ .expect(function (res) {
+ if (res.body.data.padIDs.includes(testPadId) !== true) {
+ throw new Error('Unable to find pad in pad list')
+ }
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ })
+})
+
describe('deletePad', function(){
it('deletes a Pad', function(done) {
api.get(endPoint('deletePad')+"&padID="+testPadId)
@@ -177,6 +197,19 @@ describe('deletePad', function(){
});
})
+describe('listAllPads', function () {
+ it('list all pads', function (done) {
+ api.get(endPoint('listAllPads'))
+ .expect(function (res) {
+ if (res.body.data.padIDs.includes(testPadId) !== false) {
+ throw new Error('Test pad should not be in pads list')
+ }
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ })
+})
+
describe('getHTML', function(){
it('get the HTML of a Pad -- Should return a failure', function(done) {
api.get(endPoint('getHTML')+"&padID="+testPadId)
@@ -204,7 +237,7 @@ describe('getText', function(){
api.get(endPoint('getText')+"&padID="+testPadId)
.expect(function(res){
if(res.body.data.text !== "testText\n") throw new Error("Pad Creation with text")
- })
+ })
.expect('Content-Type', /json/)
.expect(200, done)
});
@@ -212,7 +245,11 @@ describe('getText', function(){
describe('setText', function(){
it('creates a new Pad with text', function(done) {
- api.get(endPoint('setText')+"&padID="+testPadId+"&text=testTextTwo")
+ api.post(endPoint('setText'))
+ .send({
+ "padID": testPadId,
+ "text": "testTextTwo",
+ })
.expect(function(res){
if(res.body.code !== 0) throw new Error("Pad setting text failed");
})
@@ -327,7 +364,11 @@ describe('getLastEdited', function(){
describe('setText', function(){
it('creates a new Pad with text', function(done) {
- api.get(endPoint('setText')+"&padID="+testPadId+"&text=testTextTwo")
+ api.post(endPoint('setText'))
+ .send({
+ "padID": testPadId,
+ "text": "testTextTwo",
+ })
.expect(function(res){
if(res.body.code !== 0) throw new Error("Pad setting text failed");
})
@@ -388,7 +429,7 @@ describe('createPad', function(){
describe('setText', function(){
it('Sets text on a pad Id', function(done) {
api.post(endPoint('setText')+"&padID="+testPadId)
- .send({text: text})
+ .field({text: text})
.expect(function(res){
if(res.body.code !== 0) throw new Error("Pad Set Text failed")
})
@@ -412,7 +453,7 @@ describe('getText', function(){
describe('setText', function(){
it('Sets text on a pad Id including an explicit newline', function(done) {
api.post(endPoint('setText')+"&padID="+testPadId)
- .send({text: text+'\n'})
+ .field({text: text+'\n'})
.expect(function(res){
if(res.body.code !== 0) throw new Error("Pad Set Text failed")
})
@@ -526,7 +567,11 @@ describe('getText', function(){
describe('setHTML', function(){
it('Sets the HTML of a Pad attempting to pass ugly HTML', function(done) {
var html = "Hello HTML
";
- api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+html)
+ api.post(endPoint('setHTML'))
+ .send({
+ "padID": testPadId,
+ "html": html,
+ })
.expect(function(res){
if(res.body.code !== 1) throw new Error("Allowing crappy HTML to be imported")
})
@@ -537,7 +582,11 @@ describe('setHTML', function(){
describe('setHTML', function(){
it('Sets the HTML of a Pad with complex nested lists of different types', function(done) {
- api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+ulHtml)
+ api.post(endPoint('setHTML'))
+ .send({
+ "padID": testPadId,
+ "html": ulHtml,
+ })
.expect(function(res){
if(res.body.code !== 0) throw new Error("List HTML cant be imported")
})
@@ -573,7 +622,7 @@ describe('createPad', function(){
it('errors if pad can be created', function(done) {
var badUrlChars = ["/", "%23", "%3F", "%26"];
async.map(
- badUrlChars,
+ badUrlChars,
function (badUrlChar, cb) {
api.get(endPoint('createPad')+"&padID="+badUrlChar)
.expect(function(res){
diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js
index 961cb2df9a4..cbc6e0a1abf 100644
--- a/tests/backend/specs/api/sessionsAndGroups.js
+++ b/tests/backend/specs/api/sessionsAndGroups.js
@@ -1,7 +1,7 @@
var assert = require('assert')
supertest = require(__dirname+'/../../../../src/node_modules/supertest'),
fs = require('fs'),
- settings = require(__dirname+'/../../loadSettings').loadSettings(),
+ settings = require(__dirname + '/../../../../src/node/utils/Settings'),
api = supertest('http://'+settings.ip+":"+settings.port),
path = require('path');
diff --git a/tests/container/loadSettings.js b/tests/container/loadSettings.js
new file mode 100644
index 00000000000..4a1c46021ef
--- /dev/null
+++ b/tests/container/loadSettings.js
@@ -0,0 +1,37 @@
+/*
+ * ACHTUNG: this file is a hack used to load "settings.json.docker" instead of
+ * "settings.json", since in its present form the Settings module does
+ * not allow it.
+ * This is a remnant of an analogous file that was placed in
+ * /tests/backend/loadSettings.js
+ *
+ * TODO: modify the Settings module:
+ * 1) no side effects on module load
+ * 2) write a factory method that loads a configuration file (taking the
+ * file name from the command line, a function argument, or falling
+ * back to a default)
+ */
+
+var jsonminify = require(__dirname+"/../../src/node_modules/jsonminify");
+const fs = require('fs');
+
+function loadSettings(){
+ var settingsStr = fs.readFileSync(__dirname+"/../../settings.json.docker").toString();
+ // try to parse the settings
+ try {
+ if(settingsStr) {
+ settingsStr = jsonminify(settingsStr).replace(",]","]").replace(",}","}");
+ var settings = JSON.parse(settingsStr);
+
+ // custom settings for running in a container
+ settings.ip = 'localhost';
+ settings.port = '9001';
+
+ return settings;
+ }
+ }catch(e){
+ console.error("whoops something is bad with settings");
+ }
+}
+
+exports.loadSettings = loadSettings;
diff --git a/tests/container/specs/api/pad.js b/tests/container/specs/api/pad.js
new file mode 100644
index 00000000000..f50b7986468
--- /dev/null
+++ b/tests/container/specs/api/pad.js
@@ -0,0 +1,38 @@
+/*
+ * ACHTUNG: this file was copied & modified from the analogous
+ * /tests/backend/specs/api/pad.js
+ *
+ * TODO: unify those two files, and merge in a single one.
+ */
+
+const supertest = require(__dirname+'/../../../../src/node_modules/supertest');
+const settings = require(__dirname+'/../../loadSettings').loadSettings();
+const api = supertest('http://'+settings.ip+":"+settings.port);
+
+var apiVersion = 1;
+
+describe('Connectivity', function(){
+ it('can connect', function(done) {
+ api.get('/api/')
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
+describe('API Versioning', function(){
+ it('finds the version tag', function(done) {
+ api.get('/api/')
+ .expect(function(res){
+ if (!res.body.currentVersion) throw new Error("No version set in API");
+ return;
+ })
+ .expect(200, done)
+ });
+})
+
+describe('Permission', function(){
+ it('errors with invalid APIKey', function(done) {
+ api.get('/api/'+apiVersion+'/createPad?apikey=wrong_password&padID=test')
+ .expect(401, done)
+ });
+})
diff --git a/tests/frontend/helper.js b/tests/frontend/helper.js
index 7a8d19b6029..800dfb29315 100644
--- a/tests/frontend/helper.js
+++ b/tests/frontend/helper.js
@@ -56,6 +56,22 @@ var helper = {};
window.document.cookie = "";
}
+ // Functionality for knowing what key event type is required for tests
+ var evtType = "keydown";
+ // if it's IE require keypress
+ if(window.navigator.userAgent.indexOf("MSIE") > -1){
+ evtType = "keypress";
+ }
+ // Edge also requires keypress.
+ if(window.navigator.userAgent.indexOf("Edge") > -1){
+ evtType = "keypress";
+ }
+ // Opera also requires keypress.
+ if(window.navigator.userAgent.indexOf("OPR") > -1){
+ evtType = "keypress";
+ }
+ helper.evtType = evtType;
+
helper.newPad = function(cb, padName){
//build opts object
var opts = {clearCookies: true}
@@ -216,4 +232,4 @@ var helper = {};
_it(name, func);
}
-})()
\ No newline at end of file
+})()
diff --git a/tests/frontend/lib/expect.js b/tests/frontend/lib/expect.js
index ab5a1eea30f..c647cf2be06 100644
--- a/tests/frontend/lib/expect.js
+++ b/tests/frontend/lib/expect.js
@@ -65,7 +65,7 @@
var name = $flags[i]
, assertion = new Assertion(this.obj, name, this)
-
+
if ('function' == typeof Assertion.prototype[name]) {
// clone the function, make sure we dont touch the prot reference
var old = this[name];
@@ -148,7 +148,7 @@
if ('object' == typeof fn && not) {
// in the presence of a matcher, ensure the `not` only applies to
// the matching.
- this.flags.not = false;
+ this.flags.not = false;
}
var name = this.obj.name || 'fn';
@@ -219,7 +219,7 @@
};
/**
- * Assert within start to finish (inclusive).
+ * Assert within start to finish (inclusive).
*
* @param {Number} start
* @param {Number} finish
@@ -298,7 +298,7 @@
, function(){ return 'expected ' + i(this.obj) + ' to be above ' + n });
return this;
};
-
+
/**
* Assert string value matches _regexp_.
*
@@ -359,13 +359,13 @@
} catch (e) {
hasProp = undefined !== this.obj[name]
}
-
+
this.assert(
hasProp
, function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) }
, function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) });
}
-
+
if (undefined !== val) {
this.assert(
val === this.obj[name]
@@ -537,7 +537,7 @@
return html;
}
};
-
+
// Returns true if object is a DOM element.
var isDOMElement = function (object) {
if (typeof HTMLElement === 'object') {
@@ -843,9 +843,9 @@
expect.eql = function eql (actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
- if (actual === expected) {
+ if (actual === expected) {
return true;
- } else if ('undefined' != typeof Buffer
+ } else if ('undefined' != typeof Buffer
&& Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
if (actual.length != expected.length) return false;
diff --git a/tests/frontend/lib/mocha.js b/tests/frontend/lib/mocha.js
index 5f2da01399e..0b1e894866e 100644
--- a/tests/frontend/lib/mocha.js
+++ b/tests/frontend/lib/mocha.js
@@ -32,11 +32,11 @@ require.register = function (path, fn){
require.relative = function (parent) {
return function(p){
if ('.' != p.charAt(0)) return require(p);
-
+
var path = parent.split('/')
, segs = p.split('/');
path.pop();
-
+
for (var i = 0; i < segs.length; i++) {
var seg = segs[i];
if ('..' == seg) path.pop();
@@ -52,7 +52,7 @@ require.register("browser/debug.js", function(module, exports, require){
module.exports = function(type){
return function(){
-
+
}
};
}); // module: browser/debug.js
@@ -530,7 +530,7 @@ var Suite = require('../suite')
/**
* BDD-style interface:
- *
+ *
* describe('Array', function(){
* describe('#indexOf()', function(){
* it('should return -1 when not present', function(){
@@ -542,7 +542,7 @@ var Suite = require('../suite')
* });
* });
* });
- *
+ *
*/
module.exports = function(suite){
@@ -587,7 +587,7 @@ module.exports = function(suite){
* and callback `fn` containing nested suites
* and/or tests.
*/
-
+
context.describe = context.context = function(title, fn){
var suite = Suite.create(suites[0], title);
suites.unshift(suite);
@@ -667,19 +667,19 @@ var Suite = require('../suite')
/**
* TDD-style interface:
- *
+ *
* exports.Array = {
* '#indexOf()': {
* 'should return -1 when the value is not present': function(){
- *
+ *
* },
*
* 'should return the correct index when the value is present': function(){
- *
+ *
* }
* }
* };
- *
+ *
*/
module.exports = function(suite){
@@ -739,27 +739,27 @@ var Suite = require('../suite')
/**
* QUnit-style interface:
- *
+ *
* suite('Array');
- *
+ *
* test('#length', function(){
* var arr = [1,2,3];
* ok(arr.length == 3);
* });
- *
+ *
* test('#indexOf()', function(){
* var arr = [1,2,3];
* ok(arr.indexOf(1) == 0);
* ok(arr.indexOf(2) == 1);
* ok(arr.indexOf(3) == 2);
* });
- *
+ *
* suite('String');
- *
+ *
* test('#length', function(){
* ok('foo'.length == 3);
* });
- *
+ *
*/
module.exports = function(suite){
@@ -802,7 +802,7 @@ module.exports = function(suite){
/**
* Describe a "suite" with the given `title`.
*/
-
+
context.suite = function(title){
if (suites.length > 1) suites.shift();
var suite = Suite.create(suites[0], title);
@@ -840,7 +840,7 @@ var Suite = require('../suite')
* suiteSetup(function(){
*
* });
- *
+ *
* test('should return -1 when not present', function(){
*
* });
@@ -2689,7 +2689,7 @@ exports = module.exports = Min;
function Min(runner) {
Base.call(this, runner);
-
+
runner.on('start', function(){
// clear screen
process.stdout.write('\u001b[2J');
@@ -3337,7 +3337,7 @@ function XUnit(runner) {
runner.on('pass', function(test){
tests.push(test);
});
-
+
runner.on('fail', function(test){
tests.push(test);
});
@@ -3354,7 +3354,7 @@ function XUnit(runner) {
}, false));
tests.forEach(test);
- console.log('');
+ console.log('');
});
}
@@ -3615,7 +3615,7 @@ Runnable.prototype.run = function(fn){
}
return;
}
-
+
// sync
try {
if (!this.pending) this.fn.call(ctx);
@@ -4536,7 +4536,7 @@ exports.indexOf = function(arr, obj, start){
/**
* Array#reduce (<=IE8)
- *
+ *
* @param {Array} array
* @param {Function} fn
* @param {Object} initial value
diff --git a/tests/frontend/lib/sendkeys.js b/tests/frontend/lib/sendkeys.js
index b57618476a9..7550df38f53 100644
--- a/tests/frontend/lib/sendkeys.js
+++ b/tests/frontend/lib/sendkeys.js
@@ -305,11 +305,11 @@ var START_TO_END = 1;
var END_TO_END = 2;
var END_TO_START = 3;
// from the Mozilla documentation, for range.compareBoundaryPoints(how, sourceRange)
-// -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before, equal to, or after the corresponding boundary-point of sourceRange.
+// -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before, equal to, or after the corresponding boundary-point of sourceRange.
// * Range.END_TO_END compares the end boundary-point of sourceRange to the end boundary-point of range.
// * Range.END_TO_START compares the end boundary-point of sourceRange to the start boundary-point of range.
// * Range.START_TO_END compares the start boundary-point of sourceRange to the end boundary-point of range.
- // * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range.
+ // * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range.
function w3cstart(rng, constraint){
if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning
if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length;
diff --git a/tests/frontend/runner.css b/tests/frontend/runner.css
index ce158b818ea..66d3283ca3e 100644
--- a/tests/frontend/runner.css
+++ b/tests/frontend/runner.css
@@ -6,6 +6,9 @@ body {
padding: 0px;
margin: 0px;
height: 100%;
+ display: flex;
+ flex-direction: row;
+ overflow: hidden;
}
#console {
@@ -14,33 +17,30 @@ body {
#iframe-container {
width: 50%;
+ min-width: 820px;
height: 100%;
}
#iframe-container iframe {
height: 100%;
- position:absolute;
- min-width:500px;
- max-width:800px;
- left:50%;
width:100%;
}
#mocha {
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
border-right: 2px solid #999;
- width: 50%;
+ flex: 1 auto;
height: 100%;
- position: absolute;
overflow: auto;
- float:left;
}
#mocha #report {
- margin-top: 50px;
+ margin: 0;
+ padding: 0;
+ margin-top: 10px;
}
-#mocha ul, #mocha li {
+#mocha li {
margin: 0;
padding: 0;
}
@@ -175,6 +175,10 @@ body {
-webkit-box-shadow: 0 1px 3px #eee;
}
+#report ul {
+ padding: 0;
+}
+
#report.pass .test.fail {
display: none;
}
@@ -191,12 +195,11 @@ body {
}
#stats {
- position: fixed;
- top: 15px;
- right: 52%;
+ padding: 10px;
font-size: 12px;
margin: 0;
color: #888;
+ text-align: right;
}
#stats .progress {
diff --git a/tests/frontend/runner.js b/tests/frontend/runner.js
index e77f6707fd9..1d3ef64150f 100644
--- a/tests/frontend/runner.js
+++ b/tests/frontend/runner.js
@@ -52,7 +52,7 @@ $(function(){
}
/*
- This reporter wraps the original html reporter plus reports plain text into a hidden div.
+ This reporter wraps the original html reporter plus reports plain text into a hidden div.
This allows the webdriver client to pick up the test results
*/
var WebdriverAndHtmlReporter = function(html_reporter){
@@ -170,7 +170,7 @@ $(function(){
//get the list of specs and filter it if requested
var specs = specs_list.slice();
-
+
//inject spec scripts into the dom
var $body = $('body');
$.each(specs, function(i, spec){
@@ -195,4 +195,4 @@ $(function(){
mocha.run();
});
-});
+});
diff --git a/tests/frontend/specs/alphabet.js b/tests/frontend/specs/alphabet.js
index ae9570e41c4..5d16c983a30 100644
--- a/tests/frontend/specs/alphabet.js
+++ b/tests/frontend/specs/alphabet.js
@@ -8,12 +8,12 @@ describe("All the alphabet works n stuff", function(){
});
it("when you enter any char it appears right", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
-
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
//get the first text element out of the inner iframe
var firstTextElement = inner$("div").first();
-
+
// simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all
firstTextElement.sendkeys('{del}'); // clear the first line
diff --git a/tests/frontend/specs/bold.js b/tests/frontend/specs/bold.js
index 888eb660258..94e3a9b5b89 100644
--- a/tests/frontend/specs/bold.js
+++ b/tests/frontend/specs/bold.js
@@ -6,22 +6,22 @@ describe("bold button", function(){
});
it("makes text bold on click", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
-
+
//select this text element
$firstTextElement.sendkeys('{selectall}');
//get the bold button and click it
var $boldButton = chrome$(".buttonicon-bold");
$boldButton.click();
-
+
//ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
-
+
// is there a element now?
var isBold = $newFirstTextElement.find("b").length === 1;
@@ -44,13 +44,7 @@ describe("bold button", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
- if(inner$(window)[0].bowser.modernIE){ // if it's IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
-
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 66; // b
inner$("#innerdocbody").trigger(e);
diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js
index 14ff8d6a6da..45c2eb3bec2 100644
--- a/tests/frontend/specs/caret.js
+++ b/tests/frontend/specs/caret.js
@@ -198,9 +198,9 @@ console.log(inner$);
/*
it("Creates N rows, changes height of rows, updates UI by caret key events", function(done){
var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var chrome$ = helper.padChrome$;
var numberOfRows = 50;
-
+
//ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
var originalDivHeight = inner$("div").first().css("height");
@@ -211,7 +211,7 @@ console.log(inner$);
}).done(function(){ // Once the DOM has registered the items
inner$("div").each(function(index){ // Randomize the item heights (replicates images / headings etc)
var random = Math.floor(Math.random() * (50)) + 20;
- $(this).css("height", random+"px");
+ $(this).css("height", random+"px");
});
console.log(caretPosition(inner$));
@@ -253,7 +253,7 @@ console.log(inner$);
keyEvent(inner$, 33, false, false); // doesn't work
i++;
}
-
+
// Does scrolling back up the pad with the up arrow show the correct contents?
helper.waitFor(function(){ // Wait for the new position to be in place
try{
@@ -280,7 +280,7 @@ console.log(inner$);
helper.waitFor(function(){ // Wait for the new position to be in place
return isScrolledIntoView(inner$("div:nth-child(1)"), inner$); // Wait for the DOM to scroll into place
}).done(function(){ // Once the DOM has registered the items
- expect(true).to.be(true);
+ expect(true).to.be(true);
done();
});
*/
@@ -297,20 +297,15 @@ function prepareDocument(n, target){ // generates a random document with random
}
function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the window
- if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
- var e = target.Event(evtType);
- console.log(e);
+
+ var e = target.Event(helper.evtType);
if(ctrl){
e.ctrlKey = true; // Control key
}
if(shift){
e.shiftKey = true; // Shift Key
}
- e.which = charCode;
+ e.which = charCode;
e.keyCode = charCode;
target("#innerdocbody").trigger(e);
}
diff --git a/tests/frontend/specs/change_user_color.js b/tests/frontend/specs/change_user_color.js
new file mode 100644
index 00000000000..5969eabe298
--- /dev/null
+++ b/tests/frontend/specs/change_user_color.js
@@ -0,0 +1,104 @@
+describe("change user color", function(){
+ //create a new pad before each test run
+ beforeEach(function(cb){
+ helper.newPad(cb);
+ this.timeout(60000);
+ });
+
+ it("Color picker matches original color and remembers the user color after a refresh", function(done) {
+ this.timeout(60000);
+ var chrome$ = helper.padChrome$;
+
+ //click on the settings button to make settings visible
+ var $userButton = chrome$(".buttonicon-showusers");
+ $userButton.click();
+
+ var $userSwatch = chrome$("#myswatch");
+ $userSwatch.click();
+
+ var fb = chrome$.farbtastic('#colorpicker')
+ var $colorPickerSave = chrome$("#mycolorpickersave");
+ var $colorPickerPreview = chrome$("#mycolorpickerpreview");
+
+ // Same color represented in two different ways
+ const testColorHash = '#abcdef'
+ const testColorRGB = 'rgb(171, 205, 239)'
+
+ // Check that the color picker matches the automatically assigned random color on the swatch.
+ // NOTE: This has a tiny chance of creating a false positive for passing in the
+ // off-chance the randomly assigned color is the same as the test color.
+ expect($colorPickerPreview.css('background-color')).to.be($userSwatch.css('background-color'))
+
+ // The swatch updates as the test color is picked.
+ fb.setColor(testColorHash)
+ expect($colorPickerPreview.css('background-color')).to.be(testColorRGB)
+ $colorPickerSave.click();
+ expect($userSwatch.css('background-color')).to.be(testColorRGB)
+
+ setTimeout(function(){ //give it a second to save the color on the server side
+ helper.newPad({ // get a new pad, but don't clear the cookies
+ clearCookies: false
+ , cb: function(){
+ var chrome$ = helper.padChrome$;
+
+ //click on the settings button to make settings visible
+ var $userButton = chrome$(".buttonicon-showusers");
+ $userButton.click();
+
+ var $userSwatch = chrome$("#myswatch");
+ $userSwatch.click();
+
+ var $colorPickerPreview = chrome$("#mycolorpickerpreview");
+
+ expect($colorPickerPreview.css('background-color')).to.be(testColorRGB)
+ expect($userSwatch.css('background-color')).to.be(testColorRGB)
+
+ done();
+ }
+ });
+ }, 1000);
+ });
+
+ it("Own user color is shown when you enter a chat", function(done) {
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
+ var $colorOption = helper.padChrome$('#options-colorscheck');
+ if (!$colorOption.is(':checked')) {
+ $colorOption.click();
+ }
+
+ //click on the settings button to make settings visible
+ var $userButton = chrome$(".buttonicon-showusers");
+ $userButton.click();
+
+ var $userSwatch = chrome$("#myswatch");
+ $userSwatch.click();
+
+ var fb = chrome$.farbtastic('#colorpicker')
+ var $colorPickerSave = chrome$("#mycolorpickersave");
+
+ // Same color represented in two different ways
+ const testColorHash = '#abcdef'
+ const testColorRGB = 'rgb(171, 205, 239)'
+
+ fb.setColor(testColorHash)
+ $colorPickerSave.click();
+
+ //click on the chat button to make chat visible
+ var $chatButton = chrome$("#chaticon");
+ $chatButton.click();
+ var $chatInput = chrome$("#chatinput");
+ $chatInput.sendkeys('O hi'); // simulate a keypress of typing user
+ $chatInput.sendkeys('{enter}'); // simulate a keypress of enter actually does evt.which = 10 not 13
+
+ //check if chat shows up
+ helper.waitFor(function(){
+ return chrome$("#chattext").children("p").length !== 0; // wait until the chat message shows up
+ }).done(function(){
+ var $firstChatMessage = chrome$("#chattext").children("p");
+ expect($firstChatMessage.css('background-color')).to.be(testColorRGB); // expect the first chat message to be of the user's color
+ done();
+ });
+ });
+});
diff --git a/tests/frontend/specs/change_user_name.js b/tests/frontend/specs/change_user_name.js
index ba089c90b10..b0a5df15f27 100644
--- a/tests/frontend/specs/change_user_name.js
+++ b/tests/frontend/specs/change_user_name.js
@@ -12,7 +12,7 @@ describe("change username value", function(){
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
-
+
var $usernameInput = chrome$("#myusernameedit");
$usernameInput.click();
@@ -45,7 +45,7 @@ describe("change username value", function(){
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
-
+
var $usernameInput = chrome$("#myusernameedit");
$usernameInput.click();
diff --git a/tests/frontend/specs/chat.js b/tests/frontend/specs/chat.js
index 7ebb76fb305..8eaf08a9514 100644
--- a/tests/frontend/specs/chat.js
+++ b/tests/frontend/specs/chat.js
@@ -6,8 +6,8 @@ describe("Chat messages and UI", function(){
});
it("opens chat, sends a message and makes sure it exists on the page", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
var chatValue = "JohnMcLear";
//click on the chat button to make chat visible
@@ -39,8 +39,8 @@ describe("Chat messages and UI", function(){
});
it("makes sure that an empty message can't be sent", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
//click on the chat button to make chat visible
var $chatButton = chrome$("#chaticon");
@@ -65,8 +65,8 @@ describe("Chat messages and UI", function(){
});
it("makes chat stick to right side of the screen", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
@@ -75,56 +75,60 @@ describe("Chat messages and UI", function(){
//get the chat selector
var $stickychatCheckbox = chrome$("#options-stickychat");
- //select chat always on screen and fire change event
- $stickychatCheckbox.attr('selected','selected');
- $stickychatCheckbox.change();
- $stickychatCheckbox.click();
+ //select chat always on screen
+ if (!$stickychatCheckbox.is(':checked')) {
+ $stickychatCheckbox.click();
+ }
- //check if chat changed to get the stickychat Class
- var $chatbox = chrome$("#chatbox");
- var hasStickyChatClass = $chatbox.hasClass("stickyChat");
- expect(hasStickyChatClass).to.be(true);
+ // due to animation, we need to make some timeout...
+ setTimeout(function() {
+ //check if chat changed to get the stickychat Class
+ var $chatbox = chrome$("#chatbox");
+ var hasStickyChatClass = $chatbox.hasClass("stickyChat");
+ expect(hasStickyChatClass).to.be(true);
- //select chat always on screen and fire change event
- $stickychatCheckbox.attr('selected','selected');
- $stickychatCheckbox.change();
- $stickychatCheckbox.click();
+ // select chat always on screen and fire change event
+ $stickychatCheckbox.click();
+
+ setTimeout(function() {
+ //check if chat changed to remove the stickychat Class
+ var hasStickyChatClass = $chatbox.hasClass("stickyChat");
+ expect(hasStickyChatClass).to.be(false);
+
+ done();
+ }, 10)
+ }, 10)
- //check if chat changed to remove the stickychat Class
- var hasStickyChatClass = $chatbox.hasClass("stickyChat");
- expect(hasStickyChatClass).to.be(false);
- done();
});
it("makes chat stick to right side of the screen then makes it one step smaller", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
-
- //click on the settings button to make settings visible
- var $settingsButton = chrome$(".buttonicon-settings");
- $settingsButton.click();
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
- //get the chat selector
- var $stickychatCheckbox = chrome$("#options-stickychat");
+ // open chat
+ chrome$('#chaticon').click();
- //select chat always on screen and fire change event
- $stickychatCheckbox.attr('selected','selected');
- $stickychatCheckbox.change();
- $stickychatCheckbox.click();
+ // select chat always on screen from chatbox
+ chrome$('.stick-to-screen-btn').click();
- //check if chat changed to get the stickychat Class
- var $chatbox = chrome$("#chatbox");
- var hasStickyChatClass = $chatbox.hasClass("stickyChat");
- expect(hasStickyChatClass).to.be(true);
+ // due to animation, we need to make some timeout...
+ setTimeout(function() {
+ //check if chat changed to get the stickychat Class
+ var $chatbox = chrome$("#chatbox");
+ var hasStickyChatClass = $chatbox.hasClass("stickyChat");
+ expect(hasStickyChatClass).to.be(true);
- //select chat always on screen and fire change event
- chrome$('#titlecross').click();
+ // select chat always on screen and fire change event
+ chrome$('#titlecross').click();
- //check if chat changed to remove the stickychat Class
- var hasStickyChatClass = $chatbox.hasClass("stickyChat");
- expect(hasStickyChatClass).to.be(false);
+ setTimeout(function() {
+ //check if chat changed to remove the stickychat Class
+ var hasStickyChatClass = $chatbox.hasClass("stickyChat");
+ expect(hasStickyChatClass).to.be(false);
- done();
+ done();
+ }, 10)
+ }, 10)
});
});
diff --git a/tests/frontend/specs/chat_load_messages.js b/tests/frontend/specs/chat_load_messages.js
index 7b09b47e1b8..29040798d34 100644
--- a/tests/frontend/specs/chat_load_messages.js
+++ b/tests/frontend/specs/chat_load_messages.js
@@ -1,21 +1,21 @@
describe("chat-load-messages", function(){
var padName;
-
+
it("creates a pad", function(done) {
padName = helper.newPad(done);
this.timeout(60000);
});
it("adds a lot of messages", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
var chatButton = chrome$("#chaticon");
chatButton.click();
var chatInput = chrome$("#chatinput");
var chatText = chrome$("#chattext");
-
+
this.timeout(60000);
-
+
var messages = 140;
for(var i=1; i <= messages; i++) {
var num = ''+i;
@@ -33,7 +33,7 @@ describe("chat-load-messages", function(){
helper.newPad(done, padName);
});
});
-
+
it("checks initial message count", function(done) {
var chatText;
var expectedCount = 101;
@@ -48,7 +48,7 @@ describe("chat-load-messages", function(){
done();
});
});
-
+
it("loads more messages", function(done) {
var expectedCount = 122;
var chrome$ = helper.padChrome$;
@@ -56,7 +56,7 @@ describe("chat-load-messages", function(){
chatButton.click();
var chatText = chrome$("#chattext");
var loadMsgBtn = chrome$("#chatloadmessagesbutton");
-
+
loadMsgBtn.click();
helper.waitFor(function(){
return chatText.children("p").length == expectedCount;
@@ -65,7 +65,7 @@ describe("chat-load-messages", function(){
done();
});
});
-
+
it("checks for button vanishing", function(done) {
var expectedDisplay = 'none';
var chrome$ = helper.padChrome$;
diff --git a/tests/frontend/specs/clear_authorship_colors.js b/tests/frontend/specs/clear_authorship_colors.js
index 1417f63c62f..efadb08a0f0 100644
--- a/tests/frontend/specs/clear_authorship_colors.js
+++ b/tests/frontend/specs/clear_authorship_colors.js
@@ -6,8 +6,8 @@ describe("clear authorship colors button", function(){
});
it("makes text clear authorship colors", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
// override the confirm dialogue functioon
helper.padChrome$.window.confirm = function(){
@@ -19,7 +19,7 @@ describe("clear authorship colors button", function(){
// Get the original text
var originalText = inner$("div").first().text();
-
+
// Set some new text
var sentText = "Hello";
@@ -47,6 +47,76 @@ describe("clear authorship colors button", function(){
var hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false);
+ setTimeout(function(){
+ var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1
+ expect(disconnectVisible).to.be(true);
+ },1000);
+
+ done();
+ });
+
+ });
+
+ it("makes text clear authorship colors and checks it can't be undone", function(done) {
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
+ // override the confirm dialogue functioon
+ helper.padChrome$.window.confirm = function(){
+ return true;
+ }
+
+ //get the first text element out of the inner iframe
+ var $firstTextElement = inner$("div").first();
+
+ // Get the original text
+ var originalText = inner$("div").first().text();
+
+ // Set some new text
+ var sentText = "Hello";
+
+ //select this text element
+ $firstTextElement.sendkeys('{selectall}');
+ $firstTextElement.sendkeys(sentText);
+ $firstTextElement.sendkeys('{rightarrow}');
+
+ helper.waitFor(function(){
+ return inner$("div span").first().attr("class").indexOf("author") !== -1; // wait until we have the full value available
+ }).done(function(){
+ //IE hates you if you don't give focus to the inner frame bevore you do a clearAuthorship
+ inner$("div").first().focus();
+
+ //get the clear authorship colors button and click it
+ var $clearauthorshipcolorsButton = chrome$(".buttonicon-clearauthorship");
+ $clearauthorshipcolorsButton.click();
+
+ // does the first divs span include an author class?
+ console.log(inner$("div span").first().attr("class"));
+ var hasAuthorClass = inner$("div span").first().attr("class").indexOf("author") !== -1;
+ //expect(hasAuthorClass).to.be(false);
+
+ // does the first div include an author class?
+ var hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
+ expect(hasAuthorClass).to.be(false);
+
+ var e = inner$.Event(helper.evtType);
+ e.ctrlKey = true; // Control key
+ e.which = 90; // z
+ inner$("#innerdocbody").trigger(e); // shouldn't od anything
+
+ // does the first div include an author class?
+ hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
+ expect(hasAuthorClass).to.be(false);
+
+ // get undo and redo buttons
+ var $undoButton = chrome$(".buttonicon-undo");
+
+ // click the button
+ $undoButton.click(); // shouldn't do anything
+ hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
+ expect(hasAuthorClass).to.be(false);
+
+
setTimeout(function(){
var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1
expect(disconnectVisible).to.be(true);
@@ -57,3 +127,4 @@ describe("clear authorship colors button", function(){
});
});
+
diff --git a/tests/frontend/specs/delete.js b/tests/frontend/specs/delete.js
index 86e76f56f20..616cd4ddc51 100644
--- a/tests/frontend/specs/delete.js
+++ b/tests/frontend/specs/delete.js
@@ -6,12 +6,12 @@ describe("delete keystroke", function(){
});
it("makes text delete", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
-
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
-
+
// get the original length of this element
var elementLength = $firstTextElement.text().length;
@@ -25,7 +25,7 @@ describe("delete keystroke", function(){
//ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
-
+
// get the new length of this element
var newElementLength = $newFirstTextElement.text().length;
diff --git a/tests/frontend/specs/embed_value.js b/tests/frontend/specs/embed_value.js
index 029f0dd5d91..aa031e2366e 100644
--- a/tests/frontend/specs/embed_value.js
+++ b/tests/frontend/specs/embed_value.js
@@ -16,9 +16,9 @@ describe("embed links", function(){
var $embediFrame = $(embedCode);
//read and check the frame attributes
- var width = $embediFrame.attr("width");
- var height = $embediFrame.attr("height");
- var name = $embediFrame.attr("name");
+ var width = $embediFrame.attr("width");
+ var height = $embediFrame.attr("height");
+ var name = $embediFrame.attr("name");
expect(width).to.be('600');
expect(height).to.be('400');
expect(name).to.be(readonly ? "embed_readonly" : "embed_readwrite");
@@ -43,7 +43,7 @@ describe("embed links", function(){
} else {
expect(url).to.be(helper.padChrome$.window.location.href);
}
-
+
//check if all parts of the url are like expected
expect(params).to.eql(expectedParams);
}
@@ -57,7 +57,7 @@ describe("embed links", function(){
describe("the share link", function(){
it("is the actual pad url", function(done){
- var chrome$ = helper.padChrome$;
+ var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
@@ -73,14 +73,14 @@ describe("embed links", function(){
describe("the embed as iframe code", function(){
it("is an iframe with the the correct url parameters and correct size", function(done){
- var chrome$ = helper.padChrome$;
+ var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
//get the link of the share field + the actual pad url and compare them
var embedCode = chrome$("#embedinput").val();
-
+
checkiFrameCode(embedCode, false)
done();
@@ -96,7 +96,7 @@ describe("embed links", function(){
describe("the share link", function(){
it("shows a read only url", function(done){
- var chrome$ = helper.padChrome$;
+ var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
@@ -114,7 +114,7 @@ describe("embed links", function(){
describe("the embed as iframe code", function(){
it("is an iframe with the the correct url parameters and correct size", function(done){
- var chrome$ = helper.padChrome$;
+ var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
@@ -125,9 +125,9 @@ describe("embed links", function(){
//get the link of the share field + the actual pad url and compare them
var embedCode = chrome$("#embedinput").val();
-
+
checkiFrameCode(embedCode, true);
-
+
done();
});
});
diff --git a/tests/frontend/specs/enter.js b/tests/frontend/specs/enter.js
index baafeded25b..9c3457b02f9 100644
--- a/tests/frontend/specs/enter.js
+++ b/tests/frontend/specs/enter.js
@@ -6,12 +6,12 @@ describe("enter keystroke", function(){
});
it("creates a new line & puts cursor onto a new line", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
-
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
-
+
// get the original string value minus the last char
var originalTextValue = $firstTextElement.text();
@@ -20,7 +20,7 @@ describe("enter keystroke", function(){
//ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
-
+
helper.waitFor(function(){
return inner$("div").first().text() === "";
}).done(function(){
diff --git a/tests/frontend/specs/font_type.js b/tests/frontend/specs/font_type.js
index d2c7bc636ba..9b149873e4d 100644
--- a/tests/frontend/specs/font_type.js
+++ b/tests/frontend/specs/font_type.js
@@ -6,8 +6,8 @@ describe("font select", function(){
});
it("makes text monospace", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
diff --git a/tests/frontend/specs/helper.js b/tests/frontend/specs/helper.js
index 727e47960e9..fa7ed4ddaec 100644
--- a/tests/frontend/specs/helper.js
+++ b/tests/frontend/specs/helper.js
@@ -20,7 +20,7 @@ describe("the test helper", function(){
});
it("gives me 3 jquery instances of chrome, outer and inner", function(done){
- this.timeout(5000);
+ this.timeout(10000);
helper.newPad(function(){
//check if the jquery selectors have the desired elements
@@ -136,7 +136,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
- expect(cleanText(selection.toString())).to.be("ort lines to t");
+
+ /*
+ * replace() is required here because Firefox keeps the line breaks.
+ *
+ * I'm not sure this is ideal behavior of getSelection() where the text
+ * is not consistent between browsers but that's the situation so that's
+ * how I'm covering it in this test.
+ */
+ expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to t");
done();
});
@@ -154,7 +162,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
- expect(cleanText(selection.toString())).to.be("ort lines to test");
+
+ /*
+ * replace() is required here because Firefox keeps the line breaks.
+ *
+ * I'm not sure this is ideal behavior of getSelection() where the text
+ * is not consistent between browsers but that's the situation so that's
+ * how I'm covering it in this test.
+ */
+ expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to test");
done();
});
@@ -172,7 +188,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
- expect(cleanText(selection.toString())).to.be("ort lines ");
+
+ /*
+ * replace() is required here because Firefox keeps the line breaks.
+ *
+ * I'm not sure this is ideal behavior of getSelection() where the text
+ * is not consistent between browsers but that's the situation so that's
+ * how I'm covering it in this test.
+ */
+ expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines ");
done();
});
@@ -190,7 +214,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
- expect(cleanText(selection.toString())).to.be("ort lines to test");
+
+ /*
+ * replace() is required here because Firefox keeps the line breaks.
+ *
+ * I'm not sure this is ideal behavior of getSelection() where the text
+ * is not consistent between browsers but that's the situation so that's
+ * how I'm covering it in this test.
+ */
+ expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to test");
done();
});
@@ -205,7 +237,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine);
var selection = inner$.document.getSelection();
- expect(cleanText(selection.toString())).to.be("short lines to test");
+
+ /*
+ * replace() is required here because Firefox keeps the line breaks.
+ *
+ * I'm not sure this is ideal behavior of getSelection() where the text
+ * is not consistent between browsers but that's the situation so that's
+ * how I'm covering it in this test.
+ */
+ expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("short lines to test");
done();
});
diff --git a/tests/frontend/specs/importexport.js b/tests/frontend/specs/importexport.js
index 2dc002ba0cf..3466f7cfbc4 100644
--- a/tests/frontend/specs/importexport.js
+++ b/tests/frontend/specs/importexport.js
@@ -159,7 +159,7 @@ describe("import functionality", function(){
//\n\
// \n')
})
-
+
var results = exportfunc(helper.padChrome$.window.location.href)
expect(results[0][1]).to.be('bullet line 1 bullet line 2 bullet2 line 1 bullet4 line 2 bullet4 line 2 bullet4 line 2 bullet3 line 1 bullet2 line 1 ')
expect(results[1][1]).to.be('\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t* bullet3 line 1\n\t* bullet2 line 1\n\n')
@@ -183,11 +183,11 @@ describe("import functionality", function(){
\n')
})
var results = exportfunc(helper.padChrome$.window.location.href)
- expect(results[0][1]).to.be('bullet4 line 2 bisu bullet4 line 2 bs bullet4 line 2 uuis ')
+ expect(results[0][1]).to.be('bullet4 line 2 bisu bullet4 line 2 bs bullet4 line 2 uuis ')
expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\t\t\t\t\t\t\t\t* foo\n\t\t\t\t\t\t\t\t* foobar bs\n\t\t\t\t\t* foobar\n\n')
done()
})
-
+
xit("import a pad with ordered lists from html", function(done){
var importurl = helper.padChrome$.window.location.href+'/import'
var htmlWithBullets = 'number 1 line 1 number 2 line 2 '
diff --git a/tests/frontend/specs/importindents.js b/tests/frontend/specs/importindents.js
index 326d9e971a6..284a90f3281 100644
--- a/tests/frontend/specs/importindents.js
+++ b/tests/frontend/specs/importindents.js
@@ -66,7 +66,7 @@ describe("import indents functionality", function(){
expect(results[1][1]).to.be('\tindent line 1\n\tindent line 2\n\t\tindent2 line 1\n\t\tindent2 line 2\n\n')
done()
})
-
+
xit("import a pad with indented lists and newlines from html", function(done){
var importurl = helper.padChrome$.window.location.href+'/import'
var htmlWithIndents = ' '
@@ -104,7 +104,7 @@ describe("import indents functionality", function(){
\n')
})
var results = exportfunc(helper.padChrome$.window.location.href)
- expect(results[0][1]).to.be('indent4 line 2 bisu indent4 line 2 bs indent4 line 2 uuis ')
+ expect(results[0][1]).to.be('indent4 line 2 bisu indent4 line 2 bs indent4 line 2 uuis ')
expect(results[1][1]).to.be('\tindent line 1\n\n\tindent line 2\n\t\tindent2 line 1\n\n\t\t\t\tindent4 line 2 bisu\n\t\t\t\tindent4 line 2 bs\n\t\t\t\tindent4 line 2 uuis\n\t\t\t\t\t\t\t\tfoo\n\t\t\t\t\t\t\t\tfoobar bs\n\t\t\t\t\tfoobar\n\n')
done()
})
diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js
index dd12fc317c4..2173f9e07ff 100644
--- a/tests/frontend/specs/indentation.js
+++ b/tests/frontend/specs/indentation.js
@@ -15,13 +15,7 @@ describe("indentation button", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
- if(inner$(window)[0].bowser.modernIE){ // if it's IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
-
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.keyCode = 9; // tab :|
inner$("#innerdocbody").trigger(e);
@@ -325,12 +319,7 @@ describe("indentation button", function(){
function pressEnter(){
var inner$ = helper.padInner$;
- if(inner$(window)[0].bowser.modernIE){ // if it's IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.keyCode = 13; // enter :|
inner$("#innerdocbody").trigger(e);
}
diff --git a/tests/frontend/specs/italic.js b/tests/frontend/specs/italic.js
index cecbc180831..3c62e00e86b 100644
--- a/tests/frontend/specs/italic.js
+++ b/tests/frontend/specs/italic.js
@@ -6,22 +6,22 @@ describe("italic some text", function(){
});
it("makes text italic using button", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
-
+
//select this text element
$firstTextElement.sendkeys('{selectall}');
//get the bold button and click it
var $boldButton = chrome$(".buttonicon-italic");
$boldButton.click();
-
+
//ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
-
+
// is there a element now?
var isItalic = $newFirstTextElement.find("i").length === 1;
@@ -44,13 +44,7 @@ describe("italic some text", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
- if(inner$(window)[0].bowser.modernIE){ // if it's IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
-
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 105; // i
inner$("#innerdocbody").trigger(e);
diff --git a/tests/frontend/specs/ordered_list.js b/tests/frontend/specs/ordered_list.js
index e7509e883e8..f4aa572731c 100644
--- a/tests/frontend/specs/ordered_list.js
+++ b/tests/frontend/specs/ordered_list.js
@@ -111,12 +111,7 @@ describe("assign ordered list", function(){
var triggerCtrlShiftShortcut = function(shortcutChar) {
var inner$ = helper.padInner$;
- if(inner$(window)[0].bowser.modernIE) { // if it's IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.ctrlKey = true;
e.shiftKey = true;
e.which = shortcutChar.toString().charCodeAt(0);
diff --git a/tests/frontend/specs/pad_modal.js b/tests/frontend/specs/pad_modal.js
index 80752e4b829..15a8bc138b2 100644
--- a/tests/frontend/specs/pad_modal.js
+++ b/tests/frontend/specs/pad_modal.js
@@ -1,6 +1,6 @@
describe('Pad modal', function() {
context('when modal is a "force reconnect" message', function() {
- var MODAL_SELECTOR = '#connectivity .slowcommit';
+ var MODAL_SELECTOR = '#connectivity';
beforeEach(function(done) {
helper.newPad(function() {
@@ -10,7 +10,7 @@ describe('Pad modal', function() {
// wait for modal to be displayed
var $modal = helper.padChrome$(MODAL_SELECTOR);
helper.waitFor(function() {
- return $modal.is(':visible');
+ return $modal.hasClass('popup-show');
}, 50000).done(done);
});
@@ -30,7 +30,7 @@ describe('Pad modal', function() {
it('does not close the modal', function(done) {
var $modal = helper.padChrome$(MODAL_SELECTOR);
- var modalIsVisible = $modal.is(':visible');
+ var modalIsVisible = $modal.hasClass('popup-show');
expect(modalIsVisible).to.be(true);
@@ -45,7 +45,7 @@ describe('Pad modal', function() {
it('does not close the modal', function(done) {
var $modal = helper.padChrome$(MODAL_SELECTOR);
- var modalIsVisible = $modal.is(':visible');
+ var modalIsVisible = $modal.hasClass('popup-show');
expect(modalIsVisible).to.be(true);
@@ -126,6 +126,7 @@ describe('Pad modal', function() {
var isModalOpened = function(modalSelector) {
var $modal = helper.padChrome$(modalSelector);
- return $modal.is(':visible');
+
+ return $modal.hasClass('popup-show');
}
});
diff --git a/tests/frontend/specs/redo.js b/tests/frontend/specs/redo.js
index caa32feec7d..a8e874da76b 100644
--- a/tests/frontend/specs/redo.js
+++ b/tests/frontend/specs/redo.js
@@ -7,7 +7,7 @@ describe("undo button then redo button", function(){
it("redo some typing with button", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
-
+
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value
@@ -47,18 +47,12 @@ describe("undo button then redo button", function(){
var modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
- if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
-
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 90; // z
inner$("#innerdocbody").trigger(e);
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 121; // y
inner$("#innerdocbody").trigger(e);
diff --git a/tests/frontend/specs/scroll.js b/tests/frontend/specs/scroll.js
deleted file mode 100644
index 94756b85666..00000000000
--- a/tests/frontend/specs/scroll.js
+++ /dev/null
@@ -1,649 +0,0 @@
-describe('scroll when focus line is out of viewport', function () {
- before(function (done) {
- helper.newPad(function(){
- cleanPad(function(){
- forceUseMonospacedFont();
- scrollWhenPlaceCaretInTheLastLineOfViewport();
- createPadWithSeveralLines(function(){
- resizeEditorHeight();
- done();
- });
- });
- });
- this.timeout(20000);
- });
-
- context('when user presses any arrow keys on a line above the viewport', function(){
- context('and scroll percentage config is set to 0.2 on settings.json', function(){
- var lineCloseOfTopOfPad = 10;
- before(function (done) {
- setScrollPercentageWhenFocusLineIsOutOfViewport(0.2, true);
- scrollEditorToBottomOfPad();
-
- placeCaretInTheBeginningOfLine(lineCloseOfTopOfPad, function(){ // place caret in the 10th line
- // warning: even pressing right arrow, the caret does not change of position
- // the column where the caret is, it has not importance, only the line
- pressAndReleaseRightArrow();
- done();
- });
- });
-
- it('keeps the focus line scrolled 20% from the top of the viewport', function (done) {
- // default behavior is to put the line in the top of viewport, but as
- // scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.2, we have an extra 20% of lines scrolled
- // (2 lines, which are the 20% of the 10 that are visible on viewport)
- var firstLineOfViewport = getFirstLineVisibileOfViewport();
- expect(lineCloseOfTopOfPad).to.be(firstLineOfViewport + 2);
- done();
- });
- });
- });
-
- context('when user presses any arrow keys on a line below the viewport', function(){
- context('and scroll percentage config is set to 0.7 on settings.json', function(){
- var lineCloseToBottomOfPad = 50;
- before(function (done) {
- setScrollPercentageWhenFocusLineIsOutOfViewport(0.7);
-
- // firstly, scroll to make the lineCloseToBottomOfPad visible. After that, scroll to make it out of viewport
- scrollEditorToTopOfPad();
- placeCaretAtTheEndOfLine(lineCloseToBottomOfPad); // place caret in the 50th line
- setTimeout(function() {
- // warning: even pressing right arrow, the caret does not change of position
- pressAndReleaseLeftArrow();
- done();
- }, 1000);
- });
-
- it('keeps the focus line scrolled 70% from the bottom of the viewport', function (done) {
- // default behavior is to put the line in the top of viewport, but as
- // scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.7, we have an extra 70% of lines scrolled
- // (7 lines, which are the 70% of the 10 that are visible on viewport)
- var lastLineOfViewport = getLastLineVisibleOfViewport();
- expect(lineCloseToBottomOfPad).to.be(lastLineOfViewport - 7);
- done();
- });
- });
- });
-
- context('when user presses arrow up on the first line of the viewport', function(){
- context('and percentageToScrollWhenUserPressesArrowUp is set to 0.3', function () {
- var lineOnTopOfViewportWhenThePadIsScrolledDown;
- before(function (done) {
- setPercentageToScrollWhenUserPressesArrowUp(0.3);
-
- // we need some room to make the scroll up
- scrollEditorToBottomOfPad();
- lineOnTopOfViewportWhenThePadIsScrolledDown = 91;
- placeCaretAtTheEndOfLine(lineOnTopOfViewportWhenThePadIsScrolledDown);
- setTimeout(function() {
- // warning: even pressing up arrow, the caret does not change of position
- pressAndReleaseUpArrow();
- done();
- }, 1000);
- });
-
- it('keeps the focus line scrolled 30% of the top of the viewport', function (done) {
- // default behavior is to put the line in the top of viewport, but as
- // PercentageToScrollWhenUserPressesArrowUp is set to 0.3, we have an extra 30% of lines scrolled
- // (3 lines, which are the 30% of the 10 that are visible on viewport)
- var firstLineOfViewport = getFirstLineVisibileOfViewport();
- expect(firstLineOfViewport).to.be(lineOnTopOfViewportWhenThePadIsScrolledDown - 3);
- done();
- })
- });
- });
-
- context('when user edits the last line of viewport', function(){
- context('and scroll percentage config is set to 0 on settings.json', function(){
- var lastLineOfViewportBeforeEnter = 10;
- before(function () {
- // the default value
- resetScrollPercentageWhenFocusLineIsOutOfViewport();
-
- // make sure the last line on viewport is the 10th one
- scrollEditorToTopOfPad();
- placeCaretAtTheEndOfLine(lastLineOfViewportBeforeEnter);
- pressEnter();
- });
-
- it('keeps the focus line on the bottom of the viewport', function (done) {
- var lastLineOfViewportAfterEnter = getLastLineVisibleOfViewport();
- expect(lastLineOfViewportAfterEnter).to.be(lastLineOfViewportBeforeEnter + 1);
- done();
- });
- });
-
- context('and scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.3', function(){ // this value is arbitrary
- var lastLineOfViewportBeforeEnter = 9;
- before(function () {
- setScrollPercentageWhenFocusLineIsOutOfViewport(0.3);
-
- // make sure the last line on viewport is the 10th one
- scrollEditorToTopOfPad();
- placeCaretAtTheEndOfLine(lastLineOfViewportBeforeEnter);
- pressBackspace();
- });
-
- it('scrolls 30% of viewport up', function (done) {
- var lastLineOfViewportAfterEnter = getLastLineVisibleOfViewport();
- // default behavior is to scroll one line at the bottom of viewport, but as
- // scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.3, we have an extra 30% of lines scrolled
- // (3 lines, which are the 30% of the 10 that are visible on viewport)
- expect(lastLineOfViewportAfterEnter).to.be(lastLineOfViewportBeforeEnter + 3);
- done();
- });
- });
-
- context('and it is set to a value that overflow the interval [0, 1]', function(){
- var lastLineOfViewportBeforeEnter = 10;
- before(function(){
- var scrollPercentageWhenFocusLineIsOutOfViewport = 1.5;
- scrollEditorToTopOfPad();
- placeCaretAtTheEndOfLine(lastLineOfViewportBeforeEnter);
- setScrollPercentageWhenFocusLineIsOutOfViewport(scrollPercentageWhenFocusLineIsOutOfViewport);
- pressEnter();
- });
-
- it('keeps the default behavior of moving the focus line on the bottom of the viewport', function (done) {
- var lastLineOfViewportAfterEnter = getLastLineVisibleOfViewport();
- expect(lastLineOfViewportAfterEnter).to.be(lastLineOfViewportBeforeEnter + 1);
- done();
- });
- });
- });
-
- context('when user edits a line above the viewport', function(){
- context('and scroll percentage config is set to 0 on settings.json', function(){
- var lineCloseOfTopOfPad = 10;
- before(function () {
- // the default value
- setScrollPercentageWhenFocusLineIsOutOfViewport(0);
-
- // firstly, scroll to make the lineCloseOfTopOfPad visible. After that, scroll to make it out of viewport
- scrollEditorToTopOfPad();
- placeCaretAtTheEndOfLine(lineCloseOfTopOfPad); // place caret in the 10th line
- scrollEditorToBottomOfPad();
- pressBackspace(); // edit the line where the caret is, which is above the viewport
- });
-
- it('keeps the focus line on the top of the viewport', function (done) {
- var firstLineOfViewportAfterEnter = getFirstLineVisibileOfViewport();
- expect(firstLineOfViewportAfterEnter).to.be(lineCloseOfTopOfPad);
- done();
- });
- });
-
- context('and scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.2', function(){ // this value is arbitrary
- var lineCloseToBottomOfPad = 50;
- before(function () {
- // we force the line edited to be above the top of the viewport
- setScrollPercentageWhenFocusLineIsOutOfViewport(0.2, true); // set scroll jump to 20%
- scrollEditorToTopOfPad();
- placeCaretAtTheEndOfLine(lineCloseToBottomOfPad);
- scrollEditorToBottomOfPad();
- pressBackspace(); // edit line
- });
-
- it('scrolls 20% of viewport down', function (done) {
- // default behavior is to scroll one line at the top of viewport, but as
- // scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.2, we have an extra 20% of lines scrolled
- // (2 lines, which are the 20% of the 10 that are visible on viewport)
- var firstLineVisibileOfViewport = getFirstLineVisibileOfViewport();
- expect(lineCloseToBottomOfPad).to.be(firstLineVisibileOfViewport + 2);
- done();
- });
- });
- });
-
- context('when user places the caret at the last line visible of viewport', function(){
- var lastLineVisible;
- context('and scroll percentage config is set to 0 on settings.json', function(){
- before(function (done) {
- // reset to the default value
- resetScrollPercentageWhenFocusLineIsOutOfViewport();
-
- placeCaretInTheBeginningOfLine(0, function(){ // reset caret position
- scrollEditorToTopOfPad();
- lastLineVisible = getLastLineVisibleOfViewport();
- placeCaretInTheBeginningOfLine(lastLineVisible, done); // place caret in the 9th line
- });
-
- });
-
- it('does not scroll', function(done){
- setTimeout(function() {
- var lastLineOfViewport = getLastLineVisibleOfViewport();
- var lineDoesNotScroll = lastLineOfViewport === lastLineVisible;
- expect(lineDoesNotScroll).to.be(true);
- done();
- }, 1000);
- });
- });
- context('and scroll percentage config is set to 0.5 on settings.json', function(){
- before(function (done) {
- setScrollPercentageWhenFocusLineIsOutOfViewport(0.5);
- scrollEditorToTopOfPad();
- placeCaretInTheBeginningOfLine(0, function(){ // reset caret position
- // this timeout inside a callback is ugly but it necessary to give time to aceSelectionChange
- // realizes that the selection has been changed
- setTimeout(function() {
- lastLineVisible = getLastLineVisibleOfViewport();
- placeCaretInTheBeginningOfLine(lastLineVisible, done); // place caret in the 9th line
- }, 1000);
- });
- });
-
- it('scrolls line to 50% of the viewport', function(done){
- helper.waitFor(function(){
- var lastLineOfViewport = getLastLineVisibleOfViewport();
- var lastLinesScrolledFiveLinesUp = lastLineOfViewport - 5 === lastLineVisible;
- return lastLinesScrolledFiveLinesUp;
- }).done(done);
- });
- });
- });
-
- // This is a special case. When user is selecting a text with arrow down or arrow left we have
- // to keep the last line selected on focus
- context('when the first line selected is out of the viewport and user presses shift arrow down', function(){
- var lastLineOfPad = 99;
- before(function (done) {
- scrollEditorToTopOfPad();
-
- // make a selection bigger than the viewport height
- var $firstLineOfSelection = getLine(0);
- var $lastLineOfSelection = getLine(lastLineOfPad);
- var lengthOfLastLine = $lastLineOfSelection.text().length;
- helper.selectLines($firstLineOfSelection, $lastLineOfSelection, 0, lengthOfLastLine);
-
- // place the last line selected on the viewport
- scrollEditorToBottomOfPad();
-
- // press a key to make the selection goes down
- // although we can't simulate the extending of selection. It's possible to send a key event
- // which is captured on ace2_inner scroll function.
- pressAndReleaseLeftArrow(true);
- done();
- });
-
- it('keeps the last line selected on focus', function (done) {
- var lastLineOfSelectionIsVisible = isLineOnViewport(lastLineOfPad);
- expect(lastLineOfSelectionIsVisible).to.be(true);
- done();
- });
- });
-
- // In this scenario we avoid the bouncing scroll. E.g Let's suppose we have a big line that is
- // the size of the viewport, and its top is above the viewport. When user presses '<-', this line
- // will scroll down because the top is out of the viewport. When it scrolls down, the bottom of
- // line gets below the viewport so when user presses '<-' again it scrolls up to make the bottom
- // of line visible. If user presses arrow keys more than one time, the editor will keep scrolling up and down
- context('when the line height is bigger than the scroll amount percentage * viewport height', function(){
- var scrollOfEditorBeforePressKey;
- var BIG_LINE_NUMBER = 0;
- var MIDDLE_OF_BIG_LINE = 51;
- before(function (done) {
- createPadWithALineHigherThanViewportHeight(this, BIG_LINE_NUMBER, function(){
- setScrollPercentageWhenFocusLineIsOutOfViewport(0.5); // set any value to force scroll to outside to viewport
- var $bigLine = getLine(BIG_LINE_NUMBER);
-
- // each line has about 5 chars, we place the caret in the middle of the line
- helper.selectLines($bigLine, $bigLine, MIDDLE_OF_BIG_LINE, MIDDLE_OF_BIG_LINE);
-
- scrollEditorToLeaveTopAndBottomOfBigLineOutOfViewport($bigLine);
- scrollOfEditorBeforePressKey = getEditorScroll();
-
- // press a key to force to scroll
- pressAndReleaseRightArrow();
- done();
- });
- });
-
- // reset pad to the original text
- after(function (done) {
- this.timeout(5000);
- cleanPad(function(){
- createPadWithSeveralLines(function(){
- resetEditorWidth();
- done();
- });
- });
- });
-
- // as the editor.line is inside of the viewport, it should not scroll
- it('should not scroll', function (done) {
- var scrollOfEditorAfterPressKey = getEditorScroll();
- expect(scrollOfEditorAfterPressKey).to.be(scrollOfEditorBeforePressKey);
- done();
- });
- });
-
- // Some plugins, for example the ep_page_view, change the editor dimensions. This plugin, for example,
- // adds padding-top to the ace_outer, which changes the viewport height
- describe('integration with plugins which changes the margin of editor', function(){
- context('when editor dimensions changes', function(){
- before(function () {
- // reset the size of editor. Now we show more than 10 lines as in the other tests
- resetResizeOfEditorHeight();
- scrollEditorToTopOfPad();
-
- // height of the editor viewport
- var editorHeight = getEditorHeight();
-
- // add a big padding-top, 50% of the viewport
- var paddingTopOfAceOuter = editorHeight/2;
- var chrome$ = helper.padChrome$;
- var $outerIframe = chrome$('iframe');
- $outerIframe.css('padding-top', paddingTopOfAceOuter);
-
- // we set a big value to check if the scroll is made
- setScrollPercentageWhenFocusLineIsOutOfViewport(1);
- });
-
- context('and user places the caret in the last line visible of the pad', function(){
- var lastLineVisible;
- beforeEach(function (done) {
- lastLineVisible = getLastLineVisibleOfViewport();
- placeCaretInTheBeginningOfLine(lastLineVisible, done);
- });
-
- it('scrolls the line where caret is', function(done){
- helper.waitFor(function(){
- var firstLineVisibileOfViewport = getFirstLineVisibileOfViewport();
- var linesScrolled = firstLineVisibileOfViewport !== 0;
- return linesScrolled;
- }).done(done);
- });
- });
- });
- });
-
- /* ********************* Helper functions/constants ********************* */
- var TOP_OF_PAGE = 0;
- var BOTTOM_OF_PAGE = 5000; // we use a big value to force the page to be scrolled all the way down
- var LINES_OF_PAD = 100;
- var ENTER = 13;
- var BACKSPACE = 8;
- var LEFT_ARROW = 37;
- var UP_ARROW = 38;
- var RIGHT_ARROW = 39;
- var LINES_ON_VIEWPORT = 10;
- var WIDTH_OF_EDITOR_RESIZED = 100;
- var LONG_TEXT_CHARS = 100;
-
- var cleanPad = function(callback) {
- var inner$ = helper.padInner$;
- var $padContent = inner$('#innerdocbody');
- $padContent.html('');
-
- // wait for Etherpad to re-create first line
- helper.waitFor(function(){
- var lineNumber = inner$('div').length;
- return lineNumber === 1;
- }, 2000).done(callback);
- };
-
- var createPadWithSeveralLines = function(done) {
- var line = 'a ';
- var $firstLine = helper.padInner$('div').first();
- var lines = line.repeat(LINES_OF_PAD); //arbitrary number, we need to create lines that is over the viewport
- $firstLine.html(lines);
-
- helper.waitFor(function(){
- var linesCreated = helper.padInner$('div').length;
- return linesCreated === LINES_OF_PAD;
- }, 4000).done(done);
- };
-
- var createPadWithALineHigherThanViewportHeight = function(test, line, done) {
- var viewportHeight = 160; //10 lines * 16px (height of line)
- test.timeout(5000);
- cleanPad(function(){
- // make the editor smaller to make test easier
- // with that width the each line has about 5 chars
- resizeEditorWidth();
-
- // we create a line with 100 chars, which makes about 20 lines
- setLongTextOnLine(line);
- helper.waitFor(function () {
- var $firstLine = getLine(line);
-
- var heightOfLine = $firstLine.get(0).getBoundingClientRect().height;
- return heightOfLine >= viewportHeight;
- }, 4000).done(done);
- });
- };
-
- var setLongTextOnLine = function(line) {
- var $line = getLine(line);
- var longText = 'a'.repeat(LONG_TEXT_CHARS);
- $line.html(longText);
- };
-
- // resize the editor to make the tests easier
- var resizeEditorHeight = function() {
- var chrome$ = helper.padChrome$;
- chrome$('#editorcontainer').css('height', getSizeOfViewport());
- };
-
- // this makes about 5 chars per line
- var resizeEditorWidth = function() {
- var chrome$ = helper.padChrome$;
- chrome$('#editorcontainer').css('width', WIDTH_OF_EDITOR_RESIZED);
- };
-
- var resetResizeOfEditorHeight = function() {
- var chrome$ = helper.padChrome$;
- chrome$('#editorcontainer').css('height', '');
- };
-
- var resetEditorWidth = function () {
- var chrome$ = helper.padChrome$;
- chrome$('#editorcontainer').css('width', '');
- };
-
- var getEditorHeight = function() {
- var chrome$ = helper.padChrome$;
- var $editor = chrome$('#editorcontainer');
- var editorHeight = $editor.get(0).clientHeight;
- return editorHeight;
- };
-
- var getSizeOfViewport = function() {
- return getLinePositionOnViewport(LINES_ON_VIEWPORT) - getLinePositionOnViewport(0);
- };
-
- var scrollPageTo = function(value) {
- var outer$ = helper.padOuter$;
- var $ace_outer = outer$('#outerdocbody').parent();
- $ace_outer.parent().scrollTop(value);
- };
-
- var scrollEditorToTopOfPad = function() {
- scrollPageTo(TOP_OF_PAGE);
- };
-
- var scrollEditorToBottomOfPad = function() {
- scrollPageTo(BOTTOM_OF_PAGE);
- };
-
- var scrollEditorToLeaveTopAndBottomOfBigLineOutOfViewport = function ($bigLine) {
- var lineHeight = $bigLine.get(0).getBoundingClientRect().height;
- var middleOfLine = lineHeight/2;
- scrollPageTo(middleOfLine);
- };
-
- var getLine = function(lineNum) {
- var inner$ = helper.padInner$;
- var $line = inner$('div').eq(lineNum);
- return $line;
- };
-
- var placeCaretAtTheEndOfLine = function(lineNum) {
- var $targetLine = getLine(lineNum);
- var lineLength = $targetLine.text().length;
- helper.selectLines($targetLine, $targetLine, lineLength, lineLength);
- };
-
- var placeCaretInTheBeginningOfLine = function(lineNum, cb) {
- var $targetLine = getLine(lineNum);
- helper.selectLines($targetLine, $targetLine, 0, 0);
- helper.waitFor(function() {
- var $lineWhereCaretIs = getLineWhereCaretIs();
- return $targetLine.get(0) === $lineWhereCaretIs.get(0);
- }).done(cb);
- };
-
- var getLineWhereCaretIs = function() {
- var inner$ = helper.padInner$;
- var nodeWhereCaretIs = inner$.document.getSelection().anchorNode;
- var $lineWhereCaretIs = $(nodeWhereCaretIs).closest('div');
- return $lineWhereCaretIs;
- };
-
- var getFirstLineVisibileOfViewport = function() {
- return _.find(_.range(0, LINES_OF_PAD - 1), isLineOnViewport);
- };
-
- var getLastLineVisibleOfViewport = function() {
- return _.find(_.range(LINES_OF_PAD - 1, 0, -1), isLineOnViewport);
- };
-
- var pressKey = function(keyCode, shiftIsPressed){
- var inner$ = helper.padInner$;
- var evtType;
- if(inner$(window)[0].bowser.modernIE){ // if it's IE
- evtType = 'keypress';
- }else{
- evtType = 'keydown';
- }
- var e = inner$.Event(evtType);
- e.shiftKey = shiftIsPressed;
- e.keyCode = keyCode;
- e.which = keyCode; // etherpad listens to 'which'
- inner$('#innerdocbody').trigger(e);
- };
-
- var releaseKey = function(keyCode){
- var inner$ = helper.padInner$;
- var evtType = 'keyup';
- var e = inner$.Event(evtType);
- e.keyCode = keyCode;
- e.which = keyCode; // etherpad listens to 'which'
- inner$('#innerdocbody').trigger(e);
- };
-
- var pressEnter = function() {
- pressKey(ENTER);
- };
-
- var pressBackspace = function() {
- pressKey(BACKSPACE);
- };
-
- var pressAndReleaseUpArrow = function() {
- pressKey(UP_ARROW);
- releaseKey(UP_ARROW);
- };
-
- var pressAndReleaseRightArrow = function() {
- pressKey(RIGHT_ARROW);
- releaseKey(RIGHT_ARROW);
- };
-
- var pressAndReleaseLeftArrow = function(shiftIsPressed) {
- pressKey(LEFT_ARROW, shiftIsPressed);
- releaseKey(LEFT_ARROW);
- };
-
- var isLineOnViewport = function(lineNumber) {
- // in the function scrollNodeVerticallyIntoView from ace2_inner.js, iframePadTop is used to calculate
- // how much scroll is needed. Although the name refers to padding-top, this value is not set on the
- // padding-top.
- var iframePadTop = 8;
- var $line = getLine(lineNumber);
- var linePosition = $line.get(0).getBoundingClientRect();
-
- // position relative to the current viewport
- var linePositionTopOnViewport = linePosition.top - getEditorScroll() + iframePadTop;
- var linePositionBottomOnViewport = linePosition.bottom - getEditorScroll();
-
- var lineBellowTop = linePositionBottomOnViewport > 0;
- var lineAboveBottom = linePositionTopOnViewport < getClientHeightVisible();
- var isVisible = lineBellowTop && lineAboveBottom;
-
- return isVisible;
- };
-
- var getEditorScroll = function () {
- var outer$ = helper.padOuter$;
- var scrollTopFirefox = outer$('#outerdocbody').parent().scrollTop(); // works only on firefox
- var scrollTop = outer$('#outerdocbody').scrollTop() || scrollTopFirefox;
- return scrollTop;
- };
-
- // clientHeight includes padding, so we have to subtract it and consider only the visible viewport
- var getClientHeightVisible = function () {
- var outer$ = helper.padOuter$;
- var $ace_outer = outer$('#outerdocbody').parent();
- var ace_outerHeight = $ace_outer.get(0).clientHeight;
- var ace_outerPaddingTop = getIntValueOfCSSProperty($ace_outer, 'padding-top');
- var paddingAddedWhenPageViewIsEnable = getPaddingAddedWhenPageViewIsEnable();
- var clientHeight = ace_outerHeight - ( ace_outerPaddingTop + paddingAddedWhenPageViewIsEnable);
-
- return clientHeight;
- };
-
- // ep_page_view changes the dimensions of the editor. We have to guarantee
- // the viewport height is calculated right
- var getPaddingAddedWhenPageViewIsEnable = function () {
- var chrome$ = helper.padChrome$;
- var $outerIframe = chrome$('iframe');
- var paddingAddedWhenPageViewIsEnable = parseInt($outerIframe.css('padding-top'));
- return paddingAddedWhenPageViewIsEnable;
- };
-
- var getIntValueOfCSSProperty = function($element, property){
- var valueString = $element.css(property);
- return parseInt(valueString) || 0;
- };
-
- var forceUseMonospacedFont = function () {
- helper.padChrome$.window.clientVars.padOptions.useMonospaceFont = true;
- };
-
- var setScrollPercentageWhenFocusLineIsOutOfViewport = function(value, editionAboveViewport) {
- var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
- if (editionAboveViewport) {
- scrollSettings.percentage.editionAboveViewport = value;
- }else{
- scrollSettings.percentage.editionBelowViewport = value;
- }
- };
-
- var resetScrollPercentageWhenFocusLineIsOutOfViewport = function() {
- var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
- scrollSettings.percentage.editionAboveViewport = 0;
- scrollSettings.percentage.editionBelowViewport = 0;
- };
-
- var setPercentageToScrollWhenUserPressesArrowUp = function (value) {
- var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
- scrollSettings.percentageToScrollWhenUserPressesArrowUp = value;
- };
-
- var scrollWhenPlaceCaretInTheLastLineOfViewport = function() {
- var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
- scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport = true;
- };
-
- var getLinePositionOnViewport = function(lineNumber) {
- var $line = getLine(lineNumber);
- var linePosition = $line.get(0).getBoundingClientRect();
-
- // position relative to the current viewport
- return linePosition.top - getEditorScroll();
- };
-});
-
diff --git a/tests/frontend/specs/select_formatting_buttons.js b/tests/frontend/specs/select_formatting_buttons.js
index b6ec6d0c3e9..57285895783 100644
--- a/tests/frontend/specs/select_formatting_buttons.js
+++ b/tests/frontend/specs/select_formatting_buttons.js
@@ -88,13 +88,7 @@ describe("select formatting buttons when selection has style applied", function(
//select this text element
$firstTextElement.sendkeys('{selectall}');
- if(inner$(window)[0].bowser.modernIE){ // if it's IE
- var evtType = "keypress";
- }else{
- var evtType = "keydown";
- }
-
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = key.charCodeAt(0); // I, U, B, 5
inner$("#innerdocbody").trigger(e);
diff --git a/tests/frontend/specs/strikethrough.js b/tests/frontend/specs/strikethrough.js
index 9afcea0fd5e..dc37b36f451 100644
--- a/tests/frontend/specs/strikethrough.js
+++ b/tests/frontend/specs/strikethrough.js
@@ -6,22 +6,22 @@ describe("strikethrough button", function(){
});
it("makes text strikethrough", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
-
+
//select this text element
$firstTextElement.sendkeys('{selectall}');
//get the strikethrough button and click it
var $strikethroughButton = chrome$(".buttonicon-strikethrough");
$strikethroughButton.click();
-
+
//ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
-
+
// is there a element now?
var isstrikethrough = $newFirstTextElement.find("s").length === 1;
diff --git a/tests/frontend/specs/timeslider.js b/tests/frontend/specs/timeslider.js
index cb37bacb6ea..bca80ba495c 100644
--- a/tests/frontend/specs/timeslider.js
+++ b/tests/frontend/specs/timeslider.js
@@ -8,7 +8,7 @@ xdescribe("timeslider button takes you to the timeslider of a pad", function(){
it("timeslider contained in URL", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
-
+
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value
diff --git a/tests/frontend/specs/timeslider_labels.js b/tests/frontend/specs/timeslider_labels.js
index d39685efe43..09213a45227 100644
--- a/tests/frontend/specs/timeslider_labels.js
+++ b/tests/frontend/specs/timeslider_labels.js
@@ -6,9 +6,9 @@ describe("timeslider", function(){
});
it("Shows a date and time in the timeslider and make sure it doesn't include NaN", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
-
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
// make some changes to produce 100 revisions
var revs = 10;
this.timeout(60000);
@@ -18,15 +18,15 @@ describe("timeslider", function(){
inner$("div").first().sendkeys('a');
}, 200);
}
-
+
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
-
+
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
-
+
var latestContents = timeslider$('#padcontent').text();
// Expect the date and time to be shown
@@ -36,17 +36,17 @@ describe("timeslider", function(){
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 45;
$sliderBar.trigger(e);
-
+
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 40;
$sliderBar.trigger(e);
-
+
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 50;
$sliderBar.trigger(e);
-
+
$sliderBar.trigger('mouseup')
setTimeout(function() {
diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js
index 2afd2e9d318..67123344bba 100644
--- a/tests/frontend/specs/timeslider_revisions.js
+++ b/tests/frontend/specs/timeslider_revisions.js
@@ -6,12 +6,12 @@ describe("timeslider", function(){
});
it("loads adds a hundred revisions", function(done) { // passes
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
-
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
// make some changes to produce 100 revisions
var timePerRev = 900
- , revs = 100;
+ , revs = 99;
this.timeout(revs*timePerRev+10000);
for(var i=0; i < revs; i++) {
setTimeout(function() {
@@ -20,43 +20,45 @@ describe("timeslider", function(){
}, timePerRev*i);
}
chrome$('.buttonicon-savedRevision').click();
-
+
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
-
+
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
-
- var latestContents = timeslider$('#padcontent').text();
+
+ var latestContents = timeslider$('#innerdocbody').text();
// Click somewhere on the timeslider
var e = new jQuery.Event('mousedown');
+ // sets y co-ordinate of the pad slider modal.
+ var base = (timeslider$('#ui-slider-bar').offset().top - 24)
e.clientX = e.pageX = 150;
- e.clientY = e.pageY = 45;
+ e.clientY = e.pageY = base+5;
$sliderBar.trigger(e);
-
+
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
- e.clientY = e.pageY = 40;
+ e.clientY = e.pageY = base;
$sliderBar.trigger(e);
-
+
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
- e.clientY = e.pageY = 50;
+ e.clientY = e.pageY = base-5;
$sliderBar.trigger(e);
-
+
$sliderBar.trigger('mouseup')
setTimeout(function() {
//make sure the text has changed
- expect( timeslider$('#padcontent').text() ).not.to.eql( latestContents );
+ expect( timeslider$('#innerdocbody').text() ).not.to.eql( latestContents );
var starIsVisible = timeslider$('.star').is(":visible");
expect( starIsVisible ).to.eql( true );
done();
}, 1000);
-
+
}, 6000);
}, revs*timePerRev);
});
@@ -64,9 +66,9 @@ describe("timeslider", function(){
// Disabled as jquery trigger no longer works properly
xit("changes the url when clicking on the timeslider", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
-
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
+
// make some changes to produce 7 revisions
var timePerRev = 1000
, revs = 20;
@@ -77,24 +79,24 @@ describe("timeslider", function(){
inner$("div").first().sendkeys('a');
}, timePerRev*i);
}
-
+
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
-
+
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
-
- var latestContents = timeslider$('#padcontent').text();
+
+ var latestContents = timeslider$('#innerdocbody').text();
var oldUrl = $('#iframe-container iframe')[0].contentWindow.location.hash;
-
+
// Click somewhere on the timeslider
var e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 60;
$sliderBar.trigger(e);
-
+
helper.waitFor(function(){
return $('#iframe-container iframe')[0].contentWindow.location.hash != oldUrl;
}, 6000).always(function(){
@@ -105,7 +107,7 @@ describe("timeslider", function(){
}, revs*timePerRev);
});
it("jumps to a revision given in the url", function(done) {
- var inner$ = helper.padInner$;
+ var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
this.timeout(20000);
@@ -118,7 +120,7 @@ describe("timeslider", function(){
expect( oldLength ).to.not.eql( 0 );
inner$("div").first().sendkeys('a');
var timeslider$;
-
+
// wait for our additional revision to be added
helper.waitFor(function(){
// newLines takes the new lines into account which are strippen when using
@@ -131,17 +133,17 @@ describe("timeslider", function(){
}, 6000).always(function() {
// go to timeslider with a specific revision set
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
-
+
// wait for the timeslider to be loaded
helper.waitFor(function(){
try {
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
} catch(e){}
if(timeslider$){
- return timeslider$('#padcontent').text().length == oldLength;
+ return timeslider$('#innerdocbody').text().length == oldLength;
}
}, 6000).always(function(){
- expect( timeslider$('#padcontent').text().length ).to.eql( oldLength );
+ expect( timeslider$('#innerdocbody').text().length ).to.eql( oldLength );
done();
});
});
@@ -149,17 +151,17 @@ describe("timeslider", function(){
});
it("checks the export url", function(done) {
- var inner$ = helper.padInner$;
- var chrome$ = helper.padChrome$;
+ var inner$ = helper.padInner$;
+ var chrome$ = helper.padChrome$;
this.timeout(11000);
inner$("div").first().sendkeys('a');
-
+
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
var timeslider$;
var exportLink;
-
+
helper.waitFor(function(){
try{
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
diff --git a/tests/frontend/specs/undo.js b/tests/frontend/specs/undo.js
index 3644734f4dd..172a2b81e7b 100644
--- a/tests/frontend/specs/undo.js
+++ b/tests/frontend/specs/undo.js
@@ -4,11 +4,10 @@ describe("undo button", function(){
this.timeout(60000);
});
-/*
it("undo some typing by clicking undo button", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
-
+
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value
@@ -30,7 +29,6 @@ describe("undo button", function(){
done();
});
});
-*/
it("undo some typing using a keypress", function(done){
var inner$ = helper.padInner$;
@@ -44,13 +42,7 @@ describe("undo button", function(){
var modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
- /*
- * ACHTUNG: this is the only place in the test codebase in which a keydown
- * is sent for IE. Everywhere else IE uses keypress.
- */
- var evtType = "keydown";
-
- var e = inner$.Event(evtType);
+ var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 90; // z
inner$("#innerdocbody").trigger(e);
diff --git a/tests/frontend/specs/unordered_list.js b/tests/frontend/specs/unordered_list.js
index 4ea77b8ac7b..8a7fdd7a7b9 100644
--- a/tests/frontend/specs/unordered_list.js
+++ b/tests/frontend/specs/unordered_list.js
@@ -6,7 +6,7 @@ describe("assign unordered list", function(){
});
it("insert unordered list text then removes by outdent", function(done){
- var inner$ = helper.padInner$;
+ var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var originalText = inner$("div").first().text();
diff --git a/tests/frontend/travis/remote_runner.js b/tests/frontend/travis/remote_runner.js
index 87311d905d1..09c892f6676 100644
--- a/tests/frontend/travis/remote_runner.js
+++ b/tests/frontend/travis/remote_runner.js
@@ -67,41 +67,39 @@ var sauceTestWorker = async.queue(function (testSettings, callback) {
});
}, 5); //run 5 tests in parrallel
-// Firefox
+// 1) Firefox on Linux
sauceTestWorker.push({
'platform' : 'Linux'
, 'browserName' : 'firefox'
- , 'version' : ''
+ , 'version' : 'latest'
});
-// Chrome
+// 2) Chrome on Linux
sauceTestWorker.push({
'platform' : 'Linux'
, 'browserName' : 'googlechrome'
- , 'version' : ''
+ , 'version' : 'latest'
});
-/*
-// IE 8
+// 3) Safari on OSX 10.15
sauceTestWorker.push({
- 'platform' : 'Windows 2003'
- , 'browserName' : 'iexplore'
- , 'version' : '8'
+ 'platform' : 'OS X 10.15'
+ , 'browserName' : 'safari'
+ , 'version' : 'latest'
});
-*/
-// IE 9
+// 4) IE 10 on Win 8
sauceTestWorker.push({
- 'platform' : 'Windows XP'
+ 'platform' : 'Windows 8'
, 'browserName' : 'iexplore'
- , 'version' : '9'
+ , 'version' : '10.0'
});
-// IE 10
+// 5) Edge on Win 10
sauceTestWorker.push({
- 'platform' : 'Windows 2012'
- , 'browserName' : 'iexplore'
- , 'version' : '10'
+ 'platform' : 'Windows 10'
+ , 'browserName' : 'microsoftedge'
+ , 'version' : 'latest'
});
sauceTestWorker.drain = function() {
diff --git a/tests/frontend/travis/runner.sh b/tests/frontend/travis/runner.sh
index 1ae5f6ff045..2a4d76bd2aa 100755
--- a/tests/frontend/travis/runner.sh
+++ b/tests/frontend/travis/runner.sh
@@ -1,13 +1,48 @@
-#!/bin/sh
+#!/bin/bash
+if [ -z "${SAUCE_USERNAME}" ]; then echo "SAUCE_USERNAME is unset - exiting"; exit 1; fi
+if [ -z "${SAUCE_ACCESS_KEY}" ]; then echo "SAUCE_ACCESS_KEY is unset - exiting"; exit 1; fi
-#Move to the base folder
-cd `dirname $0`
+# do not continue if there is an error
+set -eu
-#start Etherpad
-../../../bin/run.sh > /dev/null &
-sleep 10
+# source: https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128
+MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
-#start remote runner
+# reliably move to the etherpad base folder before running it
+cd "${MY_DIR}/../../../"
+
+# start Etherpad, assuming all dependencies are already installed.
+#
+# This is possible because the "install" section of .travis.yml already contains
+# a call to bin/installDeps.sh
+echo "Running Etherpad directly, assuming bin/installDeps.sh has already been run"
+node node_modules/ep_etherpad-lite/node/server.js "${@}" > /dev/null &
+
+echo "Now I will try for 15 seconds to connect to Etherpad on http://localhost:9001"
+
+# wait for at most 15 seconds until Etherpad starts accepting connections
+#
+# modified from:
+# https://unix.stackexchange.com/questions/5277/how-do-i-tell-a-script-to-wait-for-a-process-to-start-accepting-requests-on-a-po#349138
+#
+(timeout 15 bash -c 'until echo > /dev/tcp/localhost/9001; do sleep 0.5; done') || \
+ (echo "Could not connect to Etherpad on http://localhost:9001" ; exit 1)
+
+echo "Successfully connected to Etherpad on http://localhost:9001"
+
+# just in case, let's wait for another second before going on
+sleep 1
+
+# On the Travis VM, remote_runner.js is found at
+# /home/travis/build/ether/[secure]/tests/frontend/travis/remote_runner.js
+# which is the same directory that contains this script.
+# Let's move back there.
+#
+# Probably remote_runner.js is injected by Saucelabs.
+cd "${MY_DIR}"
+
+# start the remote runner
+echo "Now starting the remote runner"
node remote_runner.js
exit_code=$?
@@ -15,4 +50,4 @@ kill $!
kill $(cat /tmp/sauce.pid)
sleep 30
-exit $exit_code
\ No newline at end of file
+exit $exit_code
diff --git a/tests/frontend/travis/runnerBackend.sh b/tests/frontend/travis/runnerBackend.sh
new file mode 100755
index 00000000000..50e5ce36b77
--- /dev/null
+++ b/tests/frontend/travis/runnerBackend.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+# do not continue if there is an error
+set -eu
+
+# source: https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128
+MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+# reliably move to the etherpad base folder before running it
+cd "${MY_DIR}/../../../"
+
+# start Etherpad, assuming all dependencies are already installed.
+#
+# This is possible because the "install" section of .travis.yml already contains
+# a call to bin/installDeps.sh
+echo "Running Etherpad directly, assuming bin/installDeps.sh has already been run"
+node node_modules/ep_etherpad-lite/node/server.js "${@}" > /dev/null &
+
+echo "Now I will try for 15 seconds to connect to Etherpad on http://localhost:9001"
+
+# wait for at most 15 seconds until Etherpad starts accepting connections
+#
+# modified from:
+# https://unix.stackexchange.com/questions/5277/how-do-i-tell-a-script-to-wait-for-a-process-to-start-accepting-requests-on-a-po#349138
+#
+(timeout 15 bash -c 'until echo > /dev/tcp/localhost/9001; do sleep 0.5; done') || \
+ (echo "Could not connect to Etherpad on http://localhost:9001" ; exit 1)
+
+echo "Successfully connected to Etherpad on http://localhost:9001"
+
+# just in case, let's wait for another second before going on
+sleep 1
+
+# a copy of settings.json is necessary for the backend tests to work
+cp settings.json.template settings.json
+
+# run the backend tests
+echo "Now run the backend tests"
+cd src
+npm run test
+exit_code=$?
+
+kill $!
+sleep 5
+
+exit $exit_code
diff --git a/tests/frontend/travis/sauce_tunnel.sh b/tests/frontend/travis/sauce_tunnel.sh
index b19268d0583..b8e1bc59e58 100755
--- a/tests/frontend/travis/sauce_tunnel.sh
+++ b/tests/frontend/travis/sauce_tunnel.sh
@@ -1,11 +1,20 @@
#!/bin/bash
# download and unzip the sauce connector
-curl https://saucelabs.com/downloads/sc-latest-linux.tar.gz > /tmp/sauce.tar.gz
+#
+# ACHTUNG: as of 2019-12-21, downloading sc-latest-linux.tar.gz does not work.
+# It is necessary to explicitly download a specific version, for
+# example https://saucelabs.com/downloads/sc-4.5.4-linux.tar.gz
+# Supported versions are currently listed at:
+# https://wiki.saucelabs.com/display/DOCS/Downloading+Sauce+Connect+Proxy
+if [ -z "${SAUCE_USERNAME}" ]; then echo "SAUCE_USERNAME is unset - exiting"; exit 1; fi
+if [ -z "${SAUCE_ACCESS_KEY}" ]; then echo "SAUCE_ACCESS_KEY is unset - exiting"; exit 1; fi
+
+curl https://saucelabs.com/downloads/sc-4.5.4-linux.tar.gz > /tmp/sauce.tar.gz
tar zxf /tmp/sauce.tar.gz --directory /tmp
mv /tmp/sc-*-linux /tmp/sauce_connect
# start the sauce connector in background and make sure it doesn't output the secret key
-(/tmp/sauce_connect/bin/sc --user $SAUCE_USERNAME --key $SAUCE_ACCESS_KEY --pidfile /tmp/sauce.pid --readyfile /tmp/tunnel > /dev/null )&
+(/tmp/sauce_connect/bin/sc --user "${SAUCE_USERNAME}" --key "${SAUCE_ACCESS_KEY}" --pidfile /tmp/sauce.pid --readyfile /tmp/tunnel > /dev/null )&
# wait for the tunnel to build up
while [ ! -e "/tmp/tunnel" ]