From d80eb55f05a82029ef2942c301b3f68d21112cb5 Mon Sep 17 00:00:00 2001 From: KUNDU Date: Mon, 22 Jan 2018 11:17:08 +0000 Subject: [PATCH 001/195] Added config 3 to reflect WYSIWYG config in as is state GCMS-745 --- fields/oup-ckeditor-field.js | 63 ++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 0dfffb2..867e4d5 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -36,6 +36,69 @@ define(function(require, exports, module) { "Source" ] ] + }, + "config3" : { + "toolbar": [ + [ + "Format", + "Font", + "FontSize", + "Styles" + ], + [ + "cloudcms-image" + ], + [ + "Bold", + "Italic", + "-", + "Undo", + "Redo", + "-", + "Cut", + "Copy", + "Paste", + "Find", + "Replace", + "-", + "Outdent", + "Indent", + "-", + "Print" + ], + [ + "NumberedList", + "BulletedList", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Table", + "-", + "Link", + "Smiley", + "TextColor", + "BGColor", + "Source" + ] + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP Custom Inline", + "element": "span", + "attributes": { + "class": "mine" + } + } + ] } }, From db446c484f8c755eb020bc25aff86651ce1b5581 Mon Sep 17 00:00:00 2001 From: KUNDU Date: Wed, 21 Mar 2018 15:09:18 +0000 Subject: [PATCH 002/195] Implemented GCMS-1066 as per given configs --- fields/oup-ckeditor-field.js | 139 ++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 867e4d5..d2543cb 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -13,91 +13,110 @@ define(function(require, exports, module) { "config1" : { "toolbar": [ [ - "Format", - "Font", - "FontSize" + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor", + "cloudcms-link" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], [ "Bold", "Italic", - "Underline", - "StrikeThrough", + "Strike", + "Subscript", + "Superscript", "-", - "Cut", - "Undo", - "Redo" - ] + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format" + ], + [ + "cloudcms-image" + ] ] }, "config2" : { "toolbar": [ [ - "Font", - "Source" - ] + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] ] }, "config3" : { "toolbar": [ [ - "Format", - "Font", - "FontSize", - "Styles" - ], - [ - "cloudcms-image" - ], - [ - "Bold", - "Italic", - "-", - "Undo", - "Redo", - "-", "Cut", "Copy", "Paste", - "Find", - "Replace", - "-", - "Outdent", - "Indent", "-", - "Print" + "Undo", + "Redo" ], [ - "NumberedList", - "BulletedList", + "Link", + "Unlink", + "cloudcms-link" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" + "RemoveFormat" + ], + [ + "Format" ], [ - "Table", - "-", + "cloudcms-image" + ] + ] + }, + "config4" : { + "toolbar": [ + [ "Link", - "Smiley", - "TextColor", - "BGColor", - "Source" - ] - ], - "removeButtons": null, - "stylesSet": [ - { - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP Custom Inline", - "element": "span", - "attributes": { - "class": "mine" - } - } + "Unlink", + "cloudcms-link" + ] ] } }, From 0beb117259bec803411fc3a7994a1a3344f86dff Mon Sep 17 00:00:00 2001 From: padgettm Date: Fri, 23 Mar 2018 12:48:46 +0000 Subject: [PATCH 003/195] added config for cloudcms-link --- fields/oup-ckeditor-field.js | 343 ++++++++++++++++++----------------- 1 file changed, 172 insertions(+), 171 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d2543cb..3ab3022 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -1,186 +1,187 @@ -define(function(require, exports, module) { +define(function (require, exports, module) { - var $ = require("jquery"); + var $ = require("jquery"); - var Alpaca = $.alpaca; + var Alpaca = $.alpaca; - Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( - /** - * @lends Alpaca.Fields.OUPCKEditorField.prototype - */ - { - toolbarOptions: { - "config1" : { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor", - "cloudcms-link" - ], - [ - "Table", - "HorizontalRule", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" - ], - [ - "Format" - ], - [ - "cloudcms-image" - ] - ] - }, - "config2" : { - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, - "config3" : { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "cloudcms-link" - ], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat" - ], - [ - "Format" - ], - [ - "cloudcms-image" - ] - ] - }, - "config4" : { - "toolbar": [ - [ - "Link", - "Unlink", - "cloudcms-link" - ] - ] - } - }, + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( + /** + * @lends Alpaca.Fields.OUPCKEditorField.prototype + */ + { + toolbarOptions: { + "config1": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor", + "cloudcms-link" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format" + ], + [ + "cloudcms-image" + ] + ] + }, + "config2": { + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + }, + "config3": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "cloudcms-link" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat" + ], + [ + "Format" + ], + [ + "cloudcms-image" + ] + ] + }, + "config4": { + "toolbar": [ + [ + "Link", + "Unlink", + "cloudcms-link" + ] + ], + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "initialContainerPath": "/" + } + } + }, - /** - * @see Alpaca.Fields.TextField#getFieldType - */ - getFieldType: function() { - return "oup-ckeditor-field"; - }, + /** + * @see Alpaca.Fields.TextField#getFieldType + */ + getFieldType: function () { + return "oup-ckeditor-field"; + }, - /** - * @see Alpaca.Fields.TextField#setup - */ - setup: function() - { - if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { - this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] - } + /** + * @see Alpaca.Fields.TextField#setup + */ + setup: function () { + if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { + this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] + } - this.base(); - }, + this.base(); + }, - afterRenderControl: function(model, callback) - { - var self = this; - self.base(model, function(){ - callback(); - }); - }, + afterRenderControl: function (model, callback) { + var self = this; + self.base(model, function () { + callback(); + }); + }, - /** - * @see Alpaca.Fields.TextField#handleValidate - */ - handleValidate: function() - { - return this.base(); - } + /** + * @see Alpaca.Fields.TextField#handleValidate + */ + handleValidate: function () { + return this.base(); + } - /* builder_helpers */ - , + /* builder_helpers */ + , - /** - * @see Alpaca.Fields.TextField#getTitle - */ - getTitle: function() { - return "OUP ckeditor Field"; - }, + /** + * @see Alpaca.Fields.TextField#getTitle + */ + getTitle: function () { + return "OUP ckeditor Field"; + }, - /** - * @see Alpaca.Fields.TextField#getDescription - */ - getDescription: function() { - return "Allow output markup rule overrides to ckeditor"; - } + /** + * @see Alpaca.Fields.TextField#getDescription + */ + getDescription: function () { + return "Allow output markup rule overrides to ckeditor"; + } - /* end_builder_helpers */ - }); + /* end_builder_helpers */ + }); - Alpaca.registerMessages({ - "noDependentField": "No local config found" - }); - - Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); + Alpaca.registerMessages({ + "noDependentField": "No local config found" + }); -}); + Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); + }); \ No newline at end of file From 8068297ab8ae561957d19988c8dfae24de196a0d Mon Sep 17 00:00:00 2001 From: padgettm Date: Fri, 23 Mar 2018 12:53:50 +0000 Subject: [PATCH 004/195] added config for cloudcms-link --- fields/oup-ckeditor-field.js | 343 ++++++++++++++++++----------------- 1 file changed, 172 insertions(+), 171 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 3ab3022..3fd59b8 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -1,187 +1,188 @@ define(function (require, exports, module) { - var $ = require("jquery"); + var $ = require("jquery"); - var Alpaca = $.alpaca; + var Alpaca = $.alpaca; - Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( - /** - * @lends Alpaca.Fields.OUPCKEditorField.prototype - */ - { - toolbarOptions: { - "config1": { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor", - "cloudcms-link" - ], - [ - "Table", - "HorizontalRule", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" - ], - [ - "Format" - ], - [ - "cloudcms-image" - ] - ] - }, - "config2": { - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, - "config3": { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "cloudcms-link" - ], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat" - ], - [ - "Format" - ], - [ - "cloudcms-image" - ] - ] - }, - "config4": { - "toolbar": [ - [ - "Link", - "Unlink", - "cloudcms-link" - ] - ], - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "initialContainerPath": "/" - } - } - }, + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( + /** + * @lends Alpaca.Fields.OUPCKEditorField.prototype + */ + { + toolbarOptions: { + "config1": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor", + "cloudcms-link" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format" + ], + [ + "cloudcms-image" + ] + ] + }, + "config2": { + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + }, + "config3": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "cloudcms-link" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat" + ], + [ + "Format" + ], + [ + "cloudcms-image" + ] + ] + }, + "config4": { + "toolbar": [ + [ + "Link", + "Unlink", + "cloudcms-link" + ] + ], + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "initialContainerPath": "/" + } + } + }, + }, - /** - * @see Alpaca.Fields.TextField#getFieldType - */ - getFieldType: function () { - return "oup-ckeditor-field"; - }, + /** + * @see Alpaca.Fields.TextField#getFieldType + */ + getFieldType: function () { + return "oup-ckeditor-field"; + }, - /** - * @see Alpaca.Fields.TextField#setup - */ - setup: function () { - if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { - this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] - } + /** + * @see Alpaca.Fields.TextField#setup + */ + setup: function () { + if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { + this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] + } - this.base(); - }, + this.base(); + }, - afterRenderControl: function (model, callback) { - var self = this; - self.base(model, function () { - callback(); - }); - }, + afterRenderControl: function (model, callback) { + var self = this; + self.base(model, function () { + callback(); + }); + }, - /** - * @see Alpaca.Fields.TextField#handleValidate - */ - handleValidate: function () { - return this.base(); - } + /** + * @see Alpaca.Fields.TextField#handleValidate + */ + handleValidate: function () { + return this.base(); + } - /* builder_helpers */ - , + /* builder_helpers */ + , - /** - * @see Alpaca.Fields.TextField#getTitle - */ - getTitle: function () { - return "OUP ckeditor Field"; - }, + /** + * @see Alpaca.Fields.TextField#getTitle + */ + getTitle: function () { + return "OUP ckeditor Field"; + }, - /** - * @see Alpaca.Fields.TextField#getDescription - */ - getDescription: function () { - return "Allow output markup rule overrides to ckeditor"; - } + /** + * @see Alpaca.Fields.TextField#getDescription + */ + getDescription: function () { + return "Allow output markup rule overrides to ckeditor"; + } - /* end_builder_helpers */ - }); + /* end_builder_helpers */ + }); - Alpaca.registerMessages({ - "noDependentField": "No local config found" - }); + Alpaca.registerMessages({ + "noDependentField": "No local config found" + }); - Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); + Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); - }); \ No newline at end of file +}); \ No newline at end of file From 1b5cdbacee2d49cf4403d50016ce49059fd5a192 Mon Sep 17 00:00:00 2001 From: KUNDU Date: Thu, 29 Mar 2018 13:40:32 +0100 Subject: [PATCH 005/195] commit micks changes --- fields/oup-ckeditor-field.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 3fd59b8..a5b8411 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -125,6 +125,27 @@ define(function (require, exports, module) { } } }, + "config5" : { + "toolbar": [ + ['Format', 'Font', 'FontSize'], + ['Bold', 'Italic', 'Underline'], + ['Image', 'Table', 'Source'], + ['cloudcms-image', 'cloudcms-link'] + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "/Images", + "imagePickerConfig": { + "initialContainerPath": "/Images" + } + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "initialContainerPath": "/" + } + } + }, }, /** From 0fcecbc357235c08ce3097aa067edc0beb4be675 Mon Sep 17 00:00:00 2001 From: KUNDU Date: Thu, 29 Mar 2018 13:45:22 +0100 Subject: [PATCH 006/195] GCMS-922 --- fields/oup-ckeditor-field.js | 44 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index a5b8411..cbaceeb 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -59,12 +59,28 @@ define(function (require, exports, module) { "JustifyRight", "JustifyBlock" ], - [ - "Format" - ], [ "cloudcms-image" ] + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" + }, + { + "name": "Heading 2", + "element": "H2" + }, + { + "name": "Heading 3", + "element": "H3" + }, + { + "name": "Heading 4", + "element": "H4" + } ] }, "config2": { @@ -102,12 +118,28 @@ define(function (require, exports, module) { "-", "RemoveFormat" ], - [ - "Format" - ], [ "cloudcms-image" ] + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" + }, + { + "name": "Heading 2", + "element": "H2" + }, + { + "name": "Heading 3", + "element": "H3" + }, + { + "name": "Heading 4", + "element": "H4" + } ] }, "config4": { From f00ed4ee0dc11e5b6429409ee014ca6bf9fe4965 Mon Sep 17 00:00:00 2001 From: kundur Date: Tue, 3 Apr 2018 09:50:39 +0100 Subject: [PATCH 007/195] Trying other options --- fields/oup-ckeditor-field.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index cbaceeb..d05b095 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -59,11 +59,13 @@ define(function (require, exports, module) { "JustifyRight", "JustifyBlock" ], + [ + "Format" + ], [ "cloudcms-image" ] - ], - "removeButtons": null, + ], "stylesSet": [ { "name": "Paragraph", From f4dfd0806d585fca9f7071f6b7eba6691cf6e6c6 Mon Sep 17 00:00:00 2001 From: kundur Date: Tue, 3 Apr 2018 09:54:27 +0100 Subject: [PATCH 008/195] Trying out more options --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d05b095..83e4fef 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -65,7 +65,8 @@ define(function (require, exports, module) { [ "cloudcms-image" ] - ], + ], + "removeButtons": null, "stylesSet": [ { "name": "Paragraph", From 6129ec45e82f4851bd36fade5a6ecf6a3ec3d774 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 10 May 2018 16:57:41 +0100 Subject: [PATCH 009/195] Added cloudcms-image and cloudcms-link for config1 --- fields/oup-ckeditor-field.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 83e4fef..61acf02 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -63,7 +63,7 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image" + "cloudcms-image","cloudcms-link" ] ], "removeButtons": null, @@ -84,7 +84,20 @@ define(function (require, exports, module) { "name": "Heading 4", "element": "H4" } - ] + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image_Library", + "imagePickerConfig": { + "initialContainerPath": "/Image_Library" + } + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "initialContainerPath": "../" + } + } }, "config2": { "toolbar": [ From 9ce75c7034c2472dfc28e96e05fa249291e21b37 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 10 May 2018 17:33:31 +0100 Subject: [PATCH 010/195] Changed config1 --- fields/oup-ckeditor-field.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 61acf02..89b2bbf 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -87,9 +87,10 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": "../Image_Library", + "imageUploadPath": "../Image Library", "imagePickerConfig": { - "initialContainerPath": "/Image_Library" + "rootContainerPath": "../..", + "initialContainerPath": "/Image Library" } }, "cloudcms-link": { From 4022056e4861e9cea6b4a03c6c8ee12c6ca55f3c Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 10 May 2018 17:46:03 +0100 Subject: [PATCH 011/195] update --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 89b2bbf..d66d25e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -90,7 +90,7 @@ define(function (require, exports, module) { "imageUploadPath": "../Image Library", "imagePickerConfig": { "rootContainerPath": "../..", - "initialContainerPath": "/Image Library" + "initialContainerPath": "../Image Library" } }, "cloudcms-link": { From 5a0c8a04ce1cf617378e780c1db228f774cd4881 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 11 May 2018 12:19:50 +0100 Subject: [PATCH 012/195] update --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d66d25e..d13d5a8 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -96,7 +96,8 @@ define(function (require, exports, module) { "cloudcms-link": { "linkPickerType": "file-picker", "linkPickerConfig": { - "initialContainerPath": "../" + "rootContainerPath": "../..", + "initialContainerPath": "." } } }, From f71f8aea5ba906ec03b901a07609c9e0235c7048 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 11 May 2018 15:59:38 +0100 Subject: [PATCH 013/195] Update --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d13d5a8..9dcb222 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -97,7 +97,7 @@ define(function (require, exports, module) { "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../..", - "initialContainerPath": "." + "initialContainerPath": "../" } } }, From 177b57b50fc4160abd7620788eeb1a32f031c018 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 11 May 2018 16:23:47 +0100 Subject: [PATCH 014/195] update --- fields/oup-ckeditor-field.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 9dcb222..52cdf39 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -96,8 +96,7 @@ define(function (require, exports, module) { "cloudcms-link": { "linkPickerType": "file-picker", "linkPickerConfig": { - "rootContainerPath": "../..", - "initialContainerPath": "../" + "initialContainerPath": "../" } } }, From 4e296c2e26fa690d1cad2982d93668a6e2a9b63b Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 14 May 2018 15:45:57 +0100 Subject: [PATCH 015/195] update --- fields/oup-ckeditor-field.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 52cdf39..24e5e12 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -89,14 +89,15 @@ define(function (require, exports, module) { "imagePickerType": "file-picker", "imageUploadPath": "../Image Library", "imagePickerConfig": { - "rootContainerPath": "../..", + "rootContainerPath": "../../..", "initialContainerPath": "../Image Library" } }, "cloudcms-link": { "linkPickerType": "file-picker", "linkPickerConfig": { - "initialContainerPath": "../" + "rootContainerPath": "../../..", + "initialContainerPath": "../" } } }, From 95f11b2e0b3742bced374298d51134ae1504603a Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 14 May 2018 15:50:46 +0100 Subject: [PATCH 016/195] Update --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 24e5e12..a07e873 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -97,7 +97,7 @@ define(function (require, exports, module) { "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../" + "initialContainerPath": "../Image Library" } } }, From baf3ffbdc7b4ac72c874eabd59529a04e2598a2c Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Tue, 15 May 2018 09:33:46 +0100 Subject: [PATCH 017/195] GCMS-1162 --- fields/oup-ckeditor-field.js | 40 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index a07e873..105f95c 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -99,7 +99,8 @@ define(function (require, exports, module) { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library" } - } + }, + }, "config2": { "toolbar": [ @@ -158,7 +159,22 @@ define(function (require, exports, module) { "name": "Heading 4", "element": "H4" } - ] + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + } }, "config4": { "toolbar": [ @@ -173,29 +189,23 @@ define(function (require, exports, module) { "linkPickerConfig": { "initialContainerPath": "/" } - } - }, - "config5" : { - "toolbar": [ - ['Format', 'Font', 'FontSize'], - ['Bold', 'Italic', 'Underline'], - ['Image', 'Table', 'Source'], - ['cloudcms-image', 'cloudcms-link'] - ], + }, "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": "/Images", + "imageUploadPath": "../Image Library", "imagePickerConfig": { - "initialContainerPath": "/Images" + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" } }, "cloudcms-link": { "linkPickerType": "file-picker", "linkPickerConfig": { - "initialContainerPath": "/" + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" } } - }, + } }, /** From cb3cea577454b40a1b8021f37f71487d9824895b Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Tue, 15 May 2018 14:41:27 +0100 Subject: [PATCH 018/195] update --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 105f95c..b9fc9d9 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -63,7 +63,7 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image","cloudcms-link" + "cloudcms-image" ] ], "removeButtons": null, @@ -97,7 +97,7 @@ define(function (require, exports, module) { "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" + "initialContainerPath": "../" } }, From 6afa90c92fbf1e57b0110c0a42eb0b29e9a622ac Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 31 May 2018 14:47:37 +0100 Subject: [PATCH 019/195] Addec config5 --- fields/oup-ckeditor-field.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index b9fc9d9..f7b35fc 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -205,6 +205,18 @@ define(function (require, exports, module) { "initialContainerPath": "../Image Library" } } + }, + "config5": { + "toolbar": [ + [ + "Bold", + "H4", + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] } }, From 261b4699285cefdf4fd158e6956dcfd49687f692 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 4 Jun 2018 10:56:49 +0100 Subject: [PATCH 020/195] chnages related to h4 --- fields/oup-ckeditor-field.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f7b35fc..2df3835 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,12 +210,17 @@ define(function (require, exports, module) { "toolbar": [ [ "Bold", - "H4", "SpecialChar", "Italic", "Subscript", "Superscript" ] + ], + "stylesSet": [ + { + "name": "Heading 4", + "element": "H4" + } ] } }, From 0a1882c903f3776e3b45bfd9981fafc055310ac5 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 4 Jun 2018 14:15:38 +0100 Subject: [PATCH 021/195] h4 related change --- fields/oup-ckeditor-field.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 2df3835..09fe858 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,17 +210,12 @@ define(function (require, exports, module) { "toolbar": [ [ "Bold", + "h4", "SpecialChar", "Italic", "Subscript", "Superscript" ] - ], - "stylesSet": [ - { - "name": "Heading 4", - "element": "H4" - } ] } }, From 17e259eefbcdf89636cb7121fbe352394d83ed00 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 4 Jun 2018 14:20:22 +0100 Subject: [PATCH 022/195] update --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 09fe858..62d9475 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,7 +210,7 @@ define(function (require, exports, module) { "toolbar": [ [ "Bold", - "h4", + "Format", "SpecialChar", "Italic", "Subscript", From 629acfda7b713606609cd971255832f6a754a047 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Tue, 5 Jun 2018 09:08:41 +0100 Subject: [PATCH 023/195] update --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 62d9475..7f74ad2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -267,7 +267,8 @@ define(function (require, exports, module) { */ getDescription: function () { return "Allow output markup rule overrides to ckeditor"; - } + }, + format_tags :'p;h1;h2;h3;h4;pre' /* end_builder_helpers */ }); From f3638bf12a3492a6464d8af86f4d2c7a37f25d69 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Tue, 5 Jun 2018 16:22:21 +0100 Subject: [PATCH 024/195] update --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7f74ad2..f27d1ef 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -232,6 +232,7 @@ define(function (require, exports, module) { */ setup: function () { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { + this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] } @@ -268,7 +269,6 @@ define(function (require, exports, module) { getDescription: function () { return "Allow output markup rule overrides to ckeditor"; }, - format_tags :'p;h1;h2;h3;h4;pre' /* end_builder_helpers */ }); From a16ce7f6c3fee458464f48727bf8dd6eaa743bec Mon Sep 17 00:00:00 2001 From: Biren Rao <33708101+birenrao@users.noreply.github.com> Date: Mon, 11 Jun 2018 15:18:53 +0100 Subject: [PATCH 025/195] Update oup-ckeditor-field.js cloudecms-image config updated --- fields/oup-ckeditor-field.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f27d1ef..a26b443 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -86,13 +86,20 @@ define(function (require, exports, module) { } ], "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" - } - }, + "imagePickerType": "file-picker", + "imagePickerConfig": { + "initialContainerPath": "/", + "mimetypeRegex": "image\/([^;\s]+)" + }, + "imageClass": "", + "enableAltText": false, + "enableLink": false, + "linkPickerType": "file-picker", + "linkPickerConfig": { + "initialContainerPath": "/" + }, + "linkClass": "" + }, "cloudcms-link": { "linkPickerType": "file-picker", "linkPickerConfig": { @@ -279,4 +286,4 @@ define(function (require, exports, module) { Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); -}); \ No newline at end of file +}); From bee8a959ab81808a11c1909e48badff9aa49d64d Mon Sep 17 00:00:00 2001 From: Biren Rao <33708101+birenrao@users.noreply.github.com> Date: Wed, 13 Jun 2018 11:50:55 +0100 Subject: [PATCH 026/195] GCMS-1168 Image alignment options update --- fields/oup-ckeditor-field.js | 492 ++++++++++++++++------------------- 1 file changed, 220 insertions(+), 272 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index a26b443..1d85407 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -1,289 +1,237 @@ -define(function (require, exports, module) { +define(function(require, exports, module) { + var $ = require("jquery"); - var $ = require("jquery"); + var Alpaca = $.alpaca; - var Alpaca = $.alpaca; - - Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( - /** - * @lends Alpaca.Fields.OUPCKEditorField.prototype - */ - { - toolbarOptions: { - "config1": { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor", - "cloudcms-link" - ], - [ - "Table", - "HorizontalRule", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" - ], - [ - "Format" - ], - [ - "cloudcms-image" - ] - ], - "removeButtons": null, - "stylesSet": [ - { - "name": "Paragraph", - "element": "p" - }, - { - "name": "Heading 2", - "element": "H2" - }, - { - "name": "Heading 3", - "element": "H3" - }, - { - "name": "Heading 4", - "element": "H4" - } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "imagePickerConfig": { - "initialContainerPath": "/", - "mimetypeRegex": "image\/([^;\s]+)" + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( + /** + * @lends Alpaca.Fields.OUPCKEditorField.prototype + */ + { + toolbarOptions: { + config1: { + toolbar: [ + ["Cut", "Copy", "Paste", "-", "Undo", "Redo"], + ["Link", "Unlink", "Anchor", "cloudcms-link"], + ["Table", "HorizontalRule", "SpecialChar"], + ["Maximize", "ShowBlocks", "Source", "Preview"], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + ["Format"], + ["cloudcms-image"] + ], + removeButtons: null, + stylesSet: [ + { + name: "Paragraph", + element: "p" + }, + { + name: "Heading 2", + element: "H2" + }, + { + name: "Heading 3", + element: "H3" + }, + { + name: "Heading 4", + element: "H4" + } + ], + "cloudcms-image": { + imagePickerType: "file-picker", + imagePickerConfig: { + initialContainerPath: "/", + mimetypeRegex: "image/([^;s]+)" + }, + imageClass: "", + enableAltText: false, + enableLink: false, + linkPickerType: "file-picker", + linkPickerConfig: { + initialContainerPath: "/" + }, + linkClass: "" + }, + "cloudcms-link": { + linkPickerType: "file-picker", + linkPickerConfig: { + rootContainerPath: "../../..", + initialContainerPath: "../" + } + } }, - "imageClass": "", - "enableAltText": false, - "enableLink": false, - "linkPickerType": "file-picker", - "linkPickerConfig": { - "initialContainerPath": "/" + config2: { + toolbar: [["SpecialChar", "Italic", "Subscript", "Superscript"]] }, - "linkClass": "" - }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../" - } - }, - - }, - "config2": { - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, - "config3": { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "cloudcms-link" - ], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat" - ], - [ - "cloudcms-image" - ] - ], - "removeButtons": null, - "stylesSet": [ - { - "name": "Paragraph", - "element": "p" - }, - { - "name": "Heading 2", - "element": "H2" - }, - { - "name": "Heading 3", - "element": "H3" - }, - { - "name": "Heading 4", - "element": "H4" - } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" - } - }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" - } - } - }, - "config4": { - "toolbar": [ - [ - "Link", - "Unlink", - "cloudcms-link" - ] - ], - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "initialContainerPath": "/" - } - }, - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" - } - }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" - } - } - }, - "config5": { - "toolbar": [ - [ - "Bold", - "Format", - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - } + config3: { + toolbar: [ + ["Cut", "Copy", "Paste", "-", "Undo", "Redo"], + ["Link", "Unlink", "cloudcms-link"], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat" + ], + ["cloudcms-image"] + ], + removeButtons: null, + stylesSet: [ + { + name: "Paragraph", + element: "p" }, - - /** - * @see Alpaca.Fields.TextField#getFieldType - */ - getFieldType: function () { - return "oup-ckeditor-field"; + { + name: "Heading 2", + element: "H2" }, - - /** - * @see Alpaca.Fields.TextField#setup - */ - setup: function () { - if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { - this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; - this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] - } - - this.base(); + { + name: "Heading 3", + element: "H3" }, + { + name: "Heading 4", + element: "H4" + } + ], + "cloudcms-image": { + imagePickerType: "file-picker", + imageUploadPath: "../Image Library", + imagePickerConfig: { + rootContainerPath: "../../..", + initialContainerPath: "../Image Library" + } + }, + "cloudcms-link": { + linkPickerType: "file-picker", + linkPickerConfig: { + rootContainerPath: "../../..", + initialContainerPath: "../Image Library" + } + } + }, + config4: { + toolbar: [["Link", "Unlink", "cloudcms-link"]], + "cloudcms-link": { + linkPickerType: "file-picker", + linkPickerConfig: { + initialContainerPath: "/" + } + }, + "cloudcms-image": { + imagePickerType: "file-picker", + imageUploadPath: "../Image Library", + imagePickerConfig: { + rootContainerPath: "../../..", + initialContainerPath: "../Image Library" + } + }, + "cloudcms-link": { + linkPickerType: "file-picker", + linkPickerConfig: { + rootContainerPath: "../../..", + initialContainerPath: "../Image Library" + } + } + }, + config5: { + toolbar: [ + [ + "Bold", + "Format", + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + } + }, - afterRenderControl: function (model, callback) { - var self = this; - self.base(model, function () { - callback(); - }); - }, + /** + * @see Alpaca.Fields.TextField#getFieldType + */ + getFieldType: function() { + return "oup-ckeditor-field"; + }, - /** - * @see Alpaca.Fields.TextField#handleValidate - */ - handleValidate: function () { - return this.base(); - } + /** + * @see Alpaca.Fields.TextField#setup + */ + setup: function() { + if ( + this.options.ckeditor && + this.toolbarOptions[this.options.ckeditor] + ) { + this.options.ckeditor.format_tags = "p;h1;h2;h3;h4;pre"; + this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + } - /* builder_helpers */ - , + this.base(); + }, - /** - * @see Alpaca.Fields.TextField#getTitle - */ - getTitle: function () { - return "OUP ckeditor Field"; - }, + afterRenderControl: function(model, callback) { + var self = this; + self.base(model, function() { + callback(); + }); + }, - /** - * @see Alpaca.Fields.TextField#getDescription - */ - getDescription: function () { - return "Allow output markup rule overrides to ckeditor"; - }, + /** + * @see Alpaca.Fields.TextField#handleValidate + */ + handleValidate: function() { + return this.base(); + }, - /* end_builder_helpers */ - }); + /* builder_helpers */ + /** + * @see Alpaca.Fields.TextField#getTitle + */ + getTitle: function() { + return "OUP ckeditor Field"; + }, + + /** + * @see Alpaca.Fields.TextField#getDescription + */ + getDescription: function() { + return "Allow output markup rule overrides to ckeditor"; + } - Alpaca.registerMessages({ - "noDependentField": "No local config found" - }); + /* end_builder_helpers */ + } + ); - Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); + Alpaca.registerMessages({ + noDependentField: "No local config found" + }); + Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); }); From 737c0690ae90161f9d7f8ff7bade05bf34d560b4 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 28 Jun 2018 11:47:02 +0100 Subject: [PATCH 027/195] Revert "GCMS-1168" This reverts commit bee8a959ab81808a11c1909e48badff9aa49d64d. --- fields/oup-ckeditor-field.js | 492 +++++++++++++++++++---------------- 1 file changed, 272 insertions(+), 220 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 1d85407..a26b443 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -1,237 +1,289 @@ -define(function(require, exports, module) { - var $ = require("jquery"); +define(function (require, exports, module) { - var Alpaca = $.alpaca; + var $ = require("jquery"); - Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( - /** - * @lends Alpaca.Fields.OUPCKEditorField.prototype - */ - { - toolbarOptions: { - config1: { - toolbar: [ - ["Cut", "Copy", "Paste", "-", "Undo", "Redo"], - ["Link", "Unlink", "Anchor", "cloudcms-link"], - ["Table", "HorizontalRule", "SpecialChar"], - ["Maximize", "ShowBlocks", "Source", "Preview"], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" - ], - ["Format"], - ["cloudcms-image"] - ], - removeButtons: null, - stylesSet: [ - { - name: "Paragraph", - element: "p" - }, - { - name: "Heading 2", - element: "H2" - }, - { - name: "Heading 3", - element: "H3" - }, - { - name: "Heading 4", - element: "H4" - } - ], - "cloudcms-image": { - imagePickerType: "file-picker", - imagePickerConfig: { - initialContainerPath: "/", - mimetypeRegex: "image/([^;s]+)" - }, - imageClass: "", - enableAltText: false, - enableLink: false, - linkPickerType: "file-picker", - linkPickerConfig: { - initialContainerPath: "/" - }, - linkClass: "" - }, - "cloudcms-link": { - linkPickerType: "file-picker", - linkPickerConfig: { - rootContainerPath: "../../..", - initialContainerPath: "../" - } - } + var Alpaca = $.alpaca; + + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( + /** + * @lends Alpaca.Fields.OUPCKEditorField.prototype + */ + { + toolbarOptions: { + "config1": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor", + "cloudcms-link" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format" + ], + [ + "cloudcms-image" + ] + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" + }, + { + "name": "Heading 2", + "element": "H2" + }, + { + "name": "Heading 3", + "element": "H3" + }, + { + "name": "Heading 4", + "element": "H4" + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imagePickerConfig": { + "initialContainerPath": "/", + "mimetypeRegex": "image\/([^;\s]+)" }, - config2: { - toolbar: [["SpecialChar", "Italic", "Subscript", "Superscript"]] + "imageClass": "", + "enableAltText": false, + "enableLink": false, + "linkPickerType": "file-picker", + "linkPickerConfig": { + "initialContainerPath": "/" }, - config3: { - toolbar: [ - ["Cut", "Copy", "Paste", "-", "Undo", "Redo"], - ["Link", "Unlink", "cloudcms-link"], - [ - "Bold", - "Italic", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat" - ], - ["cloudcms-image"] - ], - removeButtons: null, - stylesSet: [ - { - name: "Paragraph", - element: "p" - }, - { - name: "Heading 2", - element: "H2" + "linkClass": "" + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../" + } + }, + + }, + "config2": { + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + }, + "config3": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "cloudcms-link" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat" + ], + [ + "cloudcms-image" + ] + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" + }, + { + "name": "Heading 2", + "element": "H2" + }, + { + "name": "Heading 3", + "element": "H3" + }, + { + "name": "Heading 4", + "element": "H4" + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + } + }, + "config4": { + "toolbar": [ + [ + "Link", + "Unlink", + "cloudcms-link" + ] + ], + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "initialContainerPath": "/" + } + }, + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + } + }, + "config5": { + "toolbar": [ + [ + "Bold", + "Format", + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + } }, - { - name: "Heading 3", - element: "H3" + + /** + * @see Alpaca.Fields.TextField#getFieldType + */ + getFieldType: function () { + return "oup-ckeditor-field"; }, - { - name: "Heading 4", - element: "H4" - } - ], - "cloudcms-image": { - imagePickerType: "file-picker", - imageUploadPath: "../Image Library", - imagePickerConfig: { - rootContainerPath: "../../..", - initialContainerPath: "../Image Library" - } - }, - "cloudcms-link": { - linkPickerType: "file-picker", - linkPickerConfig: { - rootContainerPath: "../../..", - initialContainerPath: "../Image Library" - } - } - }, - config4: { - toolbar: [["Link", "Unlink", "cloudcms-link"]], - "cloudcms-link": { - linkPickerType: "file-picker", - linkPickerConfig: { - initialContainerPath: "/" - } - }, - "cloudcms-image": { - imagePickerType: "file-picker", - imageUploadPath: "../Image Library", - imagePickerConfig: { - rootContainerPath: "../../..", - initialContainerPath: "../Image Library" - } - }, - "cloudcms-link": { - linkPickerType: "file-picker", - linkPickerConfig: { - rootContainerPath: "../../..", - initialContainerPath: "../Image Library" - } - } - }, - config5: { - toolbar: [ - [ - "Bold", - "Format", - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - } - }, - /** - * @see Alpaca.Fields.TextField#getFieldType - */ - getFieldType: function() { - return "oup-ckeditor-field"; - }, + /** + * @see Alpaca.Fields.TextField#setup + */ + setup: function () { + if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { + this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; + this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] + } - /** - * @see Alpaca.Fields.TextField#setup - */ - setup: function() { - if ( - this.options.ckeditor && - this.toolbarOptions[this.options.ckeditor] - ) { - this.options.ckeditor.format_tags = "p;h1;h2;h3;h4;pre"; - this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - } + this.base(); + }, - this.base(); - }, + afterRenderControl: function (model, callback) { + var self = this; + self.base(model, function () { + callback(); + }); + }, - afterRenderControl: function(model, callback) { - var self = this; - self.base(model, function() { - callback(); - }); - }, + /** + * @see Alpaca.Fields.TextField#handleValidate + */ + handleValidate: function () { + return this.base(); + } - /** - * @see Alpaca.Fields.TextField#handleValidate - */ - handleValidate: function() { - return this.base(); - }, + /* builder_helpers */ + , - /* builder_helpers */ - /** - * @see Alpaca.Fields.TextField#getTitle - */ - getTitle: function() { - return "OUP ckeditor Field"; - }, + /** + * @see Alpaca.Fields.TextField#getTitle + */ + getTitle: function () { + return "OUP ckeditor Field"; + }, + + /** + * @see Alpaca.Fields.TextField#getDescription + */ + getDescription: function () { + return "Allow output markup rule overrides to ckeditor"; + }, - /** - * @see Alpaca.Fields.TextField#getDescription - */ - getDescription: function() { - return "Allow output markup rule overrides to ckeditor"; - } + /* end_builder_helpers */ + }); - /* end_builder_helpers */ - } - ); + Alpaca.registerMessages({ + "noDependentField": "No local config found" + }); - Alpaca.registerMessages({ - noDependentField: "No local config found" - }); + Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); - Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); }); From 6891d4ef9640d9d35f288df08677acc97b0774fd Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 28 Jun 2018 11:47:12 +0100 Subject: [PATCH 028/195] Revert "Update oup-ckeditor-field.js" This reverts commit a16ce7f6c3fee458464f48727bf8dd6eaa743bec. --- fields/oup-ckeditor-field.js | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index a26b443..f27d1ef 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -86,20 +86,13 @@ define(function (require, exports, module) { } ], "cloudcms-image": { - "imagePickerType": "file-picker", - "imagePickerConfig": { - "initialContainerPath": "/", - "mimetypeRegex": "image\/([^;\s]+)" - }, - "imageClass": "", - "enableAltText": false, - "enableLink": false, - "linkPickerType": "file-picker", - "linkPickerConfig": { - "initialContainerPath": "/" - }, - "linkClass": "" - }, + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + }, "cloudcms-link": { "linkPickerType": "file-picker", "linkPickerConfig": { @@ -286,4 +279,4 @@ define(function (require, exports, module) { Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); -}); +}); \ No newline at end of file From 76253f89b252a269939f2d8e1138deda51d49cc9 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 28 Jun 2018 13:57:25 +0100 Subject: [PATCH 029/195] GCMS-1162 --- fields/oup-ckeditor-field.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f27d1ef..7db568e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -94,7 +94,7 @@ define(function (require, exports, module) { } }, "cloudcms-link": { - "linkPickerType": "file-picker", + "linkPickerType": "node-picker", "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../" @@ -172,7 +172,7 @@ define(function (require, exports, module) { "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" + "initialContainerPath": "../" } } }, @@ -202,7 +202,7 @@ define(function (require, exports, module) { "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" + "initialContainerPath": "../" } } }, From 74b146f8d7270ac75691a286e7623ca2bd9202b1 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 28 Jun 2018 13:59:02 +0100 Subject: [PATCH 030/195] GCMS-1162 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7db568e..29b1631 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -94,7 +94,7 @@ define(function (require, exports, module) { } }, "cloudcms-link": { - "linkPickerType": "node-picker", + "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../" From f3146465570487ef1b1ee94e9c296bf8dc92b271 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 28 Jun 2018 18:35:54 +0530 Subject: [PATCH 031/195] GCMS-1257 GCMS-1257 - Changes given in config3 by Ben --- fields/oup-ckeditor-field.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 29b1631..7c6d75e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -130,6 +130,7 @@ define(function (require, exports, module) { [ "Bold", "Italic", + "BulletedList", "Strike", "Subscript", "Superscript", @@ -137,6 +138,9 @@ define(function (require, exports, module) { "-", "RemoveFormat" ], + [ + "Format" + ], [ "cloudcms-image" ] From c7662b9bd3ab6f17075e1c1f109160574efc04c5 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 5 Jul 2018 09:37:14 +0100 Subject: [PATCH 032/195] GCMS-1114 --- fields/oup-ckeditor-field.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7c6d75e..d45f0ca 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -213,12 +213,17 @@ define(function (require, exports, module) { "config5": { "toolbar": [ [ - "Bold", - "Format", - "SpecialChar", + "H4", + "-", "Italic", + "SpecialChar", "Subscript", - "Superscript" + "Superscript", + "-", + "Link", + "Unlink", + "-", + "ShowBlocks" ] ] } From 5415e9e03ffb835ce61c480c18eee0892e982eab Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 5 Jul 2018 11:59:18 +0100 Subject: [PATCH 033/195] GCMS-1114 --- fields/oup-ckeditor-field.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d45f0ca..8f65edf 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -213,7 +213,7 @@ define(function (require, exports, module) { "config5": { "toolbar": [ [ - "H4", + "oupHello", "-", "Italic", "SpecialChar", @@ -243,6 +243,22 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] + this.options.plugins.add(pluginName, { + icons: "oupHello", + init: function (editor) { + editor.ui.addButton(pluginName, { + label: 'Hello', + command: pluginName + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); + } this.base(); From 6eda46f8f48d59aaafcb34d66b9072696279f055 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 5 Jul 2018 12:08:32 +0100 Subject: [PATCH 034/195] GCMS-1114 --- fields/oup-ckeditor-field.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 8f65edf..618deaa 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -213,7 +213,7 @@ define(function (require, exports, module) { "config5": { "toolbar": [ [ - "oupHello", + "H4", "-", "Italic", "SpecialChar", @@ -242,23 +242,7 @@ define(function (require, exports, module) { setup: function () { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; - this.options.ckeditor = this.toolbarOptions[this.options.ckeditor] - this.options.plugins.add(pluginName, { - icons: "oupHello", - init: function (editor) { - editor.ui.addButton(pluginName, { - label: 'Hello', - command: pluginName - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); - + this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; } this.base(); From 65ba16cf305fc0909d0138fee500539c3ce4b060 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 20 Jul 2018 14:11:02 +0100 Subject: [PATCH 035/195] added cloudcms-iframe --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 618deaa..4f391e4 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -63,7 +63,8 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image" + "cloudcms-image", + "cloudcms-iframe" ] ], "removeButtons": null, From 7d557425ac99a98f7ecdd16d95fbdf30a116910e Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 23 Jul 2018 09:52:11 +0100 Subject: [PATCH 036/195] PI-5 Sprint-1 - GCMS-1162 --- fields/oup-ckeditor-field.js | 47 ++++-------------------------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 4f391e4..429f12e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -23,8 +23,7 @@ define(function (require, exports, module) { [ "Link", "Unlink", - "Anchor", - "cloudcms-link" + "Anchor" ], [ "Table", @@ -94,13 +93,6 @@ define(function (require, exports, module) { "initialContainerPath": "../Image Library" } }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../" - } - }, }, "config2": { @@ -125,8 +117,7 @@ define(function (require, exports, module) { ], [ "Link", - "Unlink", - "cloudcms-link" + "Unlink" ], [ "Bold", @@ -172,44 +163,16 @@ define(function (require, exports, module) { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library" } - }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../" - } } }, "config4": { "toolbar": [ [ "Link", - "Unlink", - "cloudcms-link" + "Unlink" ] - ], - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "initialContainerPath": "/" - } - }, - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" - } - }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../" - } - } + ] + }, "config5": { "toolbar": [ From 92ab0f52cc4c90ad57b7ea180179c91ccc6f317b Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 23 Jul 2018 17:50:25 +0100 Subject: [PATCH 037/195] update --- fields/oup-ckeditor-field.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 429f12e..f7ef50a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,7 +134,8 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image" + "cloudcms-image", + "ouphello" ] ], "removeButtons": null, @@ -207,6 +208,23 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + + this.options.ckeditor.plugins.add(pluginName, { + icons: "ouphello", + init: function (editor) { + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: "ouphello" + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); } this.base(); From 5afc56eefbe2f5967340b7e03d6b0553830ea2d9 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 23 Jul 2018 18:04:40 +0100 Subject: [PATCH 038/195] reverted the update --- fields/oup-ckeditor-field.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f7ef50a..429f12e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,8 +134,7 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image", - "ouphello" + "cloudcms-image" ] ], "removeButtons": null, @@ -208,23 +207,6 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - - this.options.ckeditor.plugins.add(pluginName, { - icons: "ouphello", - init: function (editor) { - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: "ouphello" - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); } this.base(); From 10c333080d8458f0e0d27ca31f2ee7af66aa41f1 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 26 Jul 2018 09:32:22 +0100 Subject: [PATCH 039/195] custom button code --- fields/oup-ckeditor-field.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 429f12e..f7ef50a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,7 +134,8 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image" + "cloudcms-image", + "ouphello" ] ], "removeButtons": null, @@ -207,6 +208,23 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + + this.options.ckeditor.plugins.add(pluginName, { + icons: "ouphello", + init: function (editor) { + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: "ouphello" + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); } this.base(); From 8c124fe86d9c5f3a2fa2c3a58f71ea7edcc86ff5 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 27 Jul 2018 14:40:42 +0100 Subject: [PATCH 040/195] Revert "custom button code" This reverts commit 10c333080d8458f0e0d27ca31f2ee7af66aa41f1. --- fields/oup-ckeditor-field.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f7ef50a..429f12e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,8 +134,7 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image", - "ouphello" + "cloudcms-image" ] ], "removeButtons": null, @@ -208,23 +207,6 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - - this.options.ckeditor.plugins.add(pluginName, { - icons: "ouphello", - init: function (editor) { - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: "ouphello" - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); } this.base(); From 433a5d5371659a439d484a7656b1fb6611c46e09 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 27 Jul 2018 14:43:14 +0100 Subject: [PATCH 041/195] Revert "Revert "custom button code"" This reverts commit 8c124fe86d9c5f3a2fa2c3a58f71ea7edcc86ff5. --- fields/oup-ckeditor-field.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 429f12e..f7ef50a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,7 +134,8 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image" + "cloudcms-image", + "ouphello" ] ], "removeButtons": null, @@ -207,6 +208,23 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + + this.options.ckeditor.plugins.add(pluginName, { + icons: "ouphello", + init: function (editor) { + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: "ouphello" + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); } this.base(); From 960bb4b9bb0a818743429f7d7c786643f4fb5687 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 27 Jul 2018 15:59:34 +0100 Subject: [PATCH 042/195] Revert "Revert "Revert "custom button code""" This reverts commit 433a5d5371659a439d484a7656b1fb6611c46e09. --- fields/oup-ckeditor-field.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f7ef50a..429f12e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,8 +134,7 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image", - "ouphello" + "cloudcms-image" ] ], "removeButtons": null, @@ -208,23 +207,6 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - - this.options.ckeditor.plugins.add(pluginName, { - icons: "ouphello", - init: function (editor) { - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: "ouphello" - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); } this.base(); From 095240847f4552caaaf2dc1a635fdf4b8c1fffa7 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 27 Jul 2018 16:00:52 +0100 Subject: [PATCH 043/195] Revert "Revert "Revert "Revert "custom button code"""" This reverts commit 960bb4b9bb0a818743429f7d7c786643f4fb5687. --- fields/oup-ckeditor-field.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 429f12e..f7ef50a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,7 +134,8 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image" + "cloudcms-image", + "ouphello" ] ], "removeButtons": null, @@ -207,6 +208,23 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + + this.options.ckeditor.plugins.add(pluginName, { + icons: "ouphello", + init: function (editor) { + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: "ouphello" + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); } this.base(); From 3a2a012534ca1c5548de8ebc9bf67f8cafc4efcf Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 30 Jul 2018 12:06:47 +0530 Subject: [PATCH 044/195] Revert "Revert "Revert "Revert "Revert "custom button code""""" This reverts commit 095240847f4552caaaf2dc1a635fdf4b8c1fffa7. --- fields/oup-ckeditor-field.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f7ef50a..429f12e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -134,8 +134,7 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image", - "ouphello" + "cloudcms-image" ] ], "removeButtons": null, @@ -208,23 +207,6 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - - this.options.ckeditor.plugins.add(pluginName, { - icons: "ouphello", - init: function (editor) { - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: "ouphello" - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); } this.base(); From 5ad7ac1202f4103cf7f2c28f8405cedf56c50bfb Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 10:45:42 +0100 Subject: [PATCH 045/195] update --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f7ef50a..7b0b693 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -135,7 +135,8 @@ define(function (require, exports, module) { ], [ "cloudcms-image", - "ouphello" + "ouphello", + "stylesSet" ] ], "removeButtons": null, From 370d9693b47967a04c6c0b85291aa9c462aa0b57 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 11:25:14 +0100 Subject: [PATCH 046/195] update --- fields/oup-ckeditor-field.js | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d567567..0e1ca7c 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -63,8 +63,7 @@ define(function (require, exports, module) { ], [ "cloudcms-image", - "cloudcms-iframe", - "stylesSet" + "cloudcms-iframe" ] ], "removeButtons": null, @@ -84,7 +83,22 @@ define(function (require, exports, module) { { "name": "Heading 4", "element": "H4" + }, + { + "name": "OUP Custom Inline", + "element": "span", + "attributes": { + "class": "mine" + } + }, + { + "name": "OUP bulleted list", + "element": "ul", + "attributes": { + "class": "bullet" + } } + ], "cloudcms-image": { "imagePickerType": "file-picker", @@ -156,6 +170,20 @@ define(function (require, exports, module) { { "name": "Heading 4", "element": "H4" + }, + { + "name": "OUP Custom Inline", + "element": "span", + "attributes": { + "class": "mine" + } + }, + { + "name": "OUP bulleted list", + "element": "ul", + "attributes": { + "class": "bullet" + } } ], "cloudcms-image": { From 8c0826d7c1c6d19ee4e08ad6de1f39046f50187e Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 11:32:42 +0100 Subject: [PATCH 047/195] update --- fields/oup-ckeditor-field.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 0e1ca7c..ba1b60d 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -149,8 +149,7 @@ define(function (require, exports, module) { "Format" ], [ - "cloudcms-image", - "stylesSet" + "cloudcms-image" ] ], "removeButtons": null, From 152febbd25fe7ae99e52b6fe960d1bc3b374e906 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 11:37:40 +0100 Subject: [PATCH 048/195] Update --- fields/oup-ckeditor-field.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index ba1b60d..530fbac 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -59,7 +59,8 @@ define(function (require, exports, module) { "JustifyBlock" ], [ - "Format" + "Format", + "Styles" ], [ "cloudcms-image", @@ -146,7 +147,8 @@ define(function (require, exports, module) { "RemoveFormat" ], [ - "Format" + "Format", + "Styles" ], [ "cloudcms-image" From 1edc65b7b6a5fdafa04796ce72ee1ad55001aaab Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 14:16:56 +0100 Subject: [PATCH 049/195] Update --- fields/oup-ckeditor-field.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 530fbac..fd884d9 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -64,7 +64,8 @@ define(function (require, exports, module) { ], [ "cloudcms-image", - "cloudcms-iframe" + "cloudcms-iframe", + "ouphello" ] ], "removeButtons": null, @@ -151,7 +152,8 @@ define(function (require, exports, module) { "Styles" ], [ - "cloudcms-image" + "cloudcms-image", + "ouphello" ] ], "removeButtons": null, @@ -238,6 +240,23 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + this.options.ckeditor.plugins.add(pluginName, { + icons: "ouphello", + init: function (editor) { + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: "ouphello" + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); + } this.base(); From b7b34407e6aea77bd1f085aaa2e79b15458ed4c0 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 14:23:38 +0100 Subject: [PATCH 050/195] Update --- fields/oup-ckeditor-field.js | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index fd884d9..530fbac 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -64,8 +64,7 @@ define(function (require, exports, module) { ], [ "cloudcms-image", - "cloudcms-iframe", - "ouphello" + "cloudcms-iframe" ] ], "removeButtons": null, @@ -152,8 +151,7 @@ define(function (require, exports, module) { "Styles" ], [ - "cloudcms-image", - "ouphello" + "cloudcms-image" ] ], "removeButtons": null, @@ -240,23 +238,6 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - this.options.ckeditor.plugins.add(pluginName, { - icons: "ouphello", - init: function (editor) { - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: "ouphello" - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); - } this.base(); From 85b5cee96e6939e8bd8be6e53b91fd451eede1d1 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 15:17:26 +0100 Subject: [PATCH 051/195] Update --- fields/oup-ckeditor-field.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 530fbac..ce72e7a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -64,7 +64,8 @@ define(function (require, exports, module) { ], [ "cloudcms-image", - "cloudcms-iframe" + "cloudcms-iframe", + "ouphello" ] ], "removeButtons": null, @@ -238,6 +239,23 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + this.options.ckeditor.plugins.add(pluginName, { + icons: "ouphello", + init: function (editor) { + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: "ouphello" + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); + } this.base(); From 21df6a44ed2f0b60a7dd2146399ac6dac9c96798 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 1 Aug 2018 17:49:02 +0100 Subject: [PATCH 052/195] Revert "Update" This reverts commit 85b5cee96e6939e8bd8be6e53b91fd451eede1d1. --- fields/oup-ckeditor-field.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index ce72e7a..530fbac 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -64,8 +64,7 @@ define(function (require, exports, module) { ], [ "cloudcms-image", - "cloudcms-iframe", - "ouphello" + "cloudcms-iframe" ] ], "removeButtons": null, @@ -239,23 +238,6 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - this.options.ckeditor.plugins.add(pluginName, { - icons: "ouphello", - init: function (editor) { - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: "ouphello" - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); - } this.base(); From 114cf3350833fe2bad551249958ae226b1258545 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 6 Aug 2018 14:45:56 +0530 Subject: [PATCH 053/195] GCMS 1114 Checking functionality of Hello button --- fields/oup-ckeditor-field.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 530fbac..8a68c26 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,6 +210,9 @@ define(function (require, exports, module) { [ "H4", "-", + "Hello", + "ouphello", + "-", "Italic", "SpecialChar", "Subscript", @@ -239,7 +242,24 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; } - + var pluginName = "ouphello"; + window.CKEDITOR.plugins.add(pluginName, { + icons: pluginName, + init: function (editor) { + + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: pluginName + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); this.base(); }, From 61610c7583ef2d0fa78d41ba2e479db4ac054fce Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 6 Aug 2018 14:53:58 +0530 Subject: [PATCH 054/195] GCMS 1114 update --- fields/oup-ckeditor-field.js | 1 - 1 file changed, 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 8a68c26..34a1f7b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,7 +210,6 @@ define(function (require, exports, module) { [ "H4", "-", - "Hello", "ouphello", "-", "Italic", From d37fd87dfa4be97875f4c0c34de88b4c03090293 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 6 Aug 2018 14:54:54 +0530 Subject: [PATCH 055/195] GCMS 1114 --- fields/oup-ckeditor-field.js | 1 - 1 file changed, 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 34a1f7b..29348a1 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,7 +210,6 @@ define(function (require, exports, module) { [ "H4", "-", - "ouphello", "-", "Italic", "SpecialChar", From feace61ab78d2e3ca974937127e11d00c089e18c Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 6 Aug 2018 14:56:08 +0530 Subject: [PATCH 056/195] Revert "GCMS 1114" This reverts commit d37fd87dfa4be97875f4c0c34de88b4c03090293. --- fields/oup-ckeditor-field.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 29348a1..34a1f7b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,6 +210,7 @@ define(function (require, exports, module) { [ "H4", "-", + "ouphello", "-", "Italic", "SpecialChar", From 5bdc462e0bfbaaf1971f21bea2826d4e778d86cc Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 6 Aug 2018 15:02:50 +0530 Subject: [PATCH 057/195] Revert "GCMS 1114" This reverts commit 114cf3350833fe2bad551249958ae226b1258545. --- fields/oup-ckeditor-field.js | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 34a1f7b..530fbac 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,8 +210,6 @@ define(function (require, exports, module) { [ "H4", "-", - "ouphello", - "-", "Italic", "SpecialChar", "Subscript", @@ -241,24 +239,7 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; } - var pluginName = "ouphello"; - window.CKEDITOR.plugins.add(pluginName, { - icons: pluginName, - init: function (editor) { - - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: pluginName - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); + this.base(); }, From 9bdd40382c0c235b13525eb0c1ba12bc7d57ef78 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 6 Aug 2018 15:14:43 +0530 Subject: [PATCH 058/195] GCMS 1114 --- fields/oup-ckeditor-field.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 530fbac..34a1f7b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,6 +210,8 @@ define(function (require, exports, module) { [ "H4", "-", + "ouphello", + "-", "Italic", "SpecialChar", "Subscript", @@ -239,7 +241,24 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; } - + var pluginName = "ouphello"; + window.CKEDITOR.plugins.add(pluginName, { + icons: pluginName, + init: function (editor) { + + // button + editor.ui.addButton(pluginName, { + label: 'Hello', + command: pluginName + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + editor.insertHtml("Hello"); + } + }); + } + }); this.base(); }, From f7a5ca4b8530cdea80a8342ac6394db0f5e0fb8d Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 6 Aug 2018 15:19:06 +0530 Subject: [PATCH 059/195] Revert "GCMS 1114" This reverts commit 9bdd40382c0c235b13525eb0c1ba12bc7d57ef78. --- fields/oup-ckeditor-field.js | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 34a1f7b..530fbac 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -210,8 +210,6 @@ define(function (require, exports, module) { [ "H4", "-", - "ouphello", - "-", "Italic", "SpecialChar", "Subscript", @@ -241,24 +239,7 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; } - var pluginName = "ouphello"; - window.CKEDITOR.plugins.add(pluginName, { - icons: pluginName, - init: function (editor) { - - // button - editor.ui.addButton(pluginName, { - label: 'Hello', - command: pluginName - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - editor.insertHtml("Hello"); - } - }); - } - }); + this.base(); }, From 76c132acf3bfbb68b902af58b2aac2ad45bd93c2 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 7 Aug 2018 19:50:53 +0530 Subject: [PATCH 060/195] GCMS 1114 Adding H4 custom button --- index.js | 11 +++++++++-- plugins/action.js | 11 +++++++++++ plugins/icons/H4.png | Bin 0 -> 1547 bytes plugins/plugin.js | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 plugins/action.js create mode 100644 plugins/icons/H4.png create mode 100644 plugins/plugin.js diff --git a/index.js b/index.js index d14276e..78e83b5 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,10 @@ -define(function(require) { +define(function (require, exports, module) { require("./fields/oup-ckeditor-field.js"); -}); + + // Custom plugins/buttons + var plugin = require("./plugins/plugin.js"); + var action = require("./plugins/action.js"); + + // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using + plugin.generatePlugin("H4", "H4", action.actionH4); +}); \ No newline at end of file diff --git a/plugins/action.js b/plugins/action.js new file mode 100644 index 0000000..9e90b44 --- /dev/null +++ b/plugins/action.js @@ -0,0 +1,11 @@ +define(function (require, exports, module) { + + return { + actionH4: function (editor) { + var newElement = new CKEDITOR.dom.element("h4"); + newElement.setText(editor.getSelection().getSelectedText()); + editor.insertElement(newElement); + } + }; + +}); \ No newline at end of file diff --git a/plugins/icons/H4.png b/plugins/icons/H4.png new file mode 100644 index 0000000000000000000000000000000000000000..af7eb569b797aac5eadeeee67653b9ae0c4a3647 GIT binary patch literal 1547 zcmV+m2K4!fP){-;9!Hnn0?`3xS7qn+&w?WQ*!Rfc}Xs&WfYP((aZCF z?)&-vejWjy@Q?6>>kk!x|J#EV4^%u*rUfb?RHgyT7O!}qY!jBbL5>H`o;}+vNzxHY z>GpGs@lS(;gQEaI3%#qqzrRJ2q$Xb0G|iQvp`lbMIt~mBv{Fi6wCV8O@bK`A(>^?a z*xTEC&hPi1ru08u5kj{|Mn+x+fJ^~J#7HFasn6%@j!30O*APypc%cd>()%0QkEI zz_6kyy}Jd7#bRw37mau7>%mXC>Sh8+C5DJC%1%ttR7~_3* z>$Cq$0+3}nCxl7`;Pb~NfEbU*uOftw^RoQkt^vZ~@cBR>upQNEK$hk05Mq{d z0}zYFjuJv{5<)yq)-TBeb^vAt<^eYVU0q$nfj}T92Mog~n8dBEt-<~K_b+3N>v@Ya zrFZQCdlElJ03jZapGOFNXemo6rJAPwswm2{yf;b;p+^B=DOAV<(P;DqK@h&7l*-on zbUOXLuIoSS-Mg37{D+@x8Fg2MSP0nx0N{ZnNnd)s-W^TEdI>^EGz{Y+=B| zIB>w%)YNnfWBk0e9%HQEy?giFhK7d5U@#cx!${Y4_W<@P@ z+BD5iI61n$zCJNKJ3AN(g`!neRRg;M=`QHAnVhGNedB&$M`lao1}_|&OW4U!}+ z5kmG@hR}8Wmm4>398IUwzp1KvMp2YAypddcARmB}#rYVudx>vkJV16__!=Sfs5^I{aGC4WvI03@;&pX(A^ydD2&t<2 zOifMAJ%rHH)*6iQ#`5y=8@08ys|ODr)YjJ4yv+K3zyAXpz}(u}T7<39G|d-x3n>1lTW)-bml04B>CSvG`( z!-o$a!5G(4N=05dTf7VP_4TjS)z!7|Yc@AGZ?CSdUKd5tB!v9Dva)i=I$rMZfYU5g zDgZwVSxF243IO<=Iy?8w)85|RUtL|DQ-!v+wq~zfxxyx0cBEvX!@eIL+#e}`{XD>V zpy-)63*lvrEKeU`XW||EJTd@PRddb*Orc!>c*<&IEQy_}(8R<<(ImD5ShLXO%a>i< zN@P7y7J%p2v17+2NouhYO%Mc12>EetZf+0&whF|rr=_K(-RJW)Te9qMLI_!!pPwIb zx=qNlm#4Y8`7OWS|0dTns9_l2E-Wl0obIOh5MqDt0e~6+uo@0a6s9eX0Q<$mzg3F1ynC%j&*4-Q2bp+rWO|%ODC~D`xj7N7k;BYaJ@L(?#m~ xWB%eG7VU^_`kg`CT(bWqObq}002ovPDHLkV1fq-?e72p literal 0 HcmV?d00001 diff --git a/plugins/plugin.js b/plugins/plugin.js new file mode 100644 index 0000000..c95756f --- /dev/null +++ b/plugins/plugin.js @@ -0,0 +1,35 @@ +define(function (require, exports, module) { + + var CKEDITOR = window.CKEDITOR; + + var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; + + return { + generatePlugin: function (pluginName, label, addCommandAction) { + + CKEDITOR.plugins.add(pluginName, { + icons: pluginName, + init: function (editor) { + + editor.ui.addButton(pluginName, { + label: label, + icon: '../../../' + moduleId + '/oup-ckeditor-poc/plugins/icons/' + pluginName + '.png', + command: pluginName + }); + + editor.addCommand(pluginName, { + exec: function (editor) { + addCommandAction(editor); + } + }); + } + }); + + if (CKEDITOR.config.extraPlugins.length > 0) { + CKEDITOR.config.extraPlugins += ","; + } + CKEDITOR.config.extraPlugins += pluginName; + } + }; + +}); \ No newline at end of file From 0b4f294e7ffdb38d7469f677d9fbc41a8c6b41c2 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 7 Aug 2018 19:53:39 +0530 Subject: [PATCH 061/195] GCMS 1114 updating icon directory --- plugins/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/plugin.js b/plugins/plugin.js index c95756f..8153792 100644 --- a/plugins/plugin.js +++ b/plugins/plugin.js @@ -13,7 +13,7 @@ define(function (require, exports, module) { editor.ui.addButton(pluginName, { label: label, - icon: '../../../' + moduleId + '/oup-ckeditor-poc/plugins/icons/' + pluginName + '.png', + icon: '../../../' + moduleId + '/oup-ckeditor/plugins/icons/' + pluginName + '.png', command: pluginName }); From 85397c974e54292bd50f6530d715649ef28550c8 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 9 Aug 2018 17:11:13 +0530 Subject: [PATCH 062/195] GCMS 1095 --- fields/oup-ckeditor-field.js | 22 ++++++++++++++++++++-- index.js | 11 +++++++++-- plugins/action.js | 8 ++++++++ plugins/icons/DIV.png | Bin 0 -> 1991 bytes plugins/icons/ouphello.png | Bin 0 -> 609 bytes 5 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 plugins/icons/DIV.png create mode 100644 plugins/icons/ouphello.png diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 530fbac..b83eb48 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -4,6 +4,11 @@ define(function (require, exports, module) { var Alpaca = $.alpaca; + //window.CKEDITOR.config.scayt_autoStartup = true; + //window.CKEDITOR.config.wsc_autoStartup = true; + //window.CKEDITOR.config.disableNativeSpellChecker = false; + //window.CKEDITOR.config.removePlugins = 'scayt'; + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( /** * @lends Alpaca.Fields.OUPCKEditorField.prototype @@ -146,6 +151,7 @@ define(function (require, exports, module) { "-", "RemoveFormat" ], + '/', [ "Format", "Styles" @@ -208,7 +214,18 @@ define(function (require, exports, module) { "config5": { "toolbar": [ [ + "ouphello", "H4", + "DIV", + "BidiLtr", + "BidiRtl", + "Scayt", + "SpellChecker", + "Smiley", + "-", + "Print", + "Preview", + "PageBreak", "-", "Italic", "SpecialChar", @@ -239,7 +256,6 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; } - this.base(); }, @@ -277,10 +293,12 @@ define(function (require, exports, module) { /* end_builder_helpers */ }); + + Alpaca.registerMessages({ "noDependentField": "No local config found" }); Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); - + }); \ No newline at end of file diff --git a/index.js b/index.js index 78e83b5..9f521da 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,17 @@ define(function (require, exports, module) { - require("./fields/oup-ckeditor-field.js"); - // Custom plugins/buttons var plugin = require("./plugins/plugin.js"); var action = require("./plugins/action.js"); // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using + plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); plugin.generatePlugin("H4", "H4", action.actionH4); + plugin.generatePlugin("DIV", "DIV", action.actionDIV); + + if (CKEDITOR.config.extraPlugins.length > 0) { + CKEDITOR.config.extraPlugins += ","; + } + CKEDITOR.config.extraPlugins += "smiley"; + + require("./fields/oup-ckeditor-field.js"); }); \ No newline at end of file diff --git a/plugins/action.js b/plugins/action.js index 9e90b44..ddd855b 100644 --- a/plugins/action.js +++ b/plugins/action.js @@ -1,10 +1,18 @@ define(function (require, exports, module) { return { + actionOUPHELLO: function (editor) { + editor.insertHtml("Hello"); + }, actionH4: function (editor) { var newElement = new CKEDITOR.dom.element("h4"); newElement.setText(editor.getSelection().getSelectedText()); editor.insertElement(newElement); + }, + actionDIV: function (editor) { + var newElement = new CKEDITOR.dom.element("div"); + newElement.setText(editor.getSelection().getSelectedText()); + editor.insertElement(newElement); } }; diff --git a/plugins/icons/DIV.png b/plugins/icons/DIV.png new file mode 100644 index 0000000000000000000000000000000000000000..bf35b488d0127aa81309767f29a7e352a72ef81c GIT binary patch literal 1991 zcmV;&2RQhNP)Q%c{FfJ{zKn%djjKLh~s zwI@(gQu3l+uOAUbF%!XtLLuMf%a>oBn3%W=0O7AY0h!5UIv|tDtN{O89KBxeCswQV zAOOs)7CfF`O$jme_4S3gpz*mCT9>)YjIjR4Ub8LWnsS4C?y&`o8OQIz90K7z_s8h7B7=D5Y|IPY8K* z>C&YFkH<5Hd@3s|k7s0L{6Q|4w{slVbmq*Nzpb>ak|toWSYG5f&KL{^D+nRElv0eS z*KW7xJ$Ue7G7bS*RaKRRVVEWw3 z(cy5sdHwqJ&j3IG0JglmyjHDN|32C$+VVW_<~Z&nN@;ULL&LJ=m52ay>eQ(sLdXe0 z5Q@X$Fc*7C1XMx-hYuhAkxHeyg+TFaFc|!&&1NeO1Ooq#=s$b#;K3R!p_rg728iQ0 zKg+UqrBc~=`t)gMyt%Q2KxJj6a`WcR`-8z?l^_T^D5bIl!0G(_{Itj8xzgR;{hrU~ zn~q60Q&m;fz%WdWbbe}TsvR$G#up8g$~ z?8#U*H8tT-L?Ku*2Q+50IaUsZ@$vDG>~{MFLI{?L7XThdqL^rpVN51d3ji3TU_5;I z@V$RjWq!{^hB$Jt$nX_llp5JM;S{!-{8Ihi*rlxEFSSZ_QdW!_2Q^&Gw zm}S{7SeCU3g3#L7*oaX~`0U=jTdUXW?@>y#&?p=Z`>$TT`eT>N^<@$oFSA%IFA+ix zhr{9hf*^c5UO6Ws@WRofM@JM2#m0DPLolcca=E;dpG2%J7Rv@oDGtZq5<*@Jg+fd`+lZP?G&eVYU-B27fi$gFyS1R8 z;O#A2w!EuUDs>FQEWVG9LJ5z<#Oxb;Na<~Rf08{OuYc`Gs)iNavkaE>G>E} zhgeWGH8pyMVLl*){F)GgDil}sL{Sv|e*c$aV`G0B8XD@Ho16Prqz9Lj5Q4>KdV%Np z?1F-VpXhYDQjJDaz%Wdrh_S5i8BQkK_0 zR*Qoy#wX60ucoJ`J8$2<-8L~XaX(7+aDp!pkL6YgNfl75Gj%%M_ww`ef0L7wvybQb zEbJ2`V7J?)Rf02_OrKLqUx@$`@7%ewx4*x?Cw5Lmh_D9^99Uc>L{X%EzyBZOy_+^|+OJe9vpYLG^`oPsljY^* zKTxaHf0wc<2!hLIv%MY&1aQ%dEgy8tmX(#&C=`mi+1c68M@B|2IUJ6GNSWX~iaTRs z`zTog(mCvf#Xi}!Yu65k!_hrEJ3DJM8W&avsT`)Kr_Z&uwS52pPYlP1Gp4Ah=vBAd zo$hkEMwcs-~2qd%0pXR7C9 z0Iybsp5=~pJ+oFBSXYqi-Nerge4<_GxdK}0<*5={X~fprK9zyBj_6ALr82P6h^@8# Ze*r5-VqUYY4l4iv002ovPDHLkV1m+a$O-@e literal 0 HcmV?d00001 diff --git a/plugins/icons/ouphello.png b/plugins/icons/ouphello.png new file mode 100644 index 0000000000000000000000000000000000000000..4aa750dc46ddad7b890415a89aaf6c200d21bb1d GIT binary patch literal 609 zcmV-n0-pVeP)Sv>;qc(NyS@FG+4n5&gF*|5FPUWC%bS0G|9La;pW*#$EWC?(Vy%+uPd@gCGdz*VfkViwGh@Sqi0; zL}54)Pz6On6=Mu>9M7(-uHN-LZ_5CP2-Z3Bq9DsN(m{%mzBFqN0e7m!L8v zGYS}Es8%XOVaRk85k(IhS@)>4*&%Z~6& z#2{SZd-Q^f(KI4TkbR<>zQ>v+ROdzlBcIMW%CbcEx2R>WDfeMg&uSg!Fot z{1wu3c;O<>Itp713G6ZCDe4&J69Q2@9A>00000NkvXXu0mjfZW1J> literal 0 HcmV?d00001 From 98bf2903a36e4f292c7166a78b7f06a2ec533156 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 10 Aug 2018 18:16:00 +0530 Subject: [PATCH 063/195] GCMS-1114 GCMS-1114 --- fields/oup-ckeditor-field.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index b83eb48..97c1286 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -214,18 +214,7 @@ define(function (require, exports, module) { "config5": { "toolbar": [ [ - "ouphello", - "H4", - "DIV", - "BidiLtr", - "BidiRtl", - "Scayt", - "SpellChecker", - "Smiley", - "-", - "Print", - "Preview", - "PageBreak", + "Styles", "-", "Italic", "SpecialChar", @@ -237,7 +226,18 @@ define(function (require, exports, module) { "-", "ShowBlocks" ] - ] + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" + }, + { + "name": "Heading 4", + "element": "H4" + } + ], } }, From ea48fd1e9b1529b81b6444000caff768771c24ec Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 14 Aug 2018 12:29:28 +0530 Subject: [PATCH 064/195] GCMS 811 --- fields/oup-ckeditor-field.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 97c1286..56a3967 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -208,6 +208,9 @@ define(function (require, exports, module) { "Link", "Unlink" ] + [ + "cloudcms-link" + ] ] }, From d6b9267b5eeda3a610bdab8b1bfae412dd14aa00 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 14 Aug 2018 12:34:12 +0530 Subject: [PATCH 065/195] GCMS 811 --- fields/oup-ckeditor-field.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 56a3967..bfc93ea 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -211,7 +211,14 @@ define(function (require, exports, module) { [ "cloudcms-link" ] - ] + ], + "cloudcms-link": { + "linkPickerType": "file-picker", + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Secondary Pages" + } + } }, "config5": { From 3cd62f3615f14b2fba6b0a04fc166c9a7a59e2e9 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 14 Aug 2018 12:36:17 +0530 Subject: [PATCH 066/195] GCMS 811 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index bfc93ea..2cefacd 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -207,7 +207,7 @@ define(function (require, exports, module) { [ "Link", "Unlink" - ] + ], [ "cloudcms-link" ] From 84a440d69947612544ea089494743ead3b8641a6 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 14 Aug 2018 12:43:22 +0530 Subject: [PATCH 067/195] GCMS 811 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 2cefacd..62b6d4f 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -216,7 +216,7 @@ define(function (require, exports, module) { "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Secondary Pages" + "initialContainerPath": "../" } } From d7f1be31fc94762ea5b2f3f65dede244bd93b66c Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 14 Aug 2018 19:45:24 +0530 Subject: [PATCH 068/195] GCMS 922 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 62b6d4f..1fda97b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -263,8 +263,8 @@ define(function (require, exports, module) { */ setup: function () { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { - this.options.ckeditor.format_tags = 'p;h1;h2;h3;h4;pre'; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; + this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } this.base(); }, From b3eae15071358ee0cd402665b0680b517dc1bc6d Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 17 Aug 2018 16:18:40 +0530 Subject: [PATCH 069/195] GCMS 1322 changes as per ticket --- fields/oup-ckeditor-field.js | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 1fda97b..6c6f4a3 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -21,6 +21,8 @@ define(function (require, exports, module) { "Cut", "Copy", "Paste", + "PasteText", + "PasteFromWord", "-", "Undo", "Redo" @@ -68,7 +70,9 @@ define(function (require, exports, module) { "Styles" ], [ - "cloudcms-image", + "cloudcms-image" + ] + [ "cloudcms-iframe" ] ], @@ -86,17 +90,6 @@ define(function (require, exports, module) { "name": "Heading 3", "element": "H3" }, - { - "name": "Heading 4", - "element": "H4" - }, - { - "name": "OUP Custom Inline", - "element": "span", - "attributes": { - "class": "mine" - } - }, { "name": "OUP bulleted list", "element": "ul", @@ -174,17 +167,6 @@ define(function (require, exports, module) { "name": "Heading 3", "element": "H3" }, - { - "name": "Heading 4", - "element": "H4" - }, - { - "name": "OUP Custom Inline", - "element": "span", - "attributes": { - "class": "mine" - } - }, { "name": "OUP bulleted list", "element": "ul", From 93f9be2cb40c6131984ba31d4a8ff5f67dcccd66 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 17 Aug 2018 16:25:00 +0530 Subject: [PATCH 070/195] GCMS 1322 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 6c6f4a3..cf09d96 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -71,7 +71,7 @@ define(function (require, exports, module) { ], [ "cloudcms-image" - ] + ], [ "cloudcms-iframe" ] From 48427c42e0a468c3f3b2017914dc27754889d13f Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 17 Aug 2018 17:06:58 +0530 Subject: [PATCH 071/195] GCMS 1314 --- fields/oup-ckeditor-field.js | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index cf09d96..97b6095 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -230,6 +230,65 @@ define(function (require, exports, module) { "element": "H4" } ], + }, + "config6": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Bold", + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" + ] + ] } }, From 23286f93cdd6c8f8ed42081dcae2cbcc26ce752c Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 20 Aug 2018 15:11:08 +0530 Subject: [PATCH 072/195] GCMS-1298, GCMS-1168 --- fields/oup-ckeditor-field.js | 77 +++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 97b6095..74b930c 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -96,6 +96,20 @@ define(function (require, exports, module) { "attributes": { "class": "bullet" } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } } ], @@ -173,6 +187,20 @@ define(function (require, exports, module) { "attributes": { "class": "bullet" } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } } ], "cloudcms-image": { @@ -280,7 +308,8 @@ define(function (require, exports, module) { "JustifyBlock" ], [ - "Format" + "Format", + "Styles" ], [ "cloudcms-image" @@ -288,7 +317,51 @@ define(function (require, exports, module) { [ "cloudcms-iframe" ] - ] + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" + }, + { + "name": "Heading 2", + "element": "H2" + }, + { + "name": "Heading 3", + "element": "H3" + }, + { + "name": "OUP bulleted list", + "element": "ul", + "attributes": { + "class": "bullet" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + } } }, From ea0a60e01fc24768ef1e5c612c75a5468c951a3a Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 20 Aug 2018 15:20:06 +0530 Subject: [PATCH 073/195] GCMS-1298, GCMS-1168 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 74b930c..e6b30ec 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -208,7 +208,7 @@ define(function (require, exports, module) { "imageUploadPath": "../Image Library", "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" + "initialContainerPath": "../" } } }, @@ -359,7 +359,7 @@ define(function (require, exports, module) { "imageUploadPath": "../Image Library", "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" + "initialContainerPath": "../" } } } From 371eb202b19bb9b9e58a9a1acade8ac18afcbdca Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 20 Aug 2018 15:22:39 +0530 Subject: [PATCH 074/195] GCMS-1298, GCMS-1168 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index e6b30ec..93d894d 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -226,7 +226,7 @@ define(function (require, exports, module) { "linkPickerType": "file-picker", "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../" + "initialContainerPath": "/" } } @@ -359,7 +359,7 @@ define(function (require, exports, module) { "imageUploadPath": "../Image Library", "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../" + "initialContainerPath": "/" } } } From 62d41a8702af9d5d4b715b5e096debce6ed189b0 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 20 Aug 2018 15:56:56 +0530 Subject: [PATCH 075/195] GCMS-1298, GCMS-1168 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 93d894d..7834e52 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -208,7 +208,7 @@ define(function (require, exports, module) { "imageUploadPath": "../Image Library", "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../" + "initialContainerPath": "./" } } }, @@ -359,7 +359,7 @@ define(function (require, exports, module) { "imageUploadPath": "../Image Library", "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "/" + "initialContainerPath": "./" } } } From 6be05033073b93158185651586ae8cdb988c81f2 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 22 Aug 2018 15:00:30 +0530 Subject: [PATCH 076/195] GCMS 811 reverting changes for 811 which are not required now after change given by Ben --- fields/oup-ckeditor-field.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7834e52..90df85d 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -217,19 +217,8 @@ define(function (require, exports, module) { [ "Link", "Unlink" - ], - [ - "cloudcms-link" - ] - ], - "cloudcms-link": { - "linkPickerType": "file-picker", - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "/" - } - } - + ] + ] }, "config5": { "toolbar": [ From 779457999ea1dcb8d8cf67cf735dbc30126d6883 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 23 Aug 2018 13:16:01 +0530 Subject: [PATCH 077/195] WCMS PI-5 Sprint 4 GCMS-1345 --- fields/oup-ckeditor-field.js | 112 ++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 90df85d..d576607 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -82,14 +82,6 @@ define(function (require, exports, module) { "name": "Paragraph", "element": "p" }, - { - "name": "Heading 2", - "element": "H2" - }, - { - "name": "Heading 3", - "element": "H3" - }, { "name": "OUP bulleted list", "element": "ul", @@ -156,15 +148,13 @@ define(function (require, exports, module) { "Superscript", "SpecialChar", "-", - "RemoveFormat" + "RemoveFormat", + "-", + "ShowBlocks" ], - '/', [ "Format", "Styles" - ], - [ - "cloudcms-image" ] ], "removeButtons": null, @@ -173,14 +163,6 @@ define(function (require, exports, module) { "name": "Paragraph", "element": "p" }, - { - "name": "Heading 2", - "element": "H2" - }, - { - "name": "Heading 3", - "element": "H3" - }, { "name": "OUP bulleted list", "element": "ul", @@ -202,15 +184,7 @@ define(function (require, exports, module) { "class": "floatRight" } } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - } - } + ] }, "config4": { "toolbar": [ @@ -314,12 +288,76 @@ define(function (require, exports, module) { "element": "p" }, { - "name": "Heading 2", - "element": "H2" + "name": "OUP bulleted list", + "element": "ul", + "attributes": { + "class": "bullet" + } }, { - "name": "Heading 3", - "element": "H3" + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + } + } + }, + "config7": { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Bold", + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + ], + "removeButtons": null, + "stylesSet": [ + { + "name": "Paragraph", + "element": "p" }, { "name": "OUP bulleted list", @@ -367,7 +405,11 @@ define(function (require, exports, module) { setup: function () { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; + if(this.options.ckeditor && this.options.ckeditor == "config5"){ + this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; + } else { + this.options.ckeditor.format_tags = 'p;h2;h3;pre'; + } } this.base(); }, From be68d5e9bc3822f941e8737df8704eb91920a33b Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 23 Aug 2018 14:06:06 +0530 Subject: [PATCH 078/195] WCMS PI-5 Sprint 4 GCMS-1345 --- fields/oup-ckeditor-field.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d576607..b874f48 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -197,7 +197,7 @@ define(function (require, exports, module) { "config5": { "toolbar": [ [ - "Styles", + "Format", "-", "Italic", "SpecialChar", @@ -210,17 +210,7 @@ define(function (require, exports, module) { "ShowBlocks" ] ], - "removeButtons": null, - "stylesSet": [ - { - "name": "Paragraph", - "element": "p" - }, - { - "name": "Heading 4", - "element": "H4" - } - ], + "removeButtons": null }, "config6": { "toolbar": [ From d62e24ac7bc7123bf31486bf7b8f30b287726210 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 23 Aug 2018 14:35:04 +0530 Subject: [PATCH 079/195] WCMS PI-5 Sprint 4 GCMS-1345 --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index b874f48..c5c13ae 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -394,8 +394,9 @@ define(function (require, exports, module) { */ setup: function () { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { + var type = this.options.ckeditor; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - if(this.options.ckeditor && this.options.ckeditor == "config5"){ + if(type && type == "config5"){ this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; From edf30fa547aa0e064e063bb98050190cc44bdb94 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 23 Aug 2018 14:42:59 +0530 Subject: [PATCH 080/195] Testing new GCMS feature of dynamic path --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c5c13ae..332d827 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -375,8 +375,8 @@ define(function (require, exports, module) { "imagePickerType": "file-picker", "imageUploadPath": "../Image Library", "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" + "rootContainerPath": "./", + "initialContainerPath": "{{nearestJournalFolderPath}}/Row Containers, Content Blocks, and Widgets" } } } From 50613ae5ba0739f33478732b6ea6e46061a687f8 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 23 Aug 2018 14:44:40 +0530 Subject: [PATCH 081/195] Revert "Testing new GCMS feature of dynamic path" This reverts commit edf30fa547aa0e064e063bb98050190cc44bdb94. --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 332d827..c5c13ae 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -375,8 +375,8 @@ define(function (require, exports, module) { "imagePickerType": "file-picker", "imageUploadPath": "../Image Library", "imagePickerConfig": { - "rootContainerPath": "./", - "initialContainerPath": "{{nearestJournalFolderPath}}/Row Containers, Content Blocks, and Widgets" + "rootContainerPath": "../../..", + "initialContainerPath": "./" } } } From 9e1fdfe9ae39e710933bf4efcf384d07f342a0ca Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 24 Aug 2018 11:54:23 +0530 Subject: [PATCH 082/195] WCMS PI-5 Sprint 4 GCMS-1345 --- fields/oup-ckeditor-field.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c5c13ae..b9e48f9 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -155,7 +155,10 @@ define(function (require, exports, module) { [ "Format", "Styles" - ] + ], + [ + "cloudcms-image" + ], ], "removeButtons": null, "stylesSet": [ @@ -184,7 +187,15 @@ define(function (require, exports, module) { "class": "floatRight" } } - ] + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + } + } }, "config4": { "toolbar": [ @@ -338,10 +349,7 @@ define(function (require, exports, module) { [ "Format", "Styles" - ], - [ - "cloudcms-image" - ], + ] ], "removeButtons": null, "stylesSet": [ @@ -370,15 +378,7 @@ define(function (require, exports, module) { "class": "floatRight" } } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - } - } + ] } }, @@ -397,7 +397,7 @@ define(function (require, exports, module) { var type = this.options.ckeditor; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; if(type && type == "config5"){ - this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; + this.options.ckeditor.format_tags = 'p;h4;pre'; } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } From 19e72efbd79ee85851e62b54fc719045b3d70734 Mon Sep 17 00:00:00 2001 From: padgettm Date: Tue, 28 Aug 2018 17:40:05 +0100 Subject: [PATCH 083/195] testing --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 9f521da..8b7cc97 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ define(function (require, exports, module) { // Custom plugins/buttons - var plugin = require("./plugins/plugin.js"); - var action = require("./plugins/action.js"); + var plugin = require("plugins/plugin.js"); + var action = require("plugins/action.js"); // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); @@ -14,4 +14,4 @@ define(function (require, exports, module) { CKEDITOR.config.extraPlugins += "smiley"; require("./fields/oup-ckeditor-field.js"); -}); \ No newline at end of file +}); From bb8c411d6deb53b1a2820a4f71bf07404b266102 Mon Sep 17 00:00:00 2001 From: padgettm Date: Tue, 28 Aug 2018 17:41:34 +0100 Subject: [PATCH 084/195] testing --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 8b7cc97..f460827 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ define(function (require, exports, module) { // Custom plugins/buttons - var plugin = require("plugins/plugin.js"); - var action = require("plugins/action.js"); + var plugin = require(".plugins/plugin.js"); + var action = require(".plugins/action.js"); // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); From 522d4f889ea3ef607e7f9aba80a20b114df13da3 Mon Sep 17 00:00:00 2001 From: padgettm Date: Tue, 28 Aug 2018 17:42:59 +0100 Subject: [PATCH 085/195] testing --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index f460827..324c275 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ define(function (require, exports, module) { // Custom plugins/buttons - var plugin = require(".plugins/plugin.js"); - var action = require(".plugins/action.js"); + var plugin = require("./plugins/plugin.js"); + var action = require("./plugins/action.js"); // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); From dc58cb83ba6fee2bbd5c318256e27d8ce7baf251 Mon Sep 17 00:00:00 2001 From: padgettm Date: Tue, 28 Aug 2018 17:46:38 +0100 Subject: [PATCH 086/195] testing --- index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 324c275..5c69ef6 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,10 @@ define(function (require, exports, module) { // Custom plugins/buttons - var plugin = require("./plugins/plugin.js"); - var action = require("./plugins/action.js"); + //var plugin = require("./plugins/plugin.js"); + // var action = require("./plugins/action.js"); + + var plugin = require("/oneteam-1535212568443/modules/app/plugins/ckeditor/cloudcms-image/plugin.js"); + var action = require("/oneteam-1535212568443/modules/app/plugins/ckeditor/cloudcms-image/action.js"); // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); From 77c91dcf6684bfdd4485ab010423a381f2c1ea7c Mon Sep 17 00:00:00 2001 From: padgettm Date: Tue, 28 Aug 2018 17:48:23 +0100 Subject: [PATCH 087/195] testing --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 5c69ef6..652bcf9 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,10 @@ define(function (require, exports, module) { // Custom plugins/buttons //var plugin = require("./plugins/plugin.js"); - // var action = require("./plugins/action.js"); + var action = require("./plugins/action.js"); var plugin = require("/oneteam-1535212568443/modules/app/plugins/ckeditor/cloudcms-image/plugin.js"); - var action = require("/oneteam-1535212568443/modules/app/plugins/ckeditor/cloudcms-image/action.js"); + // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); From 818d02317ce504ebdc4d0ed209e1791e5b930755 Mon Sep 17 00:00:00 2001 From: padgettm Date: Tue, 28 Aug 2018 17:52:07 +0100 Subject: [PATCH 088/195] reverting --- index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/index.js b/index.js index 652bcf9..9172d68 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,8 @@ define(function (require, exports, module) { // Custom plugins/buttons - //var plugin = require("./plugins/plugin.js"); + var plugin = require("./plugins/plugin.js"); var action = require("./plugins/action.js"); - var plugin = require("/oneteam-1535212568443/modules/app/plugins/ckeditor/cloudcms-image/plugin.js"); - // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); From 934ea7072a02344229f2749f418def20d41f6303 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 29 Aug 2018 11:46:59 +0530 Subject: [PATCH 089/195] Removing custom plugins I am removing this temporary as anyway we are not using them currently --- fields/oup-ckeditor-field.js | 5 ----- index.js | 15 --------------- plugins/action.js | 19 ------------------- plugins/icons/DIV.png | Bin 1991 -> 0 bytes plugins/icons/H4.png | Bin 1547 -> 0 bytes plugins/icons/ouphello.png | Bin 609 -> 0 bytes plugins/plugin.js | 35 ----------------------------------- 7 files changed, 74 deletions(-) delete mode 100644 plugins/action.js delete mode 100644 plugins/icons/DIV.png delete mode 100644 plugins/icons/H4.png delete mode 100644 plugins/icons/ouphello.png delete mode 100644 plugins/plugin.js diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index b9e48f9..0cdcd36 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -4,11 +4,6 @@ define(function (require, exports, module) { var Alpaca = $.alpaca; - //window.CKEDITOR.config.scayt_autoStartup = true; - //window.CKEDITOR.config.wsc_autoStartup = true; - //window.CKEDITOR.config.disableNativeSpellChecker = false; - //window.CKEDITOR.config.removePlugins = 'scayt'; - Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( /** * @lends Alpaca.Fields.OUPCKEditorField.prototype diff --git a/index.js b/index.js index 9172d68..8a54f35 100644 --- a/index.js +++ b/index.js @@ -1,18 +1,3 @@ define(function (require, exports, module) { - // Custom plugins/buttons - var plugin = require("./plugins/plugin.js"); - var action = require("./plugins/action.js"); - - - // register new plugin below. Pass Plugin Name, Plugin Label and Action item as parameters and put action item in action.js using - plugin.generatePlugin("ouphello", "Hello", action.actionOUPHELLO); - plugin.generatePlugin("H4", "H4", action.actionH4); - plugin.generatePlugin("DIV", "DIV", action.actionDIV); - - if (CKEDITOR.config.extraPlugins.length > 0) { - CKEDITOR.config.extraPlugins += ","; - } - CKEDITOR.config.extraPlugins += "smiley"; - require("./fields/oup-ckeditor-field.js"); }); diff --git a/plugins/action.js b/plugins/action.js deleted file mode 100644 index ddd855b..0000000 --- a/plugins/action.js +++ /dev/null @@ -1,19 +0,0 @@ -define(function (require, exports, module) { - - return { - actionOUPHELLO: function (editor) { - editor.insertHtml("Hello"); - }, - actionH4: function (editor) { - var newElement = new CKEDITOR.dom.element("h4"); - newElement.setText(editor.getSelection().getSelectedText()); - editor.insertElement(newElement); - }, - actionDIV: function (editor) { - var newElement = new CKEDITOR.dom.element("div"); - newElement.setText(editor.getSelection().getSelectedText()); - editor.insertElement(newElement); - } - }; - -}); \ No newline at end of file diff --git a/plugins/icons/DIV.png b/plugins/icons/DIV.png deleted file mode 100644 index bf35b488d0127aa81309767f29a7e352a72ef81c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1991 zcmV;&2RQhNP)Q%c{FfJ{zKn%djjKLh~s zwI@(gQu3l+uOAUbF%!XtLLuMf%a>oBn3%W=0O7AY0h!5UIv|tDtN{O89KBxeCswQV zAOOs)7CfF`O$jme_4S3gpz*mCT9>)YjIjR4Ub8LWnsS4C?y&`o8OQIz90K7z_s8h7B7=D5Y|IPY8K* z>C&YFkH<5Hd@3s|k7s0L{6Q|4w{slVbmq*Nzpb>ak|toWSYG5f&KL{^D+nRElv0eS z*KW7xJ$Ue7G7bS*RaKRRVVEWw3 z(cy5sdHwqJ&j3IG0JglmyjHDN|32C$+VVW_<~Z&nN@;ULL&LJ=m52ay>eQ(sLdXe0 z5Q@X$Fc*7C1XMx-hYuhAkxHeyg+TFaFc|!&&1NeO1Ooq#=s$b#;K3R!p_rg728iQ0 zKg+UqrBc~=`t)gMyt%Q2KxJj6a`WcR`-8z?l^_T^D5bIl!0G(_{Itj8xzgR;{hrU~ zn~q60Q&m;fz%WdWbbe}TsvR$G#up8g$~ z?8#U*H8tT-L?Ku*2Q+50IaUsZ@$vDG>~{MFLI{?L7XThdqL^rpVN51d3ji3TU_5;I z@V$RjWq!{^hB$Jt$nX_llp5JM;S{!-{8Ihi*rlxEFSSZ_QdW!_2Q^&Gw zm}S{7SeCU3g3#L7*oaX~`0U=jTdUXW?@>y#&?p=Z`>$TT`eT>N^<@$oFSA%IFA+ix zhr{9hf*^c5UO6Ws@WRofM@JM2#m0DPLolcca=E;dpG2%J7Rv@oDGtZq5<*@Jg+fd`+lZP?G&eVYU-B27fi$gFyS1R8 z;O#A2w!EuUDs>FQEWVG9LJ5z<#Oxb;Na<~Rf08{OuYc`Gs)iNavkaE>G>E} zhgeWGH8pyMVLl*){F)GgDil}sL{Sv|e*c$aV`G0B8XD@Ho16Prqz9Lj5Q4>KdV%Np z?1F-VpXhYDQjJDaz%Wdrh_S5i8BQkK_0 zR*Qoy#wX60ucoJ`J8$2<-8L~XaX(7+aDp!pkL6YgNfl75Gj%%M_ww`ef0L7wvybQb zEbJ2`V7J?)Rf02_OrKLqUx@$`@7%ewx4*x?Cw5Lmh_D9^99Uc>L{X%EzyBZOy_+^|+OJe9vpYLG^`oPsljY^* zKTxaHf0wc<2!hLIv%MY&1aQ%dEgy8tmX(#&C=`mi+1c68M@B|2IUJ6GNSWX~iaTRs z`zTog(mCvf#Xi}!Yu65k!_hrEJ3DJM8W&avsT`)Kr_Z&uwS52pPYlP1Gp4Ah=vBAd zo$hkEMwcs-~2qd%0pXR7C9 z0Iybsp5=~pJ+oFBSXYqi-Nerge4<_GxdK}0<*5={X~fprK9zyBj_6ALr82P6h^@8# Ze*r5-VqUYY4l4iv002ovPDHLkV1m+a$O-@e diff --git a/plugins/icons/H4.png b/plugins/icons/H4.png deleted file mode 100644 index af7eb569b797aac5eadeeee67653b9ae0c4a3647..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1547 zcmV+m2K4!fP){-;9!Hnn0?`3xS7qn+&w?WQ*!Rfc}Xs&WfYP((aZCF z?)&-vejWjy@Q?6>>kk!x|J#EV4^%u*rUfb?RHgyT7O!}qY!jBbL5>H`o;}+vNzxHY z>GpGs@lS(;gQEaI3%#qqzrRJ2q$Xb0G|iQvp`lbMIt~mBv{Fi6wCV8O@bK`A(>^?a z*xTEC&hPi1ru08u5kj{|Mn+x+fJ^~J#7HFasn6%@j!30O*APypc%cd>()%0QkEI zz_6kyy}Jd7#bRw37mau7>%mXC>Sh8+C5DJC%1%ttR7~_3* z>$Cq$0+3}nCxl7`;Pb~NfEbU*uOftw^RoQkt^vZ~@cBR>upQNEK$hk05Mq{d z0}zYFjuJv{5<)yq)-TBeb^vAt<^eYVU0q$nfj}T92Mog~n8dBEt-<~K_b+3N>v@Ya zrFZQCdlElJ03jZapGOFNXemo6rJAPwswm2{yf;b;p+^B=DOAV<(P;DqK@h&7l*-on zbUOXLuIoSS-Mg37{D+@x8Fg2MSP0nx0N{ZnNnd)s-W^TEdI>^EGz{Y+=B| zIB>w%)YNnfWBk0e9%HQEy?giFhK7d5U@#cx!${Y4_W<@P@ z+BD5iI61n$zCJNKJ3AN(g`!neRRg;M=`QHAnVhGNedB&$M`lao1}_|&OW4U!}+ z5kmG@hR}8Wmm4>398IUwzp1KvMp2YAypddcARmB}#rYVudx>vkJV16__!=Sfs5^I{aGC4WvI03@;&pX(A^ydD2&t<2 zOifMAJ%rHH)*6iQ#`5y=8@08ys|ODr)YjJ4yv+K3zyAXpz}(u}T7<39G|d-x3n>1lTW)-bml04B>CSvG`( z!-o$a!5G(4N=05dTf7VP_4TjS)z!7|Yc@AGZ?CSdUKd5tB!v9Dva)i=I$rMZfYU5g zDgZwVSxF243IO<=Iy?8w)85|RUtL|DQ-!v+wq~zfxxyx0cBEvX!@eIL+#e}`{XD>V zpy-)63*lvrEKeU`XW||EJTd@PRddb*Orc!>c*<&IEQy_}(8R<<(ImD5ShLXO%a>i< zN@P7y7J%p2v17+2NouhYO%Mc12>EetZf+0&whF|rr=_K(-RJW)Te9qMLI_!!pPwIb zx=qNlm#4Y8`7OWS|0dTns9_l2E-Wl0obIOh5MqDt0e~6+uo@0a6s9eX0Q<$mzg3F1ynC%j&*4-Q2bp+rWO|%ODC~D`xj7N7k;BYaJ@L(?#m~ xWB%eG7VU^_`kg`CT(bWqObq}002ovPDHLkV1fq-?e72p diff --git a/plugins/icons/ouphello.png b/plugins/icons/ouphello.png deleted file mode 100644 index 4aa750dc46ddad7b890415a89aaf6c200d21bb1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcmV-n0-pVeP)Sv>;qc(NyS@FG+4n5&gF*|5FPUWC%bS0G|9La;pW*#$EWC?(Vy%+uPd@gCGdz*VfkViwGh@Sqi0; zL}54)Pz6On6=Mu>9M7(-uHN-LZ_5CP2-Z3Bq9DsN(m{%mzBFqN0e7m!L8v zGYS}Es8%XOVaRk85k(IhS@)>4*&%Z~6& z#2{SZd-Q^f(KI4TkbR<>zQ>v+ROdzlBcIMW%CbcEx2R>WDfeMg&uSg!Fot z{1wu3c;O<>Itp713G6ZCDe4&J69Q2@9A>00000NkvXXu0mjfZW1J> diff --git a/plugins/plugin.js b/plugins/plugin.js deleted file mode 100644 index 8153792..0000000 --- a/plugins/plugin.js +++ /dev/null @@ -1,35 +0,0 @@ -define(function (require, exports, module) { - - var CKEDITOR = window.CKEDITOR; - - var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; - - return { - generatePlugin: function (pluginName, label, addCommandAction) { - - CKEDITOR.plugins.add(pluginName, { - icons: pluginName, - init: function (editor) { - - editor.ui.addButton(pluginName, { - label: label, - icon: '../../../' + moduleId + '/oup-ckeditor/plugins/icons/' + pluginName + '.png', - command: pluginName - }); - - editor.addCommand(pluginName, { - exec: function (editor) { - addCommandAction(editor); - } - }); - } - }); - - if (CKEDITOR.config.extraPlugins.length > 0) { - CKEDITOR.config.extraPlugins += ","; - } - CKEDITOR.config.extraPlugins += pluginName; - } - }; - -}); \ No newline at end of file From 1a3770a714ee4a307f47092f920088715806a96d Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 30 Aug 2018 14:43:19 +0530 Subject: [PATCH 090/195] WCMS PI-5 Sprint 4 GCMS-1329 Adding new style feature of notice div tag --- fields/oup-ckeditor-field.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 0cdcd36..6a88ae5 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -77,6 +77,13 @@ define(function (require, exports, module) { "name": "Paragraph", "element": "p" }, + { + "name": "OUP Notice", + "element": "div", + "attributes": { + "class": "notice" + } + }, { "name": "OUP bulleted list", "element": "ul", From a95a64da886156137226ed1c490762807aa1faf6 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 30 Aug 2018 13:13:06 +0100 Subject: [PATCH 091/195] Removed cloudcms custom buttons --- fields/oup-ckeditor-field.js | 45 +++--------------------------------- 1 file changed, 3 insertions(+), 42 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 6a88ae5..65b8e7a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -63,12 +63,6 @@ define(function (require, exports, module) { [ "Format", "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" ] ], "removeButtons": null, @@ -107,14 +101,6 @@ define(function (require, exports, module) { } ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" - } - }, }, "config2": { @@ -157,10 +143,7 @@ define(function (require, exports, module) { [ "Format", "Styles" - ], - [ - "cloudcms-image" - ], + ] ], "removeButtons": null, "stylesSet": [ @@ -189,15 +172,7 @@ define(function (require, exports, module) { "class": "floatRight" } } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - } - } + ] }, "config4": { "toolbar": [ @@ -276,12 +251,6 @@ define(function (require, exports, module) { [ "Format", "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" ] ], "removeButtons": null, @@ -311,15 +280,7 @@ define(function (require, exports, module) { "class": "floatRight" } } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - } - } + ] }, "config7": { "toolbar": [ From 62537679e581d90c6ddcb8a399ebf0ad94de7a70 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 30 Aug 2018 13:34:30 +0100 Subject: [PATCH 092/195] Revert "Removed cloudcms custom buttons" This reverts commit a95a64da886156137226ed1c490762807aa1faf6. --- fields/oup-ckeditor-field.js | 45 +++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 65b8e7a..6a88ae5 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -63,6 +63,12 @@ define(function (require, exports, module) { [ "Format", "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" ] ], "removeButtons": null, @@ -101,6 +107,14 @@ define(function (require, exports, module) { } ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library" + } + }, }, "config2": { @@ -143,7 +157,10 @@ define(function (require, exports, module) { [ "Format", "Styles" - ] + ], + [ + "cloudcms-image" + ], ], "removeButtons": null, "stylesSet": [ @@ -172,7 +189,15 @@ define(function (require, exports, module) { "class": "floatRight" } } - ] + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + } + } }, "config4": { "toolbar": [ @@ -251,6 +276,12 @@ define(function (require, exports, module) { [ "Format", "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" ] ], "removeButtons": null, @@ -280,7 +311,15 @@ define(function (require, exports, module) { "class": "floatRight" } } - ] + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Image Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + } + } }, "config7": { "toolbar": [ From e799f21a4ba3aed4f5b55ecd97e420dd4238e13f Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 3 Sep 2018 17:54:37 +0530 Subject: [PATCH 093/195] GCMS-1359 adding cloudcms-link in config1 --- fields/oup-ckeditor-field.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 6a88ae5..d5191bb 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -69,6 +69,9 @@ define(function (require, exports, module) { ], [ "cloudcms-iframe" + ], + [ + "cloudcms-link" ] ], "removeButtons": null, @@ -115,7 +118,14 @@ define(function (require, exports, module) { "initialContainerPath": "../Image Library" } }, - + "cloudcms-link": { + "imagePickerType": "file-picker", + "imageUploadPath": "../Document Library", + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Document Library" + } + } }, "config2": { "toolbar": [ From 05259ead47d6c3f295610aac0bcc6ca51995276f Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 3 Sep 2018 18:01:25 +0530 Subject: [PATCH 094/195] GCMS-1359 --- fields/oup-ckeditor-field.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d5191bb..5ce0a19 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -119,9 +119,9 @@ define(function (require, exports, module) { } }, "cloudcms-link": { - "imagePickerType": "file-picker", - "imageUploadPath": "../Document Library", - "imagePickerConfig": { + "linkPickerType": "file-picker", + "linkUploadPath": "../Document Library", + "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Document Library" } From 49d85d81d3896d5a902086e2b14ba81f5f12ee13 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 9 Oct 2018 19:14:43 +0530 Subject: [PATCH 095/195] GCMS-1491 Removing upload buttons from pickers --- fields/oup-ckeditor-field.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 5ce0a19..3c45454 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -116,7 +116,8 @@ define(function (require, exports, module) { "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library" - } + }, + "uploadPath": null }, "cloudcms-link": { "linkPickerType": "file-picker", @@ -124,7 +125,8 @@ define(function (require, exports, module) { "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Document Library" - } + }, + "uploadPath": null } }, "config2": { @@ -206,7 +208,8 @@ define(function (require, exports, module) { "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "./" - } + }, + "uploadPath": null } }, "config4": { @@ -328,7 +331,8 @@ define(function (require, exports, module) { "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "./" - } + }, + "uploadPath": null } }, "config7": { From 39f0eacfa746dd8af0f583ad4a54123f6e5fffba Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 9 Oct 2018 19:20:48 +0530 Subject: [PATCH 096/195] GCMS-1491 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 3c45454..3b7e396 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -117,7 +117,7 @@ define(function (require, exports, module) { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library" }, - "uploadPath": null + "imageUploadPath": null }, "cloudcms-link": { "linkPickerType": "file-picker", @@ -126,7 +126,7 @@ define(function (require, exports, module) { "rootContainerPath": "../../..", "initialContainerPath": "../Document Library" }, - "uploadPath": null + "linkUploadPath": null } }, "config2": { From 80615b897469cb9007948ed3f91541e6bc34eaa0 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 9 Oct 2018 19:43:07 +0530 Subject: [PATCH 097/195] GCMS-1491 --- fields/oup-ckeditor-field.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 3b7e396..de4cfb5 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -115,7 +115,8 @@ define(function (require, exports, module) { "imageUploadPath": "../Image Library", "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library" + "initialContainerPath": "../Image Library", + "uploadPath": null, }, "imageUploadPath": null }, @@ -124,7 +125,8 @@ define(function (require, exports, module) { "linkUploadPath": "../Document Library", "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Document Library" + "initialContainerPath": "../Document Library", + "uploadPath": null, }, "linkUploadPath": null } From c6cf2276f3dce422c15c8dc4060689efbc55c86f Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 9 Oct 2018 19:52:34 +0530 Subject: [PATCH 098/195] GCMS-1491 --- fields/oup-ckeditor-field.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index de4cfb5..c84e7f5 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -112,23 +112,20 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", + "imageUploadPath": null, "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library", - "uploadPath": null, - }, - "imageUploadPath": null + } }, "cloudcms-link": { "linkPickerType": "file-picker", - "linkUploadPath": "../Document Library", + "linkUploadPath": null, "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Document Library", "uploadPath": null, - }, - "linkUploadPath": null + } } }, "config2": { @@ -206,7 +203,7 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", + "imageUploadPath": null, "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "./" @@ -329,7 +326,7 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": "../Image Library", + "imageUploadPath": null, "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "./" From 1e4c9e6638dd31517ed5e2feb2d573e53ba1b826 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 9 Oct 2018 20:04:39 +0530 Subject: [PATCH 099/195] GCMS-1491 --- fields/oup-ckeditor-field.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c84e7f5..30f9fa5 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -112,7 +112,6 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": null, "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library", @@ -120,7 +119,6 @@ define(function (require, exports, module) { }, "cloudcms-link": { "linkPickerType": "file-picker", - "linkUploadPath": null, "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Document Library", From b89e9c1392be7600d33cb267e269cf1b62eddacc Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 9 Oct 2018 20:39:17 +0530 Subject: [PATCH 100/195] GCMS-1491 --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 30f9fa5..ae145e0 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -112,6 +112,7 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", + "imageUploadPath": "", "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library", @@ -119,10 +120,10 @@ define(function (require, exports, module) { }, "cloudcms-link": { "linkPickerType": "file-picker", + "uploadPath": null, "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Document Library", - "uploadPath": null, } } }, From 0bc5f29137cb0c449b9ee54a3d8d2af2d5a03c63 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 15 Oct 2018 12:55:08 +0530 Subject: [PATCH 101/195] GCMS-1319 Table options editing --- fields/oup-ckeditor-field.js | 44 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index ae145e0..7f29a2a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -73,10 +73,9 @@ define(function (require, exports, module) { [ "cloudcms-link" ] - ], - "removeButtons": null, - "stylesSet": [ - { + ], + "removeButtons": null, + "stylesSet": [{ "name": "Paragraph", "element": "p" }, @@ -108,7 +107,7 @@ define(function (require, exports, module) { "class": "floatRight" } } - + ], "cloudcms-image": { "imagePickerType": "file-picker", @@ -173,8 +172,7 @@ define(function (require, exports, module) { ], ], "removeButtons": null, - "stylesSet": [ - { + "stylesSet": [{ "name": "Paragraph", "element": "p" }, @@ -215,7 +213,7 @@ define(function (require, exports, module) { [ "Link", "Unlink" - ] + ] ] }, "config5": { @@ -296,8 +294,7 @@ define(function (require, exports, module) { ] ], "removeButtons": null, - "stylesSet": [ - { + "stylesSet": [{ "name": "Paragraph", "element": "p" }, @@ -366,8 +363,7 @@ define(function (require, exports, module) { ] ], "removeButtons": null, - "stylesSet": [ - { + "stylesSet": [{ "name": "Paragraph", "element": "p" }, @@ -410,10 +406,10 @@ define(function (require, exports, module) { if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { var type = this.options.ckeditor; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; - if(type && type == "config5"){ - this.options.ckeditor.format_tags = 'p;h4;pre'; + if (type && type == "config5") { + this.options.ckeditor.format_tags = 'p;h4;pre'; } else { - this.options.ckeditor.format_tags = 'p;h2;h3;pre'; + this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } } this.base(); @@ -453,12 +449,26 @@ define(function (require, exports, module) { /* end_builder_helpers */ }); - + Alpaca.registerMessages({ "noDependentField": "No local config found" }); + window.CKEDITOR.on('dialogDefinition', function (ev) { + var dialogName = ev.data.name; + var dialogDefinition = ev.data.definition; + if (dialogName == "table") { + var infoTab = dialogDefinition.getContents("info"); + infoTab.get("txtRows")["defau1t"] = 100; + infoTab.get("txtCols")["defau1t"] = 100; + + var advancedTab = dialogDefinition.getContents("advanced"); + advancedTab.get("adeSSClasses")["default"] = "mystyle"; + advancedTab.get("advId")["defau1t"] = "myid"; + } + }); + Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); - + }); \ No newline at end of file From a16ae0cb49194325c37154fa970c694d9f861b13 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 15 Oct 2018 13:01:41 +0530 Subject: [PATCH 102/195] GCMS-1319 --- fields/oup-ckeditor-field.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7f29a2a..9446a87 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -454,17 +454,19 @@ define(function (require, exports, module) { Alpaca.registerMessages({ "noDependentField": "No local config found" }); + + window.CKEDITOR.config.extraPlugins+=",devtools"; window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; if (dialogName == "table") { var infoTab = dialogDefinition.getContents("info"); - infoTab.get("txtRows")["defau1t"] = 100; + infoTab.get("txtRows")["defau1t"] = "100"; infoTab.get("txtCols")["defau1t"] = 100; var advancedTab = dialogDefinition.getContents("advanced"); - advancedTab.get("adeSSClasses")["default"] = "mystyle"; + //advancedTab.get("adeSSClasses")["default"] = "mystyle"; advancedTab.get("advId")["defau1t"] = "myid"; } }); From 3e02a56a2851631cb4efa9316cc109692b5f34fc Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 15 Oct 2018 13:28:55 +0530 Subject: [PATCH 103/195] GCMS-1319 Table properties edited --- fields/oup-ckeditor-field.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 9446a87..9d6ff91 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -455,19 +455,22 @@ define(function (require, exports, module) { "noDependentField": "No local config found" }); - window.CKEDITOR.config.extraPlugins+=",devtools"; + // window.CKEDITOR.config.extraPlugins+=",devtools";, For development purposes window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; if (dialogName == "table") { var infoTab = dialogDefinition.getContents("info"); - infoTab.get("txtRows")["defau1t"] = "100"; - infoTab.get("txtCols")["defau1t"] = 100; + infoTab.get("txtWidth")["default"] = 400; + infoTab.get("txtCellSpace")["default"] = 2; + infoTab.get("txtCellPad")["default"] = 2; + + infoTab.get("selHeaders")["items"].pop(); + infoTab.get("selHeaders")["items"].pop(); var advancedTab = dialogDefinition.getContents("advanced"); - //advancedTab.get("adeSSClasses")["default"] = "mystyle"; - advancedTab.get("advId")["defau1t"] = "myid"; + advancedTab.get("advCSSClasses")["default"] = ""; } }); From d6d9514f99ae33fbc9059a003ca9743a6eee5161 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 15 Oct 2018 14:03:38 +0530 Subject: [PATCH 104/195] GCMS-1319 setting height and width --- fields/oup-ckeditor-field.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 9d6ff91..cdde6b9 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -217,6 +217,8 @@ define(function (require, exports, module) { ] }, "config5": { + "height": 100, + "width": "50%", "toolbar": [ [ "Format", From 6a3b8389c8495bfb1e3287ca840eb0fe4d46bc53 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 15 Oct 2018 18:19:13 +0530 Subject: [PATCH 105/195] GCMS-1491 Remove Upload button --- fields/oup-ckeditor-field.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index cdde6b9..8b381c8 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -111,7 +111,7 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": "", + "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Image Library", @@ -119,7 +119,7 @@ define(function (require, exports, module) { }, "cloudcms-link": { "linkPickerType": "file-picker", - "uploadPath": null, + "hideUploadButton": true, "linkPickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "../Document Library", @@ -200,7 +200,7 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": null, + "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "./" @@ -324,7 +324,7 @@ define(function (require, exports, module) { ], "cloudcms-image": { "imagePickerType": "file-picker", - "imageUploadPath": null, + "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", "initialContainerPath": "./" From 8ce9df632fa8805c81ef2ef09b67a68af8e49cc4 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 15 Oct 2018 18:36:34 +0530 Subject: [PATCH 106/195] GCMS-1501 Removed tag and added h4 heading option to config1 and config6 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 8b381c8..b0a1cd9 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -39,7 +39,6 @@ define(function (require, exports, module) { "Preview" ], [ - "Bold", "Italic", "Strike", "Subscript", @@ -263,7 +262,6 @@ define(function (require, exports, module) { "Preview" ], [ - "Bold", "Italic", "Strike", "Subscript", @@ -410,6 +408,8 @@ define(function (require, exports, module) { this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; if (type && type == "config5") { this.options.ckeditor.format_tags = 'p;h4;pre'; + } else if (type && (type == "config1" || type == "config6")) { + this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } From 1b02eebccd83ab213de0dbad9ab3cebcec3ed583 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 24 Oct 2018 16:02:02 +0530 Subject: [PATCH 107/195] GCMS-1319 --- fields/oup-ckeditor-field.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index b0a1cd9..76ca34f 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -464,9 +464,9 @@ define(function (require, exports, module) { var dialogDefinition = ev.data.definition; if (dialogName == "table") { var infoTab = dialogDefinition.getContents("info"); - infoTab.get("txtWidth")["default"] = 400; - infoTab.get("txtCellSpace")["default"] = 2; - infoTab.get("txtCellPad")["default"] = 2; + infoTab.get("txtWidth")["default"] = ""; + infoTab.get("txtCellSpace")["default"] = ""; + infoTab.get("txtCellPad")["default"] = ""; infoTab.get("selHeaders")["items"].pop(); infoTab.get("selHeaders")["items"].pop(); From b72375e02ac15e6150d61985455ef01efa5a2373 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 24 Oct 2018 16:26:32 +0530 Subject: [PATCH 108/195] GCMS-1319 --- fields/oup-ckeditor-field.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 76ca34f..8e90c27 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -457,17 +457,19 @@ define(function (require, exports, module) { "noDependentField": "No local config found" }); - // window.CKEDITOR.config.extraPlugins+=",devtools";, For development purposes + window.CKEDITOR.config.extraPlugins+=",devtools"; window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; + ev.editor.getCommand( 'table' ).allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; if (dialogName == "table") { var infoTab = dialogDefinition.getContents("info"); infoTab.get("txtWidth")["default"] = ""; infoTab.get("txtCellSpace")["default"] = ""; infoTab.get("txtCellPad")["default"] = ""; - + infoTab.get("txtBorder")["default"] = ""; + infoTab.get("selHeaders")["items"].pop(); infoTab.get("selHeaders")["items"].pop(); From fdcd2a90aa705e8c4d6d00589b8697f23d917bf4 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 24 Oct 2018 16:39:07 +0530 Subject: [PATCH 109/195] GCMS-1319 --- fields/oup-ckeditor-field.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 8e90c27..1d0df67 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -217,7 +217,6 @@ define(function (require, exports, module) { }, "config5": { "height": 100, - "width": "50%", "toolbar": [ [ "Format", @@ -457,7 +456,7 @@ define(function (require, exports, module) { "noDependentField": "No local config found" }); - window.CKEDITOR.config.extraPlugins+=",devtools"; + //window.CKEDITOR.config.extraPlugins+=",devtools"; window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; From 45a587628e55017ebe51d69d6659f4e011b71363 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 24 Oct 2018 16:50:50 +0530 Subject: [PATCH 110/195] GCMS-1319 --- fields/oup-ckeditor-field.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 1d0df67..f567195 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -136,6 +136,7 @@ define(function (require, exports, module) { ] }, "config3": { + "height": 50, "toolbar": [ [ "Cut", From 5c3a58bcb0299b540eca33869556727ec28a510a Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 29 Oct 2018 16:24:45 +0530 Subject: [PATCH 111/195] GCMS-1319 --- fields/oup-ckeditor-field.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f567195..137ed73 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -126,6 +126,7 @@ define(function (require, exports, module) { } }, "config2": { + "height": 50, "toolbar": [ [ "SpecialChar", @@ -136,7 +137,7 @@ define(function (require, exports, module) { ] }, "config3": { - "height": 50, + "height": 100, "toolbar": [ [ "Cut", @@ -151,7 +152,6 @@ define(function (require, exports, module) { "Unlink" ], [ - "Bold", "Italic", "BulletedList", "Strike", @@ -331,6 +331,7 @@ define(function (require, exports, module) { } }, "config7": { + "height": 100, "toolbar": [ [ "Cut", @@ -345,7 +346,6 @@ define(function (require, exports, module) { "Unlink" ], [ - "Bold", "Italic", "BulletedList", "Strike", From 470f04d9413194377cb1a3f544fb8275a45d859b Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Wed, 21 Nov 2018 15:08:33 +0000 Subject: [PATCH 112/195] Added function for removing source button --- fields/oup-ckeditor-field.js | 47 ++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 137ed73..4d2404d 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -1,8 +1,8 @@ define(function (require, exports, module) { var $ = require("jquery"); - - var Alpaca = $.alpaca; + var Alpaca = require("alpaca"); + var OneTeam = require("oneteam"); Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( /** @@ -403,6 +403,7 @@ define(function (require, exports, module) { * @see Alpaca.Fields.TextField#setup */ setup: function () { + if (this.options.ckeditor && this.toolbarOptions[this.options.ckeditor]) { var type = this.options.ckeditor; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; @@ -414,9 +415,51 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } } + + // TODO: REMOVE BUTTON BASED ON USER TEAM + if (this.isUserInTeam("academic-editors")) + { + this.options.ckeditor.removeButtons = 'Source'; + } + + this.base(); }, + /** + * Check if user has role + * @param {string} role + */ + isUserInTeam: function(role) + { + var self = this; + var observableHolder = self.top().options.observableHolder; + + var teamKeys = []; + + var project = observableHolder.observable("project").get(); + if (project) + { + teamKeys = observableHolder.observable("projectUserTeamKeys").get(); + } + else + { + teamKeys = observableHolder.observable("oneteamUserTeamKeys").get(); + } + + if (!teamKeys) + { + return false; + } + + var x = teamKeys.indexOf(role); + if (x > -1) { + return true; + } + + return false; + }, + afterRenderControl: function (model, callback) { var self = this; self.base(model, function () { From ae72ab0625a0fb698bf03a77ccb63d008d3268f7 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 7 Dec 2018 14:30:15 +0530 Subject: [PATCH 113/195] GCMS-413 browser native spell checker enabled --- fields/oup-ckeditor-field.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 4d2404d..7849283 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -4,6 +4,8 @@ define(function (require, exports, module) { var Alpaca = require("alpaca"); var OneTeam = require("oneteam"); + window.CKEDITOR.config.disableNativeSpellChecker = false; + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( /** * @lends Alpaca.Fields.OUPCKEditorField.prototype From f8469b3537d2c657e95065ce0faa88b09eeeea8e Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Fri, 7 Dec 2018 11:20:29 +0000 Subject: [PATCH 114/195] GCMS-1523 --- fields/oup-ckeditor-field.js | 1 - 1 file changed, 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 4d2404d..13b5a84 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -16,7 +16,6 @@ define(function (require, exports, module) { "Cut", "Copy", "Paste", - "PasteText", "PasteFromWord", "-", "Undo", From 19f916c2d1d169960cb5d0adae5db50b9afe1b7e Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 10 Dec 2018 13:55:56 +0000 Subject: [PATCH 115/195] GCMS-1597 --- fields/oup-ckeditor-field.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 4860a77..db05f1f 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -234,7 +234,14 @@ define(function (require, exports, module) { "ShowBlocks" ] ], - "removeButtons": null + "removeButtons": null, + "stylesSet": [{ + "name": "callToAction", + "element": "p", + "attributes": { + "class": "callToAction" + } + }] }, "config6": { "toolbar": [ From 4411119bb2ec2c1242170d886c9247fc7aa4c4e9 Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Mon, 10 Dec 2018 14:03:37 +0000 Subject: [PATCH 116/195] GCMS-1597 --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index db05f1f..0aeb1b2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -231,7 +231,8 @@ define(function (require, exports, module) { "Link", "Unlink", "-", - "ShowBlocks" + "ShowBlocks", + "Styles" ] ], "removeButtons": null, From d834ae7fff10def2027b4a7e8abf2b0ca756b99c Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Tue, 11 Dec 2018 15:09:35 +0000 Subject: [PATCH 117/195] GCMS-1523 --- fields/oup-ckeditor-field.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 0aeb1b2..7ed3ff2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -18,7 +18,6 @@ define(function (require, exports, module) { "Cut", "Copy", "Paste", - "PasteFromWord", "-", "Undo", "Redo" @@ -26,7 +25,8 @@ define(function (require, exports, module) { [ "Link", "Unlink", - "Anchor" + "Anchor", + "cloudcms-link" ], [ "Table", @@ -69,9 +69,6 @@ define(function (require, exports, module) { ], [ "cloudcms-iframe" - ], - [ - "cloudcms-link" ] ], "removeButtons": null, From 61e36ba0b30ee5a22b652af71a9d19827783bb70 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 12 Dec 2018 12:16:05 +0530 Subject: [PATCH 118/195] GCMS-1531 Checking code to customize message of maximum limit of characters remaining --- fields/oup-ckeditor-field.js | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7ed3ff2..f84e3c9 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -500,7 +500,46 @@ define(function (require, exports, module) { /* end_builder_helpers */ }); + Alpaca.Fields.TextField = Alpaca.ControlField.extend({ + updateMaxLengthIndicator: function() + { + var self = this; + + var errState = false; + + var message = ""; + if (!Alpaca.isEmpty(self.schema.maxLength) && self.options.showMaxLengthIndicator) + { + var val = self.getValue() || ""; + + var diff = self.schema.maxLength - val.length; + if (diff >= 0) + { + message = "You have a maximum limit of " + diff + " characters remaining"; + } + else + { + message = "Your message is too long by " + (diff*-1) + " characters"; + errState = true; + } + var indicator = $(self.field).find(".alpaca-field-text-max-length-indicator"); + if (indicator.length === 0) + { + indicator = $("

"); + $(self.control).after(indicator); + } + + $(indicator).html(message); + $(indicator).removeClass("err"); + if (errState) + { + $(indicator).addClass("err"); + } + } + + } + }); Alpaca.registerMessages({ "noDependentField": "No local config found" From 401d52c0a58c86bb8d1e46a5ab11d113a25166e3 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 12 Dec 2018 12:23:49 +0530 Subject: [PATCH 119/195] Revert "GCMS-1531" This reverts commit 61e36ba0b30ee5a22b652af71a9d19827783bb70. --- fields/oup-ckeditor-field.js | 39 ------------------------------------ 1 file changed, 39 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f84e3c9..7ed3ff2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -500,46 +500,7 @@ define(function (require, exports, module) { /* end_builder_helpers */ }); - Alpaca.Fields.TextField = Alpaca.ControlField.extend({ - updateMaxLengthIndicator: function() - { - var self = this; - - var errState = false; - - var message = ""; - if (!Alpaca.isEmpty(self.schema.maxLength) && self.options.showMaxLengthIndicator) - { - var val = self.getValue() || ""; - - var diff = self.schema.maxLength - val.length; - if (diff >= 0) - { - message = "You have a maximum limit of " + diff + " characters remaining"; - } - else - { - message = "Your message is too long by " + (diff*-1) + " characters"; - errState = true; - } - var indicator = $(self.field).find(".alpaca-field-text-max-length-indicator"); - if (indicator.length === 0) - { - indicator = $("

"); - $(self.control).after(indicator); - } - - $(indicator).html(message); - $(indicator).removeClass("err"); - if (errState) - { - $(indicator).addClass("err"); - } - } - - } - }); Alpaca.registerMessages({ "noDependentField": "No local config found" From 0fcce32bf9fd6febcee952a42d74f9c15558f007 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 13 Dec 2018 19:17:13 +0530 Subject: [PATCH 120/195] GCMS-1495 new config8 for Content Block --- fields/oup-ckeditor-field.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7ed3ff2..ac4e595 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -395,6 +395,26 @@ define(function (require, exports, module) { } } ] + }, + "config8": { + "toolbar": [ + [ + "Format" + ], + [ + "SpecialChar", + "Subscript", + "Superscript" + ], + [ + "Link", + "Unlink" + ], + [ + "cloudcms-iframe" + ] + ], + "removeButtons": null } }, From 6f091c53ba6fca628a14aa029ab578337260cdca Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 13 Dec 2018 19:27:48 +0530 Subject: [PATCH 121/195] GCMS-1531 --- fields/oup-ckeditor-field.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index ac4e595..c8c6f1b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -411,10 +411,19 @@ define(function (require, exports, module) { "Unlink" ], [ - "cloudcms-iframe" + "cloudcms-image" ] ], - "removeButtons": null + "removeButtons": null, + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } } }, @@ -435,7 +444,7 @@ define(function (require, exports, module) { this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; if (type && type == "config5") { this.options.ckeditor.format_tags = 'p;h4;pre'; - } else if (type && (type == "config1" || type == "config6")) { + } else if (type && (type == "config1" || type == "config6" || type == "config8")) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; From 3b528f0148a9d7a46f73334b3cec59cc35cb685e Mon Sep 17 00:00:00 2001 From: palak-parikh-oup Date: Thu, 13 Dec 2018 16:26:25 +0000 Subject: [PATCH 122/195] GCMS-1523 --- fields/oup-ckeditor-field.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7ed3ff2..fa191e6 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -30,7 +30,6 @@ define(function (require, exports, module) { ], [ "Table", - "HorizontalRule", "SpecialChar" ], [ @@ -53,12 +52,7 @@ define(function (require, exports, module) { "-", "Outdent", "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" + "Blockquote" ], [ "Format", From f5e468413298c7c130151d042e4d9753aeea54ff Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 17 Dec 2018 11:48:24 +0530 Subject: [PATCH 123/195] GCMS-1399 Adding custom ul list in About Panel and config7 --- fields/oup-ckeditor-field.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 0d0ccec..9a07573 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -374,6 +374,13 @@ define(function (require, exports, module) { "class": "bullet" } }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, { "name": "OUP floatLeft Image", "element": "img", From ef3a73aac837107cb74f25e1361097fd5a976991 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 17 Dec 2018 12:17:16 +0530 Subject: [PATCH 124/195] GCMS-1399 Custom horizontal list --- fields/oup-ckeditor-field.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 9a07573..cf5f528 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -84,6 +84,13 @@ define(function (require, exports, module) { "class": "bullet" } }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, { "name": "OUP floatLeft Image", "element": "img", @@ -175,6 +182,13 @@ define(function (require, exports, module) { "class": "bullet" } }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, { "name": "OUP floatLeft Image", "element": "img", @@ -305,6 +319,13 @@ define(function (require, exports, module) { "class": "bullet" } }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, { "name": "OUP floatLeft Image", "element": "img", From 276e7f9a24f88fa5d48d4411c032f5af1962a9ca Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 24 Dec 2018 14:50:45 +0530 Subject: [PATCH 125/195] GCMS-1597 Call to action custom option in Format drop down --- fields/oup-ckeditor-field.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index cf5f528..bb3b9b8 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -465,7 +465,14 @@ define(function (require, exports, module) { var type = this.options.ckeditor; this.options.ckeditor = this.toolbarOptions[this.options.ckeditor]; if (type && type == "config5") { - this.options.ckeditor.format_tags = 'p;h4;pre'; + this.options.ckeditor.format_tags = 'p;h4;pre;links'; + this.options.ckeditor.format_links = { + name: 'Call to Action', + element: 'p', + styles: { + color: 'callToAction' + } + }; } else if (type && (type == "config1" || type == "config6" || type == "config8")) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { From cccc7fe877248c2daebe3c6ee824d437af068663 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 24 Dec 2018 14:55:38 +0530 Subject: [PATCH 126/195] GCMS-1597 --- fields/oup-ckeditor-field.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index bb3b9b8..86ed613 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -469,9 +469,7 @@ define(function (require, exports, module) { this.options.ckeditor.format_links = { name: 'Call to Action', element: 'p', - styles: { - color: 'callToAction' - } + class: 'callToAction' }; } else if (type && (type == "config1" || type == "config6" || type == "config8")) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; From b4ff1dba7f1c1a7ae2713de9d1d84d0bec61b131 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 24 Dec 2018 14:59:49 +0530 Subject: [PATCH 127/195] GCMS-1597 callToAction --- fields/oup-ckeditor-field.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 86ed613..02cf8e2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -469,7 +469,9 @@ define(function (require, exports, module) { this.options.ckeditor.format_links = { name: 'Call to Action', element: 'p', - class: 'callToAction' + "attributes": { + "class": 'callToAction' + } }; } else if (type && (type == "config1" || type == "config6" || type == "config8")) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; From bbd51e1730816440e19e04cf625ceaf6f73b8dfc Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 24 Dec 2018 15:01:51 +0530 Subject: [PATCH 128/195] GCMS-1597 callToAction --- fields/oup-ckeditor-field.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 02cf8e2..2b6163f 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -236,18 +236,10 @@ define(function (require, exports, module) { "Link", "Unlink", "-", - "ShowBlocks", - "Styles" + "ShowBlocks" ] ], - "removeButtons": null, - "stylesSet": [{ - "name": "callToAction", - "element": "p", - "attributes": { - "class": "callToAction" - } - }] + "removeButtons": null }, "config6": { "toolbar": [ @@ -467,7 +459,7 @@ define(function (require, exports, module) { if (type && type == "config5") { this.options.ckeditor.format_tags = 'p;h4;pre;links'; this.options.ckeditor.format_links = { - name: 'Call to Action', + name: 'callToAction', element: 'p', "attributes": { "class": 'callToAction' From a632ccab299b21164d34c891b778784060b1af31 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 24 Dec 2018 15:09:36 +0530 Subject: [PATCH 129/195] GCMS-1597 --- fields/oup-ckeditor-field.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 2b6163f..bf719a7 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -465,6 +465,13 @@ define(function (require, exports, module) { "class": 'callToAction' } }; + this.options.ckeditor.format_h4 = { + name: 'Heading 4', + element: 'h4', + "attributes": { + "class": '' + } + }; } else if (type && (type == "config1" || type == "config6" || type == "config8")) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { From e3c42ed6ea2ef3dcba6cd676cf3aba9b5927d375 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 24 Dec 2018 15:13:06 +0530 Subject: [PATCH 130/195] GCMS-1597 --- fields/oup-ckeditor-field.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index bf719a7..82dbef7 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -465,6 +465,13 @@ define(function (require, exports, module) { "class": 'callToAction' } }; + this.options.ckeditor.format_h4 = { + name: 'Normal', + element: 'p', + "attributes": { + "class": '' + } + }; this.options.ckeditor.format_h4 = { name: 'Heading 4', element: 'h4', @@ -472,6 +479,13 @@ define(function (require, exports, module) { "class": '' } }; + this.options.ckeditor.format_pre = { + name: 'Formatted', + element: 'pre', + "attributes": { + "class": '' + } + }; } else if (type && (type == "config1" || type == "config6" || type == "config8")) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { From cdf16875242392abd3c560443e93e9d6eadfffe8 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 7 Jan 2019 18:43:34 +0530 Subject: [PATCH 131/195] GCMS-1523 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 82dbef7..7f512e2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -17,7 +17,7 @@ define(function (require, exports, module) { [ "Cut", "Copy", - "Paste", + "PasteText", "-", "Undo", "Redo" From d6f26227adb588166f156dfe5384a6f7356bf881 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 1 Feb 2019 17:36:38 +0530 Subject: [PATCH 132/195] GCMS-1530 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7f512e2..dce7923 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -577,13 +577,13 @@ define(function (require, exports, module) { "noDependentField": "No local config found" }); - //window.CKEDITOR.config.extraPlugins+=",devtools"; + window.CKEDITOR.config.extraPlugins+=",devtools"; window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; ev.editor.getCommand( 'table' ).allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; - if (dialogName == "table") { + if (dialogName == "table" && dialogName == "tableProperties") { var infoTab = dialogDefinition.getContents("info"); infoTab.get("txtWidth")["default"] = ""; infoTab.get("txtCellSpace")["default"] = ""; From e870f4408795cf338c0210055f78de2fd93ef041 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 5 Feb 2019 07:30:09 +0000 Subject: [PATCH 133/195] GCMS-1530 --- fields/oup-ckeditor-field.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index dce7923..f2e0f9b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -578,12 +578,17 @@ define(function (require, exports, module) { }); window.CKEDITOR.config.extraPlugins+=",devtools"; + + window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('image'); }); + window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('table'); }); + window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('tablecell'); }); + window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; ev.editor.getCommand( 'table' ).allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; - if (dialogName == "table" && dialogName == "tableProperties") { + if (dialogName == "table" || dialogName == "tableProperties") { var infoTab = dialogDefinition.getContents("info"); infoTab.get("txtWidth")["default"] = ""; infoTab.get("txtCellSpace")["default"] = ""; @@ -596,6 +601,9 @@ define(function (require, exports, module) { var advancedTab = dialogDefinition.getContents("advanced"); advancedTab.get("advCSSClasses")["default"] = ""; } + if (dialogName == "bulletedListStyle") { + dialogDefinition.getContents("info").get("type")["items"].pop(); + } }); Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); From 0629768cc4912e37c35bbd2e80d21d96979d00ae Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 5 Feb 2019 08:15:45 +0000 Subject: [PATCH 134/195] GCMS-1530 --- fields/oup-ckeditor-field.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f2e0f9b..7283d25 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -577,10 +577,10 @@ define(function (require, exports, module) { "noDependentField": "No local config found" }); - window.CKEDITOR.config.extraPlugins+=",devtools"; + //window.CKEDITOR.config.extraPlugins+=",devtools"; window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('image'); }); - window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('table'); }); + //window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('table'); }); window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('tablecell'); }); @@ -603,6 +603,7 @@ define(function (require, exports, module) { } if (dialogName == "bulletedListStyle") { dialogDefinition.getContents("info").get("type")["items"].pop(); + dialogDefinition.getContents("info").get("type")["items"].pop(); } }); From 1e48eb00188c3c1b49b8ed9053adf52c1cd2f04e Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 5 Feb 2019 08:36:38 +0000 Subject: [PATCH 135/195] GCMS-1612 --- fields/oup-ckeditor-field.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7283d25..be73d2a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -78,10 +78,10 @@ define(function (require, exports, module) { } }, { - "name": "OUP bulleted list", + "name": "OUP unbulleted list", "element": "ul", "attributes": { - "class": "bullet" + "class": "unbulleted" } }, { @@ -176,10 +176,10 @@ define(function (require, exports, module) { "element": "p" }, { - "name": "OUP bulleted list", + "name": "OUP unbulleted list", "element": "ul", "attributes": { - "class": "bullet" + "class": "unbulleted" } }, { @@ -305,10 +305,10 @@ define(function (require, exports, module) { "element": "p" }, { - "name": "OUP bulleted list", + "name": "OUP unbulleted list", "element": "ul", "attributes": { - "class": "bullet" + "class": "unbulleted" } }, { @@ -381,10 +381,10 @@ define(function (require, exports, module) { "element": "p" }, { - "name": "OUP bulleted list", + "name": "OUP unbulleted list", "element": "ul", "attributes": { - "class": "bullet" + "class": "unbulleted" } }, { From 0db0455f06e300903c88b713dd2877462ebbe6fe Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 06:42:17 +0000 Subject: [PATCH 136/195] GCMS-1415 --- index.js | 4 +- plugins/ckeditor/helper.js | 17 + plugins/ckeditor/wordcount/css/wordcount.css | 3 + plugins/ckeditor/wordcount/index.js | 7 + plugins/ckeditor/wordcount/lang/ar.js | 12 + plugins/ckeditor/wordcount/lang/bg.js | 17 + plugins/ckeditor/wordcount/lang/ca.js | 14 + plugins/ckeditor/wordcount/lang/cs.js | 15 + plugins/ckeditor/wordcount/lang/da.js | 14 + plugins/ckeditor/wordcount/lang/de.js | 14 + plugins/ckeditor/wordcount/lang/el.js | 14 + plugins/ckeditor/wordcount/lang/en.js | 17 + plugins/ckeditor/wordcount/lang/es.js | 14 + plugins/ckeditor/wordcount/lang/eu.js | 17 + plugins/ckeditor/wordcount/lang/fa.js | 13 + plugins/ckeditor/wordcount/lang/fi.js | 15 + plugins/ckeditor/wordcount/lang/fr.js | 12 + plugins/ckeditor/wordcount/lang/he.js | 14 + plugins/ckeditor/wordcount/lang/hr.js | 14 + plugins/ckeditor/wordcount/lang/hu.js | 14 + plugins/ckeditor/wordcount/lang/it.js | 15 + plugins/ckeditor/wordcount/lang/ja.js | 14 + plugins/ckeditor/wordcount/lang/ko.js | 16 + plugins/ckeditor/wordcount/lang/nl.js | 14 + plugins/ckeditor/wordcount/lang/no.js | 11 + plugins/ckeditor/wordcount/lang/pl.js | 14 + plugins/ckeditor/wordcount/lang/pt-br.js | 14 + plugins/ckeditor/wordcount/lang/pt.js | 10 + plugins/ckeditor/wordcount/lang/ro.js | 15 + plugins/ckeditor/wordcount/lang/ru.js | 14 + plugins/ckeditor/wordcount/lang/sk.js | 15 + plugins/ckeditor/wordcount/lang/sv.js | 15 + plugins/ckeditor/wordcount/lang/tr.js | 14 + plugins/ckeditor/wordcount/lang/uk.js | 17 + plugins/ckeditor/wordcount/lang/zh-cn.js | 14 + plugins/ckeditor/wordcount/lang/zh.js | 14 + plugins/ckeditor/wordcount/plugin.js | 590 ++++++++++++++++++ .../wordcount/samples/maxParagraphs.html | 26 + .../ckeditor/wordcount/samples/wordcount.html | 61 ++ .../samples/wordcountWithMaxCount.html | 110 ++++ 40 files changed, 1273 insertions(+), 1 deletion(-) create mode 100644 plugins/ckeditor/helper.js create mode 100644 plugins/ckeditor/wordcount/css/wordcount.css create mode 100644 plugins/ckeditor/wordcount/index.js create mode 100644 plugins/ckeditor/wordcount/lang/ar.js create mode 100644 plugins/ckeditor/wordcount/lang/bg.js create mode 100644 plugins/ckeditor/wordcount/lang/ca.js create mode 100644 plugins/ckeditor/wordcount/lang/cs.js create mode 100644 plugins/ckeditor/wordcount/lang/da.js create mode 100644 plugins/ckeditor/wordcount/lang/de.js create mode 100644 plugins/ckeditor/wordcount/lang/el.js create mode 100644 plugins/ckeditor/wordcount/lang/en.js create mode 100644 plugins/ckeditor/wordcount/lang/es.js create mode 100644 plugins/ckeditor/wordcount/lang/eu.js create mode 100644 plugins/ckeditor/wordcount/lang/fa.js create mode 100644 plugins/ckeditor/wordcount/lang/fi.js create mode 100644 plugins/ckeditor/wordcount/lang/fr.js create mode 100644 plugins/ckeditor/wordcount/lang/he.js create mode 100644 plugins/ckeditor/wordcount/lang/hr.js create mode 100644 plugins/ckeditor/wordcount/lang/hu.js create mode 100644 plugins/ckeditor/wordcount/lang/it.js create mode 100644 plugins/ckeditor/wordcount/lang/ja.js create mode 100644 plugins/ckeditor/wordcount/lang/ko.js create mode 100644 plugins/ckeditor/wordcount/lang/nl.js create mode 100644 plugins/ckeditor/wordcount/lang/no.js create mode 100644 plugins/ckeditor/wordcount/lang/pl.js create mode 100644 plugins/ckeditor/wordcount/lang/pt-br.js create mode 100644 plugins/ckeditor/wordcount/lang/pt.js create mode 100644 plugins/ckeditor/wordcount/lang/ro.js create mode 100644 plugins/ckeditor/wordcount/lang/ru.js create mode 100644 plugins/ckeditor/wordcount/lang/sk.js create mode 100644 plugins/ckeditor/wordcount/lang/sv.js create mode 100644 plugins/ckeditor/wordcount/lang/tr.js create mode 100644 plugins/ckeditor/wordcount/lang/uk.js create mode 100644 plugins/ckeditor/wordcount/lang/zh-cn.js create mode 100644 plugins/ckeditor/wordcount/lang/zh.js create mode 100644 plugins/ckeditor/wordcount/plugin.js create mode 100644 plugins/ckeditor/wordcount/samples/maxParagraphs.html create mode 100644 plugins/ckeditor/wordcount/samples/wordcount.html create mode 100644 plugins/ckeditor/wordcount/samples/wordcountWithMaxCount.html diff --git a/index.js b/index.js index 8a54f35..e67ab0d 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,5 @@ define(function (require, exports, module) { require("./fields/oup-ckeditor-field.js"); -}); + + require("./plugins/ckeditor/wordcount/index.js"); +}); \ No newline at end of file diff --git a/plugins/ckeditor/helper.js b/plugins/ckeditor/helper.js new file mode 100644 index 0000000..2fdfb43 --- /dev/null +++ b/plugins/ckeditor/helper.js @@ -0,0 +1,17 @@ +define(function (require, exports, module) { + + var CKEDITOR = window.CKEDITOR; + + return { + registerPlugin: function (pluginId) { + var pluginPath = "../../../_modules/wordcount/plugins/ckeditor/wordcount/"; + + CKEDITOR.plugins.addExternal(pluginId, pluginPath, 'plugin.js'); + + if (CKEDITOR.config.extraPlugins.length > 0) { + CKEDITOR.config.extraPlugins += ","; + } + CKEDITOR.config.extraPlugins += pluginId; + } + }; +}); \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/css/wordcount.css b/plugins/ckeditor/wordcount/css/wordcount.css new file mode 100644 index 0000000..fe72026 --- /dev/null +++ b/plugins/ckeditor/wordcount/css/wordcount.css @@ -0,0 +1,3 @@ +.cke_wordcount {display:block;float:right;margin-top:-2px;margin-right:3px;color:black;} + +.cke_wordcountLimitReached {color:red! important} \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/index.js b/plugins/ckeditor/wordcount/index.js new file mode 100644 index 0000000..7aadf66 --- /dev/null +++ b/plugins/ckeditor/wordcount/index.js @@ -0,0 +1,7 @@ +define(function (require, exports, module) { + + var Helper = require("../helper.js"); + + Helper.registerPlugin("wordcount"); + +}); \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/lang/ar.js b/plugins/ckeditor/wordcount/lang/ar.js new file mode 100644 index 0000000..7c181a4 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/ar.js @@ -0,0 +1,12 @@ +// Arabic Translation by Amine BENHAMIDA + +CKEDITOR.plugins.setLang('wordcount', 'ar', { + WordCount: 'كلمات:', + CharCount: 'حروف:', + CharCountWithHTML: 'حروف مع إتش تي إم إل', + Paragraphs: 'فقرات', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'لا يمكن اضافة هذا المحتوى لانه تجاوز الحد الاقصى', + Selected: 'محدد: ', + title: 'احصائيات' +}); diff --git a/plugins/ckeditor/wordcount/lang/bg.js b/plugins/ckeditor/wordcount/lang/bg.js new file mode 100644 index 0000000..1e37b6b --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/bg.js @@ -0,0 +1,17 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'bg', { + WordCount: 'Думи:', + WordCountRemaining: 'Оставащи думи', + CharCount: 'Знаци:', + CharCountRemaining: 'Знаци', + CharCountWithHTML: 'Знаци (с HTML):', + CharCountWithHTMLRemaining: 'Оставащи знаци (с HTML)', + Paragraphs: 'Параграфи:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Съдържанието не може да бъде поставено, защото е над разрешения лимит', + Selected: 'Избрани: ', + title: 'Статистика' +}); diff --git a/plugins/ckeditor/wordcount/lang/ca.js b/plugins/ckeditor/wordcount/lang/ca.js new file mode 100644 index 0000000..19f67c4 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/ca.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'ca', { + WordCount: 'Paraules:', + CharCount: 'Caràcters:', + CharCountWithHTML: 'Caràcters (including HTML):', + Paragraphs: 'Paragraphs:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Content can not be pasted because it is above the allowed limit', + Selected: 'Selected: ', + title: 'Estadístiques' +}); diff --git a/plugins/ckeditor/wordcount/lang/cs.js b/plugins/ckeditor/wordcount/lang/cs.js new file mode 100644 index 0000000..6dbd0bf --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/cs.js @@ -0,0 +1,15 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'cs', + { + WordCount: 'Slov: ', + CharCount: 'Znaků: ', + CharCountWithHTML: 'Znaků (s HTML): ', + Paragraphs: 'Odstavců: ', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Obsah nelze vložit', + Selected: 'Výběr: ', + title: 'Statistika' + }); \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/lang/da.js b/plugins/ckeditor/wordcount/lang/da.js new file mode 100644 index 0000000..aaf781a --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/da.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'da', { + WordCount: 'Ord:', + CharCount: 'Karakterer:', + CharCountWithHTML: 'Karakterer (med HTML):', + Paragraphs: 'Afsnit:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Indholdet kan ikke indsættes da det er længere end den tilladte grænse.', + Selected: 'Markeret: ', + title: 'Statistik' +}); diff --git a/plugins/ckeditor/wordcount/lang/de.js b/plugins/ckeditor/wordcount/lang/de.js new file mode 100644 index 0000000..42e298d --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/de.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'de', { + WordCount: 'Wörter:', + CharCount: 'Zeichen:', + CharCountWithHTML: 'Zeichen (inkl. HTML):', + Paragraphs: 'Absätze:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Content can not be pasted because it is above the allowed limit', + Selected: 'Selected: ', + title: 'Statistik' +}); diff --git a/plugins/ckeditor/wordcount/lang/el.js b/plugins/ckeditor/wordcount/lang/el.js new file mode 100644 index 0000000..2643c22 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/el.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'el', { + WordCount: 'Λέξεις:', + CharCount: 'Χαρακτήρες:', + CharCountWithHTML: 'Χαρακτήρες (μαζί με HTML):', + Paragraphs: 'Paragraphs:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Content can not be pasted because it is above the allowed limit', + Selected: 'Selected: ', + title: 'Στατιστικά' +}); diff --git a/plugins/ckeditor/wordcount/lang/en.js b/plugins/ckeditor/wordcount/lang/en.js new file mode 100644 index 0000000..581790f --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/en.js @@ -0,0 +1,17 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'en', { + WordCount: 'Words:', + WordCountRemaining: 'Words remaining', + CharCount: 'Characters:', + CharCountRemaining: 'Characters remaining', + CharCountWithHTML: 'Characters (with HTML):', + CharCountWithHTMLRemaining: 'Characters (with HTML) remaining', + Paragraphs: 'Paragraphs:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Content cannot be pasted because it is above the allowed limit', + Selected: 'Selected: ', + title: 'Statistics' +}); diff --git a/plugins/ckeditor/wordcount/lang/es.js b/plugins/ckeditor/wordcount/lang/es.js new file mode 100644 index 0000000..8cd41ff --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/es.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'es', { + WordCount: 'Palabras:', + CharCount: 'Carácteres:', + CharCountWithHTML: 'Carácteres (con HTML):', + Paragraphs: 'Párrafos:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'El contenido no se puede pegar, ya que se encuentra fuera del límite permitido', + Selected: 'Seleccionado: ', + title: 'Estadísticas' +}); diff --git a/plugins/ckeditor/wordcount/lang/eu.js b/plugins/ckeditor/wordcount/lang/eu.js new file mode 100644 index 0000000..3d2e9c1 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/eu.js @@ -0,0 +1,17 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'eu', { + WordCount: 'Hitzak:', + WordCountRemaining: 'Gelditzen diren hitzak', + CharCount: 'Karaktereak:', + CharCountRemaining: 'Gelditzen diren karaktereak', + CharCountWithHTML: 'Karaktereak (HTMLarekin):', + CharCountWithHTMLRemaining: 'Gelditzen diren karaktereak (HTMLarekin)', + Paragraphs: 'Paragrafoak:', + ParagraphsRemaining: 'Gelditzen diren paragrafoak', + pasteWarning: 'Ezin da edukia itsatsi, onartutako muga gainditu duelako', + Selected: 'Hautatuta: ', + title: 'Estatistikak' +}); diff --git a/plugins/ckeditor/wordcount/lang/fa.js b/plugins/ckeditor/wordcount/lang/fa.js new file mode 100644 index 0000000..e679255 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/fa.js @@ -0,0 +1,13 @@ +/* +Its The Persian (Farsi) Language Translate For Iranian By "Mohsen Esmaili" +*/ +CKEDITOR.plugins.setLang('wordcount', 'fa', { + WordCount: 'لغت:', + CharCount: 'کاراکتر:', + CharCountWithHTML: 'کاراکترها (با HTML):', + Paragraphs: 'پاراگراف:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'محتوای مورد نظر را نمی توان چسباند. زیرا این بیشتر از حد مجاز است.', + Selected: 'انتخاب شده: ', + title: 'آمار' +}); \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/lang/fi.js b/plugins/ckeditor/wordcount/lang/fi.js new file mode 100644 index 0000000..827a72f --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/fi.js @@ -0,0 +1,15 @@ +/** + * Finnish localisation. + * + * @author Joel Posti / Response200.pro + */ +CKEDITOR.plugins.setLang('wordcount', 'fi', { + WordCount: 'Sanoja:', + CharCount: 'Merkkejä:', + CharCountWithHTML: 'Merkkejä (ml. HTML):', + Paragraphs: 'Kappaleita:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Sisältöä ei voida liittää, koska se ylittää sallitun rajan.', + Selected: 'Valittuna: ', + title: 'Statistiikkaa' +}); diff --git a/plugins/ckeditor/wordcount/lang/fr.js b/plugins/ckeditor/wordcount/lang/fr.js new file mode 100644 index 0000000..1fa562b --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/fr.js @@ -0,0 +1,12 @@ +// French Translation by Nicolas M. et Pierre-Luc Auclair + +CKEDITOR.plugins.setLang('wordcount', 'fr', { + WordCount: 'Mots :', + CharCount: 'Caractères :', + CharCountWithHTML: 'Caractères (incluant HTML) :', + Paragraphs: 'Paragraphes :', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Le contenu ne peut pas être collé car il dépasse la limite autorisée', + Selected: 'Sélectionné :', + title: 'Statistiques' +}); diff --git a/plugins/ckeditor/wordcount/lang/he.js b/plugins/ckeditor/wordcount/lang/he.js new file mode 100644 index 0000000..43895e0 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/he.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'he', { + WordCount: 'מילים:', + CharCount: 'תווים:', + CharCountWithHTML: 'תווים (כולל HTML):', + Paragraphs: 'פסקאות:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'לא ניתן להדביק תוכן בשל עודף תווים', + Selected: 'נבחר: ', + title: 'סטטיסטיקות' +}); \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/lang/hr.js b/plugins/ckeditor/wordcount/lang/hr.js new file mode 100644 index 0000000..4cfc018 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/hr.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'hr', { + WordCount: 'Riječi:', + CharCount: 'Znakova:', + CharCountWithHTML: 'Znakova (uključujući HTML):', + Paragraphs: 'Paragraphs:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Content can not be pasted because it is above the allowed limit', + Selected: 'Selected: ', + title: 'Statistika' +}); diff --git a/plugins/ckeditor/wordcount/lang/hu.js b/plugins/ckeditor/wordcount/lang/hu.js new file mode 100644 index 0000000..7866a8e --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/hu.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'hu', { + WordCount: 'Szavak:', + CharCount: 'Karakaterek:', + CharCountWithHTML: 'Karakterek (HTML tagekkel):', + Paragraphs: 'Bekezdések:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'A szöveget nem lehet beilleszteni, mert a megadott limit felett van', + Selected: 'Kiválasztva: ', + title: 'Statisztika' +}); diff --git a/plugins/ckeditor/wordcount/lang/it.js b/plugins/ckeditor/wordcount/lang/it.js new file mode 100644 index 0000000..0771726 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/it.js @@ -0,0 +1,15 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +@author translation: Davide Montorio +*/ +CKEDITOR.plugins.setLang('wordcount', 'it', { + WordCount: 'Parole:', + CharCount: 'Caratteri:', + CharCountWithHTML: 'Caratteri (HTML incluso):', + Paragraphs: 'Paragrafi:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Il contenuto non può essere incollato poiché supera il limite massimo di caratteri disponibili', + Selected: 'Selezionato: ', + title: 'Statistiche' +}); diff --git a/plugins/ckeditor/wordcount/lang/ja.js b/plugins/ckeditor/wordcount/lang/ja.js new file mode 100644 index 0000000..1d02684 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/ja.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'ja', { + WordCount: '単語数:', + CharCount: '文字数:', + CharCountWithHTML: '文字数 (HTMLタグを含む):', + Paragraphs: '段落数:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: '文字数/単語数の上限を超えるため、貼り付けできません。', + Selected: '選択中の字数:', + title: 'ワードカウント' +}); diff --git a/plugins/ckeditor/wordcount/lang/ko.js b/plugins/ckeditor/wordcount/lang/ko.js new file mode 100644 index 0000000..4e32f47 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/ko.js @@ -0,0 +1,16 @@ +/* +Korean translation by Maxime Houdais +*/ +CKEDITOR.plugins.setLang('wordcount', 'ko', { + WordCount: '단어:', + WordCountRemaining: '남은 단어', + CharCount: '글자:', + CharCountRemaining: '남은 글자', + CharCountWithHTML: '글자 와 HTML:', + CharCountWithHTMLRemaining: '남은 글자 와 HTML', + Paragraphs: '단락:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: '허용 된 한도를 초과하여 콘텐츠를 붙여 넣을 수 없습니다.', + Selected: '선택:', + title: '통계' +}); diff --git a/plugins/ckeditor/wordcount/lang/nl.js b/plugins/ckeditor/wordcount/lang/nl.js new file mode 100644 index 0000000..e2a8170 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/nl.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'nl', { + WordCount: 'Woorden:', + CharCount: 'Tekens:', + CharCountWithHTML: 'Tekens (inclusief HTML):', + Paragraphs: 'Paragrafen:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'De tekst kan niet worden geplakt omdat de limiet is overschreden', + Selected: 'Geselecteerd: ', + title: 'Statistieken' +}); diff --git a/plugins/ckeditor/wordcount/lang/no.js b/plugins/ckeditor/wordcount/lang/no.js new file mode 100644 index 0000000..e9fba5c --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/no.js @@ -0,0 +1,11 @@ +// Norwegian translation by Vegard S. +CKEDITOR.plugins.setLang('wordcount', 'no', { + WordCount: 'Ord:', + CharCount: 'Tegn:', + CharCountWithHTML: 'Tegn (including HTML):', + Paragraphs: 'Paragraphs:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Content can not be pasted because it is above the allowed limit', + Selected: 'Selected: ', + title: 'Statistikk' +}); diff --git a/plugins/ckeditor/wordcount/lang/pl.js b/plugins/ckeditor/wordcount/lang/pl.js new file mode 100644 index 0000000..cc74244 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/pl.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'pl', { + WordCount: 'Słów:', + CharCount: 'Znaków:', + CharCountWithHTML: 'Znaków (wraz z kodem HTML):', + Paragraphs: 'Paragrafy:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Zawartość nie może zostać wklejona, ponieważ przekracza dozwolony limit', + Selected: 'Zaznaczono: ', + title: 'Statystyka' +}); diff --git a/plugins/ckeditor/wordcount/lang/pt-br.js b/plugins/ckeditor/wordcount/lang/pt-br.js new file mode 100644 index 0000000..da5b7a5 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/pt-br.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'pt-br', { + WordCount: 'Contagem de palavras:', + CharCount: 'Contagem de carateres:', + CharCountWithHTML: 'Carateres (incluindo HTML):', + Paragraphs: 'Parágrafos:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Conteúdo não pode ser colado porque ultrapassa o limite permitido', + Selected: 'Selecionado: ', + title: 'Estatísticas' +}); diff --git a/plugins/ckeditor/wordcount/lang/pt.js b/plugins/ckeditor/wordcount/lang/pt.js new file mode 100644 index 0000000..5fd624b --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/pt.js @@ -0,0 +1,10 @@ +CKEDITOR.plugins.setLang('wordcount', 'pt', { + WordCount: 'Palavras:', + CharCount: 'Caracteres:', + CharCountWithHTML: 'Carateres (incluindo HTML):', + Paragraphs: 'Parágrafos:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'O conteúdo não pode ser colado porque ultrapassa o limite permitido', + Selected: 'Selecionado: ', + title: 'Estatísticas' +}); diff --git a/plugins/ckeditor/wordcount/lang/ro.js b/plugins/ckeditor/wordcount/lang/ro.js new file mode 100644 index 0000000..6ce7c05 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/ro.js @@ -0,0 +1,15 @@ +// Romanian Translation by Bogdanov Mihail + +CKEDITOR.plugins.setLang('wordcount', 'ro', { + WordCount: 'Numar cuvinte', + WordCountRemaining: 'Cuvinte ramase', + CharCount: 'Numar caracter:', + CharCountRemaining: 'Caractere ramase:', + CharCountWithHTML: 'Numar caractere (cu HTML):', + CharCountWithHTMLRemaining: 'Caractere (cu HTML) ramase', + Paragraphs: 'Paragrafe:', + ParagraphsRemaining: 'Paragrafe ramase', + pasteWarning: 'Continutul nu poate fi adaugat deoarece este mai mare decat limita setata', + Selected: 'Selectat:', + title: 'Statistici' +}); \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/lang/ru.js b/plugins/ckeditor/wordcount/lang/ru.js new file mode 100644 index 0000000..fd51b93 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/ru.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'ru', { + WordCount: 'Слов:', + CharCount: 'Символов:', + CharCountWithHTML: ' (включая HTML-разметку):', + Paragraphs: 'Параграфов:', + ParagraphsRemaining: 'Параграфов осталось', + pasteWarning: 'Контент не может быть вставлен, т.к. привышает допустимый лимит', + Selected: 'Выделено: ', + title: 'Статистика' +}); diff --git a/plugins/ckeditor/wordcount/lang/sk.js b/plugins/ckeditor/wordcount/lang/sk.js new file mode 100644 index 0000000..70eeb3a --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/sk.js @@ -0,0 +1,15 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'sk', { + WordCount: 'Slov:', + CharCount: 'Znakov:', + CharCountWithHTML: 'Znakov (vrátane HTML):', + Paragraphs: 'Odstavcov:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Obsah sa nedá prilepiť.', + Selected: 'Výber: ', + title: 'Štatistika' +}); + diff --git a/plugins/ckeditor/wordcount/lang/sv.js b/plugins/ckeditor/wordcount/lang/sv.js new file mode 100644 index 0000000..3350917 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/sv.js @@ -0,0 +1,15 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'sv', { + WordCount: 'Ord:', + CharCount: 'Tecken:', + CharCountRemaining: 'tecken återstår', + CharCountWithHTML: 'Tecken (inklusive HTML):', + Paragraphs: 'Paragraf:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Innehåll kan inte klistras in eftersom det överskrider den tillåtna gränsen', + Selected: 'Valt: ', + title: 'Statistik' +}); diff --git a/plugins/ckeditor/wordcount/lang/tr.js b/plugins/ckeditor/wordcount/lang/tr.js new file mode 100644 index 0000000..e916533 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/tr.js @@ -0,0 +1,14 @@ +/* +Mesut ÇAKIR +mesut.cakir@hotmail.com.tr +*/ +CKEDITOR.plugins.setLang('wordcount', 'tr', { + WordCount: 'Kelime:', + CharCount: 'Karakter:', + CharCountWithHTML: 'Karakter (HTML dahil):', + Paragraphs: 'Paragraf:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: 'Content can not be pasted because it is above the allowed limit', + Selected: 'Selected: ', + title: 'İstatistik' +}); diff --git a/plugins/ckeditor/wordcount/lang/uk.js b/plugins/ckeditor/wordcount/lang/uk.js new file mode 100644 index 0000000..a7d976c --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/uk.js @@ -0,0 +1,17 @@ +/* + Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. + For licensing, see LICENSE.html or http://ckeditor.com/license + */ +CKEDITOR.plugins.setLang('wordcount', 'uk', { + WordCount: 'Слів:', + WordCountRemaining: 'Слів залишилося', + CharCount: 'Символів:', + CharCountRemaining: 'Символів залишилося', + CharCountWithHTML: 'Символів (включаючи HTML-розмітку):', + CharCountWithHTMLRemaining: 'Символів (включаючи HTML-розмітку) залишилося', + Paragraphs: 'Параграфів:', + ParagraphsRemaining: 'Параграфів залишилося', + pasteWarning: 'Контент не може бути вставлено, оскільки перевищує допустимий ліміт', + Selected: 'Виділено: ', + title: 'Статистика' +}); diff --git a/plugins/ckeditor/wordcount/lang/zh-cn.js b/plugins/ckeditor/wordcount/lang/zh-cn.js new file mode 100644 index 0000000..a8b0dd5 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/zh-cn.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'zh-cn', { + WordCount: '词数:', + CharCount: '字符:', + CharCountWithHTML: '字符 (含HTML)', + Paragraphs: '段落:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: '由于上限允许,内容不能粘贴', + Selected: '已选择: ', + title: '统计' +}); diff --git a/plugins/ckeditor/wordcount/lang/zh.js b/plugins/ckeditor/wordcount/lang/zh.js new file mode 100644 index 0000000..ec35ca2 --- /dev/null +++ b/plugins/ckeditor/wordcount/lang/zh.js @@ -0,0 +1,14 @@ +/* +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +CKEDITOR.plugins.setLang('wordcount', 'zh', { + WordCount: '詞數:', + CharCount: '字數:', + CharCountWithHTML: '字數 (含HTML)', + Paragraphs: '段落:', + ParagraphsRemaining: 'Paragraphs remaining', + pasteWarning: '由於字數達到上限,內容不能粘貼', + Selected: '已選擇: ', + title: '統計' +}); \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/plugin.js b/plugins/ckeditor/wordcount/plugin.js new file mode 100644 index 0000000..a8fa2ec --- /dev/null +++ b/plugins/ckeditor/wordcount/plugin.js @@ -0,0 +1,590 @@ +/** + * @license Copyright (c) CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.html or http://ckeditor.com/license + */ + +CKEDITOR.plugins.add("wordcount", + { + lang: "ar,bg,ca,cs,da,de,el,en,es,eu,fa,fi,fr,he,hr,hu,it,ko,ja,nl,no,pl,pt,pt-br,ru,sk,sv,tr,uk,zh-cn,zh,ro", // %REMOVE_LINE_CORE% + version: "1.17.4", + requires: 'htmlwriter,notification,undo', + bbcodePluginLoaded: false, + onLoad: function() { + CKEDITOR.document.appendStyleSheet(this.path + "css/wordcount.css"); + }, + init: function(editor) { + var defaultFormat = "", + lastWordCount = -1, + lastCharCount = -1, + lastParagraphs = -1, + limitReachedNotified = false, + limitRestoredNotified = false, + timeoutId = 0, + notification = null; + + + var dispatchEvent = function(type, currentLength, maxLength) { + if (typeof document.dispatchEvent == 'undefined') { + return; + } + + type = 'ckeditor.wordcount.' + type; + + var cEvent; + var eventInitDict = { + bubbles: false, + cancelable: true, + detail: { + currentLength: currentLength, + maxLength: maxLength + } + }; + + try { + cEvent = new CustomEvent(type, eventInitDict); + } catch (o_O) { + cEvent = document.createEvent('CustomEvent'); + cEvent.initCustomEvent( + type, + eventInitDict.bubbles, + eventInitDict.cancelable, + eventInitDict.detail + ); + } + + document.dispatchEvent(cEvent); + }; + + // Default Config + var defaultConfig = { + showRemaining: false, + showParagraphs: true, + showWordCount: true, + showCharCount: false, + countBytesAsChars: false, + countSpacesAsChars: false, + countHTML: false, + countLineBreaks: false, + hardLimit: true, + + //MAXLENGTH Properties + maxWordCount: -1, + maxCharCount: -1, + maxParagraphs: -1, + + // Filter + filter: null, + + // How long to show the 'paste' warning + pasteWarningDuration: 0, + + //DisAllowed functions + wordCountGreaterThanMaxLengthEvent: function(currentLength, maxLength) { + dispatchEvent('wordCountGreaterThanMaxLengthEvent', currentLength, maxLength); + }, + charCountGreaterThanMaxLengthEvent: function(currentLength, maxLength) { + dispatchEvent('charCountGreaterThanMaxLengthEvent', currentLength, maxLength); + }, + + //Allowed Functions + wordCountLessThanMaxLengthEvent: function(currentLength, maxLength) { + dispatchEvent('wordCountLessThanMaxLengthEvent', currentLength, maxLength); + }, + charCountLessThanMaxLengthEvent: function(currentLength, maxLength) { + dispatchEvent('charCountLessThanMaxLengthEvent', currentLength, maxLength); + } + }; + + // Get Config & Lang + var config = CKEDITOR.tools.extend(defaultConfig, editor.config.wordcount || {}, true); + + if (config.showParagraphs) { + if (config.maxParagraphs > -1) { + if (config.showRemaining) { + defaultFormat += "%paragraphsCount% " + editor.lang.wordcount.ParagraphsRemaining; + } else { + defaultFormat += editor.lang.wordcount.Paragraphs + " %paragraphsCount%"; + + defaultFormat += "/" + config.maxParagraphs; + } + } else { + defaultFormat += editor.lang.wordcount.Paragraphs + " %paragraphsCount%"; + } + } + + if (config.showParagraphs && (config.showWordCount || config.showCharCount)) { + defaultFormat += ", "; + } + + if (config.showWordCount) { + if (config.maxWordCount > -1) { + if (config.showRemaining) { + defaultFormat += "%wordCount% " + editor.lang.wordcount.WordCountRemaining; + } else { + defaultFormat += editor.lang.wordcount.WordCount + " %wordCount%"; + + defaultFormat += "/" + config.maxWordCount; + } + } else { + defaultFormat += editor.lang.wordcount.WordCount + " %wordCount%"; + } + } + + if (config.showCharCount && config.showWordCount) { + defaultFormat += ", "; + } + + if (config.showCharCount) { + if (config.maxCharCount > -1) { + if (config.showRemaining) { + defaultFormat += "%charCount% " + + editor.lang.wordcount[config.countHTML + ? "CharCountWithHTMLRemaining" + : "CharCountRemaining"]; + } else { + defaultFormat += editor.lang.wordcount[config.countHTML + ? "CharCountWithHTML" + : "CharCount"] + + " %charCount%"; + + defaultFormat += "/" + config.maxCharCount; + } + } else { + defaultFormat += editor.lang.wordcount[config.countHTML ? "CharCountWithHTML" : "CharCount"] + + " %charCount%"; + } + } + + var format = defaultFormat; + + bbcodePluginLoaded = typeof editor.plugins.bbcode != 'undefined'; + + function counterId(editorInstance) { + return "cke_wordcount_" + editorInstance.name; + } + + function counterElement(editorInstance) { + return document.getElementById(counterId(editorInstance)); + } + + function strip(html) { + if (bbcodePluginLoaded) { + // stripping out BBCode tags [...][/...] + return html.replace(/\[.*?\]/gi, ''); + } + + var tmp = document.createElement("div"); + + // Add filter before strip + html = filter(html); + + tmp.innerHTML = html; + + if (tmp.textContent == "" && typeof tmp.innerText == "undefined") { + return ""; + } + + return tmp.textContent || tmp.innerText; + } + + /** + * Implement filter to add or remove before counting + * @param html + * @returns string + */ + function filter(html) { + if (config.filter instanceof CKEDITOR.htmlParser.filter) { + var fragment = CKEDITOR.htmlParser.fragment.fromHtml(html), + writer = new CKEDITOR.htmlParser.basicWriter(); + config.filter.applyTo(fragment); + fragment.writeHtml(writer); + return writer.getHtml(); + } + return html; + } + + function countCharacters(text) { + if (config.countHTML) { + return (filter(text).length); + } + + var normalizedText; + + // strip body tags + if (editor.config.fullPage) { + var i = text.search(new RegExp("", "i")); + if (i != -1) { + var j = text.search(new RegExp("", "i")); + text = text.substring(i + 6, j); + } + + } + + normalizedText = text; + + if (!config.countSpacesAsChars) { + normalizedText = text.replace(/\s/g, "").replace(/ /g, ""); + } + + if (config.countLineBreaks) { + normalizedText = normalizedText.replace(/(\r\n|\n|\r)/gm, " "); + } else { + normalizedText = normalizedText.replace(/(\r\n|\n|\r)/gm, "").replace(/ /gi, " "); + } + + normalizedText = strip(normalizedText).replace(/^([\t\r\n]*)$/, ""); + + return config.countBytesAsChars ? (countBytes(normalizedText)) : (normalizedText.length); + } + + function countBytes(text) { + var count = 0, stringLength = text.length, i; + text = String(text || ""); + for (i = 0; i < stringLength; i++) { + var partCount = encodeURI(text[i]).split("%").length; + count += partCount == 1 ? 1 : partCount - 1; + } + return count; + } + + function countParagraphs(text) { + return (text.replace(/ /g, " ").replace(/(<([^>]+)>)/ig, "").replace(/^\s*$[\n\r]{1,}/gm, "++") + .split("++").length); + } + + function countWords(text) { + var normalizedText = text.replace(/(\r\n|\n|\r)/gm, " ").replace(/^\s+|\s+$/g, "") + .replace(" ", " "); + + normalizedText = strip(normalizedText); + + var words = normalizedText.split(/\s+/); + + for (var wordIndex = words.length - 1; wordIndex >= 0; wordIndex--) { + if (words[wordIndex].match(/^([\s\t\r\n]*)$/)) { + words.splice(wordIndex, 1); + } + } + + return (words.length); + } + + function limitReached(editorInstance, notify) { + limitReachedNotified = true; + limitRestoredNotified = false; + + if (config.hardLimit) { + editorInstance.execCommand('undo'); + } + + if (!notify) { + counterElement(editorInstance).className = "cke_path_item cke_wordcountLimitReached"; + editorInstance.fire("limitReached", { firedBy: "wordCount.limitReached" }, editor); + } + } + + function limitRestored(editorInstance) { + limitRestoredNotified = true; + limitReachedNotified = false; + editorInstance.fire('saveSnapshot'); + + counterElement(editorInstance).className = "cke_path_item"; + } + + function updateCounter(editorInstance) { + if (!counterElement(editorInstance)) { + return; + } + + var paragraphs = 0, + wordCount = 0, + charCount = 0, + text; + + // BeforeGetData and getData events are fired when calling + // getData(). We can prevent this by passing true as an + // argument to getData(). This allows us to fire the events + // manually with additional event data: firedBy. This additional + // data helps differentiate calls to getData() made by + // wordCount plugin from calls made by other plugins/code. + editorInstance.fire("beforeGetData", { firedBy: "wordCount.updateCounter" }, editor); + text = editorInstance.getData(true); + editorInstance.fire("getData", { dataValue: text, firedBy: "wordCount.updateCounter" }, editor); + + if (text) { + if (config.showCharCount) { + charCount = countCharacters(text); + } + + if (config.showParagraphs) { + paragraphs = countParagraphs(text); + } + + if (config.showWordCount) { + wordCount = countWords(text); + } + } + + var html = format; + if (config.showRemaining) { + if (config.maxCharCount >= 0) { + html = html.replace("%charCount%", config.maxCharCount - charCount); + } else { + html = html.replace("%charCount%", charCount); + } + + if (config.maxWordCount >= 0) { + html = html.replace("%wordCount%", config.maxWordCount - wordCount); + } else { + html = html.replace("%wordCount%", wordCount); + } + + if (config.maxParagraphs >= 0) { + html = html.replace("%paragraphsCount%", config.maxParagraphs - paragraphs); + } else { + html = html.replace("%paragraphsCount%", paragraphs); + } + } else { + html = html.replace("%wordCount%", wordCount).replace("%charCount%", charCount).replace("%paragraphsCount%", paragraphs); + } + + (editorInstance.config.wordcount || (editorInstance.config.wordcount = {})).wordCount = wordCount; + (editorInstance.config.wordcount || (editorInstance.config.wordcount = {})).charCount = charCount; + + if (CKEDITOR.env.gecko) { + counterElement(editorInstance).innerHTML = html; + } else { + counterElement(editorInstance).innerText = html; + } + + if (charCount == lastCharCount && wordCount == lastWordCount && paragraphs == lastParagraphs) { + if (charCount == config.maxCharCount || wordCount == config.maxWordCount || paragraphs > config.maxParagraphs) { + editorInstance.fire('saveSnapshot'); + } + return true; + } + + //If the limit is already over, allow the deletion of characters/words. Otherwise, + //the user would have to delete at one go the number of offending characters + var deltaWord = wordCount - lastWordCount; + var deltaChar = charCount - lastCharCount; + var deltaParagraphs = paragraphs - lastParagraphs; + + lastWordCount = wordCount; + lastCharCount = charCount; + lastParagraphs = paragraphs; + + if (lastWordCount == -1) { + lastWordCount = wordCount; + } + if (lastCharCount == -1) { + lastCharCount = charCount; + } + if (lastParagraphs == -1) { + lastParagraphs = paragraphs; + } + + // Check for word limit and/or char limit + if ((config.maxWordCount > -1 && wordCount > config.maxWordCount && deltaWord > 0) || + (config.maxCharCount > -1 && charCount > config.maxCharCount && deltaChar > 0) || + (config.maxParagraphs > -1 && paragraphs > config.maxParagraphs && deltaParagraphs > 0)) { + + limitReached(editorInstance, limitReachedNotified); + } else if ((config.maxWordCount == -1 || wordCount <= config.maxWordCount) && + (config.maxCharCount == -1 || charCount <= config.maxCharCount) && + (config.maxParagraphs == -1 || paragraphs <= config.maxParagraphs)) { + + limitRestored(editorInstance); + } else { + editorInstance.fire('saveSnapshot'); + } + + // update instance + editorInstance.wordCount = + { + paragraphs: paragraphs, + wordCount: wordCount, + charCount: charCount + }; + + + // Fire Custom Events + if (config.charCountGreaterThanMaxLengthEvent && config.charCountLessThanMaxLengthEvent) { + if (charCount > config.maxCharCount && config.maxCharCount > -1) { + config.charCountGreaterThanMaxLengthEvent(charCount, config.maxCharCount); + } else { + config.charCountLessThanMaxLengthEvent(charCount, config.maxCharCount); + } + } + + if (config.wordCountGreaterThanMaxLengthEvent && config.wordCountLessThanMaxLengthEvent) { + if (wordCount > config.maxWordCount && config.maxWordCount > -1) { + config.wordCountGreaterThanMaxLengthEvent(wordCount, config.maxWordCount); + + } else { + config.wordCountLessThanMaxLengthEvent(wordCount, config.maxWordCount); + } + } + + return true; + } + + function isCloseToLimits() { + if (config.maxWordCount > -1 && config.maxWordCount - lastWordCount < 5) { + return true; + } + + if (config.maxCharCount > -1 && config.maxCharCount - lastCharCount < 20) { + return true; + } + + if (config.maxParagraphs > -1 && config.maxParagraphs - lastParagraphs < 1) { + return true; + } + + return false; + } + + editor.on("key", + function(event) { + if (editor.mode === "source") { + clearTimeout(timeoutId); + timeoutId = setTimeout( + updateCounter.bind(this, event.editor), + 250 + ); + } + }, + editor, + null, + 100); + + editor.on("change", + function(event) { + var ms = isCloseToLimits() ? 5 : 250; + clearTimeout(timeoutId); + timeoutId = setTimeout( + updateCounter.bind(this, event.editor), + ms + ); + }, + editor, + null, + 100); + + editor.on("uiSpace", + function(event) { + if (editor.elementMode === CKEDITOR.ELEMENT_MODE_INLINE) { + if (event.data.space == "top") { + event.data.html += "
 
"; + } + } else { + if (event.data.space == "bottom") { + event.data.html += "
 
"; + } + } + + }, + editor, + null, + 100); + + editor.on("dataReady", + function(event) { + updateCounter(event.editor); + }, + editor, + null, + 100); + + editor.on("paste", + function(event) { + if (config.maxWordCount > 0 || config.maxCharCount > 0 || config.maxParagraphs > 0) { + + // Check if pasted content is above the limits + var wordCount = -1, + charCount = -1, + paragraphs = -1; + + // BeforeGetData and getData events are fired when calling + // getData(). We can prevent this by passing true as an + // argument to getData(). This allows us to fire the events + // manually with additional event data: firedBy. This additional + // data helps differentiate calls to getData() made by + // wordCount plugin from calls made by other plugins/code. + event.editor.fire("beforeGetData", { firedBy: "wordCount.onPaste" }, event.editor); + var text = event.editor.getData(true); + event.editor.fire("getData", { dataValue: text, firedBy: "wordCount.onPaste" }, event.editor); + + text += event.data.dataValue; + + if (config.showCharCount) { + charCount = countCharacters(text); + } + + if (config.showWordCount) { + wordCount = countWords(text); + } + + if (config.showParagraphs) { + paragraphs = countParagraphs(text); + } + + + // Instantiate the notification when needed and only have one instance + if (notification === null) { + notification = new CKEDITOR.plugins.notification(event.editor, + { + message: event.editor.lang.wordcount.pasteWarning, + type: 'warning', + duration: config.pasteWarningDuration + }); + } + + if (config.maxCharCount > 0 && charCount > config.maxCharCount && config.hardLimit) { + if (!notification.isVisible()) { + notification.show(); + } + event.cancel(); + } + + if (config.maxWordCount > 0 && wordCount > config.maxWordCount && config.hardLimit) { + if (!notification.isVisible()) { + notification.show(); + } + event.cancel(); + } + + if (config.maxParagraphs > 0 && paragraphs > config.maxParagraphs && config.hardLimit) { + if (!notification.isVisible()) { + notification.show(); + } + event.cancel(); + } + } + }, + editor, + null, + 100); + + editor.on("afterPaste", + function(event) { + updateCounter(event.editor); + }, + editor, + null, + 100); + } + }); diff --git a/plugins/ckeditor/wordcount/samples/maxParagraphs.html b/plugins/ckeditor/wordcount/samples/maxParagraphs.html new file mode 100644 index 0000000..372556c --- /dev/null +++ b/plugins/ckeditor/wordcount/samples/maxParagraphs.html @@ -0,0 +1,26 @@ + + + + + CKEditor + + + + + + + + \ No newline at end of file diff --git a/plugins/ckeditor/wordcount/samples/wordcount.html b/plugins/ckeditor/wordcount/samples/wordcount.html new file mode 100644 index 0000000..5649b7f --- /dev/null +++ b/plugins/ckeditor/wordcount/samples/wordcount.html @@ -0,0 +1,61 @@ + + + + + + WordCount — CKEditor Sample + + + + + + + + + +

+ CKEditor Samples » WordCount Plugin +

+ +
+

+ WordCount Plugin for CKEditor that counts the words an shows the word count in the footer of the editor.. +

+

+ In order to use the new plugin, include it in the config.extraPlugins configuration setting. +

+
+CKEDITOR.replace( 'textarea_id', {
+	extraPlugins: 'wordcount'
+} );
+
+
+ + + + + + + diff --git a/plugins/ckeditor/wordcount/samples/wordcountWithMaxCount.html b/plugins/ckeditor/wordcount/samples/wordcountWithMaxCount.html new file mode 100644 index 0000000..dd46ed2 --- /dev/null +++ b/plugins/ckeditor/wordcount/samples/wordcountWithMaxCount.html @@ -0,0 +1,110 @@ + + + + + + WordCount — CKEditor Sample + + + + + + + + + + +

+ CKEditor Samples » WordCount Plugin +

+ +
+

+ WordCount Plugin for CKEditor that counts the words an shows the word count in the footer of the editor.. +

+

+ In order to use the new plugin, include it in the config.extraPlugins configuration setting. +

+
+CKEDITOR.replace( 'textarea_id', {
+	extraPlugins: 'wordcount', 
+                maxWordCount: 4,
+	        maxCharCount: 10,
+                // optional events
+	        paragraphsCountGreaterThanMaxLengthEvent: function (currentLength, maxLength) {
+	                $("#informationparagraphs").css("background-color", "crimson").css("color", "white").text(currentLength + "/" + maxLength + " - paragraphs").show();
+	        },
+	        wordCountGreaterThanMaxLengthEvent: function (currentLength, maxLength) {
+	                $("#informationword").css("background-color", "crimson").css("color", "white").text(currentLength + "/" + maxLength + " - word").show();
+	        },
+	        charCountGreaterThanMaxLengthEvent: function (currentLength, maxLength) {
+	                $("#informationchar").css("background-color", "crimson").css("color", "white").text(currentLength + "/" + maxLength + " - char").show();
+	        },
+	        charCountLessThanMaxLengthEvent: function (currentLength, maxLength) {
+	                $("#informationchar").css("background-color", "white").css("color", "black").hide();
+	        },
+	        paragraphsCountLessThanMaxLengthEvent: function (currentLength, maxLength) {
+	                $("#informationparagraphs").css("background-color", "white").css("color", "black").hide();
+	        },
+	        wordCountLessThanMaxLengthEvent: function (currentLength, maxLength) {
+	                $("#informationword").css("background-color", "white").css("color", "black").hide();
+	        }
+} );
+
+
+ + +
+
+
+ + + + + From bbd10024204b99a64f72f7456f146716fd34f6c8 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 06:49:00 +0000 Subject: [PATCH 137/195] GCMS-1415 --- plugins/ckeditor/helper.js | 7 ++++--- plugins/ckeditor/wordcount/index.js | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/ckeditor/helper.js b/plugins/ckeditor/helper.js index 2fdfb43..9e36277 100644 --- a/plugins/ckeditor/helper.js +++ b/plugins/ckeditor/helper.js @@ -4,9 +4,10 @@ define(function (require, exports, module) { return { registerPlugin: function (pluginId) { - var pluginPath = "../../../_modules/wordcount/plugins/ckeditor/wordcount/"; - - CKEDITOR.plugins.addExternal(pluginId, pluginPath, 'plugin.js'); + // var pluginPath = "../../../_modules/wordcount/plugins/ckeditor/wordcount/"; + // var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; + // '../../../' + moduleId + '/oup-ckeditor-dev/plugins/icons/' + pluginName + '.png' + //CKEDITOR.plugins.addExternal(pluginId, moduleId, 'plugin.js'); if (CKEDITOR.config.extraPlugins.length > 0) { CKEDITOR.config.extraPlugins += ","; diff --git a/plugins/ckeditor/wordcount/index.js b/plugins/ckeditor/wordcount/index.js index 7aadf66..04b747b 100644 --- a/plugins/ckeditor/wordcount/index.js +++ b/plugins/ckeditor/wordcount/index.js @@ -1,7 +1,7 @@ define(function (require, exports, module) { var Helper = require("../helper.js"); - - Helper.registerPlugin("wordcount"); + var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; + Helper.registerPlugin("wordcount", moduleId); }); \ No newline at end of file From 4a19f94dd15142c663a0858f5738af72e5830651 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 06:52:09 +0000 Subject: [PATCH 138/195] GCMS-1415 --- plugins/ckeditor/helper.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/ckeditor/helper.js b/plugins/ckeditor/helper.js index 9e36277..c6cccdc 100644 --- a/plugins/ckeditor/helper.js +++ b/plugins/ckeditor/helper.js @@ -3,11 +3,14 @@ define(function (require, exports, module) { var CKEDITOR = window.CKEDITOR; return { - registerPlugin: function (pluginId) { + registerPlugin: function (pluginId, moduleId) { // var pluginPath = "../../../_modules/wordcount/plugins/ckeditor/wordcount/"; // var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; // '../../../' + moduleId + '/oup-ckeditor-dev/plugins/icons/' + pluginName + '.png' //CKEDITOR.plugins.addExternal(pluginId, moduleId, 'plugin.js'); + var pluginPath = "../../../" + moduleId + "/oup-ckeditor/plugins/ckeditor/wordcount/"; + + CKEDITOR.plugins.addExternal(pluginId, pluginPath, 'plugin.js'); if (CKEDITOR.config.extraPlugins.length > 0) { CKEDITOR.config.extraPlugins += ","; From 75939b2c43e06b89bd34bb9fff28432ee29e8811 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 07:05:09 +0000 Subject: [PATCH 139/195] GCMS-1415 --- plugins/ckeditor/helper.js | 4 ---- plugins/ckeditor/wordcount/index.js | 10 +++++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/ckeditor/helper.js b/plugins/ckeditor/helper.js index c6cccdc..9deb95f 100644 --- a/plugins/ckeditor/helper.js +++ b/plugins/ckeditor/helper.js @@ -4,10 +4,6 @@ define(function (require, exports, module) { return { registerPlugin: function (pluginId, moduleId) { - // var pluginPath = "../../../_modules/wordcount/plugins/ckeditor/wordcount/"; - // var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; - // '../../../' + moduleId + '/oup-ckeditor-dev/plugins/icons/' + pluginName + '.png' - //CKEDITOR.plugins.addExternal(pluginId, moduleId, 'plugin.js'); var pluginPath = "../../../" + moduleId + "/oup-ckeditor/plugins/ckeditor/wordcount/"; CKEDITOR.plugins.addExternal(pluginId, pluginPath, 'plugin.js'); diff --git a/plugins/ckeditor/wordcount/index.js b/plugins/ckeditor/wordcount/index.js index 04b747b..3b8b6bd 100644 --- a/plugins/ckeditor/wordcount/index.js +++ b/plugins/ckeditor/wordcount/index.js @@ -3,5 +3,13 @@ define(function (require, exports, module) { var Helper = require("../helper.js"); var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; Helper.registerPlugin("wordcount", moduleId); - + window.CKEDITOR.config.wordcount = { + showParagraphs: false, + showWordCount: false, + showCharCount: false, + countSpacesAsChars: false, + countHTML: false + //maxWordCount: 10, + //maxCharCount: 10 + }; }); \ No newline at end of file From 2ca0b684d29f60d5aec232af2d4951e7ed919a73 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 07:06:34 +0000 Subject: [PATCH 140/195] GCMS-1415 --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index e67ab0d..393ccd4 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ define(function (require, exports, module) { - require("./fields/oup-ckeditor-field.js"); - require("./plugins/ckeditor/wordcount/index.js"); + + require("./fields/oup-ckeditor-field.js"); }); \ No newline at end of file From 2f2e9c50c596842661055b3bb9a178b7054858a6 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 07:16:26 +0000 Subject: [PATCH 141/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 56 ++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index be73d2a..fb25d87 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -491,14 +491,27 @@ define(function (require, exports, module) { } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } + + if (this.name == "editorsWidgetText") { + window.CKEDITOR.replace(this.id, { + wordcount: { + showParagraphs: true, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 50, + } + }); + } } // TODO: REMOVE BUTTON BASED ON USER TEAM - if (this.isUserInTeam("academic-editors")) - { + if (this.isUserInTeam("academic-editors")) { this.options.ckeditor.removeButtons = 'Source'; } - + this.base(); }, @@ -507,33 +520,28 @@ define(function (require, exports, module) { * Check if user has role * @param {string} role */ - isUserInTeam: function(role) - { + isUserInTeam: function (role) { var self = this; var observableHolder = self.top().options.observableHolder; var teamKeys = []; var project = observableHolder.observable("project").get(); - if (project) - { + if (project) { teamKeys = observableHolder.observable("projectUserTeamKeys").get(); - } - else - { + } else { teamKeys = observableHolder.observable("oneteamUserTeamKeys").get(); } - - if (!teamKeys) - { + + if (!teamKeys) { return false; } - + var x = teamKeys.indexOf(role); if (x > -1) { return true; } - + return false; }, @@ -576,20 +584,24 @@ define(function (require, exports, module) { Alpaca.registerMessages({ "noDependentField": "No local config found" }); - + //window.CKEDITOR.config.extraPlugins+=",devtools"; - - window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('image'); }); + + window.CKEDITOR.on('instanceReady', function (ck) { + ck.editor.removeMenuItem('image'); + }); //window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('table'); }); - window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('tablecell'); }); - + window.CKEDITOR.on('instanceReady', function (ck) { + ck.editor.removeMenuItem('tablecell'); + }); + window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; - ev.editor.getCommand( 'table' ).allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; + ev.editor.getCommand('table').allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; if (dialogName == "table" || dialogName == "tableProperties") { - var infoTab = dialogDefinition.getContents("info"); + var infoTab = dialogDefinition.getContents("info"); infoTab.get("txtWidth")["default"] = ""; infoTab.get("txtCellSpace")["default"] = ""; infoTab.get("txtCellPad")["default"] = ""; From 7f0bddabfcb45dafbeaee86ea87f1e9653cc5ad3 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 07:27:06 +0000 Subject: [PATCH 142/195] Revert "GCMS-1415" This reverts commit 2f2e9c50c596842661055b3bb9a178b7054858a6. --- fields/oup-ckeditor-field.js | 56 ++++++++++++++---------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index fb25d87..be73d2a 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -491,27 +491,14 @@ define(function (require, exports, module) { } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } - - if (this.name == "editorsWidgetText") { - window.CKEDITOR.replace(this.id, { - wordcount: { - showParagraphs: true, - showWordCount: true, - showCharCount: true, - countSpacesAsChars: true, - countHTML: false, - maxWordCount: -1, - maxCharCount: 50, - } - }); - } } // TODO: REMOVE BUTTON BASED ON USER TEAM - if (this.isUserInTeam("academic-editors")) { + if (this.isUserInTeam("academic-editors")) + { this.options.ckeditor.removeButtons = 'Source'; } - + this.base(); }, @@ -520,28 +507,33 @@ define(function (require, exports, module) { * Check if user has role * @param {string} role */ - isUserInTeam: function (role) { + isUserInTeam: function(role) + { var self = this; var observableHolder = self.top().options.observableHolder; var teamKeys = []; var project = observableHolder.observable("project").get(); - if (project) { + if (project) + { teamKeys = observableHolder.observable("projectUserTeamKeys").get(); - } else { + } + else + { teamKeys = observableHolder.observable("oneteamUserTeamKeys").get(); } - - if (!teamKeys) { + + if (!teamKeys) + { return false; } - + var x = teamKeys.indexOf(role); if (x > -1) { return true; } - + return false; }, @@ -584,24 +576,20 @@ define(function (require, exports, module) { Alpaca.registerMessages({ "noDependentField": "No local config found" }); - + //window.CKEDITOR.config.extraPlugins+=",devtools"; - - window.CKEDITOR.on('instanceReady', function (ck) { - ck.editor.removeMenuItem('image'); - }); + + window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('image'); }); //window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('table'); }); - window.CKEDITOR.on('instanceReady', function (ck) { - ck.editor.removeMenuItem('tablecell'); - }); - + window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('tablecell'); }); + window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; - ev.editor.getCommand('table').allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; + ev.editor.getCommand( 'table' ).allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; if (dialogName == "table" || dialogName == "tableProperties") { - var infoTab = dialogDefinition.getContents("info"); + var infoTab = dialogDefinition.getContents("info"); infoTab.get("txtWidth")["default"] = ""; infoTab.get("txtCellSpace")["default"] = ""; infoTab.get("txtCellPad")["default"] = ""; From 53e735054989cd488273de242241b593190c0e0c Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 08:34:52 +0000 Subject: [PATCH 143/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index be73d2a..2dec4dd 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -490,7 +490,17 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; - } + } + + // this.options.ckeditor.wordcount = { + // showParagraphs: true, + // showWordCount: true, + // showCharCount: true, + // countSpacesAsChars: true, + // countHTML: false, + // maxWordCount: -1, + // maxCharCount: 50, + // } } // TODO: REMOVE BUTTON BASED ON USER TEAM @@ -609,4 +619,19 @@ define(function (require, exports, module) { Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); + $(document).ready(function(){ + window.CKEDITOR.replace( 'alpaca10', + { + wordcount : + { + showParagraphs: true, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 50, + } + } ); + }); }); \ No newline at end of file From 0edea23a860e7307d35a77a48764835eafb85ecf Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 08:36:48 +0000 Subject: [PATCH 144/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 81 ++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 2dec4dd..f842202 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -490,7 +490,7 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h2;h3;h4;pre'; } else { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; - } + } // this.options.ckeditor.wordcount = { // showParagraphs: true, @@ -504,11 +504,10 @@ define(function (require, exports, module) { } // TODO: REMOVE BUTTON BASED ON USER TEAM - if (this.isUserInTeam("academic-editors")) - { + if (this.isUserInTeam("academic-editors")) { this.options.ckeditor.removeButtons = 'Source'; } - + this.base(); }, @@ -517,33 +516,28 @@ define(function (require, exports, module) { * Check if user has role * @param {string} role */ - isUserInTeam: function(role) - { + isUserInTeam: function (role) { var self = this; var observableHolder = self.top().options.observableHolder; var teamKeys = []; var project = observableHolder.observable("project").get(); - if (project) - { + if (project) { teamKeys = observableHolder.observable("projectUserTeamKeys").get(); - } - else - { + } else { teamKeys = observableHolder.observable("oneteamUserTeamKeys").get(); } - - if (!teamKeys) - { + + if (!teamKeys) { return false; } - + var x = teamKeys.indexOf(role); if (x > -1) { return true; } - + return false; }, @@ -586,20 +580,35 @@ define(function (require, exports, module) { Alpaca.registerMessages({ "noDependentField": "No local config found" }); - + //window.CKEDITOR.config.extraPlugins+=",devtools"; - - window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('image'); }); + + window.CKEDITOR.on('instanceReady', function (ck) { + ck.editor.removeMenuItem('image'); + }); //window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('table'); }); - window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('tablecell'); }); - + window.CKEDITOR.on('instanceReady', function (ck) { + ck.editor.removeMenuItem('tablecell'); + window.CKEDITOR.replace('alpaca10', { + wordcount: { + showParagraphs: true, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 50, + } + }); + }); + window.CKEDITOR.on('dialogDefinition', function (ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; - ev.editor.getCommand( 'table' ).allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; + ev.editor.getCommand('table').allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; if (dialogName == "table" || dialogName == "tableProperties") { - var infoTab = dialogDefinition.getContents("info"); + var infoTab = dialogDefinition.getContents("info"); infoTab.get("txtWidth")["default"] = ""; infoTab.get("txtCellSpace")["default"] = ""; infoTab.get("txtCellPad")["default"] = ""; @@ -619,19 +628,17 @@ define(function (require, exports, module) { Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); - $(document).ready(function(){ - window.CKEDITOR.replace( 'alpaca10', - { - wordcount : - { - showParagraphs: true, - showWordCount: true, - showCharCount: true, - countSpacesAsChars: true, - countHTML: false, - maxWordCount: -1, - maxCharCount: 50, - } - } ); + $(document).ready(function () { + window.CKEDITOR.replace('alpaca10', { + wordcount: { + showParagraphs: true, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 50, + } + }); }); }); \ No newline at end of file From fb077410714b2708212eb213896e7cc82a3a7a8c Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 08:53:58 +0000 Subject: [PATCH 145/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 45 ++++++++++-------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index f842202..3fb3acf 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -492,15 +492,18 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } - // this.options.ckeditor.wordcount = { - // showParagraphs: true, - // showWordCount: true, - // showCharCount: true, - // countSpacesAsChars: true, - // countHTML: false, - // maxWordCount: -1, - // maxCharCount: 50, - // } + if (type && (type == "config7")) { + this.options.ckeditor.wordcount = { + showParagraphs: true, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 50, + } + } + } // TODO: REMOVE BUTTON BASED ON USER TEAM @@ -589,17 +592,6 @@ define(function (require, exports, module) { //window.CKEDITOR.on('instanceReady', function(ck) { ck.editor.removeMenuItem('table'); }); window.CKEDITOR.on('instanceReady', function (ck) { ck.editor.removeMenuItem('tablecell'); - window.CKEDITOR.replace('alpaca10', { - wordcount: { - showParagraphs: true, - showWordCount: true, - showCharCount: true, - countSpacesAsChars: true, - countHTML: false, - maxWordCount: -1, - maxCharCount: 50, - } - }); }); @@ -628,17 +620,4 @@ define(function (require, exports, module) { Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); - $(document).ready(function () { - window.CKEDITOR.replace('alpaca10', { - wordcount: { - showParagraphs: true, - showWordCount: true, - showCharCount: true, - countSpacesAsChars: true, - countHTML: false, - maxWordCount: -1, - maxCharCount: 50, - } - }); - }); }); \ No newline at end of file From a3dfdead6a103ec168a0873c0c1db39a4194f0d1 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:04:26 +0000 Subject: [PATCH 146/195] GCMS-1415 --- fields/config2.js | 11 +++++++++++ fields/oup-ckeditor-field.js | 25 +++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 fields/config2.js diff --git a/fields/config2.js b/fields/config2.js new file mode 100644 index 0000000..c5861a1 --- /dev/null +++ b/fields/config2.js @@ -0,0 +1,11 @@ +{ + "height": 50, + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] +} \ No newline at end of file diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 3fb3acf..1f87fe6 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -124,17 +124,7 @@ define(function (require, exports, module) { } } }, - "config2": { - "height": 50, - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, + "config2": './config2.js', "config3": { "height": 100, "toolbar": [ @@ -492,7 +482,18 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } - if (type && (type == "config7")) { + if (type && (type == "config2")) { + this.options.ckeditor.wordcount = { + showParagraphs: true, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 50, + } + } + if (type && (type == "config3")) { this.options.ckeditor.wordcount = { showParagraphs: true, showWordCount: true, From c547ac78e72f8c13de13c937184ad8adbfd9b7c2 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:05:56 +0000 Subject: [PATCH 147/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 1f87fe6..d355e19 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -124,7 +124,9 @@ define(function (require, exports, module) { } } }, - "config2": './config2.js', + "config2": { + 'customConfig': './config2.js' + }, "config3": { "height": 100, "toolbar": [ From 1b27e57bcae8b3bf76586185938254de09b8baad Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:08:37 +0000 Subject: [PATCH 148/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d355e19..94eaccd 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -3,6 +3,8 @@ define(function (require, exports, module) { var $ = require("jquery"); var Alpaca = require("alpaca"); var OneTeam = require("oneteam"); + var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; + var path = "../../../" + moduleId + "/oup-ckeditor/fields/"; window.CKEDITOR.config.disableNativeSpellChecker = false; @@ -125,7 +127,7 @@ define(function (require, exports, module) { } }, "config2": { - 'customConfig': './config2.js' + 'customConfig': path + 'config2.js' }, "config3": { "height": 100, From 4de572612ef54be429c4fd82d856f45066b3a6c9 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:11:39 +0000 Subject: [PATCH 149/195] GCMS-1415 --- fields/config2.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fields/config2.js b/fields/config2.js index c5861a1..f343e5f 100644 --- a/fields/config2.js +++ b/fields/config2.js @@ -1,11 +1,11 @@ -{ - "height": 50, - "toolbar": [ +CKEDITOR.editorConfig = function( config ) { + config.height = 50; + config.toolbar = [ [ "SpecialChar", "Italic", "Subscript", "Superscript" ] - ] + ]; } \ No newline at end of file From 9676c448bb2ff7a47334deb65ef64d398f550ebf Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:15:56 +0000 Subject: [PATCH 150/195] GCMS-1415 --- fields/config3.js | 79 ++++++++++++++++++++++++++++++++++++ fields/oup-ckeditor-field.js | 78 +---------------------------------- 2 files changed, 80 insertions(+), 77 deletions(-) create mode 100644 fields/config3.js diff --git a/fields/config3.js b/fields/config3.js new file mode 100644 index 0000000..2c1299c --- /dev/null +++ b/fields/config3.js @@ -0,0 +1,79 @@ +CKEDITOR.editorConfig = function (config) { + config.height = 100; + config.toolbar = [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + ]; + config.removeButtons = null; + config.stylesSet = [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ]; + config["cloudcms-image"] = { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + }; +} \ No newline at end of file diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 94eaccd..e8856bd 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -130,83 +130,7 @@ define(function (require, exports, module) { 'customConfig': path + 'config2.js' }, "config3": { - "height": 100, - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink" - ], - [ - "Italic", - "BulletedList", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat", - "-", - "ShowBlocks" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - } + 'customConfig': path + 'config3.js' }, "config4": { "toolbar": [ From 0a544d48db8c21a6d8606c803bc085d6c01e255d Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:29:46 +0000 Subject: [PATCH 151/195] GCMS-1415 --- fields/config1.js | 112 ++++++++++++ fields/config2.js | 6 +- fields/config3.js | 8 +- fields/config4.js | 8 + fields/config5.js | 19 ++ fields/config6.js | 102 +++++++++++ fields/config7.js | 67 +++++++ fields/config8.js | 29 +++ fields/oup-ckeditor-field.js | 343 ++--------------------------------- 9 files changed, 359 insertions(+), 335 deletions(-) create mode 100644 fields/config1.js create mode 100644 fields/config4.js create mode 100644 fields/config5.js create mode 100644 fields/config6.js create mode 100644 fields/config7.js create mode 100644 fields/config8.js diff --git a/fields/config1.js b/fields/config1.js new file mode 100644 index 0000000..d39b6d0 --- /dev/null +++ b/fields/config1.js @@ -0,0 +1,112 @@ +CKEDITOR.editorConfig = function (config) { + config["toolbar"] = [ + [ + "Cut", + "Copy", + "PasteText", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor", + "cloudcms-link" + ], + [ + "Table", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" + ] + ]; + config["removeButtons"] = null; + config["stylesSet"] = [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP Notice", + "element": "div", + "attributes": { + "class": "notice" + } + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + + ]; + config["cloudcms-image"] = { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library", + } + }; + config["cloudcms-link"] = { + "linkPickerType": "file-picker", + "hideUploadButton": true, + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Document Library", + } + }; +} \ No newline at end of file diff --git a/fields/config2.js b/fields/config2.js index f343e5f..761914d 100644 --- a/fields/config2.js +++ b/fields/config2.js @@ -1,6 +1,6 @@ -CKEDITOR.editorConfig = function( config ) { - config.height = 50; - config.toolbar = [ +CKEDITOR.editorConfig = function (config) { + config["height"] = 50; + config["toolbar"] = [ [ "SpecialChar", "Italic", diff --git a/fields/config3.js b/fields/config3.js index 2c1299c..f180b4d 100644 --- a/fields/config3.js +++ b/fields/config3.js @@ -1,6 +1,6 @@ CKEDITOR.editorConfig = function (config) { - config.height = 100; - config.toolbar = [ + config["height"] = 100; + config["toolbar"] = [ [ "Cut", "Copy", @@ -33,8 +33,8 @@ CKEDITOR.editorConfig = function (config) { "cloudcms-image" ], ]; - config.removeButtons = null; - config.stylesSet = [{ + config["removeButtons"] = null; + config["stylesSet"] = [{ "name": "Paragraph", "element": "p" }, diff --git a/fields/config4.js b/fields/config4.js new file mode 100644 index 0000000..44a6279 --- /dev/null +++ b/fields/config4.js @@ -0,0 +1,8 @@ +CKEDITOR.editorConfig = function (config) { + config["toolbar"] = [ + [ + "Link", + "Unlink" + ] + ]; +} \ No newline at end of file diff --git a/fields/config5.js b/fields/config5.js new file mode 100644 index 0000000..40ffedf --- /dev/null +++ b/fields/config5.js @@ -0,0 +1,19 @@ +CKEDITOR.editorConfig = function (config) { + config["height"] = 100; + config["toolbar"] = [ + [ + "Format", + "-", + "Italic", + "SpecialChar", + "Subscript", + "Superscript", + "-", + "Link", + "Unlink", + "-", + "ShowBlocks" + ] + ]; + config["removeButtons"] = null; +} \ No newline at end of file diff --git a/fields/config6.js b/fields/config6.js new file mode 100644 index 0000000..7e342dd --- /dev/null +++ b/fields/config6.js @@ -0,0 +1,102 @@ +CKEDITOR.editorConfig = function (config) { + config["toolbar"] = [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" + ] + ]; + config["removeButtons"] = null; + config["stylesSet"] = [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ]; + config["cloudcms-image"] = { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + }; +} \ No newline at end of file diff --git a/fields/config7.js b/fields/config7.js new file mode 100644 index 0000000..a13840a --- /dev/null +++ b/fields/config7.js @@ -0,0 +1,67 @@ +CKEDITOR.editorConfig = function (config) { + config["height"] = 100; + config["toolbar"] = [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ] + ]; + config["removeButtons"] = null; + config["stylesSet"] = [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ] +} \ No newline at end of file diff --git a/fields/config8.js b/fields/config8.js new file mode 100644 index 0000000..d850c2e --- /dev/null +++ b/fields/config8.js @@ -0,0 +1,29 @@ +CKEDITOR.editorConfig = function (config) { + config["toolbar"] = [ + [ + "Format" + ], + [ + "SpecialChar", + "Subscript", + "Superscript" + ], + [ + "Link", + "Unlink" + ], + [ + "cloudcms-image" + ] + ]; + config["removeButtons"] = null; + config["cloudcms-image"] = { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + }; +} \ No newline at end of file diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index e8856bd..0310a32 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -15,347 +15,34 @@ define(function (require, exports, module) { { toolbarOptions: { "config1": { - "toolbar": [ - [ - "Cut", - "Copy", - "PasteText", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor", - "cloudcms-link" - ], - [ - "Table", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" - ] - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP Notice", - "element": "div", - "attributes": { - "class": "notice" - } - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library", - } - }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "hideUploadButton": true, - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Document Library", - } - } + 'customConfig': path + 'config1.js' }, "config2": { 'customConfig': path + 'config2.js' }, + "config21": { + 'customConfig': path + 'config2.js' + }, "config3": { 'customConfig': path + 'config3.js' }, + "config31": { + 'customConfig': path + 'config3.js' + }, "config4": { - "toolbar": [ - [ - "Link", - "Unlink" - ] - ] + 'customConfig': path + 'config4.js' }, "config5": { - "height": 100, - "toolbar": [ - [ - "Format", - "-", - "Italic", - "SpecialChar", - "Subscript", - "Superscript", - "-", - "Link", - "Unlink", - "-", - "ShowBlocks" - ] - ], - "removeButtons": null + 'customConfig': path + 'config5.js' }, "config6": { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor" - ], - [ - "Table", - "HorizontalRule", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" - ] - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - } + 'customConfig': path + 'config6.js' }, "config7": { - "height": 100, - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink" - ], - [ - "Italic", - "BulletedList", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat", - "-", - "ShowBlocks" - ], - [ - "Format", - "Styles" - ] - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ] + 'customConfig': path + 'config7.js' }, "config8": { - "toolbar": [ - [ - "Format" - ], - [ - "SpecialChar", - "Subscript", - "Superscript" - ], - [ - "Link", - "Unlink" - ], - [ - "cloudcms-image" - ] - ], - "removeButtons": null, - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - } + 'customConfig': path + 'config8.js' } }, @@ -410,7 +97,7 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } - if (type && (type == "config2")) { + if (type && (type == "config21")) { this.options.ckeditor.wordcount = { showParagraphs: true, showWordCount: true, @@ -421,7 +108,7 @@ define(function (require, exports, module) { maxCharCount: 50, } } - if (type && (type == "config3")) { + if (type && (type == "config31")) { this.options.ckeditor.wordcount = { showParagraphs: true, showWordCount: true, @@ -429,7 +116,7 @@ define(function (require, exports, module) { countSpacesAsChars: true, countHTML: false, maxWordCount: -1, - maxCharCount: 50, + maxCharCount: 100, } } From 8f78f0f58a916c5396b6f25747abc128c0f0797a Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:50:41 +0000 Subject: [PATCH 152/195] GCMS-1415 --- fields/config1.js | 112 -------- fields/config2.js | 11 - fields/config3.js | 79 ------ fields/config4.js | 8 - fields/config5.js | 19 -- fields/config6.js | 102 ------- fields/config7.js | 67 ----- fields/config8.js | 29 -- fields/oup-ckeditor-field.js | 512 ++++++++++++++++++++++++++++++++++- 9 files changed, 499 insertions(+), 440 deletions(-) delete mode 100644 fields/config1.js delete mode 100644 fields/config2.js delete mode 100644 fields/config3.js delete mode 100644 fields/config4.js delete mode 100644 fields/config5.js delete mode 100644 fields/config6.js delete mode 100644 fields/config7.js delete mode 100644 fields/config8.js diff --git a/fields/config1.js b/fields/config1.js deleted file mode 100644 index d39b6d0..0000000 --- a/fields/config1.js +++ /dev/null @@ -1,112 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["toolbar"] = [ - [ - "Cut", - "Copy", - "PasteText", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor", - "cloudcms-link" - ], - [ - "Table", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" - ] - ]; - config["removeButtons"] = null; - config["stylesSet"] = [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP Notice", - "element": "div", - "attributes": { - "class": "notice" - } - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - - ]; - config["cloudcms-image"] = { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library", - } - }; - config["cloudcms-link"] = { - "linkPickerType": "file-picker", - "hideUploadButton": true, - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Document Library", - } - }; -} \ No newline at end of file diff --git a/fields/config2.js b/fields/config2.js deleted file mode 100644 index 761914d..0000000 --- a/fields/config2.js +++ /dev/null @@ -1,11 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["height"] = 50; - config["toolbar"] = [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ]; -} \ No newline at end of file diff --git a/fields/config3.js b/fields/config3.js deleted file mode 100644 index f180b4d..0000000 --- a/fields/config3.js +++ /dev/null @@ -1,79 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["height"] = 100; - config["toolbar"] = [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink" - ], - [ - "Italic", - "BulletedList", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat", - "-", - "ShowBlocks" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - ]; - config["removeButtons"] = null; - config["stylesSet"] = [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ]; - config["cloudcms-image"] = { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - }; -} \ No newline at end of file diff --git a/fields/config4.js b/fields/config4.js deleted file mode 100644 index 44a6279..0000000 --- a/fields/config4.js +++ /dev/null @@ -1,8 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["toolbar"] = [ - [ - "Link", - "Unlink" - ] - ]; -} \ No newline at end of file diff --git a/fields/config5.js b/fields/config5.js deleted file mode 100644 index 40ffedf..0000000 --- a/fields/config5.js +++ /dev/null @@ -1,19 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["height"] = 100; - config["toolbar"] = [ - [ - "Format", - "-", - "Italic", - "SpecialChar", - "Subscript", - "Superscript", - "-", - "Link", - "Unlink", - "-", - "ShowBlocks" - ] - ]; - config["removeButtons"] = null; -} \ No newline at end of file diff --git a/fields/config6.js b/fields/config6.js deleted file mode 100644 index 7e342dd..0000000 --- a/fields/config6.js +++ /dev/null @@ -1,102 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["toolbar"] = [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor" - ], - [ - "Table", - "HorizontalRule", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" - ] - ]; - config["removeButtons"] = null; - config["stylesSet"] = [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ]; - config["cloudcms-image"] = { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - }; -} \ No newline at end of file diff --git a/fields/config7.js b/fields/config7.js deleted file mode 100644 index a13840a..0000000 --- a/fields/config7.js +++ /dev/null @@ -1,67 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["height"] = 100; - config["toolbar"] = [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink" - ], - [ - "Italic", - "BulletedList", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat", - "-", - "ShowBlocks" - ], - [ - "Format", - "Styles" - ] - ]; - config["removeButtons"] = null; - config["stylesSet"] = [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ] -} \ No newline at end of file diff --git a/fields/config8.js b/fields/config8.js deleted file mode 100644 index d850c2e..0000000 --- a/fields/config8.js +++ /dev/null @@ -1,29 +0,0 @@ -CKEDITOR.editorConfig = function (config) { - config["toolbar"] = [ - [ - "Format" - ], - [ - "SpecialChar", - "Subscript", - "Superscript" - ], - [ - "Link", - "Unlink" - ], - [ - "cloudcms-image" - ] - ]; - config["removeButtons"] = null; - config["cloudcms-image"] = { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - }; -} \ No newline at end of file diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 0310a32..d31e7a1 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -3,8 +3,6 @@ define(function (require, exports, module) { var $ = require("jquery"); var Alpaca = require("alpaca"); var OneTeam = require("oneteam"); - var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; - var path = "../../../" + moduleId + "/oup-ckeditor/fields/"; window.CKEDITOR.config.disableNativeSpellChecker = false; @@ -15,34 +13,521 @@ define(function (require, exports, module) { { toolbarOptions: { "config1": { - 'customConfig': path + 'config1.js' + "toolbar": [ + [ + "Cut", + "Copy", + "PasteText", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor", + "cloudcms-link" + ], + [ + "Table", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" + ] + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP Notice", + "element": "div", + "attributes": { + "class": "notice" + } + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library", + } + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "hideUploadButton": true, + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Document Library", + } + } }, "config2": { - 'customConfig': path + 'config2.js' + "height": 50, + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] }, "config21": { - 'customConfig': path + 'config2.js' + "height": 50, + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] }, "config3": { - 'customConfig': path + 'config3.js' + "height": 100, + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } }, "config31": { - 'customConfig': path + 'config3.js' + "height": 100, + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } }, "config4": { - 'customConfig': path + 'config4.js' + "toolbar": [ + [ + "Link", + "Unlink" + ] + ] }, "config5": { - 'customConfig': path + 'config5.js' + "height": 100, + "toolbar": [ + [ + "Format", + "-", + "Italic", + "SpecialChar", + "Subscript", + "Superscript", + "-", + "Link", + "Unlink", + "-", + "ShowBlocks" + ] + ], + "removeButtons": null }, "config6": { - 'customConfig': path + 'config6.js' + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" + ] + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } }, "config7": { - 'customConfig': path + 'config7.js' + "height": 100, + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ] + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ] }, "config8": { - 'customConfig': path + 'config8.js' + "toolbar": [ + [ + "Format" + ], + [ + "SpecialChar", + "Subscript", + "Superscript" + ], + [ + "Link", + "Unlink" + ], + [ + "cloudcms-image" + ] + ], + "removeButtons": null, + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } } }, @@ -108,9 +593,10 @@ define(function (require, exports, module) { maxCharCount: 50, } } + if (type && (type == "config31")) { this.options.ckeditor.wordcount = { - showParagraphs: true, + showParagraphs: false, showWordCount: true, showCharCount: true, countSpacesAsChars: true, From 7fe6b04589bb76178ae7207f0d9831b28ee6ffac Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:55:22 +0000 Subject: [PATCH 153/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d31e7a1..5f6f180 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -584,7 +584,7 @@ define(function (require, exports, module) { if (type && (type == "config21")) { this.options.ckeditor.wordcount = { - showParagraphs: true, + showParagraphs: false, showWordCount: true, showCharCount: true, countSpacesAsChars: true, @@ -596,7 +596,7 @@ define(function (require, exports, module) { if (type && (type == "config31")) { this.options.ckeditor.wordcount = { - showParagraphs: false, + showParagraphs: true, showWordCount: true, showCharCount: true, countSpacesAsChars: true, From 2224305e3d333c61fb6d270a8f7d10f00c4e7697 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 09:56:48 +0000 Subject: [PATCH 154/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 5f6f180..647f15c 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -590,7 +590,7 @@ define(function (require, exports, module) { countSpacesAsChars: true, countHTML: false, maxWordCount: -1, - maxCharCount: 50, + maxCharCount: 40, } } @@ -602,7 +602,7 @@ define(function (require, exports, module) { countSpacesAsChars: true, countHTML: false, maxWordCount: -1, - maxCharCount: 100, + maxCharCount: 110, } } From 81d8297091a7ff4bc5a27f6a02f0935840d5c5c4 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 10:02:03 +0000 Subject: [PATCH 155/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 647f15c..9515da5 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -6,6 +6,18 @@ define(function (require, exports, module) { window.CKEDITOR.config.disableNativeSpellChecker = false; + let config2 = { + "height": 50, + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + }; + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( /** * @lends Alpaca.Fields.OUPCKEditorField.prototype @@ -135,17 +147,7 @@ define(function (require, exports, module) { ] ] }, - "config21": { - "height": 50, - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, + "config21": config2, "config3": { "height": 100, "toolbar": [ From 6d9e2bc1cd8d951848633cc7990ee480dde16a26 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 10:09:54 +0000 Subject: [PATCH 156/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 936 ++++++++++++++++------------------- 1 file changed, 429 insertions(+), 507 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 9515da5..4d9a8dd 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -6,7 +6,120 @@ define(function (require, exports, module) { window.CKEDITOR.config.disableNativeSpellChecker = false; - let config2 = { + + let config1 = { + "toolbar": [ + [ + "Cut", + "Copy", + "PasteText", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor", + "cloudcms-link" + ], + [ + "Table", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" + ] + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP Notice", + "element": "div", + "attributes": { + "class": "notice" + } + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Image Library", + } + }, + "cloudcms-link": { + "linkPickerType": "file-picker", + "hideUploadButton": true, + "linkPickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "../Document Library", + } + } + }; + let config2 = { "height": 50, "toolbar": [ [ @@ -17,6 +130,311 @@ define(function (require, exports, module) { ] ] }; + let config3 = { + "height": 100, + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } + }; + + let config4 = { + "toolbar": [ + [ + "Link", + "Unlink" + ] + ] + }; + let config5 = { + "height": 100, + "toolbar": [ + [ + "Format", + "-", + "Italic", + "SpecialChar", + "Subscript", + "Superscript", + "-", + "Link", + "Unlink", + "-", + "ShowBlocks" + ] + ], + "removeButtons": null + }; + let config6 = { + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink", + "Anchor" + ], + [ + "Table", + "HorizontalRule", + "SpecialChar" + ], + [ + "Maximize", + "ShowBlocks", + "Source", + "Preview" + ], + [ + "Italic", + "Strike", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "NumberedList", + "BulletedList", + "-", + "Outdent", + "Indent", + "Blockquote", + "-", + "JustifyLeft", + "JustifyCenter", + "JustifyRight", + "JustifyBlock" + ], + [ + "Format", + "Styles" + ], + [ + "cloudcms-image" + ], + [ + "cloudcms-iframe" + ] + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ], + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } + }; + let config7 = { + "height": 100, + "toolbar": [ + [ + "Cut", + "Copy", + "Paste", + "-", + "Undo", + "Redo" + ], + [ + "Link", + "Unlink" + ], + [ + "Italic", + "BulletedList", + "Strike", + "Subscript", + "Superscript", + "SpecialChar", + "-", + "RemoveFormat", + "-", + "ShowBlocks" + ], + [ + "Format", + "Styles" + ] + ], + "removeButtons": null, + "stylesSet": [{ + "name": "Paragraph", + "element": "p" + }, + { + "name": "OUP unbulleted list", + "element": "ul", + "attributes": { + "class": "unbulleted" + } + }, + { + "name": "OUP horizontal list", + "element": "ul", + "attributes": { + "class": "inline" + } + }, + { + "name": "OUP floatLeft Image", + "element": "img", + "attributes": { + "class": "floatLeft" + } + }, + { + "name": "OUP floatRight Image", + "element": "img", + "attributes": { + "class": "floatRight" + } + } + ] + }; + let config8 = { + "toolbar": [ + [ + "Format" + ], + [ + "SpecialChar", + "Subscript", + "Superscript" + ], + [ + "Link", + "Unlink" + ], + [ + "cloudcms-image" + ] + ], + "removeButtons": null, + "cloudcms-image": { + "imagePickerType": "file-picker", + "hideUploadButton": true, + "imagePickerConfig": { + "rootContainerPath": "../../..", + "initialContainerPath": "./" + }, + "uploadPath": null + } + }; Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( /** @@ -24,513 +442,17 @@ define(function (require, exports, module) { */ { toolbarOptions: { - "config1": { - "toolbar": [ - [ - "Cut", - "Copy", - "PasteText", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor", - "cloudcms-link" - ], - [ - "Table", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" - ] - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP Notice", - "element": "div", - "attributes": { - "class": "notice" - } - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library", - } - }, - "cloudcms-link": { - "linkPickerType": "file-picker", - "hideUploadButton": true, - "linkPickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "../Document Library", - } - } - }, - "config2": { - "height": 50, - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, + "config1": config1, + "config2": config2, "config21": config2, - "config3": { - "height": 100, - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink" - ], - [ - "Italic", - "BulletedList", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat", - "-", - "ShowBlocks" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - } - }, - "config31": { - "height": 100, - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink" - ], - [ - "Italic", - "BulletedList", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat", - "-", - "ShowBlocks" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - } - }, - "config4": { - "toolbar": [ - [ - "Link", - "Unlink" - ] - ] - }, - "config5": { - "height": 100, - "toolbar": [ - [ - "Format", - "-", - "Italic", - "SpecialChar", - "Subscript", - "Superscript", - "-", - "Link", - "Unlink", - "-", - "ShowBlocks" - ] - ], - "removeButtons": null - }, - "config6": { - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink", - "Anchor" - ], - [ - "Table", - "HorizontalRule", - "SpecialChar" - ], - [ - "Maximize", - "ShowBlocks", - "Source", - "Preview" - ], - [ - "Italic", - "Strike", - "Subscript", - "Superscript", - "-", - "RemoveFormat" - ], - [ - "NumberedList", - "BulletedList", - "-", - "Outdent", - "Indent", - "Blockquote", - "-", - "JustifyLeft", - "JustifyCenter", - "JustifyRight", - "JustifyBlock" - ], - [ - "Format", - "Styles" - ], - [ - "cloudcms-image" - ], - [ - "cloudcms-iframe" - ] - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ], - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - } - }, - "config7": { - "height": 100, - "toolbar": [ - [ - "Cut", - "Copy", - "Paste", - "-", - "Undo", - "Redo" - ], - [ - "Link", - "Unlink" - ], - [ - "Italic", - "BulletedList", - "Strike", - "Subscript", - "Superscript", - "SpecialChar", - "-", - "RemoveFormat", - "-", - "ShowBlocks" - ], - [ - "Format", - "Styles" - ] - ], - "removeButtons": null, - "stylesSet": [{ - "name": "Paragraph", - "element": "p" - }, - { - "name": "OUP unbulleted list", - "element": "ul", - "attributes": { - "class": "unbulleted" - } - }, - { - "name": "OUP horizontal list", - "element": "ul", - "attributes": { - "class": "inline" - } - }, - { - "name": "OUP floatLeft Image", - "element": "img", - "attributes": { - "class": "floatLeft" - } - }, - { - "name": "OUP floatRight Image", - "element": "img", - "attributes": { - "class": "floatRight" - } - } - ] - }, - "config8": { - "toolbar": [ - [ - "Format" - ], - [ - "SpecialChar", - "Subscript", - "Superscript" - ], - [ - "Link", - "Unlink" - ], - [ - "cloudcms-image" - ] - ], - "removeButtons": null, - "cloudcms-image": { - "imagePickerType": "file-picker", - "hideUploadButton": true, - "imagePickerConfig": { - "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null - } - } + "config3": config3, + "config31": config3, + "config4": config4, + "config5": config5, + "config6": config6, + "config7": config7, + "config8": config8 + }, /** From 224aadc264b81843da16fe571cfdd266f330c0c8 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 10:17:39 +0000 Subject: [PATCH 157/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 4d9a8dd..8cc403b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -447,6 +447,7 @@ define(function (require, exports, module) { "config21": config2, "config3": config3, "config31": config3, + "config32": config3, "config4": config4, "config5": config5, "config6": config6, @@ -530,6 +531,18 @@ define(function (require, exports, module) { } } + if (type && (type == "config32")) { + this.options.ckeditor.wordcount = { + showParagraphs: true, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 170, + } + } + } // TODO: REMOVE BUTTON BASED ON USER TEAM From a74d3f5d61445d79ed0fdb1ba3849f3fc6872314 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 10:27:35 +0000 Subject: [PATCH 158/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 8cc403b..b3d88e6 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -443,8 +443,28 @@ define(function (require, exports, module) { { toolbarOptions: { "config1": config1, - "config2": config2, - "config21": config2, + "config2": { + "height": 50, + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + }, + "config21": { + "height": 50, + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript" + ] + ] + }, "config3": config3, "config31": config3, "config32": config3, From 693b069290f0a4bb0ac9f4e26dee7fc89e0225ea Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 10:32:14 +0000 Subject: [PATCH 159/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index b3d88e6..d183d31 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -443,28 +443,8 @@ define(function (require, exports, module) { { toolbarOptions: { "config1": config1, - "config2": { - "height": 50, - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, - "config21": { - "height": 50, - "toolbar": [ - [ - "SpecialChar", - "Italic", - "Subscript", - "Superscript" - ] - ] - }, + "config2": Object.create(config2), + "config21": Object.create(config2), "config3": config3, "config31": config3, "config32": config3, From e715f9f9581ceb9d092cb5b54477179feb5e88c9 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 8 Feb 2019 10:33:44 +0000 Subject: [PATCH 160/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d183d31..c93a01b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -442,17 +442,17 @@ define(function (require, exports, module) { */ { toolbarOptions: { - "config1": config1, + "config1": Object.create(config1), "config2": Object.create(config2), "config21": Object.create(config2), - "config3": config3, - "config31": config3, - "config32": config3, - "config4": config4, - "config5": config5, - "config6": config6, - "config7": config7, - "config8": config8 + "config3": Object.create(config3), + "config31": Object.create(config3), + "config32": Object.create(config3), + "config4": Object.create(config4), + "config5": Object.create(config5), + "config6": Object.create(config6), + "config7": Object.create(config7), + "config8": Object.create(config8) }, From aa599af5a52300c9c0e06f6b70b0b5ba7fc3e2cd Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 12 Feb 2019 12:56:59 +0000 Subject: [PATCH 161/195] GCMS-1431 --- plugins/ckeditor/wordcount/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ckeditor/wordcount/plugin.js b/plugins/ckeditor/wordcount/plugin.js index a8fa2ec..f4499f8 100644 --- a/plugins/ckeditor/wordcount/plugin.js +++ b/plugins/ckeditor/wordcount/plugin.js @@ -274,7 +274,7 @@ CKEDITOR.plugins.add("wordcount", limitRestoredNotified = false; if (config.hardLimit) { - editorInstance.execCommand('undo'); + // editorInstance.execCommand('undo'); } if (!notify) { From 82a0b9175db22e8879ddad55b01ad20e8854db41 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 12 Feb 2019 12:59:58 +0000 Subject: [PATCH 162/195] GCMS-1431 --- plugins/ckeditor/wordcount/plugin.js | 120 +++++++++++++-------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/plugins/ckeditor/wordcount/plugin.js b/plugins/ckeditor/wordcount/plugin.js index f4499f8..d1bc54e 100644 --- a/plugins/ckeditor/wordcount/plugin.js +++ b/plugins/ckeditor/wordcount/plugin.js @@ -513,66 +513,66 @@ CKEDITOR.plugins.add("wordcount", function(event) { if (config.maxWordCount > 0 || config.maxCharCount > 0 || config.maxParagraphs > 0) { - // Check if pasted content is above the limits - var wordCount = -1, - charCount = -1, - paragraphs = -1; - - // BeforeGetData and getData events are fired when calling - // getData(). We can prevent this by passing true as an - // argument to getData(). This allows us to fire the events - // manually with additional event data: firedBy. This additional - // data helps differentiate calls to getData() made by - // wordCount plugin from calls made by other plugins/code. - event.editor.fire("beforeGetData", { firedBy: "wordCount.onPaste" }, event.editor); - var text = event.editor.getData(true); - event.editor.fire("getData", { dataValue: text, firedBy: "wordCount.onPaste" }, event.editor); - - text += event.data.dataValue; - - if (config.showCharCount) { - charCount = countCharacters(text); - } - - if (config.showWordCount) { - wordCount = countWords(text); - } - - if (config.showParagraphs) { - paragraphs = countParagraphs(text); - } - - - // Instantiate the notification when needed and only have one instance - if (notification === null) { - notification = new CKEDITOR.plugins.notification(event.editor, - { - message: event.editor.lang.wordcount.pasteWarning, - type: 'warning', - duration: config.pasteWarningDuration - }); - } - - if (config.maxCharCount > 0 && charCount > config.maxCharCount && config.hardLimit) { - if (!notification.isVisible()) { - notification.show(); - } - event.cancel(); - } - - if (config.maxWordCount > 0 && wordCount > config.maxWordCount && config.hardLimit) { - if (!notification.isVisible()) { - notification.show(); - } - event.cancel(); - } - - if (config.maxParagraphs > 0 && paragraphs > config.maxParagraphs && config.hardLimit) { - if (!notification.isVisible()) { - notification.show(); - } - event.cancel(); - } + // // Check if pasted content is above the limits + // var wordCount = -1, + // charCount = -1, + // paragraphs = -1; + + // // BeforeGetData and getData events are fired when calling + // // getData(). We can prevent this by passing true as an + // // argument to getData(). This allows us to fire the events + // // manually with additional event data: firedBy. This additional + // // data helps differentiate calls to getData() made by + // // wordCount plugin from calls made by other plugins/code. + // event.editor.fire("beforeGetData", { firedBy: "wordCount.onPaste" }, event.editor); + // var text = event.editor.getData(true); + // event.editor.fire("getData", { dataValue: text, firedBy: "wordCount.onPaste" }, event.editor); + + // text += event.data.dataValue; + + // if (config.showCharCount) { + // charCount = countCharacters(text); + // } + + // if (config.showWordCount) { + // wordCount = countWords(text); + // } + + // if (config.showParagraphs) { + // paragraphs = countParagraphs(text); + // } + + + // // Instantiate the notification when needed and only have one instance + // if (notification === null) { + // notification = new CKEDITOR.plugins.notification(event.editor, + // { + // message: event.editor.lang.wordcount.pasteWarning, + // type: 'warning', + // duration: config.pasteWarningDuration + // }); + // } + + // if (config.maxCharCount > 0 && charCount > config.maxCharCount && config.hardLimit) { + // if (!notification.isVisible()) { + // notification.show(); + // } + // event.cancel(); + // } + + // if (config.maxWordCount > 0 && wordCount > config.maxWordCount && config.hardLimit) { + // if (!notification.isVisible()) { + // notification.show(); + // } + // event.cancel(); + // } + + // if (config.maxParagraphs > 0 && paragraphs > config.maxParagraphs && config.hardLimit) { + // if (!notification.isVisible()) { + // notification.show(); + // } + // event.cancel(); + // } } }, editor, From 4a92226f1d882e242584adfb8e96e6d30947fa89 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 21 Feb 2019 07:53:52 +0000 Subject: [PATCH 163/195] GCMS-1674 --- fields/oup-ckeditor-field.js | 25 +++-- plugins/ckeditor/wordcount/plugin.js | 134 ++++++++++++++------------- 2 files changed, 89 insertions(+), 70 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c93a01b..14d6dfe 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -450,6 +450,7 @@ define(function (require, exports, module) { "config32": Object.create(config3), "config4": Object.create(config4), "config5": Object.create(config5), + "config51": Object.create(config5), "config6": Object.create(config6), "config7": Object.create(config7), "config8": Object.create(config8) @@ -521,25 +522,37 @@ define(function (require, exports, module) { if (type && (type == "config31")) { this.options.ckeditor.wordcount = { - showParagraphs: true, + showParagraphs: false, showWordCount: true, showCharCount: true, countSpacesAsChars: true, countHTML: false, - maxWordCount: -1, - maxCharCount: 110, + warnOnLimitOnly: true, + maxCharCount: 110 } } if (type && (type == "config32")) { this.options.ckeditor.wordcount = { - showParagraphs: true, + showParagraphs: false, showWordCount: true, showCharCount: true, countSpacesAsChars: true, countHTML: false, - maxWordCount: -1, - maxCharCount: 170, + warnOnLimitOnly: true, + maxCharCount: 170 + } + } + + if (type && (type == "config51")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + warnOnLimitOnly: true, + maxCharCount: 400 } } diff --git a/plugins/ckeditor/wordcount/plugin.js b/plugins/ckeditor/wordcount/plugin.js index d1bc54e..394680f 100644 --- a/plugins/ckeditor/wordcount/plugin.js +++ b/plugins/ckeditor/wordcount/plugin.js @@ -6,7 +6,7 @@ CKEDITOR.plugins.add("wordcount", { lang: "ar,bg,ca,cs,da,de,el,en,es,eu,fa,fi,fr,he,hr,hu,it,ko,ja,nl,no,pl,pt,pt-br,ru,sk,sv,tr,uk,zh-cn,zh,ro", // %REMOVE_LINE_CORE% - version: "1.17.4", + version: "1.17.5", requires: 'htmlwriter,notification,undo', bbcodePluginLoaded: false, onLoad: function() { @@ -66,6 +66,7 @@ CKEDITOR.plugins.add("wordcount", countHTML: false, countLineBreaks: false, hardLimit: true, + warnOnLimitOnly: false, //MAXLENGTH Properties maxWordCount: -1, @@ -273,8 +274,10 @@ CKEDITOR.plugins.add("wordcount", limitReachedNotified = true; limitRestoredNotified = false; - if (config.hardLimit) { - // editorInstance.execCommand('undo'); + if (!config.warnOnLimitOnly) { + if (config.hardLimit) { + editorInstance.execCommand('undo'); + } } if (!notify) { @@ -286,7 +289,10 @@ CKEDITOR.plugins.add("wordcount", function limitRestored(editorInstance) { limitRestoredNotified = true; limitReachedNotified = false; - editorInstance.fire('saveSnapshot'); + + if (!config.warnOnLimitOnly) { + editorInstance.fire('saveSnapshot'); + } counterElement(editorInstance).className = "cke_path_item"; } @@ -513,66 +519,66 @@ CKEDITOR.plugins.add("wordcount", function(event) { if (config.maxWordCount > 0 || config.maxCharCount > 0 || config.maxParagraphs > 0) { - // // Check if pasted content is above the limits - // var wordCount = -1, - // charCount = -1, - // paragraphs = -1; - - // // BeforeGetData and getData events are fired when calling - // // getData(). We can prevent this by passing true as an - // // argument to getData(). This allows us to fire the events - // // manually with additional event data: firedBy. This additional - // // data helps differentiate calls to getData() made by - // // wordCount plugin from calls made by other plugins/code. - // event.editor.fire("beforeGetData", { firedBy: "wordCount.onPaste" }, event.editor); - // var text = event.editor.getData(true); - // event.editor.fire("getData", { dataValue: text, firedBy: "wordCount.onPaste" }, event.editor); - - // text += event.data.dataValue; - - // if (config.showCharCount) { - // charCount = countCharacters(text); - // } - - // if (config.showWordCount) { - // wordCount = countWords(text); - // } - - // if (config.showParagraphs) { - // paragraphs = countParagraphs(text); - // } - - - // // Instantiate the notification when needed and only have one instance - // if (notification === null) { - // notification = new CKEDITOR.plugins.notification(event.editor, - // { - // message: event.editor.lang.wordcount.pasteWarning, - // type: 'warning', - // duration: config.pasteWarningDuration - // }); - // } - - // if (config.maxCharCount > 0 && charCount > config.maxCharCount && config.hardLimit) { - // if (!notification.isVisible()) { - // notification.show(); - // } - // event.cancel(); - // } - - // if (config.maxWordCount > 0 && wordCount > config.maxWordCount && config.hardLimit) { - // if (!notification.isVisible()) { - // notification.show(); - // } - // event.cancel(); - // } - - // if (config.maxParagraphs > 0 && paragraphs > config.maxParagraphs && config.hardLimit) { - // if (!notification.isVisible()) { - // notification.show(); - // } - // event.cancel(); - // } + // Check if pasted content is above the limits + var wordCount = -1, + charCount = -1, + paragraphs = -1; + + // BeforeGetData and getData events are fired when calling + // getData(). We can prevent this by passing true as an + // argument to getData(). This allows us to fire the events + // manually with additional event data: firedBy. This additional + // data helps differentiate calls to getData() made by + // wordCount plugin from calls made by other plugins/code. + event.editor.fire("beforeGetData", { firedBy: "wordCount.onPaste" }, event.editor); + var text = event.editor.getData(true); + event.editor.fire("getData", { dataValue: text, firedBy: "wordCount.onPaste" }, event.editor); + + text += event.data.dataValue; + + if (config.showCharCount) { + charCount = countCharacters(text); + } + + if (config.showWordCount) { + wordCount = countWords(text); + } + + if (config.showParagraphs) { + paragraphs = countParagraphs(text); + } + + + // Instantiate the notification when needed and only have one instance + if (notification === null) { + notification = new CKEDITOR.plugins.notification(event.editor, + { + message: event.editor.lang.wordcount.pasteWarning, + type: 'warning', + duration: config.pasteWarningDuration + }); + } + + if (config.maxCharCount > 0 && charCount > config.maxCharCount && config.hardLimit) { + if (!notification.isVisible()) { + notification.show(); + } + event.cancel(); + } + + if (config.maxWordCount > 0 && wordCount > config.maxWordCount && config.hardLimit) { + if (!notification.isVisible()) { + notification.show(); + } + event.cancel(); + } + + if (config.maxParagraphs > 0 && paragraphs > config.maxParagraphs && config.hardLimit) { + if (!notification.isVisible()) { + notification.show(); + } + event.cancel(); + } } }, editor, From 984e1fcc50a0d531817fdcee52f7b3a784646fb7 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 21 Feb 2019 09:02:52 +0000 Subject: [PATCH 164/195] GCMS-1674 --- plugins/ckeditor/wordcount/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ckeditor/wordcount/plugin.js b/plugins/ckeditor/wordcount/plugin.js index 394680f..634b9fd 100644 --- a/plugins/ckeditor/wordcount/plugin.js +++ b/plugins/ckeditor/wordcount/plugin.js @@ -517,7 +517,7 @@ CKEDITOR.plugins.add("wordcount", editor.on("paste", function(event) { - if (config.maxWordCount > 0 || config.maxCharCount > 0 || config.maxParagraphs > 0) { + if (!config.warnOnLimitOnly && (config.maxWordCount > 0 || config.maxCharCount > 0 || config.maxParagraphs > 0)) { // Check if pasted content is above the limits var wordCount = -1, From 03e9e957992fb78fee3adeee0520c9be3d4d64e1 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 27 Feb 2019 12:41:03 +0530 Subject: [PATCH 165/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 87 +++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 14d6dfe..47757c8 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -444,15 +444,20 @@ define(function (require, exports, module) { toolbarOptions: { "config1": Object.create(config1), "config2": Object.create(config2), - "config21": Object.create(config2), + "configMainTitleHP": Object.create(config2), + "configHeadCB": Object.create(config2), + "configSecTitleHP": Object.create(config2), "config3": Object.create(config3), - "config31": Object.create(config3), - "config32": Object.create(config3), + "configSecDescHP": Object.create(config3), + "configMainDescHP": Object.create(config3), + "configSnippet": Object.create(config3), "config4": Object.create(config4), "config5": Object.create(config5), - "config51": Object.create(config5), + "configSpnsrSP": Object.create(config5), "config6": Object.create(config6), "config7": Object.create(config7), + "configAbtPnlTxtHP": Object.create(config7), + "configTextCB": Object.create(config7), "config8": Object.create(config8) }, @@ -508,7 +513,7 @@ define(function (require, exports, module) { this.options.ckeditor.format_tags = 'p;h2;h3;pre'; } - if (type && (type == "config21")) { + if (type && (type == "configMainTitleHP")) { this.options.ckeditor.wordcount = { showParagraphs: false, showWordCount: true, @@ -516,11 +521,47 @@ define(function (require, exports, module) { countSpacesAsChars: true, countHTML: false, maxWordCount: -1, - maxCharCount: 40, + maxCharCount: 32, } } - if (type && (type == "config31")) { + if (type && (type == "configHeadCB")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 150, + } + } + + if (type && (type == "configSecTitleHP")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 36, + } + } + + if (type && (type == "configSecDescHP")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + warnOnLimitOnly: true, + maxCharCount: 100 + } + } + + if (type && (type == "configMainDescHP")) { this.options.ckeditor.wordcount = { showParagraphs: false, showWordCount: true, @@ -528,11 +569,11 @@ define(function (require, exports, module) { countSpacesAsChars: true, countHTML: false, warnOnLimitOnly: true, - maxCharCount: 110 + maxCharCount: 150 } } - if (type && (type == "config32")) { + if (type && (type == "configSnippet")) { this.options.ckeditor.wordcount = { showParagraphs: false, showWordCount: true, @@ -540,11 +581,11 @@ define(function (require, exports, module) { countSpacesAsChars: true, countHTML: false, warnOnLimitOnly: true, - maxCharCount: 170 + maxCharCount: 325 } } - if (type && (type == "config51")) { + if (type && (type == "configSpnsrSP")) { this.options.ckeditor.wordcount = { showParagraphs: false, showWordCount: true, @@ -556,6 +597,30 @@ define(function (require, exports, module) { } } + if (type && (type == "configAbtPnlTxtHP")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + warnOnLimitOnly: true, + maxCharCount: 210 + } + } + + if (type && (type == "configTextCB")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + warnOnLimitOnly: true, + maxCharCount: 450 + } + } + } // TODO: REMOVE BUTTON BASED ON USER TEAM From b6fa5d9bdc4d0eeee0e2699cfdc8d8425c0e01bb Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 28 Feb 2019 15:01:19 +0530 Subject: [PATCH 166/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 47757c8..d5acb09 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -444,6 +444,8 @@ define(function (require, exports, module) { toolbarOptions: { "config1": Object.create(config1), "config2": Object.create(config2), + "configAbtPnlHead": Object.create(config2), + "configAbtPnlLinkText": Object.create(config2), "configMainTitleHP": Object.create(config2), "configHeadCB": Object.create(config2), "configSecTitleHP": Object.create(config2), @@ -525,6 +527,32 @@ define(function (require, exports, module) { } } + if (type && (type == "configAbtPnlHead")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 35, + } + this.options.ckeditor.autoParagraph = false; + } + + if (type && (type == "configAbtPnlLinkText")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + maxWordCount: -1, + maxCharCount: 35, + } + this.options.ckeditor.autoParagraph = false; + } + if (type && (type == "configHeadCB")) { this.options.ckeditor.wordcount = { showParagraphs: false, From df39582092babb5781fca9872c625574db11e9fe Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 4 Mar 2019 13:13:41 +0530 Subject: [PATCH 167/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index d5acb09..69fc3a4 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -449,10 +449,10 @@ define(function (require, exports, module) { "configMainTitleHP": Object.create(config2), "configHeadCB": Object.create(config2), "configSecTitleHP": Object.create(config2), + "configSnippet": Object.create(config2), "config3": Object.create(config3), "configSecDescHP": Object.create(config3), "configMainDescHP": Object.create(config3), - "configSnippet": Object.create(config3), "config4": Object.create(config4), "config5": Object.create(config5), "configSpnsrSP": Object.create(config5), From b933543afd4f5f5d53d0af42cee3570c18bb96af Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 6 Mar 2019 15:24:35 +0530 Subject: [PATCH 168/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 69fc3a4..c5683b3 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -611,6 +611,7 @@ define(function (require, exports, module) { warnOnLimitOnly: true, maxCharCount: 325 } + this.options.ckeditor.height = "100"; } if (type && (type == "configSpnsrSP")) { From 7d6380334c251e7fdf6c7f94019ed9652be8ff07 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 6 Mar 2019 15:26:07 +0530 Subject: [PATCH 169/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c5683b3..62cfcc2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -611,7 +611,7 @@ define(function (require, exports, module) { warnOnLimitOnly: true, maxCharCount: 325 } - this.options.ckeditor.height = "100"; + this.options.ckeditor.height = "120"; } if (type && (type == "configSpnsrSP")) { From 5b0f3fd95058dfb4660279dbe14e72bfbb425581 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 6 Mar 2019 15:33:03 +0530 Subject: [PATCH 170/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 62cfcc2..25c9f7e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -522,7 +522,7 @@ define(function (require, exports, module) { showCharCount: true, countSpacesAsChars: true, countHTML: false, - maxWordCount: -1, + warnOnLimitOnly: true, maxCharCount: 32, } } @@ -534,7 +534,7 @@ define(function (require, exports, module) { showCharCount: true, countSpacesAsChars: true, countHTML: false, - maxWordCount: -1, + warnOnLimitOnly: true, maxCharCount: 35, } this.options.ckeditor.autoParagraph = false; @@ -547,7 +547,7 @@ define(function (require, exports, module) { showCharCount: true, countSpacesAsChars: true, countHTML: false, - maxWordCount: -1, + warnOnLimitOnly: true, maxCharCount: 35, } this.options.ckeditor.autoParagraph = false; @@ -560,7 +560,7 @@ define(function (require, exports, module) { showCharCount: true, countSpacesAsChars: true, countHTML: false, - maxWordCount: -1, + warnOnLimitOnly: true, maxCharCount: 150, } } @@ -572,7 +572,7 @@ define(function (require, exports, module) { showCharCount: true, countSpacesAsChars: true, countHTML: false, - maxWordCount: -1, + warnOnLimitOnly: true, maxCharCount: 36, } } @@ -636,6 +636,7 @@ define(function (require, exports, module) { warnOnLimitOnly: true, maxCharCount: 210 } + this.options.ckeditor.autoParagraph = false; } if (type && (type == "configTextCB")) { From 7105e9c509f0925cdacabe2e216f34484ea82bff Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 6 Mar 2019 15:40:51 +0530 Subject: [PATCH 171/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 25c9f7e..fa5e920 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -611,7 +611,7 @@ define(function (require, exports, module) { warnOnLimitOnly: true, maxCharCount: 325 } - this.options.ckeditor.height = "120"; + this.options.ckeditor.height = "100"; } if (type && (type == "configSpnsrSP")) { From 9a6b28d508f20ac25fdabbbcf3dfdade47df8346 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 6 Mar 2019 15:50:42 +0530 Subject: [PATCH 172/195] GCMS-1415 --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index fa5e920..0955231 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -538,6 +538,7 @@ define(function (require, exports, module) { maxCharCount: 35, } this.options.ckeditor.autoParagraph = false; + this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; } if (type && (type == "configAbtPnlLinkText")) { @@ -551,6 +552,7 @@ define(function (require, exports, module) { maxCharCount: 35, } this.options.ckeditor.autoParagraph = false; + this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; } if (type && (type == "configHeadCB")) { @@ -636,7 +638,6 @@ define(function (require, exports, module) { warnOnLimitOnly: true, maxCharCount: 210 } - this.options.ckeditor.autoParagraph = false; } if (type && (type == "configTextCB")) { From 08f951ff04391011aa3b262159c33b3d8217b76b Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 25 Apr 2019 15:07:22 +0530 Subject: [PATCH 173/195] GCMS-1671 --- fields/oup-ckeditor-field.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 0955231..c3959c2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -764,6 +764,11 @@ define(function (require, exports, module) { dialogDefinition.getContents("info").get("type")["items"].pop(); dialogDefinition.getContents("info").get("type")["items"].pop(); } + if (dialogName == "image") { + dialogDefinition.getContents("info").elements.pop(); + dialogDefinition.getContents("info").elements.pop(); + dialogDefinition.contents.pop(); + } }); Alpaca.registerFieldClass("oup-ckeditor", Alpaca.Fields.OUPCKEditorField); From fd114e2fd4269de21845e7e314790d073e0ae2e6 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 29 Apr 2019 14:57:55 +0530 Subject: [PATCH 174/195] GCMS-1621 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c3959c2..46fd2ed 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -5,7 +5,7 @@ define(function (require, exports, module) { var OneTeam = require("oneteam"); window.CKEDITOR.config.disableNativeSpellChecker = false; - + window.CKEDITOR.config.forcePasteAsPlainText = true; let config1 = { "toolbar": [ From 62f1779a68dbe0d55cbcadeb1d26e01090af59a5 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 10 May 2019 10:18:46 +0100 Subject: [PATCH 175/195] RemoveFormat added config2/5/8 --- fields/oup-ckeditor-field.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 46fd2ed..676b085 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -126,7 +126,9 @@ define(function (require, exports, module) { "SpecialChar", "Italic", "Subscript", - "Superscript" + "Superscript", + "-", + "RemoveFormat" ] ] }; @@ -229,6 +231,8 @@ define(function (require, exports, module) { "Subscript", "Superscript", "-", + "RemoveFormat", + "-", "Link", "Unlink", "-", @@ -414,7 +418,9 @@ define(function (require, exports, module) { [ "SpecialChar", "Subscript", - "Superscript" + "Superscript", + "-", + "RemoveFormat", ], [ "Link", From b1315037b60183c9d2257a76fc38f11bbb940d5a Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 24 May 2019 14:42:59 +0530 Subject: [PATCH 176/195] GCMS-1660 --- fields/oup-ckeditor-field.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 676b085..6728ee3 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -755,16 +755,15 @@ define(function (require, exports, module) { ev.editor.getCommand('table').allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; if (dialogName == "table" || dialogName == "tableProperties") { var infoTab = dialogDefinition.getContents("info"); - infoTab.get("txtWidth")["default"] = ""; - infoTab.get("txtCellSpace")["default"] = ""; - infoTab.get("txtCellPad")["default"] = ""; - infoTab.get("txtBorder")["default"] = ""; + dialogDefinition.getContents("info").elements.pop(); + dialogDefinition.getContents("info").elements.pop(); + dialogDefinition.contents.pop(); + infoTab.elements[0].children[0].children.pop(); + infoTab.elements[0].children[0].children.pop(); + infoTab.elements[0].children.pop(); infoTab.get("selHeaders")["items"].pop(); infoTab.get("selHeaders")["items"].pop(); - - var advancedTab = dialogDefinition.getContents("advanced"); - advancedTab.get("advCSSClasses")["default"] = ""; } if (dialogName == "bulletedListStyle") { dialogDefinition.getContents("info").get("type")["items"].pop(); From 706174cb9ce4f5c220838060e1e28696c4a693f4 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 24 May 2019 16:19:29 +0530 Subject: [PATCH 177/195] Revert "GCMS-1660" This reverts commit b1315037b60183c9d2257a76fc38f11bbb940d5a. --- fields/oup-ckeditor-field.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 6728ee3..676b085 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -755,15 +755,16 @@ define(function (require, exports, module) { ev.editor.getCommand('table').allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; if (dialogName == "table" || dialogName == "tableProperties") { var infoTab = dialogDefinition.getContents("info"); - dialogDefinition.getContents("info").elements.pop(); - dialogDefinition.getContents("info").elements.pop(); - dialogDefinition.contents.pop(); - infoTab.elements[0].children[0].children.pop(); - infoTab.elements[0].children[0].children.pop(); - infoTab.elements[0].children.pop(); + infoTab.get("txtWidth")["default"] = ""; + infoTab.get("txtCellSpace")["default"] = ""; + infoTab.get("txtCellPad")["default"] = ""; + infoTab.get("txtBorder")["default"] = ""; infoTab.get("selHeaders")["items"].pop(); infoTab.get("selHeaders")["items"].pop(); + + var advancedTab = dialogDefinition.getContents("advanced"); + advancedTab.get("advCSSClasses")["default"] = ""; } if (dialogName == "bulletedListStyle") { dialogDefinition.getContents("info").get("type")["items"].pop(); From aa0ff8d419ced0e3dc519883b7fa04076a244de4 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 29 May 2019 19:10:46 +0530 Subject: [PATCH 178/195] Revert "Revert "GCMS-1660"" This reverts commit 706174cb9ce4f5c220838060e1e28696c4a693f4. --- fields/oup-ckeditor-field.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 676b085..6728ee3 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -755,16 +755,15 @@ define(function (require, exports, module) { ev.editor.getCommand('table').allowedContent = "table{width,height}[align,border,cellpadding,cellspacing,summary];caption tbody thead tfoot;th td tr;table[id,dir](*){*}"; if (dialogName == "table" || dialogName == "tableProperties") { var infoTab = dialogDefinition.getContents("info"); - infoTab.get("txtWidth")["default"] = ""; - infoTab.get("txtCellSpace")["default"] = ""; - infoTab.get("txtCellPad")["default"] = ""; - infoTab.get("txtBorder")["default"] = ""; + dialogDefinition.getContents("info").elements.pop(); + dialogDefinition.getContents("info").elements.pop(); + dialogDefinition.contents.pop(); + infoTab.elements[0].children[0].children.pop(); + infoTab.elements[0].children[0].children.pop(); + infoTab.elements[0].children.pop(); infoTab.get("selHeaders")["items"].pop(); infoTab.get("selHeaders")["items"].pop(); - - var advancedTab = dialogDefinition.getContents("advanced"); - advancedTab.get("advCSSClasses")["default"] = ""; } if (dialogName == "bulletedListStyle") { dialogDefinition.getContents("info").get("type")["items"].pop(); From bae762bd9a254a3fbef7cc10b744acbb8a3c31a9 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 30 May 2019 11:42:27 +0100 Subject: [PATCH 179/195] GCMS-1660 --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 6728ee3..cda7f60 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -757,7 +757,8 @@ define(function (require, exports, module) { var infoTab = dialogDefinition.getContents("info"); dialogDefinition.getContents("info").elements.pop(); dialogDefinition.getContents("info").elements.pop(); - dialogDefinition.contents.pop(); + dialogDefinition.contents[1].elements[0].children.shift(); + dialogDefinition.contents[1].elements[0].children[0].children.shift(); infoTab.elements[0].children[0].children.pop(); infoTab.elements[0].children[0].children.pop(); infoTab.elements[0].children.pop(); From a015043dfc3c5dfb62ebe77efa0080836774475e Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 23 Jul 2019 20:09:06 +0530 Subject: [PATCH 180/195] GCMS-1458 --- fields/oup-ckeditor-field.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index cda7f60..6f324f6 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -7,6 +7,9 @@ define(function (require, exports, module) { window.CKEDITOR.config.disableNativeSpellChecker = false; window.CKEDITOR.config.forcePasteAsPlainText = true; + let configempty = { + "height": 50 + }; let config1 = { "toolbar": [ [ @@ -456,6 +459,7 @@ define(function (require, exports, module) { "configHeadCB": Object.create(config2), "configSecTitleHP": Object.create(config2), "configSnippet": Object.create(config2), + "configSnippetPodHP": Object.create(configempty), "config3": Object.create(config3), "configSecDescHP": Object.create(config3), "configMainDescHP": Object.create(config3), @@ -622,6 +626,21 @@ define(function (require, exports, module) { this.options.ckeditor.height = "100"; } + if (type && (type == "configSnippetPodHP")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + warnOnLimitOnly: true, + maxCharCount: 250 + } + this.options.ckeditor.height = "100"; + this.options.ckeditor.autoParagraph = false; + this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; + } + if (type && (type == "configSpnsrSP")) { this.options.ckeditor.wordcount = { showParagraphs: false, From 1c86d8e632d28aa471db6613cd3497cdebb7811e Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 23 Jul 2019 20:10:40 +0530 Subject: [PATCH 181/195] GCMS-1458 --- fields/oup-ckeditor-field.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 6f324f6..c2fc4bb 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -8,7 +8,8 @@ define(function (require, exports, module) { window.CKEDITOR.config.forcePasteAsPlainText = true; let configempty = { - "height": 50 + "height": 50, + "toolbar": [] }; let config1 = { "toolbar": [ From f342b483e32078c84cccdfc287f7cb2e9c0935e0 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Fri, 26 Jul 2019 13:11:35 +0530 Subject: [PATCH 182/195] GCMS-1458 --- fields/oup-ckeditor-field.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c2fc4bb..1c958ce 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -461,6 +461,7 @@ define(function (require, exports, module) { "configSecTitleHP": Object.create(config2), "configSnippet": Object.create(config2), "configSnippetPodHP": Object.create(configempty), + "configSnippetPodHP2": Object.create(config2), "config3": Object.create(config3), "configSecDescHP": Object.create(config3), "configMainDescHP": Object.create(config3), @@ -642,6 +643,21 @@ define(function (require, exports, module) { this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; } + if (type && (type == "configSnippetPodHP2")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + warnOnLimitOnly: true, + maxCharCount: 250 + } + this.options.ckeditor.height = "100"; + this.options.ckeditor.autoParagraph = false; + this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; + } + if (type && (type == "configSpnsrSP")) { this.options.ckeditor.wordcount = { showParagraphs: false, From 2b5c089a498f56fd404dc78269ae098ec682272d Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 21 Aug 2019 16:42:41 +0530 Subject: [PATCH 183/195] GCMS-1672 --- fields/oup-ckeditor-field.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 1c958ce..c192d9c 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -136,6 +136,23 @@ define(function (require, exports, module) { ] ] }; + let config9 = { + "height": 50, + "toolbar": [ + [ + "SpecialChar", + "Italic", + "Subscript", + "Superscript", + "-", + "RemoveFormat" + ], + [ + "Link", + "Unlink" + ] + ] + }; let config3 = { "height": 100, "toolbar": [ @@ -462,6 +479,7 @@ define(function (require, exports, module) { "configSnippet": Object.create(config2), "configSnippetPodHP": Object.create(configempty), "configSnippetPodHP2": Object.create(config2), + "configSnippetPodHP3": Object.create(config9), "config3": Object.create(config3), "configSecDescHP": Object.create(config3), "configMainDescHP": Object.create(config3), From f704380d311a1112bbf288e79c200ccb1f42e886 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 21 Aug 2019 16:44:19 +0530 Subject: [PATCH 184/195] GCMS-1672 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index c192d9c..041a6d2 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -633,7 +633,7 @@ define(function (require, exports, module) { } } - if (type && (type == "configSnippet")) { + if (type && (type == "configSnippet" || type == "configSnippetPodHP3")) { this.options.ckeditor.wordcount = { showParagraphs: false, showWordCount: true, From fa02b350aabf96608aad4c13eb932a627b6a3966 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 9 Sep 2019 19:04:47 +0530 Subject: [PATCH 185/195] GCMS-1802 amendments for Podcast in Home page --- fields/oup-ckeditor-field.js | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 041a6d2..7dca1e3 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -478,8 +478,6 @@ define(function (require, exports, module) { "configSecTitleHP": Object.create(config2), "configSnippet": Object.create(config2), "configSnippetPodHP": Object.create(configempty), - "configSnippetPodHP2": Object.create(config2), - "configSnippetPodHP3": Object.create(config9), "config3": Object.create(config3), "configSecDescHP": Object.create(config3), "configMainDescHP": Object.create(config3), @@ -633,7 +631,7 @@ define(function (require, exports, module) { } } - if (type && (type == "configSnippet" || type == "configSnippetPodHP3")) { + if (type && (type == "configSnippet")) { this.options.ckeditor.wordcount = { showParagraphs: false, showWordCount: true, @@ -654,22 +652,7 @@ define(function (require, exports, module) { countSpacesAsChars: true, countHTML: false, warnOnLimitOnly: true, - maxCharCount: 250 - } - this.options.ckeditor.height = "100"; - this.options.ckeditor.autoParagraph = false; - this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; - } - - if (type && (type == "configSnippetPodHP2")) { - this.options.ckeditor.wordcount = { - showParagraphs: false, - showWordCount: true, - showCharCount: true, - countSpacesAsChars: true, - countHTML: false, - warnOnLimitOnly: true, - maxCharCount: 250 + maxCharCount: 325 } this.options.ckeditor.height = "100"; this.options.ckeditor.autoParagraph = false; From baa3b70907ec875fc6755d4a32e209a3dba8f37d Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Mon, 9 Sep 2019 19:07:44 +0530 Subject: [PATCH 186/195] GCMS-1802 Updates considering backword compatibility --- fields/oup-ckeditor-field.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 7dca1e3..83a157e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -478,6 +478,8 @@ define(function (require, exports, module) { "configSecTitleHP": Object.create(config2), "configSnippet": Object.create(config2), "configSnippetPodHP": Object.create(configempty), + "configSnippetPodHP2": Object.create(config2), + "configSnippetPodHP3": Object.create(config9), "config3": Object.create(config3), "configSecDescHP": Object.create(config3), "configMainDescHP": Object.create(config3), @@ -631,7 +633,7 @@ define(function (require, exports, module) { } } - if (type && (type == "configSnippet")) { + if (type && (type == "configSnippet" || type == "configSnippetPodHP3")) { this.options.ckeditor.wordcount = { showParagraphs: false, showWordCount: true, @@ -659,6 +661,21 @@ define(function (require, exports, module) { this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; } + if (type && (type == "configSnippetPodHP2")) { + this.options.ckeditor.wordcount = { + showParagraphs: false, + showWordCount: true, + showCharCount: true, + countSpacesAsChars: true, + countHTML: false, + warnOnLimitOnly: true, + maxCharCount: 325 + } + this.options.ckeditor.height = "100"; + this.options.ckeditor.autoParagraph = false; + this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; + } + if (type && (type == "configSpnsrSP")) { this.options.ckeditor.wordcount = { showParagraphs: false, From c44f5e6d73425428b3443291b6bced4f50b873b5 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 9 Oct 2019 17:26:40 +0530 Subject: [PATCH 187/195] GCMS-1383 CSS level customization --- fields/oup-ckeditor-field.js | 4 ++++ resources/ckeditorCstm.css | 15 +++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 resources/ckeditorCstm.css diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 83a157e..50564b3 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -4,8 +4,12 @@ define(function (require, exports, module) { var Alpaca = require("alpaca"); var OneTeam = require("oneteam"); + var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; + var cssPath = "../../../" + moduleId + "/oup-ckeditor/resources/ckeditorCstm.css"; + window.CKEDITOR.config.disableNativeSpellChecker = false; window.CKEDITOR.config.forcePasteAsPlainText = true; + window.CKEDITOR.config.contentsCss = cssPath; let configempty = { "height": 50, diff --git a/resources/ckeditorCstm.css b/resources/ckeditorCstm.css new file mode 100644 index 0000000..2cd5351 --- /dev/null +++ b/resources/ckeditorCstm.css @@ -0,0 +1,15 @@ +h1 { + color: navy; +} + +h2 { + color: red; +} + +h3 { + color: green; +} + +h3 { + color: yellow; +} \ No newline at end of file From fa019c22ea32b3b1dcec3ec795180e6a75343cab Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 9 Oct 2019 17:32:56 +0530 Subject: [PATCH 188/195] GCMS-1383 --- resources/ckeditorCstm.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/ckeditorCstm.css b/resources/ckeditorCstm.css index 2cd5351..9fb9cef 100644 --- a/resources/ckeditorCstm.css +++ b/resources/ckeditorCstm.css @@ -1,5 +1,5 @@ h1 { - color: navy; + color: purple; } h2 { @@ -10,6 +10,6 @@ h3 { color: green; } -h3 { - color: yellow; +h4 { + color: navy; } \ No newline at end of file From d5db5a1eeb0c43c2d78489b3e2fb484ff52cbf54 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 9 Oct 2019 17:41:07 +0530 Subject: [PATCH 189/195] Revert GCMS-1383 Revert --- fields/oup-ckeditor-field.js | 4 ---- resources/ckeditorCstm.css | 15 --------------- 2 files changed, 19 deletions(-) delete mode 100644 resources/ckeditorCstm.css diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 50564b3..83a157e 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -4,12 +4,8 @@ define(function (require, exports, module) { var Alpaca = require("alpaca"); var OneTeam = require("oneteam"); - var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; - var cssPath = "../../../" + moduleId + "/oup-ckeditor/resources/ckeditorCstm.css"; - window.CKEDITOR.config.disableNativeSpellChecker = false; window.CKEDITOR.config.forcePasteAsPlainText = true; - window.CKEDITOR.config.contentsCss = cssPath; let configempty = { "height": 50, diff --git a/resources/ckeditorCstm.css b/resources/ckeditorCstm.css deleted file mode 100644 index 9fb9cef..0000000 --- a/resources/ckeditorCstm.css +++ /dev/null @@ -1,15 +0,0 @@ -h1 { - color: purple; -} - -h2 { - color: red; -} - -h3 { - color: green; -} - -h4 { - color: navy; -} \ No newline at end of file From 1e63b3fac344d8bd46a1f5b9a18e1ac486dcfa43 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Wed, 16 Oct 2019 16:02:32 +0530 Subject: [PATCH 190/195] GCMS-1861 --- fields/oup-ckeditor-field.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 83a157e..dbfdb7c 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -462,6 +462,7 @@ define(function (require, exports, module) { "uploadPath": null } }; + Alpaca.Fields.OUPCKEditorField = Alpaca.Fields.CKEditorField.extend( /** @@ -478,6 +479,7 @@ define(function (require, exports, module) { "configSecTitleHP": Object.create(config2), "configSnippet": Object.create(config2), "configSnippetPodHP": Object.create(configempty), + "configSnippetBR": Object.create(configempty), "configSnippetPodHP2": Object.create(config2), "configSnippetPodHP3": Object.create(config9), "config3": Object.create(config3), @@ -646,6 +648,13 @@ define(function (require, exports, module) { this.options.ckeditor.height = "100"; } + if (type && (type == "configSnippetBR")) { + this.options.ckeditor.height = "100"; + this.options.ckeditor.autoParagraph = false; + this.options.ckeditor.enterMode = window.CKEDITOR.ENTER_BR ; + } + + if (type && (type == "configSnippetPodHP")) { this.options.ckeditor.wordcount = { showParagraphs: false, From 14a3a1f825aec1ae94d2ebe43a7438e828a3159f Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Tue, 5 Nov 2019 14:57:11 +0530 Subject: [PATCH 191/195] GCMS-1855 Content saved at a sub-sub-folder level --- fields/oup-ckeditor-field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index dbfdb7c..2fbbb86 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -111,7 +111,7 @@ define(function (require, exports, module) { "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Image Library", + "initialContainerPath": "./", } }, "cloudcms-link": { @@ -119,7 +119,7 @@ define(function (require, exports, module) { "hideUploadButton": true, "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "../Document Library", + "initialContainerPath": "./", } } }; From 49e984ee67550085c9782cfc8c9f3871ad0edbc2 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 7 Nov 2019 16:21:19 +0530 Subject: [PATCH 192/195] GCMS-1855 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 2fbbb86..9d09da4 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -107,7 +107,7 @@ define(function (require, exports, module) { ], "cloudcms-image": { - "imagePickerType": "file-picker", + "imagePickerType": "oup-file-picker", "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", From 31b783fff2ee017286e1428076519dda979d0134 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 7 Nov 2019 16:28:29 +0530 Subject: [PATCH 193/195] GCMS-1855 --- fields/oup-ckeditor-field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index 9d09da4..caf2f8b 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -111,7 +111,7 @@ define(function (require, exports, module) { "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "./", + "initialContainerPath": "{{nearestJournalFolderPath}}/Image Library", } }, "cloudcms-link": { From 950c56194a6f1028e6d3a5d27a8f64e0cb1b9790 Mon Sep 17 00:00:00 2001 From: ChintanMangukiya-OUP Date: Thu, 7 Nov 2019 16:33:30 +0530 Subject: [PATCH 194/195] GCMS-1855 --- fields/oup-ckeditor-field.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index caf2f8b..af87904 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -115,11 +115,11 @@ define(function (require, exports, module) { } }, "cloudcms-link": { - "linkPickerType": "file-picker", + "linkPickerType": "oup-file-picker", "hideUploadButton": true, "linkPickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "./", + "initialContainerPath": "{{nearestJournalFolderPath}}/Row Containers, Content Blocks, and Widgets", } } }; @@ -223,13 +223,12 @@ define(function (require, exports, module) { } ], "cloudcms-image": { - "imagePickerType": "file-picker", + "imagePickerType": "oup-file-picker", "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null + "initialContainerPath": "{{nearestJournalFolderPath}}/Image Library", + } } }; @@ -355,13 +354,12 @@ define(function (require, exports, module) { } ], "cloudcms-image": { - "imagePickerType": "file-picker", + "imagePickerType": "oup-file-picker", "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null + "initialContainerPath": "{{nearestJournalFolderPath}}/Image Library", + } } }; let config7 = { @@ -453,13 +451,12 @@ define(function (require, exports, module) { ], "removeButtons": null, "cloudcms-image": { - "imagePickerType": "file-picker", + "imagePickerType": "oup-file-picker", "hideUploadButton": true, "imagePickerConfig": { "rootContainerPath": "../../..", - "initialContainerPath": "./" - }, - "uploadPath": null + "initialContainerPath": "{{nearestJournalFolderPath}}/Image Library", + } } }; From 7f7be820102a6c3f7a6b6d72d3ad9bc2b4d3eb2b Mon Sep 17 00:00:00 2001 From: MounikaB-01 <57654332+MounikaB-01@users.noreply.github.com> Date: Wed, 27 Nov 2019 15:51:45 +0530 Subject: [PATCH 195/195] GCMS-1858 Added Accessibility and balloon pugins in oup ckeditor --- fields/oup-ckeditor-field.js | 3 +- index.js | 2 + plugins/ckeditor/a11ychecker/CHANGES.md | 48 + plugins/ckeditor/a11ychecker/LICENSE.md | 340 + plugins/ckeditor/a11ychecker/README.md | 36 + .../a11ychecker/icons/a11ychecker.png | Bin 0 -> 3245 bytes .../a11ychecker/icons/hidpi/a11ychecker.png | Bin 0 -> 4029 bytes plugins/ckeditor/a11ychecker/index.js | 7 + plugins/ckeditor/a11ychecker/lang/de.js | 28 + plugins/ckeditor/a11ychecker/lang/en.js | 28 + plugins/ckeditor/a11ychecker/lang/nl.js | 28 + plugins/ckeditor/a11ychecker/lang/pt-br.js | 28 + .../libs/quail/guidelines/508.json | 52 + .../libs/quail/guidelines/508.tests.json | 1 + .../libs/quail/guidelines/wcag.json | 2323 ++++ .../libs/quail/guidelines/wcag.tests.json | 1 + .../a11ychecker/libs/quail/preconditions.json | 90 + .../a11ychecker/libs/quail/quail.jquery.js | 10600 ++++++++++++++++ .../libs/quail/quail.jquery.min.js | 6 + .../a11ychecker/libs/quail/tests.json | 8447 ++++++++++++ .../a11ychecker/libs/quail/tests.min.json | 1 + .../a11ychecker/libs/quail/wcag2.json | 615 + plugins/ckeditor/a11ychecker/plugin.js | 178 + .../quickfix/de/AddTableCaption.js | 75 + .../a11ychecker/quickfix/de/AnchorsMerge.js | 80 + .../quickfix/de/AttributeRename.js | 86 + .../quickfix/de/AttributeRenameDefault.js | 40 + .../a11ychecker/quickfix/de/DateUnfold.js | 133 + .../a11ychecker/quickfix/de/ElementRemove.js | 43 + .../a11ychecker/quickfix/de/ElementReplace.js | 59 + .../a11ychecker/quickfix/de/ImgAlt.js | 89 + .../a11ychecker/quickfix/de/ImgAltNonEmpty.js | 44 + .../quickfix/de/LocalizedRepository.js | 174 + .../quickfix/de/ParagraphToHeader.js | 168 + .../a11ychecker/quickfix/de/QuickFix.js | 90 + .../a11ychecker/quickfix/de/TableHeaders.js | 98 + .../quickfix/en/AddTableCaption.js | 75 + .../a11ychecker/quickfix/en/AnchorsMerge.js | 80 + .../quickfix/en/AttributeRename.js | 86 + .../quickfix/en/AttributeRenameDefault.js | 40 + .../a11ychecker/quickfix/en/DateUnfold.js | 133 + .../a11ychecker/quickfix/en/ElementRemove.js | 43 + .../a11ychecker/quickfix/en/ElementReplace.js | 59 + .../a11ychecker/quickfix/en/ImgAlt.js | 89 + .../a11ychecker/quickfix/en/ImgAltNonEmpty.js | 44 + .../quickfix/en/LocalizedRepository.js | 174 + .../quickfix/en/ParagraphToHeader.js | 168 + .../a11ychecker/quickfix/en/QuickFix.js | 90 + .../a11ychecker/quickfix/en/TableHeaders.js | 98 + .../quickfix/nl/AddTableCaption.js | 75 + .../a11ychecker/quickfix/nl/AnchorsMerge.js | 80 + .../quickfix/nl/AttributeRename.js | 86 + .../quickfix/nl/AttributeRenameDefault.js | 40 + .../a11ychecker/quickfix/nl/DateUnfold.js | 133 + .../a11ychecker/quickfix/nl/ElementRemove.js | 43 + .../a11ychecker/quickfix/nl/ElementReplace.js | 59 + .../a11ychecker/quickfix/nl/ImgAlt.js | 89 + .../a11ychecker/quickfix/nl/ImgAltNonEmpty.js | 44 + .../quickfix/nl/LocalizedRepository.js | 174 + .../quickfix/nl/ParagraphToHeader.js | 168 + .../a11ychecker/quickfix/nl/QuickFix.js | 90 + .../a11ychecker/quickfix/nl/TableHeaders.js | 98 + .../quickfix/pt-br/AddTableCaption.js | 75 + .../quickfix/pt-br/AnchorsMerge.js | 80 + .../quickfix/pt-br/AttributeRename.js | 86 + .../quickfix/pt-br/AttributeRenameDefault.js | 40 + .../a11ychecker/quickfix/pt-br/DateUnfold.js | 133 + .../quickfix/pt-br/ElementRemove.js | 43 + .../quickfix/pt-br/ElementReplace.js | 59 + .../a11ychecker/quickfix/pt-br/ImgAlt.js | 89 + .../quickfix/pt-br/ImgAltNonEmpty.js | 44 + .../quickfix/pt-br/LocalizedRepository.js | 174 + .../quickfix/pt-br/ParagraphToHeader.js | 168 + .../a11ychecker/quickfix/pt-br/QuickFix.js | 90 + .../quickfix/pt-br/TableHeaders.js | 98 + .../skins/moono-lisa/a11ychecker.css | 4 + .../a11ychecker/skins/moono-lisa/contents.css | 4 + .../a11ychecker/skins/moono/a11ychecker.css | 4 + .../a11ychecker/skins/moono/contents.css | 4 + .../a11ychecker/skins/moono/images/reload.png | Bin 0 -> 603 bytes plugins/ckeditor/balloonpanel/index.js | 6 + plugins/ckeditor/balloonpanel/plugin.js | 919 ++ .../balloonpanel/skins/kama/balloonpanel.css | 220 + .../skins/moono-lisa/balloonpanel.css | 223 + .../skins/moono-lisa/images/close.png | Bin 0 -> 615 bytes .../skins/moono-lisa/images/hidpi/close.png | Bin 0 -> 1259 bytes .../balloonpanel/skins/moono/balloonpanel.css | 237 + .../balloonpanel/skins/moono/images/close.png | Bin 0 -> 824 bytes .../skins/moono/images/hidpi/close.png | Bin 0 -> 1271 bytes plugins/ckeditor/helper.js | 20 + 90 files changed, 29218 insertions(+), 1 deletion(-) create mode 100644 plugins/ckeditor/a11ychecker/CHANGES.md create mode 100644 plugins/ckeditor/a11ychecker/LICENSE.md create mode 100644 plugins/ckeditor/a11ychecker/README.md create mode 100644 plugins/ckeditor/a11ychecker/icons/a11ychecker.png create mode 100644 plugins/ckeditor/a11ychecker/icons/hidpi/a11ychecker.png create mode 100644 plugins/ckeditor/a11ychecker/index.js create mode 100644 plugins/ckeditor/a11ychecker/lang/de.js create mode 100644 plugins/ckeditor/a11ychecker/lang/en.js create mode 100644 plugins/ckeditor/a11ychecker/lang/nl.js create mode 100644 plugins/ckeditor/a11ychecker/lang/pt-br.js create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.json create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.tests.json create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.json create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.tests.json create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/preconditions.json create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.js create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.min.js create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/tests.json create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/tests.min.json create mode 100644 plugins/ckeditor/a11ychecker/libs/quail/wcag2.json create mode 100644 plugins/ckeditor/a11ychecker/plugin.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/AddTableCaption.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/AnchorsMerge.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/AttributeRename.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/AttributeRenameDefault.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/DateUnfold.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/ElementRemove.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/ElementReplace.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/ImgAlt.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/ImgAltNonEmpty.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/LocalizedRepository.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/ParagraphToHeader.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/QuickFix.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/de/TableHeaders.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/AddTableCaption.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/AnchorsMerge.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/AttributeRename.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/AttributeRenameDefault.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/DateUnfold.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/ElementRemove.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/ElementReplace.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/ImgAlt.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/ImgAltNonEmpty.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/LocalizedRepository.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/ParagraphToHeader.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/QuickFix.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/en/TableHeaders.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/AddTableCaption.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/AnchorsMerge.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRename.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRenameDefault.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/DateUnfold.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/ElementRemove.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/ElementReplace.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/ImgAlt.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/ImgAltNonEmpty.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/LocalizedRepository.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/ParagraphToHeader.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/QuickFix.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/nl/TableHeaders.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/AddTableCaption.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/AnchorsMerge.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRename.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRenameDefault.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/DateUnfold.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementRemove.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementReplace.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAlt.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAltNonEmpty.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/LocalizedRepository.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/ParagraphToHeader.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/QuickFix.js create mode 100644 plugins/ckeditor/a11ychecker/quickfix/pt-br/TableHeaders.js create mode 100644 plugins/ckeditor/a11ychecker/skins/moono-lisa/a11ychecker.css create mode 100644 plugins/ckeditor/a11ychecker/skins/moono-lisa/contents.css create mode 100644 plugins/ckeditor/a11ychecker/skins/moono/a11ychecker.css create mode 100644 plugins/ckeditor/a11ychecker/skins/moono/contents.css create mode 100644 plugins/ckeditor/a11ychecker/skins/moono/images/reload.png create mode 100644 plugins/ckeditor/balloonpanel/index.js create mode 100644 plugins/ckeditor/balloonpanel/plugin.js create mode 100644 plugins/ckeditor/balloonpanel/skins/kama/balloonpanel.css create mode 100644 plugins/ckeditor/balloonpanel/skins/moono-lisa/balloonpanel.css create mode 100644 plugins/ckeditor/balloonpanel/skins/moono-lisa/images/close.png create mode 100644 plugins/ckeditor/balloonpanel/skins/moono-lisa/images/hidpi/close.png create mode 100644 plugins/ckeditor/balloonpanel/skins/moono/balloonpanel.css create mode 100644 plugins/ckeditor/balloonpanel/skins/moono/images/close.png create mode 100644 plugins/ckeditor/balloonpanel/skins/moono/images/hidpi/close.png diff --git a/fields/oup-ckeditor-field.js b/fields/oup-ckeditor-field.js index af87904..4bad13f 100644 --- a/fields/oup-ckeditor-field.js +++ b/fields/oup-ckeditor-field.js @@ -132,7 +132,8 @@ define(function (require, exports, module) { "Subscript", "Superscript", "-", - "RemoveFormat" + "RemoveFormat", + "a11ychecker" ] ] }; diff --git a/index.js b/index.js index 393ccd4..feda5cc 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ define(function (require, exports, module) { require("./plugins/ckeditor/wordcount/index.js"); + require("./plugins/ckeditor/a11ychecker/index.js"); + require("./plugins/ckeditor/balloonpanel/index.js"); require("./fields/oup-ckeditor-field.js"); }); \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/CHANGES.md b/plugins/ckeditor/a11ychecker/CHANGES.md new file mode 100644 index 0000000..7d8b070 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/CHANGES.md @@ -0,0 +1,48 @@ +CKEditor Accessibility Checker Changelog +======================================== + +[CKEditor Accessibility Checker](https://ckeditor.com/ckeditor-4/accessibility-checker/) + +Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + +## Version 1.1.1 + +New Features: + +* [#240](https://github.com/cksource/ckeditor-plugin-a11ychecker/pull/240): Added Brazilian Portuguese localization. Thanks to [Guilherme Alves](https://github.com/gsag)! +* [#247](https://github.com/cksource/ckeditor-plugin-a11ychecker/issues/247): Introduced the `process` event in the `Engine` type, allowing for adding custom issue types. + +Fixed Issues: + +* [#233](https://github.com/cksource/ckeditor-plugin-a11ychecker/issues/233): Fixed: Balloon classes are localized, causing issues of a different testability to look the same. + +## Version 1.1.0 + +New Features: + +* [#228](https://github.com/cksource/ckeditor-plugin-a11ychecker/issues/228): Added compatibility with the new default `moono-lisa` skin. + +Fixed Issues: + +* [#201](https://github.com/cksource/ckeditor-plugin-a11ychecker/issues/201): `imgShouldNotHaveTitle` Quick Fix - if the image has both `title` and `alt` attributes, the `alt` will be used as the default value. + +* [#185](https://github.com/cksource/ckeditor-plugin-a11ychecker/issues/185): Added a more verbose error message when jQuery is missing in the built version of . +* [#185](https://github.com/cksource/ckeditor-plugin-a11ychecker/issues/185): Added a more verbose error message when jQuery is missing in the built version of Accessibility Checker. + +## Version 1.0 + +A brand new CKEditor plugin that lets you inspect accessibility level of content created in CKEditor and immediately solve any issues that are found. For an overview of its features, see the [Accessibility Checker website](https://ckeditor.com/ckeditor-4/accessibility-checker/). + +It is built upon three key elements: + +* User Interface optimized for quick problem solving. +* Flexibility allowing you to use the accessibility checking engine of your choice (default: [Quail](http://quailjs.org/)). +* Quick Fix feature letting you fix common problems fully automatically! + +All of this comes bundled with a tight integration with CKEditor. + +The first release includes three language versions: + +* English +* German (provided by Sebastian Peilicke of [Sopra Steria GmbH](http://www.soprasteria.de/de)) +* Dutch (provided by [Dutch Government](https://www.government.nl/)) diff --git a/plugins/ckeditor/a11ychecker/LICENSE.md b/plugins/ckeditor/a11ychecker/LICENSE.md new file mode 100644 index 0000000..e384f9f --- /dev/null +++ b/plugins/ckeditor/a11ychecker/LICENSE.md @@ -0,0 +1,340 @@ +Software License Agreement +========================== + +CKEditor Accessibility Checker Plugin +Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + +License under the terms of the GNU General Public License Version 2 or later (the "GPL") (Appendix A). + +This license is not valid for integration within commercial +applications or applications whose license is not compatible +with the GPL. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Sources of Intellectual Property Included +----------------------------------------- + +Where not otherwise indicated, all program code is authored by +CKSource engineers and consists of CKSource-owned intellectual +property. In some specific instances, the program will incorporate work +done by developers outside of CKSource with their express permission. + +Third-party software included: + +- QUAIL, Copyright (c) 2013 Kevin Miller (libs/quail)
+ https://github.com/quailjs/quail
+ License under the terms of the MIT license. +  + +Parts of code taken from the following libraries are included in CKEditor Accessibility Checker: + +- CKEditor, Copyright (c) 2003 CKSource - Frederico Knabben (quickfix/TableHeaders.js)
+ https://ckeditor.com/
+ License under the terms of the GNU General Public License Version 2 license. + +Trademarks +---------- + +CKEditor, CKEditor Accessibility Checker and CKEditor A11y Checker are +trademarks of CKSource - Frederico Knabben. All other brand and product names +are trademarks, registered trademarks or service marks of their respective +holders. + +--- + +Appendix A: The GPL License +--------------------------- + +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software-to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +GNU GENERAL PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS diff --git a/plugins/ckeditor/a11ychecker/README.md b/plugins/ckeditor/a11ychecker/README.md new file mode 100644 index 0000000..b7981fd --- /dev/null +++ b/plugins/ckeditor/a11ychecker/README.md @@ -0,0 +1,36 @@ +CKEditor Accessibility Checker +================================================== + +# Overview + +This package contains the distribution version of Accessibility Checker. + +## Requirements + +* CKEditor **4.3.0** or later. +* [Balloon Panel](https://ckeditor.com/cke4/addon/balloonpanel) plugin for CKEditor. +* jQuery **1.x** or later in order to run [Quail](http://quailjs.org/). + +## Browser Support + +Accessibility Checker has [the same browser compatibility as CKEditor](https://docs.ckeditor.com/ckeditor4/latest/guide/dev_browsers.html), with the following exceptions: + +* Internet Explorer 8 is not supported. +* Internet Explorer 9 Quirks Mode is not supported. + +## Installation + +The recommended way to install Accessibility Checker is through [online builder](https://ckeditor.com/cke4/builder). + +Select Accessibility Checker from the list of Available Plugins and add it to your editor build - CKBuilder will automatically resolve the dependencies for you and include all necessary plugins in your configuration. + +### Limitations + +**Running on local filesystem:** You cannot run Accessibility Checker on a local filesystem, since Quail uses an `XMLHttpRequest` for fetching its resources. This is not allowed when working with the `file://` scheme. + +## License + +Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved.
+Licensed under the terms of the [GNU General Public License Version 2 or later (the "GPL")](http://www.gnu.org/licenses/gpl.html). + +See LICENSE.md for more information. diff --git a/plugins/ckeditor/a11ychecker/icons/a11ychecker.png b/plugins/ckeditor/a11ychecker/icons/a11ychecker.png new file mode 100644 index 0000000000000000000000000000000000000000..a518e16e3abc54ae510b86fba771bf29e554f38b GIT binary patch literal 3245 zcmV;e3{vxnP)X+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@R5%fBQ$4F1K@`2~?xJAf7b+Hl zipDR*XlH37HbSt#3u^sL&c9Yq7rp;&)_aDyn*YT zIdjgLduK-?^N?g&e&G1Vv6Uq0AM$Pez*NahHv?f7e0I59Z&In0&F}Y<)oP{F=|s!r zl6t+~aktz3(KPKHn~ZfM`8EkFit;^^$pj09g6ecSsaC5|qtPI{-A=RFj3SYU!j*V8 zo6U#o_4>%VztCDZB%96lHJwg7_+$&8K!{SQM2w5f0&pprOj^JLA39)cD%ZH|kNJEa zWQFQ0NEun+*no&>Lg5TG4j9B^Hp+ z=QD6jrqe0z<>zEFd8;sqD@zi}>~uON;%kd)wJJ>e{eBYr3YV(#1=8hm5hd%Q!C;_I zO*q<8RTV9&!ocBh5ceZ4EFO=GUiotxVoZQz=ofrA90)qJXvyVrr}cU*T0j+O1^Iqp zeZL1jbl_MjmCEn=d`{={S%_hvf3{dGCVX@Z89eZz69)Bq{Zk+ipz(NYoUR-2;}Hh; z6@Ujmbi#n&!DGAK{>3j$nBW8my4|iY)H861!2=&U;f^1^obYfsTxYXc&Fl4Q{Mxi= zG^!PgMUk-vj(zaJH^lk`AeY{4Hk(?xT-H1ukH)8nj5ToVzbd{n!GGaB#}|$lj?*n; f4O~2TosawX+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@Q!}jReh`9>+0?okcUxF&Dxavlv|W5Vyr^;W4x?#UOB3K ztNbXGtx;Yne^K@x$k^h8?=LBk#-d-xCY8h3piyu6fe z-@eJg!GY}T?8y1~x$D=*XXU?@=d2@{OqKwBW6BX63JVLRxw%=&%F1Z|k`50KWo2bW zPESwGc3hQFW!~UDx|;y)DdnL+AmFvMv;>|%e=e%W9Ydjz^!N8mM@NV2TU=a}xw$#H zzP@&Lp7#T1V`D>>mX@^X;XI=}tb8wt3>W3+{|9EoXzgM-rB z+bh}G*^cY#>Lep0L)O;T#K18vDk_qal9IsQ-kx`JbJL_t#0_tuO{`Yb_{{zQmLY$YlRtT?d- zNP<$yZEbCpyu7@40{$SD$il*c`{vjn-`@JL{qW&~TwGkB zQbPZsFnhLFx4|HJg+`_$O$Q9l%*+%@5?xojOAZ^7G<(yLa76_>xFd&&7SeM0AxtlOVBTgW0OD! z1M2DNahT}AuCA_+)F-cpw~#IXV@9>LwF0Ljvquse46wj7zo71B^Dke%bZtt^X7A75 zk}@Zg*cf5s_qiL#$`>p!&5v@3j*N^PakyxG+JWK54Zwogj2PsEd-m*^v*Y0hw}h>a zLx|X5fTiZ#oE-(V#BTM1`OoB}yABAMvRG2h$Q&&>Ya^`>Y0NgizP|3vzyJ$O^Ro@% zt={uKMw;A?Ch+QciVIVxVyXS4j%NOp&^-?nj+4Ca>6MWPax*_MjaxfqoXoD z&Taa~{{Y{b>p8TwwdG`Wb#>A<*Ku|^`#)gjV`F20k1M}+PN^OW9#EcE_H>4xE*h4( zWBqoX{+M_Q?CAmHb#``odKq;(eT-pa_V^MbM#M~*Y^of1eI&t~@bs_8o0ypJw07fx z4Iq`o79V_LM68tHDF@VvD>S7MW`RYcQJJ5gm*wSU+27xHtYhw$!-1whG1Ax9Cv9zQ z5<6hsui4#ij?aThlgL(nrTj(tp@c0y3D@sbpef&g-$8>>u5HTPZ literal 0 HcmV?d00001 diff --git a/plugins/ckeditor/a11ychecker/index.js b/plugins/ckeditor/a11ychecker/index.js new file mode 100644 index 0000000..40030cd --- /dev/null +++ b/plugins/ckeditor/a11ychecker/index.js @@ -0,0 +1,7 @@ +define(function (require, exports, module) { + + var Helper = require("../helper.js"); + var moduleId = module.uri.match(/^.+(_modules[^\/]+)\/.*/)[1]; + Helper.registerPlugin1("a11ychecker", moduleId); + +}); \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/lang/de.js b/plugins/ckeditor/a11ychecker/lang/de.js new file mode 100644 index 0000000..f96429b --- /dev/null +++ b/plugins/ckeditor/a11ychecker/lang/de.js @@ -0,0 +1,28 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'a11ychecker', 'de', { + toolbar: 'Barrierefreiheit prüfen', + closeBtn: 'Schließen', + testability: { + '0': 'Hinweis', + '0.5': 'Warnung', + '1': 'Fehler' + }, + ignoreBtn: 'Ignorieren', + ignoreBtnTitle: 'Problem ignorieren', + stopIgnoreBtn: 'Nicht mehr ignorieren', + listeningInfo: 'Wartet auf manuelle Inhaltsänderung. Nach Abschluss der Änderungen bitte Erneut prüfen anklicken.', + listeningCheckAgain: 'Erneut prüfen', + balloonLabel: 'Barrierefreiheitsprüfung', + navigationNext: 'Vorwärts', + navigationNextTitle: 'Nächstes Problem', + navigationPrev: 'Zurück', + navigationPrevTitle: 'Vorheriges Problem', + quickFixButton: 'Schnellbehebung', + quickFixButtonTitle: 'Schnellbehebung dieses Problems', + navigationCounter: 'Problem {current} von {total} ({testability})', + noIssuesMessage: 'Dieses Dokument enthält keine Barrierefreiheitsprobleme.' +} ); \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/lang/en.js b/plugins/ckeditor/a11ychecker/lang/en.js new file mode 100644 index 0000000..3e4675b --- /dev/null +++ b/plugins/ckeditor/a11ychecker/lang/en.js @@ -0,0 +1,28 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'a11ychecker', 'en', { + toolbar: 'Check Accessibility', + closeBtn: 'Close', + testability: { + '0': 'notice', + '0.5': 'warning', + '1': 'error' + }, + ignoreBtn: 'Ignore', + ignoreBtnTitle: 'Ignore this issue', + stopIgnoreBtn: 'Stop ignoring', + listeningInfo: 'Waiting for manual content changes. When done, click Check again below.', + listeningCheckAgain: 'Check again', + balloonLabel: 'Accessibility Checker', + navigationNext: 'Next', + navigationNextTitle: 'Next issue', + navigationPrev: 'Previous', + navigationPrevTitle: 'Previous issue', + quickFixButton: 'Quick fix', + quickFixButtonTitle: 'Quick fix this issue', + navigationCounter: 'Issue {current} of {total} ({testability})', + noIssuesMessage: 'The document does not contain any accessibility issues.' +} ); diff --git a/plugins/ckeditor/a11ychecker/lang/nl.js b/plugins/ckeditor/a11ychecker/lang/nl.js new file mode 100644 index 0000000..0b502e1 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/lang/nl.js @@ -0,0 +1,28 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'a11ychecker', 'nl', { + toolbar: 'Toegankelijkheid controleren', + closeBtn: 'Afsluiten', + testability: { + '0': 'opmerking', + '0.5': 'waarschuwing', + '1': 'fout' + }, + ignoreBtn: 'Negeren', + ignoreBtnTitle: 'Probleem negeren', + stopIgnoreBtn: 'Stop negeren', + listeningInfo: 'Na handmatige correcties, klik de knop Opnieuw controleren hieronder.', + listeningCheckAgain: 'Opnieuw controleren', + balloonLabel: 'Toegankelijkheidsassistent', + navigationNext: 'Volgende', + navigationNextTitle: 'Volgende probleem', + navigationPrev: 'Vorige', + navigationPrevTitle: 'Vorige probleem', + quickFixButton: 'Quick fix', + quickFixButtonTitle: 'Probleem oplossen met een quick fix', + navigationCounter: 'Probleem {current} van {total} ({testability})', + noIssuesMessage: 'Het document bevat geen toegankelijkheidsproblemen.' +} ); diff --git a/plugins/ckeditor/a11ychecker/lang/pt-br.js b/plugins/ckeditor/a11ychecker/lang/pt-br.js new file mode 100644 index 0000000..30aec52 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/lang/pt-br.js @@ -0,0 +1,28 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'a11ychecker', 'pt-br', { + toolbar: 'Verificador de Acessibilidade', + closeBtn: 'Fechar', + testability: { + '0': 'observação', + '0.5': 'alerta', + '1': 'erro' + }, + ignoreBtn: 'Ignorar', + ignoreBtnTitle: 'Ignorar este problema', + stopIgnoreBtn: 'Parar de ignorar', + listeningInfo: 'Aguardando por mudanças no conteúdo. Quando terminar, clique em Verificar novamente abaixo.', + listeningCheckAgain: 'Verificar novamente', + balloonLabel: 'Verificador de Acessibilidade', + navigationNext: 'Próximo', + navigationNextTitle: 'Próximo problema', + navigationPrev: 'Anterior', + navigationPrevTitle: 'Problema anterior', + quickFixButton: 'Reparar rapidamente', + quickFixButtonTitle: 'Reparar rapidamente este problema', + navigationCounter: 'Problema {current} de {total} ({testability})', + noIssuesMessage: 'O documento não possui nenhum problema de acessibilidade identificado.' +} ); diff --git a/plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.json b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.json new file mode 100644 index 0000000..d90414c --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.json @@ -0,0 +1,52 @@ +{ + "guidelines": { + "a": { + "title": "A text equivalent for every non-text element shall be provided (e.g., via 'alt', 'longdesc', or in element content)." + }, + "b": { + "title": "Equivalent alternatives for any multimedia presentation shall be synchronized with the presentation." + }, + "c": { + "title": "Web pages shall be designed so that all information conveyed with color is also available without color, for example from context or markup." + }, + "d": { + "title": "Documents shall be organized so they are readable without requiring an associated style sheet." + }, + "e": { + "title": "Redundant text links shall be provided for each active region of a server-side image map." + }, + "f": { + "title": "Client-side image maps shall be provided instead of server-side image maps except where the regions cannot be defined with an available geometric shape." + }, + "g": { + "title": "Row and column headers shall be identified for data tables." + }, + "h": { + "title": "Markup shall be used to associate data cells and header cells for data tables that have two or more logical levels of row or column headers." + }, + "i": { + "title": "Frames shall be titled with text that facilitates frame identification and navigation." + }, + "j": { + "title": "Pages shall be designed to avoid causing the screen to flicker with a frequency greater than 2 Hz and lower than 55 Hz." + }, + "k": { + "title": "A text-only page, with equivalent information or functionality, shall be provided to make a web site comply with the provisions of this part, when compliance cannot be accomplished in any other way. The content of the text-only page shall be updated whenever the primary page changes." + }, + "l": { + "title": "When pages utilize scripting languages to display content, or to create interface elements, the information provided by the script shall be identified with functional text that can be read by assistive technology." + }, + "m": { + "title": "When a web page requires that an applet, plug-in or other application be present on the client system to interpret page content, the page must provide a link to a plug-in or applet that complies with §1194.21(a) through (l)." + }, + "n": { + "title": "When electronic forms are designed to be completed on-line, the form shall allow people using assistive technology to access the information, field elements, and functionality required for completion and submission of the form, including all directions and cues." + }, + "o": { + "title": "A method shall be provided that permits users to skip repetitive navigation links." + }, + "p": { + "title": "When a timed response is required, the user shall be alerted and given sufficient time to indicate more time is required." + } + } +} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.tests.json b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.tests.json new file mode 100644 index 0000000..e8fe91f --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/508.tests.json @@ -0,0 +1 @@ +["aLinksToMultiMediaRequireTranscript","aLinksToSoundFilesNeedTranscripts","appletContainsTextEquivalent","appletContainsTextEquivalentInAlt","appletTextEquivalentsGetUpdated","appletUIMustBeAccessible","appletsDoNotFlicker","appletsDonotUseColorAlone","checkboxHasLabel","documentContentReadableWithoutStylesheets","fileHasLabel","imgAltIsDifferent","imgAltIsSameInText","imgAltIsTooLong","imgAltNotEmptyInAnchor","imgAltNotPlaceHolder","imgGifNoFlicker","imgHasAlt","imgMapAreasHaveDuplicateLink","imgNonDecorativeHasAlt","imgNotReferredToByColorAlone","imgWithMapHasUseMap","inputDoesNotUseColorAlone","inputImageAltIsNotFileName","inputImageAltIsNotPlaceholder","inputImageAltIsShort","inputImageHasAlt","objectDoesNotFlicker","objectDoesNotUseColorAlone","objectTextUpdatesWhenObjectChanges","passwordHasLabel","radioHasLabel","scriptInBodyMustHaveNoscript","scriptOnclickRequiresOnKeypress","scriptOndblclickRequiresOnKeypress","scriptOnmousedownRequiresOnKeypress","scriptOnmousemove","scriptOnmouseoutHasOnmouseblur","scriptOnmouseoverHasOnfocus","scriptOnmouseupHasOnkeyup","scriptUIMustBeAccessible","scriptsDoNotFlicker","scriptsDoNotUseColorAlone","skipToContentLinkProvided","tableDataShouldHaveTh","tableWithBothHeadersUseScope","videoProvidesCaptions"] \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.json b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.json new file mode 100644 index 0000000..ab5d6e2 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.json @@ -0,0 +1,2323 @@ +{ + "guidelines": { + "1.1.1": { + "id": "text-equiv-all", + "title": "Non-text Content", + "description": "All non-text content that is presented to the user has a text alternative that serves the equivalent purpose, except for the situations listed below.", + "uri": "http://www.w3.org/TR/WCAG20/#text-equiv-all", + "techniques": [ + "C9", + "C18", + "F3", + "F13", + "F20", + "F30", + "F38", + "F39", + "F65", + "F67", + "F71", + "F72", + "FLASH1", + "FLASH2", + "FLASH3", + "FLASH5", + "FLASH6", + "FLASH11", + "FLASH25", + "FLASH27", + "FLASH28", + "FLASH29", + "FLASH30", + "FLASH32", + "G68", + "G73", + "G74", + "G82", + "G92", + "G94", + "G95", + "G100", + "G143", + "G144", + "G196", + "H2", + "H24", + "H27", + "H30", + "H35", + "H36", + "H37", + "H44", + "H45", + "H46", + "H53", + "H65", + "H67", + "H86", + "SL5", + "SL8", + "SL18", + "SL19", + "SL26", + "SL30" + ] + }, + "1.2.1": { + "id": "media-equiv-av-only-alt", + "title": "Audio-only and Video-only (Prerecorded)", + "description": "For prerecorded audio-only and prerecorded video-only media, the following are true, except when the audio or video is a media alternative for text and is clearly labeled as such:", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-av-only-alt", + "techniques": [ + "F30", + "F67", + "G158", + "G159", + "G166", + "SL17" + ] + }, + "1.2.2": { + "id": "media-equiv-captions", + "title": "Captions (Prerecorded)", + "description": "Captions are provided for all prerecorded audio content in synchronized media, except when the media is a media alternative for text and is clearly labeled as such.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-captions", + "techniques": [ + "F8", + "F74", + "F75", + "FLASH9", + "G87", + "G93", + "SL16", + "SL28", + "SM11", + "SM12" + ] + }, + "1.2.3": { + "id": "media-equiv-audio-desc", + "title": "Audio Description or Media Alternative (Prerecorded)", + "description": "An alternative for time-based media or audio description of the prerecorded video content is provided for synchronized media, except when the media is a media alternative for text and is clearly labeled as such.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-audio-desc", + "techniques": [ + "FLASH26", + "G8", + "G58", + "G69", + "G78", + "G173", + "G203", + "H53", + "SL1", + "SL17", + "SM1", + "SM2", + "SM6", + "SM7" + ] + }, + "1.2.4": { + "id": "media-equiv-real-time-captions", + "title": "Captions (Live)", + "description": "Captions are provided for all live audio content in synchronized media.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-real-time-captions", + "techniques": [ + "G9", + "G87", + "G93", + "SM11", + "SM12" + ] + }, + "1.2.5": { + "id": "media-equiv-audio-desc-only", + "title": "Audio Description (Prerecorded)", + "description": "Audio description is provided for all prerecorded video content in synchronized media.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-audio-desc-only", + "techniques": [ + "FLASH26", + "G8", + "G78", + "G173", + "G203", + "SL1", + "SM1", + "SM2", + "SM6", + "SM7" + ] + }, + "1.2.6": { + "id": "media-equiv-sign", + "title": "Sign Language (Prerecorded)", + "description": "Sign language interpretation is provided for all prerecorded audio content in synchronized media.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-sign", + "techniques": [ + "G54", + "G81", + "SM13", + "SM14" + ] + }, + "1.2.7": { + "id": "media-equiv-extended-ad", + "title": "Extended Audio Description (Prerecorded)", + "description": "Where pauses in foreground audio are insufficient to allow audio descriptions to convey the sense of the video, extended audio description is provided for all prerecorded video content in synchronized media.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-extended-ad", + "techniques": [ + "G8", + "SM1", + "SM2" + ] + }, + "1.2.8": { + "id": "media-equiv-text-doc", + "title": "Media Alternative (Prerecorded)", + "description": "An alternative for time-based media is provided for all prerecorded synchronized media and for all prerecorded video-only media.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-text-doc", + "techniques": [ + "F74", + "G58", + "G69", + "G159", + "H46", + "H53", + "SL17" + ] + }, + "1.2.9": { + "id": "media-equiv-live-audio-only", + "title": "Audio-only (Live)", + "description": "An alternative for time-based media that presents equivalent information for live audio-only content is provided.", + "uri": "http://www.w3.org/TR/WCAG20/#media-equiv-live-audio-only", + "techniques": [ + "G150", + "G151", + "G157" + ] + }, + "1.3.1": { + "id": "content-structure-separation-programmatic", + "title": "Info and Relationships", + "description": "Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.", + "uri": "http://www.w3.org/TR/WCAG20/#content-structure-separation-programmatic", + "techniques": [ + "ARIA1", + "ARIA2", + "C22", + "F2", + "F17", + "F33", + "F34", + "F42", + "F43", + "F46", + "F48", + "F62", + "F68", + "F87", + "F90", + "F91", + "FLASH8", + "FLASH21", + "FLASH23", + "FLASH25", + "FLASH29", + "FLASH31", + "FLASH32", + "G115", + "G117", + "G138", + "G140", + "G141", + "G162", + "H39", + "H42", + "H43", + "H44", + "H48", + "H49", + "H51", + "H63", + "H65", + "H71", + "H73", + "H85", + "T1", + "T2", + "T3", + "SCR21", + "SL20", + "SL26" + ] + }, + "1.3.2": { + "id": "content-structure-separation-sequence", + "title": "Meaningful Sequence", + "description": "When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.", + "uri": "http://www.w3.org/TR/WCAG20/#content-structure-separation-sequence", + "techniques": [ + "C6", + "C8", + "C27", + "F1", + "F32", + "F33", + "F34", + "F49", + "FLASH15", + "G57", + "H34", + "H56", + "SL34" + ] + }, + "1.3.3": { + "id": "content-structure-separation-understanding", + "title": "Sensory Characteristics", + "description": "Instructions provided for understandingand operating content do not rely solely on sensory characteristics of components such as shape, size, visual location, orientation, or sound.", + "uri": "http://www.w3.org/TR/WCAG20/#content-structure-separation-understanding", + "techniques": [ + "F14", + "F26", + "G96" + ] + }, + "1.4.1": { + "id": "visual-audio-contrast-without-color", + "title": "Use of Color", + "description": "Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-without-color", + "techniques": [ + "C15", + "F13", + "F73", + "F81", + "G14", + "G111", + "G182", + "G183", + "H92" + ] + }, + "1.4.2": { + "id": "visual-audio-contrast-dis-audio", + "title": "Audio Control", + "description": "If any audio on a Web page plays automatically for more than 3 seconds, either a mechanism is available to pause or stop the audio, or a mechanism is available to control audio volume independently from the overall system volume level.", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-dis-audio", + "techniques": [ + "F23", + "FLASH18", + "FLASH34", + "G60", + "G170", + "G171", + "SL3", + "SL24" + ] + }, + "1.4.3": { + "id": "visual-audio-contrast-contrast", + "title": "Contrast (Minimum)", + "description": "The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, except for the following:", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast", + "techniques": [ + "F24", + "F83", + "G18", + "G145", + "G148", + "G156", + "G174", + "SL13" + ] + }, + "1.4.4": { + "id": "visual-audio-contrast-scale", + "title": "Resize text", + "description": "Except for captions and images of text, text can be resized without assistive technology up to 200 percent without loss of content or functionality.", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-scale", + "techniques": [ + "C12", + "C13", + "C14", + "C17", + "C20", + "C22", + "C28", + "F69", + "F80", + "G142", + "G146", + "G178", + "G179", + "SCR34", + "SL22", + "SL23" + ] + }, + "1.4.5": { + "id": "visual-audio-contrast-text-presentation", + "title": "Images of Text", + "description": "If the technologies being used can achieve the visual presentation, text is used to convey information rather than images of text except for the following:", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-text-presentation", + "techniques": [ + "C6", + "C8", + "C12", + "C13", + "C14", + "C22", + "C30", + "G140", + "SL31" + ] + }, + "1.4.6": { + "id": "visual-audio-contrast7", + "title": "Contrast (Enhanced)", + "description": "The visual presentation of text and images of text has a contrast ratio of at least 7:1, except for the following:", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast7", + "techniques": [ + "F24", + "F83", + "G17", + "G18", + "G148", + "G156", + "G174", + "SL13" + ] + }, + "1.4.7": { + "id": "visual-audio-contrast-noaudio", + "title": "Low or No Background Audio", + "description": "For prerecorded audio-only content that (1) contains primarily speech in the foreground, (2) is not an audio CAPTCHA or audio logo, and (3) is not vocalization intended to be primarily musical expression such as singing or rapping, at least one of the following is true:", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-noaudio", + "techniques": [ + "G56" + ] + }, + "1.4.8": { + "id": "visual-audio-contrast-visual-presentation", + "title": "Visual Presentation", + "description": "For the visual presentation of blocks of text, a mechanism is available to achieve the following:", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-visual-presentation", + "techniques": [ + "C12", + "C13", + "C14", + "C19", + "C20", + "C21", + "C23", + "C24", + "C25", + "C26", + "F24", + "F88", + "FLASH33", + "G146", + "G148", + "G156", + "G169", + "G172", + "G175", + "G188", + "H87", + "SCR34" + ] + }, + "1.4.9": { + "id": "visual-audio-contrast-text-images", + "title": "Images of Text (No Exception)", + "description": "Images of text are only used for pure decoration or where a particular presentation of text is essential to the information being conveyed.", + "uri": "http://www.w3.org/TR/WCAG20/#visual-audio-contrast-text-images", + "techniques": [ + "C6", + "C8", + "C12", + "C13", + "C14", + "C22", + "C30", + "G140", + "SL31" + ] + }, + "2.1.1": { + "id": "keyboard-operation-keyboard-operable", + "title": "Keyboard", + "description": "All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints.", + "uri": "http://www.w3.org/TR/WCAG20/#keyboard-operation-keyboard-operable", + "techniques": [ + "F42", + "F54", + "F55", + "FLASH14", + "FLASH16", + "FLASH17", + "FLASH22", + "G90", + "G202", + "H91", + "SCR2", + "SCR20", + "SCR29", + "SCR35", + "SL9", + "SL14", + "SL15" + ] + }, + "2.1.2": { + "id": "keyboard-operation-trapping", + "title": "No Keyboard Trap", + "description": "If keyboard focus can be moved to a component of the page using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away.", + "uri": "http://www.w3.org/TR/WCAG20/#keyboard-operation-trapping", + "techniques": [ + "F10", + "FLASH17", + "G21" + ] + }, + "2.1.3": { + "id": "keyboard-operation-all-funcs", + "title": "Keyboard (No Exception)", + "description": "All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.", + "uri": "http://www.w3.org/TR/WCAG20/#keyboard-operation-all-funcs", + "techniques": [ + "F42", + "F54", + "F55", + "FLASH14", + "FLASH16", + "FLASH17", + "FLASH22", + "G90", + "G202", + "H91", + "SCR2", + "SCR20", + "SCR29", + "SCR35", + "SL9", + "SL14", + "SL15" + ] + }, + "2.2.1": { + "id": "time-limits-required-behaviors", + "title": "Timing Adjustable", + "description": "For each time limit that is set by the content, at least one of the following is true:", + "uri": "http://www.w3.org/TR/WCAG20/#time-limits-required-behaviors", + "techniques": [ + "F40", + "F41", + "F58", + "FLASH19", + "FLASH24", + "G4", + "G133", + "G180", + "G198", + "SCR1", + "SCR16", + "SCR33", + "SCR36", + "SL21" + ] + }, + "2.2.2": { + "id": "time-limits-pause", + "title": "Pause, Stop, Hide", + "description": "For moving, blinking, scrolling, or auto-updating information, all of the following are true:", + "uri": "http://www.w3.org/TR/WCAG20/#time-limits-pause", + "techniques": [ + "F4", + "F7", + "F16", + "F47", + "F50", + "FLASH35", + "FLASH36", + "G4", + "G11", + "G152", + "G186", + "G187", + "G191", + "SCR22", + "SCR33", + "SL11", + "SL12", + "SL24" + ] + }, + "2.2.3": { + "id": "time-limits-no-exceptions", + "title": "No Timing", + "description": "Timing is not an essential part of the event or activity presented by the content, except for non-interactive synchronized media and real-time events.", + "uri": "http://www.w3.org/TR/WCAG20/#time-limits-no-exceptions", + "techniques": [ + "G5" + ] + }, + "2.2.4": { + "id": "time-limits-postponed", + "title": "Interruptions", + "description": "Interruptions can be postponed or suppressed by the user, except interruptions involving an emergency.", + "uri": "http://www.w3.org/TR/WCAG20/#time-limits-postponed", + "techniques": [ + "F40", + "F41", + "G75", + "G76", + "SCR14" + ] + }, + "2.2.5": { + "id": "time-limits-server-timeout", + "title": "Re-authenticating", + "description": "When an authenticated session expires, the user can continue the activity without loss of data after re-authenticating.", + "uri": "http://www.w3.org/TR/WCAG20/#time-limits-server-timeout", + "techniques": [ + "F12", + "G105", + "G181" + ] + }, + "2.3.1": { + "id": "seizure-does-not-violate", + "title": "Three Flashes or Below Threshold", + "description": "Web pages do not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.", + "uri": "http://www.w3.org/TR/WCAG20/#seizure-does-not-violate", + "techniques": [ + "G15", + "G19", + "G176" + ] + }, + "2.3.2": { + "id": "seizure-three-times", + "title": "Three Flashes", + "description": "Web pages do not contain anything that flashes more than three times in any one second period.", + "uri": "http://www.w3.org/TR/WCAG20/#seizure-three-times", + "techniques": [ + "G19" + ] + }, + "2.4.1": { + "id": "navigation-mechanisms-skip", + "title": "Bypass Blocks", + "description": "A mechanism is available to bypass blocks of content that are repeated on multiple Web pages.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-skip", + "techniques": [ + "C6", + "G1", + "G123", + "G124", + "H64", + "H69", + "H70", + "SCR28", + "SL25", + "SL29" + ] + }, + "2.4.2": { + "id": "navigation-mechanisms-title", + "title": "Page Titled", + "description": "Web pages have titles that describe topic or purpose.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-title", + "techniques": [ + "ARIA1", + "F25", + "G88", + "G127", + "H25" + ] + }, + "2.4.3": { + "id": "navigation-mechanisms-focus-order", + "title": "Focus Order", + "description": "If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-focus-order", + "techniques": [ + "C27", + "F44", + "F85", + "FLASH15", + "G59", + "H4", + "SCR26", + "SCR27", + "SCR37", + "SL34" + ] + }, + "2.4.4": { + "id": "navigation-mechanisms-refs", + "title": "Link Purpose (In Context)", + "description": "The purpose of each link can be determined from the link text alone or from the link text together with its programmatically determined link context, except where the purpose of the link would be ambiguous to users in general.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-refs", + "techniques": [ + "ARIA1", + "C7", + "F63", + "F89", + "FLASH5", + "FLASH7", + "FLASH27", + "G53", + "G91", + "G189", + "H2", + "H24", + "H30", + "H33", + "H77", + "H78", + "H79", + "H80", + "H81", + "SCR30", + "SL18" + ] + }, + "2.4.5": { + "id": "navigation-mechanisms-mult-loc", + "title": "Multiple Ways", + "description": "More than one way is available to locate a Web page within a set of Web pages except where the Web Page is the result of, or a step in, a process.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-mult-loc", + "techniques": [ + "G63", + "G64", + "G125", + "G126", + "G161", + "G185", + "H59" + ] + }, + "2.4.6": { + "id": "navigation-mechanisms-descriptive", + "title": "Headings and Labels", + "description": "Headings and labels describe topic or purpose.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-descriptive", + "techniques": [ + "G130", + "G131" + ] + }, + "2.4.7": { + "id": "navigation-mechanisms-focus-visible", + "title": "Focus Visible", + "description": "Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-focus-visible", + "techniques": [ + "C15", + "F55", + "F78", + "FLASH20", + "G149", + "G165", + "G195", + "SCR31", + "SL2", + "SL7" + ] + }, + "2.4.8": { + "id": "navigation-mechanisms-location", + "title": "Location", + "description": "Information about the user's location within a set of Web pages is available.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-location", + "techniques": [ + "G63", + "G65", + "G127", + "G128", + "H59" + ] + }, + "2.4.9": { + "id": "navigation-mechanisms-link", + "title": "Link Purpose (Link Only)", + "description": "A mechanism is available to allow the purpose of each link to be identified from link text alone, except where the purpose of the link would be ambiguous to users in general.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-link", + "techniques": [ + "C7", + "F84", + "F89", + "FLASH5", + "FLASH7", + "FLASH27", + "G91", + "G189", + "H2", + "H24", + "H30", + "H33", + "SCR30", + "SL18" + ] + }, + "2.4.10": { + "id": "navigation-mechanisms-headings", + "title": "Section Headings", + "description": "Section headings are used to organize the content.", + "uri": "http://www.w3.org/TR/WCAG20/#navigation-mechanisms-headings", + "techniques": [ + "G141" + ] + }, + "3.1.1": { + "id": "meaning-doc-lang-id", + "title": "Language of Page", + "description": "The default human language of each Web page can be programmatically determined.", + "uri": "http://www.w3.org/TR/WCAG20/#meaning-doc-lang-id", + "techniques": [ + "FLASH13", + "H57", + "SVR5" + ] + }, + "3.1.2": { + "id": "meaning-other-lang-id", + "title": "Language of Parts", + "description": "The human language of each passage or phrase in the content can be programmatically determined except for proper names, technical terms, words of indeterminate language, and words or phrases that have become part of the vernacular of the immediately surrounding text.", + "uri": "http://www.w3.org/TR/WCAG20/#meaning-other-lang-id", + "techniques": [ + "FLASH13", + "H58", + "SL4", + "SL27" + ] + }, + "3.1.3": { + "id": "meaning-idioms", + "title": "Unusual Words", + "description": "A mechanism is available for identifying specific definitions of words or phrases used in an unusual or restricted way, including idioms and jargon.", + "uri": "http://www.w3.org/TR/WCAG20/#meaning-idioms", + "techniques": [ + "G55", + "G62", + "G70", + "G101", + "G112", + "H40", + "H54", + "H60" + ] + }, + "3.1.4": { + "id": "meaning-located", + "title": "Abbreviations", + "description": "A mechanism for identifying the expanded form or meaning of abbreviations is available.", + "uri": "http://www.w3.org/TR/WCAG20/#meaning-located", + "techniques": [ + "G55", + "G62", + "G70", + "G97", + "G102", + "H28", + "H60" + ] + }, + "3.1.5": { + "id": "meaning-supplements", + "title": "Reading Level", + "description": "When text requires reading ability more advanced than the lower secondary education level after removal of proper names and titles, supplemental content, or a version that does not require reading ability more advanced than the lower secondary education level, is available.", + "uri": "http://www.w3.org/TR/WCAG20/#meaning-supplements", + "techniques": [ + "G79", + "G86", + "G103", + "G153", + "G160" + ] + }, + "3.1.6": { + "id": "meaning-pronunciation", + "title": "Pronunciation", + "description": "A mechanism is available for identifying specific pronunciation of words where meaning of the words, in context, is ambiguous without knowing the pronunciation.", + "uri": "http://www.w3.org/TR/WCAG20/#meaning-pronunciation", + "techniques": [ + "G62", + "G120", + "G121", + "G163", + "H62" + ] + }, + "3.2.1": { + "id": "consistent-behavior-receive-focus", + "title": "On Focus", + "description": "When any user interface component receives focus, it does not initiate a change of context.", + "uri": "http://www.w3.org/TR/WCAG20/#consistent-behavior-receive-focus", + "techniques": [ + "F52", + "F55", + "G107", + "G200", + "G201" + ] + }, + "3.2.2": { + "id": "consistent-behavior-unpredictable-change", + "title": "On Input", + "description": "Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component.", + "uri": "http://www.w3.org/TR/WCAG20/#consistent-behavior-unpredictable-change", + "techniques": [ + "F36", + "F37", + "F76", + "FLASH4", + "G13", + "G80", + "G201", + "H32", + "H84", + "SCR19", + "SL10" + ] + }, + "3.2.3": { + "id": "consistent-behavior-consistent-locations", + "title": "Consistent Navigation", + "description": "Navigational mechanisms that are repeated on multiple Web pages within a set of Web pages occur in the same relative order each time they are repeated, unless a change is initiated by the user.", + "uri": "http://www.w3.org/TR/WCAG20/#consistent-behavior-consistent-locations", + "techniques": [ + "F66", + "G61" + ] + }, + "3.2.4": { + "id": "consistent-behavior-consistent-functionality", + "title": "Consistent Identification", + "description": "Components that have the same functionality within a set of Web pages are identified consistently.", + "uri": "http://www.w3.org/TR/WCAG20/#consistent-behavior-consistent-functionality", + "techniques": [ + "F31", + "G197" + ] + }, + "3.2.5": { + "id": "consistent-behavior-no-extreme-changes-context", + "title": "Change on Request", + "description": "Changes of context are initiated only by user request or a mechanism is available to turn off such changes.", + "uri": "http://www.w3.org/TR/WCAG20/#consistent-behavior-no-extreme-changes-context", + "techniques": [ + "F9", + "F22", + "F41", + "F52", + "F60", + "F61", + "G76", + "G110", + "G200", + "H76", + "H83", + "SCR19", + "SCR24", + "SVR1" + ] + }, + "3.3.1": { + "id": "minimize-error-identified", + "title": "Error Identification", + "description": "If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.", + "uri": "http://www.w3.org/TR/WCAG20/#minimize-error-identified", + "techniques": [ + "FLASH12", + "G83", + "G84", + "G85", + "G139", + "G199", + "SCR18", + "SCR32", + "SL35" + ] + }, + "3.3.2": { + "id": "minimize-error-cues", + "title": "Labels or Instructions", + "description": "Labels or instructions are provided when content requires user input.", + "uri": "http://www.w3.org/TR/WCAG20/#minimize-error-cues", + "techniques": [ + "ARIA1", + "ARIA2", + "F82", + "FLASH8", + "FLASH10", + "FLASH25", + "FLASH29", + "FLASH32", + "G13", + "G83", + "G89", + "G131", + "G162", + "G167", + "G184", + "H44", + "H65", + "H71", + "H90", + "SL8", + "SL19", + "SL26" + ] + }, + "3.3.3": { + "id": "minimize-error-suggestions", + "title": "Error Suggestion", + "description": "If an input error is automatically detected and suggestions for correction are known, then the suggestions are provided to the user, unless it would jeopardize the security or purpose of the content.", + "uri": "http://www.w3.org/TR/WCAG20/#minimize-error-suggestions", + "techniques": [ + "ARIA2", + "ARIA3", + "FLASH12", + "G83", + "G84", + "G85", + "G139", + "G177", + "G199", + "SCR18", + "SCR32", + "SL35" + ] + }, + "3.3.4": { + "id": "minimize-error-reversible", + "title": "Error Prevention (Legal, Financial, Data)", + "description": "For Web pages that cause legal commitments or financial transactions for the user to occur, that modify or delete user-controllable data in data storage systems, or that submit user test responses, at least one of the following is true:", + "uri": "http://www.w3.org/TR/WCAG20/#minimize-error-reversible", + "techniques": [ + "G98", + "G99", + "G155", + "G164", + "G168", + "G199", + "SCR18", + "SL35" + ] + }, + "3.3.5": { + "id": "minimize-error-context-help", + "title": "Help", + "description": "Context-sensitive help is available.", + "uri": "http://www.w3.org/TR/WCAG20/#minimize-error-context-help", + "techniques": [ + "G71", + "G89", + "G184", + "G193", + "G194", + "H89" + ] + }, + "3.3.6": { + "id": "minimize-error-reversible-all", + "title": "Error Prevention (All)", + "description": "For Web pages that require the user to submit information, at least one of the following is true:", + "uri": "http://www.w3.org/TR/WCAG20/#minimize-error-reversible-all", + "techniques": [ + "G98", + "G99", + "G155", + "G164", + "G168", + "G199" + ] + }, + "4.1.1": { + "id": "ensure-compat-parses", + "title": "Parsing", + "description": "In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features.", + "uri": "http://www.w3.org/TR/WCAG20/#ensure-compat-parses", + "techniques": [ + "F17", + "F62", + "F70", + "F77", + "G134", + "G192", + "H74", + "H75", + "H88", + "H93", + "H94", + "SL33" + ] + }, + "4.1.2": { + "id": "ensure-compat-rsv", + "title": "Name, Role, Value", + "description": "For all user interface components (including but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies.", + "uri": "http://www.w3.org/TR/WCAG20/#ensure-compat-rsv", + "techniques": [ + "F15", + "F20", + "F59", + "F68", + "F79", + "F86", + "F89", + "FLASH29", + "FLASH30", + "FLASH32", + "G10", + "G108", + "G135", + "H44", + "H64", + "H65", + "H88", + "H91", + "SL6", + "SL18", + "SL20", + "SL26", + "SL30", + "SL32" + ] + } + }, + "techniques": { + "ARIA1": { + "description": "Using the aria-describedby property to provide a descriptive label for input controls" + }, + "ARIA2": { + "description": "Identifying required fields with the aria-required property" + }, + "ARIA3": { + "description": "Identifying valid range information with the aria-valuemin and aria-valuemax properties" + }, + "C6": { + "description": "Positioning content based on structural markup" + }, + "C7": { + "description": "Using CSS to hide a portion of the link text" + }, + "C8": { + "description": "Using CSS letter-spacing to control spacing within a word" + }, + "C9": { + "description": "Using CSS to include decorative images" + }, + "C12": { + "description": "Using percent for font sizes" + }, + "C13": { + "description": "Using named font sizes" + }, + "C14": { + "description": "Using em units for font sizes" + }, + "C15": { + "description": "Using CSS to change the presentation of a user interface component when it receives focus" + }, + "C17": { + "description": "Scaling form elements which contain text" + }, + "C18": { + "description": "Using CSS margin and padding rules instead of spacer images for layout design" + }, + "C19": { + "description": "Specifying alignment either to the left OR right in CSS" + }, + "C20": { + "description": "Using relative measurements to set column widths so that lines can average 80 characters or less when the browser is resized" + }, + "C21": { + "description": "Specifying line spacing in CSS" + }, + "C22": { + "description": "Using CSS to control visual presentation of text" + }, + "C23": { + "description": "Specifying text and background colors of secondary content such as banners, features and navigation in CSS while not specifying text and background colors of the main content" + }, + "C24": { + "description": "Using percentage values in CSS for container sizes" + }, + "C25": { + "description": "Specifying borders and layout in CSS to delineate areas of a Web page while not specifying text and text-background colors" + }, + "C26": { + "description": "Providing options within the content to switch to a layout that does not require the user to scroll horizontally to read a line of text" + }, + "C27": { + "description": "Making the DOM order match the visual order" + }, + "C28": { + "description": "Specifying the size of text containers using em units" + }, + "C30": { + "description": "Using CSS to replace text with images of text and providing user interface controls to switch" + }, + "F1": { + "description": "Failure of Success Criterion 1.3.2 due to changing the meaning of content by positioning information with CSS" + }, + "F2": { + "description": "Failure of Success Criterion 1.3.1 due to using changes in text presentation to convey information without using the appropriate markup or text" + }, + "F3": { + "description": "Failure of Success Criterion 1.1.1 due to using CSS to include images that convey important information" + }, + "F4": { + "description": "Failure of Success Criterion 2.2.2 due to using text-decoration:blink without a mechanism to stop it in less than five seconds" + }, + "F7": { + "description": "Failure of Success Criterion 2.2.2 due to an object or applet, such as Java or Flash, that has blinking content without a mechanism to pause the content that blinks for more than five seconds" + }, + "F8": { + "description": "Failure of Success Criterion 1.2.2 due to captions omitting some dialogue or important sound effects" + }, + "F9": { + "description": "Failure of Success Criterion 3.2.5 due to changing the context when the user removes focus from a form element" + }, + "F10": { + "description": "Failure of Success Criterion 2.1.2 and Conformance Requirement 5 due to combining multiple content formats in a way that traps users inside one format type" + }, + "F12": { + "description": "Failure of Success Criterion 2.2.5 due to having a session time limit without a mechanism for saving user's input and re-establishing that information upon re-authentication" + }, + "F13": { + "description": "Failure of Success Criterion 1.1.1 and 1.4.1 due to having a text alternative that does not include information that is conveyed by color differences in the image" + }, + "F14": { + "description": "Failure of Success Criterion 1.3.3 due to identifying content only by its shape or location" + }, + "F15": { + "description": "Failure of Success Criterion 4.1.2 due to implementing custom controls that do not use an accessibility API for the technology, or do so incompletely" + }, + "F16": { + "description": "Failure of Success Criterion 2.2.2 due to including scrolling content where movement is not essential to the activity without also including a mechanism to pause and restart the content" + }, + "F17": { + "description": "Failure of Success Criterion 1.3.1 and 4.1.1 due to insufficient information in DOM to determine one-to-one relationships (e.g., between labels with same id) in HTML" + }, + "F20": { + "description": "Failure of Success Criterion 1.1.1 and 4.1.2 due to not updating text alternatives when changes to non-text content occur" + }, + "F22": { + "description": "Failure of Success Criterion 3.2.5 due to opening windows that are not requested by the user" + }, + "F23": { + "description": "Failure of 1.4.2 due to playing a sound longer than 3 seconds where there is no mechanism to turn it off" + }, + "F24": { + "description": "Failure of Success Criterion 1.4.3, 1.4.6 and 1.4.8 due to specifying foreground colors without specifying background colors or vice versa" + }, + "F25": { + "description": "Failure of Success Criterion 2.4.2 due to the title of a Web page not identifying the contents" + }, + "F26": { + "description": "Failure of Success Criterion 1.3.3 due to using a graphical symbol alone to convey information" + }, + "F30": { + "description": "Failure of Success Criterion 1.1.1 and 1.2.1 due to using text alternatives that are not alternatives (e.g., filenames or placeholder text)" + }, + "F31": { + "description": "Failure of Success Criterion 3.2.4 due to using two different labels for the same function on different Web pages within a set of Web pages" + }, + "F32": { + "description": "Failure of Success Criterion 1.3.2 due to using white space characters to control spacing within a word" + }, + "F33": { + "description": "Failure of Success Criterion 1.3.1 and 1.3.2 due to using white space characters to create multiple columns in plain text content" + }, + "F34": { + "description": "Failure of Success Criterion 1.3.1 and 1.3.2 due to using white space characters to format tables in plain text content" + }, + "F36": { + "description": "Failure of Success Criterion 3.2.2 due to automatically submitting a form and presenting new content without prior warning when the last field in the form is given a value" + }, + "F37": { + "description": "Failure of Success Criterion 3.2.2 due to launching a new window without prior warning when the status of a radio button, check box or select list is changed" + }, + "F38": { + "description": "Failure of Success Criterion 1.1.1 due to omitting the alt-attribute for non-text content used for decorative purposes only in HTML" + }, + "F39": { + "description": "Failure of Success Criterion 1.1.1 due to providing a text alternative that is not null (e.g., alt='spacer' or alt='image') for images that should be ignored by assistive technology" + }, + "F40": { + "description": "Failure of Success Criterion 2.2.1 and 2.2.4 due to using meta redirect with a time limit" + }, + "F41": { + "description": "Failure of Success Criterion 2.2.1, 2.2.4, and 3.2.5 due to using meta refresh with a time-out" + }, + "F42": { + "description": "Failure of Success Criterion 1.3.1 and 2.1.1 due to using scripting events to emulate links in a way that is not programmatically determinable" + }, + "F43": { + "description": "Failure of Success Criterion 1.3.1 due to using structural markup in a way that does not represent relationships in the content" + }, + "F44": { + "description": "Failure of Success Criterion 2.4.3 due to using tabindex to create a tab order that does not preserve meaning and operability" + }, + "F46": { + "description": "Failure of Success Criterion 1.3.1 due to using th elements, caption elements, or non-empty summary attributes in layout tables" + }, + "F47": { + "description": "Failure of Success Criterion 2.2.2 due to using the blink element" + }, + "F48": { + "description": "Failure of Success Criterion 1.3.1 due to using the pre element to markup tabular information" + }, + "F49": { + "description": "Failure of Success Criterion 1.3.2 due to using an HTML layout table that does not make sense when linearized " + }, + "F50": { + "description": "Failure of Success Criterion 2.2.2 due to a script that causes a blink effect without a mechanism to stop the blinking at 5 seconds or less" + }, + "F52": { + "description": "Failure of Success Criterion 3.2.1 and 3.2.5 due to opening a new window as soon as a new page is loaded" + }, + "F54": { + "description": "Failure of Success Criterion 2.1.1 due to using only pointing-device-specific event handlers (including gesture) for a function" + }, + "F55": { + "description": "Failure of Success Criteria 2.1.1, 2.4.7, and 3.2.1 due to using script to remove focus when focus is received" + }, + "F58": { + "description": "Failure of Success Criterion 2.2.1 due to using server-side techniques to automatically redirect pages after a time-out" + }, + "F59": { + "description": "Failure of Success Criterion 4.1.2 due to using script to make div or span a user interface control in HTML" + }, + "F60": { + "description": "Failure of Success Criterion 3.2.5 due to launching a new window when a user enters text into an input field" + }, + "F61": { + "description": "Failure of Success Criterion 3.2.5 due to complete change of main content through an automatic update that the user cannot disable from within the content" + }, + "F62": { + "description": "Failure of Success Criterion 1.3.1 and 4.1.1 due to insufficient information in DOM to determine specific relationships in XML" + }, + "F63": { + "description": "Failure of Success Criterion 2.4.4 due to providing link context only in content that is not related to the link" + }, + "F65": { + "description": "Failure of Success Criterion 1.1.1 due to omitting the alt attribute on img elements, area elements, and input elements of type 'image'" + }, + "F66": { + "description": "Failure of Success Criterion 3.2.3 due to presenting navigation links in a different relative order on different pages" + }, + "F67": { + "description": "Failure of Success Criterion 1.1.1 and 1.2.1 due to providing long descriptions for non-text content that does not serve the same purpose or does not present the same information" + }, + "F68": { + "description": "Failure of Success Criterion 1.3.1 and 4.1.2 due to the association of label and user interface controls not being programmatically determinable" + }, + "F69": { + "description": "Failure of Success Criterion 1.4.4 when resizing visually rendered text up to 200 percent causes the text, image or controls to be clipped, truncated or obscured" + }, + "F70": { + "description": "Failure of Success Criterion 4.1.1 due to incorrect use of start and end tags or attribute markup" + }, + "F71": { + "description": "Failure of Success Criterion 1.1.1 due to using text look-alikes to represent text without providing a text alternative" + }, + "F72": { + "description": "Failure of Success Criterion 1.1.1 due to using ASCII art without providing a text alternative" + }, + "F73": { + "description": "Failure of Success Criterion 1.4.1 due to creating links that are not visually evident without color vision" + }, + "F74": { + "description": "Failure of Success Criterion 1.2.2 and 1.2.8 due to not labeling a synchronized media alternative to text as an alternative" + }, + "F75": { + "description": "Failure of Success Criterion 1.2.2 by providing synchronized media without captions when the synchronized media presents more information than is presented on the page" + }, + "F76": { + "description": "Failure of Success Criterion 3.2.2 due to providing instruction material about the change of context by change of setting in a user interface element at a location that users may bypass" + }, + "F77": { + "description": "Failure of Success Criterion 4.1.1 due to duplicate values of type ID" + }, + "F78": { + "description": "Failure of Success Criterion 2.4.7 due to styling element outlines and borders in a way that removes or renders non-visible the visual focus indicator" + }, + "F79": { + "description": "Failure of Success Criterion 4.1.2 due to the focus state of a user interface component not being programmatically determinable or no notification of change of focus state available" + }, + "F80": { + "description": "Failure of Success Criterion 1.4.4 when text-based form controls do not resize when visually rendered text is resized up to 200%" + }, + "F81": { + "description": "Failure of Success Criterion 1.4.1 due to identifying required or error fields using color differences only" + }, + "F82": { + "description": "Failure of Success Criterion 3.3.2 by visually formatting a set of phone number fields but not including a text label" + }, + "F83": { + "description": "Failure of Success Criterion 1.4.3 and 1.4.6 due to using background images that do not provide sufficient contrast with foreground text (or images of text)" + }, + "F84": { + "description": "Failure of Success Criterion 2.4.9 due to using a non-specific link such as 'click here' or 'more' without a mechanism to change the link text to specific text." + }, + "F85": { + "description": "Failure of Success Criterion 2.4.3 due to using dialogs or menus that are not adjacent to their trigger control in the sequential navigation order" + }, + "F86": { + "description": "Failure of Success Criterion 4.1.2 due to not providing names for each part of a multi-part form field, such as a US telephone number" + }, + "F87": { + "description": "Failure of Success Criterion 1.3.1 due to inserting non-decorative content by using :before and :after pseudo-elements and the 'content' property in CSS" + }, + "F88": { + "description": "Failure of Success Criterion 1.4.8 due to using text that is justified (aligned to both the left and the right margins)" + }, + "F89": { + "description": "Failure of Success Criteria 2.4.4, 2.4.9 and 4.1.2 due to using null alt on an image where the image is the only content in a link" + }, + "F90": { + "description": "Failure of Success Criterion 1.3.1 for incorrectly associating table headers and content via the headers and id attributes" + }, + "F91": { + "description": "Failure of Success Criterion 1.3.1 for not correctly marking up table headers" + }, + "FLASH1": { + "description": "Setting the name property for a non-text object" + }, + "FLASH2": { + "description": "Setting the description property for a non-text object in Flash" + }, + "FLASH3": { + "description": "Marking objects in Flash so that they can be ignored by AT" + }, + "FLASH4": { + "description": "Providing submit buttons in Flash" + }, + "FLASH5": { + "description": "Combining adjacent image and text buttons for the same resource" + }, + "FLASH6": { + "description": "Creating accessible hotspots using invisible buttons" + }, + "FLASH7": { + "description": "Using scripting to change control labels" + }, + "FLASH8": { + "description": "Adding a group name to the accessible name of a form control" + }, + "FLASH9": { + "description": "Applying captions to prerecorded synchronized media" + }, + "FLASH10": { + "description": "Indicating required form controls in Flash" + }, + "FLASH11": { + "description": "Providing a longer text description of an object" + }, + "FLASH12": { + "description": "Providing client-side validation and adding error text via the accessible description" + }, + "FLASH13": { + "description": "Using HTML language attributes to specify language in Flash content" + }, + "FLASH14": { + "description": "Using redundant keyboard and mouse event handlers in Flash" + }, + "FLASH15": { + "description": "Using the tabIndex property to specify a logical reading order and a logical tab order in Flash" + }, + "FLASH16": { + "description": "Making actions keyboard accessible by using the click event on standard components" + }, + "FLASH17": { + "description": "Providing keyboard access to a Flash object and avoiding a keyboard trap" + }, + "FLASH18": { + "description": "Providing a control to turn off sounds that play automatically in Flash" + }, + "FLASH19": { + "description": "Providing a script that warns the user a time limit is about to expire and provides a way to extend it" + }, + "FLASH20": { + "description": "Reskinning Flash components to provide highly visible focus indication" + }, + "FLASH21": { + "description": "Using the DataGrid component to associate column headers with cells" + }, + "FLASH22": { + "description": "Adding keyboard-accessible actions to static elements" + }, + "FLASH23": { + "description": "Adding summary information to a DataGrid" + }, + "FLASH24": { + "description": "Allowing the user to extend the default time limit" + }, + "FLASH25": { + "description": "Labeling a form control by setting its accessible name" + }, + "FLASH26": { + "description": "Applying audio descriptions to Flash video" + }, + "FLASH27": { + "description": "Providing button labels that describe the purpose of a button" + }, + "FLASH28": { + "description": "Providing text alternatives for ASCII art, emoticons, and leetspeak in Flash" + }, + "FLASH29": { + "description": "Setting the label property for form components" + }, + "FLASH30": { + "description": "Specifying accessible names for image buttons" + }, + "FLASH31": { + "description": "Specifying caption text for a DataGrid" + }, + "FLASH32": { + "description": "Using auto labeling to associate text labels with form controls" + }, + "FLASH33": { + "description": "Using relative values for Flash object dimensions" + }, + "FLASH34": { + "description": "Turning off sounds that play automatically when an assistive technology is detected" + }, + "FLASH35": { + "description": "Using script to scroll Flash content, and providing a mechanism to pause it" + }, + "FLASH36": { + "description": "Using scripts to control blinking and stop it in five seconds or less" + }, + "G1": { + "description": "Adding a link at the top of each page that goes directly to the main content area" + }, + "G4": { + "description": "Allowing the content to be paused and restarted from where it was paused" + }, + "G5": { + "description": "Allowing users to complete an activity without any time limit" + }, + "G8": { + "description": "Providing a movie with extended audio descriptions" + }, + "G9": { + "description": "Creating captions for live synchronized media" + }, + "G10": { + "description": "Creating components using a technology that supports the accessibilityAPI features of the platforms on which the user agents will be run to expose thenames and roles, allow user-settable properties to be directly set, and providenotification of changes" + }, + "G11": { + "description": "Creating content that blinks for less than 5 seconds" + }, + "G13": { + "description": "Describing what will happen before a change to a form control that causes a change of context to occur is made" + }, + "G14": { + "description": "Ensuring that information conveyed by color differences is also available in text" + }, + "G15": { + "description": "Using a tool to ensure that content does not violate the general flash threshold or red flash threshold" + }, + "G17": { + "description": "Ensuring that a contrast ratio of at least 7:1 exists between text (and images of text)and background behind the text" + }, + "G18": { + "description": "Ensuring that a contrast ratio of at least 4.5:1 exists between text (and images of text) and background behind the text" + }, + "G19": { + "description": "Ensuring that no component of the content flashes more than three times in any 1-second period" + }, + "G21": { + "description": "Ensuring that users are not trapped in content" + }, + "G53": { + "description": "Identifying the purpose of a link using link text combined with the text of the enclosing sentence" + }, + "G54": { + "description": "Including a sign language interpreter in the video stream" + }, + "G55": { + "description": "Linking to definitions" + }, + "G56": { + "description": "Mixing audio files so that non-speech sounds are at least 20 decibelslower than the speech audio content" + }, + "G57": { + "description": "Ordering the content in a meaningful sequence" + }, + "G58": { + "description": "Placing a link to the alternative for time-based media immediately next to the non-text content" + }, + "G59": { + "description": "Placing the interactive elements in an order that follows sequences and relationships within the content" + }, + "G60": { + "description": "Playing a sound that turns off automatically within three seconds" + }, + "G61": { + "description": "Presenting repeated components in the same relative order each time they appear" + }, + "G62": { + "description": "Providing a glossary" + }, + "G63": { + "description": "Providing a site map" + }, + "G64": { + "description": "Providing a Table of Contents" + }, + "G65": { + "description": "Providing a breadcrumb trail" + }, + "G68": { + "description": "Providing a short text alternative that describes the purpose of liveaudio-only and live video-only content" + }, + "G69": { + "description": "Providing an alternative for time based media" + }, + "G70": { + "description": "Providing a function to search an online dictionary" + }, + "G71": { + "description": "Providing a help link on every Web page" + }, + "G73": { + "description": "Providing a long description in another location with a link to it thatis immediately adjacent to the non-text content" + }, + "G74": { + "description": "Providing a long description in text near the non-text content, with areference to the location of the long description in the short description" + }, + "G75": { + "description": "Providing a mechanism to postpone any updating of content" + }, + "G76": { + "description": "Providing a mechanism to request an update of the content instead ofupdating automatically" + }, + "G78": { + "description": "Providing a second, user-selectable, audio track that includes audio descriptions" + }, + "G79": { + "description": "Providing a spoken version of the text" + }, + "G80": { + "description": "Providing a submit button to initiate a change of context" + }, + "G81": { + "description": "Providing a synchronized video of the sign language interpreter that canbe displayed in a different viewport or overlaid on the image by the player" + }, + "G82": { + "description": "Providing a text alternative that identifies the purpose of the non-text content" + }, + "G83": { + "description": "Providing text descriptions to identify required fields that were not completed" + }, + "G84": { + "description": "Providing a text description when the user provides information that is not in the list of allowed values" + }, + "G85": { + "description": "Providing a text description when user input falls outside the required format or values" + }, + "G86": { + "description": "Providing a text summary that requires reading ability less advanced than the upper secondary education level" + }, + "G87": { + "description": "Providing closed captions" + }, + "G88": { + "description": "Providing descriptive titles for Web pages" + }, + "G89": { + "description": "Providing expected data format and example" + }, + "G90": { + "description": "Providing keyboard-triggered event handlers" + }, + "G91": { + "description": "Providing link text that describes the purpose of a link" + }, + "G92": { + "description": "Providing long description for non-text content that serves the samepurpose and presents the same information" + }, + "G93": { + "description": "Providing open (always visible) captions" + }, + "G94": { + "description": "Providing short text alternative for non-text content that serves the same purpose and presents the same information as the non-text content" + }, + "G95": { + "description": "Providing short text alternatives that provide a brief description ofthe non-text content" + }, + "G96": { + "description": "Providing textual identification of items that otherwise rely only on sensory information to be understood" + }, + "G97": { + "description": "Providing the first use of an abbreviation immediately before or after the expanded form" + }, + "G98": { + "description": "Providing the ability for the user to review and correct answers before submitting" + }, + "G99": { + "description": "Providing the ability to recover deleted information" + }, + "G100": { + "description": "Providing a short text alternative which is the accepted name or a descriptive name of the non-text content" + }, + "G101": { + "description": "Providing the definition of a word or phrase used in an unusual or restricted way" + }, + "G102": { + "description": "Providing the expansion or explanation of an abbreviation" + }, + "G103": { + "description": "Providing visual illustrations, pictures, and symbols to help explain ideas, events, and processes" + }, + "G105": { + "description": "Saving data so that it can be used after a user re-authenticates" + }, + "G107": { + "description": "Using 'activate' rather than 'focus' as a trigger for changes of context" + }, + "G108": { + "description": "Using markup features to expose the name and role, allow user-settable properties to be directly set, and provide notification of changes" + }, + "G110": { + "description": "Using an instant client-side redirect" + }, + "G111": { + "description": "Using color and pattern" + }, + "G112": { + "description": "Using inline definitions" + }, + "G115": { + "description": "Using semantic elements to mark up structure" + }, + "G117": { + "description": "Using text to convey information that is conveyed by variations in presentation of text" + }, + "G120": { + "description": "Providing the pronunciation immediately following the word" + }, + "G121": { + "description": "Linking to pronunciations" + }, + "G123": { + "description": "Adding a link at the beginning of a block of repeated content to go to the end of the block" + }, + "G124": { + "description": "Adding links at the top of the page to each area of the content" + }, + "G125": { + "description": "Providing links to navigate to related Web pages" + }, + "G126": { + "description": "Providing a list of links to all other Web pages" + }, + "G127": { + "description": "Identifying a Web page's relationship to a larger collection of Web pages" + }, + "G128": { + "description": "Indicating current location within navigation bars" + }, + "G130": { + "description": "Providing descriptive headings" + }, + "G131": { + "description": "Providing descriptive labels" + }, + "G133": { + "description": "Providing a checkbox on the first page of a multipart form that allows users to ask for longer session time limit or no session time limit" + }, + "G134": { + "description": "Validating Web pages" + }, + "G135": { + "description": "Using the accessibility API features of a technology to expose names androles, to allow user-settable properties to be directly set, and to providenotification of changes" + }, + "G138": { + "description": "Using semantic markup whenever color cues are used" + }, + "G139": { + "description": "Creating a mechanism that allows users to jump to errors" + }, + "G140": { + "description": "Separating information and structure from presentation to enable different presentations" + }, + "G141": { + "description": "Organizing a page using headings" + }, + "G142": { + "description": "Using a technology that has commonly-available user agents that support zoom" + }, + "G143": { + "description": "Providing a text alternative that describes the purpose of the CAPTCHA" + }, + "G144": { + "description": "Ensuring that the Web Page contains another CAPTCHA serving the same purpose using a different modality" + }, + "G145": { + "description": "Ensuring that a contrast ratio of at least 3:1 exists between text (and images of text) and background behind the text" + }, + "G146": { + "description": "Using liquid layout" + }, + "G148": { + "description": "Not specifying background color, not specifying text color, and not using technology features that change those defaults" + }, + "G149": { + "description": "Using user interface components that are highlighted by the user agent when they receive focus" + }, + "G150": { + "description": "Providing text based alternatives for live audio-only content" + }, + "G151": { + "description": "Providing a link to a text transcript of a prepared statement or script if the script is followed" + }, + "G152": { + "description": "Setting animated gif images to stop blinking after n cycles (within 5 seconds)" + }, + "G153": { + "description": "Making the text easier to read" + }, + "G155": { + "description": "Providing a checkbox in addition to a submit button" + }, + "G156": { + "description": "Using a technology that has commonly-available user agents that can change the foreground and background of blocks of text" + }, + "G157": { + "description": "Incorporating a live audio captioning service into a Web page" + }, + "G158": { + "description": "Providing an alternative for time-based media for audio-only content" + }, + "G159": { + "description": "Providing an alternative for time-based media for video-only content" + }, + "G160": { + "description": "Providing sign language versions of information, ideas, and processes that must be understood in order to use the content" + }, + "G161": { + "description": "Providing a search function to help users find content" + }, + "G162": { + "description": "Positioning labels to maximize predictability of relationships" + }, + "G163": { + "description": "Using standard diacritical marks that can be turned off" + }, + "G164": { + "description": "Providing a stated time within which an online request (or transaction) may be amended or canceled by the user after making the request" + }, + "G165": { + "description": "Using the default focus indicator for the platform so that high visibility default focus indicators will carry over" + }, + "G166": { + "description": "Providing audio that describes the important video content and describing it as such" + }, + "G167": { + "description": "Using an adjacent button to label the purpose of a field" + }, + "G168": { + "description": "Requesting confirmation to continue with selected action" + }, + "G169": { + "description": "Aligning text on only one side" + }, + "G170": { + "description": "Providing a control near the beginning of the Web page that turns off sounds that play automatically" + }, + "G171": { + "description": "Playing sounds only on user request" + }, + "G172": { + "description": "Providing a mechanism to remove full justification of text" + }, + "G173": { + "description": "Providing a version of a movie with audio descriptions" + }, + "G174": { + "description": "Providing a control with a sufficient contrast ratio that allows users to switch to a presentation that uses sufficient contrast" + }, + "G175": { + "description": "Providing a multi color selection tool on the page for foreground and background colors" + }, + "G176": { + "description": "Keeping the flashing area small enough" + }, + "G177": { + "description": "Providing suggested correction text" + }, + "G178": { + "description": "Providing controls on the Web page that allow users to incrementally change the size of all text on the page up to 200 percent" + }, + "G179": { + "description": "Ensuring that there is no loss of content or functionality when the text resizes and text containers do not change their width" + }, + "G180": { + "description": "Providing the user with a means to set the time limit to 10 times the default time limit" + }, + "G181": { + "description": "Encoding user data as hidden or encrypted data in a re-authorization page" + }, + "G182": { + "description": "Ensuring that additional visual cues are available when text color differences are used to convey information" + }, + "G183": { + "description": "Using a contrast ratio of 3:1 with surrounding text and providing additional visual cues on focus for links or controls where color alone is used to identify them" + }, + "G184": { + "description": "Providing text instructions at the beginning of a form or set of fields that describes the necessary input" + }, + "G185": { + "description": "Linking to all of the pages on the site from the home page" + }, + "G186": { + "description": "Using a control in the Web page that stops moving, blinking, or auto-updating content" + }, + "G187": { + "description": "Using a technology to include blinking content that can be turned off via the user agent" + }, + "G188": { + "description": "Providing a button on the page to increase line spaces and paragraph spaces" + }, + "G189": { + "description": "Providing a control near the beginning of the Web page that changes the link text" + }, + "G191": { + "description": "Providing a link, button, or other mechanism that reloads the page without any blinking content" + }, + "G192": { + "description": "Fully conforming to specifications" + }, + "G193": { + "description": "Providing help by an assistant in the Web page" + }, + "G194": { + "description": "Providing spell checking and suggestions for text input" + }, + "G195": { + "description": "Using an author-supplied, highly visible focus indicator" + }, + "G196": { + "description": "Using a text alternative on one item within a group of images that describes all items in the group" + }, + "G197": { + "description": "Using labels, names, and text alternatives consistently for content that has the same functionality" + }, + "G198": { + "description": "Providing a way for the user to turn the time limit off" + }, + "G199": { + "description": "Providing success feedback when data is submitted successfully" + }, + "G200": { + "description": "Opening new windows and tabs from a link only when necessary" + }, + "G201": { + "description": "Giving users advanced warning when opening a new window" + }, + "G202": { + "description": "Ensuring keyboard control for all functionality" + }, + "G203": { + "description": "Using a static text alternative to describe a talking head video" + }, + "H2": { + "description": "Combining adjacent image and text links for the same resource" + }, + "H4": { + "description": "Creating a logical tab order through links, form controls, and objects" + }, + "H24": { + "description": "Providing text alternatives for the area elements of image maps " + }, + "H25": { + "description": "Providing a title using the title element" + }, + "H27": { + "description": "Providing text and non-text alternatives for object" + }, + "H28": { + "description": "Providing definitions for abbreviations by using the abbr and acronym elements" + }, + "H30": { + "description": "Providing link text that describes the purpose of a link for anchor elements" + }, + "H32": { + "description": "Providing submit buttons" + }, + "H33": { + "description": "Supplementing link text with the title attribute" + }, + "H34": { + "description": "Using a Unicode right-to-left mark (RLM) or left-to-right mark (LRM) to mix textdirection inline" + }, + "H35": { + "description": "Providing text alternatives on applet elements" + }, + "H36": { + "description": "Using alt attributes on images used as submit buttons" + }, + "H37": { + "description": "Using alt attributes on img elements" + }, + "H39": { + "description": "Using caption elements to associate data table captions with data tables" + }, + "H40": { + "description": "Using definition lists" + }, + "H42": { + "description": "Using h1-h6 to identify headings" + }, + "H43": { + "description": "Using id and headers attributes to associate data cells with header cells indata tables" + }, + "H44": { + "description": "Using label elements to associate text labels with form controls" + }, + "H45": { + "description": "Using longdesc" + }, + "H46": { + "description": "Using noembed with embed" + }, + "H48": { + "description": "Using ol, ul and dl for lists or groups of links" + }, + "H49": { + "description": "Using semantic markup to mark emphasized or special text" + }, + "H51": { + "description": "Using table markup to present tabular information" + }, + "H53": { + "description": "Using the body of the object element" + }, + "H54": { + "description": "Using the dfn element to identify the defining instance of a word" + }, + "H56": { + "description": "Using the dir attribute on an inline element to resolve problems with nested directional runs" + }, + "H57": { + "description": "Using language attributes on the html element " + }, + "H58": { + "description": "Using language attributes to identify changes in the human language " + }, + "H59": { + "description": "Using the link element and navigation tools" + }, + "H60": { + "description": "Using the link element to link to a glossary" + }, + "H62": { + "description": "Using the ruby element" + }, + "H63": { + "description": "Using the scope attribute to associate header cells and data cells in datatables" + }, + "H64": { + "description": "Using the title attribute of the frame and iframe elements" + }, + "H65": { + "description": "Using the title attribute to identify form controls when the label element cannot be used" + }, + "H67": { + "description": "Using null alt text and no title attribute on img elements for images that ATshould ignore" + }, + "H69": { + "description": "Providing heading elements at the beginning of each section of content" + }, + "H70": { + "description": "Using frame elements to group blocks of repeated material" + }, + "H71": { + "description": "Providing a description for groups of form controls using fieldset and legendelements" + }, + "H73": { + "description": "Using the summary attribute of the table element to give an overview of datatables" + }, + "H74": { + "description": "Ensuring that opening and closing tags are used according to specification" + }, + "H75": { + "description": "Ensuring that Web pages are well-formed" + }, + "H76": { + "description": "Using meta refresh to create an instant client-side redirect" + }, + "H77": { + "description": "Identifying the purpose of a link using link text combined with its enclosing list item" + }, + "H78": { + "description": "Identifying the purpose of a link using link text combined with its enclosing paragraph" + }, + "H79": { + "description": "Identifying the purpose of a link using link text combined with its enclosing table cell and associated table headings" + }, + "H80": { + "description": "Identifying the purpose of a link using link text combined with the preceding heading element" + }, + "H81": { + "description": "Identifying the purpose of a link in a nested list using link text combined with the parent list item under which the list is nested" + }, + "H83": { + "description": "Using the target attribute to open a new window on user request and indicating this in link text" + }, + "H84": { + "description": "Using a button with a select element to perform an action" + }, + "H85": { + "description": "Using OPTGROUP to group OPTION elements inside a SELECT" + }, + "H86": { + "description": "Providing text alternatives for ASCII art, emoticons, and leetspeak" + }, + "H87": { + "description": "Not interfering with the user agent's reflow of text as the viewing window is narrowed" + }, + "H88": { + "description": "Using HTML according to spec" + }, + "H89": { + "description": "Using the title attribute to provide context-sensitive help" + }, + "H90": { + "description": "Indicating required form controls using label or legend" + }, + "H91": { + "description": "Using HTML form controls and links" + }, + "H92": { + "description": "Including a text cue for colored form control labels" + }, + "H93": { + "description": "Ensuring that id attributes are unique on a Web page" + }, + "H94": { + "description": "Ensuring that elements do not contain duplicate attributes" + }, + "T1": { + "description": "Using standard text formatting conventions for paragraphs" + }, + "T2": { + "description": "Using standard text formatting conventions for lists" + }, + "T3": { + "description": "Using standard text formatting conventions for headings" + }, + "SCR1": { + "description": "Allowing the user to extend the default time limit" + }, + "SCR2": { + "description": "Using redundant keyboard and mouse event handlers" + }, + "SCR14": { + "description": "Using scripts to make nonessential alerts optional" + }, + "SCR16": { + "description": "Providing a script that warns the user a time limit is about to expire" + }, + "SCR18": { + "description": "Providing client-side validation and alert" + }, + "SCR19": { + "description": "Using an onchange event on a select element without causing a change of context" + }, + "SCR20": { + "description": "Using both keyboard and other device-specific functions" + }, + "SCR21": { + "description": "Using functions of the Document Object Model (DOM) to add content to a page" + }, + "SCR22": { + "description": "Using scripts to control blinking and stop it in five seconds or less" + }, + "SCR24": { + "description": "Using progressive enhancement to open new windows on user request" + }, + "SCR26": { + "description": "Inserting dynamic content into the Document Object Model immediately following its trigger element" + }, + "SCR27": { + "description": "Reordering page sections using the Document Object Model" + }, + "SCR28": { + "description": "Using an expandable and collapsible menu to bypass block of content" + }, + "SCR29": { + "description": "Adding keyboard-accessible actions to static HTML elements" + }, + "SCR30": { + "description": "Using scripts to change the link text" + }, + "SCR31": { + "description": "Using script to change the background color or border of the element with focus" + }, + "SCR32": { + "description": "Providing client-side validation and adding error text via the DOM" + }, + "SCR33": { + "description": "Using script to scroll content, and providing a mechanism to pause it" + }, + "SCR34": { + "description": "Calculating size and position in a way that scales with text size" + }, + "SCR35": { + "description": "Making actions keyboard accessible by using the onclick event of anchors and buttons" + }, + "SCR36": { + "description": "Providing a mechanism to allow users to display moving, scrolling, or auto-updating text in a static window or area" + }, + "SCR37": { + "description": "Creating Custom Dialogs in a Device Independent Way" + }, + "SVR1": { + "description": "Implementing automatic redirects on the server side instead of on the client side" + }, + "SVR5": { + "description": "Specifying the default language in the HTTP header" + }, + "SL1": { + "description": "Accessing Alternate Audio Tracks in Silverlight Media" + }, + "SL2": { + "description": "Changing The Visual Focus Indicator in Silverlight" + }, + "SL3": { + "description": "Controlling Silverlight MediaElement Audio Volume" + }, + "SL4": { + "description": "Declaring Discrete Silverlight Objects to Specify Language Parts in the HTML DOM" + }, + "SL5": { + "description": "Defining a Focusable Image Class for Silverlight" + }, + "SL6": { + "description": "Defining a UI Automation Peer for a Custom Silverlight Control" + }, + "SL7": { + "description": "Designing a Focused Visual State for Custom Silverlight Controls" + }, + "SL8": { + "description": "Displaying HelpText in Silverlight User Interfaces" + }, + "SL9": { + "description": "Handling Key Events to Enable Keyboard Functionality in Silverlight" + }, + "SL10": { + "description": "Implementing a Submit-Form Pattern in Silverlight" + }, + "SL11": { + "description": "Pausing or Stopping A Decorative Silverlight Animation" + }, + "SL12": { + "description": "Pausing, Stopping, or Playing Media in Silverlight MediaElements" + }, + "SL13": { + "description": "Providing A Style Switcher To Switch To High Contrast" + }, + "SL14": { + "description": "Providing Custom Control Key Handling for Keyboard Functionality in Silverlight" + }, + "SL15": { + "description": "Providing Keyboard Shortcuts that Work Across the Entire Silverlight Application" + }, + "SL16": { + "description": "Providing Script-Embedded Text Captions for MediaElement Content" + }, + "SL17": { + "description": "Providing Static Alternative Content for Silverlight Media Playing in a MediaElement" + }, + "SL18": { + "description": "Providing Text Equivalent for Nontext Silverlight Controls With AutomationProperties.Name" + }, + "SL19": { + "description": "Providing User Instructions With AutomationProperties.HelpText in Silverlight" + }, + "SL20": { + "description": "Relying on Silverlight AutomationPeer Behavior to Set AutomationProperties.Name" + }, + "SL21": { + "description": "Replacing A Silverlight Timed Animation With a Nonanimated Element" + }, + "SL22": { + "description": "Supporting Browser Zoom in Silverlight" + }, + "SL23": { + "description": "Using A Style Switcher to Increase Font Size of Silverlight Text Elements" + }, + "SL24": { + "description": "Using AutoPlay to Keep Silverlight Media from Playing Automatically" + }, + "SL25": { + "description": "Using Controls and Programmatic Focus to Bypass Blocks of Content in Silverlight" + }, + "SL26": { + "description": "Using LabeledBy to Associate Labels and Targets in Silverlight" + }, + "SL27": { + "description": "Using Language/Culture Properties as Exposed by Silverlight Applications and Assistive Technologies" + }, + "SL28": { + "description": "Using Separate Text-Format Text Captions for MediaElement Content" + }, + "SL29": { + "description": "Using Silverlight 'List' Controls to Define Blocks that can be Bypassed" + }, + "SL30": { + "description": "Using Silverlight Control Compositing and AutomationProperties.Name" + }, + "SL31": { + "description": "Using Silverlight Font Properties to Control Text Presentation" + }, + "SL32": { + "description": "Using Silverlight Text Elements for Appropriate Accessibility Role" + }, + "SL33": { + "description": "Using Well-Formed XAML to Define a Silverlight User Interface" + }, + "SL34": { + "description": "Using the Silverlight Default Tab Sequence and Altering Tab Sequences With Properties" + }, + "SL35": { + "description": "Using the Validation and ValidationSummary APIs to Implement Client Side Forms Validation in Silverlight" + }, + "SM1": { + "description": "Adding extended audio description in SMIL 1.0" + }, + "SM2": { + "description": "Adding extended audio description in SMIL 2.0" + }, + "SM6": { + "description": "Providing audio description in SMIL 1.0" + }, + "SM7": { + "description": "Providing audio description in SMIL 2.0" + }, + "SM11": { + "description": "Providing captions through synchronized text streams in SMIL 1.0" + }, + "SM12": { + "description": "Providing captions through synchronized text streams in SMIL 2.0" + }, + "SM13": { + "description": "Providing sign language interpretation through synchronized video streams in SMIL 1.0" + }, + "SM14": { + "description": "Providing sign language interpretation through synchronized video streams in SMIL 2.0" + } + } +} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.tests.json b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.tests.json new file mode 100644 index 0000000..40ea26b --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/guidelines/wcag.tests.json @@ -0,0 +1 @@ +["aAdjacentWithSameResourceShouldBeCombined","aImgAltNotRepetitive","aLinkTextDoesNotBeginWithRedundantWord","aLinksDontOpenNewWindow","aLinksToMultiMediaRequireTranscript","aLinksToSoundFilesNeedTranscripts","aLinkWithNonText","aMustContainText","aSuspiciousLinkText","aTitleDescribesDestination","appletContainsTextEquivalent","appletContainsTextEquivalentInAlt","appletTextEquivalentsGetUpdated","appletUIMustBeAccessible","appletsDoNotFlicker","areaAltIdentifiesDestination","areaHasAltValue","areaLinksToSoundFile","blinkIsNotUsed","blockquoteNotUsedForIndentation","blockquoteUseForQuotations","buttonHasName","checkboxHasLabel","closingTagsAreUsed","contentPositioningShouldNotChangeMeaning","cssDocumentMakesSenseStyleTurnedOff","colorFontContrast","colorElementBehindContrast","colorBackgroundImageContrast","colorElementBehindBackgroundImageContrast","colorBackgroundGradientContrast","colorElementBehindBackgroundGradientContrast","definitionListsAreUsed","documentAbbrIsUsed","documentAcronymsHaveElement","documentContentReadableWithoutStylesheets","documentHasTitleElement","documentIDsMustBeUnique","documentIsWrittenClearly","documentLangIsISO639Standard","documentMetaNotUsedWithTimeout","documentReadingDirection","documentTitleDescribesDocument","documentTitleIsNotPlaceholder","documentTitleNotEmpty","documentValidatesToDocType","documentVisualListsAreMarkedUp","domOrderMatchesVisualOrder","embedProvidesMechanismToReturnToParent","emoticonsExcessiveUse","emoticonsMissingAbbr","fieldsetHasLabel","fileHasLabel","focusIndicatorVisible","formButtonsHaveValue","formHasSubmitButton","formWithRequiredLabel","framesAreUsedToGroupContent","frameTitlesDescribeFunction","frameTitlesNotEmpty","frameTitlesNotPlaceholder","framesHaveATitle","headerH1","headerH1Format","headerH2","headerH2Format","headerH3","headerH3Format","headerH4","headerH4Format","headerH5Format","headerH6Format","headerTextIsTooLong","headersHaveText","headersUsedToIndicateMainContent","headersUseToMarkSections","idRefHasCorrespondingId","idrefsHasCorrespondingId","imgAltEmptyForDecorativeImages","imgAltIsDifferent","imgAltIsSameInText","imgAltIsTooLong","imgAltNotEmptyInAnchor","imgAltNotPlaceHolder","imgGifNoFlicker","imgHasAlt","imgHasLongDesc","imgNonDecorativeHasAlt","imgNotReferredToByColorAlone","inputCheckboxRequiresFieldset","inputImageAltIdentifiesPurpose","inputImageAltIsNotFileName","inputImageAltIsNotPlaceholder","inputImageAltIsShort","inputImageAltNotRedundant","inputImageHasAlt","inputImageNotDecorative","inputTextHasLabel","inputWithoutLabelHasTitle","labelMustBeUnique","labelMustNotBeEmpty","languageDirAttributeIsUsed","languageChangesAreIdentified","languageDirectionPunctuation","languageUnicodeDirection","legendDescribesListOfChoices","legendTextNotEmpty","legendTextNotPlaceholder","listNotUsedForFormatting","listOfLinksUseList","newWindowIsOpened","objectDoesNotFlicker","objectMustContainText","objectMustHaveTitle","pNotUsedAsHeader","paragraphIsWrittenClearly","passwordHasLabel","preShouldNotBeUsedForTabularLayout","radioHasLabel","radioMarkedWithFieldgroupAndLegend","scriptOnclickRequiresOnKeypress","scriptOndblclickRequiresOnKeypress","scriptOnmousedownRequiresOnKeypress","scriptOnmousemove","scriptOnmouseoutHasOnmouseblur","scriptOnmouseoverHasOnfocus","scriptOnmouseupHasOnkeyup","scriptsDoNotFlicker","selectHasAssociatedLabel","selectJumpMenu","selectWithOptionsHasOptgroup","siteMap","skipToContentLinkProvided","svgContainsTitle","tableAxisHasCorrespondingId","tabIndexFollowsLogicalOrder","tableCaptionIdentifiesTable","tableComplexHasSummary","tableDataShouldHaveTh","tableLayoutDataShouldNotHaveTh","tableLayoutHasNoCaption","tableLayoutHasNoSummary","tableLayoutMakesSenseLinearized","tableNotUsedForLayout","tableShouldUseHeaderIDs","tableUsesCaption","tableUsesScopeForRow","tableWithBothHeadersUseScope","tagsAreNestedCorrectly","tabularDataIsInTable","textareaHasAssociatedLabel","videoProvidesCaptions","videosEmbeddedOrLinkedNeedCaptions","whiteSpaceInWord","whiteSpaceNotUsedForFormatting","doNotUseGraphicalSymbolToConveyInformation"] \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/libs/quail/preconditions.json b/plugins/ckeditor/a11ychecker/libs/quail/preconditions.json new file mode 100644 index 0000000..7e2ecf0 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/preconditions.json @@ -0,0 +1,90 @@ +{ + "videoMayBePresent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Das Video sollte geprüft werden, wenn Untertitel, Transkript und Audio-Beschreibungen gefordert sind", + "en": "The video should be tested if captions, transcript and audio description is required", + "nl": "De video dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn", + "pt-br": "O vídeo deve ser testado se as legendas, a transcrição e a descrição do áudio forem necessárias" + }, + "description": { + "de": "Das Video sollte geprüft werden, wenn Untertitel, Transkript und Audio-Beschreibungen gefordert sind", + "en": "The video should be tested if captions, transcript and audio description is required", + "nl": "De video dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn", + "pt-br": "O vídeo deve ser testado se as legendas, a transcrição e a descrição do áudio forem necessárias" + }, + "guidelines": [], + "tags": [ + "link", + "video" + ], + "callback": "videoMayBePresent" + }, + "audioMayBePresent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Das Audio sollte geprüft werden, wenn Untertitel, Transkript und Audio-Beschreibungen gefordert sind", + "en": "The audio should be tested if captions, transcript and audio description is required", + "nl": "De audio dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn", + "pt-br": "O áudio deve ser testado se as legendas, a transcrição e a descrição do áudio forem necessárias" + }, + "description": { + "de": "Das Audio sollte geprüft werden, wenn Untertitel, Transkript und Audio-Beschreibungen gefordert sind", + "en": "The audio should be tested if captions, transcript and audio description is required", + "nl": "De audio dient te worden getest of ondertiteling, transcript en audiodescription nodig zijn", + "pt-br": "O áudio deve ser testado se as legendas, a transcrição e a descrição do áudio forem necessárias" + }, + "guidelines": [], + "tags": [ + "link", + "audio" + ], + "callback": "audioMayBePresent" + }, + "animatedGifMayBePresent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Es sollte geprüft werden, ob .gif-Dateien auf der Seite verwendet werden und, ob vorhandene .gif-Dateien mehr als einen Frame enthalten", + "en": "Test if a .gif is used on the page. Test if the .gif contains more then one frame", + "nl": "Test of een .gif afbeelding gebruikt is op de pagina. Test of het .gif bestand uit meer dan één frame bestaat", + "pt-br": "Teste se um .gif é usado na página. Teste se o .gif contém mais de um quadro" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "link", + "gif" + ], + "callback": "animatedGifMayBePresent" + }, + "userInputMayBeRequired": { + "type": "custom", + "testability": 1, + "title": { + "de": "Formulare sollten dem Benutzer ausreichend Rückmeldungen geben", + "en": "Form should be checked to ensure sufficient feedback is given to the user", + "nl": "Formulieren dienen te worden gecontroleerd of voldoende feedback aan gebruikers wordt gegeven", + "pt-br": "O formulário deve ser verificado para garantir que o feedback é suficiente para o usuário" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "form", + "input" + ], + "callback": "userInputMayBeRequired" + } +} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.js b/plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.js new file mode 100644 index 0000000..585e435 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.js @@ -0,0 +1,10600 @@ +// %LEAVE_UNMINIFIED% %REMOVE_LINE% +/*! QUAIL quailjs.org | quailjs.org/license */ +;(function($) { +'use strict'; +// Polyfill Function.prototype.bind +// @see https://gist.github.com/dsingleton/1312328 +Function.prototype.bind=Function.prototype.bind||function(b){if(typeof this!=="function"){throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");}var a=Array.prototype.slice,f=a.call(arguments,1),e=this,c=function(){},d=function(){return e.apply(this instanceof c?this:b||window,f.concat(a.call(arguments)));};c.prototype=this.prototype;d.prototype=new c();return d;}; + +var quail = { + + options : { }, + + components : { }, + + lib : { }, + + testabilityTranslation : { + 0 : 'suggestion', + 0.5 : 'moderate', + 1 : 'severe' + }, + + html : null, + + strings : { }, + + accessibilityResults : { }, + + accessibilityTests : null, + + guidelines: { + wcag: { + /** + * Perform WCAG specific setup. + */ + setup: function (tests, listener, callbacks) { + callbacks = callbacks || {}; + // Associate Success Criteria with the TestCollection. + for (var sc in this.successCriteria) { + if (this.successCriteria.hasOwnProperty(sc)) { + var criteria = this.successCriteria[sc]; + criteria.registerTests(tests); + if (listener && listener.listenTo && typeof listener.listenTo === 'function') { + // Allow the invoker to listen to successCriteriaEvaluated events + // on each SuccessCriteria. + if (callbacks.successCriteriaEvaluated) { + listener.listenTo(criteria, 'successCriteriaEvaluated', callbacks.successCriteriaEvaluated); + } + } + } + } + }, + successCriteria: { } + } + }, + + // @var TestCollection + tests : { }, + + /** + * A list of HTML elements that can contain actual text. + */ + textSelector : ':not(:empty)', + + /** + * Suspect tags that would indicate a paragraph is being used as a header. + * I know, font tag, I know. Don't get me started. + */ + suspectPHeaderTags : ['strong', 'b', 'em', 'i', 'u', 'font'], + + /** + * Suspect CSS styles that might indicate a paragraph tag is being used as a header. + */ + suspectPCSSStyles : ['color', 'font-weight', 'font-size', 'font-family'], + + /** + * Elements that can (naturally) receive keyboard focus. + */ + focusElements : 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]', + + /** + * Regular expression to find emoticons. + */ + emoticonRegex: /((?::|;|B|P|=)(?:-)?(?:\)|\(|o|O|D|P))/g, + + /** + * A list of self-closing tags. + */ + selfClosingTags : ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'], + + + /** + * A list of tags that optionally can be closed + */ + optionalClosingTags: ['p', 'li', 'th','tr', 'td'], + + /** + * Main run function for quail. It bundles up some accessibility tests, + * and if tests are not passed, it instead fetches them using getJSON. + */ + run : function (options) { + if (options.reset) { + quail.accessibilityResults = { }; + } + + function buildTests (quail, data, options) { + // Filter for specific tests. + if (options.guideline && options.guideline.length) { + quail.tests = quail.lib.TestCollection([], { + scope: quail.html || null + }); + for (var i = 0, il = options.guideline.length; i < il; ++i) { + var t = options.guideline[i]; + if (data[t]) { + data[t].scope = quail.html || null; + quail.tests.set(t, data[t]); + } + } + } + // Or use all of the tests. + else { + quail.tests = quail.lib.TestCollection(data, { + scope: quail.html || null + }); + } + } + + /** + * A private, internal run function. + * + * This function is called when the tests are collected, which might occur + * after an AJAX request for a test JSON file. + */ + function _run () { + // Push custom tests into the test collection. + if (typeof options.customTests !== 'undefined') { + for (var testName in options.customTests) { + if (options.customTests.hasOwnProperty(testName)) { + options.customTests[testName].scope = quail.html || null; + quail.tests.set(testName, options.customTests[testName]); + } + } + } + + // Set up Guideline-specific behaviors. + var noop = function () {}; + for (var guideline in quail.guidelines) { + if (quail.guidelines[guideline] && typeof quail.guidelines[guideline].setup === 'function') { + quail.guidelines[guideline].setup(quail.tests, this, { + successCriteriaEvaluated: options.successCriteriaEvaluated || noop + }); + } + } + + // Invoke all the registered tests. + quail.tests.run({ + preFilter: options.preFilter || function () {}, + caseResolve: options.caseResolve || function () {}, + testComplete: options.testComplete || function () {}, + testCollectionComplete: options.testCollectionComplete || function () {}, + complete: options.complete || function () {} + }); + } + + // Create an empty TestCollection. + quail.tests = quail.lib.TestCollection([], { + scope: quail.html || null + }); + // The quail builder at quailjs.org/build provides an in-scope test object. + if (typeof quailBuilderTests !== 'undefined') { + quail.tests = quail.lib.TestCollection(quailBuilderTests, { + scope: quail.html || null + }); + _run.call(quail); + } + // Let wcag2 run itself, will call quail again when it knows what + // to + else if (options.guideline === 'wcag2') { + quail.lib.wcag2.run(options); + } + + // If a list of specific tests is provided, use them. + else if (options.accessibilityTests) { + buildTests(quail, options.accessibilityTests, options); + _run.call(quail); + } + + // Otherwise get the tests from the json data list. + else { + var url = options.jsonPath; + // Get a specific guideline. + if (typeof options.guideline === 'string') { + url += '/guidelines/' + options.guideline; + } + + $.ajax({ + url : url + '/tests.json', + dataType : 'json', + success : function (data) { + if (typeof data === 'object') { + buildTests(quail, data, options); + _run.call(quail); + } + }, + error : function () { + throw new Error('Tests could not be loaded'); + } + }); + } + }, + + // @todo, make this a set of methods that all classes extend. + listenTo: function (dispatcher, eventName, handler) { + // @todo polyfill Function.prototype.bind. + handler = handler.bind(this); + dispatcher.registerListener.call(dispatcher, eventName, handler); + }, + + getConfiguration : function(testName) { + var test = this.tests.find(testName); + var guidelines = test && test.get('guidelines'); + var guideline = guidelines && this.options.guidelineName && guidelines[this.options.guidelineName]; + var configuration = guideline && guideline.configuration; + if (configuration) { + return configuration; + } + return false; + }, + + /** + * Helper function to determine if a string of text is even readable. + * @todo - This will be added to in the future... we should also include + * phonetic tests. + */ + isUnreadable : function(text) { + if (typeof text !== 'string') { + return true; + } + return (text.trim().length) ? false : true; + }, + + /** + * Read more about this function here: https://github.com/kevee/quail/wiki/Layout-versus-data-tables + */ + isDataTable : function(table) { + // If there are less than three rows, why do a table? + if (table.find('tr').length < 3) { + return false; + } + // If you are scoping a table, it's probably not being used for layout + if (table.find('th[scope]').length) { + return true; + } + var numberRows = table.find('tr:has(td)').length; + // Check for odd cell spanning + var spanCells = table.find('td[rowspan], td[colspan]'); + var isDataTable = true; + if (spanCells.length) { + var spanIndex = {}; + spanCells.each(function() { + if (typeof spanIndex[$(this).index()] === 'undefined') { + spanIndex[$(this).index()] = 0; + } + spanIndex[$(this).index()]++; + }); + $.each(spanIndex, function(index, count) { + if (count < numberRows) { + isDataTable = false; + } + }); + } + // If there are sub tables, but not in the same column row after row, this is a layout table + var subTables = table.find('table'); + if (subTables.length) { + var subTablesIndexes = {}; + subTables.each(function() { + var parentIndex = $(this).parent('td').index(); + if (parentIndex !== false && typeof subTablesIndexes[parentIndex] === 'undefined') { + subTablesIndexes[parentIndex] = 0; + } + subTablesIndexes[parentIndex]++; + }); + $.each(subTablesIndexes, function(index, count) { + if (count < numberRows) { + isDataTable = false; + } + }); + } + return isDataTable; + }, + + /** + * Returns text contents for nodes depending on their semantics + */ + getTextContents : function($element) { + if ($element.is('p, pre, blockquote, ol, ul, li, dl, dt, dd, figure, figcaption')) { + return $element.text(); + } + // Loop through all text nodes to get everything around children. + var text = ''; + var children = $element[0].childNodes; + for (var i = 0, il = children.length; i < il; i += 1) { + // Only text nodes. + if (children[i].nodeType === 3) { + text += children[i].nodeValue; + } + } + return text; + }, + + /** + * Helper function to determine if a given URL is even valid. + */ + validURL : function(url) { + return url.search(' ') === -1; + }, + + cleanString : function(string) { + return string.toLowerCase().replace(/^\s\s*/, ''); + }, + + containsReadableText : function(element, children) { + element = element.clone(); + element.find('option').remove(); + if (!quail.isUnreadable(element.text())) { + return true; + } + if (!quail.isUnreadable(element.attr('alt'))) { + return true; + } + if (children) { + var readable = false; + element.find('*').each(function() { + if (quail.containsReadableText($(this), true)) { + readable = true; + } + }); + if (readable) { + return true; + } + } + return false; + } +}; + +// Provide a global to access quail. +if (window) { + window.quail = quail; +} + +$.fn.quail = function(options) { + if (!this.length) { + return this; + } + quail.options = options; + quail.html = this; + + quail.run(options); + + return this; +}; + +$.expr[':'].quailCss = function(obj, index, meta) { + var args = meta[3].split(/\s*=\s*/); + return $(obj).css(args[0]).search(args[1]) > -1; +}; + +quail.components.acronym = function(quail, test, Case) { + test.get('$scope').each(function() { + var $scope = $(this); + var alreadyReported = { }; + var predefined = { }; + + // Find defined acronyms within this scope. + $scope.find('acronym[title], abbr[title]').each(function() { + predefined[$(this).text().toUpperCase().trim()] = $(this).attr('title'); + }); + + // Consider all block-level html elements that contain text. + $scope.find('p, div, h1, h2, h3, h4, h5').each(function(){ + var el = this; + var $el = $(el); + + var words = $el.text().split(' '); + // Keep a list of words that might be acronyms. + var infractions = []; + // If there is more than one word and ??. + if (words.length > 1 && $el.text().toUpperCase() !== $el.text()) { + // Check each word. + $.each(words, function(index, word) { + // Only consider words great than one character. + if (word.length < 2) { + return; + } + // Only consider words that have not been predefined. + // Remove any non-alpha characters. + word = word.replace(/[^a-zA-Zs]/, ''); + // If this is an uppercase word that has not been defined, it fails. + if (word.toUpperCase() === word && typeof predefined[word.toUpperCase().trim()] === 'undefined') { + if (typeof alreadyReported[word.toUpperCase()] === 'undefined') { + infractions.push(word); + } + alreadyReported[word.toUpperCase()] = word; + } + }); + // If undefined acronyms are discovered, fail this case. + if (infractions.length) { + test.add(Case({ + element: el, + expected: $el.closest('.quail-test').data('expected'), + info: {acronyms : infractions}, + status: 'failed' + })); + } + else { + test.add(Case({ + element: el, + expected: $el.closest('.quail-test').data('expected'), + status: 'passed' + })); + } + } + else { + test.add(Case({ + element: el, + expected: $el.closest('.quail-test').data('expected'), + status: 'passed' + })); + } + }); + + }); +}; + +quail.components.color = (function () { + + function buildCase(test, Case, element, status, id, message) { + test.add(Case({ + element: element, + expected: (function (element, id) { + return quail.components.resolveExpectation(element, id); + }(element, id)), + message: message, + status: status + })); + } + + function notempty(s) { + return $.trim(s) !== ''; + } + + + + var colors = { + cache: {}, + /** + * Returns the lumosity of a given foreground and background object, + * in the format of {r: red, g: green, b: blue } in rgb color values. + */ + getLuminosity : function(foreground, background) { + var cacheKey = 'getLuminosity_' + foreground + '_' + background; + foreground = colors.parseColor(foreground); + background = colors.parseColor(background); + + if (colors.cache[cacheKey] !== undefined) { + return colors.cache[cacheKey]; + } + + var RsRGB = foreground.r/255; + var GsRGB = foreground.g/255; + var BsRGB = foreground.b/255; + var R = (RsRGB <= 0.03928) ? RsRGB/12.92 : Math.pow((RsRGB+0.055)/1.055, 2.4); + var G = (GsRGB <= 0.03928) ? GsRGB/12.92 : Math.pow((GsRGB+0.055)/1.055, 2.4); + var B = (BsRGB <= 0.03928) ? BsRGB/12.92 : Math.pow((BsRGB+0.055)/1.055, 2.4); + + var RsRGB2 = background.r/255; + var GsRGB2 = background.g/255; + var BsRGB2 = background.b/255; + var R2 = (RsRGB2 <= 0.03928) ? RsRGB2/12.92 : Math.pow((RsRGB2+0.055)/1.055, 2.4); + var G2 = (GsRGB2 <= 0.03928) ? GsRGB2/12.92 : Math.pow((GsRGB2+0.055)/1.055, 2.4); + var B2 = (BsRGB2 <= 0.03928) ? BsRGB2/12.92 : Math.pow((BsRGB2+0.055)/1.055, 2.4); + var l1, l2; + l1 = (0.2126 * R + 0.7152 * G + 0.0722 * B); + l2 = (0.2126 * R2 + 0.7152 * G2 + 0.0722 * B2); + + colors.cache[cacheKey] = Math.round((Math.max(l1, l2) + 0.05)/(Math.min(l1, l2) + 0.05)*10)/10; + return colors.cache[cacheKey]; + }, + + /** + * Returns the average color for a given image + * using a canvas element. + */ + fetchImageColorAtPixel : function(img, x, y) { + x = typeof x !== 'undefined' ? x : 1; + y = typeof y !== 'undefined' ? y : 1; + var can = document.createElement('canvas'); + var context = can.getContext('2d'); + context.drawImage(img, 0, 0); + var data = context.getImageData(x, y, 1, 1).data; + return 'rgb(' + data[0] + ',' + data[1] + ',' + data[2] + ')'; + }, + + testElmContrast: function (algorithm, element, level) { + var background = colors.getColor(element, 'background'); + return colors.testElmBackground(algorithm, element, background, level); + }, + + testElmBackground: function (algorithm, element, background, level) { + var foreground = colors.getColor(element, 'foreground'); + var res; + if (algorithm === 'wcag') { + res = colors.passesWCAGColor(element, foreground, background, level); + } else if (algorithm === 'wai') { + res = colors.passesWAIColor(foreground, background); + } + return res; + }, + + /** + * Returns whether an element's color passes + * WCAG at a certain contrast ratio. + */ + passesWCAGColor : function(element, foreground, background, level) { + var pxfsize = quail.components.convertToPx(element.css('fontSize')); + if (typeof level === 'undefined') { + if (pxfsize >= 18) { + level = 3; + } + else { + var fweight = element.css('fontWeight'); + if (pxfsize >= 14 && (fweight === 'bold' || parseInt(fweight, 10) >= 700)) { + level = 3; + } + else { + level = 4.5; + } + } + } + return (colors.getLuminosity(foreground, background) > level); + }, + + /** + * Returns whether an element's color passes + * WAI brightness levels. + */ + passesWAIColor : function(foreground, background) { + var contrast = colors.getWAIErtContrast(foreground, background); + var brightness = colors.getWAIErtBrightness(foreground, background); + + return (contrast > 500 && brightness > 125); + }, + + /** + * Compused contrast of a foreground and background + * per the ERT contrast spec. + */ + getWAIErtContrast : function(foreground, background) { + var diffs = colors.getWAIDiffs(foreground, background); + return diffs.red + diffs.green + diffs.blue; + }, + + /** + * Computed contrast of a foreground and background + * per the ERT brightness spec. + */ + getWAIErtBrightness : function(foreground, background) { + var diffs = colors.getWAIDiffs(foreground, background); + return ((diffs.red * 299) + (diffs.green * 587) + (diffs.blue * 114)) / 1000; + + }, + + /** + * Returns differences between two colors. + */ + getWAIDiffs : function(foreground, background) { + return { + red: Math.abs(foreground.r - background.r), + green: Math.abs(foreground.g - background.g), + blue: Math.abs(foreground.b - background.b) + }; + }, + + /** + * Retrieves the background or foreground of an element. + * There are some normalizations here for the way + * different browsers can return colors, and handling transparencies. + */ + getColor : function(element, type) { + var self = colors; + if (!element.attr('data-cacheId')) { + element.attr('data-cacheId', 'id_' + Math.random()); + } + var cacheKey = 'getColor_' + type + '_' + element.attr('data-cacheId'); + if (colors.cache[cacheKey] !== undefined) { + return colors.cache[cacheKey]; + } + + if (type === 'foreground') { + colors.cache[cacheKey] = (element.css('color')) ? element.css('color') : 'rgb(0,0,0)'; + return colors.cache[cacheKey]; + } + + var bcolor = element.css('background-color'); + if (colors.hasBackgroundColor(bcolor)) { + colors.cache[cacheKey] = bcolor; + return colors.cache[cacheKey]; + } + + element.parents().each(function(){ + var pcolor = $(this).css('background-color'); + if (colors.hasBackgroundColor(pcolor)) { + return self.cache[cacheKey] = pcolor; + } + }); + // Assume the background is white. + colors.cache[cacheKey] = 'rgb(255,255,255)'; + return colors.cache[cacheKey]; + }, + + getForeground: function(element) { + return colors.getColor(element, 'foreground'); + }, + + /** + * Returns an object with rgba taken from a string. + */ + parseColor : function(color) { + if (typeof color === 'object') { + return color; + } + + if (color.substr(0, 1) === '#') { + return { r : parseInt(color.substr(1, 2), 16), + g : parseInt(color.substr(3, 2), 16), + b : parseInt(color.substr(5, 2), 16), + a : false + }; + } + + if (color.substr(0, 3) === 'rgb') { + color = color.replace('rgb(', '').replace('rgba(', '').replace(')', '').split(','); + return { r : color[0], + g : color[1], + b : color[2], + a : ((typeof color[3] === 'undefined') ? false : color[3]) + }; + } + }, + + /** + * Returns background image of an element or its parents. + */ + getBackgroundImage: function(element) { + if (!element.attr('data-cacheId')) { + element.attr('data-cacheId', 'id_' + Math.random()); + } + + var cacheKey = 'getBackgroundImage_' + element.attr('data-cacheId'); + if (colors.cache[cacheKey] !== undefined) { + return colors.cache[cacheKey]; + } + element = element[0]; + while(element && element.nodeType === 1 && element.nodeName !== 'BODY' && element.nodeName !== 'HTML') { + var bimage = $(element).css('background-image'); + if (bimage && bimage !== 'none' && bimage.search(/^(.*?)url(.*?)$/i) !== -1) { + colors.cache[cacheKey] = bimage.replace('url(', '').replace(/['"]/g, '').replace(')', ''); + return colors.cache[cacheKey]; + } + element = element.parentNode; + } + colors.cache[cacheKey] = false; + return false; + }, + + /** + * Returns background image of an element or its parents. + */ + getBackgroundGradient: function(element) { + if (!element.attr('data-cacheId')) { + element.attr('data-cacheId', 'id_' + Math.random()); + } + + var cacheKey = 'getBackgroundGradient_' + element.attr('data-cacheId'); + if (colors.cache[cacheKey] !== undefined) { + return colors.cache[cacheKey]; + } + + var notEmpty = function(s) { + return $.trim(s) !== ''; + }; + element = element[0]; + while(element && element.nodeType === 1 && element.nodeName !== 'BODY' && element.nodeName !== 'HTML') { + // Exit if element has a background color. + if (colors.hasBackgroundColor($(element).css('background-color'))) { + colors.cache[cacheKey] = false; + return false; + } + var bimage = $(element).css('backgroundImage'); + if (bimage && bimage !== 'none' && bimage.search(/^(.*?)gradient(.*?)$/i) !== -1) { + var gradient = bimage.match(/gradient(\(.*\))/g); + if (gradient.length > 0) { + gradient = gradient[0].replace(/(linear|radial|from|\bto\b|gradient|top|left|bottom|right|\d*%)/g, ''); + colors.cache[cacheKey] = $.grep(gradient.match(/(rgb\([^\)]+\)|#[a-z\d]*|[a-z]*)/g), notEmpty); + return colors.cache[cacheKey]; + } + } + element = element.parentNode; + } + colors.cache[cacheKey] = false; + return false; + }, + + /** + * Calculates average color of an image. + */ + getAverageRGB: function(img) { + var cacheKey = img.src; + if (colors.cache[cacheKey] !== undefined) { + return colors.cache[cacheKey]; + } + + var blockSize = 5, // only visit every 5 pixels + defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs + canvas = document.createElement('canvas'), + context = canvas.getContext && canvas.getContext('2d'), + data, width, height, + i = -4, + length, + rgb = {r:0, g:0, b:0, a:0}, + count = 0; + + if (!context) { + colors.cache[cacheKey] = defaultRGB; + return defaultRGB; + } + + height = canvas.height = img.height; + width = canvas.width = img.width; + context.drawImage(img, 0, 0); + + try { + data = context.getImageData(0, 0, width, height); + } catch(e) { + colors.cache[cacheKey] = defaultRGB; + return defaultRGB; + } + + length = data.data.length; + + while ((i += blockSize * 4) < length) { + ++count; + rgb.r += data.data[i]; + rgb.g += data.data[i+1]; + rgb.b += data.data[i+2]; + } + + // ~~ used to floor values + rgb.r = ~~(rgb.r/count); + rgb.g = ~~(rgb.g/count); + rgb.b = ~~(rgb.b/count); + + colors.cache[cacheKey] = rgb; + return rgb; + }, + + /** + * Convert color to hex value. + */ + colorToHex: function(c) { + var m = /rgba?\((\d+), (\d+), (\d+)/.exec(c); + return m ? '#' + (1 << 24 | m[1] << 16 | m[2] << 8 | m[3]).toString(16).substr(1) : c; + }, + + /** + * Check if element has a background color. + */ + hasBackgroundColor: function(bcolor) { + return bcolor !== 'rgba(0, 0, 0, 0)' && bcolor !== 'transparent'; + }, + + /** + * Traverse visual tree for background property. + */ + traverseVisualTreeForBackground: function(element, property) { + if (!element.attr('data-cacheId')) { + element.attr('data-cacheId', 'id_' + Math.random()); + } + + var cacheKey = 'traverseVisualTreeForBackground_' + element.attr('data-cacheId') + '_' + property; + if (colors.cache[cacheKey] !== undefined) { + return colors.cache[cacheKey]; + } + + var foundIt; + var scannedElements = []; + + // Scroll to make sure element is visible. + element[0].scrollIntoView(); + + // Get relative x and y. + var x = element.offset().left - $(window).scrollLeft(); + var y = element.offset().top - $(window).scrollTop(); + + // Hide current element. + scannedElements.push({ + element: element, + visibility: element.css('visibility') + }); + element.css('visibility', 'hidden'); + + // Get element at position x, y. This only selects visible elements. + var el = document.elementFromPoint(x,y); + while (foundIt === undefined && el && el.tagName !== 'BODY' && el.tagName !== 'HTML') { + el = $(el); + var bcolor = el.css('backgroundColor'); + var bimage; + // Only check visible elements. + switch (property) { + case 'background-color': + if (colors.hasBackgroundColor(bcolor)) { + foundIt = bcolor; + } + break; + case 'background-gradient': + // Bail out if the element has a background color. + if (colors.hasBackgroundColor(bcolor)) { + foundIt = false; + continue; + } + + bimage = el.css('backgroundImage'); + if (bimage && bimage !== 'none' && bimage.search(/^(.*?)gradient(.*?)$/i) !== -1) { + var gradient = bimage.match(/gradient(\(.*\))/g); + if (gradient.length > 0) { + gradient = gradient[0].replace(/(linear|radial|from|\bto\b|gradient|top|left|bottom|right|\d*%)/g, ''); + foundIt = $.grep(gradient.match(/(rgb\([^\)]+\)|#[a-z\d]*|[a-z]*)/g), notempty); + } + } + break; + case 'background-image': + // Bail out if the element has a background color. + if (colors.hasBackgroundColor(bcolor)) { + foundIt = false; + continue; + } + bimage = el.css('backgroundImage'); + if (bimage && bimage !== 'none' && bimage.search(/^(.*?)url(.*?)$/i) !== -1) { + foundIt = bimage.replace('url(', '').replace(/['"]/g, '').replace(')', ''); + } + break; + } + scannedElements.push({ + element: el, + visibility: el.css('visibility') + }); + el.css('visibility', 'hidden'); + el = document.elementFromPoint(x,y); + } + + // Reset visibility. + for(var i = 0; i < scannedElements.length; i++){ + scannedElements[i].element.css('visibility', scannedElements[i].visibility); + } + + colors.cache[cacheKey] = foundIt; + return foundIt; + }, + + /** + * Get first element behind current with a background color. + */ + getBehindElementBackgroundColor: function(element) { + return colors.traverseVisualTreeForBackground(element, 'background-color'); + }, + + /** + * Get first element behind current with a background gradient. + */ + getBehindElementBackgroundGradient: function(element) { + return colors.traverseVisualTreeForBackground(element, 'background-gradient'); + }, + + /** + * Get first element behind current with a background image. + */ + getBehindElementBackgroundImage: function(element) { + return colors.traverseVisualTreeForBackground(element, 'background-image'); + } + }; + + function textShouldBeTested(textNode) { + // We want a tag, not just the text node. + var element = textNode.parentNode; + var $this = $(element); + + // The nodeType of the element must be 1. Nodes of type 1 implement the Element + // interface which is required of the first argument passed to window.getComputedStyle. + // Failure to pass an Element to window.getComputedStyle will raised an exception + // if Firefox. + if (element.nodeType !== 1) { + return false; + + // Ignore elements whose content isn't displayed to the page. + } else if (['script', 'style', 'title', 'object', 'applet', 'embed', 'template', 'noscript'] + .indexOf(element.nodeName.toLowerCase()) !== -1) { + return false; + + // Bail out if the text is not readable. + } else if (quail.isUnreadable($this.text())) { + return false; + + } else { + return true; + } + } + + /** + * For the color test, if any case passes for a given element, then all the + * cases for that element pass. + */ + function postInvoke(test) { + var passed = {}; + var groupsBySelector = test.groupCasesBySelector(); + + /** + * Determine the length of an object. + * + * @param object obj + * The object whose size will be determined. + * + * @return number + * The size of the object determined by the number of keys. + */ + function size (obj) { + return Object.keys(obj).length; + } + + // Go through each selector group. + var nub = ''; + for (var selector in groupsBySelector) { + if (groupsBySelector.hasOwnProperty(selector)) { + var cases = groupsBySelector[selector]; + cases.each(function (index, _case) { + if (_case.get('status') === passed) { + // This can just be an empty string. We only need the passed hash + // to contain keys, not values. + passed[selector] = nub; + } + }); + } + } + + return size(passed) === size(groupsBySelector); + } + + return { + colors: colors, + textShouldBeTested: textShouldBeTested, + postInvoke: postInvoke, + buildCase: buildCase + }; + +}()); + +quail.components.content = { + + /** + * Iterates over elments in the given context and looks + * for elements that could be considered the main content area. + * + * @param {jQuery} $element + * The DOM element wrapper in jQuery to search for a content element within. + * @return {jQuery} + * The jQuery element that is considered the most likely content element. + */ + findContent : function($element) { + var $topScore = $element; + //If an element has the ARIA role of "main," it's safe to assume that it is the main content. + if ($element.is('[role=main]')) { + return $element; + } + if ($element.find('[role=main]').length) { + return $element.find('[role=main]').first(); + } + //If there are no paragraphs in the subject at all, we return the subject. + if ($element.find('p').length === 0) { + return $element; + } + $element.find('p').each(function() { + var $parent = $(this).parent(); + var element = $parent.get(0); + var contentScore = $parent.data('content-score') || 0; + if (!$parent.data('content-score')) { + + contentScore = $parent.find('p').length; + + if (element.className.match(/(comment|meta|footer|footnote)/)) { + contentScore -= 50; + } + else { + if (element.className.match(/((^|\\s)(post|hentry|entry[-]?(content|text|body)?|article[-]?(content|text|body)?)(\\s|$))/)) { + contentScore += 25; + } + } + + if (element.id.match(/(comment|meta|footer|footnote)/)) { + contentScore -= 50; + } + else { + if (element.id.match(/^(post|hentry|entry[-]?(content|text|body)?|article[-]?(content|text|body)?)$/)) { + contentScore += 25; + } + } + $parent.data('content-score', contentScore); + } + contentScore += $(this).text().split(',').length; + if (typeof $topScore.data('content-score') === 'undefined' || contentScore > $topScore.data('content-score')) { + $topScore = $parent; + } + }); + return $topScore; + } +}; + +quail.components.convertToPx = function(unit) { + if (unit.search('px') > -1) { + return parseInt(unit, 10); + } + var $test = $('
 
').appendTo(quail.html); + var height = $test.height(); + $test.remove(); + return height; +}; + +quail.components.event = function(quail, test, Case, options) { + var $scope = test.get('$scope'); + var $items = options.selector && $scope.find(options.selector) || $scope.find('*'); + var searchEvent = options.searchEvent || ''; + var correspondingEvent = options.correspondingEvent || ''; + $items.each(function() { + var eventName = searchEvent.replace('on', ''); + var hasOnListener = quail.components.hasEventListener($(this), eventName); + // Determine if the element has jQuery listeners for the event. + var jqevents; + if ($._data) { + jqevents = $._data(this, 'events'); + } + var hasjQueryOnListener = jqevents && jqevents[eventName] && !!jqevents[eventName].length; + var hasCorrespondingEvent = !!correspondingEvent.length; + var hasSpecificCorrespondingEvent = quail.components.hasEventListener($(this), correspondingEvent.replace('on', '')); + var expected = $(this).closest('.quail-test').data('expected'); + var _case = test.add(Case({ + element: this, + expected: expected + })); + if ((hasOnListener || hasjQueryOnListener) && (!hasCorrespondingEvent || !hasSpecificCorrespondingEvent)) { + _case.set({status: 'failed'}); + } + else { + _case.set({status: 'passed'}); + } + }); +}; + +quail.components.hasEventListener = function(element, event) { + if (typeof $(element).attr('on' + event) !== 'undefined') { + return true; + } + // jQuery events are stored in private objects + if ($._data($(element)[0], 'events') && + typeof $._data($(element)[0], 'events')[event] !== 'undefined') { + return true; + } + // Certain elements always have default events, so we create a new element to compare default events. + if ($(element).is('a[href], input, button, video, textarea') && + typeof $(element)[0][event] !== 'undefined' && + (event === 'click' || event === 'focus')) { + if ($(element)[0][event].toString().search(/^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i) > -1) { + return false; + } + } + return typeof $(element)[0][event] !== 'undefined'; +}; + +quail.components.headingLevel = function(quail, test, Case, options) { + var priorLevel = false; + test.get('$scope').find(':header').each(function() { + var level = parseInt($(this).get(0).tagName.substr(-1, 1), 10); + var element = this; + if (priorLevel === options.headingLevel && level > priorLevel + 1) { + test.add(Case({ + element: element, + // @todo, make the expected property retrievable through a callback so + // that we don't need to overload a test with this kind of logic. + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + status: 'passed' + })); + } + priorLevel = level; + }); +}; + +quail.components.htmlSource = { + + getHtml: function(callback) { + var that = this; + if (typeof quail.options.htmlSource !== 'undefined' && quail.options.htmlSource) { + callback(quail.options.htmlSource, that.parseHtml(quail.options.htmlSource)); + return; + } + var data = $.ajax({ url : window.location.href, async : false }); + if (data && typeof data.responseText !== 'undefined') { + callback(data.responseText, that.parseHtml(data.responseText)); + } + }, + + traverse: function(parsed, callback, number, alreadyCalled) { + var that = this; + if (typeof alreadyCalled === 'undefined') { + callback(parsed, number, false); + } + if (typeof parsed.children !== 'undefined') { + parsed.childCount = 1; + $.each(parsed.children, function(index, child) { + callback(child, parsed.childCount, parsed); + that.traverse(child, callback, parsed.childCount, true); + if (child.type === 'tag') { + parsed.childCount++; + } + }); + } + if ($.isArray(parsed)) { + $.each(parsed, function(index, element) { + that.traverse(element, callback); + }); + } + }, + + addSelector: function(element, childNumber, parent) { + if (element.type !== 'tag' || typeof element.name === 'undefined') { + return; + } + if (typeof element.selector === 'undefined') { + element.selector = (parent && typeof parent.selector !== 'undefined') ? parent.selector.slice() : []; + } + else { + return; + } + var selector = element.name; + if (typeof element.attributes !== 'undefined') { + if (typeof element.attributes.id !== 'undefined') { + selector += '#' + element.attributes.id[0]; + } + else { + if (typeof element.attributes.class !== 'undefined') { + selector += '.' + element.attributes.class[0].replace(/\s/, '.'); + } + } + } + + if (childNumber && (typeof element.attributes === 'undefined' || typeof element.attributes.id === 'undefined')) { + selector += ':nth-child('+ childNumber + ')'; + } + element.selector.push(selector); + return element.selector; + }, + + parseHtml: function(html) { + if (typeof Tautologistics === 'undefined') { + return false; + } + // NodeHtmlParser chokes on doctype tags + html = html.replace(/]*)>/g, ''); + var handler = new Tautologistics.NodeHtmlParser.HtmlBuilder(function() { }, { }); + var parser = new Tautologistics.NodeHtmlParser.Parser(handler); + parser.parseComplete(html); + var parsed = handler.dom; + var that = this; + // Traverse through the HTML objects and add a selector property + this.traverse(parsed, that.addSelector); + return parsed; + } +}; + +if (typeof Tautologistics !== 'undefined') { + var Mode = { + Text: 'text', + Tag: 'tag', + Attr: 'attr', + CData: 'cdata', + Comment: 'comment' + }; + + Tautologistics.NodeHtmlParser.HtmlBuilder.prototype.write = function(element) { + // this._raw.push(element); + if (this._done) { + this.handleCallback(new Error("Writing to the builder after done() called is not allowed without a reset()")); + } + if (this._options.includeLocation) { + if (element.type !== Mode.Attr) { + element.location = this._getLocation(); + this._updateLocation(element); + } + } + if (element.type === Mode.Text && this._options.ignoreWhitespace) { + if (HtmlBuilder.reWhitespace.test(element.data)) { + return; + } + } + var parent; + var node; + if (!this._tagStack.last()) { // There are no parent elements + // If the element can be a container, add it to the tag stack and the top level list + if (element.type === Mode.Tag) { + if (element.name.charAt(0) !== "/") { // Ignore closing tags that obviously don't have an opening tag + node = this._copyElement(element); + node.closingTag = true; + this.dom.push(node); + if (!this.isEmptyTag(node)) { // Don't add tags to the tag stack that can't have children + this._tagStack.push(node); + } + this._lastTag = node; + } + } else if (element.type === Mode.Attr && this._lastTag) { + if (!this._lastTag.attributes) { + this._lastTag.attributes = {}; + } + if (typeof this._lastTag.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()] === 'undefined') { + this._lastTag.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()] = []; + } + this._lastTag.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()].push(element.data); + } else { // Otherwise just add to the top level list + this.dom.push(this._copyElement(element)); + } + } + else { + parent = this._tagStack.last(); + + // There are parent elements + // If the element can be a container, add it as a child of the element + // on top of the tag stack and then add it to the tag stack + if (element.type === Mode.Tag) { + if (element.name.charAt(0) === "/") { + // This is a closing tag, scan the tagStack to find the matching opening tag + // and pop the stack up to the opening tag's parent + var baseName = this._options.caseSensitiveTags ? + element.name.substring(1) : + element.name.substring(1).toLowerCase(); + if (parent.name === baseName) { + parent.closingTag = true; + } + if (!this.isEmptyTag(element)) { + var pos = this._tagStack.length - 1; + while (pos > -1 && this._tagStack[pos--].name !== baseName) { + } + if (pos > -1 || this._tagStack[0].name === baseName) { + while (pos < this._tagStack.length - 1) { + this._tagStack.pop(); + } + } + } + } + else { // This is not a closing tag + if (element.type === Mode.Attr) { + if (!parent.attributes) { + parent.attributes = {}; + } + if (typeof parent.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()] === 'undefined') { + parent.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()] = []; + } + parent.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()].push(element.data); + } else { + node = this._copyElement(element); + if (!parent.children) { + parent.children = []; + } + parent.children.push(node); + if (!this.isEmptyTag(node)) { // Don't add tags to the tag stack that can't have children + this._tagStack.push(node); + } + if (element.type === Mode.Tag) { + this._lastTag = node; + } + } + } + } + else { // This is not a container element + parent = this._tagStack.last(); + if (element.type === Mode.Attr) { + if (!parent.attributes) { + parent.attributes = {}; + } + if (typeof parent.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()] === 'undefined') { + parent.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()] = []; + } + parent.attributes[this._options.caseSensitiveAttr ? element.name : element.name.toLowerCase()].push(element.data); + } else { + if (!parent.children) { + parent.children = []; + } + parent.children.push(this._copyElement(element)); + } + } + } + }; +} + +var htmlTagValidator=function(){ + var startingTagFirstChar="<", + startingTagLastChar=">", + closingTagSecondChar="/", + selfClosingTagSecondToLastChar="/", + commentSecondCharacter="!", + doctypeSecondCharacterPattern=new RegExp("[dD]"), + startTagPattern=new RegExp("[a-z0-9-]"), + commentPattern=new RegExp("^"); + + var parserFunc, previousParserFunc, currentTagName, startingTags, + characterIndex, currentComment, options; + + var selfClosing=[ + "area", + "base", + "br", + "col", + "command", + "embed", + "hr", + "img", + "input", + "keygen", + "link", + "meta", + "param", + "source", + "track", + "wbr" + ]; + + var ignoreWithin=[ + "pre", + "code", + "textarea", + "script", + "style" + ]; + + var optionalClosing=[ + "p", + "li", + "tr", + "th", + "td" + ]; + + var tagObject=function(lIndex, cIndex){ + return {name: currentTagName, line: lIndex + 1, char: cIndex}; + }; + + var throwEndingTagError=function(tagObj){ + var newError=new Error("Ending tag not found for: " + tagObj.name + " at line: " + tagObj.line + " char: " + tagObj.char + " starting tags: " + startingTags[0].name); + newError.lineData=tagObj; + throw newError; + }; + + var throwEndingCommentError=function(commentObj){ + var newError=new Error("Comment ending not found for: `comment` at line: " + commentObj.line + " char: " + commentObj.char); + newError.lineData=commentObj; + throw newError; + }; + + var throwSelfClosingFormatError=function(tagObj){ + var newError=new Error("Ending `/` not found for: `" + tagObj.name + "` at line: " + tagObj.line + " char: " + tagObj.char); + newError.lineData=tagObj.name; + throw newError; + }; + + var setParserFunc=function(func){ + previousParserFunc=parserFunc; + parserFunc=func; + }; + + var goBackNumChars=function(num){ + characterIndex-=num; + }; + + // Handle starting html tags + var startingTagNameFinder=function startingTagNameFinder(character, lIndex, cIndex){ + // If the character matches the matcher for approved tag name characters add it to + // the currentTagName + if (startTagPattern.test(character)) { + currentTagName+=character; + // If the character matches the closing tag second character set the finder function + // to the endingTagNameFinder + } else if (character === closingTagSecondChar) { + setParserFunc(endingTagNameFinder); + // If the character looks like a commentSecondCharacter(!) then check to see if it's + // really a comment or a comment with the commentOrDoctypeFinder + } else if (character === commentSecondCharacter) { + currentTagName=""; + setParserFunc(commentOrDoctypeFinder); + + // If the current tag name is a self closing tag, start looking for a new + // tag name with startingTagBeginningFinder + } else if (selfClosing.indexOf(currentTagName) > -1) { + if (options['strict_self_closing_tags']) { + setParserFunc(selfClosingEndingSlashFinder); + } else { + currentTagName=""; + setParserFunc(startingTagBeginningFinder); + } + + // If nothing else trips a check, the record the currentTag name and either: + // ignore all the contents of the tag is an ignoredWithin tag (script, style, pre, etc) + // or + // start looking for the matching ending tag. + } else { + var tagObj=tagObject(lIndex, cIndex); + startingTags.push(tagObj); + + if (ignoreWithin.indexOf(currentTagName) > -1) { + currentTagName=""; + goBackNumChars(1); + setParserFunc(ignoredWithinEndingTagStartFinder); + } else { + currentTagName=""; + goBackNumChars(1); + setParserFunc(startingTagEndingFinder); + } + } + }; + + var selfClosingEndingSlashFinder=function selfClosingEndingSlashFinder(character, lIndex, cIndex){ + if (character === selfClosingTagSecondToLastChar) { + currentTagName=''; + setParserFunc(endingTagBeginningFinder); + } else if (character === startingTagLastChar) { + throwSelfClosingFormatError(tagObject(lIndex, cIndex)); + } + }; + + var startingTagEndingFinder=function startingTagEndingFinder(character){ + if (character === startingTagLastChar) { + setParserFunc(endingTagBeginningFinder); + } + }; + + var startingTagBeginningFinder=function startingTagBeginningFinder(character){ + if (character === startingTagFirstChar) { + setParserFunc(startingTagNameFinder); + } + }; + + var endingTagNameFinder=function endingTagNameFinder(character){ + + function loopThroughTags () { + var lastStartTag=startingTags.pop(); + + // If the next tag in the startTags stack is the current tag, then we move on. + if (lastStartTag.name === currentTagName) { + setParserFunc(startingTagBeginningFinder); + } + // If the next tag in the startingTags is an optional tag, try popping it + // and repeating this process. + else if(optionalClosing.indexOf(lastStartTag.name) > -1) { + loopThroughTags(); + } + // If this is not an optional closing tag, then the mismatch is an error. + else { + throwEndingTagError(lastStartTag); + } + } + + if (startTagPattern.test(character)) { + currentTagName+=character; + } + else { + loopThroughTags(); + currentTagName=""; + } + }; + + var endingTagSlashFinder=function endingTagSlashFinder(character){ + if (character === closingTagSecondChar) { + setParserFunc(endingTagNameFinder); + } else { + goBackNumChars(1); + setParserFunc(startingTagNameFinder); + } + }; + + var endingTagBeginningFinder=function endingTagBeginningFinder(character){ + if (character === startingTagFirstChar) { + setParserFunc(endingTagSlashFinder); + } + }; + + // Ignore with ignored tag list ex. pre, script, code + var ignoredWithinEndingTagStartFinder=function ignoredWithinEndingTagStartFinder(character){ + if (character === startingTagFirstChar) { + setParserFunc(ignoredWithinEndingTagSlashFinder); + } + }; + + var ignoredWithinEndingTagSlashFinder=function ignoredWithinEndingTagSlashFinder(character){ + if (character === closingTagSecondChar) { + setParserFunc(ignoredWithinEndingTagNameFinder); + } + }; + + var ignoredWithinEndingTagNameFinder=function ignoredWithinEndingTagNameFinder(character){ + if (startTagPattern.test(character)) { + currentTagName+=character; + } else { + var lastStartTag=startingTags.pop(); + + if (lastStartTag.name === currentTagName) { + setParserFunc(startingTagBeginningFinder); + } else { + throwEndingTagError(lastStartTag); + } + currentTagName=""; + } + }; + + // Comments and doctypes both start with ` 0) { + var lastStartTag=startingTags[startingTags.length - 1]; + + if(optionalClosing.indexOf(lastStartTag.name) === -1) { + throwEndingTagError(lastStartTag); + } + } + returnState = null; + } catch (e) { + returnState = e.message; + } finally { + return returnState; + } + }; + + return checkTags; +}; + +quail.components.htmlTagValidator=htmlTagValidator(); + +quail.components.label = function(quail, test, Case, options) { + var $scope = test.get('$scope'); + $scope.each(function() { + var $local = $(this); + $local.find(options.selector).each(function() { + if ((!$(this).parent('label').length || + !$local.find('label[for=' + $(this).attr('id') + ']').length || + !quail.containsReadableText($(this).parent('label'))) && + (!quail.containsReadableText($local.find('label[for=' + $(this).attr('id') + ']')))) { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'passed' + })); + } + }); + }); +}; + +quail.components.labelProximity = function(quail, test, Case, options) { + var $scope = test.get('$scope'); + $scope.find(options.selector).each(function() { + var $label = $scope.find('label[for=' + $(this).attr('id') + ']').first(); + if (!$label.length) { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'failed' + })); + } + else if (!$(this).parent().is($label.parent())) { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'passed' + })); + } + }); +}; + +quail.components.language = { + + /** + * The maximum distance possible between two trigram models. + */ + maximumDistance: 300, + + /** + * Regular expressions to capture unicode blocks that are either + * explicitly right-to-left or left-to-right. + */ + textDirection : { + rtl : /[\u0600-\u06FF]|[\u0750-\u077F]|[\u0590-\u05FF]|[\uFE70-\uFEFF]/mg, + ltr : /[\u0041-\u007A]|[\u00C0-\u02AF]|[\u0388-\u058F]/mg + }, + + /** + * Special characters that indicate text direction changes. + */ + textDirectionChanges : { + rtl : /[\u200E]|‏/mg, + ltr : /[\u200F]|‎/mg + }, + + /** + * List of single-script blocks that encapsulate a list of languages. + */ + scripts: { + basicLatin: { + regularExpression: /[\u0041-\u007F]/g, + languages: [ + "ceb", + "en", + "eu", + "ha", + "haw", + "id", + "la", + "nr", + "nso", + "so", + "ss", + "st", + "sw", + "tlh", + "tn", + "ts", + "xh", + "zu", + "af", + "az", + "ca", + "cs", + "cy", + "da", + "de", + "es", + "et", + "fi", + "fr", + "hr", + "hu", + "is", + "it", + "lt", + "lv", + "nl", + "no", + "pl", + "pt", + "ro", + "sk", + "sl", + "sq", + "sv", + "tl", + "tr", + "ve", + "vi" + ] + }, + arabic: { + regularExpression: /[\u0600-\u06FF]/g, + languages: [ + "ar", + "fa", + "ps", + "ur" + ] + }, + cryllic: { + regularExpression: /[\u0400-\u04FF]|[\u0500-\u052F]/g, + languages: [ + "bg", + "kk", + "ky", + "mk", + "mn", + "ru", + "sr", + "uk", + "uz" + ] + } + }, + + /** + * List of regular expressions that capture only unicode text blocks that are + * associated with a single language. + */ + scriptSingletons : { + bn: /[\u0980-\u09FF]/g, + bo: /[\u0F00-\u0FFF]/g, + el: /[\u0370-\u03FF]/g, + gu: /[\u0A80-\u0AFF]/g, + he: /[\u0590-\u05FF]/g, + hy: /[\u0530-\u058F]/g, + ja: /[\u3040-\u309F]|[\u30A0-\u30FF]/g, + ka: /[\u10A0-\u10FF]/g, + km: /[\u1780-\u17FF]|[\u19E0-\u19FF]/g, + kn: /[\u0C80-\u0CFF]/g, + ko: /[\u1100-\u11FF]|[\u3130-\u318F]|[\uAC00-\uD7AF]/g, + lo: /[\u0E80-\u0EFF]/g, + ml: /[\u0D00-\u0D7F]/g, + mn: /[\u1800-\u18AF]/g, + or: /[\u0B00-\u0B7F]/g, + pa: /[\u0A00-\u0A7F]/g, + si: /[\u0D80-\u0DFF]/g, + ta: /[\u0B80-\u0BFF]/g, + te: /[\u0C00-\u0C7F]/g, + th: /[\u0E00-\u0E7F]/g, + zh: /[\u3100-\u312F]|[\u2F00-\u2FDF]/g + }, + + /** + * Determines the document's language by looking at + * first the browser's default, then the HTML element's "lang" attribute, + * then the "lang" attribute of the element passed to quail. + */ + getDocumentLanguage: function(scope, returnIso) { + var language = navigator.language || navigator.userLanguage; + if (typeof quail.options.language !== 'undefined') { + language = quail.options.language; + } + if (scope.parents('[lang]').length) { + language = scope.parents('[lang]:first').attr('lang'); + } + if (typeof scope.attr('lang') !== 'undefined') { + language = scope.attr('lang'); + } + language = language.toLowerCase().trim(); + if (returnIso) { + return language.split('-')[0]; + } + return language; + } +}; + +quail.components.placeholder = function(quail, test, Case, options) { + + var resolve = function (element, resolution) { + test.add(Case({ + element: element, + expected: $(element).closest('.quail-test').data('expected'), + status: resolution + })); + }; + + test.get('$scope').find(options.selector).each(function() { + var text = ''; + if($(this).css('display') === 'none' && !$(this).is('title')){ + resolve(this, 'inapplicable'); + return; + } + if (typeof options.attribute !== 'undefined') { + if ((typeof $(this).attr(options.attribute) === 'undefined' || + (options.attribute === 'tabindex' && + $(this).attr(options.attribute) <= 0 + ) + ) && + !options.content + ) { + resolve(this, 'failed'); + return; + } + else { + if ($(this).attr(options.attribute) && $(this).attr(options.attribute) !== 'undefined') { + text += $(this).attr(options.attribute); + } + } + } + if (typeof options.attribute === 'undefined' || + !options.attribute || + options.content) { + text += $(this).text(); + $(this).find('img[alt]').each(function() { + text += $(this).attr('alt'); + }); + } + if (typeof text === 'string' && text.length > 0) { + text = quail.cleanString(text); + var regex = /^([0-9]*)(k|kb|mb|k bytes|k byte)$/g; + var regexResults = regex.exec(text.toLowerCase()); + if (regexResults && regexResults[0].length) { + resolve(this, 'failed'); + } + else if (options.empty && quail.isUnreadable(text)) { + resolve(this, 'failed'); + } + else if (quail.strings.placeholders.indexOf(text) > -1 ) { + resolve(this, 'failed'); + } + // It passes. + else { + resolve(this, 'passed'); + } + } + else { + if (options.empty && typeof text !== 'number') { + resolve(this, 'failed'); + } + } + }); +}; + +quail.components.resolveExpectation = function(element, caseID) { + var $scope = $(element).closest('.quail-test'); + var expected = $scope.data('expected'); + var result; + // If no caseID is supplied, assume that the expected data attribute could + // contain a simple, singular expectation. + if (!caseID) { + result = $scope.data('expected'); + } + var expectations = typeof expected === 'string' && expected.split('|'); + // This might be a single case ID expectation. + if (caseID && expectations.length === 0 && expected.indexOf(':') > -1) { + expectations = [expected]; + } + + if (expectations.length > 0 && element.nodeType === 1) { + var condition, $el; + // Split apart the compound expectations. + for (var i = 0, il = expectations.length; i < il; ++i) { + condition = expectations[i].split(':'); + // If a caseID is supplied, assume the expect targets them. + if (caseID) { + if (condition[0] === caseID) { + if (!condition[1] || condition[1] === 'ignore') { + return; + } + else { + // Retrieve the expectation for this element. + result = condition[1]; + } + } + } + // Try to use the condition zero element as a selector. + else { + $el = $(condition[0], $scope); + if ($el.length === 1 && element === $el.get(0)) { + if (!condition[1] || condition[1] === 'ignore') { + return; + } + else { + // Retrieve the expectation for this element. + result = condition[1]; + } + } + } + } + } + // Otherwise the expectation is given as a simple value. + return result; +}; + +quail.components.selector = function (quail, test, Case, options) { + this.get('$scope').each(function() { + var $scope = $(this); + var candidates = $(this).find(options.selector); + // Passes. + if (!candidates.length) { + // Passes. + test.add(quail.lib.Case({ + element: undefined, + expected: $scope.data('expected') || $scope.find('[data-expected]').data('expected'), + // status: 'passed' + status: (options.test ? 'inapplicable' : 'passed') + })); + } + else { + // Fails. + candidates.each(function () { + var status, + $this = $(this); + + // If a test is defined, then use it + if (options.test && !$this.is(options.test)) { + status = 'passed'; + } else { + status = 'failed'; + } + + test.add(quail.lib.Case({ + element: this, + expected: $this.closest('.quail-test').data('expected'), + status: status + })); + }); + } + }); +}; + +quail.statistics = { + + setDecimal : function( num, numOfDec ){ + var pow10s = Math.pow( 10, numOfDec || 0 ); + return ( numOfDec ) ? Math.round( pow10s * num ) / pow10s : num; + }, + + average : function( numArr, numOfDec ){ + var i = numArr.length, + sum = 0; + while( i-- ){ + sum += numArr[ i ]; + } + return quail.statistics.setDecimal( (sum / numArr.length ), numOfDec ); + }, + + variance : function( numArr, numOfDec ){ + var avg = quail.statistics.average( numArr, numOfDec ), + i = numArr.length, + v = 0; + + while( i-- ){ + v += Math.pow( (numArr[ i ] - avg), 2 ); + } + v /= numArr.length; + return quail.statistics.setDecimal( v, numOfDec ); + }, + + standardDeviation : function( numArr, numOfDec ){ + var stdDev = Math.sqrt( quail.statistics.variance( numArr, numOfDec ) ); + return quail.statistics.setDecimal( stdDev, numOfDec ); + } +}; + +quail.components.textStatistics = { + + cleanText : function(text) { + return text.replace(/[,:;()\-]/, ' ') + .replace(/[\.!?]/, '.') + .replace(/[ ]*(\n|\r\n|\r)[ ]*/, ' ') + .replace(/([\.])[\. ]+/, '$1') + .replace(/[ ]*([\.])/, '$1') + .replace(/[ ]+/, ' ') + .toLowerCase(); + + }, + + sentenceCount : function(text) { + return text.split('.').length + 1; + }, + + wordCount : function(text) { + return text.split(' ').length + 1; + }, + + averageWordsPerSentence : function(text) { + return this.wordCount(text) / this.sentenceCount(text); + }, + + averageSyllablesPerWord : function(text) { + var that = this; + var count = 0; + var wordCount = that.wordCount(text); + if (!wordCount) { + return 0; + } + $.each(text.split(' '), function(index, word) { + count += that.syllableCount(word); + }); + return count / wordCount; + }, + + syllableCount : function(word) { + var matchedWord = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '') + .match(/[aeiouy]{1,2}/g); + if (!matchedWord || matchedWord.length === 0) { + return 1; + } + return matchedWord.length; + } +}; +quail.components.video = { + + /** + * Iterates over listed video providers and runs their `isVideo` method. + * @param jQuery $element + * An element in a jQuery wrapper. + * + * @return Boolean + * Whether the element is a video. + */ + isVideo : function(element) { + var isVideo = false; + $.each(this.providers, function() { + if (element.is(this.selector) && this.isVideo(element)) { + isVideo = true; + } + }); + return isVideo; + }, + + findVideos : function(element, callback) { + $.each(this.providers, function(name, provider) { + element.find(this.selector).each(function() { + var video = $(this); + if (provider.isVideo(video)) { + provider.hasCaptions(video, callback); + } + }); + }); + }, + + providers : { + + youTube : { + + selector : 'a, iframe', + + apiUrl : 'http://gdata.youtube.com/feeds/api/videos/?q=%video&caption&v=2&alt=json', + + isVideo : function(element) { + return (this.getVideoId(element) !== false) ? true : false; + }, + + getVideoId : function(element) { + var attribute = (element.is('iframe')) ? 'src' : 'href'; + var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&\?]*).*/; + var match = element.attr(attribute).match(regExp); + if (match && match[7].length === 11) { + return match[7]; + } + return false; + }, + + hasCaptions : function(element, callback) { + var videoId = this.getVideoId(element); + $.ajax({url : this.apiUrl.replace('%video', videoId), + async : false, + dataType : 'json', + success : function(data) { + callback(element, (data.feed.openSearch$totalResults.$t > 0)); + } + }); + } + }, + + flash : { + + selector : 'object', + + isVideo : function(element) { + var isVideo = false; + if (element.find('param').length === 0) { + return false; + } + element.find('param[name=flashvars]').each(function() { + if ($(this).attr('value').search(/\.(flv|mp4)/i) > -1) { + isVideo = true; + } + }); + return isVideo; + }, + + hasCaptions : function(element, callback) { + var hasCaptions = false; + element.find('param[name=flashvars]').each(function() { + if (($(this).attr('value').search('captions') > -1 && + $(this).attr('value').search('.srt') > -1) || + $(this).attr('value').search('captions.pluginmode') > -1) { + hasCaptions = true; + } + }); + callback(element, hasCaptions); + } + }, + + videoElement : { + + selector : 'video', + + isVideo : function(element) { + return element.is('video'); + }, + + hasCaptions : function(element, callback) { + var $captions = element.find('track[kind=subtitles], track[kind=captions]'); + if (!$captions.length) { + callback(element, false); + return; + } + var language = quail.components.language.getDocumentLanguage(element, true); + if (element.parents('[lang]').length) { + language = element.parents('[lang]').first().attr('lang').split('-')[0]; + } + var foundLanguage = false; + $captions.each(function() { + if (!$(this).attr('srclang') || $(this).attr('srclang').toLowerCase() === language) { + foundLanguage = true; + try{ + var request = $.ajax({ url: $(this).attr('src'), + type: 'HEAD', + async: false, + error: function() { } + }); + if (request.status === 404) { + foundLanguage = false; + } + } + catch(e) { + + } + } + }); + if (!foundLanguage) { + callback(element, false); + return; + } + callback(element, true); + } + } + } + +}; + +quail.strings.colors = { + "aliceblue": "f0f8ff", + "antiquewhite": "faebd7", + "aqua": "00ffff", + "aquamarine": "7fffd4", + "azure": "f0ffff", + "beige": "f5f5dc", + "bisque": "ffe4c4", + "black": "000000", + "blanchedalmond": "ffebcd", + "blue": "0000ff", + "blueviolet": "8a2be2", + "brown": "a52a2a", + "burlywood": "deb887", + "cadetblue": "5f9ea0", + "chartreuse": "7fff00", + "chocolate": "d2691e", + "coral": "ff7f50", + "cornflowerblue": "6495ed", + "cornsilk": "fff8dc", + "crimson": "dc143c", + "cyan": "00ffff", + "darkblue": "00008b", + "darkcyan": "008b8b", + "darkgoldenrod": "b8860b", + "darkgray": "a9a9a9", + "darkgreen": "006400", + "darkkhaki": "bdb76b", + "darkmagenta": "8b008b", + "darkolivegreen": "556b2f", + "darkorange": "ff8c00", + "darkorchid": "9932cc", + "darkred": "8b0000", + "darksalmon": "e9967a", + "darkseagreen": "8fbc8f", + "darkslateblue": "483d8b", + "darkslategray": "2f4f4f", + "darkturquoise": "00ced1", + "darkviolet": "9400d3", + "deeppink": "ff1493", + "deepskyblue": "00bfff", + "dimgray": "696969", + "dodgerblue": "1e90ff", + "firebrick": "b22222", + "floralwhite": "fffaf0", + "forestgreen": "228b22", + "fuchsia": "ff00ff", + "gainsboro": "dcdcdc", + "ghostwhite": "f8f8ff", + "gold": "ffd700", + "goldenrod": "daa520", + "gray": "808080", + "green": "008000", + "greenyellow": "adff2f", + "honeydew": "f0fff0", + "hotpink": "ff69b4", + "indianred": "cd5c5c", + "indigo": "4b0082", + "ivory": "fffff0", + "khaki": "f0e68c", + "lavender": "e6e6fa", + "lavenderblush": "fff0f5", + "lawngreen": "7cfc00", + "lemonchiffon": "fffacd", + "lightblue": "add8e6", + "lightcoral": "f08080", + "lightcyan": "e0ffff", + "lightgoldenrodyellow": "fafad2", + "lightgrey": "d3d3d3", + "lightgreen": "90ee90", + "lightpink": "ffb6c1", + "lightsalmon": "ffa07a", + "lightseagreen": "20b2aa", + "lightskyblue": "87cefa", + "lightslategray": "778899", + "lightsteelblue": "b0c4de", + "lightyellow": "ffffe0", + "lime": "00ff00", + "limegreen": "32cd32", + "linen": "faf0e6", + "magenta": "ff00ff", + "maroon": "800000", + "mediumaquamarine": "66cdaa", + "mediumblue": "0000cd", + "mediumorchid": "ba55d3", + "mediumpurple": "9370d8", + "mediumseagreen": "3cb371", + "mediumslateblue": "7b68ee", + "mediumspringgreen": "00fa9a", + "mediumturquoise": "48d1cc", + "mediumvioletred": "c71585", + "midnightblue": "191970", + "mintcream": "f5fffa", + "mistyrose": "ffe4e1", + "moccasin": "ffe4b5", + "navajowhite": "ffdead", + "navy": "000080", + "oldlace": "fdf5e6", + "olive": "808000", + "olivedrab": "6b8e23", + "orange": "ffa500", + "orangered": "ff4500", + "orchid": "da70d6", + "palegoldenrod": "eee8aa", + "palegreen": "98fb98", + "paleturquoise": "afeeee", + "palevioletred": "d87093", + "papayawhip": "ffefd5", + "peachpuff": "ffdab9", + "peru": "cd853f", + "pink": "ffc0cb", + "plum": "dda0dd", + "powderblue": "b0e0e6", + "purple": "800080", + "red": "ff0000", + "rosybrown": "bc8f8f", + "royalblue": "4169e1", + "saddlebrown": "8b4513", + "salmon": "fa8072", + "sandybrown": "f4a460", + "seagreen": "2e8b57", + "seashell": "fff5ee", + "sienna": "a0522d", + "silver": "c0c0c0", + "skyblue": "87ceeb", + "slateblue": "6a5acd", + "slategray": "708090", + "snow": "fffafa", + "springgreen": "00ff7f", + "steelblue": "4682b4", + "tan": "d2b48c", + "teal": "008080", + "thistle": "d8bfd8", + "tomato": "ff6347", + "turquoise": "40e0d0", + "violet": "ee82ee", + "wheat": "f5deb3", + "white": "ffffff", + "whitesmoke": "f5f5f5", + "yellow": "ffff00", + "yellowgreen": "9acd32" +}; +quail.strings.languageCodes = [ + "bh", + "bi", + "nb", + "bs", + "br", + "bg", + "my", + "es", + "ca", + "km", + "ch", + "ce", + "ny", + "ny", + "zh", + "za", + "cu", + "cu", + "cv", + "kw", + "co", + "cr", + "hr", + "cs", + "da", + "dv", + "dv", + "nl", + "dz", + "en", + "eo", + "et", + "ee", + "fo", + "fj", + "fi", + "nl", + "fr", + "ff", + "gd", + "gl", + "lg", + "ka", + "de", + "ki", + "el", + "kl", + "gn", + "gu", + "ht", + "ht", + "ha", + "he", + "hz", + "hi", + "ho", + "hu", + "is", + "io", + "ig", + "id", + "ia", + "ie", + "iu", + "ik", + "ga", + "it", + "ja", + "jv", + "kl", + "kn", + "kr", + "ks", + "kk", + "ki", + "rw", + "ky", + "kv", + "kg", + "ko", + "kj", + "ku", + "kj", + "ky", + "lo", + "la", + "lv", + "lb", + "li", + "li", + "li", + "ln", + "lt", + "lu", + "lb", + "mk", + "mg", + "ms", + "ml", + "dv", + "mt", + "gv", + "mi", + "mr", + "mh", + "ro", + "ro", + "mn", + "na", + "nv", + "nv", + "nd", + "nr", + "ng", + "ne", + "nd", + "se", + "no", + "nb", + "nn", + "ii", + "ny", + "nn", + "ie", + "oc", + "oj", + "cu", + "cu", + "cu", + "or", + "om", + "os", + "os", + "pi", + "pa", + "ps", + "fa", + "pl", + "pt", + "pa", + "ps", + "qu", + "ro", + "rm", + "rn", + "ru", + "sm", + "sg", + "sa", + "sc", + "gd", + "sr", + "sn", + "ii", + "sd", + "si", + "si", + "sk", + "sl", + "so", + "st", + "nr", + "es", + "su", + "sw", + "ss", + "sv", + "tl", + "ty", + "tg", + "ta", + "tt", + "te", + "th", + "bo", + "ti", + "to", + "ts", + "tn", + "tr", + "tk", + "tw", + "ug", + "uk", + "ur", + "ug", + "uz", + "ca", + "ve", + "vi", + "vo", + "wa", + "cy", + "fy", + "wo", + "xh", + "yi", + "yo", + "za", + "zu" +]; +quail.strings.newWindow = [ + /new (browser )?(window|frame)/, + /popup (window|frame)/ +]; +quail.strings.placeholders = [ + "title", + "untitled", + "untitled document", + "this is the title", + "the title", + "content", + " ", + "new page", + "new", + "nbsp", + " ", + "spacer", + "image", + "img", + "photo", + "frame", + "frame title", + "iframe", + "iframe title", + "legend" +]; +quail.strings.redundant = { + "inputImage":[ + "submit", + "button" + ], + "link":[ + "link to", + "link", + "go to", + "click here", + "link", + "click", + "more" + ], + "required":[ + "*" + ] +}; +quail.strings.siteMap = [ + "site map", + "map", + "sitemap" +]; +quail.strings.skipContent = [ + /(jump|skip) (.*) (content|main|post)/i +]; +quail.strings.suspiciousLinks = [ + "click here", + "click", + "more", + "here", + "read more", + "download", + "add", + "delete", + "clone", + "order", + "view", + "read", + "clic aquí", + "clic", + "haga clic", + "más", + "aquí", + "image" +]; +quail.strings.symbols = [ + "|", + "*", + /\*/g, + "
*", + '•', + '•', + '♦', + '›', + '»', + '‣', + '▶', + '.', + '◦', + '✓', + '◽', + '•', + '—', + '◾' +]; + +quail.KINGStrongList = function (quail, test, Case) { + test.get('$scope').find('strong').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + _case.set({ + 'status': $(this).parent().is('li') ? 'passed' : 'failed' + }); + }); +}; + +quail.KINGUseCurrencyAsSymbol = function (quail, test, Case) { + function testCurrencyFormat(index, element) { + // Detect dates with several separators. + var currencyNames = [ + 'dollar', + 'euro', + 'pound', + 'franc', + 'krona', + 'rupee', + 'ruble', + 'dinar' + ]; + // Test the words and any eventual extra letters for s and all. + var currencyReg = new RegExp('\\d{1,}\\s*(' + currencyNames.join('|') + ')\\w*\\b|(' + currencyNames.join('|') + ')\\w*\\b\\s*\\d{1,}', 'ig'); + + var text = quail.getTextContents($(element)); + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + _case.set({ + 'status': currencyReg.test(text) ? 'failed' : 'passed' + }); + } + test.get('$scope').find('p').each(testCurrencyFormat); +}; + +quail.KINGUseLongDateFormat = function (quail, test, Case) { + + function testDateFormat(index, element) { + // Detect dates with several separators. + var dateReg = /\d{1,2}([./-])\d{1,2}\1\d{2,4}/g; + var elemChildNodes = element.childNodes; + var issueOccured = false; + + // Lets find all the *direct* text node children. + var textNodeChildren = []; + var i = 0; + var childCount; + + for (childCount = elemChildNodes.length; i < childCount; i++) { + if (elemChildNodes[i].nodeType === Node.TEXT_NODE) { + textNodeChildren.push(elemChildNodes[i]); + } + } + + // Now we're going to check if any text node matches the pattern. + // Micro-optimization: check also if issueOccured == false, because we + // are not reporting more than one issue occurence. + for (i = 0; i < textNodeChildren.length && !issueOccured; i++) { + var curTextContent = textNodeChildren[i].nodeValue; + + if ( dateReg.test( curTextContent ) ) { + issueOccured = true; + } + } + + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + // Only test if there is one date in the wrong format and call it. + _case.set({ + 'status': issueOccured ? 'failed' : 'passed' + }); + } + + // Note it should also contain div, but that would lead to other issues. + var appliableElements = 'a, article, aside, b, blockquote, caption, cite, dd, del, div, em, figcaption, footer, h1, h2, h3, h4, h5, h6, header, i, label, legend, li, mark, nav, option, p, q, s, section, small, span, strong, sub, summary, sup, td, th, title, u'; + + test.get('$scope').find(appliableElements).each(testDateFormat); +}; + +quail.KINGUsePercentageWithSymbol = function (quail, test, Case) { + function testPercentFormat(index, element) { + // Detect dates with several separators. + var percentName = [ + 'percent', + 'pct\\.' + ]; + // Test the words and any eventual extra letters for s and all. + var percentReg = new RegExp('\\d{1,}\\s*(' + percentName.join('|') + ')|(' + percentName.join('|') + ')\\s*\\d{1,}', 'ig'); + + var text = quail.getTextContents($(element)); + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + _case.set({ + 'status': percentReg.test(text) ? 'failed' : 'passed' + }); + } + test.get('$scope').find('p').each(testPercentFormat); +}; + +quail.aAdjacentWithSameResourceShouldBeCombined = function(quail, test, Case) { + //var $applicableElements = test.get('$scope').find('a'); + + function findAdjacent(index, element) { + var $element = $(element); + var adjacentLinks = $element.find('a + a').length > 0; + var $links = $element.find('a'); + + // no adjacent links, exclude all. + if (!adjacentLinks) { + $links.each(excludeSingleLinks); + } + else { + $links.each(checkNextLink); + } + } + + function checkNextLink(index, element) { + var $element = $(element); + var thisHref = element.getAttribute('href'); + var $nextLink = $element.find('+ a'); + if (!$nextLink.length) { + // We're going over the second link. + return; + } + var nextHref = $nextLink[0].getAttribute('href'); + var status = 'passed'; + var _case = Case({ + element: element, + expected: $element.closest('.quail-test').data('expected') + }); + if (thisHref === nextHref) { + status = 'failed'; + } + + test.add(_case); + _case.set({'status': status}); + } + + function excludeSingleLinks(index, element) { + var _case = Case({ element: element }); + test.add(_case); + _case.set({ + 'status': 'inapplicable', + expected: $(element).closest('.quail-test').data('expected') + }); + } + + test.get('$scope').each(findAdjacent); +}; + +quail.aImgAltNotRepetitive = function(quail, test, Case) { + test.get('$scope').find('a img[alt]').each(function() { + var _case = test.add(Case({ + element: this + })); + var expected = $(this).closest('.quail-test').data('expected'); + if (quail.cleanString($(this).attr('alt')) === quail.cleanString($(this).parent('a').text())) { + _case.set({ + 'expected': expected, + 'status': 'failed' + }); + } + else { + _case.set({ + 'expected': expected, + 'status': 'passed' + }); + } + }); +}; + +quail.aInPHasADistinctStyle=function(quail, test, Case){ + + /** + * Checks if an element has a border set + * @param element + * @returns {boolean} + */ + function hasBorder(element) { + return (element.outerWidth() - element.innerWidth() > 0) || + (element.outerHeight() - element.innerHeight() > 0); + } + + /** + * Test if two elements have a distinct style from it's ancestor + * @param {jQuery node} $elm + * @param {jQuery node} $parent + * @return {boolean} + */ + function elmHasDistinctStyle($elm, $parent) { + var result = false; + var styleProperties = ['font-weight', 'font-style']; + var textDecoration = $elm.css('text-decoration'); + + if (textDecoration !== 'none' && + textDecoration !== $parent.css('text-decoration')) { + result = true; + } + + if ($elm.css('background-color') !== 'rgba(0, 0, 0, 0)') { + styleProperties.push('background'); + } + + $.each(styleProperties, function (i, styleProp) { + if (!result && $elm.css(styleProp) !== $parent.css(styleProp)) { + result = true; + } + }); + + return result || hasBorder($elm); + } + + function elmHasDistinctPosition($elm) { + var isBlock = ($elm.css('display') === 'block'); + var position = $elm.css('position'); + var isPositioned = position !== 'relative' && position !== 'static'; + return isBlock || isPositioned; + } + + // Ignore links where the p only contains white space, <, >, |, \, / and - chars + var allowedPText = /^([\s|-]|>|<|\\|\/|&(gt|lt);)*$/i; + + test.get('$scope').each(function () { + var $scope = $(this); + var anchors = $scope.find('p a[href]:visible'); + + anchors.each(function () { + var $this = $(this); + var $p = $this.closest('p'); + var $parent = $this.parent(); + + var _case=Case({ + element: this, + expected: $this.closest('.quail-test').data('expected') + }); + test.add(_case); + + var aText = $this.text().trim(); + + // Get all text of the p element with all anchors removed + var pText = $p.clone().find('a[href]').remove().end().text(); + + if (aText === '' || pText.match(allowedPText)) { + _case.set('status', 'inapplicable'); + + } else if ($this.css('color') === $p.css('color')) { + _case.set('status', 'passed'); + + } else if (elmHasDistinctStyle($this, $p)) { + _case.set('status', 'passed'); + + } else if (elmHasDistinctPosition($this)) { + _case.set('status', 'passed'); + + } else if ($this.find('img').length > 0) { + _case.set('status', 'passed'); + + } else if ($parent.text().trim() === aText && + elmHasDistinctStyle($parent, $p)) { + _case.set('status', 'passed'); + + } else { + _case.set('status', 'failed'); + } + }); + + }); + +}; + +quail.aLinkTextDoesNotBeginWithRedundantWord = function(quail, test, Case) { + test.get('$scope').find('a').each(function() { + var $link = $(this); + var text = ''; + if ($(this).find('img[alt]').length) { + text = text + $(this).find('img[alt]:first').attr('alt'); + } + text = text + $(this).text(); + text = text.toLowerCase(); + var _case; + $.each(quail.strings.redundant.link, function(index, phrase) { + if (text.search(phrase) > -1) { + _case = test.add(Case({ + element: this, + 'expected': $link.closest('.quail-test').data('expected'), + 'status': 'failed' + })); + } + }); + // If the case didn't fail, then it passed. + if (!_case) { + test.add(Case({ + element: this, + 'expected': $link.closest('.quail-test').data('expected'), + 'status': 'passed' + })); + } + }); +}; + +quail.aLinkWithNonText = function(quail, test, Case) { + test.get('$scope').find('a').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (!$(this).is('a:has(img, object, embed)[href]')) { + _case.set({ + 'status': 'inapplicable' + }); + return; + } + if (!quail.isUnreadable($(this).text())) { + _case.set({ + 'status': 'passed' + }); + return; + } + var unreadable = 0; + $(this).find('img, object, embed').each(function() { + if (($(this).is('img') && quail.isUnreadable($(this).attr('alt'))) || + (!$(this).is('img') && quail.isUnreadable($(this).attr('title')))) { + unreadable++; + } + }); + if ($(this).find('img, object, embed').length === unreadable) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.aLinksAreSeparatedByPrintableCharacters = function(quail, test, Case) { + test.get('$scope').find('a').each(function() { + var _case = test.add(Case({ + element: this + })); + var expected = $(this).closest('.quail-test').data('expected'); + // Only test if there's another a tag. + if ($(this).next('a').length) { + if (quail.isUnreadable($(this).get(0).nextSibling.wholeText)) { + _case.set({ + 'expected': expected, + 'status': 'failed' + }); + } + else { + _case.set({ + 'expected': expected, + 'status': 'passed' + }); + } + } + }); +}; + +quail.aLinksDontOpenNewWindow = function(quail, test, Case) { + // Links without a target attribute pass. + test.get('$scope').find('a').not('[target=_new], [target=_blank]').each(function () { + test.add(Case({ + element: this, + 'expected': $(this).closest('.quail-test').data('expected'), + 'status': 'passed' + })); + }); + // Links with a target attribute pass if the link text indicates that the + // link will open a new window. + test.get('$scope').find('a[target=_new], a[target=_blank]').each(function() { + var $link = $(this); + var passes = false; + var i = 0; + var text = $link.text() + ' ' + $link.attr('title'); + var phrase = ''; + // Test the link text against strings the indicate the link will open + // in a new window. + do { + phrase = quail.strings.newWindow[i]; + if (text.search(phrase) > -1) { + passes = true; + } + ++i; + + } while (!passes && i < quail.strings.newWindow.length); + // Build a Case. + if (passes) { + test.add(Case({ + element: this, + 'expected': $link.closest('.quail-test').data('expected'), + 'status': 'passed' + })); + } + else { + test.add(Case({ + element: this, + 'expected': $link.closest('.quail-test').data('expected'), + 'status': 'failed' + })); + } + }); +}; + +quail.aLinksNotSeparatedBySymbols = function(quail, test, Case) { + test.get('$scope').find('a').each(function() { + var $link = $(this); + if ($link.next('a').length) { + var text = $link.get(0).nextSibling.wholeText; + if (typeof text === 'string') { + // The string between the links is composed of symbols. + if (quail.strings.symbols.indexOf(text.toLowerCase().trim()) !== -1 ) { + test.add(Case({ + element: this, + 'expected': $link.closest('.quail-test').data('expected'), + 'status': 'failed' + })); + } + } + // The string between the links is composed of words. + else { + test.add(Case({ + element: this, + 'expected': $link.closest('.quail-test').data('expected'), + 'status': 'passed' + })); + } + } + // If nothing follows the link, then there is nothing to test. + else { + test.add(Case({ + 'status': 'inapplicable' + })); + } + }); +}; + +quail.aMustContainText = function(quail, test, Case) { + test.get('$scope').find('a').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + if (!$(this).attr('href') || + $(this).css('display') === 'none') { + _case.set({ + 'status': 'inapplicable' + }); + return; + } + + if (quail.containsReadableText($(this), true)){ + _case.set({ + 'status': 'passed' + }); + } + else { + _case.set({ + 'status': 'failed' + }); + } + }); +}; + +quail.aSuspiciousLinkText = function(quail, test, Case) { + test.get('$scope').find('a').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (!$(this).attr('href')) { + _case.set({ + 'status': 'inapplicable' + }); + return; + } + var text = $(this).text(); + $(this).find('img[alt]').each(function() { + text = text + $(this).attr('alt'); + }); + if (quail.strings.suspiciousLinks.indexOf(quail.cleanString(text)) > -1) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.animatedGifMayBePresent=function(quail, test, Case){ + + /** + * Test if gif is animated + * Implemented from: https://gist.github.com/3012623.git + * @param src + * @param ext + * @param cb + */ + function isAnimatedGif(src, ext, cb){ + + if(ext !== 'gif'){ + cb(false); + return; + } + + var request=new XMLHttpRequest(); + request.open('GET', src, true); + request.responseType='arraybuffer'; + request.addEventListener('load', function () { + var arr = new Uint8Array(request.response); + var frames = 0; + + // make sure it's a gif (GIF8) + if (arr[0] !== 0x47 || arr[1] !== 0x49 || + arr[2] !== 0x46 || arr[3] !== 0x38) + { + cb(false); + return; + } + + //ported from php http://www.php.net/manual/en/function.imagecreatefromgif.php#104473 + //an animated gif contains multiple "frames", with each frame having a + //header made up of: + // * a static 4-byte sequence (\x00\x21\xF9\x04) + // * 4 variable bytes + // * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?) + // We read through the file til we reach the end of the file, or we've found + // at least 2 frame headers + for (var i=0; i < arr.length -9; i++) { + if (arr[i] === 0x00 && arr[i+1] === 0x21 && + arr[i+2] === 0xF9 && arr[i+3] === 0x04 && + arr[i+8] === 0x00 && + (arr[i+9] === 0x2C || arr[i+9] === 0x21)) + { + frames++; + } + if(frames > 1){ + cb(true); + return; + } + } + + cb(false); + }); + request.send(); + } + + test.get('$scope').find('img').each(function(){ + + var _case=Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + var imgSrc=$(this).attr('src'); + var ext=$(this).attr('src').split('.').pop().toLowerCase(); + + if (ext !== 'gif') { + _case.set({ + 'status': 'inapplicable' + }); + return; + } + + isAnimatedGif(imgSrc, ext, function(animated){ + if (animated) { + _case.set({ + 'status': 'cantTell' + }); + return; + } else{ + _case.set({ + 'status': 'inapplicable' + }); + return; + } + }); + }); +}; + +quail.appletContainsTextEquivalent = function(quail, test, Case) { + test.get('$scope').find('applet[alt=""], applet:not(applet[alt])').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (quail.isUnreadable($(this).text())) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +// options: selector: "body *:not(*[role] *, *[role], script, meta, link)" + +quail.ariaOrphanedContent = function(quail, test, Case) { + var $scope = test.get('$scope'); + //debugger; + $scope.each(function() { + var $local = $(this); + // Determine if the scope has a role. + var scopeHasRole = !!$local.attr('role'); + // Determine if any child nodes have a role. + var childrenHaveRole = !!$local.find('[role]').length; + // If no roles exist, then this test is not applicable. + if (!scopeHasRole && !childrenHaveRole) { + test.add(Case({ + expected: $local.data('expected'), + status: 'inapplicable' + })); + return; + } + // If roles exist, make sure all content is within a role. + var $orphans = $local.find('*:not(*[role] *, *[role], script, meta, link)'); + if (!$orphans.length) { + test.add(Case({ + expected: $local.data('expected'), + status: 'passed' + })); + } + // Otherwise, fail the content that falls outside a role. + else { + $orphans.each(function() { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'failed' + })); + }); + } + }); +}; + +quail.audioMayBePresent=function(quail, test, Case){ + var audioExtensions = ['mp3', 'm4p', 'ogg', 'oga', 'opus', 'wav', 'wma', 'wv']; + + test.get('$scope').each(function() { + var $this = $(this); + var hasCase = false; // Test if a case has been created + + // Audio is definately an audio, and objects could be too. + $this.find('object, audio').each(function () { + hasCase = true; + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'cantTell' + })); + }); + + // Links refering to files with an audio extensions are good indicators too + $this.find('a[href]').each(function () { + var $this = $(this); + var extension = $this.attr('href').split('.').pop(); + if ($.inArray(extension, audioExtensions) !== -1) { + hasCase = true; + test.add(Case({ + element: this, + expected: $this.closest('.quail-test').data('expected'), + status: 'cantTell' + })); + } + }); + + // if no case was added, return inapplicable + if (!hasCase) { + test.add(Case({ + element: this, + status: 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + })); + } + }); + +}; +quail.blockquoteUseForQuotations = function(quail, test, Case) { + test.get('$scope').find('p').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).parents('blockquote').length > 0) { + _case.set({ + 'status': 'inapplicable' + }); + return; + } + if ($(this).text().substr(0, 1).search(/'|"|«|“|「/) > -1 && + $(this).text().substr(-1, 1).search(/'|"|»|„|」/) > -1) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.closingTagsAreUsed = function(quail, test, Case) { + quail.components.htmlSource.getHtml(function(html, parsed) { + quail.components.htmlSource.traverse(parsed, function(element) { + if (element.type !== 'tag' || !$.isArray(element.selector)) { + return; + } + var selector; + // Use the element's ID if it has one. + if (/#/.test(element.selector.slice(-1)[0])) { + selector = element.selector.slice(-1)[0]; + } + // Otherwise construct the path from the selector pieces. + else { + selector = element.selector.join(' > '); + } + + // If selector matches a DOM node in the scope, get a reference to the + // node, otherwise we'll have to back off to just giving details about + // the node. This might happen if the DOM in the real page is + // transformed too drastically from the parsed DOM. + var node = $(selector, test.get('$scope')).get(0); + if (!node) { + node = element.raw || selector; + } + + if (typeof element.closingTag === 'undefined' && + !element.closingTag && + quail.selfClosingTags.indexOf(element.name.toLowerCase()) === -1) { + test.add(Case({ + element: node, + // Only attempt to get an expectation for the testrunner if the node + // is a DOM node. + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + status: 'failed' + })); + } + else { + test.add(Case({ + element: node, + // Only attempt to get an expectation for the testrunner if the node + // is a DOM node. + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + status: 'passed' + })); + } + }); + }); +}; + +quail.colorBackgroundGradientContrast = function (quail, test, Case, options) { + + var colors = quail.components.color.colors; + var buildCase = quail.components.color.buildCase; + var id = 'colorBackgroundGradientContrast'; + + /** + * + */ + function colorBackgroundGradientContrast(test, Case, options, $this, element) { + // Check if there's a background gradient using DOM. + var failureFound, rainbow, numberOfSamples; + var backgroundGradientColors = colors.getBackgroundGradient($this); + + if (!backgroundGradientColors) { + return; + } + + // Convert colors to hex notation. + for (var i = 0; i < backgroundGradientColors.length; i++) { + if (backgroundGradientColors[i].substr(0, 3) === 'rgb') { + backgroundGradientColors[i] = colors.colorToHex(backgroundGradientColors[i]); + } + } + + // Create a rainbow. + /* global Rainbow */ + rainbow = new Rainbow(); + rainbow.setSpectrumByArray(backgroundGradientColors); + // @todo, make the number of samples configurable. + numberOfSamples = backgroundGradientColors.length * options.gradientSampleMultiplier; + + // Check each color. + failureFound = false; + for (i = 0; !failureFound && i < numberOfSamples; i++) { + var testResult = colors.testElmBackground(options.algorithm, $this, + '#' + rainbow.colourAt(i)); + + if (!testResult) { + buildCase(test, Case, element, 'failed', id, 'The background gradient makes the text unreadable'); + failureFound = true; + } + } + + // If no failure was found, the element passes for this case type. + if (!failureFound) { + buildCase(test, Case, element, 'passed', id, 'The background gradient does not affect readability'); + } + } + + + test.get('$scope').each(function () { + var textNodes = document.evaluate('descendant::text()[normalize-space()]', this, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + var nodes = []; + var textNode = textNodes.iterateNext(); + + // Loop has to be separated. If we try to iterate and rund testCandidates + // the xpath thing will crash because document is being modified. + while (textNode) { + if (quail.components.color.textShouldBeTested(textNode)) { + nodes.push(textNode.parentNode); + } + textNode = textNodes.iterateNext(); + } + + if (nodes.length === 0) { + buildCase(test, Case, null, 'inapplicable', '', 'There is no text to evaluate'); + } + + nodes.forEach(function (element) { + colorBackgroundGradientContrast(test, Case, options, $(element), element); + }); + + }); +}; + +quail.colorBackgroundImageContrast = function (quail, test, Case, options) { + + var colors = quail.components.color.colors; + var buildCase = quail.components.color.buildCase; + var id = 'colorBackgroundImageContrast'; + + /** + * + */ + function colorBackgroundImageContrast(test, Case, options, $this, element) { + // Check if there's a backgroundImage using DOM. + var backgroundImage = colors.getBackgroundImage($this); + if (!backgroundImage) { + return; + } + + var img = document.createElement('img'); + img.crossOrigin = "Anonymous"; + + // Get average color of the background image. The image must first load + // before information about it is available to the DOM. + img.onload = function () { + var averageColorBackgroundImage = colors.getAverageRGB(img); + var testResult = colors.testElmBackground(options.algorithm, $this, + averageColorBackgroundImage); + + // Build a case. + if (!testResult) { + buildCase(test, Case, element, 'failed', id, 'The element\'s background image makes the text unreadable'); + + } else { + buildCase(test, Case, element, 'passed', id, 'The element\'s background image does not affect readability'); + } + }; + + img.onerror = img.onabort = function () { + buildCase(test, Case, element, 'cantTell', id, 'The element\'s background image could not be loaded (' + backgroundImage + ')'); + }; + + // Load the image. + img.src = backgroundImage; + } + + + test.get('$scope').each(function () { + var textNodes = document.evaluate('descendant::text()[normalize-space()]', this, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + var nodes = []; + var textNode = textNodes.iterateNext(); + + // Loop has to be separated. If we try to iterate and rund testCandidates + // the xpath thing will crash because document is being modified. + while (textNode) { + if (quail.components.color.textShouldBeTested(textNode)) { + nodes.push(textNode.parentNode); + } + textNode = textNodes.iterateNext(); + } + + if (nodes.length === 0) { + buildCase(test, Case, null, 'inapplicable', '', 'There is no text to evaluate'); + } + + nodes.forEach(function (element) { + colorBackgroundImageContrast(test, Case, options, $(element), element); + }); + }); +}; + +quail.colorElementBehindBackgroundGradientContrast = function (quail, test, Case, options) { + + var colors = quail.components.color.colors; + var buildCase = quail.components.color.buildCase; + var id = 'colorElementBehindBackgroundGradientContrast'; + + + /** + * + */ + function colorElementBehindBackgroundGradientContrast(test, Case, options, $this, element) { + // Check if there's a background gradient using element behind current element. + var behindGradientColors; + var failureFound; + // The option element is problematic. + if (!$this.is('option')) { + behindGradientColors = colors.getBehindElementBackgroundGradient($this); + } + + if (!behindGradientColors) { + return; + } + + // Convert colors to hex notation. + for (var i = 0; i < behindGradientColors.length; i++) { + if (behindGradientColors[i].substr(0, 3) === 'rgb') { + behindGradientColors[i] = colors.colorToHex(behindGradientColors[i]); + } + } + + // Create a rainbow. + /* global Rainbow */ + var rainbow = new Rainbow(); + rainbow.setSpectrumByArray(behindGradientColors); + var numberOfSamples = behindGradientColors.length * options.gradientSampleMultiplier; + + // Check each color. + failureFound = false; + for (i = 0; !failureFound && i < numberOfSamples; i++) { + failureFound = !colors.testElmBackground(options.algorithm, $this, + '#' + rainbow.colourAt(i)); + } + + // If no failure was found, the element passes for this case type. + if (failureFound) { + buildCase(test, Case, element, 'failed', id, 'The background gradient of the element behind this element makes the text unreadable'); + } else { + buildCase(test, Case, element, 'passed', id, 'The background gradient of the element behind this element does not affect readability'); + } + } + + + test.get('$scope').each(function () { + var textNodes = document.evaluate('descendant::text()[normalize-space()]', this, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + var nodes = []; + var textNode = textNodes.iterateNext(); + + // Loop has to be separated. If we try to iterate and rund testCandidates + // the xpath thing will crash because document is being modified. + while (textNode) { + if (quail.components.color.textShouldBeTested(textNode)) { + nodes.push(textNode.parentNode); + } + textNode = textNodes.iterateNext(); + } + + if (nodes.length === 0) { + buildCase(test, Case, null, 'inapplicable', '', 'There is no text to evaluate'); + } + + nodes.forEach(function (element) { + colorElementBehindBackgroundGradientContrast(test, Case, options, $(element), element); + }); + }); + +}; + +quail.colorElementBehindBackgroundImageContrast = function (quail, test, Case, options) { + + var colors = quail.components.color.colors; + var buildCase = quail.components.color.buildCase; + var id = 'colorElementBehindBackgroundImageContrast'; + + /** + * + */ + function colorElementBehindBackgroundImageContrast(test, Case, options, $this, element) { + // Check if there's a backgroundImage using element behind current element. + var behindBackgroundImage; + + // The option element is problematic. + if (!$this.is('option')) { + behindBackgroundImage = colors.getBehindElementBackgroundImage($this); + } + + if (!behindBackgroundImage) { + return; + } + + var img = document.createElement('img'); + img.crossOrigin = "Anonymous"; + // The image must first load before information about it is available to + // the DOM. + img.onload = function () { + + // Get average color of the background image. + var averageColorBehindBackgroundImage = colors.getAverageRGB(img); + var testResult = colors.testElmBackground(options.algorithm, $this, + averageColorBehindBackgroundImage); + if (!testResult) { + buildCase(test, Case, element, 'failed', id, 'The background image of the element behind this element makes the text unreadable'); + + } else { + buildCase(test, Case, element, 'passed', id, 'The background image of the element behind this element does not affect readability'); + } + }; + img.onerror = img.onabort = function () { + buildCase(test, Case, element, 'cantTell', id, 'The background image of the element behind this element could not be loaded (' + behindBackgroundImage + ')'); + }; + // Load the image. + img.src = behindBackgroundImage; + } + + + test.get('$scope').each(function () { + var textNodes = document.evaluate('descendant::text()[normalize-space()]', this, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + var nodes = []; + var textNode = textNodes.iterateNext(); + + // Loop has to be separated. If we try to iterate and rund testCandidates + // the xpath thing will crash because document is being modified. + while (textNode) { + if (quail.components.color.textShouldBeTested(textNode)) { + nodes.push(textNode.parentNode); + } + textNode = textNodes.iterateNext(); + } + + if (nodes.length === 0) { + buildCase(test, Case, null, 'inapplicable', '', 'There is no text to evaluate'); + } + + nodes.forEach(function (element) { + colorElementBehindBackgroundImageContrast(test, Case, options, $(element), element); + }); + }); +}; + +quail.colorElementBehindContrast = function (quail, test, Case, options) { + + var colors = quail.components.color.colors; + var buildCase = quail.components.color.buildCase; + var id = 'colorElementBehindContrast'; + + function colorElementBehindContrast(test, Case, options, $this, element) { + // Check text and background using element behind current element. + var backgroundColorBehind; + // The option element is problematic. + if (!$this.is('option')) { + backgroundColorBehind = colors.getBehindElementBackgroundColor($this); + } + if (!backgroundColorBehind) { + return; + } + + var testResult = colors.testElmBackground(options.algorithm, $this, + backgroundColorBehind); + + // Build a case. + if (!testResult) { + buildCase(test, Case, element, 'failed', id, 'The element behind this element makes the text unreadable'); + } + else { + buildCase(test, Case, element, 'passed', id, 'The element behind this element does not affect readability'); + } + } + + + test.get('$scope').each(function () { + var textNodes = document.evaluate('descendant::text()[normalize-space()]', this, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + var nodes = []; + var textNode = textNodes.iterateNext(); + + // Loop has to be separated. If we try to iterate and rund testCandidates + // the xpath thing will crash because document is being modified. + while (textNode) { + if (quail.components.color.textShouldBeTested(textNode)) { + nodes.push(textNode.parentNode); + } + textNode = textNodes.iterateNext(); + } + + if (nodes.length === 0) { + buildCase(test, Case, null, 'inapplicable', '', 'There is no text to evaluate'); + } + + nodes.forEach(function (element) { + colorElementBehindContrast(test, Case, options, $(element), element); + }); + + }); +}; + +quail.colorFontContrast = function (quail, test, Case, options) { + + var colors = quail.components.color.colors; + var buildCase = quail.components.color.buildCase; + var id = 'colorFontContrast'; + + /** + * + */ + function colorFontContrast(test, Case, options, $this, element) { + // Check text and background color using DOM. + // Build a case. + if (!colors.testElmContrast(options.algorithm, $this)) { + buildCase(test, Case, element, 'failed', id, 'The font contrast of the text impairs readability'); + } + else { + buildCase(test, Case, element, 'passed', id, 'The font contrast of the text is sufficient for readability'); + } + } + + + test.get('$scope').each(function () { + var textNodes = document.evaluate('descendant::text()[normalize-space()]', this, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + var nodes = []; + var textNode = textNodes.iterateNext(); + + // Loop has to be separated. If we try to iterate and rund testCandidates + // the xpath thing will crash because document is being modified. + while (textNode) { + if (quail.components.color.textShouldBeTested(textNode)) { + nodes.push(textNode.parentNode); + } + textNode = textNodes.iterateNext(); + } + + if (nodes.length === 0) { + buildCase(test, Case, null, 'inapplicable', '', 'There is no text to evaluate'); + } + + nodes.forEach(function (element) { + colorFontContrast(test, Case, options, $(element), element); + }); + }); +}; + +quail.contentPositioningShouldNotChangeMeaning = function(quail, test, Case) { + //Look for absolute positioned elements that are being put into grids or columns + var positions = ['top', 'left', 'right', 'bottom']; + var coordinates = {}; + var failed = false; + test.get('$scope').find('*:has(*:quailCss(position=absolute))').each(function() { + coordinates = {top: {}, left: {}, right: {}, bottom: {}}; + failed = false; + var $container = $(this); + $container.find('h1, h2, h3, h4, h5, h6, p, blockquote, ol, li, ul, dd, dt').filter(':quailCss(position=absolute)').each(function() { + for (var i = 0; i < positions.length; i++) { + if (typeof $(this).css(positions[i]) !== 'undefined' && $(this).css(positions[i]) !== 'auto') { + if (typeof coordinates[positions[i]][$(this).css(positions[i])] === 'undefined') { + coordinates[positions[i]][$(this).css(positions[i])] = 0; + } + coordinates[positions[i]][$(this).css(positions[i])]++; + } + } + }); + + $.each(positions, function() { + $.each(coordinates[this], function() { + if (this > 2 && !failed) { + failed = true; + test.add(Case({ + element: $container.get(0), + expected: $container.closest('.quail-test').data('expected'), + status: 'failed' + })); + } + }); + }); + }); +}; + +quail.definitionListsAreUsed = function(quail, test, Case) { + test.get('$scope').find('dl').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + _case.set({ + 'status': 'inapplicable' + }); + }); + test.get('$scope').find('p, li').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var $item = $(this); + $(this).find('span, strong, em, b, i').each(function() { + if ($(this).text().length < 50 && $item.text().search($(this).text()) === 0) { + if ($(this).is('span')) { + if ($(this).css('font-weight') === $item.css('font-weight') && + $(this).css('font-style') === $item.css('font-style') ) { + _case.set({ + 'status': 'passed' + }); + return; + } + } + _case.set({ + 'status': 'failed' + }); + } + }); + }); +}; + +quail.doNotUseGraphicalSymbolToConveyInformation = function(quail, test, Case) { + // Passes and fails. + test.get('$scope').find(quail.textSelector + ':not(abbr, acronym)').each(function() { + var whiteList = '✓'; + var blackList = '?xo[]()+-!*xX'; + + var text = $(this).text(); + + // @todo add support for other languages. + // Remove all alphanumeric characters. + var textLeft = text.replace(/[\W\s]+/g, ''); + // If we have an empty string something is wrong. + if (textLeft.length === 0) { + // Unless if it's white listed. + if (whiteList.indexOf(text) === -1) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + } + // Check regularly used single character symbols. + else if (text.length === 1 && blackList.indexOf(text) >= 0) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + }); + // Not applicables. + test.get('$scope').find(quail.textSelector).filter('abbr, acronym').each(function() { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'inapplicable' + })); + }); +}; + +quail.doctypeProvided = function(quail, test, Case) { + var doc = test.get('$scope').get(0); + if ($(doc.doctype).length === 0 && !document.doctype) { + test.add(Case({ + element: doc, + expected: 'fail', + status: 'failed' + })); + } + else { + test.add(Case({ + element: doc, + expected: 'pass', + status: 'passed' + })); + } +}; + +quail.documentAbbrIsUsed = function(quail, test, Case) { + quail.components.acronym(quail, test, Case, 'abbr'); +}; + +quail.documentAcronymsHaveElement = function(quail, test, Case) { + quail.components.acronym(quail, test, Case, 'acronym'); +}; + +quail.documentIDsMustBeUnique = function(quail, test, Case) { + test.get('$scope').each(function(){ + if($(this).children().length === 0) { + test.add(Case({ + element: this, + 'status': 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + })); + } + }); + test.get('$scope').find(':not([id])').each(function() { + test.add(Case({ + element: this, + 'status': 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + })); + }); + test.get('$scope').each(function(){ + var ids = {}; + $(this).find('[id]').each(function() { + var _case = Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) + }); + test.add(_case); + if(typeof ids[$(this).attr('id')] === 'undefined' && Object.keys(ids).length === 0){ + _case.set({ + 'status': 'inapplicable' + }); + ids[$(this).attr('id')] = $(this).attr('id'); + }else if (typeof ids[$(this).attr('id')] === 'undefined') { + _case.set({ + 'status': 'passed' + }); + ids[$(this).attr('id')] = $(this).attr('id'); + } + else { + _case.set({ + 'status': 'failed' + }); + } + }); + }); +}; + +quail.documentIsWrittenClearly = function(quail, test, Case) { + test.get('$scope').find(quail.textSelector).each(function() { + var text = quail.components.textStatistics.cleanText($(this).text()); + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (quail.isUnreadable(text)) { + _case.set({ + 'status' : 'inapplicable' + }); + return; + } + if (Math.round((206.835 - (1.015 * quail.components.textStatistics.averageWordsPerSentence(text)) - (84.6 * quail.components.textStatistics.averageSyllablesPerWord(text)))) < 60) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.documentLangIsISO639Standard = function(quail, test, Case) { + var $element = (test.get('$scope').is('html')) ? + test.get('$scope') : + test.get('$scope').find('html').first(); + + var _case = Case({ + element: $element[0], + expected: ($element.closest('.quail-test').length) ? + $element.closest('.quail-test').data('expected') : + $element.data('expected') + }); + + var langAttr = $element.attr('lang'); + var matchedLang = false; // Check to see if a languagecode was matched + + test.add(_case); + if (!$element.is('html') || typeof langAttr === 'undefined') { + _case.set({ + 'status' : 'inapplicable' + }); + } else { + // Loop over all language codes, checking if the current lang attribute starts + // with a value that's in the languageCodes array + $.each(quail.strings.languageCodes, function (i, currentLangCode) { + if (!matchedLang && langAttr.indexOf(currentLangCode) === 0) { + matchedLang = true; + } + }); + + if (!matchedLang) { + _case.set({'status': 'failed'}); + + } else if (langAttr.match(/^[a-z]{2}(-[A-Z]{2})?$/) === null) { + _case.set({'status': 'failed'}); + + } else { + _case.set({'status': 'passed'}); + } + } + +}; + +quail.documentStrictDocType = function(quail, test, Case) { + if (typeof document.doctype === 'undefined' || + !document.doctype || + document.doctype.systemId.search('strict') === -1) { + test.add(Case({ + element: document, + expected: test.get('$scope').data('expected'), + status: 'failed' + })); + } + else { + test.add(Case({ + element: document, + expected: test.get('$scope').data('expected'), + status: 'passed' + })); + } +}; + +quail.documentTitleIsShort = function(quail, test, Case) { + var $title = test.get('$scope').find('head title:first'); + var _case = Case({ + element: $title, + expected: $title.closest('.quail-test').data('expected') + }); + test.add(_case); + if (!$title.length) { + _case.set({ + element: test.get('$scope'), + 'status' : 'inapplicable' + }); + return; + } + _case.set({ + 'status': $title.text().length > 150 ? + 'failed' : + 'passed' + }); +}; + +quail.documentValidatesToDocType = function() { + if (typeof document.doctype === 'undefined') { + return; + } +}; + +quail.documentVisualListsAreMarkedUp = function(quail, test, Case) { + + var itemStarters = [ + '♦', '›', '»', '‣', '▶', '◦', '✓', '◽', '•', '—', '◾', // single characters + '-\\D', // dash, except for negative numbers + '\\\\', // Just an escaped slash + '\\*(?!\\*)', // *, but not ** (which could be a foot note) + '\\.\\s', 'x\\s', // characters that should be followed by a space + '•', '•', '>', // HTML entities + '[0-9]+\\.', '\\(?[0-9]+\\)', // Numbers: 1., 13., 13), (14) + '[\\u25A0-\\u25FF]', // Unicode characters that look like bullets + '[IVX]{1,5}\\.\\s' // Roman numerals up to (at least) 27, followed by ". " E.g. II. IV. + ]; + + var symbols = RegExp( + '(^|]*>)' + // Match the String start or a
element + '[\\s]*' + // Optionally followed by white space characters + '(' + itemStarters.join('|') + ')', // Followed by a character that could indicate a list + 'gi'); // global (for counting), case insensitive (capitalisation in elements / entities) + + + test.get('$scope').find(quail.textSelector).each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var matches = $(this).html().match(symbols); + _case.set({ + 'status': (matches && matches.length > 2) ? + 'failed' : + 'passed' + }); + }); +}; + +quail.elementAttributesAreValid = function(quail, test, Case) { + quail.components.htmlSource.getHtml(function(html, parsed) { + if (!parsed) { + return; + } + quail.components.htmlSource.traverse(parsed, function(element) { + if (typeof element.raw === 'undefined' || !$.isArray(element.selector)) { + return; + } + + var failed = false, selector; + // Use the element's ID if it has one. + if (/#/.test(element.selector.slice(-1)[0])) { + selector = element.selector.slice(-1)[0]; + } + // Otherwise construct the path from the selector pieces. + else { + selector = element.selector.join(' > '); + } + + // If selector matches a DOM node in the scope, get a reference to the + // node, otherwise we'll have to back off to just giving details about + // the node. This might happen if the DOM in the real page is + // transformed too drastically from the parsed DOM. + var node = $(selector, test.get('$scope')).get(0); + if (!node) { + node = element.raw || selector; + } + + //Element has mis-matched quotes + var quotes = element.raw.match(/\'|\"/g); + if (quotes && quotes.length % 2 !== 0) { + test.add(Case({ + element: node, + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + status: 'failed' + })); + failed = true; + } + + //Element attributes not separated by a space + if (element.raw.search(/([a-z]*)=(\'|\")([a-z\.]*)(\'|\")[a-z]/i) > -1) { + test.add(Case({ + element: node, + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + status: 'failed' + })); + failed = true; + } + + //Element with space as an attribute is not surrounded by quotes + var splitElement = element.raw.split('='); + splitElement.shift(); + $.each(splitElement, function() { + if (this.search(/\'|\"/) === -1 && this.search(/\s/i) > -1) { + test.add(Case({ + element: node, + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + status: 'failed' + })); + failed = true; + } + }); + + // Passes. + if (!failed) { + test.add(Case({ + element: node, + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + status: 'passed' + })); + } + }); + }); +}; + +quail.elementsDoNotHaveDuplicateAttributes = function(quail, test, Case) { + quail.components.htmlSource.getHtml(function(html, parsed) { + if (!parsed) { + return; + } + quail.components.htmlSource.traverse(parsed, function(element) { + if (element.type !== 'tag' || !$.isArray(element.selector)) { + return; + } + var selector; + // Use the element's ID if it has one. + if (/#/.test(element.selector.slice(-1)[0])) { + selector = element.selector.slice(-1)[0]; + } + // Otherwise construct the path from the selector pieces. + else { + selector = element.selector.join(' > '); + } + // If selector matches a DOM node in the scope, get a reference to the + // node, otherwise we'll have to back off to just giving details about + // the node. This might happen if the DOM in the real page is + // transformed too drastically from the parsed DOM. + var node = $(selector, test.get('$scope')).get(0); + if (!node) { + node = element.raw || selector; + } + if (typeof element.attributes !== 'undefined') { + var attrs = []; + $.each(element.attributes, function(index, attribute) { + if (attribute.length > 1) { + attrs.push(attribute); + } + }); + // If multiple attributes are found on a node, the test fails. + if (attrs.length) { + test.add(Case({ + element: node, + // Only attempt to get an expectation for the testrunner if the node + // is a DOM node. + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + info: attrs, + status: 'failed' + })); + } + // Otherwise it passes. + else { + test.add(Case({ + element: node, + // Only attempt to get an expectation for the testrunner if the node + // is a DOM node. + expected: (typeof node === 'object') && (node.nodeType === 1) && $(node).closest('.quail-test').data('expected') || null, + info: attrs, + status: 'passed' + })); + } + } + }); + }); +}; + +quail.embedHasAssociatedNoEmbed = function(quail, test, Case) { + test.get('$scope').find('embed').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + _case.set({ + 'status': ($(this).find('noembed').length || $(this).next().is('noembed')) ? + 'passed' : + 'failed' + }); + }); +}; + +quail.emoticonsExcessiveUse = function(quail, test, Case) { + test.get('$scope').find(quail.textSelector).each(function() { + var count = 0; + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + $.each($(this).text().split(' '), function(index, word) { + if (word.search(quail.emoticonRegex) > -1 ) { + count++; + } + }); + if (count === 0) { + _case.set({ + 'status': 'inapplicable' + }); + } + else { + _case.set({ + 'status': (count > 4) ? + 'failed' : + 'passed' + }); + } + }); +}; + +quail.emoticonsMissingAbbr = function(quail, test, Case) { + test.get('$scope').find(quail.textSelector + ':not(abbr, acronym)').each(function() { + var $element = $(this); + var $clone = $element.clone(); + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + $clone.find('abbr, acronym').each(function() { + $(this).remove(); + }); + var status = 'passed'; + $.each($clone.text().split(' '), function(index, word) { + if (word.search(quail.emoticonRegex) > -1 ) { + status = 'failed'; + } + }); + _case.set({ + 'status': status + }); + }); +}; + +quail.focusIndicatorVisible = function(quail, test, Case) { + test.get('$scope').find(quail.focusElements).each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var noFocus = { + borderWidth : $(this).css('border-width'), + borderColor : $(this).css('border-color'), + backgroundColor : $(this).css('background-color'), + boxShadow : $(this).css('box-shadow') + }; + $(this).focus(); + if (noFocus.backgroundColor.trim() !== $(this).css('background-color').trim()) { + $(this).blur(); + _case.set({ + 'status': 'passed' + }); + return; + } + + var borderWidth = quail.components.convertToPx($(this).css('border-width')); + if (borderWidth > 2 && borderWidth !== quail.components.convertToPx(noFocus.borderWidth)) { + $(this).blur(); + _case.set({ + 'status': 'passed' + }); + return; + } + + var boxShadow = ($(this).css('box-shadow') && $(this).css('box-shadow') !== 'none') ? $(this).css('box-shadow').match(/(-?\d+px)|(rgb\(.+\))/g) : false; + if (boxShadow && $(this).css('box-shadow') !== noFocus.boxShadow && quail.components.convertToPx(boxShadow[3]) > 3) { + $(this).blur(); + _case.set({ + 'status': 'passed' + }); + return; + } + $(this).blur(); + _case.set({ + 'status': 'failed' + }); + }); +}; + +quail.formWithRequiredLabel = function(quail, test, Case) { + var redundant = quail.strings.redundant; + var lastStyle, currentStyle = false; + redundant.required[redundant.required.indexOf('*')] = /\*/g; + test.get('$scope').each(function () { + var $local = $(this); + $local.find('label').each(function() { + var text = $(this).text().toLowerCase(); + var $label = $(this); + var _case = test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) + })); + for (var word in redundant.required) { + if (text.search(word) >= 0 && !test.get('$scope').find('#' + $label.attr('for')).attr('aria-required')) { + _case.set({ + 'status': 'failed' + }); + } + } + currentStyle = $label.css('color') + $label.css('font-weight') + $label.css('background-color'); + if (lastStyle && currentStyle !== lastStyle) { + _case.set({ + 'status': 'failed' + }); + } + lastStyle = currentStyle; + if (typeof _case.get('status') === 'undefined') { + _case.set({ + 'status': 'passed' + }); + } + }); + }); +}; + +quail.headerTextIsTooLong = function(quail, test, Case) { + var headerMaxLength = 128; + + test.get('$scope').find('h1, h2, h3, h4, h5, h6').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: $(this).text().replace(/^\s+|\s+$/gm,'').length > headerMaxLength ? + 'failed' : + 'passed' + }); + test.add(_case); + }); +}; + +quail.headersAttrRefersToATableCell = function(quail, test, Case) { + + // Table cell headers without referred ids + test.get('$scope').find('table').each(function() { + + var element = this; + var _case = Case({ + element: element, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var elmHeaders = $(element).find('th[headers], td[headers]'); + + if (elmHeaders.length === 0) { + _case.set({ + 'status': 'inapplicable' + }); + return; + } else { + elmHeaders.each(function() { + var headers = $(this).attr('headers').split(/\s+/); + $.each(headers, function(index, item) { + if (item === "" || $(element).find('th#' + item + ',td#' + item).length > 0) { + _case.set({ + 'status': 'passed' + }); + return; + } else { + _case.set({ + 'status': 'failed' + }); + return; + } + }); + }); + } + }); +}; + +quail.headersUseToMarkSections = function(quail, test, Case) { + test.get('$scope').find('p').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var $paragraph = $(this); + $paragraph.find('strong:first, em:first, i:first, b:first').each(function() { + _case.set({ + 'status': ($paragraph.text().trim() === $(this).text().trim()) ? + 'failed' : + 'passed' + }); + }); + }); + + test.get('$scope').find('ul, ol').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var $list = $(this); + if ($list.prevAll(':header').length || + $list.find('li').length !== $list.find('li:has(a)').length) { + _case.set({ + 'status': 'passed' + }); + return; + } + var isNavigation = true; + $list.find('li:has(a)').each(function() { + if ($(this).text().trim() !== $(this).find('a:first').text().trim()) { + isNavigation = false; + } + }); + if (isNavigation) { + _case.set({ + 'status': 'failed' + }); + } + }); +}; + +quail.headersUsedToIndicateMainContent = function(quail, test, Case) { + test.get('$scope').each(function() { + var $local = $(this); + var $content = quail.components.content.findContent($local); + + if (typeof $content !== 'undefined' && ( + $content.find(':header').length === 0 || + !$content.find(quail.textSelector).first().is(':header') + )) { + test.add(Case({ + element: $content.get(0), + expected: $content.closest('.quail-test').data('expected'), + status: 'failed' + })); + } + else { + test.add(Case({ + element: $content.get(0), + expected: $content.closest('.quail-test').data('expected'), + status: 'passed' + })); + } + }); +}; + +quail.idRefHasCorrespondingId = function(quail, test, Case) { + test.get('$scope').find('label[for], *[aria-activedescendant]').each(function() { + var $this = $(this); + var _case = Case({ + element: this, + expected: $this.closest('.quail-test').data('expected') + }); + test.add(_case); + + var find = $this.attr('for') || $this.attr('aria-activedescendant'); + if (test.get('$scope').find('#' + find).length === 0) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.idrefsHasCorrespondingId = function( quail, test, Case ) { + + function getAttribute($element){ + var attribute = []; + var attributeList = ['headers', 'aria-controls', 'aria-describedby', 'aria-flowto', 'aria-labelledby', 'aria-owns']; + + $.each(attributeList, function(index, item){ + + var attr = $element.attr(item); + + if(typeof attr !== typeof undefined && attr !== false){ + attribute = attr; + return; + } + }); + return attribute.split( /\s+/ ); + } + + test.get('$scope').each(function() { + + var testableElements = $(this).find( + 'td[headers], th[headers], [aria-controls], [aria-describedby], [aria-flowto], ' + + '[aria-labelledby], [aria-owns]'); + + if (testableElements.length === 0) { + + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'inapplicable' + })); + return; + } else { + + testableElements.each(function() { + var element = this; + var _case = test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + })); + + var attributes = getAttribute($(element)); + var status = 'passed'; + + $.each(attributes, function(index, item) { + + if (item !== "" && $('#' + item).length === 0) { + status = 'failed'; + return; + } + }); + + _case.set({ + 'status': status + }); + }); + } + + } + ); +}; +quail.imgAltIsDifferent = function(quail, test, Case) { + test.get('$scope').find('img:not([src])').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + 'status': 'inapplicable' + }); + test.add(_case); + }); + test.get('$scope').find('img[alt][src]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).attr('src') === $(this).attr('alt') || $(this).attr('src').split('/').pop() === $(this).attr('alt')) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.imgAltIsTooLong = function(quail, test, Case) { + test.get('$scope').find('img[alt]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + _case.set({ + 'status': ($(this).attr('alt').length > 100) ? + 'failed' : + 'passed' + }); + }); +}; + +quail.imgAltNotEmptyInAnchor = function(quail, test, Case) { + test.get('$scope').find('a[href]:has(img)').each(function() { + var $a = $(this); + var text = $a.text(); + + var _case = Case({ + element: this, + expected: $a.closest('.quail-test').data('expected') + }); + test.add(_case); + + // Concat all alt attributes of images to the text of the paragraph + $a.find('img[alt]').each(function () { + text += ' ' + $(this).attr('alt'); + }); + + if (quail.isUnreadable(text)) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.imgAltTextNotRedundant = function(quail, test, Case) { + var altText = {}; + test.get('$scope').find('img[alt]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (typeof altText[$(this).attr('alt')] === 'undefined') { + altText[$(this).attr('alt')] = $(this).attr('src'); + } + else { + if (altText[$(this).attr('alt')] !== $(this).attr('src')) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + } + }); +}; + +quail.imgGifNoFlicker = function(quail, test, Case) { + test.get('$scope').find('img[src$=".gif"]').each(function() { + var $image = $(this); + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + $.ajax({ + url: $image.attr('src'), + dataType: 'text', + success: function(data) { + if (data.search('NETSCAPE2.0') !== -1) { + _case.set({ + 'status' : 'failed' + }); + } + else { + _case.set({ + 'status' : 'inapplicable' + }); + } + } + }); + }); +}; + +quail.imgHasLongDesc = function (quail, test, Case) { + test.get('$scope').find('img[longdesc]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).attr('longdesc') === $(this).attr('alt') || + !quail.validURL($(this).attr('longdesc'))) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.imgImportantNoSpacerAlt = function (quail, test, Case) { + test.get('$scope').find('img[alt]').each(function() { + var width = ($(this).width()) ? $(this).width() : parseInt($(this).attr('width'), 10); + var height = ($(this).height()) ? $(this).height() : parseInt($(this).attr('height'), 10); + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (quail.isUnreadable($(this).attr('alt').trim()) && + $(this).attr('alt').length > 0 && + width > 50 && + height > 50) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.imgMapAreasHaveDuplicateLink = function (quail, test, Case) { + var links = { }; + test.get('$scope').find('a').each(function() { + links[$(this).attr('href')] = $(this).attr('href'); + }); + test.get('$scope').find('img[usemap]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var $image = $(this); + var $map = test.get('$scope').find($image.attr('usemap')); + if (!$map.length) { + $map = test.get('$scope').find('map[name="' + $image.attr('usemap').replace('#', '') + '"]'); + } + if ($map.length) { + $map.find('area').each(function() { + if (typeof links[$(this).attr('href')] === 'undefined') { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); + } + else { + _case.set({ + 'status': 'inapplicable' + }); + } + }); +}; + +quail.imgNonDecorativeHasAlt = function (quail, test, Case) { + test.get('$scope').find('img[alt]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (quail.isUnreadable($(this).attr('alt')) && + ($(this).width() > 100 || $(this).height() > 100)) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.imgWithMathShouldHaveMathEquivalent = function (quail, test, Case) { + test.get('$scope').find('img:not(img:has(math), img:has(tagName))').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (!$(this).parent().find('math').length) { + _case.set({ + 'status': 'failed' + }); + } + }); +}; + +quail.inputCheckboxRequiresFieldset = function (quail, test, Case) { + test.get('$scope').find(':checkbox').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (!$(this).parents('fieldset').length) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.inputImageAltIsNotFileName = function (quail, test, Case) { + test.get('$scope').find('input[type=image][alt]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).attr('src') === $(this).attr('alt')) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.inputImageAltIsShort = function (quail, test, Case) { + test.get('$scope').find('input[type=image]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).attr('alt').length > 100) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.inputImageAltNotRedundant = function (quail, test, Case) { + test.get('$scope').find('input[type=image][alt]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (quail.strings.redundant.inputImage.indexOf(quail.cleanString($(this).attr('alt'))) > -1) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.inputWithoutLabelHasTitle = function (quail, test, Case) { + + test.get('$scope').each(function(){ + + var testableElements = $(this).find('input, select, textarea'); + + if(testableElements.length === 0){ + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'inapplicable' + }); + test.add(_case); + return; + }else{ + testableElements.each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + if($(this).css('display') === 'none'){ + _case.set({ + 'status': 'inapplicable' + }); + return; + } + if (!test.get('$scope').find('label[for=' + $(this).attr('id') + ']').length && + (!$(this).attr('title') || quail.isUnreadable($(this).attr('title')))) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); + } + }); +}; +quail.labelMustBeUnique = function (quail, test, Case) { + var labels = { }; + test.get('$scope').find('label[for]').each(function() { + if (typeof labels[$(this).attr('for')] === 'undefined') { + labels[$(this).attr('for')] = 0; + } + labels[$(this).attr('for')]++; + }); + test.get('$scope').find('label[for]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: (labels[$(this).attr('for')] === 1) ? + 'passed' : + 'failed' + }); + test.add(_case); + }); +}; + +quail.labelsAreAssignedToAnInput = function(quail, test, Case) { + test.get('$scope').find('label').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if (!$(this).attr('for')) { + _case.set({ + 'status': 'failed' + }); + } + else { + if (!test.get('$scope').find('#' + $(this).attr('for')).length || + !test.get('$scope').find('#' + $(this).attr('for')).is(':input')) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + } + }); +}; + +quail.languageChangesAreIdentified = function(quail, test, Case) { + var $scope = test.get('$scope'); + var currentLanguage = quail.components.language.getDocumentLanguage($scope, true); + var text, regularExpression, matches, $element, element, failed; + + var noCharactersMatch = function($element, language, matches, regularExpression) { + var $children = $element.find('[lang=' + language + ']'); + var childMatches; + if ($children.length === 0) { + return true; + } + matches = matches.length; + $children.each(function() { + childMatches = quail.getTextContents($(this)).match(regularExpression); + if (childMatches) { + matches -= childMatches.length; + } + }); + return matches > 0; + }; + + var findCurrentLanguage = function($element) { + if ($element.attr('lang')) { + return $element.attr('lang').trim().toLowerCase().split('-')[0]; + } + if ($element.parents('[lang]').length) { + return $element.parents('[lang]:first').attr('lang').trim().toLowerCase().split('-')[0]; + } + return quail.components.language.getDocumentLanguage($scope, true); + }; + + $scope.find(quail.textSelector).each(function() { + element = this; + $element = $(this); + currentLanguage = findCurrentLanguage($element); + text = quail.getTextContents($element); + failed = false; + + $.each(quail.components.language.scriptSingletons, function(code, regularExpression) { + if (code === currentLanguage) { + return; + } + matches = text.match(regularExpression); + if (matches && matches.length && noCharactersMatch($element, code, matches, regularExpression)) { + //debugger; + test.add(Case({ + element: element, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + info: { language : code }, + status: 'failed' + })); + failed = true; + } + }); + $.each(quail.components.language.scripts, function(code, script) { + if (script.languages.indexOf(currentLanguage) !== -1) { + return; + } + matches = text.match(script.regularExpression); + if (matches && matches.length && noCharactersMatch($element, code, matches, regularExpression)) { + //debugger; + test.add(Case({ + element: element, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + info: { language : code }, + status: 'failed' + })); + failed = true; + } + }); + if (typeof guessLanguage !== 'undefined' && !$element.find('[lang]').length && $element.text().trim().length > 400) { + guessLanguage.info($element.text(), function(info) { + if (info[0] !== currentLanguage) { + //debugger; + test.add(Case({ + element: element, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + info: { language : info[0] }, + status: 'failed' + })); + failed = true; + } + }); + } + // Passes. + if (!failed) { + test.add(Case({ + element: element, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + status: 'passed' + })); + } + }); +}; + +quail.languageDirAttributeIsUsed = function(quail, test, Case) { + + var textDirection = quail.components.language.textDirection; + + function countDirAttributes() { + var $el = $(this); + var currentDirection = $el.attr('dir'); + if (!currentDirection) { + var parentDir = $el.closest('[dir]').attr('dir'); + currentDirection = parentDir || currentDirection; + } + if (typeof currentDirection === 'string') { + currentDirection = currentDirection.toLowerCase(); + } + if (typeof textDirection[currentDirection] === 'undefined') { + currentDirection = 'ltr'; + } + var oppositeDirection = (currentDirection === 'ltr') ? 'rtl' : 'ltr'; + var text = quail.getTextContents($el); + var textMatches = text.match(textDirection[oppositeDirection]); + if (!textMatches) { + return; + } + var matches = textMatches.length; + $el.find('[dir=' + oppositeDirection + ']').each(function() { + var childMatches = $el[0].textContent.match(textDirection[oppositeDirection]); + if (childMatches) { + matches -= childMatches.length; + } + }); + + var _case = test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) + })); + + _case.set({status: (matches > 0) ? 'failed' : 'passed'}); + } + + test.get('$scope').each(function () { + $(this).find(quail.textSelector).each(countDirAttributes); + }); +}; + +quail.languageDirectionPunctuation = function(quail, test, Case) { + var $scope = test.get('$scope'); + var punctuation = {}; + var punctuationRegex = /[\u2000-\u206F]|[!"#$%&'\(\)\]\[\*+,\-.\/:;<=>?@^_`{|}~]/gi; + var currentDirection = ($scope.attr('dir')) ? $scope.attr('dir').toLowerCase() : 'ltr'; + var oppositeDirection = (currentDirection === 'ltr') ? 'rtl' : 'ltr'; + var textDirection = quail.components.language.textDirection; + $scope.each(function () { + var $local = $(this); + $local.find(quail.textSelector).each(function() { + var $el = $(this); + if ($el.attr('dir')) { + currentDirection = $el.attr('dir').toLowerCase(); + } + else { + currentDirection = ($el.parent('[dir]').first().attr('dir')) ? $el.parent('[dir]').first().attr('dir').toLowerCase() : currentDirection; + } + if (typeof textDirection[currentDirection] === 'undefined') { + currentDirection = 'ltr'; + } + oppositeDirection = (currentDirection === 'ltr') ? 'rtl' : 'ltr'; + var text = quail.getTextContents($el); + var matches = text.match(textDirection[oppositeDirection]); + var _case = test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) + })); + if (!matches) { + _case.set({status : 'inapplicable'}); + return; + } + var first = text.search(textDirection[oppositeDirection]); + var last = text.lastIndexOf(matches.pop()); + while (punctuation = punctuationRegex.exec(text)) { + if(punctuation.index === first - 1 || + punctuation.index === last + 1) { + _case.set({status: 'failed'}); + return; + } + } + _case.set({status : 'passed'}); + }); + }); +}; + +quail.languageUnicodeDirection = function(quail, test, Case) { + var $scope = test.get('$scope'); + var textDirection = quail.components.language.textDirection; + var textDirectionChanges = quail.components.language.textDirectionChanges; + $scope.each(function () { + var $local = $(this); + $local.find(quail.textSelector).each(function() { + var _case = test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) + })); + var $el = $(this); + var text = $el.text().trim(); + var otherDirection = (text.substr(0, 1).search(textDirection['ltr']) !== -1) ? + 'rtl' : + 'ltr'; + if (text.search(textDirection[otherDirection]) === -1) { + _case.set({status: 'inapplicable'}); + } + else { + if(text.search(textDirectionChanges[otherDirection]) !== -1) { + _case.set({status: 'passed'}); + } + else { + _case.set({status: 'failed'}); + } + } + }); + }); +}; + +quail.linkHasAUniqueContext = function (quail, test, Case) { + + var blockStyle = [ + 'block', + 'flex', + 'list-item', + 'table', + 'table-caption', + 'table-cell' + ]; + + function getLinkSentence (link) { + // Find the closest block-like element + var $link = $(link); + var block = $link; + var text = simplifyText($link.text()); + + while(!block.is('body, html') && blockStyle.indexOf(block.css('display')) === -1) { + block = block.parent(); + } + + var sentences = block.text().match(/[^\.!\?]+[\.!\?]+/g); + if (sentences === null) { + sentences = [block.text()]; + } + + for (var i = 0; i < sentences.length; i+= 1) { + if (simplifyText(sentences[i]).indexOf(text) !== -1) { + return sentences[i].trim(); + } + } + } + + function simplifyText (text) { + var tmp = text.match(/\w+/g); + if (tmp !== null) { + text = tmp.join(' '); + } + return text.toLowerCase(); + } + + function txtNotAlike (a, b) { + return simplifyText("" + a) !== simplifyText("" + b); + } + + + function shareContext (linkA, linkB) { + + if (linkA.href === linkB.href) { + return false; + + } else if (txtNotAlike(linkA.title, linkB.title)) { + return false; + } + + // Find the nearest list item, paragraph or table cell of both items + var linkACtxt = $(linkA).closest('p, li, dd, dt, td, th'); + var linkBCtxt = $(linkB).closest('p, li, dd, dt, td, th'); + + // check if they are different + if (linkACtxt.length !== 0 && linkBCtxt.length !== 0 && + txtNotAlike(getLinkText(linkACtxt), getLinkText(linkBCtxt))) { + return false; + } + + // If one is a table cell and the other isn't, allow it + if (linkACtxt.is('td, th') && !linkBCtxt.is('td, th')) { + return false; + + } else if (linkACtxt.is('td, th') && linkBCtxt.is('td, th')) { + var headerDiff = false; + var headersA = []; + + // Make a list with the simplified text of link A + linkACtxt.tableHeaders().each(function () { + headersA.push(simplifyText($(this).text())); + }); + + // Compare it to the header context of link B + linkBCtxt.tableHeaders().each(function () { + var text = simplifyText($(this).text()); + var pos = headersA.indexOf(text); + // Link B has something not part of link A's context, pass + if (pos === -1) { + headerDiff = true; + + // Remove items part of both header lists + } else { + headersA.splice(pos, 1); + } + }); + // Pass if A or B had a header not part of the other. + if (headerDiff || headersA.length > 0) { + return false; + } + } + + if (txtNotAlike(getLinkSentence(linkA), getLinkSentence(linkB))) { + return false; + } + + return true; + } + + + /** + * Get the text value of the link, including alt attributes + * @param {jQuery} $link + * @return {string} + */ + function getLinkText ($link) { + var text = $link.text(); + $link.find('img[alt]').each(function () { + text += ' ' + this.alt.trim(); + }); + return simplifyText(text); + } + + test.get('$scope').each(function() { + var $scope = $(this); + var $links = $scope.find('a[href]:visible'); + var linkMap = {}; + + + if ($links.length === 0) { + var _case = Case({ + element: this, + status: 'inapplicable', + expected: $scope.closest('.quail-test').data('expected') + }); + test.add(_case); + } + + // Make a map with the link text as key and an array of links with + // that link text as it's value + $links.each(function () { + var text = getLinkText($(this)); + if (typeof linkMap[text] === 'undefined') { + linkMap[text] = []; + } + linkMap[text].push(this); + }); + + + // Iterate over each item in the linkMap + $.each(linkMap, function (linkText, links) { + + // Link text is not unique, so the context should be checked + while (links.length > 1) { + var linkA = links.pop(); + var linkAFailed = false; + + for (var i=links.length - 1; i >= 0; i -= 1) { + var linkB = links[i]; + if (shareContext(linkA, linkB)) { + linkAFailed = true; + links.splice(i, 1); + test.add(Case({ + element: linkB, + status: 'failed', + expected: $(linkB).closest('.quail-test').data('expected') + })); + } + } + test.add(Case({ + element: linkA, + status: (linkAFailed ? 'failed' : 'passed'), + expected: $(linkA).closest('.quail-test').data('expected') + })); + } + + // The link text is unique, pass + if (links.length === 1) { + test.add(Case({ + element: links[0], + status: 'passed', + expected: $(links[0]).closest('.quail-test').data('expected') + })); + } + }); + }); +}; + +quail.listNotUsedForFormatting = function(quail, test, Case) { + test.get('$scope').find('ol, ul').each(function() { + var _case = Case({ + element : this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).find('li').length < 2) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.listOfLinksUseList = function(quail, test, Case) { + var unreadableText = /(♦|›|»|‣|▶|.|◦|>|✓|◽|•|—|◾|\||\*|•|•)/g; + test.get('$scope').find('a').each(function() { + var _case = test.add(Case({ + element: this + })); + var expected = $(this).closest('.quail-test').data('expected'); + // Only test if there's another a tag. + if ($(this).next('a').length) { + var nextText = $(this).get(0).nextSibling.wholeText.replace(unreadableText, ''); + if (!$(this).parent('li').length && quail.isUnreadable(nextText)) { + _case.set({ + 'expected': expected, + 'status': 'failed' + }); + } + else { + _case.set({ + 'expected': expected, + 'status': 'passed' + }); + } + } + }); +}; + +quail.newWindowIsOpened = function(quail, test, Case) { + + var fenestrate = window.open; + var _case; + + window.open = function (event) { + test.each(function (index, _case) { + var href = _case.get('element').href; + if (href.indexOf(event) > -1) { + _case.set('status', 'failed'); + } + }); + }; + + test.get('$scope').find('a').each(function () { + // Save a reference to this clicked tag. + _case = Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) + }); + test.add(_case); + $(this).trigger('click'); + }); + + window.open = fenestrate; +}; + +quail.pNotUsedAsHeader = function(quail, test, Case) { + test.get('$scope').find('p').each(function() { + var _case = Case({ + element : this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).text().search('.') >= 1) { + _case.set({ + 'status': 'inapplicable' + }); + } + var failed = false; + if ($(this).text().search('.') < 1) { + var $paragraph = $(this), + priorParagraph = $paragraph.prev('p'); + // Checking if any of suspectPHeaderTags has exact the same text as a paragraph. + $.each(quail.suspectPHeaderTags, function(index, tag) { + if ($paragraph.find(tag).length) { + $paragraph.find(tag).each(function() { + if ($(this).text().trim() === $paragraph.text().trim()) { + _case.set({ + 'status': 'failed' + }); + failed = true; + } + }); + } + }); + + // Checking if previous paragraph has a different values for style properties given in quail.suspectPCSSStyles. + if ( priorParagraph.length ) { + $.each(quail.suspectPCSSStyles, function(index, cssProperty) { + if ( $paragraph.css(cssProperty) !== priorParagraph.css(cssProperty) ) { + _case.set({ + 'status': 'failed' + }); + failed = true; + return false; // Micro optimization - we no longer need to iterate here. jQuery css() method might be expansive. + } + }); + } + if ($paragraph.css('font-weight') === 'bold') { + _case.set({ + 'status': 'failed' + }); + failed = true; + } + } + if (!failed) { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.paragraphIsWrittenClearly = function (quail, test, Case) { + test.get('$scope').find('p').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var text = quail.components.textStatistics.cleanText($(this).text()); + if (Math.round((206.835 - (1.015 * quail.components.textStatistics.averageWordsPerSentence(text)) - (84.6 * quail.components.textStatistics.averageSyllablesPerWord(text)))) < 60) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.preShouldNotBeUsedForTabularLayout = function(quail, test, Case) { + test.get('$scope').find('pre').each(function() { + var _case = Case({ + element : this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + var rows = $(this).text().split(/[\n\r]+/); + _case.set({ + 'status': (rows.length > 1 && $(this).text().search(/\t/) > -1) ? + 'failed' : + 'passed' + }); + }); +}; + +quail.scriptFocusIndicatorVisible = function() { + quail.html.find(quail.focusElements).each(function() { + + // Preparation for test: remove focus indicators done with CSS + var sheet, rules, rulesCache, rule; + + rulesCache = []; + + for (var i = 0, l = document.styleSheets.length; i < l; ++i) { + sheet = document.styleSheets[i]; + rules = sheet.cssRules || sheet.rules; + + for (var j = rules.length - 1; j >= 0; --j) { + rule = rules[j]; + if (rule.selectorText && rule.selectorText.indexOf(':focus') !== -1) { + rulesCache.push({ + css: rule.cssText, + index: j, + sheet: i + }); + + sheet.deleteRule(j); + } + } + } + + var noFocus = { + borderWidth : $(this).css('border-width'), + borderColor : $(this).css('border-color'), + backgroundColor : $(this).css('background-color'), + boxShadow : $(this).css('box-shadow'), + outlineWidth : $(this).css('outline-width'), + outlineColor : $(this).css('outline-color') + }; + + $(this).focus(); + + // it is sufficient to not remove the default outline on focus: pass test + var outlineWidth = quail.components.convertToPx($(this).css('outline-width')); + if (outlineWidth > 2 && outlineWidth !== quail.components.convertToPx(noFocus.outlineWidth)) { + $(this).blur(); + return; + } + + // in any other case, it is acceptable to change other visual components + + + if (noFocus.backgroundColor !== $(this).css('background-color')) { + $(this).blur(); + return; + } + + var borderWidth = quail.components.convertToPx($(this).css('border-width')); + if (borderWidth > 2 && borderWidth !== quail.components.convertToPx(noFocus.borderWidth)) { + $(this).blur(); + return; + } + + var boxShadow = ($(this).css('box-shadow') && $(this).css('box-shadow') !== 'none') ? $(this).css('box-shadow').match(/(-?\d+px)|(rgb\(.+\))/g) : false; + if (boxShadow && $(this).css('box-shadow') !== noFocus.boxShadow && quail.components.convertToPx(boxShadow[3]) > 3) { + $(this).blur(); + return; + } + + $(this).blur(); + + var ruleCache; + + for (var k = rulesCache.length - 1; k >= 0; --i) { + ruleCache = rulesCache[k]; + + document.styleSheets[ruleCache.sheet].insertRule(ruleCache.css, ruleCache.index); + } + + quail.testFails('scriptFocusIndicatorVisible', $(this)); + }); +}; +quail.selectJumpMenu = function(quail, test, Case) { + var $scope = test.get('$scope'); + if ($scope.find('select').length === 0) { + return; + } + + $scope.find('select').each(function() { + if ($(this).parent('form').find(':submit').length === 0 && + quail.components.hasEventListener($(this), 'change')) { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'passed' + })); + } + }); +}; + +quail.siteMap = function(quail, test, Case) { + var set = true; + var _case = Case({ + element: test.get('$scope').get(0), + expected: test.get('$scope').data('expected') + }); + test.add(_case); + test.get('$scope').find('a').each(function() { + if (_case.get('status') === 'passed') { + return; + } + var text = $(this).text().toLowerCase(); + $.each(quail.strings.siteMap, function(index, string) { + if (text.search(string) > -1) { + set = false; + return; + } + }); + if (set === false) { + _case.set({ + 'status': 'failed' + }); + return; + } + + if (set) { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.skipToContentLinkProvided = function(quail, test, Case) { + test.get('$scope').each(function () { + var $local = $(this); + var skipLinkFound = false; + + $local.find('a[href*="#"]').each(function() { + if (skipLinkFound) { + return; + } + var $link = $(this); + + var fragment = $link.attr('href').split('#').pop(); + var $target = $local.find('#' + fragment); + var strs = quail.strings.skipContent.slice(); + while (!skipLinkFound && strs.length) { + var str = strs.pop(); + if ($link.text().search(str) > -1 && $target.length) { + $link.focus(); + if ($link.is(':visible') && $link.css('visibility') !== 'hidden') { + skipLinkFound = true; + test.add(Case({ + element: $link.get(0), + expected: $link.closest('.quail-test').data('expected'), + 'status': 'passed' + })); + return; + } + $link.blur(); + } + } + }); + if (!skipLinkFound) { + test.add(Case({ + expected: $local.data('expected') || $local.find('[data-expected]').data('expected'), + 'status': 'failed' + })); + } + }); +}; + +quail.tabIndexFollowsLogicalOrder = function (quail, test, Case) { + test.get('$scope').each(function () { + var $local = $(this); + var index = 0; + $local.find('[tabindex]').each(function() { + var $el = $(this); + var tabindex = $el.attr('tabindex'); + if (parseInt(tabindex, 10) >= 0 && parseInt(tabindex, 10) !== index + 1) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + index++; + }); + }); +}; + +quail.tableAxisHasCorrespondingId = function (quail, test, Case) { + test.get('$scope').find('[axis]').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).parents('table').first().find('th#' + $(this).attr('axis')).length === 0) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.tableHeaderLabelMustBeTerse = function (quail, test, Case) { + test.get('$scope').find('th, table tr:first td').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + if ($(this).text().length > 20 && + (!$(this).attr('abbr') || $(this).attr('abbr').length > 20)) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.tableLayoutDataShouldNotHaveTh = function (quail, test, Case) { + test.get('$scope').find('table').each(function() { + var _case = Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + if ($(this).find('th').length !== 0) { + if (!quail.isDataTable($(this))) { + _case.set({ + 'status': 'failed' + }); + } + else { + _case.set({ + 'status': 'passed' + }); + } + } + else { + _case.set({ + 'status': 'inapplicable' + }); + } + }); +}; + +quail.tableLayoutHasNoCaption = function (quail, test, Case) { + test.get('$scope').find('table').each(function() { + if ($(this).find('caption').length) { + if (!quail.isDataTable($(this))) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + 'status': 'inapplicable' + })); + } + }); +}; + +quail.tableLayoutHasNoSummary = function(quail, test, Case) { + test.get('$scope').each(function () { + var $local = $(this); + $local.find('table[summary]').each(function() { + var _case = test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + })); + if (!quail.isDataTable($(this)) && !quail.isUnreadable($(this).attr('summary'))) { + _case.set({status: 'failed'}); + } + else { + _case.set({status: 'passed'}); + } + }); + }); +}; + +quail.tableLayoutMakesSenseLinearized = function(quail, test, Case) { + test.get('$scope').find('table').each(function() { + if (!quail.isDataTable($(this))) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); +}; + +quail.tableNotUsedForLayout = function(quail, test, Case) { + test.get('$scope').find('table').each(function() { + if (!quail.isDataTable($(this))) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + }); +}; + +quail.tableShouldUseHeaderIDs = function(quail, test, Case) { + test.get('$scope').find('table').each(function() { + var $table = $(this); + var tableFailed = false; + if (quail.isDataTable($table)) { + $table.find('th').each(function() { + if (!tableFailed && !$(this).attr('id')) { + tableFailed = true; + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); + if (!tableFailed) { + $table.find('td[header]').each(function() { + if (!tableFailed) { + $.each($(this).attr('header').split(' '), function(index, id) { + if (!$table.find('#' + id).length) { + tableFailed = true; + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); + } + }); + } + } + }); +}; + +quail.tableSummaryDoesNotDuplicateCaption = function(quail, test, Case) { + test.get('$scope').find('table[summary]:has(caption)').each(function() { + if (quail.cleanString($(this).attr('summary')) === quail.cleanString($(this).find('caption:first').text())) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + }); +}; + +quail.tableSummaryIsNotTooLong = function(quail, test, Case) { + test.get('$scope').find('table[summary]').each(function() { + if ($(this).attr('summary').trim().length > 100) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); +}; + +quail.tableUseColGroup = function(quail, test, Case) { + test.get('$scope').find('table').each(function() { + if (quail.isDataTable($(this)) && !$(this).find('colgroup').length) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); +}; + +quail.tableUsesAbbreviationForHeader = function(quail, test, Case) { + test.get('$scope').find('th:not(th[abbr])').each(function() { + if ($(this).text().length > 20) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); +}; + +quail.tableUsesScopeForRow = function(quail, test, Case) { + test.get('$scope').find('table').each(function() { + $(this).find('td:first-child').each(function() { + var $next = $(this).next('td'); + if (($(this).css('font-weight') === 'bold' && $next.css('font-weight') !== 'bold') || + ($(this).find('strong').length && !$next.find('strong').length)) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); + $(this).find('td:last-child').each(function() { + var $prev = $(this).prev('td'); + if (($(this).css('font-weight') === 'bold' && $prev.css('font-weight') !== 'bold') || + ($(this).find('strong').length && !$prev.find('strong').length)) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); + }); +}; + +quail.tableWithMoreHeadersUseID = function(quail, test, Case) { + test.get('$scope').find('table:has(th)').each(function() { + var $table = $(this); + var rows = 0; + $table.find('tr').each(function() { + if ($(this).find('th').length) { + rows++; + } + if (rows > 1 && !$(this).find('th[id]').length) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + }); + }); +}; + +quail.tabularDataIsInTable = function(quail, test, Case) { + test.get('$scope').find('pre').each(function() { + if ($(this).html().search('\t') >= 0) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + }); +}; + +quail.tagsAreNestedCorrectly=function(quail, test, Case){ + + quail.components.htmlSource.getHtml(function(html) { + + var validationResults = quail.components.htmlTagValidator(html); + + var _case = Case({ + // This is just for internal Quail testing. Get the first quail test element + // and then its expected attribute value. This will return 'undefined' for + // any other testing environment. + expected: test.get('$scope').filter('.quail-test').eq(0).data('expected') + }); + + test.add(_case); + + // An error message is returned if a parsing error is found. + if (validationResults) { + _case.set({ + 'status': 'failed', + 'html': validationResults + }); + // Null is return if no parsing error is found; thus the test passes. + } else { + _case.set({ + 'status': 'passed' + }); + } + }); +}; + +quail.textIsNotSmall = function(quail, test, Case) { + test.get('$scope').find(quail.textSelector).each(function() { + var fontSize = $(this).css('font-size'); + if (fontSize.search('em') > 0) { + fontSize = quail.components.convertToPx(fontSize); + } + fontSize = parseInt(fontSize.replace('px', ''), 10); + + if (fontSize < 10) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + }); +}; + +quail.userInputMayBeRequired=function(quail, test, Case){ + test.get('$scope').each(function(){ + + var _case=Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected') + }); + test.add(_case); + + var forms = $(this).find('form'); + var formInputs = 0; + var inputsOutsideForm = $(this).find('input:not(form input, [type=button],[type=reset],[type=image],[type=submit],[type=hidden])'); + + forms.each(function(){ + var inputs=$(this).find('input:not([type=button],[type=reset],[type=image],[type=submit],[type=hidden])'); + if (inputs.length > 1) { + formInputs = inputs.length; + } + }); + + if(formInputs > 0){ + _case.set({ + 'status': 'cantTell' + }); + return; + } + + if(inputsOutsideForm.length > 1) { + _case.set({ + 'status': 'cantTell' + }); + return; + } + + _case.set({ + 'status': 'inapplicable' + }); + + }); +}; + +quail.videoMayBePresent=function(quail, test, Case){ + + var videoExtensions = ['webm', 'flv', 'ogv', 'ogg', 'avi', 'mov', 'qt', 'wmv', 'asf', + 'mp4', 'm4p', 'm4v', 'mpg', 'mp2', 'mpeg', 'mpg', 'mpe', 'mpv', 'm2v', '3gp', '3g2']; + var videoHosts = ['//www.youtube.com/embed/', '//player.vimeo.com/video/']; + + test.get('$scope').each(function(){ + var $this = $(this); + var hasCase = false; // Test if a case has been created + + // video elm is definately a video, and objects could be too. + $this.find('object, video').each(function () { + hasCase = true; + test.add(Case({ + element: this, + expected: $(this).closest('.quail-test').data('expected'), + status: 'cantTell' + })); + }); + + // Links refering to files with an video extensions are probably video + // though the file may not exist. + $this.find('a[href]').each(function () { + var $this = $(this); + var extension = $this.attr('href').split('.').pop(); + if ($.inArray(extension, videoExtensions) !== -1) { + hasCase = true; + test.add(Case({ + element: this, + expected: $this.closest('.quail-test').data('expected'), + status: 'cantTell' + })); + } + }); + + // some iframes with URL's of known video providers are also probably videos + $this.find('iframe').each(function () { + if (this.src.indexOf(videoHosts[0]) !== -1 || + this.src.indexOf(videoHosts[1]) !== -1) { + hasCase = true; + test.add(Case({ + element: this, + expected: $this.closest('.quail-test').data('expected'), + status: 'cantTell' + })); + } + }); + + // if no case was added, return inapplicable + if (!hasCase) { + test.add(Case({ + element: this, + status: 'inapplicable', + expected: $(this).closest('.quail-test').data('expected') + })); + } + + }); +}; + +quail.videosEmbeddedOrLinkedNeedCaptions = function (quail, test, Case) { + + quail.components.video.findVideos(test.get('$scope'), function(element, pass) { + if (!pass) { + test.add(Case({ + element: element[0], + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: element[0], + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(element)), + status: 'passed' + })); + } + }); +}; + +quail.whiteSpaceInWord = function(quail, test, Case) { + var whitespaceGroup, nonWhitespace; + test.get('$scope').find(quail.textSelector).each(function() { + nonWhitespace = ($(this).text()) ? $(this).text().match(/[^\s\\]/g) : false; + whitespaceGroup = ($(this).text()) ? $(this).text().match(/[^\s\\]\s[^\s\\]/g) : false; + if (nonWhitespace && + whitespaceGroup && + whitespaceGroup.length > 3 && + whitespaceGroup.length >= (nonWhitespace.length / 2) - 2) { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'failed' + })); + } + else { + test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)), + status: 'passed' + })); + } + }); +}; + + +quail.whiteSpaceNotUsedForFormatting = function(quail, test, Case) { + test.get('$scope').find(quail.textSelector).each(function() { + var _case = test.add(Case({ + element: this, + expected: (function (element) { + return quail.components.resolveExpectation(element); + }(this)) + })); + if ($(this).find('br').length === 0) { + _case.set({status: 'passed'}); + return; + } + var lines = $(this).html().toLowerCase().split(/()+/); + var lineCount = 0; + $.each(lines, function(index, line) { + if (line.search(/(\s|\ ){2,}/g) !== -1) { + lineCount++; + } + }); + if(lineCount > 1) { + _case.set({status: 'failed'}); + } + else { + _case.set({status: 'cantTell'}); + } + }); +}; + +quail.lib.Case = (function () { + + /** + * A Case is a test against an element. + */ + function Case (attributes) { + return new Case.fn.init(attributes); + } + + // Prototype object of the Case. + Case.fn = Case.prototype = { + constructor: Case, + init: function (attributes) { + this.listeners = {}; + this.timeout = null; + this.attributes = attributes || {}; + + var that = this; + // Dispatch a resolve event if the case is initiated with a status. + if (this.attributes.status && this.attributes.status !== 'untested') { + // Delay the status dispatch to the next execution cycle so that the + // Case will register listeners in this execution cycle first. + setTimeout(function() { + that.resolve(); + }, 0); + } + // Set up a time out for this case to resolve within. + else { + this.attributes.status = 'untested'; + this.timeout = setTimeout(function () { + that.giveup(); + }, 350); + } + + return this; + }, + // Details of the Case. + attributes: null, + get: function (attr) { + return this.attributes[attr]; + }, + set: function (attr, value) { + var isStatusChanged = false; + // Allow an object of attributes to be passed in. + if (typeof attr === 'object') { + for (var prop in attr) { + if (attr.hasOwnProperty(prop)) { + if (prop === 'status') { + isStatusChanged = true; + } + this.attributes[prop] = attr[prop]; + } + } + } + // Assign a single attribute value. + else { + if (attr === 'status') { + isStatusChanged = true; + } + this.attributes[attr] = value; + } + + if (isStatusChanged) { + this.resolve(); + } + return this; + }, + /** + * A test that determines if a case has one of a set of statuses. + * + * @return boolean + * A bit that indicates if the case has one of the supplied statuses. + */ + hasStatus: function (statuses) { + // This is a rought test of arrayness. + if (typeof statuses !== 'object') { + statuses = [statuses]; + } + var status = this.get('status'); + for (var i = 0, il = statuses.length; i < il; ++i) { + if (statuses[i] === status) { + return true; + } + } + return false; + }, + /** + * Dispatches the resolve event; clears the timeout fallback event. + */ + resolve: function () { + clearTimeout(this.timeout); + + var el = this.attributes.element; + var outerEl; + + // Get a selector and HTML if an element is provided. + if (el && el.nodeType && el.nodeType === 1) { + // Allow a test to provide a selector. Programmatically find one if none + // is provided. + this.attributes.selector = this.defineUniqueSelector(el); + + // Get a serialized HTML representation of the element the raised the error + // if the Test did not provide it. + if (!this.attributes.html) { + this.attributes.html = ''; + + // If the element is either the or elements, + // just report that. Otherwise we might be returning the entire page + // as a string. + if (el.nodeName === 'HTML' || el.nodeName === 'BODY') { + this.attributes.html = '<' + el.nodeName + '>'; + } + // Get the parent node in order to get the innerHTML for the selected + // element. Trim wrapping whitespace, remove linebreaks and spaces. + else if (typeof el.outerHTML === 'string') { + outerEl = el.outerHTML.trim().replace(/(\r\n|\n|\r)/gm,"").replace(/>\s+<'); + // Guard against insanely long elements. + // @todo, make this length configurable eventually. + if (outerEl.length > 200) { + outerEl = outerEl.substr(0, 200) + '... [truncated]'; + } + this.attributes.html = outerEl; + } + } + } + + this.dispatch('resolve', this); + }, + /** + * Abandons the Case if it not resolved within the timeout period. + */ + giveup: function () { + clearTimeout(this.timeout); + // @todo, the set method should really have a 'silent' option. + this.attributes.status = 'notTested'; + this.dispatch('timeout', this); + }, + // @todo, make this a set of methods that all classes extend. + listenTo: function (dispatcher, eventName, handler) { + // @todo polyfill Function.prototype.bind. + handler = handler.bind(this); + dispatcher.registerListener.call(dispatcher, eventName, handler); + }, + registerListener: function (eventName, handler) { + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + this.listeners[eventName].push(handler); + }, + dispatch: function (eventName) { + if (this.listeners[eventName] && this.listeners[eventName].length) { + var eventArgs = [].slice.call(arguments); + this.listeners[eventName].forEach(function (handler) { + // Pass any additional arguments from the event dispatcher to the + // handler function. + handler.apply(null, eventArgs); + }); + } + }, + + /** + * Creates a page-unique selector for the selected DOM element. + * + * @param {jQuery} element + * An element in a jQuery wrapper. + * + * @return {string} + * A unique selector for this element. + */ + defineUniqueSelector: function (element) { + /** + * Indicates whether the selector string represents a unique DOM element. + * + * @param {string} selector + * A string selector that can be used to query a DOM element. + * + * @return Boolean + * Whether or not the selector string represents a unique DOM element. + */ + function isUniquePath (selector) { + return $(selector).length === 1; + } + + /** + * Creates a selector from the element's id attribute. + * + * Temporary IDs created by the module that contain "visitorActions" are excluded. + * + * @param {HTMLElement} element + * + * @return {string} + * An id selector or an empty string. + */ + function applyID (element) { + var selector = ''; + var id = element.id || ''; + if (id.length > 0) { + selector = '#' + id; + } + return selector; + } + + /** + * Creates a selector from classes on the element. + * + * Classes with known functional components like the word 'active' are + * excluded because these often denote state, not identity. + * + * @param {HTMLElement} element + * + * @return {string} + * A selector of classes or an empty string. + */ + function applyClasses (element) { + var selector = ''; + // Try to make a selector from the element's classes. + var classes = element.className || ''; + if (classes.length > 0) { + classes = classes.split(/\s+/); + // Filter out classes that might represent state. + classes = reject(classes, function (cl) { + return (/active|enabled|disabled|first|last|only|collapsed|open|clearfix|processed/).test(cl); + }); + if (classes.length > 0) { + return '.' + classes.join('.'); + } + } + return selector; + } + + /** + * Finds attributes on the element and creates a selector from them. + * + * @param {HTMLElement} element + * + * @return {string} + * A selector of attributes or an empty string. + */ + function applyAttributes (element) { + var selector = ''; + var attributes = ['href', 'type']; + var value; + if (typeof element === 'undefined' || + typeof element.attributes === 'undefined' || + element.attributes === null) { + return selector; + } + // Try to make a selector from the element's classes. + for (var i = 0, len = attributes.length; i < len; i++) { + value = element.attributes[attributes[i]] && element.attributes[attributes[i]].value; + if (value) { + selector += '[' + attributes[i] + '="' + value + '"]'; + } + } + return selector; + } + + /** + * Creates a unique selector using id, classes and attributes. + * + * It is possible that the selector will not be unique if there is no + * unique description using only ids, classes and attributes of an + * element that exist on the page already. If uniqueness cannot be + * determined and is required, you will need to add a unique identifier + * to the element through theming development. + * + * @param {HTMLElement} element + * + * @return {string} + * A unique selector for the element. + */ + function generateSelector (element) { + var selector = ''; + var scopeSelector = ''; + var pseudoUnique = false; + var firstPass = true; + + do { + scopeSelector = ''; + // Try to apply an ID. + if ((scopeSelector = applyID(element)).length > 0) { + selector = scopeSelector + ' ' + selector; + // Assume that a selector with an ID in the string is unique. + break; + } + + // Try to apply classes. + if (!pseudoUnique && (scopeSelector = applyClasses(element)).length > 0) { + // If the classes don't create a unique path, tack them on and + // continue. + selector = scopeSelector + ' ' + selector; + // If the classes do create a unique path, mark this selector as + // pseudo unique. We will keep attempting to find an ID to really + // guarantee uniqueness. + if (isUniquePath(selector)) { + pseudoUnique = true; + } + } + + // Process the original element. + if (firstPass) { + // Try to add attributes. + if ((scopeSelector = applyAttributes(element)).length > 0) { + // Do not include a space because the attributes qualify the + // element. Append classes if they exist. + selector = scopeSelector + selector; + } + + // Add the element nodeName. + selector = element.nodeName.toLowerCase() + selector; + + // The original element has been processed. + firstPass = false; + } + + // Try the parent element to apply some scope. + element = element.parentNode; + } while (element && element.nodeType === 1 && element.nodeName !== 'BODY' && element.nodeName !== 'HTML'); + + return selector.trim(); + } + + /** + * Helper function to filter items from a list that pass the comparator + * test. + * + * @param {Array} list + * @param {function} comparator + * A function that return a boolean. True means the list item will be + * discarded from the list. + * @return array + * A list of items the excludes items that passed the comparator test. + */ + function reject (list, comparator) { + var keepers = []; + for (var i = 0, il = list.length; i < il; i++) { + if (!comparator.call(null, list[i])) { + keepers.push(list[i]); + } + } + return keepers; + } + + return element && generateSelector(element); + }, + push: [].push, + sort: [].sort, + concat: [].concat, + splice: [].splice + }; + + // Give the init function the Case prototype. + Case.fn.init.prototype = Case.fn; + + return Case; +}()); + +quail.lib.Section = (function () { + + /** + * A Collection of Tests. + */ + function Section (id, details) { + return new Section.fn.init(id, details); + } + + // Prototype object of the Section. + Section.fn = Section.prototype = { + constructor: Section, + init: function (id, details) { + if (!id) { + return this; + } + this.id = id; + // Create Technique instances for each technique in this section. + if (details.techniques && details.techniques.length) { + for (var i = 0, il = details.techniques.length; i < il; ++i) { + this.push(quail.lib.Technique(details.techniques[i])); + } + return this; + } + return this; + }, + // Setting a length property makes it behave like an array. + length: 0, + // Execute a callback for every element in the matched set. + each: function (iterator) { + var args = [].slice.call(arguments, 1); + for (var i = 0, len = this.length; i < len; ++i) { + args.unshift(this[i]); + args.unshift(i); + iterator.apply(this[i], args); + } + return this; + }, + find: function (testname) { + for (var i = 0, il = this.length; i < il; ++i) { + if (this[i].get('name') === testname) { + return this[i]; + } + } + // Return an empty Section for chaining. + return null; + }, + set: function (testname, details) { + for (var i = 0, il = this.length; i < il; ++i) { + if (this[i].get('name') === testname) { + this[i].set(details); + return this[i]; + } + } + var test = quail.lib.Test(testname, details); + this.push(test); + return test; + }, + addTechnique: function (technique) { + // Register for result events on the technique. + //this.listenTo(technique, 'result', this.regiterTechniqueTestResult); + this.push(technique); + }, + regiterTechniqueTestResult: function () { + + }, + push: [].push, + sort: [].sort, + splice: [].splice + }; + + // Give the init function the Section prototype. + Section.fn.init.prototype = Section.fn; + + return Section; +}()); + +quail.lib.SuccessCriteria = (function () { + + /** + * A Collection of Tests. + */ + function SuccessCriteria (tests) { + return new SuccessCriteria.fn.init(tests); + } + + // Prototype object of the SuccessCriteria. + SuccessCriteria.fn = SuccessCriteria.prototype = { + constructor: SuccessCriteria, + init: function (options) { + // Event listeners. + this.listeners = {}; + + // By default a Success Criteria is untested. + this.attributes = this.attributes || {}; + this.attributes.status = 'untested'; + this.attributes.results = {}; + this.attributes.totals = {}; + + // The evaluator is a callback that will be invoked when tests have + // finished running. + this.set(options || {}); + + return this; + }, + // Setting a length property makes it behave like an array. + length: 0, + // Details of the test. + attributes: null, + get: function (attr) { + // Return the document wrapped in jQuery if scope is not defined. + if (attr === '$scope') { + var scope = this.attributes['scope']; + var $scope = $(this.attributes['scope']); + // @todo, pass in a ref to jQuery to this module. + return (this.attributes[attr]) ? this.attributes[attr] : ((scope) ? $scope : $(document)); + } + return this.attributes[attr]; + }, + set: function (attr, value) { + var isStatusChanged = false; + // Allow an object of attributes to be passed in. + if (typeof attr === 'object') { + for (var prop in attr) { + if (attr.hasOwnProperty(prop)) { + if (prop === 'status') { + isStatusChanged = true; + } + this.attributes[prop] = attr[prop]; + } + } + } + // Assign a single attribute value. + else { + this.attributes[attr] = value; + } + return this; + }, + /** + * Execute a callback for every element in the matched set. + */ + each: function (iterator) { + var args = [].slice.call(arguments, 1); + for (var i = 0, len = this.length; i < len; ++i) { + args.unshift(this[i]); + args.unshift(i); + var cont = iterator.apply(this[i], args); + // Allow an iterator to break from the loop. + if (cont === false) { + break; + } + } + return this; + }, + /** + * Add a Case to the Success Criteria instance, keyed by selector. + */ + add: function (_case) { + if (!this.find(_case.get('selector'))) { + this.push(_case); + } + }, + /** + * Finds a case by its selector. + */ + find: function (selector) { + for (var i = 0, il = this.length; i < il; ++i) { + if (this[i].get('selector') === selector) { + return this[i]; + } + } + return null; + }, + /** + * Adds a TestCollection to be listened to. + * + * There is a preEvaluator function run before tests are added to make sure + * that the test is actually needed. + */ + registerTests: function (testCollection) { + var preEvaluator = this.get('preEvaluator'); + var hasPreEvaluator = typeof preEvaluator !== 'undefined'; + // true means we'll run all the tests as usual, false, we skip the whole thing. + var passedPreEvaluation = true; + if (hasPreEvaluator) { + passedPreEvaluation = preEvaluator.call(this, testCollection); + } + if (!passedPreEvaluation) { + this.set('status', 'inapplicable'); + } + this.set('tests', testCollection); + this.listenTo(testCollection, 'complete', this.evaluate); + }, + /** + * Returns a collection of tests for this success criteria. + */ + filterTests: function (tests) { + var criteriaTests = new quail.lib.TestCollection(); + var name = this.get('name'); + if (!name) { + throw new Error('Success Criteria instances require a name in order to have tests filtered.'); + } + var identifier = name.split(':')[1]; + tests.each(function (index, test) { + var guidelineCoverage = test.getGuidelineCoverage('wcag'); + // Get tests for this success criteria. + for (var criteriaID in guidelineCoverage) { + if (guidelineCoverage.hasOwnProperty(criteriaID)) { + if (criteriaID === identifier) { + criteriaTests.add(test); + } + } + } + }); + return criteriaTests; + }, + /** + * Adds a Case conclusion to the Success Criteria. + * + * @param string conclusion + * @param quail.lib.Case _case + */ + addConclusion: function (conclusion, _case) { + if (!this.get('results')[conclusion]) { + this.get('results')[conclusion] = quail.lib.Test(); + } + this.get('results')[conclusion].push(_case); + // Incremement totals for this conclusion type. + if (!this.get('totals')[conclusion]) { + this.get('totals')[conclusion] = 0; + } + ++(this.get('totals')[conclusion]); + // Incremement totals for the number of cases found. + if (!this.get('totals')['cases']) { + this.get('totals')['cases'] = 0; + } + ++(this.get('totals')['cases']); + }, + /** + * Runs the evaluator callbacks against the completed TestCollection. + */ + evaluate: function (eventName, testCollection) { + if (this.get('status') !== 'inapplicable') { + var sc = this; + var associatedTests = this.filterTests(testCollection); + + // If there are no associated tests, then this Success + // Criteria has no coverage. + if (associatedTests.length === 0) { + this.set('status', 'noTestCoverage'); + } + else { + associatedTests.each(function (index, test) { + test.each(function (index, _case) { + sc.addConclusion(_case.get('status'), _case); + }); + }); + if (size(this.get('results')) === 0) { + this.set('status', 'noResults'); + } + else { + this.set('status', 'tested'); + } + } + } + this.report(); + }, + /** + * Dispatches the complete event. + */ + report: function () { + var args = Array.prototype.slice.call(arguments); + args = [].concat(['successCriteriaEvaluated', this, this.get('tests')], args); + this.dispatch.apply(this, args); + }, + // @todo, make this a set of methods that all classes extend. + listenTo: function (dispatcher, eventName, handler) { + // @todo polyfill Function.prototype.bind. + handler = handler.bind(this); + dispatcher.registerListener.call(dispatcher, eventName, handler); + }, + registerListener: function (eventName, handler) { + // nb: 'this' is the dispatcher object, not the one that invoked listenTo. + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + + this.listeners[eventName].push(handler); + }, + dispatch: function (eventName) { + if (this.listeners[eventName] && this.listeners[eventName].length) { + var eventArgs = [].slice.call(arguments); + this.listeners[eventName].forEach(function (handler) { + // Pass any additional arguments from the event dispatcher to the + // handler function. + handler.apply(null, eventArgs); + }); + } + }, + push: [].push, + sort: [].sort, + splice: [].splice + }; + + /** + * Determines the length of an object. + * + * @param object obj + * The object whose size will be determined. + * + * @return number + * The size of the object determined by the number of keys. + */ + function size (obj) { + return Object.keys(obj).length; + } + + // Give the init function the SuccessCriteria prototype. + SuccessCriteria.fn.init.prototype = SuccessCriteria.fn; + + return SuccessCriteria; +}()); + +quail.lib.Technique = (function () { + + /** + * A collection of Cases. + */ + function Technique (name, attributes) { + return new Technique.fn.init(name, attributes); + } + + // Prototype object of the Technique. + Technique.fn = Technique.prototype = { + constructor: Technique, + init: function (name, attributes) { + this.listeners = {}; + if (!name) { + return this; + } + this.attributes = attributes || {}; + this.attributes.name = name; + + return this; + }, + // Setting a length property makes it behave like an array. + length: 0, + // Details of the test. + attributes: {}, + // Execute a callback for every element in the matched set. + each: function (iterator) { + var args = [].slice.call(arguments, 1); + for (var i = 0, len = this.length; i < len; ++i) { + args.unshift(this[i]); + args.unshift(i); + iterator.apply(this[i], args); + } + return this; + }, + get: function (attr) { + return this.attributes[attr]; + }, + set: function (attr, value) { + // Allow an object of attributes to be passed in. + if (typeof attr === 'object') { + for (var prop in attr) { + if (attr.hasOwnProperty(prop)) { + this.attributes[prop] = attr[prop]; + } + } + } + // Assign a single attribute value. + else { + this.attributes[attr] = value; + } + return this; + }, + addTest: function () { + + }, + report: function (eventName, test) { + window.console && window.console.log(this.get('name'), test.status, test, test[0] && test[0].status); + }, + // @todo, make this a set of methods that all classes extend. + listenTo: function (dispatcher, eventName, handler) { + // @todo polyfill Function.prototype.bind. + handler = handler.bind(this); + dispatcher.registerListener.call(dispatcher, eventName, handler); + }, + registerListener: function (eventName, handler) { + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + this.listeners[eventName].push(handler); + }, + dispatch: function (eventName) { + if (this.listeners[eventName] && this.listeners[eventName].length) { + var eventArgs = [].slice.call(arguments); + this.listeners[eventName].forEach(function (handler) { + // Pass any additional arguments from the event dispatcher to the + // handler function. + handler.apply(null, eventArgs); + }); + } + }, + push: [].push, + sort: [].sort, + splice: [].splice + }; + + // Give the init function the Technique prototype. + Technique.fn.init.prototype = Technique.fn; + + return Technique; +}()); + +quail.lib.Test = (function () { + + /** + * A collection of Cases. + */ + function Test (name, attributes) { + return new Test.fn.init(name, attributes); + } + + // Prototype object of the Test. + Test.fn = Test.prototype = { + constructor: Test, + init: function (name, attributes) { + this.listeners = {}; + this.length = 0; + if (!name) { + return this; + } + this.attributes = attributes || {}; + this.attributes.name = name; + this.attributes.status = 'untested'; + this.attributes.complete = false; + + return this; + }, + // Setting a length property makes it behave like an array. + length: 0, + // Details of the test. + attributes: null, + // Execute a callback for every element in the matched set. + each: function (iterator) { + var args = [].slice.call(arguments, 1); + for (var i = 0, len = this.length; i < len; ++i) { + args.unshift(this[i]); + args.unshift(i); + iterator.apply(this[i], args); + } + return this; + }, + get: function (attr) { + // Return the document wrapped in jQuery if scope is not defined. + if (attr === '$scope') { + var scope = this.attributes['scope']; + var $scope = $(this.attributes['scope']); + // @todo, pass in a ref to jQuery to this module. + return (this.attributes[attr]) ? this.attributes[attr] : ((scope) ? $scope : $(document)); + } + return this.attributes[attr]; + }, + set: function (attr, value) { + var isStatusChanged = false; + // Allow an object of attributes to be passed in. + if (typeof attr === 'object') { + for (var prop in attr) { + if (attr.hasOwnProperty(prop)) { + if (prop === 'status') { + isStatusChanged = true; + } + this.attributes[prop] = attr[prop]; + } + } + } + // Assign a single attribute value. + else { + if (attr === 'status') { + isStatusChanged = true; + } + this.attributes[attr] = value; + } + + if (isStatusChanged) { + this.resolve(); + } + return this; + }, + add: function (_case) { + this.listenTo(_case, 'resolve', this.caseResponded); + this.listenTo(_case, 'timeout', this.caseResponded); + // If the case is already resolved because it has a status, then trigger + // its resolve event. + if (_case.status) { + _case.dispatch('resolve', _case); + } + this.push(_case); + return _case; + }, + invoke: function () { + // This test is already running. + if (this.testComplete) { + throw new Error('The test ' + this.get('name') + ' is already running.'); + } + // This test has already been run. + if (this.attributes.complete) { + throw new Error('The test ' + this.get('name') + ' has already been run.'); + } + + var type = this.get('type'); + var options = this.get('options') || {}; + var callback = this.get('callback'); + var test = this; + + // Set the test complete method to the closure function that dispatches + // the complete event. This method needs to be debounced so it is only + // called after a pause of invocations. + this.testComplete = debounce(testComplete.bind(this), 400); + + // Invoke the complete dispatcher to prevent the test from never + // completing in the off chance that no Cases are created. + this.testComplete(false); + + if (type === 'custom') { + if (typeof callback === 'function') { + try { + callback.call(this, quail, test, quail.lib.Case, options); + } + catch (e) { + if (window.console && window.console.error) { + window.console.error(e); + } + } + } + else if (type === 'custom' && typeof quail[callback] === 'function') { + try { + quail[callback].call(this, quail, test, quail.lib.Case, options); + } + catch (e) { + if (window.console && window.console.error) { + window.console.error(e); + } + } + } + else { + throw new Error('The callback ' + callback + ' cannot be invoked.'); + } + } + else if (typeof quail.components[type] === 'function') { + try { + quail.components[type].call(this, quail, test, quail.lib.Case, options); + } + catch (e) { + if (window.console && window.console.error) { + window.console.error(e); + } + } + } + else { + throw new Error('The component type ' + type + ' is not defined.'); + } + + // Invoke the complete dispatcher to prevent the test from never + // completing in the off chance that no Cases are created. + this.testComplete(); + + return this; + }, + /** + * Finds cases by their status. + */ + findByStatus: function (statuses) { + if (!statuses) { + return; + } + var test = new Test(); + // A single status or an array of statuses is allowed. Always act on an + // array. + if (typeof statuses === 'string') { + statuses = [statuses]; + } + // Loop the through the statuses and find tests with them. + for (var i = 0, il = statuses.length; i < il; ++i) { + var status = statuses[i]; + // Loop through the cases. + this.each(function (index, _case) { + var caseStatus = _case.get('status'); + if (caseStatus === status) { + test.add(_case); + } + }); + } + return test; + }, + /** + * Returns a set of cases with corresponding to th supplied selector. + */ + findCasesBySelector: function (selector) { + var cases = this.groupCasesBySelector(); + if (cases.hasOwnProperty(selector)) { + return cases[selector]; + } + return new Test(); + }, + /** + * Returns a single Case object the matches the supplied HTML. + * + * We make the assumption, rightly or wrongly, that if the HTML is the + * same for a number of cases in a Test, then the outcome will also + * be the same, so only use this method if you are probing the result + * of the case, not other specifics of it. + * + * @param string html + * A string representing an HTML structure. + * + * @needstests + */ + findCaseByHtml: function (html) { + var _case; + for (var i = 0, il = this.length; i < il; ++i) { + _case = this[i]; + if (html === _case.get('html')) { + return _case; + } + } + // Always return a Case object. + return quail.lib.Case(); + }, + /** + * Groups the cases by element selector. + * + * @return object + * A hash of cases, keyed by the element selector. + */ + groupCasesBySelector: function () { + var casesBySelector = {}; + // Loop through the cases. + this.each(function (index, _case) { + var selector = _case.get('selector'); + if (!casesBySelector[selector]) { + casesBySelector[selector] = new Test(); + } + casesBySelector[selector].add(_case); + }); + return casesBySelector; + }, + /** + * Groups the cases by serialized HTML string. + * + * @todo, the html string index needs to be hashed to a uniform length. + * + * @return object + * A hash of cases, keyed by the element selector. + */ + groupCasesByHtml: function () { + var casesByHtml = {}; + // Loop through the cases. + this.each(function (index, _case) { + var html = _case.get('html'); + if (!casesByHtml[html]) { + casesByHtml[html] = new Test(); + } + casesByHtml[html].add(_case); + }); + return casesByHtml; + }, + /** + * @needsdoc + */ + getGuidelineCoverage: function (name) { + var config = this.get('guidelines'); + return config && config[name] || {}; + }, + /** + * Adds the test that owns the Case to the set of arguments passed up to + * listeners of this test's cases. + */ + caseResponded: function (eventName, _case) { + this.dispatch(eventName, this, _case); + // Attempt to declare the Test complete. + if (typeof this.testComplete === 'function') { + this.testComplete(); + } + }, + /** + * Evaluates the test's cases and sets the test's status. + */ + determineStatus: function () { + // Invoke post filtering. This is a very special case for color.js. + var type = this.get('type'); + var passed; + if (quail.components[type] && typeof quail.components[type].postInvoke === 'function') { + passed = quail.components[type].postInvoke.call(this, this); + } + // The post invocation function for the component declares that this test + // passed. + if (passed === true) { + this.set({ + 'status': 'passed' + }); + } + // CantTell. + else if (this.findByStatus(['cantTell']).length === this.length) { + this.set({ + 'status': 'cantTell' + }); + } + // inapplicable. + else if (this.findByStatus(['inapplicable']).length === this.length) { + this.set({ + 'status': 'inapplicable' + }); + } + // Failed. + else if (this.findByStatus(['failed', 'untested']).length) { + this.set({ + 'status': 'failed' + }); + } + else { + this.set({ + 'status': 'passed' + }); + } + }, + resolve: function () { + this.dispatch('complete', this); + }, + /** + * A stub method implementation. + * + * It is assigned a function value when the Test is invoked. See the + * testComplete function in outer scope. + */ + testComplete: null, + // @todo, make this a set of methods that all classes extend. + listenTo: function (dispatcher, eventName, handler) { + // @todo polyfill Function.prototype.bind. + handler = handler.bind(this); + dispatcher.registerListener.call(dispatcher, eventName, handler); + }, + registerListener: function (eventName, handler) { + // nb: 'this' is the dispatcher object, not the one that invoked listenTo. + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + + this.listeners[eventName].push(handler); + }, + dispatch: function (eventName) { + if (this.listeners[eventName] && this.listeners[eventName].length) { + var eventArgs = [].slice.call(arguments); + this.listeners[eventName].forEach(function (handler) { + // Pass any additional arguments from the event dispatcher to the + // handler function. + handler.apply(null, eventArgs); + }); + } + }, + push: [].push, + sort: [].sort, + concat: [].concat, + splice: [].splice + }; + + /** + * Dispatches the complete event. + * + * This function is meant to be bound to a Test as a method through + * a debounced proxy function. + */ + function testComplete (complete) { + complete = (typeof complete === 'undefined') ? true : complete; + // @todo, this iteration would be faster with _.findWhere, that breaks on + // the first match. + this.each(function (index, _case) { + if (!_case.get('status')) { + complete = false; + } + }); + // If all the Cases have been evaluated, dispatch the event. + if (complete) { + this.testComplete = null; + // @todo, this should be set with the set method and a silent flag. + this.attributes.complete = true; + this.determineStatus(); + } + // Otherwise attempt to the complete the Test again after the debounce + // period has expired. + else { + this.testComplete(); + } + } + + /** + * Limits the invocations of a function in a given time frame. + * + * Adapted from underscore.js. Replace with debounce from underscore once class + * loading with modules is in place. + * + * @param {Function} callback + * The function to be invoked. + * + * @param {Number} wait + * The time period within which the callback function should only be + * invoked once. For example if the wait period is 250ms, then the callback + * will only be called at most 4 times per second. + */ + function debounce (func, wait, immediate) { + + "use strict"; + + var timeout, result; + return function () { + var context = this; + var args = arguments; + var later = function () { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + } + + // Give the init function the Test prototype. + Test.fn.init.prototype = Test.fn; + + return Test; +}()); + +quail.lib.TestCollection = (function () { + + /** + * A Collection of Tests. + */ + function TestCollection (tests) { + return new TestCollection.fn.init(tests); + } + + // Prototype object of the TestCollection. + TestCollection.fn = TestCollection.prototype = { + constructor: TestCollection, + init: function (tests, options) { + this.listeners = {}; + options = options || {}; + if (!tests) { + return this; + } + if (typeof tests === 'object') { + var test; + for (var name in tests) { + if (tests.hasOwnProperty(name)) { + tests[name].scope = tests[name].scope || options.scope; + test = new quail.lib.Test(name, tests[name]); + this.listenTo(test, 'results', this.report); + this.push(test); + } + } + return this; + } + return this; + }, + // Setting a length property makes it behave like an array. + length: 0, + // Invoke all the tests in a set. + run: function (callbacks) { + var tc = this; + callbacks = callbacks || {}; + this.each(function (index, test) { + // Allow a prefilter to remove a case. + if (callbacks.preFilter) { + tc.listenTo(test, 'resolve', function (eventName, test, _case) { + var result = callbacks.preFilter(eventName, test, _case); + if (result === false) { + // Manipulate the attributes directly so that change events + // are not triggered. + _case.attributes.status = 'notTested'; + _case.attributes.expected = null; + } + }); + } + // Allow the invoker to listen to resolve events on each Case. + if (callbacks.caseResolve) { + tc.listenTo(test, 'resolve', callbacks.caseResolve); + } + // Allow the invoker to listen to complete events on each Test. + if (callbacks.testComplete) { + tc.listenTo(test, 'complete', callbacks.testComplete); + } + }); + + // Allow the invoker to listen to complete events for the + // TestCollection. + if (callbacks.testCollectionComplete) { + tc.listenTo(tc, 'complete', callbacks.testCollectionComplete); + } + + // Set the test complete method to the closure function that dispatches + // the complete event. This method needs to be debounced so it is + // only called after a pause of invocations. + this.testsComplete = debounce(testsComplete.bind(this), 500); + + // Invoke each test. + this.each(function(index, test) { + test.invoke(); + }); + + // Invoke the complete dispatcher to prevent the collection from never + // completing in the off chance that no Tests are run. + this.testsComplete(); + + return this; + }, + /** + * Execute a callback for every element in the matched set. + */ + each: function (iterator) { + var args = [].slice.call(arguments, 1); + for (var i = 0, len = this.length; i < len; ++i) { + args.unshift(this[i]); + args.unshift(i); + var cont = iterator.apply(this[i], args); + // Allow an iterator to break from the loop. + if (cont === false) { + break; + } + } + return this; + }, + /** + * Add a Test object to the set. + */ + add: function (test) { + // Don't add a test that already exists in this set. + if (!this.find(test.get('name'))) { + this.push(test); + } + }, + /** + * Finds a test by its name. + */ + find: function (testname) { + for (var i = 0, il = this.length; i < il; ++i) { + if (this[i].get('name') === testname) { + return this[i]; + } + } + return null; + }, + /** + * @info, this should be a static method. + */ + findByGuideline: function (guidelineName) { + + var methods = { + 'wcag': function (section, technique) { + + function findAllTestsForTechnique (guidelineName, sectionId, techniqueName) { + // Return a TestCollection instance. + var tests = new TestCollection(); + this.each(function (index, test) { + // Get the configured guidelines for the test. + var guidelines = test.get('guidelines'); + // If this test is configured for this section and it has + // associated techniques, then loop thorugh them. + var testTechniques = guidelines[guidelineName] && guidelines[guidelineName][sectionId] && guidelines[guidelineName][sectionId]['techniques']; + if (testTechniques) { + for (var i = 0, il = testTechniques.length; i < il; ++i) { + // If this test is configured for the techniqueName, add it + // to the list of tests. + if (testTechniques[i] === techniqueName) { + tests.listenTo(test, 'results', tests.report); + tests.add(test); + } + } + } + }); + return tests; + } + var sectionId = section.id; + var techniqueName = technique.get('name'); + if (sectionId && techniqueName) { + return findAllTestsForTechnique.call(this, guidelineName, sectionId, techniqueName); + } + } + }; + // Process the request using a specific guideline finding method. + // @todo, make these pluggable eventually. + if (methods[guidelineName]) { + var args = [].slice.call(arguments, 1); + return methods[guidelineName].apply(this, args); + } + }, + /** + * Finds tests by their status. + */ + findByStatus: function (statuses) { + if (!statuses) { + return; + } + var tests = new TestCollection(); + // A single status or an array of statuses is allowed. Always act on an + // array. + if (typeof statuses === 'string') { + statuses = [statuses]; + } + // Loop the through the statuses and find tests with them. + for (var i = 0, il = statuses.length; i < il; ++i) { + var status = statuses[i]; + // Loop through the tests. + this.each(function (index, test) { + var testStatus = test.get('status'); + if (testStatus === status) { + tests.add(test); + } + }); + } + return tests; + }, + /** + * Create a new test from a name and details. + */ + set: function (testname, details) { + for (var i = 0, il = this.length; i < il; ++i) { + if (this[i].get('name') === testname) { + this[i].set(details); + return this[i]; + } + } + var test = quail.lib.Test(testname, details); + this.push(test); + return test; + }, + /** + * A stub method implementation. + * + * It is assigned a function value when the collection is run. See the + * testsComplete function in outer scope. + */ + testsComplete: null, + report: function () { + this.dispatch.apply(this, arguments); + }, + // @todo, make this a set of methods that all classes extend. + listenTo: function (dispatcher, eventName, handler) { + // @todo polyfill Function.prototype.bind. + handler = handler.bind(this); + dispatcher.registerListener.call(dispatcher, eventName, handler); + }, + registerListener: function (eventName, handler) { + // nb: 'this' is the dispatcher object, not the one that invoked listenTo. + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + + this.listeners[eventName].push(handler); + }, + dispatch: function (eventName) { + if (this.listeners[eventName] && this.listeners[eventName].length) { + var eventArgs = [].slice.call(arguments); + this.listeners[eventName].forEach(function (handler) { + // Pass any additional arguments from the event dispatcher to the + // handler function. + handler.apply(null, eventArgs); + }); + } + }, + push: [].push, + sort: [].sort, + splice: [].splice + }; + + /** + * Dispatches the complete event. + * + * This function is meant to be bound to a Test as a method through + * a debounced proxy function. + */ + function testsComplete () { + var complete = true; + // @todo, this iteration would be faster with _.findWhere, that breaks on + // the first match. + this.each(function (index, test) { + if (!test.get('complete')) { + complete = false; + } + }); + // If all the Tests have completed, dispatch the event. + if (complete) { + this.testsComplete = null; + this.dispatch('complete', this); + } + // Otherwise attempt to the complete the Tests again after the debounce + // period has expired. + else { + this.testsComplete(); + } + } + + /** + * Limits the invocations of a function in a given time frame. + * + * Adapted from underscore.js. Replace with debounce from underscore once class + * loading with modules is in place. + * + * @param {Function} callback + * The function to be invoked. + * + * @param {Number} wait + * The time period within which the callback function should only be + * invoked once. For example if the wait period is 250ms, then the callback + * will only be called at most 4 times per second. + */ + function debounce (func, wait, immediate) { + + "use strict"; + + var timeout, result; + return function () { + var context = this; + var args = arguments; + var later = function () { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + } + + // Give the init function the TestCollection prototype. + TestCollection.fn.init.prototype = TestCollection.fn; + + return TestCollection; +}()); + +// Iterates over Techniques. + +quail.lib.WCAGGuideline = (function () { + + /** + * A Collection of Tests. + */ + var WCAGGuideline = function (tests) { + return new WCAGGuideline.fn.init(tests); + }; + + // Prototype object of the WCAGGuideline. + WCAGGuideline.fn = WCAGGuideline.prototype = { + constructor: WCAGGuideline, + init: function (config) { + if (!config) { + return this; + } + this.techniques = []; + var guidelines, section, techniques, techniqueName, technique; + + if (typeof config === 'object') { + if (config.guidelines) { + guidelines = config.guidelines; + for (var sectionNumber in guidelines) { + if (guidelines.hasOwnProperty(sectionNumber)) { + section = guidelines[sectionNumber]; + // Create a section object. Exclude techniques. These will be + // instantiated as object later. + if (section.techniques && section.techniques.length) { + techniques = section.techniques; + delete section.techniques; + } + // Instantiate a Section object. + section = quail.lib.Section(sectionNumber, section); + // Find all the techniques in the sections. + if (techniques.length) { + for (var i = 0, il = techniques.length; i < il; ++i) { + techniqueName = techniques[i]; + // The technique requires a definition (description) within + // the guideline. + if (!config.techniques[techniqueName]) { + throw new Error('Definition for Technique ' + techniqueName + ' is missing from the guideline specification'); + } + // Create a new technique instance if this one does not exist + // yet. + technique = this.findTechnique(techniqueName); + if (!technique) { + technique = quail.lib.Technique(techniqueName, config.techniques[techniqueName]); + this.techniques.push(technique); + } + // Add the technique to the currently processing section. + section.addTechnique(technique); + } + } + this.push(section); + } + } + } + return this; + } + return this; + }, + // Setting a length property makes it behave like an array. + length: 0, + // Execute a callback for every element in the matched set. + each: function (iterator) { + var args = [].slice.call(arguments, 1); + for (var i = 0, len = this.length; i < len; ++i) { + args.unshift(this[i]); + args.unshift(i); + iterator.apply(this[i], args); + } + return this; + }, + find: function (testname) { + for (var i = 0, il = this.length; i < il; ++i) { + if (this[i].get('name') === testname) { + return this[i]; + } + } + return null; + }, + findTechnique : function (techniqueName) { + for (var i = 0, il = this.techniques.length; i < il; ++i) { + if (this.techniques[i].get('name') === techniqueName) { + return this.techniques[i]; + } + } + return null; + }, + set: function (testname, details) { + for (var i = 0, il = this.length; i < il; ++i) { + if (this[i].get('name') === testname) { + this[i].set(details); + return this[i]; + } + } + var test = quail.lib.Test(testname, details); + this.push(test); + return test; + }, + evaluate: function () { + /* loop through all the techniques and evaluate them against the page. */ + }, + results: function () { + /* return evaluation results */ + /* Filter by SC? */ + }, + push: [].push, + sort: [].sort, + splice: [].splice + }; + + // Give the init function the WCAGGuideline prototype. + WCAGGuideline.fn.init.prototype = WCAGGuideline.fn; + + return WCAGGuideline; +}()); + +(function ($) { + var scopeValues = ['row', 'col', 'rowgroup', 'colgroup']; + + $.fn.getTableMap = function () { + var map = []; + this.find('tr').each(function (y) { + if (typeof map[y] === 'undefined') { + map[y] = []; + } + var row = map[y]; + $(this).children().each(function () { + var x; + var i, il; + var cell = $(this); + + // Grab the width and height, undefined, invalid or 0 become 1 + var height = +cell.attr('rowspan') || 1; + var width = +cell.attr('colspan') || 1; + // Make x the first undefined cell in the row + for (i = 0, il = row.length; i <= il; i += 1) { + if (x === undefined && row[i] === undefined) { + x = i; + } + } + // add 'this' to each coordinate in the map based on width and height + for (i = 0, il = width * height; i < il; i += 1) { + // Create a new row if it doesn't exist yet + if (map[y + ~~(i/width)] === undefined) { + map[y + ~~(i/width)] = []; + } + // Add the cell to the correct x / y coordinates + map[y + ~~(i/width)][x + (i % width)] = this; + } + }); + + }); + return map; + }; + + function isColumnHeader(tableMap, cell, x, y) { + var height = cell.attr('rowspan') || 1; + var scope = cell.attr('scope'); + if (scope === 'col') { + return true; + } else if (scopeValues.indexOf(scope) !== -1) { + return false; + } + + for (var i = 0; i < height * tableMap[y].length -1; i+=1) { + var currCell = $(tableMap[y + i % height][~~(i / height)]); + if (currCell.is('td')) { + return false; + } + } + return true; + } + + function isRowHeader(tableMap, cell, x, y) { + var width = cell.attr('colspan') || 1; + var scope = cell.attr('scope'); + + if (scope === 'row') { + return true; + } else if (scopeValues.indexOf(scope) !== -1 || + isColumnHeader(tableMap, cell, x, y)) { + return false; + } + + for (var i = 0; i < width * tableMap.length -1; i+=1) { + var currCell = $(tableMap[~~(i / width)][x + i % width]); + if (currCell.is('td')) { + return false; + } + } + return true; + } + + function scanHeaders(tableMap, x, y, deltaX, deltaY) { + var headerList = $(); + var cell = $(tableMap[y][x]); + var opaqueHeaders = []; + var inHeaderBlock; + var headersFromCurrBlock; + + if (cell.is('th')) { + headersFromCurrBlock = [{ + cell: cell, + x: x, + y: y + }]; + + inHeaderBlock = true; + } else { + inHeaderBlock = false; + headersFromCurrBlock = []; + } + + for (; x >= 0 && y >= 0; x += deltaX, y += deltaY) { + var currCell = $(tableMap[y][x]); + var dir = (deltaX === 0 ? 'col' : 'row'); + + if (currCell.is('th')) { + inHeaderBlock = true; + headersFromCurrBlock.push({ + cell: currCell, + x: x, + y: y + }); + var blocked = false; + if (deltaY === -1 && isRowHeader(tableMap, currCell, x, y) || + deltaX === -1 && isColumnHeader(tableMap, currCell, x, y)) { + blocked = true; + + } else { + $.each(opaqueHeaders, function (i, opaqueHeader) { + var currSize = +currCell.attr(dir + 'span') || 1; + var opaqueSize = +$(opaqueHeader.cell).attr(dir + 'span') || 1; + if (currSize === opaqueSize) { + if (deltaY === -1 && opaqueHeader.x === x || + deltaX === -1 && opaqueHeader.y === y) { + blocked = true; + } + } + }); + } + if (blocked === false) { + headerList = headerList.add(currCell); + } + + } else if (currCell.is('td') && inHeaderBlock === true) { + inHeaderBlock = false; + opaqueHeaders.push(headersFromCurrBlock); + headersFromCurrBlock = $(); + } + } + return headerList; + } + + /** + * Get header cells based on the headers attribute of a cell + */ + function getHeadersFromAttr(cell) { + var table = cell.closest('table'); + var ids = cell.attr('headers').split(/\s/); + var headerCells = $(); + // For each IDREF select an element with that ID from the table + // Only th/td cells in the same table can be headers + $.each(ids, function (i, id) { + headerCells = headerCells.add($('th#' + id + ', td#' + id, table)); + }); + return headerCells; + } + + function findCellInTableMap(tableMap, cell) { + var i = 0; + var y = 0; + var x; + // Locate the x and y coordinates of the current cell + while (x === undefined) { + if (tableMap[y] === undefined) { + return; + } else if (tableMap[y][i] === cell[0]) { + x = i; + + } else if (i + 1 === tableMap[y].length) { + y += 1; + i = 0; + } else { + i += 1; + } + } + return {x: x, y: y}; + } + + + function getHeadersFromScope(cell, tableMap) { + var i; + var headerCells = $(); + var coords = findCellInTableMap(tableMap, cell); + + // Grab the width and height, undefined, invalid or 0 become 1 + var height = +cell.attr('rowspan') || 1; + var width = +cell.attr('colspan') || 1; + + for (i = 0; i < width; i++) { + headerCells = headerCells.add( + scanHeaders(tableMap, coords.x + i, coords.y, 0, -1) + ); + } + + for (i = 0; i < height; i++) { + headerCells = headerCells.add( + scanHeaders(tableMap, coords.x, coords.y + i, -1, 0) + ); + } + return headerCells; + } + + + function getHeadersFromGroups(cell, tableMap) { + var cellCoords = findCellInTableMap(tableMap, cell); + var headers = $(); + + cell.closest('thead, tbody, tfoot') + .find('th[scope=rowgroup]').each(function () { + var headerCoords = findCellInTableMap(tableMap, $(this)); + if (headerCoords.x <= cellCoords.x && headerCoords.y <= cellCoords.y) { + headers = headers.add(this); + } + }); + + // TODO colgroups + + } + + $.fn.tableHeaders = function () { + var headers = $(); + this.each(function () { + var $this = $(this); + + if ($this.is(':not(td, th)')) { + return; + } + + if ($this.is('[headers]')) { + headers = headers.add(getHeadersFromAttr($this)); + + } else { + var map = $this.closest('table').getTableMap(); + headers = headers + .add(getHeadersFromScope($this, map)) + .add(getHeadersFromGroups($this, map)); + + } + }); + return headers.not(':empty').not(this); + }; + + +}(jQuery)); + +quail.lib.wcag2 = (function () { + 'use strict'; + var ajaxOpt = {async: false, dataType: 'json'}; + + /** + * Run Quail using WCAG 2 + * + * Options can be used either to tell Quail where to load the wcag2 structure file + * or to give it directly (if it's already loaded). For the first, jsonPath + * must be provided. For the second the wcag2.json data must be given to + * options.wcag2Structure and the tests data to options.accessibilityTests. + * + * @param {[object]} options Quail options + */ + function runWCAG20Quail(options) { + if (options.wcag2Structure && options.accessibilityTests && options.preconditionTests) { + startWCAG20Quail( + options, + options.wcag2Structure, + options.accessibilityTests, + options.preconditionTests); + + } else { + // Load the required json files + $.when( + $.ajax(options.jsonPath + '/wcag2.json', ajaxOpt), + $.ajax(options.jsonPath + '/tests.json', ajaxOpt), + $.ajax(options.jsonPath + '/preconditions.json', ajaxOpt)) + + // Setup quail given the tests described in the json files + .done(function (wcag2Call, testsCall, preconditionCall) { + startWCAG20Quail(options, wcag2Call[0], testsCall[0], preconditionCall[0]); + }); + + } + } + + function startWCAG20Quail(options, wcag2Call, tests, preconditionTests) { + var criteria, accessibilityTests, knownTests; + var allTests = []; + + criteria = $.map(wcag2Call, function (critData) { + return new quail.lib.wcag2.Criterion( + critData, tests, preconditionTests, options.subject); + }); + + // Create the accessibiliyTests object, based on the + // tests in the criteria + $.each(criteria, function (i, criterion) { + allTests.push.apply(allTests, criterion.getTests()); + }); + + knownTests = []; + accessibilityTests = []; + + // Remove duplicates + // TODO: Figure out why some tests are created multiple times + $.each(allTests, function (i, test) { + if (knownTests.indexOf(test.title.en) === -1) { + knownTests.push(test.title.en); + accessibilityTests.push(test); + } + }); + + // Run quail with the tests instead of the guideline + $(quail.html).quail({ + accessibilityTests: accessibilityTests, + // Have wcag2 intercept the callback + testCollectionComplete: createCallback( + criteria, options.testCollectionComplete) + }); + } + + + function createCallback(criteria, callback) { + return function (status, data) { + if (status === 'complete') { + data = $.map(criteria, function (criterion) { + return criterion.getResult(data); + }); + } + + callback(status, data); + }; + } + + return { + run: runWCAG20Quail + }; + +}()); +quail.guidelines.wcag.successCriteria['1.1.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.1.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = { + 'F3': 'Using CSS to include images that convey important information', + 'F13': 'Having a text alternative that does not include information that is conveyed by color differences in the image', + 'F20': 'Not updating text alternatives when changes to non-text content occur', + 'F30': 'Using text alternatives that are not alternatives (e.g., filenames or placeholder text)', + 'F38': 'Not marking up decorative images in HTML in a way that allows assistive technology to ignore them', + 'F39': 'Providing a text alternative that is not null (e.g., alt="spacer" or alt="image") for images that should be ignored by assistive technology', + 'F65': 'Omitting the alt attribute or text alternative on img elements, area elements, and input elements of type "image"', + 'F67': 'Providing long descriptions for non-text content that does not serve the same purpose or does not present the same information', + 'F71': 'Using text look-alikes to represent text without providing a text alternative', + 'F72': 'Using ASCII art without providing a text alternative' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.2'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G93': 'Providing open (always visible) captions', + // OR + 'G87': 'Providing closed captions' + }; + + // Failures + sc.failures = { + 'F74': 'Not labeling a synchronized media alternative to text as an alternative', + // OR + 'F75': 'Providing synchronized media without captions when the synchronized media presents more information than is presented on the page', + // OR + 'F8': 'Captions omitting some dialogue or important sound effects' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.3'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.4'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.4', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G9': 'Creating captions for live synchronized media', + // AND + 'G93': 'Providing open (always visible) captions', + 'G87': 'Providing closed captions using any readily available media format that has a video player that supports closed captioning' + }; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.5'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.5', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G78': 'Providing a second, user-selectable, audio track that includes audio descriptions', + // OR + 'G173': 'Providing a version of a movie with audio descriptions', + // OR + 'SC1.2.8': 'Providing a movie with extended audio descriptions', + 'G8': 'Providing a movie with extended audio descriptions', + // OR if a talking head video + 'G203': 'Using a static text alternative to describe a talking head video' + }; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.7'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.7', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.8'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.8', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.2.9'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.2.9', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.3.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.3.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + // Situation A: The technology provides semantic structure to make information and relationships conveyed through presentation programmatically determinable + 'G115': 'Using semantic elements to mark up structure AND H49: Using semantic markup to mark emphasized or special text', + // OR + 'G117': 'Using text to convey information that is conveyed by variations in presentation of text', + // OR + 'G140': 'Separating information and structure from presentation to enable different presentations', + // OR Making information and relationships conveyed through presentation programmatically determinable using the following techniques: + 'G138': 'Using semantic markup whenever color cues are used', + 'H48': 'Using ol, ul and dl for lists or groups of links', + 'H42': 'Using h1-h6 to identify headings', + 'SCR21': 'Using functions of the Document Object Model (DOM) to add content to a page (Scripting)', + // Tables + 'H51': 'Using table markup to present tabular information', + 'H39': 'Using caption elements to associate data table captions with data tables', + 'H73': 'Using the summary attribute of the table element to give an overview of data tables', + 'H63': 'Using the scope attribute to associate header cells and data cells in data tables', + 'H43': 'Using id and headers attributes to associate data cells with header cells in data tables', + // Forms + 'H44': 'Using label elements to associate text labels with form controls', + 'H65': 'Using the title attribute to identify form controls when the label element cannot be used', + 'H71': 'Providing a description for groups of form controls using fieldset and legend elements', + 'H85': 'Using OPTGROUP to group OPTION elements inside a SELECT', + // OR + 'ARIA11': 'Using ARIA landmarks to identify regions of a page (ARIA)', + // OR + 'ARIA12': 'Using role=heading to identify headings (ARIA)', + // OR + 'ARIA13': 'Using aria-labelledby to name regions and landmarks (ARIA)', + // OR + 'ARIA16': 'Using aria-labelledby to provide a name for user interface controls (ARIA)', + 'ARIA17': 'Using grouping roles to identify related form controls (ARIA)' + }; + + // Failures + sc.failures = { + // Web page structure + 'F2': 'Using changes in text presentation to convey information without using the appropriate markup or text', + 'F17': 'Insufficient information in DOM to determine one-to-one relationships (e.g., between labels with same id) in HTML', + 'F42': 'Using scripting events to emulate links in a way that is not programmatically determinable', + 'F43': 'Using structural markup in a way that does not represent relationships in the content', + 'F87': 'Inserting non-decorative content by using :before and :after pseudo-elements and the content property in CSS', + // Tables + 'F46': 'Using th elements, caption elements, or non-empty summary attributes in layout tables', + 'F48': 'Using the pre element to markup tabular information', + 'F90': 'Incorrectly associating table headers and content via the headers and id attributes', + 'F91': 'Not correctly marking up table headers', + 'F33': 'Using white space characters to create multiple columns in plain text content', + 'F34': 'Using white space characters to format tables in plain text content', + // Forms + 'F68': 'Association of label and user interface controls not being programmatically determinable' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.3.2'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.3.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G57': 'Ordering the content in a meaningful sequence (scope: for all the content in the Web page)', + // OR + 'H34': 'Using a Unicode right-to-left mark (RLM) or left-to-right mark (LRM) to mix text direction inline (languageUnicodeDirection)', + 'H56': 'Using the dir attribute on an inline element to resolve problems with nested directional runs', + 'C6': 'Positioning content based on structural markup (CSS)', + 'C8': 'Using CSS letter-spacing to control spacing within a word', + // OR + 'C27': 'Making the DOM order match the visual order (CSS)' + }; + + // Failures + sc.failures = { + // HTML Failures + 'F49': 'Using an HTML layout table that does not make sense when linearized', + 'F32': 'Using white space characters to control spacing within a word (whiteSpaceInWord)', + 'F1': 'Changing the meaning of content by positioning information with CSS', + // Plain text Failures + 'F34': 'Using white space characters to format tables in plain text content (tabularDataIsInTable)', + 'F33': 'Using white space characters to create multiple columns in plain text content (tabularDataIsInTable)' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.3.3'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.3.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G96': 'Providing textual identification of items that otherwise rely only on sensory information to be understood' + }; + + // Failures + sc.failures = { + 'F14': 'Identifying content only by its shape or location', + 'F26': 'Using a graphical symbol alone to convey information' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.2'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + // This criteria applies if any media objects exist on the page. It's a + // very crude preEvaluator, to be fair. + return !!$('audio, video, object, embed').length; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G60': 'Playing a sound that turns off automatically within three seconds', + 'G170': 'Providing a control near the beginning of the Web page that turns off sounds that play automatically', + 'G171': 'Playing sounds only on user request' + }; + + // Failures + sc.failures = { + 'F23': 'Playing a sound longer than 3 seconds where there is no mechanism to turn it off' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.3'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + /** + * (unused right now) + * + * Evaluates the Success Criteria. + */ + //function evaluator(tests) { + // // The set of tests that were run that pertain to this Success Criteria. This + // // will be the union of the tests that were run and the required tests. + // var criteriaTests = sc.filterTests(tests, sc.requiredTests); + // // If the length of the union equals the length of the required tests, + // // then we have the necessary tests to evaluate this success criteria. + // if (criteriaTests.length === requiredTests.length) { + // // Find the tests to evaluate. + // var cssTextHasContrast = tests.find('cssTextHasContrast'); + // // Cycle through the cases in the Success Criteria. + // sc.each(function (index, _case) { + // var selector = _case.get('selector'); + // var conclusion = 'untested'; + // var testCase, caseGroups; + + // // Process 'labelsAreAssignedToAnInput'. + // caseGroups = cssTextHasContrast.groupCasesBySelector(selector); + // testCase = caseGroups && caseGroups[selector] && caseGroups[selector][0]; + + // if (testCase) { + // conclusion = testCase.get('status') || 'cantTell'; + // } + + // // Add the case to the Success Criteria. + // sc.addConclusion(conclusion, _case); + // }); + // } + //} + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G148': 'Not specifying background color, not specifying text color, and not using technology features that change those defaults', + 'G174': 'Providing a control with a sufficient contrast ratio that allows users to switch to a presentation that uses sufficient contrast', + 'G18': 'Ensuring that a contrast ratio of at least 4.5:1 exists between text (and images of text) and background behind the text for situation A AND G145: Ensuring that a contrast ratio of at least 3:1 exists between text (and images of text) and background behind the text for situation B' + }; + + // Failures + sc.failures = { + 'F24': 'Specifying foreground colors without specifying background colors or vice versa', + 'F83': 'Using background images that do not provide sufficient contrast with foreground text (or images of text)' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.4'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.4', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G142': 'Using a technology that has commonly-available user agents that support zoom', + 'C12': 'Using percent for font sizes', + 'C13': 'Using named font sizes', + 'C14': 'Using em units for font, sizes', + 'SCR34': 'Calculating size and ,position in a way that scales with text size (Scripting)', + 'G146': 'Using liquid layout', + 'G178': 'Providing controls on the Web page that allow users to incrementally change the size of all text on the page up to 200 percent', + 'G179': 'Ensuring that there is no loss of content or functionality when the text resizes and text containers do not change their width' + }; + + // Failures + sc.failures = { + 'F69': 'Resizing visually rendered text up to 200 percent causes the text, image or controls to be clipped, truncated or obscured', + 'F80': 'Text-based form controls do not resize when visually rendered text is resized up to 200%' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.5'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + // Check for image tags. If the page does not have any, then there is + // nothing to test. + return !!document.querySelectorAll('img, map').length; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.5', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'C22': 'Using CSS to control visual presentation of text (CSS)', + 'C30': 'Using CSS to replace text with images of text and providing user interface controls to switch', + 'G140': 'Separating information and structure from presentation to enable different presentations' + }; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.6'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.6', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.7'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.7', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.8'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.8', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['1.4.9'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:1.4.9', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.1.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.1.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.1.2'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.1.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.1.3'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.1.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.2.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.2.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.2.2'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.2.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.2.3'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.2.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.2.4'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.2.4', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.2.5'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.2.5', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.3.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.3.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.3.2'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.3.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.1'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G1': 'Adding a link at the top of each page that goes directly to the main content area', + 'G123': 'Adding a link at the beginning of a block of repeated content to go to the end of the block', + 'G124': 'Adding links at the top of the page to each area of the content', + 'H69': 'Providing heading elements at the beginning of each section of content', + 'H70': 'Using frame elements to group blocks of repeated material AND H64: Using the title attribute of the frame and iframe elements', + 'SCR28': 'Using an expandable and collapsible menu to bypass block of content' + }; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.10'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.10', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.2'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.3'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.4'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.4', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.5'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.5', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.6'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.6', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.7'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.7', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.8'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.8', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['2.4.9'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:2.4.9', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.1.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.1.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.1.2'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.1.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.1.3'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.1.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.1.4'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.1.4', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.1.5'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.1.5', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.1.6'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.1.6', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.2.1'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.2.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G107': 'Using "activate" rather than "focus" as a trigger for changes of context' + }; + + // Failures + sc.failures = { + 'F52': 'Opening a new window as soon as a new page is loaded', + 'F55': 'Using script to remove focus when focus is received' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.2.2'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.2.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G80': 'Providing a submit button to initiate a change of context', + // AND + 'H32': 'Providing submit buttons', + 'H84': 'Using a button with a select element to perform an action', + + 'G13': 'Describing what will happen before a change to a form control that causes a change of context to occur is made', + 'SCR19': 'Using an onchange event on a select element without causing a change of context' + }; + + // Failures + sc.failures = { + 'F36': 'Automatically submitting a form and presenting new content without prior warning when the last field in the form is given a value', + 'F37': 'Launching a new window without prior warning when the status of a radio button, check box or select list is changed', + 'F76': 'Providing instruction material about the change of context by change of setting in a user interface element at a location that users may bypass' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.2.3'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.2.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G61': 'Presenting repeated components in the same relative order each time they appear' + }; + + // Failures + sc.failures = { + 'F66': 'Presenting navigation links in a different relative order on different pages' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.2.4'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.2.4', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'G197': 'Using labels, names, and text alternatives consistently for content that has the same functionality AND following the sufficient techniques for Success Criterion 1.1.1 and sufficient techniques for Success Criterion 4.1.2 for providing labels, names, and text alternatives.' + }; + + // Failures + sc.failures = { + 'F31': 'Using two different labels for the same function on different Web pages within a set of Web pages' + }; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.2.5'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.2.5', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.3.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.3.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.3.2'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.3.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.3.3'] = (function (quail) { + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + var complexInputTypes = [ + //'button', // Defines a clickable button (mostly used with a JavaScript to activate a script) + 'checkbox', // Defines a checkbox + 'color', // Defines a color picker + 'date', // Defines a date control (year, month and day (no time)) + 'datetime', // Defines a date and time control (year, month, day, hour, minute, second, and fraction of a second, based on UTC time zone) + 'datetime-local', // Defines a date and time control (year, month, day, hour, minute, second, and fraction of a second (no time zone) + 'email', // Defines a field for an e-mail address + 'file', // Defines a file-select field and a "Browse..." button (for file uploads) + 'hidden', // Defines a hidden input field + //'image', // Defines an image as the submit button + 'month', // Defines a month and year control (no time zone) + 'number', // Defines a field for entering a number + 'password', // Defines a password field (characters are masked) + 'radio', // Defines a radio button + 'range', // Defines a control for entering a number whose exact value is not important (like a slider control) + //'reset', // Defines a reset button (resets all form values to default values) + 'search', // Defines a text field for entering a search string + //'submit', // Defines a submit button + 'tel', // Defines a field for entering a telephone number + //'text', // Default. Defines a single-line text field (default width is 20 characters) + 'time', // Defines a control for entering a time (no time zone) + 'url', // Defines a field for entering a URL + 'week' // Defines a week and year control (no time zone) + ]; + + var requiredAttrs = [{ + 'required': 'required' + }, + { + 'aria-required': 'true' + }]; + + // Searches this for complex for types. + // + // @return boolean + // Whether the complex input type exists in the scoped DOM element. + function hasComplexTypes (type) { + return !!this.querySelectorAll('[type="' + type + '"]').length; + } + + function hasTypesWithAttr (attr) { + var key = Object.keys(attr)[0]; + return !!this.querySelectorAll('[' + key + '="' + attr[key] + '"]').length; + } + + + // Testing forms. + // + // If any of the complex form types are present in the document, this + // success criteria applies. + if (document.querySelectorAll('form').length) { + if (complexInputTypes.some(hasComplexTypes, document) || + requiredAttrs.some(hasTypesWithAttr, document) + ) { + return true; + } + } + else { + return false; + } + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.3.3', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + // Required fields + 'G83': 'Providing text descriptions to identify required fields that were not completed', + 'ARIA2': 'Identifying a required field with the aria-required property', + // Field that requires specific data formats + 'ARIA18': 'Using aria-alertdialog to Identify Errors (ARIA)', + 'G85': 'Providing a text description when user input falls outside the required format or values', + 'G177': 'Providing suggested correction text', + 'SCR18': 'Providing client-side validation and alert (Scripting)', + 'SCR32': 'Providing client-side validation and adding error text via the DOM (Scripting)', + // Field with limited set of values + // 'ARIA18': 'Using aria-alertdialog to Identify Errors (ARIA)', + 'G84': 'Providing a text description when the user provides information that is not in the list of allowed values' + // 'G177': 'Providing suggested correction text', + // 'SCR18': 'Providing client-side validation and alert (Scripting)', + // 'SCR32': 'Providing client-side validation and adding error text via the DOM (Scripting)' + }; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.3.4'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.3.4', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.3.5'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.3.5', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['3.3.6'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:3.3.6', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['4.1.1'] = (function (quail) { + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:4.1.1', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = {}; + + // Failures + sc.failures = {}; + + return sc; +}(quail)); + +quail.guidelines.wcag.successCriteria['4.1.2'] = (function (quail) { + + // The tests that must be run in order to evaluate this Success Criteria. + // @todo, identify the complete set of required tests for this Success Criteria. + // var requiredTests = ['labelsAreAssignedToAnInput', 'labelMustBeUnique', 'inputWithoutLabelHasTitle']; + + /** + * Determines if this Success Criteria applies to the document. + */ + function preEvaluator() { + return true; + } + + /** + * (Unused right now) + * + * Evaluates the Success Criteria. + */ + // function evaluator(tests) { + // // The set of tests that were run that pertain to this Success Criteria. This + // // will be the union of the tests that were run and the required tests. + // var criteriaTests = sc.filterTests(tests, sc.requiredTests); + // // If the length of the union equals the length of the required tests, + // // then we have the necessary tests to evaluate this success criteria. + // if (criteriaTests.length === requiredTests.length) { + // // Find the tests to evaluate. + // var labelsAreAssignedToAnInput = tests.find('labelsAreAssignedToAnInput'); + // var labelMustBeUnique = tests.find('labelMustBeUnique'); + // var inputWithoutLabelHasTitle = tests.find('inputWithoutLabelHasTitle'); + + // // Cycle through the cases in the Success Criteria. + // sc.each(function (index, _case) { + // var conclusion = 'untested'; + + // if (_case.get('status') !== 'notTested') { + // var selector = _case.get('selector'); + // if (selector) { + + // // @dev, we'll look at each test individually for this selector. + // // Process 'labelsAreAssignedToAnInput'. + // var cases_labelsAreAssignedToAnInput = labelsAreAssignedToAnInput.findCasesBySelector(selector); + // // Process 'labelMustBeUnique'. + // var cases_labelMustBeUnique = labelMustBeUnique.findCasesBySelector(selector); + // // Process 'inputWithoutLabelHasTitle'. + // var cases_inputWithoutLabelHasTitle = inputWithoutLabelHasTitle.findCasesBySelector(selector); + + // var passing = ['passed', 'inapplicable']; + + // // Make sure the arrays are not empty. + // if ((cases_labelsAreAssignedToAnInput.length >= 1 && cases_labelsAreAssignedToAnInput[0].hasStatus(passing)) && + // (cases_labelMustBeUnique.length >= 1 && cases_labelMustBeUnique[0].hasStatus(passing)) && + // (cases_inputWithoutLabelHasTitle.length >= 1 && cases_inputWithoutLabelHasTitle[0].hasStatus(passing))) { + // conclusion = 'passed'; + // } + + // // Don't bother if it isn't passed? + // if (conclusion === 'passed') { + // // (1) Determine if any of the following failures apply to the element + // // at the selector. If so, fail the Success Criteria for that selector. + // var element = cases_labelMustBeUnique[0].attributes.element; + + // // F59: Using script to make div or span a user interface control in HTML without providing a role for the control (This failure may be solved in the future using DHTML roadmap techniques.) + // if (element.nodeName === 'DIV' || element.nodeName === 'SPAN') { + // conclusion = 'failed'; + // } + + // // F20: Not updating text alternatives when changes to non-text content occur + // // N/A for labelMustBeUnique. + + // // F68: Association of label and user interface controls not being programmatically determined + + // // F79: Focus state of a user interface component not being programmatically determinable or no notification of change of focus state available + // // F86: Not providing names for each part of a multi-part form field, such as a US telephone number + // // F89: Using null alt on an image where the image is the only content in a link ( #59 :ok: ) + // // (2) If no Failures are detected, so if the Techniques are satisfied. If + // // one of the techniques is satisfed for the element at the selector, + // // Success Criteria is satisfied. + // // + // // ARIA14: Using aria-label to provide an invisible label where a visible label cannot be used + // // ARIA16: Using aria-labelledby to provide a name for user interface controls + // // + // // G108: Using markup features to expose the name and role, allow user-settable properties to be directly set, and provide notification of changes using technology-specific techniques below: + // // AND + // // + // // H91: Using HTML form controls and links ( #66 ) + // // H44: Using label elements to associate text labels with form controls + // // H64: Using the title attribute of the frame and iframe elements ( #65 ) + // // H65: Using the title attribute to identify form controls when the label element cannot be used ( #64 ) + // // H88: Using HTML according to spec ( #86 ) + // } + // } + // } + // // Add the case to the Success Criteria. + // sc.addConclusion(conclusion, _case); + // }); + // } + // } + + // Create a new SuccessCriteria and pass it the evaluation callbacks. + var sc = quail.lib.SuccessCriteria({ + 'name': 'wcag:4.1.2', + preEvaluator: preEvaluator + }); + + // Techniques + sc.techniques = { + 'ARIA14': 'Using aria-label to provide an invisible label where a visible label cannot be used', + 'ARIA16': 'Using aria-labelledby to provide a name for user interface controls', + 'G108': 'Using markup features to expose the name and role, allow user-settable properties to be directly set, and provide notification of changes using technology-specific techniques below:', + //AND + 'H91': 'Using HTML form controls and links', + 'H44': 'Using label elements to associate text labels with form controls', + 'H64': 'Using the title attribute of the frame and iframe elements', + 'H65': 'Using the title attribute to identify form controls when the label element cannot be used', + 'H88': 'Using HTML according to spec' + }; + + // Failures + sc.failures = { + 'F59': 'Using script to make div or span a user interface control in HTML without providing a role for the control (This failure may be solved in the future using DHTML roadmap techniques.)', + 'F20': 'Not updating text alternatives when changes to non-text content occur', + 'F68': 'Association of label and user interface controls not being programmatically determined', + 'F79': 'Focus state of a user interface component not being programmatically determinable or no notification of change of focus state available', + 'F86': 'Not providing names for each part of a multi-part form field, such as a US telephone number', + 'F89': 'Using null alt on an image where the image is the only content in a link' + }; + + return sc; +}(quail)); + +quail.lib.wcag2.Criterion = (function () { + + // Provide default values for the assert objects + function aggregateParts(parts, defaultResult) { + var getResultPriority = quail.lib.wcag2.EarlAssertion.getResultPriority; + var outcome = {result: defaultResult}; + + $.each(parts, function (i, part) { + if (getResultPriority(outcome) < getResultPriority(part)) { + outcome.result = part.outcome.result; + } + }); + return outcome; + } + + + function constructor (data, testDefinitions, preconditionDefinitions, subject) { + var testAggregators = []; + var criterion = {}; + var defaultResult = data['default'] || 'untested'; + var id = data.id; + + // Create a TestAggregator object for each aggregator (if any) + if ($.isArray(data.testAggregators)) { + testAggregators = $.map(data.testAggregators, function (aggregateConf) { + return new quail.lib.wcag2.TestAggregator( + aggregateConf, testDefinitions, subject + ); + }); + } + + // Create a precondition test + if ($.isArray(data.preconditions)) { + var preconditionTest = { + type: 'stacking', // If any of it's content is found it should return cantTell + tests: data.preconditions + }; + // Add a test aggregator for the precondition tests + testAggregators.push(new quail.lib.wcag2.TestAggregator( + preconditionTest, preconditionDefinitions, subject + )); + } + + + criterion.getResult = function (data) { + var result; + var parts = []; + + $.each(testAggregators, function (i, aggregator) { + var part = aggregator.getResults(data); + parts.push.apply(parts, part); + }); + result = new quail.lib.wcag2.EarlAssertion({ + testRequirement: id, + outcome: aggregateParts(parts, defaultResult), + subject: subject + }); + if (parts.length > 0) { + result.hasPart = parts; + } + return result; + }; + + /** + * Get an array of test used in this criterion + * @param {[json]} criteria + * @return {[array]} + */ + criterion.getTests = function () { + var tests = []; + $.each(testAggregators, function (i, aggregator) { + tests.push.apply(tests, aggregator.tests); + }); + return tests; + }; + + return criterion; + } + + return constructor; + +}()); + +quail.lib.wcag2.EarlAssertion = (function () { + var pageUrl; + var resultPriorityMap = [ + 'untested', 'inapplicable', 'passed', + 'cantTell', 'failed' + ]; + var defaultAssertion = { + type: 'assertion', + subject: pageUrl, + assertedBy: { + type: 'earl:Software', + name: 'QuailJS' + }, + mode: 'automated' + }; + + + if (window && window.location) { + pageUrl = window.location.href; + } + + /** + * Create a new earl assert object + * @param {object} base Properties from this object are added to the Assertion + * and override the default. + */ + function Assertion(base) { + $.extend(this, base, defaultAssertion); + this.outcome = $.extend({}, this.outcome); + } + + + /** + * Return the priorty index of the result + * @param {result|assert|outcome} val + * @return {integer} Result index in order of prioerty + */ + Assertion.getResultPriority = function (val) { + if (typeof val === 'object') { + if (val.outcome) { + val = val.outcome.result; + } else { + val = val.result; + } + } + return resultPriorityMap.indexOf(val); + }; + + return Assertion; + +}()); +quail.lib.wcag2.TestAggregator = (function () { + + var pointerMap = { + elms: [], + pointers: [], + add: function (testCase) { + var pointer; + if (pointerMap.elms.indexOf(testCase.get('element')) === -1) { + if (testCase.get('html')) { + pointer = [{ + type: 'CharSnippetCompoundPointer', + chars: testCase.get('html'), + CSSSelector: testCase.get('selector') + }]; + } + pointerMap.elms.push(testCase.get('element')); + pointerMap.pointers.push(pointer); + } + }, + getPointer: function (elm) { + var index = pointerMap.elms.indexOf(elm); + return pointerMap.pointers[index]; + } + }; + + /** + * Run the callback for each testcase within the array of tests + * @param {array} tests + * @param {Function} callback Given the parameters (test, testcase) + */ + function eachTestCase(tests, callback) { + $.each(tests, function (i, test) { + test.each(function () { + callback.call(this, test, this); + }); + }); + } + + /** + * Get an array of elements common to all tests provided + * @param {Object} tests + * @return {Array} Array of HTML elements + */ + function getCommonElements(tests) { + var common = []; + var map = []; + + $.each(tests, function (i, test) { + var elms = []; + test.each(function () { + elms.push(this.get('element')); + pointerMap.add(this); + }); + map.push(elms); + }); + $.each(map, function (i, arr) { + if (i === 0) { + common = arr; + return; + } + var newArr = []; + $.each(arr, function (i, val) { + if (common.indexOf(val) !== -1) { + newArr.push(val); + } + }); + common = newArr; + }); + + return common; + } + + /** + * Get an array of elements in the given tests + * @param {Object} tests + * @return {Array} Array of HTML elements + */ + function getAllElements(tests) { + var elms = []; + eachTestCase(tests, function (test, testCase) { + var elm = testCase.get('element'); + if (elms.indexOf(elm) === -1) { + elms.push(elm); + pointerMap.add(testCase); + } + }); + return elms; + } + + /** + * Look at each unique element and create an assert for it + * @param {array[DOM element]} elms + * @param {object} base Base object for the assert + * @return {array[assert]} Array with asserts + */ + function createAssertionsForEachElement(elms, base) { + var assertions = []; + // Create asserts for each element + $.each(elms, function (i, elm) { + var assertion = new quail.lib.wcag2.EarlAssertion(base); + if (elm) { // Don't do undefined pointers + assertion.outcome.pointer = pointerMap.getPointer(elm); + } + assertions.push(assertion); + }); + return assertions; + } + + /** + * Combine the test results of an Aggregator into asserts + * + * A combinbing aggregator is an aggregator which only fails if all it's tests fail + * + * @param {Object} aggregator + * @param {Array[Object]} tests + * @return {Array[Object]} Array of Asserts + */ + function getCombinedAssertions(aggregator, tests) { + // element should already be unique, but some tests have bugs that cause them + // not to be. This prevents those problems from escalating + var elms = jQuery.unique(getCommonElements(tests)); + + var assertions = createAssertionsForEachElement(jQuery.unique(elms), { + testCase: aggregator.id, + outcome: {result: 'failed'} + }); + + // Iterate over all results to build the assert + eachTestCase(tests, function (test, testCase) { + // Look up the assert, if any + var newResult = testCase.get('status'); + var getResultPriority = quail.lib.wcag2.EarlAssertion.getResultPriority; + var assertion = assertions[elms.indexOf( + testCase.get('element') + )]; + + // Allow the aggregator to override the results + if (aggregator[newResult]) { + newResult = aggregator[newResult]; + } + + // Override if the resultId is higher or equal (combine) + if (assertion && getResultPriority(assertion) >= getResultPriority(newResult)) { + var pointer = assertion.outcome.pointer; + assertion.outcome = { + result: newResult, + info: test.get('title') + }; + if (pointer) { + assertion.outcome.pointer = pointer; + } + } + }); + + return assertions; + } + + + /** + * Stack the test results of a aggregator into asserts + * + * A stacked aggregator is one that fails if any of the tests fail + * + * @param {Object} aggregator + * @param {Array[Object]} tests + * @return {Array[Object]} Array of Asserts + */ + function getStackedAssertions(aggregator, tests) { + var elms = getAllElements(tests); + var asserts = createAssertionsForEachElement(elms, { + testCase: aggregator.id, + outcome: { result: 'untested'} + }); + + // Iterate over all results to build the assert + eachTestCase(tests, function (test, testCase) { + // Look up the assert, if any + var newResult = testCase.get('status'); + var getResultPriority = quail.lib.wcag2.EarlAssertion.getResultPriority; + var assert = asserts[elms.indexOf( + testCase.get('element') + )]; + + // Allow the aggregator to override the results + if (aggregator[newResult]) { + newResult = aggregator[newResult]; + } + + // Override if the resultId is lower (stacked) + if (assert && getResultPriority(assert) < getResultPriority(newResult)) { + assert.outcome = { + result: newResult, + info: test.get('title') + }; + } + }); + return asserts; + } + + + function TestAggregator(config, testDefinitions, subject) { + $.extend(this, { + id: config.tests.join('+'), + subject: subject + }, config); + + this.tests = $.map(this.tests, function (test) { + return testDefinitions[test]; + }); + } + + + /** + * Filter the data array so it only contains results + * from this aggregator + * @param {Array} data + * @return {Array} + */ + TestAggregator.prototype.filterDataToTests = function (data) { + var names = $.map(this.tests, function (test) { + return test.name; + }); + var testData = []; + + $.each(data, function (i, result) { + if (names.indexOf(result.get('name')) !== -1) { + testData.push(result); + } + }); + return testData; + }; + + /** + * Get the results of this test aggregator based on the tests provided for it + * @param {object} tests As provided by Quail + * @return {object} EARL assertions + */ + TestAggregator.prototype.getResults = function (tests) { + var assertions, assertion; + var filteredTests = this.filterDataToTests(tests); + + if (filteredTests.length === 1 || this.type === 'combined') { + assertions = getCombinedAssertions(this, filteredTests); + + } else if (this.type === "stacking") { + assertions = getStackedAssertions(this, filteredTests); + + } else if (window) { + window.console.error( + "Unknown type for aggregator " + this.id + ); + } + + // Return a default assert if none was defined + if (assertions) { + if (assertions.length === 0) { + assertion = new quail.lib.wcag2.EarlAssertion({ + testCase: this.id, + subject: this.subject, + outcome: { + result: 'inapplicable' + } + }), + assertions.push(assertion); + } + return assertions; + } + }; + + return TestAggregator; +}()); +})(jQuery); \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.min.js b/plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.min.js new file mode 100644 index 0000000..d055ae8 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/quail.jquery.min.js @@ -0,0 +1,6 @@ +// %LEAVE_UNMINIFIED% %REMOVE_LINE% +/*! QUAIL quailjs.org | quailjs.org/license */!function(a){"use strict";Function.prototype.bind=Function.prototype.bind||function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice,c=b.call(arguments,1),d=this,e=function(){},f=function(){return d.apply(this instanceof e?this:a||window,c.concat(b.call(arguments)))};return e.prototype=this.prototype,f.prototype=new e,f};var b={options:{},components:{},lib:{},testabilityTranslation:{0:"suggestion",.5:"moderate",1:"severe"},html:null,strings:{},accessibilityResults:{},accessibilityTests:null,guidelines:{wcag:{setup:function(a,b,c){c=c||{};for(var d in this.successCriteria)if(this.successCriteria.hasOwnProperty(d)){var e=this.successCriteria[d];e.registerTests(a),b&&b.listenTo&&"function"==typeof b.listenTo&&c.successCriteriaEvaluated&&b.listenTo(e,"successCriteriaEvaluated",c.successCriteriaEvaluated)}},successCriteria:{}}},tests:{},textSelector:":not(:empty)",suspectPHeaderTags:["strong","b","em","i","u","font"],suspectPCSSStyles:["color","font-weight","font-size","font-family"],focusElements:"a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]",emoticonRegex:/((?::|;|B|P|=)(?:-)?(?:\)|\(|o|O|D|P))/g,selfClosingTags:["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"],optionalClosingTags:["p","li","th","tr","td"],run:function(c){function d(a,b,c){if(c.guideline&&c.guideline.length){a.tests=a.lib.TestCollection([],{scope:a.html||null});for(var d=0,e=c.guideline.length;e>d;++d){var f=c.guideline[d];b[f]&&(b[f].scope=a.html||null,a.tests.set(f,b[f]))}}else a.tests=a.lib.TestCollection(b,{scope:a.html||null})}function e(){if("undefined"!=typeof c.customTests)for(var a in c.customTests)c.customTests.hasOwnProperty(a)&&(c.customTests[a].scope=b.html||null,b.tests.set(a,c.customTests[a]));var d=function(){};for(var e in b.guidelines)b.guidelines[e]&&"function"==typeof b.guidelines[e].setup&&b.guidelines[e].setup(b.tests,this,{successCriteriaEvaluated:c.successCriteriaEvaluated||d});b.tests.run({preFilter:c.preFilter||function(){},caseResolve:c.caseResolve||function(){},testComplete:c.testComplete||function(){},testCollectionComplete:c.testCollectionComplete||function(){},complete:c.complete||function(){}})}if(c.reset&&(b.accessibilityResults={}),b.tests=b.lib.TestCollection([],{scope:b.html||null}),"undefined"!=typeof quailBuilderTests)b.tests=b.lib.TestCollection(quailBuilderTests,{scope:b.html||null}),e.call(b);else if("wcag2"===c.guideline)b.lib.wcag2.run(c);else if(c.accessibilityTests)d(b,c.accessibilityTests,c),e.call(b);else{var f=c.jsonPath;"string"==typeof c.guideline&&(f+="/guidelines/"+c.guideline),a.ajax({url:f+"/tests.json",dataType:"json",success:function(a){"object"==typeof a&&(d(b,a,c),e.call(b))},error:function(){throw new Error("Tests could not be loaded")}})}},listenTo:function(a,b,c){c=c.bind(this),a.registerListener.call(a,b,c)},getConfiguration:function(a){var b=this.tests.find(a),c=b&&b.get("guidelines"),d=c&&this.options.guidelineName&&c[this.options.guidelineName],e=d&&d.configuration;return e?e:!1},isUnreadable:function(a){return"string"!=typeof a?!0:a.trim().length?!1:!0},isDataTable:function(b){if(b.find("tr").length<3)return!1;if(b.find("th[scope]").length)return!0;var c=b.find("tr:has(td)").length,d=b.find("td[rowspan], td[colspan]"),e=!0;if(d.length){var f={};d.each(function(){"undefined"==typeof f[a(this).index()]&&(f[a(this).index()]=0),f[a(this).index()]++}),a.each(f,function(a,b){c>b&&(e=!1)})}var g=b.find("table");if(g.length){var h={};g.each(function(){var b=a(this).parent("td").index();b!==!1&&"undefined"==typeof h[b]&&(h[b]=0),h[b]++}),a.each(h,function(a,b){c>b&&(e=!1)})}return e},getTextContents:function(a){if(a.is("p, pre, blockquote, ol, ul, li, dl, dt, dd, figure, figcaption"))return a.text();for(var b="",c=a[0].childNodes,d=0,e=c.length;e>d;d+=1)3===c[d].nodeType&&(b+=c[d].nodeValue);return b},validURL:function(a){return-1===a.search(" ")},cleanString:function(a){return a.toLowerCase().replace(/^\s\s*/,"")},containsReadableText:function(c,d){if(c=c.clone(),c.find("option").remove(),!b.isUnreadable(c.text()))return!0;if(!b.isUnreadable(c.attr("alt")))return!0;if(d){var e=!1;if(c.find("*").each(function(){b.containsReadableText(a(this),!0)&&(e=!0)}),e)return!0}return!1}};if(window&&(window.quail=b),a.fn.quail=function(a){return this.length?(b.options=a,b.html=this,b.run(a),this):this},a.expr[":"].quailCss=function(b,c,d){var e=d[3].split(/\s*=\s*/);return a(b).css(e[0]).search(e[1])>-1},b.components.acronym=function(b,c,d){c.get("$scope").each(function(){var b=a(this),e={},f={};b.find("acronym[title], abbr[title]").each(function(){f[a(this).text().toUpperCase().trim()]=a(this).attr("title")}),b.find("p, div, h1, h2, h3, h4, h5").each(function(){var b=this,g=a(b),h=g.text().split(" "),i=[];h.length>1&&g.text().toUpperCase()!==g.text()?(a.each(h,function(a,b){b.length<2||(b=b.replace(/[^a-zA-Zs]/,""),b.toUpperCase()===b&&"undefined"==typeof f[b.toUpperCase().trim()]&&("undefined"==typeof e[b.toUpperCase()]&&i.push(b),e[b.toUpperCase()]=b))}),c.add(i.length?d({element:b,expected:g.closest(".quail-test").data("expected"),info:{acronyms:i},status:"failed"}):d({element:b,expected:g.closest(".quail-test").data("expected"),status:"passed"}))):c.add(d({element:b,expected:g.closest(".quail-test").data("expected"),status:"passed"}))})})},b.components.color=function(){function c(a,c,d,e,f,g){a.add(c({element:d,expected:function(a,c){return b.components.resolveExpectation(a,c)}(d,f),message:g,status:e}))}function d(b){return""!==a.trim(b)}function e(c){var d=c.parentNode,e=a(d);return 1!==d.nodeType?!1:-1!==["script","style","title","object","applet","embed","template","noscript"].indexOf(d.nodeName.toLowerCase())?!1:b.isUnreadable(e.text())?!1:!0}function f(a){function b(a){return Object.keys(a).length}var c={},d=a.groupCasesBySelector(),e="";for(var f in d)if(d.hasOwnProperty(f)){var g=d[f];g.each(function(a,b){b.get("status")===c&&(c[f]=e)})}return b(c)===b(d)}var g={cache:{},getLuminosity:function(a,b){var c="getLuminosity_"+a+"_"+b;if(a=g.parseColor(a),b=g.parseColor(b),void 0!==g.cache[c])return g.cache[c];var d,e,f=a.r/255,h=a.g/255,i=a.b/255,j=.03928>=f?f/12.92:Math.pow((f+.055)/1.055,2.4),k=.03928>=h?h/12.92:Math.pow((h+.055)/1.055,2.4),l=.03928>=i?i/12.92:Math.pow((i+.055)/1.055,2.4),m=b.r/255,n=b.g/255,o=b.b/255,p=.03928>=m?m/12.92:Math.pow((m+.055)/1.055,2.4),q=.03928>=n?n/12.92:Math.pow((n+.055)/1.055,2.4),r=.03928>=o?o/12.92:Math.pow((o+.055)/1.055,2.4);return d=.2126*j+.7152*k+.0722*l,e=.2126*p+.7152*q+.0722*r,g.cache[c]=Math.round((Math.max(d,e)+.05)/(Math.min(d,e)+.05)*10)/10,g.cache[c]},fetchImageColorAtPixel:function(a,b,c){b="undefined"!=typeof b?b:1,c="undefined"!=typeof c?c:1;var d=document.createElement("canvas"),e=d.getContext("2d");e.drawImage(a,0,0);var f=e.getImageData(b,c,1,1).data;return"rgb("+f[0]+","+f[1]+","+f[2]+")"},testElmContrast:function(a,b,c){var d=g.getColor(b,"background");return g.testElmBackground(a,b,d,c)},testElmBackground:function(a,b,c,d){var e,f=g.getColor(b,"foreground");return"wcag"===a?e=g.passesWCAGColor(b,f,c,d):"wai"===a&&(e=g.passesWAIColor(f,c)),e},passesWCAGColor:function(a,c,d,e){var f=b.components.convertToPx(a.css("fontSize"));if("undefined"==typeof e)if(f>=18)e=3;else{var h=a.css("fontWeight");e=f>=14&&("bold"===h||parseInt(h,10)>=700)?3:4.5}return g.getLuminosity(c,d)>e},passesWAIColor:function(a,b){var c=g.getWAIErtContrast(a,b),d=g.getWAIErtBrightness(a,b);return c>500&&d>125},getWAIErtContrast:function(a,b){var c=g.getWAIDiffs(a,b);return c.red+c.green+c.blue},getWAIErtBrightness:function(a,b){var c=g.getWAIDiffs(a,b);return(299*c.red+587*c.green+114*c.blue)/1e3},getWAIDiffs:function(a,b){return{red:Math.abs(a.r-b.r),green:Math.abs(a.g-b.g),blue:Math.abs(a.b-b.b)}},getColor:function(b,c){var d=g;b.attr("data-cacheId")||b.attr("data-cacheId","id_"+Math.random());var e="getColor_"+c+"_"+b.attr("data-cacheId");if(void 0!==g.cache[e])return g.cache[e];if("foreground"===c)return g.cache[e]=b.css("color")?b.css("color"):"rgb(0,0,0)",g.cache[e];var f=b.css("background-color");return g.hasBackgroundColor(f)?(g.cache[e]=f,g.cache[e]):(b.parents().each(function(){var b=a(this).css("background-color");return g.hasBackgroundColor(b)?d.cache[e]=b:void 0}),g.cache[e]="rgb(255,255,255)",g.cache[e])},getForeground:function(a){return g.getColor(a,"foreground")},parseColor:function(a){return"object"==typeof a?a:"#"===a.substr(0,1)?{r:parseInt(a.substr(1,2),16),g:parseInt(a.substr(3,2),16),b:parseInt(a.substr(5,2),16),a:!1}:"rgb"===a.substr(0,3)?(a=a.replace("rgb(","").replace("rgba(","").replace(")","").split(","),{r:a[0],g:a[1],b:a[2],a:"undefined"==typeof a[3]?!1:a[3]}):void 0},getBackgroundImage:function(b){b.attr("data-cacheId")||b.attr("data-cacheId","id_"+Math.random());var c="getBackgroundImage_"+b.attr("data-cacheId");if(void 0!==g.cache[c])return g.cache[c];for(b=b[0];b&&1===b.nodeType&&"BODY"!==b.nodeName&&"HTML"!==b.nodeName;){var d=a(b).css("background-image");if(d&&"none"!==d&&-1!==d.search(/^(.*?)url(.*?)$/i))return g.cache[c]=d.replace("url(","").replace(/['"]/g,"").replace(")",""),g.cache[c];b=b.parentNode}return g.cache[c]=!1,!1},getBackgroundGradient:function(b){b.attr("data-cacheId")||b.attr("data-cacheId","id_"+Math.random());var c="getBackgroundGradient_"+b.attr("data-cacheId");if(void 0!==g.cache[c])return g.cache[c];var d=function(b){return""!==a.trim(b)};for(b=b[0];b&&1===b.nodeType&&"BODY"!==b.nodeName&&"HTML"!==b.nodeName;){if(g.hasBackgroundColor(a(b).css("background-color")))return g.cache[c]=!1,!1;var e=a(b).css("backgroundImage");if(e&&"none"!==e&&-1!==e.search(/^(.*?)gradient(.*?)$/i)){var f=e.match(/gradient(\(.*\))/g);if(f.length>0)return f=f[0].replace(/(linear|radial|from|\bto\b|gradient|top|left|bottom|right|\d*%)/g,""),g.cache[c]=a.grep(f.match(/(rgb\([^\)]+\)|#[a-z\d]*|[a-z]*)/g),d),g.cache[c]}b=b.parentNode}return g.cache[c]=!1,!1},getAverageRGB:function(a){var b=a.src;if(void 0!==g.cache[b])return g.cache[b];var c,d,e,f,h=5,i={r:0,g:0,b:0},j=document.createElement("canvas"),k=j.getContext&&j.getContext("2d"),l=-4,m={r:0,g:0,b:0,a:0},n=0;if(!k)return g.cache[b]=i,i;e=j.height=a.height,d=j.width=a.width,k.drawImage(a,0,0);try{c=k.getImageData(0,0,d,e)}catch(o){return g.cache[b]=i,i}for(f=c.data.length;(l+=4*h)0&&(n=n[0].replace(/(linear|radial|from|\bto\b|gradient|top|left|bottom|right|\d*%)/g,""),f=a.grep(n.match(/(rgb\([^\)]+\)|#[a-z\d]*|[a-z]*)/g),d))}break;case"background-image":if(g.hasBackgroundColor(m)){f=!1;continue}l=k.css("backgroundImage"),l&&"none"!==l&&-1!==l.search(/^(.*?)url(.*?)$/i)&&(f=l.replace("url(","").replace(/['"]/g,"").replace(")",""))}h.push({element:k,visibility:k.css("visibility")}),k.css("visibility","hidden"),k=document.elementFromPoint(i,j)}for(var o=0;oc.data("content-score"))&&(c=b)}),c)}},b.components.convertToPx=function(c){if(c.search("px")>-1)return parseInt(c,10);var d=a('
 
').appendTo(b.html),e=d.height();return d.remove(),e},b.components.event=function(b,c,d,e){var f=c.get("$scope"),g=e.selector&&f.find(e.selector)||f.find("*"),h=e.searchEvent||"",i=e.correspondingEvent||"";g.each(function(){var e,f=h.replace("on",""),g=b.components.hasEventListener(a(this),f);a._data&&(e=a._data(this,"events"));var j=e&&e[f]&&!!e[f].length,k=!!i.length,l=b.components.hasEventListener(a(this),i.replace("on","")),m=a(this).closest(".quail-test").data("expected"),n=c.add(d({element:this,expected:m}));n.set(!g&&!j||k&&l?{status:"passed"}:{status:"failed"})})},b.components.hasEventListener=function(b,c){return"undefined"!=typeof a(b).attr("on"+c)?!0:a._data(a(b)[0],"events")&&"undefined"!=typeof a._data(a(b)[0],"events")[c]?!0:a(b).is("a[href], input, button, video, textarea")&&"undefined"!=typeof a(b)[0][c]&&("click"===c||"focus"===c)&&a(b)[0][c].toString().search(/^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i)>-1?!1:"undefined"!=typeof a(b)[0][c]},b.components.headingLevel=function(b,c,d,e){var f=!1;c.get("$scope").find(":header").each(function(){var g=parseInt(a(this).get(0).tagName.substr(-1,1),10),h=this;c.add(f===e.headingLevel&&g>f+1?d({element:h,expected:function(a){return b.components.resolveExpectation(a)}(h),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(h),status:"passed"})),f=g})},b.components.htmlSource={getHtml:function(c){var d=this;if("undefined"!=typeof b.options.htmlSource&&b.options.htmlSource)return void c(b.options.htmlSource,d.parseHtml(b.options.htmlSource));var e=a.ajax({url:window.location.href,async:!1});e&&"undefined"!=typeof e.responseText&&c(e.responseText,d.parseHtml(e.responseText))},traverse:function(b,c,d,e){var f=this;"undefined"==typeof e&&c(b,d,!1),"undefined"!=typeof b.children&&(b.childCount=1,a.each(b.children,function(a,d){c(d,b.childCount,b),f.traverse(d,c,b.childCount,!0),"tag"===d.type&&b.childCount++})),a.isArray(b)&&a.each(b,function(a,b){f.traverse(b,c)})},addSelector:function(a,b,c){if("tag"===a.type&&"undefined"!=typeof a.name&&"undefined"==typeof a.selector){a.selector=c&&"undefined"!=typeof c.selector?c.selector.slice():[];var d=a.name;return"undefined"!=typeof a.attributes&&("undefined"!=typeof a.attributes.id?d+="#"+a.attributes.id[0]:"undefined"!=typeof a.attributes["class"]&&(d+="."+a.attributes["class"][0].replace(/\s/,"."))),!b||"undefined"!=typeof a.attributes&&"undefined"!=typeof a.attributes.id||(d+=":nth-child("+b+")"),a.selector.push(d),a.selector}},parseHtml:function(a){if("undefined"==typeof Tautologistics)return!1;a=a.replace(/]*)>/g,"");var b=new Tautologistics.NodeHtmlParser.HtmlBuilder(function(){},{}),c=new Tautologistics.NodeHtmlParser.Parser(b);c.parseComplete(a);var d=b.dom,e=this;return this.traverse(d,e.addSelector),d}},"undefined"!=typeof Tautologistics){var c={Text:"text",Tag:"tag",Attr:"attr",CData:"cdata",Comment:"comment"};Tautologistics.NodeHtmlParser.HtmlBuilder.prototype.write=function(a){if(this._done&&this.handleCallback(new Error("Writing to the builder after done() called is not allowed without a reset()")),this._options.includeLocation&&a.type!==c.Attr&&(a.location=this._getLocation(),this._updateLocation(a)),a.type!==c.Text||!this._options.ignoreWhitespace||!HtmlBuilder.reWhitespace.test(a.data)){var b,d;if(this._tagStack.last())if(b=this._tagStack.last(),a.type===c.Tag)if("/"===a.name.charAt(0)){var e=this._options.caseSensitiveTags?a.name.substring(1):a.name.substring(1).toLowerCase();if(b.name===e&&(b.closingTag=!0),!this.isEmptyTag(a)){for(var f=this._tagStack.length-1;f>-1&&this._tagStack[f--].name!==e;);if(f>-1||this._tagStack[0].name===e)for(;f"),p=["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"],q=["pre","code","textarea","script","style"],r=["p","li","tr","th","td"],s=function(a,b){return{name:c,line:a+1,"char":b}},t=function(a){var b=new Error("Ending tag not found for: "+a.name+" at line: "+a.line+" char: "+a["char"]+" starting tags: "+d[0].name);throw b.lineData=a,b},u=function(a){var b=new Error("Comment ending not found for: `comment` at line: "+a.line+" char: "+a["char"]);throw b.lineData=a,b},v=function(a){var b=new Error("Ending `/` not found for: `"+a.name+"` at line: "+a.line+" char: "+a["char"]);throw b.lineData=a.name,b},w=function(c){b=a,a=c},x=function(a){e-=a},y=function(a,b,e){if(n.test(a))c+=a;else if(a===j)w(C);else if(a===l)c="",w(I);else if(p.indexOf(c)>-1)g.strict_self_closing_tags?w(z):(c="",w(B));else{var f=s(b,e);d.push(f),q.indexOf(c)>-1?(c="",x(1),w(F)):(c="",x(1),w(A))}},z=function(a,b,d){a===k?(c="",w(E)):a===i&&v(s(b,d))},A=function(a){a===i&&w(E)},B=function(a){a===h&&w(y)},C=function(a){function b(){var a=d.pop();a.name===c?w(B):r.indexOf(a.name)>-1?b():t(a)}n.test(a)?c+=a:(b(),c="")},D=function(a){a===j?w(C):(x(1),w(y))},E=function(a){a===h&&w(D)},F=function(a){a===h&&w(G)},G=function(a){a===j&&w(H)},H=function(a){if(n.test(a))c+=a;else{var b=d.pop();b.name===c?w(B):t(b),c=""}},I=function(a){m.test(a)?(c="",w(B)):(x(3),w(J))},J=function(a,b,c){f||(f={content:"",line:b+1,"char":c+1,name:"comment"}),f.content+=a,o.test(f.content)&&(f=null,w(B))},K=function(b,h){var i=null;try{var j,k=b.split("\n");w(B),c="",d=[],f=null,g=h||{};for(var l=0,m=k.length;m>l;l++)for(e=0,j=k[l].length;j>e&&a;e++)a(k[l][e],l,e);if(f)u(f);else if(d.length>0){var n=d[d.length-1];-1===r.indexOf(n.name)&&t(n)}i=null}catch(o){i=o.message}finally{return i}};return K};b.components.htmlTagValidator=d(),b.components.label=function(b,c,d,e){var f=c.get("$scope");f.each(function(){var f=a(this);f.find(e.selector).each(function(){c.add(a(this).parent("label").length&&f.find("label[for="+a(this).attr("id")+"]").length&&b.containsReadableText(a(this).parent("label"))||b.containsReadableText(f.find("label[for="+a(this).attr("id")+"]"))?d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"passed"}):d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"failed"}))})})},b.components.labelProximity=function(b,c,d,e){var f=c.get("$scope");f.find(e.selector).each(function(){var b=f.find("label[for="+a(this).attr("id")+"]").first();c.add(d(b.length?a(this).parent().is(b.parent())?{element:this,expected:a(this).closest(".quail-test").data("expected"),status:"passed"}:{element:this,expected:a(this).closest(".quail-test").data("expected"),status:"failed"}:{element:this,expected:a(this).closest(".quail-test").data("expected"),status:"failed"}))})},b.components.language={maximumDistance:300,textDirection:{rtl:/[\u0600-\u06FF]|[\u0750-\u077F]|[\u0590-\u05FF]|[\uFE70-\uFEFF]/gm,ltr:/[\u0041-\u007A]|[\u00C0-\u02AF]|[\u0388-\u058F]/gm},textDirectionChanges:{rtl:/[\u200E]|‏/gm,ltr:/[\u200F]|‎/gm},scripts:{basicLatin:{regularExpression:/[\u0041-\u007F]/g,languages:["ceb","en","eu","ha","haw","id","la","nr","nso","so","ss","st","sw","tlh","tn","ts","xh","zu","af","az","ca","cs","cy","da","de","es","et","fi","fr","hr","hu","is","it","lt","lv","nl","no","pl","pt","ro","sk","sl","sq","sv","tl","tr","ve","vi"]},arabic:{regularExpression:/[\u0600-\u06FF]/g,languages:["ar","fa","ps","ur"]},cryllic:{regularExpression:/[\u0400-\u04FF]|[\u0500-\u052F]/g,languages:["bg","kk","ky","mk","mn","ru","sr","uk","uz"]}},scriptSingletons:{bn:/[\u0980-\u09FF]/g,bo:/[\u0F00-\u0FFF]/g,el:/[\u0370-\u03FF]/g,gu:/[\u0A80-\u0AFF]/g,he:/[\u0590-\u05FF]/g,hy:/[\u0530-\u058F]/g,ja:/[\u3040-\u309F]|[\u30A0-\u30FF]/g,ka:/[\u10A0-\u10FF]/g,km:/[\u1780-\u17FF]|[\u19E0-\u19FF]/g,kn:/[\u0C80-\u0CFF]/g,ko:/[\u1100-\u11FF]|[\u3130-\u318F]|[\uAC00-\uD7AF]/g,lo:/[\u0E80-\u0EFF]/g,ml:/[\u0D00-\u0D7F]/g,mn:/[\u1800-\u18AF]/g,or:/[\u0B00-\u0B7F]/g,pa:/[\u0A00-\u0A7F]/g,si:/[\u0D80-\u0DFF]/g,ta:/[\u0B80-\u0BFF]/g,te:/[\u0C00-\u0C7F]/g,th:/[\u0E00-\u0E7F]/g,zh:/[\u3100-\u312F]|[\u2F00-\u2FDF]/g},getDocumentLanguage:function(a,c){var d=navigator.language||navigator.userLanguage;return"undefined"!=typeof b.options.language&&(d=b.options.language),a.parents("[lang]").length&&(d=a.parents("[lang]:first").attr("lang")),"undefined"!=typeof a.attr("lang")&&(d=a.attr("lang")),d=d.toLowerCase().trim(),c?d.split("-")[0]:d}},b.components.placeholder=function(b,c,d,e){var f=function(b,e){c.add(d({element:b,expected:a(b).closest(".quail-test").data("expected"),status:e}))};c.get("$scope").find(e.selector).each(function(){var c="";if("none"===a(this).css("display")&&!a(this).is("title"))return void f(this,"inapplicable");if("undefined"!=typeof e.attribute){if(("undefined"==typeof a(this).attr(e.attribute)||"tabindex"===e.attribute&&a(this).attr(e.attribute)<=0)&&!e.content)return void f(this,"failed");a(this).attr(e.attribute)&&"undefined"!==a(this).attr(e.attribute)&&(c+=a(this).attr(e.attribute))}if(("undefined"==typeof e.attribute||!e.attribute||e.content)&&(c+=a(this).text(),a(this).find("img[alt]").each(function(){c+=a(this).attr("alt")})),"string"==typeof c&&c.length>0){c=b.cleanString(c);var d=/^([0-9]*)(k|kb|mb|k bytes|k byte)$/g,g=d.exec(c.toLowerCase());g&&g[0].length?f(this,"failed"):e.empty&&b.isUnreadable(c)?f(this,"failed"):b.strings.placeholders.indexOf(c)>-1?f(this,"failed"):f(this,"passed")}else e.empty&&"number"!=typeof c&&f(this,"failed")})},b.components.resolveExpectation=function(b,c){var d,e=a(b).closest(".quail-test"),f=e.data("expected");c||(d=e.data("expected"));var g="string"==typeof f&&f.split("|");if(c&&0===g.length&&f.indexOf(":")>-1&&(g=[f]),g.length>0&&1===b.nodeType)for(var h,i,j=0,k=g.length;k>j;++j)if(h=g[j].split(":"),c){if(h[0]===c){if(!h[1]||"ignore"===h[1])return;d=h[1]}}else if(i=a(h[0],e),1===i.length&&b===i.get(0)){if(!h[1]||"ignore"===h[1])return;d=h[1]}return d},b.components.selector=function(b,c,d,e){this.get("$scope").each(function(){var d=a(this),f=a(this).find(e.selector);f.length?f.each(function(){var d,f=a(this);d=e.test&&!f.is(e.test)?"passed":"failed",c.add(b.lib.Case({element:this,expected:f.closest(".quail-test").data("expected"),status:d}))}):c.add(b.lib.Case({element:void 0,expected:d.data("expected")||d.find("[data-expected]").data("expected"),status:e.test?"inapplicable":"passed"}))})},b.statistics={setDecimal:function(a,b){var c=Math.pow(10,b||0);return b?Math.round(c*a)/c:a},average:function(a,c){for(var d=a.length,e=0;d--;)e+=a[d];return b.statistics.setDecimal(e/a.length,c)},variance:function(a,c){for(var d=b.statistics.average(a,c),e=a.length,f=0;e--;)f+=Math.pow(a[e]-d,2);return f/=a.length,b.statistics.setDecimal(f,c)},standardDeviation:function(a,c){var d=Math.sqrt(b.statistics.variance(a,c));return b.statistics.setDecimal(d,c)}},b.components.textStatistics={cleanText:function(a){return a.replace(/[,:;()\-]/," ").replace(/[\.!?]/,".").replace(/[ ]*(\n|\r\n|\r)[ ]*/," ").replace(/([\.])[\. ]+/,"$1").replace(/[ ]*([\.])/,"$1").replace(/[ ]+/," ").toLowerCase()},sentenceCount:function(a){return a.split(".").length+1},wordCount:function(a){return a.split(" ").length+1},averageWordsPerSentence:function(a){return this.wordCount(a)/this.sentenceCount(a)},averageSyllablesPerWord:function(b){var c=this,d=0,e=c.wordCount(b);return e?(a.each(b.split(" "),function(a,b){d+=c.syllableCount(b)}),d/e):0},syllableCount:function(a){var b=a.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/,"").match(/[aeiouy]{1,2}/g);return b&&0!==b.length?b.length:1}},b.components.video={isVideo:function(b){var c=!1;return a.each(this.providers,function(){b.is(this.selector)&&this.isVideo(b)&&(c=!0)}),c},findVideos:function(b,c){a.each(this.providers,function(d,e){b.find(this.selector).each(function(){var b=a(this);e.isVideo(b)&&e.hasCaptions(b,c)})})},providers:{youTube:{selector:"a, iframe",apiUrl:"http://gdata.youtube.com/feeds/api/videos/?q=%video&caption&v=2&alt=json",isVideo:function(a){return this.getVideoId(a)!==!1?!0:!1},getVideoId:function(a){var b=a.is("iframe")?"src":"href",c=/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&\?]*).*/,d=a.attr(b).match(c);return d&&11===d[7].length?d[7]:!1},hasCaptions:function(b,c){var d=this.getVideoId(b);a.ajax({url:this.apiUrl.replace("%video",d),async:!1,dataType:"json",success:function(a){c(b,a.feed.openSearch$totalResults.$t>0)}})}},flash:{selector:"object",isVideo:function(b){var c=!1;return 0===b.find("param").length?!1:(b.find("param[name=flashvars]").each(function(){a(this).attr("value").search(/\.(flv|mp4)/i)>-1&&(c=!0)}),c)},hasCaptions:function(b,c){var d=!1;b.find("param[name=flashvars]").each(function(){(a(this).attr("value").search("captions")>-1&&a(this).attr("value").search(".srt")>-1||a(this).attr("value").search("captions.pluginmode")>-1)&&(d=!0)}),c(b,d)}},videoElement:{selector:"video",isVideo:function(a){return a.is("video")},hasCaptions:function(c,d){var e=c.find("track[kind=subtitles], track[kind=captions]");if(!e.length)return void d(c,!1);var f=b.components.language.getDocumentLanguage(c,!0);c.parents("[lang]").length&&(f=c.parents("[lang]").first().attr("lang").split("-")[0]);var g=!1;return e.each(function(){if(!a(this).attr("srclang")||a(this).attr("srclang").toLowerCase()===f){g=!0;try{var b=a.ajax({url:a(this).attr("src"),type:"HEAD",async:!1,error:function(){}});404===b.status&&(g=!1)}catch(c){}}}),g?void d(c,!0):void d(c,!1)}}}},b.strings.colors={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"},b.strings.languageCodes=["bh","bi","nb","bs","br","bg","my","es","ca","km","ch","ce","ny","ny","zh","za","cu","cu","cv","kw","co","cr","hr","cs","da","dv","dv","nl","dz","en","eo","et","ee","fo","fj","fi","nl","fr","ff","gd","gl","lg","ka","de","ki","el","kl","gn","gu","ht","ht","ha","he","hz","hi","ho","hu","is","io","ig","id","ia","ie","iu","ik","ga","it","ja","jv","kl","kn","kr","ks","kk","ki","rw","ky","kv","kg","ko","kj","ku","kj","ky","lo","la","lv","lb","li","li","li","ln","lt","lu","lb","mk","mg","ms","ml","dv","mt","gv","mi","mr","mh","ro","ro","mn","na","nv","nv","nd","nr","ng","ne","nd","se","no","nb","nn","ii","ny","nn","ie","oc","oj","cu","cu","cu","or","om","os","os","pi","pa","ps","fa","pl","pt","pa","ps","qu","ro","rm","rn","ru","sm","sg","sa","sc","gd","sr","sn","ii","sd","si","si","sk","sl","so","st","nr","es","su","sw","ss","sv","tl","ty","tg","ta","tt","te","th","bo","ti","to","ts","tn","tr","tk","tw","ug","uk","ur","ug","uz","ca","ve","vi","vo","wa","cy","fy","wo","xh","yi","yo","za","zu"],b.strings.newWindow=[/new (browser )?(window|frame)/,/popup (window|frame)/],b.strings.placeholders=["title","untitled","untitled document","this is the title","the title","content"," ","new page","new","nbsp"," ","spacer","image","img","photo","frame","frame title","iframe","iframe title","legend"],b.strings.redundant={inputImage:["submit","button"],link:["link to","link","go to","click here","link","click","more"],required:["*"]},b.strings.siteMap=["site map","map","sitemap"],b.strings.skipContent=[/(jump|skip) (.*) (content|main|post)/i],b.strings.suspiciousLinks=["click here","click","more","here","read more","download","add","delete","clone","order","view","read","clic aquí","clic","haga clic","más","aquí","image"],b.strings.symbols=["|","*",/\*/g,"
*","•","•","♦","›","»","‣","▶",".","◦","✓","◽","•","—","◾"],b.KINGStrongList=function(b,c,d){c.get("$scope").find("strong").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")}); +c.add(b),b.set({status:a(this).parent().is("li")?"passed":"failed"})})},b.KINGUseCurrencyAsSymbol=function(b,c,d){function e(e,f){var g=["dollar","euro","pound","franc","krona","rupee","ruble","dinar"],h=new RegExp("\\d{1,}\\s*("+g.join("|")+")\\w*\\b|("+g.join("|")+")\\w*\\b\\s*\\d{1,}","ig"),i=b.getTextContents(a(f)),j=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(j),j.set({status:h.test(i)?"failed":"passed"})}c.get("$scope").find("p").each(e)},b.KINGUseLongDateFormat=function(b,c,d){function e(b,e){var f,g=/\d{1,2}([./-])\d{1,2}\1\d{2,4}/g,h=e.childNodes,i=!1,j=[],k=0;for(f=h.length;f>k;k++)h[k].nodeType===Node.TEXT_NODE&&j.push(h[k]);for(k=0;k0,h=d.find("a");h.each(e?f:g)}function f(b,e){var f=a(e),g=e.getAttribute("href"),h=f.find("+ a");if(h.length){var i=h[0].getAttribute("href"),j="passed",k=d({element:e,expected:f.closest(".quail-test").data("expected")});g===i&&(j="failed"),c.add(k),k.set({status:j})}}function g(b,e){var f=d({element:e});c.add(f),f.set({status:"inapplicable",expected:a(e).closest(".quail-test").data("expected")})}c.get("$scope").each(e)},b.aImgAltNotRepetitive=function(b,c,d){c.get("$scope").find("a img[alt]").each(function(){var e=c.add(d({element:this})),f=a(this).closest(".quail-test").data("expected");e.set(b.cleanString(a(this).attr("alt"))===b.cleanString(a(this).parent("a").text())?{expected:f,status:"failed"}:{expected:f,status:"passed"})})},b.aInPHasADistinctStyle=function(b,c,d){function e(a){return a.outerWidth()-a.innerWidth()>0||a.outerHeight()-a.innerHeight()>0}function f(b,c){var d=!1,f=["font-weight","font-style"],g=b.css("text-decoration");return"none"!==g&&g!==c.css("text-decoration")&&(d=!0),"rgba(0, 0, 0, 0)"!==b.css("background-color")&&f.push("background"),a.each(f,function(a,e){d||b.css(e)===c.css(e)||(d=!0)}),d||e(b)}function g(a){var b="block"===a.css("display"),c=a.css("position"),d="relative"!==c&&"static"!==c;return b||d}var h=/^([\s|-]|>|<|\\|\/|&(gt|lt);)*$/i;c.get("$scope").each(function(){var b=a(this),e=b.find("p a[href]:visible");e.each(function(){var b=a(this),e=b.closest("p"),i=b.parent(),j=d({element:this,expected:b.closest(".quail-test").data("expected")});c.add(j);var k=b.text().trim(),l=e.clone().find("a[href]").remove().end().text();""===k||l.match(h)?j.set("status","inapplicable"):b.css("color")===e.css("color")?j.set("status","passed"):f(b,e)?j.set("status","passed"):g(b)?j.set("status","passed"):b.find("img").length>0?j.set("status","passed"):i.text().trim()===k&&f(i,e)?j.set("status","passed"):j.set("status","failed")})})},b.aLinkTextDoesNotBeginWithRedundantWord=function(b,c,d){c.get("$scope").find("a").each(function(){var e=a(this),f="";a(this).find("img[alt]").length&&(f+=a(this).find("img[alt]:first").attr("alt")),f+=a(this).text(),f=f.toLowerCase();var g;a.each(b.strings.redundant.link,function(a,b){f.search(b)>-1&&(g=c.add(d({element:this,expected:e.closest(".quail-test").data("expected"),status:"failed"})))}),g||c.add(d({element:this,expected:e.closest(".quail-test").data("expected"),status:"passed"}))})},b.aLinkWithNonText=function(b,c,d){c.get("$scope").find("a").each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});if(c.add(e),!a(this).is("a:has(img, object, embed)[href]"))return void e.set({status:"inapplicable"});if(!b.isUnreadable(a(this).text()))return void e.set({status:"passed"});var f=0;a(this).find("img, object, embed").each(function(){(a(this).is("img")&&b.isUnreadable(a(this).attr("alt"))||!a(this).is("img")&&b.isUnreadable(a(this).attr("title")))&&f++}),e.set(a(this).find("img, object, embed").length===f?{status:"failed"}:{status:"passed"})})},b.aLinksAreSeparatedByPrintableCharacters=function(b,c,d){c.get("$scope").find("a").each(function(){var e=c.add(d({element:this})),f=a(this).closest(".quail-test").data("expected");a(this).next("a").length&&e.set(b.isUnreadable(a(this).get(0).nextSibling.wholeText)?{expected:f,status:"failed"}:{expected:f,status:"passed"})})},b.aLinksDontOpenNewWindow=function(b,c,d){c.get("$scope").find("a").not("[target=_new], [target=_blank]").each(function(){c.add(d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"passed"}))}),c.get("$scope").find("a[target=_new], a[target=_blank]").each(function(){var e=a(this),f=!1,g=0,h=e.text()+" "+e.attr("title"),i="";do i=b.strings.newWindow[g],h.search(i)>-1&&(f=!0),++g;while(!f&&g-1?{status:"failed"}:{status:"passed"})})},b.animatedGifMayBePresent=function(b,c,d){function e(a,b,c){if("gif"!==b)return void c(!1);var d=new XMLHttpRequest;d.open("GET",a,!0),d.responseType="arraybuffer",d.addEventListener("load",function(){var a=new Uint8Array(d.response),b=0;if(71!==a[0]||73!==a[1]||70!==a[2]||56!==a[3])return void c(!1);for(var e=0;e1)return void c(!0);c(!1)}),d.send()}c.get("$scope").find("img").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var f=a(this).attr("src"),g=a(this).attr("src").split(".").pop().toLowerCase();return"gif"!==g?void b.set({status:"inapplicable"}):void e(f,g,function(a){return a?void b.set({status:"cantTell"}):void b.set({status:"inapplicable"})})})},b.appletContainsTextEquivalent=function(b,c,d){c.get("$scope").find('applet[alt=""], applet:not(applet[alt])').each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e),e.set(b.isUnreadable(a(this).text())?{status:"failed"}:{status:"passed"})})},b.ariaOrphanedContent=function(b,c,d){var e=c.get("$scope");e.each(function(){var b=a(this),e=!!b.attr("role"),f=!!b.find("[role]").length;if(!e&&!f)return void c.add(d({expected:b.data("expected"),status:"inapplicable"}));var g=b.find("*:not(*[role] *, *[role], script, meta, link)");g.length?g.each(function(){c.add(d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"failed"}))}):c.add(d({expected:b.data("expected"),status:"passed"}))})},b.audioMayBePresent=function(b,c,d){var e=["mp3","m4p","ogg","oga","opus","wav","wma","wv"];c.get("$scope").each(function(){var b=a(this),f=!1;b.find("object, audio").each(function(){f=!0,c.add(d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"cantTell"}))}),b.find("a[href]").each(function(){var b=a(this),g=b.attr("href").split(".").pop();-1!==a.inArray(g,e)&&(f=!0,c.add(d({element:this,expected:b.closest(".quail-test").data("expected"),status:"cantTell"})))}),f||c.add(d({element:this,status:"inapplicable",expected:a(this).closest(".quail-test").data("expected")}))})},b.blockquoteUseForQuotations=function(b,c,d){c.get("$scope").find("p").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});return c.add(b),a(this).parents("blockquote").length>0?void b.set({status:"inapplicable"}):void b.set(a(this).text().substr(0,1).search(/'|"|«|“|「/)>-1&&a(this).text().substr(-1,1).search(/'|"|»|„|」/)>-1?{status:"failed"}:{status:"passed"})})},b.closingTagsAreUsed=function(b,c,d){b.components.htmlSource.getHtml(function(e,f){b.components.htmlSource.traverse(f,function(e){if("tag"===e.type&&a.isArray(e.selector)){var f;f=/#/.test(e.selector.slice(-1)[0])?e.selector.slice(-1)[0]:e.selector.join(" > ");var g=a(f,c.get("$scope")).get(0);g||(g=e.raw||f),c.add("undefined"!=typeof e.closingTag||e.closingTag||-1!==b.selfClosingTags.indexOf(e.name.toLowerCase())?d({element:g,expected:"object"==typeof g&&1===g.nodeType&&a(g).closest(".quail-test").data("expected")||null,status:"passed"}):d({element:g,expected:"object"==typeof g&&1===g.nodeType&&a(g).closest(".quail-test").data("expected")||null,status:"failed"}))}})})},b.colorBackgroundGradientContrast=function(b,c,d,e){function f(a,b,c,d,e){var f,j,k,l=g.getBackgroundGradient(d);if(l){for(var m=0;mm;m++){var n=g.testElmBackground(c.algorithm,d,"#"+j.colourAt(m));n||(h(a,b,e,"failed",i,"The background gradient makes the text unreadable"),f=!0)}f||h(a,b,e,"passed",i,"The background gradient does not affect readability")}}var g=b.components.color.colors,h=b.components.color.buildCase,i="colorBackgroundGradientContrast";c.get("$scope").each(function(){for(var g=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),i=[],j=g.iterateNext();j;)b.components.color.textShouldBeTested(j)&&i.push(j.parentNode),j=g.iterateNext();0===i.length&&h(c,d,null,"inapplicable","","There is no text to evaluate"),i.forEach(function(b){f(c,d,e,a(b),b)})})},b.colorBackgroundImageContrast=function(b,c,d,e){function f(a,b,c,d,e){var f=g.getBackgroundImage(d);if(f){var j=document.createElement("img");j.crossOrigin="Anonymous",j.onload=function(){var f=g.getAverageRGB(j),k=g.testElmBackground(c.algorithm,d,f);k?h(a,b,e,"passed",i,"The element's background image does not affect readability"):h(a,b,e,"failed",i,"The element's background image makes the text unreadable")},j.onerror=j.onabort=function(){h(a,b,e,"cantTell",i,"The element's background image could not be loaded ("+f+")")},j.src=f}}var g=b.components.color.colors,h=b.components.color.buildCase,i="colorBackgroundImageContrast";c.get("$scope").each(function(){for(var g=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),i=[],j=g.iterateNext();j;)b.components.color.textShouldBeTested(j)&&i.push(j.parentNode),j=g.iterateNext();0===i.length&&h(c,d,null,"inapplicable","","There is no text to evaluate"),i.forEach(function(b){f(c,d,e,a(b),b)})})},b.colorElementBehindBackgroundGradientContrast=function(b,c,d,e){function f(a,b,c,d,e){var f,j;if(d.is("option")||(f=g.getBehindElementBackgroundGradient(d)),f){for(var k=0;kk;k++)j=!g.testElmBackground(c.algorithm,d,"#"+l.colourAt(k));j?h(a,b,e,"failed",i,"The background gradient of the element behind this element makes the text unreadable"):h(a,b,e,"passed",i,"The background gradient of the element behind this element does not affect readability")}}var g=b.components.color.colors,h=b.components.color.buildCase,i="colorElementBehindBackgroundGradientContrast";c.get("$scope").each(function(){for(var g=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),i=[],j=g.iterateNext();j;)b.components.color.textShouldBeTested(j)&&i.push(j.parentNode),j=g.iterateNext();0===i.length&&h(c,d,null,"inapplicable","","There is no text to evaluate"),i.forEach(function(b){f(c,d,e,a(b),b)})})},b.colorElementBehindBackgroundImageContrast=function(b,c,d,e){function f(a,b,c,d,e){var f;if(d.is("option")||(f=g.getBehindElementBackgroundImage(d)),f){var j=document.createElement("img");j.crossOrigin="Anonymous",j.onload=function(){var f=g.getAverageRGB(j),k=g.testElmBackground(c.algorithm,d,f);k?h(a,b,e,"passed",i,"The background image of the element behind this element does not affect readability"):h(a,b,e,"failed",i,"The background image of the element behind this element makes the text unreadable")},j.onerror=j.onabort=function(){h(a,b,e,"cantTell",i,"The background image of the element behind this element could not be loaded ("+f+")")},j.src=f}}var g=b.components.color.colors,h=b.components.color.buildCase,i="colorElementBehindBackgroundImageContrast";c.get("$scope").each(function(){for(var g=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),i=[],j=g.iterateNext();j;)b.components.color.textShouldBeTested(j)&&i.push(j.parentNode),j=g.iterateNext();0===i.length&&h(c,d,null,"inapplicable","","There is no text to evaluate"),i.forEach(function(b){f(c,d,e,a(b),b)})})},b.colorElementBehindContrast=function(b,c,d,e){function f(a,b,c,d,e){var f;if(d.is("option")||(f=g.getBehindElementBackgroundColor(d)),f){var j=g.testElmBackground(c.algorithm,d,f);j?h(a,b,e,"passed",i,"The element behind this element does not affect readability"):h(a,b,e,"failed",i,"The element behind this element makes the text unreadable")}}var g=b.components.color.colors,h=b.components.color.buildCase,i="colorElementBehindContrast";c.get("$scope").each(function(){for(var g=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),i=[],j=g.iterateNext();j;)b.components.color.textShouldBeTested(j)&&i.push(j.parentNode),j=g.iterateNext();0===i.length&&h(c,d,null,"inapplicable","","There is no text to evaluate"),i.forEach(function(b){f(c,d,e,a(b),b)})})},b.colorFontContrast=function(b,c,d,e){function f(a,b,c,d,e){g.testElmContrast(c.algorithm,d)?h(a,b,e,"passed",i,"The font contrast of the text is sufficient for readability"):h(a,b,e,"failed",i,"The font contrast of the text impairs readability")}var g=b.components.color.colors,h=b.components.color.buildCase,i="colorFontContrast";c.get("$scope").each(function(){for(var g=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),i=[],j=g.iterateNext();j;)b.components.color.textShouldBeTested(j)&&i.push(j.parentNode),j=g.iterateNext();0===i.length&&h(c,d,null,"inapplicable","","There is no text to evaluate"),i.forEach(function(b){f(c,d,e,a(b),b)})})},b.contentPositioningShouldNotChangeMeaning=function(b,c,d){var e=["top","left","right","bottom"],f={},g=!1;c.get("$scope").find("*:has(*:quailCss(position=absolute))").each(function(){f={top:{},left:{},right:{},bottom:{}},g=!1;var b=a(this);b.find("h1, h2, h3, h4, h5, h6, p, blockquote, ol, li, ul, dd, dt").filter(":quailCss(position=absolute)").each(function(){for(var b=0;b2&&!g&&(g=!0,c.add(d({element:b.get(0),expected:b.closest(".quail-test").data("expected"),status:"failed"})))})})})},b.definitionListsAreUsed=function(b,c,d){c.get("$scope").find("dl").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set({status:"inapplicable"})}),c.get("$scope").find("p, li").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var e=a(this);a(this).find("span, strong, em, b, i").each(function(){if(a(this).text().length<50&&0===e.text().search(a(this).text())){if(a(this).is("span")&&a(this).css("font-weight")===e.css("font-weight")&&a(this).css("font-style")===e.css("font-style"))return void b.set({status:"passed"});b.set({status:"failed"})}})})},b.doNotUseGraphicalSymbolToConveyInformation=function(b,c,d){c.get("$scope").find(b.textSelector+":not(abbr, acronym)").each(function(){var e="✓",f="?xo[]()+-!*xX",g=a(this).text(),h=g.replace(/[\W\s]+/g,"");0===h.length?-1===e.indexOf(g)&&c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"})):c.add(1===g.length&&f.indexOf(g)>=0?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"}))}),c.get("$scope").find(b.textSelector).filter("abbr, acronym").each(function(){c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"inapplicable"}))})},b.doctypeProvided=function(b,c,d){var e=c.get("$scope").get(0);c.add(d(0!==a(e.doctype).length||document.doctype?{element:e,expected:"pass",status:"passed"}:{element:e,expected:"fail",status:"failed"}))},b.documentAbbrIsUsed=function(a,b,c){a.components.acronym(a,b,c,"abbr")},b.documentAcronymsHaveElement=function(a,b,c){a.components.acronym(a,b,c,"acronym")},b.documentIDsMustBeUnique=function(b,c,d){c.get("$scope").each(function(){0===a(this).children().length&&c.add(d({element:this,status:"inapplicable",expected:a(this).closest(".quail-test").data("expected")}))}),c.get("$scope").find(":not([id])").each(function(){c.add(d({element:this,status:"inapplicable",expected:a(this).closest(".quail-test").data("expected")}))}),c.get("$scope").each(function(){var e={};a(this).find("[id]").each(function(){var f=d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this)});c.add(f),"undefined"==typeof e[a(this).attr("id")]&&0===Object.keys(e).length?(f.set({status:"inapplicable"}),e[a(this).attr("id")]=a(this).attr("id")):"undefined"==typeof e[a(this).attr("id")]?(f.set({status:"passed"}),e[a(this).attr("id")]=a(this).attr("id")):f.set({status:"failed"})})})},b.documentIsWrittenClearly=function(b,c,d){c.get("$scope").find(b.textSelector).each(function(){var e=b.components.textStatistics.cleanText(a(this).text()),f=d({element:this,expected:a(this).closest(".quail-test").data("expected")});return c.add(f),b.isUnreadable(e)?void f.set({status:"inapplicable"}):void f.set(Math.round(206.835-1.015*b.components.textStatistics.averageWordsPerSentence(e)-84.6*b.components.textStatistics.averageSyllablesPerWord(e))<60?{status:"failed"}:{status:"passed"})})},b.documentLangIsISO639Standard=function(b,c,d){var e=c.get("$scope").is("html")?c.get("$scope"):c.get("$scope").find("html").first(),f=d({element:e[0],expected:e.closest(".quail-test").length?e.closest(".quail-test").data("expected"):e.data("expected")}),g=e.attr("lang"),h=!1;c.add(f),e.is("html")&&"undefined"!=typeof g?(a.each(b.strings.languageCodes,function(a,b){h||0!==g.indexOf(b)||(h=!0)}),f.set(h?null===g.match(/^[a-z]{2}(-[A-Z]{2})?$/)?{status:"failed"}:{status:"passed"}:{status:"failed"})):f.set({status:"inapplicable"})},b.documentStrictDocType=function(a,b,c){b.add("undefined"!=typeof document.doctype&&document.doctype&&-1!==document.doctype.systemId.search("strict")?c({element:document,expected:b.get("$scope").data("expected"),status:"passed"}):c({element:document,expected:b.get("$scope").data("expected"),status:"failed"}))},b.documentTitleIsShort=function(a,b,c){var d=b.get("$scope").find("head title:first"),e=c({element:d,expected:d.closest(".quail-test").data("expected")});return b.add(e),d.length?void e.set({status:d.text().length>150?"failed":"passed"}):void e.set({element:b.get("$scope"),status:"inapplicable"})},b.documentValidatesToDocType=function(){"undefined"==typeof document.doctype},b.documentVisualListsAreMarkedUp=function(b,c,d){var e=["♦","›","»","‣","▶","◦","✓","◽","•","—","◾","-\\D","\\\\","\\*(?!\\*)","\\.\\s","x\\s","•","•",">","[0-9]+\\.","\\(?[0-9]+\\)","[\\u25A0-\\u25FF]","[IVX]{1,5}\\.\\s"],f=RegExp("(^|]*>)[\\s]*("+e.join("|")+")","gi");c.get("$scope").find(b.textSelector).each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var e=a(this).html().match(f);b.set({status:e&&e.length>2?"failed":"passed"})})},b.elementAttributesAreValid=function(b,c,d){b.components.htmlSource.getHtml(function(e,f){f&&b.components.htmlSource.traverse(f,function(b){if("undefined"!=typeof b.raw&&a.isArray(b.selector)){var e,f=!1;e=/#/.test(b.selector.slice(-1)[0])?b.selector.slice(-1)[0]:b.selector.join(" > ");var g=a(e,c.get("$scope")).get(0);g||(g=b.raw||e);var h=b.raw.match(/\'|\"/g);h&&h.length%2!==0&&(c.add(d({element:g,expected:"object"==typeof g&&1===g.nodeType&&a(g).closest(".quail-test").data("expected")||null,status:"failed"})),f=!0),b.raw.search(/([a-z]*)=(\'|\")([a-z\.]*)(\'|\")[a-z]/i)>-1&&(c.add(d({element:g,expected:"object"==typeof g&&1===g.nodeType&&a(g).closest(".quail-test").data("expected")||null,status:"failed"})),f=!0);var i=b.raw.split("=");i.shift(),a.each(i,function(){-1===this.search(/\'|\"/)&&this.search(/\s/i)>-1&&(c.add(d({element:g,expected:"object"==typeof g&&1===g.nodeType&&a(g).closest(".quail-test").data("expected")||null,status:"failed"})),f=!0)}),f||c.add(d({element:g,expected:"object"==typeof g&&1===g.nodeType&&a(g).closest(".quail-test").data("expected")||null,status:"passed"}))}})})},b.elementsDoNotHaveDuplicateAttributes=function(b,c,d){b.components.htmlSource.getHtml(function(e,f){f&&b.components.htmlSource.traverse(f,function(b){if("tag"===b.type&&a.isArray(b.selector)){var e;e=/#/.test(b.selector.slice(-1)[0])?b.selector.slice(-1)[0]:b.selector.join(" > ");var f=a(e,c.get("$scope")).get(0);if(f||(f=b.raw||e),"undefined"!=typeof b.attributes){var g=[];a.each(b.attributes,function(a,b){b.length>1&&g.push(b)}),c.add(g.length?d({element:f,expected:"object"==typeof f&&1===f.nodeType&&a(f).closest(".quail-test").data("expected")||null,info:g,status:"failed"}):d({element:f,expected:"object"==typeof f&&1===f.nodeType&&a(f).closest(".quail-test").data("expected")||null,info:g,status:"passed"}))}}})})},b.embedHasAssociatedNoEmbed=function(b,c,d){c.get("$scope").find("embed").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set({status:a(this).find("noembed").length||a(this).next().is("noembed")?"passed":"failed"})})},b.emoticonsExcessiveUse=function(b,c,d){c.get("$scope").find(b.textSelector).each(function(){var e=0,f=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(f),a.each(a(this).text().split(" "),function(a,c){c.search(b.emoticonRegex)>-1&&e++}),f.set(0===e?{status:"inapplicable"}:{status:e>4?"failed":"passed"})})},b.emoticonsMissingAbbr=function(b,c,d){c.get("$scope").find(b.textSelector+":not(abbr, acronym)").each(function(){var e=a(this),f=e.clone(),g=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(g),f.find("abbr, acronym").each(function(){a(this).remove()});var h="passed";a.each(f.text().split(" "),function(a,c){c.search(b.emoticonRegex)>-1&&(h="failed")}),g.set({status:h})})},b.focusIndicatorVisible=function(b,c,d){c.get("$scope").find(b.focusElements).each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e);var f={borderWidth:a(this).css("border-width"),borderColor:a(this).css("border-color"),backgroundColor:a(this).css("background-color"),boxShadow:a(this).css("box-shadow")};if(a(this).focus(),f.backgroundColor.trim()!==a(this).css("background-color").trim())return a(this).blur(),void e.set({status:"passed"});var g=b.components.convertToPx(a(this).css("border-width"));if(g>2&&g!==b.components.convertToPx(f.borderWidth))return a(this).blur(),void e.set({status:"passed"});var h=a(this).css("box-shadow")&&"none"!==a(this).css("box-shadow")?a(this).css("box-shadow").match(/(-?\d+px)|(rgb\(.+\))/g):!1;return h&&a(this).css("box-shadow")!==f.boxShadow&&b.components.convertToPx(h[3])>3?(a(this).blur(),void e.set({status:"passed"})):(a(this).blur(),void e.set({status:"failed"}))})},b.formWithRequiredLabel=function(b,c,d){var e,f=b.strings.redundant,g=!1;f.required[f.required.indexOf("*")]=/\*/g,c.get("$scope").each(function(){var h=a(this);h.find("label").each(function(){var h=a(this).text().toLowerCase(),i=a(this),j=c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this)}));for(var k in f.required)h.search(k)>=0&&!c.get("$scope").find("#"+i.attr("for")).attr("aria-required")&&j.set({status:"failed"});g=i.css("color")+i.css("font-weight")+i.css("background-color"),e&&g!==e&&j.set({status:"failed"}),e=g,"undefined"==typeof j.get("status")&&j.set({status:"passed"})})})},b.headerTextIsTooLong=function(b,c,d){var e=128;c.get("$scope").find("h1, h2, h3, h4, h5, h6").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:a(this).text().replace(/^\s+|\s+$/gm,"").length>e?"failed":"passed"});c.add(b)})},b.headersAttrRefersToATableCell=function(b,c,d){c.get("$scope").find("table").each(function(){var b=this,e=d({element:b,expected:a(this).closest(".quail-test").data("expected")});c.add(e);var f=a(b).find("th[headers], td[headers]");return 0===f.length?void e.set({status:"inapplicable"}):void f.each(function(){var c=a(this).attr("headers").split(/\s+/);a.each(c,function(c,d){return""===d||a(b).find("th#"+d+",td#"+d).length>0?void e.set({status:"passed"}):void e.set({status:"failed"})})})})},b.headersUseToMarkSections=function(b,c,d){c.get("$scope").find("p").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var e=a(this);e.find("strong:first, em:first, i:first, b:first").each(function(){b.set({status:e.text().trim()===a(this).text().trim()?"failed":"passed"})})}),c.get("$scope").find("ul, ol").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var e=a(this);if(e.prevAll(":header").length||e.find("li").length!==e.find("li:has(a)").length)return void b.set({status:"passed"});var f=!0;e.find("li:has(a)").each(function(){a(this).text().trim()!==a(this).find("a:first").text().trim()&&(f=!1)}),f&&b.set({status:"failed"})})},b.headersUsedToIndicateMainContent=function(b,c,d){c.get("$scope").each(function(){var e=a(this),f=b.components.content.findContent(e);c.add(d("undefined"==typeof f||0!==f.find(":header").length&&f.find(b.textSelector).first().is(":header")?{element:f.get(0),expected:f.closest(".quail-test").data("expected"),status:"passed"}:{element:f.get(0),expected:f.closest(".quail-test").data("expected"),status:"failed"}))})},b.idRefHasCorrespondingId=function(b,c,d){c.get("$scope").find("label[for], *[aria-activedescendant]").each(function(){var b=a(this),e=d({element:this,expected:b.closest(".quail-test").data("expected")});c.add(e);var f=b.attr("for")||b.attr("aria-activedescendant");e.set(0===c.get("$scope").find("#"+f).length?{status:"failed"}:{status:"passed"})})},b.idrefsHasCorrespondingId=function(b,c,d){function e(b){var c=[],d=["headers","aria-controls","aria-describedby","aria-flowto","aria-labelledby","aria-owns"];return a.each(d,function(a,d){var e=b.attr(d);return"undefined"!=typeof e&&e!==!1?void(c=e):void 0}),c.split(/\s+/)}c.get("$scope").each(function(){var b=a(this).find("td[headers], th[headers], [aria-controls], [aria-describedby], [aria-flowto], [aria-labelledby], [aria-owns]");return 0===b.length?void c.add(d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"inapplicable"})):void b.each(function(){var b=this,f=c.add(d({element:this,expected:a(this).closest(".quail-test").data("expected")})),g=e(a(b)),h="passed";a.each(g,function(b,c){return""!==c&&0===a("#"+c).length?void(h="failed"):void 0}),f.set({status:h})})})},b.imgAltIsDifferent=function(b,c,d){c.get("$scope").find("img:not([src])").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"inapplicable"});c.add(b)}),c.get("$scope").find("img[alt][src]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(a(this).attr("src")===a(this).attr("alt")||a(this).attr("src").split("/").pop()===a(this).attr("alt")?{status:"failed"}:{status:"passed"})})},b.imgAltIsTooLong=function(b,c,d){c.get("$scope").find("img[alt]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set({status:a(this).attr("alt").length>100?"failed":"passed"})})},b.imgAltNotEmptyInAnchor=function(b,c,d){c.get("$scope").find("a[href]:has(img)").each(function(){var e=a(this),f=e.text(),g=d({element:this,expected:e.closest(".quail-test").data("expected")});c.add(g),e.find("img[alt]").each(function(){f+=" "+a(this).attr("alt")}),g.set(b.isUnreadable(f)?{status:"failed"}:{status:"passed"})})},b.imgAltTextNotRedundant=function(b,c,d){var e={};c.get("$scope").find("img[alt]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),"undefined"==typeof e[a(this).attr("alt")]?e[a(this).attr("alt")]=a(this).attr("src"):b.set(e[a(this).attr("alt")]!==a(this).attr("src")?{status:"failed"}:{status:"passed"})})},b.imgGifNoFlicker=function(b,c,d){c.get("$scope").find('img[src$=".gif"]').each(function(){var b=a(this),e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e),a.ajax({url:b.attr("src"),dataType:"text",success:function(a){e.set(-1!==a.search("NETSCAPE2.0")?{status:"failed"}:{status:"inapplicable"})}})})},b.imgHasLongDesc=function(b,c,d){c.get("$scope").find("img[longdesc]").each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e),e.set(a(this).attr("longdesc")!==a(this).attr("alt")&&b.validURL(a(this).attr("longdesc"))?{status:"passed"}:{status:"failed"})})},b.imgImportantNoSpacerAlt=function(b,c,d){c.get("$scope").find("img[alt]").each(function(){var e=a(this).width()?a(this).width():parseInt(a(this).attr("width"),10),f=a(this).height()?a(this).height():parseInt(a(this).attr("height"),10),g=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(g),g.set(b.isUnreadable(a(this).attr("alt").trim())&&a(this).attr("alt").length>0&&e>50&&f>50?{status:"failed"}:{status:"passed"})})},b.imgMapAreasHaveDuplicateLink=function(b,c,d){var e={};c.get("$scope").find("a").each(function(){e[a(this).attr("href")]=a(this).attr("href")}),c.get("$scope").find("img[usemap]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var f=a(this),g=c.get("$scope").find(f.attr("usemap"));g.length||(g=c.get("$scope").find('map[name="'+f.attr("usemap").replace("#","")+'"]')),g.length?g.find("area").each(function(){b.set("undefined"==typeof e[a(this).attr("href")]?{status:"failed"}:{status:"passed"}) +}):b.set({status:"inapplicable"})})},b.imgNonDecorativeHasAlt=function(b,c,d){c.get("$scope").find("img[alt]").each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e),e.set(b.isUnreadable(a(this).attr("alt"))&&(a(this).width()>100||a(this).height()>100)?{status:"failed"}:{status:"passed"})})},b.imgWithMathShouldHaveMathEquivalent=function(b,c,d){c.get("$scope").find("img:not(img:has(math), img:has(tagName))").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),a(this).parent().find("math").length||b.set({status:"failed"})})},b.inputCheckboxRequiresFieldset=function(b,c,d){c.get("$scope").find(":checkbox").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(a(this).parents("fieldset").length?{status:"passed"}:{status:"failed"})})},b.inputImageAltIsNotFileName=function(b,c,d){c.get("$scope").find("input[type=image][alt]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(a(this).attr("src")===a(this).attr("alt")?{status:"failed"}:{status:"passed"})})},b.inputImageAltIsShort=function(b,c,d){c.get("$scope").find("input[type=image]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(a(this).attr("alt").length>100?{status:"failed"}:{status:"passed"})})},b.inputImageAltNotRedundant=function(b,c,d){c.get("$scope").find("input[type=image][alt]").each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e),e.set(b.strings.redundant.inputImage.indexOf(b.cleanString(a(this).attr("alt")))>-1?{status:"failed"}:{status:"passed"})})},b.inputWithoutLabelHasTitle=function(b,c,d){c.get("$scope").each(function(){var e=a(this).find("input, select, textarea");if(0===e.length){var f=d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"inapplicable"});return void c.add(f)}e.each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});return c.add(e),"none"===a(this).css("display")?void e.set({status:"inapplicable"}):void e.set(c.get("$scope").find("label[for="+a(this).attr("id")+"]").length||a(this).attr("title")&&!b.isUnreadable(a(this).attr("title"))?{status:"passed"}:{status:"failed"})})})},b.labelMustBeUnique=function(b,c,d){var e={};c.get("$scope").find("label[for]").each(function(){"undefined"==typeof e[a(this).attr("for")]&&(e[a(this).attr("for")]=0),e[a(this).attr("for")]++}),c.get("$scope").find("label[for]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:1===e[a(this).attr("for")]?"passed":"failed"});c.add(b)})},b.labelsAreAssignedToAnInput=function(b,c,d){c.get("$scope").find("label").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(a(this).attr("for")?c.get("$scope").find("#"+a(this).attr("for")).length&&c.get("$scope").find("#"+a(this).attr("for")).is(":input")?{status:"passed"}:{status:"failed"}:{status:"failed"})})},b.languageChangesAreIdentified=function(b,c,d){var e,f,g,h,i,j,k=c.get("$scope"),l=b.components.language.getDocumentLanguage(k,!0),m=function(c,d,e,f){var g,h=c.find("[lang="+d+"]");return 0===h.length?!0:(e=e.length,h.each(function(){g=b.getTextContents(a(this)).match(f),g&&(e-=g.length)}),e>0)},n=function(a){return a.attr("lang")?a.attr("lang").trim().toLowerCase().split("-")[0]:a.parents("[lang]").length?a.parents("[lang]:first").attr("lang").trim().toLowerCase().split("-")[0]:b.components.language.getDocumentLanguage(k,!0)};k.find(b.textSelector).each(function(){i=this,h=a(this),l=n(h),e=b.getTextContents(h),j=!1,a.each(b.components.language.scriptSingletons,function(a,f){a!==l&&(g=e.match(f),g&&g.length&&m(h,a,g,f)&&(c.add(d({element:i,expected:function(a){return b.components.resolveExpectation(a)}(i),info:{language:a},status:"failed"})),j=!0))}),a.each(b.components.language.scripts,function(a,k){-1===k.languages.indexOf(l)&&(g=e.match(k.regularExpression),g&&g.length&&m(h,a,g,f)&&(c.add(d({element:i,expected:function(a){return b.components.resolveExpectation(a)}(i),info:{language:a},status:"failed"})),j=!0))}),"undefined"!=typeof guessLanguage&&!h.find("[lang]").length&&h.text().trim().length>400&&guessLanguage.info(h.text(),function(a){a[0]!==l&&(c.add(d({element:i,expected:function(a){return b.components.resolveExpectation(a)}(i),info:{language:a[0]},status:"failed"})),j=!0)}),j||c.add(d({element:i,expected:function(a){return b.components.resolveExpectation(a)}(i),status:"passed"}))})},b.languageDirAttributeIsUsed=function(b,c,d){function e(){var e=a(this),g=e.attr("dir");if(!g){var h=e.closest("[dir]").attr("dir");g=h||g}"string"==typeof g&&(g=g.toLowerCase()),"undefined"==typeof f[g]&&(g="ltr");var i="ltr"===g?"rtl":"ltr",j=b.getTextContents(e),k=j.match(f[i]);if(k){var l=k.length;e.find("[dir="+i+"]").each(function(){var a=e[0].textContent.match(f[i]);a&&(l-=a.length)});var m=c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this)}));m.set({status:l>0?"failed":"passed"})}}var f=b.components.language.textDirection;c.get("$scope").each(function(){a(this).find(b.textSelector).each(e)})},b.languageDirectionPunctuation=function(b,c,d){var e=c.get("$scope"),f={},g=/[\u2000-\u206F]|[!"#$%&'\(\)\]\[\*+,\-.\/:;<=>?@^_`{|}~]/gi,h=e.attr("dir")?e.attr("dir").toLowerCase():"ltr",i="ltr"===h?"rtl":"ltr",j=b.components.language.textDirection;e.each(function(){var e=a(this);e.find(b.textSelector).each(function(){var e=a(this);h=e.attr("dir")?e.attr("dir").toLowerCase():e.parent("[dir]").first().attr("dir")?e.parent("[dir]").first().attr("dir").toLowerCase():h,"undefined"==typeof j[h]&&(h="ltr"),i="ltr"===h?"rtl":"ltr";var k=b.getTextContents(e),l=k.match(j[i]),m=c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this)}));if(!l)return void m.set({status:"inapplicable"});for(var n=k.search(j[i]),o=k.lastIndexOf(l.pop());f=g.exec(k);)if(f.index===n-1||f.index===o+1)return void m.set({status:"failed"});m.set({status:"passed"})})})},b.languageUnicodeDirection=function(b,c,d){var e=c.get("$scope"),f=b.components.language.textDirection,g=b.components.language.textDirectionChanges;e.each(function(){var e=a(this);e.find(b.textSelector).each(function(){var e=c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this)})),h=a(this),i=h.text().trim(),j=-1!==i.substr(0,1).search(f.ltr)?"rtl":"ltr";e.set(-1===i.search(f[j])?{status:"inapplicable"}:-1!==i.search(g[j])?{status:"passed"}:{status:"failed"})})})},b.linkHasAUniqueContext=function(b,c,d){function e(b){for(var c=a(b),d=c,e=f(c.text());!d.is("body, html")&&-1===j.indexOf(d.css("display"));)d=d.parent();var g=d.text().match(/[^\.!\?]+[\.!\?]+/g);null===g&&(g=[d.text()]);for(var h=0;h0)return!1}return g(e(b),e(c))?!1:!0}function i(a){var b=a.text();return a.find("img[alt]").each(function(){b+=" "+this.alt.trim()}),f(b)}var j=["block","flex","list-item","table","table-caption","table-cell"];c.get("$scope").each(function(){var b=a(this),e=b.find("a[href]:visible"),f={};if(0===e.length){var g=d({element:this,status:"inapplicable",expected:b.closest(".quail-test").data("expected")});c.add(g)}e.each(function(){var b=i(a(this));"undefined"==typeof f[b]&&(f[b]=[]),f[b].push(this)}),a.each(f,function(b,e){for(;e.length>1;){for(var f=e.pop(),g=!1,i=e.length-1;i>=0;i-=1){var j=e[i];h(f,j)&&(g=!0,e.splice(i,1),c.add(d({element:j,status:"failed",expected:a(j).closest(".quail-test").data("expected")})))}c.add(d({element:f,status:g?"failed":"passed",expected:a(f).closest(".quail-test").data("expected")}))}1===e.length&&c.add(d({element:e[0],status:"passed",expected:a(e[0]).closest(".quail-test").data("expected")}))})})},b.listNotUsedForFormatting=function(b,c,d){c.get("$scope").find("ol, ul").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(a(this).find("li").length<2?{status:"failed"}:{status:"passed"})})},b.listOfLinksUseList=function(b,c,d){var e=/(♦|›|»|‣|▶|.|◦|>|✓|◽|•|—|◾|\||\*|•|•)/g;c.get("$scope").find("a").each(function(){var f=c.add(d({element:this})),g=a(this).closest(".quail-test").data("expected");if(a(this).next("a").length){var h=a(this).get(0).nextSibling.wholeText.replace(e,"");f.set(!a(this).parent("li").length&&b.isUnreadable(h)?{expected:g,status:"failed"}:{expected:g,status:"passed"})}})},b.newWindowIsOpened=function(b,c,d){var e,f=window.open;window.open=function(a){c.each(function(b,c){var d=c.get("element").href;d.indexOf(a)>-1&&c.set("status","failed")})},c.get("$scope").find("a").each(function(){e=d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this)}),c.add(e),a(this).trigger("click")}),window.open=f},b.pNotUsedAsHeader=function(b,c,d){c.get("$scope").find("p").each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e),a(this).text().search(".")>=1&&e.set({status:"inapplicable"});var f=!1;if(a(this).text().search(".")<1){var g=a(this),h=g.prev("p");a.each(b.suspectPHeaderTags,function(b,c){g.find(c).length&&g.find(c).each(function(){a(this).text().trim()===g.text().trim()&&(e.set({status:"failed"}),f=!0)})}),h.length&&a.each(b.suspectPCSSStyles,function(a,b){return g.css(b)!==h.css(b)?(e.set({status:"failed"}),f=!0,!1):void 0}),"bold"===g.css("font-weight")&&(e.set({status:"failed"}),f=!0)}f||e.set({status:"passed"})})},b.paragraphIsWrittenClearly=function(b,c,d){c.get("$scope").find("p").each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e);var f=b.components.textStatistics.cleanText(a(this).text());e.set(Math.round(206.835-1.015*b.components.textStatistics.averageWordsPerSentence(f)-84.6*b.components.textStatistics.averageSyllablesPerWord(f))<60?{status:"failed"}:{status:"passed"})})},b.preShouldNotBeUsedForTabularLayout=function(b,c,d){c.get("$scope").find("pre").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var e=a(this).text().split(/[\n\r]+/);b.set({status:e.length>1&&a(this).text().search(/\t/)>-1?"failed":"passed"})})},b.scriptFocusIndicatorVisible=function(){b.html.find(b.focusElements).each(function(){var c,d,e,f;e=[];for(var g=0,h=document.styleSheets.length;h>g;++g){c=document.styleSheets[g],d=c.cssRules||c.rules;for(var i=d.length-1;i>=0;--i)f=d[i],f.selectorText&&-1!==f.selectorText.indexOf(":focus")&&(e.push({css:f.cssText,index:i,sheet:g}),c.deleteRule(i))}var j={borderWidth:a(this).css("border-width"),borderColor:a(this).css("border-color"),backgroundColor:a(this).css("background-color"),boxShadow:a(this).css("box-shadow"),outlineWidth:a(this).css("outline-width"),outlineColor:a(this).css("outline-color")};a(this).focus();var k=b.components.convertToPx(a(this).css("outline-width"));if(k>2&&k!==b.components.convertToPx(j.outlineWidth))return void a(this).blur();if(j.backgroundColor!==a(this).css("background-color"))return void a(this).blur();var l=b.components.convertToPx(a(this).css("border-width"));if(l>2&&l!==b.components.convertToPx(j.borderWidth))return void a(this).blur();var m=a(this).css("box-shadow")&&"none"!==a(this).css("box-shadow")?a(this).css("box-shadow").match(/(-?\d+px)|(rgb\(.+\))/g):!1;if(m&&a(this).css("box-shadow")!==j.boxShadow&&b.components.convertToPx(m[3])>3)return void a(this).blur();a(this).blur();for(var n,o=e.length-1;o>=0;--g)n=e[o],document.styleSheets[n.sheet].insertRule(n.css,n.index);b.testFails("scriptFocusIndicatorVisible",a(this))})},b.selectJumpMenu=function(b,c,d){var e=c.get("$scope");0!==e.find("select").length&&e.find("select").each(function(){c.add(0===a(this).parent("form").find(":submit").length&&b.components.hasEventListener(a(this),"change")?d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"failed"}):d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"passed"}))})},b.siteMap=function(b,c,d){var e=!0,f=d({element:c.get("$scope").get(0),expected:c.get("$scope").data("expected")});c.add(f),c.get("$scope").find("a").each(function(){if("passed"!==f.get("status")){var c=a(this).text().toLowerCase();return a.each(b.strings.siteMap,function(a,b){return c.search(b)>-1?void(e=!1):void 0}),e===!1?void f.set({status:"failed"}):void(e&&f.set({status:"passed"}))}})},b.skipToContentLinkProvided=function(b,c,d){c.get("$scope").each(function(){var e=a(this),f=!1;e.find('a[href*="#"]').each(function(){if(!f)for(var g=a(this),h=g.attr("href").split("#").pop(),i=e.find("#"+h),j=b.strings.skipContent.slice();!f&&j.length;){var k=j.pop();if(g.text().search(k)>-1&&i.length){if(g.focus(),g.is(":visible")&&"hidden"!==g.css("visibility"))return f=!0,void c.add(d({element:g.get(0),expected:g.closest(".quail-test").data("expected"),status:"passed"}));g.blur()}}}),f||c.add(d({expected:e.data("expected")||e.find("[data-expected]").data("expected"),status:"failed"}))})},b.tabIndexFollowsLogicalOrder=function(b,c,d){c.get("$scope").each(function(){var e=a(this),f=0;e.find("[tabindex]").each(function(){var e=a(this),g=e.attr("tabindex");c.add(parseInt(g,10)>=0&&parseInt(g,10)!==f+1?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"})),f++})})},b.tableAxisHasCorrespondingId=function(b,c,d){c.get("$scope").find("[axis]").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(0===a(this).parents("table").first().find("th#"+a(this).attr("axis")).length?{status:"failed"}:{status:"passed"})})},b.tableHeaderLabelMustBeTerse=function(b,c,d){c.get("$scope").find("th, table tr:first td").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b),b.set(a(this).text().length>20&&(!a(this).attr("abbr")||a(this).attr("abbr").length>20)?{status:"failed"}:{status:"passed"})})},b.tableLayoutDataShouldNotHaveTh=function(b,c,d){c.get("$scope").find("table").each(function(){var e=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(e),e.set(0!==a(this).find("th").length?b.isDataTable(a(this))?{status:"passed"}:{status:"failed"}:{status:"inapplicable"})})},b.tableLayoutHasNoCaption=function(b,c,d){c.get("$scope").find("table").each(function(){c.add(a(this).find("caption").length?b.isDataTable(a(this))?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"inapplicable"}))})},b.tableLayoutHasNoSummary=function(b,c,d){c.get("$scope").each(function(){var e=a(this);e.find("table[summary]").each(function(){var e=c.add(d({element:this,expected:a(this).closest(".quail-test").data("expected")}));e.set(b.isDataTable(a(this))||b.isUnreadable(a(this).attr("summary"))?{status:"passed"}:{status:"failed"})})})},b.tableLayoutMakesSenseLinearized=function(b,c,d){c.get("$scope").find("table").each(function(){b.isDataTable(a(this))||c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))})},b.tableNotUsedForLayout=function(b,c,d){c.get("$scope").find("table").each(function(){c.add(b.isDataTable(a(this))?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))})},b.tableShouldUseHeaderIDs=function(b,c,d){c.get("$scope").find("table").each(function(){var e=a(this),f=!1;b.isDataTable(e)&&(e.find("th").each(function(){f||a(this).attr("id")||(f=!0,c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"})))}),f||e.find("td[header]").each(function(){f||a.each(a(this).attr("header").split(" "),function(a,g){e.find("#"+g).length||(f=!0,c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"})))})}))})},b.tableSummaryDoesNotDuplicateCaption=function(b,c,d){c.get("$scope").find("table[summary]:has(caption)").each(function(){c.add(b.cleanString(a(this).attr("summary"))===b.cleanString(a(this).find("caption:first").text())?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"}))})},b.tableSummaryIsNotTooLong=function(b,c,d){c.get("$scope").find("table[summary]").each(function(){a(this).attr("summary").trim().length>100&&c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))})},b.tableUseColGroup=function(b,c,d){c.get("$scope").find("table").each(function(){b.isDataTable(a(this))&&!a(this).find("colgroup").length&&c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))})},b.tableUsesAbbreviationForHeader=function(b,c,d){c.get("$scope").find("th:not(th[abbr])").each(function(){a(this).text().length>20&&c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))})},b.tableUsesScopeForRow=function(b,c,d){c.get("$scope").find("table").each(function(){a(this).find("td:first-child").each(function(){var e=a(this).next("td");("bold"===a(this).css("font-weight")&&"bold"!==e.css("font-weight")||a(this).find("strong").length&&!e.find("strong").length)&&c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))}),a(this).find("td:last-child").each(function(){var e=a(this).prev("td");("bold"===a(this).css("font-weight")&&"bold"!==e.css("font-weight")||a(this).find("strong").length&&!e.find("strong").length)&&c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))})})},b.tableWithMoreHeadersUseID=function(b,c,d){c.get("$scope").find("table:has(th)").each(function(){var e=a(this),f=0;e.find("tr").each(function(){a(this).find("th").length&&f++,f>1&&!a(this).find("th[id]").length&&c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}))})})},b.tabularDataIsInTable=function(b,c,d){c.get("$scope").find("pre").each(function(){c.add(a(this).html().search(" ")>=0?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"}))})},b.tagsAreNestedCorrectly=function(a,b,c){a.components.htmlSource.getHtml(function(d){var e=a.components.htmlTagValidator(d),f=c({expected:b.get("$scope").filter(".quail-test").eq(0).data("expected")});b.add(f),f.set(e?{status:"failed",html:e}:{status:"passed"})})},b.textIsNotSmall=function(b,c,d){c.get("$scope").find(b.textSelector).each(function(){var e=a(this).css("font-size");e.search("em")>0&&(e=b.components.convertToPx(e)),e=parseInt(e.replace("px",""),10),c.add(10>e?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"}))})},b.userInputMayBeRequired=function(b,c,d){c.get("$scope").each(function(){var b=d({element:this,expected:a(this).closest(".quail-test").data("expected")});c.add(b);var e=a(this).find("form"),f=0,g=a(this).find("input:not(form input, [type=button],[type=reset],[type=image],[type=submit],[type=hidden])");return e.each(function(){var b=a(this).find("input:not([type=button],[type=reset],[type=image],[type=submit],[type=hidden])");b.length>1&&(f=b.length)}),f>0?void b.set({status:"cantTell"}):g.length>1?void b.set({status:"cantTell"}):void b.set({status:"inapplicable"})})},b.videoMayBePresent=function(b,c,d){var e=["webm","flv","ogv","ogg","avi","mov","qt","wmv","asf","mp4","m4p","m4v","mpg","mp2","mpeg","mpg","mpe","mpv","m2v","3gp","3g2"],f=["//www.youtube.com/embed/","//player.vimeo.com/video/"];c.get("$scope").each(function(){var b=a(this),g=!1;b.find("object, video").each(function(){g=!0,c.add(d({element:this,expected:a(this).closest(".quail-test").data("expected"),status:"cantTell"}))}),b.find("a[href]").each(function(){var b=a(this),f=b.attr("href").split(".").pop();-1!==a.inArray(f,e)&&(g=!0,c.add(d({element:this,expected:b.closest(".quail-test").data("expected"),status:"cantTell"})))}),b.find("iframe").each(function(){(-1!==this.src.indexOf(f[0])||-1!==this.src.indexOf(f[1]))&&(g=!0,c.add(d({element:this,expected:b.closest(".quail-test").data("expected"),status:"cantTell"})))}),g||c.add(d({element:this,status:"inapplicable",expected:a(this).closest(".quail-test").data("expected")}))})},b.videosEmbeddedOrLinkedNeedCaptions=function(a,b,c){a.components.video.findVideos(b.get("$scope"),function(d,e){b.add(e?c({element:d[0],expected:function(b){return a.components.resolveExpectation(b)}(d),status:"passed"}):c({element:d[0],expected:function(b){return a.components.resolveExpectation(b)}(d),status:"failed"}))})},b.whiteSpaceInWord=function(b,c,d){var e,f;c.get("$scope").find(b.textSelector).each(function(){f=a(this).text()?a(this).text().match(/[^\s\\]/g):!1,e=a(this).text()?a(this).text().match(/[^\s\\]\s[^\s\\]/g):!1,c.add(f&&e&&e.length>3&&e.length>=f.length/2-2?d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"failed"}):d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this),status:"passed"}))})},b.whiteSpaceNotUsedForFormatting=function(b,c,d){c.get("$scope").find(b.textSelector).each(function(){var e=c.add(d({element:this,expected:function(a){return b.components.resolveExpectation(a)}(this)}));if(0===a(this).find("br").length)return void e.set({status:"passed"});var f=a(this).html().toLowerCase().split(/()+/),g=0;a.each(f,function(a,b){-1!==b.search(/(\s|\ ){2,}/g)&&g++}),e.set(g>1?{status:"failed"}:{status:"cantTell"})})},b.lib.Case=function(){function b(a){return new b.fn.init(a)}return b.fn=b.prototype={constructor:b,init:function(a){this.listeners={},this.timeout=null,this.attributes=a||{};var b=this;return this.attributes.status&&"untested"!==this.attributes.status?setTimeout(function(){b.resolve()},0):(this.attributes.status="untested",this.timeout=setTimeout(function(){b.giveup()},350)),this},attributes:null,get:function(a){return this.attributes[a]},set:function(a,b){var c=!1;if("object"==typeof a)for(var d in a)a.hasOwnProperty(d)&&("status"===d&&(c=!0),this.attributes[d]=a[d]);else"status"===a&&(c=!0),this.attributes[a]=b;return c&&this.resolve(),this},hasStatus:function(a){"object"!=typeof a&&(a=[a]);for(var b=this.get("status"),c=0,d=a.length;d>c;++c)if(a[c]===b)return!0;return!1},resolve:function(){clearTimeout(this.timeout);var a,b=this.attributes.element;b&&b.nodeType&&1===b.nodeType&&(this.attributes.selector=this.defineUniqueSelector(b),this.attributes.html||(this.attributes.html="","HTML"===b.nodeName||"BODY"===b.nodeName?this.attributes.html="<"+b.nodeName+">":"string"==typeof b.outerHTML&&(a=b.outerHTML.trim().replace(/(\r\n|\n|\r)/gm,"").replace(/>\s+<"),a.length>200&&(a=a.substr(0,200)+"... [truncated]"),this.attributes.html=a))),this.dispatch("resolve",this)},giveup:function(){clearTimeout(this.timeout),this.attributes.status="notTested",this.dispatch("timeout",this)},listenTo:function(a,b,c){c=c.bind(this),a.registerListener.call(a,b,c)},registerListener:function(a,b){this.listeners[a]||(this.listeners[a]=[]),this.listeners[a].push(b)},dispatch:function(a){if(this.listeners[a]&&this.listeners[a].length){var b=[].slice.call(arguments);this.listeners[a].forEach(function(a){a.apply(null,b)})}},defineUniqueSelector:function(b){function c(b){return 1===a(b).length}function d(a){var b="",c=a.id||"";return c.length>0&&(b="#"+c),b}function e(a){var b="",c=a.className||"";return c.length>0&&(c=c.split(/\s+/),c=h(c,function(a){return/active|enabled|disabled|first|last|only|collapsed|open|clearfix|processed/.test(a)}),c.length>0)?"."+c.join("."):b}function f(a){var b,c="",d=["href","type"];if("undefined"==typeof a||"undefined"==typeof a.attributes||null===a.attributes)return c;for(var e=0,f=d.length;f>e;e++)b=a.attributes[d[e]]&&a.attributes[d[e]].value,b&&(c+="["+d[e]+'="'+b+'"]');return c}function g(a){var b="",g="",h=!1,i=!0;do{if(g="",(g=d(a)).length>0){b=g+" "+b;break}!h&&(g=e(a)).length>0&&(b=g+" "+b,c(b)&&(h=!0)),i&&((g=f(a)).length>0&&(b=g+b),b=a.nodeName.toLowerCase()+b,i=!1),a=a.parentNode}while(a&&1===a.nodeType&&"BODY"!==a.nodeName&&"HTML"!==a.nodeName);return b.trim()}function h(a,b){for(var c=[],d=0,e=a.length;e>d;d++)b.call(null,a[d])||c.push(a[d]);return c}return b&&g(b)},push:[].push,sort:[].sort,concat:[].concat,splice:[].splice},b.fn.init.prototype=b.fn,b}(),b.lib.Section=function(){function a(b,c){return new a.fn.init(b,c)}return a.fn=a.prototype={constructor:a,init:function(a,c){if(!a)return this;if(this.id=a,c.techniques&&c.techniques.length){for(var d=0,e=c.techniques.length;e>d;++d)this.push(b.lib.Technique(c.techniques[d]));return this}return this},length:0,each:function(a){for(var b=[].slice.call(arguments,1),c=0,d=this.length;d>c;++c)b.unshift(this[c]),b.unshift(c),a.apply(this[c],b);return this},find:function(a){for(var b=0,c=this.length;c>b;++b)if(this[b].get("name")===a)return this[b];return null},set:function(a,c){for(var d=0,e=this.length;e>d;++d)if(this[d].get("name")===a)return this[d].set(c),this[d];var f=b.lib.Test(a,c);return this.push(f),f},addTechnique:function(a){this.push(a)},regiterTechniqueTestResult:function(){},push:[].push,sort:[].sort,splice:[].splice},a.fn.init.prototype=a.fn,a}(),b.lib.SuccessCriteria=function(){function c(a){return new c.fn.init(a)}function d(a){return Object.keys(a).length}return c.fn=c.prototype={constructor:c,init:function(a){return this.listeners={},this.attributes=this.attributes||{},this.attributes.status="untested",this.attributes.results={},this.attributes.totals={},this.set(a||{}),this},length:0,attributes:null,get:function(b){if("$scope"===b){var c=this.attributes.scope,d=a(this.attributes.scope);return this.attributes[b]?this.attributes[b]:c?d:a(document)}return this.attributes[b]},set:function(a,b){var c=!1;if("object"==typeof a)for(var d in a)a.hasOwnProperty(d)&&("status"===d&&(c=!0),this.attributes[d]=a[d]);else this.attributes[a]=b;return this},each:function(a){for(var b=[].slice.call(arguments,1),c=0,d=this.length;d>c;++c){b.unshift(this[c]),b.unshift(c);var e=a.apply(this[c],b);if(e===!1)break}return this},add:function(a){this.find(a.get("selector"))||this.push(a)},find:function(a){for(var b=0,c=this.length;c>b;++b)if(this[b].get("selector")===a)return this[b];return null},registerTests:function(a){var b=this.get("preEvaluator"),c="undefined"!=typeof b,d=!0;c&&(d=b.call(this,a)),d||this.set("status","inapplicable"),this.set("tests",a),this.listenTo(a,"complete",this.evaluate)},filterTests:function(a){var c=new b.lib.TestCollection,d=this.get("name");if(!d)throw new Error("Success Criteria instances require a name in order to have tests filtered.");var e=d.split(":")[1];return a.each(function(a,b){var d=b.getGuidelineCoverage("wcag");for(var f in d)d.hasOwnProperty(f)&&f===e&&c.add(b)}),c},addConclusion:function(a,c){this.get("results")[a]||(this.get("results")[a]=b.lib.Test()),this.get("results")[a].push(c),this.get("totals")[a]||(this.get("totals")[a]=0),++this.get("totals")[a],this.get("totals").cases||(this.get("totals").cases=0),++this.get("totals").cases},evaluate:function(a,b){if("inapplicable"!==this.get("status")){var c=this,e=this.filterTests(b);0===e.length?this.set("status","noTestCoverage"):(e.each(function(a,b){b.each(function(a,b){c.addConclusion(b.get("status"),b)})}),0===d(this.get("results"))?this.set("status","noResults"):this.set("status","tested"))}this.report()},report:function(){var a=Array.prototype.slice.call(arguments);a=[].concat(["successCriteriaEvaluated",this,this.get("tests")],a),this.dispatch.apply(this,a)},listenTo:function(a,b,c){c=c.bind(this),a.registerListener.call(a,b,c)},registerListener:function(a,b){this.listeners[a]||(this.listeners[a]=[]),this.listeners[a].push(b)},dispatch:function(a){if(this.listeners[a]&&this.listeners[a].length){var b=[].slice.call(arguments);this.listeners[a].forEach(function(a){a.apply(null,b)})}},push:[].push,sort:[].sort,splice:[].splice},c.fn.init.prototype=c.fn,c}(),b.lib.Technique=function(){function a(b,c){return new a.fn.init(b,c)}return a.fn=a.prototype={constructor:a,init:function(a,b){return this.listeners={},a?(this.attributes=b||{},this.attributes.name=a,this):this},length:0,attributes:{},each:function(a){for(var b=[].slice.call(arguments,1),c=0,d=this.length;d>c;++c)b.unshift(this[c]),b.unshift(c),a.apply(this[c],b);return this},get:function(a){return this.attributes[a]},set:function(a,b){if("object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&(this.attributes[c]=a[c]);else this.attributes[a]=b;return this},addTest:function(){},report:function(a,b){window.console&&window.console.log(this.get("name"),b.status,b,b[0]&&b[0].status)},listenTo:function(a,b,c){c=c.bind(this),a.registerListener.call(a,b,c)},registerListener:function(a,b){this.listeners[a]||(this.listeners[a]=[]),this.listeners[a].push(b)},dispatch:function(a){if(this.listeners[a]&&this.listeners[a].length){var b=[].slice.call(arguments);this.listeners[a].forEach(function(a){a.apply(null,b)})}},push:[].push,sort:[].sort,splice:[].splice},a.fn.init.prototype=a.fn,a}(),b.lib.Test=function(){function c(a,b){return new c.fn.init(a,b)}function d(a){a="undefined"==typeof a?!0:a,this.each(function(b,c){c.get("status")||(a=!1)}),a?(this.testComplete=null,this.attributes.complete=!0,this.determineStatus()):this.testComplete()}function e(a,b,c){var d,e;return function(){var f=this,g=arguments,h=function(){d=null,c||(e=a.apply(f,g))},i=c&&!d;return clearTimeout(d),d=setTimeout(h,b),i&&(e=a.apply(f,g)),e}}return c.fn=c.prototype={constructor:c,init:function(a,b){return this.listeners={},this.length=0,a?(this.attributes=b||{},this.attributes.name=a,this.attributes.status="untested",this.attributes.complete=!1,this):this},length:0,attributes:null,each:function(a){for(var b=[].slice.call(arguments,1),c=0,d=this.length;d>c;++c)b.unshift(this[c]),b.unshift(c),a.apply(this[c],b);return this},get:function(b){if("$scope"===b){var c=this.attributes.scope,d=a(this.attributes.scope);return this.attributes[b]?this.attributes[b]:c?d:a(document)}return this.attributes[b]},set:function(a,b){var c=!1;if("object"==typeof a)for(var d in a)a.hasOwnProperty(d)&&("status"===d&&(c=!0),this.attributes[d]=a[d]);else"status"===a&&(c=!0),this.attributes[a]=b;return c&&this.resolve(),this},add:function(a){return this.listenTo(a,"resolve",this.caseResponded),this.listenTo(a,"timeout",this.caseResponded),a.status&&a.dispatch("resolve",a),this.push(a),a},invoke:function(){if(this.testComplete)throw new Error("The test "+this.get("name")+" is already running."); +if(this.attributes.complete)throw new Error("The test "+this.get("name")+" has already been run.");var a=this.get("type"),c=this.get("options")||{},f=this.get("callback"),g=this;if(this.testComplete=e(d.bind(this),400),this.testComplete(!1),"custom"===a)if("function"==typeof f)try{f.call(this,b,g,b.lib.Case,c)}catch(h){window.console&&window.console.error&&window.console.error(h)}else{if("custom"!==a||"function"!=typeof b[f])throw new Error("The callback "+f+" cannot be invoked.");try{b[f].call(this,b,g,b.lib.Case,c)}catch(h){window.console&&window.console.error&&window.console.error(h)}}else{if("function"!=typeof b.components[a])throw new Error("The component type "+a+" is not defined.");try{b.components[a].call(this,b,g,b.lib.Case,c)}catch(h){window.console&&window.console.error&&window.console.error(h)}}return this.testComplete(),this},findByStatus:function(a){if(a){var b=new c;"string"==typeof a&&(a=[a]);for(var d=0,e=a.length;e>d;++d){var f=a[d];this.each(function(a,c){var d=c.get("status");d===f&&b.add(c)})}return b}},findCasesBySelector:function(a){var b=this.groupCasesBySelector();return b.hasOwnProperty(a)?b[a]:new c},findCaseByHtml:function(a){for(var c,d=0,e=this.length;e>d;++d)if(c=this[d],a===c.get("html"))return c;return b.lib.Case()},groupCasesBySelector:function(){var a={};return this.each(function(b,d){var e=d.get("selector");a[e]||(a[e]=new c),a[e].add(d)}),a},groupCasesByHtml:function(){var a={};return this.each(function(b,d){var e=d.get("html");a[e]||(a[e]=new c),a[e].add(d)}),a},getGuidelineCoverage:function(a){var b=this.get("guidelines");return b&&b[a]||{}},caseResponded:function(a,b){this.dispatch(a,this,b),"function"==typeof this.testComplete&&this.testComplete()},determineStatus:function(){var a,c=this.get("type");b.components[c]&&"function"==typeof b.components[c].postInvoke&&(a=b.components[c].postInvoke.call(this,this)),this.set(a===!0?{status:"passed"}:this.findByStatus(["cantTell"]).length===this.length?{status:"cantTell"}:this.findByStatus(["inapplicable"]).length===this.length?{status:"inapplicable"}:this.findByStatus(["failed","untested"]).length?{status:"failed"}:{status:"passed"})},resolve:function(){this.dispatch("complete",this)},testComplete:null,listenTo:function(a,b,c){c=c.bind(this),a.registerListener.call(a,b,c)},registerListener:function(a,b){this.listeners[a]||(this.listeners[a]=[]),this.listeners[a].push(b)},dispatch:function(a){if(this.listeners[a]&&this.listeners[a].length){var b=[].slice.call(arguments);this.listeners[a].forEach(function(a){a.apply(null,b)})}},push:[].push,sort:[].sort,concat:[].concat,splice:[].splice},c.fn.init.prototype=c.fn,c}(),b.lib.TestCollection=function(){function a(b){return new a.fn.init(b)}function c(){var a=!0;this.each(function(b,c){c.get("complete")||(a=!1)}),a?(this.testsComplete=null,this.dispatch("complete",this)):this.testsComplete()}function d(a,b,c){var d,e;return function(){var f=this,g=arguments,h=function(){d=null,c||(e=a.apply(f,g))},i=c&&!d;return clearTimeout(d),d=setTimeout(h,b),i&&(e=a.apply(f,g)),e}}return a.fn=a.prototype={constructor:a,init:function(a,c){if(this.listeners={},c=c||{},!a)return this;if("object"==typeof a){var d;for(var e in a)a.hasOwnProperty(e)&&(a[e].scope=a[e].scope||c.scope,d=new b.lib.Test(e,a[e]),this.listenTo(d,"results",this.report),this.push(d));return this}return this},length:0,run:function(a){var b=this;return a=a||{},this.each(function(c,d){a.preFilter&&b.listenTo(d,"resolve",function(b,c,d){var e=a.preFilter(b,c,d);e===!1&&(d.attributes.status="notTested",d.attributes.expected=null)}),a.caseResolve&&b.listenTo(d,"resolve",a.caseResolve),a.testComplete&&b.listenTo(d,"complete",a.testComplete)}),a.testCollectionComplete&&b.listenTo(b,"complete",a.testCollectionComplete),this.testsComplete=d(c.bind(this),500),this.each(function(a,b){b.invoke()}),this.testsComplete(),this},each:function(a){for(var b=[].slice.call(arguments,1),c=0,d=this.length;d>c;++c){b.unshift(this[c]),b.unshift(c);var e=a.apply(this[c],b);if(e===!1)break}return this},add:function(a){this.find(a.get("name"))||this.push(a)},find:function(a){for(var b=0,c=this.length;c>b;++b)if(this[b].get("name")===a)return this[b];return null},findByGuideline:function(b){var c={wcag:function(c,d){function e(b,c,d){var e=new a;return this.each(function(a,f){var g=f.get("guidelines"),h=g[b]&&g[b][c]&&g[b][c].techniques;if(h)for(var i=0,j=h.length;j>i;++i)h[i]===d&&(e.listenTo(f,"results",e.report),e.add(f))}),e}var f=c.id,g=d.get("name");return f&&g?e.call(this,b,f,g):void 0}};if(c[b]){var d=[].slice.call(arguments,1);return c[b].apply(this,d)}},findByStatus:function(b){if(b){var c=new a;"string"==typeof b&&(b=[b]);for(var d=0,e=b.length;e>d;++d){var f=b[d];this.each(function(a,b){var d=b.get("status");d===f&&c.add(b)})}return c}},set:function(a,c){for(var d=0,e=this.length;e>d;++d)if(this[d].get("name")===a)return this[d].set(c),this[d];var f=b.lib.Test(a,c);return this.push(f),f},testsComplete:null,report:function(){this.dispatch.apply(this,arguments)},listenTo:function(a,b,c){c=c.bind(this),a.registerListener.call(a,b,c)},registerListener:function(a,b){this.listeners[a]||(this.listeners[a]=[]),this.listeners[a].push(b)},dispatch:function(a){if(this.listeners[a]&&this.listeners[a].length){var b=[].slice.call(arguments);this.listeners[a].forEach(function(a){a.apply(null,b)})}},push:[].push,sort:[].sort,splice:[].splice},a.fn.init.prototype=a.fn,a}(),b.lib.WCAGGuideline=function(){var a=function(b){return new a.fn.init(b)};return a.fn=a.prototype={constructor:a,init:function(a){if(!a)return this;this.techniques=[];var c,d,e,f,g;if("object"==typeof a){if(a.guidelines){c=a.guidelines;for(var h in c)if(c.hasOwnProperty(h)){if(d=c[h],d.techniques&&d.techniques.length&&(e=d.techniques,delete d.techniques),d=b.lib.Section(h,d),e.length)for(var i=0,j=e.length;j>i;++i){if(f=e[i],!a.techniques[f])throw new Error("Definition for Technique "+f+" is missing from the guideline specification");g=this.findTechnique(f),g||(g=b.lib.Technique(f,a.techniques[f]),this.techniques.push(g)),d.addTechnique(g)}this.push(d)}}return this}return this},length:0,each:function(a){for(var b=[].slice.call(arguments,1),c=0,d=this.length;d>c;++c)b.unshift(this[c]),b.unshift(c),a.apply(this[c],b);return this},find:function(a){for(var b=0,c=this.length;c>b;++b)if(this[b].get("name")===a)return this[b];return null},findTechnique:function(a){for(var b=0,c=this.techniques.length;c>b;++b)if(this.techniques[b].get("name")===a)return this.techniques[b];return null},set:function(a,c){for(var d=0,e=this.length;e>d;++d)if(this[d].get("name")===a)return this[d].set(c),this[d];var f=b.lib.Test(a,c);return this.push(f),f},evaluate:function(){},results:function(){},push:[].push,sort:[].sort,splice:[].splice},a.fn.init.prototype=a.fn,a}(),function(a){function b(b,c,d,e){var f=c.attr("rowspan")||1,g=c.attr("scope");if("col"===g)return!0;if(-1!==i.indexOf(g))return!1;for(var h=0;h=0&&f>=0;e+=g,f+=h){var n=a(d[f][e]),o=0===g?"col":"row";if(n.is("th")){i=!0,j.push({cell:n,x:e,y:f});var p=!1;-1===h&&c(d,n,e,f)||-1===g&&b(d,n,e,f)?p=!0:a.each(m,function(b,c){var d=+n.attr(o+"span")||1,i=+a(c.cell).attr(o+"span")||1;d===i&&(-1===h&&c.x===e||-1===g&&c.y===f)&&(p=!0)}),p===!1&&(k=k.add(n))}else n.is("td")&&i===!0&&(i=!1,m.push(j),j=a())}return k}function e(b){var c=b.closest("table"),d=b.attr("headers").split(/\s/),e=a();return a.each(d,function(b,d){e=e.add(a("th#"+d+", td#"+d,c))}),e}function f(a,b){for(var c,d=0,e=0;void 0===c;){if(void 0===a[e])return;a[e][d]===b[0]?c=d:d+1===a[e].length?(e+=1,d=0):d+=1}return{x:c,y:e}}function g(b,c){var e,g=a(),h=f(c,b),i=+b.attr("rowspan")||1,j=+b.attr("colspan")||1;for(e=0;j>e;e++)g=g.add(d(c,h.x+e,h.y,0,-1));for(e=0;i>e;e++)g=g.add(d(c,h.x,h.y+e,-1,0));return g}function h(b,c){var d=f(c,b),e=a();b.closest("thead, tbody, tfoot").find("th[scope=rowgroup]").each(function(){var b=f(c,a(this));b.x<=d.x&&b.y<=d.y&&(e=e.add(this))})}var i=["row","col","rowgroup","colgroup"];a.fn.getTableMap=function(){var b=[];return this.find("tr").each(function(c){"undefined"==typeof b[c]&&(b[c]=[]);var d=b[c];a(this).children().each(function(){var e,f,g,h=a(this),i=+h.attr("rowspan")||1,j=+h.attr("colspan")||1;for(f=0,g=d.length;g>=f;f+=1)void 0===e&&void 0===d[f]&&(e=f);for(f=0,g=j*i;g>f;f+=1)void 0===b[c+~~(f/j)]&&(b[c+~~(f/j)]=[]),b[c+~~(f/j)][e+f%j]=this})}),b},a.fn.tableHeaders=function(){var b=a();return this.each(function(){var c=a(this);if(!c.is(":not(td, th)"))if(c.is("[headers]"))b=b.add(e(c));else{var d=c.closest("table").getTableMap();b=b.add(g(c,d)).add(h(c,d))}}),b.not(":empty").not(this)}}(jQuery),b.lib.wcag2=function(){function c(b){b.wcag2Structure&&b.accessibilityTests&&b.preconditionTests?d(b,b.wcag2Structure,b.accessibilityTests,b.preconditionTests):a.when(a.ajax(b.jsonPath+"/wcag2.json",f),a.ajax(b.jsonPath+"/tests.json",f),a.ajax(b.jsonPath+"/preconditions.json",f)).done(function(a,c,e){d(b,a[0],c[0],e[0])})}function d(c,d,f,g){var h,i,j,k=[];h=a.map(d,function(a){return new b.lib.wcag2.Criterion(a,f,g,c.subject)}),a.each(h,function(a,b){k.push.apply(k,b.getTests())}),j=[],i=[],a.each(k,function(a,b){-1===j.indexOf(b.title.en)&&(j.push(b.title.en),i.push(b))}),a(b.html).quail({accessibilityTests:i,testCollectionComplete:e(h,c.testCollectionComplete)})}function e(b,c){return function(d,e){"complete"===d&&(e=a.map(b,function(a){return a.getResult(e)})),c(d,e)}}var f={async:!1,dataType:"json"};return{run:c}}(),b.guidelines.wcag.successCriteria["1.1.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.1.1",preEvaluator:b});return c.techniques={},c.failures={F3:"Using CSS to include images that convey important information",F13:"Having a text alternative that does not include information that is conveyed by color differences in the image",F20:"Not updating text alternatives when changes to non-text content occur",F30:"Using text alternatives that are not alternatives (e.g., filenames or placeholder text)",F38:"Not marking up decorative images in HTML in a way that allows assistive technology to ignore them",F39:'Providing a text alternative that is not null (e.g., alt="spacer" or alt="image") for images that should be ignored by assistive technology',F65:'Omitting the alt attribute or text alternative on img elements, area elements, and input elements of type "image"',F67:"Providing long descriptions for non-text content that does not serve the same purpose or does not present the same information",F71:"Using text look-alikes to represent text without providing a text alternative",F72:"Using ASCII art without providing a text alternative"},c}(b),b.guidelines.wcag.successCriteria["1.2.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.2.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.2",preEvaluator:b});return c.techniques={G93:"Providing open (always visible) captions",G87:"Providing closed captions"},c.failures={F74:"Not labeling a synchronized media alternative to text as an alternative",F75:"Providing synchronized media without captions when the synchronized media presents more information than is presented on the page",F8:"Captions omitting some dialogue or important sound effects"},c}(b),b.guidelines.wcag.successCriteria["1.2.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.3",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.2.4"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.4",preEvaluator:b});return c.techniques={G9:"Creating captions for live synchronized media",G93:"Providing open (always visible) captions",G87:"Providing closed captions using any readily available media format that has a video player that supports closed captioning"},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.2.5"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.5",preEvaluator:b});return c.techniques={G78:"Providing a second, user-selectable, audio track that includes audio descriptions",G173:"Providing a version of a movie with audio descriptions","SC1.2.8":"Providing a movie with extended audio descriptions",G8:"Providing a movie with extended audio descriptions",G203:"Using a static text alternative to describe a talking head video"},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.2.7"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.7",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.2.8"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.8",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.2.9"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.2.9",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.3.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.3.1",preEvaluator:b});return c.techniques={G115:"Using semantic elements to mark up structure AND H49: Using semantic markup to mark emphasized or special text",G117:"Using text to convey information that is conveyed by variations in presentation of text",G140:"Separating information and structure from presentation to enable different presentations",G138:"Using semantic markup whenever color cues are used",H48:"Using ol, ul and dl for lists or groups of links",H42:"Using h1-h6 to identify headings",SCR21:"Using functions of the Document Object Model (DOM) to add content to a page (Scripting)",H51:"Using table markup to present tabular information",H39:"Using caption elements to associate data table captions with data tables",H73:"Using the summary attribute of the table element to give an overview of data tables",H63:"Using the scope attribute to associate header cells and data cells in data tables",H43:"Using id and headers attributes to associate data cells with header cells in data tables",H44:"Using label elements to associate text labels with form controls",H65:"Using the title attribute to identify form controls when the label element cannot be used",H71:"Providing a description for groups of form controls using fieldset and legend elements",H85:"Using OPTGROUP to group OPTION elements inside a SELECT",ARIA11:"Using ARIA landmarks to identify regions of a page (ARIA)",ARIA12:"Using role=heading to identify headings (ARIA)",ARIA13:"Using aria-labelledby to name regions and landmarks (ARIA)",ARIA16:"Using aria-labelledby to provide a name for user interface controls (ARIA)",ARIA17:"Using grouping roles to identify related form controls (ARIA)"},c.failures={F2:"Using changes in text presentation to convey information without using the appropriate markup or text",F17:"Insufficient information in DOM to determine one-to-one relationships (e.g., between labels with same id) in HTML",F42:"Using scripting events to emulate links in a way that is not programmatically determinable",F43:"Using structural markup in a way that does not represent relationships in the content",F87:"Inserting non-decorative content by using :before and :after pseudo-elements and the content property in CSS",F46:"Using th elements, caption elements, or non-empty summary attributes in layout tables",F48:"Using the pre element to markup tabular information",F90:"Incorrectly associating table headers and content via the headers and id attributes",F91:"Not correctly marking up table headers",F33:"Using white space characters to create multiple columns in plain text content",F34:"Using white space characters to format tables in plain text content",F68:"Association of label and user interface controls not being programmatically determinable"},c}(b),b.guidelines.wcag.successCriteria["1.3.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.3.2",preEvaluator:b});return c.techniques={G57:"Ordering the content in a meaningful sequence (scope: for all the content in the Web page)",H34:"Using a Unicode right-to-left mark (RLM) or left-to-right mark (LRM) to mix text direction inline (languageUnicodeDirection)",H56:"Using the dir attribute on an inline element to resolve problems with nested directional runs",C6:"Positioning content based on structural markup (CSS)",C8:"Using CSS letter-spacing to control spacing within a word",C27:"Making the DOM order match the visual order (CSS)"},c.failures={F49:"Using an HTML layout table that does not make sense when linearized",F32:"Using white space characters to control spacing within a word (whiteSpaceInWord)",F1:"Changing the meaning of content by positioning information with CSS",F34:"Using white space characters to format tables in plain text content (tabularDataIsInTable)",F33:"Using white space characters to create multiple columns in plain text content (tabularDataIsInTable)"},c}(b),b.guidelines.wcag.successCriteria["1.3.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.3.3",preEvaluator:b});return c.techniques={G96:"Providing textual identification of items that otherwise rely only on sensory information to be understood"},c.failures={F14:"Identifying content only by its shape or location",F26:"Using a graphical symbol alone to convey information"},c}(b),b.guidelines.wcag.successCriteria["1.4.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.4.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.4.2"]=function(b){function c(){return!!a("audio, video, object, embed").length}var d=b.lib.SuccessCriteria({name:"wcag:1.4.2",preEvaluator:c});return d.techniques={G60:"Playing a sound that turns off automatically within three seconds",G170:"Providing a control near the beginning of the Web page that turns off sounds that play automatically",G171:"Playing sounds only on user request"},d.failures={F23:"Playing a sound longer than 3 seconds where there is no mechanism to turn it off"},d}(b),b.guidelines.wcag.successCriteria["1.4.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.4.3",preEvaluator:b});return c.techniques={G148:"Not specifying background color, not specifying text color, and not using technology features that change those defaults",G174:"Providing a control with a sufficient contrast ratio that allows users to switch to a presentation that uses sufficient contrast",G18:"Ensuring that a contrast ratio of at least 4.5:1 exists between text (and images of text) and background behind the text for situation A AND G145: Ensuring that a contrast ratio of at least 3:1 exists between text (and images of text) and background behind the text for situation B"},c.failures={F24:"Specifying foreground colors without specifying background colors or vice versa",F83:"Using background images that do not provide sufficient contrast with foreground text (or images of text)"},c}(b),b.guidelines.wcag.successCriteria["1.4.4"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.4.4",preEvaluator:b});return c.techniques={G142:"Using a technology that has commonly-available user agents that support zoom",C12:"Using percent for font sizes",C13:"Using named font sizes",C14:"Using em units for font, sizes",SCR34:"Calculating size and ,position in a way that scales with text size (Scripting)",G146:"Using liquid layout",G178:"Providing controls on the Web page that allow users to incrementally change the size of all text on the page up to 200 percent",G179:"Ensuring that there is no loss of content or functionality when the text resizes and text containers do not change their width"},c.failures={F69:"Resizing visually rendered text up to 200 percent causes the text, image or controls to be clipped, truncated or obscured",F80:"Text-based form controls do not resize when visually rendered text is resized up to 200%"},c}(b),b.guidelines.wcag.successCriteria["1.4.5"]=function(a){function b(){return!!document.querySelectorAll("img, map").length}var c=a.lib.SuccessCriteria({name:"wcag:1.4.5",preEvaluator:b});return c.techniques={C22:"Using CSS to control visual presentation of text (CSS)",C30:"Using CSS to replace text with images of text and providing user interface controls to switch",G140:"Separating information and structure from presentation to enable different presentations"},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.4.6"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.4.6",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.4.7"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.4.7",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.4.8"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.4.8",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["1.4.9"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:1.4.9",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.1.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.1.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.1.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.1.2",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.1.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.1.3",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.2.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.2.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.2.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.2.2",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.2.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.2.3",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.2.4"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.2.4",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.2.5"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.2.5",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.3.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.3.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.3.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.3.2",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.1",preEvaluator:b});return c.techniques={G1:"Adding a link at the top of each page that goes directly to the main content area",G123:"Adding a link at the beginning of a block of repeated content to go to the end of the block",G124:"Adding links at the top of the page to each area of the content",H69:"Providing heading elements at the beginning of each section of content",H70:"Using frame elements to group blocks of repeated material AND H64: Using the title attribute of the frame and iframe elements",SCR28:"Using an expandable and collapsible menu to bypass block of content"},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.10"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.10",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.2",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.3",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.4"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.4",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.5"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.5",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.6"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.6",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.7"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.7",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.8"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.8",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["2.4.9"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:2.4.9",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.1.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.1.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.1.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.1.2",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.1.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.1.3",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.1.4"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.1.4",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.1.5"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.1.5",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.1.6"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.1.6",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.2.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.2.1",preEvaluator:b});return c.techniques={G107:'Using "activate" rather than "focus" as a trigger for changes of context'},c.failures={F52:"Opening a new window as soon as a new page is loaded",F55:"Using script to remove focus when focus is received"},c}(b),b.guidelines.wcag.successCriteria["3.2.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.2.2",preEvaluator:b});return c.techniques={G80:"Providing a submit button to initiate a change of context",H32:"Providing submit buttons",H84:"Using a button with a select element to perform an action",G13:"Describing what will happen before a change to a form control that causes a change of context to occur is made",SCR19:"Using an onchange event on a select element without causing a change of context"},c.failures={F36:"Automatically submitting a form and presenting new content without prior warning when the last field in the form is given a value",F37:"Launching a new window without prior warning when the status of a radio button, check box or select list is changed",F76:"Providing instruction material about the change of context by change of setting in a user interface element at a location that users may bypass"},c}(b),b.guidelines.wcag.successCriteria["3.2.3"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.2.3",preEvaluator:b});return c.techniques={G61:"Presenting repeated components in the same relative order each time they appear"},c.failures={F66:"Presenting navigation links in a different relative order on different pages"},c}(b),b.guidelines.wcag.successCriteria["3.2.4"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.2.4",preEvaluator:b});return c.techniques={G197:"Using labels, names, and text alternatives consistently for content that has the same functionality AND following the sufficient techniques for Success Criterion 1.1.1 and sufficient techniques for Success Criterion 4.1.2 for providing labels, names, and text alternatives."},c.failures={F31:"Using two different labels for the same function on different Web pages within a set of Web pages"},c}(b),b.guidelines.wcag.successCriteria["3.2.5"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.2.5",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.3.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.3.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.3.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.3.2",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.3.3"]=function(a){function b(){function a(a){return!!this.querySelectorAll('[type="'+a+'"]').length}function b(a){var b=Object.keys(a)[0];return!!this.querySelectorAll("["+b+'="'+a[b]+'"]').length}var c=["checkbox","color","date","datetime","datetime-local","email","file","hidden","month","number","password","radio","range","search","tel","time","url","week"],d=[{required:"required"},{"aria-required":"true"}];return document.querySelectorAll("form").length?c.some(a,document)||d.some(b,document)?!0:void 0:!1}var c=a.lib.SuccessCriteria({name:"wcag:3.3.3",preEvaluator:b});return c.techniques={G83:"Providing text descriptions to identify required fields that were not completed",ARIA2:"Identifying a required field with the aria-required property",ARIA18:"Using aria-alertdialog to Identify Errors (ARIA)",G85:"Providing a text description when user input falls outside the required format or values",G177:"Providing suggested correction text",SCR18:"Providing client-side validation and alert (Scripting)",SCR32:"Providing client-side validation and adding error text via the DOM (Scripting)",G84:"Providing a text description when the user provides information that is not in the list of allowed values"},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.3.4"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.3.4",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.3.5"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.3.5",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["3.3.6"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:3.3.6",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["4.1.1"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:4.1.1",preEvaluator:b});return c.techniques={},c.failures={},c}(b),b.guidelines.wcag.successCriteria["4.1.2"]=function(a){function b(){return!0}var c=a.lib.SuccessCriteria({name:"wcag:4.1.2",preEvaluator:b});return c.techniques={ARIA14:"Using aria-label to provide an invisible label where a visible label cannot be used",ARIA16:"Using aria-labelledby to provide a name for user interface controls",G108:"Using markup features to expose the name and role, allow user-settable properties to be directly set, and provide notification of changes using technology-specific techniques below:",H91:"Using HTML form controls and links",H44:"Using label elements to associate text labels with form controls",H64:"Using the title attribute of the frame and iframe elements",H65:"Using the title attribute to identify form controls when the label element cannot be used",H88:"Using HTML according to spec"},c.failures={F59:"Using script to make div or span a user interface control in HTML without providing a role for the control (This failure may be solved in the future using DHTML roadmap techniques.)",F20:"Not updating text alternatives when changes to non-text content occur",F68:"Association of label and user interface controls not being programmatically determined",F79:"Focus state of a user interface component not being programmatically determinable or no notification of change of focus state available",F86:"Not providing names for each part of a multi-part form field, such as a US telephone number",F89:"Using null alt on an image where the image is the only content in a link"},c +}(b),b.lib.wcag2.Criterion=function(){function c(c,d){var e=b.lib.wcag2.EarlAssertion.getResultPriority,f={result:d};return a.each(c,function(a,b){e(f)0&&(e.hasPart=f),e},i.getTests=function(){var b=[];return a.each(h,function(a,c){b.push.apply(b,c.tests)}),b},i}return d}(),b.lib.wcag2.EarlAssertion=function(){function b(b){a.extend(this,b,e),this.outcome=a.extend({},this.outcome)}var c,d=["untested","inapplicable","passed","cantTell","failed"],e={type:"assertion",subject:c,assertedBy:{type:"earl:Software",name:"QuailJS"},mode:"automated"};return window&&window.location&&(c=window.location.href),b.getResultPriority=function(a){return"object"==typeof a&&(a=a.outcome?a.outcome.result:a.result),d.indexOf(a)},b}(),b.lib.wcag2.TestAggregator=function(){function c(b,c){a.each(b,function(a,b){b.each(function(){c.call(this,b,this)})})}function d(b){var c=[],d=[];return a.each(b,function(a,b){var c=[];b.each(function(){c.push(this.get("element")),j.add(this)}),d.push(c)}),a.each(d,function(b,d){if(0===b)return void(c=d);var e=[];a.each(d,function(a,b){-1!==c.indexOf(b)&&e.push(b)}),c=e}),c}function e(a){var b=[];return c(a,function(a,c){var d=c.get("element");-1===b.indexOf(d)&&(b.push(d),j.add(c))}),b}function f(c,d){var e=[];return a.each(c,function(a,c){var f=new b.lib.wcag2.EarlAssertion(d);c&&(f.outcome.pointer=j.getPointer(c)),e.push(f)}),e}function g(a,e){var g=jQuery.unique(d(e)),h=f(jQuery.unique(g),{testCase:a.id,outcome:{result:"failed"}});return c(e,function(c,d){var e=d.get("status"),f=b.lib.wcag2.EarlAssertion.getResultPriority,i=h[g.indexOf(d.get("element"))];if(a[e]&&(e=a[e]),i&&f(i)>=f(e)){var j=i.outcome.pointer;i.outcome={result:e,info:c.get("title")},j&&(i.outcome.pointer=j)}}),h}function h(a,d){var g=e(d),h=f(g,{testCase:a.id,outcome:{result:"untested"}});return c(d,function(c,d){var e=d.get("status"),f=b.lib.wcag2.EarlAssertion.getResultPriority,i=h[g.indexOf(d.get("element"))];a[e]&&(e=a[e]),i&&f(i)a.-Elemente sollten das \"javascript\"-Protokoll im \"href\"-Attribut nicht verwenden.", + "en": "Anchor (a. elements may not use the \"javascript\" protocol in their \"href\" attributes.", + "nl": "Anchor(a.-elementen mogen geen \"javascript\"protocol in hun \"href\"-attributen hebben staan.", + "pt-br": "Os elementos de âncora ( a ) não podem usar o protocolo \"javascript\" em seus atributos \"href\"." + }, + "guidelines": [], + "tags": [ + "link", + "content" + ], + "options": { + "selector": "a[href^='javascript:']" + } + }, + "aSuspiciousLinkText": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verknüpfungstexte sollten sinnvoll sein", + "en": "Link text should be useful", + "nl": "Linkteksten moeten bruikbaar en betekenisvol zijn", + "pt-br": "Texto do link deve ser útil" + }, + "description": { + "de": "Da viele Benutzer von Bildschirmvorleseprogrammen Verknüpfungen verwenden, um in der Seite zu navigieren, erlauben Verknüpfungen mit einfachen Texten (bspw. \"Hier klicken\") keinen Rückschluss auf ihren Zweck.", + "en": "Because many users of screen-readers use links to navigate the page, providing links with text that simply read \"click here\" gives no hint of the function of the link.", + "nl": "Veel gebruikers van schermlezers gebruiken links om op de pagina te navigeren. Links met de tekst \"klik hier\" zijn voor deze gebruikers niet betekenisvol en niet bruikbaar.", + "pt-br": "Como muitos usuários de tela-leitores usam links para navegar na página, fornecendo links com texto que simplesmente ler \"clique aqui\" não dá nenhuma dica da função do link." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H30" + ] + }, + "2.4.4": { + "techniques": [ + "H30" + ] + }, + "2.4.9": { + "techniques": [ + "H30" + ] + } + } + }, + "tags": [ + "link", + "content" + ], + "strings": [ + "suspiciousLinks" + ], + "callback": "aSuspiciousLinkText" + }, + "aTitleDescribesDestination": { + "type": "selector", + "testability": 0, + "title": { + "de": "Das Title-Attribut von Verknüpfungen beschreibt das Verknüpfungs-Ziel.", + "en": "The title attribute of all source a (anchor) elements describes the link destination.", + "nl": "Het titel-attribuut van alle source a (anchor)-elementen beschrijven de bestemming van de link", + "pt-br": "O atributo título de todos os elementos (âncora) descrevem o destino do link." + }, + "description": { + "de": "Jede Verknüpfung muss ein \"title\"-Attribut haben, welches den Zweck oder das Ziel der Verknüpfung beschreibt.", + "en": "Every link must have a \"title\" attribute which describes the purpose or destination of the link.", + "nl": "Elke link moet een \"title\"-attribuut hebben waarin het doel of de bestemming van de link wordt beschreven.", + "pt-br": "Cada link deve ter um atributo \"title\" que descreve a finalidade ou destino do link." + }, + "guidelines": { + "wcag": { + "2.4.9": { + "techniques": [ + "H33", + "H25" + ] + } + } + }, + "tags": [ + "link", + "content" + ], + "options": { + "selector": "a[title]" + } + }, + "addressForAuthor": { + "type": "selector", + "testability": 1, + "title": { + "de": "Das Dokument sollte eine korrekte Kontaktadresse zum Autor beinhalten", + "en": "The document should contain an address for the author", + "nl": "Het document moet een adres voor de auteur bevatten", + "pt-br": "O documento deve conter o endereço eletrônico do autor" + }, + "description": { + "de": "Dokumente sollten eine korrekte E-Mail-Addresse in einem address-Element enthalten.", + "en": "Documents should provide a valid email address within an address element.", + "nl": "Documenten moeten een e-mailadres bevatten binnen het address-element.", + "pt-br": "Os documentos devem fornecer um endereço de e-mail válido dentro de um elemento address ." + }, + "guidelines": [], + "tags": [ + "document" + ], + "options": { + "selector": "body:not(body:has(address))" + } + }, + "addressForAuthorMustBeValid": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Das Dokument sollte eine korrekte E-Mail-Adresse zum Autor beinhalten", + "en": "The document should contain a valid email address for the author", + "nl": "Het document moet een geldig e-mailadres bevatten voor de auteur", + "pt-br": "O documento deve conter um email válido do autor" + }, + "description": { + "de": "Dokumente sollten eine korrekte E-Mail-Addresse in einem address-Element enthalten.", + "en": "Documents should provide a valid email address within an address element.", + "nl": "Documenten moeten een geldig e-mailadres bevatten binnen het address element.", + "pt-br": "Os documentos devem fornecer um endereço de e-mail válido dentro de um elemento address." + }, + "guidelines": [], + "tags": [ + "document" + ], + "options": { + "selector": "address" + } + }, + "appletContainsTextEquivalent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Applets sollten den Inhalt im Element des Applets selbst enthalten", + "en": "All applets should contain the same content within the body of the applet", + "nl": "Alle applets moeten dezelfde content bevatten in de body van de applet", + "pt-br": "Todos os applets devem conter o mesmo conteúdo dentro do corpo do applet" + }, + "description": { + "de": "Applets sollten ihre Text-Äquivalente oder eine Beschreibung innerhalb des applet-Tag selbst enthalten.", + "en": "Applets should contain their text equivalents or description within the applet tag itself.", + "nl": "Applets moeten hun tekstuele equivalent of beschrijving bevatten in de applet tag.", + "pt-br": "As applets devem conter seus equivalentes de texto ou descrição dentro da própria tag applet." + }, + "guidelines": { + "508": [ + "m" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "G74", + "H35" + ] + } + } + }, + "tags": [ + "objects", + "applet", + "content" + ], + "callback": "appletContainsTextEquivalent" + }, + "appletContainsTextEquivalentInAlt": { + "type": "placeholder", + "testability": 0.5, + "title": { + "de": "Alle Applets sollten ein Text-Äquivalent im \"alt\"-Attribut enthalten", + "en": "All applets should contain a text equivalent in the \"alt\" attribute", + "nl": "Alle applets moeten een tekstuele equivalent bevatten in het \"alt\"-attribuut", + "pt-br": "Todos os applets devem conter um equivalente de texto no atributo \"alt\"" + }, + "description": { + "de": "Applets sollten ihr Text-Äquivalent oder eine Beschreibung im \"alt\"-Attribut enthalten.", + "en": "Applets should contain their text equivalents or description in an \"alt\" attribute.", + "nl": "Applets moeten hun tekstuele equivalent of beschrijving bevatten in een \"alt\"-attribuut.", + "pt-br": "Os applets devem conter seus equivalentes de texto ou descrição em um atributo \"alt\"." + }, + "guidelines": { + "508": [ + "m" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "G74", + "H35" + ] + } + } + }, + "tags": [ + "objects", + "applet", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "alt", + "empty": true, + "selector": "applet" + } + }, + "appletProvidesMechanismToReturnToParent": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle Applets sollten einen Weg für Tastatur-Benutzer bieten, um das Applet zu verlassen.", + "en": "All applets should provide a way for keyboard users to escape", + "nl": "Alle applets moeten door toetsenbordgebruikers kunnen worden verlaten", + "pt-br": "Todos os applets devem fornecer uma maneira de escape para os usuários de teclado" + }, + "description": { + "de": "Stellen Sie sicher, dass ein Benutzer, der nur eine Tastatur als Eingabegerät besitzt, ein applet-Element per Tastaturbefehl verlassen kann. Dies muss manuell getestet werden.", + "en": "Ensure that a user who has only a keyboard as an input device can escape an applet element. This requires manual confirmation.", + "nl": "Zorg ervoor dat gebruikers die alleen het toetsenbord gebruiken als bediening een applet-element kunnen verlaten. Hiervoor is handmatige bevestiging nodig.", + "pt-br": "Assegure-se de que um usuário que tenha apenas um teclado como dispositivo de entrada possa escapar de um elemento applet. Isso requer confirmação manual." + }, + "guidelines": [], + "tags": [ + "objects", + "applet", + "content" + ], + "options": { + "selector": "applet" + } + }, + "appletTextEquivalentsGetUpdated": { + "type": "selector", + "testability": 0, + "guidelines": { + "508": [ + "m" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "G74", + "H35" + ] + } + } + }, + "tags": [ + "objects", + "applet", + "content" + ], + "options": { + "selector": "applet" + } + }, + "appletUIMustBeAccessible": { + "type": "selector", + "testability": 0, + "title": { + "de": "Jede Benutzerschnittstelle in einem Applet muss barrierefrei sein", + "en": "Any user interface in an applet must be accessible", + "nl": "Elke user interface in een applet moet toegankelijk zijn", + "pt-br": "Qualquer interface de usuário em um applet deve ser acessível" + }, + "description": { + "de": "Inhalte in Applets sollten auf Barrierefreiheit geprüft werden.", + "en": "Applet content should be assessed for accessibility.", + "nl": "Content in een applet moet getoetst worden op toegankelijkheid.", + "pt-br": "O conteúdo do applet deve ser avaliado quanto à acessibilidade." + }, + "guidelines": { + "508": [ + "m" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "G74", + "H35" + ] + } + } + }, + "tags": [ + "objects", + "applet", + "content" + ], + "options": { + "selector": "applet" + } + }, + "appletsDoNotFlicker": { + "type": "selector", + "testability": 0, + "title": { + "de": "Applets sollten nicht flackern oder blinken", + "en": "All applets do not flicker", + "nl": "Applets knipperen of flitsen niet", + "pt-br": "Todos os applets não cintilam" + }, + "description": { + "de": "Applets sollten nicht flackern oder blinken.", + "en": "Applets should not flicker.", + "nl": "Geen enkele applet mag knipperen of flitsen.", + "pt-br": "Todos os applets não devem cintilar" + }, + "guidelines": { + "508": [ + "j" + ], + "wcag": { + "2.2.2": { + "techniques": [ + "F7" + ] + } + } + }, + "tags": [ + "objects", + "applet", + "content" + ], + "options": { + "selector": "applet" + } + }, + "appletsDonotUseColorAlone": { + "type": "selector", + "testability": 0, + "title": { + "de": "Applets sollten nicht nur Farben verwenden, um Inhalte zu kommunizieren", + "en": "Applets should not use color alone to communicate content", + "nl": "Applets mogen niet alleen kleur gebruiken om een boodschap over te brengen", + "pt-br": "Applets não devem usar uma cor única para comunicar conteúdo" + }, + "description": { + "de": "Applets sollten Inhalte so darstellen, das sie ohne Farben verständlich und für farbenblinde Benutzer zugänglich sind.", + "en": "Applets should contain content that makes sense without color and is accessible to users who are color blind.", + "nl": "Applets moeten content bevatten die ook bruikbaar is zonder kleur en die toegankelijk is voor gebruikers met kleurenblindheid.", + "pt-br": "Applets devem conter conteúdo que faz sentido sem cor e é acessível para usuários que são daltônicos." + }, + "guidelines": { + "508": [ + "c" + ] + }, + "tags": [ + "objects", + "applet", + "content" + ], + "options": { + "selector": "applet" + } + }, + "areaAltIdentifiesDestination": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle \"area\"-Elementes müssen ein \"alt\"-Attribut haben, welches das Verknüpfungsziel beschreibt", + "en": "All \"area\" elements must have an \"alt\" attribute which describes the link destination", + "nl": "Alle \"area\"-elementen moeten een \"alt\"-attribuut hebben die de bestemming van de link beschrijft", + "pt-br": "Todos os elementos de \"área\" devem ter um atributo \"alt\" que descreve o destino do link" + }, + "description": { + "de": "Alle area-Element mit einem map-Element müsssen ein \"alt\"-Attribut haben", + "en": "All area elements within a map must have an \"alt\" attribute", + "nl": "Alle area-elementen binnen een map moeten een \"alt\"-attribuut hebben.", + "pt-br": "Todos os elementos area dentro de um mapa devem ter um atributo \"alt\"" + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "G74" + ] + } + } + }, + "tags": [ + "objects", + "applet", + "content" + ], + "options": { + "selector": "area[alt]" + } + }, + "areaAltRefersToText": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alternativtexte für \"area\"-Elemente sollten den Text wiedergeben, der in den Grafik-Verknüpfungen dargestellt ist", + "en": "Alt text for \"area\" elements should replicate the text found in the image", + "nl": "Alt-tekst voor \"area\"-elementen moeten de tekst bevatten zoals die ook in de afbeelding staat", + "pt-br": "Texto alternativo para elementos \"área\" devem replicar o texto encontrado na imagem" + }, + "description": { + "de": "Wenn ein Grafik als map verwendet wird und ein area-Element den Text im Grafik umschließt, sollte das \"alt\"-Attribut dieser area denselben Text enthalten, wie der im Grafik dargestellte Text.", + "en": "If an image is being used as a map, and an area encompasses text within the image, then the \"alt\" attribute of that area element should be the same as the text found in the image.", + "nl": "Als een afbeelding als kaart wordt gebruikt, en een area bevat tekst binnen de afbeelding, dan moet het \"alt\"-attribuut van dat area-element hetzelfde zijn als de tekst die in de afbeelding staat.", + "pt-br": "Se uma imagem está sendo usada como um mapa e uma área abrange texto dentro da imagem, então o atributo \"alt\" desse elemento area deve ser o mesmo que o texto encontrado na imagem." + }, + "guidelines": [], + "tags": [ + "imagemap", + "content" + ], + "options": { + "selector": "area" + } + }, + "areaDontOpenNewWindow": { + "type": "selector", + "testability": 1, + "title": { + "de": "Kein \"area\"-Element sollte ein neues Fenster ohne Warnung öffnen", + "en": "No \"area\" elements should open a new window without warning", + "nl": "\"area\"-elementen mogen geen nieuw scherm openen zonder melding", + "pt-br": "Nenhum elemento \"área\" deve abrir uma nova janela sem aviso prévio" + }, + "description": { + "de": "area-Elemente sollte ein neues Fenster nicht ohne vorhergehende Warnung öffnen.", + "en": "No area elements should open a new window without warning.", + "nl": "area-elementen mogen geen nieuw scherm openen zonder dat de gebruiker hiervan een melding krijgt.", + "pt-br": "Nenhum elemento area deve abrir uma nova janela sem aviso prévio." + }, + "guidelines": [], + "tags": [ + "imagemap", + "content" + ], + "options": { + "selector": "area[target='new window'], area[target=_new], area[target=_blank], area[target=_blank]" + } + }, + "areaHasAltValue": { + "type": "selector", + "testability": 1, + "title": { + "de": "Alle \"area\"-Elemente müssen ein \"alt\"-Attribut haben", + "en": "All \"area\" elements must have an \"alt\" attribute", + "nl": "Alle \"area\"-elementen moeten een \"alt\"-attribuut hebben", + "pt-br": "Todos os elementos de \"área\" devem ter um atributo \"alt\"" + }, + "description": { + "de": "Alle area-Elemente in einem map-Element müssen ein \"alt\"-Attribut haben.", + "en": "All area elements within a map must have an \"alt\" attribute.", + "nl": "Alle area-elementen binnen een map moeten een \"alt\"-attribuut hebben.", + "pt-br": "Todos os elementos area dentro de um mapa devem ter um atributo \"alt\"." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "F65", + "G74", + "H24" + ] + }, + "1.4.3": { + "techniques": [ + "G145" + ] + } + } + }, + "tags": [ + "imagemap", + "content" + ], + "options": { + "selector": "area", + "test": ":not(area[alt])" + } + }, + "areaLinksToSoundFile": { + "type": "selector", + "testability": 1, + "title": { + "de": "Alle \"area\"-Elemente mit einer Verknüpfung zu einer Audio-Datei sollten auch eine Verknüpfung zu einem Transkript enthalten", + "en": "All \"area\" elements which link to a sound file should also provide a link to a transcript", + "nl": "Alle \"area\"-elementen met een link naar een geluidsbestand moeten ook een link bevatten naar een transcriptie", + "pt-br": "Todos os elementos de \"area\" que ligam a um arquivo de som devem também fornecer um link para uma transcrição" + }, + "description": { + "de": "Alle \"area\"-Elemente mit einer Verknüpfung Audio-Datei sollten eine Verknüpfung zu einem Transkript haben.", + "en": "All area elements which link to a sound file should have a text transcript.", + "nl": "Alle \"area\"-elementen met een link naar een geluidsbestand moeten een transcriptie hebben in tekst.", + "pt-br": "Todos os elementos area que ligam a um arquivo de som devem ter uma transcrição de texto." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "G74" + ] + } + } + }, + "tags": [ + "imagemap", + "media", + "content" + ], + "options": { + "selector": "area[href$=wav], area[href$=snd], area[href$=mp3], area[href$=iff], area[href$=svx], area[href$=sam], area[href$=smp], area[href$=vce], area[href$=vox], area[href$=pcm], area[href$=aif]" + } + }, + "ariaOrphanedContent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Seiten mit ARIA-roles sollten keinen verwaisten Inhalt haben", + "en": "Pages using ARIA roles should not have orphaned content", + "nl": "Pagina's die ARIA-rollen gebruiken mogen geen verweesde content hebben", + "pt-br": "Páginas que usam funções ARIA não devem ter conteúdo orfão" + }, + "description": { + "de": "Wenn eine Seite ARIA-roles verwendet, sollte es keinen Seiteninhalt ohne ARIA-role geben, da dies fehlerhafte Darstellungen in Bildschirmleseprogrammen bewirken kann.", + "en": "If a page makes use of ARIA roles, then there should not be any content on the page which is not within an element that exposes a role, as it could cause that content to be rendreed inaccessible to users with screen readers.", + "nl": "Als een pagina gebruik maakt van ARIA-rollen, mag er geen content op de pagina staan buiten een element dat een rol vertegenwoordigt. In dat geval kan het zijn dat deze content niet toegankelijk is voor gebruikers van schermlezers.", + "pt-br": "Se uma página fizer uso de funções ARIA, então não deve haver nenhum conteúdo na página que não esteja dentro de um elemento que expõe uma função, pois poderia causar que o conteúdo fosse tornado inacessível para usuários com leitores de tela." + }, + "guidelines": [], + "tags": [ + "aria", + "content" + ], + "callback": "ariaOrphanedContent" + }, + "basefontIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Basefont sollte nicht verwendet werden", + "en": "Basefont should not be used", + "nl": "Basefont moet niet worden gebruikt", + "pt-br": "Basefont não deve ser usado" + }, + "description": { + "de": "Der basefont-Tag ist deprecated und sollte nicht verwendet werden. Verwenden Sie besser Stylesheets.", + "en": "The basefont tag is deprecated and should not be used. Investigate using stylesheets instead.", + "nl": "The basefont-tag is afgekeurd en moet niet worden gebruikt. Gebruik in plaats hiervan stylesheets.", + "pt-br": "A tag basefont está obsoleta e não deve ser usada. Investigue usando folhas de estilo." + }, + "guidelines": [], + "tags": [ + "document", + "deprecated" + ], + "options": { + "selector": "basefont" + } + }, + "blinkIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Der \"blink\"-Tag sollte nicht verwendet werden", + "en": "The \"blink\" tag should not be used", + "nl": "De \"blink\"-tag moet niet worden gebruikt", + "pt-br": "A tag \"blink\" não deve ser usada" + }, + "description": { + "de": "Der blink-Tag sollte niemals verwendet werden.", + "en": "The blink tag should not be used. Ever.", + "nl": "Het is nooit toegestaan om de \"blink\"-tag te gebruiken.", + "pt-br": "A tag blink não deve ser usada. Nunca." + }, + "guidelines": { + "wcag": { + "2.2.2": { + "techniques": [ + "F47" + ] + } + } + }, + "tags": [ + "deprecated", + "content" + ], + "options": { + "selector": "blink" + } + }, + "blockquoteNotUsedForIndentation": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Der \"blockquote\"-Tag sollte nicht zur Einrückung verwnendet werden", + "en": "The block quote should not be used just for indentation", + "nl": "De \"blockquote\"-tag mag niet gebruikt worden om in te springen", + "pt-br": "A citação de bloco não deve ser usada apenas para indentação" + }, + "description": { + "de": "Blockquotes sind für längere Zitate gedacht und sollten nicht zum Einrücken von Text verwendet werden.", + "en": "Blockquote tags are for longer quotes, and should not be used to indent text.", + "nl": "Blockquotes zijn bedoeld voor lange stukken geciteerde tekst, en niet om tekst te laten inspringen.", + "pt-br": "As tags Blockquote são para citações mais longas e não devem ser usadas para recuar texto." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H49" + ] + } + } + }, + "tags": [ + "blockquote", + "content" + ], + "options": { + "selector": "blockquote:not(blockquote[cite])" + } + }, + "blockquoteUseForQuotations": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Für längere Zitate sollte das \"blockquote\"-Element verwendet werden", + "en": "If long quotes are in the document, use the \"blockquote\" element to mark them", + "nl": "Gebruik voor lange citaten in het document het \"blockquote\"-element", + "pt-br": "e as citações longas estiverem no documento, use o elemento \"blockquote\" para marcá-las" + }, + "description": { + "de": "Wenn längere Zitate über einen Absatz wiedergegeben werden sollen, sollte das \"blockquote\"-Element verwendet werden.", + "en": "If there is a paragraph or more of a quote, use the blockquote element to mark it as such.", + "nl": "Als er een hele alinea of meer alinea's zijn van geciteerde tekst, gebruik dan blockquote om deze als zodanig te markeren.", + "pt-br": "Se houver um parágrafo ou mais de uma citação, use o elemento blockquote para marcá-lo como tal." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H49" + ] + } + } + }, + "tags": [ + "blockquote", + "content" + ], + "callback": "blockquoteUseForQuotations" + }, + "boldIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Das \"b\"-Element (fett) wird nicht verwendet.", + "en": "The \"b\" (bold) element is not used", + "nl": "Het \"b\"-element (bold) wordt niet gebruikt", + "pt-br": "O elemento \"b \" (negrito) não é usado" + }, + "description": { + "de": "Das b-Element bietet keine Hervorhebung für sehbehinderte Benutzer. Das strong-Element sollte anstelle dessen verwendet werden.", + "en": "The b (bold) element provides no emphasis for non-sighted readers. Use the strong tag instead.", + "nl": "Het b-element voorziet niet in nadruk voor blinde en slechtziende gebruikers. Gebruik de strong-tag instead.", + "pt-br": "O elemento b (negrito) não dá ênfase aos leitores não-videntes. Use a tag strong em vez disso." + }, + "guidelines": [], + "tags": [ + "semantics", + "content" + ], + "options": { + "selector": "bold" + } + }, + "buttonHasName": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Schaltflächen sollten beschriftet sein", + "en": "Button should contain text", + "nl": "Een knop moet tekst bevatten", + "pt-br": "Botões devem conter texto" + }, + "description": { + "de": "Schaltflächen sollten eine Beschriftung im Element oder im \"value\"-Attribut enthalten.", + "en": "Buttons should contain a text value within the element, or have a value attribute.", + "nl": "Knoppen moeten een tekstwaarde binnen het element hebben, of een waarde-attribuut.", + "pt-br": "Os botões devem conter um valor de texto dentro do elemento ou ter um atributo de valor." + }, + "guidelines": { + "wcag": { + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "4.1.2": { + "techniques": [ + "H91" + ] + } + } + }, + "tags": [ + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "content": true, + "empty": true, + "attribute": "title", + "selector": "button" + } + }, + "checkboxHasLabel": { + "type": "label", + "testability": 1, + "title": { + "de": "Alle Checkboxen muüssen ein dazugehöriges Label haben", + "en": "All checkboxes must have a corresponding label", + "nl": "Alle keuzevakjes moeten een bijbehorend label hebben", + "pt-br": "Todas as caixas de seleção devem ter um rótulo correspondente" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"checkbox\" sollten ein dazugehöriges label-Element haben. Bildschirmleseprogramme haben oftmals eine Formular-Modus, in dem nur Labels vorgelesen werden.", + "en": "All input elements with a type of \"checkbox\" should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user", + "nl": "Alle input-elementen met een \"keuzevakje\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen.", + "pt-br": "Todos os elementos input com um tipo de \"checkbox\" devem ter um elemento label correspondente. Os leitores de tela costumam entrar em um \"modo de formulário\" onde somente o texto do rótulo é lido em voz alta para o usuário" + }, + "guidelines": { + "508": [ + "c" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44", + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "label" + ], + "options": { + "selector": "input[type=checkbox]" + } + }, + "checkboxLabelIsNearby": { + "type": "labelProximity", + "testability": 0.5, + "title": { + "de": "Alle \"checkbox\"-Elemente sollten ein dazugehöriges Label haben", + "en": "All \"checkbox\" input elements have a label that is close", + "nl": "Van alle \"keuzevakjes\" invoerelementen staat het label in de buurt", + "pt-br": "Todos os elementos de entrada \"checkbox\" têm um rótulo que está próximo" + }, + "description": { + "de": "Alle Eingabeelemente vom Typ \"checkbox\" müssen ein dazugehöriges Label haben das in der Nähe des Eingabeelements positioniert ist. Nutzer von Bildschirmvergrößerung oder mit reduzierten räumlichen Fähigkeiten werden bei der Verwendung eines Formularelements behindert, wenn das Label für das Element weit entfernt positioniert ist.", + "en": "All input elements of type \"checkbox\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.", + "nl": "Alle inputelementen van het type \"keuzevakje\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat.", + "pt-br": "Todos os elementos de entrada do tipo \"checkbox\" devem ter um rótulo correspondente próximo ao elemento do formulário. Usuários de ampliação de tela ou com habilidades espaciais reduzidas são impedidos de usar um elemento de formulário se o rótulo para esse elemento estiver localizado longe." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "labelProximity" + ], + "options": { + "selector": "input[type=checkbox]" + } + }, + "closingTagsAreUsed": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle schließenden Tags müssen vorhanden sein, wenn erforderlich", + "en": "All tags that require closing tags have closing tags", + "nl": "Alle tags die een afsluitende tag behoeven, hebben een afsluitende tag", + "pt-br": "Todas as tags que exigem tags de fechamento têm tags de fechamento" + }, + "description": { + "de": "Wenn Tags wie p, ul or li verwendet werden, müssen sie einen schließenden Tag besitzen.", + "en": "When using tags such as p, ul, or li, there must be a closing tag.", + "nl": "Gebruik voor tags als p, ul, of li altijd een afsluitende tag, dus /p, /ul, /li.", + "pt-br": "Ao usar tags como p, ul ou li, deve haver uma tag de fechamento, tal como /p, /ul, /li." + }, + "guidelines": { + "wcag": { + "4.1.1": { + "techniques": [ + "H74" + ] + } + } + }, + "tags": [ + "html" + ], + "components": [ + "htmlSource" + ], + "callback": "closingTagsAreUsed" + }, + "contentPositioningShouldNotChangeMeaning": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Bedeutung sollte nicht über Positionierung gegeben werden", + "en": "Meaning should not be created through positioning", + "nl": "Cre�er geen betekenis door positionering", + "pt-br": "O significado não deve ser criado através do posicionamento" + }, + "description": { + "de": "Die Position sollte nicht die Bedeutung eines Elementes verändern.", + "en": "Positioning should not be used to change the meaning of an element.", + "nl": "Positionering moet niet worden gebruikt om de betekenis van een element te veranderen.", + "pt-br": "O posicionamento não deve ser usado para alterar o significado de um elemento." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "C6", + "F1", + "G57" + ] + }, + "1.4.5": { + "techniques": [ + "C6" + ] + }, + "1.4.9": { + "techniques": [ + "C6" + ] + }, + "2.4.1": { + "techniques": [ + "C6" + ] + } + } + }, + "tags": [ + "content", + "structure" + ], + "callback": "contentPositioningShouldNotChangeMeaning" + }, + "cssDocumentMakesSenseStyleTurnedOff": { + "type": "selector", + "testability": 0, + "title": { + "de": "Das Dokument muss lesbar sein, wenn die Stylesheets deaktiviert sind", + "en": "The document must be readable with styles turned off", + "nl": "Het document moet leesbaar zijn met stijlen uit", + "pt-br": "O documento deve ser legível com estilos desligados" + }, + "description": { + "de": "Wenn alle Stylesheets für die Seite deaktiviert sind, sollte der Inhalt der Seite immernoch sinnvoll sein. Versuchen Sie die Stylesheets zu deaktivieren und prüfen Sie, ob der Inhalt dann noch lesbar und verständlich ist.", + "en": "With all the styles for a page turned off, the content of the page should still make sense. Try to turn styles off in the browser and see if the page content is readable and clear.", + "nl": "Als alle stijlen voor een pagina zijn uitgezet, moet de content van de pagina nog steeds betekenisvol zijn. Zet stijlen uit in de browser en controleer of de content op de pagina nog steeds leesbaar en duidelijk is.", + "pt-br": "Com todos os estilos para uma página desativada, o conteúdo da página ainda deve fazer sentido. Tente desativar estilos no navegador e verifique se o conteúdo da página é legível e claro." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "G140" + ] + }, + "1.4.5": { + "techniques": [ + "G140" + ] + }, + "1.4.9": { + "techniques": [ + "G140" + ] + } + } + }, + "tags": [ + "color" + ], + "options": { + "selector": "link[rel=stylesheet], stylesheet, *[style]" + } + }, + "colorFontContrast": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Elemente sollten einen angemessenen Farbkontrast haben", + "en": "All elements should have appropriate color contrast", + "nl": "Alle elementen moeten een toepasselijk kleurcontract hebben", + "pt-br": "Todos os elementos devem ter o contraste de cor apropriado" + }, + "description": { + "de": "Für farbenblinde Benutzer sollten alle Texte und andere Elemente ein Kontrastverhältnis von 5:1 haben.", + "en": "For users who have color blindness, all text or other elements should have a color contrast of 5:1.", + "nl": "Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1.", + "pt-br": "Para usuários que têm daltonismo, todos os textos ou outros elementos devem ter um contraste de cor de 5:1." + }, + "guidelines": { + "wcag": { + "1.4.3": { + "techniques": [ + "G18" + ] + } + } + }, + "tags": [ + "color" + ], + "callback": [ + "colorFontContrast" + ], + "options": { + "algorithm": "wcag", + "selector": "*", + "gradientSampleMultiplier": 3 + } + }, + "colorElementBehindContrast": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Elemente sollten einen angemessenen Farbkontrast haben", + "en": "All elements should have appropriate color contrast", + "nl": "Alle elementen moeten een toepasselijk kleurcontract hebben", + "pt-br": "Todos os elementos devem ter o contraste de cor apropriado" + }, + "description": { + "de": "Für farbenblinde Benutzer sollten alle Texte und andere Elemente ein Kontrastverhältnis von 5:1 haben.", + "en": "For users who have color blindness, all text or other elements should have a color contrast of 5:1.", + "nl": "Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1.", + "pt-br": "Para usuários que têm daltonismo, todos os textos ou outros elementos devem ter um contraste de cor de 5:1." + }, + "guidelines": { + "wcag": { + "1.4.3": { + "techniques": [ + "G18" + ] + } + } + }, + "tags": [ + "color" + ], + "callback": [ + "colorElementBehindContrast" + ], + "options": { + "algorithm": "wcag", + "selector": "*", + "gradientSampleMultiplier": 3 + } + }, + "colorBackgroundImageContrast": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Elemente sollten einen angemessenen Farbkontrast haben", + "en": "All elements should have appropriate color contrast", + "nl": "Alle elementen moeten een toepasselijk kleurcontract hebben", + "pt-br": "Todos os elementos devem ter o contraste de cor apropriado" + }, + "description": { + "de": "Für farbenblinde Benutzer sollten alle Texte und andere Elemente ein Kontrastverhältnis von 5:1 haben.", + "en": "For users who have color blindness, all text or other elements should have a color contrast of 5:1.", + "nl": "Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1.", + "pt-br": "Para usuários que têm daltonismo, todos os textos ou outros elementos devem ter um contraste de cor de 5:1." + }, + "guidelines": { + "wcag": { + "1.4.3": { + "techniques": [ + "G18" + ] + } + } + }, + "tags": [ + "color" + ], + "callback": [ + "colorBackgroundImageContrast" + ], + "options": { + "algorithm": "wcag", + "selector": "*", + "gradientSampleMultiplier": 3 + } + }, + "colorElementBehindBackgroundImageContrast": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Elemente sollten einen angemessenen Farbkontrast haben", + "en": "All elements should have appropriate color contrast", + "nl": "Alle elementen moeten een toepasselijk kleurcontract hebben", + "pt-br": "Todos os elementos devem ter o contraste de cor apropriado" + }, + "description": { + "de": "Für farbenblinde Benutzer sollten alle Texte und andere Elemente ein Kontrastverhältnis von 5:1 haben.", + "en": "For users who have color blindness, all text or other elements should have a color contrast of 5:1.", + "nl": "Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1.", + "pt-br": "Para usuários que têm daltonismo, todos os textos ou outros elementos devem ter um contraste de cor de 5:1." + }, + "guidelines": { + "wcag": { + "1.4.3": { + "techniques": [ + "G18" + ] + } + } + }, + "tags": [ + "color" + ], + "callback": [ + "colorElementBehindBackgroundImageContrast" + ], + "options": { + "algorithm": "wcag", + "selector": "*", + "gradientSampleMultiplier": 3 + } + }, + "colorBackgroundGradientContrast": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Elemente sollten einen angemessenen Farbkontrast haben", + "en": "All elements should have appropriate color contrast", + "nl": "Alle elementen moeten een toepasselijk kleurcontract hebben", + "pt-br": "Todos os elementos devem ter o contraste de cor apropriado" + }, + "description": { + "de": "Für farbenblinde Benutzer sollten alle Texte und andere Elemente ein Kontrastverhältnis von 5:1 haben.", + "en": "For users who have color blindness, all text or other elements should have a color contrast of 5:1.", + "nl": "Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1.", + "pt-br": "Para usuários que têm daltonismo, todos os textos ou outros elementos devem ter um contraste de cor de 5:1." + }, + "guidelines": { + "wcag": { + "1.4.3": { + "techniques": [ + "G18" + ] + } + } + }, + "tags": [ + "color" + ], + "callback": [ + "colorBackgroundGradientContrast" + ], + "options": { + "algorithm": "wcag", + "selector": "*", + "gradientSampleMultiplier": 3 + } + }, + "colorElementBehindBackgroundGradientContrast": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Elemente sollten einen angemessenen Farbkontrast haben", + "en": "All elements should have appropriate color contrast", + "nl": "Alle elementen moeten een toepasselijk kleurcontract hebben", + "pt-br": "Todos os elementos devem ter o contraste de cor apropriado" + }, + "description": { + "de": "Für farbenblinde Benutzer sollten alle Texte und andere Elemente ein Kontrastverhältnis von 5:1 haben.", + "en": "For users who have color blindness, all text or other elements should have a color contrast of 5:1.", + "nl": "Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1.", + "pt-br": "Para usuários que têm daltonismo, todos os textos ou outros elementos devem ter um contraste de cor de 5:1." + }, + "guidelines": { + "wcag": { + "1.4.3": { + "techniques": [ + "G18" + ] + } + } + }, + "tags": [ + "color" + ], + "callback": [ + "colorElementBehindBackgroundGradientContrast" + ], + "options": { + "algorithm": "wcag", + "selector": "*", + "gradientSampleMultiplier": 3 + } + }, + "definitionListsAreUsed": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Eine Definitionsliste sollte zum definieren von Begriffen verwendet werden", + "en": "Use a definition list for defining terms", + "nl": "Gebruik een definition list voor definities", + "pt-br": "Use uma lista de definições para definir termos" + }, + "description": { + "de": "Das \"dl\"-Element sollte verwendet werden, um eine Definitionsliste zum definieren von Begriffen zu erstellen", + "en": "When providing a list of terms or definitions, use a definition list.", + "nl": "Wanneer er gebruik wordt gemaakt van een lijst termen of definities, gebruik hiervoor dan een definition list.", + "pt-br": "Ao fornecer uma lista de termos ou definições, use uma lista de definições." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H48" + ] + } + } + }, + "tags": [ + "structure" + ], + "callback": "definitionListsAreUsed" + }, + "doctypeProvided": { + "type": "custom", + "testability": 1, + "title": { + "de": "Das Dokument sollte eine valide \"doctype\"-Deklaration enthalten", + "en": "The document should contain a valid \"doctype\" declaration", + "nl": "Het document moet een geldige \"doctype\"-verklaring hebben", + "pt-br": "O documento deve conter uma declaração \"doctype\" válida" + }, + "description": { + "de": "Jedes Dokument muss eine valide DOCTYPE-Deklaration enthalten.", + "en": "Each document must contain a valid doctype declaration.", + "nl": "Ieder document moet een geldige doctype-verklaring hebben.", + "pt-br": "Cada documento deve conter uma declaração válida de doctype." + }, + "guidelines": [], + "tags": [ + "doctype" + ], + "callback": "doctypeProvided" + }, + "documentAbbrIsUsed": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Abkürzungen müssen mit einem \"abbr\"-Element gekennzeichnet werden", + "en": "Abbreviations must be marked with an \"abbr\" element", + "nl": "Afkortingen moeten worden gemarkeerd met een \"abbr\"-element", + "pt-br": "As abreviações devem ser marcadas com um elemento \"abbr\"" + }, + "description": { + "de": "Abkürzungen sollten mindestens einmal pro Seite mit einem abbr-Element gekennzeichnet werden.", + "en": "Abbreviations should be marked with an abbr element, at least once on the page for each abbreviation.", + "nl": "Afkortingen moeten worden gemarkeerd door middel van het abbr-element. Doe dit ten minste een keer per pagina voor elke afkorting.", + "pt-br": "As abreviações devem ser marcadas com um elemento abbr, pelo menos uma vez na página para cada abreviatura." + }, + "guidelines": { + "wcag": { + "3.1.4": { + "techniques": [ + "H28" + ] + } + } + }, + "tags": [ + "acronym", + "content" + ], + "components": [ + "acronym" + ], + "callback": "documentAbbrIsUsed" + }, + "documentAcronymsHaveElement": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Akronyme müssen mit einem \"acronym\"-Element gekennzeichnet werden", + "en": "Acronyms must be marked with an \"acronym\" element", + "nl": "Acroniemen moeten worden gemarkeerd met een \"acronym\"-element", + "pt-br": "As siglas devem ser marcadas com um elemento \"acronym\"" + }, + "description": { + "de": "Akronyme sollten mindestens einmal pro Seite mit einem acronym-Element gekennzeichnet werden.", + "en": "Acronyms should be marked with an acronym element, at least once on the page for each acronym.", + "nl": "Acroniemen moeten worden gemarkeerd door middel van het acronym-element. Doe dit ten minste een keer per pagina voor elke acroniem.", + "pt-br": "As siglas devem ser marcadas com um elemento acronym, pelo menos uma vez na página de cada acrônimo." + }, + "guidelines": { + "wcag": { + "3.1.4": { + "techniques": [ + "H28" + ] + } + } + }, + "tags": [ + "acronym", + "content" + ], + "components": [ + "acronym" + ], + "callback": "documentAcronymsHaveElement" + }, + "documentAutoRedirectNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Automatische Umleitungen per \"meta\"-Elements sind unzulässig", + "en": "Auto-redirect with \"meta\" elements must not be used", + "nl": "Auto-redirect met \"meta\"-elementen moeten niet worden gebruikt", + "pt-br": "Redirecionamento automático com elementos \"meta\" não deve ser usado" + }, + "description": { + "de": "Weil Benutzer unterschiedliche Geschwindigkeiten und Fähigkeiten haben, wenn es um das Erfassen von Inhalten einer Seite geht, kann die \"meta-refresh\"-Methode dazu führen, dass Benutzer den Inhalt nicht vollständig verstehen, bevor sie umgeleitet werden.", + "en": "Because different users have different speeds and abilities when it comes to parsing the content of a page, a \"meta-refresh\" method to redirect users can prevent users from fully understanding the document before being redirected.", + "nl": "Omdat verschillende gebruikers verschillende snelheden en vaardigheden hebben met het scannen van content op een pagina, kan een \"meta-refresh\"-methode om gebruikers door te sturen hen verhinderen het document volledig te begrijpen voor ze worden doorgestuurd.", + "pt-br": "Como diferentes usuários têm diferentes velocidades e habilidades quando se trata de analisar o conteúdo de uma página, um método de \"meta-atualização\" para redirecionar os usuários pode impedir que os usuários entendam completamente o documento antes de serem redirecionados." + }, + "guidelines": [], + "tags": [ + "document" + ], + "options": { + "selector": "meta[http-equiv=refresh]" + } + }, + "documentContentReadableWithoutStylesheets": { + "type": "selector", + "testability": 0, + "title": { + "de": "Inhalte sollten ohne Stylesheets lesbar sein", + "en": "Content should be readable without style sheets", + "nl": "Content moet zonder stylesheets leesbaar zijn", + "pt-br": "O conteúdo deve ser legível sem folhas de estilo" + }, + "description": { + "de": "Wenn alle Stylesheets für die Seite deaktiviert sind, sollte der Inhalt der Seite immernoch sinnvoll sein. Versuchen Sie die Stylesheets zu deaktivieren und prüfen Sie, ob der Inhalt dann noch lesbar und verständlich ist.", + "en": "With all the styles for a page turned off, the content of the page should still make sense. Try to turn styles off in the browser and see if the page content is readable and clear.", + "nl": "Ook als alle stijlen voor een pagina zijn uitgezet, moet de content van de pagina nog steeds betekenisvol zijn. Zet de stylesheets uit in de browser en controleer of de content nog steeds leesbaar en duidelijk is.", + "pt-br": "Com todos os estilos para uma página desativada, o conteúdo da página ainda deve fazer sentido. Tente desativar estilos no navegador e verifique se o conteúdo da página é legível e claro." + }, + "guidelines": { + "508": [ + "d" + ], + "wcag": { + "1.3.1": { + "techniques": [ + "G140" + ] + }, + "1.4.5": { + "techniques": [ + "G140" + ] + }, + "1.4.9": { + "techniques": [ + "G140" + ] + } + } + }, + "tags": [ + "document", + "color" + ], + "options": { + "selector": "html:has(link[rel=stylesheet], style) body, body:has(*[style])" + } + }, + "documentHasTitleElement": { + "type": "selector", + "testability": 1, + "title": { + "de": "Das Dokument sollte ein \"title\"-Element haben", + "en": "The document should have a title element", + "nl": "Het document moet een titelelement hebben", + "pt-br": "O documento deve ter um elemento de título" + }, + "description": { + "de": "Das Dokument sollte ein \"title\"-Element haben", + "en": "The document should have a title element.", + "nl": "Het document moet een titelelement hebben.", + "pt-br": "O documento deve ter um elemento de título." + }, + "guidelines": { + "wcag": { + "2.4.2": { + "techniques": [ + "H25" + ] + } + } + }, + "tags": [ + "document", + "head" + ], + "options": { + "selector": "html:not(html:has(title))" + } + }, + "documentIDsMustBeUnique": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle \"id\"-Attribute von Elementen müssen einmalig sein", + "en": "All element \"id\" attributes must be unique", + "nl": "Alle element \"id\"-attributen moeten uniek zijn", + "pt-br": "Todos os atributos \"id\" dos elementos devem ser único" + }, + "description": { + "de": "Alle \"id\"-Attribute von Elementen müssen einmalig sein", + "en": "Element \"id\" attributes must be unique.", + "nl": "Element \"id\"-attributen moeten uniek zijn.", + "pt-br": "O atributo \"id\" de um elemento deve ser único" + }, + "guidelines": { + "wcag": { + "4.1.1": { + "techniques": [ + "F77", + "H93" + ] + } + } + }, + "tags": [ + "document", + "semantics" + ], + "callback": "documentIDsMustBeUnique" + }, + "documentIsWrittenClearly": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Das Dokument sollte zur Zielgruppe passend geschrieben und deutlich lesbar sein.", + "en": "The document should be written to the target audience and read clearly", + "nl": "Het document moet geschreven zijn op het niveau van de doelgroep", + "pt-br": "O documento deve ser escrito para o público-alvo e ser lido claramente" + }, + "description": { + "de": "Bei umfangreichen Dokumenten sollte eine Zusammenfassung oder eine Hilfestellung für das Verständnis des Inhalts vorgesehen werden.", + "en": "If a document is beyond a 10th grade level, then a summary or other guide should also be provided to guide the user through the content.", + "nl": "Als de inhoud van een document moeilijker is dan het vastgestelde taalniveau, moet een samenvatting of andere begeleiding worden toegevoegd om de gebruiker te helpen met de content.", + "pt-br": "Se um documento está além de um nível de 10ª série, um guia de resumo ou outro também deve ser fornecido para orientar o usuário através do conteúdo." + }, + "guidelines": { + "wcag": { + "3.1.5": { + "techniques": [ + "G86" + ] + } + } + }, + "tags": [ + "language", + "content" + ], + "components": [ + "textStatistics" + ], + "callback": "documentIsWrittenClearly" + }, + "documentLangIsISO639Standard": { + "type": "custom", + "testability": 1, + "title": { + "de": "Das \"language\"-Attribut des Dokumentes sollte einen ISO-Standardcode enthalten", + "en": "The document's language attribute should be a standard code", + "nl": "Het language-attribuut van het document moet een standaard code zijn", + "pt-br": "O atributo de idioma do documento deve ser um código padrão" + }, + "description": { + "de": "Das Dokument sollte eine Standardsprache haben und diese Sprache sollte durch einen validen 2- bis 3-stelligen Sprachcode nach ISO 639 gekennzeichnet sein.", + "en": "The document should have a default langauge, and that language should use the valid 2 or 3 letter language code according to ISO specification 639.", + "nl": "Het document moet een standaardtaal hebben en die taal moet de geldige 2- of 3-letterige taalcode hebben volgens de ISO-specificatie 639.", + "pt-br": "O documento deve ter um idioma padrão, e esse idioma deve usar o código de idioma válido de 2 ou 3 letras de acordo com a especificação ISO 639." + }, + "guidelines": { + "wcag": { + "3.1.1": { + "techniques": [ + "H57" + ] + } + } + }, + "tags": [ + "document", + "language" + ], + "strings": [ + "languageCodes" + ], + "callback": "documentLangIsISO639Standard" + }, + "documentLangNotIdentified": { + "type": "selector", + "testability": 1, + "title": { + "de": "Das Dokument muss ein \"lang\"-Attribut haben.", + "en": "The document must have a \"lang\" attribute", + "nl": "Het document moet een \"lang\"-attribuut hebben", + "pt-br": "O documento deve ter um atributo \"lang\"" + }, + "description": { + "de": "Das Dokument sollte eine Standardsprache haben, die im \"lang\"-Attribut im HTML-Element gesetzt ist.", + "en": "The document should have a default language, by setting the \"lang\" attribute in the html element.", + "nl": "Het document moet een standaardtaal hebben, vastgelegd in het \"lang\"-attribuut in het html-element.", + "pt-br": "O documento deve ter um idioma padrão, definindo o atributo \"lang\" no elemento html." + }, + "guidelines": [], + "tags": [ + "document", + "language" + ], + "options": { + "selector": "html:not(html[lang])" + } + }, + "documentMetaNotUsedWithTimeout": { + "type": "selector", + "testability": 1, + "title": { + "de": "Meta-Elemente dürfen nicht zum Aktualisieren der Seite verwendet werden", + "en": "Meta elements must not be used to refresh the content of a page", + "nl": "Meta-elementen mogen niet worden gebruikt om content op een pagina te verversen", + "pt-br": "Meta elementos não devem ser usados ​​para atualizar o conteúdo de uma página" + }, + "description": { + "de": "Weil Benutzer unterschiedliche Geschwindigkeiten und Fähigkeiten haben, wenn es um das Erfassen von Inhalten einer Seite geht, kann die \"meta-refresh\"-Methode dazu führen, dass Benutzer den Inhalt nicht vollständig verstehen, bevor sie umgeleitet werden. Verwenden Sie eine \"Inhalt aktualisieren\"-Verknüpfung.", + "en": "Because different users have different speeds and abilities when it comes to parsing the content of a page, a \"meta-refresh\" method to reload the content of the page can prevent users from having full access to the content. Try to use a \"refresh this\" link instead.", + "nl": "Omdat verschillende gebruikers verschillende snelheden en vaardigheden hebben met het scannen van content op een pagina, kan een \"meta-refresh\"-methode om de pagina te herladen gebruikers hinderen in toegang tot de content. Gebruik een \"refresh this\" link hiervoor.", + "pt-br": "Como diferentes usuários têm diferentes velocidades e habilidades quando se trata de analisar o conteúdo de uma página, um método de \"meta-refresh\" para recarregar o conteúdo da página pode impedir que os usuários tenham acesso total ao conteúdo. Tente usar um link \"atualizar este\" em vez disso." + }, + "guidelines": { + "wcag": { + "2.2.1": { + "techniques": [ + "F40", + "F41" + ] + }, + "2.2.4": { + "techniques": [ + "F40", + "F41" + ] + }, + "3.2.5": { + "techniques": [ + "F41" + ] + } + } + }, + "tags": [ + "document" + ], + "options": { + "selector": "meta[http-equiv=refresh]" + } + }, + "documentReadingDirection": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Die Leserichtung des Textes ist korrekt angegeben", + "en": "Reading direction of text is correctly marked", + "nl": "De leesrichting van de tekst staat juist aangegeven", + "pt-br": "Direção de leitura do texto está marcada corretamente" + }, + "description": { + "de": "Wenn notwendig, muss die Leserichtung (für Sprachen mit abweichender Leserichtung) für das Dokument oder Textabschnitte deklariert sein.", + "en": "Where required, the reading direction of the document (for language that read in different directions), or portions of the text, must be declared.", + "nl": "Voor talen die een andere leesrichting hebben, moet de leesrichting van (een deel van) de tekst in een document worden opgenomen.", + "pt-br": "Quando necessário, deve ser declarada a direção de leitura do documento (para o idioma que lê em direções diferentes) ou partes do texto." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "H34" + ] + } + } + }, + "tags": [ + "document", + "language" + ], + "options": { + "selector": "*[lang=he]:not(*[dir=rtl]), *[lang=ar]:not(*[dir=rtl])" + } + }, + "documentStrictDocType": { + "type": "custom", + "testability": 1, + "title": { + "de": "Die Seite verwendet einen strikten DOCTYPE", + "en": "The page uses a strict doctype", + "nl": "De pagina gebruikt een strikt doctype", + "pt-br": "A página usa um doctype estrito" + }, + "description": { + "de": "Der Doctype Seite sollte entweder HTML Strict oder XHTML Strict sein.", + "en": "The doctype of the page or document should be either an HTML or XHTML strict doctype.", + "nl": "Het doctype van een pagina of document moet een HTML of XHTML strikt doctype zijn.", + "pt-br": "O doctype da página ou documento deve ser um doctype HTML ou XHTML estrito." + }, + "guidelines": [], + "tags": [ + "document", + "doctype" + ], + "callback": "documentStrictDocType" + }, + "documentTitleDescribesDocument": { + "type": "selector", + "testability": 0, + "title": { + "de": "Der Titel beschreibt das Dokument", + "en": "The title describes the document", + "nl": "De titel beschrijft het document", + "pt-br": "O título descreve o documento" + }, + "description": { + "de": "Der Dokumententitel sollte die Seite beschreiben. Bildschirmleseprogramme verwenden oft den Titel um von Fenster zu Fenster zu navigieren.", + "en": "The document title should actually describe the page. Often, screen readers use the title to navigate from one window to another.", + "nl": "De documenttitel moet een beschrijving zijn van de pagina. Schermlezen gebruiken de titels van pagina's om van het ene naar het andere scherm te navigeren.", + "pt-br": "O título do documento deve realmente descrever a página. Muitas vezes, os leitores de tela usam o título para navegar de uma janela para outra." + }, + "guidelines": { + "wcag": { + "2.4.2": { + "techniques": [ + "F25", + "G88" + ] + } + } + }, + "tags": [ + "document", + "head" + ], + "options": { + "selector": "head title:first" + } + }, + "documentTitleIsNotPlaceholder": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Der Dokumententitel sollte kein Platzhaltertext sein", + "en": "The document title should not be placeholder text", + "nl": "De documenttitle moet geen placeholder tekst zijn", + "pt-br": "O título do documento não deve ser um texto de espaço reservado" + }, + "description": { + "de": "Der Dokumententitel sollte nicht durch einen Platzhalter verschwendet werden, der die Seite nicht beschreibt.", + "en": "The document title should not be wasted placeholder text which does not describe the page.", + "nl": "De documenttitel moet geen placeholder tekst zijn die geen goede beschrijving van de pagina is.", + "pt-br": "O título do documento não deve ser desperdiçado com um texto que não descreve a página." + }, + "guidelines": { + "wcag": { + "2.4.2": { + "techniques": [ + "F25", + "G88" + ] + } + } + }, + "tags": [ + "document", + "head" + ], + "components": [ + "placeholder" + ], + "options": { + "content": true, + "selector": "head title:first" + } + }, + "documentTitleIsShort": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Der Dokumententitel sollte kurz sein", + "en": "The document title should be short", + "nl": "De documenttitel moet kort zijn", + "pt-br": "O título do documento deve ser curto" + }, + "description": { + "de": "Der Dokumententitel sollte kurz und prägnant sein. Dieser Test schlägt bei 150 Zeichen fehl, es sollte aber als Empfehlung aufgefasst werden.", + "en": "The document title should be short and succinct. This test fails at 150 characters, but authors should consider this to be a suggestion.", + "nl": "De documenttitel moet kort en beknopt zijn. Probeer bij een titel langer dan 150 tekense de titel in te korten waar mogelijk.", + "pt-br": "O título do documento deve ser curto e sucinto. Este teste falha em 150 caracteres, mas os autores devem considerar isso como uma sugestão." + }, + "guidelines": [], + "tags": [ + "document", + "head" + ], + "callback": "documentTitleIsShort" + }, + "documentTitleNotEmpty": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Das Dokument sollte keinen leeren Titel haben", + "en": "The document should not have an empty title", + "nl": "Het document mag geen lege titel hebben", + "pt-br": "O documento não deve ter um título vazio" + }, + "description": { + "de": "Das Dokument sollte einen Titel haben, der nicht leer ist.", + "en": "The document should have a title element that is not white space.", + "nl": "Het document moet een titelelement hebben dat is ingevuld.", + "pt-br": "O documento deve ter um elemento de título que não seja espaço em branco." + }, + "guidelines": { + "wcag": { + "2.4.2": { + "techniques": [ + "F25", + "H25" + ] + } + } + }, + "tags": [ + "document", + "head" + ], + "components": [ + "placeholder" + ], + "options": { + "content": true, + "empty": true, + "selector": "head title" + } + }, + "documentValidatesToDocType": { + "type": "custom", + "testability": 1, + "title": { + "de": "Das Dokument muss sich gegen den Doctype validieren lassen", + "en": "Document must validate to the doctype", + "nl": "Het document moet valideren met het doctype", + "pt-br": "Documento deve validar para o doctype" + }, + "description": { + "de": "Das Dokument muss sich gegen den Doctype validieren lassen", + "en": "The document must validate to the declared doctype.", + "nl": "Het document moet valideren met het vastgestelde doctype.", + "pt-br": "O documento deve validar para o doctype declarado." + }, + "guidelines": { + "wcag": { + "4.1.1": { + "techniques": [ + "G134" + ] + } + } + }, + "tags": [ + "document", + "doctype" + ], + "callback": "documentValidatesToDocType" + }, + "documentVisualListsAreMarkedUp": { + "type": "custom", + "testability": 1, + "title": { + "de": "Listen sollten mittels Listen-Markup deklariert werden", + "en": "Lists of items should be marked using list markup", + "nl": "Lijsten moeten gemarkeerd worden als ordered of unordered lists", + "pt-br": "As listas de itens devem ser marcadas usando marcação de lista" + }, + "description": { + "de": "Verwende die sortierten (ol) oder unsortierten (ul) Listen-Elemente anstelle von Zeilenumbrüchen mit Zahlen oder Zeichen, um Listen auszuzeichen.", + "en": "Use the ordered or unordered (bulleted) list elements to mark lists instead of using new lines that start with numbers or other characters to create a visual list.", + "nl": "Gebruik ordered (ol) of unordered (ul) elementen voor lijsten, in plaats van een nieuwe regel per item aan te maken die je laat beginnen met een nummer of teken om een visuele lijst te maken.", + "pt-br": "Use os elementos de lista ordenados ou não ordenados (com marcadores) para marcar listas em vez de usar novas linhas que começam com números ou outros caracteres para criar uma lista visual." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H28", + "H48", + "T2" + ] + } + } + }, + "tags": [ + "list", + "semantics", + "content" + ], + "strings": [ + "symbols" + ], + "callback": "documentVisualListsAreMarkedUp" + }, + "domOrderMatchesVisualOrder": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Stellen Sie sicher, dass die visuelle Reihenfolge der Seitet mit dem DOM übereinstimmt", + "en": "Ensure that the visual order of the page matches the DOM", + "nl": "Zorg ervoor dat de visuele ordening van de pagina overeenkomt met de DOM", + "pt-br": "Certifique-se de que a ordem visual da página corresponde ao DOM" + }, + "description": { + "de": "Wenn Sie Positionierungstechniken verwenden, stellen Sie sicher, dass die visuelle Reihenfolge mit dem Document Object Model (DOM) übereinstimmt.", + "en": "When using positioning techniques, make sure that the visual order of the page matches the DOM.", + "nl": "Wanneer je gebruik maakt van positioneringstechnieken, zorg er dan voor dat de visuele ordening van de pagina overeenkomt met de DOM.", + "pt-br": "Ao usar técnicas de posicionamento, certifique-se de que a ordem visual da página corresponde ao DOM." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "C27" + ] + }, + "2.4.3": { + "techniques": [ + "C27" + ] + } + } + }, + "tags": [ + "content" + ], + "options": { + "selector": "*:quailCss(position=absolute), *:quailCss(position=fixed), *:quailCss(float=right), *:quailCss(float=left)" + } + }, + "elementAttributesAreValid": { + "type": "custom", + "testability": 1, + "title": { + "de": "Attribute sollten mit Leerzeichen getrennt sein und in Anführungszeichen gesetzt werden", + "en": "Attributes should have spaces between them and be wrapped in quotes", + "nl": "Attributen moeten gescheiden zijn door spaties en ze moeten tussen aanhalingstekens", + "pt-br": "Os atributos devem ter espaços entre eles e ser envolvidos entre aspas" + }, + "description": { + "de": "Attribute sollten mit Leerzeichen getrennt sein und in Anführungszeichen gesetzt werden.", + "en": "Attributes should have spaces between them and be wrapped in quotes", + "nl": "Tussen attributen moeten spaties staan en ze moeten tussen aanhalingstekens staan.", + "pt-br": "Os atributos devem ter espaços entre eles e ser envolvidos entre aspas" + }, + "guidelines": { + "4.1.1": { + "techniques": [ + "F70" + ] + } + }, + "callback": "elementAttributesAreValid" + }, + "embedHasAssociatedNoEmbed": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle \"embed\"-Elemente sollten ein zugehöriges \"noembed\"-Element haben", + "en": "All \"embed\" elements have an associated \"noembed\" element", + "nl": "Alle \"embed\" elementen moeten een bijbehorend \"noembed\"-element hebben", + "pt-br": "Todos os elementos \"embed\" têm um elemento \"noembed\" associado" + }, + "description": { + "de": "Da einige Benutzer das embed-Element nicht verwenden können, sollte eine alternativer Inhalt im noembed-Element angeboten werden.", + "en": "Because some users cannot use the embed element, provide alternative content in a noembed element.", + "nl": "Sommige gebruikers kunnen het embed-element niet gebruiken. Biedt hiervoor alternatieve content aan in een noembed-element.", + "pt-br": "Como alguns usuários não podem usar o elemento embed , forneça conteúdo alternativo em um elemento noembed ." + }, + "guidelines": [], + "tags": [ + "object", + "embed", + "content" + ], + "callback": "embedHasAssociatedNoEmbed" + }, + "elementsDoNotHaveDuplicateAttributes": { + "type": "custom", + "testability": 1, + "title": { + "de": "Elemente sollten keine doppelten Attribute haben", + "en": "Elements should not have duplicate attributes", + "nl": "Elementen mogen geen dubbele attributen hebben", + "pt-br": "Elementos não devem ter atributos duplicados" + }, + "description": { + "de": "Elemente sollten jedes Attribut nur einmal haben. Zum Beispiel dürfen keine zwei \"class\"-Attribute vorhanden sein.", + "en": "Elements should only have one type of any attribute. For example, there should not be two 'class' attributes.", + "nl": "Elementen mogen maar een type attribuut hebben. Er mogen bijvoorbeeld niet twee 'class'-attributen zijn.", + "pt-br": "Os elementos devem ter apenas um tipo de qualquer atributo. Por exemplo, não deve haver dois atributos 'class'." + }, + "guidelines": [], + "tags": [ + "content" + ], + "components": [ + "htmlSource" + ], + "libraries": [ + "node-htmlparser/lib/htmlparser.js" + ], + "callback": "elementsDoNotHaveDuplicateAttributes" + }, + "embedMustHaveAltAttribute": { + "type": "selector", + "testability": 1, + "title": { + "de": "\"Embed\"-Elemente müssen ein \"alt\"-Attribut haben", + "en": "\"Embed\" elements must have an \"alt\" attribute", + "nl": "\"Embed\"-elementen moeten een \"alt\"-attribuut hebben", + "pt-br": "Elementos \"embed\" devem ter um atributo \"alt\"" + }, + "description": { + "de": "Alle embed-Elemente müssen ein \"alt\"-Attribut haben.", + "en": "All embed elements must have an \"alt\" attribute.", + "nl": "Alle embed-elementen moeten een \"alt\"-attribuut hebben.", + "pt-br": "Todos os elementos embed devem ter um atributo \"alt\"." + }, + "guidelines": [], + "tags": [ + "object", + "embed", + "content" + ], + "options": { + "selector": "embed:not([alt])" + } + }, + "embedMustNotHaveEmptyAlt": { + "type": "selector", + "testability": 1, + "title": { + "de": "\"Embed\"-Elemente dürfen kein leeres \"alt\"-Attribut haben", + "en": "\"Embed\" elements cannot have an empty \"alt\" attribute", + "nl": "\"Embed\"-elementen mogen geen leeg \"alt\"-attribuut hebben", + "pt-br": "Elementos \"embed\" não podem ter um atributo \"alt\" vazio" + }, + "description": { + "de": "Alle embed-Elemente müssen ein \"alt\"-Attribut haben, dass nicht leer ist.", + "en": "All embed elements must have an \"alt\" attribute that is not empty.", + "nl": "Alle embed-elementen moeten een gevuld \"alt\"-attribuut hebben.", + "pt-br": "Todos os elementos embed devem ter um atributo \"alt\" que não esteja vazio." + }, + "guidelines": [], + "tags": [ + "object", + "embed", + "content" + ], + "options": { + "selector": "embed[alt=]" + } + }, + "embedProvidesMechanismToReturnToParent": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle embed-Elemente sollten einen Weg für Tastatur-Benutzern bieten das Element zu verlassen.", + "en": "All embed elements should provide a way for keyboard users to escape", + "nl": "Alle embed-elementen moeten een manier bieden voor toetsenbordgebruikers om het element te verlaten", + "pt-br": "Todos os elementos 'embed' devem fornecer uma maneira para que os usuários do teclado escapem" + }, + "description": { + "de": "Stellen Sie sicher, dass embed-Elemente einem Tastatur-Benutzer die Möglichkeit zum Verlassen des Elements bietet. Dies muss manuell getestet werden.", + "en": "Ensure that a user who has only a keyboard as an input device can escape an embed element. This requires manual confirmation.", + "nl": "Zorg ervoor dat een gebruiker die alleen het toetsenbord gebruikt voor bediening een embed-element kan verlaten. Hiervoor is handmatige bevestiging nodig.", + "pt-br": "Certifique-se de que um usuário que tenha apenas um teclado como um dispositivo de entrada pode escapar de um elemento embed. Isso requer confirmação manual." + }, + "guidelines": { + "wcag": { + "2.1.2": { + "techniques": [ + "G21" + ] + } + } + }, + "tags": [ + "object", + "embed", + "content" + ], + "options": { + "selector": "embed" + } + }, + "emoticonsExcessiveUse": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Emoticons sollten nicht exzessiv genutzt werden", + "en": "Emoticons should not be used excessively", + "nl": "Gebruik emoticons spaarzaam", + "pt-br": "Emoticons não devem ser usados ​​excessivamente" + }, + "description": { + "de": "Emoticons sollten nicht exzessiv genutzt werden um Gefühle oder Inhalte zu kommunizieren. Versuchen Sie das Dokument so umzuschreiben, dass der Inhalt durch den Text wiedergegeben wird oder betten Sie Emoticons in ein abbr-Element ein. Emoticons werden von Bildschirmleseprogrammen nicht vorgelesen und werden oft verwendet, um Gefühle mitzuteilen, die für das Verständnis des Dokuments notwendig sind.", + "en": "Emoticons should not be used excessively to communicate feelings or content. Try to rewrite the document to have more textual meaning, or wrapping the emoticons in an abbr element as outlined below. Emoticons are not read by screen-readers, and are often used to communicate feelings or other things which are relevant to the content of the document.", + "nl": "Gebruik emoticons spaarzaam om gevoel of content over te brengen. Probeer het document te herschrijven in tekst, of geef de emoticons een abbr-element. Emoticons worden niet voorgelezen door schermlezers, maar bevatten wel vaak informatie die relevant is voor de content van het document.", + "pt-br": "Emoticons não devem ser usados ​​excessivamente para comunicar sentimentos ou conteúdo. Tente reescrever o documento para ter mais significado textual ou envolver os emoticons em um elemento abbr conforme descrito abaixo. Emoticons não são lidos por leitores de tela, e são freqüentemente usados ​​para comunicar sentimentos ou outras coisas que são relevantes para o conteúdo do documento." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H86" + ] + } + } + }, + "tags": [ + "language", + "emoticons", + "content" + ], + "callback": "emoticonsExcessiveUse" + }, + "emoticonsMissingAbbr": { + "type": "custom", + "testability": 1, + "title": { + "de": "Emoticons sollten Abkürzungen haben", + "en": "Emoticons should have abbreviations", + "nl": "Emoticons moeten afkortingen hebben", + "pt-br": "Emoticons devem ter abreviaturas" + }, + "description": { + "de": "Emoticons werden von Bildschirmleseprogrammen nicht vorgelesen und werden oft verwendet, um Gefühle mitzuteilen, die für das Verständnis des Dokuments notwendig sind. Wenn ein Emoticon inhaltlich relevant ist, verwenden Sie ein \"abbr\"-Element oder \"acronym\"-Element, um zu markieren.", + "en": "Emoticons are not read by screen-readers, and are often used to communicate feelings or other things which are relevant to the content of the document. If this emoticon is important content, mark it up with an \"abbr\" or \"acronym\" tag.", + "nl": "Emoticons worden niet voorgelezen door schermlezers, maar bevatten wel vaak informatie die relevant is voor de content van het document. Als een emoticon belangrijke content is, gebruik dan een \"abbr\" or \"acronym\" tag om het te markeren.", + "pt-br": "Emoticons não são lidos por leitores de tela, e são frequentemente usados ​​para comunicar sentimentos ou outras coisas que são relevantes para o conteúdo do documento. Se este emoticon for conteúdo importante, marque-o com uma tag \"abbr\" ou \"acronym\"." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H86" + ] + } + } + }, + "tags": [ + "language", + "emoticons", + "content" + ], + "callback": "emoticonsMissingAbbr" + }, + "fieldsetHasLabel": { + "type": "selector", + "testability": 1, + "title": { + "de": "Fieldsets benötigen ein \"label\"-Element", + "en": "Fieldsets require a label element", + "nl": "Fieldsets behoeven een label-element", + "pt-br": "Fieldsets requerem um elemento label" + }, + "description": { + "de": "Fieldsets zur Gruppierung von gleichen Formular-Elementen, bspw. Checkboxen, sollten ein Label haben, welches die Gruppierung beschreibt.", + "en": "Fieldsets used to group similar form elements like checkboxes should have a label that describes the group of elements.", + "nl": "Fieldsets die een groep gelijkwaardige elementen bevatten moeten een label hebben die deze groep elementen beschrijft.", + "pt-br": "Fieldsets usados ​​para agrupar elementos de formulário semelhantes como caixas de seleção devem ter um rótulo que descreve o grupo de elementos." + }, + "guidelines": { + "wcag": { + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "4.1.2": { + "techniques": [ + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "fieldset:not(fieldset:has(legend))" + } + }, + "fileHasLabel": { + "type": "label", + "testability": 1, + "title": { + "de": "Alle \"file\"-Eingabeelemente haben ein dazugehöriges Label", + "en": "All \"file\" input elements have a corresponding label", + "nl": "Alle \"file\"-invoerelementen hebben een bijbehorend label", + "pt-br": "Todos os elementos de entrada \"file\" têm um rótulo correspondente" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"file\" sollten ein dazugehöriges label-Element haben. Bildschirmleseprogramme haben oftmals eine Formular-Modus, in dem nur Labels vorgelesen werden.", + "en": "All input elements of type \"file\" should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user.", + "nl": "Alle input-elementen van het type \"file\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen.", + "pt-br": "Todos os elementos input do tipo \"file\" devem ter um elemento label correspondente. Os leitores de tela costumam entrar em um \"modo de formulário\" onde somente o texto da etiqueta é lido em voz alta para o usuário." + }, + "guidelines": { + "508": [ + "n" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "label" + ], + "options": { + "selector": "input[type=file]" + } + }, + "fileLabelIsNearby": { + "type": "labelProximity", + "testability": 0.5, + "title": { + "de": "Alle \"file\"-Eingabeelemente sollten ein dazugehöriges Label haben", + "en": "All \"file\" input elements have a label that is close", + "nl": "Van alle \"file\"-invoerelementen staat het label in de buurt", + "pt-br": "Todos os elementos de entrada \"file\" têm um rótulo que está próximo" + }, + "description": { + "de": "Alle Eingabeelemente vom Typ \"file\" müssen ein dazugehöriges Label haben das in der Nähe des Eingabeelements positioniert ist. Nutzer von Bildschirmvergrößerung oder mit reduzierten räumlichen Fähigkeiten werden bei der Verwendung eines Formularelements behindert, wenn das Label für das Element weit entfernt positioniert ist.", + "en": "All input elements of type \"file\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.", + "nl": "Alle inputelementen van het type \"file\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat.", + "pt-br": "Todos os elementos de entrada do tipo \"arquivo\" devem ter um rótulo correspondente que esteja próximo ao elemento do formulário. Usuários de ampliação de tela ou com habilidades espaciais reduzidas são impedidos de usar um elemento de formulário se o rótulo para esse elemento estiver localizado longe." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "labelProximity" + ], + "options": { + "selector": "input[type=file]" + } + }, + "focusIndicatorVisible": { + "type": "custom", + "title": { + "de": "Focus indicators müssen gut sichtbar sein", + "en": "Focus indicators have high visibility", + "nl": "Focus indicators moeten goed zichtbaar zijn", + "pt-br": "Indicadores de foco têm alta visibilidade" + }, + "description": { + "de": "Wenn ein focus indicator verwendet wird, sollte es ausreichend Kontrast zum Hintergrund aufweisen und groß genug sein, um sichtbar zu sein.", + "en": "When a focus indicator is used, it should have enough contrast with the background and big enough to be highly visible.", + "nl": "Wanneer je een focus indicator gebruikt, moet het contrast tussen de indicator en de achtergrond groot genoeg zijn in verband met de zichtbaarheid.", + "pt-br": "Quando um indicador de foco é usado, ele deve ter bastante contraste com o fundo e grande o suficiente para ser altamente visível." + }, + "guidelines": { + "wcag": { + "2.4.7": { + "techniques": [ + "C15", + "G165", + "G195" + ] + } + } + }, + "tags": [ + "focus", + "content" + ], + "components": [ + "color", + "convertToPx" + ], + "callback": "focusIndicatorVisible" + }, + "fontIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Font.Elemente sollten nicht verwendet werden", + "en": "Font elements should not be used", + "nl": "Het font element moet niet worden gebruikt", + "pt-br": "Os elementos de fonte não devem ser usados" + }, + "description": { + "de": "Der basefontbasefont tag is deprecated and should not be used. Investigate using stylesheets instead.", + "nl": "De basefont-tag is afgekeurd en moet niet worden gebruikt. Gebruik in plaats hiervan stylesheets.", + "pt-br": "A tag basefont está obsoleta e não deve ser usada. Investigue usando folhas de estilo." + }, + "guidelines": [], + "tags": [ + "deprecated", + "content" + ], + "options": { + "selector": "font" + } + }, + "formAllowsCheckIfIrreversable": { + "type": "selector", + "testability": 0, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "options": { + "selector": "form" + } + }, + "formButtonsHaveValue": { + "type": "selector", + "testability": 1, + "title": { + "de": "\"input\"-Elemente für button, submit oder reset müssen ein \"value\"-Attribut haben", + "en": "Input elements for button, submit, or reset must have a value attribute", + "nl": "Invoerelementen voor knoppen, indienen of resetten moeten een waarde-attribuut hebben", + "pt-br": "Elementos de entrada para botão, enviar ou redefinir devem ter um atributo de valor" + }, + "description": { + "de": "Jedes form-Element, welches als Schaltfläche (\"button\") angezeigt wird, muss ein lesbares \"value\"-Attribut haben.", + "en": "Any form element that is rendered as a button has to have a readable value attribute.", + "nl": "Elk invoerelement dat eruit ziet als een knop moet een leesbaar waarde-attribuut hebben.", + "pt-br": "Qualquer elemento de formulário que é processado como um botão tem que ter um atributo de valor legível." + }, + "guidelines": { + "wcag": { + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "4.1.2": { + "techniques": [ + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "input[type=button], input[type=submit], input[type=reset]", + "test": ":not([value])" + } + }, + "formDeleteIsReversable": { + "type": "selector", + "testability": 0, + "title": { + "de": "Das Löschen von Elementen über Formulare sollte rückgängig gemacht werden können", + "en": "Deleting items using a form should be reversable", + "nl": "De verwijdering van een item in een formulier moet ongedaan gemaakt kunnen worden", + "pt-br": "Excluir itens usando um formulário deve ser reversível" + }, + "description": { + "de": "Wenn ein Formular die Option bietet ein Element zu löschen, stellen Sie sicher, dass der Benutzer den Vorgang rückgängig machen kann oder den Löschvorgang zuvor bestätigen muss. Dies muss manuell getestet werden.", + "en": "Check that, if a form has the option to delete an item, that the user has a chance to either reverse the delete process, or is asked for confirmation before the item is deleted. This is not something that can be checked through automated testing and requires manual confirmation.", + "nl": "Controleer of een gebruiker de verwijdering van een invoer in een formulier ongedaan kan maken wanneer het mogelijk is om een invoer te verwijderen. Dit kan niet met een automatische test en moet handmatig gecontroleerd en bevestigd worden.", + "pt-br": "Verifique se, se um formulário tem a opção de excluir um item, que o usuário tem a chance de inverter o processo de exclusão ou é solicitada confirmação antes que o item seja excluído. Isso não é algo que pode ser verificado através de testes automatizados e requer confirmação manual." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "options": { + "selector": "form" + } + }, + "formErrorMessageHelpsUser": { + "type": "selector", + "testability": 0, + "title": { + "de": "Formulare erlauben es dem Benutzer die Eingabe zu prüfen, bevor eine irreversible Aktion durchgeführt wird", + "en": "Forms offer the user a way to check the results of their form before performing an irrevocable action", + "nl": "Formulieren bieden gebruikers de gelegenheid om hun formulier te controleren voor ze een onomkeerbare actie uitvoeren", + "pt-br": "Os formulários oferecem ao usuário uma maneira de verificar os resultados de seu formulário antes de executar uma ação irrevogável" + }, + "description": { + "de": "Wenn ein Formular eine irreversible Aktion durchführt, bspw. eine Bestellung, muss sichergestellt werden, dass der Benutzer die Eingabe vor dem Absenden des Formulars prüfen kann. Dies muss manuell getestet werden.", + "en": "If the form allows users to perform some irrevocable action, like ordreing a product, ensure that users have the ability to review the contents of the form they submitted first. This is not something that can be checked through automated testing and requires manual confirmation.", + "nl": "Als een formulier een gebruiker toestaat om een onomkeerbare actie uit te voeren, zoals het bestellen van een product, zorg er dan voor dat ze eerst het formulier kunnen controleren. Dit kan niet met een automatische test en moet handmatig gecontroleerd en bevestigd worden.", + "pt-br": "Se o formulário permitir que os usuários executem alguma ação irrevogável, como ordenar um produto, assegure-se de que os usuários tenham a capacidade de revisar o conteúdo do formulário que enviaram primeiro. Isso não é algo que pode ser verificado através de testes automatizados e requer confirmação manual." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "options": { + "selector": "form" + } + }, + "formHasGoodErrorMessage": { + "type": "selector", + "testability": 0, + "title": { + "de": "Fehlermeldungen im Formular sollten beim Ausfüllen unterstützen", + "en": "Form error messages should assist in solving errors", + "nl": "Foutmeldingen in formulieren moeten fouten helpen oplossen", + "pt-br": "Mensagens de erro de formulário devem ajudar a resolver erros" + }, + "description": { + "de": "Wenn ein Formular Pflichtfelder hat oder der Benutzer das Formular aufgrund fehlerhafter Eingaben nicht absenden kann, sollten die Fehlermeldungen verständlich sein. Verwenden Sie die Begriffe \"Pflicht\" oder \"Fehler\" im label-Element des Eingabefeldes, in dem der Fehler aufgetreten ist", + "en": "If the form has some required fields or other ways in which the user can commit an error, check that the reply is accessible. Use the words \"required\" or \"error\" within the label element of input items where the errors happened.", + "nl": "Als het formulier verplichte velden heeft of op andere manier verkeerd ingevuld kan worden, controleer dan of de bijbehorende foutmelding begrijpelijk is. Gebruik de woorden \"required\" of \"error\" in het label-element of in de invoeritems waar de fout is opgetredenitems where the errors happened.", + "pt-br": "Se o formulário tiver alguns campos obrigatórios ou outras formas pelas quais o usuário pode cometer um erro, verifique se a resposta está acessível. Use as palavras \"required\" ou \"error\" dentro do elemento label dos itens de entrada onde ocorreram os erros." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "options": { + "selector": "form" + } + }, + "formHasSubmitButton": { + "type": "selector", + "testability": 1, + "title": { + "de": "Formulare sollten eine Absenden-Schaltfläche haben", + "en": "Form should have a submit button", + "nl": "Formulieren moeten een indienknop hebben", + "pt-br": "O formulário deve ter um botão de submissão de dados" + }, + "description": { + "de": "Formulare sollten eine Schaltfläche haben, die es dem Benutzer erlaubt zu entscheiden, wann er das Formular absendet.", + "en": "Forms should have a button that allows the user to select when they want to submit the form.", + "nl": "Formulieren moeten een knop hebben waarmee de gebruiker kan bepalen wanneer zij een formulieren willen versturen.", + "pt-br": "Os formulários devem ter um botão que permite ao usuário selecionar quando deseja enviar o formulário." + }, + "guidelines": { + "wcag": { + "3.2.2": { + "techniques": [ + "H32", + "G80" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "form:not(form:has(input[type=image], input[type=submit], button[type=submit]))" + } + }, + "formWithRequiredLabel": { + "type": "custom", + "testability": 0, + "title": { + "de": "Pflichtfelder in einem Formular sollten auch im \"label\"-Element als solche markiert sein", + "en": "Input items which are required are marked as so in the label element", + "nl": "Invoervelden die verplicht zijn, zijn zo gemarkeerd in het label-element", + "pt-br": "Os itens de entrada que são necessários são marcados como tal no elemento de etiqueta" + }, + "description": { + "de": "Pflichtfelder in Formularen sollten entsprechend gekennzeichnet sein. Es sollte kein einfaches, rotes Sternchen sein sondern als \"Pflicht\" im Text markiert sein. Der Hinweis, dass ein Feld ein Pflichtfeld ist, sollte im label-Element enthalten sein.", + "en": "If a form element is required, it should be marked as so. This should not be a mere red asterisk, but instead either a 'required' image with alt text of \"required\" or the actual text \"required\". The indicator that an item is required should be included in the input element's label element.", + "nl": "Als een formulierveld verplicht is, moet het ook zichtbaar zijn. Doe dit niet alleen met een asterisk achter het veld, maar met bijvoorbeeld een afbeelding met als alttekst \"required\" of de tekst \"required\". De indicatie dat een veld verplicht is moet opgenomen zijn in het label-element van het invoerveld.", + "pt-br": "Se for necessário um elemento de formulário, ele deve ser marcado como tal. Este não deve ser um mero asterisco vermelho, mas sim uma imagem 'necessária' com texto alternativo de \"required\" ou o texto real \"required\". O indicador de que um item é necessário deve ser incluído no elemento label do elemento de entrada." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "ARIA2" + ] + }, + "1.4.1": { + "techniques": [ + "F81" + ] + }, + "3.3.2": { + "techniques": [ + "ARIA2", + "H90" + ] + }, + "3.3.3": { + "techniques": [ + "ARIA2" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "callback": "formWithRequiredLabel" + }, + "frameIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Verwenden Sie keine Frames", + "en": "Frames are not used", + "nl": "Gebruik geen frames", + "pt-br": "Os quadros não são usados" + }, + "description": { + "de": "Frames sollten nicht verwendet werden, um eine Seite zu organisieren", + "en": "Frames should not be used to organize a page.", + "nl": "Gebruik geen frames om een pagina te organiseren.", + "pt-br": "Os quadros não devem ser usados ​​para organizar uma página." + }, + "guidelines": [], + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frame" + } + }, + "frameRelationshipsMustBeDescribed": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Komplexe Framesets sollten ein \"longdesc\"-Attribut enthalten", + "en": "Complex framesets should contain a \"longdesc\" attribute", + "nl": "Complexe framesets moeten een \"longdesc\"-attribuut bevatten", + "pt-br": "Conjuntos de quadros complexos devem conter um atributo \"longdesc\"" + }, + "description": { + "de": "Wenn ein frameset drei oder mehr Frames enthält, sollte ein \"longdesc\"-Attribut verwendet werden, um den Zweck der Frames zu beschreiben.", + "en": "If a frameset contains three or more frames, use a \"longdesc\" attribute to help describe the purpose of the frames.", + "nl": "Als een frameset drie of meer frames bevat, gebruik dan een \"longdesc\"-attribuut om het doel van de frames te beschrijven.", + "pt-br": "Se um frameset contém três ou mais quadros, use um atributo \"longdesc\" para ajudar a descrever o propósito dos quadros." + }, + "guidelines": [], + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frameset:not(frameset[longdesc])" + } + }, + "framesAreUsedToGroupContent": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Verwenden Sie \"frame\"-Elemente um sich wiederholenden Inhalt darzustellen", + "en": "Use frame elements to group repeated materials", + "nl": "Gebruik frame-elementen om herhaalde content te groeperen", + "pt-br": "Use elementos de estrutura para agrupar materiais repetidos" + }, + "description": { + "de": "Wenn sich in einer Seite Inhaltsblöcke wiederholen, sollten Frames verwendet werden, um den gleichartigen Inhalt zu gruppieren", + "en": "When blocks of repeated content are used on a site, use frames to group content that is the same across pages.", + "nl": "Wanneer blokken content op een site herhaald worden, gebruik dan frames om content die op verschillende pagina's voorkomt te groeperen.", + "pt-br": "Quando blocos de conteúdo repetido são usados ​​em um site, use quadros para agrupar conteúdo que é o mesmo em páginas." + }, + "guidelines": { + "wcag": { + "2.4.1": { + "techniques": [ + "H70" + ] + } + } + }, + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "body:not(body:has(frameset))" + } + }, + "frameSrcIsAccessible": { + "type": "selector", + "testability": 0, + "title": { + "de": "Die Quelle jedes Frames sollte barrierefreien Inhalt beinhalten", + "en": "The source for each frame is accessible content.", + "nl": "De bron van elk frame is toegankelijke content.", + "pt-br": "A fonte para cada quadro é um conteúdo acessível." + }, + "description": { + "de": "Jeder Frame sollte barrierefreien Inhalt enthalten und für Bildschirmleseprogramme zugänglichen Inhalt beinhalten, bspw. als HTML anstatt von Grafiken", + "en": "Each frame should contain accessible content, and contain content accessible to screen readers, like HTML as opposed to an image.", + "nl": "Elk frame moet toegankelijke content bevatten, en content die toegankelijk is voor schermlezers, zoals HTML in tegenstelling tot een afbeelding.", + "pt-br": "Cada quadro deve conter conteúdo acessível e conter conteúdo acessível aos leitores de tela, como HTML em oposição a uma imagem." + }, + "guidelines": [], + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frame" + } + }, + "frameTitlesDescribeFunction": { + "type": "placeholder", + "testability": 0, + "title": { + "de": "Alle \"frame\"-Elemente sollten ein \"title\"-Attribut haben, dass den Zweck des Frames beschreibt", + "en": "All \"frame\" elements should have a \"title\" attribute that describes the purpose of the frame", + "nl": "Alle \"frame\" elementen moeten een \"title\"-attribuut hebben dat het doel van het frame beschrijft", + "pt-br": "Todos os elementos \"frame\" devem ter um atributo \"title\" que descreva a finalidade do frame" + }, + "description": { + "de": "Jedes frame-Element sollte ein \"title\"-Attribut haben, welches den Zweck und die Funktion des Frames beschreibt", + "en": "Each frame elements should have a \"title\" attribute which describes the purpose or function of the frame.", + "nl": "Elk frame-element moet een \"title\"-attribuut hebben dat het doel of de functie van het frame beschrijft.", + "pt-br": "Cada elemento frame deve ter um atributo \"title\" que descreva a finalidade ou função da moldura." + }, + "guidelines": { + "wcag": { + "2.4.1": { + "techniques": [ + "H64" + ] + } + } + }, + "tags": [ + "deprecated", + "frame" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "title", + "empty": true, + "selector": "frame[title], iframe[title]" + } + }, + "frameTitlesNotEmpty": { + "type": "selector", + "testability": 1, + "title": { + "de": "Frames dürfen kein leeres \"title\"-Attribut haben", + "en": "Frames cannot have empty \"title\" attributes", + "nl": "Frames mogen geen leeg \"title\"-attribuut hebben", + "pt-br": "Os quadros não podem ter atributos \"título\" vazios" + }, + "description": { + "de": "Alle frame-Elemente müssen ein korrektes \"title\"-Attribut haben", + "en": "All frame elements must have a valid \"title\" attribute.", + "nl": "Alle frame-elementen moeten een geldig \"title\"-attribuut hebben.", + "pt-br": "Todos os elementos frame devem ter um atributo \"title\" válido." + }, + "guidelines": { + "wcag": { + "2.4.1": { + "techniques": [ + "H64" + ] + }, + "4.1.2": { + "techniques": [ + "H64" + ] + } + } + }, + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frame:not(frame[title]), frame[title=''], iframe:not(iframe[title]), iframe[title='']" + } + }, + "frameTitlesNotPlaceholder": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Frames dürfen kein \"title\"-Attributes haben, dass nur aus Platzhaltertext besteht", + "en": "Frames cannot have \"title\" attributes that are just placeholder text", + "nl": "Frames mogen geen \"title\"-attribuut hebben met placeholdertekst", + "pt-br": "Os quadros não podem ter atributo \"title\" que são apenas texto de espaço reservado" + }, + "description": { + "de": "\"title\"-Attribute in Frames sollten kein einfacher Platzhaltertext wie bspw. \"frame\" enthalten.", + "en": "Frame \"title\" attributes should not be simple placeholder text like \"frame\".", + "nl": "Frame \"title\"-attributen mogen geen placeholdertekst bevatten zoals \"frame\".", + "pt-br": "Atributos \"title\" de frames não devem possuir texto simples como \"frame\"." + }, + "guidelines": { + "wcag": { + "2.4.1": { + "techniques": [ + "H64" + ] + }, + "4.1.2": { + "techniques": [ + "H64" + ] + } + } + }, + "tags": [ + "deprecated", + "frame" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "title", + "selector": "frame, iframe" + } + }, + "framesHaveATitle": { + "type": "selector", + "testability": 1, + "title": { + "de": "Alle \"frame\"-Elemente sollten ein \"title\"-Attribut haben", + "en": "All \"frame\" elements should have a \"title\" attribute", + "nl": "Alle \"frame\"-elementen moeten een \"title\"-attribuut hebben", + "pt-br": "Todos os elementos \"frame\" devem ter um atributo \"title\"" + }, + "description": { + "de": "Jedes frame-Element sollte ein \"title\"-Attribut haben.", + "en": "Each frame elements should have a \"title\" attribute.", + "nl": "Elk frame-elementen moeten een \"title\"-attribuut hebben.", + "pt-br": "Cada elemento frame deve ter um atributo \"title\"." + }, + "guidelines": { + "wcag": { + "2.4.1": { + "techniques": [ + "H64" + ] + }, + "4.1.2": { + "techniques": [ + "H64" + ] + } + } + }, + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frame:visible, iframe:visible", + "test": ":not([title])" + } + }, + "framesetIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Das \"frameset\"-Element sollte nicht verwendet werden", + "en": "The \"frameset\" element should not be used", + "nl": "Het \"frameset\"-element wordt niet gebruikt", + "pt-br": "O elemento \"frameset\" não deve ser usado" + }, + "description": { + "de": "Frames und Framesets sollten nicht verwendet werden, um Inhalt zu organisieren", + "en": "Frames and framesets should not be used to organize content.", + "nl": "Frames en framesets moeten niet gebruikt worden om content te organiseren.", + "pt-br": "Frames e framesets não devem ser usados para organizar o conteúdo." + }, + "guidelines": [], + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frameset" + } + }, + "framesetMustHaveNoFramesSection": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Alle Framesets sollten ein noframes-Element beinhalten", + "en": "All framesets should contain a noframes section", + "nl": "Alle framesets moeten een noframes-sectie bevatten", + "pt-br": "Todos os framesets devem conter uma seção noframes" + }, + "description": { + "de": "Jedes frameset sollte ein noframes-Element beinhalten, welches den Inhalt des Framesets beschreibt.", + "en": "If a frameset contains three or more frames, use a \"longdesc\" attribute to help describe the purpose of the frames.", + "nl": "Als een frameset drie of meer frames bevat, gebruik dan een \"longdesc\"-attribuut om het doel van de frames te beschrijven.", + "pt-br": "Se um frameset contém três ou mais quadros, use um atributo \"longdesc\" para ajudar a descrever o propósito dos quadros." + }, + "guidelines": [], + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frameset:not(frameset:has(noframes))" + } + }, + "headersAttrRefersToATableCell": { + "type": "custom", + "testability": 1, + "title": { + "de": "Tabellenzellen mit \"header\"-Attribut müssen eine entsprechende Datenzelle in der gleichen Tabelle mit demselben \"id\"-Attribut haben.", + "en": "Table cell headers attributes must within the same table have an associated data cell with the same id", + "nl": "Tabel cellen met een headers attribuut moeten binnen dezelfde tabel een overeenkomende data cel hebben in het id attribuut dezelfde waarde", + "pt-br": "Os atributos de cabeçalhos de tabela devem ter dentro da mesma tabela uma célula de dados associada com o mesmo id" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "headers", + "td", + "th" + ], + "callback": "headersAttrRefersToATableCell" + }, + "headerH1": { + "type": "headingLevel", + "testability": 0, + "title": { + "de": "Überschriften mit Level 1 können nur gefolgt werden von Level 2", + "en": "Header level 1 can only be followed by level 2", + "nl": "De header die volgt op een h1 is niet h3 tot h6", + "pt-br": "Cabeçalho nível 1 só pode ser seguido por nível 2" + }, + "description": { + "de": "Überschriften sollten keinen Level überspringen. Auf eine h1-Überschrift sollte kein h3, h4, h5 oder h6 folgen.", + "en": "Header order should not skip a level. Do not follow a header level 1 with a level 3, 4, 5, or 6.", + "nl": "Headers mogen geen niveau overslaan. Laat een h1-header niet volgen door een h3, h4, h5, of h6.", + "pt-br": "Ordem de cabeçalho não deve saltar um nível. Não siga um nível de cabeçalho 1 com um nível 3, 4, 5 ou 6." + }, + "guidelines": { + "wcag": { + "2.4.6": { + "techniques": [ + "G130" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "headingLevel" + ], + "options": { + "headingLevel": 1 + } + }, + "headerH1Format": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle h1-Elemente werden nicht für Formatierungen verwendet", + "en": "All h1 elements are not used for formatting", + "nl": "H1-elementen worden niet gebruikt voor formatting", + "pt-br": "Todos os elementos h1 não são usados para formatação" + }, + "description": { + "de": "Ein h1-Element darf nicht nur für Formatierung verwendet werden", + "en": "An h1 element may not be used purely for formatting.", + "nl": "Een h1-element mag niet alleen gebruikt worden voor formatting.", + "pt-br": "Um elemento h1 não pode ser usado apenas para formatação." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "T3" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "header" + ], + "options": { + "selector": "h1" + } + }, + "headerH2": { + "type": "headingLevel", + "testability": 0, + "title": { + "de": "Überschriften mit Level 2 können nicht gefolgt werden von Level 4 bis 6", + "en": "Header level 2 can not be followed by levels from 4 to 6", + "nl": "De header volgend op een h2 is geen h4, h5, of h6", + "pt-br": "O nível de cabeçalho 2 não pode ser seguido por níveis de 4 a 6" + }, + "description": { + "de": "Überschriften sollten keinen Level überspringen. Auf eine h2-Überschrift sollte kein h4, h5 oder h6 folgen.", + "en": "Header order should not skip a level. Do not follow a header level 2 with a level 4, 5, or 6.", + "nl": "Headers mogen geen niveau overslaan. Laat een h2-header niet volgen door een h4, h5, of h6.", + "pt-br": "Ordem de cabeçalho não deve saltar um nível. Não siga um nível de cabeçalho 2 com um nível 4, 5 ou 6." + }, + "guidelines": { + "wcag": { + "2.4.6": { + "techniques": [ + "G130" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "headingLevel" + ], + "options": { + "headingLevel": 2 + } + }, + "headerH2Format": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle h2-Elemente werden nicht für Formatierungen verwendet", + "en": "All h2 elements are not used for formatting", + "nl": "H2-elementen worden niet gebruikt voor formatting", + "pt-br": "Todos os elementos h2 não são usados para formatação" + }, + "description": { + "de": "Alle h2-Elemente dürfen nicht nur für Formatierung verwendet werden", + "en": "An h2 element may not be used purely for formatting.", + "nl": "Een h2-element mag niet alleen gebruikt worden voor formatting.", + "pt-br": "Um elemento h2 não pode ser usado puramente para formatação." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "T3" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "header" + ], + "options": { + "selector": "h2" + } + }, + "headerH3": { + "type": "headingLevel", + "testability": 0, + "title": { + "de": "Überschriften mit Level 3 können nicht gefolgt werden von Level 5 und 6", + "en": "Header level 3 can not be followed by levels 5 and 6", + "nl": "De header volgend op een h3 is geen h5, of h6", + "pt-br": "Cabeçalho nível 3 não pode ser seguido por níveis 5 e 6" + }, + "description": { + "de": "Überschriften sollten keinen Level überspringen. Auf eine h3-Überschrift sollte kein h5 oder h6 folgen.", + "en": "Header order should not skip a level. Do not follow a header level 3 with a level 5 or 6.", + "nl": "Headers mogen geen niveau overslaan. Laat een h3-header niet volgen door een h5, of h6.", + "pt-br": "Ordem de cabeçalho não deve saltar um nível. Não siga um nível de cabeçalho 3 com um nível 5 ou 6." + }, + "guidelines": { + "wcag": { + "2.4.6": { + "techniques": [ + "G130" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "headingLevel" + ], + "options": { + "headingLevel": 3 + } + }, + "headerH3Format": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle h3-Elemente werden nicht für Formatierungen verwendet", + "en": "All h3 elements are not used for formatting", + "nl": "H3-elementen worden niet gebruikt voor formatting", + "pt-br": "Todos os elementos h3 não são usados para formatação" + }, + "description": { + "de": "Ein h3-Element darf nicht nur für Formatierung verwendet werden", + "en": "An h3 element may not be used purely for formatting.", + "nl": "Een h3-element mag niet alleen gebruikt worden voor formatting.", + "pt-br": "Um elemento h3 não pode ser usado apenas para formatação." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "T3" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "header" + ], + "options": { + "selector": "h3" + } + }, + "headerH4": { + "type": "headingLevel", + "testability": 0, + "title": { + "de": "Überschriften mit Level 4 können nicht gefolgt werden von Level 6", + "en": "Header level 4 can not be followed by level 6", + "nl": "De header volgend op een h4 is geen h6", + "pt-br": "Cabeçalho nível 4 não pode ser seguido por nível 6" + }, + "description": { + "de": "Überschriften sollten keinen Level überspringen. Auf eine h4-Überschrift sollte kein h6 folgen.", + "en": "Header order should not skip a level. Do not follow a header level 4 with level 6 header.", + "nl": "Headers mogen geen niveau overslaan. Laat een h4/code> header niet volgen door een h6.", + "pt-br": "Ordem de cabeçalho não deve saltar um nível. Não siga um cabeçalho nível 4 com nível 6 cabeçalho." + }, + "guidelines": { + "wcag": { + "2.4.6": { + "techniques": [ + "G130" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "headingLevel" + ], + "options": { + "headingLevel": 4 + } + }, + "headerH4Format": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle h4-Elemente werden nicht für Formatierungen verwendet", + "en": "All h4 elements are not used for formatting", + "nl": "H4-elementen worden niet gebruikt voor formatting", + "pt-br": "Todos os elementos h4 não são usados para formatação" + }, + "description": { + "de": "Ein h4-Element darf nicht nur für Formatierung verwendet werden", + "en": "An h4 element may not be used purely for formatting.", + "nl": "Een h4-element mag niet alleen gebruikt worden voor formatting.", + "pt-br": "Um elemento h4 não pode ser usado apenas para formatação." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "T3" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "header" + ], + "options": { + "selector": "h4" + } + }, + "headerH5Format": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle h5-Elemente werden nicht für Formatierungen verwendet", + "en": "All h5 elements are not used for formatting", + "nl": "H5-elementen worden niet gebruikt voor formatting", + "pt-br": "Todos os elementos h5 não são usados para formatação" + }, + "description": { + "de": "Ein h5-Element darf nicht nur für Formatierung verwendet werden", + "en": "An h5 element may not be used purely for formatting.", + "nl": "Een h5-element mag niet alleen gebruikt worden voor formatting.", + "pt-br": "Um elemento h5 não pode ser usado apenas para formatação." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "T3" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "options": { + "selector": "h5" + } + }, + "headerH6Format": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle h6-Elemente werden nicht für Formatierungen verwendet", + "en": "All h6 elements are not used for formatting", + "nl": "H6-elementen worden niet gebruikt voor formatting", + "pt-br": "Todos os elementos h6 não são usados para formatação" + }, + "description": { + "de": "Ein h6-Element darf nicht nur für Formatierung verwendet werden", + "en": "An h6 element may not be used purely for formatting.", + "nl": "Een h6-element mag niet alleen gebruikt worden voor formatting.", + "pt-br": "Um elemento h6 não pode ser usado apenas para formatação." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "T3" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "options": { + "selector": "h6" + } + }, + "headerTextIsTooLong": { + "type": "custom", + "testability": 1, + "title": { + "de": "Überschriftentext ist zu lang", + "en": "Header text is too long", + "nl": "Koptekst is te lang", + "pt-br": "O texto do cabeçalho é muito longo" + }, + "description": { + "de": "Überschriften sollten keinen zu langen Text enthalten, da sie die Dokumentenstruktur zeigen sollen. Benutzer überfliegen Dokumente oft nur anhand der Überschriften.", + "en": "Headers should not contain too long text as they're designed to show document structure. Users will often skim document only by its headings.", + "nl": "Headers moet niet te lang tekst bevatten als ze zijn ontworpen om de documentstructuur te tonen. Gebruikers zullen vaak magere document alleen door zijn koppen.", + "pt-br": "Cabeçalhos não devem conter texto muito longo como eles são projetados para mostrar a estrutura do documento. Os usuários muitas vezes identificam os documentos apenas por seus títulos." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H42" + ] + } + } + }, + "tags": [ + "content" + ], + "callback": "headerTextIsTooLong" + }, + "headersHaveText": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle Überschriften sollten lesbaren Text enthalten", + "en": "All headers should contain readable text", + "nl": "Alle headers moeten leesbare tekst bevatten", + "pt-br": "Todos os cabeçalhos devem conter texto legível" + }, + "description": { + "de": "Benutzer mit Bildschirmleseprogrammen verwenden Überschriften wie Tabulatoren, um über die Seitenstruktur zu navigieren.", + "en": "Users with screen readers use headings (e.g. h1 elements), just like the tabs to navigate the structure of a page. All headings should contain either text, or images with appropriate alternative text.", + "nl": "Gebruikers van schermlezers gebruiken headers om via de structuur van een pagina te navigeren. Alle headers moeten daarom tekst bevatten of afbeeldingen met toepasselijk alt-attributen.", + "pt-br": "Os usuários com leitores de tela usam títulos (por exemplo, elementos h1), assim como as guias para navegar pela estrutura de uma página. Todos os títulos devem conter texto ou imagens com texto alternativo apropriado." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "G141" + ] + }, + "2.4.10": { + "techniques": [ + "G141" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "content": true, + "empty": true, + "selector": "h1, h2, h3, h4, h5, h6" + } + }, + "headersUsedToIndicateMainContent": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Verwenden Sie Überschriften, um den Beginn des Inhalts kenntlich zu machen", + "en": "Use header to indicate start of main content", + "nl": "Gebruik headers om de start van belangrijke content aan te geven", + "pt-br": "Usar o cabeçalho para indicar o início do conteúdo principal" + }, + "description": { + "de": "Für jeden Hauptinhaltsbereich sollte eine Überschrift vergeben werden", + "en": "For every main content area, indicate the beginning of the main content using a header.", + "nl": "Geef het begin van de belangrijkste content in elk contentvlak aan door middel van een header.", + "pt-br": "Para cada área de conteúdo principal, indique o início do conteúdo principal usando um cabeçalho." + }, + "guidelines": { + "wcag": { + "2.4.1": { + "techniques": [ + "H69" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "callback": "headersUsedToIndicateMainContent" + }, + "headersUseToMarkSections": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Verwenden Sie Überschriften, um den Anfang eines Absatz kenntlich zu machen", + "en": "Use headers to mark the beginning of each section", + "nl": "Gebruik headers om de start van elke sectie aan te geven.", + "pt-br": "Use cabeçalhos para marcar o início de cada seção" + }, + "description": { + "de": "Stellen Sie sicher, dass jeder logische Abschnitt in der Seite durch eine Überschrift abgetrennt wird.", + "en": "Check that each logical section of the page is broken or introduced with a header (h1-h6) element.", + "nl": "Controleer dat elke logische sectie van een pagina wordt onderbroken door of start met een header-element (h1-h6).", + "pt-br": "Verifique se cada seção lógica da página está quebrada ou introduzida com um elemento de cabeçalho (h1-h6)." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "G141" + ] + }, + "2.4.1": { + "techniques": [ + "G141", + "H69" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "callback": "headersUseToMarkSections" + }, + "idRefHasCorrespondingId": { + "type": "custom", + "testability": 1, + "title": { + "de": "Elemente mit einem \"idref type\"-Attribut müssen mit einem Element mit der ID korrespondieren", + "en": "Elements with an idref type attribute must correspond to an element with an ID", + "nl": "Elementen met een idref type attribuut moeten corresponderen met een element met een ID", + "pt-br": "Os elementos com um atributo de tipo idref devem corresponder a um elemento com um ID" + }, + "description": { + "de": "Wenn ein \"idref type\"-Attrbiut verwendet wird, muss das Ziel-Element mit der ID auf der Seite existieren.", + "en": "When using an idref type attribute, the target element with the ID must exist on the page.", + "nl": "Wanneer je een idref type attibuut gebruikt, moet het doelelement met dit ID ook bestaan op de pagina.", + "pt-br": "Ao usar um atributo de tipo idref, o elemento de destino com o ID deve existir na página." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F17" + ] + }, + "4.1.1": { + "techniques": [ + "F17" + ] + } + } + }, + "callback": "idRefHasCorrespondingId" + }, + "idrefsHasCorrespondingId": { + "type": "custom", + "testability": 1, + "title": { + "de": "Elemente mit einem \"idref\"-Attribut muss mit einem Element mit der ID korrespondieren", + "en": "Elements with an idref attribute must correspond to an element with an ID", + "nl": "Elementen met een idref-attribuut moeten corresponderen met een element met een ID", + "pt-br": "Os elementos com um atributo idref devem corresponder a um elemento com um ID" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F17" + ] + }, + "4.1.1": { + "techniques": [ + "F17" + ] + } + } + }, + "callback": "idrefsHasCorrespondingId" + }, + "iIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Das \"i\"-Element (italic) darf nicht verwendet werden", + "en": "The \"i\" (italic) element is not used", + "nl": "Het \"i\"-element (cursief) wordt niet gebruikt", + "pt-br": "O elemento \"i\" (itálico) não é usado" + }, + "description": { + "de": "Das i-Element (italic) bietet keine Hervorhebung für sehbehinderte Benutzer. Das em-Element sollte anstelle dessen verwendet werden", + "en": "The i (italic) element provides no emphasis for non-sighted readers. Use the em tag instead.", + "nl": "Het i-element biedt geen nadruk voor slechtziende en blinde lezers. Gebruik in plaats daarvan de em-tag.", + "pt-br": "O elemento i (itálico) não fornece ênfase para os leitores não-videntes. Em vez disso, use a tag em." + }, + "guidelines": [], + "tags": [ + "deprecated", + "content" + ], + "options": { + "selector": "i" + } + }, + "iframeMustNotHaveLongdesc": { + "type": "selector", + "testability": 1, + "title": { + "de": "Inline-Frames (\"ifframes\") sollten kein \"longdesc\"-Attribut haben", + "en": "Inline frames (\"iframes\") should not have a \"longdesc\" attribute", + "nl": "Inline frames (\"iframes\") krijgen geen \"longdesc\"-attribuut", + "pt-br": "Os frames inline (\"iframes\") não devem ter um atributo \"longdesc\"" + }, + "description": { + "de": "Inline-Frames (\"ifframes\") sollten kein \"longdesc\"-Attribut haben", + "en": "Inline frames (iframe) should not have a \"longdesc\" attribute.", + "nl": "Inline frames (\"iframes\") krijgen geen \"longdesc\"-attribuut.", + "pt-br": "Os frames inline (iframe) não devem ter um atributo \"longdesc\"." + }, + "guidelines": [], + "tags": [ + "objects", + "iframe", + "content" + ], + "options": { + "selector": "iframe[longdesc]" + } + }, + "imageMapServerSide": { + "type": "selector", + "testability": 1, + "title": { + "de": "Alle Verknüpfungen in einer server-seitigen map sollten duplizierte Verknüpfungen im Dokument haben", + "en": "All links in a server-side map should have duplicate links available in the document", + "nl": "Alle links in een server-side map moeten elders in het document terugkeren", + "pt-br": "Todos os links em um mapa do lado do servidor devem ter links duplicados disponíveis no documento" + }, + "description": { + "de": "Für jede Grafik mit einem \"usemap\"-Attribut, für eine server-seitige \"image map\", sollten die Verknüpfungen der \"image map\" an andere Stelle dupliziert haben", + "en": "Any image with an \"usemap\" attribute for a server-side image map should have the available links duplicated elsewhere.", + "nl": "Elke afbeelding met een \"usemap\"-attribuut voor een server-side map moet de beschikbare links ook elders hebben.", + "pt-br": "Qualquer imagem com um atributo \"usemap\" para um mapa de imagem do lado do servidor deve ter os links disponíveis duplicados em outro lugar." + }, + "guidelines": [], + "tags": [ + "objects", + "iframe", + "content" + ], + "options": { + "selector": "img[ismap]" + } + }, + "imgAltEmptyForDecorativeImages": { + "type": "selector", + "testability": 0, + "title": { + "de": "Wenn eine Grafik nur für dekorative Zwecke verwendet wird, muss das \"alt\"-Attribut leer sein", + "en": "If an image is purely decorative, the \"alt\" text must be empty", + "nl": "Als een afbeelding alleen ter decoratie is, moet de \"alt\"-tekst leeg zijn", + "pt-br": "Se uma imagem é puramente decorativa, o texto \"alt\" deve estar vazio" + }, + "description": { + "de": "Bei jeder Grafik, die nur dekorativen Zwecken dient (ohne Funktion oder inhaltliche Bedeutung), muss das \"alt\"-Attribut leer sein", + "en": "Any image that is only decorative (serves no function or adds to the purpose of the page content) should have an empty \"alt\" attribute.", + "nl": "Elke afbeelding die alleen ter decoratie is (en die dus geen functie heeft of bijdraagt aan het doel van een contentpagina) moet een leeg \"alt\"-attirbuut hebben.", + "pt-br": "Qualquer imagem que seja apenas decorativa (não serve a nenhuma função ou adiciona à finalidade do conteúdo da página) deve ter um atributo \"alt\" vazio." + }, + "guidelines": { + "wcag": { + "1.3.3": { + "techniques": [ + "F26" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "options": { + "selector": "img[alt]" + } + }, + "imgAltIdentifiesLinkDestination": { + "type": "selector", + "testability": 0, + "title": { + "de": "Jede Grafik mit einer Verknüpfung muss einen \"alt\"-Text haben, der das Verknüpfungsziel beschreibt", + "en": "Any image within a link must have \"alt\" text the describes the link destination", + "nl": "Elke afbeelding binnen een link moet een \"alt\"-tekst hebben die de bestemming van de link beschrijft", + "pt-br": "Qualquer imagem dentro de um link deve ter texto \"alt\" descreve o destino do link" + }, + "description": { + "de": "Jede Grafik in einer Verknüpfung muss einen \"alt\"-Text haben, der das Ziel und den Zweck der Verknüpfung beschreibt", + "en": "Any image that is within a link should have an \"alt\" attribute which identifies the destination or purpose of the link.", + "nl": "Elke afbeelding binnen link moet een \"alt\"-tekst hebben die de bestemming of het doel van de link beschrijft." + }, + "guidelines": [], + "tags": [ + "image", + "content" + ], + "options": { + "selector": "a img[alt]:first" + } + }, + "imgAltIsDifferent": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Der Alternativtext einer Grafik sollte nicht mit dem Dateinamen übereinstimmen", + "en": "Image alternative text should not be the same as the file name", + "nl": "\"Alt\"-attributen van afbeeldingen moeten niet hetzelfde zijn als de bestandsnaam", + "pt-br": "O texto alternativo da imagem não deve ser o mesmo que o nome do arquivo" + }, + "description": { + "de": "Jede Grafik sollte einen aussagekräftigen Alternativtext haben. Der Dateiname ist selten aussagekräftig und reflektiert nicht immer die in der Grafik dargestellte Information.", + "en": "All images should have a meaningful alternative text. The file name is rarely meaningful and does not always reflect information presented by the image.", + "nl": "Alle img-elementen moeten een \"alt\"-attribuut hebben dat anders is dan de bestandsnaam van de afbeelding.", + "pt-br": "Todas as imagens devem ter um texto alternativo significativo. O nome do arquivo raramente é significativo e nem sempre reflete as informações apresentadas pela imagem." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H37" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "callback": "imgAltIsDifferent" + }, + "imgAltIsSameInText": { + "type": "selector", + "testability": 0, + "title": { + "de": "Prüfen Sie, das Texte in einer Grafik auch im \"alt\"-Attribut vorhanden sind", + "en": "Check that any text within an image is also in the \"alt\" attribute", + "nl": "Controleer dat tekst in een afbeelding ook is opgenomen in het \"alt\"-attribuut", + "pt-br": "Verifique se qualquer texto dentro de uma imagem também está no atributo \"alt\"" + }, + "description": { + "de": "Wenn eine Grafik einen Text enthält, sollte dieser auch im \"alt\"-Attribut enthalten sein", + "en": "If an image has text within it, that text should be repeated in the \"alt\" attribute", + "nl": "Als een afbeelding tekst bevat, moet deze tekst herhaald worden in het \"alt\"-attribuut.", + "pt-br": "Se uma imagem tiver texto dentro dele, esse texto deve ser repetido no atributo \"alt\"" + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "G74", + "H37" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "options": { + "selector": "img" + } + }, + "imgAltIsTooLong": { + "type": "custom", + "testability": 1, + "title": { + "de": "Der Alternativtext der Grafik ist zu lang", + "en": "Image alternative text is too long", + "nl": "Altteksten voor een afbeelding zijn kort", + "pt-br": "O texto alternativo da imagem é muito longo" + }, + "description": { + "de": "Der Alternativtext der Grafik sollte kurz und prägnant sein. Es sollte geprüft werden, ob Alternativtexte die länger als 100 Zeichen sind, gekürzt werden können", + "en": "Image alternative text should be clear and concise. Alternative text longer than 100 characters should be reviewed to see if it can be shortened.", + "nl": "Alle \"alt\"-attributen voor img-elementen moeten duidelijk en bondig zijn. Verifieer \"alt\"-attributen langer dan 100 tekens en kort ze in waar mogelijk.", + "pt-br": "O texto alternativo da imagem deve ser claro e conciso. Texto alternativo com mais de 100 caracteres deve ser revisado para ver se ele pode ser encurtado." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H37" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "callback": "imgAltIsTooLong" + }, + "imgAltNotEmptyInAnchor": { + "type": "custom", + "testability": 1, + "title": { + "de": "Eine Grafik innerhalb einer Verknüpfung darf keinen leeren Alternativtext haben, wenn kein anderer Text in der Verknüpfung vorkommt", + "en": "An image within a link cannot have an empty alternative text if there is no other text within the link", + "nl": "Een afbeelding binnen een link mag geen leeg \"alt\"-attribuut hebben als er geen andere tekst is in de link", + "pt-br": "Uma imagem dentro de um link não pode ter um texto alternativo vazio se não houver outro texto dentro do link" + }, + "description": { + "de": "Jede Grafik innerhalb einer Verknüpfung ohne Verknüpfungstext, darf keinen leeren Alternativtext haben", + "en": "Any image that is within a link that has no other text cannot have an empty or missing alternative text.", + "nl": "Elke afbeelding binnen een link (een a-element) die geen andere tekst heeft, mag geen leeg of ontbrekend \"alt\"-attribuut hebben.", + "pt-br": "Qualquer imagem que esteja dentro de um link que não tenha outro texto não pode ter um texto alternativo vazio ou ausente." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "2.4.4": { + "techniques": [ + "H30" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "callback": "imgAltNotEmptyInAnchor" + }, + "imgAltNotPlaceHolder": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Grafiken sollten keinen einfachen Platzhaltertext im \"alt\"-Attribut haben", + "en": "Images should not have a simple placeholder text as an \"alt\" attribute", + "nl": "Afbeeldingen mogen geen placeholdertkest als \"alt\"-attribuut hebben", + "pt-br": "As imagens não devem ter um texto de espaço reservado simples como um atributo \"alt\"" + }, + "description": { + "de": "Jede Grafik die nicht zur Dekoration oder für das Layout verwendet wird, muss einen Alternativtext haben, der nicht nur ein Platzhalter ist", + "en": "Any image that is not used decorativey or which is purely for layout purposes cannot have an \"alt\" attribute that consists solely of placeholders.", + "nl": "Elke afbeelding die niet ter decoratie is of die alleen voor lay-out doeleinden is bedoeld, mag geen \"alt\"-attribuut hebben met daarin placeholdertekst.", + "pt-br": "Qualquer imagem que não é usada decorativamente ou que é puramente para fins de layout não pode ter um atributo \"alt\" que consiste exclusivamente em espaços reservados." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "F30", + "F39" + ] + }, + "1.2.1": { + "techniques": [ + "F30" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "alt", + "selector": "img" + } + }, + "imgAltTextNotRedundant": { + "type": "custom", + "testability": 1, + "title": { + "de": "Der Alternativtext sollten nur einmal vorkommen, außer die Grafik wird redundant verwendet", + "en": "Unless the image files are the same, no image should contain redundant alt text", + "nl": "Tenzij afbeeldingen hetzelfde zijn, mag geen enkele afbeelding dezelfde alttekst hebben", + "pt-br": "A menos que os arquivos de imagem sejam iguais, nenhuma imagem deve conter texto alternativo redundante" + }, + "description": { + "de": "Jede Grafik auf einer Seite solle einen eigenen Alternativtext haben, der sich von anderen unterscheidet, um Verwirrungen oder Redundanz zu vermeiden", + "en": "Every distinct image on a page should have it's own alt text which is different than all the others on the page to avoid redundancy and confusion.", + "nl": "Elke unieke afbeelding op een pagina moet zijn eigen alttekst hebben die anders is dan die van andere afbeeldingen op de pagina om dubbeling en verwarring te voorkomen.", + "pt-br": "Cada imagem distinta em uma página deve ter seu próprio texto alternativo que é diferente de todos os outros na página para evitar redundância e confusão." + }, + "guidelines": [], + "tags": [ + "image", + "content" + ], + "callback": "imgAltTextNotRedundant" + }, + "imgGifNoFlicker": { + "type": "custom", + "testability": 1, + "title": { + "de": "Animierte GIFs sollte nicht flackern", + "en": "Any animated GIF should not flicker", + "nl": "Geen enkele animated GIF mag knipperen of flitsen", + "pt-br": "Qualquer GIF animado não deve piscar" + }, + "description": { + "de": "Animierte GIF-Dateien sollten nicht flackern (über 2 Hz und unter 55 Hz). Sie können die Flacker-Rate der GIF mit Hilfe eines Online-Werkzeugs prüfen.", + "en": "Animated GIF files should not flicker with a frequency over 2 Hz and lower than 55 Hz. You can check the flicker rate of this GIF using an online tool.", + "nl": "Animated GIF-bestanden mogen niet knipperen of flitsen met een frequentie hoger dan 2 Hz en lager dan 55 Hz. Controleer de frequentie van deze GIF met een online tool.", + "pt-br": "Arquivos GIF animados não devem piscar com uma freqüência acima de 2 Hz e inferior a 55 Hz. Você pode verificar a taxa de cintilação deste GIF usando uma ferramenta on-line ." + }, + "guidelines": { + "508": [ + "j" + ], + "wcag": { + "2.2.2": { + "techniques": [ + "G152" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "callback": "imgGifNoFlicker" + }, + "imgHasAlt": { + "type": "selector", + "testability": 1, + "title": { + "de": "Grafiken sollten einen Alternativtext haben", + "en": "Images must provide alternative text", + "nl": "Afbeeldingselementen moeten een \"alt\"-attribuut hebben", + "pt-br": "As imagens devem fornecer texto alternativo" + }, + "description": { + "de": "Der Alternativtext sollte dieselben Informationen liefern, wie die Grafik. Dieser Text wird verwendet, wenn Grafiken im Browser deaktiviert wurden, die Grafik auf dem Server nicht gefunden werden kann oder für sehbehinderte Benutzer, die ein Bildschirmleseprogramm verwenden.", + "en": "Alternative text needs to convey the same information as the image. This text will be used when the browser has disabled images, the image was not found on the server, or by non-sighted visitors who use screen readers.", + "nl": "Alle img-elementen moeten een \"alt\"-attribuut hebben.", + "pt-br": "O texto alternativo precisa transmitir as mesmas informações da imagem. Este texto será usado quando o navegador tiver desativado imagens, a imagem não foi encontrada no servidor ou por visitantes sem visão que usam leitores de tela." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "F65", + "H37" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "options": { + "selector": "img:not(img[alt])" + } + }, + "imgHasLongDesc": { + "type": "custom", + "testability": 1, + "title": { + "de": "Ein \"longdesc\"-Attribut wird für jede Grafik benötigt, bei der die Informationen im \"alt\"-Attribut nicht ausreichend ist", + "en": "A \"longdesc\" attribute is required for any image where additional information not in the \"alt\" attribute is required", + "nl": "Een \"longdesc\"-attribuut is verplicht voor elke afbeelding waar aanvullende informatie niet benodigd is in het \"alt\"-attribuut", + "pt-br": "Um atributo \"longdesc\" é necessário para qualquer imagem onde informações adicionais que não estejam no atributo \"alt\" sejam necessárias" + }, + "description": { + "de": "Jede Grafik mit einem \"alt\"-Attribut, welches die volle Bedeutung der Grafik nicht vollständig beschreibt, muss ein \"longdesc\"-Attribut haben", + "en": "Any image that has an \"alt\" attribute that does not fully convey the meaning of the image must have a \"longdesc\" attribute.", + "nl": "Elke afbeelding die een \"alt\"-attribuut heeft dat de volledige betekenis van de afbeelding bevat, moet een \"longdesc\"-attribuut hebben.", + "pt-br": "Qualquer imagem que tenha um atributo \"alt\" que não transmita completamente o significado da imagem deve ter um atributo \"longdesc\"." + }, + "guidelines": { + "wcag": { + "2.4.4": { + "techniques": [ + "G91" + ] + }, + "2.4.9": { + "techniques": [ + "G91" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "callback": "imgHasLongDesc" + }, + "imgImportantNoSpacerAlt": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Wichtige Grafiken sollten nicht nur ein leeres \"alt\"-Attribut haben", + "en": "Images that are important should not have a purely white-space \"alt\" attribute", + "nl": "Afbeeldingen die belangrijk zijn mogen geen leeg \"alt\"-attribuut hebben", + "pt-br": "As imagens que são importantes não devem ter um atributo \"alt\" de espaço branco" + }, + "description": { + "de": "Jede Grafik, die nicht nur zur Dekoration oder für das Layout verwendet wird, sollte ein \"alt\"-Attribut haben, dass nicht nur aus Leerzeichen besteht", + "en": "Any image that is not used decorativey or which is purely for layout purposes cannot have an \"alt\" attribute that consists solely of white space (i.e. a space).", + "nl": "Elke afbeelding die niet ter decoratie is of die alleen voor lay-out doeleinden is bedoeld, mag geen leeg \"alt\"-attribuut hebben (bijvoorbeeld alleen een spatie).", + "pt-br": "Qualquer imagem que não é usada decorativamente ou que é puramente para fins de layout não pode ter um atributo \"alt\" que consiste exclusivamente em espaço em branco (ou seja, um espaço)." + }, + "guidelines": [], + "tags": [ + "image", + "content" + ], + "callback": "imgImportantNoSpacerAlt" + }, + "imgMapAreasHaveDuplicateLink": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Verknüpfungen in einer client-seitigen image map sollten duplizierte Verknüpfungen im Dokument haben", + "en": "All links within a client-side image are duplicated elsewhere in the document", + "nl": "Alle links met een client-side afbeelding moeten elders in het document terugkeren", + "pt-br": "Todos os links dentro de uma imagem do lado do cliente são duplicados em outro local do documento" + }, + "description": { + "de": "Für jede Grafik mit einem \"usemap\"-Attribut, für eine client-seitige \"image map\", sollten die Verknüpfungen der \"image map\" an andere Stelle dupliziert haben", + "en": "Any image that has a \"usemap\" attribute must have links replicated somewhere else in the document.", + "nl": "Elke afbeelding met een \"usemap\"-attribuut moet een link elders in het document hebben.", + "pt-br": "Qualquer imagem que tenha um atributo \"usemap\" deve ter links replicados em outro lugar no documento." + }, + "guidelines": { + "508": [ + "ef", + "ef" + ] + }, + "tags": [ + "image", + "imagemap" + ], + "callback": "imgMapAreasHaveDuplicateLink" + }, + "imgNonDecorativeHasAlt": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Jede nicht-dekorative Grafik sollte ein nicht-leeres \"alt\"-Attribut haben", + "en": "Any non-decorative images should have a non-empty \"alt\" attribute", + "nl": "Elke niet-decoratieve afbeelding moet een gevuld \"alt\"-attribuut hebben", + "pt-br": "Todas as imagens não-decorativas devem ter um atributo \"alt\" não vazio" + }, + "description": { + "de": "Jede Grafik die nicht zur Dekoration oder für das Layout verwendet wird, darf kein leeres \"alt\"-Attribut haben", + "en": "Any image that is not used decorativey or which is purely for layout purposes cannot have an empty \"alt\" attribute.", + "nl": "Elke afbeelding die niet ter decoratie is of voor lay-out doeleinden wordt gebruikt, moet een gevuld \"alt\"-attribuut hebben.", + "pt-br": "Qualquer imagem que não seja usada decorativamente ou que seja puramente para fins de layout não pode ter um atributo \"alt\" vazio." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "F38" + ] + } + } + }, + "tags": [ + "image", + "content" + ], + "callback": "imgNonDecorativeHasAlt" + }, + "imgNotReferredToByColorAlone": { + "type": "selector", + "testability": 0, + "title": { + "de": "Der Alternativtext einer Grafik kann nicht nur auf eine Farbe verweisen", + "en": "For any image, the \"alt\" text cannot refer to color alone", + "nl": "Voor elke afbeelding geldt dat de \"alt\"-tekst niet alleen aan kleur mag refereren", + "pt-br": "Para qualquer imagem, o texto \"alt\" não pode se referir à cor sozinho" + }, + "description": { + "de": "Der \"alt\"-Text oder Inhaltstext einer Grafik sollte nicht nur auf die Farbe der Grafik verweisen. Das \"alt\"-Attribut sollte die Bedeutung der Grafik beinhalten", + "en": "The \"alt\" text or content text for any image should not refer to the image by color alone. This is often fixed by changing the \"alt\" text to the meaning of the image", + "nl": "De \"alt\"-tekst of content voor elke afbeelding mag niet alleen maar een kleur bevatten. Neem in de \"alt\"-tekst de betekenis van de afbeelding op.", + "pt-br": "O texto \"alt\" ou texto de conteúdo para qualquer imagem não deve se referir à imagem por cor sozinho. Isso é muitas vezes corrigido alterando o texto \"alt\" para o significado da imagem" + }, + "guidelines": { + "508": [ + "c" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "F13" + ] + }, + "1.4.1": { + "techniques": [ + "F13" + ] + } + } + }, + "tags": [ + "image", + "color", + "content" + ], + "options": { + "selector": "img" + } + }, + "imgServerSideMapNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Server-seitige image maps sollten nicht verwendet werden", + "en": "Server-side image maps should not be used", + "nl": "Server-side image maps moeten niet worden gebruikt", + "pt-br": "Mapas de imagens do lado do servidor não devem ser usados" + }, + "description": { + "de": "Server-seitige image maps sollten nicht verwendet werden", + "en": "Server-side image maps should not be used.", + "nl": "Server-side image maps mogen niet worden gebruikt.", + "pt-br": "Os mapas de imagem do lado do servidor não devem ser usados." + }, + "guidelines": [], + "tags": [ + "image", + "imagemap", + "content" + ], + "options": { + "selector": "img[ismap]" + } + }, + "imgShouldNotHaveTitle": { + "type": "selector", + "testability": 1, + "title": { + "de": "Grafiken sollten kein \"title\"-Attribut haben", + "en": "Images should not have a \"title\" attribute", + "nl": "Afbeeldingen moeten geen \"title\"-attribuut hebben", + "pt-br": "As imagens não devem ter um atributo \"title\"" + }, + "description": { + "de": "Grafiken sollten kein \"title\"-Attribut haben", + "en": "Images should not contain a \"title\" attribute.", + "nl": "Afbeeldingen zouden geen \"title\"-attribuut moeten bevatten.", + "pt-br": "As imagens não devem conter um atributo \"title\"." + }, + "guidelines": [], + "tags": [ + "image", + "content" + ], + "options": { + "selector": "img[title]" + } + }, + "imgWithEmptyAlt": { + "type": "selector", + "testability": 0, + "title": { + "de": "Leere Alternativtexte sollte nur für dekorative Grafiken verwendet werden", + "en": "Use empty alternative text only for decorative images", + "nl": "", + "pt-br": "Utilize texto alternativo vazio apenas para imagens decorativas" + }, + "description": { + "de": "Leere Alternativtexte können nur für Grafiken verwendet werden, die rein dekorativ sind. Wenn eine Grafik eine Information übermitteln soll, muss ein korrekter Alternativtext gesetzt werden.", + "en": "Empty alternative text can only be used if the image serves purely decoration purposes. If the image is supposed to convey any information whatsoever, you need to set a proper alternative text for it.", + "nl": "", + "pt-br": "Texto alternativo vazio só pode ser usado se a imagem serve apenas para fins decorativos. Se a imagem é suposto para transmitir qualquer informação que seja, você precisa definir um texto alternativo adequado para ele." + }, + "guidelines": [], + "tags": [ + "image", + "content" + ], + "options": { + "selector": "img[alt=\"\"]" + } + }, + "imgWithMapHasUseMap": { + "type": "selector", + "testability": 1, + "title": { + "de": "Jede Grafik mit einem \"ismap\"-Attribut muss ein korrektes \"usemap\"-Attribut haben", + "en": "Any image with an \"ismap\" attribute have a valid \"usemap\" attribute", + "nl": "Elke afbeelding met een \"ismap\"-attribuut heeft een geldig \"usemap\"-attribuut", + "pt-br": "Qualquer imagem com um atributo \"ismap\" tem um atributo \"usemap\" válido" + }, + "description": { + "de": "Wenn eine Grafik ein \"ismap\"-Attribut hat, muss es ein korrektes \"usemap\"-Attribut haben", + "en": "If an image has an \"ismap\" attribute it must have a valid \"usemap\" attribute.", + "nl": "Als een afbeelding een \"ismap\"-attribuut heeft, moet het ook een geldig \"usemap\"-attribuut hebben", + "pt-br": "Se uma imagem tem um atributo \"ismap\" ela deve ter um atributo \"usemap\" válido." + }, + "guidelines": { + "508": [ + "ef", + "ef" + ] + }, + "tags": [ + "image", + "imagemap", + "content" + ], + "options": { + "selector": "img[ismap]:not(img[usemap])" + } + }, + "imgWithMathShouldHaveMathEquivalent": { + "type": "custom", + "testability": 0, + "title": { + "de": "Grafiken, die mathematische Gleichungen enthalten, sollten ein equivalentes MathML beinhalten", + "en": "Images which contain math equations should provide equivalent MathML", + "nl": "Afbeeldingen met wiskundige vergelijking moeten een equivalent in MathML bieden", + "pt-br": "As imagens que contêm equações matemáticas devem fornecer MathML equivalente" + }, + "description": { + "de": "Grafiken die eine mathematische Gleichung enthalten, sollten von einem MathML-Markup oder eine Verknüpfung dazu begleitet werden.", + "en": "Images which contain math equations should be accompanied or link to a document with the equivalent equation marked up with MathML.", + "nl": "Afbeeldingen die wiskundige vergelijkingen bevatten moeten vergezeld zijn van of linken naar een document met daarin een equivalent van de vergelijking in MathML.", + "pt-br": "As imagens que contêm equações matemáticas devem ser acompanhadas ou conectadas à um documento com a equação equivalente marcada com MathML ." + }, + "guidelines": [], + "tags": [ + "image", + "content" + ], + "callback": "imgWithMathShouldHaveMathEquivalent" + }, + "inputCheckboxHasTabIndex": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"checkbox\"-Eingabeelemente erfordern ein korrektes \"tabindex\"-Attribut", + "en": "All \"checkbox\" input elements require a valid \"tabindex\" attribute", + "nl": "Alle \"checkbox\"-invoerelementen moeten een geldig \"tabindex\"-attribuut hebben", + "pt-br": "Todos os elementos de entrada \"checkbox\" requerem um atributo \"tabindex\" válido" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"checkbox\" sollten ein \"tabindex\"-Attribut haben, um das Navigieren per Tastatur zu unterstützen", + "en": "All input elements of type \"checkbox\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.", + "nl": "Alle invoerelementen van het type \"checkbox\" moeten \"tabindex\"-attribuut hebben dat ervoor zorgt dat je door een formulier kunt navigeren met het toetsenbord.", + "pt-br": "Todos os elementos input do tipo \"checkbox\" devem ter um atributo \"tabindex\" para ajudar a navegar no formulário com um teclado." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "tabindex", + "empty": true, + "selector": "input[type=checkbox]" + } + }, + "inputCheckboxRequiresFieldset": { + "type": "custom", + "testability": 1, + "title": { + "de": "Logische Gruppen von Checkboxen sollten durch ein \"fieldset\" gruppiert werden", + "en": "Logical groups of check boxes should be grouped with a fieldset", + "nl": "Logische groepen van keuzevakjes moeten gegroepeerd zijn in een fieldset", + "pt-br": "Os grupos lógicos de caixas de seleção devem ser agrupados com um fieldset" + }, + "description": { + "de": "Zusammengehörige \"checkbox\"-Eingabefelder sollten mit Hilfe eines fieldset gruppiert werden.", + "en": "Related \"checkbox\" input fields should be grouped together using a fieldset.", + "nl": "Gerelateerde \"keuzevakjes\"-invoervelden moeten bij elkaar staan in een fieldset.", + "pt-br": "Os campos de entrada \"checkbox\" relacionados devem ser agrupados usando um fieldset." + }, + "guidelines": { + "wcag": { + "3.3.2": { + "techniques": [ + "H71" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "callback": "inputCheckboxRequiresFieldset" + }, + "inputDoesNotUseColorAlone": { + "type": "selector", + "testability": 0, + "title": { + "de": "Ein \"input\"-Element sollte nicht nur Farbe verwenden", + "en": "An \"input\" element should not use color alone", + "nl": "Een invoerveld mag niet alleen maar kleur gebruiken", + "pt-br": "Um elemento \"input\" não deve usar cor sozinho" + }, + "description": { + "de": "\"input\"-Elemente sollten sicht nicht nur über Farbe auf Inhalte beziehen", + "en": "All input elements should not refer to content by color alone.", + "nl": "Elk invoerveld moet naar content verwijzen door middel van meer dan alleen kleur.", + "pt-br": "Todos os elementos de entrada não devem referir-se ao conteúdo apenas por cor." + }, + "guidelines": { + "508": [ + "c" + ] + }, + "tags": [ + "form", + "color", + "content" + ], + "options": { + "selector": "input:not(input[type=hidden])" + } + }, + "inputElementsDontHaveAlt": { + "type": "selector", + "testability": 1, + "title": { + "de": "Eingabelemente, die keine Grafiken sind, sollten kein \"alt\"-Attribut haben", + "en": "Input elements which are not images should not have an \"alt\" attribute", + "nl": "Invoervelden die geen afbeelding zijn, moeten geen \"alt\"-attribuut hebben", + "pt-br": "Os elementos de entrada que não são imagens não devem ter um atributo \"alt\"." + }, + "description": { + "de": "Eingabeelemente, die keine Grafik sind, sollten kein \"alt\"-Attribut haben, weil dies zu Inkonsistenz bei der Benutzung von Alternativtexten führt.", + "en": "Input elements which are not images should not have an \"alt\" attribute, because of inconsistencies in how user agents use the \"alt\" attribute.", + "nl": "Invoervelden die geen afbeelding zijn, moeten geen \"alt\"-attribuut hebben, omdat user agents het \"alt\"-attribuut niet consistent gebruiken.", + "pt-br": "Os elementos de entrada que não são imagens não devem ter um atributo \"alt\", devido a inconsistências na forma como os agentes de utilizador utilizam o atributo \"alt\"." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "options": { + "selector": "input[type!=image][alt]" + } + }, + "inputFileHasTabIndex": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"file\"-Eingabeelemente erfordern ein korrektes \"tabindex\"-Attribut", + "en": "All \"file\" input elements require a valid \"tabindex\" attribute", + "nl": "Alle \"document\"-invoerelementen moeten een geldig \"tabindex\"-attribuut hebben", + "pt-br": "Todos os elementos de entrada \"file\" requerem um atributo \"tabindex\" válido" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"file\" sollten ein \"tabindex\"-Attribut haben, um das Navigieren per Tastatur zu unterstützen", + "en": "All input elements of type \"file\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.", + "nl": "Alle invoer-elementen van het type \"file\" moeten een \"tabindex\"-attribuut hebben om navigatie van het formulier met het toetsenbord mogelijk te maken.", + "pt-br": "Todos os elementos input do tipo \"file\" devem ter um atributo \"tabindex\" para ajudar a navegar o formulário com um teclado sozinho." + }, + "guidelines": [], + "tags": [ + "form", + "tabindex" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "tabindex", + "empty": true, + "selector": "input[type=file]" + } + }, + "inputImageAltIdentifiesPurpose": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle \"input\"-Elemente vom typ \"image\" müssen ein \"alt\"-Attribut haben, dass die Funktion beschreibt", + "en": "All \"input\" elements with a type of \"image\" must have an \"alt\" attribute that describes the function of the input", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat de functie van de invoer beschrijft", + "pt-br": "Todos os elementos \"input\" com um tipo de \"imagem\" devem ter um atributo \"alt\" que descreve a função da entrada" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"image\" sollten ein \"alt\"-Attribut haben", + "en": "All input elements with a type of \"image\" should have an \"alt\" attribute.", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben.", + "pt-br": "Todos os elementos input com um tipo de \"imagem\" devem ter um atributo \"alt\"." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H36" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "input[type=image][alt]" + } + }, + "inputImageAltIsNotFileName": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle \"input\"-Elemente vom Typ \"image\" müssen ein \"alt\"-Attribut haben, dass nicht dem Dateinamen entspricht", + "en": "All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is not the same as the filename", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat anders is dan de bestandsnaam", + "pt-br": "Todos os elementos \"input\" com um tipo de \"imagem\" devem ter um atributo \"alt\" que não é o mesmo que o nome do arquivo" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"image\" müssen ein \"alt\"-Attribut haben, dass nicht dem Dateinamen entspricht", + "en": "All input elements with a type of \"image\" should have an \"alt\" attribute which is not the same as the filename.", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat anders is dan de bestandsnaam.", + "pt-br": "Todos os elementos input com um tipo de \"imagem\" devem ter um atributo \"alt\" que não seja o mesmo que o nome do arquivo." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H36" + ] + } + } + }, + "tags": [ + "form", + "image", + "content" + ], + "callback": "inputImageAltIsNotFileName" + }, + "inputImageAltIsNotPlaceholder": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"input\"-Elemente mit einem \"image\" müssen ein \"alt\"-Attribut haben, das kein Platzhalter ist", + "en": "All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is not placeholder text.", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben anders dan alleen placeholdertekst.", + "pt-br": "Todos os elementos \"input\" com um tipo de \"imagem\" devem ter um atributo \"alt\" que não seja texto de espaço reservado." + }, + "description": { + "de": "Alle \"input\"-Elemente mit einem \"image\" müssen ein \"alt\"-Attribut haben, das kein Platzhalter ist", + "en": "All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is not placeholder text.", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben anders dan alleen placeholdertekst.", + "pt-br": "Todos os elementos \"input\" com um tipo de \"imagem\" devem ter um atributo \"alt\" que não seja texto de espaço reservado." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H36" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "4.1.2": { + "techniques": [ + "H91" + ] + } + } + }, + "tags": [ + "form", + "image", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "alt", + "selector": "input[type=image]" + } + }, + "inputImageAltIsShort": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle \"input\"-Elemente mit einem \"image\" müssen ein \"alt\"-Attribut haben, das so kurz wie möglich ist", + "en": "All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is as short as possible", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat zo kort mogelijk is", + "pt-br": "Todos os elementos \"input\" com um tipo de \"imagem\" devem ter um atributo \"alt\" o mais curto possível" + }, + "description": { + "de": "Alle \"input\"-Elemente mit einem\"image\" müssen ein \"alt\"-Attribut haben, das so kurz wie möglich ist", + "en": "All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is as short as possible.", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat zo kort mogelijk is.", + "pt-br": "Todos os elementos \"input\" com um tipo de \"imagem\" devem ter um atributo \"alt\" o mais curto possível." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H36" + ] + } + } + }, + "tags": [ + "form", + "image", + "content" + ], + "callback": "inputImageAltIsShort" + }, + "inputImageAltNotRedundant": { + "type": "custom", + "testability": 1, + "title": { + "de": "Der \"alt\"-Text für grafische Absenden-Schaltflächen in Formularen darf kein Platzhalter sein", + "en": "The \"alt\" text for input \"image\" submit buttons must not be filler text", + "nl": "De \"alt\"-tekst for \"image\"-knoppen moet anders zijn dan alleen placeholdertekst", + "pt-br": "O texto \"alt\" para entrada \"imagem\" enviar botões não deve ser texto de preenchimento" + }, + "description": { + "de": "Jede grafische Schaltfläche in einem Formular sollte nicht nur Platzhalter wie \"Schaltfläche\", \"Absenden\" als \"alt\"-Text haben", + "en": "Every form image button should not simply use filler text like \"button\" or \"submit\" as the \"alt\" text.", + "nl": "Elke formulierknop die een afbeelding is, moet bruikbare tekst als \"alt\"-tekst hebben, anders dan \"knop\" of \"verstuur\".", + "pt-br": "Cada botão de imagem de formulário não deve simplesmente usar texto de preenchimento como \"botão\" ou \"enviar\" como o texto \"alt\"." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H36" + ] + } + } + }, + "tags": [ + "form", + "image", + "content" + ], + "strings": [ + "redundant.inputImage" + ], + "callback": "inputImageAltNotRedundant" + }, + "inputImageHasAlt": { + "type": "selector", + "testability": 1, + "title": { + "de": "Alle \"input\"-Elemente mit einem \"image\" müssen ein \"alt\"-Attribut haben", + "en": "All \"input\" elements with a type of \"image\" must have an \"alt\" attribute", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben", + "pt-br": "Todos os elementos \"input\" com um tipo de \"imagem\" devem ter um atributo \"alt\"" + }, + "description": { + "de": "Alle input-Elemente mit einem \"image\" müssen ein \"alt\"-Attribut haben", + "en": "All input elements with a type of \"image\" should have an \"alt\" attribute.", + "nl": "Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben.", + "pt-br": "Todos os elementos input com um tipo de \"imagem\" devem ter um atributo \"alt\"." + }, + "guidelines": { + "508": [ + "a" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "F65", + "G94", + "H36" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "4.1.2": { + "techniques": [ + "H91" + ] + } + } + }, + "tags": [ + "form", + "image", + "content" + ], + "options": { + "selector": "input[type=image]:visible", + "test": ":not(input[type=image][alt])" + } + }, + "inputImageNotDecorative": { + "type": "selector", + "testability": 0, + "title": { + "de": "Der \"alt\"-Text für eine grafische Schaltfläche muss derselben sein, wie in der Grafik", + "en": "The \"alt\" text for input \"image\" buttons must be the same as text inside the image", + "nl": "De \"alt\"-tekst voor afbeeldingen van invoerknoppen moet hetzelfde zijn als de tekst in de afbeeldingen" + }, + "description": { + "de": "Jede grafische Schaltfläche mit einem Text in der Grafik (bspw. \"Suche\" in der Grafik in einer speziellen Schrifthart) sollte diesen Text im \"alt\"-Text haben", + "en": "Every form image button which has text within the image (say, a picture of the word \"Search\" in a special font) must also have this text in the \"alt\" text.", + "nl": "Elke formulierknop die een afbeelding is en tekst in de afbeelding heeft (bijvoorbeeld \"Zoek\") moet deze tekst ook in de \"alt\"-tekst hebben." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H36" + ] + } + } + }, + "tags": [ + "form", + "image", + "content" + ], + "options": { + "selector": "input[type=image]" + } + }, + "inputPasswordHasTabIndex": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"password\"-Eingabeelemente erfordern ein korrektes \"tabindex\"-Attribut", + "en": "All \"password\" input elements require a valid \"tabindex\" attribute", + "nl": "Alle \"paswoord\"-invoervelden moeten een geldig \"tabindex\"-attribuut hebben", + "pt-br": "Todos os elementos de entrada \"password\" requerem um atributo \"tabindex\" válido" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"password\" sollten ein \"tabindex\"-Attribut haben, um das Navigieren per Tastatur zu unterstützen", + "en": "All input elements of type \"password\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.", + "nl": "Alle input-elementen van het type \"paswoord\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken.", + "pt-br": "Todos os elementos input do tipo \"password\" devem ter um atributo \"tabindex\" para ajudar a navegar o formulário com um teclado sozinho." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "tabindex", + "empty": true, + "selector": "input[type=password]" + } + }, + "inputRadioHasTabIndex": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"radio\"-Eingabeelemente erfordern ein korrektes \"tabindex\"-Attribut", + "en": "All \"radio\" input elements require a valid \"tabindex\" attribute", + "nl": "Alle invoerelementen van het type \"radio\" moeten een geldig \"tabindex\"-attribuut hebben", + "pt-br": "Todos os elementos de entrada \"radio\" requerem um atributo \"tabindex\" válido" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"radio\" sollten ein \"tabindex\"-Attribut haben, um das Navigieren per Tastatur zu unterstützen", + "en": "All input elements of type \"radio\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.", + "nl": "Alle input-elementen van het type \"radio\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken.", + "pt-br": "Todos os elementos input do tipo \"radio\" devem ter um atributo \"tabindex\" para ajudar a navegar o formulário com um teclado sozinho." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "tabindex", + "empty": true, + "selector": "input[type=radio]" + } + }, + "inputSubmitHasTabIndex": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"submit\"-Eingabeelemente erfordern ein korrektes \"tabindex\"-Attribut", + "en": "All \"submit\" input elements require a \"tabindex\" attribute", + "nl": "Alle invoerelementen van het type \"submit\" moeten een geldig \"tabindex\"-attribuut hebben", + "pt-br": "Todos os elementos de entrada \"submit\" requerem um atributo \"tabindex\"." + }, + "description": { + "de": "Alle input-Elemente vom Typ \"submit\" sollten ein \"tabindex\"-Attribut haben, um das Navigieren per Tastatur zu unterstützen", + "en": "All input elements of type \"submit\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.", + "nl": "Alle input-elementen van het type \"submit\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken.", + "pt-br": "Todos os elementos input do tipo \"submit\" devem ter um atributo \"tabindex\" para ajudar a navegar o formulário com um teclado." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "tabindex", + "empty": true, + "selector": "input[type=submit]" + } + }, + "inputTextHasLabel": { + "type": "label", + "testability": 1, + "title": { + "de": "Alle \"input\"-Elemente sollten ein dazugehöriges \"label\" haben", + "en": "All \"input\" elements should have a corresponding \"label\"", + "nl": "Alle invoerelementen moeten een bijbehorend \"label\" hebben", + "pt-br": "Todos os elementos \"input\" devem ter um elemento \"label\" correspondente" + }, + "description": { + + "de": "Alle input-Elemente sollten ein dazugehöriges label-Element haben. Bildschirmleseprogramme haben oftmals eine Formular-Modus, in dem nur Labels vorgelesen werden.", + "en": "All input elements should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user", + "nl": "Alle input-elementen moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen.", + "pt-br": "Todos os elementos input devem ter um elemento label correspondente. Os leitores de tela costumam entrar em um \"modo de formulário\" onde somente o texto do rótulo é lido em voz alta para o usuário" + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44", + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "input[type=text]" + } + }, + "inputTextHasTabIndex": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"text\"-Eingabeelemente erfordern ein korrektes \"tabindex\"-Attribut", + "en": "All \"text\" input elements require a valid \"tabindex\" attribute", + "nl": "Alle invoerelementen van het type \"text\" moeten een geldig \"tabindex\"-attribuut hebben", + "pt-br": "Todos os elementos de entrada \"text\" requerem um atributo \"tabindex\" válido" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"text\" sollten ein \"tabindex\"-Attribut haben, um das Navigieren per Tastatur zu unterstützen", + "en": "All input elements of type \"text\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.", + "nl": "Alle input-elementen van het type \"text\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken.", + "pt-br": "Todos os elementos input do tipo \"text\" devem ter um atributo \"tabindex\" para ajudar a navegar o formulário com um teclado." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "tabindex", + "empty": true, + "selector": "input[type=text]" + } + }, + "inputTextHasValue": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle \"input\"-Elemente vom Typ \"text\" müssen einen Standard-Text haben", + "en": "All \"input\" elements of type \"text\" must have a default text", + "nl": "Alle invoerelementen van het type \"text\" moeten een standaardtekst hebben", + "pt-br": "Todos os elementos \"input\" do tipo \"text\" devem ter um texto padrão" + }, + "description": { + "de": "Alle input-Elements vom Typ \"text\" sollten einen Standard-Text haben.", + "en": "All input elements of type \"text\" should have a default text.", + "nl": "Alle invoerelementen van het type \"text\" moeten een standaardtekst hebben.", + "pt-br": "Todos os elementos input do tipo \"text\" devem ter um texto padrão." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "value", + "empty": true, + "selector": "input[type=text]" + } + }, + "inputTextValueNotEmpty": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Text-Eingabeelemente erfordert einen nicht-leeren Standardtext", + "en": "Text input elements require a non-whitespace default text", + "nl": "Tekstinvoerelementen mogen geen lege standaardtekst hebben", + "pt-br": "Elementos de entrada de texto requerem um texto padrão que não permita somente espaços em branco" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"text\" sollten einen Standardtext haben, der nicht leer ist", + "en": "All input elements with a type of \"text\" should have a default text which is not empty.", + "nl": "Alle invoerelementen van het type \"text\" moeten een standaardtekst hebben die gevuld is.", + "pt-br": "Todos os elementos input com um tipo de \"text\" devem ter um texto padrão que não esteja vazio." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "value", + "empty": true, + "selector": "input[type=text]" + } + }, + "inputWithoutLabelHasTitle": { + "type": "custom", + "testability": 1, + "title": { + "de": "Formular-Steuerelemente ohne \"label\" sollten ein \"title\"-Attribut haben", + "en": "Form controls without label should have a title attribute", + "nl": "Formulierelementen zonder label moeten een titelattribuut hebben", + "pt-br": "Os controles de formulário sem rótulo devem ter um atributo de título" + }, + "description": { + "de": "Wenn es nicht möglich ist ein \"label\" für ein Formular-Steuerelement zu haben, sollte es ein \"title\"-Attribut haben, welches den Zweck des Steuerelements beschreibt", + "en": "If it is not possible to have a label for a form control, then a title attribute on the element should be provided that describes the purpose of the control.", + "nl": "Als een formulierelement geen label kan krijgen, dan moet een dat element een titelattribuut krijgen dat het doel van het element beschrijft.", + "pt-br": "Se não for possível ter um rótulo para um controle de formulário, um atributo de título no elemento deve ser fornecido que descreve a finalidade do controle." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H65" + ] + }, + "1.3.1": { + "techniques": [ + "H65" + ] + }, + "3.3.2": { + "techniques": [ + "H65" + ] + }, + "4.1.2": { + "techniques": [ + "H65" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "callback": "inputWithoutLabelHasTitle" + }, + "labelDoesNotContainInput": { + "type": "selector", + "testability": 1, + "title": { + "de": "\"label\"-Elemente sollten keine \"input\"-Elemente enthalten", + "en": "Label elements should not contain an input element", + "nl": "Labelelementen moeten geen invoerelementen bevatten", + "pt-br": "Os elementos de rótulo não devem conter um elemento de entrada" + }, + "description": { + "de": "\"label\"-Elemente sollten kein \"input\"-Element enthalten, da diese sonst von Bildschirmleseprogrammen doppelt vorgelesen werden", + "en": "Label elements should not wrap around another input element, as this can cause the label to be read twice by screen readers.", + "nl": "Labelelementen moeten niet om een ander invoerelement heenstaan, omdat dan het label twee keer kan worden voorgelezen door schermlezers.", + "pt-br": "Elementos de rótulo não devem envolver outro elemento de entrada, pois isso pode causar o rótulo para ser lido duas vezes por leitores de tela." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "options": { + "selector": "label:has(input)" + } + }, + "labelMustBeUnique": { + "type": "custom", + "testability": 1, + "title": { + "de": "Jedes Formular-Eingabeelement darf nur ein Label haben", + "en": "Every form input must have only one label", + "nl": "Elk formulierinvoerveld heeft maar een label", + "pt-br": "Cada entrada de formulário deve ter apenas um rótulo" + }, + "description": { + "de": "Jedes Formular-Eingabeelement sollte nur ein label-Element haben", + "en": "Each form input should have only one label element.", + "nl": "Elk formulierinvoerveld mag maar een label element hebben.", + "pt-br": "Cada entrada de formulário deve ter apenas um elemento label ." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F17" + ] + }, + "4.1.1": { + "techniques": [ + "F17" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "callback": "labelMustBeUnique" + }, + "labelMustNotBeEmpty": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Labels müssen Text beinhalten", + "en": "Labels must contain text", + "nl": "Labels moeten tekst bevatten", + "pt-br": "As etiquetas devem conter texto" + }, + "description": { + "de": "Labels in Formularen müssen lesbaren Text enthalten, der den Zweck des Elements beschreibt", + "en": "Labels in forms must contain readable text that describes the target form element.", + "nl": "Labels in formulieren moeten leesbare tekst bevatten die het formulierelement beschrijven.", + "pt-br": "Etiquetas em formulários devem conter texto legível que descreve o elemento de formulário de destino." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "content": true, + "empty": true, + "selector": "label" + } + }, + "labelsAreAssignedToAnInput": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Labels müssen mit einem Eingabeelement verknüpft sein", + "en": "All labels should be associated with an input", + "nl": "Alle labels moeten horen bij een invoerveld", + "pt-br": "Todos os rótulos devem ser associados a uma entrada" + }, + "description": { + "de": "Alle label-Elemente sollten einem Eingabeelement zugewiesen sein und ein for-Attribut haben, welches mit dem id-Attribut eines Formular-Elements übereinstimmt", + "en": "All label elements should be assigned to an input item, and should have a for attribute which equals the id attribute of a form element.", + "nl": "Alle label-elementen moeten horen bij een invoerveld, en moeten een een for-attribuut hebben dat hetzelfde is als het id-attribuut van een formulierelement.", + "pt-br": "Todos os elementos label devem ser atribuídos a um item de entrada e devem ter um atributo para que seja igual ao atributo id de um elemento de formulário." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "callback": "labelsAreAssignedToAnInput" + }, + "languageDirAttributeIsUsed": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Das \"dir\"-Attribut sollte bei Änderungen der Leserichtung verwendet werden", + "en": "Use the dir attribute when the language direction changes", + "nl": "Gebruik het dir-attribuut als de richting van de taal verandert", + "pt-br": "Use o atributo dir quando a direção do idioma mudar" + }, + "description": { + "de": "Wehn verschachtelte Änderungen der Leserichtung in einem Text enthalten sind, sollte ein \"inline\"-Element mit dir-Attribut verwendet werden.", + "en": "When there are nested directional changes in text, use an inline element with a dir attribute to indicate direction.", + "nl": "Gebruik een inline element met een dir-attribuut om richting aan te geven wanneer er geneste richtingsveranderingen in de tekst zijn.", + "pt-br": "Quando há alterações direcionais aninhadas no texto, use um elemento inline com um atributo dir para indicar direção." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "H56" + ] + } + } + }, + "tags": [ + "language", + "content" + ], + "callback": "languageDirAttributeIsUsed", + "components": [ + "language" + ] + }, + "languageChangesAreIdentified": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Das \"language\"-Attribut sollte verwendet werden, um die Leserichtung zu ändern", + "en": "Use language attributes to indicate changes in language", + "nl": "Gebruik het taal-attribuut om aan te geven dat de taal verandert", + "pt-br": "Usar atributos de linguagem para indicar mudanças na linguagem" + }, + "description": { + "de": "Wenn die Sprache in einem Dokument wechselt, setzen Sie den Text mit der Sprachänderung in eine Element mit lang-Attribut.", + "en": "When the language of the document changes, make sure to wrap those changes in an element with the lang attribute.", + "nl": "Als de taal van het document verandert, zet deze veranderingen dan in een element met het lang-attribuut.", + "pt-br": "Quando o idioma do documento é alterado, certifique-se de envolver essas alterações em um elemento com o atributo lang ." + }, + "guidelines": { + "wcag": { + "3.1.2": { + "techniques": [ + "H58" + ] + } + } + }, + "tags": [ + "language", + "content" + ], + "callback": "languageChangesAreIdentified", + "components": [ + "language" + ] + }, + "languageDirectionPunctuation": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Setzen Sie Satzzeichen bei Änderungen der Leserichtung in der korrekten Reihenfolge", + "en": "Place punctuation around language direction changes in the right order", + "nl": "Zet interpunctie bij richtingsveranderingen in taal in de juiste volgorde", + "pt-br": "Coloque a pontuação em torno das mudanças na direção da linguagem na ordem correta" + }, + "description": { + "de": "Wenn Satzzeichen bei einer Änderung der Leserichtung verwendet werden, stellen Sie die korrekte Reihenfolge sicher", + "en": "If punctuation is used around a change in language direction, ensure the punctuation appears in the correct place.", + "nl": "Als er interpunctie staat bij een richtingsverandering in de taal, zorg dat deze dan op de goede plek staat.", + "pt-br": "Se a pontuação é usada em torno de uma mudança na direção do idioma, verifique se a pontuação aparece no lugar correto." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "G57" + ] + } + } + }, + "tags": [ + "language", + "content" + ], + "callback": "languageDirectionPunctuation", + "components": [ + "language" + ] + }, + "languageUnicodeDirection": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verwenden Sie die Unicode-Sprachausrichtung", + "en": "Use the unicode language direction", + "nl": "Gebruik de unicode taalrichting", + "pt-br": "Use a direção da linguagem unicode" + }, + "description": { + "de": "Wenn verschachtelte Änderungen der Leserichtung auftreten, verwenden Sie die Unicode-Zeichen RTL/LTR", + "en": "When there are nested directional changes in language, use unicode RTL/LTR characters.", + "nl": "Gebruik de unicode RTL/LTR afkortingen als er geneste richtingsveranderingen in de taal zijn.", + "pt-br": "Quando houver alterações direcionais aninhadas no idioma, use caracteres RTL / LTR unicode." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "H34" + ] + } + } + }, + "tags": [ + "language", + "content" + ], + "callback": "languageUnicodeDirection", + "components": [ + "language" + ] + }, + "legendDescribesListOfChoices": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle \"legend\"-Elemente müssen die Auswahl-Gruppe beschreiben", + "en": "All \"legend\" elements must describe the group of choices", + "nl": "Alle \"legend\"-elementen moeten een groep keuzes beschrijven", + "pt-br": "" + }, + "description": { + "de": "Wenn ein legend-Element in einem Fieldset verwendet wird, muss der Inhalt im legend-Element die Auswahlgruppe beschreiben", + "en": "If a legend element is used in a fieldset, the legend content must describe the group of choices.", + "nl": "Als een legend-element wordt gebruikt in een fieldset, moet de legend content de groep keuzes beschrijven.", + "pt-br": "Se um elemento legend for usado em um fieldset, o conteúdo legend deve descrever o grupo de opções." + }, + "guidelines": { + "wcag": { + "2.4.6": { + "techniques": [ + "G131" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "legend" + } + }, + "legendTextNotEmpty": { + "type": "selector", + "testability": 1, + "title": { + "de": "Legenden dürfen keine Leerzeichen enthalten", + "en": "Legend text must not contain just whitespace", + "nl": "Legend-tekst moet ingevuld zijn", + "pt-br": "O texto da legenda não deve conter apenas espaços em branco" + }, + "description": { + "de": "Wenn ein legend-Element in einem Fieldset verwendet wird, sollte das legend-Element nicht leer sein", + "en": "If a legend element is used in a fieldset, the legend should not contain empty text.", + "nl": "Als een legend-element wordt gebruikt in een fieldset, moet de legend ingevuld zijn.", + "pt-br": "Se um elemento legenda for usado em um fieldset, a legenda não deve conter texto vazio." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H71" + ] + }, + "2.4.6": { + "techniques": [ + "G131" + ] + }, + "3.3.2": { + "techniques": [ + "H71" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "legend:empty" + } + }, + "legendTextNotPlaceholder": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "\"Legend\"-Texte drüfen keine Platzhalter enthalten", + "en": "\"Legend\" text must not contain placeholder text", + "nl": "\"Legend\"-tekst moet geen placeholdertekst bevatten", + "pt-br": "\"Legend\" não deve conter um texto reservado (placeholder)" + }, + "description": { + "de": "Wenn ein legend-Element in einem Fieldset verwendet wird, sollte das legend-Element keine Platzhalter enthalten.", + "en": "If a legend element is used in a fieldset, the legend should not contain useless placeholder text like \"form\" or \"field\".", + "nl": "Als een legend-element wordt gebruikt in een fieldset, moet de legend geen placeholdertekst bevatten zoals \"form\" of \"field\".", + "pt-br":"Se um elemento legenda for usado em um fieldset, a legenda não deve conter texto reservado inútil como \"form\" ou \"field\"." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H71" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.4.6": { + "techniques": [ + "G131" + ] + }, + "3.3.2": { + "techniques": [ + "H71" + ] + }, + "4.1.3": { + "techniques": [ + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "content": true, + "emtpy": true, + "selector": "legend" + } + }, + "liDontUseImageForBullet": { + "type": "selector", + "testability": 0.5, + "guidelines": [], + "tags": [ + "list", + "content" + ], + "options": { + "selector": "li:has(img)" + } + }, + "linkHasAUniqueContext": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verknüpfungen sollten einen eindeutigen Zusammenhang haben", + "en": "Links should have a unique context", + "nl": "Links moeten een unieke context hebben", + "pt-br": "Links devem ter um contexto único" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "link", + "content" + ], + "callback": "linkHasAUniqueContext" + }, + "linkUsedForAlternateContent": { + "type": "selector", + "testability": 0, + "title": { + "de": "Verwende ein \"link\"-Element für alternative Inhalte", + "en": "Use a \"link\" element for alternate content", + "nl": "Gebruik een \"link\"-element for andersoortige content", + "pt-br": "Usar um elemento \"link\" para conteúdo alternativo" + }, + "description": { + "de": "Dokumente die Inhalte, wie bspw. Videos, Audios oder andere Medien enthalten, die nicht barrrierefrei sind, sollten ein link-Element mit einem \"rel\"-Attribut der Alternative im Header des Dokumentes enthalten.", + "en": "Documents which contain things like videos, sound, or other forms of media that are not accessible, should provide a link element with a \"rel\" attribute of \"alternate\" in the document header.", + "nl": "Documenten die content zoals video's, geluid of andere niet-toegankelijke vormen van media bevatten, moeten een link-element met een \"rel\"-attribuut of \"alternate\" in de documentheaderlink aanbieden.", + "pt-br": "Os documentos que contenham coisas como vídeos, som ou outras formas de mídia que não estejam acessíveis devem fornecer um elemento link com um atributo \"rel\" de \"alternate\" no cabeçalho do documento." + }, + "guidelines": [], + "tags": [ + "document" + ], + "options": { + "selector": "html:not(html:has(link[rel=alternate])) body" + } + }, + "linkUsedToDescribeNavigation": { + "type": "selector", + "testability": 1, + "title": { + "de": "Das Dokument verwendet Verknüpfungen um die Navigation zu beschreiben, wenn es in einer Sammlung ist", + "en": "The document uses link elements to describe navigation if it is within a collection.", + "nl": "Het document gebruikt link-elementen om navigatie te beschrijven wanneer het binnen een collectie staat.", + "pt-br": "O documento usa elementos de link para descrever a navegação se ela estiver dentro de uma coleção." + }, + "description": { + "de": "Das Verknüpfungs-Element kann Metadaten über die Position einer HTML-Seite in einem Set von Webseiten liefern.", + "en": "The link element can provide metadata about the position of an HTML page within a set of Web units or can assist in locating content with a set of Web units.", + "nl": "Het link-element kan metadata bevatten over de positie van een HTML-pagina binnen een swet web units, of kan behulpzaamn zijn bij het lokaliseren van content binnen web units.", + "pt-br": "O elemento link pode fornecer metadados sobre a posição de uma página HTML dentro de um conjunto de unidades da Web ou pode ajudar a localizar conteúdo com um conjunto de unidades da Web." + }, + "guidelines": [], + "tags": [ + "document" + ], + "options": { + "selector": "html:not(html:has(link[rel=index]))" + } + }, + "listNotUsedForFormatting": { + "type": "custom", + "testability": 0, + "title": { + "de": "Listen sollten nicht für die Formatierung genutzt werden", + "en": "Lists should not be used for formatting", + "nl": "Lijsten worden niet gebruikt voor opmaak", + "pt-br": "As listas não devem ser usadas para formatação" + }, + "description": { + "de": "Listen, wie bspw. ul und ol dienen der Strukturierung von Listen und sollten nicht zur Formatierung von Text genutzt werden. Diese Test prüft Listen mit nur einem Eintrag, sollte aber manuell überprüft werden. ", + "en": "Lists like ul and ol are to provide a structured list, and should not be used to format text. This test views any list with just one item as suspicious, but should be manually reviewed.", + "nl": "Lijsten zoals ul en ol zijn bedoeld om gestructureerde lijsten te maken. Ze moeten niet gebruikt worden om text op te maken. Controleer of deze lijst echt bedoeld is als lijst of om tekst op te maken.", + "pt-br": "Listas como ul e ol são para fornecer uma lista estruturada e não devem ser usadas para formatar texto. Este teste exibe qualquer lista com apenas um item como suspeito, mas deve ser revisado manualmente." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "F1" + ] + } + } + }, + "tags": [ + "list", + "content" + ], + "callback": "listNotUsedForFormatting" + }, + "listOfLinksUseList": { + "type": "custom", + "testability": 1, + "title": { + "de": "Eine Liste von Verknüpfung sollte in einer geordneten oder ungeordneten Liste sein", + "en": "A list of links separated by non-readable characters should be in an ul or ol", + "nl": "Een lijst van links die worden gescheiden door onleesbare tekens moeten in een bulleted of genummerde lijst staan", + "pt-br": "Uma lista de links separados por caracteres não legíveis deve estar em um ul ou ol" + }, + "description": { + "de": "Eine Liste von Verknüpfungen ohne Trennung zwischen den Verknüpfungen sollte in einem ul-Element oder ol-Element enthalten sein", + "en": "A list of links without separation between them should be placed in an ol or ul element.", + "nl": "Een lijst van links die niet duidelijk gescheiden zijn moeten in een bulleted of genummerde lijst staan.", + "pt-br": "Uma lista de links sem separação entre eles deve ser colocada em um elemento ol ou ul." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H48" + ] + } + } + }, + "tags": [ + "link", + "content" + ], + "callback": "listOfLinksUseList" + }, + "marqueeIsNotUsed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Der \"marquee\"-Tag sollte nicht verwendet werden", + "en": "The \"marquee\" tag should not be used", + "nl": "De \"marquee\"-tag wordt niet gebruikt", + "pt-br": "A tag \"marquee\" não deve ser usada" + }, + "description": { + "de": "Das marquee-Element ist für Benutzer schwierig zu lesen und kein HTML-Standardelement. Nutzen Sie ein anderes Element zur Hervorhebung der Bedeutung eines Textes.", + "en": "The marquee element is difficult for users to read and is not a standard HTML element. Try to find another way to convey the importance of this text.", + "nl": "Het marquee-element is moeilijk te lezen voor gebruikers en is geen standaard HTML-element. Gebruik een andere manier om aan te duiden dat het belangrijke content is.", + "pt-br": "O elemento marquee é difícil de ler e não é um elemento HTML padrão. Tente encontrar outra maneira de transmitir a importância deste texto." + }, + "guidelines": [], + "tags": [ + "deprecated", + "content" + ], + "options": { + "selector": "marquee" + } + }, + "menuNotUsedToFormatText": { + "type": "selector", + "testability": 0, + "title": { + "de": "\"menu\"-Elemente sollten nicht für Formatierung genutzt werden", + "en": "Menu elements should not be used for formatting", + "nl": "Menu-elementen worden niet gebruikt voor opmaak", + "pt-br": "Os elementos de menu não devem ser usados para formatação" + }, + "description": { + "de": "Der \"menu\"-Tag ist veraltet, aber wird im Doctype HTML Transitional noch immer berücksichtigt. \"menu\"-Tags dienen der Strukturierung von Dokumenten und sollten nicht für Formatierungen genutzt werden. Der \"menu\"-Tag sollte dieser nur in Listen von Verknüpfungen verwendet werden", + "en": "Menu is a deprecated tag, but is still honored in a transitional DTD. Menu tags are to provide structure for a document and should not be used for formatting. If a menu tag is to be used, it should only contain an ordered or unordered list of links.", + "nl": "Menu is een afgekeurd tag, maar wordt nog wel gebruikt om structuur aan een document te geven. Het mag niet worden gebruikt voor opmaak. Als een menu-tag wordt gebruikt, mag het alleen bulleted of genummerde lijsten bevatten.", + "pt-br": "Menu é uma tag obsoleta, mas ainda é homenageada em um DTD transicional. As tags de menu devem fornecer estrutura para um documento e não devem ser usadas para formatação. Se uma tag de menu deve ser usada, ela deve conter apenas uma lista ordenada ou não ordenada de links." + }, + "guidelines": [], + "tags": [ + "list", + "content" + ], + "options": { + "selector": "menu:not(menu li:parent(menu))" + } + }, + "newWindowIsOpened": { + "type": "custom", + "testability": 1, + "title": { + "de": "Eine Verknüpfung sollte nicht in einem neuen Fenster geöffnet werden", + "en": "A link should not open a new window", + "nl": "Een link opent geen nieuw scherm", + "pt-br": "Um link não deve abrir uma nova janela" + }, + "description": { + "de": "Vermeiden Sie Verwirrung durch neu öffnende Fenster, die nicht vom Benutzer angefordert wurden.", + "en": "Avoid confusion that may be caused by the appearance of new windows that were not requested by the user.", + "nl": "Voorkom verwarring die veroorzaakt wordt door het openen van nieuwe schermen die de gebruiker niet verwacht.", + "pt-br": "Evite confusão que pode ser causada pela aparição de novas janelas que não foram solicitadas pelo usuário." + }, + "guidelines": { + "wcag": { + "2.0.0": { + "techniques": [ + "H83" + ] + } + } + }, + "tags": [ + "javascript", + "html" + ], + "callback": "newWindowIsOpened" + }, + "noembedHasEquivalentContent": { + "type": "selector", + "testability": 0, + "title": { + "de": "\"Noembed\"-Elemente müssen denselben Inhalt beschreiben, wie das \"embed\"-Element", + "en": "Noembed elements must be the same content as their \"embed\" element", + "nl": "Noembed-elementen moeten dezelfde content hebben als hun \"embed\"-element", + "pt-br": "Elementos Noembed devem ter o mesmo conteúdo que o elemento \"embed \"" + }, + "description": { + "de": "Alle noembed-Elemente müssen eine barrierefreie Version des Inhalts des embed-Elements enthalten oder darauf verweisen", + "en": "All noembed elements must contain or link to an accessible version of their embed counterparts.", + "nl": "Alle noembed-elementen moeten een toegankelijke versie van hun embed-tegenhangers bevatten of hier naar linken.", + "pt-br": "Todos os elementos noembed devem conter ou vincular a uma versão acessível de suas contrapartes embed ." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "noembed" + } + }, + "noframesSectionMustHaveTextEquivalent": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Alle \"noframes\"-Elemente sollten den Inhalt aller Frames enthalten", + "en": "All \"noframes\" elements should contain the text content from all frames", + "nl": "Alle \"noframes\"-elementen moeten de content van alle frames bevatten", + "pt-br": "Todos os elementos \"noframes\" devem conter o conteúdo de texto de todos os quadros" + }, + "description": { + "de": "Der Inhalte in noframes sollte den sichtbaren Inhalt der Frames wiedergeben oder darauf verweisen", + "en": "The noframes content should either replicate or link to the content visible within the frames.", + "nl": "The noframes-content moet de zichtbare content binnen de frames repliceren of er naar linken.", + "pt-br": "O conteúdo noframes deve replicar ou vincular ao conteúdo visível dentro dos quadros." + }, + "guidelines": [], + "tags": [ + "deprecated", + "frame" + ], + "options": { + "selector": "frameset:not(frameset:has(noframes))" + } + }, + "objectContentUsableWhenDisabled": { + "type": "selector", + "testability": 0, + "title": { + "de": "Wenn Objekte deaktiviert sind, sollte der Inhalt dennoch angezeigt werden", + "en": "When objects are disabled, content should still be available", + "nl": "Als objecten zijn uitgeschakeld, moet de content nog wel beschikbaar zijn", + "pt-br": "Quando os objetos estão desativados, o conteúdo ainda deve estar disponível" + }, + "description": { + "de": "Der Inhalt in Objekten sollte dennoch angezeigt werden, auch wenn das Objekt deaktiviert ist. Dazu setzen Sie eine Verknüpfung zur \"source\" des Objekts im object-Tag.", + "en": "The content within objects should still be available, even if the object is disabled. To do this, place a link to the direct object source within the object tag.", + "nl": "Content binnen objecten moet beschikbaar blijven, ook als het object is uitgeschakeld. Plaats hiervoor een link naar de bron van het object binnen de object-tag.", + "pt-br": "O conteúdo dentro de objetos ainda deve estar disponível, mesmo se o objeto estiver desativado. Para fazer isso, coloque um link para a fonte de objeto direto dentro da tag object ." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectDoesNotFlicker": { + "type": "selector", + "testability": 0, + "title": { + "de": "Objekte dürfen nicht flackern", + "en": "Objects do not flicker", + "nl": "Objecten knipperen of flitsen niet", + "pt-br": "Objetos não piscam" + }, + "description": { + "de": "Der Inhalt in einem object-Tag darf nicht flackern", + "en": "The content within an object tag must not flicker.", + "nl": "De content binnen een object-tag knippert of flitst niet.", + "pt-br": "O conteúdo dentro de uma tag object não deve piscar." + }, + "guidelines": { + "508": [ + "j" + ], + "wcag": { + "2.2.2": { + "techniques": [ + "F7" + ] + } + } + }, + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectDoesNotUseColorAlone": { + "type": "selector", + "testability": 0, + "title": { + "de": "Objekte sollten nicht nur Farben verwenden, um Inhalte zu kommunizieren", + "en": "Objects must not use color to communicate alone", + "nl": "Objecten gebruiken meer dan alleen kleur om hun boodschap over te brengen", + "pt-br": "Os objetos não devem usar cor para se comunicar sozinhos" + }, + "description": { + "de": "Objekte sollten Inhalte so darstellen, das sie ohne Farben verständlich und für farbenblinde Benutzer zugänglich sind.", + "en": "Objects should contain content that makes sense without color and is accessible to users who are color blind.", + "nl": "Objecten moeten content bevatten die duidelijk is zonder het kleurgebruik en toegankelijk is voor gebruikers met kleurenblindheid.", + "pt-br": "Objetos devem conter conteúdo que faz sentido sem cor e é acessível para usuários que são daltônicos." + }, + "guidelines": { + "508": [ + "c" + ] + }, + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectInterfaceIsAccessible": { + "type": "selector", + "testability": 0, + "title": { + "de": "Jede Benutzerschnittstelle in einem Objekt muss barrierefrei sein", + "en": "Interfaces within objects must be accessible", + "nl": "Interfaces binnen objecten moeten toegankelijk zijn", + "pt-br": "Interfaces dentro de objetos devem ser acessíveis" + }, + "description": { + "de": "Inhalte in Objekten sollten auf Barrierefreiheit geprüft werden.", + "en": "Object content should be assessed for accessibility.", + "nl": "Content binnen objecten moeten gecontroleerd worden op toegankelijkheid.", + "pt-br": "O conteúdo do objeto deve ser avaliado quanto à acessibilidade." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectLinkToMultimediaHasTextTranscript": { + "type": "selector", + "testability": 0, + "title": { + "de": "Objekte, die auf eine Multimedia-Datei verweisen, sollten auch eine Verknüpfung zu einem Transkript enthalten", + "en": "Objects which reference multimedia files should also provide a link to a transcript", + "nl": "Objecten die verwijzen naar multimediabestanden moeten ook een link aanbieden naar de transcriptie", + "pt-br": "Os objetos que fazem referência a arquivos multimídia também devem fornecer um link para uma transcrição" + }, + "description": { + "de": "Auf eine Verknüpfung zu einer Multimedia-Datei in einem Objekt sollte eine Verknüpfung zu einem Transkript folgen.", + "en": "If an object contains a video, a link to the transcript should be provided near the object.", + "nl": "Als een object een video bevat, moet een link naar de transcriptie hiervan worden aangeboden bij het object.", + "pt-br": "Se um objeto contiver um vídeo, um link para o transcrito deve ser fornecido perto do objeto." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectMustContainText": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Alle Objekte sollten den Inhalt im Element des Objekts selbst enthalten", + "en": "Objects must contain their text equivalents", + "nl": "Objecten moeten hun tekstuele equivalent bevatten", + "pt-br": "Os objetos devem conter seus equivalentes de texto" + }, + "description": { + "de": "Objekte sollten ihre Text-Äquivalente oder eine Beschreibung innerhalb des object-Tag selbst enthalten.", + "en": "All object elements should contain a text equivalent if the object cannot be rendered.", + "nl": "Alle object-elementen moeten een tekstequivalent bevatten in het geval het object niet getoond kan worden.", + "pt-br": "Todos os elementos object devem conter um equivalente de texto se o objeto não puder ser renderizado." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "FLASH1", + "H27" + ] + } + } + }, + "tags": [ + "objects", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "content": true, + "empty": true, + "selector": "object" + } + }, + "objectMustHaveEmbed": { + "type": "selector", + "testability": 1, + "title": { + "de": "Jedes Objekt sollte ein \"embed\"-Element enthalten", + "en": "Every object should contain an \"embed\" element", + "nl": "Elk object moet een \"embed\"-element bevatten", + "pt-br": "Todo objeto deve conter um elemento \"embed\"" + }, + "description": { + "de": "Jedes object-Element muss auch ein embed-Element beinhalten", + "en": "Every object element must also contain an embed element.", + "nl": "Elk object-element moet ook een \"embed\"-element bevatten.", + "pt-br": "Todo elemento object também deve conter um elemento embed ." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object:not(object:has(embed))" + } + }, + "objectMustHaveTitle": { + "type": "selector", + "testability": 1, + "title": { + "de": "Objekte sollten ein \"title\"-Attribut haben", + "en": "Objects should have a title attribute", + "nl": "Objecten moeten een titelattribuut hebben", + "pt-br": "Objetos devem ter um atributo de título" + }, + "description": { + "de": "Alle object-Elemente sollten ein \"title\"-Attribut haben", + "en": "All object elements should contain a \"title\" attribute.", + "nl": "Alle object-elementen moeten een \"titel\"-attribuut bevatten.", + "pt-br": "Todos os elementos object devem conter um atributo \"title \"." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H27" + ] + } + } + }, + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object:not(object[title])" + } + }, + "objectMustHaveValidTitle": { + "type": "placeholder", + "testability": 1, + "title": { + "de": "Objekte sollten kein leeres \"title\"-Attribut haben", + "en": "Objects must not have an empty title attribute", + "nl": "Objecten hebben geen leeg titelattribuut", + "pt-br": "Objetos não devem ter um atributo title vazio" + }, + "description": { + "de": "Alle object-Elemente sollten ein \"title\"-Attribut haben, das nicht leer ist", + "en": "All object elements should have a \"title\" attribute which is not empty.", + "nl": "All object-elementen hebben een \"titel\"-attribuut dat gevuld is.", + "pt-br": "Todos os elementos object devem ter um atributo \"title\" que não esteja vazio." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "title", + "empty": true, + "selector": "object" + } + }, + "objectProvidesMechanismToReturnToParent": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle Objekte sollten einen Weg für Tastatur-Benutzer bieten, um das Objekt zu verlassen.", + "en": "All objects should provide a way for keyboard users to escape", + "nl": "Alle objecten moeten een manier bevatten voor toetsenbordgebruikers een manier om het object te verlaten", + "pt-br": "Todos os objetos devem fornecer uma maneira de escape para os usuários de teclado" + }, + "description": { + "de": "Stellen Sie sicher, dass ein Benutzer, der nur eine Tastatur als Eingabegerät besitzt, ein object-Element per Tastaturbefehl verlassen kann. Dies muss manuell getestet werden.", + "en": "Ensure that a user who has only a keyboard as an input device can escape a object element. This requires manual confirmation.", + "nl": "Zorg ervoor dat een gebruiker die alleen het toetsenbord als bediening gebruikt een object-element. Hiervoor is handmatige bevestiging nodig.", + "pt-br": "Certifique-se de que um usuário que tenha apenas um teclado como dispositivo de entrada pode escapar de um elemento object . Isso requer confirmação manual." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectShouldHaveLongDescription": { + "type": "selector", + "testability": 0, + "title": { + "de": "Ein Objekt erfordert möglicherweise eine Langbeschreibung", + "en": "An object might require a long description", + "nl": "Een object heeft soms een lange beschrijving nodig", + "pt-br": "Um objeto pode exigir uma descrição longa" + }, + "description": { + "de": "Objekte erfordern möglicherweise eine Langbeschreibung, besonders wenn deren Inhalt kompliziert ist", + "en": "Objects might require a long description, especially if their content is complicated.", + "nl": "Objecten hebben soms een lange beschrijving nodig, zeker in het geval van ingewikkelde content.", + "pt-br": "Objetos podem exigir uma descrição longa, especialmente se o seu conteúdo é complicado." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectTextUpdatesWhenObjectChanges": { + "type": "selector", + "testability": 0, + "title": { + "de": "Die Textbeschreibung eines Objekts sollte aktualisiert werden, wenn das Objekt sich ändert", + "en": "The text equivalents of an object should update if the object changes", + "nl": "De tekstuele equivalent van een object moet bijgewerkt worden als het object verandert", + "pt-br": "Os equivalentes de texto de um objeto devem ser atualizados se o objeto for modificado" + }, + "description": { + "de": "Wenn ein Objekt sich ändert, sollte die Textbeschreibung des Objektes sich ebenfalls ändern", + "en": "If an object changes, the text equivalent of that object should also change.", + "nl": "Als een object verandert, moet zijn tekstuele equivalent ook veranderen.", + "pt-br": "Se um objeto for alterado, o texto equivalente desse objeto também deve ser alterado." + }, + "guidelines": { + "508": [ + "a" + ] + }, + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectUIMustBeAccessible": { + "type": "selector", + "testability": 0, + "title": { + "de": "Inhalt in einem \"object\"-Element sollte nutzbar sein, wenn das Objekt deaktiviert ist", + "en": "Content within an \"object\" element should be usable with objects disabled", + "nl": "Content binnen een \"object\"-element moet bruikbaar blijven als het object uitgeschakeld is", + "pt-br": "O conteúdo dentro de um elemento \"objeto\" deve ser utilizável com objetos desativados" + }, + "description": { + "de": "Objekte, deren Inhalte mit Hilfe von Java, ActiveX oder ähnlichen Technologien aktualisiert werden, sollten den Standardtext aktualisieren, wenn ihr Inhalt geändert wird", + "en": "Objects who's content changes using java, ActiveX, or other similar technologies, should have their default text change when the object's content changes.", + "nl": "Van objecten waarvan de content java, ActiveX of vergelijkbare technologie�n gebruiken, moet de standaardtekst veranderen als de content van het object verandert.", + "pt-br": "Os objetos com alterações de conteúdo usando java, ActiveX ou outras tecnologias similares devem ter seu texto padrão alterado quando o conteúdo do objeto for alterado." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object" + } + }, + "objectWithClassIDHasNoText": { + "type": "selector", + "testability": 1, + "title": { + "de": "Objekte mit \"classid\"-Attribut sollten nicht ihren Text ändern, wenn sich der Inhalt des Objektes ändert", + "en": "Objects with \"classid\" attributes should change their text if the content of the object changes", + "nl": "Objecten met \"classid\"-attributen moeten hun tekst veranderen wanneer de content van het object verandert", + "pt-br": "Os objetos com \"classid\" atributos devem alterar seu texto se o conteúdo do objeto for alterado" + }, + "description": { + "de": "Objekte mit \"classid\"-Attribut sollten nicht ihren Standardtext ändern, wenn sich der Inhalt des Objektes ändert.", + "en": "Objects with \"classid\" attributes, should have their default text change when the object's content changes.", + "nl": "Van objecten met \"classid\"-attributen moet de standaardtekst veranderen als de content van het object verandert.", + "pt-br": "Os objetos com atributos \"classid\" devem ter seu texto padrão alterado quando o conteúdo do objeto for alterado." + }, + "guidelines": [], + "tags": [ + "objects", + "content" + ], + "options": { + "selector": "object[classid]:not(object[classid]:empty)" + } + }, + "pNotUsedAsHeader": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Paragraphen dürfen nicht für Überschriften verwendet werden", + "en": "Paragraphs must not be used for headers", + "nl": "Alinea's worden niet gebruikt als header", + "pt-br": "Os parágrafos não devem ser usados para cabeçalhos" + }, + "description": { + "de": "Überschriften sind für nicht-sehende Benutzer sehr nützlich um in der Seitenstruktur zu navigieren. Einen Paragraphen nur groß oder fett zu formatieren, damit es wie eine Überschrift aussieht, macht daraus keine Überschrift.", + "en": "Headers are extremely useful for non-sighted users to navigate the structure of the page. Formatting a paragraph to just be big or bold, while it might visually look like a header, does not make it a header.", + "nl": "Headers van h1 - h6 zijn handig voor blinde en slechtziende gebruikers om door een pagina te navigeren. Maak alinea's daarom niet op zodat deze lijkt op een header. Dit werkt verwarrend.", + "pt-br": "Cabeçalhos são extremamente úteis para usuários sem visão para navegar na estrutura da página. Formatando um parágrafo para ser apenas grande ou negrito, enquanto ele pode visualmente se parecer com um cabeçalho, não torná-lo um cabeçalho." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "G141", + "H42" + ] + }, + "2.4.10": { + "techniques": [ + "G141" + ] + } + } + }, + "tags": [ + "header", + "content" + ], + "callback": "pNotUsedAsHeader" + }, + "paragraphIsWrittenClearly": { + "type": "custom", + "testability": 0.5, + "guidelines": { + "wcag": { + "3.1.5": { + "techniques": [ + "G86" + ] + } + } + }, + "tags": [ + "language", + "content" + ], + "components": [ + "textStatistics" + ], + "callback": "paragraphIsWrittenClearly" + }, + "passwordHasLabel": { + "type": "label", + "testability": 1, + "title": { + "de": "Alle \"password\"-Eingabeelemente sollten ein dazugehöriges Label haben", + "en": "All password input elements should have a corresponding label", + "nl": "Alle paswoordinvoerelementen hebben een bijbehorend label", + "pt-br": "Todos os elementos de entrada de senha devem ter um rótulo correspondente" + }, + "description": { + "de": "Alle input-Elemente vom Typ \"password\" sollten ein dazugehöriges label-Element haben. Bildschirmleseprogramme haben oftmals eine Formular-Modus, in dem nur Labels vorgelesen werden.", + "en": "All input elements with a type of \"password\"should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user", + "nl": "Alle input-elementen van het type \"paswoord\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen.", + "pt-br": "Todos os elementos input com um tipo de \"senha\" devem ter um elemento label correspondente. Os leitores de tela costumam entrar em um \"modo de formulário\" onde somente o texto da etiqueta é lido em voz alta para o usuário" + }, + "guidelines": { + "508": [ + "n" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44", + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "label" + ], + "options": { + "selector": "input[type=password]" + } + }, + "passwordLabelIsNearby": { + "type": "labelProximity", + "testability": 0.5, + "title": { + "de": "Alle \"password\"-Eingabeelemente sollten ein dazugehöriges Label haben", + "en": "All \"password\" input elements have a label that is close", + "nl": "Alle paswoordinvoerelementen hebben een label dat dicht bij het element staat", + "pt-br": "Todos os elementos de entrada de \"senha\" têm um rótulo que está próximo" + }, + "description": { + "de": "Alle Eingabeelemente vom Typ \"password\" müssen ein dazugehöriges Label haben das in der Nähe des Eingabeelements positioniert ist. Nutzer von Bildschirmvergrößerung oder mit reduzierten räumlichen Fähigkeiten werden bei der Verwendung eines Formularelements behindert, wenn das Label für das Element weit entfernt positioniert ist.", + "en": "All input elements of type \"password\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.", + "nl": "Alle inputelementen van het type \"paswoord\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat.", + "pt-br": "Todos os elementos de entrada do tipo \"senha\" devem ter um rótulo correspondente que esteja próximo ao elemento do formulário. Usuários de ampliação de tela ou com habilidades espaciais reduzidas são impedidos de usar um elemento de formulário se o rótulo para esse elemento estiver localizado longe." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "labelProximity" + ], + "options": { + "selector": "input[type=password]" + } + }, + "preShouldNotBeUsedForTabularLayout": { + "type": "custom", + "testability": 0, + "title": { + "de": "Pre-Elemente sollten nicht für Tabellendaten verwendet werden", + "en": "Pre elements should not be used for tabular data", + "nl": "Pre-elementen worden niet gebruikt om data als tabel te rangschikken", + "pt-br": "Pré-elementos não devem ser usados para dados tabulares" + }, + "description": { + "de": "Wenn ein pre-Element für Tabellendaten verwendet wird, ändern Sie die Daten sodass eine wohlgeformte Tabelle verwendet wird.", + "en": "If a pre element is used for tabular data, change the data to use a well-formed table.", + "nl": "Als een pre-element wordt gebruikt om data als tabel te rangschikken, verander de data dan zodat je een echte tabel kunt maken.", + "pt-br": "Se um elemento pre for usado para dados tabulares, altere os dados para usar uma tabela bem formada." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F33", + "F34", + "F48" + ] + }, + "1.3.2": { + "techniques": [ + "F33", + "F34" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "callback": "preShouldNotBeUsedForTabularLayout" + }, + "radioHasLabel": { + "type": "label", + "testability": 1, + "title": { + "de": "Alle \"Radio\"-Eingabeelemente müssen ein dazugehöriges Label haben", + "en": "All \"radio\" input elements have a corresponding label", + "nl": "Alle invoerelementen van het type \"radio\" hebben een bijbehorend label", + "pt-br": "Todos os elementos de entrada \"radio\" têm uma etiqueta correspondente" + }, + "description": { + "de": "Alle Input-Elemente vom Typ \"radio\" sollten ein dazugehöriges label-Element haben. Bildschirmleseprogramme verwenden oft einen Formularmodus, wenn nur Label-Text vorgelesen wird.", + "en": "All input elements of type \"radio\" should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user", + "nl": "Alle input-elementen van het \"radio\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen.", + "pt-br": "Todos os elementos input do tipo \"radio\" devem ter um elemento label correspondente. Os leitores de tela costumam entrar em um \"modo de formulário\" onde somente o texto do rótulo é lido em voz alta para o usuário" + }, + "guidelines": { + "508": [ + "n" + ], + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44", + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "label" + ], + "options": { + "selector": "input[type=radio]" + } + }, + "radioLabelIsNearby": { + "type": "labelProximity", + "testability": 0.5, + "title": { + "de": "Alle \"radio\"-Elemente sollten ein dazugehöriges Label haben", + "en": "All \"radio\" input elements have a label that is close", + "nl": "Alle invoerelementen van het type \"radio\" hebben een label dat dicht bij het element staat", + "pt-br": "Todos os elementos de entrada \"radio\" têm um rótulo que está próximo" + }, + "description": { + "de": "Alle Eingabeelemente vom Typ \"radio\" müssen ein dazugehöriges Label haben das in der Nähe des Eingabeelements positioniert ist. Nutzer von Bildschirmvergrößerung oder mit reduzierten räumlichen Fähigkeiten werden bei der Verwendung eines Formularelements behindert, wenn das Label für das Element weit entfernt positioniert ist.", + "en": "All input elements of type \"radio\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.", + "nl": "Alle inputelementen van het type \"radio\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat.", + "pt-br": "Todos os elementos de entrada do tipo \"radio\" devem ter um rótulo correspondente que esteja próximo ao elemento do formulário. Usuários de ampliação de tela ou com habilidades espaciais reduzidas são impedidos de usar um elemento de formulário se o rótulo para esse elemento estiver localizado longe." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "labelProximity" + ], + "options": { + "selector": "input[type=radio]" + } + }, + "radioMarkedWithFieldgroupAndLegend": { + "type": "selector", + "testability": 1, + "title": { + "de": "Alle Gruppen von Radio-Buttons sind mit Fieldsets und Legend-Elementen gekennzeichnet", + "en": "All radio button groups are marked using fieldset and legend elementsTodos os grupos de botões de rádio são marcados usando os elementos fieldset e legenda", + "nl": "Alle groepjes van radio buttons zijn gemarkeerd met fieldset- en legend-elementen", + "pt-br": "Todos os grupos de botões de radio são marcados usando os elementos fieldset e legenda" + }, + "description": { + "de": "Formular-Elemente müssen sowohl ein Fieldset als auch Legend-Elemente beinhalten, wenn Radio-Buttons verwendet werden", + "en": "Form element content must contain both fieldset and legend elements if there are related radio buttons.", + "nl": "Content van formulierelementen moeten zowel fieldset- als legend-elementen bevatten als er gerelateerde radio buttons instaan.", + "pt-br": "O conteúdo do elemento de formulário deve conter elementos de campo e legenda se houver botões radio relacionados." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H71" + ] + }, + "3.3.2": { + "techniques": [ + "H71" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "input[type=radio]:not(fieldset input[type=radio])" + } + }, + "scriptContentAccessibleWithScriptsTurnedOff": { + "type": "selector", + "testability": 0, + "title": { + "de": "Der Inhalt einer Seite sollte auch bei deaktivierten Scripten verfügbar sein", + "en": "Content on the page should still be available if scripts are disabled", + "nl": "Content op de pagina moet beschikbaar blijven als scripts zijn uitgeschakeld", + "pt-br": "O conteúdo da página ainda deve estar disponível se os scripts estiverem desativados" + }, + "description": { + "de": "Es sollte geprüft werden, ob der Seiteninhalt immer noch verfügbar ist, wenn Skripte im Browser deaktiviert wurden", + "en": "All scripts should be assessed to see if, when the user is browsing with scrips turned off, the page content is still available.", + "nl": "Alle scripts moeten gecontroleerd worden of, wanneer een gebruiker scripts heeft uitgezet, de content van de pagina nog steeds beschikbaar is.", + "pt-br": "Todos os scripts devem ser avaliados para ver se, quando o usuário está navegando com scripts desligados, o conteúdo da página ainda está disponível." + }, + "guidelines": [], + "tags": [ + "javascript" + ], + "options": { + "selector": "script" + } + }, + "scriptInBodyMustHaveNoscript": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Skripte sollten ein dazugehöriges \"noscript\"-Element haben", + "en": "Scripts should have a corresponding \"noscript\" element", + "nl": "Scripts moeten een bijbehorend \"noscript\"-element hebben", + "pt-br": "Os scripts devem ter um elemento \"noscript\" correspondente" + }, + "description": { + "de": "Skripte sollten von einem noscript-Element gefolgt werden, um dem Benutzer einen alternativen Zugang zum Inhalt zu bieten", + "en": "Scripts should be followed by a noscripts element to guide the user to content in an alternative way.", + "nl": "Scripts moeten worden gevolgd door een noscripts-element om de gebruiker de weg te wijzen naar de content op een andere manier.", + "pt-br": "Os scripts devem ser seguidos por um elemento noscripts para orientar o usuário para o conteúdo de uma maneira alternativa." + }, + "guidelines": { + "508": [ + "l" + ] + }, + "tags": [ + "javascript" + ], + "options": { + "selector": "html:not(html:has(noscript)):has(script) body" + } + }, + "scriptOnclickRequiresOnKeypress": { + "type": "event", + "testability": 1, + "title": { + "de": "Wenn ein Element ein \"onclick\"-Attribut hat sollte es auch ein \"onkeypress\"-Attribut haben", + "en": "If an element has an \"onclick\" attribute it should also have an \"onkeypress\" attribute", + "nl": "Als een element een \"onclick\"-attribuut heeft, moet het ook een \"onkeypress\"-attribuut hebben", + "pt-br": "Se um elemento tem um atributo \"onclick\", ele também deve ter um atributo \"onkeypress\"" + }, + "description": { + "de": "Wenn ein Element ein \"onclick\"-Attribut hat sollte es auch ein \"onkeypress\"-Attribut haben", + "en": "If an element has an \"onclick\" attribute it should also have an \"onkeypress\" attribute", + "nl": "Als een element een \"onclick\"-attribuut heeft, moet het ook een \"onkeypress\"-attribuut hebben", + "pt-br": "Se um elemento tem um atributo \"onclick\", ele também deve ter um atributo \"onkeypress\"" + }, + "guidelines": { + "508": [ + "l" + ], + "wcag": { + "2.1.1": { + "techniques": [ + "G90", + "SCR2", + "SCR20" + ] + }, + "2.1.3": { + "techniques": [ + "G90", + "SCR20" + ] + } + } + }, + "tags": [ + "javascript" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "correspondingEvent": "onkeypress", + "searchEvent": "onclick" + } + }, + "scriptOndblclickRequiresOnKeypress": { + "type": "event", + "testability": 1, + "title": { + "de": "Jedes Element mit einem \"ondblclick\"-Attribut sollte auch eine Tastatur-Aktion haben", + "en": "Any element with an \"ondblclick\" attribute should have a keyboard-related action as well", + "nl": "Elk element met een \"ondblclick\"-attribuut moet een vergelijkbare actie hebben die kan worden uitgevoerd met een toetsenbord", + "pt-br": "Qualquer elemento com um atributo \"ondblclick\" também deve ter uma ação relacionada ao teclado" + }, + "description": { + "de": "Wenn ein Element ein \"ondblclick\"-Attribut hat, sollte es auch eine Tastatur-Aktion haben", + "en": "If an element has an \"ondblclick\" attribute, it should also have a keyboard-related action.", + "nl": "Als een element een \"ondblclick\"-attribuut heeft, moet het ook een actie bevatten die kan worden uitgevoerd met een toetsenbord.", + "pt-br": "Se um elemento tem um atributo \"ondblclick\", ele também deve ter uma ação relacionada ao teclado." + }, + "guidelines": { + "508": [ + "l" + ], + "wcag": { + "2.1.1": { + "techniques": [ + "G90", + "SCR2", + "SCR20" + ] + }, + "2.1.3": { + "techniques": [ + "G90", + "SCR20" + ] + } + } + }, + "tags": [ + "javascript" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "correspondingEvent": "onkeypress", + "searchEvent": "ondblclick" + } + }, + "scriptOnmousedownRequiresOnKeypress": { + "type": "event", + "testability": 1, + "title": { + "de": "Wenn ein Element ein \"mousedown\"-Attribut hat sollte es auch ein \"onkeydown\"-Attribut haben", + "en": "If an element has a \"mousedown\" attribute it should also have an \"onkeydown\" attribute", + "nl": "Als een element een \"mousedown\"-attribuut heeft moet het ook een \"onkeydown\"-attribuut hebben", + "pt-br": "Se um elemento tem um atributo \"mousedown\" ele também deve ter um atributo \"onkeydown\"" + }, + "description": { + "de": "Wenn ein Element ein \"mousedown\"-Attribut hat sollte es auch ein \"onkeydown\"-Attribut haben", + "en": "If an element has a \"mousedown\" attribute it should also have an \"onkeydown\" attribute.", + "nl": "Als een element een \"mousedown\"-attribuut heeft moet het ook een \"onkeydown\"-attribuut hebben.", + "pt-br": "Se um elemento tem um atributo \"mousedown\" ele também deve ter um atributo \"onkeydown\"" + }, + "guidelines": { + "508": [ + "l" + ], + "wcag": { + "2.1.1": { + "techniques": [ + "G90", + "SCR2", + "SCR20" + ] + }, + "2.1.3": { + "techniques": [ + "G90", + "SCR20" + ] + } + } + }, + "tags": [ + "javascript" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "correspondingEvent": "onkeydown", + "searchEvent": "onmousedown" + } + }, + "scriptOnmousemove": { + "type": "event", + "testability": 1, + "title": { + "de": "Jedes Element mit einem \"onmousemove\"-Attribut sollte auch eine Tastatur-Aktion haben", + "en": "Any element with an \"onmousemove\" attribute should have a keyboard-related action as well", + "nl": "Elk element met een \"onmousemove\"-attribuut moet een vergelijkbare actie hebben die kan worden uitgevoerd met een toetsenbord", + "pt-br": "Qualquer elemento com um atributo \"onmousemove\" também deve ter uma ação relacionada ao teclado" + }, + "description": { + "de": "Wenn ein Element ein \"onmousemove\"-Attribut hat, sollte es auch eine Tastatur-Aktion haben", + "en": "If an element has an \"onmousemove\" attribute it should have a keyboard-related action as well.", + "nl": "Als een element een \"onmousemove\"-attribuut heeft, moet het een vergelijkbare actie hebben die kan worden uitgevoerd met een toetsenbord.", + "pt-br": "Se um elemento tiver um atributo \"onmousemove\", ele também deverá ter uma ação relacionada ao teclado." + }, + "guidelines": { + "508": [ + "l" + ], + "wcag": { + "2.1.1": { + "techniques": [ + "G90", + "SCR2", + "SCR20" + ] + }, + "2.1.3": { + "techniques": [ + "G90", + "SCR20" + ] + } + } + }, + "tags": [ + "javascript" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "correspondingEvent": "onkeypress", + "searchEvent": "onmousemove" + } + }, + "scriptOnmouseoutHasOnmouseblur": { + "type": "event", + "testability": 1, + "title": { + "de": "Wenn ein Element ein \"onmouseout\"-Attribut hat sollte es auch ein \"onblur\"-Attribut haben", + "en": "If an element has a \"onmouseout\" attribute it should also have an \"onblur\" attribute", + "nl": "Als een element een \"onmouseout\"-attribuut heeft, moet het ook een \"onblur\" attribuut hebben", + "pt-br": "Se um elemento tem um atributo \"onmouseout\", ele também deve ter um atributo \"onblur\"" + }, + "description": { + "de": "Wenn ein Element ein \"mousedown\"-Attribut hat sollte es auch ein \"onkeydown\"-Attribut haben", + "en": "If an element has a \"onmouseout\" attribute it should also have an \"onblur\" attribute.", + "nl": "Als een element een \"onmouseout\"-attribuut heeft, moet het ook een \"onblur\"-attribuut hebben.", + "pt-br": "Se um elemento tem um atributo \"onmouseout\", ele também deve ter um atributo \"onblur\"" + }, + "guidelines": { + "508": [ + "l" + ], + "wcag": { + "2.1.1": { + "techniques": [ + "G90", + "SCR2", + "SCR20" + ] + }, + "2.1.3": { + "techniques": [ + "G90", + "SCR20" + ] + } + } + }, + "tags": [ + "javascript" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "correspondingEvent": "onblur", + "searchEvent": "onmouseout" + } + }, + "scriptOnmouseoverHasOnfocus": { + "type": "event", + "testability": 1, + "title": { + "de": "Wenn ein Element ein \"onmouseover\"-Attribut hat sollte es auch ein \"onfocus\"-Attribut haben", + "en": "If an element has a \"onmouseover\" attribute it should also have an \"onfocus\" attribute", + "nl": "Als een element een \"onmouseover\"-attribuut heeft, moet het ook een \"onfocus\"-attribuut hebben", + "pt-br": "Se um elemento tem um atributo \"onmouseover\" ele também deve ter um atributo \"onfocus\"" + }, + "description": { + "de": "Wenn ein Element ein \"mousedown\"-Attribut hat sollte es auch ein \"onkeydown\"-Attribut haben", + "en": "If an element has a \"onmouseover\" attribute it should also have an \"onfocus\" attribute.", + "nl": "Als een element een \"onmouseover\"-attribuut heeft, moet het ook een \"onfocus\"-attribuut hebben.", + "pt-br": "Se um elemento tem um atributo \"onmouseover\" ele também deve ter um atributo \"onfocus\"." + }, + "guidelines": { + "508": [ + "l" + ], + "wcag": { + "2.1.1": { + "techniques": [ + "G90", + "SCR2", + "SCR20" + ] + }, + "2.1.3": { + "techniques": [ + "G90", + "SCR20" + ] + } + } + }, + "tags": [ + "javascript" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "correspondingEvent": "onfocus", + "searchEvent": "onmouseover" + } + }, + "scriptOnmouseupHasOnkeyup": { + "type": "event", + "testability": 1, + "title": { + "de": "Wenn ein Element ein \"onmouseup\"-Attribut hat sollte es auch ein \"onkeyup\"-Attribut haben", + "en": "If an element has a \"onmouseup\" attribute it should also have an \"onkeyup\" attribute", + "nl": "Als een element een \"onmouseup\"-attribuut heeft, moet het ook een \"onkeyup\"-attribuut hebben", + "pt-br": "Se um elemento tem um atributo \"onmouseup\" ele também deve ter um atributo \"onkeyup\"." + }, + "description": { + "de": "Wenn ein Element ein \"onmouseup\"-Attribut hat sollte es auch ein \"onkeyup\"-Attribut haben", + "en": "If an element has a \"onmouseup\" attribute it should also have an \"onkeyup\" attribute.", + "nl": "Als een element een \"onmouseup\"-attribuut heeft, moet het ook een \"onkeyup\"-attribuut hebben.", + "pt-br": "Se um elemento tem um atributo \"onmouseup\" ele também deve ter um atributo \"onkeyup\"." + }, + "guidelines": { + "508": [ + "l" + ], + "wcag": { + "2.1.1": { + "techniques": [ + "G90", + "SCR2", + "SCR20" + ] + }, + "2.1.3": { + "techniques": [ + "G90", + "SCR20" + ] + } + } + }, + "tags": [ + "javascript" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "correspondingEvent": "onkeyup", + "searchEvent": "onmouseup" + } + }, + "scriptUIMustBeAccessible": { + "type": "selector", + "testability": 0, + "title": { + "de": "Die Benutzerschnittstelle von Skripten sollte barrierefrei sein", + "en": "The user interface for scripts should be accessible", + "nl": "De user interface voor scripts moet toegankelijk zijn", + "pt-br": "A interface de usuário para scripts deve ser acessível" + }, + "description": { + "de": "Alle Skripte sollten dahingehend geprüft werden, ob ihre Benutzerschnittstelle barrierefrei ist", + "en": "All scripts should be assessed to see if their interface is accessible.", + "nl": "Alle scripts moeten gecontroleerd worden op toegankelijkheid van hun interface.", + "pt-br": "Todos os scripts devem ser avaliados para ver se sua interface é acessível." + }, + "guidelines": { + "508": [ + "l" + ] + }, + "tags": [ + "javascript" + ], + "options": { + "selector": "script" + } + }, + "scriptsDoNotFlicker": { + "type": "selector", + "testability": 0, + "title": { + "de": "Skripte sollten kein Flackern des Bildschirms verursachen", + "en": "Scripts should not cause the screen to flicker", + "nl": "Scripts mogen het scherm niet laten knipperen of flitsen", + "pt-br": "Scripts não devem causar a cintilação da tela" + }, + "description": { + "de": "Alle Skripte sollten dahingehend geprüft werden, dass ihre Benutzerschnittstelle nicht flackert", + "en": "All scripts should be assessed to see if their interface does not flicker.", + "nl": "Alle scripts moeten gecontroleerd worden om te zien of zij de interface niet laten knipperen of flitsen.", + "pt-br": "Todos os scripts devem ser avaliados para que se sua interface não pisque." + }, + "guidelines": { + "508": [ + "j" + ], + "wcag": { + "2.2.2": { + "techniques": [ + "F7" + ] + } + } + }, + "tags": [ + "javascript" + ], + "options": { + "selector": "script" + } + }, + "scriptsDoNotUseColorAlone": { + "type": "selector", + "testability": 0, + "title": { + "de": "Die Benutzerschnittstelle von Skripten sollte nicht nur Farben verwenden, um Inhalte zu kommunizieren", + "en": "The interface in scripts should not use color alone", + "nl": "De interface in scripts gebruikt niet alleen maar kleur", + "pt-br": "A interface em scripts não deve usar cor sozinha" + }, + "description": { + "de": "Alle Skripte sollten dahingehend geprüft werden, dass es für ihre Benutzerschnittstelle nicht nötig ist zwischen Farben unterscheiden zu können", + "en": "All scripts should be assessed to see if their interface does not have an interface which requires distinguishing between colors alone.", + "nl": "Alle scripts moeten gecontroleerd worden om te zien of hun interface geen interface heeft die alleen op kleur kan worden onderscheiden.", + "pt-br": "Todos os scripts devem ser avaliados para ver se não possuem uma interface que exige distinção entre cores sozinhas." + }, + "guidelines": { + "508": [ + "c" + ] + }, + "tags": [ + "javascript", + "color" + ], + "options": { + "selector": "script" + } + }, + "selectDoesNotChangeContext": { + "type": "event", + "testability": 1, + "title": { + "de": "\"select\"-Elemente dürfen kein \"onchange\"-Attribut haben", + "en": "\"Select\" elements must not contain an \"onchange\" attribute", + "nl": "\"Select\" elements bevatten geen \"onchange\" attribute", + "pt-br": "Os elementos \"Select\" não devem conter um atributo \"onchange\"" + }, + "description": { + "de": "Aktionen wie \"onchange\" entziehen dem Benutzer die Kontrolle beim Versuch auf der Seite zu navigieren. \"onchange\"-Attribute für Dinge wie Auswahl-Listenmenüs sollten durch Drop-Downs und eine Schaltfläche zur Initiierung der Aktion ersetzt werden.", + "en": "Actions like \"onchange\" can take control away from users who are trying to navigate the page. Using an \"onchange\" attribute for things like select-list menus should instead be replaced with a drop-down and a button which initiates the action.", + "nl": "Acties als \"onchange\" kunnen de controle ontnemen van gebruikers die op een pagina proberen te navigeren. Het gebruik van een \"onchange\"-attribuut voor zaken zoals select-list menu's moet vervangen worden door een drop-down en een knop waarmee je de actie start.", + "pt-br": "Ações como \"onchange\" podem tirar o controle dos usuários que estão tentando navegar na página. O uso de um atributo \"onchange\" para coisas como menus de lista de seleção deve ser substituído por um drop-down e um botão que inicia a ação." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "searchEvent": "onchange", + "selector": "select" + } + }, + "selectHasAssociatedLabel": { + "type": "label", + "testability": 1, + "title": { + "de": "Alle \"select\"-Elemente haben explizit assoziierte Labels", + "en": "All select elements have an explicitly associated label", + "nl": "Alle select-elementen hebben een expliciet bijbehorend label", + "pt-br": "Todos os elementos selecionados têm um rótulo associado explicitamente" + }, + "description": { + "de": "Alle select-Elemente sollten ein dazugehöriges label-Element haben. Bildschirmleseprogramme haben oftmals eine Formular-Modus, in dem nur Labels vorgelesen werden.", + "en": "All select elements should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user", + "nl": "Alle select-elementen moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen.", + "pt-br": "Todos os elementos select devem ter um elemento label correspondente. Os leitores de tela costumam entrar em um \"modo de formulário\" onde somente o texto do rótulo é lido em voz alta para o usuário" + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44", + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "label" + ], + "options": { + "selector": "select" + } + }, + "selectJumpMenu": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Sprungmenüs sollten die Weiterleitung durch Betätigen einer Schaltfläche ausführen, nicht auf einen Statuswechsel des Menüs", + "en": "Select jump menus should jump on button press, not on state change", + "nl": "Select jump menu's moeten springen wanneer de knop wordt gebruikt, niet bij statusverandering", + "pt-br": "Menus de salto devem realizar o encaminhamento ao pressionar um botão, não uma mudança no status do menu" + }, + "description": { + "de": "Wenn Sie ein Sprungmenü verwenden wollen, welches den Benutzer nach Auswahl eines Menüpunktes weiterleitet, sollte die Weiterleitung durch das Betätigen einer Schaltfläche geschehen, nicht durch die Auswahl eines Menüelements", + "en": "If you wish to use a 'Jump' menu with a select item that then redirects users to another page, the jump should occur on the user pressing a button, rather than on the change event of that select element.", + "nl": "Als je een 'Jump'-menu wilt gebruiken met een select item dat gebruikers naar een andere pagina verwijst, moet de verwijzing plaatsvinden als de gebruiker een knop gebruikt en niet op het moment dat het select element verandert.", + "pt-br": "Se você deseja usar um menu de salto com um item de seleção que, em seguida, redireciona os usuários para outra página, o salto deve ocorrer no usuário pressionando um botão, em vez de no evento de alteração desse elemento de seleção." + }, + "guidelines": { + "wcag": { + "3.2.2": { + "techniques": [ + "F37" + ] + }, + "3.2.5": { + "techniques": [ + "F9" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "hasEventListener" + ], + "callback": "selectJumpMenu" + }, + "selectWithOptionsHasOptgroup": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Auswahlelemente in Formularen sollten \"optgroups\" für lange Auswahlen verwenden", + "en": "Form select elements should use optgroups for long selections", + "nl": "Formulier select-elementen moeten optgroups gebruiken voor lange selecties", + "pt-br": "Os elementos de seleção de formulário devem usar optgroup para seleções longas" + }, + "description": { + "de": "Wenn ein Auswahlelement viele Optionen hat, sollten \"optgroups\" zur Unterstützung der Navigation verwendet werden", + "en": "If a select element has many options, use optgroups to help ease navigation.", + "nl": "Als een select-element veel opties heeft, gebruik dan optgroups om navigatie makkelijker te maken.", + "pt-br": "Se um elemento select tiver muitas opções, use optgroups para facilitar a navegação." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H85" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "options": { + "selector": "select:not(select:has(optgroup)) option:nth-child(5)" + } + }, + "siteMap": { + "type": "custom", + "testability": 0, + "title": { + "de": "Webseiten müssen eine Sitemap haben", + "en": "Websites must have a site map", + "nl": "Websites moeten een sitemap hebben", + "pt-br": "Websites devem ter um mapa do site" + }, + "description": { + "de": "Jede Webseite sollte eine Seite haben, die eine Sitemap oder eine andere Methode zur Navigation zu den wichtigsten Seiten anbietet, um den Benutzern von Assistenztechnologien Zeit zu sparen", + "en": "Every web site should have a page which provides a site map or another method to navigate most of the site from a single page to save time for users of assistive devices.", + "nl": "Elke website moet een pagina hebben waarop een sitemap staat of een andere methode om op de site te navigeren vanaf een pagina. Dit spaart gebruikers die hulpmiddelen gebruiken tijd.", + "pt-br": "Cada site deve ter uma página que fornece um mapa do site ou outro método para navegar a maior parte do site a partir de uma única página para economizar tempo para os usuários de dispositivos assistivos." + }, + "guidelines": { + "wcag": { + "2.4.5": { + "techniques": [ + "G63" + ] + }, + "2.4.8": { + "techniques": [ + "G63" + ] + } + } + }, + "tags": [ + "document" + ], + "strings": [ + "siteMap" + ], + "callback": "siteMap" + }, + "skipToContentLinkProvided": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Eine \"Weiter zum Inhalt\"-Verknüpfung sollte auf der Seite eine der ersten Verknüpfungen sein", + "en": "A \"skip to content\" link should exist as one of the first links on the page", + "nl": "Er moet een \"skip to content\"-link zijn als een van de eerste links op de pagina", + "pt-br": "Um link \"pular para o conteúdo\" deve existir como um dos primeiros links na página" + }, + "description": { + "de": "Eine \"Weiter zum Inhalt\"-Verknüpfung sollte auf der Seite eine der ersten Verknüpfungen sein", + "en": "A link reading \"skip to content\" should be the first link on a page.", + "nl": "Er moet een link zijn om naar de content te navigeren als een van de eerste links op de pagina.", + "pt-br": "Um link de leitura \"pular para conteúdo\" deve ser o primeiro link em uma página." + }, + "guidelines": { + "508": [ + "o" + ], + "wcag": { + "2.4.1": { + "techniques": [ + "G1" + ] + } + } + }, + "tags": [ + "document" + ], + "strings": [ + "skipContent" + ], + "callback": "skipToContentLinkProvided" + }, + "svgContainsTitle": { + "type": "selector", + "testability": 1, + "title": { + "de": "Inline-SVG sollte \"title\"-Elemente haben", + "en": "Inline SVG should use Title elements", + "nl": "Inline SVG moet titelelementen gebruiken", + "pt-br": "O SVG inline deve usar elementos de título" + }, + "description": { + "de": "Jede inline SVG-Grafik sollte ein eingebettetes title-Element haben", + "en": "Any inline SVG image should have an embedded title element.", + "nl": "Elke inline SVG-afbeelding moet een ingebed title-element hebben.", + "pt-br": "Qualquer imagem SVG in-line deve ter um elemento title incorporado." + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "F65" + ] + } + } + }, + "tags": [ + "image", + "svg", + "content" + ], + "options": { + "selector": "svg:not(svg:has(title))" + } + }, + "tableAxisHasCorrespondingId": { + "type": "custom", + "testability": 1, + "title": { + "de": "\"axis\"-Attribute sollten dazugehörige IDs haben", + "en": "Axis attribute should have corresponding IDs", + "nl": "Axis-attributen moeten bijbehorende IDs hebben", + "pt-br": "O atributo Axis deve ter IDs correspondentes" + }, + "description": { + "de": "Wenn ein \"axis\"-Attribut verwendet wird, um Zellen zu gruppieren, sollte sichergestellt sein, dass das Ziel-Element mit derselben ID vorhanden ist", + "en": "When using the axis attribute to group cells together, ensure they have a target element with the same ID.", + "nl": "Wanneer er axis-attributen gebruikt worden om cellen te groeperen, zorg er dan voor dat hun doelelement hetzelfde ID heeft.", + "pt-br": "Ao usar o atributo de eixo para agrupar células em conjunto, certifique-se de que eles tenham um elemento de destino com o mesmo ID." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F17" + ] + }, + "4.1.1": { + "techniques": [ + "F17" + ] + } + } + }, + "callback": "tableAxisHasCorrespondingId" + }, + "tabIndexFollowsLogicalOrder": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Die Tabulatorreihenfolge eines Dokumentes ist logisch", + "en": "The tab order of a document is logical", + "nl": "De tabvolgorde van een document is logisch", + "pt-br": "A ordem de tabulação de um documento é lógica" + }, + "description": { + "de": "Prüfen Sie, ob die Tabulatorreihenfolge der Seite logisch ist", + "en": "Check that the tab order of a page is logical.", + "nl": "Controleer of de tabvolgorde van een pagina logisch is.", + "pt-br": "Verifique se a ordem de tabulação de uma página é lógica." + }, + "guidelines": { + "wcag": { + "2.4.3": { + "techniques": [ + "H4" + ] + } + } + }, + "tags": [ + "document" + ], + "callback": "tabIndexFollowsLogicalOrder" + }, + "tableCaptionIdentifiesTable": { + "type": "selector", + "testability": 0, + "title": { + "de": "Überschriften sollten ihre Tabelle identifizieren", + "en": "Captions should identify their table", + "nl": "Beschrijvingen moeten hun tabellen identificeren", + "pt-br": "As legendas devem identificar as suas tabelas" + }, + "description": { + "de": "Stellen Sie sicher, dass eine Tabellenüberschrift die Tabelle mit Name, Figurennummer etc. identifiziert", + "en": "Check to make sure that a table's caption identifies the table with a name, figure number, etc.", + "nl": "Controleer of de beschrijving van een tabel de tabel identificeert met een naam, nummer en dergelijke.", + "pt-br": "Verifique se a legenda de uma tabela identifica a tabela com um nome, número da figura, etc." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H39" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "options": { + "selector": "caption" + } + }, + "tableComplexHasSummary": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Komplexe Tabellen sollten eine Zusammenfassung haben", + "en": "Complex tables should have a summary", + "nl": "Complexe tabellen moeten een samenvatting hebben", + "pt-br": "As tabelas complexas devem ter um resumo" + }, + "description": { + "de": "Wenn eine Tabelle komplex ist (z.B. Zellen mit \"colspan\"-Attributen hat), sollte die Tabelle eine Zusammenfassung haben", + "en": "If a table is complex (for example, has some cells with \"colspan\" attributes, the table should have a summary.", + "nl": "Als een tabel complex is (bijvoorbeeld, als er cellen zijn met \"colspan\"-attributen, moet de tabel een samenvatting hebben.", + "pt-br": "Se uma tabela é complexa (por exemplo, tem algumas células com atributos \"colspan\", esta deve ter um resumo." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H39" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "options": { + "selector": "table:not(table[summary], table:has(caption))" + } + }, + "tableDataShouldHaveTh": { + "type": "selector", + "testability": 1, + "title": { + "de": "Datentabellen sollten eine Überschrift haben", + "en": "Data tables should contain a header", + "nl": "Datatabellen moeten \"th\"-elementen bevatten", + "pt-br": "As tabelas de dados devem conter um cabeçalho" + }, + "description": { + "de": "Tabellen die Daten beinhalten (anders als Layout-Tabellen), sollten korrekte \"header\"-Elemente haben, um sie für Bildschirmleseprogramme hervorzuheben und die Struktur des Dokumentes zu verbessern", + "en": "Tables which contain data (as opposed to layout tables) should contain proper table header elements to mark them for screen readers and enhance the structure of the document.", + "nl": "Tabellen die data bevatten (in tegenstelling tot lay-out tabellen) moeten th-elementen bevatten om koppen te markeren voor schermlezers en om de structuur van het document te verbeteren.", + "pt-br": "Tabelas que contêm dados (em oposição a tabelas de layout) devem conter elementos de cabeçalho de tabela adequada para marcá-los para leitores de tela e aprimorar a estrutura do documento." + }, + "guidelines": { + "508": [ + "g" + ], + "wcag": { + "1.3.1": { + "techniques": [ + "F91" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "options": { + "selector": "table:not(table:has(th))" + } + }, + "tableHeaderLabelMustBeTerse": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Tabellenüberschriften müssen kurz sein", + "en": "Table header lables must be terse", + "nl": "Tabelkoppen moeten bondig zijn", + "pt-br": "As etiquetas de cabeçalho da tabela devem ser concisas" + }, + "description": { + "de": "Das \"abbr\"-Attribut für Tabellenüberschriften muss kurz sein (max. 20 Zeichen)", + "en": "The \"abbr\" attribute for table headers must be terse (less than 20 characters long).", + "nl": "Het \"abbr\"-attribuut voor tabelkoppen moet bondig zijn (minder dan 20 tekens lang).", + "pt-br": "O atributo \"abbr\" para cabeçalhos de tabela deve ser conciso (menos de 20 caracteres)." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "callback": "tableHeaderLabelMustBeTerse" + }, + "tableIsGrouped": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Kennzeichnen Sie die Bereiche einer Tabelle mit \"thead\" und \"tbody\"", + "en": "Mark up the areas of tables using thead and tbody", + "nl": "Gebruik thead en tbody voor tabellen", + "pt-br": "Marcar as áreas das tabelas usando thead e tbody" + }, + "description": { + "de": "Kennzeichnen Sie die Bereiche einer Tabelle mit \"thead\" und \"tbody\"", + "en": "Mark up the areas of tables using thead and tbody.", + "nl": "Gebruik thead en tbody voor tabellen.", + "pt-br": "Marcar as áreas das tabelas usando thead e tbody" + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "options": { + "selector": "table:not(table:has(thead), table:has(tfoot))" + } + }, + "tableLayoutDataShouldNotHaveTh": { + "type": "custom", + "testability": 0, + "title": { + "de": "Layout-Tabellen sollten keine \"th\"-Elemente beinhalten", + "en": "Layout tables should not contain \"th\" elements", + "nl": "Lay-out tabellen bevatten geen \"th\"-elementen", + "pt-br": "Tabelas de layout não devem conter \"th\" elementos" + }, + "description": { + "de": "Tabellen, die nur für das Layout verwendet werden (im Gegesatz zu Datentabellen), should not contain th-Elemente, welche die Tabelle als Datentabelle erscheinen lassen", + "en": "Tables which are used purely for layout (as opposed to data tables), should not contain th elements, which would make the table appear to be a data table.", + "nl": "Tabellen die alleen voor lay-out worden gebruikt (in tegenstelling tot datatabellen), moeten geen th-elementen bevatten, omdat deze de indruk wekken dat het een datatabel betreft.", + "pt-br": "As tabelas que são usadas puramente para o layout (em oposição às tabelas de dados), não devem conter elementos th , o que tornaria a tabela aparente ser uma tabela de dados." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F46" + ] + } + } + }, + "tags": [ + "table", + "layout", + "content" + ], + "callback": "tableLayoutDataShouldNotHaveTh" + }, + "tableLayoutHasNoCaption": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Layout-Tabellen haben kein \"caption\"-Element", + "en": "All tables used for layout have no \"caption\" element", + "nl": "Alle tabellen die alleen voor lay-out worden gebruikt hebben geen \"caption\"-element", + "pt-br": "Todas as tabelas usadas para o layout não têm elemento \"caption\"" + }, + "description": { + "de": "Wenn eine Tabelle keine Daten beinhaltet und nur für das Layout verwendet wird, sollte sie kein caption-Element beinhalten", + "en": "If a table contains no data, and is used simply for layout, then it should not contain a caption element.", + "nl": "Als een tabel geen data bevat en alle voor lay-out wordt gebruikt, moet hij geen caption-element krijgen.", + "pt-br": "Se uma tabela não contém dados e é usada apenas para layout, ela não deve conter um elemento caption ." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F46" + ] + } + } + }, + "tags": [ + "table", + "layout", + "content" + ], + "callback": "tableLayoutHasNoCaption" + }, + "tableLayoutHasNoSummary": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Alle Layout-Tabellen haben keine oder eine leere Zusammenfassung", + "en": "All tables used for layout have no summary or an empty summary", + "nl": "Alle tabellen die alleen voor lay-out worden gebruikt hebben geen samenvatting", + "pt-br": "Todas as tabelas utilizadas para layout não têm resumo ou um resumo vazio" + }, + "description": { + "de": "Wenn eine Tabelle keine Daten beinhaltet, sondern nur für das Layout verwendet wird, sollte sie kein \"summary\"-Attribut haben", + "en": "If a table contains no data, and is used simply for layout, then it should not have a \"summary\" attribute.", + "nl": "Als een tabel geen data bevat en alleen voor lay-out wordt gebruikt, moet hij geen \"summary\"-attribuut krijgen.", + "pt-br": "Se uma tabela não contém dados e é usada simplesmente para layout, ela não deve ter um atributo \"summary\"." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F46" + ] + } + } + }, + "tags": [ + "table", + "layout", + "content" + ], + "callback": "tableLayoutHasNoSummary" + }, + "tableLayoutMakesSenseLinearized": { + "type": "custom", + "testability": 0, + "title": { + "de": "Alle Layout-Tabellen sollten entfernt werden können und die Seite ist dennoch sinnvoll", + "en": "All tables used for layout should make sense when removed", + "nl": "Als tabellen voor lay-out worden gebruikt moet de pagina nog duidelijk blijven als de tabel wordt verwijderd", + "pt-br": "Todas as tabelas usadas para layout devem fazer sentido quando removidas" + }, + "description": { + "de": "Wenn ein table-Element nur für Layoutzwecke verwendet wird, sollte der Inhalt der Tabelle sinnvoll sein, auch wenn die Tabelle entfernt wird", + "en": "If a table element is used for layout purposes only, then the content of the table should make sense if the table is linearized.", + "nl": "Als een table-element alleen voor lay-out-doeleinden wordt gebruikt, moet de inhoud van de tabel nog steeds duidelijk zijn als de tabel wordt verwijderd.", + "pt-br": "Se um elemento table for usado somente para fins de layout, o conteúdo da tabela deve fazer sentido se a tabela for linearizada." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "G57" + ] + }, + "4.1.1": { + "techniques": [ + "F49" + ] + } + } + }, + "tags": [ + "table", + "layout", + "content" + ], + "callback": "tableLayoutMakesSenseLinearized" + }, + "tableNotUsedForLayout": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Tabellen sollte nicht für Layouts verwendet werden", + "en": "Tables should not be used for layout", + "nl": "Tabellen moet niet worden gebruikt voor lay-out", + "pt-br": "Tabelas não devem ser usadas para layout" + }, + "description": { + "de": "Tabellen dienen der Darstellung von Daten, nicht für die Umsetzung von Layouts. Verwenden Sie stattdessen HTML und CSS.", + "en": "Tables are for data, not for creating a page layout. Consider using standard HTML and CSS techniques instead.", + "nl": "Tabellen zijn voor data, niet om een pagina op te maken. Gebruik hiervoor HTML en CSS.", + "pt-br": "As tabelas são para dados, não para criar um layout de página. Considere usar técnicas HTML e CSS padrão." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "F49" + ] + } + } + }, + "tags": [ + "table", + "layout", + "content" + ], + "callback": "tableNotUsedForLayout" + }, + "tableShouldUseHeaderIDs": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Tabellenzellen verwenden IDs um Überschriften zu identifizieren", + "en": "Table cells use IDs to identify headers", + "nl": "Tabelcellen gebruiken IDs om koppen te identificeren", + "pt-br": "As células da tabela usam IDs para identificar cabeçalhos" + }, + "description": { + "de": "Wenn eine Tabelle nicht für das Layout verwendet wird, sollte es IDs und Überschriften-Attribute verwenden, um Tabellenüberschriften zu identifizieren", + "en": "If a table is not being used for layout, it should use IDs and header attributes to identify table headers.", + "nl": "Een tabel moet IDs en header-attributen gebruiken om tabelkoppen te identificeren.", + "pt-br": "Se uma tabela não estiver sendo usada para layout, ela deve usar IDs e atributos de cabeçalho para identificar cabeçalhos de tabela." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H43" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "callback": "tableShouldUseHeaderIDs" + }, + "tableSummaryDescribesTable": { + "type": "selector", + "testability": 0, + "title": { + "de": "Tabellenzusammenfassungen sollten die Navigation und Struktur der Tabelle beschreiben", + "en": "Table summaries should describe the navigation and structure of the table", + "nl": "Tabelsamenvattingen moeten de navigatie en structuur van de tabel beschrijven", + "pt-br": "Os resumos das tabelas devem descrever a navegação ea estrutura da tabela" + }, + "description": { + "de": "summary-Elemente von Tabellen sollten die Navgation und Struktur der Tabelle erläutern und einen Überblick über den Inhalt der Tabelle bieten", + "en": "Table summary elements should describe the navigation tools and structure of the table, as well as provide an overview of what the table describes.", + "nl": "Tabel \"summary\"-elementen moeten de navigatie en structuur van de tabel beschrijven, en een overzicht bieden van wat er in de tabel staat.", + "pt-br": "Os elementos da tabela summary devem descrever as ferramentas de navegação ea estrutura da tabela, bem como fornecer uma visão geral do que a tabela descreve." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "options": { + "selector": "table[summary]" + } + }, + "tableSummaryDoesNotDuplicateCaption": { + "type": "custom", + "testability": 1, + "title": { + "de": "\"summary\"-Elemente von Tabellen sollten nicht den Inhalt des \"caption\"-Elements wiederholen", + "en": "Table \"summary\" elements should not duplicate the \"caption\" element", + "nl": "Tabel \"summary\"-elementen mogen niet hetzelfde zijn als het \"caption\"-element", + "pt-br": "Tabela \"resumo\" elementos não devem duplicar o elemento \"legenda\"" + }, + "description": { + "de": "Die Zusammenfassung und die Überschrift einer Tabelle müssen sich unterscheiden, da beide unterschiedliche Informationen liefern. Ein caption-ELement identifiziert die Tabelle, während ein \"summary\"-Attribut den Tabelleninhalt beschreibt", + "en": "The summary and the caption must be different, as both provide different information. A caption. /code element identifies the table, while the \"summary\" attribute describes the table contents.", + "nl": "De samenvatting en beschrijving van een tabel moeten verschillen, want ze bieden verschillende informatie. Een caption-element identificeert welke tabel het betreft en het \"summary\"-attribuut beschrijft de inhoud van de tabel.", + "pt-br": "O resumo ea legenda devem ser diferentes, pois ambos fornecem informações diferentes. Uma legenda . / Code identifica a tabela, enquanto o atributo \"summary\" descreve o conteúdo da tabela." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "callback": "tableSummaryDoesNotDuplicateCaption" + }, + "tableSummaryIsEmpty": { + "type": "placeholder", + "testability": 0.5, + "title": { + "de": "Alle Datentabellen sollten eine Zusammenfassung haben", + "en": "All data tables should have a summary", + "nl": "Alle datatabellen moeten een samenvatting hebben", + "pt-br": "Todas as tabelas de dados devem ter um resumo" + }, + "description": { + "de": "Wenn eine Tabelle Daten enthält, sollte sie ein \"summary\"-Attribut haben", + "en": "If a table contains data, it should have a \"summary\" attribute.", + "nl": "Als een tabel data bevat, moet hij een \"summary\"-attribuut hebben.", + "pt-br": "Se uma tabela contiver dados, ele deve ter um atributo \"summary\"." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "components": [ + "placeholder" + ], + "options": { + "attribute": "summary", + "empty": true, + "selector": "table[summary]" + } + }, + "tableSummaryIsNotTooLong": { + "type": "custom", + "testability": 0, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "callback": "tableSummaryIsNotTooLong" + }, + "tableSummaryIsSufficient": { + "type": "selector", + "testability": 0, + "title": { + "de": "Alle Datentabellen sollten eine adäquate Zusammenfassung haben", + "en": "All data tables should have an adequate summary", + "nl": "Alle datatabellen moeten een toepasselijke samenvatting hebben", + "pt-br": "Todas as tabelas de dados devem ter um resumo" + }, + "description": { + "de": "Wenn eine Tabelle Daten beinhaltet, sollte es ein \"summary\"-Attribut haben, das den Sinn und Zweck der Tabelle komplett beschreibt", + "en": "If a table contains data, it should have a \"summary\" attribute that completely communicates the function and use of the table.", + "nl": "Als een tabel data bevat, moet er een \"summary\"-attribuut zijn dat de functie en het doel van de tabel duidelijk maakt.", + "pt-br": "Se uma tabela contiver dados, ele deve ter um atributo \"summary\" que comunique completamente a função eo uso da tabela." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "options": { + "selector": "table[summary]" + } + }, + "tableUseColGroup": { + "type": "custom", + "testability": 0, + "title": { + "de": "Gruppieren Sie Spalten mit \"colgroup\"-Elementen oder \"col\"-Elementen", + "en": "Group columns using \"colgroup\" or \"col\" elements", + "nl": "Groepeer kolommen met \"colgroup\"- of \"col\"-elementen", + "pt-br": "Colunas de grupo usando elementos \"colgroup\" ou \"col\"" + }, + "description": { + "de": "Um komplexe Tabellenüberschriften sinnvoll zu gestalten, verwenden Sie colgroup oder col um sie zu gruppieren", + "en": "To help complex table headers make sense, use colgroup or col to group them together.", + "nl": "Maak complexe tabelkoppen duidelijker door \"colgroup\"- of \"col\"-elementen te gebruiken om ze te groeperen.", + "pt-br": "Para ajudar os cabeçalhos de tabela complexos fazer sentido, use colgroup ou col para agrupá-los." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "callback": "tableUseColGroup" + }, + "tableUsesAbbreviationForHeader": { + "type": "custom", + "testability": 0, + "title": { + "de": "Tabellenüberschriften über 20 Zeichen sollten ein \"abbr\"-Attribut haben", + "en": "Table headers over 20 characters should provide an \"abbr\" attribute", + "nl": "Tabelkoppen met meer dan 20 tekens moeten een \"abbr\"-attribuut hebben", + "pt-br": "Cabeçalhos de tabela com mais de 20 caracteres devem fornecer um atributo \"abbr\"" + }, + "description": { + "de": "Für lange Tabellenüberschriften verwenden Sie ein \"abbr\"-Attribut das kürzer ist (max. 20 Zeichen).", + "en": "For long table headers, use an \"abbr\" attribute that is less than short (less than 20 characters long).", + "nl": "Gebruik een \"abbr\"-attribuut korter dan 20 tekens voor lange tabelkoppen.", + "pt-br": "Para cabeçalhos de tabelas longas, use um atributo \"abbr\" que seja menos do que curto (menos de 20 caracteres)." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "callback": "tableUsesAbbreviationForHeader" + }, + "tableUsesCaption": { + "type": "selector", + "testability": 1, + "title": { + "de": "Datentabellen sollten ein \"caption\"-Element haben, wenn sie nicht anderweitig beschrieben sind", + "en": "Data tables should contain a \"caption\" element if not described elsewhere", + "nl": "Datatabellen moeten een \"caption\"-element hebben als ze nergens anders beschreven worden", + "pt-br": "As tabelas de dados devem conter um elemento \"caption\" se não for descrito em outro lugar" + }, + "description": { + "de": "Wenn Tabellen nicht anderweitig im Dokument beschrieben sind, sollten sie ein caption-Element zu beschreibung beinhalten", + "en": "Unless otherwise described in the document, tables should contain caption elements to describe the purpose of the table.", + "nl": "Tenzij elders in het document beschreven, moeten tabellen een \"caption\"-element hebben om het doel van de tabel te beschrijven.", + "pt-br": "As tabelas devem conter elementos caption para descrever o propósito da tabela." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H39" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "options": { + "selector": "table:not(table:has(caption))" + } + }, + "tableUsesScopeForRow": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Datentabellen sollten Überschrift mit \"scope\"-Attribut verwenden, wenn Zeilenüberschriften benötigt werden", + "en": "Data tables should use scoped headers for rows with headers", + "nl": "Datatabellen moeten het \"scope\"-attribuut gebruiken voor rijen met koppen", + "pt-br": "As tabelas de dados devem usar cabeçalhos com escopo para linhas com cabeçalhos" + }, + "description": { + "de": "Wenn Tabellenüberschriften für Zeilen und Spalten benötigt werden, sollte das \"scope\"-Attribut verwendet werden, um die Überschriften den richtigen Zellen zuzuordnen. Dieser Test prüft die erste und letzte Zelle in jeder Zeile und prüft, ob sie sich im Layout oder der Gewichtung der Schriftart unterscheiden.", + "en": "Where there are table headers for both rows and columns, use the \"scope\" attribute to help relate those headers with their appropriate cells. This test looks for the first and last cells in each row and sees if they differ in layout or font weight.", + "nl": "Als er tabelkoppen zijn voor zowel rijen als kolommen, gebruik dan het \"scope\"-attribuut om het juiste verband te leggen tussen de koppen en bijbehorende cellen.", + "pt-br": "Onde houver cabeçalhos de tabela para linhas e colunas, use o atributo \"scope\" para ajudar a relacionar esses cabeçalhos com suas células apropriadas. Este teste procura a primeira e última células em cada linha e vê se eles diferem no layout ou peso da fonte." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "H63" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "callback": "tableUsesScopeForRow" + }, + "tableWithBothHeadersUseScope": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Datentabellen mit mehreren Überschriften sollten das \"scope\"-Attribut verwenden", + "en": "Data tables with multiple headers should use the \"scope\" attribute", + "nl": "Datatabellen met meerdere headers moeten het \"scope\"-attribuut gebruiken", + "pt-br": "As tabelas de dados com vários cabeçalhos devem usar o atributo \"scope\"" + }, + "description": { + "de": "Wenn Tabellenüberschriften für Zeilen und Spalten verwendet werden, nutzen Sie das \"scope\"-Attribut, um die Überschriften den richtigen Zellen zuzuordnen", + "en": "Where there are table headers for both rows and columns, use the \"scope\" attribute to help relate those headers with their appropriate cells.", + "nl": "Als er tabelkoppen zijn voor zowel rijen als kolommen, gebruik dan het \"scope\"-attribuut om het juiste verband te leggen tussen de koppen en bijbehorende cellen.", + "pt-br": "Onde houver cabeçalhos de tabela para linhas e colunas, use o atributo \"scope\" para ajudar a relacionar esses cabeçalhos com suas células apropriadas." + }, + "guidelines": { + "508": [ + "h" + ], + "wcag": { + "1.3.1": { + "techniques": [ + "F91" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "options": { + "selector": "table:has(tr:not(table tr:first) th:not(th[scope]))" + } + }, + "tableWithMoreHeadersUseID": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Komplexe Datentabellen sollten \"id\"-Attribute für Überschriften bereitstellen", + "en": "Complex data tables should provide \"id\" attributes to headers", + "nl": "Complexe datatabellen moeten \"id\"-attributen aan headers koppelen", + "pt-br": "Tabelas de dados complexas devem fornecer \"id\" atributos para cabeçalhos" + }, + "description": { + "de": "Komplexe Datentabellen sollten \"id\"-Attribute für Überschriften und \"header\"-Attribute für Datenzellen verwenden.", + "en": "Complex data tables should provide \"id\" attributes to headers and \"headers\" attributes for data cells.", + "nl": "Complexe datatabellen moeten \"id\"-attributen aan headers koppelen en \"headers\"-attributen aan datacellen.", + "pt-br": "Tabelas de dados complexas devem fornecer \"id\" atributos para cabeçalhos e atributos \"headers\" para células de dados." + }, + "guidelines": [], + "tags": [ + "table", + "content" + ], + "callback": "tableWithMoreHeadersUseID" + }, + "tagsAreNestedCorrectly": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle Tags sollten korrekt verschachtelt werden", + "en": "All tags should be nested correctly", + "nl": "Alle tags moeten juist genest worden", + "pt-br": "Todas as tags devem ser aninhadas corretamente" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": { + "wcag": { + "4.1.1": { + "techniques": [ + "H74" + ] + } + } + }, + "tags": [ + "html" + ], + "components": [ + "htmlSource" + ], + "callback": "tagsAreNestedCorrectly" + }, + "tabularDataIsInTable": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Alle tabellarischen Informationen sollten in Tabellen dargestellt werden", + "en": "All tabular information should use a table", + "nl": "Alle tabelinformatie moet ook daadwerkelijk in een tabel staan", + "pt-br": "Todas as informações tabulares devem usar uma tabela" + }, + "description": { + "de": "Tabellen sollten verwendet werden, um tabellarische Informationen darzustellen", + "en": "Tables should be used when displaying tabular information.", + "nl": "Gebruik een echte tabel wanneer je tabelinformatie wilt tonen.", + "pt-br": "As tabelas devem ser usadas ao exibir informações tabulares." + }, + "guidelines": { + "wcag": { + "1.3.1": { + "techniques": [ + "F33", + "F34", + "F48" + ] + }, + "1.3.2": { + "techniques": [ + "F33", + "F34" + ] + } + } + }, + "tags": [ + "table", + "content" + ], + "callback": "tabularDataIsInTable" + }, + "textIsNotSmall": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Die Schriftgröße sollte nicht kleiner als 9px in der Höhe sein", + "en": "The text size is not less than 9 pixels high", + "nl": "De grootte van de tekst is meer dan 8 pixels hoog", + "pt-br": "O tamanho do texto não é inferior a 9 pixels de altura" + }, + "description": { + "de": "Um Benutzern zu helfen, die Schwierigkeiten haben kleinen Text zu lesen, sollte die Schriftgröße nicht kleiner als 9px in der Höhe sein", + "en": "To help users with difficulty reading small text, ensure text size is no less than 9 pixels high.", + "nl": "Help gebruikers die moeite hebben met het lezen van kleine letters, door ervoor te zorgen dat tekst groter is dan 8 pixels hoog.", + "pt-br": "Para ajudar os usuários com dificuldade em ler texto pequeno, certifique-se de que o tamanho do texto não seja inferior a 9 pixels." + }, + "guidelines": [], + "tags": [ + "textsize", + "content" + ], + "components": [ + "convertToPx" + ], + "callback": "textIsNotSmall" + }, + "textareaHasAssociatedLabel": { + "type": "label", + "testability": 1, + "title": { + "de": "Alle \"textareas\" sollten ein dazugehöriges Label haben", + "en": "All textareas should have a corresponding label", + "nl": "Alle \"textarea\"-elementen moeten een bijbehorend label hebben", + "pt-br": "Todas as textareas devem ter um rótulo correspondente" + }, + "description": { + "de": "Alle textarea-Elemente sollten ein dazugehöriges label-Element haben. Bildschirmleseprogramme haben oftmals eine Formular-Modus, in dem nur Labels vorgelesen werden.", + "en": "All textarea elements should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user", + "nl": "Alle \"textarea\"-elementen moeten een bijbehorend label hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen.", + "pt-br": "Todos os elementos textarea devem ter um elemento label correspondente. Os leitores de tela costumam entrar em um \"modo de formulário\" onde somente o texto do rótulo é lido em voz alta para o usuário" + }, + "guidelines": { + "wcag": { + "1.1.1": { + "techniques": [ + "H44" + ] + }, + "1.3.1": { + "techniques": [ + "H44", + "F68" + ] + }, + "2.1.1": { + "techniques": [ + "H91" + ] + }, + "2.1.3": { + "techniques": [ + "H91" + ] + }, + "3.3.2": { + "techniques": [ + "H44" + ] + }, + "4.1.2": { + "techniques": [ + "H44", + "H91" + ] + } + } + }, + "tags": [ + "form", + "content" + ], + "components": [ + "label" + ], + "options": { + "selector": "textarea" + } + }, + "textareaLabelPositionedClose": { + "type": "labelProximity", + "testability": 0.5, + "title": { + "de": "Alle \"textarea\"-Elemente sollten ein dazugehöriges Label haben", + "en": "All textareas should have a label that is close to it", + "nl": "Alle \"textarea\"-elementen moeten een label hebben dat dicht bij het element staat", + "pt-br": "Todas as textareas devem ter um rótulo que está próximo a ele" + }, + "description": { + "de": "Alle textarea-Elemente sollten ein dazugehöriges label-Element haben, das in der Nähe positioniert ist.", + "en": "All textarea elements should have a corresponding label element that is close in proximity.", + "nl": "Alle \"textarea\"-elementen moeten een bijbehorend label hebben dat dicht bij het element staat.", + "pt-br": "Todos os elementos textarea devem ter um elemento label correspondente próximo à proximidade." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "labelProximity" + ], + "options": { + "selector": "textarea" + } + }, + "videoProvidesCaptions": { + "type": "selector", + "testability": 0.5, + "title": { + "de": "Alle Video-Tags sollten Untertitel bereitstellen", + "en": "All video tags must provide captions", + "nl": "Alle video tags moeten bijschriften bieden", + "pt-br": "Todas as tags de vídeo devem fornecer legendas" + }, + "description": { + "de": "Alle HTML5-Video-Tags müssen Untertitel bereitstellen", + "en": "All HTML5 video tags must provide captions.", + "nl": "Alle HTML5 video tags moeten bijschriften bieden.", + "pt-br": "Todas as tags de vídeo HTML5 devem fornecer legendas." + }, + "guidelines": { + "508": [ + "b", + "b" + ], + "wcag": { + "1.2.2": { + "techniques": [ + "G87" + ] + }, + "1.2.4": { + "techniques": [ + "G87" + ] + } + } + }, + "tags": [ + "media", + "content" + ], + "options": { + "selector": "video" + } + }, + "videosEmbeddedOrLinkedNeedCaptions": { + "type": "custom", + "testability": 1, + "title": { + "de": "Alle verknüpften oder eingebettenen Videos benötigen Untertitel", + "en": "All linked or embedded videos need captions", + "nl": "Alle gekoppelde of ingebedde video's moeten bijschriften hebben", + "pt-br": "Todos os vídeos vinculados ou incorporados precisam de legendas" + }, + "description": { + "de": "Jedes gehostete, verknüpfte oder eingebettete Video muss Untertitel haben", + "en": "Any video hosted or otherwise which is linked or embedded must have a caption.", + "nl": "Elke video die is gekoppeld of ingebed in content moet een bijschrift hebben.", + "pt-br": "Qualquer vídeo hospedado ou de outra forma que esteja ligado ou incorporado deve ter uma legenda." + }, + "guidelines": { + "wcag": { + "1.2.2": { + "techniques": [ + "G87" + ] + }, + "1.2.4": { + "techniques": [ + "G87" + ] + } + } + }, + "tags": [ + "media", + "content" + ], + "components": [ + "video", + "language" + ], + "callback": "videosEmbeddedOrLinkedNeedCaptions" + }, + "whiteSpaceInWord": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Leerzeichen sollten nicht innerhalb eines Wortes verwendet werden.", + "en": "Whitespace should not be used between characters in a word", + "nl": "Zet geen witruimte tussen letters in een woord", + "pt-br": "Espaço em branco não deve ser usado entre caracteres em uma palavra" + }, + "description": { + "de": "Die Verwendung von zusätzlichen Leerzeichen in einem Word verursacht für Bildschirmleseprogrammen Probleme bei der Interpretation des Wortes. Verwenden Sie stattdessen die CSS-Eigenschaft \"letter-spacing\".", + "en": "Using extra whitespace between letters in a word causes screen readers to not interpret the word correctly, use the letter-spacing CSS property instead.", + "nl": "Het gebruik van witruimte tussen de letters van een woord, zorgen dat schermlezers het woord niet volledig kunnen lezen. Gebruik in plaats hiervan css om de ruimte tussen letters te bepalen.", + "pt-br": "Usando espaço em branco extra entre as letras em uma palavra faz com que os leitores de tela não interpretem a palavra corretamente, use a propriedade CSS \"letter-spacing\" em vez disso." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "F32", + "C8" + ] + } + } + }, + "tags": [ + "content" + ], + "callback": "whiteSpaceInWord" + }, + "whiteSpaceNotUsedForFormatting": { + "type": "custom", + "testability": 0.5, + "title": { + "de": "Leerzeichen sollten nicht für die Informationsvermittlung verwendet werden", + "en": "Whitespace should not be used for conveying information", + "nl": "Gebruik geen witruimte om informatie over te brengen", + "pt-br": "Espaço em branco não deve ser usado para transmitir informações" + }, + "description": { + "de": "Leerzeichen oder Tabulatoren werden von Assistenztechnologien nicht vorgelesen und sollten daher nicht zur Vermittlung von Informationen genutzt werden", + "en": "Spaces or tabs are not read by assistive technology and should not be used to convey meaning.", + "nl": "Spaties of tabs worden niet voorgelezen door hulpprogramma's en moeten niet worden gebruikt om betekenis over te dragen.", + "pt-br": "Espaços ou abas não são lidos por tecnologia assistiva e não devem ser usados para transmitir significado." + }, + "guidelines": { + "wcag": { + "1.3.2": { + "techniques": [ + "G57" + ] + } + } + }, + "tags": [ + "content" + ], + "callback": "whiteSpaceNotUsedForFormatting" + }, + "doNotUseGraphicalSymbolToConveyInformation": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verwendung von grafischen Symoblen zur Vermittlung von Informationen", + "en": "Using a graphical symbol alone to convey information", + "nl": "Gebruik van alleen een grafisch symbool om informatie over te brengen", + "pt-br": "Usando apenas um símbolo gráfico para transmitir informações" + }, + "description": { + "de": "Das Ziel dieser Technik ist es zu zeigen, wie grafische Symbole die Vermittlung von Informationen erschweren. Ein grafisches Symbol kann eine Grafik oder ein Piktorgram oder eine Glyphe sein, das Informationen nonverbal vermittelt. Beispiele dafür sind Symbole bestehende aus einem durchgestrichenen Kreis, ein Smiley oder ein Häkchen oder Pfeil. Benutzer von Assistenztechnologien haben ggf. Schwierigkeiten die Bedeutung des Symbols zu erkennen. Wenn ein graphisches Symbol verwendet wird, um Informationen zu übermitteln , stellen eine alternative Technologie oder einen anderen Mechanismus bereit, um die Bedeutung darzustellen. Verwenden Sie zum Beispiel ein Bild mit einem Alternativetext anstelle der Glyphe.", + "en": "The objective of this technique is to show how using a graphical symbol to convey information can make content difficult to comprehend. A graphical symbol may be an image, an image of text or a pictorial or decorative character symbol (glyph) which imparts information nonverbally. Examples of graphical symbols include an image of a red circle with a line through it, a smiley face, or a glyph which represents a check mark, arrow, or other symbol but is not the character with that meaning. Assistive technology users may have difficulty determining the meaning of the graphical symbol. If a graphical symbol is used to convey information, provide an alternative using features of the technology or use a different mechanism that can be marked with an alternative to represent the graphical symbol. For example, an image with a text alternative can be used instead of the glyph.", + "nl": "Het doel van deze techniek is te laten zien dat content moeilijker begrepen kan worden wanneer een grafisch symbool wordt gebruikt om informatie over te brengen. Een grafisch symbool kan een afbeelding, een afbeelding van tekst of een pictogram of decoratief teken zijn (glyph) dat non-verbaal informatie overbrengt. Voorbeelden hiervan zijn een rode cirkel met een lijn erdoorheen, een smiley of een vinkje, pijl of ander symbool dat niet noodzakelijkerwijs een duidelijke betekenis heeft. Gebruikers van hulpprogramma's hebben vaak moeite om de bedoeling van een grafisch symbool te duiden. Als een grafisch symbool gebruikt wordt om informatie over te brengen, biedt dan ook een (tekstueel) alternatief.", + "pt-br": "O objetivo desta técnica é mostrar como usar um símbolo gráfico para transmitir informações. Um símbolo gráfico pode ser uma imagem, uma imagem de texto ou um símbolo de carácter pictórico ou decorativo (glifo) que transmite informação não verbalmente. Exemplos de símbolos gráficos incluem uma imagem de um círculo vermelho com uma linha através dele, um rosto sorridente ou um glifo que representa uma marca de seleção, seta ou outro símbolo, mas não é o personagem com esse significado. Os usuários de tecnologia assistiva podem ter dificuldade em determinar o significado do símbolo gráfico. Se um símbolo gráfico é usado para transmitir informações, fornecer uma alternativa usando recursos da tecnologia ou usar um mecanismo diferente que pode ser marcado com uma alternativa para representar o símbolo gráfico. Por exemplo, uma imagem com uma alternativa de texto pode ser usada em vez do glifo." + }, + "guidelines": { + "wcag": { + "1.3.3": { + "techniques": [ + "F26" + ] + } + } + }, + "tags": [ + "link", + "content" + ], + "callback": "doNotUseGraphicalSymbolToConveyInformation" + }, + "linkDoesNotChangeContextOnFocus": { + "type": "event", + "testability": 1, + "title": { + "de": "Verknüpfungen dürfen das \"onfocus\"-Attribut nicht verwenden", + "en": "Link elements must not contain an \"onfocus\" attribute", + "nl": "Link-elementen bevatten geen \"onfocus\"-attribuut", + "pt-br": "Os elementos de link não devem conter um atributo \"onfocus\"" + }, + "description": { + "de": "Aktionen wie \"onfocus\" entziehen dem Benutzer die Kontrolle beim Versuch auf der Seite zu navigieren. \"onfocus\"-Attribute für Dinge wie Auswahl-Listenmenüs sollten durch Drop-Downs und eine Schaltfläche zur Initiierung der Aktion ersetzt werden.", + "en": "Actions like \"onfocus\" can take control away from users who are trying to navigate the page. Using an \"onfocus\" attribute for things like links should be replaced with css.", + "nl": "Acties zoals \"onfocus\" kunnen de controle ontnemen van gebruikers die op een pagina proberen te navigeren. Het gebruik van een \"onfocus\"-attribuut voor zaken als links moet worden vervangen door middel van css.", + "pt-br": "Ações como \"onfocus\" podem tirar o controle dos usuários que estão tentando navegar na página. Usando um atributo \"onfocus\" para coisas como links deve ser substituído por css." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "searchEvent": "onfocus", + "selector": "a" + } + }, + "buttonDoesNotChangeContextOnFocus": { + "type": "event", + "testability": 1, + "title": { + "de": "Schaltflächen dürfen das \"onfocus\"-Attribut nicht verwenden", + "en": "Buttons must not contain an \"onfocus\" attribute", + "nl": "Knoppen bevatten geen \"onfocus\"-attribuut", + "pt-br": "Os botões não devem conter um atributo \"onfocus\"" + }, + "description": { + "de": "Aktionen wie \"onfocus\" entziehen dem Benutzer die Kontrolle beim Versuch auf der Seite zu navigieren. \"onfocus\"-Attribute für Dinge wie Auswahl-Listenmenüs sollten durch Drop-Downs und eine Schaltfläche zur Initiierung der Aktion ersetzt werden.", + "en": "Actions like \"onfocus\" can take control away from users who are trying to navigate the page. Using an \"onfocus\" attribute for things like buttons should be replaced with css.", + "nl": "Acties zoals \"onfocus\" kunnen de controle ontnemen van gebruikers die op een pagina proberen te navigeren. Het gebruik van een \"onfocus\"-attribuut voor zaken als knoppen moet worden vervangen door middel van css.", + "pt-br": "Ações como \"onfocus\" podem tirar o controle dos usuários que estão tentando navegar na página. Usando um atributo \"onfocus\" para coisas como botões deve ser substituído por css." + }, + "guidelines": [], + "tags": [ + "form", + "content" + ], + "components": [ + "event", + "hasEventListener" + ], + "options": { + "searchEvent": "onfocus", + "selector": "input[type=checkbox],input[type=text],button" + } + }, + "KINGStrongList": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verwenden Sie strong nur in Listen", + "en": "Use strong in lists only", + "nl": "", + "pt-br": "Use strong apenas nas listas" + }, + "description": { + "de": "strong ist nur in li-Elementen erlaubt", + "en": "STRONG only allowed when parent element is LI.", + "nl": "", + "pt-br": " strong é permitida apenas em elementos li " + }, + "guidelines": [], + "tags": [ + "KING" + ], + "callback": "KINGStrongList" + }, + "KINGUseLongDateFormat": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verwenden Sie lange Datumsformate", + "en": "Use a long date format", + "nl": "", + "pt-br": "Use um formato de data longo" + }, + "description": { + "de": "Kurze Datumsformate können Benutzer verwirren. Benutzen Sie immer Monatsnamen, bspw. 14 März 2020", + "en": "Short date formats might confuse users, Always use the month name: 20 May 2014.", + "nl": "", + "pt-br": "Os formatos de data curtos podem confundir os usuários, Usar sempre o nome do mês: 20 de maio de 2014." + }, + "guidelines": [], + "tags": [ + "KING" + ], + "callback": "KINGUseLongDateFormat" + }, + "KINGUsePercentageWithSymbol": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verwenden Sie ein Symbol in einem Prozentsatz", + "en": "Use a symbol within a percentage", + "nl": "", + "pt-br": "Use um símbolo dentro de uma porcentagem" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "KING" + ], + "callback": "KINGUsePercentageWithSymbol" + }, + "KINGUseCurrencyAsSymbol": { + "type": "custom", + "testability": 1, + "title": { + "de": "Verwenden Sie ein Symbol für Währungen", + "en": "Use a symbol for a currency", + "nl": "", + "pt-br": "Use um símbolo para moedas" + }, + "description": { + "de": "Verwenden Sie nur Symbol und Währungsnamen anstelle von allgemeinen Namen wie € oder EUR.", + "en": "Only use symbol and currency name instead of common name such as € or EUR.", + "nl": "", + "pt-br": "Use apenas símbolo e nome de moeda em vez de nome comum, como € ou EUR." + }, + "guidelines": [], + "tags": [ + "KING" + ], + "callback": "KINGUseCurrencyAsSymbol" + }, + "videoMayBePresent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Video oder Object verwendet eine Verknüpfung die auf eine Datei mit einer Video-Dateiendung zeigt", + "en": "Video or object uses a link that points to a file with a video extension", + "nl": "Video of object met een link naar een bestand met een video extensie", + "pt-br": "Vídeo ou objeto usa um link que aponta para um arquivo com uma extensão de vídeo" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "link", + "video" + ], + "callback": "videoMayBePresent" + }, + "audioMayBePresent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Audio oder Object verwendet eine Verknüpfung die auf eine Datei mit einer Video-Dateiendung zeigt", + "en": "Audio or object uses a link that points to a file with a video extension", + "nl": "Audio of object met een link naar een bestand met een video extensie", + "pt-br": "Áudio ou objetos usam um link que aponta para um arquivo com uma extensão de vídeo" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "link", + "audio" + ], + "callback": "audioMayBePresent" + }, + "animatedGifMayBePresent": { + "type": "custom", + "testability": 1, + "title": { + "de": "Prüfen Sie, ob GIF-Dateien in der Seite verwendet werden. Prüfen Sie, ob die GIF-Dateien mehr als einen Frame enthalten", + "en": "Test if a .gif is used on the page. Test if the .gif contains more then one frame", + "nl": "Test of een .gif afbeelding gebruikt is op de pagina. Test of het .gif bestand uit meer dan één frame bestaat", + "pt-br": "Teste se um .gif é usado na página. Teste se o .gif contém mais de um quadro" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "link", + "gif" + ], + "callback": "animatedGifMayBePresent" + }, + "userInputMayBeRequired": { + "type": "custom", + "testability": 1, + "title": { + "de": "Prüfen Sie, ob eine Benutzereingabe notwendig ist", + "en": "Test if user input is required", + "nl": "Test of er invoervelden zijn", + "pt-br": "Teste se a entrada do usuário é obrigatória" + }, + "description": { + "de": "", + "en": "", + "nl": "", + "pt-br": "" + }, + "guidelines": [], + "tags": [ + "form", + "input" + ], + "callback": "userInputMayBeRequired" + } +} diff --git a/plugins/ckeditor/a11ychecker/libs/quail/tests.min.json b/plugins/ckeditor/a11ychecker/libs/quail/tests.min.json new file mode 100644 index 0000000..19cca04 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/tests.min.json @@ -0,0 +1 @@ +{"aAdjacentWithSameResourceShouldBeCombined":{"type":"custom","testability":1,"title":{"en":"Adjacent links that point to the same location should be merged","nl":"Voeg naast elkaar gelegen links die naar dezelfde locatie verwijzen samen"},"description":{"en":"Because many users of screen readers use links to navigate the page, providing two links right next to each other that point to the same location can be confusing. Try combining the links.","nl":"Veel gebruikers van schermlezers gebruiken links om op de pagina te navigeren. Voor hen zijn naast elkaar gelegen links die naar dezelfde locatie verwijzen verwarrend. Probeer de links samen te voegen."},"guidelines":{"wcag":{"2.4.4":{"techniques":["H2","F89"]},"2.4.9":{"techniques":["F89"]},"4.1.2":{"techniques":["F89"]}}},"tags":["link","content"],"callback":"aAdjacentWithSameResourceShouldBeCombined"},"aImgAltNotRepetitive":{"type":"custom","testability":1,"title":{"en":"Alternative text for image in a link should not repeat the text in the link","nl":"Als een link een afbeelding bevat, moet het \"alt\"-attribuut niet dezelfde tekst bevatten als de linktekst"},"description":{"en":"Images within a link should not have an alt attribute that simply repeats the text found in the link. This will cause screen readers to simply repeat the text twice.","nl":"Als een link een afbeelding bevat, moet deze afbeelding een andere tekst in het alt-attribuut hebben dan de tekst in de link. Hiermee voorkom je dat een schermlezer dezelfde tekst twee keer voorleest."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H30"]},"2.4.4":{"techniques":["H30"]},"2.4.9":{"techniques":["H30"]}}},"tags":["link","content"],"callback":"aImgAltNotRepetitive"},"aInPHasADistinctStyle":{"type":"custom","testability":1,"title":{"en":"Links should be have a distinct style inside a p tag","nl":"Links moeten een afwijkende stijl hebben binnen een paragraaf"},"description":{"en":"","nl":""},"guidelines":[],"tags":["link","content"],"callback":"aInPHasADistinctStyle"},"aLinkTextDoesNotBeginWithRedundantWord":{"type":"custom","testability":0,"title":{"en":"Link text should not begin with redundant text","nl":"Laat linkteksten niet beginnen met overbodige tekst"},"description":{"en":"Link text should not begin with redundant words or phrases like \"link\".","nl":"Laat linkteksten niet beginnen met overbodige woorden of woordcombinaties als \"link\" of \"klik hier\"."},"guidelines":{"wcag":{"2.4.9":{"techniques":["F84"]}}},"tags":["link","content"],"strings":["redundant.link"],"callback":"aLinkTextDoesNotBeginWithRedundantWord"},"aLinksAreSeparatedByPrintableCharacters":{"type":"custom","testability":1,"title":{"en":"Lists of links should be seperated by printable characters","nl":"Lijsten met links moeten gescheiden worden door afdrukbare tekens"},"description":{"en":"If a list of links is provided within the same element, those links should be seperated by a non-linked, printable character. Structures like lists are not included in this.","nl":"Als een rij met links binnen eenzelfde element staat, moeten de links gescheiden zijn door een niet-gelinkt, afdrukbaar teken. Dit geldt niet voor een gestructureerde lijst."},"guidelines":[],"tags":["link","content"],"callback":"aLinksAreSeparatedByPrintableCharacters"},"aLinksDontOpenNewWindow":{"type":"custom","testability":1,"title":{"en":"Links should not open a new window without warning","nl":"Met links open je geen nieuw scherm zonder melding"},"description":{"en":"Links which open a new window using the \"target\" attribute should warn users.","nl":"Voordat links door middel van het \"target\"-attribuut een nieuw scherm openen moet de gebruiker een melding hiervan krijgen."},"guidelines":{"wcag":{"3.2.5":{"techniques":["H83","SCR24"]}}},"tags":["link","content"],"strings":["newWindow"],"callback":"aLinksDontOpenNewWindow"},"aLinksNotSeparatedBySymbols":{"type":"custom","testability":1,"title":{"en":"Links should not be separated by symbols alone","nl":"Links mogen niet alleen door symbolen gescheidn worden"},"description":{"en":"Since symbols are either not read, or can be confusing when using a screen reader, do not separate links with un-readable symbols.","nl":"Symbolen worden niet voorgelezen of zijn verwarrend bij het gebruik van een schermlezer. Gebruik geen onleesbare symbolen om links van elkaar te scheiden."},"guidelines":[],"tags":["link","content"],"callback":"aLinksNotSeparatedBySymbols"},"aLinksToMultiMediaRequireTranscript":{"type":"selector","testability":0,"title":{"en":"Any links to a multimedia file should also include a link to a transcript","nl":"Elke link naar een multimediabestand moet ook een link bevatten naar een transcriptie"},"description":{"en":"Links to a multimedia file should be followed by a link to a transcript of the file.","nl":"Links naar een multimediabestand moeten worden gevolgd door een link naar de transcriptie van dit bestand."},"guidelines":{"508":["c"],"wcag":{"1.1.1":{"techniques":["G74"]}}},"tags":["link","media","content"],"options":{"selector":"a[href$='.wmv'], a[href$='.mpg'], a[href$='.mov'], a[href$='.ram'], a[href$='.aif']"}},"aLinksToSoundFilesNeedTranscripts":{"type":"selector","testability":0,"title":{"en":"Any links to a sound file should also include a link to a transcript","nl":"Elke link naar een geluidsbestand moet ook een link bevatten naar een transcriptie"},"description":{"en":"Links to a sound file should be followed by a link to a transcript of the file.","nl":"Links naar een geluidsbestand moeten worden gevolgd door een link naar de transcriptie van dit bestand."},"guidelines":{"508":["c"],"wcag":{"1.1.1":{"techniques":["G74"]}}},"tags":["link","media","content"],"options":{"selector":"a[href$='.wav'], a[href$='.snd'], a[href$='.mp3'], a[href$='.iff'], a[href$='.svx'], a[href$='.sam'], a[href$='.smp'], a[href$='.vce'], a[href$='.vox'], a[href$='.pcm'], a[href$='.aif']"}},"aLinkWithNonText":{"type":"custom","title":{"en":"Links with only non-text content should be readable","nl":"Links zonder tekstuele content moeten leesbaar zijn"},"description":{"en":"If a link contains only non-text content like an image, that content must be readable by assistive technology.","nl":"Als een link alleen maar niet-tekstuele content bevat zoals een afbeelding, moet deze content leesbaar worden gemaakt door middel van daarvoor geschikte technologie."},"guidelines":{"wcag":{"2.4.4":{"techniques":["H2","F89"]},"2.4.9":{"techniques":["F89"]},"4.1.2":{"techniques":["F89"]}}},"tags":["link","content"],"callback":"aLinkWithNonText"},"aMultimediaTextAlternative":{"type":"selector","testability":0,"guidelines":[],"tags":["link","media","content"],"options":{"selector":"a[href$='.wmv'], a[href$='.wav'], a[href$='.mpg'], a[href$='.mov'], a[href$='.ram'], a[href$='.aif']"}},"aMustContainText":{"type":"custom","testability":1,"title":{"en":"Links should contain text","nl":"Links moeten tekst bevatten"},"description":{"en":"Because many users of screen-readers use links to navigate the page, providing links with no text (or with images that have empty \"alt\" attributes and no other readable text) hinders these users.","nl":"Veel gebruikers van schermlezers gebruiken links om op de pagina te navigeren. Links zonder tekst (of met afbeeldingen die een leeg \"alt\"-attribuut hebben en geen andere leesbare tekst) hinderen deze gebruikers."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H30"]},"2.4.4":{"techniques":["H30"]},"2.4.9":{"techniques":["H30"]},"4.1.2":{"techniques":["H91"]}}},"tags":["link","content"],"callback":"aMustContainText"},"aMustHaveTitle":{"type":"selector","testability":1,"title":{"en":"All links must have a \"title\" attribute","nl":"Alle links moeten een \"title\"-attribuut hebben"},"description":{"en":"Every link must have a \"title\" attribute.","nl":"Zorg ervoor dat elke link is voorzien van een \"title\"-attribuut."},"guidelines":[],"tags":["link","content"],"options":{"selector":"a:not(a[title])"}},"aMustNotHaveJavascriptHref":{"type":"selector","testability":1,"title":{"en":"Links should not use \"javascript\" in their location","nl":"Links moeten geen \"javascript\" in hun locatie hebben"},"description":{"en":"Anchor (a. elements may not use the \"javascript\" protocol in their \"href\" attributes.","nl":"Anchor(a.-elementen mogen geen \"javascript\"protocol in hun \"href\"-attributen hebben staan."},"guidelines":[],"tags":["link","content"],"options":{"selector":"a[href^='javascript:']"}},"aSuspiciousLinkText":{"type":"custom","testability":1,"title":{"en":"Link text should be useful","nl":"Linkteksten moeten bruikbaar en betekenisvol zijn"},"description":{"en":"Because many users of screen-readers use links to navigate the page, providing links with text that simply read \"click here\" gives no hint of the function of the link.","nl":"Veel gebruikers van schermlezers gebruiken links om op de pagina te navigeren. Links met de tekst \"klik hier\" zijn voor deze gebruikers niet betekenisvol en niet bruikbaar."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H30"]},"2.4.4":{"techniques":["H30"]},"2.4.9":{"techniques":["H30"]}}},"tags":["link","content"],"strings":["suspiciousLinks"],"callback":"aSuspiciousLinkText"},"aTitleDescribesDestination":{"type":"selector","testability":0,"title":{"en":"The title attribute of all source a (anchor) elements describes the link destination.","nl":"Het titel-attribuut van alle source a (anchor)-elementen beschrijven de bestemming van de link"},"description":{"en":"Every link must have a \"title\" attribute which describes the purpose or destination of the link.","nl":"Elke link moet een \"title\"-attribuut hebben waarin het doel of de bestemming van de link wordt beschreven."},"guidelines":{"wcag":{"2.4.9":{"techniques":["H33","H25"]}}},"tags":["link","content"],"options":{"selector":"a[title]"}},"addressForAuthor":{"type":"selector","testability":1,"title":{"en":"The document should contain an address for the author","nl":"Het document moet een adres voor de auteur bevatten"},"description":{"en":"Documents should provide a valid email address within an address element.","nl":"Documenten moeten een e-mailadres bevatten binnen het address-element."},"guidelines":[],"tags":["document"],"options":{"selector":"body:not(body:has(address))"}},"addressForAuthorMustBeValid":{"type":"selector","testability":0.5,"title":{"en":"The document should contain a valid email address for the author","nl":"Het document moet een geldig e-mailadres bevatten voor de auteur"},"description":{"en":"Documents should provide a valid email address within an address element.","nl":"Documenten moeten een geldig e-mailadres bevatten binnen het address element."},"guidelines":[],"tags":["document"],"options":{"selector":"address"}},"appletContainsTextEquivalent":{"type":"custom","testability":1,"title":{"en":"All applets should contain the same content within the body of the applet","nl":"Alle applets moeten dezelfde content bevatten in de body van de applet"},"description":{"en":"Applets should contain their text equivalents or description within the applet tag itself.","nl":"Applets moeten hun tekstuele equivalent of beschrijving bevatten in de applet tag."},"guidelines":{"508":["m"],"wcag":{"1.1.1":{"techniques":["G74","H35"]}}},"tags":["objects","applet","content"],"callback":"appletContainsTextEquivalent"},"appletContainsTextEquivalentInAlt":{"type":"placeholder","testability":0.5,"title":{"en":"All applets should contain a text equivalent in the \"alt\" attribute","nl":"Alle applets moeten een tekstuele equivalent bevatten in het \"alt\"-attribuut"},"description":{"en":"Applets should contain their text equivalents or description in an \"alt\" attribute.","nl":"Applets moeten hun tekstuele equivalent of beschrijving bevatten in een \"alt\"-attribuut."},"guidelines":{"508":["m"],"wcag":{"1.1.1":{"techniques":["G74","H35"]}}},"tags":["objects","applet","content"],"components":["placeholder"],"options":{"attribute":"alt","empty":true,"selector":"applet"}},"appletProvidesMechanismToReturnToParent":{"type":"selector","testability":0,"title":{"en":"All applets should provide a way for keyboard users to escape","nl":"Alle applets moeten door toetsenbordgebruikers kunnen worden verlaten"},"description":{"en":"Ensure that a user who has only a keyboard as an input device can escape an applet element. This requires manual confirmation.","nl":"Zorg ervoor dat gebruikers die alleen het toetsenbord gebruiken als bediening een applet-element kunnen verlaten. Hiervoor is handmatige bevestiging nodig."},"guidelines":[],"tags":["objects","applet","content"],"options":{"selector":"applet"}},"appletTextEquivalentsGetUpdated":{"type":"selector","testability":0,"guidelines":{"508":["m"],"wcag":{"1.1.1":{"techniques":["G74","H35"]}}},"tags":["objects","applet","content"],"options":{"selector":"applet"}},"appletUIMustBeAccessible":{"type":"selector","testability":0,"title":{"en":"Any user interface in an applet must be accessible","nl":"Elke user interface in een applet moet toegankelijk zijn"},"description":{"en":"Applet content should be assessed for accessibility.","nl":"Content in een applet moet getoetst worden op toegankelijkheid."},"guidelines":{"508":["m"],"wcag":{"1.1.1":{"techniques":["G74","H35"]}}},"tags":["objects","applet","content"],"options":{"selector":"applet"}},"appletsDoNotFlicker":{"type":"selector","testability":0,"title":{"en":"All applets do not flicker","nl":"Applets knipperen of flitsen niet"},"description":{"en":"Applets should not flicker.","nl":"Geen enkele applet mag knipperen of flitsen."},"guidelines":{"508":["j"],"wcag":{"2.2.2":{"techniques":["F7"]}}},"tags":["objects","applet","content"],"options":{"selector":"applet"}},"appletsDonotUseColorAlone":{"type":"selector","testability":0,"title":{"en":"Applets should not use color alone to communicate content","nl":"Applets mogen niet alleen kleur gebruiken om een boodschap over te brengen"},"description":{"en":"Applets should contain content that makes sense without color and is accessible to users who are color blind.","nl":"Applets moeten content bevatten die ook bruikbaar is zonder kleur en die toegankelijk is voor gebruikers met kleurenblindheid."},"guidelines":{"508":["c"]},"tags":["objects","applet","content"],"options":{"selector":"applet"}},"areaAltIdentifiesDestination":{"type":"selector","testability":0,"title":{"en":"All \"area\" elements must have an \"alt\" attribute which describes the link destination","nl":"Alle \"area\"-elementen moeten een \"alt\"-attribuut hebben die de bestemming van de link beschrijft"},"description":{"en":"All area elements within a map must have an \"alt\" attribute","nl":"Alle area-elementen binnen een map moeten een \"alt\"-attribuut hebben."},"guidelines":{"wcag":{"1.1.1":{"techniques":["G74"]}}},"tags":["objects","applet","content"],"options":{"selector":"area[alt]"}},"areaAltRefersToText":{"type":"selector","testability":0,"title":{"en":"Alt text for \"area\" elements should replicate the text found in the image","nl":"Alt-tekst voor \"area\"-elementen moeten de tekst bevatten zoals die ook in de afbeelding staat"},"description":{"en":"If an image is being used as a map, and an area encompasses text within the image, then the \"alt\" attribute of that area element should be the same as the text found in the image.","nl":"Als een afbeelding als kaart wordt gebruikt, en een area bevat tekst binnen de afbeelding, dan moet het \"alt\"-attribuut van dat area-element hetzelfde zijn als de tekst die in de afbeelding staat."},"guidelines":[],"tags":["imagemap","content"],"options":{"selector":"area"}},"areaDontOpenNewWindow":{"type":"selector","testability":1,"title":{"en":"No \"area\" elements should open a new window without warning","nl":"\"area\"-elementen mogen geen nieuw scherm openen zonder melding"},"description":{"en":"No area elements should open a new window without warning.","nl":"area-elementen mogen geen nieuw scherm openen zonder dat de gebruiker hiervan een melding krijgt."},"guidelines":[],"tags":["imagemap","content"],"options":{"selector":"area[target='new window'], area[target=_new], area[target=_blank], area[target=_blank]"}},"areaHasAltValue":{"type":"selector","testability":1,"title":{"en":"All \"area\" elements must have an \"alt\" attribute","nl":"Alle \"area\"-elementen moeten een \"alt\"-attribuut hebben"},"description":{"en":"All area elements within a map must have an \"alt\" attribute.","nl":"Alle area-elementen binnen een map moeten een \"alt\"-attribuut hebben."},"guidelines":{"wcag":{"1.1.1":{"techniques":["F65","G74","H24"]},"1.4.3":{"techniques":["G145"]}}},"tags":["imagemap","content"],"options":{"selector":"area","test":":not(area[alt])"}},"areaLinksToSoundFile":{"type":"selector","testability":1,"title":{"en":"All \"area\" elements which link to a sound file should also provide a link to a transcript","nl":"Alle \"area\"-elementen met een link naar een geluidsbestand moeten ook een link bevatten naar een transcriptie"},"description":{"en":"All area elements which link to a sound file should have a text transcript.","nl":"Alle \"area\"-elementen met een link naar een geluidsbestand moeten een transcriptie hebben in tekst."},"guidelines":{"wcag":{"1.1.1":{"techniques":["G74"]}}},"tags":["imagemap","media","content"],"options":{"selector":"area[href$=wav], area[href$=snd], area[href$=mp3], area[href$=iff], area[href$=svx], area[href$=sam], area[href$=smp], area[href$=vce], area[href$=vox], area[href$=pcm], area[href$=aif]"}},"ariaOrphanedContent":{"type":"custom","testability":1,"title":{"en":"Pages using ARIA roles should not have orphaned content","nl":"Pagina's die ARIA-rollen gebruiken mogen geen verweesde content hebben"},"description":{"en":"If a page makes use of ARIA roles, then there should not be any content on the page which is not within an element that exposes a role, as it could cause that content to be rendreed inaccessible to users with screen readers.","nl":"Als een pagina gebruik maakt van ARIA-rollen, mag er geen content op de pagina staan buiten een element dat een rol vertegenwoordigt. In dat geval kan het zijn dat deze content niet toegankelijk is voor gebruikers van schermlezers."},"guidelines":[],"tags":["aria","content"],"callback":"ariaOrphanedContent"},"basefontIsNotUsed":{"type":"selector","testability":1,"title":{"en":"Basefont should not be used","nl":"Basefont moet niet worden gebruikt"},"description":{"en":"The basefont tag is deprecated and should not be used. Investigate using stylesheets instead.","nl":"The basefont-tag is afgekeurd en moet niet worden gebruikt. Gebruik in plaats hiervan stylesheets."},"guidelines":[],"tags":["document","deprecated"],"options":{"selector":"basefont"}},"blinkIsNotUsed":{"type":"selector","testability":1,"title":{"en":"The \"blink\" tag should not be used","nl":"De \"blink\"-tag moet niet worden gebruikt"},"description":{"en":"The blink tag should not be used. Ever.","nl":"Het is nooit toegestaan om de \"blink\"-tag te gebruiken."},"guidelines":{"wcag":{"2.2.2":{"techniques":["F47"]}}},"tags":["deprecated","content"],"options":{"selector":"blink"}},"blockquoteNotUsedForIndentation":{"type":"selector","testability":0.5,"title":{"en":"The block quote should not be used just for indentation","nl":"De \"blockquote\"-tag mag niet gebruikt worden om in te springen"},"description":{"en":"Blockquote tags are for longer quotes, and should not be used to indent text.","nl":"Blockquotes zijn bedoeld voor lange stukken geciteerde tekst, en niet om tekst te laten inspringen."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H49"]}}},"tags":["blockquote","content"],"options":{"selector":"blockquote:not(blockquote[cite])"}},"blockquoteUseForQuotations":{"type":"custom","testability":0.5,"title":{"en":"If long quotes are in the document, use the \"blockquote\" element to mark them","nl":"Gebruik voor lange citaten in het document het \"blockquote\"-element"},"description":{"en":"If there is a paragraph or more of a quote, use the blockquote element to mark it as such.","nl":"Als er een hele alinea of meer alinea's zijn van geciteerde tekst, gebruik dan blockquote om deze als zodanig te markeren."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H49"]}}},"tags":["blockquote","content"],"callback":"blockquoteUseForQuotations"},"boldIsNotUsed":{"type":"selector","testability":1,"title":{"en":"The \"b\" (bold) element is not used","nl":"Het \"b\"-element (bold) wordt niet gebruikt"},"description":{"en":"The b (bold) element provides no emphasis for non-sighted readers. Use the strong tag instead.","nl":"Het b-element voorziet niet in nadruk voor blinde en slechtziende gebruikers. Gebruik de strong-tag instead."},"guidelines":[],"tags":["semantics","content"],"options":{"selector":"bold"}},"buttonHasName":{"type":"placeholder","testability":1,"title":{"en":"Button should contain text","nl":"Een knop moet tekst bevatten"},"description":{"en":"Buttons should contain a text value within the element, or have a value attribute.","nl":"Knoppen moeten een tekstwaarde binnen het element hebben, of een waarde-attribuut."},"guidelines":{"wcag":{"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"4.1.2":{"techniques":["H91"]}}},"tags":["content"],"components":["placeholder"],"options":{"content":true,"empty":true,"attribute":"title","selector":"button"}},"checkboxHasLabel":{"type":"label","testability":1,"title":{"en":"All checkboxes must have a corresponding label","nl":"Alle keuzevakjes moeten een bijbehorend label hebben"},"description":{"en":"All input elements with a type of \"checkbox\" should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user","nl":"Alle input-elementen met een \"keuzevakje\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen."},"guidelines":{"508":["c"],"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44","H91"]}}},"tags":["form","content"],"components":["label"],"options":{"selector":"input[type=checkbox]"}},"checkboxLabelIsNearby":{"type":"labelProximity","testability":0.5,"title":{"en":"All \"checkbox\" input elements have a label that is close","nl":"Van alle \"keuzevakjes\" invoerelementen staat het label in de buurt"},"description":{"en":"All input elements of type \"checkbox\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.","nl":"Alle inputelementen van het type \"keuzevakje\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat."},"guidelines":[],"tags":["form","content"],"components":["labelProximity"],"options":{"selector":"input[type=checkbox]"}},"closingTagsAreUsed":{"type":"custom","testability":1,"title":{"en":"All tags that require closing tags have closing tags","nl":"Alle tags die een afsluitende tag behoeven, hebben een afsluitende tag"},"description":{"en":"When using tags such as p, ul, or li, there must be a closing tag.","nl":"Gebruik voor tags als p, ul, of li altijd een afsluitende tag, dus /p, /ul, /li."},"guidelines":{"wcag":{"4.1.1":{"techniques":["H74"]}}},"tags":["html"],"components":["htmlSource"],"callback":"closingTagsAreUsed"},"contentPositioningShouldNotChangeMeaning":{"type":"custom","testability":0.5,"title":{"en":"Meaning should not be created through positioning","nl":"Cre�er geen betekenis door positionering"},"description":{"en":"Positioning should not be used to change the meaning of an element.","nl":"Positionering moet niet worden gebruikt om de betekenis van een element te veranderen."},"guidelines":{"wcag":{"1.3.2":{"techniques":["C6","F1","G57"]},"1.4.5":{"techniques":["C6"]},"1.4.9":{"techniques":["C6"]},"2.4.1":{"techniques":["C6"]}}},"tags":["content","structure"],"callback":"contentPositioningShouldNotChangeMeaning"},"cssDocumentMakesSenseStyleTurnedOff":{"type":"selector","testability":0,"title":{"en":"The document must be readable with styles turned off","nl":"Het document moet leesbaar zijn met stijlen uit"},"description":{"en":"With all the styles for a page turned off, the content of the page should still make sense. Try to turn styles off in the browser and see if the page content is readable and clear.","nl":"Als alle stijlen voor een pagina zijn uitgezet, moet de content van de pagina nog steeds betekenisvol zijn. Zet stijlen uit in de browser en controleer of de content op de pagina nog steeds leesbaar en duidelijk is."},"guidelines":{"wcag":{"1.3.1":{"techniques":["G140"]},"1.4.5":{"techniques":["G140"]},"1.4.9":{"techniques":["G140"]}}},"tags":["color"],"options":{"selector":"link[rel=stylesheet], stylesheet, *[style]"}},"colorFontContrast":{"type":"custom","testability":1,"title":{"en":"All elements should have appropriate color contrast","nl":"Alle elementen moeten een toepasselijk kleurcontract hebben"},"description":{"en":"For users who have color blindness, all text or other elements should have a color contrast of 5:1.","nl":"Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1."},"guidelines":{"wcag":{"1.4.3":{"techniques":["G18"]}}},"tags":["color"],"callback":["colorFontContrast"],"options":{"algorithm":"wcag","selector":"*","gradientSampleMultiplier":3}},"colorElementBehindContrast":{"type":"custom","testability":1,"title":{"en":"All elements should have appropriate color contrast","nl":"Alle elementen moeten een toepasselijk kleurcontract hebben"},"description":{"en":"For users who have color blindness, all text or other elements should have a color contrast of 5:1.","nl":"Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1."},"guidelines":{"wcag":{"1.4.3":{"techniques":["G18"]}}},"tags":["color"],"callback":["colorElementBehindContrast"],"options":{"algorithm":"wcag","selector":"*","gradientSampleMultiplier":3}},"colorBackgroundImageContrast":{"type":"custom","testability":1,"title":{"en":"All elements should have appropriate color contrast","nl":"Alle elementen moeten een toepasselijk kleurcontract hebben"},"description":{"en":"For users who have color blindness, all text or other elements should have a color contrast of 5:1.","nl":"Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1."},"guidelines":{"wcag":{"1.4.3":{"techniques":["G18"]}}},"tags":["color"],"callback":["colorBackgroundImageContrast"],"options":{"algorithm":"wcag","selector":"*","gradientSampleMultiplier":3}},"colorElementBehindBackgroundImageContrast":{"type":"custom","testability":1,"title":{"en":"All elements should have appropriate color contrast","nl":"Alle elementen moeten een toepasselijk kleurcontract hebben"},"description":{"en":"For users who have color blindness, all text or other elements should have a color contrast of 5:1.","nl":"Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1."},"guidelines":{"wcag":{"1.4.3":{"techniques":["G18"]}}},"tags":["color"],"callback":["colorElementBehindBackgroundImageContrast"],"options":{"algorithm":"wcag","selector":"*","gradientSampleMultiplier":3}},"colorBackgroundGradientContrast":{"type":"custom","testability":1,"title":{"en":"All elements should have appropriate color contrast","nl":"Alle elementen moeten een toepasselijk kleurcontract hebben"},"description":{"en":"For users who have color blindness, all text or other elements should have a color contrast of 5:1.","nl":"Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1."},"guidelines":{"wcag":{"1.4.3":{"techniques":["G18"]}}},"tags":["color"],"callback":["colorBackgroundGradientContrast"],"options":{"algorithm":"wcag","selector":"*","gradientSampleMultiplier":3}},"colorElementBehindBackgroundGradientContrast":{"type":"custom","testability":1,"title":{"en":"All elements should have appropriate color contrast","nl":"Alle elementen moeten een toepasselijk kleurcontract hebben"},"description":{"en":"For users who have color blindness, all text or other elements should have a color contrast of 5:1.","nl":"Voor gebruikers met kleurenblindheid moeten alle tekst- en andere elementen een kleurcontrast hebben van 5:1."},"guidelines":{"wcag":{"1.4.3":{"techniques":["G18"]}}},"tags":["color"],"callback":["colorElementBehindBackgroundGradientContrast"],"options":{"algorithm":"wcag","selector":"*","gradientSampleMultiplier":3}},"definitionListsAreUsed":{"type":"custom","testability":0.5,"title":{"en":"Use a definition list for defining terms","nl":"Gebruik een definition list voor definities"},"description":{"en":"When providing a list of terms or definitions, use a definition list.","nl":"Wanneer er gebruik wordt gemaakt van een lijst termen of definities, gebruik hiervoor dan een definition list."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H48"]}}},"tags":["structure"],"callback":"definitionListsAreUsed"},"doctypeProvided":{"type":"custom","testability":1,"title":{"en":"The document should contain a valid \"doctype\" declaration","nl":"Het document moet een geldige \"doctype\"-verklaring hebben"},"description":{"en":"Each document must contain a valid doctype declaration.","nl":"Ieder document moet een geldige doctype-verklaring hebben."},"guidelines":[],"tags":["doctype"],"callback":"doctypeProvided"},"documentAbbrIsUsed":{"type":"custom","testability":0.5,"title":{"en":"Abbreviations must be marked with an \"abbr\" element","nl":"Afkortingen moeten worden gemarkeerd met een \"abbr\"-element"},"description":{"en":"Abbreviations should be marked with an abbr element, at least once on the page for each abbreviation.","nl":"Afkortingen moeten worden gemarkeerd door middel van het abbr-element. Doe dit ten minste een keer per pagina voor elke afkorting."},"guidelines":{"wcag":{"3.1.4":{"techniques":["H28"]}}},"tags":["acronym","content"],"components":["acronym"],"callback":"documentAbbrIsUsed"},"documentAcronymsHaveElement":{"type":"custom","testability":0.5,"title":{"en":"Acronyms must be marked with an \"acronym\" element","nl":"Acroniemen moeten worden gemarkeerd met een \"acronym\"-element"},"description":{"en":"Acronyms should be marked with an acronym element, at least once on the page for each acronym.","nl":"Acroniemen moeten worden gemarkeerd door middel van het acronym-element. Doe dit ten minste een keer per pagina voor elke acroniem."},"guidelines":{"wcag":{"3.1.4":{"techniques":["H28"]}}},"tags":["acronym","content"],"components":["acronym"],"callback":"documentAcronymsHaveElement"},"documentAutoRedirectNotUsed":{"type":"selector","testability":1,"title":{"en":"Auto-redirect with \"meta\" elements must not be used","nl":"Auto-redirect met \"meta\"-elementen moeten niet worden gebruikt"},"description":{"en":"Because different users have different speeds and abilities when it comes to parsing the content of a page, a \"meta-refresh\" method to redirect users can prevent users from fully understanding the document before being redirected.","nl":"Omdat verschillende gebruikers verschillende snelheden en vaardigheden hebben met het scannen van content op een pagina, kan een \"meta-refresh\"-methode om gebruikers door te sturen hen verhinderen het document volledig te begrijpen voor ze worden doorgestuurd."},"guidelines":[],"tags":["document"],"options":{"selector":"meta[http-equiv=refresh]"}},"documentContentReadableWithoutStylesheets":{"type":"selector","testability":0,"title":{"en":"Content should be readable without style sheets","nl":"Content moet zonder stylesheets leesbaar zijn"},"description":{"en":"With all the styles for a page turned off, the content of the page should still make sense. Try to turn styles off in the browser and see if the page content is readable and clear.","nl":"Ook als alle stijlen voor een pagina zijn uitgezet, moet de content van de pagina nog steeds betekenisvol zijn. Zet de stylesheets uit in de browser en controleer of de content nog steeds leesbaar en duidelijk is."},"guidelines":{"508":["d"],"wcag":{"1.3.1":{"techniques":["G140"]},"1.4.5":{"techniques":["G140"]},"1.4.9":{"techniques":["G140"]}}},"tags":["document","color"],"options":{"selector":"html:has(link[rel=stylesheet], style) body, body:has(*[style])"}},"documentHasTitleElement":{"type":"selector","testability":1,"title":{"en":"The document should have a title element","nl":"Het document moet een titelelement hebben"},"description":{"en":"The document should have a title element.","nl":"Het document moet een titelelement hebben."},"guidelines":{"wcag":{"2.4.2":{"techniques":["H25"]}}},"tags":["document","head"],"options":{"selector":"html:not(html:has(title))"}},"documentIDsMustBeUnique":{"type":"custom","testability":1,"title":{"en":"All element \"id\" attributes must be unique","nl":"Alle element \"id\"-attributen moeten uniek zijn"},"description":{"en":"Element \"id\" attributes must be unique.","nl":"Element \"id\"-attributen moeten uniek zijn."},"guidelines":{"wcag":{"4.1.1":{"techniques":["F77","H93"]}}},"tags":["document","semantics"],"callback":"documentIDsMustBeUnique"},"documentIsWrittenClearly":{"type":"custom","testability":0.5,"title":{"en":"The document should be written to the target audience and read clearly","nl":"Het document moet geschreven zijn op het niveau van de doelgroep"},"description":{"en":"If a document is beyond a 10th grade level, then a summary or other guide should also be provided to guide the user through the content.","nl":"Als de inhoud van een document moeilijker is dan het vastgestelde taalniveau, moet een samenvatting of andere begeleiding worden toegevoegd om de gebruiker te helpen met de content."},"guidelines":{"wcag":{"3.1.5":{"techniques":["G86"]}}},"tags":["language","content"],"components":["textStatistics"],"callback":"documentIsWrittenClearly"},"documentLangIsISO639Standard":{"type":"custom","testability":1,"title":{"en":"The document's language attribute should be a standard code","nl":"Het language-attribuut van het document moet een standaard code zijn"},"description":{"en":"The document should have a default langauge, and that language should use the valid 2 or 3 letter language code according to ISO specification 639.","nl":"Het document moet een standaardtaal hebben en die taal moet de geldige 2- of 3-letterige taalcode hebben volgens de ISO-specificatie 639."},"guidelines":{"wcag":{"3.1.1":{"techniques":["H57"]}}},"tags":["document","language"],"strings":["languageCodes"],"callback":"documentLangIsISO639Standard"},"documentLangNotIdentified":{"type":"selector","testability":1,"title":{"en":"The document must have a \"lang\" attribute","nl":"Het document moet een \"lang\"-attribuut hebben"},"description":{"en":"The document should have a default langauge, by setting the \"lang\" attribute in the html element.","nl":"Het document moet een standaardtaal hebben, vastgelegd in het \"lang\"-attribuut in het html-element."},"guidelines":[],"tags":["document","language"],"options":{"selector":"html:not(html[lang])"}},"documentMetaNotUsedWithTimeout":{"type":"selector","testability":1,"title":{"en":"Meta elements must not be used to refresh the content of a page","nl":"Meta-elementen mogen niet worden gebruikt om content op een pagina te verversen"},"description":{"en":"Because different users have different speeds and abilities when it comes to parsing the content of a page, a \"meta-refresh\" method to reload the content of the page can prevent users from having full access to the content. Try to use a \"refresh this\" link instead.","nl":"Omdat verschillende gebruikers verschillende snelheden en vaardigheden hebben met het scannen van content op een pagina, kan een \"meta-refresh\"-methode om de pagina te herladen gebruikers hinderen in toegang tot de content. Gebruik een \"refresh this\" link hiervoor."},"guidelines":{"wcag":{"2.2.1":{"techniques":["F40","F41"]},"2.2.4":{"techniques":["F40","F41"]},"3.2.5":{"techniques":["F41"]}}},"tags":["document"],"options":{"selector":"meta[http-equiv=refresh]"}},"documentReadingDirection":{"type":"selector","testability":0.5,"title":{"en":"Reading direction of text is correctly marked","nl":"De leesrichting van de tekst staat juist aangegeven"},"description":{"en":"Where required, the reading direction of the document (for language that read in different directions), or portions of the text, must be declared.","nl":"Voor talen die een andere leesrichting hebben, moet de leesrichting van (een deel van) de tekst in een document worden opgenomen."},"guidelines":{"wcag":{"1.3.2":{"techniques":["H34"]}}},"tags":["document","language"],"options":{"selector":"*[lang=he]:not(*[dir=rtl]), *[lang=ar]:not(*[dir=rtl])"}},"documentStrictDocType":{"type":"custom","testability":1,"title":{"en":"The page uses a strict doctype","nl":"De pagina gebruikt een strikt doctype"},"description":{"en":"The doctype of the page or document should be either an HTML or XHTML strict doctype.","nl":"Het doctype van een pagina of document moet een HTML of XHTML strikt doctype zijn."},"guidelines":[],"tags":["document","doctype"],"callback":"documentStrictDocType"},"documentTitleDescribesDocument":{"type":"selector","testability":0,"title":{"en":"The title describes the document","nl":"De titel beschrijft het document"},"description":{"en":"The document title should actually describe the page. Often, screen readers use the title to navigate from one window to another.","nl":"De documenttitel moet een beschrijving zijn van de pagina. Schermlezen gebruiken de titels van pagina's om van het ene naar het andere scherm te navigeren."},"guidelines":{"wcag":{"2.4.2":{"techniques":["F25","G88"]}}},"tags":["document","head"],"options":{"selector":"head title:first"}},"documentTitleIsNotPlaceholder":{"type":"placeholder","testability":1,"title":{"en":"The document title should not be placeholder text","nl":"De documenttitle moet geen placeholder tekst zijn"},"description":{"en":"The document title should not be wasted placeholder text which does not describe the page.","nl":"De documenttitel moet geen placeholder tekst zijn die geen goede beschrijving van de pagina is."},"guidelines":{"wcag":{"2.4.2":{"techniques":["F25","G88"]}}},"tags":["document","head"],"components":["placeholder"],"options":{"content":true,"selector":"head title:first"}},"documentTitleIsShort":{"type":"custom","testability":0.5,"title":{"en":"The document title should be short","nl":"De documenttitel moet kort zijn"},"description":{"en":"The document title should be short and succinct. This test fails at 150 characters, but authors should consider this to be a suggestion.","nl":"De documenttitel moet kort en beknopt zijn. Probeer bij een titel langer dan 150 tekense de titel in te korten waar mogelijk."},"guidelines":[],"tags":["document","head"],"callback":"documentTitleIsShort"},"documentTitleNotEmpty":{"type":"placeholder","testability":1,"title":{"en":"The document should not have an empty title","nl":"Het document mag geen lege titel hebben"},"description":{"en":"The document should have a title element that is not white space.","nl":"Het document moet een titelelement hebben dat is ingevuld."},"guidelines":{"wcag":{"2.4.2":{"techniques":["F25","H25"]}}},"tags":["document","head"],"components":["placeholder"],"options":{"content":true,"empty":true,"selector":"head title"}},"documentValidatesToDocType":{"type":"custom","testability":1,"title":{"en":"Document must validate to the doctype","nl":"Het document moet valideren met het doctype"},"description":{"en":"The document must validate to the declared doctype.","nl":"Het document moet valideren met het vastgestelde doctype."},"guidelines":{"wcag":{"4.1.1":{"techniques":["G134"]}}},"tags":["document","doctype"],"callback":"documentValidatesToDocType"},"documentVisualListsAreMarkedUp":{"type":"custom","testability":1,"title":{"en":"Lists of items should be marked using list markup","nl":"Lijsten moeten gemarkeerd worden als ordered of unordered lists"},"description":{"en":"Use the ordered or unordered (bulleted) list elements to mark lists instead of using new lines that start with numbers or other characters to create a visual list.","nl":"Gebruik ordered (ol) of unordered (ul) elementen voor lijsten, in plaats van een nieuwe regel per item aan te maken die je laat beginnen met een nummer of teken om een visuele lijst te maken."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H28","H48","T2"]}}},"tags":["list","semantics","content"],"strings":["symbols"],"callback":"documentVisualListsAreMarkedUp"},"domOrderMatchesVisualOrder":{"type":"selector","testability":0.5,"title":{"en":"Ensure that the visual order of the page matches the DOM","nl":"Zorg ervoor dat de visuele ordening van de pagina overeenkomt met de DOM"},"description":{"en":"When using positioning techniques, make sure that the visual order of the page matches the DOM.","nl":"Wanneer je gebruik maakt van positioneringstechnieken, zorg er dan voor dat de visuele ordening van de pagina overeenkomt met de DOM."},"guidelines":{"wcag":{"1.3.2":{"techniques":["C27"]},"2.4.3":{"techniques":["C27"]}}},"tags":["content"],"options":{"selector":"*:quailCss(position=absolute), *:quailCss(position=fixed), *:quailCss(float=right), *:quailCss(float=left)"}},"elementAttributesAreValid":{"type":"custom","testability":1,"title":{"en":"Attributes should have spaces between them and be wrapped in quotes","nl":"Attributen moeten gescheiden zijn door spaties en ze moeten tussen aanhalingstekens"},"description":{"en":"Attributes should have spaces between them and be wrapped in quotes.","nl":"Tussen attributen moeten spaties staan en ze moeten tussen aanhalingstekens staan."},"guidelines":{"4.1.1":{"techniques":["F70"]}},"callback":"elementAttributesAreValid"},"embedHasAssociatedNoEmbed":{"type":"custom","testability":1,"title":{"en":"All \"embed\" elements have an associated \"noembed\" element","nl":"Alle \"embed\" elementen moeten een bijbehorend \"noembed\"-element hebben"},"description":{"en":"Because some users cannot use the embed element, provide alternative content in a noembed element.","nl":"Sommige gebruikers kunnen het embed-element niet gebruiken. Biedt hiervoor alternatieve content aan in een noembed-element."},"guidelines":[],"tags":["object","embed","content"],"callback":"embedHasAssociatedNoEmbed"},"elementsDoNotHaveDuplicateAttributes":{"type":"custom","testability":1,"title":{"en":"Elements should not have duplicate attributes","nl":"Elementen mogen geen dubbele attributen hebben"},"description":{"en":"Elements should only have one type of any attribute. For example, there should not be two 'class' attributes.","nl":"Elementen mogen maar een type attribuut hebben. Er mogen bijvoorbeeld niet twee 'class'-attributen zijn."},"guidelines":[],"tags":["content"],"components":["htmlSource"],"libraries":["node-htmlparser/lib/htmlparser.js"],"callback":"elementsDoNotHaveDuplicateAttributes"},"embedMustHaveAltAttribute":{"type":"selector","testability":1,"title":{"en":"\"Embed\" elements must have an \"alt\" attribute","nl":"\"Embed\"-elementen moeten een \"alt\"-attribuut hebben"},"description":{"en":"All embed elements must have an \"alt\" attribute.","nl":"Alle embed-elementen moeten een \"alt\"-attribuut hebben."},"guidelines":[],"tags":["object","embed","content"],"options":{"selector":"embed:not([alt])"}},"embedMustNotHaveEmptyAlt":{"type":"selector","testability":1,"title":{"en":"\"Embed\" elements cannot have an empty \"alt\" attribute","nl":"\"Embed\"-elementen mogen geen leeg \"alt\"-attribuut hebben"},"description":{"en":"All embed elements must have an \"alt\" attribute that is not empty.","nl":"Alle embed-elementen moeten een gevuld \"alt\"-attribuut hebben."},"guidelines":[],"tags":["object","embed","content"],"options":{"selector":"embed[alt=]"}},"embedProvidesMechanismToReturnToParent":{"type":"selector","testability":0,"title":{"en":"All embed elements should provide a way for keyboard users to escape","nl":"Alle embed-elementen moeten een manier bieden voor toetsenbordgebruikers om het element te verlaten"},"description":{"en":"Ensure that a user who has only a keyboard as an input device can escape an embed element. This requires manual confirmation.","nl":"Zorg ervoor dat een gebruiker die alleen het toetsenbord gebruikt voor bediening een embed-element kan verlaten. Hiervoor is handmatige bevestiging nodig."},"guidelines":{"wcag":{"2.1.2":{"techniques":["G21"]}}},"tags":["object","embed","content"],"options":{"selector":"embed"}},"emoticonsExcessiveUse":{"type":"custom","testability":0.5,"title":{"en":"Emoticons should not be used excessively","nl":"Gebruik emoticons spaarzaam"},"description":{"en":"Emoticons should not be used excessively to communicate feelings or content. Try to rewrite the document to have more textual meaning, or wrapping the emoticons in an abbr element as outlined below. Emoticons are not read by screen-readers, and are often used to communicate feelings or other things which are relevant to the content of the document.","nl":"Gebruik emoticons spaarzaam om gevoel of content over te brengen. Probeer het document te herschrijven in tekst, of geef de emoticons een abbr-element. Emoticons worden niet voorgelezen door schermlezers, maar bevatten wel vaak informatie die relevant is voor de content van het document."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H86"]}}},"tags":["language","emoticons","content"],"callback":"emoticonsExcessiveUse"},"emoticonsMissingAbbr":{"type":"custom","testability":1,"title":{"en":"Emoticons should have abbreviations","nl":"Emoticons moeten afkortingen hebben"},"description":{"en":"Emoticons are not read by screen-readers, and are often used to communicate feelings or other things which are relevant to the content of the document. If this emoticon is important content, mark it up with an \"abbr\" or \"acronym\" tag.","nl":"Emoticons worden niet voorgelezen door schermlezers, maar bevatten wel vaak informatie die relevant is voor de content van het document. Als een emoticon belangrijke content is, gebruik dan een \"abbr\" or \"acronym\" tag om het te markeren."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H86"]}}},"tags":["language","emoticons","content"],"callback":"emoticonsMissingAbbr"},"fieldsetHasLabel":{"type":"selector","testability":1,"title":{"en":"Fieldsets require a label element","nl":"Fieldsets behoeven een label-element"},"description":{"en":"Fieldsets used to group similar form elements like checkboxes should have a label that describes the group of elements.","nl":"Fieldsets die een groep gelijkwaardige elementen bevatten moeten een label hebben die deze groep elementen beschrijft."},"guidelines":{"wcag":{"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"4.1.2":{"techniques":["H91"]}}},"tags":["form","content"],"options":{"selector":"fieldset:not(fieldset:has(legend))"}},"fileHasLabel":{"type":"label","testability":1,"title":{"en":"All \"file\" input elements have a corresponding label","nl":"Alle \"file\"-invoerelementen hebben een bijbehorend label"},"description":{"en":"All input elements of type \"file\" should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user.","nl":"Alle input-elementen van het type \"file\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen."},"guidelines":{"508":["n"],"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44"]}}},"tags":["form","content"],"components":["label"],"options":{"selector":"input[type=file]"}},"fileLabelIsNearby":{"type":"labelProximity","testability":0.5,"title":{"en":"All \"file\" input elements have a label that is close","nl":"Van alle \"file\"-invoerelementen staat het label in de buurt"},"description":{"en":"All input elements of type \"file\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.","nl":"Alle inputelementen van het type \"file\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat."},"guidelines":[],"tags":["form","content"],"components":["labelProximity"],"options":{"selector":"input[type=file]"}},"focusIndicatorVisible":{"type":"custom","title":{"en":"Focus indicators have high visibility","nl":"Focus indicators moeten goed zichtbaar zijn"},"description":{"en":"When a focus indicator is used, it should have enough contrast with the background and big enough to be highly visible.","nl":"Wanneer je een focus indicator gebruikt, moet het contrast tussen de indicator en de achtergrond groot genoeg zijn in verband met de zichtbaarheid."},"guidelines":{"wcag":{"2.4.7":{"techniques":["C15","G165","G195"]}}},"tags":["focus","content"],"components":["color","convertToPx"],"callback":"focusIndicatorVisible"},"fontIsNotUsed":{"type":"selector","testability":1,"title":{"en":"Font elements should not be used","nl":"Het font element moet niet worden gebruikt"},"description":{"en":"The basefont tag is deprecated and should not be used. Investigate using stylesheets instead.","nl":"De basefont-tag is afgekeurd en moet niet worden gebruikt. Gebruik in plaats hiervan stylesheets."},"guidelines":[],"tags":["deprecated","content"],"options":{"selector":"font"}},"formAllowsCheckIfIrreversable":{"type":"selector","testability":0,"guidelines":[],"tags":["form","content"],"options":{"selector":"form"}},"formButtonsHaveValue":{"type":"selector","testability":1,"title":{"en":"Input elements for button, submit, or reset must have a value attribute","nl":"Invoerelementen voor knoppen, indienen of resetten moeten een waarde-attribuut hebben"},"description":{"en":"Any form element that is rendered as a button has to have a readable value attribute.","nl":"Elk invoerelement dat eruit ziet als een knop moet een leesbaar waarde-attribuut hebben."},"guidelines":{"wcag":{"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"4.1.2":{"techniques":["H91"]}}},"tags":["form","content"],"options":{"selector":"input[type=button], input[type=submit], input[type=reset]","test":":not([value])"}},"formDeleteIsReversable":{"type":"selector","testability":0,"title":{"en":"Deleting items using a form should be reversable","nl":"De verwijdering van een item in een formulier moet ongedaan gemaakt kunnen worden"},"description":{"en":"Check that, if a form has the option to delete an item, that the user has a chance to either reverse the delete process, or is asked for confirmation before the item is deleted. This is not something that can be checked through automated testing and requires manual confirmation.","nl":"Controleer of een gebruiker de verwijdering van een invoer in een formulier ongedaan kan maken wanneer het mogelijk is om een invoer te verwijderen. Dit kan niet met een automatische test en moet handmatig gecontroleerd en bevestigd worden."},"guidelines":[],"tags":["form","content"],"options":{"selector":"form"}},"formErrorMessageHelpsUser":{"type":"selector","testability":0,"title":{"en":"Forms offer the user a way to check the results of their form before performing an irrevocable action","nl":"Formulieren bieden gebruikers de gelegenheid om hun formulier te controleren voor ze een onomkeerbare actie uitvoeren"},"description":{"en":"If the form allows users to perform some irrevocable action, like ordreing a product, ensure that users have the ability to review the contents of the form they submitted first. This is not something that can be checked through automated testing and requires manual confirmation.","nl":"Als een formulier een gebruiker toestaat om een onomkeerbare actie uit te voeren, zoals het bestellen van een product, zorg er dan voor dat ze eerst het formulier kunnen controleren. Dit kan niet met een automatische test en moet handmatig gecontroleerd en bevestigd worden."},"guidelines":[],"tags":["form","content"],"options":{"selector":"form"}},"formHasGoodErrorMessage":{"type":"selector","testability":0,"title":{"en":"Form error messages should assist in solving errors","nl":"Foutmeldingen in formulieren moeten fouten helpen oplossen"},"description":{"en":"If the form has some required fields or other ways in which the user can commit an error, check that the reply is accessible. Use the words \"required\" or \"error\" within the label element of input items where the errors happened.","nl":"Als het formulier verplichte velden heeft of op andere manier verkeerd ingevuld kan worden, controleer dan of de bijbehorende foutmelding begrijpelijk is. Gebruik de woorden \"required\" of \"error\" in het label-element of in de invoeritems waar de fout is opgetredenitems where the errors happened."},"guidelines":[],"tags":["form","content"],"options":{"selector":"form"}},"formHasSubmitButton":{"type":"selector","testability":1,"title":{"en":"Form should have a submit button","nl":"Formulieren moeten een indienknop hebben"},"description":{"en":"Forms should have a button that allows the user to select when they want to submit the form.","nl":"Formulieren moeten een knop hebben waarmee de gebruiker kan bepalen wanneer zij een formulieren willen versturen."},"guidelines":{"wcag":{"3.2.2":{"techniques":["H32","G80"]}}},"tags":["form","content"],"options":{"selector":"form:not(form:has(input[type=image], input[type=submit], button[type=submit]))"}},"formWithRequiredLabel":{"type":"custom","testability":0,"title":{"en":"Input items which are required are marked as so in the label element","nl":"Invoervelden die verplicht zijn, zijn zo gemarkeerd in het label-element"},"description":{"en":"If a form element is required, it should be marked as so. This should not be a mere red asterisk, but instead either a 'required' image with alt text of \"required\" or the actual text \"required\". The indicator that an item is required should be included in the input element's label element.","nl":"Als een formulierveld verplicht is, moet het ook zichtbaar zijn. Doe dit niet alleen met een asterisk achter het veld, maar met bijvoorbeeld een afbeelding met als alttekst \"required\" of de tekst \"required\". De indicatie dat een veld verplicht is moet opgenomen zijn in het label-element van het invoerveld."},"guidelines":{"wcag":{"1.3.1":{"techniques":["ARIA2"]},"1.4.1":{"techniques":["F81"]},"3.3.2":{"techniques":["ARIA2","H90"]},"3.3.3":{"techniques":["ARIA2"]}}},"tags":["form","content"],"callback":"formWithRequiredLabel"},"frameIsNotUsed":{"type":"selector","testability":1,"title":{"en":"Frames are not used","nl":"Gebruik geen frames"},"description":{"en":"Frames should not be used to organize a page.","nl":"Gebruik geen frames om een pagina te organiseren."},"guidelines":[],"tags":["deprecated","frame"],"options":{"selector":"frame"}},"frameRelationshipsMustBeDescribed":{"type":"selector","testability":0.5,"title":{"en":"Complex framesets should contain a \"longdesc\" attribute","nl":"Complexe framesets moeten een \"longdesc\"-attribuut bevatten"},"description":{"en":"If a frameset contains three or more frames, use a \"longdesc\" attribute to help describe the purpose of the frames.","nl":"Als een frameset drie of meer frames bevat, gebruik dan een \"longdesc\"-attribuut om het doel van de frames te beschrijven."},"guidelines":[],"tags":["deprecated","frame"],"options":{"selector":"frameset:not(frameset[longdesc])"}},"framesAreUsedToGroupContent":{"type":"selector","testability":0.5,"title":{"en":"Use frame elements to group repeated materials","nl":"Gebruik frame-elementen om herhaalde content te groeperen"},"description":{"en":"When blocks of repeated content are used on a site, use frames to group content that is the same across pages.","nl":"Wanneer blokken content op een site herhaald worden, gebruik dan frames om content die op verschillende pagina's voorkomt te groeperen."},"guidelines":{"wcag":{"2.4.1":{"techniques":["H70"]}}},"tags":["deprecated","frame"],"options":{"selector":"body:not(body:has(frameset))"}},"frameSrcIsAccessible":{"type":"selector","testability":0,"title":{"en":"The source for each frame is accessible content.","nl":"De bron van elk frame is toegankelijke content."},"description":{"en":"Each frame should contain accessible content, and contain content accessible to screen readers, like HTML as opposed to an image.","nl":"Elk frame moet toegankelijke content bevatten, en content die toegankelijk is voor schermlezers, zoals HTML in tegenstelling tot een afbeelding."},"guidelines":[],"tags":["deprecated","frame"],"options":{"selector":"frame"}},"frameTitlesDescribeFunction":{"type":"placeholder","testability":0,"title":{"en":"All \"frame\" elements should have a \"title\" attribute that describes the purpose of the frame","nl":"Alle \"frame\" elementen moeten een \"title\"-attribuut hebben dat het doel van het frame beschrijft"},"description":{"en":"Each frame elements should have a \"title\" attribute which describes the purpose or function of the frame.","nl":"Elk frame-element moet een \"title\"-attribuut hebben dat het doel of de functie van het frame beschrijft."},"guidelines":{"wcag":{"2.4.1":{"techniques":["H64"]}}},"tags":["deprecated","frame"],"components":["placeholder"],"options":{"attribute":"title","empty":true,"selector":"frame[title], iframe[title]"}},"frameTitlesNotEmpty":{"type":"selector","testability":1,"title":{"en":"Frames cannot have empty \"title\" attributes","nl":"Frames mogen geen leeg \"title\"-attribuut hebben"},"description":{"en":"All frame elements must have a valid \"title\" attribute.","nl":"Alle frame-elementen moeten een geldig \"title\"-attribuut hebben."},"guidelines":{"wcag":{"2.4.1":{"techniques":["H64"]},"4.1.2":{"techniques":["H64"]}}},"tags":["deprecated","frame"],"options":{"selector":"frame:not(frame[title]), frame[title=''], iframe:not(iframe[title]), iframe[title='']"}},"frameTitlesNotPlaceholder":{"type":"placeholder","testability":1,"title":{"en":"Frames cannot have \"title\" attributes that are just placeholder text","nl":"Frames mogen geen \"title\"-attribuut hebben met placeholdertekst"},"description":{"en":"Frame \"title\" attributes should not be simple placeholder text like \"frame\".","nl":"Frame \"title\"-attributen mogen geen placeholdertekst bevatten zoals \"frame\"."},"guidelines":{"wcag":{"2.4.1":{"techniques":["H64"]},"4.1.2":{"techniques":["H64"]}}},"tags":["deprecated","frame"],"components":["placeholder"],"options":{"attribute":"title","selector":"frame, iframe"}},"framesHaveATitle":{"type":"selector","testability":1,"title":{"en":"All \"frame\" elements should have a \"title\" attribute","nl":"Alle \"frame\"-elementen moeten een \"title\"-attribuut hebben"},"description":{"en":"Each frame elements should have a \"title\" attribute.","nl":"Elk frame-elementen moeten een \"title\"-attribuut hebben."},"guidelines":{"wcag":{"2.4.1":{"techniques":["H64"]},"4.1.2":{"techniques":["H64"]}}},"tags":["deprecated","frame"],"options":{"selector":"frame:visible, iframe:visible","test":":not([title])"}},"framesetIsNotUsed":{"type":"selector","testability":1,"title":{"en":"The \"frameset\" element should not be used","nl":"Het \"frameset\"-element wordt niet gebruikt"},"description":{"en":"Frames and framesets should not be used to organize content.","nl":"Frames en framesets moeten niet gebruikt worden om content te organiseren."},"guidelines":[],"tags":["deprecated","frame"],"options":{"selector":"frameset"}},"framesetMustHaveNoFramesSection":{"type":"selector","testability":0.5,"title":{"en":"All framesets should contain a noframes section","nl":"Alle framesets moeten een noframes-sectie bevatten"},"description":{"en":"If a frameset contains three or more frames, use a \"longdesc\" attribute to help describe the purpose of the frames.","nl":"Als een frameset drie of meer frames bevat, gebruik dan een \"longdesc\"-attribuut om het doel van de frames te beschrijven."},"guidelines":[],"tags":["deprecated","frame"],"options":{"selector":"frameset:not(frameset:has(noframes))"}},"headersAttrRefersToATableCell":{"type":"custom","testability":1,"title":{"en":"Table cell headers attrtibutes must within the same table have an associated data cell with the same id","nl":"Tabel cellen met een headers attribuut moeten binnen dezelfde tabel een overeenkomende data cel hebben in het id attribuut dezelfde waarde"},"description":{"en":"","nl":""},"guidelines":[],"tags":["headers","td","th"],"callback":"headersAttrRefersToATableCell"},"headerH1":{"type":"headingLevel","testability":0,"title":{"en":"Header level 1 can only be followed by level 2","nl":"De header die volgt op een h1 is niet h3 tot h6"},"description":{"en":"Header order should not skip a level. Do not follow a header level 1 with a level 3, 4, 5, or 6.","nl":"Headers mogen geen niveau overslaan. Laat een h1-header niet volgen door een h3, h4, h5, of h6."},"guidelines":{"wcag":{"2.4.6":{"techniques":["G130"]}}},"tags":["header","content"],"components":["headingLevel"],"options":{"headingLevel":1}},"headerH1Format":{"type":"selector","testability":0,"title":{"en":"All h1 elements are not used for formatting","nl":"H1-elementen worden niet gebruikt voor formatting"},"description":{"en":"An h1 element may not be used purely for formatting.","nl":"Een h1-element mag niet alleen gebruikt worden voor formatting."},"guidelines":{"wcag":{"1.3.1":{"techniques":["T3"]}}},"tags":["header","content"],"components":["header"],"options":{"selector":"h1"}},"headerH2":{"type":"headingLevel","testability":0,"title":{"en":"Header level 2 can not be followed by levels from 4 to 6","nl":"De header volgend op een h2 is geen h4, h5, of h6"},"description":{"en":"Header order should not skip a level. Do not follow a header level 2 with a level 4, 5, or 6.","nl":"Headers mogen geen niveau overslaan. Laat een h2-header niet volgen door een h4, h5, of h6."},"guidelines":{"wcag":{"2.4.6":{"techniques":["G130"]}}},"tags":["header","content"],"components":["headingLevel"],"options":{"headingLevel":2}},"headerH2Format":{"type":"selector","testability":0,"title":{"en":"All h2 elements are not used for formatting","nl":"H2-elementen worden niet gebruikt voor formatting"},"description":{"en":"An h2 element may not be used purely for formatting.","nl":"Een h2-element mag niet alleen gebruikt worden voor formatting."},"guidelines":{"wcag":{"1.3.1":{"techniques":["T3"]}}},"tags":["header","content"],"components":["header"],"options":{"selector":"h2"}},"headerH3":{"type":"headingLevel","testability":0,"title":{"en":"Header level 3 can not be followed by levels 5 and 6","nl":"De header volgend op een h3 is geen h5, of h6"},"description":{"en":"Header order should not skip a level. Do not follow a header level 3 with a level 5 or 6.","nl":"Headers mogen geen niveau overslaan. Laat een h3-header niet volgen door een h5, of h6."},"guidelines":{"wcag":{"2.4.6":{"techniques":["G130"]}}},"tags":["header","content"],"components":["headingLevel"],"options":{"headingLevel":3}},"headerH3Format":{"type":"selector","testability":0,"title":{"en":"All h3 elements are not used for formatting","nl":"H3-elementen worden niet gebruikt voor formatting"},"description":{"en":"An h3 element may not be used purely for formatting.","nl":"Een h3-element mag niet alleen gebruikt worden voor formatting."},"guidelines":{"wcag":{"1.3.1":{"techniques":["T3"]}}},"tags":["header","content"],"components":["header"],"options":{"selector":"h3"}},"headerH4":{"type":"headingLevel","testability":0,"title":{"en":"Header level 4 can not be followed by level 6","nl":"De header volgend op een h4 is geen h6"},"description":{"en":"Header order should not skip a level. Do not follow a header level 4 with level 6 header.","nl":"Headers mogen geen niveau overslaan. Laat een h4/code> header niet volgen door een h6."},"guidelines":{"wcag":{"2.4.6":{"techniques":["G130"]}}},"tags":["header","content"],"components":["headingLevel"],"options":{"headingLevel":4}},"headerH4Format":{"type":"selector","testability":0,"title":{"en":"All h4 elements are not used for formatting","nl":"H4-elementen worden niet gebruikt voor formatting"},"description":{"en":"An h4 element may not be used purely for formatting.","nl":"Een h4-element mag niet alleen gebruikt worden voor formatting."},"guidelines":{"wcag":{"1.3.1":{"techniques":["T3"]}}},"tags":["header","content"],"components":["header"],"options":{"selector":"h4"}},"headerH5Format":{"type":"selector","testability":0,"title":{"en":"All h5 elements are not used for formatting","nl":"H5-elementen worden niet gebruikt voor formatting"},"description":{"en":"An h5 element may not be used purely for formatting.","nl":"Een h5-element mag niet alleen gebruikt worden voor formatting."},"guidelines":{"wcag":{"1.3.1":{"techniques":["T3"]}}},"tags":["header","content"],"options":{"selector":"h5"}},"headerH6Format":{"type":"selector","testability":0,"title":{"en":"All h6 elements are not used for formatting","nl":"H6-elementen worden niet gebruikt voor formatting"},"description":{"en":"An h6 element may not be used purely for formatting.","nl":"Een h6-element mag niet alleen gebruikt worden voor formatting."},"guidelines":{"wcag":{"1.3.1":{"techniques":["T3"]}}},"tags":["header","content"],"options":{"selector":"h6"}},"headerTextIsTooLong":{"type":"custom","testability":1,"title":{"en":"Header text is too long","nl":"-- missing translation --"},"description":{"en":"Headers should not contain too long text as they're designed to show document structure. Users will often skim document only by its headings.","nl":"-- missing translation --"},"guidelines":{"wcag":{"1.3.1":{"techniques":["H42"]}}},"tags":["content"],"callback":"headerTextIsTooLong"},"headersHaveText":{"type":"placeholder","testability":1,"title":{"en":"All headers should contain readable text","nl":"Alle headers moeten leesbare tekst bevatten"},"description":{"en":"Users with screen readers use headings (e.g. h1 elements), just like the tabs to navigate the structure of a page. All headings should contain either text, or images with appropriate alternative text.","nl":"Gebruikers van schermlezers gebruiken headers om via de structuur van een pagina te navigeren. Alle headers moeten daarom tekst bevatten of afbeeldingen met toepasselijk alt-attributen."},"guidelines":{"wcag":{"1.3.1":{"techniques":["G141"]},"2.4.10":{"techniques":["G141"]}}},"tags":["header","content"],"components":["placeholder"],"options":{"content":true,"empty":true,"selector":"h1, h2, h3, h4, h5, h6"}},"headersUsedToIndicateMainContent":{"type":"custom","testability":0.5,"title":{"en":"Use header to indicate start of main content","nl":"Gebruik headers om de start van belangrijke content aan te geven"},"description":{"en":"For every main content area, indicate the beginning of the main content using a header.","nl":"Geef het begin van de belangrijkste content in elk contentvlak aan door middel van een header."},"guidelines":{"wcag":{"2.4.1":{"techniques":["H69"]}}},"tags":["header","content"],"callback":"headersUsedToIndicateMainContent"},"headersUseToMarkSections":{"type":"custom","testability":0.5,"title":{"en":"Use headers to mark the beginning of each section","nl":"Gebruik headers om de start van elke sectie aan te geven."},"description":{"en":"Check that each logical section of the page is broken or introduced with a header (h1-h6) element.","nl":"Controleer dat elke logische sectie van een pagina wordt onderbroken door of start met een header-element (h1-h6)."},"guidelines":{"wcag":{"1.3.1":{"techniques":["G141"]},"2.4.1":{"techniques":["G141","H69"]}}},"tags":["header","content"],"callback":"headersUseToMarkSections"},"idRefHasCorrespondingId":{"type":"custom","testability":1,"title":{"en":"Elements with an idref type attribute must correspond to an element with an ID","nl":"Elementen met een idref type attribuut moeten corresponderen met een element met een ID"},"description":{"en":"When using an idref type attribute, the target element with the ID must exist on the page.","nl":"Wanneer je een idref type attibuut gebruikt, moet het doelelement met dit ID ook bestaan op de pagina."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F17"]},"4.1.1":{"techniques":["F17"]}}},"callback":"idRefHasCorrespondingId"},"idrefsHasCorrespondingId":{"type":"custom","testability":1,"title":{"en":"Elements with an idref attribute must correspond to an element with an ID","nl":"Elementen met een idref-attribuut moeten corresponderen met een element met een ID"},"description":{"en":"","nl":""},"guidelines":{"wcag":{"1.3.1":{"techniques":["F17"]},"4.1.1":{"techniques":["F17"]}}},"callback":"idrefsHasCorrespondingId"},"iIsNotUsed":{"type":"selector","testability":1,"title":{"en":"The \"i\" (italic) element is not used","nl":"Het \"i\"-element (cursief) wordt niet gebruikt"},"description":{"en":"The i (italic) element provides no emphasis for non-sighted readers. Use the em tag instead.","nl":"Het i-element biedt geen nadruk voor slechtziende en blinde lezers. Gebruik in plaats daarvan de em-tag."},"guidelines":[],"tags":["deprecated","content"],"options":{"selector":"i"}},"iframeMustNotHaveLongdesc":{"type":"selector","testability":1,"title":{"en":"Inline frames (\"iframes\") should not have a \"longdesc\" attribute","nl":"Inline frames (\"iframes\") krijgen geen \"longdesc\"-attribuut"},"description":{"en":"Inline frames (iframe) should not have a \"longdesc\" attribute.","nl":"Inline frames (\"iframes\") krijgen geen \"longdesc\"-attribuut."},"guidelines":[],"tags":["objects","iframe","content"],"options":{"selector":"iframe[longdesc]"}},"imageMapServerSide":{"type":"selector","testability":1,"title":{"en":"All links in a server-side map should have duplicate links available in the document","nl":"Alle links in een server-side map moeten elders in het document terugkeren"},"description":{"en":"Any image with an \"usemap\" attribute for a server-side image map should have the available links duplicated elsewhere.","nl":"Elke afbeelding met een \"usemap\"-attribuut voor een server-side map moet de beschikbare links ook elders hebben."},"guidelines":[],"tags":["objects","iframe","content"],"options":{"selector":"img[ismap]"}},"imgAltEmptyForDecorativeImages":{"type":"selector","testability":0,"title":{"en":"If an image is purely decorative, the \"alt\" text must be empty","nl":"Als een afbeelding alleen ter decoratie is, moet de \"alt\"-tekst leeg zijn"},"description":{"en":"Any image that is only decorative (serves no function or adds to the purpose of the page content) should have an empty \"alt\" attribute.","nl":"Elke afbeelding die alleen ter decoratie is (en die dus geen functie heeft of bijdraagt aan het doel van een contentpagina) moet een leeg \"alt\"-attirbuut hebben."},"guidelines":{"wcag":{"1.3.3":{"techniques":["F26"]}}},"tags":["image","content"],"options":{"selector":"img[alt]"}},"imgAltIdentifiesLinkDestination":{"type":"selector","testability":0,"title":{"en":"Any image within a link must have \"alt\" text the describes the link destination","nl":"Elke afbeelding binnen een link moet een \"alt\"-tekst hebben die de bestemming van de link beschrijft"},"description":{"en":"Any image that is within a link should have an \"alt\" attribute which identifies the destination or purpose of the link.","nl":"Elke afbeelding binnen link moet een \"alt\"-tekst hebben die de bestemming of het doel van de link beschrijft."},"guidelines":[],"tags":["image","content"],"options":{"selector":"a img[alt]:first"}},"imgAltIsDifferent":{"type":"custom","testability":0.5,"title":{"en":"Image alternative text should not be the same as the file name","nl":"\"Alt\"-attributen van afbeeldingen moeten niet hetzelfde zijn als de bestandsnaam"},"description":{"en":"All images should have a meaningful alternative text. The file name is rarely meaningful and does not always reflect information presented by the image.","nl":"Alle img-elementen moeten een \"alt\"-attribuut hebben dat anders is dan de bestandsnaam van de afbeelding."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["H37"]}}},"tags":["image","content"],"callback":"imgAltIsDifferent"},"imgAltIsSameInText":{"type":"selector","testability":0,"title":{"en":"Check that any text within an image is also in the \"alt\" attribute","nl":"Controleer dat tekst in een afbeelding ook is opgenomen in het \"alt\"-attribuut"},"description":{"en":"If an image has text within it, that text should be repeated in the \"alt\" attribute","nl":"Als een afbeelding tekst bevat, moet deze tekst herhaald worden in het \"alt\"-attribuut."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["G74","H37"]}}},"tags":["image","content"],"options":{"selector":"img"}},"imgAltIsTooLong":{"type":"custom","testability":1,"title":{"en":"Image alternative text is too long","nl":"Altteksten voor een afbeelding zijn kort"},"description":{"en":"Image alternative text should be clear and concise. Alternative text longer than 100 characters should be reviewed to see if it can be shortened.","nl":"Alle \"alt\"-attributen voor img-elementen moeten duidelijk en bondig zijn. Verifieer \"alt\"-attributen langer dan 100 tekens en kort ze in waar mogelijk."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["H37"]}}},"tags":["image","content"],"callback":"imgAltIsTooLong"},"imgAltNotEmptyInAnchor":{"type":"custom","testability":1,"title":{"en":"An image within a link cannot have an empty alternative text if there is no other text within the link","nl":"Een afbeelding binnen een link mag geen leeg \"alt\"-attribuut hebben als er geen andere tekst is in de link"},"description":{"en":"Any image that is within a link that has no other text cannot have an empty or missing alternative text.","nl":"Elke afbeelding binnen een link (een a-element) die geen andere tekst heeft, mag geen leeg of ontbrekend \"alt\"-attribuut hebben."},"guidelines":{"508":["a"],"wcag":{"2.4.4":{"techniques":["H30"]}}},"tags":["image","content"],"callback":"imgAltNotEmptyInAnchor"},"imgAltNotPlaceHolder":{"type":"placeholder","testability":1,"title":{"en":"Images should not have a simple placeholder text as an \"alt\" attribute","nl":"Afbeeldingen mogen geen placeholdertkest als \"alt\"-attribuut hebben"},"description":{"en":"Any image that is not used decorativey or which is purely for layout purposes cannot have an \"alt\" attribute that consists solely of placeholders.","nl":"Elke afbeelding die niet ter decoratie is of die alleen voor lay-out doeleinden is bedoeld, mag geen \"alt\"-attribuut hebben met daarin placeholdertekst."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["F30","F39"]},"1.2.1":{"techniques":["F30"]}}},"tags":["image","content"],"components":["placeholder"],"options":{"attribute":"alt","selector":"img"}},"imgAltTextNotRedundant":{"type":"custom","testability":1,"title":{"en":"Unless the image files are the same, no image should contain redundant alt text","nl":"Tenzij afbeeldingen hetzelfde zijn, mag geen enkele afbeelding dezelfde alttekst hebben"},"description":{"en":"Every distinct image on a page should have it's own alt text which is different than all the others on the page to avoid redundancy and confusion.","nl":"Elke unieke afbeelding op een pagina moet zijn eigen alttekst hebben die anders is dan die van andere afbeeldingen op de pagina om dubbeling en verwarring te voorkomen."},"guidelines":[],"tags":["image","content"],"callback":"imgAltTextNotRedundant"},"imgGifNoFlicker":{"type":"custom","testability":1,"title":{"en":"Any animated GIF should not flicker","nl":"Geen enkele animated GIF mag knipperen of flitsen"},"description":{"en":"Animated GIF files should not flicker with a frequency over 2 Hz and lower than 55 Hz. You can check the flicker rate of this GIF using an online tool.","nl":"Animated GIF-bestanden mogen niet knipperen of flitsen met een frequentie hoger dan 2 Hz en lager dan 55 Hz. Controleer de frequentie van deze GIF met een online tool."},"guidelines":{"508":["j"],"wcag":{"2.2.2":{"techniques":["G152"]}}},"tags":["image","content"],"callback":"imgGifNoFlicker"},"imgHasAlt":{"type":"selector","testability":1,"title":{"en":"Images must provide alternative text","nl":"Afbeeldingselementen moeten een \"alt\"-attribuut hebben"},"description":{"en":"Alternative text needs to convey the same information as the image. This text will be used when the browser has disabled images, the image was not found on the server, or by non-sighted visitors who use screen readers.","nl":"Alle img-elementen moeten een \"alt\"-attribuut hebben."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["F65","H37"]}}},"tags":["image","content"],"options":{"selector":"img:not(img[alt])"}},"imgHasLongDesc":{"type":"custom","testability":1,"title":{"en":"A \"longdesc\" attribute is required for any image where additional information not in the \"alt\" attribute is required","nl":"Een \"longdesc\"-attribuut is verplicht voor elke afbeelding waar aanvullende informatie niet benodigd is in het \"alt\"-attribuut"},"description":{"en":"Any image that has an \"alt\" attribute that does not fully convey the meaning of the image must have a \"longdesc\" attribute.","nl":"Elke afbeelding die een \"alt\"-attribuut heeft dat de volledige betekenis van de afbeelding bevat, moet een \"longdesc\"-attribuut hebben."},"guidelines":{"wcag":{"2.4.4":{"techniques":["G91"]},"2.4.9":{"techniques":["G91"]}}},"tags":["image","content"],"callback":"imgHasLongDesc"},"imgImportantNoSpacerAlt":{"type":"custom","testability":0.5,"title":{"en":"Images that are important should not have a purely white-space \"alt\" attribute","nl":"Afbeeldingen die belangrijk zijn mogen geen leeg \"alt\"-attribuut hebben"},"description":{"en":"Any image that is not used decorativey or which is purely for layout purposes cannot have an \"alt\" attribute that consists solely of white space (i.e. a space).","nl":"Elke afbeelding die niet ter decoratie is of die alleen voor lay-out doeleinden is bedoeld, mag geen leeg \"alt\"-attribuut hebben (bijvoorbeeld alleen een spatie)."},"guidelines":[],"tags":["image","content"],"callback":"imgImportantNoSpacerAlt"},"imgMapAreasHaveDuplicateLink":{"type":"custom","testability":1,"title":{"en":"All links within a client-side image are duplicated elsewhere in the document","nl":"Alle links met een client-side afbeelding moeten elders in het document terugkeren"},"description":{"en":"Any image that has a \"usemap\" attribute must have links replicated somewhere else in the document.","nl":"Elke afbeelding met een \"usemap\"-attribuut moet een link elders in het document hebben."},"guidelines":{"508":["ef","ef"]},"tags":["image","imagemap"],"callback":"imgMapAreasHaveDuplicateLink"},"imgNonDecorativeHasAlt":{"type":"custom","testability":0.5,"title":{"en":"Any non-decorative images should have a non-empty \"alt\" attribute","nl":"Elke niet-decoratieve afbeelding moet een gevuld \"alt\"-attribuut hebben"},"description":{"en":"Any image that is not used decorativey or which is purely for layout purposes cannot have an empty \"alt\" attribute.","nl":"Elke afbeelding die niet ter decoratie is of voor lay-out doeleinden wordt gebruikt, moet een gevuld \"alt\"-attribuut hebben."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["F38"]}}},"tags":["image","content"],"callback":"imgNonDecorativeHasAlt"},"imgNotReferredToByColorAlone":{"type":"selector","testability":0,"title":{"en":"For any image, the \"alt\" text cannot refer to color alone","nl":"Voor elke afbeelding geldt dat de \"alt\"-tekst niet alleen aan kleur mag refereren"},"description":{"en":"The \"alt\" text or content text for any image should not refer to the image by color alone. This is often fixed by changing the \"alt\" text to the meaning of the image","nl":"De \"alt\"-tekst of content voor elke afbeelding mag niet alleen maar een kleur bevatten. Neem in de \"alt\"-tekst de betekenis van de afbeelding op."},"guidelines":{"508":["c"],"wcag":{"1.1.1":{"techniques":["F13"]},"1.4.1":{"techniques":["F13"]}}},"tags":["image","color","content"],"options":{"selector":"img"}},"imgServerSideMapNotUsed":{"type":"selector","testability":1,"title":{"en":"Server-side image maps should not be used","nl":"Server-side image maps moeten niet worden gebruikt"},"description":{"en":"Server-side image maps should not be used.","nl":"Server-side image maps mogen niet worden gebruikt."},"guidelines":[],"tags":["image","imagemap","content"],"options":{"selector":"img[ismap]"}},"imgShouldNotHaveTitle":{"type":"selector","testability":1,"title":{"en":"Images should not have a \"title\" attribute","nl":"Afbeeldingen moeten geen \"title\"-attribuut hebben"},"description":{"en":"Images should not contain a \"title\" attribute.","nl":"Afbeeldingen zouden geen \"title\"-attribuut moeten bevatten."},"guidelines":[],"tags":["image","content"],"options":{"selector":"img[title]"}},"imgWithEmptyAlt":{"type":"selector","testability":0,"title":{"en":"Use empty alternative text only for decorative images","nl":""},"description":{"en":"Empty alternative text can only be used if the image serves purely decoration purposes. If the image is supposed to convey any information whatsoever, you need to set a proper alternative text for it.","nl":""},"guidelines":[],"tags":["image","content"],"options":{"selector":"img[alt=\"\"]"}},"imgWithMapHasUseMap":{"type":"selector","testability":1,"title":{"en":"Any image with an \"ismap\" attribute have a valid \"usemap\" attribute","nl":"Elke afbeelding met een \"ismap\"-attribuut heeft een geldig \"usemap\"-attribuut"},"description":{"en":"If an image has an \"ismap\" attribute it must have a valid \"usemap\" attribute.","nl":"Als een afbeelding een \"ismap\"-attribuut heeft, moet het ook een geldig \"usemap\"-attribuut hebben"},"guidelines":{"508":["ef","ef"]},"tags":["image","imagemap","content"],"options":{"selector":"img[ismap]:not(img[usemap])"}},"imgWithMathShouldHaveMathEquivalent":{"type":"custom","testability":0,"title":{"en":"Images which contain math equations should provide equivalent MathML","nl":"Afbeeldingen met wiskundige vergelijking moeten een equivalent in MathML bieden"},"description":{"en":"Images which contain math equations should be accompanied or link to a document with the equivalent equation marked up with MathML.","nl":"Afbeeldingen die wiskundige vergelijkingen bevatten moeten vergezeld zijn van of linken naar een document met daarin een equivalent van de vergelijking in MathML."},"guidelines":[],"tags":["image","content"],"callback":"imgWithMathShouldHaveMathEquivalent"},"inputCheckboxHasTabIndex":{"type":"placeholder","testability":1,"title":{"en":"All \"checkbox\" input elements require a valid \"tabindex\" attribute","nl":"Alle \"checkbox\"-invoerelementen moeten een geldig \"tabindex\"-attribuut hebben"},"description":{"en":"All input elements of type \"checkbox\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.","nl":"Alle invoerelementen van het type \"checkbox\" moeten \"tabindex\"-attribuut hebben dat ervoor zorgt dat je door een formulier kunt navigeren met het toetsenbord."},"guidelines":[],"tags":["form","content"],"components":["placeholder"],"options":{"attribute":"tabindex","empty":true,"selector":"input[type=checkbox]"}},"inputCheckboxRequiresFieldset":{"type":"custom","testability":1,"title":{"en":"Logical groups of check boxes should be grouped with a fieldset","nl":"Logische groepen van keuzevakjes moeten gegroepeerd zijn in een fieldset"},"description":{"en":"Related \"checkbox\" input fields should be grouped together using a fieldset.","nl":"Gerelateerde \"keuzevakjes\"-invoervelden moeten bij elkaar staan in een fieldset."},"guidelines":{"wcag":{"3.3.2":{"techniques":["H71"]}}},"tags":["form","content"],"callback":"inputCheckboxRequiresFieldset"},"inputDoesNotUseColorAlone":{"type":"selector","testability":0,"title":{"en":"An \"input\" element should not use color alone","nl":"Een invoerveld mag niet alleen maar kleur gebruiken"},"description":{"en":"All input elements should not refer to content by color alone.","nl":"Elk invoerveld moet naar content verwijzen door middel van meer dan alleen kleur."},"guidelines":{"508":["c"]},"tags":["form","color","content"],"options":{"selector":"input:not(input[type=hidden])"}},"inputElementsDontHaveAlt":{"type":"selector","testability":1,"title":{"en":"Input elements which are not images should not have an \"alt\" attribute","nl":"Invoervelden die geen afbeelding zijn, moeten geen \"alt\"-attribuut hebben"},"description":{"en":"Input elements which are not images should not have an \"alt\" attribute, because of inconsistencies in how user agents use the \"alt\" attribute.","nl":"Invoervelden die geen afbeelding zijn, moeten geen \"alt\"-attribuut hebben, omdat user agents het \"alt\"-attribuut niet consistent gebruiken."},"guidelines":[],"tags":["form","content"],"options":{"selector":"input[type!=image][alt]"}},"inputFileHasTabIndex":{"type":"placeholder","testability":1,"title":{"en":"All \"file\" input elements require a valid \"tabindex\" attribute","nl":"Alle \"document\"-invoerelementen moeten een geldig \"tabindex\"-attribuut hebben"},"description":{"en":"All input elements of type \"file\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.","nl":"Alle invoer-elementen van het type \"file\" moeten een \"tabindex\"-attribuut hebben om navigatie van het formulier met het toetsenbord mogelijk te maken."},"guidelines":[],"tags":["form","tabindex"],"components":["placeholder"],"options":{"attribute":"tabindex","empty":true,"selector":"input[type=file]"}},"inputImageAltIdentifiesPurpose":{"type":"selector","testability":0,"title":{"en":"All \"input\" elements with a type of \"image\" must have an \"alt\" attribute that describes the function of the input","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat de functie van de invoer beschrijft"},"description":{"en":"All input elements with a type of \"image\" should have an \"alt\" attribute.","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H36"]}}},"tags":["form","content"],"options":{"selector":"input[type=image][alt]"}},"inputImageAltIsNotFileName":{"type":"custom","testability":1,"title":{"en":"All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is not the same as the filename","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat anders is dan de bestandsnaam"},"description":{"en":"All input elements with a type of \"image\" should have an \"alt\" attribute which is not the same as the filename.","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat anders is dan de bestandsnaam."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["H36"]}}},"tags":["form","image","content"],"callback":"inputImageAltIsNotFileName"},"inputImageAltIsNotPlaceholder":{"type":"placeholder","testability":1,"title":{"en":"All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is not placeholder text.","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben anders dan alleen placeholdertekst."},"description":{"en":"All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is not placeholder text.","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben anders dan alleen placeholdertekst."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["H36"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"4.1.2":{"techniques":["H91"]}}},"tags":["form","image","content"],"components":["placeholder"],"options":{"attribute":"alt","selector":"input[type=image]"}},"inputImageAltIsShort":{"type":"custom","testability":1,"title":{"en":"All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is as short as possible","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat zo kort mogelijk is"},"description":{"en":"All \"input\" elements with a type of \"image\" must have an \"alt\" attribute which is as short as possible.","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben dat zo kort mogelijk is."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["H36"]}}},"tags":["form","image","content"],"callback":"inputImageAltIsShort"},"inputImageAltNotRedundant":{"type":"custom","testability":1,"title":{"en":"The \"alt\" text for input \"image\" submit buttons must not be filler text","nl":"De \"alt\"-tekst for \"image\"-knoppen moet anders zijn dan alleen placeholdertekst"},"description":{"en":"Every form image button should not simply use filler text like \"button\" or \"submit\" as the \"alt\" text.","nl":"Elke formulierknop die een afbeelding is, moet bruikbare tekst als \"alt\"-tekst hebben, anders dan \"knop\" of \"verstuur\"."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H36"]}}},"tags":["form","image","content"],"strings":["redundant.inputImage"],"callback":"inputImageAltNotRedundant"},"inputImageHasAlt":{"type":"selector","testability":1,"title":{"en":"All \"input\" elements with a type of \"image\" must have an \"alt\" attribute","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben"},"description":{"en":"All input elements with a type of \"image\" should have an \"alt\" attribute.","nl":"Elk \"invoer\"-element met een type \"afbeelding\" moet een \"alt\"-attribuut hebben."},"guidelines":{"508":["a"],"wcag":{"1.1.1":{"techniques":["F65","G94","H36"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"4.1.2":{"techniques":["H91"]}}},"tags":["form","image","content"],"options":{"selector":"input[type=image]:visible","test":":not(input[type=image][alt])"}},"inputImageNotDecorative":{"type":"selector","testability":0,"title":{"en":"The \"alt\" text for input \"image\" buttons must be the same as text inside the image","nl":"De \"alt\"-tekst voor afbeeldingen van invoerknoppen moet hetzelfde zijn als de tekst in de afbeeldingen"},"description":{"en":"Every form image button which has text within the image (say, a picture of the word \"Search\" in a special font) must also have this text in the \"alt\" text.","nl":"Elke formulierknop die een afbeelding is en tekst in de afbeelding heeft (bijvoorbeeld \"Zoek\") moet deze tekst ook in de \"alt\"-tekst hebben."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H36"]}}},"tags":["form","image","content"],"options":{"selector":"input[type=image]"}},"inputPasswordHasTabIndex":{"type":"placeholder","testability":1,"title":{"en":"All \"password\" input elements require a valid \"tabindex\" attribute","nl":"Alle \"paswoord\"-invoervelden moeten een geldig \"tabindex\"-attribuut hebben"},"description":{"en":"All input elements of type \"password\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.","nl":"Alle input-elementen van het type \"paswoord\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken."},"guidelines":[],"tags":["form","content"],"components":["placeholder"],"options":{"attribute":"tabindex","empty":true,"selector":"input[type=password]"}},"inputRadioHasTabIndex":{"type":"placeholder","testability":1,"title":{"en":"All \"radio\" input elements require a valid \"tabindex\" attribute","nl":"Alle invoerelementen van het type \"radio\" moeten een geldig \"tabindex\"-attribuut hebben"},"description":{"en":"All input elements of type \"radio\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.","nl":"Alle input-elementen van het type \"radio\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken."},"guidelines":[],"tags":["form","content"],"components":["placeholder"],"options":{"attribute":"tabindex","empty":true,"selector":"input[type=radio]"}},"inputSubmitHasTabIndex":{"type":"placeholder","testability":1,"title":{"en":"All \"submit\" input elements require a \"tabindex\" attribute","nl":"Alle invoerelementen van het type \"submit\" moeten een geldig \"tabindex\"-attribuut hebben"},"description":{"en":"All input elements of type \"submit\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.","nl":"Alle input-elementen van het type \"submit\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken."},"guidelines":[],"tags":["form","content"],"components":["placeholder"],"options":{"attribute":"tabindex","empty":true,"selector":"input[type=submit]"}},"inputTextHasLabel":{"type":"label","testability":1,"title":{"en":"All \"input\" elements should have a corresponding \"label\"","nl":"Alle invoerelementen moeten een bijbehorend \"label\" hebben"},"description":{"en":"All input elements should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user","nl":"Alle input-elementen moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44","H91"]}}},"tags":["form","content"],"options":{"selector":"input[type=text]"}},"inputTextHasTabIndex":{"type":"placeholder","testability":1,"title":{"en":"All \"text\" input elements require a valid \"tabindex\" attribute","nl":"Alle invoerelementen van het type \"text\" moeten een geldig \"tabindex\"-attribuut hebben"},"description":{"en":"All input elements of type \"text\" should have a \"tabindex\" attribute to help navigate the form with a keyboard alone.","nl":"Alle input-elementen van het type \"text\" moeten een \"tabindex\"-attribuut hebben om navigatie met alleen het toetsenbord mogelijk te maken."},"guidelines":[],"tags":["form","content"],"components":["placeholder"],"options":{"attribute":"tabindex","empty":true,"selector":"input[type=text]"}},"inputTextHasValue":{"type":"placeholder","testability":1,"title":{"en":"All \"input\" elements of type \"text\" must have a default text","nl":"Alle invoerelementen van het type \"text\" moeten een standaardtekst hebben"},"description":{"en":"All input elements of type \"text\" should have a default text.","nl":"Alle invoerelementen van het type \"text\" moeten een standaardtekst hebben."},"guidelines":[],"tags":["form","content"],"components":["placeholder"],"options":{"attribute":"value","empty":true,"selector":"input[type=text]"}},"inputTextValueNotEmpty":{"type":"placeholder","testability":1,"title":{"en":"Text input elements require a non-whitespace default text","nl":"Tekstinvoerelementen mogen geen lege standaardtekst hebben"},"description":{"en":"All input elements with a type of \"text\" should have a default text which is not empty.","nl":"Alle invoerelementen van het type \"text\" moeten een standaardtekst hebben die gevuld is."},"guidelines":[],"tags":["form","content"],"components":["placeholder"],"options":{"attribute":"value","empty":true,"selector":"input[type=text]"}},"inputWithoutLabelHasTitle":{"type":"custom","testability":1,"title":{"en":"Form controls without label should have a title attribute","nl":"Formulierelementen zonder label moeten een titelattribuut hebben"},"description":{"en":"If it is not possible to have a label for a form control, then a title attribute on the element should be provided that describes the purpose of the control.","nl":"Als een formulierelement geen label kan krijgen, dan moet een dat element een titelattribuut krijgen dat het doel van het element beschrijft."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H65"]},"1.3.1":{"techniques":["H65"]},"3.3.2":{"techniques":["H65"]},"4.1.2":{"techniques":["H65"]}}},"tags":["form","content"],"components":["placeholder"],"callback":"inputWithoutLabelHasTitle"},"labelDoesNotContainInput":{"type":"selector","testability":1,"title":{"en":"Label elements should not contain an input element","nl":"Labelelementen moeten geen invoerelementen bevatten"},"description":{"en":"Label elements should not wrap around another input element, as this can cause the label to be read twice by screen readers.","nl":"Labelelementen moeten niet om een ander invoerelement heenstaan, omdat dan het label twee keer kan worden voorgelezen door schermlezers."},"guidelines":[],"tags":["form","content"],"options":{"selector":"label:has(input)"}},"labelMustBeUnique":{"type":"custom","testability":1,"title":{"en":"Every form input must have only one label","nl":"Elk formulierinvoerveld heeft maar een label"},"description":{"en":"Each form input should have only one label element.","nl":"Elk formulierinvoerveld mag maar een label element hebben."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F17"]},"4.1.1":{"techniques":["F17"]}}},"tags":["form","content"],"callback":"labelMustBeUnique"},"labelMustNotBeEmpty":{"type":"placeholder","testability":1,"title":{"en":"Labels must contain text","nl":"Labels moeten tekst bevatten"},"description":{"en":"Labels in forms must contain readable text that describes the target form element.","nl":"Labels in formulieren moeten leesbare tekst bevatten die het formulierelement beschrijven."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44"]}}},"tags":["form","content"],"components":["placeholder"],"options":{"content":true,"empty":true,"selector":"label"}},"labelsAreAssignedToAnInput":{"type":"custom","testability":1,"title":{"en":"All labels should be associated with an input","nl":"Alle labels moeten horen bij een invoerveld"},"description":{"en":"All label elements should be assigned to an input item, and should have a for attribute which equals the id attribute of a form element.","nl":"Alle label-elementen moeten horen bij een invoerveld, en moeten een een for-attribuut hebben dat hetzelfde is als het id-attribuut van een formulierelement."},"guidelines":[],"tags":["form","content"],"callback":"labelsAreAssignedToAnInput"},"languageDirAttributeIsUsed":{"type":"custom","testability":0.5,"title":{"en":"Use the dir attribute when the language direction changes","nl":"Gebruik het dir-attribuut als de richting van de taal verandert"},"description":{"en":"When there are nested directional changes in text, use an inline element with a dir attribute to indicate direction.","nl":"Gebruik een inline element met een dir-attribuut om richting aan te geven wanneer er geneste richtingsveranderingen in de tekst zijn."},"guidelines":{"wcag":{"1.3.2":{"techniques":["H56"]}}},"tags":["language","content"],"callback":"languageDirAttributeIsUsed","components":["language"]},"languageChangesAreIdentified":{"type":"custom","testability":0.5,"title":{"en":"Use language attributes to indicate changes in language","nl":"Gebruik het taal-attribuut om aan te geven dat de taal verandert"},"description":{"en":"When the language of the document changes, make sure to wrap those changes in an element with the lang attribute.","nl":"Als de taal van het document verandert, zet deze veranderingen dan in een element met het lang-attribuut."},"guidelines":{"wcag":{"3.1.2":{"techniques":["H58"]}}},"tags":["language","content"],"callback":"languageChangesAreIdentified","components":["language"]},"languageDirectionPunctuation":{"type":"custom","testability":0.5,"title":{"en":"Place punctuation around language direction changes in the right order","nl":"Zet interpunctie bij richtingsveranderingen in taal in de juiste volgorde"},"description":{"en":"If punctuation is used around a change in language direction, ensure the punctuation appears in the correct place.","nl":"Als er interpunctie staat bij een richtingsverandering in de taal, zorg dat deze dan op de goede plek staat."},"guidelines":{"wcag":{"1.3.2":{"techniques":["G57"]}}},"tags":["language","content"],"callback":"languageDirectionPunctuation","components":["language"]},"languageUnicodeDirection":{"type":"custom","testability":1,"title":{"en":"Use the unicode language direction","nl":"Gebruik de unicode taalrichting"},"description":{"en":"When there are nested directional changes in language, use unicode RTL/LTR characters.","nl":"Gebruik de unicode RTL/LTR afkortingen als er geneste richtingsveranderingen in de taal zijn."},"guidelines":{"wcag":{"1.3.2":{"techniques":["H34"]}}},"tags":["language","content"],"callback":"languageUnicodeDirection","components":["language"]},"legendDescribesListOfChoices":{"type":"selector","testability":0,"title":{"en":"All \"legend\" elements must describe the group of choices","nl":"Alle \"legend\"-elementen moeten een groep keuzes beschrijven"},"description":{"en":"If a legend element is used in a fieldset, the legend content must describe the group of choices.","nl":"Als een legend-element wordt gebruikt in een fieldset, moet de legend content de groep keuzes beschrijven."},"guidelines":{"wcag":{"2.4.6":{"techniques":["G131"]}}},"tags":["form","content"],"options":{"selector":"legend"}},"legendTextNotEmpty":{"type":"selector","testability":1,"title":{"en":"Legend text must not contain just whitespace","nl":"Legend-tekst moet ingevuld zijn"},"description":{"en":"If a legend element is used in a fieldset, the legend should not contain empty text.","nl":"Als een legend-element wordt gebruikt in een fieldset, moet de legend ingevuld zijn."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H71"]},"2.4.6":{"techniques":["G131"]},"3.3.2":{"techniques":["H71"]}}},"tags":["form","content"],"options":{"selector":"legend:empty"}},"legendTextNotPlaceholder":{"type":"placeholder","testability":1,"title":{"en":"\"Legend\" text must not contain placeholder text","nl":"\"Legend\"-tekst moet geen placeholdertekst bevatten"},"description":{"en":"If a legend element is used in a fieldset, the legend should not contain useless placeholder text like \"form\" or \"field\".","nl":"Als een legend-element wordt gebruikt in een fieldset, moet de legend geen placeholdertekst bevatten zoals \"form\" of \"field\"."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H71"]},"2.1.1":{"techniques":["H91"]},"2.4.6":{"techniques":["G131"]},"3.3.2":{"techniques":["H71"]},"4.1.3":{"techniques":["H91"]}}},"tags":["form","content"],"components":["placeholder"],"options":{"content":true,"emtpy":true,"selector":"legend"}},"liDontUseImageForBullet":{"type":"selector","testability":0.5,"guidelines":[],"tags":["list","content"],"options":{"selector":"li:has(img)"}},"linkHasAUniqueContext":{"type":"custom","testability":1,"title":{"en":"Links should have a unique context","nl":"Links moeten een unieke context hebben"},"description":{"en":"","nl":""},"guidelines":[],"tags":["link","content"],"callback":"linkHasAUniqueContext"},"linkUsedForAlternateContent":{"type":"selector","testability":0,"title":{"en":"Use a \"link\" element for alternate content","nl":"Gebruik een \"link\"-element for andersoortige content"},"description":{"en":"Documents which contain things like videos, sound, or other forms of media that are not accessible, should provide a link element with a \"rel\" attribute of \"alternate\" in the document header.","nl":"Documenten die content zoals video's, geluid of andere niet-toegankelijke vormen van media bevatten, moeten een link-element met een \"rel\"-attribuut of \"alternate\" in de documentheaderlink aanbieden."},"guidelines":[],"tags":["document"],"options":{"selector":"html:not(html:has(link[rel=alternate])) body"}},"linkUsedToDescribeNavigation":{"type":"selector","testability":1,"title":{"en":"The document uses link elements to describe navigation if it is within a collection.","nl":"Het document gebruikt link-elementen om navigatie te beschrijven wanneer het binnen een collectie staat."},"description":{"en":"The link element can provide metadata about the position of an HTML page within a set of Web units or can assist in locating content with a set of Web units.","nl":"Het link-element kan metadata bevatten over de positie van een HTML-pagina binnen een swet web units, of kan behulpzaamn zijn bij het lokaliseren van content binnen web units."},"guidelines":[],"tags":["document"],"options":{"selector":"html:not(html:has(link[rel=index]))"}},"listNotUsedForFormatting":{"type":"custom","testability":0,"title":{"en":"Lists should not be used for formatting","nl":"Lijsten worden niet gebruikt voor opmaak"},"description":{"en":"Lists like ul and ol are to provide a structured list, and should not be used to format text. This test views any list with just one item as suspicious, but should be manually reviewed.","nl":"Lijsten zoals ul en ol zijn bedoeld om gestructureerde lijsten te maken. Ze moeten niet gebruikt worden om text op te maken. Controleer of deze lijst echt bedoeld is als lijst of om tekst op te maken."},"guidelines":{"wcag":{"1.3.2":{"techniques":["F1"]}}},"tags":["list","content"],"callback":"listNotUsedForFormatting"},"listOfLinksUseList":{"type":"custom","testability":1,"title":{"en":"A list of links separated by non-readable characters should be in an ul or ol","nl":"Een lijst van links die worden gescheiden door onleesbare tekens moeten in een bulleted of genummerde lijst staan"},"description":{"en":"A list of links without separation between them should be placed in an ol or ul element.","nl":"Een lijst van links die niet duidelijk gescheiden zijn moeten in een bulleted of genummerde lijst staan."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H48"]}}},"tags":["link","content"],"callback":"listOfLinksUseList"},"marqueeIsNotUsed":{"type":"selector","testability":1,"title":{"en":"The \"marquee\" tag should not be used","nl":"De \"marquee\"-tag wordt niet gebruikt"},"description":{"en":"The marquee element is difficult for users to read and is not a standard HTML element. Try to find another way to convey the importance of this text.","nl":"Het marquee-element is moeilijk te lezen voor gebruikers en is geen standaard HTML-element. Gebruik een andere manier om aan te duiden dat het belangrijke content is."},"guidelines":[],"tags":["deprecated","content"],"options":{"selector":"marquee"}},"menuNotUsedToFormatText":{"type":"selector","testability":0,"title":{"en":"Menu elements should not be used for formatting","nl":"Menu-elementen worden niet gebruikt voor opmaak"},"description":{"en":"Menu is a deprecated tag, but is still honored in a transitional DTD. Menu tags are to provide structure for a document and should not be used for formatting. If a menu tag is to be used, it should only contain an ordered or unordered list of links.","nl":"Menu is een afgekeurd tag, maar wordt nog wel gebruikt om structuur aan een document te geven. Het mag niet worden gebruikt voor opmaak. Als een menu-tag wordt gebruikt, mag het alleen bulleted of genummerde lijsten bevatten."},"guidelines":[],"tags":["list","content"],"options":{"selector":"menu:not(menu li:parent(menu))"}},"newWindowIsOpened":{"type":"custom","testability":1,"title":{"en":"A link should not open a new window","nl":"Een link opent geen nieuw scherm"},"description":{"en":"Avoid confusion that may be caused by the appearance of new windows that were not requested by the user.","nl":"Voorkom verwarring die veroorzaakt wordt door het openen van nieuwe schermen die de gebruiker niet verwacht."},"guidelines":{"wcag":{"2.0.0":{"techniques":["H83"]}}},"tags":["javascript","html"],"callback":"newWindowIsOpened"},"noembedHasEquivalentContent":{"type":"selector","testability":0,"title":{"en":"Noembed elements must be the same content as their \"embed\" element","nl":"Noembed-elementen moeten dezelfde content hebben als hun \"embed\"-element"},"description":{"en":"All noembed elements must contain or link to an accessible version of their embed counterparts.","nl":"Alle noembed-elementen moeten een toegankelijke versie van hun embed-tegenhangers bevatten of hier naar linken."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"noembed"}},"noframesSectionMustHaveTextEquivalent":{"type":"selector","testability":0.5,"title":{"en":"All \"noframes\" elements should contain the text content from all frames","nl":"Alle \"noframes\"-elementen moeten de content van alle frames bevatten"},"description":{"en":"The noframes content should either replicate or link to the content visible within the frames.","nl":"The noframes-content moet de zichtbare content binnen de frames repliceren of er naar linken."},"guidelines":[],"tags":["deprecated","frame"],"options":{"selector":"frameset:not(frameset:has(noframes))"}},"objectContentUsableWhenDisabled":{"type":"selector","testability":0,"title":{"en":"When objects are disabled, content should still be available","nl":"Als objecten zijn uitgeschakeld, moet de content nog wel beschikbaar zijn"},"description":{"en":"The content within objects should still be available, even if the object is disabled. To do this, place a link to the direct object source within the object tag.","nl":"Content binnen objecten moet beschikbaar blijven, ook als het object is uitgeschakeld. Plaats hiervoor een link naar de bron van het object binnen de object-tag."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object"}},"objectDoesNotFlicker":{"type":"selector","testability":0,"title":{"en":"Objects do not flicker","nl":"Objecten knipperen of flitsen niet"},"description":{"en":"The content within an object tag must not flicker.","nl":"De content binnen een object-tag knippert of flitst niet."},"guidelines":{"508":["j"],"wcag":{"2.2.2":{"techniques":["F7"]}}},"tags":["objects","content"],"options":{"selector":"object"}},"objectDoesNotUseColorAlone":{"type":"selector","testability":0,"title":{"en":"Objects must not use color to communicate alone","nl":"Objecten gebruiken meer dan alleen kleur om hun boodschap over te brengen"},"description":{"en":"Objects should contain content that makes sense without color and is accessible to users who are color blind.","nl":"Objecten moeten content bevatten die duidelijk is zonder het kleurgebruik en toegankelijk is voor gebruikers met kleurenblindheid."},"guidelines":{"508":["c"]},"tags":["objects","content"],"options":{"selector":"object"}},"objectInterfaceIsAccessible":{"type":"selector","testability":0,"title":{"en":"Interfaces within objects must be accessible","nl":"Interfaces binnen objecten moeten toegankelijk zijn"},"description":{"en":"Object content should be assessed for accessibility.","nl":"Content binnen objecten moeten gecontroleerd worden op toegankelijkheid."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object"}},"objectLinkToMultimediaHasTextTranscript":{"type":"selector","testability":0,"title":{"en":"Objects which reference multimedia files should also provide a link to a transcript","nl":"Objecten die verwijzen naar multimediabestanden moeten ook een link aanbieden naar de transcriptie"},"description":{"en":"If an object contains a video, a link to the transcript should be provided near the object.","nl":"Als een object een video bevat, moet een link naar de transcriptie hiervan worden aangeboden bij het object."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object"}},"objectMustContainText":{"type":"placeholder","testability":1,"title":{"en":"Objects must contain their text equivalents","nl":"Objecten moeten hun tekstuele equivalent bevatten"},"description":{"en":"All object elements should contain a text equivalent if the object cannot be rendered.","nl":"Alle object-elementen moeten een tekstequivalent bevatten in het geval het object niet getoond kan worden."},"guidelines":{"wcag":{"1.1.1":{"techniques":["FLASH1","H27"]}}},"tags":["objects","content"],"components":["placeholder"],"options":{"content":true,"empty":true,"selector":"object"}},"objectMustHaveEmbed":{"type":"selector","testability":1,"title":{"en":"Every object should contain an \"embed\" element","nl":"Elk object moet een \"embed\"-element bevatten"},"description":{"en":"Every object element must also contain an embed element.","nl":"Elk object-element moet ook een \"embed\"-element bevatten."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object:not(object:has(embed))"}},"objectMustHaveTitle":{"type":"selector","testability":1,"title":{"en":"Objects should have a title attribute","nl":"Objecten moeten een titelattribuut hebben"},"description":{"en":"All object elements should contain a \"title\" attribute.","nl":"Alle object-elementen moeten een \"titel\"-attribuut bevatten."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H27"]}}},"tags":["objects","content"],"options":{"selector":"object:not(object[title])"}},"objectMustHaveValidTitle":{"type":"placeholder","testability":1,"title":{"en":"Objects must not have an empty title attribute","nl":"Objecten hebben geen leeg titelattribuut"},"description":{"en":"All object elements should have a \"title\" attribute which is not empty.","nl":"All object-elementen hebben een \"titel\"-attribuut dat gevuld is."},"guidelines":[],"tags":["objects","content"],"components":["placeholder"],"options":{"attribute":"title","empty":true,"selector":"object"}},"objectProvidesMechanismToReturnToParent":{"type":"selector","testability":0,"title":{"en":"All objects should provide a way for keyboard users to escape","nl":"Alle objecten moeten een manier bevatten voor toetsenbordgebruikers een manier om het object te verlaten"},"description":{"en":"Ensure that a user who has only a keyboard as an input device can escape a object element. This requires manual confirmation.","nl":"Zorg ervoor dat een gebruiker die alleen het toetsenbord als bediening gebruikt een object-element. Hiervoor is handmatige bevestiging nodig."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object"}},"objectShouldHaveLongDescription":{"type":"selector","testability":0,"title":{"en":"An object might require a long description","nl":"Een object heeft soms een lange beschrijving nodig"},"description":{"en":"Objects might require a long description, especially if their content is complicated.","nl":"Objecten hebben soms een lange beschrijving nodig, zeker in het geval van ingewikkelde content."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object"}},"objectTextUpdatesWhenObjectChanges":{"type":"selector","testability":0,"title":{"en":"The text equivalents of an object should update if the object changes","nl":"De tekstuele equivalent van een object moet bijgewerkt worden als het object verandert"},"description":{"en":"If an object changes, the text equivalent of that object should also change.","nl":"Als een object verandert, moet zijn tekstuele equivalent ook veranderen."},"guidelines":{"508":["a"]},"tags":["objects","content"],"options":{"selector":"object"}},"objectUIMustBeAccessible":{"type":"selector","testability":0,"title":{"en":"Content within an \"object\" element should be usable with objects disabled","nl":"Content binnen een \"object\"-element moet bruikbaar blijven als het object uitgeschakeld is"},"description":{"en":"Objects who's content changes using java, ActiveX, or other similar technologies, should have their default text change when the object's content changes.","nl":"Van objecten waarvan de content java, ActiveX of vergelijkbare technologie�n gebruiken, moet de standaardtekst veranderen als de content van het object verandert."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object"}},"objectWithClassIDHasNoText":{"type":"selector","testability":1,"title":{"en":"Objects with \"classid\" attributes should change their text if the content of the object changes","nl":"Objecten met \"classid\"-attributen moeten hun tekst veranderen wanneer de content van het object verandert"},"description":{"en":"Objects with \"classid\" attributes, should have their default text change when the object's content changes.","nl":"Van objecten met \"classid\"-attributen moet de standaardtekst veranderen als de content van het object verandert."},"guidelines":[],"tags":["objects","content"],"options":{"selector":"object[classid]:not(object[classid]:empty)"}},"pNotUsedAsHeader":{"type":"custom","testability":0.5,"title":{"en":"Paragraphs must not be used for headers","nl":"Alinea's worden niet gebruikt als header"},"description":{"en":"Headers are extremely useful for non-sighted users to navigate the structure of the page. Formatting a paragraph to just be big or bold, while it might visually look like a header, does not make it a header.","nl":"Headers van h1 - h6 zijn handig voor blinde en slechtziende gebruikers om door een pagina te navigeren. Maak alinea's daarom niet op zodat deze lijkt op een header. Dit werkt verwarrend."},"guidelines":{"wcag":{"1.3.1":{"techniques":["G141","H42"]},"2.4.10":{"techniques":["G141"]}}},"tags":["header","content"],"callback":"pNotUsedAsHeader"},"paragraphIsWrittenClearly":{"type":"custom","testability":0.5,"guidelines":{"wcag":{"3.1.5":{"techniques":["G86"]}}},"tags":["language","content"],"components":["textStatistics"],"callback":"paragraphIsWrittenClearly"},"passwordHasLabel":{"type":"label","testability":1,"title":{"en":"All password input elements should have a corresponding label","nl":"Alle paswoordinvoerelementen hebben een bijbehorend label"},"description":{"en":"All input elements with a type of \"password\"should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user","nl":"Alle input-elementen van het type \"paswoord\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen."},"guidelines":{"508":["n"],"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44","H91"]}}},"tags":["form","content"],"components":["label"],"options":{"selector":"input[type=password]"}},"passwordLabelIsNearby":{"type":"labelProximity","testability":0.5,"title":{"en":"All \"password\" input elements have a label that is close","nl":"Alle paswoordinvoerelementen hebben een label dat dicht bij het element staat"},"description":{"en":"All input elements of type \"password\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.","nl":"Alle inputelementen van het type \"paswoord\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat."},"guidelines":[],"tags":["form","content"],"components":["labelProximity"],"options":{"selector":"input[type=password]"}},"preShouldNotBeUsedForTabularLayout":{"type":"custom","testability":0,"title":{"en":"Pre elements should not be used for tabular data","nl":"Pre-elementen worden niet gebruikt om data als tabel te rangschikken"},"description":{"en":"If a pre element is used for tabular data, change the data to use a well-formed table.","nl":"Als een pre-element wordt gebruikt om data als tabel te rangschikken, verander de data dan zodat je een echte tabel kunt maken."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F33","F34","F48"]},"1.3.2":{"techniques":["F33","F34"]}}},"tags":["table","content"],"callback":"preShouldNotBeUsedForTabularLayout"},"radioHasLabel":{"type":"label","testability":1,"title":{"en":"All \"radio\" input elements have a corresponding label","nl":"Alle invoerelementen van het type \"radio\" hebben een bijbehorend label"},"description":{"en":"All input elements of type \"radio\" should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user","nl":"Alle input-elementen van het \"radio\" moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen."},"guidelines":{"508":["n"],"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44","H91"]}}},"tags":["form","content"],"components":["label"],"options":{"selector":"input[type=radio]"}},"radioLabelIsNearby":{"type":"labelProximity","testability":0.5,"title":{"en":"All \"radio\" input elements have a label that is close","nl":"Alle invoerelementen van het type \"radio\" hebben een label dat dicht bij het element staat"},"description":{"en":"All input elements of type \"radio\" must have a corresponding label that is close to the form element. Users of screen magnification or with reduced spatial skills are hampered in using a form element if the label for that element is located far away.","nl":"Alle inputelementen van het type \"radio\" moeten een bijbehorend label hebben dat dicht bij het formulierelement staat. Gebruikers die het scherm vergroten of met beperkte ruimtelijke vaardigheden kunnen een formulier niet gebruiken als het label van een veld te ver weg staat."},"guidelines":[],"tags":["form","content"],"components":["labelProximity"],"options":{"selector":"input[type=radio]"}},"radioMarkedWithFieldgroupAndLegend":{"type":"selector","testability":1,"title":{"en":"All radio button groups are marked using fieldset and legend elements","nl":"Alle groepjes van radio buttons zijn gemarkeerd met fieldset- en legend-elementen"},"description":{"en":"Form element content must contain both fieldset and legend elements if there are related radio buttons.","nl":"Content van formulierelementen moeten zowel fieldset- als legend-elementen bevatten als er gerelateerde radio buttons instaan."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H71"]},"3.3.2":{"techniques":["H71"]}}},"tags":["form","content"],"options":{"selector":"input[type=radio]:not(fieldset input[type=radio])"}},"scriptContentAccessibleWithScriptsTurnedOff":{"type":"selector","testability":0,"title":{"en":"Content on the page should still be available if scripts are disabled","nl":"Content op de pagina moet beschikbaar blijven als scripts zijn uitgeschakeld"},"description":{"en":"All scripts should be assessed to see if, when the user is browsing with scrips turned off, the page content is still available.","nl":"Alle scripts moeten gecontroleerd worden of, wanneer een gebruiker scripts heeft uitgezet, de content van de pagina nog steeds beschikbaar is."},"guidelines":[],"tags":["javascript"],"options":{"selector":"script"}},"scriptInBodyMustHaveNoscript":{"type":"selector","testability":0.5,"title":{"en":"Scripts should have a corresponding \"noscript\" element","nl":"Scripts moeten een bijbehorend \"noscript\"-element hebben"},"description":{"en":"Scripts should be followed by a noscripts element to guide the user to content in an alternative way.","nl":"Scripts moeten worden gevolgd door een noscripts-element om de gebruiker de weg te wijzen naar de content op een andere manier."},"guidelines":{"508":["l"]},"tags":["javascript"],"options":{"selector":"html:not(html:has(noscript)):has(script) body"}},"scriptOnclickRequiresOnKeypress":{"type":"event","testability":1,"title":{"en":"If an element has an \"onclick\" attribute it should also have an \"onkeypress\" attribute","nl":"Als een element een \"onclick\"-attribuut heeft, moet het ook een \"onkeypress\"-attribuut hebben"},"description":{"en":"If an element has an \"onclick\" attribute it should also have an \"onkeypress\" attribute","nl":"Als een element een \"onclick\"-attribuut heeft, moet het ook een \"onkeypress\"-attribuut hebben"},"guidelines":{"508":["l"],"wcag":{"2.1.1":{"techniques":["G90","SCR2","SCR20"]},"2.1.3":{"techniques":["G90","SCR20"]}}},"tags":["javascript"],"components":["event","hasEventListener"],"options":{"correspondingEvent":"onkeypress","searchEvent":"onclick"}},"scriptOndblclickRequiresOnKeypress":{"type":"event","testability":1,"title":{"en":"Any element with an \"ondblclick\" attribute should have a keyboard-related action as well","nl":"Elk element met een \"ondblclick\"-attribuut moet een vergelijkbare actie hebben die kan worden uitgevoerd met een toetsenbord"},"description":{"en":"If an element has an \"ondblclick\" attribute, it should also have a keyboard-related action.","nl":"Als een element een \"ondblclick\"-attribuut heeft, moet het ook een actie bevatten die kan worden uitgevoerd met een toetsenbord."},"guidelines":{"508":["l"],"wcag":{"2.1.1":{"techniques":["G90","SCR2","SCR20"]},"2.1.3":{"techniques":["G90","SCR20"]}}},"tags":["javascript"],"components":["event","hasEventListener"],"options":{"correspondingEvent":"onkeypress","searchEvent":"ondblclick"}},"scriptOnmousedownRequiresOnKeypress":{"type":"event","testability":1,"title":{"en":"If an element has a \"mousedown\" attribute it should also have an \"onkeydown\" attribute","nl":"Als een element een \"mousedown\"-attribuut heeft moet het ook een \"onkeydown\"-attribuut hebben"},"description":{"en":"If an element has a \"mousedown\" attribute it should also have an \"onkeydown\" attribute.","nl":"Als een element een \"mousedown\"-attribuut heeft moet het ook een \"onkeydown\"-attribuut hebben."},"guidelines":{"508":["l"],"wcag":{"2.1.1":{"techniques":["G90","SCR2","SCR20"]},"2.1.3":{"techniques":["G90","SCR20"]}}},"tags":["javascript"],"components":["event","hasEventListener"],"options":{"correspondingEvent":"onkeydown","searchEvent":"onmousedown"}},"scriptOnmousemove":{"type":"event","testability":1,"title":{"en":"Any element with an \"onmousemove\" attribute should have a keyboard-related action as well","nl":"Elk element met een \"onmousemove\"-attribuut moet een vergelijkbare actie hebben die kan worden uitgevoerd met een toetsenbord"},"description":{"en":"If an element has an \"onmousemove\" attribute it should have a keyboard-related action as well.","nl":"Als een element een \"onmousemove\"-attribuut heeft, moet het een vergelijkbare actie hebben die kan worden uitgevoerd met een toetsenbord."},"guidelines":{"508":["l"],"wcag":{"2.1.1":{"techniques":["G90","SCR2","SCR20"]},"2.1.3":{"techniques":["G90","SCR20"]}}},"tags":["javascript"],"components":["event","hasEventListener"],"options":{"correspondingEvent":"onkeypress","searchEvent":"onmousemove"}},"scriptOnmouseoutHasOnmouseblur":{"type":"event","testability":1,"title":{"en":"If an element has a \"onmouseout\" attribute it should also have an \"onblur\" attribute","nl":"Als een element een \"onmouseout\"-attribuut heeft, moet het ook een \"onblur\" attribuut hebben"},"description":{"en":"If an element has a \"onmouseout\" attribute it should also have an \"onblur\" attribute.","nl":"Als een element een \"onmouseout\"-attribuut heeft, moet het ook een \"onblur\"-attribuut hebben."},"guidelines":{"508":["l"],"wcag":{"2.1.1":{"techniques":["G90","SCR2","SCR20"]},"2.1.3":{"techniques":["G90","SCR20"]}}},"tags":["javascript"],"components":["event","hasEventListener"],"options":{"correspondingEvent":"onblur","searchEvent":"onmouseout"}},"scriptOnmouseoverHasOnfocus":{"type":"event","testability":1,"title":{"en":"If an element has a \"onmouseover\" attribute it should also have an \"onfocus\" attribute","nl":"Als een element een \"onmouseover\"-attribuut heeft, moet het ook een \"onfocus\"-attribuut hebben"},"description":{"en":"If an element has a \"onmouseover\" attribute it should also have an \"onfocus\" attribute.","nl":"Als een element een \"onmouseover\"-attribuut heeft, moet het ook een \"onfocus\"-attribuut hebben."},"guidelines":{"508":["l"],"wcag":{"2.1.1":{"techniques":["G90","SCR2","SCR20"]},"2.1.3":{"techniques":["G90","SCR20"]}}},"tags":["javascript"],"components":["event","hasEventListener"],"options":{"correspondingEvent":"onfocus","searchEvent":"onmouseover"}},"scriptOnmouseupHasOnkeyup":{"type":"event","testability":1,"title":{"en":"If an element has a \"onmouseup\" attribute it should also have an \"onkeyup\" attribute","nl":"Als een element een \"onmouseup\"-attribuut heeft, moet het ook een \"onkeyup\"-attribuut hebben"},"description":{"en":"If an element has a \"onmouseup\" attribute it should also have an \"onkeyup\" attribute.","nl":"Als een element een \"onmouseup\"-attribuut heeft, moet het ook een \"onkeyup\"-attribuut hebben."},"guidelines":{"508":["l"],"wcag":{"2.1.1":{"techniques":["G90","SCR2","SCR20"]},"2.1.3":{"techniques":["G90","SCR20"]}}},"tags":["javascript"],"components":["event","hasEventListener"],"options":{"correspondingEvent":"onkeyup","searchEvent":"onmouseup"}},"scriptUIMustBeAccessible":{"type":"selector","testability":0,"title":{"en":"The user interface for scripts should be accessible","nl":"De user interface voor scripts moet toegankelijk zijn"},"description":{"en":"All scripts should be assessed to see if their interface is accessible.","nl":"Alle scripts moeten gecontroleerd worden op toegankelijkheid van hun interface."},"guidelines":{"508":["l"]},"tags":["javascript"],"options":{"selector":"script"}},"scriptsDoNotFlicker":{"type":"selector","testability":0,"title":{"en":"Scripts should not cause the screen to flicker","nl":"Scripts mogen het scherm niet laten knipperen of flitsen"},"description":{"en":"All scripts should be assessed to see if their interface does not flicker.","nl":"Alle scripts moeten gecontroleerd worden om te zien of zij de interface niet laten knipperen of flitsen."},"guidelines":{"508":["j"],"wcag":{"2.2.2":{"techniques":["F7"]}}},"tags":["javascript"],"options":{"selector":"script"}},"scriptsDoNotUseColorAlone":{"type":"selector","testability":0,"title":{"en":"The interface in scripts should not use color alone","nl":"De interface in scripts gebruikt niet alleen maar kleur"},"description":{"en":"All scripts should be assessed to see if their interface does not have an interface which requires distinguishing between colors alone.","nl":"Alle scripts moeten gecontroleerd worden om te zien of hun interface geen interface heeft die alleen op kleur kan worden onderscheiden."},"guidelines":{"508":["c"]},"tags":["javascript","color"],"options":{"selector":"script"}},"selectDoesNotChangeContext":{"type":"event","testability":1,"title":{"en":"\"Select\" elements must not contain an \"onchange\" attribute","nl":"\"Select\" elements bevatten geen \"onchange\" attribute"},"description":{"en":"Actions like \"onchange\" can take control away from users who are trying to navigate the page. Using an \"onchange\" attribute for things like select-list menus should instead be replaced with a drop-down and a button which initiates the action.","nl":"Acties als \"onchange\" kunnen de controle ontnemen van gebruikers die op een pagina proberen te navigeren. Het gebruik van een \"onchange\"-attribuut voor zaken zoals select-list menu's moet vervangen worden door een drop-down en een knop waarmee je de actie start."},"guidelines":[],"tags":["form","content"],"components":["event","hasEventListener"],"options":{"searchEvent":"onchange","selector":"select"}},"selectHasAssociatedLabel":{"type":"label","testability":1,"title":{"en":"All select elements have an explicitly associated label","nl":"Alle select-elementen hebben een expliciet bijbehorend label"},"description":{"en":"All select elements should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user","nl":"Alle select-elementen moeten een bijbehorend label-element hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44","H91"]}}},"tags":["form","content"],"components":["label"],"options":{"selector":"select"}},"selectJumpMenu":{"type":"custom","testability":0.5,"title":{"en":"Select jump menus should jump on button press, not on state change","nl":"Select jump menu's moeten springen wanneer de knop wordt gebruikt, niet bij statusverandering"},"description":{"en":"If you wish to use a 'Jump' menu with a select item that then redirects users to another page, the jump should occur on the user pressing a button, rather than on the change event of that select element.","nl":"Als je een 'Jump'-menu wilt gebruiken met een select item dat gebruikers naar een andere pagina verwijst, moet de verwijzing plaatsvinden als de gebruiker een knop gebruikt en niet op het moment dat het select element verandert."},"guidelines":{"wcag":{"3.2.2":{"techniques":["F37"]},"3.2.5":{"techniques":["F9"]}}},"tags":["form","content"],"components":["hasEventListener"],"callback":"selectJumpMenu"},"selectWithOptionsHasOptgroup":{"type":"selector","testability":0.5,"title":{"en":"Form select elements should use optgroups for long selections","nl":"Formulier select-elementen moeten optgroups gebruiken voor lange selecties"},"description":{"en":"If a select element has many options, use optgroups to help ease navigation.","nl":"Als een select-element veel opties heeft, gebruik dan optgroups om navigatie makkelijker te maken."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H85"]}}},"tags":["form","content"],"options":{"selector":"select:not(select:has(optgroup)) option:nth-child(5)"}},"siteMap":{"type":"custom","testability":0,"title":{"en":"Websites must have a site map","nl":"Websites moeten een sitemap hebben"},"description":{"en":"Every web site should have a page which provides a site map or another method to navigate most of the site from a single page to save time for users of assistive devices.","nl":"Elke website moet een pagina hebben waarop een sitemap staat of een andere methode om op de site te navigeren vanaf een pagina. Dit spaart gebruikers die hulpmiddelen gebruiken tijd."},"guidelines":{"wcag":{"2.4.5":{"techniques":["G63"]},"2.4.8":{"techniques":["G63"]}}},"tags":["document"],"strings":["siteMap"],"callback":"siteMap"},"skipToContentLinkProvided":{"type":"custom","testability":0.5,"title":{"en":"A \"skip to content\" link should exist as one of the first links on the page","nl":"Er moet een \"skip to content\"-link zijn als een van de eerste links op de pagina"},"description":{"en":"A link reading \"skip to content\" should be the first link on a page.","nl":"Er moet een link zijn om naar de content te navigeren als een van de eerste links op de pagina."},"guidelines":{"508":["o"],"wcag":{"2.4.1":{"techniques":["G1"]}}},"tags":["document"],"strings":["skipContent"],"callback":"skipToContentLinkProvided"},"svgContainsTitle":{"type":"selector","testability":1,"title":{"en":"Inline SVG should use Title elements","nl":"Inline SVG moet titelelementen gebruiken"},"description":{"en":"Any inline SVG image should have an embedded title element.","nl":"Elke inline SVG-afbeelding moet een ingebed title-element hebben."},"guidelines":{"wcag":{"1.1.1":{"techniques":["F65"]}}},"tags":["image","svg","content"],"options":{"selector":"svg:not(svg:has(title))"}},"tableAxisHasCorrespondingId":{"type":"custom","testability":1,"title":{"en":"Axis attribute should have corresponding IDs","nl":"Axis-attributen moeten bijbehorende IDs hebben"},"description":{"en":"When using the axis attribute to group cells together, ensure they have a target element with the same ID.","nl":"Wanneer er axis-attributen gebruikt worden om cellen te groeperen, zorg er dan voor dat hun doelelement hetzelfde ID heeft."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F17"]},"4.1.1":{"techniques":["F17"]}}},"callback":"tableAxisHasCorrespondingId"},"tabIndexFollowsLogicalOrder":{"type":"custom","testability":0.5,"title":{"en":"The tab order of a document is logical","nl":"De tabvolgorde van een document is logisch"},"description":{"en":"Check that the tab order of a page is logical.","nl":"Controleer of de tabvolgorde van een pagina logisch is."},"guidelines":{"wcag":{"2.4.3":{"techniques":["H4"]}}},"tags":["document"],"callback":"tabIndexFollowsLogicalOrder"},"tableCaptionIdentifiesTable":{"type":"selector","testability":0,"title":{"en":"Captions should identify their table","nl":"Beschrijvingen moeten hun tabellen identificeren"},"description":{"en":"Check to make sure that a table's caption identifies the table with a name, figure number, etc.","nl":"Controleer of de beschrijving van een tabel de tabel identificeert met een naam, nummer en dergelijke."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H39"]}}},"tags":["table","content"],"options":{"selector":"caption"}},"tableComplexHasSummary":{"type":"selector","testability":0.5,"title":{"en":"Complex tables should have a summary","nl":"Complexe tabellen moeten een samenvatting hebben"},"description":{"en":"If a table is complex (for example, has some cells with \"colspan\" attributes, the table should have a summary.","nl":"Als een tabel complex is (bijvoorbeeld, als er cellen zijn met \"colspan\"-attributen, moet de tabel een samenvatting hebben."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H39"]}}},"tags":["table","content"],"options":{"selector":"table:not(table[summary], table:has(caption))"}},"tableDataShouldHaveTh":{"type":"selector","testability":1,"title":{"en":"Data tables should contain a header","nl":"Datatabellen moeten \"th\"-elementen bevatten"},"description":{"en":"Tables which contain data (as opposed to layout tables) should contain proper table header elements to mark them for screen readers and enhance the structure of the document.","nl":"Tabellen die data bevatten (in tegenstelling tot lay-out tabellen) moeten th-elementen bevatten om koppen te markeren voor schermlezers en om de structuur van het document te verbeteren."},"guidelines":{"508":["g"],"wcag":{"1.3.1":{"techniques":["F91"]}}},"tags":["table","content"],"options":{"selector":"table:not(table:has(th))"}},"tableHeaderLabelMustBeTerse":{"type":"custom","testability":0.5,"title":{"en":"Table header lables must be terse","nl":"Tabelkoppen moeten bondig zijn"},"description":{"en":"The \"abbr\" attribute for table headers must be terse (less than 20 characters long).","nl":"Het \"abbr\"-attribuut voor tabelkoppen moet bondig zijn (minder dan 20 tekens lang)."},"guidelines":[],"tags":["table","content"],"callback":"tableHeaderLabelMustBeTerse"},"tableIsGrouped":{"type":"selector","testability":0.5,"title":{"en":"Mark up the areas of tables using thead and tbody","nl":"Gebruik thead en tbody voor tabellen"},"description":{"en":"Mark up the areas of tables using thead and tbody.","nl":"Gebruik thead en tbody voor tabellen."},"guidelines":[],"tags":["table","content"],"options":{"selector":"table:not(table:has(thead), table:has(tfoot))"}},"tableLayoutDataShouldNotHaveTh":{"type":"custom","testability":0,"title":{"en":"Layout tables should not contain \"th\" elements","nl":"Lay-out tabellen bevatten geen \"th\"-elementen"},"description":{"en":"Tables which are used purely for layout (as opposed to data tables), should not contain th elements, which would make the table appear to be a data table.","nl":"Tabellen die alleen voor lay-out worden gebruikt (in tegenstelling tot datatabellen), moeten geen th-elementen bevatten, omdat deze de indruk wekken dat het een datatabel betreft."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F46"]}}},"tags":["table","layout","content"],"callback":"tableLayoutDataShouldNotHaveTh"},"tableLayoutHasNoCaption":{"type":"custom","testability":1,"title":{"en":"All tables used for layout have no \"caption\" element","nl":"Alle tabellen die alleen voor lay-out worden gebruikt hebben geen \"caption\"-element"},"description":{"en":"If a table contains no data, and is used simply for layout, then it should not contain a caption element.","nl":"Als een tabel geen data bevat en alle voor lay-out wordt gebruikt, moet hij geen caption-element krijgen."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F46"]}}},"tags":["table","layout","content"],"callback":"tableLayoutHasNoCaption"},"tableLayoutHasNoSummary":{"type":"custom","testability":0.5,"title":{"en":"All tables used for layout have no summary or an empty summary","nl":"Alle tabellen die alleen voor lay-out worden gebruikt hebben geen samenvatting"},"description":{"en":"If a table contains no data, and is used simply for layout, then it should not have a \"summary\" attribute.","nl":"Als een tabel geen data bevat en alleen voor lay-out wordt gebruikt, moet hij geen \"summary\"-attribuut krijgen."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F46"]}}},"tags":["table","layout","content"],"callback":"tableLayoutHasNoSummary"},"tableLayoutMakesSenseLinearized":{"type":"custom","testability":0,"title":{"en":"All tables used for layout should make sense when removed","nl":"Als tabellen voor lay-out worden gebruikt moet de pagina nog duidelijk blijven als de tabel wordt verwijderd"},"description":{"en":"If a table element is used for layout purposes only, then the content of the table should make sense if the table is linearized.","nl":"Als een table-element alleen voor lay-out-doeleinden wordt gebruikt, moet de inhoud van de tabel nog steeds duidelijk zijn als de tabel wordt verwijderd."},"guidelines":{"wcag":{"1.3.2":{"techniques":["G57"]},"4.1.1":{"techniques":["F49"]}}},"tags":["table","layout","content"],"callback":"tableLayoutMakesSenseLinearized"},"tableNotUsedForLayout":{"type":"custom","testability":0.5,"title":{"en":"Tables should not be used for layout","nl":"Tabellen moet niet worden gebruikt voor lay-out"},"description":{"en":"Tables are for data, not for creating a page layout. Consider using standard HTML and CSS techniques instead.","nl":"Tabellen zijn voor data, niet om een pagina op te maken. Gebruik hiervoor HTML en CSS."},"guidelines":{"wcag":{"1.3.2":{"techniques":["F49"]}}},"tags":["table","layout","content"],"callback":"tableNotUsedForLayout"},"tableShouldUseHeaderIDs":{"type":"custom","testability":0.5,"title":{"en":"Table cells use IDs to identify headers","nl":"Tabelcellen gebruiken IDs om koppen te identificeren"},"description":{"en":"If a table is not being used for layout, it should use IDs and header attributes to identify table headers.","nl":"Een tabel moet IDs en header-attributen gebruiken om tabelkoppen te identificeren."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H43"]}}},"tags":["table","content"],"callback":"tableShouldUseHeaderIDs"},"tableSummaryDescribesTable":{"type":"selector","testability":0,"title":{"en":"Table summaries should describe the navigation and structure of the table","nl":"Tabelsamenvattingen moeten de navigatie en structuur van de tabel beschrijven"},"description":{"en":"Table summary elements should describe the navigation tools and structure of the table, as well as provide an overview of what the table describes.","nl":"Tabel \"summary\"-elementen moeten de navigatie en structuur van de tabel beschrijven, en een overzicht bieden van wat er in de tabel staat."},"guidelines":[],"tags":["table","content"],"options":{"selector":"table[summary]"}},"tableSummaryDoesNotDuplicateCaption":{"type":"custom","testability":1,"title":{"en":"Table \"summary\" elements should not duplicate the \"caption\" element","nl":"Tabel \"summary\"-elementen mogen niet hetzelfde zijn als het \"caption\"-element"},"description":{"en":"The summary and the caption must be different, as both provide different information. A caption. /code element identifies the table, while the \"summary\" attribute describes the table contents.","nl":"De samenvatting en beschrijving van een tabel moeten verschillen, want ze bieden verschillende informatie. Een caption-element identificeert welke tabel het betreft en het \"summary\"-attribuut beschrijft de inhoud van de tabel."},"guidelines":[],"tags":["table","content"],"callback":"tableSummaryDoesNotDuplicateCaption"},"tableSummaryIsEmpty":{"type":"placeholder","testability":0.5,"title":{"en":"All data tables should have a summary","nl":"Alle datatabellen moeten een samenvatting hebben"},"description":{"en":"If a table contains data, it should have a \"summary\" attribute.","nl":"Als een tabel data bevat, moet hij een \"summary\"-attribuut hebben."},"guidelines":[],"tags":["table","content"],"components":["placeholder"],"options":{"attribute":"summary","empty":true,"selector":"table[summary]"}},"tableSummaryIsNotTooLong":{"type":"custom","testability":0,"guidelines":[],"tags":["table","content"],"callback":"tableSummaryIsNotTooLong"},"tableSummaryIsSufficient":{"type":"selector","testability":0,"title":{"en":"All data tables should have an adequate summary","nl":"Alle datatabellen moeten een toepasselijke samenvatting hebben"},"description":{"en":"If a table contains data, it should have a \"summary\" attribute that completely communicates the function and use of the table.","nl":"Als een tabel data bevat, moet er een \"summary\"-attribuut zijn dat de functie en het doel van de tabel duidelijk maakt."},"guidelines":[],"tags":["table","content"],"options":{"selector":"table[summary]"}},"tableUseColGroup":{"type":"custom","testability":0,"title":{"en":"Group columns using \"colgroup\" or \"col\" elements","nl":"Groepeer kolommen met \"colgroup\"- of \"col\"-elementen"},"description":{"en":"To help complex table headers make sense, use colgroup or col to group them together.","nl":"Maak complexe tabelkoppen duidelijker door \"colgroup\"- of \"col\"-elementen te gebruiken om ze te groeperen."},"guidelines":[],"tags":["table","content"],"callback":"tableUseColGroup"},"tableUsesAbbreviationForHeader":{"type":"custom","testability":0,"title":{"en":"Table headers over 20 characters should provide an \"abbr\" attribute","nl":"Tabelkoppen met meer dan 20 tekens moeten een \"abbr\"-attribuut hebben"},"description":{"en":"For long table headers, use an \"abbr\" attribute that is less than short (less than 20 characters long).","nl":"Gebruik een \"abbr\"-attribuut korter dan 20 tekens voor lange tabelkoppen."},"guidelines":[],"tags":["table","content"],"callback":"tableUsesAbbreviationForHeader"},"tableUsesCaption":{"type":"selector","testability":1,"title":{"en":"Data tables should contain a \"caption\" element if not described elsewhere","nl":"Datatabellen moeten een \"caption\"-element hebben als ze nergens anders beschreven worden"},"description":{"en":"Unless otherwise described in the document, tables should contain caption elements to describe the purpose of the table.","nl":"Tenzij elders in het document beschreven, moeten tabellen een \"caption\"-element hebben om het doel van de tabel te beschrijven."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H39"]}}},"tags":["table","content"],"options":{"selector":"table:not(table:has(caption))"}},"tableUsesScopeForRow":{"type":"custom","testability":0.5,"title":{"en":"Data tables should use scoped headers for rows with headers","nl":"Datatabellen moeten het \"scope\"-attribuut gebruiken voor rijen met koppen"},"description":{"en":"Where there are table headers for both rows and columns, use the \"scope\" attribute to help relate those headers with their appropriate cells. This test looks for the first and last cells in each row and sees if they differ in layout or font weight.","nl":"Als er tabelkoppen zijn voor zowel rijen als kolommen, gebruik dan het \"scope\"-attribuut om het juiste verband te leggen tussen de koppen en bijbehorende cellen."},"guidelines":{"wcag":{"1.3.1":{"techniques":["H63"]}}},"tags":["table","content"],"callback":"tableUsesScopeForRow"},"tableWithBothHeadersUseScope":{"type":"selector","testability":0.5,"title":{"en":"Data tables with multiple headers should use the \"scope\" attribute","nl":"Datatabellen met meerdere headers moeten het \"scope\"-attribuut gebruiken"},"description":{"en":"Where there are table headers for both rows and columns, use the \"scope\" attribute to help relate those headers with their appropriate cells.","nl":"Als er tabelkoppen zijn voor zowel rijen als kolommen, gebruik dan het \"scope\"-attribuut om het juiste verband te leggen tussen de koppen en bijbehorende cellen."},"guidelines":{"508":["h"],"wcag":{"1.3.1":{"techniques":["F91"]}}},"tags":["table","content"],"options":{"selector":"table:has(tr:not(table tr:first) th:not(th[scope]))"}},"tableWithMoreHeadersUseID":{"type":"custom","testability":0.5,"title":{"en":"Complex data tables should provide \"id\" attributes to headers","nl":"Complexe datatabellen moeten \"id\"-attributen aan headers koppelen"},"description":{"en":"Complex data tables should provide \"id\" attributes to headers and \"headers\" attributes for data cells.","nl":"Complexe datatabellen moeten \"id\"-attributen aan headers koppelen en \"headers\"-attributen aan datacellen."},"guidelines":[],"tags":["table","content"],"callback":"tableWithMoreHeadersUseID"},"tagsAreNestedCorrectly":{"type":"custom","testability":1,"title":{"en":"All tags should be nested correctly","nl":"Alle tags moeten juist genest worden"},"description":{"en":"","nl":""},"guidelines":{"wcag":{"4.1.1":{"techniques":["H74"]}}},"tags":["html"],"components":["htmlSource"],"callback":"tagsAreNestedCorrectly"},"tabularDataIsInTable":{"type":"custom","testability":0.5,"title":{"en":"All tabular information should use a table","nl":"Alle tabelinformatie moet ook daadwerkelijk in een tabel staan"},"description":{"en":"Tables should be used when displaying tabular information.","nl":"Gebruik een echte tabel wanneer je tabelinformatie wilt tonen."},"guidelines":{"wcag":{"1.3.1":{"techniques":["F33","F34","F48"]},"1.3.2":{"techniques":["F33","F34"]}}},"tags":["table","content"],"callback":"tabularDataIsInTable"},"textIsNotSmall":{"type":"custom","testability":0.5,"title":{"en":"The text size is not less than 9 pixels high","nl":"De grootte van de tekst is meer dan 8 pixels hoog"},"description":{"en":"To help users with difficulty reading small text, ensure text size is no less than 9 pixels high.","nl":"Help gebruikers die moeite hebben met het lezen van kleine letters, door ervoor te zorgen dat tekst groter is dan 8 pixels hoog."},"guidelines":[],"tags":["textsize","content"],"components":["convertToPx"],"callback":"textIsNotSmall"},"textareaHasAssociatedLabel":{"type":"label","testability":1,"title":{"en":"All textareas should have a corresponding label","nl":"Alle \"textarea\"-elementen moeten een bijbehorend label hebben"},"description":{"en":"All textarea elements should have a corresponding label element. Screen readers often enter a \"form mode\" where only label text is read aloud to the user","nl":"Alle \"textarea\"-elementen moeten een bijbehorend label hebben. Schermlezers maken vaak gebruik van een \"formuliereninstelling\" waarbij alleen de tekst van de labels hardop aan de gebruiker wordt voorgelezen."},"guidelines":{"wcag":{"1.1.1":{"techniques":["H44"]},"1.3.1":{"techniques":["H44","F68"]},"2.1.1":{"techniques":["H91"]},"2.1.3":{"techniques":["H91"]},"3.3.2":{"techniques":["H44"]},"4.1.2":{"techniques":["H44","H91"]}}},"tags":["form","content"],"components":["label"],"options":{"selector":"textarea"}},"textareaLabelPositionedClose":{"type":"labelProximity","testability":0.5,"title":{"en":"All textareas should have a label that is close to it","nl":"Alle \"textarea\"-elementen moeten een label hebben dat dicht bij het element staat"},"description":{"en":"All textarea elements should have a corresponding label element that is close in proximity.","nl":"Alle \"textarea\"-elementen moeten een bijbehorend label hebben dat dicht bij het element staat."},"guidelines":[],"tags":["form","content"],"components":["labelProximity"],"options":{"selector":"textarea"}},"videoProvidesCaptions":{"type":"selector","testability":0.5,"title":{"en":"All video tags must provide captions","nl":"Alle video tags moeten bijschriften bieden"},"description":{"en":"All HTML5 video tags must provide captions.","nl":"Alle HTML5 video tags moeten bijschriften bieden."},"guidelines":{"508":["b","b"],"wcag":{"1.2.2":{"techniques":["G87"]},"1.2.4":{"techniques":["G87"]}}},"tags":["media","content"],"options":{"selector":"video"}},"videosEmbeddedOrLinkedNeedCaptions":{"type":"custom","testability":1,"title":{"en":"All linked or embedded videos need captions","nl":"Alle gekoppelde of ingebedde video's moeten bijschriften hebben"},"description":{"en":"Any video hosted or otherwise which is linked or embedded must have a caption.","nl":"Elke video die is gekoppeld of ingebed in content moet een bijschrift hebben."},"guidelines":{"wcag":{"1.2.2":{"techniques":["G87"]},"1.2.4":{"techniques":["G87"]}}},"tags":["media","content"],"components":["video","language"],"callback":"videosEmbeddedOrLinkedNeedCaptions"},"whiteSpaceInWord":{"type":"custom","testability":0.5,"title":{"en":"Whitespace should not be used between characters in a word","nl":"Zet geen witruimte tussen letters in een woord"},"description":{"en":"Using extra whitespace between letters in a word causes screen readers to not interpret the word correctly, use the letter-spacing CSS property instead.","nl":"Het gebruik van witruimte tussen de letters van een woord, zorgen dat schermlezers het woord niet volledig kunnen lezen. Gebruik in plaats hiervan css om de ruimte tussen letters te bepalen."},"guidelines":{"wcag":{"1.3.2":{"techniques":["F32","C8"]}}},"tags":["content"],"callback":"whiteSpaceInWord"},"whiteSpaceNotUsedForFormatting":{"type":"custom","testability":0.5,"title":{"en":"Whitespace should not be used for conveying information","nl":"Gebruik geen witruimte om informatie over te brengen"},"description":{"en":"Spaces or tabs are not read by assistive technology and should not be used to convey meaning.","nl":"Spaties of tabs worden niet voorgelezen door hulpprogramma's en moeten niet worden gebruikt om betekenis over te dragen."},"guidelines":{"wcag":{"1.3.2":{"techniques":["G57"]}}},"tags":["content"],"callback":"whiteSpaceNotUsedForFormatting"},"doNotUseGraphicalSymbolToConveyInformation":{"type":"custom","testability":1,"title":{"en":"Using a graphical symbol alone to convey information","nl":"Gebruik van alleen een grafisch symbool om informatie over te brengen"},"description":{"en":"The objective of this technique is to show how using a graphical symbol to convey information can make content difficult to comprehend. A graphical symbol may be an image, an image of text or a pictorial or decorative character symbol (glyph) which imparts information nonverbally. Examples of graphical symbols include an image of a red circle with a line through it, a smiley face, or a glyph which represents a check mark, arrow, or other symbol but is not the character with that meaning. Assistive technology users may have difficulty determining the meaning of the graphical symbol. If a graphical symbol is used to convey information, provide an alternative using features of the technology or use a different mechanism that can be marked with an alternative to represent the graphical symbol. For example, an image with a text alternative can be used instead of the glyph.","nl":"Het doel van deze techniek is te laten zien dat content moeilijker begrepen kan worden wanneer een grafisch symbool wordt gebruikt om informatie over te brengen. Een grafisch symbool kan een afbeelding, een afbeelding van tekst of een pictogram of decoratief teken zijn (glyph) dat non-verbaal informatie overbrengt. Voorbeelden hiervan zijn een rode cirkel met een lijn erdoorheen, een smiley of een vinkje, pijl of ander symbool dat niet noodzakelijkerwijs een duidelijke betekenis heeft. Gebruikers van hulpprogramma's hebben vaak moeite om de bedoeling van een grafisch symbool te duiden. Als een grafisch symbool gebruikt wordt om informatie over te brengen, biedt dan ook een (tekstueel) alternatief."},"guidelines":{"wcag":{"1.3.3":{"techniques":["F26"]}}},"tags":["link","content"],"callback":"doNotUseGraphicalSymbolToConveyInformation"},"linkDoesNotChangeContextOnFocus":{"type":"event","testability":1,"title":{"en":"Link elements must not contain an \"onfocus\" attribute","nl":"Link-elementen bevatten geen \"onfocus\"-attribuut"},"description":{"en":"Actions like \"onfocus\" can take control away from users who are trying to navigate the page. Using an \"onfocus\" attribute for things like links should be replaced with css.","nl":"Acties zoals \"onfocus\" kunnen de controle ontnemen van gebruikers die op een pagina proberen te navigeren. Het gebruik van een \"onfocus\"-attribuut voor zaken als links moet worden vervangen door middel van css."},"guidelines":[],"tags":["form","content"],"components":["event","hasEventListener"],"options":{"searchEvent":"onfocus","selector":"a"}},"buttonDoesNotChangeContextOnFocus":{"type":"event","testability":1,"title":{"en":"Buttons must not contain an \"onfocus\" attribute","nl":"Knoppen bevatten geen \"onfocus\"-attribuut"},"description":{"en":"Actions like \"onfocus\" can take control away from users who are trying to navigate the page. Using an \"onfocus\" attribute for things like buttons should be replaced with css.","nl":"Acties zoals \"onfocus\" kunnen de controle ontnemen van gebruikers die op een pagina proberen te navigeren. Het gebruik van een \"onfocus\"-attribuut voor zaken als knoppen moet worden vervangen door middel van css."},"guidelines":[],"tags":["form","content"],"components":["event","hasEventListener"],"options":{"searchEvent":"onfocus","selector":"input[type=checkbox],input[type=text],button"}},"KINGStrongList":{"type":"custom","testability":1,"title":{"en":"Use strong in lists only"},"description":{"en":"STRONG only allowed when parent element is LI."},"guidelines":[],"tags":["KING"],"callback":"KINGStrongList"},"KINGUseLongDateFormat":{"type":"custom","testability":1,"title":{"en":"Use a long date format"},"description":{"en":"Short date formats might confuse users, Always use the month name: 20 May 2014."},"guidelines":[],"tags":["KING"],"callback":"KINGUseLongDateFormat"},"KINGUsePercentageWithSymbol":{"type":"custom","testability":1,"title":{"en":"Use a symbol within a percentage"},"description":{"en":""},"guidelines":[],"tags":["KING"],"callback":"KINGUsePercentageWithSymbol"},"KINGUseCurrencyAsSymbol":{"type":"custom","testability":1,"title":{"en":"Use a symbol for a currency"},"description":{"en":"Only use symbol and currency name instead of common name such as € or EUR."},"guidelines":[],"tags":["KING"],"callback":"KINGUseCurrencyAsSymbol"},"videoMayBePresent":{"type":"custom","testability":1,"title":{"en":"Video or object uses a link that points to a file with a video extension","nl":"Video of object met een link naar een bestand met een video extensie"},"description":{"en":"","nl":""},"guidelines":[],"tags":["link","video"],"callback":"videoMayBePresent"},"audioMayBePresent":{"type":"custom","testability":1,"title":{"en":"Audio or object uses a link that points to a file with a video extension","nl":"Audio of object met een link naar een bestand met een video extensie"},"description":{"en":"","nl":""},"guidelines":[],"tags":["link","audio"],"callback":"audioMayBePresent"},"animatedGifMayBePresent":{"type":"custom","testability":1,"title":{"en":"Test if a .gif is used on the page. Test if the .gif contains more then one frame","nl":"Test of een .gif afbeelding gebruikt is op de pagina. Test of het .gif bestand uit meer dan één frame bestaat"},"description":{"en":"","nl":""},"guidelines":[],"tags":["link","gif"],"callback":"animatedGifMayBePresent"},"userInputMayBeRequired":{"type":"custom","testability":1,"title":{"en":"Test if user input is required","nl":"Test of er invoervelden zijn"},"description":{"en":"","nl":""},"guidelines":[],"tags":["form","input"],"callback":"userInputMayBeRequired"}} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/libs/quail/wcag2.json b/plugins/ckeditor/a11ychecker/libs/quail/wcag2.json new file mode 100644 index 0000000..dfd6182 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/libs/quail/wcag2.json @@ -0,0 +1,615 @@ +{ + "1.1.1": { + "id": "wcag20:text-equiv-all", + "level": "wcag20:level_a", + "default": "cantTell", + "testAggregators": [ + { + "passed": "cantTell", + "failed": "failed", + "tests": [ + "areaHasAltValue" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "tests": [ + "imgAltNotEmptyInAnchor" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "tests": [ + "imgHasAlt" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "stacking", + "tests": [ + "inputImageAltNotRedundant", + "inputImageAltIsNotPlaceholder", + "inputImageAltIsNotFileName", + "inputImageHasAlt" + ] + } + ] + }, + "1.2.1": { + "id": "wcag20:media-equiv-av-only-alt", + "level": "wcag20:level_a", + "preconditions": [ + "videoMayBePresent", + "audioMayBePresent" + ] + }, + "1.2.2": { + "id": "wcag20:media-equiv-captions", + "level": "wcag20:level_a", + "testAggregators": [ + { + "passed": "cantTell", + "failed": "failed", + "tests": [ + "videosEmbeddedOrLinkedNeedCaptions" + ] + } + ] + }, + "1.2.3": { + "id": "wcag20:media-equiv-audio-desc", + "level": "wcag20:level_a", + "preconditions": [ + "videoMayBePresent" + ] + }, + "1.2.4": { + "id": "wcag20:media-equiv-real-time-captions", + "level": "wcag20:level_aa", + "preconditions": [ + "videoMayBePresent" + ] + }, + "1.2.5": { + "id": "wcag20:media-equiv-audio-desc-only", + "level": "wcag20:level_aa", + "preconditions": [ + "videoMayBePresent" + ] + }, + "1.2.6": { + "id": "wcag20:media-equiv-sign", + "level": "wcag20:level_aaa" + }, + "1.2.7": { + "id": "wcag20:media-equiv-extended-ad", + "level": "wcag20:level_aaa" + }, + "1.2.8": { + "id": "wcag20:media-equiv-text-doc", + "level": "wcag20:level_aaa" + }, + "1.2.9": { + "id": "wcag20:media-equiv-live-audio-only", + "level": "wcag20:level_aaa" + }, + "1.3.1": { + "id": "wcag20:content-structure-separation-programmatic", + "level": "wcag20:level_a", + "default": "cantTell", + "testAggregators": [ + { + "passed": "passed", + "failed": "failed", + "type": "combined", + "tests": [ + "fileHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "combined", + "tests": [ + "inputTextHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "combined", + "tests": [ + "passwordHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "combined", + "tests": [ + "checkboxHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "combined", + "tests": [ + "radioHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "combined", + "tests": [ + "selectHasAssociatedLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "combined", + "tests": [ + "textareaHasAssociatedLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "cantTell", + "tests": [ + "pNotUsedAsHeader" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "tests": [ + "tableLayoutDataShouldNotHaveTh" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "tests": [ + "tableLayoutHasNoSummary" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "tests": [ + "tableLayoutHasNoCaption" + ] + } + ] + }, + "1.3.2": { + "id": "wcag20:content-structure-separation-sequence", + "level": "wcag20:level_a" + }, + "1.3.3": { + "id": "wcag20:content-structure-separation-understanding", + "level": "wcag20:level_a" + }, + "1.4.1": { + "id": "wcag20:visual-audio-contrast-without-color", + "level": "wcag20:level_a", + "default": "cantTell", + "testAggregators": [ + { + "passed": "passed", + "failed": "failed", + "tests": [ + "aInPHasADistinctStyle" + ] + } + ] + }, + "1.4.2": { + "id": "wcag20:visual-audio-contrast-dis-audio", + "level": "wcag20:level_a", + "preconditions": [ + "videoMayBePresent", + "audioMayBePresent" + ] + }, + "1.4.3": { + "id": "wcag20:visual-audio-contrast-contrast", + "level": "wcag20:level_aa", + "testAggregators": [ + { + "passed": "passed", + "failed": "failed", + "tests": [ + "colorFontContrast", + "colorElementBehindContrast", + "colorBackgroundImageContrast", + "colorElementBehindBackgroundImageContrast", + "colorBackgroundGradientContrast", + "colorElementBehindBackgroundGradientContrast" + ] + } + ] + }, + "1.4.4": { + "id": "wcag20:visual-audio-contrast-scale", + "level": "wcag20:level_aa" + }, + "1.4.5": { + "id": "wcag20:visual-audio-contrast-text-presentation", + "level": "wcag20:level_aa" + }, + "1.4.6": { + "id": "wcag20:visual-audio-contrast7", + "level": "wcag20:level_aaa" + }, + "1.4.7": { + "id": "wcag20:visual-audio-contrast-noaudio", + "level": "wcag20:level_aaa" + }, + "1.4.8": { + "id": "wcag20:visual-audio-contrast-visual-presentation", + "level": "wcag20:level_aaa" + }, + "1.4.9": { + "id": "wcag20:visual-audio-contrast-text-images", + "level": "wcag20:level_aaa" + }, + "2.1.1": { + "id": "wcag20:keyboard-operation-keyboard-operable", + "level": "wcag20:level_a" + }, + "2.1.2": { + "id": "wcag20:keyboard-operation-trapping", + "level": "wcag20:level_a" + }, + "2.1.3": { + "id": "wcag20:keyboard-operation-all-funcs", + "level": "wcag20:level_aaa" + }, + "2.2.1": { + "id": "wcag20:time-limits-required-behaviors", + "level": "wcag20:level_a" + }, + "2.2.2": { + "id": "wcag20:time-limits-pause", + "level": "wcag20:level_a" + }, + "2.2.3": { + "id": "wcag20:time-limits-no-exceptions", + "level": "wcag20:level_aaa" + }, + "2.2.4": { + "id": "wcag20:time-limits-postponed", + "level": "wcag20:level_aaa" + }, + "2.2.5": { + "id": "wcag20:time-limits-server-timeout", + "level": "wcag20:level_aaa" + }, + "2.3.1": { + "id": "wcag20:seizure-does-not-violate", + "level": "wcag20:level_a" + }, + "2.3.2": { + "id": "wcag20:seizure-three-times", + "level": "wcag20:level_aaa" + }, + "2.4.1": { + "id": "wcag20:navigation-mechanisms-skip", + "level": "wcag20:level_a" + }, + "2.4.2": { + "id": "wcag20:navigation-mechanisms-title", + "level": "wcag20:level_a" + }, + "2.4.3": { + "id": "wcag20:navigation-mechanisms-focus-order", + "level": "wcag20:level_a" + }, + "2.4.4": { + "id": "wcag20:navigation-mechanisms-refs", + "level": "wcag20:level_a", + "default": "cantTell", + "testAggregators": [ + { + "passed": "passed", + "failed": "failed", + "tests": [ + "linkHasAUniqueContext" + ] + } + ] + }, + "2.4.5": { + "id": "wcag20:navigation-mechanisms-mult-loc", + "level": "wcag20:level_aa" + }, + "2.4.6": { + "id": "wcag20:navigation-mechanisms-descriptive", + "level": "wcag20:level_aa" + }, + "2.4.7": { + "id": "wcag20:navigation-mechanisms-focus-visible", + "level": "wcag20:level_aa" + }, + "2.4.8": { + "id": "wcag20:navigation-mechanisms-location", + "level": "wcag20:level_aaa" + }, + "2.4.9": { + "id": "wcag20:navigation-mechanisms-link", + "level": "wcag20:level_aaa" + }, + "2.4.10": { + "id": "wcag20:navigation-mechanisms-headings", + "level": "wcag20:level_aaa" + }, + "3.1.1": { + "id": "wcag20:meaning-doc-lang-id", + "level": "wcag20:level_a", + "testAggregators": [ + { + "passed": "cantTell", + "failed": "failed", + "type": "stacking", + "tests": [ + "documentLangIsISO639Standard", + "documentLangNotIdentified" + ] + } + ] + }, + "3.1.2": { + "id": "wcag20:meaning-other-lang-id", + "level": "wcag20:level_aa" + }, + "3.1.3": { + "id": "wcag20:meaning-idioms", + "level": "wcag20:level_aaa" + }, + "3.1.4": { + "id": "wcag20:meaning-located", + "level": "wcag20:level_aaa" + }, + "3.1.5": { + "id": "wcag20:meaning-supplements", + "level": "wcag20:level_aaa" + }, + "3.1.6": { + "id": "wcag20:meaning-pronunciation", + "level": "wcag20:level_aaa" + }, + "3.2.1": { + "id": "wcag20:consistent-behavior-receive-focus", + "level": "wcag20:level_a" + }, + "3.2.2": { + "id": "wcag20:consistent-behavior-unpredictable-change", + "level": "wcag20:level_a" + }, + "3.2.3": { + "id": "wcag20:consistent-behavior-consistent-locations", + "level": "wcag20:level_aa" + }, + "3.2.4": { + "id": "wcag20:consistent-behavior-consistent-functionality", + "level": "wcag20:level_aa" + }, + "3.2.5": { + "id": "wcag20:consistent-behavior-no-extreme-changes-context", + "level": "wcag20:level_aaa" + }, + "3.3.1": { + "id": "wcag20:minimize-error-identified", + "level": "wcag20:level_a", + "preconditions": [ + "userInputMayBeRequired" + ] + }, + "3.3.2": { + "id": "wcag20:minimize-error-cues", + "level": "wcag20:level_a" + }, + "3.3.3": { + "id": "wcag20:minimize-error-suggestions", + "level": "wcag20:level_aa", + "preconditions": [ + "userInputMayBeRequired" + ] + }, + "3.3.4": { + "id": "wcag20:minimize-error-reversible", + "level": "wcag20:level_aa", + "preconditions": [ + "userInputMayBeRequired" + ] + }, + "3.3.5": { + "id": "wcag20:minimize-error-context-help", + "level": "wcag20:level_aaa" + }, + "3.3.6": { + "id": "wcag20:minimize-error-reversible-all", + "level": "wcag20:level_aaa" + }, + "4.1.1": { + "id": "wcag20:ensure-compat-parses", + "level": "wcag20:level_a", + "default": "cantTell", + "testAggregators": [ + { + "passed": "passed", + "failed": "failed", + "tests": [ + "documentIDsMustBeUnique" + ] + }, + { + "passed": "passed", + "failed": "failed", + "tests": [ + "elementsDoNotHaveDuplicateAttributes" + ] + }, + { + "passed": "passed", + "failed": "failed", + "tests": [ + "idRefHasCorrespondingId" + ] + }, + { + "passed": "passed", + "failed": "failed", + "tests": [ + "idrefsHasCorrespondingId" + ] + }, + { + "passed": "passed", + "failed": "failed", + "tests": [ + "tagsAreNestedCorrectly" + ] + } + ] + }, + "4.1.2": { + "id": "wcag20:ensure-compat-rsv", + "level": "wcag20:level_a", + "default": "cantTell", + "testAggregators": [ + { + "passed": "passed", + "failed": "failed", + "tests": [ + "areaHasAltValue" + ] + }, + { + "passed": "passed", + "failed": "failed", + "tests": [ + "formButtonsHaveValue" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "type": "combined", + "tests": [ + "fileHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "stacking", + "tests": [ + "frameTitlesNotPlaceholder", + "frameTitlesNotEmpty", + "framesHaveATitle" + ] + }, + { + "passed": "passed", + "failed": "cantTell", + "tests": [ + "imgAltNotEmptyInAnchor" + ] + }, + { + "passed": "passed", + "failed": "failed", + "type": "stacking", + "tests": [ + "inputImageAltNotRedundant", + "inputImageAltIsNotFileName", + "inputImageHasAlt", + "inputImageAltIsNotPlaceholder" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "type": "combined", + "tests": [ + "inputTextHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "type": "combined", + "tests": [ + "passwordHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "type": "combined", + "tests": [ + "checkboxHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "type": "combined", + "tests": [ + "radioHasLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "type": "combined", + "tests": [ + "selectHasAssociatedLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "cantTell", + "failed": "failed", + "type": "combined", + "tests": [ + "textareaHasAssociatedLabel", + "inputWithoutLabelHasTitle" + ] + }, + { + "passed": "passed", + "failed": "failed", + "tests": [ + "aMustContainText" + ] + }, + { + "passed": "passed", + "failed": "failed", + "tests": [ + "buttonHasName" + ] + } + ] + } +} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/plugin.js b/plugins/ckeditor/a11ychecker/plugin.js new file mode 100644 index 0000000..13e645d --- /dev/null +++ b/plugins/ckeditor/a11ychecker/plugin.js @@ -0,0 +1,178 @@ +/** +* @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. +* For licensing, see LICENSE.md or https://ckeditor.com/license +*/ + +!function(){var t,e,n,i,s,a,r,o,c,u,l,d,h,p,f,g,m,v,b,y,x,w,C,T,k,I,E;C=function(){"use strict";function t(){this.list=[]}function e(t,e){var n=0;return n=t.element.getPosition(e.element)&CKEDITOR.POSITION_FOLLOWING?1:-1}return t.prototype={list:[],currentIndex:-1},t.prototype.constructor=t,t.prototype.each=function(t){var e=this.list;if(e.map)e.map(t,this);else for(var n=0,i=e.length;i>n;n++)t.call(this,e[n])},t.prototype.count=function(t){if(t){var e=0,n=0;for(n=0;nthis.count()-1?0!==this.currentIndex&&this.moveTo(0):this.moveTo(this.currentIndex+1),this.getFocused()):null},t.prototype.prev=function(){if(!this.count())return null;var t=this.count()-1;return 0===this.currentIndex||-1==this.currentIndex?this.currentIndex!=t&&this.moveTo(t):this.moveTo(this.currentIndex-1),this.getFocused()},t.prototype.getIssueByElement=function(t){var e=null;return this.each(function(n){n.element.equals(t)&&(e=n)}),e},t.prototype.getIssuesByElement=function(t,e){for(var n,i,s=[],a=this.list,r=0,o=a.length;o>r;r++)n=a[r],i=!e||!n.isIgnored(),n.element.equals(t)&&i&&s.push(n);return s},t.prototype.indexOf=function(t){return CKEDITOR.tools.indexOf(this.list,t)},t.prototype.getIssueByIndex=function(t){var e=this.getItem(t);return e.element; +},t.prototype.sort=function(){this.list.sort(e)},t.prototype.filter=function(t){return this.list=this.list.filter(t),this.list},CKEDITOR.event.implementOn(t.prototype),t}(),T=function(t){"use strict";function e(){}return e.prototype={fixesMapping:{},config:{}},CKEDITOR.event.implementOn(e.prototype),e.prototype.constructor=e,e.fixes={},e.prototype.process=function(e,n,i){var s=new t;return this.fire("process",{issues:s,contentElement:n})===!1?!1:(i&&i(s),!0)},e.prototype.filterIssues=function(t,e){if(this._filterIssue){var n=this,i=function(t){return n._filterIssue.call(n,t,e)};t.filter(i)}},e.prototype.getIssueDetails=function(t,e){},e.getFixType=function(t,n){e.fixes[t]?n&&n(e.fixes[t]):!function(i){e.fixes[t]=i,n&&n(i)}()},e.prototype.getFixes=function(t,e,n){var i=this.fixesMapping[t.id];if(i&&i.length){var s,a=[],r=function(t){a.push(t),a.length===i.length&&e(a)};for(s=0;ss;s++)a[s](i);delete n[t]},t.prototype.setLoadedTypes=function(t){e=t},t.prototype.getLoadedTypes=function(){return e},t.prototype.getWaitingCallbacks=function(){return n},CKEDITOR.event.implementOn(t.prototype),t}(),e=function(t){"use strict";function e(e){t.call(this,e); +}e.prototype=new t,e.prototype.constructor=e,e.prototype._langDictionary={};var n={},i=[];return e.prototype.get=function(e){return e.langCode=e.langCode||"en",this.deferGetCall(e.langCode,arguments)?void 0:(CKEDITOR.plugins.a11ychecker.dev||(e.name=e.langCode+"/"+e.name),t.prototype.get.call(this,e))},e.prototype.getInstance=function(t){t=t||{};var e=t.name,n=t.langCode||"en",i=this;this.get({name:e,callback:function(s){var a=new s(t.issue);CKEDITOR.plugins.a11ychecker.dev&&(a.lang=i._langDictionary[n][e]),t.callback(a)},langCode:n})},e.prototype.deferGetCall=function(t,e){var n=CKEDITOR.tools.indexOf;return!CKEDITOR.plugins.a11ychecker.dev||this._langDictionary[t]?!1:(this._addDeferredGet(t,e),-1===n(i,t)&&(i.push(t),CKEDITOR.scriptLoader.load(this.basePath+"lang/"+t+".js")),!0)},e.prototype.add=function(e,n){return t.prototype.add.call(this,e,n)},e.prototype.lang=function(t,e){this._langDictionary[t]=e;var i=n[t];if(i)for(var s=i.length-1;s>=0;s--)this.get.apply(this,i[s])},e.prototype._addDeferredGet=function(t,e){ +n[t]?n[t].push(e):n[t]=[e]},e.prototype._getDeferredGetCount=function(t){return n[t]?n[t].length:0},e.prototype._clearDeferredGetQueue=function(){n={}},e}(t),n=function(){"use strict";function t(t){this.controller=t,this._storedSel=null}return t.prototype={},t.prototype.constructor=t,t.prototype.init=function(){var t=this.controller,e=t.editor;e&&e.fire("lockSnapshot",{dontUpdate:!0}),t.issues&&t.editableDecorator.markIssues(t.issues),CKEDITOR.env.chrome&&t.editor&&(this._storedSel=t.editor.getSelection().createBookmarks())},t.prototype.close=function(){var t=this.controller;t.editableDecorator.removeMarkup(),t.viewerController&&t.viewerController.viewer.panel.hide(),t.issues&&t.issues.resetFocus(),this._storedSel&&this.controller.editor.getSelection().selectBookmarks(this._storedSel),this.controller.editor.fire("unlockSnapshot")},t.prototype.unsetStoredSelection=function(){var t=this._storedSel;t&&(this.removeBookmark(t),this._storedSel=null)},t.prototype.removeBookmark=function(t){for(var e=0;en;n++)e=t.getItem(n),this.markIssueElement(e,t)},t.prototype.addListeners=function(){var e=this.editor,n=e.editable(),i=this,s=CKEDITOR.tools.bind(i.clickListener,i);if(!n)throw new Error("Editable not available");n.attachListener(n,"click",s),e.on("contentDom",function(){var t=e.editable();t.attachListener(t,"click",s)}),e.dataProcessor.htmlFilter.addRules({elements:{$:function(n){return e._.a11ychecker.disableFilterStrip||delete n.attributes[t.ID_ATTRIBUTE_NAME_FULL], +e.config.a11ychecker_noIgnoreData&&delete n.attributes["data-a11y-ignore"],n}}})},t.prototype.applyMarkup=function(){var i=this.editable(),s=!!this.editor.plugins.fakeobjects,a=t.INITIAL_ID_VALUE;i.forEach(function(i){return i.data(t.ID_ATTRIBUTE_NAME,a),s&&e(i)&&n(i,t.ID_ATTRIBUTE_NAME_FULL,a),a+=1,!0},CKEDITOR.NODE_ELEMENT,!1)},t.prototype.decorateScratchpad=function(e){e.data(t.ID_ATTRIBUTE_NAME,t.INITIAL_ID_VALUE)},t.prototype.removeMarkup=function(){var n=this.editable(),s=!!this.editor.plugins.fakeobjects,a=this.unmarkIssueElement;n.forEach(function(n){n.removeAttribute&&n.removeAttribute(t.ID_ATTRIBUTE_NAME_FULL),s&&e(n)&&i(n,t.ID_ATTRIBUTE_NAME_FULL),n.hasClass("cke_a11yc_issue")&&a(n)},CKEDITOR.NODE_ELEMENT,!1)},t.prototype.clickListener=function(t){var e=t.data.getTarget(),n=this.editor._.a11ychecker;if(!e.hasClass("cke_a11yc_issue")){var i,s=e.getParents(!0);for(e=null,i=0;is;s++)n=e.getItem(s),i=n.originalElement.data(t.ID_ATTRIBUTE_NAME),i===String(t.INITIAL_ID_VALUE)?n.element=r:n.element=r.findOne("*["+t.ID_ATTRIBUTE_NAME_FULL+'="'+i+'"]')},t.prototype.markIgnoredIssue=function(t){t.element.addClass("cke_a11yc_ignored")},t.prototype.markIssueElement=function(t,e){var n=t.element,i=t.testability,s=t.isIgnored()&&!e.getIssuesByElement(n,!0).length;void 0===i&&(i=1),n.addClass("cke_a11yc_issue"),s?this.markIgnoredIssue(t):(n.addClass(this.testabilityClasses[i]),n.removeClass("cke_a11yc_ignored"))},t.prototype.unmarkIssueElement=function(t,e){var n=t.removeClass?t:t.element;e||n.removeClass("cke_a11yc_issue"),n.removeClass("cke_a11yc_error").removeClass("cke_a11yc_warning").removeClass("cke_a11yc_notice").removeClass("cke_a11yc_ignored").removeClass("cke_a11yc_focused"); +},t}(),r=function(){"use strict";function t(){}return t.prototype={preferredIssue:null},t.prototype.constructor=t,t.prototype.set=function(t){this.preferredIssue=t},t.prototype.unset=function(t){this.set(null)},t.prototype.getFromList=function(e){var n=null,i=this.preferredIssue&&this.preferredIssue.element,s=!1;return 0===e.count()?n:i?(i&&t._nodeIsRemoved(i)&&(i=t._retreiveElementFromSelection(i.getDocument())),e.each(function(t){s||(t.element.equals(i)?(s=!0,n=t):!n&&t.element.getPosition(i)&CKEDITOR.POSITION_FOLLOWING&&(n=t))}),n||e.getItem(0)):e.getItem(0)},t._retreiveElementFromSelection=function(t){var e=t.getSelection();return e?e.getCommonAncestor():null},t.prototype.getFromListIndex=function(t){var e=this.getFromList(t);return e?t.indexOf(e):null},t._nodeIsRemoved=function(t){var e=t.getParents();return!(e[0]&&"html"==e[0].getName())},t}(),o=function(){"use strict";function t(t){this.controller=t}return t.prototype={show:function(){this.getEditorCommand().setState(CKEDITOR.TRISTATE_ON); +},hide:function(){this._selectIssue(),this.getEditorCommand().setState(CKEDITOR.TRISTATE_OFF)},update:function(){this.controller.issues.on("focusChanged",this.focusChanged,this)},focusChanged:function(t){var e=t.data;e.previous&&this.unmarkFocus(e.previous.element),e.current&&this.markFocus(e.current.element)},unmarkFocus:function(t){t.removeClass("cke_a11yc_focused")},markFocus:function(t){t.addClass("cke_a11yc_focused")},getEditorCommand:function(){return this.controller.editor.getCommand("a11ychecker")}},t.prototype.constructor=t,t.prototype._selectIssue=function(){var t=this.controller,e=t.issues.getFocused();e&&t._withUndoManager(function(){var n=t.editor,i=t.mode;t.editableDecorator.removeMarkup(),n.getSelection().selectElement(e.element),i.unsetStoredSelection&&i.unsetStoredSelection(),n.fire("updateSnapshot"),i.unsetStoredSelection&&(i._storedSel=n.getSelection().createBookmarks())})},t}(),c=function(){"use strict";function t(t){this.viewer=t,this.templates={};for(var e in this.templateDefinitions)this.templates[e]=new CKEDITOR.template(this.templateDefinitions[e]); +this.parts={},this.lang=t.editor.lang.a11ychecker,this.build()}return t.prototype={templateDefinitions:{wrapper:'
',title:'',info:'

'},setTitle:function(t){this.parts.title.setHtml(t)},setInfo:function(t){this.parts.info.setHtml(t)},build:function(){this.parts={wrapper:CKEDITOR.dom.element.createFromHtml(this.templates.wrapper.output()),title:CKEDITOR.dom.element.createFromHtml(this.templates.title.output()),info:CKEDITOR.dom.element.createFromHtml(this.templates.info.output())},this.parts.title.appendTo(this.parts.wrapper),this.parts.info.appendTo(this.parts.wrapper)}},CKEDITOR.event.implementOn(t.prototype),t}(),u=function(t){"use strict";function e(t,e){this.viewer=t,this.lang=e,this.templates={};for(var n in this.templateDefinitions)this.templates[n]=new CKEDITOR.template(this.templateDefinitions[n]);this.templates.counterText=new CKEDITOR.template(this.lang.navigationCounter), +this.parts={},this.build()}function n(t,e){return function(n){var i=n.data.getKeystroke();i==t&&(e.call(this),n.data.preventDefault())}}var i={};return i[t.testability.ERROR]="error",i[t.testability.WARNING]="warning",i[t.testability.NOTICE]="notice",e.prototype={templateDefinitions:{wrapper:'
',counter:'
',buttonWrapper:'
',button:'{text}'},update:function(t,e,n){var s=this.lang.testability,a=s[void 0!==n?n:1],r=i[void 0!==n?n:1];this.parts.counter.setText(this.templates.counterText.output({current:t+1,total:e,testability:a}));for(var o in i)this.parts.wrapper.removeClass("cke_a11yc_testability_"+i[o]);this.parts.wrapper.addClass("cke_a11yc_testability_"+r)},build:function(){ +this.parts={wrapper:CKEDITOR.dom.element.createFromHtml(this.templates.wrapper.output()),counter:CKEDITOR.dom.element.createFromHtml(this.templates.counter.output()),previous:CKEDITOR.dom.element.createFromHtml(this.templates.button.output({title:this.lang.navigationPrevTitle,"class":"previous",text:this.lang.navigationPrev})),next:CKEDITOR.dom.element.createFromHtml(this.templates.button.output({title:this.lang.navigationNextTitle,"class":"next",text:this.lang.navigationNext}))};var t=CKEDITOR.dom.element.createFromHtml(this.templates.buttonWrapper.output()),e=t.clone();t.append(this.parts.previous),e.append(this.parts.next),this.parts.wrapper.append(this.parts.counter),this.parts.wrapper.append(t),this.parts.wrapper.append(e),this.parts.previous.unselectable(),this.parts.next.unselectable();var i=n(32,function(t){this.fire("click")});this.parts.previous.on("keydown",i),this.parts.next.on("keydown",i),this.parts.previous.on("click",function(){this.fire("previous")},this),this.parts.next.on("click",function(){ +this.fire("next")},this)}},CKEDITOR.event.implementOn(e.prototype),e}(k),l=function(){"use strict";function t(t,e){e&&(CKEDITOR.tools.extend(this,e,!0),this.name=t,this.id=CKEDITOR.tools.getNextId()+"_input",this.wrapper=CKEDITOR.dom.element.createFromHtml(this.wrapperTemplate.output({label:this.label,id:this.id})))}return t.prototype={wrapperTemplate:new CKEDITOR.template(''),getValue:function(){return this.input.getValue()},setValue:function(t){this.input.setValue(t)},setInitialValue:function(){void 0!==this.value&&this.setValue(this.value)},remove:function(){this.wrapper.remove()}},t}(),d=function(t){"use strict";var e={Text:function(e,n){t.apply(this,arguments),this.input=CKEDITOR.dom.element.createFromHtml(this.inputTemplate.output({id:this.id})),this.input.appendTo(this.wrapper),this.setInitialValue()},Checkbox:function(e,n){t.apply(this,arguments), +this.input=CKEDITOR.dom.element.createFromHtml(this.inputTemplate.output({id:this.id})),this.input.appendTo(this.wrapper),this.setInitialValue()},Select:function(e,n){var i,s;t.apply(this,arguments),this.options={},this.input=CKEDITOR.dom.element.createFromHtml(this.inputTemplate.output({id:this.id}));for(i in n.options)s=new CKEDITOR.dom.element("option"),s.setText(n.options[i]),s.setAttribute("value",i),s.appendTo(this.input),this.options[i]=s;this.input.appendTo(this.wrapper),this.setInitialValue()}};return e.Text.prototype=CKEDITOR.tools.extend(new t,{inputTemplate:new CKEDITOR.template('')}),e.Checkbox.prototype=CKEDITOR.tools.extend(new t,{inputTemplate:new CKEDITOR.template(''),getValue:function(){return this.input.$.checked; +}},!0),e.Select.prototype=CKEDITOR.tools.extend(new t,{inputTemplate:new CKEDITOR.template('')}),e}(l),h=function(t){"use strict";function e(t){this.viewer=t,this.templates={};for(var e in this.templateDefinitions)this.templates[e]=new CKEDITOR.template(this.templateDefinitions[e]);this.inputs={},this.parts={},this.build()}function n(t,e){return function(n){var i=n.data.getKeystroke(),s=CKEDITOR.tools,a=s.isArray(t)?-1!==s.indexOf(t,i):i==t;a&&(e.call(this,n),n.data.preventDefault())}}return e.prototype={templateDefinitions:{wrapper:'',fieldset:'',actionset:'',buttonWrapper:'
',button:'{text}' +},addInput:function(e,n){this.inputs[e]=new(t[CKEDITOR.tools.capitalize(n.type)])(e,n),this.inputs[e].wrapper.appendTo(this.parts.fieldset),this.fire("addInput",this.inputs[e])},removeInput:function(t){this.inputs[t].remove(),this.fire("removeInput",this.inputs[t]),this.inputs[t]=null},setInputs:function(t){this.removeInputs(),this.inputs={};for(var e in t)this.addInput(e,t[e])},removeInputs:function(){for(var t in this.inputs)this.removeInput(t)},serialize:function(){var t={};for(var e in this.inputs)t[e]=this.inputs[e].getValue();return t},build:function(){var t=this.viewer.editor.lang.a11ychecker;this.parts={wrapper:CKEDITOR.dom.element.createFromHtml(this.templates.wrapper.output()),fieldset:CKEDITOR.dom.element.createFromHtml(this.templates.fieldset.output()),actionset:CKEDITOR.dom.element.createFromHtml(this.templates.actionset.output()),quickfixButton:CKEDITOR.dom.element.createFromHtml(this.templates.button.output({title:t.quickFixButtonTitle,text:t.quickFixButton,"class":"cke_a11yc_ui_button_ok" +})),ignoreButton:CKEDITOR.dom.element.createFromHtml(this.templates.button.output({title:t.ignoreBtnTitle,text:t.ignoreBtn,"class":"cke_a11yc_ui_button_ignore"}))},this.parts.fieldset.appendTo(this.parts.wrapper),this.parts.actionset.appendTo(this.parts.wrapper);var e=CKEDITOR.dom.element.createFromHtml(this.templates.buttonWrapper.output({"class":"cke_a11yc_ui_button_ok_wrapper"})),i=CKEDITOR.dom.element.createFromHtml(this.templates.buttonWrapper.output({"class":"cke_a11yc_ui_button_ignore_wrapper"}));this.parts.quickfixButton.appendTo(e),this.parts.ignoreButton.appendTo(i),e.appendTo(this.parts.actionset),i.appendTo(this.parts.actionset),this.parts.quickfixButton.on("click",function(t){this.fire("submit"),t.data.preventDefault()},this),this.parts.fieldset.on("keydown",n(13,function(t){this.fire("submit")}),this),this.parts.quickfixButton.on("keydown",n(32,function(t){this.fire("submit")}),this),this.parts.ignoreButton.on("click",function(t){this.fire("ignore"),t.data.preventDefault(); +},this),this.parts.ignoreButton.on("keydown",function(t){32==t.data.getKeystroke()&&(t.data.preventDefault(),this.fire("ignore"))},this)},show:function(){this.parts.fieldset.show(),this.parts.quickfixButton.show()},hide:function(){this.parts.fieldset.hide(),this.parts.quickfixButton.hide()},setIgnored:function(t){var e=this.parts.ignoreButton;e.getFirst().setHtml(this.viewer.editor.lang.a11ychecker[t?"stopIgnoreBtn":"ignoreBtn"]),e.setAttribute("aria-pressed",t)}},CKEDITOR.event.implementOn(e.prototype),e}(d),p=function(){"use strict";function t(t){this.viewer=t,this.templates={};for(var e in this.templateDefinitions)this.templates[e]=new CKEDITOR.template(this.templateDefinitions[e]);this.parts={},this.build()}return t.prototype={templateDefinitions:{wrapper:'
',info:"

{text}

",button:'{text}'}, +build:function(){var t=this.viewer.editor.lang.a11ychecker;this.parts={wrapper:CKEDITOR.dom.element.createFromHtml(this.templates.wrapper.output()),info:CKEDITOR.dom.element.createFromHtml(this.templates.info.output({text:t.listeningInfo})),button:CKEDITOR.dom.element.createFromHtml(this.templates.button.output({title:t.listeningCheckAgain,text:t.listeningCheckAgain}))},this.parts.wrapper.append(this.parts.info),this.parts.wrapper.append(this.parts.button),this.parts.button.on("click",function(){this.fire("check")},this)}},CKEDITOR.event.implementOn(t.prototype),t}(),f=function(){"use strict";function t(){this.list=[]}return t.prototype={},t.prototype.constructor=t,t.prototype.count=function(){return this.list.length},t.prototype.addItem=function(t){t.on("keydown",this.keyDownListener,this),this.list.push(t),this.sort()},t.prototype.removeItem=function(t){"number"!=typeof t&&(t=CKEDITOR.tools.indexOf(this.list,t)),this.list[t].removeListener("keydown",this.keyDownListener),this.list.splice(t,1); +},t.prototype.getItem=function(t){return this.list[t]},t.prototype.keyDownListener=function(t){var e=CKEDITOR.tools.indexOf(this.list,t.sender),n=t.data.getKey(),i=t.data.getKeystroke();if(-1!==e&&1!==this.list.length&&9==n){var s=i&CKEDITOR.SHIFT,a=s?this.getPrev(e):this.getNext(e);a&&(a.focus(),t.data.preventDefault(1),t.data.stopPropagation())}},t.prototype.getNext=function(t){var e,n=0,i=this.list.length;"number"==typeof t&&i>t+1&&(n=t+1),e=this.getItem(n);var s,a;for(s=1;i-1>s&&!e.isVisible();s++)a=n+s,a>=i&&(a-=i),e=this.getItem(a);return s!=i-1||e.isVisible()?e:void 0},t.prototype.getPrev=function(t){var e,n=this.list.length,i=n-1;"number"==typeof t&&t>0&&(i=t-1),e=this.getItem(i);var s,a;for(s=1;n-1>s&&!e.isVisible();s++)a=i-s,0>a&&(a=n+a),e=this.getItem(a);return s!=n-1||e.isVisible()?e:void 0},t.prototype.sort=function(){this.list.sort(this._sort)},t.prototype._sort=function(t,e){var n=0;return n=t.getPosition(e)&CKEDITOR.POSITION_FOLLOWING?1:-1},t}(),g=function(){"use strict"; +function t(t,e){this.viewer=t,CKEDITOR.tools.extend(this,e),this.panelShowListeners=this.panelShowListeners(t),this.activePanelShowlisteners=[]}return t.prototype={enterMode:function(){this.init&&this.init(this.viewer),this.addPanelShowListeners()},leaveMode:function(){this.close&&this.close(this.viewer),this.removePanelShowListeners()},addPanelShowListener:function(t){this.activePanelShowlisteners.push(t)},addPanelShowListeners:function(t){if(this.panelShowListeners)for(var e=0;es;s++)t[s].display(n,e.editor);i&&n.show()},i)},quickFixAccepted:function(t){var e,n=this.viewer.editor,i=n._.a11ychecker,s=i.viewerController,a=this.serialize(),r=s.quickFixSelected;if(r)if(e=r.validate(a),e.length){alert(e.join(","));var o=CKEDITOR.tools.objectKeys(this.inputs);o.length&&this.inputs[o[0]].input.focus(),t.cancel()}else i.applyQuickFix(r,a);else console.error("No quickfix available!"),t.cancel()},showIssue:function(t,e){t.element.scrollIntoView(),this.viewer.panel.attach(t.element),e&&(e.event&&this.fire(e.event),e.callback&&e.callback.call(this))},startListening:function(){this.viewer.setMode("listening"),this.viewer.panel.show(),this.editor.focus()},stopListening:function(){this.viewer.panel.hide(),this.viewer.setMode("checking")}},CKEDITOR.event.implementOn(e.prototype), +e}(m),b=function(){"use strict";function t(e){if(this.controller=e,e){var n=t.parseConfig(e.editor.config.a11ychecker_keystrokes);this.setEditorHotkeys(e.editor,n),this.setBalloonHotkeys(e.viewerController,n)}}return t.prototype={},t.prototype.constructor=t,t.prototype.setEditorHotkeys=function(t,e){for(var n in e)t.setKeystroke(Number(n),e[n])},t.prototype.setBalloonHotkeys=function(t,e){var n=this,i=n.controller.editor,s=t.viewer.panel,a=s.parts.panel;s.addShowListener(function(){return a.on("keydown",n.getBalloonKeydown(i,e))})},t.prototype.getBalloonKeydown=function(t,e){return function(n){var i=e[n.data.getKeystroke()];i&&(t.execCommand(i),n.data.preventDefault())}},t.parseConfig=function(t){var e,n,i={},s=t||{},a={open:CKEDITOR.CTRL+CKEDITOR.ALT+69,next:CKEDITOR.CTRL+69,prev:CKEDITOR.CTRL+CKEDITOR.SHIFT+69,listen:CKEDITOR.SHIFT+27,close:27};for(n in a)void 0===s[n]&&(s[n]=a[n]),e="open"==n?"":"."+n,i[s[n]]="a11ychecker"+e;return i},t}(),y=function(){"use strict";function t(){}return t.getPreferredLanguage=function(t,e,n,i){ +i=i||window.navigator;var s=[t,e,"en"],a=/([a-z]+)(?:-([a-z]+))?/,r=i.language||i.userLanguage,o=CKEDITOR.tools.indexOf;r&&s.splice(1,0,r);for(var c=0,u=s.length;u>c;c++)if(s[c]){var l=s[c].toLowerCase(),d=l.match(a),h=d[1],p=d[2];if(p&&-1!==o(n,l))return l;if(-1!==o(n,h))return h}return null},t}(),x=function(t,e,n,i,s,a,r,o,c){"use strict";function u(t){this._={},this.editor=t,this.editableDecorator=new i(this.editor),this.ui=new a(this),this.preferredIssueFinder=new s,t&&(this.viewerController=new r(this,{title:t.lang.a11ychecker.balloonLabel}),this.attachEditorListeners(t),this.hotkeyManager=new o(this))}return u.modes={CHECKING:1,LISTENING:2,BUSY:3},u.prototype={issues:null,viewerController:null,enabled:!1,disableFilterStrip:!1},u.prototype.constructor=u,u.prototype.setEngine=function(t){this.engine=t},u.prototype.exec=function(t){return this.enabled?void this.close():(this.issues&&this.issues.clear(),this.enable(),void this.check({ui:!0,callback:t}))},u.prototype.listen=function(){ +if(this.enabled){var t=u.modes;if(this.modeType===t.LISTENING)this.check();else{var e=this.issues.getFocused()&&this.issues.getFocused().element;this.setMode(t.LISTENING),e&&this.editor.getSelection().selectElement(e)}}},u.prototype.check=function(t){t=t||{};var e,n=this,i=n.editor;this.setMode(u.modes.BUSY),t.ui&&this.ui.show(),e=this.getTempOutput(),this.editableDecorator.applyMarkup(),this.editableDecorator.decorateScratchpad(e),this.disableFilterStrip=!0,e.setHtml(i.getData()),this.disableFilterStrip=!1,CKEDITOR.document.getBody().append(e);var s=function(e){n._engineProcessed.call(n,e,t)};this.engine.process(this,e,s)},u.prototype.disable=function(){this.enabled&&(this.enabled=!1,this.fire("disabled"))},u.prototype.enable=function(){this.enabled||(this.enabled=!0,this.fire("enabled"),this.setMode(u.modes.CHECKING))},u.prototype.next=function(t){var e,n=this.issues;0!==n.count()&&(e=this.issues.next(),this.viewerController.showIssue(e,{event:"next",callback:t}))},u.prototype.prev=function(t){ +var e,n=this.issues;0!==n.count()&&(e=this.issues.prev(),this.viewerController.showIssue(e,{event:"prev",callback:t}))},u.prototype.showIssue=function(t,e){var n,i,s=this.issues,a=t,r=function(){this.viewer.navigation.parts.next.focus(),e&&e.call(this)};return"number"!=typeof a&&(a=s.indexOf(a)),n=s.getItem(a),n&&n==s.getFocused()?(r.call(this.viewerController),!0):(i=s.moveTo(a),i&&this.viewerController&&this.viewerController.showIssue(s.getItem(a),{callback:r}),i)},u.prototype.showIssueByElement=function(t,e){var n=this.issues.getIssueByElement(t);return n?this.showIssue(n,e):!1},u.prototype.ignoreIssue=function(){var t=this.issues.getFocused();t&&(t.setIgnored(!t.isIgnored()),this.editableDecorator.markIssueElement(t,this.issues))},u.prototype.close=function(){this.enabled&&(this.ui.hide(),this.disable(),this.issues.clear(),this.preferredIssueFinder.unset(),this.mode.close(),this.mode=null,this.modeType=null)},u.prototype.setMode=function(i){var s,a={};if(a[u.modes.CHECKING]=t,a[u.modes.LISTENING]=e, +a[u.modes.BUSY]=n,s=a[i],!s)throw new Error("Invalid mode value, use Controller.modes members");i!==this.modeType&&(this.mode&&this.mode.close(),this.mode=new s(this),this.mode.init(),this.modeType=i)},u.prototype.attachEditorListeners=function(t){var e=this,n=["a11ychecker","a11ychecker.listen","a11ychecker.next","a11ychecker.prev","a11ychecker.close","wysiwyg","source"];t.on("beforeSetMode",function(){e.close()}),t.on("beforeCommandExec",function(t){var i=String(t.data.name);-1===CKEDITOR.tools.indexOf(n,i)&&e.enabled&&e.setMode(u.modes.LISTENING)},null,null,9999)},u.prototype.applyQuickFix=function(t,e){this._withUndoManager(function(){var e=this.mode,n=this.editor;this.editableDecorator.removeMarkup(),t.markSelection(n,n.getSelection()),e.unsetStoredSelection&&e.unsetStoredSelection(),this.editor.fire("updateSnapshot")}),t.fix(e,CKEDITOR.tools.bind(this._onQuickFix,this))},u.prototype._onQuickFix=function(t){this._withUndoManager(function(){this.editor.fire("saveSnapshot")});var e={ +quickFix:t,issue:t.issue},n=this.fire("fixed",e,this.editor);n!==!1&&this.check({ui:!0})},u.prototype._setIssueList=function(t){var e=this;t.sort(),t.on("focusChanged",function(t){var n=t.data.current;n&&e.preferredIssueFinder.set(n)}),e.issues=t},u.prototype.onNoIssues=function(){alert(this.editor.lang.a11ychecker.noIssuesMessage),this.close()},u.prototype.getTempOutput=function(){var t=this._;return t.scratchpad||(t.scratchpad=CKEDITOR.document.createElement("div"),t.scratchpad.setStyle("display","none")),t.scratchpad},u.prototype.getQuickFixLang=function(){var t=this.editor,e=t.config,n=t.plugins.a11ychecker.quickFixesLang.split(",");return c.getPreferredLanguage(e.language,e.defaultLanguage,n)||n[0]},u.prototype._engineProcessed=function(t,e){var n,i,s=this;s.editableDecorator.resolveEditorElements(t),s._setIssueList(t),s.setMode(u.modes.CHECKING),e.ui&&s.ui.update(),n=s.fire("checked",{issues:t}),e.callback&&e.callback.call(s,t.count(!0)===!0,t),n!==!1&&(t.count()?(i=s.preferredIssueFinder.getFromListIndex(t)||0, +i>=t.count()&&(i=0),s.showIssue(t.getItem(i))):s.onNoIssues())},u.prototype._withUndoManager=function(t){var e=this.editor,n=!!e.undoManager.locked;n&&e.fire("unlockSnapshot"),t.call(this),n&&e.fire("lockSnapshot",{dontUpdate:!0})},CKEDITOR.event.implementOn(u.prototype),u}(n,i,s,a,r,o,v,b,y),E=function(){"use strict";function t(){}return t.prototype={guideline:["aAdjacentWithSameResourceShouldBeCombined","aImgAltNotRepetitive","aLinksAreSeparatedByPrintableCharacters","aMustNotHaveJavascriptHref","aSuspiciousLinkText","blockquoteNotUsedForIndentation","documentVisualListsAreMarkedUp","headerH1","headerH2","headerH3","headerH4","imgAltIsDifferent","imgAltIsTooLong","imgAltNotEmptyInAnchor","imgAltTextNotRedundant","imgHasAlt","imgShouldNotHaveTitle","pNotUsedAsHeader","tableDataShouldHaveTh","imgWithEmptyAlt"]},t}(),function(){"use strict";function t(t){return o(t)?r(t,"a11ychecker.next","next"):void 0}function n(t){return o(t)?r(t,"a11ychecker.prev","prev"):void 0}function i(t){return r(t,"a11ychecker","exec"); +}function s(t){return t._.a11ychecker.listen()}function a(t){return t._.a11ychecker.close()}function r(t,e,n){return t._.a11ychecker[n](function(){t.fire("afterCommandExec",{name:e,command:t.getCommand(e),commandData:{}})})}function o(t){var e=t._.a11ychecker;return e&&e.modeType===e.constructor.modes.CHECKING}function c(t){var e=this.config,n=e.contentsCss;CKEDITOR.tools.isArray(n)||(e.contentsCss=n?[n]:[]),e.contentsCss.push(t)}var u="a11ychecker";CKEDITOR.plugins.add(u,{requires:"balloonpanel",lang:"en,nl,de,pt-br",quickFixesLang:"en,nl,de,pt-br",icons:u,hidpi:!0,onLoad:function(){var t=this.path;CKEDITOR.document.appendStyleSheet(t+"skins/"+this.getStylesSkinName()+"/a11ychecker.css"),function(e,n,i,s,a){CKEDITOR.tools.extend(CKEDITOR.plugins.a11ychecker,{Engine:e,Issue:n,IssueList:i,IssueDetails:s}),CKEDITOR.plugins.a11ychecker.quickFixes=new a(t+"quickfix/")}(T,k,C,I,e)},beforeInit:function(t){var e=this;t.config.a11ychecker_noIgnoreData||t.filter.allow("*[data-a11y-ignore]","a11ychecker"), +this.createTemporaryNamespace(t),t.once("instanceReady",function(){!function(n){var i=new n(t),s=t._.a11ychecker;t._.a11ychecker=i,s.getEngineType(function(n){i.setEngine(new n(e)),i.engine.config=i.engine.createConfig(t),s.fire("loaded",null,t)})}(x)}),e.commandRegister.call(e,t),e.guiRegister(t)},guiRegister:function(t){var e=this.path+"skins/"+this.getStylesSkinName()+"/contents.css",n=t.addContentsCss||c;t.ui.addButton&&t.ui.addButton("A11ychecker",{label:t.lang.a11ychecker.toolbar,command:u,toolbar:"document,10"}),n.call(t,e)},commandRegister:function(e){e.addCommand(u,{exec:i,async:!0,canUndo:!1,editorFocus:!1}),e.addCommand(u+".listen",{exec:s,canUndo:!1,editorFocus:!1}),e.addCommand(u+".next",{exec:t,async:!0,canUndo:!1,editorFocus:!1}),e.addCommand(u+".prev",{exec:n,async:!0,canUndo:!1,editorFocus:!1}),e.addCommand(u+".close",{exec:a})},createTemporaryNamespace:function(t){t._.a11ychecker={getEngineType:function(t){function e(t){this.jsonPath=(t?t.path:"")+"libs/quail/"}var n,i,s=CKEDITOR.plugins.a11ychecker,a=s.Engine,r=s.IssueList,o=s.Issue,c=s.IssueDetails,u=window.jQuery||window.$; +!function(t){i=t}(E),function(){if(!u||!u.fn)throw new Error("Missing jQuery. Accessibility Checker's default engine, Quail.js requires jQuery to work correctly.");u.fn.quail||!function(t){Function.prototype.bind=Function.prototype.bind||function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice,n=e.call(arguments,1),i=this,s=function(){},a=function(){return i.apply(this instanceof s?this:t||window,n.concat(e.call(arguments)))};return s.prototype=this.prototype,a.prototype=new s,a};var e={options:{},components:{},lib:{},testabilityTranslation:{0:"suggestion",.5:"moderate",1:"severe"},html:null,strings:{},accessibilityResults:{},accessibilityTests:null,guidelines:{wcag:{setup:function(t,e,n){n=n||{};for(var i in this.successCriteria)if(this.successCriteria.hasOwnProperty(i)){var s=this.successCriteria[i];s.registerTests(t),e&&e.listenTo&&"function"==typeof e.listenTo&&n.successCriteriaEvaluated&&e.listenTo(s,"successCriteriaEvaluated",n.successCriteriaEvaluated); +}},successCriteria:{}}},tests:{},textSelector:":not(:empty)",suspectPHeaderTags:["strong","b","em","i","u","font"],suspectPCSSStyles:["color","font-weight","font-size","font-family"],focusElements:"a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]",emoticonRegex:/((?::|;|B|P|=)(?:-)?(?:\)|\(|o|O|D|P))/g,selfClosingTags:["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"],optionalClosingTags:["p","li","th","tr","td"],run:function(n){function i(t,e,n){if(n.guideline&&n.guideline.length){t.tests=t.lib.TestCollection([],{scope:t.html||null});for(var i=0,s=n.guideline.length;s>i;++i){var a=n.guideline[i];e[a]&&(e[a].scope=t.html||null,t.tests.set(a,e[a]))}}else t.tests=t.lib.TestCollection(e,{scope:t.html||null})}function s(){if("undefined"!=typeof n.customTests)for(var t in n.customTests)n.customTests.hasOwnProperty(t)&&(n.customTests[t].scope=e.html||null, +e.tests.set(t,n.customTests[t]));var i=function(){};for(var s in e.guidelines)e.guidelines[s]&&"function"==typeof e.guidelines[s].setup&&e.guidelines[s].setup(e.tests,this,{successCriteriaEvaluated:n.successCriteriaEvaluated||i});e.tests.run({preFilter:n.preFilter||function(){},caseResolve:n.caseResolve||function(){},testComplete:n.testComplete||function(){},testCollectionComplete:n.testCollectionComplete||function(){},complete:n.complete||function(){}})}if(n.reset&&(e.accessibilityResults={}),e.tests=e.lib.TestCollection([],{scope:e.html||null}),"undefined"!=typeof quailBuilderTests)e.tests=e.lib.TestCollection(quailBuilderTests,{scope:e.html||null}),s.call(e);else if("wcag2"===n.guideline)e.lib.wcag2.run(n);else if(n.accessibilityTests)i(e,n.accessibilityTests,n),s.call(e);else{var a=n.jsonPath;"string"==typeof n.guideline&&(a+="/guidelines/"+n.guideline),t.ajax({url:a+"/tests.json",dataType:"json",success:function(t){"object"==typeof t&&(i(e,t,n),s.call(e))},error:function(){throw new Error("Tests could not be loaded"); +}})}},listenTo:function(t,e,n){n=n.bind(this),t.registerListener.call(t,e,n)},getConfiguration:function(t){var e=this.tests.find(t),n=e&&e.get("guidelines"),i=n&&this.options.guidelineName&&n[this.options.guidelineName],s=i&&i.configuration;return s?s:!1},isUnreadable:function(t){return"string"!=typeof t?!0:t.trim().length?!1:!0},isDataTable:function(e){if(e.find("tr").length<3)return!1;if(e.find("th[scope]").length)return!0;var n=e.find("tr:has(td)").length,i=e.find("td[rowspan], td[colspan]"),s=!0;if(i.length){var a={};i.each(function(){"undefined"==typeof a[t(this).index()]&&(a[t(this).index()]=0),a[t(this).index()]++}),t.each(a,function(t,e){n>e&&(s=!1)})}var r=e.find("table");if(r.length){var o={};r.each(function(){var e=t(this).parent("td").index();e!==!1&&"undefined"==typeof o[e]&&(o[e]=0),o[e]++}),t.each(o,function(t,e){n>e&&(s=!1)})}return s},getTextContents:function(t){if(t.is("p, pre, blockquote, ol, ul, li, dl, dt, dd, figure, figcaption"))return t.text();for(var e="",n=t[0].childNodes,i=0,s=n.length;s>i;i+=1)3===n[i].nodeType&&(e+=n[i].nodeValue); +return e},validURL:function(t){return-1===t.search(" ")},cleanString:function(t){return t.toLowerCase().replace(/^\s\s*/,"")},containsReadableText:function(n,i){if(n=n.clone(),n.find("option").remove(),!e.isUnreadable(n.text()))return!0;if(!e.isUnreadable(n.attr("alt")))return!0;if(i){var s=!1;if(n.find("*").each(function(){e.containsReadableText(t(this),!0)&&(s=!0)}),s)return!0}return!1}};if(window&&(window.quail=e),t.fn.quail=function(t){return this.length?(e.options=t,e.html=this,e.run(t),this):this},t.expr[":"].quailCss=function(e,n,i){var s=i[3].split(/\s*=\s*/);return t(e).css(s[0]).search(s[1])>-1},e.components.acronym=function(e,n,i){n.get("$scope").each(function(){var e=t(this),s={},a={};e.find("acronym[title], abbr[title]").each(function(){a[t(this).text().toUpperCase().trim()]=t(this).attr("title")}),e.find("p, div, h1, h2, h3, h4, h5").each(function(){var e=this,r=t(e),o=r.text().split(" "),c=[];o.length>1&&r.text().toUpperCase()!==r.text()?(t.each(o,function(t,e){e.length<2||(e=e.replace(/[^a-zA-Zs]/,""), +e.toUpperCase()===e&&"undefined"==typeof a[e.toUpperCase().trim()]&&("undefined"==typeof s[e.toUpperCase()]&&c.push(e),s[e.toUpperCase()]=e))}),n.add(i(c.length?{element:e,expected:r.closest(".quail-test").data("expected"),info:{acronyms:c},status:"failed"}:{element:e,expected:r.closest(".quail-test").data("expected"),status:"passed"}))):n.add(i({element:e,expected:r.closest(".quail-test").data("expected"),status:"passed"}))})})},e.components.color=function(){function n(t,n,i,s,a,r){t.add(n({element:i,expected:function(t,n){return e.components.resolveExpectation(t,n)}(i,a),message:r,status:s}))}function i(e){return""!==t.trim(e)}function s(n){var i=n.parentNode,s=t(i);return 1!==i.nodeType?!1:-1!==["script","style","title","object","applet","embed","template","noscript"].indexOf(i.nodeName.toLowerCase())?!1:e.isUnreadable(s.text())?!1:!0}function a(t){function e(t){return Object.keys(t).length}var n={},i=t.groupCasesBySelector(),s="";for(var a in i)if(i.hasOwnProperty(a)){var r=i[a];r.each(function(t,e){ +e.get("status")===n&&(n[a]=s)})}return e(n)===e(i)}var r={cache:{},getLuminosity:function(t,e){var n="getLuminosity_"+t+"_"+e;if(t=r.parseColor(t),e=r.parseColor(e),void 0!==r.cache[n])return r.cache[n];var i,s,a=t.r/255,o=t.g/255,c=t.b/255,u=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),l=.03928>=o?o/12.92:Math.pow((o+.055)/1.055,2.4),d=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),h=e.r/255,p=e.g/255,f=e.b/255,g=.03928>=h?h/12.92:Math.pow((h+.055)/1.055,2.4),m=.03928>=p?p/12.92:Math.pow((p+.055)/1.055,2.4),v=.03928>=f?f/12.92:Math.pow((f+.055)/1.055,2.4);return i=.2126*u+.7152*l+.0722*d,s=.2126*g+.7152*m+.0722*v,r.cache[n]=Math.round((Math.max(i,s)+.05)/(Math.min(i,s)+.05)*10)/10,r.cache[n]},fetchImageColorAtPixel:function(t,e,n){e="undefined"!=typeof e?e:1,n="undefined"!=typeof n?n:1;var i=document.createElement("canvas"),s=i.getContext("2d");s.drawImage(t,0,0);var a=s.getImageData(e,n,1,1).data;return"rgb("+a[0]+","+a[1]+","+a[2]+")"},testElmContrast:function(t,e,n){var i=r.getColor(e,"background"); +return r.testElmBackground(t,e,i,n)},testElmBackground:function(t,e,n,i){var s,a=r.getColor(e,"foreground");return"wcag"===t?s=r.passesWCAGColor(e,a,n,i):"wai"===t&&(s=r.passesWAIColor(a,n)),s},passesWCAGColor:function(t,n,i,s){var a=e.components.convertToPx(t.css("fontSize"));if("undefined"==typeof s)if(a>=18)s=3;else{var o=t.css("fontWeight");s=a>=14&&("bold"===o||parseInt(o,10)>=700)?3:4.5}return r.getLuminosity(n,i)>s},passesWAIColor:function(t,e){var n=r.getWAIErtContrast(t,e),i=r.getWAIErtBrightness(t,e);return n>500&&i>125},getWAIErtContrast:function(t,e){var n=r.getWAIDiffs(t,e);return n.red+n.green+n.blue},getWAIErtBrightness:function(t,e){var n=r.getWAIDiffs(t,e);return(299*n.red+587*n.green+114*n.blue)/1e3},getWAIDiffs:function(t,e){return{red:Math.abs(t.r-e.r),green:Math.abs(t.g-e.g),blue:Math.abs(t.b-e.b)}},getColor:function(e,n){var i=r;e.attr("data-cacheId")||e.attr("data-cacheId","id_"+Math.random());var s="getColor_"+n+"_"+e.attr("data-cacheId");if(void 0!==r.cache[s])return r.cache[s]; +if("foreground"===n)return r.cache[s]=e.css("color")?e.css("color"):"rgb(0,0,0)",r.cache[s];var a=e.css("background-color");return r.hasBackgroundColor(a)?(r.cache[s]=a,r.cache[s]):(e.parents().each(function(){var e=t(this).css("background-color");return r.hasBackgroundColor(e)?i.cache[s]=e:void 0}),r.cache[s]="rgb(255,255,255)",r.cache[s])},getForeground:function(t){return r.getColor(t,"foreground")},parseColor:function(t){return"object"==typeof t?t:"#"===t.substr(0,1)?{r:parseInt(t.substr(1,2),16),g:parseInt(t.substr(3,2),16),b:parseInt(t.substr(5,2),16),a:!1}:"rgb"===t.substr(0,3)?(t=t.replace("rgb(","").replace("rgba(","").replace(")","").split(","),{r:t[0],g:t[1],b:t[2],a:"undefined"==typeof t[3]?!1:t[3]}):void 0},getBackgroundImage:function(e){e.attr("data-cacheId")||e.attr("data-cacheId","id_"+Math.random());var n="getBackgroundImage_"+e.attr("data-cacheId");if(void 0!==r.cache[n])return r.cache[n];for(e=e[0];e&&1===e.nodeType&&"BODY"!==e.nodeName&&"HTML"!==e.nodeName;){var i=t(e).css("background-image"); +if(i&&"none"!==i&&-1!==i.search(/^(.*?)url(.*?)$/i))return r.cache[n]=i.replace("url(","").replace(/['"]/g,"").replace(")",""),r.cache[n];e=e.parentNode}return r.cache[n]=!1,!1},getBackgroundGradient:function(e){e.attr("data-cacheId")||e.attr("data-cacheId","id_"+Math.random());var n="getBackgroundGradient_"+e.attr("data-cacheId");if(void 0!==r.cache[n])return r.cache[n];var i=function(e){return""!==t.trim(e)};for(e=e[0];e&&1===e.nodeType&&"BODY"!==e.nodeName&&"HTML"!==e.nodeName;){if(r.hasBackgroundColor(t(e).css("background-color")))return r.cache[n]=!1,!1;var s=t(e).css("backgroundImage");if(s&&"none"!==s&&-1!==s.search(/^(.*?)gradient(.*?)$/i)){var a=s.match(/gradient(\(.*\))/g);if(a.length>0)return a=a[0].replace(/(linear|radial|from|\bto\b|gradient|top|left|bottom|right|\d*%)/g,""),r.cache[n]=t.grep(a.match(/(rgb\([^\)]+\)|#[a-z\d]*|[a-z]*)/g),i),r.cache[n]}e=e.parentNode}return r.cache[n]=!1,!1},getAverageRGB:function(t){var e=t.src;if(void 0!==r.cache[e])return r.cache[e];var n,i,s,a,o=5,c={ +r:0,g:0,b:0},u=document.createElement("canvas"),l=u.getContext&&u.getContext("2d"),d=-4,h={r:0,g:0,b:0,a:0},p=0;if(!l)return r.cache[e]=c,c;s=u.height=t.height,i=u.width=t.width,l.drawImage(t,0,0);try{n=l.getImageData(0,0,i,s)}catch(f){return r.cache[e]=c,c}for(a=n.data.length;(d+=4*o)0&&(p=p[0].replace(/(linear|radial|from|\bto\b|gradient|top|left|bottom|right|\d*%)/g,""),a=t.grep(p.match(/(rgb\([^\)]+\)|#[a-z\d]*|[a-z]*)/g),i))}break;case"background-image":if(r.hasBackgroundColor(h)){a=!1;continue}d=l.css("backgroundImage"),d&&"none"!==d&&-1!==d.search(/^(.*?)url(.*?)$/i)&&(a=d.replace("url(","").replace(/['"]/g,"").replace(")",""))}o.push({element:l,visibility:l.css("visibility")}),l.css("visibility","hidden"),l=document.elementFromPoint(c,u)}for(var f=0;fn.data("content-score"))&&(n=e)}),n)}},e.components.convertToPx=function(n){if(n.search("px")>-1)return parseInt(n,10);var i=t('
 
').appendTo(e.html),s=i.height();return i.remove(),s},e.components.event=function(e,n,i,s){var a=n.get("$scope"),r=s.selector&&a.find(s.selector)||a.find("*"),o=s.searchEvent||"",c=s.correspondingEvent||"";r.each(function(){var s,a=o.replace("on",""),r=e.components.hasEventListener(t(this),a);t._data&&(s=t._data(this,"events"));var u=s&&s[a]&&!!s[a].length,l=!!c.length,d=e.components.hasEventListener(t(this),c.replace("on","")),h=t(this).closest(".quail-test").data("expected"),p=n.add(i({element:this,expected:h}));p.set(!r&&!u||l&&d?{status:"passed"}:{status:"failed"})})},e.components.hasEventListener=function(e,n){return"undefined"!=typeof t(e).attr("on"+n)?!0:t._data(t(e)[0],"events")&&"undefined"!=typeof t._data(t(e)[0],"events")[n]?!0:t(e).is("a[href], input, button, video, textarea")&&"undefined"!=typeof t(e)[0][n]&&("click"===n||"focus"===n)&&t(e)[0][n].toString().search(/^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i)>-1?!1:"undefined"!=typeof t(e)[0][n]; +},e.components.headingLevel=function(e,n,i,s){var a=!1;n.get("$scope").find(":header").each(function(){var r=parseInt(t(this).get(0).tagName.substr(-1,1),10),o=this;n.add(i(a===s.headingLevel&&r>a+1?{element:o,expected:function(t){return e.components.resolveExpectation(t)}(o),status:"failed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(o),status:"passed"})),a=r})},e.components.htmlSource={getHtml:function(n){var i=this;if("undefined"!=typeof e.options.htmlSource&&e.options.htmlSource)return void n(e.options.htmlSource,i.parseHtml(e.options.htmlSource));var s=t.ajax({url:window.location.href,async:!1});s&&"undefined"!=typeof s.responseText&&n(s.responseText,i.parseHtml(s.responseText))},traverse:function(e,n,i,s){var a=this;"undefined"==typeof s&&n(e,i,!1),"undefined"!=typeof e.children&&(e.childCount=1,t.each(e.children,function(t,i){n(i,e.childCount,e),a.traverse(i,n,e.childCount,!0),"tag"===i.type&&e.childCount++})),t.isArray(e)&&t.each(e,function(t,e){ +a.traverse(e,n)})},addSelector:function(t,e,n){if("tag"===t.type&&"undefined"!=typeof t.name&&"undefined"==typeof t.selector){t.selector=n&&"undefined"!=typeof n.selector?n.selector.slice():[];var i=t.name;return"undefined"!=typeof t.attributes&&("undefined"!=typeof t.attributes.id?i+="#"+t.attributes.id[0]:"undefined"!=typeof t.attributes["class"]&&(i+="."+t.attributes["class"][0].replace(/\s/,"."))),!e||"undefined"!=typeof t.attributes&&"undefined"!=typeof t.attributes.id||(i+=":nth-child("+e+")"),t.selector.push(i),t.selector}},parseHtml:function(t){if("undefined"==typeof Tautologistics)return!1;t=t.replace(/]*)>/g,"");var e=new Tautologistics.NodeHtmlParser.HtmlBuilder(function(){},{}),n=new Tautologistics.NodeHtmlParser.Parser(e);n.parseComplete(t);var i=e.dom,s=this;return this.traverse(i,s.addSelector),i}},"undefined"!=typeof Tautologistics){var n={Text:"text",Tag:"tag",Attr:"attr",CData:"cdata",Comment:"comment"};Tautologistics.NodeHtmlParser.HtmlBuilder.prototype.write=function(t){ +if(this._done&&this.handleCallback(new Error("Writing to the builder after done() called is not allowed without a reset()")),this._options.includeLocation&&t.type!==n.Attr&&(t.location=this._getLocation(),this._updateLocation(t)),t.type!==n.Text||!this._options.ignoreWhitespace||!HtmlBuilder.reWhitespace.test(t.data)){var e,i;if(this._tagStack.last())if(e=this._tagStack.last(),t.type===n.Tag)if("/"===t.name.charAt(0)){var s=this._options.caseSensitiveTags?t.name.substring(1):t.name.substring(1).toLowerCase();if(e.name===s&&(e.closingTag=!0),!this.isEmptyTag(t)){for(var a=this._tagStack.length-1;a>-1&&this._tagStack[a--].name!==s;);if(a>-1||this._tagStack[0].name===s)for(;a"),g=["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"],m=["pre","code","textarea","script","style"],v=["p","li","tr","th","td"],b=function(t,e){return{name:n,line:t+1,"char":e}},y=function(t){var e=new Error("Ending tag not found for: "+t.name+" at line: "+t.line+" char: "+t["char"]+" starting tags: "+i[0].name);throw e.lineData=t,e},x=function(t){var e=new Error("Comment ending not found for: `comment` at line: "+t.line+" char: "+t["char"]);throw e.lineData=t,e},w=function(t){var e=new Error("Ending `/` not found for: `"+t.name+"` at line: "+t.line+" char: "+t["char"]);throw e.lineData=t.name,e},C=function(n){e=t,t=n},T=function(t){s-=t},k=function(t,e,s){if(p.test(t))n+=t;else if(t===u)C(_);else if(t===d)n="",C(O);else if(g.indexOf(n)>-1)r.strict_self_closing_tags?C(I):(n="",C(S));else{ +var a=b(e,s);i.push(a),m.indexOf(n)>-1?(n="",T(1),C(R)):(n="",T(1),C(E))}},I=function(t,e,i){t===l?(n="",C(q)):t===c&&w(b(e,i))},E=function(t){t===c&&C(q)},S=function(t){t===o&&C(k)},_=function(t){function e(){var t=i.pop();t.name===n?C(S):v.indexOf(t.name)>-1?e():y(t)}p.test(t)?n+=t:(e(),n="")},D=function(t){t===u?C(_):(T(1),C(k))},q=function(t){t===o&&C(D)},R=function(t){t===o&&C(F)},F=function(t){t===u&&C(A)},A=function(t){if(p.test(t))n+=t;else{var e=i.pop();e.name===n?C(S):y(e),n=""}},O=function(t){h.test(t)?(n="",C(S)):(T(3),C(L))},L=function(t,e,n){a||(a={content:"",line:e+1,"char":n+1,name:"comment"}),a.content+=t,f.test(a.content)&&(a=null,C(S))},N=function(e,o){var c=null;try{var u,l=e.split("\n");C(S),n="",i=[],a=null,r=o||{};for(var d=0,h=l.length;h>d;d++)for(s=0,u=l[d].length;u>s&&t;s++)t(l[d][s],d,s);if(a)x(a);else if(i.length>0){var p=i[i.length-1];-1===v.indexOf(p.name)&&y(p)}c=null}catch(f){c=f.message}finally{return c}};return N};e.components.htmlTagValidator=i(),e.components.label=function(e,n,i,s){ +var a=n.get("$scope");a.each(function(){var a=t(this);a.find(s.selector).each(function(){n.add(i(t(this).parent("label").length&&a.find("label[for="+t(this).attr("id")+"]").length&&e.containsReadableText(t(this).parent("label"))||e.containsReadableText(a.find("label[for="+t(this).attr("id")+"]"))?{element:this,expected:t(this).closest(".quail-test").data("expected"),status:"passed"}:{element:this,expected:t(this).closest(".quail-test").data("expected"),status:"failed"}))})})},e.components.labelProximity=function(e,n,i,s){var a=n.get("$scope");a.find(s.selector).each(function(){var e=a.find("label[for="+t(this).attr("id")+"]").first();n.add(i(e.length&&t(this).parent().is(e.parent())?{element:this,expected:t(this).closest(".quail-test").data("expected"),status:"passed"}:{element:this,expected:t(this).closest(".quail-test").data("expected"),status:"failed"}))})},e.components.language={maximumDistance:300,textDirection:{rtl:/[\u0600-\u06FF]|[\u0750-\u077F]|[\u0590-\u05FF]|[\uFE70-\uFEFF]/gm, +ltr:/[\u0041-\u007A]|[\u00C0-\u02AF]|[\u0388-\u058F]/gm},textDirectionChanges:{rtl:/[\u200E]|‏/gm,ltr:/[\u200F]|‎/gm},scripts:{basicLatin:{regularExpression:/[\u0041-\u007F]/g,languages:["ceb","en","eu","ha","haw","id","la","nr","nso","so","ss","st","sw","tlh","tn","ts","xh","zu","af","az","ca","cs","cy","da","de","es","et","fi","fr","hr","hu","is","it","lt","lv","nl","no","pl","pt","ro","sk","sl","sq","sv","tl","tr","ve","vi"]},arabic:{regularExpression:/[\u0600-\u06FF]/g,languages:["ar","fa","ps","ur"]},cryllic:{regularExpression:/[\u0400-\u04FF]|[\u0500-\u052F]/g,languages:["bg","kk","ky","mk","mn","ru","sr","uk","uz"]}},scriptSingletons:{bn:/[\u0980-\u09FF]/g,bo:/[\u0F00-\u0FFF]/g,el:/[\u0370-\u03FF]/g,gu:/[\u0A80-\u0AFF]/g,he:/[\u0590-\u05FF]/g,hy:/[\u0530-\u058F]/g,ja:/[\u3040-\u309F]|[\u30A0-\u30FF]/g,ka:/[\u10A0-\u10FF]/g,km:/[\u1780-\u17FF]|[\u19E0-\u19FF]/g,kn:/[\u0C80-\u0CFF]/g,ko:/[\u1100-\u11FF]|[\u3130-\u318F]|[\uAC00-\uD7AF]/g,lo:/[\u0E80-\u0EFF]/g,ml:/[\u0D00-\u0D7F]/g, +mn:/[\u1800-\u18AF]/g,or:/[\u0B00-\u0B7F]/g,pa:/[\u0A00-\u0A7F]/g,si:/[\u0D80-\u0DFF]/g,ta:/[\u0B80-\u0BFF]/g,te:/[\u0C00-\u0C7F]/g,th:/[\u0E00-\u0E7F]/g,zh:/[\u3100-\u312F]|[\u2F00-\u2FDF]/g},getDocumentLanguage:function(t,n){var i=navigator.language||navigator.userLanguage;return"undefined"!=typeof e.options.language&&(i=e.options.language),t.parents("[lang]").length&&(i=t.parents("[lang]:first").attr("lang")),"undefined"!=typeof t.attr("lang")&&(i=t.attr("lang")),i=i.toLowerCase().trim(),n?i.split("-")[0]:i}},e.components.placeholder=function(e,n,i,s){var a=function(e,s){n.add(i({element:e,expected:t(e).closest(".quail-test").data("expected"),status:s}))};n.get("$scope").find(s.selector).each(function(){var n="";if("none"===t(this).css("display")&&!t(this).is("title"))return void a(this,"inapplicable");if("undefined"!=typeof s.attribute){if(("undefined"==typeof t(this).attr(s.attribute)||"tabindex"===s.attribute&&t(this).attr(s.attribute)<=0)&&!s.content)return void a(this,"failed");t(this).attr(s.attribute)&&"undefined"!==t(this).attr(s.attribute)&&(n+=t(this).attr(s.attribute)); +}if(("undefined"==typeof s.attribute||!s.attribute||s.content)&&(n+=t(this).text(),t(this).find("img[alt]").each(function(){n+=t(this).attr("alt")})),"string"==typeof n&&n.length>0){n=e.cleanString(n);var i=/^([0-9]*)(k|kb|mb|k bytes|k byte)$/g,r=i.exec(n.toLowerCase());r&&r[0].length?a(this,"failed"):s.empty&&e.isUnreadable(n)?a(this,"failed"):e.strings.placeholders.indexOf(n)>-1?a(this,"failed"):a(this,"passed")}else s.empty&&"number"!=typeof n&&a(this,"failed")})},e.components.resolveExpectation=function(e,n){var i,s=t(e).closest(".quail-test"),a=s.data("expected");n||(i=s.data("expected"));var r="string"==typeof a&&a.split("|");if(n&&0===r.length&&a.indexOf(":")>-1&&(r=[a]),r.length>0&&1===e.nodeType)for(var o,c,u=0,l=r.length;l>u;++u)if(o=r[u].split(":"),n){if(o[0]===n){if(!o[1]||"ignore"===o[1])return;i=o[1]}}else if(c=t(o[0],s),1===c.length&&e===c.get(0)){if(!o[1]||"ignore"===o[1])return;i=o[1]}return i},e.components.selector=function(e,n,i,s){this.get("$scope").each(function(){var i=t(this),a=t(this).find(s.selector); +a.length?a.each(function(){var i,a=t(this);i=s.test&&!a.is(s.test)?"passed":"failed",n.add(e.lib.Case({element:this,expected:a.closest(".quail-test").data("expected"),status:i}))}):n.add(e.lib.Case({element:void 0,expected:i.data("expected")||i.find("[data-expected]").data("expected"),status:s.test?"inapplicable":"passed"}))})},e.statistics={setDecimal:function(t,e){var n=Math.pow(10,e||0);return e?Math.round(n*t)/n:t},average:function(t,n){for(var i=t.length,s=0;i--;)s+=t[i];return e.statistics.setDecimal(s/t.length,n)},variance:function(t,n){for(var i=e.statistics.average(t,n),s=t.length,a=0;s--;)a+=Math.pow(t[s]-i,2);return a/=t.length,e.statistics.setDecimal(a,n)},standardDeviation:function(t,n){var i=Math.sqrt(e.statistics.variance(t,n));return e.statistics.setDecimal(i,n)}},e.components.textStatistics={cleanText:function(t){return t.replace(/[,:;()\-]/," ").replace(/[\.!?]/,".").replace(/[ ]*(\n|\r\n|\r)[ ]*/," ").replace(/([\.])[\. ]+/,"$1").replace(/[ ]*([\.])/,"$1").replace(/[ ]+/," ").toLowerCase(); +},sentenceCount:function(t){return t.split(".").length+1},wordCount:function(t){return t.split(" ").length+1},averageWordsPerSentence:function(t){return this.wordCount(t)/this.sentenceCount(t)},averageSyllablesPerWord:function(e){var n=this,i=0,s=n.wordCount(e);return s?(t.each(e.split(" "),function(t,e){i+=n.syllableCount(e)}),i/s):0},syllableCount:function(t){var e=t.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/,"").match(/[aeiouy]{1,2}/g);return e&&0!==e.length?e.length:1}},e.components.video={isVideo:function(e){var n=!1;return t.each(this.providers,function(){e.is(this.selector)&&this.isVideo(e)&&(n=!0)}),n},findVideos:function(e,n){t.each(this.providers,function(i,s){e.find(this.selector).each(function(){var e=t(this);s.isVideo(e)&&s.hasCaptions(e,n)})})},providers:{youTube:{selector:"a, iframe",apiUrl:"http://gdata.youtube.com/feeds/api/videos/?q=%video&caption&v=2&alt=json",isVideo:function(t){return this.getVideoId(t)!==!1?!0:!1},getVideoId:function(t){var e=t.is("iframe")?"src":"href",n=/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&\?]*).*/,i=t.attr(e).match(n); +return i&&11===i[7].length?i[7]:!1},hasCaptions:function(e,n){var i=this.getVideoId(e);t.ajax({url:this.apiUrl.replace("%video",i),async:!1,dataType:"json",success:function(t){n(e,t.feed.openSearch$totalResults.$t>0)}})}},flash:{selector:"object",isVideo:function(e){var n=!1;return 0===e.find("param").length?!1:(e.find("param[name=flashvars]").each(function(){t(this).attr("value").search(/\.(flv|mp4)/i)>-1&&(n=!0)}),n)},hasCaptions:function(e,n){var i=!1;e.find("param[name=flashvars]").each(function(){(t(this).attr("value").search("captions")>-1&&t(this).attr("value").search(".srt")>-1||t(this).attr("value").search("captions.pluginmode")>-1)&&(i=!0)}),n(e,i)}},videoElement:{selector:"video",isVideo:function(t){return t.is("video")},hasCaptions:function(n,i){var s=n.find("track[kind=subtitles], track[kind=captions]");if(!s.length)return void i(n,!1);var a=e.components.language.getDocumentLanguage(n,!0);n.parents("[lang]").length&&(a=n.parents("[lang]").first().attr("lang").split("-")[0]); +var r=!1;return s.each(function(){if(!t(this).attr("srclang")||t(this).attr("srclang").toLowerCase()===a){r=!0;try{var e=t.ajax({url:t(this).attr("src"),type:"HEAD",async:!1,error:function(){}});404===e.status&&(r=!1)}catch(n){}}}),r?void i(n,!0):void i(n,!1)}}}},e.strings.colors={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3", +deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc", +mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"},e.strings.languageCodes=["bh","bi","nb","bs","br","bg","my","es","ca","km","ch","ce","ny","ny","zh","za","cu","cu","cv","kw","co","cr","hr","cs","da","dv","dv","nl","dz","en","eo","et","ee","fo","fj","fi","nl","fr","ff","gd","gl","lg","ka","de","ki","el","kl","gn","gu","ht","ht","ha","he","hz","hi","ho","hu","is","io","ig","id","ia","ie","iu","ik","ga","it","ja","jv","kl","kn","kr","ks","kk","ki","rw","ky","kv","kg","ko","kj","ku","kj","ky","lo","la","lv","lb","li","li","li","ln","lt","lu","lb","mk","mg","ms","ml","dv","mt","gv","mi","mr","mh","ro","ro","mn","na","nv","nv","nd","nr","ng","ne","nd","se","no","nb","nn","ii","ny","nn","ie","oc","oj","cu","cu","cu","or","om","os","os","pi","pa","ps","fa","pl","pt","pa","ps","qu","ro","rm","rn","ru","sm","sg","sa","sc","gd","sr","sn","ii","sd","si","si","sk","sl","so","st","nr","es","su","sw","ss","sv","tl","ty","tg","ta","tt","te","th","bo","ti","to","ts","tn","tr","tk","tw","ug","uk","ur","ug","uz","ca","ve","vi","vo","wa","cy","fy","wo","xh","yi","yo","za","zu"], +e.strings.newWindow=[/new (browser )?(window|frame)/,/popup (window|frame)/],e.strings.placeholders=["title","untitled","untitled document","this is the title","the title","content"," ","new page","new","nbsp"," ","spacer","image","img","photo","frame","frame title","iframe","iframe title","legend"],e.strings.redundant={inputImage:["submit","button"],link:["link to","link","go to","click here","link","click","more"],required:["*"]},e.strings.siteMap=["site map","map","sitemap"],e.strings.skipContent=[/(jump|skip) (.*) (content|main|post)/i],e.strings.suspiciousLinks=["click here","click","more","here","read more","download","add","delete","clone","order","view","read","clic aquí","clic","haga clic","más","aquí","image"],e.strings.symbols=["|","*",/\*/g,"
*","•","•","♦","›","»","‣","▶",".","◦","✓","◽","•","—","◾"],e.KINGStrongList=function(e,n,i){n.get("$scope").find("strong").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected") +});n.add(e),e.set({status:t(this).parent().is("li")?"passed":"failed"})})},e.KINGUseCurrencyAsSymbol=function(e,n,i){function s(s,a){var r=["dollar","euro","pound","franc","krona","rupee","ruble","dinar"],o=new RegExp("\\d{1,}\\s*("+r.join("|")+")\\w*\\b|("+r.join("|")+")\\w*\\b\\s*\\d{1,}","ig"),c=e.getTextContents(t(a)),u=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(u),u.set({status:o.test(c)?"failed":"passed"})}n.get("$scope").find("p").each(s)},e.KINGUseLongDateFormat=function(e,n,i){function s(e,s){var a,r=/\d{1,2}([.\/-])\d{1,2}\1\d{2,4}/g,o=s.childNodes,c=!1,u=[],l=0;for(a=o.length;a>l;l++)o[l].nodeType===Node.TEXT_NODE&&u.push(o[l]);for(l=0;l0,o=i.find("a");o.each(s?a:r)}function a(e,s){var a=t(s),r=s.getAttribute("href"),o=a.find("+ a");if(o.length){var c=o[0].getAttribute("href"),u="passed",l=i({element:s,expected:a.closest(".quail-test").data("expected")});r===c&&(u="failed"),n.add(l),l.set({status:u})}}function r(e,s){var a=i({element:s});n.add(a),a.set({status:"inapplicable",expected:t(s).closest(".quail-test").data("expected")})}n.get("$scope").each(s)},e.aImgAltNotRepetitive=function(e,n,i){n.get("$scope").find("a img[alt]").each(function(){ +var s=n.add(i({element:this})),a=t(this).closest(".quail-test").data("expected");s.set(e.cleanString(t(this).attr("alt"))===e.cleanString(t(this).parent("a").text())?{expected:a,status:"failed"}:{expected:a,status:"passed"})})},e.aInPHasADistinctStyle=function(e,n,i){function s(t){return t.outerWidth()-t.innerWidth()>0||t.outerHeight()-t.innerHeight()>0}function a(e,n){var i=!1,a=["font-weight","font-style"],r=e.css("text-decoration");return"none"!==r&&r!==n.css("text-decoration")&&(i=!0),"rgba(0, 0, 0, 0)"!==e.css("background-color")&&a.push("background"),t.each(a,function(t,s){i||e.css(s)===n.css(s)||(i=!0)}),i||s(e)}function r(t){var e="block"===t.css("display"),n=t.css("position"),i="relative"!==n&&"static"!==n;return e||i}var o=/^([\s|-]|>|<|\\|\/|&(gt|lt);)*$/i;n.get("$scope").each(function(){var e=t(this),s=e.find("p a[href]:visible");s.each(function(){var e=t(this),s=e.closest("p"),c=e.parent(),u=i({element:this,expected:e.closest(".quail-test").data("expected")});n.add(u);var l=e.text().trim(),d=s.clone().find("a[href]").remove().end().text(); +""===l||d.match(o)?u.set("status","inapplicable"):e.css("color")===s.css("color")?u.set("status","passed"):a(e,s)?u.set("status","passed"):r(e)?u.set("status","passed"):e.find("img").length>0?u.set("status","passed"):c.text().trim()===l&&a(c,s)?u.set("status","passed"):u.set("status","failed")})})},e.aLinkTextDoesNotBeginWithRedundantWord=function(e,n,i){n.get("$scope").find("a").each(function(){var s=t(this),a="";t(this).find("img[alt]").length&&(a+=t(this).find("img[alt]:first").attr("alt")),a+=t(this).text(),a=a.toLowerCase();var r;t.each(e.strings.redundant.link,function(t,e){a.search(e)>-1&&(r=n.add(i({element:this,expected:s.closest(".quail-test").data("expected"),status:"failed"})))}),r||n.add(i({element:this,expected:s.closest(".quail-test").data("expected"),status:"passed"}))})},e.aLinkWithNonText=function(e,n,i){n.get("$scope").find("a").each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});if(n.add(s),!t(this).is("a:has(img, object, embed)[href]"))return void s.set({ +status:"inapplicable"});if(!e.isUnreadable(t(this).text()))return void s.set({status:"passed"});var a=0;t(this).find("img, object, embed").each(function(){(t(this).is("img")&&e.isUnreadable(t(this).attr("alt"))||!t(this).is("img")&&e.isUnreadable(t(this).attr("title")))&&a++}),s.set(t(this).find("img, object, embed").length===a?{status:"failed"}:{status:"passed"})})},e.aLinksAreSeparatedByPrintableCharacters=function(e,n,i){n.get("$scope").find("a").each(function(){var s=n.add(i({element:this})),a=t(this).closest(".quail-test").data("expected");t(this).next("a").length&&s.set(e.isUnreadable(t(this).get(0).nextSibling.wholeText)?{expected:a,status:"failed"}:{expected:a,status:"passed"})})},e.aLinksDontOpenNewWindow=function(e,n,i){n.get("$scope").find("a").not("[target=_new], [target=_blank]").each(function(){n.add(i({element:this,expected:t(this).closest(".quail-test").data("expected"),status:"passed"}))}),n.get("$scope").find("a[target=_new], a[target=_blank]").each(function(){var s=t(this),a=!1,r=0,o=s.text()+" "+s.attr("title"),c=""; +do c=e.strings.newWindow[r],o.search(c)>-1&&(a=!0),++r;while(!a&&r-1?{status:"failed"}:{status:"passed"})})},e.animatedGifMayBePresent=function(e,n,i){function s(t,e,n){if("gif"!==e)return void n(!1);var i=new XMLHttpRequest;i.open("GET",t,!0),i.responseType="arraybuffer",i.addEventListener("load",function(){var t=new Uint8Array(i.response),e=0;if(71!==t[0]||73!==t[1]||70!==t[2]||56!==t[3])return void n(!1);for(var s=0;s1)return void n(!0);n(!1)}),i.send()}n.get("$scope").find("img").each(function(){ +var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var a=t(this).attr("src"),r=t(this).attr("src").split(".").pop().toLowerCase();return"gif"!==r?void e.set({status:"inapplicable"}):void s(a,r,function(t){return t?void e.set({status:"cantTell"}):void e.set({status:"inapplicable"})})})},e.appletContainsTextEquivalent=function(e,n,i){n.get("$scope").find('applet[alt=""], applet:not(applet[alt])').each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s),s.set(e.isUnreadable(t(this).text())?{status:"failed"}:{status:"passed"})})},e.ariaOrphanedContent=function(e,n,i){var s=n.get("$scope");s.each(function(){var e=t(this),s=!!e.attr("role"),a=!!e.find("[role]").length;if(!s&&!a)return void n.add(i({expected:e.data("expected"),status:"inapplicable"}));var r=e.find("*:not(*[role] *, *[role], script, meta, link)");r.length?r.each(function(){n.add(i({element:this,expected:t(this).closest(".quail-test").data("expected"), +status:"failed"}))}):n.add(i({expected:e.data("expected"),status:"passed"}))})},e.audioMayBePresent=function(e,n,i){var s=["mp3","m4p","ogg","oga","opus","wav","wma","wv"];n.get("$scope").each(function(){var e=t(this),a=!1;e.find("object, audio").each(function(){a=!0,n.add(i({element:this,expected:t(this).closest(".quail-test").data("expected"),status:"cantTell"}))}),e.find("a[href]").each(function(){var e=t(this),r=e.attr("href").split(".").pop();-1!==t.inArray(r,s)&&(a=!0,n.add(i({element:this,expected:e.closest(".quail-test").data("expected"),status:"cantTell"})))}),a||n.add(i({element:this,status:"inapplicable",expected:t(this).closest(".quail-test").data("expected")}))})},e.blockquoteUseForQuotations=function(e,n,i){n.get("$scope").find("p").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});return n.add(e),t(this).parents("blockquote").length>0?void e.set({status:"inapplicable"}):void e.set(t(this).text().substr(0,1).search(/'|"|«|“|「/)>-1&&t(this).text().substr(-1,1).search(/'|"|»|„|」/)>-1?{ +status:"failed"}:{status:"passed"})})},e.closingTagsAreUsed=function(e,n,i){e.components.htmlSource.getHtml(function(s,a){e.components.htmlSource.traverse(a,function(s){if("tag"===s.type&&t.isArray(s.selector)){var a;a=/#/.test(s.selector.slice(-1)[0])?s.selector.slice(-1)[0]:s.selector.join(" > ");var r=t(a,n.get("$scope")).get(0);r||(r=s.raw||a),n.add(i("undefined"!=typeof s.closingTag||s.closingTag||-1!==e.selfClosingTags.indexOf(s.name.toLowerCase())?{element:r,expected:"object"==typeof r&&1===r.nodeType&&t(r).closest(".quail-test").data("expected")||null,status:"passed"}:{element:r,expected:"object"==typeof r&&1===r.nodeType&&t(r).closest(".quail-test").data("expected")||null,status:"failed"}))}})})},e.colorBackgroundGradientContrast=function(e,n,i,s){function a(t,e,n,i,s){var a,u,l,d=r.getBackgroundGradient(i);if(d){for(var h=0;hh;h++){ +var p=r.testElmBackground(n.algorithm,i,"#"+u.colourAt(h));p||(o(t,e,s,"failed",c,"The background gradient makes the text unreadable"),a=!0)}a||o(t,e,s,"passed",c,"The background gradient does not affect readability")}}var r=e.components.color.colors,o=e.components.color.buildCase,c="colorBackgroundGradientContrast";n.get("$scope").each(function(){for(var r=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),c=[],u=r.iterateNext();u;)e.components.color.textShouldBeTested(u)&&c.push(u.parentNode),u=r.iterateNext();0===c.length&&o(n,i,null,"inapplicable","","There is no text to evaluate"),c.forEach(function(e){a(n,i,s,t(e),e)})})},e.colorBackgroundImageContrast=function(e,n,i,s){function a(t,e,n,i,s){var a=r.getBackgroundImage(i);if(a){var u=document.createElement("img");u.crossOrigin="Anonymous",u.onload=function(){var a=r.getAverageRGB(u),l=r.testElmBackground(n.algorithm,i,a);l?o(t,e,s,"passed",c,"The element's background image does not affect readability"):o(t,e,s,"failed",c,"The element's background image makes the text unreadable"); +},u.onerror=u.onabort=function(){o(t,e,s,"cantTell",c,"The element's background image could not be loaded ("+a+")")},u.src=a}}var r=e.components.color.colors,o=e.components.color.buildCase,c="colorBackgroundImageContrast";n.get("$scope").each(function(){for(var r=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),c=[],u=r.iterateNext();u;)e.components.color.textShouldBeTested(u)&&c.push(u.parentNode),u=r.iterateNext();0===c.length&&o(n,i,null,"inapplicable","","There is no text to evaluate"),c.forEach(function(e){a(n,i,s,t(e),e)})})},e.colorElementBehindBackgroundGradientContrast=function(e,n,i,s){function a(t,e,n,i,s){var a,u;if(i.is("option")||(a=r.getBehindElementBackgroundGradient(i)),a){for(var l=0;ll;l++)u=!r.testElmBackground(n.algorithm,i,"#"+d.colourAt(l)); +u?o(t,e,s,"failed",c,"The background gradient of the element behind this element makes the text unreadable"):o(t,e,s,"passed",c,"The background gradient of the element behind this element does not affect readability")}}var r=e.components.color.colors,o=e.components.color.buildCase,c="colorElementBehindBackgroundGradientContrast";n.get("$scope").each(function(){for(var r=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),c=[],u=r.iterateNext();u;)e.components.color.textShouldBeTested(u)&&c.push(u.parentNode),u=r.iterateNext();0===c.length&&o(n,i,null,"inapplicable","","There is no text to evaluate"),c.forEach(function(e){a(n,i,s,t(e),e)})})},e.colorElementBehindBackgroundImageContrast=function(e,n,i,s){function a(t,e,n,i,s){var a;if(i.is("option")||(a=r.getBehindElementBackgroundImage(i)),a){var u=document.createElement("img");u.crossOrigin="Anonymous",u.onload=function(){var a=r.getAverageRGB(u),l=r.testElmBackground(n.algorithm,i,a); +l?o(t,e,s,"passed",c,"The background image of the element behind this element does not affect readability"):o(t,e,s,"failed",c,"The background image of the element behind this element makes the text unreadable")},u.onerror=u.onabort=function(){o(t,e,s,"cantTell",c,"The background image of the element behind this element could not be loaded ("+a+")")},u.src=a}}var r=e.components.color.colors,o=e.components.color.buildCase,c="colorElementBehindBackgroundImageContrast";n.get("$scope").each(function(){for(var r=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),c=[],u=r.iterateNext();u;)e.components.color.textShouldBeTested(u)&&c.push(u.parentNode),u=r.iterateNext();0===c.length&&o(n,i,null,"inapplicable","","There is no text to evaluate"),c.forEach(function(e){a(n,i,s,t(e),e)})})},e.colorElementBehindContrast=function(e,n,i,s){function a(t,e,n,i,s){var a;if(i.is("option")||(a=r.getBehindElementBackgroundColor(i)),a){var u=r.testElmBackground(n.algorithm,i,a); +u?o(t,e,s,"passed",c,"The element behind this element does not affect readability"):o(t,e,s,"failed",c,"The element behind this element makes the text unreadable")}}var r=e.components.color.colors,o=e.components.color.buildCase,c="colorElementBehindContrast";n.get("$scope").each(function(){for(var r=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),c=[],u=r.iterateNext();u;)e.components.color.textShouldBeTested(u)&&c.push(u.parentNode),u=r.iterateNext();0===c.length&&o(n,i,null,"inapplicable","","There is no text to evaluate"),c.forEach(function(e){a(n,i,s,t(e),e)})})},e.colorFontContrast=function(e,n,i,s){function a(t,e,n,i,s){r.testElmContrast(n.algorithm,i)?o(t,e,s,"passed",c,"The font contrast of the text is sufficient for readability"):o(t,e,s,"failed",c,"The font contrast of the text impairs readability")}var r=e.components.color.colors,o=e.components.color.buildCase,c="colorFontContrast";n.get("$scope").each(function(){ +for(var r=document.evaluate("descendant::text()[normalize-space()]",this,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null),c=[],u=r.iterateNext();u;)e.components.color.textShouldBeTested(u)&&c.push(u.parentNode),u=r.iterateNext();0===c.length&&o(n,i,null,"inapplicable","","There is no text to evaluate"),c.forEach(function(e){a(n,i,s,t(e),e)})})},e.contentPositioningShouldNotChangeMeaning=function(e,n,i){var s=["top","left","right","bottom"],a={},r=!1;n.get("$scope").find("*:has(*:quailCss(position=absolute))").each(function(){a={top:{},left:{},right:{},bottom:{}},r=!1;var e=t(this);e.find("h1, h2, h3, h4, h5, h6, p, blockquote, ol, li, ul, dd, dt").filter(":quailCss(position=absolute)").each(function(){for(var e=0;e2&&!r&&(r=!0,n.add(i({element:e.get(0), +expected:e.closest(".quail-test").data("expected"),status:"failed"})))})})})},e.definitionListsAreUsed=function(e,n,i){n.get("$scope").find("dl").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set({status:"inapplicable"})}),n.get("$scope").find("p, li").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var s=t(this);t(this).find("span, strong, em, b, i").each(function(){if(t(this).text().length<50&&0===s.text().search(t(this).text())){if(t(this).is("span")&&t(this).css("font-weight")===s.css("font-weight")&&t(this).css("font-style")===s.css("font-style"))return void e.set({status:"passed"});e.set({status:"failed"})}})})},e.doNotUseGraphicalSymbolToConveyInformation=function(e,n,i){n.get("$scope").find(e.textSelector+":not(abbr, acronym)").each(function(){var s="✓",a="?xo[]()+-!*xX",r=t(this).text(),o=r.replace(/[\W\s]+/g,"");0===o.length?-1===s.indexOf(r)&&n.add(i({element:this, +expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"})):n.add(i(1===r.length&&a.indexOf(r)>=0?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"passed"}))}),n.get("$scope").find(e.textSelector).filter("abbr, acronym").each(function(){n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"inapplicable"}))})},e.doctypeProvided=function(e,n,i){var s=n.get("$scope").get(0);n.add(i(0!==t(s.doctype).length||document.doctype?{element:s,expected:"pass",status:"passed"}:{element:s,expected:"fail",status:"failed"}))},e.documentAbbrIsUsed=function(t,e,n){t.components.acronym(t,e,n,"abbr")},e.documentAcronymsHaveElement=function(t,e,n){t.components.acronym(t,e,n,"acronym")},e.documentIDsMustBeUnique=function(e,n,i){n.get("$scope").each(function(){0===t(this).children().length&&n.add(i({ +element:this,status:"inapplicable",expected:t(this).closest(".quail-test").data("expected")}))}),n.get("$scope").find(":not([id])").each(function(){n.add(i({element:this,status:"inapplicable",expected:t(this).closest(".quail-test").data("expected")}))}),n.get("$scope").each(function(){var s={};t(this).find("[id]").each(function(){var a=i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this)});n.add(a),"undefined"==typeof s[t(this).attr("id")]&&0===Object.keys(s).length?(a.set({status:"inapplicable"}),s[t(this).attr("id")]=t(this).attr("id")):"undefined"==typeof s[t(this).attr("id")]?(a.set({status:"passed"}),s[t(this).attr("id")]=t(this).attr("id")):a.set({status:"failed"})})})},e.documentIsWrittenClearly=function(e,n,i){n.get("$scope").find(e.textSelector).each(function(){var s=e.components.textStatistics.cleanText(t(this).text()),a=i({element:this,expected:t(this).closest(".quail-test").data("expected")});return n.add(a),e.isUnreadable(s)?void a.set({status:"inapplicable" +}):void a.set(Math.round(206.835-1.015*e.components.textStatistics.averageWordsPerSentence(s)-84.6*e.components.textStatistics.averageSyllablesPerWord(s))<60?{status:"failed"}:{status:"passed"})})},e.documentLangIsISO639Standard=function(e,n,i){var s=n.get("$scope").is("html")?n.get("$scope"):n.get("$scope").find("html").first(),a=i({element:s[0],expected:s.closest(".quail-test").length?s.closest(".quail-test").data("expected"):s.data("expected")}),r=s.attr("lang"),o=!1;n.add(a),s.is("html")&&"undefined"!=typeof r?(t.each(e.strings.languageCodes,function(t,e){o||0!==r.indexOf(e)||(o=!0)}),a.set(o?null===r.match(/^[a-z]{2}(-[A-Z]{2})?$/)?{status:"failed"}:{status:"passed"}:{status:"failed"})):a.set({status:"inapplicable"})},e.documentStrictDocType=function(t,e,n){e.add(n("undefined"!=typeof document.doctype&&document.doctype&&-1!==document.doctype.systemId.search("strict")?{element:document,expected:e.get("$scope").data("expected"),status:"passed"}:{element:document,expected:e.get("$scope").data("expected"), +status:"failed"}))},e.documentTitleIsShort=function(t,e,n){var i=e.get("$scope").find("head title:first"),s=n({element:i,expected:i.closest(".quail-test").data("expected")});return e.add(s),i.length?void s.set({status:i.text().length>150?"failed":"passed"}):void s.set({element:e.get("$scope"),status:"inapplicable"})},e.documentValidatesToDocType=function(){"undefined"==typeof document.doctype},e.documentVisualListsAreMarkedUp=function(e,n,i){var s=["♦","›","»","‣","▶","◦","✓","◽","•","—","◾","-\\D","\\\\","\\*(?!\\*)","\\.\\s","x\\s","•","•",">","[0-9]+\\.","\\(?[0-9]+\\)","[\\u25A0-\\u25FF]","[IVX]{1,5}\\.\\s"],a=RegExp("(^|]*>)[\\s]*("+s.join("|")+")","gi");n.get("$scope").find(e.textSelector).each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var s=t(this).html().match(a);e.set({status:s&&s.length>2?"failed":"passed"})})},e.elementAttributesAreValid=function(e,n,i){e.components.htmlSource.getHtml(function(s,a){ +a&&e.components.htmlSource.traverse(a,function(e){if("undefined"!=typeof e.raw&&t.isArray(e.selector)){var s,a=!1;s=/#/.test(e.selector.slice(-1)[0])?e.selector.slice(-1)[0]:e.selector.join(" > ");var r=t(s,n.get("$scope")).get(0);r||(r=e.raw||s);var o=e.raw.match(/\'|\"/g);o&&o.length%2!==0&&(n.add(i({element:r,expected:"object"==typeof r&&1===r.nodeType&&t(r).closest(".quail-test").data("expected")||null,status:"failed"})),a=!0),e.raw.search(/([a-z]*)=(\'|\")([a-z\.]*)(\'|\")[a-z]/i)>-1&&(n.add(i({element:r,expected:"object"==typeof r&&1===r.nodeType&&t(r).closest(".quail-test").data("expected")||null,status:"failed"})),a=!0);var c=e.raw.split("=");c.shift(),t.each(c,function(){-1===this.search(/\'|\"/)&&this.search(/\s/i)>-1&&(n.add(i({element:r,expected:"object"==typeof r&&1===r.nodeType&&t(r).closest(".quail-test").data("expected")||null,status:"failed"})),a=!0)}),a||n.add(i({element:r,expected:"object"==typeof r&&1===r.nodeType&&t(r).closest(".quail-test").data("expected")||null,status:"passed" +}))}})})},e.elementsDoNotHaveDuplicateAttributes=function(e,n,i){e.components.htmlSource.getHtml(function(s,a){a&&e.components.htmlSource.traverse(a,function(e){if("tag"===e.type&&t.isArray(e.selector)){var s;s=/#/.test(e.selector.slice(-1)[0])?e.selector.slice(-1)[0]:e.selector.join(" > ");var a=t(s,n.get("$scope")).get(0);if(a||(a=e.raw||s),"undefined"!=typeof e.attributes){var r=[];t.each(e.attributes,function(t,e){e.length>1&&r.push(e)}),n.add(i(r.length?{element:a,expected:"object"==typeof a&&1===a.nodeType&&t(a).closest(".quail-test").data("expected")||null,info:r,status:"failed"}:{element:a,expected:"object"==typeof a&&1===a.nodeType&&t(a).closest(".quail-test").data("expected")||null,info:r,status:"passed"}))}}})})},e.embedHasAssociatedNoEmbed=function(e,n,i){n.get("$scope").find("embed").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set({status:t(this).find("noembed").length||t(this).next().is("noembed")?"passed":"failed" +})})},e.emoticonsExcessiveUse=function(e,n,i){n.get("$scope").find(e.textSelector).each(function(){var s=0,a=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(a),t.each(t(this).text().split(" "),function(t,n){n.search(e.emoticonRegex)>-1&&s++}),a.set(0===s?{status:"inapplicable"}:{status:s>4?"failed":"passed"})})},e.emoticonsMissingAbbr=function(e,n,i){n.get("$scope").find(e.textSelector+":not(abbr, acronym)").each(function(){var s=t(this),a=s.clone(),r=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(r),a.find("abbr, acronym").each(function(){t(this).remove()});var o="passed";t.each(a.text().split(" "),function(t,n){n.search(e.emoticonRegex)>-1&&(o="failed")}),r.set({status:o})})},e.focusIndicatorVisible=function(e,n,i){n.get("$scope").find(e.focusElements).each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s);var a={borderWidth:t(this).css("border-width"),borderColor:t(this).css("border-color"), +backgroundColor:t(this).css("background-color"),boxShadow:t(this).css("box-shadow")};if(t(this).focus(),a.backgroundColor.trim()!==t(this).css("background-color").trim())return t(this).blur(),void s.set({status:"passed"});var r=e.components.convertToPx(t(this).css("border-width"));if(r>2&&r!==e.components.convertToPx(a.borderWidth))return t(this).blur(),void s.set({status:"passed"});var o=t(this).css("box-shadow")&&"none"!==t(this).css("box-shadow")?t(this).css("box-shadow").match(/(-?\d+px)|(rgb\(.+\))/g):!1;return o&&t(this).css("box-shadow")!==a.boxShadow&&e.components.convertToPx(o[3])>3?(t(this).blur(),void s.set({status:"passed"})):(t(this).blur(),void s.set({status:"failed"}))})},e.formWithRequiredLabel=function(e,n,i){var s,a=e.strings.redundant,r=!1;a.required[a.required.indexOf("*")]=/\*/g,n.get("$scope").each(function(){var o=t(this);o.find("label").each(function(){var o=t(this).text().toLowerCase(),c=t(this),u=n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t); +}(this)}));for(var l in a.required)o.search(l)>=0&&!n.get("$scope").find("#"+c.attr("for")).attr("aria-required")&&u.set({status:"failed"});r=c.css("color")+c.css("font-weight")+c.css("background-color"),s&&r!==s&&u.set({status:"failed"}),s=r,"undefined"==typeof u.get("status")&&u.set({status:"passed"})})})},e.headerTextIsTooLong=function(e,n,i){var s=128;n.get("$scope").find("h1, h2, h3, h4, h5, h6").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected"),status:t(this).text().replace(/^\s+|\s+$/gm,"").length>s?"failed":"passed"});n.add(e)})},e.headersAttrRefersToATableCell=function(e,n,i){n.get("$scope").find("table").each(function(){var e=this,s=i({element:e,expected:t(this).closest(".quail-test").data("expected")});n.add(s);var a=t(e).find("th[headers], td[headers]");return 0===a.length?void s.set({status:"inapplicable"}):void a.each(function(){var n=t(this).attr("headers").split(/\s+/);t.each(n,function(n,i){return""===i||t(e).find("th#"+i+",td#"+i).length>0?void s.set({ +status:"passed"}):void s.set({status:"failed"})})})})},e.headersUseToMarkSections=function(e,n,i){n.get("$scope").find("p").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var s=t(this);s.find("strong:first, em:first, i:first, b:first").each(function(){e.set({status:s.text().trim()===t(this).text().trim()?"failed":"passed"})})}),n.get("$scope").find("ul, ol").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var s=t(this);if(s.prevAll(":header").length||s.find("li").length!==s.find("li:has(a)").length)return void e.set({status:"passed"});var a=!0;s.find("li:has(a)").each(function(){t(this).text().trim()!==t(this).find("a:first").text().trim()&&(a=!1)}),a&&e.set({status:"failed"})})},e.headersUsedToIndicateMainContent=function(e,n,i){n.get("$scope").each(function(){var s=t(this),a=e.components.content.findContent(s);n.add(i("undefined"==typeof a||0!==a.find(":header").length&&a.find(e.textSelector).first().is(":header")?{ +element:a.get(0),expected:a.closest(".quail-test").data("expected"),status:"passed"}:{element:a.get(0),expected:a.closest(".quail-test").data("expected"),status:"failed"}))})},e.idRefHasCorrespondingId=function(e,n,i){n.get("$scope").find("label[for], *[aria-activedescendant]").each(function(){var e=t(this),s=i({element:this,expected:e.closest(".quail-test").data("expected")});n.add(s);var a=e.attr("for")||e.attr("aria-activedescendant");s.set(0===n.get("$scope").find("#"+a).length?{status:"failed"}:{status:"passed"})})},e.idrefsHasCorrespondingId=function(e,n,i){function s(e){var n=[],i=["headers","aria-controls","aria-describedby","aria-flowto","aria-labelledby","aria-owns"];return t.each(i,function(t,i){var s=e.attr(i);return"undefined"!=typeof s&&s!==!1?void(n=s):void 0}),n.split(/\s+/)}n.get("$scope").each(function(){var e=t(this).find("td[headers], th[headers], [aria-controls], [aria-describedby], [aria-flowto], [aria-labelledby], [aria-owns]");return 0===e.length?void n.add(i({element:this, +expected:t(this).closest(".quail-test").data("expected"),status:"inapplicable"})):void e.each(function(){var e=this,a=n.add(i({element:this,expected:t(this).closest(".quail-test").data("expected")})),r=s(t(e)),o="passed";t.each(r,function(e,n){return""!==n&&0===t("#"+n).length?void(o="failed"):void 0}),a.set({status:o})})})},e.imgAltIsDifferent=function(e,n,i){n.get("$scope").find("img:not([src])").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected"),status:"inapplicable"});n.add(e)}),n.get("$scope").find("img[alt][src]").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(t(this).attr("src")===t(this).attr("alt")||t(this).attr("src").split("/").pop()===t(this).attr("alt")?{status:"failed"}:{status:"passed"})})},e.imgAltIsTooLong=function(e,n,i){n.get("$scope").find("img[alt]").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set({ +status:t(this).attr("alt").length>100?"failed":"passed"})})},e.imgAltNotEmptyInAnchor=function(e,n,i){n.get("$scope").find("a[href]:has(img)").each(function(){var s=t(this),a=s.text(),r=i({element:this,expected:s.closest(".quail-test").data("expected")});n.add(r),s.find("img[alt]").each(function(){a+=" "+t(this).attr("alt")}),r.set(e.isUnreadable(a)?{status:"failed"}:{status:"passed"})})},e.imgAltTextNotRedundant=function(e,n,i){var s={};n.get("$scope").find("img[alt]").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),"undefined"==typeof s[t(this).attr("alt")]?s[t(this).attr("alt")]=t(this).attr("src"):e.set(s[t(this).attr("alt")]!==t(this).attr("src")?{status:"failed"}:{status:"passed"})})},e.imgGifNoFlicker=function(e,n,i){n.get("$scope").find('img[src$=".gif"]').each(function(){var e=t(this),s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s),t.ajax({url:e.attr("src"),dataType:"text",success:function(t){ +s.set(-1!==t.search("NETSCAPE2.0")?{status:"failed"}:{status:"inapplicable"})}})})},e.imgHasLongDesc=function(e,n,i){n.get("$scope").find("img[longdesc]").each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s),s.set(t(this).attr("longdesc")!==t(this).attr("alt")&&e.validURL(t(this).attr("longdesc"))?{status:"passed"}:{status:"failed"})})},e.imgImportantNoSpacerAlt=function(e,n,i){n.get("$scope").find("img[alt]").each(function(){var s=t(this).width()?t(this).width():parseInt(t(this).attr("width"),10),a=t(this).height()?t(this).height():parseInt(t(this).attr("height"),10),r=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(r),r.set(e.isUnreadable(t(this).attr("alt").trim())&&t(this).attr("alt").length>0&&s>50&&a>50?{status:"failed"}:{status:"passed"})})},e.imgMapAreasHaveDuplicateLink=function(e,n,i){var s={};n.get("$scope").find("a").each(function(){s[t(this).attr("href")]=t(this).attr("href")}),n.get("$scope").find("img[usemap]").each(function(){ +var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var a=t(this),r=n.get("$scope").find(a.attr("usemap"));r.length||(r=n.get("$scope").find('map[name="'+a.attr("usemap").replace("#","")+'"]')),r.length?r.find("area").each(function(){e.set("undefined"==typeof s[t(this).attr("href")]?{status:"failed"}:{status:"passed"})}):e.set({status:"inapplicable"})})},e.imgNonDecorativeHasAlt=function(e,n,i){n.get("$scope").find("img[alt]").each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s),s.set(e.isUnreadable(t(this).attr("alt"))&&(t(this).width()>100||t(this).height()>100)?{status:"failed"}:{status:"passed"})})},e.imgWithMathShouldHaveMathEquivalent=function(e,n,i){n.get("$scope").find("img:not(img:has(math), img:has(tagName))").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),t(this).parent().find("math").length||e.set({status:"failed"})})},e.inputCheckboxRequiresFieldset=function(e,n,i){ +n.get("$scope").find(":checkbox").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(t(this).parents("fieldset").length?{status:"passed"}:{status:"failed"})})},e.inputImageAltIsNotFileName=function(e,n,i){n.get("$scope").find("input[type=image][alt]").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(t(this).attr("src")===t(this).attr("alt")?{status:"failed"}:{status:"passed"})})},e.inputImageAltIsShort=function(e,n,i){n.get("$scope").find("input[type=image]").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(t(this).attr("alt").length>100?{status:"failed"}:{status:"passed"})})},e.inputImageAltNotRedundant=function(e,n,i){n.get("$scope").find("input[type=image][alt]").each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s),s.set(e.strings.redundant.inputImage.indexOf(e.cleanString(t(this).attr("alt")))>-1?{ +status:"failed"}:{status:"passed"})})},e.inputWithoutLabelHasTitle=function(e,n,i){n.get("$scope").each(function(){var s=t(this).find("input, select, textarea");if(0===s.length){var a=i({element:this,expected:t(this).closest(".quail-test").data("expected"),status:"inapplicable"});return void n.add(a)}s.each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});return n.add(s),"none"===t(this).css("display")?void s.set({status:"inapplicable"}):void s.set(n.get("$scope").find("label[for="+t(this).attr("id")+"]").length||t(this).attr("title")&&!e.isUnreadable(t(this).attr("title"))?{status:"passed"}:{status:"failed"})})})},e.labelMustBeUnique=function(e,n,i){var s={};n.get("$scope").find("label[for]").each(function(){"undefined"==typeof s[t(this).attr("for")]&&(s[t(this).attr("for")]=0),s[t(this).attr("for")]++}),n.get("$scope").find("label[for]").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected"),status:1===s[t(this).attr("for")]?"passed":"failed" +});n.add(e)})},e.labelsAreAssignedToAnInput=function(e,n,i){n.get("$scope").find("label").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(t(this).attr("for")&&n.get("$scope").find("#"+t(this).attr("for")).length&&n.get("$scope").find("#"+t(this).attr("for")).is(":input")?{status:"passed"}:{status:"failed"})})},e.languageChangesAreIdentified=function(e,n,i){var s,a,r,o,c,u,l=n.get("$scope"),d=e.components.language.getDocumentLanguage(l,!0),h=function(n,i,s,a){var r,o=n.find("[lang="+i+"]");return 0===o.length?!0:(s=s.length,o.each(function(){r=e.getTextContents(t(this)).match(a),r&&(s-=r.length)}),s>0)},p=function(t){return t.attr("lang")?t.attr("lang").trim().toLowerCase().split("-")[0]:t.parents("[lang]").length?t.parents("[lang]:first").attr("lang").trim().toLowerCase().split("-")[0]:e.components.language.getDocumentLanguage(l,!0)};l.find(e.textSelector).each(function(){c=this,o=t(this),d=p(o),s=e.getTextContents(o),u=!1, +t.each(e.components.language.scriptSingletons,function(t,a){t!==d&&(r=s.match(a),r&&r.length&&h(o,t,r,a)&&(n.add(i({element:c,expected:function(t){return e.components.resolveExpectation(t)}(c),info:{language:t},status:"failed"})),u=!0))}),t.each(e.components.language.scripts,function(t,l){-1===l.languages.indexOf(d)&&(r=s.match(l.regularExpression),r&&r.length&&h(o,t,r,a)&&(n.add(i({element:c,expected:function(t){return e.components.resolveExpectation(t)}(c),info:{language:t},status:"failed"})),u=!0))}),"undefined"!=typeof guessLanguage&&!o.find("[lang]").length&&o.text().trim().length>400&&guessLanguage.info(o.text(),function(t){t[0]!==d&&(n.add(i({element:c,expected:function(t){return e.components.resolveExpectation(t)}(c),info:{language:t[0]},status:"failed"})),u=!0)}),u||n.add(i({element:c,expected:function(t){return e.components.resolveExpectation(t)}(c),status:"passed"}))})},e.languageDirAttributeIsUsed=function(e,n,i){function s(){var s=t(this),r=s.attr("dir");if(!r){var o=s.closest("[dir]").attr("dir"); +r=o||r}"string"==typeof r&&(r=r.toLowerCase()),"undefined"==typeof a[r]&&(r="ltr");var c="ltr"===r?"rtl":"ltr",u=e.getTextContents(s),l=u.match(a[c]);if(l){var d=l.length;s.find("[dir="+c+"]").each(function(){var t=s[0].textContent.match(a[c]);t&&(d-=t.length)});var h=n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this)}));h.set({status:d>0?"failed":"passed"})}}var a=e.components.language.textDirection;n.get("$scope").each(function(){t(this).find(e.textSelector).each(s)})},e.languageDirectionPunctuation=function(e,n,i){var s=n.get("$scope"),a={},r=/[\u2000-\u206F]|[!"#$%&'\(\)\]\[\*+,\-.\/:;<=>?@^_`{|}~]/gi,o=s.attr("dir")?s.attr("dir").toLowerCase():"ltr",c="ltr"===o?"rtl":"ltr",u=e.components.language.textDirection;s.each(function(){var s=t(this);s.find(e.textSelector).each(function(){var s=t(this);o=s.attr("dir")?s.attr("dir").toLowerCase():s.parent("[dir]").first().attr("dir")?s.parent("[dir]").first().attr("dir").toLowerCase():o,"undefined"==typeof u[o]&&(o="ltr"), +c="ltr"===o?"rtl":"ltr";var l=e.getTextContents(s),d=l.match(u[c]),h=n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this)}));if(!d)return void h.set({status:"inapplicable"});for(var p=l.search(u[c]),f=l.lastIndexOf(d.pop());a=r.exec(l);)if(a.index===p-1||a.index===f+1)return void h.set({status:"failed"});h.set({status:"passed"})})})},e.languageUnicodeDirection=function(e,n,i){var s=n.get("$scope"),a=e.components.language.textDirection,r=e.components.language.textDirectionChanges;s.each(function(){var s=t(this);s.find(e.textSelector).each(function(){var s=n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this)})),o=t(this),c=o.text().trim(),u=-1!==c.substr(0,1).search(a.ltr)?"rtl":"ltr";s.set(-1===c.search(a[u])?{status:"inapplicable"}:-1!==c.search(r[u])?{status:"passed"}:{status:"failed"})})})},e.linkHasAUniqueContext=function(e,n,i){function s(e){for(var n=t(e),i=n,s=a(n.text());!i.is("body, html")&&-1===u.indexOf(i.css("display"));)i=i.parent(); +var r=i.text().match(/[^\.!\?]+[\.!\?]+/g);null===r&&(r=[i.text()]);for(var o=0;o0)return!1}return r(s(e),s(n))?!1:!0}function c(t){var e=t.text();return t.find("img[alt]").each(function(){e+=" "+this.alt.trim()}),a(e)}var u=["block","flex","list-item","table","table-caption","table-cell"];n.get("$scope").each(function(){var e=t(this),s=e.find("a[href]:visible"),a={}; +if(0===s.length){var r=i({element:this,status:"inapplicable",expected:e.closest(".quail-test").data("expected")});n.add(r)}s.each(function(){var e=c(t(this));"undefined"==typeof a[e]&&(a[e]=[]),a[e].push(this)}),t.each(a,function(e,s){for(;s.length>1;){for(var a=s.pop(),r=!1,c=s.length-1;c>=0;c-=1){var u=s[c];o(a,u)&&(r=!0,s.splice(c,1),n.add(i({element:u,status:"failed",expected:t(u).closest(".quail-test").data("expected")})))}n.add(i({element:a,status:r?"failed":"passed",expected:t(a).closest(".quail-test").data("expected")}))}1===s.length&&n.add(i({element:s[0],status:"passed",expected:t(s[0]).closest(".quail-test").data("expected")}))})})},e.listNotUsedForFormatting=function(e,n,i){n.get("$scope").find("ol, ul").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(t(this).find("li").length<2?{status:"failed"}:{status:"passed"})})},e.listOfLinksUseList=function(e,n,i){var s=/(♦|›|»|‣|▶|.|◦|>|✓|◽|•|—|◾|\||\*|•|•)/g;n.get("$scope").find("a").each(function(){ +var a=n.add(i({element:this})),r=t(this).closest(".quail-test").data("expected");if(t(this).next("a").length){var o=t(this).get(0).nextSibling.wholeText.replace(s,"");a.set(!t(this).parent("li").length&&e.isUnreadable(o)?{expected:r,status:"failed"}:{expected:r,status:"passed"})}})},e.newWindowIsOpened=function(e,n,i){var s,a=window.open;window.open=function(t){n.each(function(e,n){var i=n.get("element").href;i.indexOf(t)>-1&&n.set("status","failed")})},n.get("$scope").find("a").each(function(){s=i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this)}),n.add(s),t(this).trigger("click")}),window.open=a},e.pNotUsedAsHeader=function(e,n,i){n.get("$scope").find("p").each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s),t(this).text().search(".")>=1&&s.set({status:"inapplicable"});var a=!1;if(t(this).text().search(".")<1){var r=t(this),o=r.prev("p");t.each(e.suspectPHeaderTags,function(e,n){r.find(n).length&&r.find(n).each(function(){ +t(this).text().trim()===r.text().trim()&&(s.set({status:"failed"}),a=!0)})}),o.length&&t.each(e.suspectPCSSStyles,function(t,e){return r.css(e)!==o.css(e)?(s.set({status:"failed"}),a=!0,!1):void 0}),"bold"===r.css("font-weight")&&(s.set({status:"failed"}),a=!0)}a||s.set({status:"passed"})})},e.paragraphIsWrittenClearly=function(e,n,i){n.get("$scope").find("p").each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s);var a=e.components.textStatistics.cleanText(t(this).text());s.set(Math.round(206.835-1.015*e.components.textStatistics.averageWordsPerSentence(a)-84.6*e.components.textStatistics.averageSyllablesPerWord(a))<60?{status:"failed"}:{status:"passed"})})},e.preShouldNotBeUsedForTabularLayout=function(e,n,i){n.get("$scope").find("pre").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var s=t(this).text().split(/[\n\r]+/);e.set({status:s.length>1&&t(this).text().search(/\t/)>-1?"failed":"passed" +})})},e.scriptFocusIndicatorVisible=function(){e.html.find(e.focusElements).each(function(){var n,i,s,a;s=[];for(var r=0,o=document.styleSheets.length;o>r;++r){n=document.styleSheets[r],i=n.cssRules||n.rules;for(var c=i.length-1;c>=0;--c)a=i[c],a.selectorText&&-1!==a.selectorText.indexOf(":focus")&&(s.push({css:a.cssText,index:c,sheet:r}),n.deleteRule(c))}var u={borderWidth:t(this).css("border-width"),borderColor:t(this).css("border-color"),backgroundColor:t(this).css("background-color"),boxShadow:t(this).css("box-shadow"),outlineWidth:t(this).css("outline-width"),outlineColor:t(this).css("outline-color")};t(this).focus();var l=e.components.convertToPx(t(this).css("outline-width"));if(l>2&&l!==e.components.convertToPx(u.outlineWidth))return void t(this).blur();if(u.backgroundColor!==t(this).css("background-color"))return void t(this).blur();var d=e.components.convertToPx(t(this).css("border-width"));if(d>2&&d!==e.components.convertToPx(u.borderWidth))return void t(this).blur();var h=t(this).css("box-shadow")&&"none"!==t(this).css("box-shadow")?t(this).css("box-shadow").match(/(-?\d+px)|(rgb\(.+\))/g):!1; +if(h&&t(this).css("box-shadow")!==u.boxShadow&&e.components.convertToPx(h[3])>3)return void t(this).blur();t(this).blur();for(var p,f=s.length-1;f>=0;--r)p=s[f],document.styleSheets[p.sheet].insertRule(p.css,p.index);e.testFails("scriptFocusIndicatorVisible",t(this))})},e.selectJumpMenu=function(e,n,i){var s=n.get("$scope");0!==s.find("select").length&&s.find("select").each(function(){n.add(i(0===t(this).parent("form").find(":submit").length&&e.components.hasEventListener(t(this),"change")?{element:this,expected:t(this).closest(".quail-test").data("expected"),status:"failed"}:{element:this,expected:t(this).closest(".quail-test").data("expected"),status:"passed"}))})},e.siteMap=function(e,n,i){var s=!0,a=i({element:n.get("$scope").get(0),expected:n.get("$scope").data("expected")});n.add(a),n.get("$scope").find("a").each(function(){if("passed"!==a.get("status")){var n=t(this).text().toLowerCase();return t.each(e.strings.siteMap,function(t,e){return n.search(e)>-1?void(s=!1):void 0}),s===!1?void a.set({ +status:"failed"}):void(s&&a.set({status:"passed"}))}})},e.skipToContentLinkProvided=function(e,n,i){n.get("$scope").each(function(){var s=t(this),a=!1;s.find('a[href*="#"]').each(function(){if(!a)for(var r=t(this),o=r.attr("href").split("#").pop(),c=s.find("#"+o),u=e.strings.skipContent.slice();!a&&u.length;){var l=u.pop();if(r.text().search(l)>-1&&c.length){if(r.focus(),r.is(":visible")&&"hidden"!==r.css("visibility"))return a=!0,void n.add(i({element:r.get(0),expected:r.closest(".quail-test").data("expected"),status:"passed"}));r.blur()}}}),a||n.add(i({expected:s.data("expected")||s.find("[data-expected]").data("expected"),status:"failed"}))})},e.tabIndexFollowsLogicalOrder=function(e,n,i){n.get("$scope").each(function(){var s=t(this),a=0;s.find("[tabindex]").each(function(){var s=t(this),r=s.attr("tabindex");n.add(i(parseInt(r,10)>=0&&parseInt(r,10)!==a+1?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}:{element:this,expected:function(t){ +return e.components.resolveExpectation(t)}(this),status:"passed"})),a++})})},e.tableAxisHasCorrespondingId=function(e,n,i){n.get("$scope").find("[axis]").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(0===t(this).parents("table").first().find("th#"+t(this).attr("axis")).length?{status:"failed"}:{status:"passed"})})},e.tableHeaderLabelMustBeTerse=function(e,n,i){n.get("$scope").find("th, table tr:first td").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e),e.set(t(this).text().length>20&&(!t(this).attr("abbr")||t(this).attr("abbr").length>20)?{status:"failed"}:{status:"passed"})})},e.tableLayoutDataShouldNotHaveTh=function(e,n,i){n.get("$scope").find("table").each(function(){var s=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(s),s.set(0!==t(this).find("th").length?e.isDataTable(t(this))?{status:"passed"}:{status:"failed"}:{status:"inapplicable" +})})},e.tableLayoutHasNoCaption=function(e,n,i){n.get("$scope").find("table").each(function(){n.add(i(t(this).find("caption").length?e.isDataTable(t(this))?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"passed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"inapplicable"}))})},e.tableLayoutHasNoSummary=function(e,n,i){n.get("$scope").each(function(){var s=t(this);s.find("table[summary]").each(function(){var s=n.add(i({element:this,expected:t(this).closest(".quail-test").data("expected")}));s.set(e.isDataTable(t(this))||e.isUnreadable(t(this).attr("summary"))?{status:"passed"}:{status:"failed"})})})},e.tableLayoutMakesSenseLinearized=function(e,n,i){n.get("$scope").find("table").each(function(){e.isDataTable(t(this))||n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t); +}(this),status:"failed"}))})},e.tableNotUsedForLayout=function(e,n,i){n.get("$scope").find("table").each(function(){n.add(i(e.isDataTable(t(this))?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"passed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}))})},e.tableShouldUseHeaderIDs=function(e,n,i){n.get("$scope").find("table").each(function(){var s=t(this),a=!1;e.isDataTable(s)&&(s.find("th").each(function(){a||t(this).attr("id")||(a=!0,n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"})))}),a||s.find("td[header]").each(function(){a||t.each(t(this).attr("header").split(" "),function(t,r){s.find("#"+r).length||(a=!0,n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"})))})}))})},e.tableSummaryDoesNotDuplicateCaption=function(e,n,i){n.get("$scope").find("table[summary]:has(caption)").each(function(){ +n.add(i(e.cleanString(t(this).attr("summary"))===e.cleanString(t(this).find("caption:first").text())?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"passed"}))})},e.tableSummaryIsNotTooLong=function(e,n,i){n.get("$scope").find("table[summary]").each(function(){t(this).attr("summary").trim().length>100&&n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}))})},e.tableUseColGroup=function(e,n,i){n.get("$scope").find("table").each(function(){e.isDataTable(t(this))&&!t(this).find("colgroup").length&&n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}))})},e.tableUsesAbbreviationForHeader=function(e,n,i){n.get("$scope").find("th:not(th[abbr])").each(function(){t(this).text().length>20&&n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t); +}(this),status:"failed"}))})},e.tableUsesScopeForRow=function(e,n,i){n.get("$scope").find("table").each(function(){t(this).find("td:first-child").each(function(){var s=t(this).next("td");("bold"===t(this).css("font-weight")&&"bold"!==s.css("font-weight")||t(this).find("strong").length&&!s.find("strong").length)&&n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}))}),t(this).find("td:last-child").each(function(){var s=t(this).prev("td");("bold"===t(this).css("font-weight")&&"bold"!==s.css("font-weight")||t(this).find("strong").length&&!s.find("strong").length)&&n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}))})})},e.tableWithMoreHeadersUseID=function(e,n,i){n.get("$scope").find("table:has(th)").each(function(){var s=t(this),a=0;s.find("tr").each(function(){t(this).find("th").length&&a++,a>1&&!t(this).find("th[id]").length&&n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t); +}(this),status:"failed"}))})})},e.tabularDataIsInTable=function(e,n,i){n.get("$scope").find("pre").each(function(){n.add(i(t(this).html().search(" ")>=0?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"passed"}))})},e.tagsAreNestedCorrectly=function(t,e,n){t.components.htmlSource.getHtml(function(i){var s=t.components.htmlTagValidator(i),a=n({expected:e.get("$scope").filter(".quail-test").eq(0).data("expected")});e.add(a),a.set(s?{status:"failed",html:s}:{status:"passed"})})},e.textIsNotSmall=function(e,n,i){n.get("$scope").find(e.textSelector).each(function(){var s=t(this).css("font-size");s.search("em")>0&&(s=e.components.convertToPx(s)),s=parseInt(s.replace("px",""),10),n.add(i(10>s?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t); +}(this),status:"passed"}))})},e.userInputMayBeRequired=function(e,n,i){n.get("$scope").each(function(){var e=i({element:this,expected:t(this).closest(".quail-test").data("expected")});n.add(e);var s=t(this).find("form"),a=0,r=t(this).find("input:not(form input, [type=button],[type=reset],[type=image],[type=submit],[type=hidden])");return s.each(function(){var e=t(this).find("input:not([type=button],[type=reset],[type=image],[type=submit],[type=hidden])");e.length>1&&(a=e.length)}),a>0?void e.set({status:"cantTell"}):r.length>1?void e.set({status:"cantTell"}):void e.set({status:"inapplicable"})})},e.videoMayBePresent=function(e,n,i){var s=["webm","flv","ogv","ogg","avi","mov","qt","wmv","asf","mp4","m4p","m4v","mpg","mp2","mpeg","mpg","mpe","mpv","m2v","3gp","3g2"],a=["//www.youtube.com/embed/","//player.vimeo.com/video/"];n.get("$scope").each(function(){var e=t(this),r=!1;e.find("object, video").each(function(){r=!0,n.add(i({element:this,expected:t(this).closest(".quail-test").data("expected"), +status:"cantTell"}))}),e.find("a[href]").each(function(){var e=t(this),a=e.attr("href").split(".").pop();-1!==t.inArray(a,s)&&(r=!0,n.add(i({element:this,expected:e.closest(".quail-test").data("expected"),status:"cantTell"})))}),e.find("iframe").each(function(){(-1!==this.src.indexOf(a[0])||-1!==this.src.indexOf(a[1]))&&(r=!0,n.add(i({element:this,expected:e.closest(".quail-test").data("expected"),status:"cantTell"})))}),r||n.add(i({element:this,status:"inapplicable",expected:t(this).closest(".quail-test").data("expected")}))})},e.videosEmbeddedOrLinkedNeedCaptions=function(t,e,n){t.components.video.findVideos(e.get("$scope"),function(i,s){e.add(n(s?{element:i[0],expected:function(e){return t.components.resolveExpectation(e)}(i),status:"passed"}:{element:i[0],expected:function(e){return t.components.resolveExpectation(e)}(i),status:"failed"}))})},e.whiteSpaceInWord=function(e,n,i){var s,a;n.get("$scope").find(e.textSelector).each(function(){a=t(this).text()?t(this).text().match(/[^\s\\]/g):!1, +s=t(this).text()?t(this).text().match(/[^\s\\]\s[^\s\\]/g):!1,n.add(i(a&&s&&s.length>3&&s.length>=a.length/2-2?{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"failed"}:{element:this,expected:function(t){return e.components.resolveExpectation(t)}(this),status:"passed"}))})},e.whiteSpaceNotUsedForFormatting=function(e,n,i){n.get("$scope").find(e.textSelector).each(function(){var s=n.add(i({element:this,expected:function(t){return e.components.resolveExpectation(t)}(this)}));if(0===t(this).find("br").length)return void s.set({status:"passed"});var a=t(this).html().toLowerCase().split(/()+/),r=0;t.each(a,function(t,e){-1!==e.search(/(\s|\ ){2,}/g)&&r++}),s.set(r>1?{status:"failed"}:{status:"cantTell"})})},e.lib.Case=function(){function e(t){return new e.fn.init(t)}return e.fn=e.prototype={constructor:e,init:function(t){this.listeners={},this.timeout=null,this.attributes=t||{};var e=this;return this.attributes.status&&"untested"!==this.attributes.status?setTimeout(function(){ +e.resolve()},0):(this.attributes.status="untested",this.timeout=setTimeout(function(){e.giveup()},350)),this},attributes:null,get:function(t){return this.attributes[t]},set:function(t,e){var n=!1;if("object"==typeof t)for(var i in t)t.hasOwnProperty(i)&&("status"===i&&(n=!0),this.attributes[i]=t[i]);else"status"===t&&(n=!0),this.attributes[t]=e;return n&&this.resolve(),this},hasStatus:function(t){"object"!=typeof t&&(t=[t]);for(var e=this.get("status"),n=0,i=t.length;i>n;++n)if(t[n]===e)return!0;return!1},resolve:function(){clearTimeout(this.timeout);var t,e=this.attributes.element;e&&e.nodeType&&1===e.nodeType&&(this.attributes.selector=this.defineUniqueSelector(e),this.attributes.html||(this.attributes.html="","HTML"===e.nodeName||"BODY"===e.nodeName?this.attributes.html="<"+e.nodeName+">":"string"==typeof e.outerHTML&&(t=e.outerHTML.trim().replace(/(\r\n|\n|\r)/gm,"").replace(/>\s+<"),t.length>200&&(t=t.substr(0,200)+"... [truncated]"),this.attributes.html=t))),this.dispatch("resolve",this); +},giveup:function(){clearTimeout(this.timeout),this.attributes.status="notTested",this.dispatch("timeout",this)},listenTo:function(t,e,n){n=n.bind(this),t.registerListener.call(t,e,n)},registerListener:function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},dispatch:function(t){if(this.listeners[t]&&this.listeners[t].length){var e=[].slice.call(arguments);this.listeners[t].forEach(function(t){t.apply(null,e)})}},defineUniqueSelector:function(e){function n(e){return 1===t(e).length}function i(t){var e="",n=t.id||"";return n.length>0&&(e="#"+n),e}function s(t){var e="",n=t.className||"";return n.length>0&&(n=n.split(/\s+/),n=o(n,function(t){return/active|enabled|disabled|first|last|only|collapsed|open|clearfix|processed/.test(t)}),n.length>0)?"."+n.join("."):e}function a(t){var e,n="",i=["href","type"];if("undefined"==typeof t||"undefined"==typeof t.attributes||null===t.attributes)return n;for(var s=0,a=i.length;a>s;s++)e=t.attributes[i[s]]&&t.attributes[i[s]].value, +e&&(n+="["+i[s]+'="'+e+'"]');return n}function r(t){var e="",r="",o=!1,c=!0;do{if(r="",(r=i(t)).length>0){e=r+" "+e;break}!o&&(r=s(t)).length>0&&(e=r+" "+e,n(e)&&(o=!0)),c&&((r=a(t)).length>0&&(e=r+e),e=t.nodeName.toLowerCase()+e,c=!1),t=t.parentNode}while(t&&1===t.nodeType&&"BODY"!==t.nodeName&&"HTML"!==t.nodeName);return e.trim()}function o(t,e){for(var n=[],i=0,s=t.length;s>i;i++)e.call(null,t[i])||n.push(t[i]);return n}return e&&r(e)},push:[].push,sort:[].sort,concat:[].concat,splice:[].splice},e.fn.init.prototype=e.fn,e}(),e.lib.Section=function(){function t(e,n){return new t.fn.init(e,n)}return t.fn=t.prototype={constructor:t,init:function(t,n){if(!t)return this;if(this.id=t,n.techniques&&n.techniques.length){for(var i=0,s=n.techniques.length;s>i;++i)this.push(e.lib.Technique(n.techniques[i]));return this}return this},length:0,each:function(t){for(var e=[].slice.call(arguments,1),n=0,i=this.length;i>n;++n)e.unshift(this[n]),e.unshift(n),t.apply(this[n],e);return this},find:function(t){ +for(var e=0,n=this.length;n>e;++e)if(this[e].get("name")===t)return this[e];return null},set:function(t,n){for(var i=0,s=this.length;s>i;++i)if(this[i].get("name")===t)return this[i].set(n),this[i];var a=e.lib.Test(t,n);return this.push(a),a},addTechnique:function(t){this.push(t)},regiterTechniqueTestResult:function(){},push:[].push,sort:[].sort,splice:[].splice},t.fn.init.prototype=t.fn,t}(),e.lib.SuccessCriteria=function(){function n(t){return new n.fn.init(t)}function i(t){return Object.keys(t).length}return n.fn=n.prototype={constructor:n,init:function(t){return this.listeners={},this.attributes=this.attributes||{},this.attributes.status="untested",this.attributes.results={},this.attributes.totals={},this.set(t||{}),this},length:0,attributes:null,get:function(e){if("$scope"===e){var n=this.attributes.scope,i=t(this.attributes.scope);return this.attributes[e]?this.attributes[e]:n?i:t(document)}return this.attributes[e]},set:function(t,e){var n=!1;if("object"==typeof t)for(var i in t)t.hasOwnProperty(i)&&("status"===i&&(n=!0), +this.attributes[i]=t[i]);else this.attributes[t]=e;return this},each:function(t){for(var e=[].slice.call(arguments,1),n=0,i=this.length;i>n;++n){e.unshift(this[n]),e.unshift(n);var s=t.apply(this[n],e);if(s===!1)break}return this},add:function(t){this.find(t.get("selector"))||this.push(t)},find:function(t){for(var e=0,n=this.length;n>e;++e)if(this[e].get("selector")===t)return this[e];return null},registerTests:function(t){var e=this.get("preEvaluator"),n="undefined"!=typeof e,i=!0;n&&(i=e.call(this,t)),i||this.set("status","inapplicable"),this.set("tests",t),this.listenTo(t,"complete",this.evaluate)},filterTests:function(t){var n=new e.lib.TestCollection,i=this.get("name");if(!i)throw new Error("Success Criteria instances require a name in order to have tests filtered.");var s=i.split(":")[1];return t.each(function(t,e){var i=e.getGuidelineCoverage("wcag");for(var a in i)i.hasOwnProperty(a)&&a===s&&n.add(e)}),n},addConclusion:function(t,n){this.get("results")[t]||(this.get("results")[t]=e.lib.Test()), +this.get("results")[t].push(n),this.get("totals")[t]||(this.get("totals")[t]=0),++this.get("totals")[t],this.get("totals").cases||(this.get("totals").cases=0),++this.get("totals").cases},evaluate:function(t,e){if("inapplicable"!==this.get("status")){var n=this,s=this.filterTests(e);0===s.length?this.set("status","noTestCoverage"):(s.each(function(t,e){e.each(function(t,e){n.addConclusion(e.get("status"),e)})}),0===i(this.get("results"))?this.set("status","noResults"):this.set("status","tested"))}this.report()},report:function(){var t=Array.prototype.slice.call(arguments);t=[].concat(["successCriteriaEvaluated",this,this.get("tests")],t),this.dispatch.apply(this,t)},listenTo:function(t,e,n){n=n.bind(this),t.registerListener.call(t,e,n)},registerListener:function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},dispatch:function(t){if(this.listeners[t]&&this.listeners[t].length){var e=[].slice.call(arguments);this.listeners[t].forEach(function(t){t.apply(null,e)}); +}},push:[].push,sort:[].sort,splice:[].splice},n.fn.init.prototype=n.fn,n}(),e.lib.Technique=function(){function t(e,n){return new t.fn.init(e,n)}return t.fn=t.prototype={constructor:t,init:function(t,e){return this.listeners={},t?(this.attributes=e||{},this.attributes.name=t,this):this},length:0,attributes:{},each:function(t){for(var e=[].slice.call(arguments,1),n=0,i=this.length;i>n;++n)e.unshift(this[n]),e.unshift(n),t.apply(this[n],e);return this},get:function(t){return this.attributes[t]},set:function(t,e){if("object"==typeof t)for(var n in t)t.hasOwnProperty(n)&&(this.attributes[n]=t[n]);else this.attributes[t]=e;return this},addTest:function(){},report:function(t,e){window.console&&window.console.log(this.get("name"),e.status,e,e[0]&&e[0].status)},listenTo:function(t,e,n){n=n.bind(this),t.registerListener.call(t,e,n)},registerListener:function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},dispatch:function(t){if(this.listeners[t]&&this.listeners[t].length){ +var e=[].slice.call(arguments);this.listeners[t].forEach(function(t){t.apply(null,e)})}},push:[].push,sort:[].sort,splice:[].splice},t.fn.init.prototype=t.fn,t}(),e.lib.Test=function(){function n(t,e){return new n.fn.init(t,e)}function i(t){t="undefined"==typeof t?!0:t,this.each(function(e,n){n.get("status")||(t=!1)}),t?(this.testComplete=null,this.attributes.complete=!0,this.determineStatus()):this.testComplete()}function s(t,e,n){var i,s;return function(){var a=this,r=arguments,o=function(){i=null,n||(s=t.apply(a,r))},c=n&&!i;return clearTimeout(i),i=setTimeout(o,e),c&&(s=t.apply(a,r)),s}}return n.fn=n.prototype={constructor:n,init:function(t,e){return this.listeners={},this.length=0,t?(this.attributes=e||{},this.attributes.name=t,this.attributes.status="untested",this.attributes.complete=!1,this):this},length:0,attributes:null,each:function(t){for(var e=[].slice.call(arguments,1),n=0,i=this.length;i>n;++n)e.unshift(this[n]),e.unshift(n),t.apply(this[n],e);return this},get:function(e){ +if("$scope"===e){var n=this.attributes.scope,i=t(this.attributes.scope);return this.attributes[e]?this.attributes[e]:n?i:t(document)}return this.attributes[e]},set:function(t,e){var n=!1;if("object"==typeof t)for(var i in t)t.hasOwnProperty(i)&&("status"===i&&(n=!0),this.attributes[i]=t[i]);else"status"===t&&(n=!0),this.attributes[t]=e;return n&&this.resolve(),this},add:function(t){return this.listenTo(t,"resolve",this.caseResponded),this.listenTo(t,"timeout",this.caseResponded),t.status&&t.dispatch("resolve",t),this.push(t),t},invoke:function(){if(this.testComplete)throw new Error("The test "+this.get("name")+" is already running.");if(this.attributes.complete)throw new Error("The test "+this.get("name")+" has already been run.");var t=this.get("type"),n=this.get("options")||{},a=this.get("callback"),r=this;if(this.testComplete=s(i.bind(this),400),this.testComplete(!1),"custom"===t)if("function"==typeof a)try{a.call(this,e,r,e.lib.Case,n)}catch(o){window.console&&window.console.error&&window.console.error(o); +}else{if("custom"!==t||"function"!=typeof e[a])throw new Error("The callback "+a+" cannot be invoked.");try{e[a].call(this,e,r,e.lib.Case,n)}catch(o){window.console&&window.console.error&&window.console.error(o)}}else{if("function"!=typeof e.components[t])throw new Error("The component type "+t+" is not defined.");try{e.components[t].call(this,e,r,e.lib.Case,n)}catch(o){window.console&&window.console.error&&window.console.error(o)}}return this.testComplete(),this},findByStatus:function(t){if(t){var e=new n;"string"==typeof t&&(t=[t]);for(var i=0,s=t.length;s>i;++i){var a=t[i];this.each(function(t,n){var i=n.get("status");i===a&&e.add(n)})}return e}},findCasesBySelector:function(t){var e=this.groupCasesBySelector();return e.hasOwnProperty(t)?e[t]:new n},findCaseByHtml:function(t){for(var n,i=0,s=this.length;s>i;++i)if(n=this[i],t===n.get("html"))return n;return e.lib.Case()},groupCasesBySelector:function(){var t={};return this.each(function(e,i){var s=i.get("selector");t[s]||(t[s]=new n), +t[s].add(i)}),t},groupCasesByHtml:function(){var t={};return this.each(function(e,i){var s=i.get("html");t[s]||(t[s]=new n),t[s].add(i)}),t},getGuidelineCoverage:function(t){var e=this.get("guidelines");return e&&e[t]||{}},caseResponded:function(t,e){this.dispatch(t,this,e),"function"==typeof this.testComplete&&this.testComplete()},determineStatus:function(){var t,n=this.get("type");e.components[n]&&"function"==typeof e.components[n].postInvoke&&(t=e.components[n].postInvoke.call(this,this)),this.set(t===!0?{status:"passed"}:this.findByStatus(["cantTell"]).length===this.length?{status:"cantTell"}:this.findByStatus(["inapplicable"]).length===this.length?{status:"inapplicable"}:this.findByStatus(["failed","untested"]).length?{status:"failed"}:{status:"passed"})},resolve:function(){this.dispatch("complete",this)},testComplete:null,listenTo:function(t,e,n){n=n.bind(this),t.registerListener.call(t,e,n)},registerListener:function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e); +},dispatch:function(t){if(this.listeners[t]&&this.listeners[t].length){var e=[].slice.call(arguments);this.listeners[t].forEach(function(t){t.apply(null,e)})}},push:[].push,sort:[].sort,concat:[].concat,splice:[].splice},n.fn.init.prototype=n.fn,n}(),e.lib.TestCollection=function(){function t(e){return new t.fn.init(e)}function n(){var t=!0;this.each(function(e,n){n.get("complete")||(t=!1)}),t?(this.testsComplete=null,this.dispatch("complete",this)):this.testsComplete()}function i(t,e,n){var i,s;return function(){var a=this,r=arguments,o=function(){i=null,n||(s=t.apply(a,r))},c=n&&!i;return clearTimeout(i),i=setTimeout(o,e),c&&(s=t.apply(a,r)),s}}return t.fn=t.prototype={constructor:t,init:function(t,n){if(this.listeners={},n=n||{},!t)return this;if("object"==typeof t){var i;for(var s in t)t.hasOwnProperty(s)&&(t[s].scope=t[s].scope||n.scope,i=new e.lib.Test(s,t[s]),this.listenTo(i,"results",this.report),this.push(i));return this}return this},length:0,run:function(t){var e=this;return t=t||{}, +this.each(function(n,i){t.preFilter&&e.listenTo(i,"resolve",function(e,n,i){var s=t.preFilter(e,n,i);s===!1&&(i.attributes.status="notTested",i.attributes.expected=null)}),t.caseResolve&&e.listenTo(i,"resolve",t.caseResolve),t.testComplete&&e.listenTo(i,"complete",t.testComplete)}),t.testCollectionComplete&&e.listenTo(e,"complete",t.testCollectionComplete),this.testsComplete=i(n.bind(this),500),this.each(function(t,e){e.invoke()}),this.testsComplete(),this},each:function(t){for(var e=[].slice.call(arguments,1),n=0,i=this.length;i>n;++n){e.unshift(this[n]),e.unshift(n);var s=t.apply(this[n],e);if(s===!1)break}return this},add:function(t){this.find(t.get("name"))||this.push(t)},find:function(t){for(var e=0,n=this.length;n>e;++e)if(this[e].get("name")===t)return this[e];return null},findByGuideline:function(e){var n={wcag:function(n,i){function s(e,n,i){var s=new t;return this.each(function(t,a){var r=a.get("guidelines"),o=r[e]&&r[e][n]&&r[e][n].techniques;if(o)for(var c=0,u=o.length;u>c;++c)o[c]===i&&(s.listenTo(a,"results",s.report), +s.add(a))}),s}var a=n.id,r=i.get("name");return a&&r?s.call(this,e,a,r):void 0}};if(n[e]){var i=[].slice.call(arguments,1);return n[e].apply(this,i)}},findByStatus:function(e){if(e){var n=new t;"string"==typeof e&&(e=[e]);for(var i=0,s=e.length;s>i;++i){var a=e[i];this.each(function(t,e){var i=e.get("status");i===a&&n.add(e)})}return n}},set:function(t,n){for(var i=0,s=this.length;s>i;++i)if(this[i].get("name")===t)return this[i].set(n),this[i];var a=e.lib.Test(t,n);return this.push(a),a},testsComplete:null,report:function(){this.dispatch.apply(this,arguments)},listenTo:function(t,e,n){n=n.bind(this),t.registerListener.call(t,e,n)},registerListener:function(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},dispatch:function(t){if(this.listeners[t]&&this.listeners[t].length){var e=[].slice.call(arguments);this.listeners[t].forEach(function(t){t.apply(null,e)})}},push:[].push,sort:[].sort,splice:[].splice},t.fn.init.prototype=t.fn,t}(),e.lib.WCAGGuideline=function(){ +var t=function(e){return new t.fn.init(e)};return t.fn=t.prototype={constructor:t,init:function(t){if(!t)return this;this.techniques=[];var n,i,s,a,r;if("object"==typeof t){if(t.guidelines){n=t.guidelines;for(var o in n)if(n.hasOwnProperty(o)){if(i=n[o],i.techniques&&i.techniques.length&&(s=i.techniques,delete i.techniques),i=e.lib.Section(o,i),s.length)for(var c=0,u=s.length;u>c;++c){if(a=s[c],!t.techniques[a])throw new Error("Definition for Technique "+a+" is missing from the guideline specification");r=this.findTechnique(a),r||(r=e.lib.Technique(a,t.techniques[a]),this.techniques.push(r)),i.addTechnique(r)}this.push(i)}}return this}return this},length:0,each:function(t){for(var e=[].slice.call(arguments,1),n=0,i=this.length;i>n;++n)e.unshift(this[n]),e.unshift(n),t.apply(this[n],e);return this},find:function(t){for(var e=0,n=this.length;n>e;++e)if(this[e].get("name")===t)return this[e];return null},findTechnique:function(t){for(var e=0,n=this.techniques.length;n>e;++e)if(this.techniques[e].get("name")===t)return this.techniques[e]; +return null},set:function(t,n){for(var i=0,s=this.length;s>i;++i)if(this[i].get("name")===t)return this[i].set(n),this[i];var a=e.lib.Test(t,n);return this.push(a),a},evaluate:function(){},results:function(){},push:[].push,sort:[].sort,splice:[].splice},t.fn.init.prototype=t.fn,t}(),function(t){function e(e,n,i,s){var a=n.attr("rowspan")||1,r=n.attr("scope");if("col"===r)return!0;if(-1!==c.indexOf(r))return!1;for(var o=0;o=0&&a>=0;s+=r,a+=o){var p=t(i[a][s]),f=0===r?"col":"row";if(p.is("th")){c=!0,u.push({cell:p,x:s,y:a});var g=!1;-1===o&&n(i,p,s,a)||-1===r&&e(i,p,s,a)?g=!0:t.each(h,function(e,n){ +var i=+p.attr(f+"span")||1,c=+t(n.cell).attr(f+"span")||1;i===c&&(-1===o&&n.x===s||-1===r&&n.y===a)&&(g=!0)}),g===!1&&(l=l.add(p))}else p.is("td")&&c===!0&&(c=!1,h.push(u),u=t())}return l}function s(e){var n=e.closest("table"),i=e.attr("headers").split(/\s/),s=t();return t.each(i,function(e,i){s=s.add(t("th#"+i+", td#"+i,n))}),s}function a(t,e){for(var n,i=0,s=0;void 0===n;){if(void 0===t[s])return;t[s][i]===e[0]?n=i:i+1===t[s].length?(s+=1,i=0):i+=1}return{x:n,y:s}}function r(e,n){var s,r=t(),o=a(n,e),c=+e.attr("rowspan")||1,u=+e.attr("colspan")||1;for(s=0;u>s;s++)r=r.add(i(n,o.x+s,o.y,0,-1));for(s=0;c>s;s++)r=r.add(i(n,o.x,o.y+s,-1,0));return r}function o(e,n){var i=a(n,e),s=t();e.closest("thead, tbody, tfoot").find("th[scope=rowgroup]").each(function(){var e=a(n,t(this));e.x<=i.x&&e.y<=i.y&&(s=s.add(this))})}var c=["row","col","rowgroup","colgroup"];t.fn.getTableMap=function(){var e=[];return this.find("tr").each(function(n){"undefined"==typeof e[n]&&(e[n]=[]);var i=e[n];t(this).children().each(function(){ +var s,a,r,o=t(this),c=+o.attr("rowspan")||1,u=+o.attr("colspan")||1;for(a=0,r=i.length;r>=a;a+=1)void 0===s&&void 0===i[a]&&(s=a);for(a=0,r=u*c;r>a;a+=1)void 0===e[n+~~(a/u)]&&(e[n+~~(a/u)]=[]),e[n+~~(a/u)][s+a%u]=this})}),e},t.fn.tableHeaders=function(){var e=t();return this.each(function(){var n=t(this);if(!n.is(":not(td, th)"))if(n.is("[headers]"))e=e.add(s(n));else{var i=n.closest("table").getTableMap();e=e.add(r(n,i)).add(o(n,i))}}),e.not(":empty").not(this)}}(jQuery),e.lib.wcag2=function(){function n(e){e.wcag2Structure&&e.accessibilityTests&&e.preconditionTests?i(e,e.wcag2Structure,e.accessibilityTests,e.preconditionTests):t.when(t.ajax(e.jsonPath+"/wcag2.json",a),t.ajax(e.jsonPath+"/tests.json",a),t.ajax(e.jsonPath+"/preconditions.json",a)).done(function(t,n,s){i(e,t[0],n[0],s[0])})}function i(n,i,a,r){var o,c,u,l=[];o=t.map(i,function(t){return new e.lib.wcag2.Criterion(t,a,r,n.subject)}),t.each(o,function(t,e){l.push.apply(l,e.getTests())}),u=[],c=[],t.each(l,function(t,e){-1===u.indexOf(e.title.en)&&(u.push(e.title.en), +c.push(e))}),t(e.html).quail({accessibilityTests:c,testCollectionComplete:s(o,n.testCollectionComplete)})}function s(e,n){return function(i,s){"complete"===i&&(s=t.map(e,function(t){return t.getResult(s)})),n(i,s)}}var a={async:!1,dataType:"json"};return{run:n}}(),e.guidelines.wcag.successCriteria["1.1.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.1.1",preEvaluator:e});return n.techniques={},n.failures={F3:"Using CSS to include images that convey important information",F13:"Having a text alternative that does not include information that is conveyed by color differences in the image",F20:"Not updating text alternatives when changes to non-text content occur",F30:"Using text alternatives that are not alternatives (e.g., filenames or placeholder text)",F38:"Not marking up decorative images in HTML in a way that allows assistive technology to ignore them",F39:'Providing a text alternative that is not null (e.g., alt="spacer" or alt="image") for images that should be ignored by assistive technology', +F65:'Omitting the alt attribute or text alternative on img elements, area elements, and input elements of type "image"',F67:"Providing long descriptions for non-text content that does not serve the same purpose or does not present the same information",F71:"Using text look-alikes to represent text without providing a text alternative",F72:"Using ASCII art without providing a text alternative"},n}(e),e.guidelines.wcag.successCriteria["1.2.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.2.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.2",preEvaluator:e});return n.techniques={G93:"Providing open (always visible) captions",G87:"Providing closed captions"},n.failures={F74:"Not labeling a synchronized media alternative to text as an alternative",F75:"Providing synchronized media without captions when the synchronized media presents more information than is presented on the page", +F8:"Captions omitting some dialogue or important sound effects"},n}(e),e.guidelines.wcag.successCriteria["1.2.3"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.3",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.2.4"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.4",preEvaluator:e});return n.techniques={G9:"Creating captions for live synchronized media",G93:"Providing open (always visible) captions",G87:"Providing closed captions using any readily available media format that has a video player that supports closed captioning"},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.2.5"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.5",preEvaluator:e});return n.techniques={G78:"Providing a second, user-selectable, audio track that includes audio descriptions",G173:"Providing a version of a movie with audio descriptions","SC1.2.8":"Providing a movie with extended audio descriptions", +G8:"Providing a movie with extended audio descriptions",G203:"Using a static text alternative to describe a talking head video"},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.2.7"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.7",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.2.8"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.8",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.2.9"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.2.9",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.3.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.3.1",preEvaluator:e});return n.techniques={G115:"Using semantic elements to mark up structure AND H49: Using semantic markup to mark emphasized or special text",G117:"Using text to convey information that is conveyed by variations in presentation of text", +G140:"Separating information and structure from presentation to enable different presentations",G138:"Using semantic markup whenever color cues are used",H48:"Using ol, ul and dl for lists or groups of links",H42:"Using h1-h6 to identify headings",SCR21:"Using functions of the Document Object Model (DOM) to add content to a page (Scripting)",H51:"Using table markup to present tabular information",H39:"Using caption elements to associate data table captions with data tables",H73:"Using the summary attribute of the table element to give an overview of data tables",H63:"Using the scope attribute to associate header cells and data cells in data tables",H43:"Using id and headers attributes to associate data cells with header cells in data tables",H44:"Using label elements to associate text labels with form controls",H65:"Using the title attribute to identify form controls when the label element cannot be used",H71:"Providing a description for groups of form controls using fieldset and legend elements", +H85:"Using OPTGROUP to group OPTION elements inside a SELECT",ARIA11:"Using ARIA landmarks to identify regions of a page (ARIA)",ARIA12:"Using role=heading to identify headings (ARIA)",ARIA13:"Using aria-labelledby to name regions and landmarks (ARIA)",ARIA16:"Using aria-labelledby to provide a name for user interface controls (ARIA)",ARIA17:"Using grouping roles to identify related form controls (ARIA)"},n.failures={F2:"Using changes in text presentation to convey information without using the appropriate markup or text",F17:"Insufficient information in DOM to determine one-to-one relationships (e.g., between labels with same id) in HTML",F42:"Using scripting events to emulate links in a way that is not programmatically determinable",F43:"Using structural markup in a way that does not represent relationships in the content",F87:"Inserting non-decorative content by using :before and :after pseudo-elements and the content property in CSS",F46:"Using th elements, caption elements, or non-empty summary attributes in layout tables", +F48:"Using the pre element to markup tabular information",F90:"Incorrectly associating table headers and content via the headers and id attributes",F91:"Not correctly marking up table headers",F33:"Using white space characters to create multiple columns in plain text content",F34:"Using white space characters to format tables in plain text content",F68:"Association of label and user interface controls not being programmatically determinable"},n}(e),e.guidelines.wcag.successCriteria["1.3.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.3.2",preEvaluator:e});return n.techniques={G57:"Ordering the content in a meaningful sequence (scope: for all the content in the Web page)",H34:"Using a Unicode right-to-left mark (RLM) or left-to-right mark (LRM) to mix text direction inline (languageUnicodeDirection)",H56:"Using the dir attribute on an inline element to resolve problems with nested directional runs",C6:"Positioning content based on structural markup (CSS)", +C8:"Using CSS letter-spacing to control spacing within a word",C27:"Making the DOM order match the visual order (CSS)"},n.failures={F49:"Using an HTML layout table that does not make sense when linearized",F32:"Using white space characters to control spacing within a word (whiteSpaceInWord)",F1:"Changing the meaning of content by positioning information with CSS",F34:"Using white space characters to format tables in plain text content (tabularDataIsInTable)",F33:"Using white space characters to create multiple columns in plain text content (tabularDataIsInTable)"},n}(e),e.guidelines.wcag.successCriteria["1.3.3"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.3.3",preEvaluator:e});return n.techniques={G96:"Providing textual identification of items that otherwise rely only on sensory information to be understood"},n.failures={F14:"Identifying content only by its shape or location",F26:"Using a graphical symbol alone to convey information"},n}(e),e.guidelines.wcag.successCriteria["1.4.1"]=function(t){ +function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.4.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.4.2"]=function(e){function n(){return!!t("audio, video, object, embed").length}var i=e.lib.SuccessCriteria({name:"wcag:1.4.2",preEvaluator:n});return i.techniques={G60:"Playing a sound that turns off automatically within three seconds",G170:"Providing a control near the beginning of the Web page that turns off sounds that play automatically",G171:"Playing sounds only on user request"},i.failures={F23:"Playing a sound longer than 3 seconds where there is no mechanism to turn it off"},i}(e),e.guidelines.wcag.successCriteria["1.4.3"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.4.3",preEvaluator:e});return n.techniques={G148:"Not specifying background color, not specifying text color, and not using technology features that change those defaults",G174:"Providing a control with a sufficient contrast ratio that allows users to switch to a presentation that uses sufficient contrast", +G18:"Ensuring that a contrast ratio of at least 4.5:1 exists between text (and images of text) and background behind the text for situation A AND G145: Ensuring that a contrast ratio of at least 3:1 exists between text (and images of text) and background behind the text for situation B"},n.failures={F24:"Specifying foreground colors without specifying background colors or vice versa",F83:"Using background images that do not provide sufficient contrast with foreground text (or images of text)"},n}(e),e.guidelines.wcag.successCriteria["1.4.4"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.4.4",preEvaluator:e});return n.techniques={G142:"Using a technology that has commonly-available user agents that support zoom",C12:"Using percent for font sizes",C13:"Using named font sizes",C14:"Using em units for font, sizes",SCR34:"Calculating size and ,position in a way that scales with text size (Scripting)",G146:"Using liquid layout",G178:"Providing controls on the Web page that allow users to incrementally change the size of all text on the page up to 200 percent", +G179:"Ensuring that there is no loss of content or functionality when the text resizes and text containers do not change their width"},n.failures={F69:"Resizing visually rendered text up to 200 percent causes the text, image or controls to be clipped, truncated or obscured",F80:"Text-based form controls do not resize when visually rendered text is resized up to 200%"},n}(e),e.guidelines.wcag.successCriteria["1.4.5"]=function(t){function e(){return!!document.querySelectorAll("img, map").length}var n=t.lib.SuccessCriteria({name:"wcag:1.4.5",preEvaluator:e});return n.techniques={C22:"Using CSS to control visual presentation of text (CSS)",C30:"Using CSS to replace text with images of text and providing user interface controls to switch",G140:"Separating information and structure from presentation to enable different presentations"},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.4.6"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.4.6",preEvaluator:e}); +return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.4.7"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.4.7",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.4.8"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.4.8",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["1.4.9"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:1.4.9",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.1.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.1.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.1.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.1.2",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.1.3"]=function(t){ +function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.1.3",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.2.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.2.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.2.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.2.2",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.2.3"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.2.3",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.2.4"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.2.4",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.2.5"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.2.5",preEvaluator:e +});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.3.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.3.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.3.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.3.2",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.1",preEvaluator:e});return n.techniques={G1:"Adding a link at the top of each page that goes directly to the main content area",G123:"Adding a link at the beginning of a block of repeated content to go to the end of the block",G124:"Adding links at the top of the page to each area of the content",H69:"Providing heading elements at the beginning of each section of content",H70:"Using frame elements to group blocks of repeated material AND H64: Using the title attribute of the frame and iframe elements", +SCR28:"Using an expandable and collapsible menu to bypass block of content"},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.10"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.10",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.2",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.3"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.3",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.4"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.4",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.5"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.5",preEvaluator:e});return n.techniques={},n.failures={}, +n}(e),e.guidelines.wcag.successCriteria["2.4.6"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.6",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.7"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.7",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.8"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.8",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["2.4.9"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:2.4.9",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.1.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.1.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.1.2"]=function(t){function e(){return!0; +}var n=t.lib.SuccessCriteria({name:"wcag:3.1.2",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.1.3"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.1.3",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.1.4"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.1.4",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.1.5"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.1.5",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.1.6"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.1.6",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.2.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.2.1",preEvaluator:e});return n.techniques={ +G107:'Using "activate" rather than "focus" as a trigger for changes of context'},n.failures={F52:"Opening a new window as soon as a new page is loaded",F55:"Using script to remove focus when focus is received"},n}(e),e.guidelines.wcag.successCriteria["3.2.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.2.2",preEvaluator:e});return n.techniques={G80:"Providing a submit button to initiate a change of context",H32:"Providing submit buttons",H84:"Using a button with a select element to perform an action",G13:"Describing what will happen before a change to a form control that causes a change of context to occur is made",SCR19:"Using an onchange event on a select element without causing a change of context"},n.failures={F36:"Automatically submitting a form and presenting new content without prior warning when the last field in the form is given a value",F37:"Launching a new window without prior warning when the status of a radio button, check box or select list is changed", +F76:"Providing instruction material about the change of context by change of setting in a user interface element at a location that users may bypass"},n}(e),e.guidelines.wcag.successCriteria["3.2.3"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.2.3",preEvaluator:e});return n.techniques={G61:"Presenting repeated components in the same relative order each time they appear"},n.failures={F66:"Presenting navigation links in a different relative order on different pages"},n}(e),e.guidelines.wcag.successCriteria["3.2.4"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.2.4",preEvaluator:e});return n.techniques={G197:"Using labels, names, and text alternatives consistently for content that has the same functionality AND following the sufficient techniques for Success Criterion 1.1.1 and sufficient techniques for Success Criterion 4.1.2 for providing labels, names, and text alternatives."},n.failures={F31:"Using two different labels for the same function on different Web pages within a set of Web pages" +},n}(e),e.guidelines.wcag.successCriteria["3.2.5"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.2.5",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.3.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.3.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.3.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.3.2",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.3.3"]=function(t){function e(){function t(t){return!!this.querySelectorAll('[type="'+t+'"]').length}function e(t){var e=Object.keys(t)[0];return!!this.querySelectorAll("["+e+'="'+t[e]+'"]').length}var n=["checkbox","color","date","datetime","datetime-local","email","file","hidden","month","number","password","radio","range","search","tel","time","url","week"],i=[{required:"required"},{"aria-required":"true" +}];return document.querySelectorAll("form").length?n.some(t,document)||i.some(e,document)?!0:void 0:!1}var n=t.lib.SuccessCriteria({name:"wcag:3.3.3",preEvaluator:e});return n.techniques={G83:"Providing text descriptions to identify required fields that were not completed",ARIA2:"Identifying a required field with the aria-required property",ARIA18:"Using aria-alertdialog to Identify Errors (ARIA)",G85:"Providing a text description when user input falls outside the required format or values",G177:"Providing suggested correction text",SCR18:"Providing client-side validation and alert (Scripting)",SCR32:"Providing client-side validation and adding error text via the DOM (Scripting)",G84:"Providing a text description when the user provides information that is not in the list of allowed values"},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.3.4"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.3.4",preEvaluator:e});return n.techniques={},n.failures={},n; +}(e),e.guidelines.wcag.successCriteria["3.3.5"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.3.5",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["3.3.6"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:3.3.6",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["4.1.1"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:4.1.1",preEvaluator:e});return n.techniques={},n.failures={},n}(e),e.guidelines.wcag.successCriteria["4.1.2"]=function(t){function e(){return!0}var n=t.lib.SuccessCriteria({name:"wcag:4.1.2",preEvaluator:e});return n.techniques={ARIA14:"Using aria-label to provide an invisible label where a visible label cannot be used",ARIA16:"Using aria-labelledby to provide a name for user interface controls",G108:"Using markup features to expose the name and role, allow user-settable properties to be directly set, and provide notification of changes using technology-specific techniques below:", +H91:"Using HTML form controls and links",H44:"Using label elements to associate text labels with form controls",H64:"Using the title attribute of the frame and iframe elements",H65:"Using the title attribute to identify form controls when the label element cannot be used",H88:"Using HTML according to spec"},n.failures={F59:"Using script to make div or span a user interface control in HTML without providing a role for the control (This failure may be solved in the future using DHTML roadmap techniques.)",F20:"Not updating text alternatives when changes to non-text content occur",F68:"Association of label and user interface controls not being programmatically determined",F79:"Focus state of a user interface component not being programmatically determinable or no notification of change of focus state available",F86:"Not providing names for each part of a multi-part form field, such as a US telephone number",F89:"Using null alt on an image where the image is the only content in a link"},n}(e), +e.lib.wcag2.Criterion=function(){function n(n,i){var s=e.lib.wcag2.EarlAssertion.getResultPriority,a={result:i};return t.each(n,function(t,e){s(a)0&&(s.hasPart=a),s},c.getTests=function(){var e=[];return t.each(o,function(t,n){e.push.apply(e,n.tests)}),e},c}return i}(),e.lib.wcag2.EarlAssertion=function(){function e(e){t.extend(this,e,s),this.outcome=t.extend({},this.outcome)}var n,i=["untested","inapplicable","passed","cantTell","failed"],s={type:"assertion", +subject:n,assertedBy:{type:"earl:Software",name:"QuailJS"},mode:"automated"};return window&&window.location&&(n=window.location.href),e.getResultPriority=function(t){return"object"==typeof t&&(t=t.outcome?t.outcome.result:t.result),i.indexOf(t)},e}(),e.lib.wcag2.TestAggregator=function(){function n(e,n){t.each(e,function(t,e){e.each(function(){n.call(this,e,this)})})}function i(e){var n=[],i=[];return t.each(e,function(t,e){var n=[];e.each(function(){n.push(this.get("element")),u.add(this)}),i.push(n)}),t.each(i,function(e,i){if(0===e)return void(n=i);var s=[];t.each(i,function(t,e){-1!==n.indexOf(e)&&s.push(e)}),n=s}),n}function s(t){var e=[];return n(t,function(t,n){var i=n.get("element");-1===e.indexOf(i)&&(e.push(i),u.add(n))}),e}function a(n,i){var s=[];return t.each(n,function(t,n){var a=new e.lib.wcag2.EarlAssertion(i);n&&(a.outcome.pointer=u.getPointer(n)),s.push(a)}),s}function r(t,s){var r=jQuery.unique(i(s)),o=a(jQuery.unique(r),{testCase:t.id,outcome:{result:"failed"}});return n(s,function(n,i){ +var s=i.get("status"),a=e.lib.wcag2.EarlAssertion.getResultPriority,c=o[r.indexOf(i.get("element"))];if(t[s]&&(s=t[s]),c&&a(c)>=a(s)){var u=c.outcome.pointer;c.outcome={result:s,info:n.get("title")},u&&(c.outcome.pointer=u)}}),o}function o(t,i){var r=s(i),o=a(r,{testCase:t.id,outcome:{result:"untested"}});return n(i,function(n,i){var s=i.get("status"),a=e.lib.wcag2.EarlAssertion.getResultPriority,c=o[r.indexOf(i.get("element"))];t[s]&&(s=t[s]),c&&a(c)= 0 && year < 100 ) { + // Year 70 and greater will be considered 19xx. + if ( year >= 70 ) { + year += 1900; + } else { + // Otherwise we assume it's 20xx. + year += 2000; + } + } + + // Cast day to Number, to remove leading 0. + return [ Number( dateObj.day ), monthName, year ].join( ' ' ); + }; + + DateUnfold.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/DateUnfold', DateUnfold ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/ElementRemove.js b/plugins/ckeditor/a11ychecker/quickfix/de/ElementRemove.js new file mode 100644 index 0000000..c0be413 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/ElementRemove.js @@ -0,0 +1,43 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'de', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * The ultimate fix for unsolvable problem - removing an element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementRemove + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementRemove( issue ) { + QuickFix.call( this, issue ); + } + + ElementRemove.prototype = new QuickFix(); + ElementRemove.prototype.constructor = ElementRemove; + + ElementRemove.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementRemove.prototype.fix = function( formAttributes, callback ) { + this.issue.element.remove(); + + if ( callback ) { + callback( this ); + } + }; + + ElementRemove.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/ElementRemove', ElementRemove ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/ElementReplace.js b/plugins/ckeditor/a11ychecker/quickfix/de/ElementReplace.js new file mode 100644 index 0000000..e99c99f --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/ElementReplace.js @@ -0,0 +1,59 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'de', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementReplace + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementReplace( issue ) { + QuickFix.call( this, issue ); + } + + ElementReplace.prototype = new QuickFix(); + ElementReplace.prototype.constructor = ElementReplace; + + /** + * Returns the name of the tag that issue.element should be converted to. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.ElementReplace + * @param {Object} formAttributes Form attributes from {@link #fix}. + * @returns {String} + */ + ElementReplace.prototype.getTargetName = function( formAttributes ) { + return 'h1'; + }; + + ElementReplace.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementReplace.prototype.fix = function( formAttributes, callback ) { + var newElement = new CKEDITOR.dom.element( this.getTargetName( formAttributes ) ); + + newElement.replace( this.issue.element ); + this.issue.element.moveChildren( newElement ); + + this.issue.element = newElement; + + if ( callback ) { + callback( this ); + } + }; + + ElementReplace.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/ElementReplace', ElementReplace ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/ImgAlt.js b/plugins/ckeditor/a11ychecker/quickfix/de/ImgAlt.js new file mode 100644 index 0000000..2bb129a --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/ImgAlt.js @@ -0,0 +1,89 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'de', + name: 'QuickFix', + callback: function( QuickFix ) { + + var emptyWhitespaceRegExp = /^[\s\n\r]+$/g; + + /** + * Fixes the image with missing alt attribute. + * + * @constructor + */ + function ImgAlt( issue ) { + QuickFix.call( this, issue ); + } + + /** + * Maximal count of characters in the alt. It might be changed to `0` to prevent + * length validation. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + * @static + */ + ImgAlt.altLengthLimit = 100; + + ImgAlt.prototype = new QuickFix(); + ImgAlt.prototype.constructor = ImgAlt; + + ImgAlt.prototype.display = function( form ) { + form.setInputs( { + alt: { + type: 'text', + label: this.lang.altLabel, + value: this.issue.element.getAttribute( 'alt' ) || '' + } + } ); + }; + + ImgAlt.prototype.fix = function( formAttributes, callback ) { + this.issue.element.setAttribute( 'alt', formAttributes.alt ); + + if ( callback ) { + callback( this ); + } + }; + + ImgAlt.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + '', + imgElem = this.issue && this.issue.element, + lang = this.lang; + + // Test if the alt has only whitespaces. + if ( proposedAlt.match( emptyWhitespaceRegExp ) ) { + ret.push( lang.errorWhitespace ); + } + + // Testing against exceeding max length. + if ( ImgAlt.altLengthLimit && proposedAlt.length > ImgAlt.altLengthLimit ) { + var errorTemplate = new CKEDITOR.template( lang.errorTooLong ); + + ret.push( errorTemplate.output( { + limit: ImgAlt.altLengthLimit, + length: proposedAlt.length + } ) ); + } + + if ( imgElem ) { + var fileName = String( imgElem.getAttribute( 'src' ) ).split( '/' ).pop(); + if ( fileName == proposedAlt ) { + ret.push( lang.errorSameAsFileName ); + } + } + + return ret; + }; + + ImgAlt.prototype.lang = {"altLabel":"Alternativtext","errorTooLong":"Der Alternativtext ist zu lang. Er sollte {limit} Zeichen lang sein, ist aber aktuell {length} Zeichen lang","errorWhitespace":"Der Alternativtext kann nicht nur Leerzeichen enthalten","errorSameAsFileName":"Der Alternativtext sollte nicht dem Dateinamen entsprechen"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/ImgAlt', ImgAlt ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/ImgAltNonEmpty.js b/plugins/ckeditor/a11ychecker/quickfix/de/ImgAltNonEmpty.js new file mode 100644 index 0000000..cc09de6 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/ImgAltNonEmpty.js @@ -0,0 +1,44 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'de', + name: 'ImgAlt', + callback: function( ImgAlt ) { + + /** + * Fixes the image with missing alt attribute, requiring non-empty alt. + * + * @constructor + */ + function ImgAltNonEmpty( issue ) { + ImgAlt.call( this, issue ); + } + + ImgAltNonEmpty.prototype = new ImgAlt(); + ImgAltNonEmpty.prototype.constructor = ImgAltNonEmpty; + + ImgAltNonEmpty.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + ''; + + if ( !proposedAlt ) { + ret.push( this.lang.errorEmpty ); + } + + if ( !ret.length ) { + ret = ImgAlt.prototype.validate.call( this, formAttributes ); + } + + return ret; + }; + + ImgAltNonEmpty.prototype.lang = {"altLabel":"Alternativtext","errorTooLong":"Der Alternativtext ist zu lang. Er sollte {limit} Zeichen lang sein, ist aber aktuell {length} Zeichen lang","errorWhitespace":"Der Alternativtext kann nicht nur Leerzeichen enthalten","errorSameAsFileName":"Der Alternativtext sollte nicht dem Dateinamen entsprechen","errorEmpty":"Der Alternativtext sollte nicht leer sein"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/ImgAltNonEmpty', ImgAltNonEmpty ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/LocalizedRepository.js b/plugins/ckeditor/a11ychecker/quickfix/de/LocalizedRepository.js new file mode 100644 index 0000000..6c23482 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/LocalizedRepository.js @@ -0,0 +1,174 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ +define( [ 'quickfix/Repository' ], function( Repository ) { + 'use strict'; + + /** + * This type adds localization support for repository. + * + * @mixins CKEDITOR.event + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @constructor + * @param {String} basePath A path to the directory where QuickFix classes are + * stored. + */ + function LocalizedRepository( basePath ) { + Repository.call( this, basePath ); + } + + LocalizedRepository.prototype = new Repository(); + LocalizedRepository.prototype.constructor = LocalizedRepository; + + LocalizedRepository.prototype._langDictionary = {}; + + // An array of arguments from calls that were deferred. + var deferredGetCalls = {}, + languagesRequested = []; + + /** + * See {@link CKEDITOR.plugins.a11ychecker.quickFix.Repository#get}. This implementation + * adds only `options.langCode` param: + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {Object} options + * @param {String} options.langCode Language code of quickFix to be loaded. + * @returns {Function} + */ + LocalizedRepository.prototype.get = function( options ) { + options.langCode = options.langCode || 'en'; + + if ( this.deferGetCall( options.langCode, arguments ) ) { + // Call should be deferred, because no lang is available yet. + return; + } + + if ( !CKEDITOR.plugins.a11ychecker.dev ) { + // In case of release code we'll do a trick here. + // Each class will be represented as '/' string, that way we + // can load multiple language combination with given class. + options.name = options.langCode + '/' + options.name; + } + + // If lang is available call to the base class. + return Repository.prototype.get.call( this, options ); + }; + + /** + * Similar to {@link #get} but returns localized instance rather than class. + * + * Please note that options.callback is mandatory. + * + * @todo: this method should be also available in generic class. + */ + LocalizedRepository.prototype.getInstance = function( options ) { + options = options || {}; + var name = options.name, + langCode = options.langCode || 'en', + that = this; + + this.get( { + name: name, + callback: function( QuickFixType ) { + // This callback is guaranteed to be called when dictionary for langCode is fetched. + var instance = new QuickFixType( options.issue ); + + if ( CKEDITOR.plugins.a11ychecker.dev ) { + // We only need to assign lang for dev version, built class will already have this property. + instance.lang = that._langDictionary[ langCode ][ name ]; + } + + options.callback( instance ); + }, + langCode: langCode + } ); + }; + + + /** + * If needed will defer {@link #get} call. + * + * @returns {Boolean} `true` if call was deferred, `false` otherwise. + */ + LocalizedRepository.prototype.deferGetCall = function( langCode, getArguments ) { + var indexOf = CKEDITOR.tools.indexOf; + if ( !CKEDITOR.plugins.a11ychecker.dev || this._langDictionary[ langCode ] ) { + // Deferring is always disabled in built version, and if _langDictionary is already + // loaded. + return false; + } + + this._addDeferredGet( langCode, getArguments ); + + if ( indexOf( languagesRequested, langCode ) === -1 ) { + // This particular language was not requested yet. + languagesRequested.push( langCode ); + CKEDITOR.scriptLoader.load( this.basePath + 'lang/' + langCode + '.js' ); + } + + return true; + }; + + /** + * Registers a class of given QuickFix. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {String} name QuickFix name. + * @param {Function} cls QuickFix type. + */ + LocalizedRepository.prototype.add = function( name, cls ) { + return Repository.prototype.add.call( this, name, cls ); + }; + + /** + * Method to register language dictionary for developer version. + * + * In built version languages are already inlined into a QuickFix class file, so there's + * no need to execute it. + * + * @param {Object} dictionary + */ + LocalizedRepository.prototype.lang = function( langCode, dictionary ) { + this._langDictionary[ langCode ] = dictionary; + + var getQueue = deferredGetCalls[ langCode ]; + + if ( !getQueue ) { + // No get calls were queued for that language. + return; + } + + // All deferred gets should be called in reversed order. + for ( var i = getQueue.length - 1; i >= 0; i-- ) { + this.get.apply( this, getQueue[ i ] ); + } + }; + + LocalizedRepository.prototype._addDeferredGet = function( langCode, getArguments ) { + if ( deferredGetCalls[ langCode ] ) { + deferredGetCalls[ langCode ].push( getArguments ); + } else { + deferredGetCalls[ langCode ] = [ getArguments ]; + } + }; + + + /** + * Function created for tests, returns count of deferred get functions. + * + * @returns {Number} + */ + LocalizedRepository.prototype._getDeferredGetCount = function( langCode ) { + return deferredGetCalls[ langCode ] ? deferredGetCalls[ langCode ].length : 0; + }; + + /** + * Function created for tests, clears deferred get queue. + */ + LocalizedRepository.prototype._clearDeferredGetQueue = function() { + deferredGetCalls = {}; + }; + + return LocalizedRepository; +} ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/ParagraphToHeader.js b/plugins/ckeditor/a11ychecker/quickfix/de/ParagraphToHeader.js new file mode 100644 index 0000000..ff7c804 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/ParagraphToHeader.js @@ -0,0 +1,168 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'de', + name: 'ElementReplace', + callback: function( ElementReplace ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ParagraphToHeader + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ParagraphToHeader( issue ) { + ElementReplace.call( this, issue ); + } + + ParagraphToHeader.prototype = new ElementReplace(); + ParagraphToHeader.prototype.constructor = ParagraphToHeader; + + ParagraphToHeader.prototype.getTargetName = function( formAttributes ) { + return formAttributes.level; + }; + + ParagraphToHeader.prototype.display = function( form, editor ) { + + var levelDict = this._getFormHeaderLeves( editor ); + + form.setInputs( { + level: { + type: 'select', + label: this.lang.levelLabel, + value: 'h' + this._getPreferredLevel( editor ), + options: levelDict + } + } ); + }; + + ParagraphToHeader.prototype.fix = function( formAttributes, callback ) { + var that = this; + ElementReplace.prototype.fix.call( this, formAttributes, function() { + that._removeBoldTag(); + callback( that ); + } ); + }; + + /** + * Determines preferred heading level for the header that should be cerated. + * + * @private + * @param {CKEDITOR.editor} editor + * @returns {Number} Number ranging from `1` to `6`. + */ + ParagraphToHeader.prototype._getPreferredLevel = function( editor ) { + var ret = 1, + editable = editor.editable(), + headerTagRegExp = /^h[1-6]$/i, + range = new CKEDITOR.dom.range( editable.getDocument() ), + walker, + prevElement; + + range.setStartAt( editable, CKEDITOR.POSITION_AFTER_START ); + range.setEndAt( this.issue.element, CKEDITOR.POSITION_BEFORE_START ); + walker = new CKEDITOR.dom.walker( range ); + + while ( ( prevElement = walker.previous() ) ) { + if ( prevElement.getName && prevElement.getName().match( headerTagRegExp ) ) { + ret = Number( prevElement.getName()[ 1 ] ) + 1; + break; + } + } + + // WE can't return a higher value than 7. + return Math.min( ret, 6 ); + }; + + /** + * This method check if issue element contains `` element only as a first and the only child. + * If so we'll remove it, but move its children to the `issue.element`. + */ + ParagraphToHeader.prototype._removeBoldTag = function() { + var isElementEvaluator = function( el ) { + return el.type === CKEDITOR.NODE_ELEMENT; + }, + elem = this.issue.element, + innerElement = elem.getFirst( isElementEvaluator ), + // If first child element is at the same time last element child, then it means it has only this element. + hasSingleElement = innerElement && innerElement.equals( elem.getLast( isElementEvaluator ) ), + suspiciousTagNames = [ 'strong', 'b' ]; + + if ( hasSingleElement && CKEDITOR.tools.indexOf( suspiciousTagNames, innerElement.getName() ) !== -1 ) { + innerElement.moveChildren( elem ); + innerElement.remove(); + } + }; + + /** + * Returns minimal and maximal possible header levels for given editor. + * + * Result will be based on ACF and `config.format_tags`. + * + * @param {CKEDITOR.editor} + * @return {Object} Allowed header level boundaries. + * @return {Number} return.min Minimal allowed level. + * @return {Number} return.email Maximal allowed level. + */ + ParagraphToHeader.prototype._getPossibleLevels = function( editor ) { + var tags = ( editor.config.format_tags || '' ).split( ';' ), + ret = { + min: 1, + max: 6 + }, + i; + + // Filtering tags. + for ( i = tags.length - 1; i >= 0; i-- ) { + // If given tag is not header tag or if it's not allowed by the ACF. + if ( !tags[ i ].match( /^h[1-6]$/i ) || !editor.filter.check( tags[ i ] ) ) { + tags.splice( i, 1 ); + } else { + tags[ i ] = Number( tags[ i ][ 1 ] ); + } + } + + if ( tags.length ) { + // Note if IE8 has to be supported we need to inline sorting here. + tags.sort(); + + ret.min = tags[ 0 ]; + ret.max = tags[ tags.length - 1 ]; + } + + return ret; + }; + + /** + * Returns options dictionary that should be put in form header level select. + * + * @param {CKEDITOR.editor} editor + */ + ParagraphToHeader.prototype._getFormHeaderLeves = function( editor ) { + var dict = {}, + boundaries = this._getPossibleLevels( editor ), + preferredLevel = this._getPreferredLevel( editor ); + + for ( var i = boundaries.min; i <= boundaries.max; i++ ) { + dict[ 'h' + i ] = 'H' + i; + } + + if ( dict[ 'h' + preferredLevel ] ) { + dict[ 'h' + preferredLevel ] += ( ' ' + this.lang.suggested ); + } + + return dict; + }; + + + ParagraphToHeader.prototype.lang = {"levelLabel":"Überschriftenebene","suggested":"(Vorschlag)"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/ParagraphToHeader', ParagraphToHeader ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/QuickFix.js b/plugins/ckeditor/a11ychecker/quickfix/de/QuickFix.js new file mode 100644 index 0000000..e05c0ca --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/QuickFix.js @@ -0,0 +1,90 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +(function() { + 'use strict'; + + /** + * QuickFix type for the QuickFix objects. + * + * # Overview + * + * It encapsulates logic responsible for fixing Accessibility issue. + * + * ## Working with QuickFix objects + * + * ### Adding controls to the QuickFix form + * + * Controls can be added in {@link #display} method using {@link CKEDITOR.plugins.a11ychecker.ViewerForm} + * methods. + * + * ### Executing the fix + * + * The fixing logic is placed in {@link #fix} method, so you need to simply call it when + * you're sure to apply the fix. + * + * @since 4.6.0 + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class QuickFix + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function QuickFix( issue ) { + this.issue = issue; + } + + QuickFix.prototype = { + /** + * @member CKEDITOR.plugins.a11ychecker.quickFix.QuickFix + * @property {CKEDITOR.plugins.a11ychecker.Issue} issue Issue object that QuickFix was created for. + */ + issue: null + }; + + QuickFix.prototype.constructor = QuickFix; + + QuickFix.prototype.display = function( form, editor ) { + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + QuickFix.prototype.fix = function( formAttributes, callback ) { + if ( callback ) { + callback( this ); + } + }; + + /** + * Method used to validate data placed in form. + * + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @returns {String[]} Array of error messages. If array is empty, then it means no errors ocurred. + */ + QuickFix.prototype.validate = function( formAttributes ) { + return []; + }; + + /** + * A method called to mark the selection on object before quickfix is applied. + * + * @param {CKEDITOR.dom.selection} selection Editor selection. + */ + QuickFix.prototype.markSelection = function( editor, selection ) { + var rng = editor.createRange(); + rng.setStartBefore( this.issue.element ); + rng.setEndAfter( this.issue.element ); + selection.selectRanges( [ rng ] ); + }; + + QuickFix.prototype.lang = {}; + + QuickFix.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/QuickFix', QuickFix ); +}()); diff --git a/plugins/ckeditor/a11ychecker/quickfix/de/TableHeaders.js b/plugins/ckeditor/a11ychecker/quickfix/de/TableHeaders.js new file mode 100644 index 0000000..f3830c9 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/de/TableHeaders.js @@ -0,0 +1,98 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'de', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix adding a caption in the `table` element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class TableHeaders + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function TableHeaders( issue ) { + QuickFix.call( this, issue ); + } + + TableHeaders.prototype = new QuickFix(); + + TableHeaders.prototype.constructor = TableHeaders; + + TableHeaders.prototype.display = function( form ) { + var lang = this.lang; + + form.setInputs( { + position: { + type: 'select', + label: lang.positionLabel, + value: 'row', + options: { + 'both': lang.positionBoth, + 'row': lang.positionHorizontally, + 'col': lang.positionVertically + } + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + TableHeaders.prototype.fix = function( formAttributes, callback ) { + var table = this.issue.element, + headers = formAttributes.position, + newCell, + row, + i; + // Following code copied from CKEditor plugins/table/dialogs/table.js file. + + // Should we make all first cells in a row TH? + if ( headers == 'col' || headers == 'both' ) { + for ( row = 0; row < table.$.rows.length; row++ ) { + if ( !table.$.rows[ row ].cells.length ) { + continue; + } + + newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] ); + newCell.renameNode( 'th' ); + newCell.setAttribute( 'scope', 'row' ); + } + } + + if ( !table.$.tHead && ( headers == 'row' || headers == 'both' ) ) { + var thead = new CKEDITOR.dom.element( table.$.createTHead() ); + var tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); + var theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); + + // Change TD to TH: + for ( i = 0; i < theRow.getChildCount(); i++ ) { + var th = theRow.getChild( i ); + // Skip bookmark nodes. (#6155) + if ( th.type == CKEDITOR.NODE_ELEMENT && !th.data( 'cke-bookmark' ) ) { + th.renameNode( 'th' ); + th.setAttribute( 'scope', 'col' ); + } + } + thead.append( theRow.remove() ); + } + + if ( callback ) { + callback( this ); + } + }; + + TableHeaders.prototype.lang = {"positionLabel":"Position","positionHorizontally":"Horizontal","positionVertically":"Vertikal","positionBoth":"Beide"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'de/TableHeaders', TableHeaders ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/AddTableCaption.js b/plugins/ckeditor/a11ychecker/quickfix/en/AddTableCaption.js new file mode 100644 index 0000000..ca1456c --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/AddTableCaption.js @@ -0,0 +1,75 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + + var emptyWhitespaceRegExp = /^[\s\n\r]+$/g; + + /** + * QuickFix adding a caption in the `table` element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AddTableCaption + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AddTableCaption( issue ) { + QuickFix.call( this, issue ); + } + + AddTableCaption.prototype = new QuickFix(); + + AddTableCaption.prototype.constructor = AddTableCaption; + + AddTableCaption.prototype.display = function( form ) { + form.setInputs( { + caption: { + type: 'text', + label: this.lang.captionLabel + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AddTableCaption.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + caption = issueElement.getDocument().createElement( 'caption' ); + + caption.setHtml( formAttributes.caption ); + // Prepend the caption. + issueElement.append( caption, true ); + + if ( callback ) { + callback( this ); + } + }; + + AddTableCaption.prototype.validate = function( formAttributes ) { + var proposedCaption = formAttributes.caption, + ret = []; + + // Test if the caption has only whitespaces. + if ( !proposedCaption || proposedCaption.match( emptyWhitespaceRegExp ) ) { + ret.push( this.lang.errorEmpty ); + } + + return ret; + }; + + AddTableCaption.prototype.lang = {"captionLabel":"Caption","errorEmpty":"Caption text can not be empty"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/AddTableCaption', AddTableCaption ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/AnchorsMerge.js b/plugins/ckeditor/a11ychecker/quickfix/en/AnchorsMerge.js new file mode 100644 index 0000000..7dc73f6 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/AnchorsMerge.js @@ -0,0 +1,80 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix merging two or more sibling anchors with the same href. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AnchorsMerge + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AnchorsMerge( issue ) { + QuickFix.call( this, issue ); + } + + AnchorsMerge.prototype = new QuickFix(); + + AnchorsMerge.prototype.constructor = AnchorsMerge; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AnchorsMerge.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + nextSibling = issueElement.getNext(), + initialHref = issueElement.getAttribute( 'href' ), + extraInnerHtml = '', + isWhitespace = function( node ) { + return node.type === CKEDITOR.NODE_TEXT && node.getText().match( /^[\s]*$/ ); + }, + iterationAllowed = function( node ) { + if ( !node ) { + return false; + } + + // We only allow anchors that have the same href as the initial one + return ( node.getName && node.getName() == 'a' && nextSibling.getAttribute( 'href' ) == initialHref ) || + isWhitespace( node ); // Or whitespace text nodes. + }, + curNode; + + while ( iterationAllowed( nextSibling ) ) { + curNode = nextSibling; + + // This html will be added later on to the first anchor. + extraInnerHtml += isWhitespace( curNode ) ? curNode.getText() : curNode.getHtml(); + + // Prepare nextSibling var for next iteration. + nextSibling = curNode.getNext(); + + // And we can remove element safely. + curNode.remove(); + } + + // Adding extra html to first anchor. + if ( extraInnerHtml ) { + issueElement.setHtml( issueElement.getHtml() + extraInnerHtml ); + } + + if ( callback ) { + callback( this ); + } + }; + + AnchorsMerge.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/AnchorsMerge', AnchorsMerge ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/AttributeRename.js b/plugins/ckeditor/a11ychecker/quickfix/en/AttributeRename.js new file mode 100644 index 0000000..f6501d0 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/AttributeRename.js @@ -0,0 +1,86 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix renaming an attribute {@link #attributeName} to another name + * {@link #attributeTargetName}. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AttributeRename + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AttributeRename( issue ) { + QuickFix.call( this, issue ); + } + + AttributeRename.prototype = new QuickFix(); + + AttributeRename.prototype.constructor = AttributeRename; + + /** + * Name of the attribute to be renamed. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + */ + AttributeRename.prototype.attributeName = 'title'; + + /** + * A desired name for the attribute. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + */ + AttributeRename.prototype.attributeTargetName = 'alt'; + + /** + * Gets the proposed new value of the target attribute. + * + * @returns {String} + */ + AttributeRename.prototype.getProposedValue = function() { + return this.issue.element.getAttribute( this.attributeName ) || ''; + }; + + AttributeRename.prototype.display = function( form ) { + form.setInputs( { + value: { + type: 'text', + label: 'Value', + value: this.getProposedValue() + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AttributeRename.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + desiredValue = formAttributes.value; + + // Set desired attribute value. + issueElement.setAttribute( this.attributeTargetName, desiredValue ); + // Unset the old attribute. + issueElement.removeAttribute( this.attributeName ); + + if ( callback ) { + callback( this ); + } + }; + + AttributeRename.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/AttributeRename', AttributeRename ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/AttributeRenameDefault.js b/plugins/ckeditor/a11ychecker/quickfix/en/AttributeRenameDefault.js new file mode 100644 index 0000000..6740820 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/AttributeRenameDefault.js @@ -0,0 +1,40 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'AttributeRename', + callback: function( AttributeRename ) { + /** + * QuickFix renaming an attribute {@link #attributeName} to another name + * {@link #attributeTargetName} using a proposed default value + * based on the value of {@link #attributeTargetName}. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AttributeRenameDefault + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AttributeRenameDefault( issue ) { + AttributeRename.call( this, issue ); + } + + AttributeRenameDefault.prototype = new AttributeRename(); + + AttributeRenameDefault.prototype.constructor = AttributeRenameDefault; + + AttributeRenameDefault.prototype.getProposedValue = function() { + var element = this.issue.element; + return element.getAttribute( this.attributeTargetName ) || + element.getAttribute( this.attributeName ) || ''; + }; + + AttributeRenameDefault.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/AttributeRenameDefault', AttributeRenameDefault ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/DateUnfold.js b/plugins/ckeditor/a11ychecker/quickfix/en/DateUnfold.js new file mode 100644 index 0000000..7effe93 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/DateUnfold.js @@ -0,0 +1,133 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + // List of month names. + var monthNames = { + en: [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ] + }; + + /** + * QuickFix converting short dates to more verbose format. Eg. convert 21-3-2030 + * to 21 March 2030. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class DateUnfold + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function DateUnfold( issue ) { + QuickFix.call( this, issue ); + } + + DateUnfold.prototype = new QuickFix(); + + DateUnfold.prototype.constructor = DateUnfold; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + DateUnfold.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + innerText = issueElement.getText(), + that = this; + + innerText = innerText.replace( /(\d{1,2}[.\/-]\d{1,2}[.\/-]\d{2,4})/g, function( occur ) { + var extractedDate = that.parseDate( occur ); + + return that.getFriendlyDate( extractedDate ); + } ); + + issueElement.setText( innerText ); + + if ( callback ) { + callback( this ); + } + }; + + /** + * Parses given "short" data. Returns an object with 1-based values. + * + * Eg. 21.03.2020 would be parsed to: + * + * { + * day: "21", + * month: "03", + * year: "2020" + * } + * + * @returns {Object} An object containing properties: + * * day + * * month + * * year + */ + DateUnfold.prototype.parseDate = function( dateString ) { + var ret = dateString.split( /[.\-\/]+/ ); + + return { + day: ret[ 0 ], + month: ret[ 1 ], + year: ret[ 2 ] + }; + }; + + /** + * var ret = dateFix.getFriendlyDate( { + * day: "21", + * month: "03", + * year: "2020" + * } ); + * + * // ret === "21 March 2020" + * + * @param {Object} An object returned by {@link #parseDate} + * @returns {String} A human-friendly date representation. + */ + DateUnfold.prototype.getFriendlyDate = function( dateObj ) { + // month - 1 because monthNames is 0-based array. + var monthName = monthNames.en[ Number( dateObj.month - 1 ) ], + year = Number( dateObj.year ); + + // In case of 2 digit year value, lets fix it. + if ( year >= 0 && year < 100 ) { + // Year 70 and greater will be considered 19xx. + if ( year >= 70 ) { + year += 1900; + } else { + // Otherwise we assume it's 20xx. + year += 2000; + } + } + + // Cast day to Number, to remove leading 0. + return [ Number( dateObj.day ), monthName, year ].join( ' ' ); + }; + + DateUnfold.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/DateUnfold', DateUnfold ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/ElementRemove.js b/plugins/ckeditor/a11ychecker/quickfix/en/ElementRemove.js new file mode 100644 index 0000000..d3d135e --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/ElementRemove.js @@ -0,0 +1,43 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * The ultimate fix for unsolvable problem - removing an element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementRemove + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementRemove( issue ) { + QuickFix.call( this, issue ); + } + + ElementRemove.prototype = new QuickFix(); + ElementRemove.prototype.constructor = ElementRemove; + + ElementRemove.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementRemove.prototype.fix = function( formAttributes, callback ) { + this.issue.element.remove(); + + if ( callback ) { + callback( this ); + } + }; + + ElementRemove.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/ElementRemove', ElementRemove ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/ElementReplace.js b/plugins/ckeditor/a11ychecker/quickfix/en/ElementReplace.js new file mode 100644 index 0000000..3f60e12 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/ElementReplace.js @@ -0,0 +1,59 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementReplace + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementReplace( issue ) { + QuickFix.call( this, issue ); + } + + ElementReplace.prototype = new QuickFix(); + ElementReplace.prototype.constructor = ElementReplace; + + /** + * Returns the name of the tag that issue.element should be converted to. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.ElementReplace + * @param {Object} formAttributes Form attributes from {@link #fix}. + * @returns {String} + */ + ElementReplace.prototype.getTargetName = function( formAttributes ) { + return 'h1'; + }; + + ElementReplace.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementReplace.prototype.fix = function( formAttributes, callback ) { + var newElement = new CKEDITOR.dom.element( this.getTargetName( formAttributes ) ); + + newElement.replace( this.issue.element ); + this.issue.element.moveChildren( newElement ); + + this.issue.element = newElement; + + if ( callback ) { + callback( this ); + } + }; + + ElementReplace.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/ElementReplace', ElementReplace ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/ImgAlt.js b/plugins/ckeditor/a11ychecker/quickfix/en/ImgAlt.js new file mode 100644 index 0000000..2ea8023 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/ImgAlt.js @@ -0,0 +1,89 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + + var emptyWhitespaceRegExp = /^[\s\n\r]+$/g; + + /** + * Fixes the image with missing alt attribute. + * + * @constructor + */ + function ImgAlt( issue ) { + QuickFix.call( this, issue ); + } + + /** + * Maximal count of characters in the alt. It might be changed to `0` to prevent + * length validation. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + * @static + */ + ImgAlt.altLengthLimit = 100; + + ImgAlt.prototype = new QuickFix(); + ImgAlt.prototype.constructor = ImgAlt; + + ImgAlt.prototype.display = function( form ) { + form.setInputs( { + alt: { + type: 'text', + label: this.lang.altLabel, + value: this.issue.element.getAttribute( 'alt' ) || '' + } + } ); + }; + + ImgAlt.prototype.fix = function( formAttributes, callback ) { + this.issue.element.setAttribute( 'alt', formAttributes.alt ); + + if ( callback ) { + callback( this ); + } + }; + + ImgAlt.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + '', + imgElem = this.issue && this.issue.element, + lang = this.lang; + + // Test if the alt has only whitespaces. + if ( proposedAlt.match( emptyWhitespaceRegExp ) ) { + ret.push( lang.errorWhitespace ); + } + + // Testing against exceeding max length. + if ( ImgAlt.altLengthLimit && proposedAlt.length > ImgAlt.altLengthLimit ) { + var errorTemplate = new CKEDITOR.template( lang.errorTooLong ); + + ret.push( errorTemplate.output( { + limit: ImgAlt.altLengthLimit, + length: proposedAlt.length + } ) ); + } + + if ( imgElem ) { + var fileName = String( imgElem.getAttribute( 'src' ) ).split( '/' ).pop(); + if ( fileName == proposedAlt ) { + ret.push( lang.errorSameAsFileName ); + } + } + + return ret; + }; + + ImgAlt.prototype.lang = {"altLabel":"Alternative text","errorTooLong":"Alternative text is too long. It should be up to {limit} characters while your has {length}","errorWhitespace":"Alternative text can not only contain whitespace characters","errorSameAsFileName":"Image alt should not be the same as the file name"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/ImgAlt', ImgAlt ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/ImgAltNonEmpty.js b/plugins/ckeditor/a11ychecker/quickfix/en/ImgAltNonEmpty.js new file mode 100644 index 0000000..0af9b4c --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/ImgAltNonEmpty.js @@ -0,0 +1,44 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'ImgAlt', + callback: function( ImgAlt ) { + + /** + * Fixes the image with missing alt attribute, requiring non-empty alt. + * + * @constructor + */ + function ImgAltNonEmpty( issue ) { + ImgAlt.call( this, issue ); + } + + ImgAltNonEmpty.prototype = new ImgAlt(); + ImgAltNonEmpty.prototype.constructor = ImgAltNonEmpty; + + ImgAltNonEmpty.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + ''; + + if ( !proposedAlt ) { + ret.push( this.lang.errorEmpty ); + } + + if ( !ret.length ) { + ret = ImgAlt.prototype.validate.call( this, formAttributes ); + } + + return ret; + }; + + ImgAltNonEmpty.prototype.lang = {"altLabel":"Alternative text","errorTooLong":"Alternative text is too long. It should be up to {limit} characters while your has {length}","errorWhitespace":"Alternative text can not only contain whitespace characters","errorSameAsFileName":"Image alt should not be the same as the file name","errorEmpty":"Alternative text can not be empty"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/ImgAltNonEmpty', ImgAltNonEmpty ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/LocalizedRepository.js b/plugins/ckeditor/a11ychecker/quickfix/en/LocalizedRepository.js new file mode 100644 index 0000000..6c23482 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/LocalizedRepository.js @@ -0,0 +1,174 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ +define( [ 'quickfix/Repository' ], function( Repository ) { + 'use strict'; + + /** + * This type adds localization support for repository. + * + * @mixins CKEDITOR.event + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @constructor + * @param {String} basePath A path to the directory where QuickFix classes are + * stored. + */ + function LocalizedRepository( basePath ) { + Repository.call( this, basePath ); + } + + LocalizedRepository.prototype = new Repository(); + LocalizedRepository.prototype.constructor = LocalizedRepository; + + LocalizedRepository.prototype._langDictionary = {}; + + // An array of arguments from calls that were deferred. + var deferredGetCalls = {}, + languagesRequested = []; + + /** + * See {@link CKEDITOR.plugins.a11ychecker.quickFix.Repository#get}. This implementation + * adds only `options.langCode` param: + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {Object} options + * @param {String} options.langCode Language code of quickFix to be loaded. + * @returns {Function} + */ + LocalizedRepository.prototype.get = function( options ) { + options.langCode = options.langCode || 'en'; + + if ( this.deferGetCall( options.langCode, arguments ) ) { + // Call should be deferred, because no lang is available yet. + return; + } + + if ( !CKEDITOR.plugins.a11ychecker.dev ) { + // In case of release code we'll do a trick here. + // Each class will be represented as '/' string, that way we + // can load multiple language combination with given class. + options.name = options.langCode + '/' + options.name; + } + + // If lang is available call to the base class. + return Repository.prototype.get.call( this, options ); + }; + + /** + * Similar to {@link #get} but returns localized instance rather than class. + * + * Please note that options.callback is mandatory. + * + * @todo: this method should be also available in generic class. + */ + LocalizedRepository.prototype.getInstance = function( options ) { + options = options || {}; + var name = options.name, + langCode = options.langCode || 'en', + that = this; + + this.get( { + name: name, + callback: function( QuickFixType ) { + // This callback is guaranteed to be called when dictionary for langCode is fetched. + var instance = new QuickFixType( options.issue ); + + if ( CKEDITOR.plugins.a11ychecker.dev ) { + // We only need to assign lang for dev version, built class will already have this property. + instance.lang = that._langDictionary[ langCode ][ name ]; + } + + options.callback( instance ); + }, + langCode: langCode + } ); + }; + + + /** + * If needed will defer {@link #get} call. + * + * @returns {Boolean} `true` if call was deferred, `false` otherwise. + */ + LocalizedRepository.prototype.deferGetCall = function( langCode, getArguments ) { + var indexOf = CKEDITOR.tools.indexOf; + if ( !CKEDITOR.plugins.a11ychecker.dev || this._langDictionary[ langCode ] ) { + // Deferring is always disabled in built version, and if _langDictionary is already + // loaded. + return false; + } + + this._addDeferredGet( langCode, getArguments ); + + if ( indexOf( languagesRequested, langCode ) === -1 ) { + // This particular language was not requested yet. + languagesRequested.push( langCode ); + CKEDITOR.scriptLoader.load( this.basePath + 'lang/' + langCode + '.js' ); + } + + return true; + }; + + /** + * Registers a class of given QuickFix. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {String} name QuickFix name. + * @param {Function} cls QuickFix type. + */ + LocalizedRepository.prototype.add = function( name, cls ) { + return Repository.prototype.add.call( this, name, cls ); + }; + + /** + * Method to register language dictionary for developer version. + * + * In built version languages are already inlined into a QuickFix class file, so there's + * no need to execute it. + * + * @param {Object} dictionary + */ + LocalizedRepository.prototype.lang = function( langCode, dictionary ) { + this._langDictionary[ langCode ] = dictionary; + + var getQueue = deferredGetCalls[ langCode ]; + + if ( !getQueue ) { + // No get calls were queued for that language. + return; + } + + // All deferred gets should be called in reversed order. + for ( var i = getQueue.length - 1; i >= 0; i-- ) { + this.get.apply( this, getQueue[ i ] ); + } + }; + + LocalizedRepository.prototype._addDeferredGet = function( langCode, getArguments ) { + if ( deferredGetCalls[ langCode ] ) { + deferredGetCalls[ langCode ].push( getArguments ); + } else { + deferredGetCalls[ langCode ] = [ getArguments ]; + } + }; + + + /** + * Function created for tests, returns count of deferred get functions. + * + * @returns {Number} + */ + LocalizedRepository.prototype._getDeferredGetCount = function( langCode ) { + return deferredGetCalls[ langCode ] ? deferredGetCalls[ langCode ].length : 0; + }; + + /** + * Function created for tests, clears deferred get queue. + */ + LocalizedRepository.prototype._clearDeferredGetQueue = function() { + deferredGetCalls = {}; + }; + + return LocalizedRepository; +} ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/ParagraphToHeader.js b/plugins/ckeditor/a11ychecker/quickfix/en/ParagraphToHeader.js new file mode 100644 index 0000000..892d075 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/ParagraphToHeader.js @@ -0,0 +1,168 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'ElementReplace', + callback: function( ElementReplace ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ParagraphToHeader + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ParagraphToHeader( issue ) { + ElementReplace.call( this, issue ); + } + + ParagraphToHeader.prototype = new ElementReplace(); + ParagraphToHeader.prototype.constructor = ParagraphToHeader; + + ParagraphToHeader.prototype.getTargetName = function( formAttributes ) { + return formAttributes.level; + }; + + ParagraphToHeader.prototype.display = function( form, editor ) { + + var levelDict = this._getFormHeaderLeves( editor ); + + form.setInputs( { + level: { + type: 'select', + label: this.lang.levelLabel, + value: 'h' + this._getPreferredLevel( editor ), + options: levelDict + } + } ); + }; + + ParagraphToHeader.prototype.fix = function( formAttributes, callback ) { + var that = this; + ElementReplace.prototype.fix.call( this, formAttributes, function() { + that._removeBoldTag(); + callback( that ); + } ); + }; + + /** + * Determines preferred heading level for the header that should be cerated. + * + * @private + * @param {CKEDITOR.editor} editor + * @returns {Number} Number ranging from `1` to `6`. + */ + ParagraphToHeader.prototype._getPreferredLevel = function( editor ) { + var ret = 1, + editable = editor.editable(), + headerTagRegExp = /^h[1-6]$/i, + range = new CKEDITOR.dom.range( editable.getDocument() ), + walker, + prevElement; + + range.setStartAt( editable, CKEDITOR.POSITION_AFTER_START ); + range.setEndAt( this.issue.element, CKEDITOR.POSITION_BEFORE_START ); + walker = new CKEDITOR.dom.walker( range ); + + while ( ( prevElement = walker.previous() ) ) { + if ( prevElement.getName && prevElement.getName().match( headerTagRegExp ) ) { + ret = Number( prevElement.getName()[ 1 ] ) + 1; + break; + } + } + + // WE can't return a higher value than 7. + return Math.min( ret, 6 ); + }; + + /** + * This method check if issue element contains `` element only as a first and the only child. + * If so we'll remove it, but move its children to the `issue.element`. + */ + ParagraphToHeader.prototype._removeBoldTag = function() { + var isElementEvaluator = function( el ) { + return el.type === CKEDITOR.NODE_ELEMENT; + }, + elem = this.issue.element, + innerElement = elem.getFirst( isElementEvaluator ), + // If first child element is at the same time last element child, then it means it has only this element. + hasSingleElement = innerElement && innerElement.equals( elem.getLast( isElementEvaluator ) ), + suspiciousTagNames = [ 'strong', 'b' ]; + + if ( hasSingleElement && CKEDITOR.tools.indexOf( suspiciousTagNames, innerElement.getName() ) !== -1 ) { + innerElement.moveChildren( elem ); + innerElement.remove(); + } + }; + + /** + * Returns minimal and maximal possible header levels for given editor. + * + * Result will be based on ACF and `config.format_tags`. + * + * @param {CKEDITOR.editor} + * @return {Object} Allowed header level boundaries. + * @return {Number} return.min Minimal allowed level. + * @return {Number} return.email Maximal allowed level. + */ + ParagraphToHeader.prototype._getPossibleLevels = function( editor ) { + var tags = ( editor.config.format_tags || '' ).split( ';' ), + ret = { + min: 1, + max: 6 + }, + i; + + // Filtering tags. + for ( i = tags.length - 1; i >= 0; i-- ) { + // If given tag is not header tag or if it's not allowed by the ACF. + if ( !tags[ i ].match( /^h[1-6]$/i ) || !editor.filter.check( tags[ i ] ) ) { + tags.splice( i, 1 ); + } else { + tags[ i ] = Number( tags[ i ][ 1 ] ); + } + } + + if ( tags.length ) { + // Note if IE8 has to be supported we need to inline sorting here. + tags.sort(); + + ret.min = tags[ 0 ]; + ret.max = tags[ tags.length - 1 ]; + } + + return ret; + }; + + /** + * Returns options dictionary that should be put in form header level select. + * + * @param {CKEDITOR.editor} editor + */ + ParagraphToHeader.prototype._getFormHeaderLeves = function( editor ) { + var dict = {}, + boundaries = this._getPossibleLevels( editor ), + preferredLevel = this._getPreferredLevel( editor ); + + for ( var i = boundaries.min; i <= boundaries.max; i++ ) { + dict[ 'h' + i ] = 'H' + i; + } + + if ( dict[ 'h' + preferredLevel ] ) { + dict[ 'h' + preferredLevel ] += ( ' ' + this.lang.suggested ); + } + + return dict; + }; + + + ParagraphToHeader.prototype.lang = {"levelLabel":"Header level","suggested":"(Suggested)"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/ParagraphToHeader', ParagraphToHeader ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/QuickFix.js b/plugins/ckeditor/a11ychecker/quickfix/en/QuickFix.js new file mode 100644 index 0000000..ed2e47b --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/QuickFix.js @@ -0,0 +1,90 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +(function() { + 'use strict'; + + /** + * QuickFix type for the QuickFix objects. + * + * # Overview + * + * It encapsulates logic responsible for fixing Accessibility issue. + * + * ## Working with QuickFix objects + * + * ### Adding controls to the QuickFix form + * + * Controls can be added in {@link #display} method using {@link CKEDITOR.plugins.a11ychecker.ViewerForm} + * methods. + * + * ### Executing the fix + * + * The fixing logic is placed in {@link #fix} method, so you need to simply call it when + * you're sure to apply the fix. + * + * @since 4.6.0 + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class QuickFix + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function QuickFix( issue ) { + this.issue = issue; + } + + QuickFix.prototype = { + /** + * @member CKEDITOR.plugins.a11ychecker.quickFix.QuickFix + * @property {CKEDITOR.plugins.a11ychecker.Issue} issue Issue object that QuickFix was created for. + */ + issue: null + }; + + QuickFix.prototype.constructor = QuickFix; + + QuickFix.prototype.display = function( form, editor ) { + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + QuickFix.prototype.fix = function( formAttributes, callback ) { + if ( callback ) { + callback( this ); + } + }; + + /** + * Method used to validate data placed in form. + * + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @returns {String[]} Array of error messages. If array is empty, then it means no errors ocurred. + */ + QuickFix.prototype.validate = function( formAttributes ) { + return []; + }; + + /** + * A method called to mark the selection on object before quickfix is applied. + * + * @param {CKEDITOR.dom.selection} selection Editor selection. + */ + QuickFix.prototype.markSelection = function( editor, selection ) { + var rng = editor.createRange(); + rng.setStartBefore( this.issue.element ); + rng.setEndAfter( this.issue.element ); + selection.selectRanges( [ rng ] ); + }; + + QuickFix.prototype.lang = {}; + + QuickFix.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/QuickFix', QuickFix ); +}()); diff --git a/plugins/ckeditor/a11ychecker/quickfix/en/TableHeaders.js b/plugins/ckeditor/a11ychecker/quickfix/en/TableHeaders.js new file mode 100644 index 0000000..3840b30 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/en/TableHeaders.js @@ -0,0 +1,98 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'en', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix adding a caption in the `table` element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class TableHeaders + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function TableHeaders( issue ) { + QuickFix.call( this, issue ); + } + + TableHeaders.prototype = new QuickFix(); + + TableHeaders.prototype.constructor = TableHeaders; + + TableHeaders.prototype.display = function( form ) { + var lang = this.lang; + + form.setInputs( { + position: { + type: 'select', + label: lang.positionLabel, + value: 'row', + options: { + 'both': lang.positionBoth, + 'row': lang.positionHorizontally, + 'col': lang.positionVertically + } + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + TableHeaders.prototype.fix = function( formAttributes, callback ) { + var table = this.issue.element, + headers = formAttributes.position, + newCell, + row, + i; + // Following code copied from CKEditor plugins/table/dialogs/table.js file. + + // Should we make all first cells in a row TH? + if ( headers == 'col' || headers == 'both' ) { + for ( row = 0; row < table.$.rows.length; row++ ) { + if ( !table.$.rows[ row ].cells.length ) { + continue; + } + + newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] ); + newCell.renameNode( 'th' ); + newCell.setAttribute( 'scope', 'row' ); + } + } + + if ( !table.$.tHead && ( headers == 'row' || headers == 'both' ) ) { + var thead = new CKEDITOR.dom.element( table.$.createTHead() ); + var tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); + var theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); + + // Change TD to TH: + for ( i = 0; i < theRow.getChildCount(); i++ ) { + var th = theRow.getChild( i ); + // Skip bookmark nodes. (#6155) + if ( th.type == CKEDITOR.NODE_ELEMENT && !th.data( 'cke-bookmark' ) ) { + th.renameNode( 'th' ); + th.setAttribute( 'scope', 'col' ); + } + } + thead.append( theRow.remove() ); + } + + if ( callback ) { + callback( this ); + } + }; + + TableHeaders.prototype.lang = {"positionLabel":"Position","positionHorizontally":"Horizontally","positionVertically":"Vertically","positionBoth":"Both"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'en/TableHeaders', TableHeaders ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/AddTableCaption.js b/plugins/ckeditor/a11ychecker/quickfix/nl/AddTableCaption.js new file mode 100644 index 0000000..ae5f873 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/AddTableCaption.js @@ -0,0 +1,75 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + + var emptyWhitespaceRegExp = /^[\s\n\r]+$/g; + + /** + * QuickFix adding a caption in the `table` element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AddTableCaption + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AddTableCaption( issue ) { + QuickFix.call( this, issue ); + } + + AddTableCaption.prototype = new QuickFix(); + + AddTableCaption.prototype.constructor = AddTableCaption; + + AddTableCaption.prototype.display = function( form ) { + form.setInputs( { + caption: { + type: 'text', + label: this.lang.captionLabel + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AddTableCaption.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + caption = issueElement.getDocument().createElement( 'caption' ); + + caption.setHtml( formAttributes.caption ); + // Prepend the caption. + issueElement.append( caption, true ); + + if ( callback ) { + callback( this ); + } + }; + + AddTableCaption.prototype.validate = function( formAttributes ) { + var proposedCaption = formAttributes.caption, + ret = []; + + // Test if the caption has only whitespaces. + if ( !proposedCaption || proposedCaption.match( emptyWhitespaceRegExp ) ) { + ret.push( this.lang.errorEmpty ); + } + + return ret; + }; + + AddTableCaption.prototype.lang = {"captionLabel":"Caption","errorEmpty":"Caption (bijschtift) tekst mag niet leeg zijn"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/AddTableCaption', AddTableCaption ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/AnchorsMerge.js b/plugins/ckeditor/a11ychecker/quickfix/nl/AnchorsMerge.js new file mode 100644 index 0000000..95b9d00 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/AnchorsMerge.js @@ -0,0 +1,80 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix merging two or more sibling anchors with the same href. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AnchorsMerge + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AnchorsMerge( issue ) { + QuickFix.call( this, issue ); + } + + AnchorsMerge.prototype = new QuickFix(); + + AnchorsMerge.prototype.constructor = AnchorsMerge; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AnchorsMerge.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + nextSibling = issueElement.getNext(), + initialHref = issueElement.getAttribute( 'href' ), + extraInnerHtml = '', + isWhitespace = function( node ) { + return node.type === CKEDITOR.NODE_TEXT && node.getText().match( /^[\s]*$/ ); + }, + iterationAllowed = function( node ) { + if ( !node ) { + return false; + } + + // We only allow anchors that have the same href as the initial one + return ( node.getName && node.getName() == 'a' && nextSibling.getAttribute( 'href' ) == initialHref ) || + isWhitespace( node ); // Or whitespace text nodes. + }, + curNode; + + while ( iterationAllowed( nextSibling ) ) { + curNode = nextSibling; + + // This html will be added later on to the first anchor. + extraInnerHtml += isWhitespace( curNode ) ? curNode.getText() : curNode.getHtml(); + + // Prepare nextSibling var for next iteration. + nextSibling = curNode.getNext(); + + // And we can remove element safely. + curNode.remove(); + } + + // Adding extra html to first anchor. + if ( extraInnerHtml ) { + issueElement.setHtml( issueElement.getHtml() + extraInnerHtml ); + } + + if ( callback ) { + callback( this ); + } + }; + + AnchorsMerge.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/AnchorsMerge', AnchorsMerge ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRename.js b/plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRename.js new file mode 100644 index 0000000..380f3ab --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRename.js @@ -0,0 +1,86 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix renaming an attribute {@link #attributeName} to another name + * {@link #attributeTargetName}. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AttributeRename + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AttributeRename( issue ) { + QuickFix.call( this, issue ); + } + + AttributeRename.prototype = new QuickFix(); + + AttributeRename.prototype.constructor = AttributeRename; + + /** + * Name of the attribute to be renamed. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + */ + AttributeRename.prototype.attributeName = 'title'; + + /** + * A desired name for the attribute. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + */ + AttributeRename.prototype.attributeTargetName = 'alt'; + + /** + * Gets the proposed new value of the target attribute. + * + * @returns {String} + */ + AttributeRename.prototype.getProposedValue = function() { + return this.issue.element.getAttribute( this.attributeName ) || ''; + }; + + AttributeRename.prototype.display = function( form ) { + form.setInputs( { + value: { + type: 'text', + label: 'Value', + value: this.getProposedValue() + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AttributeRename.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + desiredValue = formAttributes.value; + + // Set desired attribute value. + issueElement.setAttribute( this.attributeTargetName, desiredValue ); + // Unset the old attribute. + issueElement.removeAttribute( this.attributeName ); + + if ( callback ) { + callback( this ); + } + }; + + AttributeRename.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/AttributeRename', AttributeRename ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRenameDefault.js b/plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRenameDefault.js new file mode 100644 index 0000000..64fe7fa --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/AttributeRenameDefault.js @@ -0,0 +1,40 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'AttributeRename', + callback: function( AttributeRename ) { + /** + * QuickFix renaming an attribute {@link #attributeName} to another name + * {@link #attributeTargetName} using a proposed default value + * based on the value of {@link #attributeTargetName}. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AttributeRenameDefault + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AttributeRenameDefault( issue ) { + AttributeRename.call( this, issue ); + } + + AttributeRenameDefault.prototype = new AttributeRename(); + + AttributeRenameDefault.prototype.constructor = AttributeRenameDefault; + + AttributeRenameDefault.prototype.getProposedValue = function() { + var element = this.issue.element; + return element.getAttribute( this.attributeTargetName ) || + element.getAttribute( this.attributeName ) || ''; + }; + + AttributeRenameDefault.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/AttributeRenameDefault', AttributeRenameDefault ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/DateUnfold.js b/plugins/ckeditor/a11ychecker/quickfix/nl/DateUnfold.js new file mode 100644 index 0000000..6a6ff8a --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/DateUnfold.js @@ -0,0 +1,133 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + // List of month names. + var monthNames = { + en: [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ] + }; + + /** + * QuickFix converting short dates to more verbose format. Eg. convert 21-3-2030 + * to 21 March 2030. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class DateUnfold + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function DateUnfold( issue ) { + QuickFix.call( this, issue ); + } + + DateUnfold.prototype = new QuickFix(); + + DateUnfold.prototype.constructor = DateUnfold; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + DateUnfold.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + innerText = issueElement.getText(), + that = this; + + innerText = innerText.replace( /(\d{1,2}[.\/-]\d{1,2}[.\/-]\d{2,4})/g, function( occur ) { + var extractedDate = that.parseDate( occur ); + + return that.getFriendlyDate( extractedDate ); + } ); + + issueElement.setText( innerText ); + + if ( callback ) { + callback( this ); + } + }; + + /** + * Parses given "short" data. Returns an object with 1-based values. + * + * Eg. 21.03.2020 would be parsed to: + * + * { + * day: "21", + * month: "03", + * year: "2020" + * } + * + * @returns {Object} An object containing properties: + * * day + * * month + * * year + */ + DateUnfold.prototype.parseDate = function( dateString ) { + var ret = dateString.split( /[.\-\/]+/ ); + + return { + day: ret[ 0 ], + month: ret[ 1 ], + year: ret[ 2 ] + }; + }; + + /** + * var ret = dateFix.getFriendlyDate( { + * day: "21", + * month: "03", + * year: "2020" + * } ); + * + * // ret === "21 March 2020" + * + * @param {Object} An object returned by {@link #parseDate} + * @returns {String} A human-friendly date representation. + */ + DateUnfold.prototype.getFriendlyDate = function( dateObj ) { + // month - 1 because monthNames is 0-based array. + var monthName = monthNames.en[ Number( dateObj.month - 1 ) ], + year = Number( dateObj.year ); + + // In case of 2 digit year value, lets fix it. + if ( year >= 0 && year < 100 ) { + // Year 70 and greater will be considered 19xx. + if ( year >= 70 ) { + year += 1900; + } else { + // Otherwise we assume it's 20xx. + year += 2000; + } + } + + // Cast day to Number, to remove leading 0. + return [ Number( dateObj.day ), monthName, year ].join( ' ' ); + }; + + DateUnfold.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/DateUnfold', DateUnfold ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/ElementRemove.js b/plugins/ckeditor/a11ychecker/quickfix/nl/ElementRemove.js new file mode 100644 index 0000000..6a2747c --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/ElementRemove.js @@ -0,0 +1,43 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * The ultimate fix for unsolvable problem - removing an element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementRemove + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementRemove( issue ) { + QuickFix.call( this, issue ); + } + + ElementRemove.prototype = new QuickFix(); + ElementRemove.prototype.constructor = ElementRemove; + + ElementRemove.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementRemove.prototype.fix = function( formAttributes, callback ) { + this.issue.element.remove(); + + if ( callback ) { + callback( this ); + } + }; + + ElementRemove.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/ElementRemove', ElementRemove ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/ElementReplace.js b/plugins/ckeditor/a11ychecker/quickfix/nl/ElementReplace.js new file mode 100644 index 0000000..659b7f2 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/ElementReplace.js @@ -0,0 +1,59 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementReplace + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementReplace( issue ) { + QuickFix.call( this, issue ); + } + + ElementReplace.prototype = new QuickFix(); + ElementReplace.prototype.constructor = ElementReplace; + + /** + * Returns the name of the tag that issue.element should be converted to. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.ElementReplace + * @param {Object} formAttributes Form attributes from {@link #fix}. + * @returns {String} + */ + ElementReplace.prototype.getTargetName = function( formAttributes ) { + return 'h1'; + }; + + ElementReplace.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementReplace.prototype.fix = function( formAttributes, callback ) { + var newElement = new CKEDITOR.dom.element( this.getTargetName( formAttributes ) ); + + newElement.replace( this.issue.element ); + this.issue.element.moveChildren( newElement ); + + this.issue.element = newElement; + + if ( callback ) { + callback( this ); + } + }; + + ElementReplace.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/ElementReplace', ElementReplace ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/ImgAlt.js b/plugins/ckeditor/a11ychecker/quickfix/nl/ImgAlt.js new file mode 100644 index 0000000..ee24f8d --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/ImgAlt.js @@ -0,0 +1,89 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + + var emptyWhitespaceRegExp = /^[\s\n\r]+$/g; + + /** + * Fixes the image with missing alt attribute. + * + * @constructor + */ + function ImgAlt( issue ) { + QuickFix.call( this, issue ); + } + + /** + * Maximal count of characters in the alt. It might be changed to `0` to prevent + * length validation. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + * @static + */ + ImgAlt.altLengthLimit = 100; + + ImgAlt.prototype = new QuickFix(); + ImgAlt.prototype.constructor = ImgAlt; + + ImgAlt.prototype.display = function( form ) { + form.setInputs( { + alt: { + type: 'text', + label: this.lang.altLabel, + value: this.issue.element.getAttribute( 'alt' ) || '' + } + } ); + }; + + ImgAlt.prototype.fix = function( formAttributes, callback ) { + this.issue.element.setAttribute( 'alt', formAttributes.alt ); + + if ( callback ) { + callback( this ); + } + }; + + ImgAlt.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + '', + imgElem = this.issue && this.issue.element, + lang = this.lang; + + // Test if the alt has only whitespaces. + if ( proposedAlt.match( emptyWhitespaceRegExp ) ) { + ret.push( lang.errorWhitespace ); + } + + // Testing against exceeding max length. + if ( ImgAlt.altLengthLimit && proposedAlt.length > ImgAlt.altLengthLimit ) { + var errorTemplate = new CKEDITOR.template( lang.errorTooLong ); + + ret.push( errorTemplate.output( { + limit: ImgAlt.altLengthLimit, + length: proposedAlt.length + } ) ); + } + + if ( imgElem ) { + var fileName = String( imgElem.getAttribute( 'src' ) ).split( '/' ).pop(); + if ( fileName == proposedAlt ) { + ret.push( lang.errorSameAsFileName ); + } + } + + return ret; + }; + + ImgAlt.prototype.lang = {"altLabel":"Alternatieve tekst","errorTooLong":"Alternatieve tekst is te lang. Deze mag maximaal {limit} karaktersbevatten terwijl opgegeven tekst {length} bevat","errorWhitespace":"Alternatieve tekst mag niet alleen uit spaties bestaan","errorSameAsFileName":"Alt-tekst van de afbeelding mag niet hetzelfde zijn als de bestandsnaam"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/ImgAlt', ImgAlt ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/ImgAltNonEmpty.js b/plugins/ckeditor/a11ychecker/quickfix/nl/ImgAltNonEmpty.js new file mode 100644 index 0000000..b73aded --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/ImgAltNonEmpty.js @@ -0,0 +1,44 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'ImgAlt', + callback: function( ImgAlt ) { + + /** + * Fixes the image with missing alt attribute, requiring non-empty alt. + * + * @constructor + */ + function ImgAltNonEmpty( issue ) { + ImgAlt.call( this, issue ); + } + + ImgAltNonEmpty.prototype = new ImgAlt(); + ImgAltNonEmpty.prototype.constructor = ImgAltNonEmpty; + + ImgAltNonEmpty.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + ''; + + if ( !proposedAlt ) { + ret.push( this.lang.errorEmpty ); + } + + if ( !ret.length ) { + ret = ImgAlt.prototype.validate.call( this, formAttributes ); + } + + return ret; + }; + + ImgAltNonEmpty.prototype.lang = {"altLabel":"Alternatieve tekst","errorTooLong":"Alternatieve tekst is te lang. Deze mag maximaal {limit} karaktersbevatten terwijl opgegeven tekst {length} bevat","errorWhitespace":"Alternatieve tekst mag niet alleen uit spaties bestaan","errorSameAsFileName":"Alt-tekst van de afbeelding mag niet hetzelfde zijn als de bestandsnaam","errorEmpty":"Alternatieve tekst mag niet leeg zijn"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/ImgAltNonEmpty', ImgAltNonEmpty ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/LocalizedRepository.js b/plugins/ckeditor/a11ychecker/quickfix/nl/LocalizedRepository.js new file mode 100644 index 0000000..6c23482 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/LocalizedRepository.js @@ -0,0 +1,174 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ +define( [ 'quickfix/Repository' ], function( Repository ) { + 'use strict'; + + /** + * This type adds localization support for repository. + * + * @mixins CKEDITOR.event + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @constructor + * @param {String} basePath A path to the directory where QuickFix classes are + * stored. + */ + function LocalizedRepository( basePath ) { + Repository.call( this, basePath ); + } + + LocalizedRepository.prototype = new Repository(); + LocalizedRepository.prototype.constructor = LocalizedRepository; + + LocalizedRepository.prototype._langDictionary = {}; + + // An array of arguments from calls that were deferred. + var deferredGetCalls = {}, + languagesRequested = []; + + /** + * See {@link CKEDITOR.plugins.a11ychecker.quickFix.Repository#get}. This implementation + * adds only `options.langCode` param: + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {Object} options + * @param {String} options.langCode Language code of quickFix to be loaded. + * @returns {Function} + */ + LocalizedRepository.prototype.get = function( options ) { + options.langCode = options.langCode || 'en'; + + if ( this.deferGetCall( options.langCode, arguments ) ) { + // Call should be deferred, because no lang is available yet. + return; + } + + if ( !CKEDITOR.plugins.a11ychecker.dev ) { + // In case of release code we'll do a trick here. + // Each class will be represented as '/' string, that way we + // can load multiple language combination with given class. + options.name = options.langCode + '/' + options.name; + } + + // If lang is available call to the base class. + return Repository.prototype.get.call( this, options ); + }; + + /** + * Similar to {@link #get} but returns localized instance rather than class. + * + * Please note that options.callback is mandatory. + * + * @todo: this method should be also available in generic class. + */ + LocalizedRepository.prototype.getInstance = function( options ) { + options = options || {}; + var name = options.name, + langCode = options.langCode || 'en', + that = this; + + this.get( { + name: name, + callback: function( QuickFixType ) { + // This callback is guaranteed to be called when dictionary for langCode is fetched. + var instance = new QuickFixType( options.issue ); + + if ( CKEDITOR.plugins.a11ychecker.dev ) { + // We only need to assign lang for dev version, built class will already have this property. + instance.lang = that._langDictionary[ langCode ][ name ]; + } + + options.callback( instance ); + }, + langCode: langCode + } ); + }; + + + /** + * If needed will defer {@link #get} call. + * + * @returns {Boolean} `true` if call was deferred, `false` otherwise. + */ + LocalizedRepository.prototype.deferGetCall = function( langCode, getArguments ) { + var indexOf = CKEDITOR.tools.indexOf; + if ( !CKEDITOR.plugins.a11ychecker.dev || this._langDictionary[ langCode ] ) { + // Deferring is always disabled in built version, and if _langDictionary is already + // loaded. + return false; + } + + this._addDeferredGet( langCode, getArguments ); + + if ( indexOf( languagesRequested, langCode ) === -1 ) { + // This particular language was not requested yet. + languagesRequested.push( langCode ); + CKEDITOR.scriptLoader.load( this.basePath + 'lang/' + langCode + '.js' ); + } + + return true; + }; + + /** + * Registers a class of given QuickFix. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {String} name QuickFix name. + * @param {Function} cls QuickFix type. + */ + LocalizedRepository.prototype.add = function( name, cls ) { + return Repository.prototype.add.call( this, name, cls ); + }; + + /** + * Method to register language dictionary for developer version. + * + * In built version languages are already inlined into a QuickFix class file, so there's + * no need to execute it. + * + * @param {Object} dictionary + */ + LocalizedRepository.prototype.lang = function( langCode, dictionary ) { + this._langDictionary[ langCode ] = dictionary; + + var getQueue = deferredGetCalls[ langCode ]; + + if ( !getQueue ) { + // No get calls were queued for that language. + return; + } + + // All deferred gets should be called in reversed order. + for ( var i = getQueue.length - 1; i >= 0; i-- ) { + this.get.apply( this, getQueue[ i ] ); + } + }; + + LocalizedRepository.prototype._addDeferredGet = function( langCode, getArguments ) { + if ( deferredGetCalls[ langCode ] ) { + deferredGetCalls[ langCode ].push( getArguments ); + } else { + deferredGetCalls[ langCode ] = [ getArguments ]; + } + }; + + + /** + * Function created for tests, returns count of deferred get functions. + * + * @returns {Number} + */ + LocalizedRepository.prototype._getDeferredGetCount = function( langCode ) { + return deferredGetCalls[ langCode ] ? deferredGetCalls[ langCode ].length : 0; + }; + + /** + * Function created for tests, clears deferred get queue. + */ + LocalizedRepository.prototype._clearDeferredGetQueue = function() { + deferredGetCalls = {}; + }; + + return LocalizedRepository; +} ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/ParagraphToHeader.js b/plugins/ckeditor/a11ychecker/quickfix/nl/ParagraphToHeader.js new file mode 100644 index 0000000..dafc3f4 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/ParagraphToHeader.js @@ -0,0 +1,168 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'ElementReplace', + callback: function( ElementReplace ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ParagraphToHeader + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ParagraphToHeader( issue ) { + ElementReplace.call( this, issue ); + } + + ParagraphToHeader.prototype = new ElementReplace(); + ParagraphToHeader.prototype.constructor = ParagraphToHeader; + + ParagraphToHeader.prototype.getTargetName = function( formAttributes ) { + return formAttributes.level; + }; + + ParagraphToHeader.prototype.display = function( form, editor ) { + + var levelDict = this._getFormHeaderLeves( editor ); + + form.setInputs( { + level: { + type: 'select', + label: this.lang.levelLabel, + value: 'h' + this._getPreferredLevel( editor ), + options: levelDict + } + } ); + }; + + ParagraphToHeader.prototype.fix = function( formAttributes, callback ) { + var that = this; + ElementReplace.prototype.fix.call( this, formAttributes, function() { + that._removeBoldTag(); + callback( that ); + } ); + }; + + /** + * Determines preferred heading level for the header that should be cerated. + * + * @private + * @param {CKEDITOR.editor} editor + * @returns {Number} Number ranging from `1` to `6`. + */ + ParagraphToHeader.prototype._getPreferredLevel = function( editor ) { + var ret = 1, + editable = editor.editable(), + headerTagRegExp = /^h[1-6]$/i, + range = new CKEDITOR.dom.range( editable.getDocument() ), + walker, + prevElement; + + range.setStartAt( editable, CKEDITOR.POSITION_AFTER_START ); + range.setEndAt( this.issue.element, CKEDITOR.POSITION_BEFORE_START ); + walker = new CKEDITOR.dom.walker( range ); + + while ( ( prevElement = walker.previous() ) ) { + if ( prevElement.getName && prevElement.getName().match( headerTagRegExp ) ) { + ret = Number( prevElement.getName()[ 1 ] ) + 1; + break; + } + } + + // WE can't return a higher value than 7. + return Math.min( ret, 6 ); + }; + + /** + * This method check if issue element contains `` element only as a first and the only child. + * If so we'll remove it, but move its children to the `issue.element`. + */ + ParagraphToHeader.prototype._removeBoldTag = function() { + var isElementEvaluator = function( el ) { + return el.type === CKEDITOR.NODE_ELEMENT; + }, + elem = this.issue.element, + innerElement = elem.getFirst( isElementEvaluator ), + // If first child element is at the same time last element child, then it means it has only this element. + hasSingleElement = innerElement && innerElement.equals( elem.getLast( isElementEvaluator ) ), + suspiciousTagNames = [ 'strong', 'b' ]; + + if ( hasSingleElement && CKEDITOR.tools.indexOf( suspiciousTagNames, innerElement.getName() ) !== -1 ) { + innerElement.moveChildren( elem ); + innerElement.remove(); + } + }; + + /** + * Returns minimal and maximal possible header levels for given editor. + * + * Result will be based on ACF and `config.format_tags`. + * + * @param {CKEDITOR.editor} + * @return {Object} Allowed header level boundaries. + * @return {Number} return.min Minimal allowed level. + * @return {Number} return.email Maximal allowed level. + */ + ParagraphToHeader.prototype._getPossibleLevels = function( editor ) { + var tags = ( editor.config.format_tags || '' ).split( ';' ), + ret = { + min: 1, + max: 6 + }, + i; + + // Filtering tags. + for ( i = tags.length - 1; i >= 0; i-- ) { + // If given tag is not header tag or if it's not allowed by the ACF. + if ( !tags[ i ].match( /^h[1-6]$/i ) || !editor.filter.check( tags[ i ] ) ) { + tags.splice( i, 1 ); + } else { + tags[ i ] = Number( tags[ i ][ 1 ] ); + } + } + + if ( tags.length ) { + // Note if IE8 has to be supported we need to inline sorting here. + tags.sort(); + + ret.min = tags[ 0 ]; + ret.max = tags[ tags.length - 1 ]; + } + + return ret; + }; + + /** + * Returns options dictionary that should be put in form header level select. + * + * @param {CKEDITOR.editor} editor + */ + ParagraphToHeader.prototype._getFormHeaderLeves = function( editor ) { + var dict = {}, + boundaries = this._getPossibleLevels( editor ), + preferredLevel = this._getPreferredLevel( editor ); + + for ( var i = boundaries.min; i <= boundaries.max; i++ ) { + dict[ 'h' + i ] = 'H' + i; + } + + if ( dict[ 'h' + preferredLevel ] ) { + dict[ 'h' + preferredLevel ] += ( ' ' + this.lang.suggested ); + } + + return dict; + }; + + + ParagraphToHeader.prototype.lang = {"levelLabel":"Header niveau","suggested":"(Suggestie)"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/ParagraphToHeader', ParagraphToHeader ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/QuickFix.js b/plugins/ckeditor/a11ychecker/quickfix/nl/QuickFix.js new file mode 100644 index 0000000..2a5ebf3 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/QuickFix.js @@ -0,0 +1,90 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +(function() { + 'use strict'; + + /** + * QuickFix type for the QuickFix objects. + * + * # Overview + * + * It encapsulates logic responsible for fixing Accessibility issue. + * + * ## Working with QuickFix objects + * + * ### Adding controls to the QuickFix form + * + * Controls can be added in {@link #display} method using {@link CKEDITOR.plugins.a11ychecker.ViewerForm} + * methods. + * + * ### Executing the fix + * + * The fixing logic is placed in {@link #fix} method, so you need to simply call it when + * you're sure to apply the fix. + * + * @since 4.6.0 + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class QuickFix + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function QuickFix( issue ) { + this.issue = issue; + } + + QuickFix.prototype = { + /** + * @member CKEDITOR.plugins.a11ychecker.quickFix.QuickFix + * @property {CKEDITOR.plugins.a11ychecker.Issue} issue Issue object that QuickFix was created for. + */ + issue: null + }; + + QuickFix.prototype.constructor = QuickFix; + + QuickFix.prototype.display = function( form, editor ) { + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + QuickFix.prototype.fix = function( formAttributes, callback ) { + if ( callback ) { + callback( this ); + } + }; + + /** + * Method used to validate data placed in form. + * + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @returns {String[]} Array of error messages. If array is empty, then it means no errors ocurred. + */ + QuickFix.prototype.validate = function( formAttributes ) { + return []; + }; + + /** + * A method called to mark the selection on object before quickfix is applied. + * + * @param {CKEDITOR.dom.selection} selection Editor selection. + */ + QuickFix.prototype.markSelection = function( editor, selection ) { + var rng = editor.createRange(); + rng.setStartBefore( this.issue.element ); + rng.setEndAfter( this.issue.element ); + selection.selectRanges( [ rng ] ); + }; + + QuickFix.prototype.lang = {}; + + QuickFix.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/QuickFix', QuickFix ); +}()); diff --git a/plugins/ckeditor/a11ychecker/quickfix/nl/TableHeaders.js b/plugins/ckeditor/a11ychecker/quickfix/nl/TableHeaders.js new file mode 100644 index 0000000..d96a8b5 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/nl/TableHeaders.js @@ -0,0 +1,98 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'nl', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix adding a caption in the `table` element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class TableHeaders + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function TableHeaders( issue ) { + QuickFix.call( this, issue ); + } + + TableHeaders.prototype = new QuickFix(); + + TableHeaders.prototype.constructor = TableHeaders; + + TableHeaders.prototype.display = function( form ) { + var lang = this.lang; + + form.setInputs( { + position: { + type: 'select', + label: lang.positionLabel, + value: 'row', + options: { + 'both': lang.positionBoth, + 'row': lang.positionHorizontally, + 'col': lang.positionVertically + } + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + TableHeaders.prototype.fix = function( formAttributes, callback ) { + var table = this.issue.element, + headers = formAttributes.position, + newCell, + row, + i; + // Following code copied from CKEditor plugins/table/dialogs/table.js file. + + // Should we make all first cells in a row TH? + if ( headers == 'col' || headers == 'both' ) { + for ( row = 0; row < table.$.rows.length; row++ ) { + if ( !table.$.rows[ row ].cells.length ) { + continue; + } + + newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] ); + newCell.renameNode( 'th' ); + newCell.setAttribute( 'scope', 'row' ); + } + } + + if ( !table.$.tHead && ( headers == 'row' || headers == 'both' ) ) { + var thead = new CKEDITOR.dom.element( table.$.createTHead() ); + var tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); + var theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); + + // Change TD to TH: + for ( i = 0; i < theRow.getChildCount(); i++ ) { + var th = theRow.getChild( i ); + // Skip bookmark nodes. (#6155) + if ( th.type == CKEDITOR.NODE_ELEMENT && !th.data( 'cke-bookmark' ) ) { + th.renameNode( 'th' ); + th.setAttribute( 'scope', 'col' ); + } + } + thead.append( theRow.remove() ); + } + + if ( callback ) { + callback( this ); + } + }; + + TableHeaders.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'nl/TableHeaders', TableHeaders ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/AddTableCaption.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AddTableCaption.js new file mode 100644 index 0000000..813d1f1 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AddTableCaption.js @@ -0,0 +1,75 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + + var emptyWhitespaceRegExp = /^[\s\n\r]+$/g; + + /** + * QuickFix adding a caption in the `table` element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AddTableCaption + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AddTableCaption( issue ) { + QuickFix.call( this, issue ); + } + + AddTableCaption.prototype = new QuickFix(); + + AddTableCaption.prototype.constructor = AddTableCaption; + + AddTableCaption.prototype.display = function( form ) { + form.setInputs( { + caption: { + type: 'text', + label: this.lang.captionLabel + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AddTableCaption.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + caption = issueElement.getDocument().createElement( 'caption' ); + + caption.setHtml( formAttributes.caption ); + // Prepend the caption. + issueElement.append( caption, true ); + + if ( callback ) { + callback( this ); + } + }; + + AddTableCaption.prototype.validate = function( formAttributes ) { + var proposedCaption = formAttributes.caption, + ret = []; + + // Test if the caption has only whitespaces. + if ( !proposedCaption || proposedCaption.match( emptyWhitespaceRegExp ) ) { + ret.push( this.lang.errorEmpty ); + } + + return ret; + }; + + AddTableCaption.prototype.lang = {"captionLabel":"Legenda da tabela","errorEmpty":"O texto da legenda da tabela não pode estar vazio"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/AddTableCaption', AddTableCaption ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/AnchorsMerge.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AnchorsMerge.js new file mode 100644 index 0000000..c3515a0 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AnchorsMerge.js @@ -0,0 +1,80 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix merging two or more sibling anchors with the same href. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AnchorsMerge + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AnchorsMerge( issue ) { + QuickFix.call( this, issue ); + } + + AnchorsMerge.prototype = new QuickFix(); + + AnchorsMerge.prototype.constructor = AnchorsMerge; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AnchorsMerge.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + nextSibling = issueElement.getNext(), + initialHref = issueElement.getAttribute( 'href' ), + extraInnerHtml = '', + isWhitespace = function( node ) { + return node.type === CKEDITOR.NODE_TEXT && node.getText().match( /^[\s]*$/ ); + }, + iterationAllowed = function( node ) { + if ( !node ) { + return false; + } + + // We only allow anchors that have the same href as the initial one + return ( node.getName && node.getName() == 'a' && nextSibling.getAttribute( 'href' ) == initialHref ) || + isWhitespace( node ); // Or whitespace text nodes. + }, + curNode; + + while ( iterationAllowed( nextSibling ) ) { + curNode = nextSibling; + + // This html will be added later on to the first anchor. + extraInnerHtml += isWhitespace( curNode ) ? curNode.getText() : curNode.getHtml(); + + // Prepare nextSibling var for next iteration. + nextSibling = curNode.getNext(); + + // And we can remove element safely. + curNode.remove(); + } + + // Adding extra html to first anchor. + if ( extraInnerHtml ) { + issueElement.setHtml( issueElement.getHtml() + extraInnerHtml ); + } + + if ( callback ) { + callback( this ); + } + }; + + AnchorsMerge.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/AnchorsMerge', AnchorsMerge ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRename.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRename.js new file mode 100644 index 0000000..9c392b7 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRename.js @@ -0,0 +1,86 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix renaming an attribute {@link #attributeName} to another name + * {@link #attributeTargetName}. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AttributeRename + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AttributeRename( issue ) { + QuickFix.call( this, issue ); + } + + AttributeRename.prototype = new QuickFix(); + + AttributeRename.prototype.constructor = AttributeRename; + + /** + * Name of the attribute to be renamed. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + */ + AttributeRename.prototype.attributeName = 'title'; + + /** + * A desired name for the attribute. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + */ + AttributeRename.prototype.attributeTargetName = 'alt'; + + /** + * Gets the proposed new value of the target attribute. + * + * @returns {String} + */ + AttributeRename.prototype.getProposedValue = function() { + return this.issue.element.getAttribute( this.attributeName ) || ''; + }; + + AttributeRename.prototype.display = function( form ) { + form.setInputs( { + value: { + type: 'text', + label: 'Value', + value: this.getProposedValue() + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + AttributeRename.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + desiredValue = formAttributes.value; + + // Set desired attribute value. + issueElement.setAttribute( this.attributeTargetName, desiredValue ); + // Unset the old attribute. + issueElement.removeAttribute( this.attributeName ); + + if ( callback ) { + callback( this ); + } + }; + + AttributeRename.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/AttributeRename', AttributeRename ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRenameDefault.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRenameDefault.js new file mode 100644 index 0000000..c737fe8 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/AttributeRenameDefault.js @@ -0,0 +1,40 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'AttributeRename', + callback: function( AttributeRename ) { + /** + * QuickFix renaming an attribute {@link #attributeName} to another name + * {@link #attributeTargetName} using a proposed default value + * based on the value of {@link #attributeTargetName}. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class AttributeRenameDefault + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function AttributeRenameDefault( issue ) { + AttributeRename.call( this, issue ); + } + + AttributeRenameDefault.prototype = new AttributeRename(); + + AttributeRenameDefault.prototype.constructor = AttributeRenameDefault; + + AttributeRenameDefault.prototype.getProposedValue = function() { + var element = this.issue.element; + return element.getAttribute( this.attributeTargetName ) || + element.getAttribute( this.attributeName ) || ''; + }; + + AttributeRenameDefault.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/AttributeRenameDefault', AttributeRenameDefault ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/DateUnfold.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/DateUnfold.js new file mode 100644 index 0000000..b3f0ab0 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/DateUnfold.js @@ -0,0 +1,133 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + // List of month names. + var monthNames = { + en: [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ] + }; + + /** + * QuickFix converting short dates to more verbose format. Eg. convert 21-3-2030 + * to 21 March 2030. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class DateUnfold + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function DateUnfold( issue ) { + QuickFix.call( this, issue ); + } + + DateUnfold.prototype = new QuickFix(); + + DateUnfold.prototype.constructor = DateUnfold; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + DateUnfold.prototype.fix = function( formAttributes, callback ) { + var issueElement = this.issue.element, + innerText = issueElement.getText(), + that = this; + + innerText = innerText.replace( /(\d{1,2}[.\/-]\d{1,2}[.\/-]\d{2,4})/g, function( occur ) { + var extractedDate = that.parseDate( occur ); + + return that.getFriendlyDate( extractedDate ); + } ); + + issueElement.setText( innerText ); + + if ( callback ) { + callback( this ); + } + }; + + /** + * Parses given "short" data. Returns an object with 1-based values. + * + * Eg. 21.03.2020 would be parsed to: + * + * { + * day: "21", + * month: "03", + * year: "2020" + * } + * + * @returns {Object} An object containing properties: + * * day + * * month + * * year + */ + DateUnfold.prototype.parseDate = function( dateString ) { + var ret = dateString.split( /[.\-\/]+/ ); + + return { + day: ret[ 0 ], + month: ret[ 1 ], + year: ret[ 2 ] + }; + }; + + /** + * var ret = dateFix.getFriendlyDate( { + * day: "21", + * month: "03", + * year: "2020" + * } ); + * + * // ret === "21 March 2020" + * + * @param {Object} An object returned by {@link #parseDate} + * @returns {String} A human-friendly date representation. + */ + DateUnfold.prototype.getFriendlyDate = function( dateObj ) { + // month - 1 because monthNames is 0-based array. + var monthName = monthNames.en[ Number( dateObj.month - 1 ) ], + year = Number( dateObj.year ); + + // In case of 2 digit year value, lets fix it. + if ( year >= 0 && year < 100 ) { + // Year 70 and greater will be considered 19xx. + if ( year >= 70 ) { + year += 1900; + } else { + // Otherwise we assume it's 20xx. + year += 2000; + } + } + + // Cast day to Number, to remove leading 0. + return [ Number( dateObj.day ), monthName, year ].join( ' ' ); + }; + + DateUnfold.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/DateUnfold', DateUnfold ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementRemove.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementRemove.js new file mode 100644 index 0000000..e33b6c2 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementRemove.js @@ -0,0 +1,43 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * The ultimate fix for unsolvable problem - removing an element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementRemove + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementRemove( issue ) { + QuickFix.call( this, issue ); + } + + ElementRemove.prototype = new QuickFix(); + ElementRemove.prototype.constructor = ElementRemove; + + ElementRemove.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementRemove.prototype.fix = function( formAttributes, callback ) { + this.issue.element.remove(); + + if ( callback ) { + callback( this ); + } + }; + + ElementRemove.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/ElementRemove', ElementRemove ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementReplace.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementReplace.js new file mode 100644 index 0000000..b89634f --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ElementReplace.js @@ -0,0 +1,59 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ElementReplace + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ElementReplace( issue ) { + QuickFix.call( this, issue ); + } + + ElementReplace.prototype = new QuickFix(); + ElementReplace.prototype.constructor = ElementReplace; + + /** + * Returns the name of the tag that issue.element should be converted to. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.ElementReplace + * @param {Object} formAttributes Form attributes from {@link #fix}. + * @returns {String} + */ + ElementReplace.prototype.getTargetName = function( formAttributes ) { + return 'h1'; + }; + + ElementReplace.prototype.display = function( form ) { + form.setInputs( {} ); + }; + + ElementReplace.prototype.fix = function( formAttributes, callback ) { + var newElement = new CKEDITOR.dom.element( this.getTargetName( formAttributes ) ); + + newElement.replace( this.issue.element ); + this.issue.element.moveChildren( newElement ); + + this.issue.element = newElement; + + if ( callback ) { + callback( this ); + } + }; + + ElementReplace.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/ElementReplace', ElementReplace ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAlt.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAlt.js new file mode 100644 index 0000000..40b4d7e --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAlt.js @@ -0,0 +1,89 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + + var emptyWhitespaceRegExp = /^[\s\n\r]+$/g; + + /** + * Fixes the image with missing alt attribute. + * + * @constructor + */ + function ImgAlt( issue ) { + QuickFix.call( this, issue ); + } + + /** + * Maximal count of characters in the alt. It might be changed to `0` to prevent + * length validation. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.AttributeRename + * @static + */ + ImgAlt.altLengthLimit = 100; + + ImgAlt.prototype = new QuickFix(); + ImgAlt.prototype.constructor = ImgAlt; + + ImgAlt.prototype.display = function( form ) { + form.setInputs( { + alt: { + type: 'text', + label: this.lang.altLabel, + value: this.issue.element.getAttribute( 'alt' ) || '' + } + } ); + }; + + ImgAlt.prototype.fix = function( formAttributes, callback ) { + this.issue.element.setAttribute( 'alt', formAttributes.alt ); + + if ( callback ) { + callback( this ); + } + }; + + ImgAlt.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + '', + imgElem = this.issue && this.issue.element, + lang = this.lang; + + // Test if the alt has only whitespaces. + if ( proposedAlt.match( emptyWhitespaceRegExp ) ) { + ret.push( lang.errorWhitespace ); + } + + // Testing against exceeding max length. + if ( ImgAlt.altLengthLimit && proposedAlt.length > ImgAlt.altLengthLimit ) { + var errorTemplate = new CKEDITOR.template( lang.errorTooLong ); + + ret.push( errorTemplate.output( { + limit: ImgAlt.altLengthLimit, + length: proposedAlt.length + } ) ); + } + + if ( imgElem ) { + var fileName = String( imgElem.getAttribute( 'src' ) ).split( '/' ).pop(); + if ( fileName == proposedAlt ) { + ret.push( lang.errorSameAsFileName ); + } + } + + return ret; + }; + + ImgAlt.prototype.lang = {"altLabel":"Texto alternativo","errorTooLong":"O texto alternativo é muito longo. Este deve conter no máximo {limit} caracteres, enquanto o seu possui {length}","errorWhitespace":"O texto alternativo não pode conter somente espaços em branco.","errorSameAsFileName":"O texto alternativo da imagem não deve ter o mesmo nome do arquivo da imagem"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/ImgAlt', ImgAlt ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAltNonEmpty.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAltNonEmpty.js new file mode 100644 index 0000000..c65cf33 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ImgAltNonEmpty.js @@ -0,0 +1,44 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'ImgAlt', + callback: function( ImgAlt ) { + + /** + * Fixes the image with missing alt attribute, requiring non-empty alt. + * + * @constructor + */ + function ImgAltNonEmpty( issue ) { + ImgAlt.call( this, issue ); + } + + ImgAltNonEmpty.prototype = new ImgAlt(); + ImgAltNonEmpty.prototype.constructor = ImgAltNonEmpty; + + ImgAltNonEmpty.prototype.validate = function( formAttributes ) { + var ret = [], + proposedAlt = formAttributes.alt + ''; + + if ( !proposedAlt ) { + ret.push( this.lang.errorEmpty ); + } + + if ( !ret.length ) { + ret = ImgAlt.prototype.validate.call( this, formAttributes ); + } + + return ret; + }; + + ImgAltNonEmpty.prototype.lang = {"altLabel":"Texto alternativo","errorTooLong":"O texto alternativo é muito longo. Este deve conter no máximo {limit} caracteres, enquanto o seu possui {length}","errorWhitespace":"O texto alternativo não pode conter somente espaços em branco.","errorSameAsFileName":"O texto alternativo da imagem não deve ter o mesmo nome do arquivo da imagem","errorEmpty":"O texto alternativo não deve estar vazio"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/ImgAltNonEmpty', ImgAltNonEmpty ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/LocalizedRepository.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/LocalizedRepository.js new file mode 100644 index 0000000..6c23482 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/LocalizedRepository.js @@ -0,0 +1,174 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ +define( [ 'quickfix/Repository' ], function( Repository ) { + 'use strict'; + + /** + * This type adds localization support for repository. + * + * @mixins CKEDITOR.event + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @constructor + * @param {String} basePath A path to the directory where QuickFix classes are + * stored. + */ + function LocalizedRepository( basePath ) { + Repository.call( this, basePath ); + } + + LocalizedRepository.prototype = new Repository(); + LocalizedRepository.prototype.constructor = LocalizedRepository; + + LocalizedRepository.prototype._langDictionary = {}; + + // An array of arguments from calls that were deferred. + var deferredGetCalls = {}, + languagesRequested = []; + + /** + * See {@link CKEDITOR.plugins.a11ychecker.quickFix.Repository#get}. This implementation + * adds only `options.langCode` param: + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {Object} options + * @param {String} options.langCode Language code of quickFix to be loaded. + * @returns {Function} + */ + LocalizedRepository.prototype.get = function( options ) { + options.langCode = options.langCode || 'en'; + + if ( this.deferGetCall( options.langCode, arguments ) ) { + // Call should be deferred, because no lang is available yet. + return; + } + + if ( !CKEDITOR.plugins.a11ychecker.dev ) { + // In case of release code we'll do a trick here. + // Each class will be represented as '/' string, that way we + // can load multiple language combination with given class. + options.name = options.langCode + '/' + options.name; + } + + // If lang is available call to the base class. + return Repository.prototype.get.call( this, options ); + }; + + /** + * Similar to {@link #get} but returns localized instance rather than class. + * + * Please note that options.callback is mandatory. + * + * @todo: this method should be also available in generic class. + */ + LocalizedRepository.prototype.getInstance = function( options ) { + options = options || {}; + var name = options.name, + langCode = options.langCode || 'en', + that = this; + + this.get( { + name: name, + callback: function( QuickFixType ) { + // This callback is guaranteed to be called when dictionary for langCode is fetched. + var instance = new QuickFixType( options.issue ); + + if ( CKEDITOR.plugins.a11ychecker.dev ) { + // We only need to assign lang for dev version, built class will already have this property. + instance.lang = that._langDictionary[ langCode ][ name ]; + } + + options.callback( instance ); + }, + langCode: langCode + } ); + }; + + + /** + * If needed will defer {@link #get} call. + * + * @returns {Boolean} `true` if call was deferred, `false` otherwise. + */ + LocalizedRepository.prototype.deferGetCall = function( langCode, getArguments ) { + var indexOf = CKEDITOR.tools.indexOf; + if ( !CKEDITOR.plugins.a11ychecker.dev || this._langDictionary[ langCode ] ) { + // Deferring is always disabled in built version, and if _langDictionary is already + // loaded. + return false; + } + + this._addDeferredGet( langCode, getArguments ); + + if ( indexOf( languagesRequested, langCode ) === -1 ) { + // This particular language was not requested yet. + languagesRequested.push( langCode ); + CKEDITOR.scriptLoader.load( this.basePath + 'lang/' + langCode + '.js' ); + } + + return true; + }; + + /** + * Registers a class of given QuickFix. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix.LocalizedRepository + * @param {String} name QuickFix name. + * @param {Function} cls QuickFix type. + */ + LocalizedRepository.prototype.add = function( name, cls ) { + return Repository.prototype.add.call( this, name, cls ); + }; + + /** + * Method to register language dictionary for developer version. + * + * In built version languages are already inlined into a QuickFix class file, so there's + * no need to execute it. + * + * @param {Object} dictionary + */ + LocalizedRepository.prototype.lang = function( langCode, dictionary ) { + this._langDictionary[ langCode ] = dictionary; + + var getQueue = deferredGetCalls[ langCode ]; + + if ( !getQueue ) { + // No get calls were queued for that language. + return; + } + + // All deferred gets should be called in reversed order. + for ( var i = getQueue.length - 1; i >= 0; i-- ) { + this.get.apply( this, getQueue[ i ] ); + } + }; + + LocalizedRepository.prototype._addDeferredGet = function( langCode, getArguments ) { + if ( deferredGetCalls[ langCode ] ) { + deferredGetCalls[ langCode ].push( getArguments ); + } else { + deferredGetCalls[ langCode ] = [ getArguments ]; + } + }; + + + /** + * Function created for tests, returns count of deferred get functions. + * + * @returns {Number} + */ + LocalizedRepository.prototype._getDeferredGetCount = function( langCode ) { + return deferredGetCalls[ langCode ] ? deferredGetCalls[ langCode ].length : 0; + }; + + /** + * Function created for tests, clears deferred get queue. + */ + LocalizedRepository.prototype._clearDeferredGetQueue = function() { + deferredGetCalls = {}; + }; + + return LocalizedRepository; +} ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/ParagraphToHeader.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ParagraphToHeader.js new file mode 100644 index 0000000..d1c1ffb --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/ParagraphToHeader.js @@ -0,0 +1,168 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'ElementReplace', + callback: function( ElementReplace ) { + /** + * Replaces provided element with element that a different tag name, preserving its children. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class ParagraphToHeader + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue + */ + function ParagraphToHeader( issue ) { + ElementReplace.call( this, issue ); + } + + ParagraphToHeader.prototype = new ElementReplace(); + ParagraphToHeader.prototype.constructor = ParagraphToHeader; + + ParagraphToHeader.prototype.getTargetName = function( formAttributes ) { + return formAttributes.level; + }; + + ParagraphToHeader.prototype.display = function( form, editor ) { + + var levelDict = this._getFormHeaderLeves( editor ); + + form.setInputs( { + level: { + type: 'select', + label: this.lang.levelLabel, + value: 'h' + this._getPreferredLevel( editor ), + options: levelDict + } + } ); + }; + + ParagraphToHeader.prototype.fix = function( formAttributes, callback ) { + var that = this; + ElementReplace.prototype.fix.call( this, formAttributes, function() { + that._removeBoldTag(); + callback( that ); + } ); + }; + + /** + * Determines preferred heading level for the header that should be cerated. + * + * @private + * @param {CKEDITOR.editor} editor + * @returns {Number} Number ranging from `1` to `6`. + */ + ParagraphToHeader.prototype._getPreferredLevel = function( editor ) { + var ret = 1, + editable = editor.editable(), + headerTagRegExp = /^h[1-6]$/i, + range = new CKEDITOR.dom.range( editable.getDocument() ), + walker, + prevElement; + + range.setStartAt( editable, CKEDITOR.POSITION_AFTER_START ); + range.setEndAt( this.issue.element, CKEDITOR.POSITION_BEFORE_START ); + walker = new CKEDITOR.dom.walker( range ); + + while ( ( prevElement = walker.previous() ) ) { + if ( prevElement.getName && prevElement.getName().match( headerTagRegExp ) ) { + ret = Number( prevElement.getName()[ 1 ] ) + 1; + break; + } + } + + // WE can't return a higher value than 7. + return Math.min( ret, 6 ); + }; + + /** + * This method check if issue element contains `` element only as a first and the only child. + * If so we'll remove it, but move its children to the `issue.element`. + */ + ParagraphToHeader.prototype._removeBoldTag = function() { + var isElementEvaluator = function( el ) { + return el.type === CKEDITOR.NODE_ELEMENT; + }, + elem = this.issue.element, + innerElement = elem.getFirst( isElementEvaluator ), + // If first child element is at the same time last element child, then it means it has only this element. + hasSingleElement = innerElement && innerElement.equals( elem.getLast( isElementEvaluator ) ), + suspiciousTagNames = [ 'strong', 'b' ]; + + if ( hasSingleElement && CKEDITOR.tools.indexOf( suspiciousTagNames, innerElement.getName() ) !== -1 ) { + innerElement.moveChildren( elem ); + innerElement.remove(); + } + }; + + /** + * Returns minimal and maximal possible header levels for given editor. + * + * Result will be based on ACF and `config.format_tags`. + * + * @param {CKEDITOR.editor} + * @return {Object} Allowed header level boundaries. + * @return {Number} return.min Minimal allowed level. + * @return {Number} return.email Maximal allowed level. + */ + ParagraphToHeader.prototype._getPossibleLevels = function( editor ) { + var tags = ( editor.config.format_tags || '' ).split( ';' ), + ret = { + min: 1, + max: 6 + }, + i; + + // Filtering tags. + for ( i = tags.length - 1; i >= 0; i-- ) { + // If given tag is not header tag or if it's not allowed by the ACF. + if ( !tags[ i ].match( /^h[1-6]$/i ) || !editor.filter.check( tags[ i ] ) ) { + tags.splice( i, 1 ); + } else { + tags[ i ] = Number( tags[ i ][ 1 ] ); + } + } + + if ( tags.length ) { + // Note if IE8 has to be supported we need to inline sorting here. + tags.sort(); + + ret.min = tags[ 0 ]; + ret.max = tags[ tags.length - 1 ]; + } + + return ret; + }; + + /** + * Returns options dictionary that should be put in form header level select. + * + * @param {CKEDITOR.editor} editor + */ + ParagraphToHeader.prototype._getFormHeaderLeves = function( editor ) { + var dict = {}, + boundaries = this._getPossibleLevels( editor ), + preferredLevel = this._getPreferredLevel( editor ); + + for ( var i = boundaries.min; i <= boundaries.max; i++ ) { + dict[ 'h' + i ] = 'H' + i; + } + + if ( dict[ 'h' + preferredLevel ] ) { + dict[ 'h' + preferredLevel ] += ( ' ' + this.lang.suggested ); + } + + return dict; + }; + + + ParagraphToHeader.prototype.lang = {"levelLabel":"Nível de cabeçalho","suggested":"(Sugerido)"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/ParagraphToHeader', ParagraphToHeader ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/QuickFix.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/QuickFix.js new file mode 100644 index 0000000..698ea24 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/QuickFix.js @@ -0,0 +1,90 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +(function() { + 'use strict'; + + /** + * QuickFix type for the QuickFix objects. + * + * # Overview + * + * It encapsulates logic responsible for fixing Accessibility issue. + * + * ## Working with QuickFix objects + * + * ### Adding controls to the QuickFix form + * + * Controls can be added in {@link #display} method using {@link CKEDITOR.plugins.a11ychecker.ViewerForm} + * methods. + * + * ### Executing the fix + * + * The fixing logic is placed in {@link #fix} method, so you need to simply call it when + * you're sure to apply the fix. + * + * @since 4.6.0 + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class QuickFix + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function QuickFix( issue ) { + this.issue = issue; + } + + QuickFix.prototype = { + /** + * @member CKEDITOR.plugins.a11ychecker.quickFix.QuickFix + * @property {CKEDITOR.plugins.a11ychecker.Issue} issue Issue object that QuickFix was created for. + */ + issue: null + }; + + QuickFix.prototype.constructor = QuickFix; + + QuickFix.prototype.display = function( form, editor ) { + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + QuickFix.prototype.fix = function( formAttributes, callback ) { + if ( callback ) { + callback( this ); + } + }; + + /** + * Method used to validate data placed in form. + * + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @returns {String[]} Array of error messages. If array is empty, then it means no errors ocurred. + */ + QuickFix.prototype.validate = function( formAttributes ) { + return []; + }; + + /** + * A method called to mark the selection on object before quickfix is applied. + * + * @param {CKEDITOR.dom.selection} selection Editor selection. + */ + QuickFix.prototype.markSelection = function( editor, selection ) { + var rng = editor.createRange(); + rng.setStartBefore( this.issue.element ); + rng.setEndAfter( this.issue.element ); + selection.selectRanges( [ rng ] ); + }; + + QuickFix.prototype.lang = {}; + + QuickFix.prototype.lang = {}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/QuickFix', QuickFix ); +}()); diff --git a/plugins/ckeditor/a11ychecker/quickfix/pt-br/TableHeaders.js b/plugins/ckeditor/a11ychecker/quickfix/pt-br/TableHeaders.js new file mode 100644 index 0000000..d75e0fa --- /dev/null +++ b/plugins/ckeditor/a11ychecker/quickfix/pt-br/TableHeaders.js @@ -0,0 +1,98 @@ +/** + * @license Copyright (c) 2014-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/license + */ + +( function() { + 'use strict'; + + CKEDITOR.plugins.a11ychecker.quickFixes.get( { langCode: 'pt-br', + name: 'QuickFix', + callback: function( QuickFix ) { + /** + * QuickFix adding a caption in the `table` element. + * + * @member CKEDITOR.plugins.a11ychecker.quickFix + * @class TableHeaders + * @constructor + * @param {CKEDITOR.plugins.a11ychecker.Issue} issue Issue QuickFix is created for. + */ + function TableHeaders( issue ) { + QuickFix.call( this, issue ); + } + + TableHeaders.prototype = new QuickFix(); + + TableHeaders.prototype.constructor = TableHeaders; + + TableHeaders.prototype.display = function( form ) { + var lang = this.lang; + + form.setInputs( { + position: { + type: 'select', + label: lang.positionLabel, + value: 'row', + options: { + 'both': lang.positionBoth, + 'row': lang.positionHorizontally, + 'col': lang.positionVertically + } + } + } ); + }; + + /** + * @param {Object} formAttributes Object containing serialized form inputs. See + * {@link CKEDITOR.plugins.a11ychecker.ViewerForm#serialize}. + * @param {Function} callback Function to be called when a fix was applied. Gets QuickFix object + * as a first parameter. + */ + TableHeaders.prototype.fix = function( formAttributes, callback ) { + var table = this.issue.element, + headers = formAttributes.position, + newCell, + row, + i; + // Following code copied from CKEditor plugins/table/dialogs/table.js file. + + // Should we make all first cells in a row TH? + if ( headers == 'col' || headers == 'both' ) { + for ( row = 0; row < table.$.rows.length; row++ ) { + if ( !table.$.rows[ row ].cells.length ) { + continue; + } + + newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] ); + newCell.renameNode( 'th' ); + newCell.setAttribute( 'scope', 'row' ); + } + } + + if ( !table.$.tHead && ( headers == 'row' || headers == 'both' ) ) { + var thead = new CKEDITOR.dom.element( table.$.createTHead() ); + var tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); + var theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); + + // Change TD to TH: + for ( i = 0; i < theRow.getChildCount(); i++ ) { + var th = theRow.getChild( i ); + // Skip bookmark nodes. (#6155) + if ( th.type == CKEDITOR.NODE_ELEMENT && !th.data( 'cke-bookmark' ) ) { + th.renameNode( 'th' ); + th.setAttribute( 'scope', 'col' ); + } + } + thead.append( theRow.remove() ); + } + + if ( callback ) { + callback( this ); + } + }; + + TableHeaders.prototype.lang = {"positionLabel":"Posição","positionHorizontally":"Horizontal","positionVertically":"Vertical","positionBoth":"Ambos"}; + CKEDITOR.plugins.a11ychecker.quickFixes.add( 'pt-br/TableHeaders', TableHeaders ); + } + } ); +}() ); diff --git a/plugins/ckeditor/a11ychecker/skins/moono-lisa/a11ychecker.css b/plugins/ckeditor/a11ychecker/skins/moono-lisa/a11ychecker.css new file mode 100644 index 0000000..5156640 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/skins/moono-lisa/a11ychecker.css @@ -0,0 +1,4 @@ +/*! +Copyright (c) 20032014-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/license +*/a.cke_a11yc_ui_button{display:inline-block;*display:inline;*zoom:1;padding:4px;margin:0;text-align:center;color:#484848;vertical-align:middle;border:1px solid #bcbcbc;background:#f8f8f8;-webkit-border-radius:3px;-webkit-background-clip:padding-box;-moz-border-radius:3px;-moz-background-clip:padding;border-radius:3px;background-clip:padding-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}a.cke_a11yc_ui_button:hover{cursor:pointer;border-color:#bcbcbc;background:#fff}a.cke_a11yc_ui_button:focus,a.cke_a11yc_ui_button:active{border-color:#139ff7;outline:none}a.cke_a11yc_ui_button>span{padding:0 6px}a.cke_a11yc_ui_button>span:hover{cursor:pointer}a.cke_a11yc_ui_button_ok{color:#fff;border-color:#09863e;background:#09863e}a.cke_a11yc_ui_button_ok:hover{border-color:#53aa78;background:#53aa78}a.cke_a11yc_ui_button_ok>span{color:#fff}select.cke_a11yc_ui_input_select{height:25px;line-height:25px;background-color:#fff;border:1px solid #c9cccf;padding:3px 3px 3px 6px;outline:none;-webkit-border-radius:3px;-webkit-background-clip:padding-box;-moz-border-radius:3px;-moz-background-clip:padding;border-radius:3px;background-clip:padding-box}select.cke_a11yc_ui_input_select:focus{outline:none;border:1px solid #139ff7}.cke_a11yc_ui_navigation{display:table;overflow:hidden;width:100%;background:#eee;outline:1px solid #ddd;border-collapse:separate;border-spacing:10px}.cke_a11yc_ui_navigation.cke_a11yc_testability_notice{background-color:#fefefe}.cke_a11yc_ui_navigation.cke_a11yc_testability_warning{background-color:#fffacd}.cke_a11yc_ui_navigation.cke_a11yc_testability_error{background-color:#ffe4e4}.cke_a11yc_ui_navigation .cke_a11yc_ui_button_wrapper,.cke_a11yc_ui_navigation .cke_a11yc_ui_navigation_counter{display:table-cell}.cke_a11yc_ui_navigation .cke_a11yc_ui_navigation_counter{width:100%;font-size:14px;font-weight:bold;vertical-align:middle;text-align:left;white-space:normal}a.cke_a11yc_ui_previous span,a.cke_a11yc_ui_next span{background-repeat:no-repeat}a.cke_a11yc_ui_next>span{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAKQWlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhUlEQVQIHWM2Nzc/JC4u/hwIXjAwMLAAMTMQMwExA6OlpeV/EOPfv39Hf/782XbhwoWDQO5fEIZLghSAAFDRkd+/f7ecO3duP4YkSMH///+vAE2JAdkBB0BdV4G6OoC61gEF/4AlkQQ3AgXB9gHpfwwmJiaRQAY3EHMBMScQswMxKxAzAwAhokLukwaZHgAAAABJRU5ErkJggg==);background-position:right center;padding-right:16px;margin-right:7px}a.cke_a11yc_ui_previous>span{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAKQWlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhklEQVQIHWNkYGBgBmImJMwIZDOamJj4soAYUAmQImZjY+MgVlbWciBbGSQJ0gUSDAUKVjAyMmoA+Qz/////xWJkZKTLxsa2GCioCRJEBszPnz9/KyIicoeZmVkJqEAWSfIvyD5WIAbbp6en58TJyVnFxMRkATIWJAmzF+5iAwMDew4OjhIAregW7mj1jM8AAAAASUVORK5CYII=);background-position:6px center;padding-left:23px}.cke_a11yc_ui_desc_wrapper{padding:0 10px;margin:15px 0 10px}.cke_a11yc_ui_desc_title,.cke_a11yc_ui_desc_info{white-space:normal}.cke_a11yc_ui_desc_title{font-size:14px;font-weight:bold}.cke_a11yc_ui_desc_info{color:#6e6e6e;margin-top:10px;line-height:1.4em}.cke_a11yc_ui_desc_info code{font-family:monospace;padding:1px 2px;background:#eee;border:1px solid #ccc;-webkit-border-radius:2px;-webkit-background-clip:padding-box;-moz-border-radius:2px;-moz-background-clip:padding;border-radius:2px;background-clip:padding-box;color:inherit}.cke_a11yc_ui_desc_info a{color:#00b2ce;text-decoration:underline}.cke_a11yc_ui_form{margin:10px 0 0;overflow:hidden}.cke_a11yc_ui_form_fieldset{width:100%;display:table;border-collapse:separate;border-spacing:10px 0}.cke_a11yc_ui_form_fieldset:not(:empty){margin-bottom:10px}.cke_a11yc_ui_form_actionset{width:100%;display:table;border-collapse:separate;border-spacing:10px}.cke_a11yc_ui_form_actionset .cke_a11yc_ui_button_wrapper{display:table-cell}.cke_a11yc_ui_form .cke_a11yc_ui_button_ok,.cke_a11yc_ui_form .cke_a11yc_ui_button_ok_wrapper{width:100%}.cke_a11yc_ui_input_label{padding-right:20px;width:10%}.cke_a11yc_ui_input_label:after{content:':'}.cke_a11yc_ui_input_wrapper{display:table-row}.cke_a11yc_ui_input_wrapper+.cke_a11yc_ui_input_wrapper{border-top:10px solid transparent}.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input,.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input_label{display:table-cell}.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input_text,.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input_select{width:100%}input.cke_a11yc_ui_input_text{background-color:#fff;border:1px solid #c9cccf;padding:4px 6px;outline:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-border-radius:3px;-webkit-background-clip:padding-box;-moz-border-radius:3px;-moz-background-clip:padding;border-radius:3px;background-clip:padding-box}input.cke_a11yc_ui_input_text:hover{border:1px solid #aeb3b9}input.cke_a11yc_ui_input_text:focus{outline:none;border:1px solid #139ff7}.cke_a11yc_ui_listening{display:none;overflow:hidden;padding:10px}.cke_a11yc_ui_listening p{white-space:normal}.cke_a11yc_ui_listening strong{font-weight:bold}.cke_a11yc_ui_listening .cke_a11yc_ui_button{cursor:pointer}.cke_a11yc_ui_listening a.cke_a11yc_ui_button{display:block;overflow:hidden;padding-left:15px;margin-top:10px;position:relative}.cke_balloon.cke_a11yc_mode_listening{-webkit-transition:0.3s ease-in-out;-moz-transition:0.3s ease-in-out;-o-transition:0.3s ease-in-out;transition:0.3s ease-in-out}.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_navigation,.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_desc_wrapper,.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_form,.cke_balloon.cke_a11yc_mode_listening .cke_balloon_triangle_outer{display:none}.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_listening{display:block}.cke_hc select:focus,.cke_hc input:focus,.cke_hc a.cke_a11yc_ui_button:focus{border-width:3px}.cke_hc select:focus.cke_a11yc_ui_button,.cke_hc input:focus.cke_a11yc_ui_button,.cke_hc a.cke_a11yc_ui_button:focus.cke_a11yc_ui_button{padding:2px}.cke_hc select:focus.cke_a11yc_ui_input_select,.cke_hc input:focus.cke_a11yc_ui_input_select,.cke_hc a.cke_a11yc_ui_button:focus.cke_a11yc_ui_input_select{padding:1px 1px 1px 4px}.cke_hc select:focus.cke_a11yc_ui_input_text,.cke_hc input:focus.cke_a11yc_ui_input_text,.cke_hc a.cke_a11yc_ui_button:focus.cke_a11yc_ui_input_text{padding:2px 4px}.cke_a11yc_ui_button_wrapper a.cke_a11yc_ui_button{padding:10px 5px;-webkit-border-radius:2px;-webkit-background-clip:padding-box;-moz-border-radius:2px;-moz-background-clip:padding;border-radius:2px;background-clip:padding-box}.cke_a11yc_ui_button_wrapper a.cke_a11yc_ui_button span{font-size:15px;line-height:16px}.cke_a11yc_ui_button_wrapper a.cke_a11yc_ui_button:focus,.cke_a11yc_ui_button_wrapper a.cke_a11yc_ui_button:active{padding:9px 4px;border-width:2px}.cke_a11yc_ui_button_wrapper a.cke_a11yc_ui_button_ignore{min-width:100px}a.cke_a11yc_ui_button_ok:focus{box-shadow:inset 0 0 0 2px #fff}.cke_a11yc_ui_navigation{padding:0 6px;margin:1px 0 0 -6px}.cke_a11yc_ui_navigation a.cke_a11yc_ui_button{position:relative}.cke_a11yc_ui_navigation a.cke_a11yc_ui_button:focus,.cke_a11yc_ui_navigation a.cke_a11yc_ui_button:active{border:solid 2px #139ff7;z-index:1}.cke_a11yc_ui_navigation a.cke_a11yc_ui_button:hover,.cke_a11yc_ui_navigation a.cke_a11yc_ui_button:hover>span{background-color:#fff}.cke_a11yc_ui_navigation a.cke_a11yc_ui_previous>span,.cke_a11yc_ui_navigation a.cke_a11yc_ui_next>span{background:none;padding:0;margin:0}.cke_a11yc_ui_navigation a.cke_a11yc_ui_previous>span>span,.cke_a11yc_ui_navigation a.cke_a11yc_ui_next>span>span{display:none}.cke_a11yc_ui_navigation a.cke_a11yc_ui_previous{padding:9px 14px 9px 6px}.cke_a11yc_ui_navigation a.cke_a11yc_ui_previous:focus,.cke_a11yc_ui_navigation a.cke_a11yc_ui_previous:active{padding:8px 13px 8px 5px}.cke_a11yc_ui_navigation a.cke_a11yc_ui_previous>span:before{content:'';display:inline-block;width:0;height:0;border:6px solid transparent;vertical-align:middle}.cke_a11yc_ui_navigation a.cke_a11yc_ui_next{padding:9px 6px 9px 14px;margin-left:-11px}.cke_a11yc_ui_navigation a.cke_a11yc_ui_next:focus,.cke_a11yc_ui_navigation a.cke_a11yc_ui_next:active{padding:8px 5px 8px 13px}.cke_a11yc_ui_navigation a.cke_a11yc_ui_next>span:after{content:'';display:inline-block;width:0;height:0;border:6px solid transparent;vertical-align:middle}.cke_a11yc_ui_navigation.cke_a11yc_testability_notice{outline-color:#d1d1d1}.cke_a11yc_ui_navigation.cke_a11yc_testability_notice .cke_a11yc_ui_button{border-color:#d1d1d1;background-color:#fafafa}.cke_a11yc_ui_navigation.cke_a11yc_testability_notice a.cke_a11yc_ui_previous>span:before{border-right-color:#797979}.cke_a11yc_ui_navigation.cke_a11yc_testability_notice a.cke_a11yc_ui_next>span:after{border-left-color:#797979}.cke_a11yc_ui_navigation.cke_a11yc_testability_notice .cke_a11yc_ui_navigation_counter{color:#797979}.cke_a11yc_ui_navigation.cke_a11yc_testability_warning{outline-color:#c9c597}.cke_a11yc_ui_navigation.cke_a11yc_testability_warning .cke_a11yc_ui_button{border-color:#c9c597;background-color:#fffffb}.cke_a11yc_ui_navigation.cke_a11yc_testability_warning a.cke_a11yc_ui_previous>span:before{border-right-color:#858048}.cke_a11yc_ui_navigation.cke_a11yc_testability_warning a.cke_a11yc_ui_next>span:after{border-left-color:#858048}.cke_a11yc_ui_navigation.cke_a11yc_testability_warning .cke_a11yc_ui_navigation_counter{color:#858048}.cke_a11yc_ui_navigation.cke_a11yc_testability_error{outline-color:#e9b6b6}.cke_a11yc_ui_navigation.cke_a11yc_testability_error .cke_a11yc_ui_button{border-color:#e9b6b6;background-color:#fffbfb}.cke_a11yc_ui_navigation.cke_a11yc_testability_error a.cke_a11yc_ui_previous>span:before{border-right-color:#be5555}.cke_a11yc_ui_navigation.cke_a11yc_testability_error a.cke_a11yc_ui_next>span:after{border-left-color:#be5555}.cke_a11yc_ui_navigation.cke_a11yc_testability_error .cke_a11yc_ui_navigation_counter{color:#be5555}.cke_a11yc_ui_desc_wrapper{margin-top:30px}.cke_a11yc_ui_desc_title{color:#484848}.cke_a11yc_ui_form{margin-top:20px}.cke_a11yc_ui_form_fieldset,.cke_a11yc_ui_form_actionset{margin-bottom:0}.cke_a11yc_ui_form .cke_a11yc_ui_button_ok{width:98%}.cke_a11yc_ui_input_label{color:#4b4b4b;font-size:14px;padding-right:0}input.cke_a11yc_ui_input_text{padding:6px;-webkit-border-radius:2px;-webkit-background-clip:padding-box;-moz-border-radius:2px;-moz-background-clip:padding;border-radius:2px;background-clip:padding-box}input.cke_a11yc_ui_input_text:hover{border:1px solid #c9cccf}input.cke_a11yc_ui_input_text:focus{border:2px solid #139ff7;padding:5px}select.cke_a11yc_ui_input_select{height:28px;line-height:28px;padding:5px 3px 4px 6px;border:1px solid #eee;-webkit-border-radius:2px;-webkit-background-clip:padding-box;-moz-border-radius:2px;-moz-background-clip:padding;border-radius:2px;background-clip:padding-box;-webkit-box-shadow:inset 0 0 0 1px #c9cccf;-moz-box-shadow:inset 0 0 0 1px #c9cccf;box-shadow:inset 0 0 0 1px #c9cccf}select.cke_a11yc_ui_input_select:focus{border-color:#139ff7;-webkit-box-shadow:inset 0 0 0 1px #139ff7;-moz-box-shadow:inset 0 0 0 1px #139ff7;box-shadow:inset 0 0 0 1px #139ff7}.cke_a11yc_ui_listening p{padding:0 5px;text-align:center}.cke_a11yc_ui_listening a.cke_a11yc_ui_button{padding-top:9px;padding-bottom:9px;background:#09863e;border-width:0}.cke_a11yc_ui_listening a.cke_a11yc_ui_button:hover{background:#53aa78}.cke_a11yc_ui_listening a.cke_a11yc_ui_button:focus,.cke_a11yc_ui_listening a.cke_a11yc_ui_button:active{border:2px solid #139ff7;padding:7px 2px 7px 13px}.cke_a11yc_ui_listening a.cke_a11yc_ui_button:focus{box-shadow:inset 0 0 0 2px #fff}.cke_a11yc_ui_listening a.cke_a11yc_ui_button span{color:#fff}_::-webkit-:not(:root:root), .cke_a11yc_ui_form select.cke_a11yc_ui_input_select{width:calc(100% - 2px);border:1px solid #c9cccf;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}_::-webkit-:not(:root:root), .cke_a11yc_ui_form select.cke_a11yc_ui_input_select:focus{border:2px solid #139ff7;width:100%;margin:0}_::-webkit-:not(:root:root), .cke_a11yc_ui_form .cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input_select{width:calc(100% - 2px);margin:0 1px} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/skins/moono-lisa/contents.css b/plugins/ckeditor/a11ychecker/skins/moono-lisa/contents.css new file mode 100644 index 0000000..f958b36 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/skins/moono-lisa/contents.css @@ -0,0 +1,4 @@ +/*! +Copyright (c) 20032014-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/license +*/.cke_editable.cke_loading a,.cke_editable.cke_loading img,.cke_editable.cke_loading.cke_editable_inline.cke_focus{cursor:wait}.cke_editable .cke_a11yc_issue{outline:3px solid #ff9f9f;background-color:#ffe4e4;font-style:italic}.cke_editable .cke_a11yc_issue:hover{cursor:pointer}.cke_editable .cke_a11yc_issue.cke_a11yc_notice{outline-color:#dcdcdc;background-color:#fefefe}.cke_editable .cke_a11yc_issue.cke_a11yc_warning{outline-color:#ffe4b5;background-color:#fffacd}.cke_editable .cke_a11yc_issue.cke_a11yc_error{outline-color:#ff9f9f;background-color:#ffe4e4}.cke_editable .cke_a11yc_issue.cke_a11yc_ignored{outline-color:#dcdcdc;background-color:transparent}.cke_editable .cke_a11yc_issue.cke_a11yc_focused:hover{cursor:auto}.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_error,.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_warning,.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_notice{outline-color:#e87b23;background-color:#f6d9be}.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_ignored{outline-color:#a9a9a9;background-color:transparent} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/skins/moono/a11ychecker.css b/plugins/ckeditor/a11ychecker/skins/moono/a11ychecker.css new file mode 100644 index 0000000..e91b185 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/skins/moono/a11ychecker.css @@ -0,0 +1,4 @@ +/*! +Copyright (c) 20032014-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/license +*/a.cke_a11yc_ui_button{display:inline-block;*display:inline;*zoom:1;padding:4px;margin:0;text-align:center;color:#333;vertical-align:middle;border:1px solid #b6b6b6;background:#e4e4e4;-webkit-border-radius:3px;-webkit-background-clip:padding-box;-moz-border-radius:3px;-moz-background-clip:padding;border-radius:3px;background-clip:padding-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}a.cke_a11yc_ui_button:hover{cursor:pointer;border-color:#9e9e9e;background:#ccc}a.cke_a11yc_ui_button:focus,a.cke_a11yc_ui_button:active{border-color:#969696;outline:none}a.cke_a11yc_ui_button>span{padding:0 6px}a.cke_a11yc_ui_button>span:hover{cursor:pointer}a.cke_a11yc_ui_button_ok{color:#fff;border-color:#62a60a #62a60a #4d9200;background:#69b10b}a.cke_a11yc_ui_button_ok:hover{border-color:#5b9909 #5b9909 #478500;background:#88be14}a.cke_a11yc_ui_button_ok>span{color:#fff}select.cke_a11yc_ui_input_select{height:25px;line-height:25px;background-color:#fff;border:1px solid #c9cccf;padding:3px 3px 3px 6px;outline:none;-webkit-border-radius:3px;-webkit-background-clip:padding-box;-moz-border-radius:3px;-moz-background-clip:padding;border-radius:3px;background-clip:padding-box}select.cke_a11yc_ui_input_select:focus{outline:none;border:1px solid #139ff7}.cke_a11yc_ui_navigation{display:table;overflow:hidden;width:100%;background:#eee;outline:1px solid #ddd;border-collapse:separate;border-spacing:10px}.cke_a11yc_ui_navigation.cke_a11yc_testability_notice{background-color:#f1f1f1}.cke_a11yc_ui_navigation.cke_a11yc_testability_warning{background-color:#fffacd}.cke_a11yc_ui_navigation.cke_a11yc_testability_error{background-color:#ffe4e4}.cke_a11yc_ui_navigation .cke_a11yc_ui_button_wrapper,.cke_a11yc_ui_navigation .cke_a11yc_ui_navigation_counter{display:table-cell}.cke_a11yc_ui_navigation .cke_a11yc_ui_navigation_counter{width:100%;font-size:14px;font-weight:bold;vertical-align:middle;text-align:left;white-space:normal}a.cke_a11yc_ui_previous span,a.cke_a11yc_ui_next span{background-repeat:no-repeat}a.cke_a11yc_ui_next>span{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAKQWlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhUlEQVQIHWM2Nzc/JC4u/hwIXjAwMLAAMTMQMwExA6OlpeV/EOPfv39Hf/782XbhwoWDQO5fEIZLghSAAFDRkd+/f7ecO3duP4YkSMH///+vAE2JAdkBB0BdV4G6OoC61gEF/4AlkQQ3AgXB9gHpfwwmJiaRQAY3EHMBMScQswMxKxAzAwAhokLukwaZHgAAAABJRU5ErkJggg==);background-position:right center;padding-right:16px;margin-right:7px}a.cke_a11yc_ui_previous>span{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAKQWlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhklEQVQIHWNkYGBgBmImJMwIZDOamJj4soAYUAmQImZjY+MgVlbWciBbGSQJ0gUSDAUKVjAyMmoA+Qz/////xWJkZKTLxsa2GCioCRJEBszPnz9/KyIicoeZmVkJqEAWSfIvyD5WIAbbp6en58TJyVnFxMRkATIWJAmzF+5iAwMDew4OjhIAregW7mj1jM8AAAAASUVORK5CYII=);background-position:6px center;padding-left:23px}.cke_a11yc_ui_desc_wrapper{padding:0 10px;margin:15px 0 10px}.cke_a11yc_ui_desc_title,.cke_a11yc_ui_desc_info{white-space:normal}.cke_a11yc_ui_desc_title{font-size:14px;font-weight:bold}.cke_a11yc_ui_desc_info{color:#6e6e6e;margin-top:10px;line-height:1.4em}.cke_a11yc_ui_desc_info code{font-family:monospace;padding:1px 2px;background:#eee;border:1px solid #ccc;-webkit-border-radius:2px;-webkit-background-clip:padding-box;-moz-border-radius:2px;-moz-background-clip:padding;border-radius:2px;background-clip:padding-box;color:inherit}.cke_a11yc_ui_desc_info a{color:#00b2ce;text-decoration:underline}.cke_a11yc_ui_form{margin:10px 0 0;overflow:hidden}.cke_a11yc_ui_form_fieldset{width:100%;display:table;border-collapse:separate;border-spacing:10px 0}.cke_a11yc_ui_form_fieldset:not(:empty){margin-bottom:10px}.cke_a11yc_ui_form_actionset{width:100%;display:table;border-collapse:separate;border-spacing:10px}.cke_a11yc_ui_form_actionset .cke_a11yc_ui_button_wrapper{display:table-cell}.cke_a11yc_ui_form .cke_a11yc_ui_button_ok,.cke_a11yc_ui_form .cke_a11yc_ui_button_ok_wrapper{width:100%}.cke_a11yc_ui_input_label{padding-right:20px;width:10%}.cke_a11yc_ui_input_label:after{content:':'}.cke_a11yc_ui_input_wrapper{display:table-row}.cke_a11yc_ui_input_wrapper+.cke_a11yc_ui_input_wrapper{border-top:10px solid transparent}.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input,.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input_label{display:table-cell}.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input_text,.cke_a11yc_ui_input_wrapper .cke_a11yc_ui_input_select{width:100%}input.cke_a11yc_ui_input_text{background-color:#fff;border:1px solid #c9cccf;padding:4px 6px;outline:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-border-radius:3px;-webkit-background-clip:padding-box;-moz-border-radius:3px;-moz-background-clip:padding;border-radius:3px;background-clip:padding-box}input.cke_a11yc_ui_input_text:hover{border:1px solid #aeb3b9}input.cke_a11yc_ui_input_text:focus{outline:none;border:1px solid #139ff7}.cke_a11yc_ui_listening{display:none;overflow:hidden;padding:10px}.cke_a11yc_ui_listening p{white-space:normal}.cke_a11yc_ui_listening strong{font-weight:bold}.cke_a11yc_ui_listening .cke_a11yc_ui_button{cursor:pointer}.cke_a11yc_ui_listening a.cke_a11yc_ui_button{display:block;overflow:hidden;padding-left:15px;margin-top:10px;position:relative}.cke_balloon.cke_a11yc_mode_listening{-webkit-transition:0.3s ease-in-out;-moz-transition:0.3s ease-in-out;-o-transition:0.3s ease-in-out;transition:0.3s ease-in-out}.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_navigation,.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_desc_wrapper,.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_form,.cke_balloon.cke_a11yc_mode_listening .cke_balloon_triangle_outer{display:none}.cke_balloon.cke_a11yc_mode_listening .cke_a11yc_ui_listening{display:block}.cke_hc select:focus,.cke_hc input:focus,.cke_hc a.cke_a11yc_ui_button:focus{border-width:3px}.cke_hc select:focus.cke_a11yc_ui_button,.cke_hc input:focus.cke_a11yc_ui_button,.cke_hc a.cke_a11yc_ui_button:focus.cke_a11yc_ui_button{padding:2px}.cke_hc select:focus.cke_a11yc_ui_input_select,.cke_hc input:focus.cke_a11yc_ui_input_select,.cke_hc a.cke_a11yc_ui_button:focus.cke_a11yc_ui_input_select{padding:1px 1px 1px 4px}.cke_hc select:focus.cke_a11yc_ui_input_text,.cke_hc input:focus.cke_a11yc_ui_input_text,.cke_hc a.cke_a11yc_ui_button:focus.cke_a11yc_ui_input_text{padding:2px 4px}a.cke_a11yc_ui_button{border-bottom-color:#999;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5), 0 0 2px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.15) inset;-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5), 0 0 2px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.15) inset;box-shadow:0 1px 0 rgba(255, 255, 255, 0.5), 0 0 2px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.15) inset;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U0ZTRlNCIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);background-image:-webkit-linear-gradient(top, #ffffff, #e4e4e4);background-image:-moz-linear-gradient(top, #ffffff, #e4e4e4);background-image:-o-linear-gradient(top, #ffffff, #e4e4e4);background-image:linear-gradient(to bottom, #ffffff, #e4e4e4)}a.cke_a11yc_ui_button:hover{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmMmYyZjIiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2NjY2NjYyIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);background-image:-webkit-linear-gradient(top, #f2f2f2, #cccccc);background-image:-moz-linear-gradient(top, #f2f2f2, #cccccc);background-image:-o-linear-gradient(top, #f2f2f2, #cccccc);background-image:linear-gradient(to bottom, #f2f2f2, #cccccc)}a.cke_a11yc_ui_button:focus,a.cke_a11yc_ui_button:active{-webkit-box-shadow:0 0 6px rgba(0, 0, 0, 0.4) inset;-moz-box-shadow:0 0 6px rgba(0, 0, 0, 0.4) inset;box-shadow:0 0 6px rgba(0, 0, 0, 0.4) inset}a.cke_a11yc_ui_button>span{text-shadow:0 1px 0 #fff}a.cke_a11yc_ui_button_ok{text-shadow:0 -1px 0 #55830c;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM5YWQ3MTciIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzY5YjEwYiIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);background-image:-webkit-linear-gradient(top, #9ad717, #69b10b);background-image:-moz-linear-gradient(top, #9ad717, #69b10b);background-image:-o-linear-gradient(top, #9ad717, #69b10b);background-image:linear-gradient(to bottom, #9ad717, #69b10b)}a.cke_a11yc_ui_button_ok:hover{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM4OGJlMTQiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzVkOWMwYSIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);background-image:-webkit-linear-gradient(top, #88be14 0%, #5d9c0a 100%);background-image:-moz-linear-gradient(top, #88be14 0%, #5d9c0a 100%);background-image:-o-linear-gradient(top, #88be14 0%, #5d9c0a 100%);background-image:linear-gradient(to bottom, #88be14 0%, #5d9c0a 100%)}a.cke_a11yc_ui_button_ok>span{text-shadow:0 -1px 0 #55830c}.cke_a11yc_ui_desc_info{font-style:italic}select.cke_a11yc_ui_input_select{border-top-color:#aeb3b9;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.15) inset;-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.15) inset;box-shadow:0 1px 2px rgba(0, 0, 0, 0.15) inset}select.cke_a11yc_ui_input_select:focus{border-top-color:#1392e9}input.cke_a11yc_ui_input_text{border-top-color:#aeb3b9;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.15) inset;-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.15) inset;box-shadow:0 1px 2px rgba(0, 0, 0, 0.15) inset}input.cke_a11yc_ui_input_text:hover{border-top-color:#a0a6ad}input.cke_a11yc_ui_input_text:focus{border-top-color:#1392e9}.cke_a11yc_ui_listening a.cke_a11yc_ui_button :before{content:url(images/reload.png );position:absolute;top:5px;left:5px} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/skins/moono/contents.css b/plugins/ckeditor/a11ychecker/skins/moono/contents.css new file mode 100644 index 0000000..3b56e77 --- /dev/null +++ b/plugins/ckeditor/a11ychecker/skins/moono/contents.css @@ -0,0 +1,4 @@ +/*! +Copyright (c) 20032014-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/license +*/.cke_editable.cke_loading a,.cke_editable.cke_loading img,.cke_editable.cke_loading.cke_editable_inline.cke_focus{cursor:wait}.cke_editable .cke_a11yc_issue{outline:3px solid #ff9f9f;background-color:#ffe4e4;font-style:italic}.cke_editable .cke_a11yc_issue:hover{cursor:pointer}.cke_editable .cke_a11yc_issue.cke_a11yc_notice{outline-color:#dcdcdc;background-color:#f1f1f1}.cke_editable .cke_a11yc_issue.cke_a11yc_warning{outline-color:#ffe4b5;background-color:#fffacd}.cke_editable .cke_a11yc_issue.cke_a11yc_error{outline-color:#ff9f9f;background-color:#ffe4e4}.cke_editable .cke_a11yc_issue.cke_a11yc_ignored{outline-color:#dcdcdc;background-color:transparent}.cke_editable .cke_a11yc_issue.cke_a11yc_focused:hover{cursor:auto}.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_error,.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_warning,.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_notice{outline-color:#e87b23;background-color:#f6d9be}.cke_editable .cke_a11yc_issue.cke_a11yc_focused.cke_a11yc_ignored{outline-color:#a9a9a9;background-color:transparent} \ No newline at end of file diff --git a/plugins/ckeditor/a11ychecker/skins/moono/images/reload.png b/plugins/ckeditor/a11ychecker/skins/moono/images/reload.png new file mode 100644 index 0000000000000000000000000000000000000000..a47b8d98090994a0595d875ebfe22624ab471729 GIT binary patch literal 603 zcmV-h0;K(kP)2)QuD zJ#)^?nK@TxMp2bYCBpeD$j)He2P`6+!IYMw-qEqKvH60lLKvTo_dCyH;2>+3W@WyT zrtRq!VO)!kIO8Qy3alBR1LVLlzy*9sX2;XCJuN^CssyoS?otan_0U)BD;|_+fSDh`G3ea;`}8O pyXDo7Y#Y$Q`FrEdi78t}{tGThis is my panel

' + * } ); + * + * // Attach the panel to an element in DOM and show it immediately. + * panel.attach( domElement ); + * + * @class + * @since 4.6.0 + * @param {CKEDITOR.editor} editor The editor instance for which the panel is created. + * @param {Object} definition An object containing the panel definition. + */ + CKEDITOR.ui.balloonPanel = function( editor, definition ) { + /** + * The editor for this balloon panel. + */ + this.editor = editor; + + CKEDITOR.tools.extend( this, { + /** + * The default width of the balloon panel. + */ + width: 360, + + /** + * The default height of the balloon panel. + */ + height: 'auto', + + /** + * The default width of the triangle that points to the element in the editable. + */ + triangleWidth: 20, + + /** + * The default height of the triangle that points to the element in the editable. + */ + triangleHeight: 20, + + /** + * The default distance between the triangle and the vertical edge of the panel. + */ + triangleMinDistance: 40 + }, definition, true ); + + /** + * Templates for UI elements in this balloon panel. + * See {@link #templateDefinitions}, {@link #parts}. + */ + this.templates = {}; + + for ( var t in this.templateDefinitions ) { + this.templates[ t ] = new CKEDITOR.template( this.templateDefinitions[ t ] ); + } + + /** + * @property parts The UI elements of the balloon panel. + * @property {CKEDITOR.dom.element} parts.title The title bar of the panel. + * @property {CKEDITOR.dom.element} parts.close The Close button. + * @property {CKEDITOR.dom.element} parts.content The element that stores the content of the panel. + * @property {CKEDITOR.dom.element} parts.panel The undermost element that stores all other elements. The panel is positioned absolutely. + * @property {CKEDITOR.dom.element} parts.triangle The panel's triangle. + * @property {CKEDITOR.dom.element} parts.triangleOuter The outer element of the triangle. + * @property {CKEDITOR.dom.element} parts.triangleInner The inner element of the triangle. + */ + this.parts = {}; + + /** + * Focusable elements in this balloon panel. + * See {@link #registerFocusable}, {@link #deregisterFocusable} and {@link CKEDITOR.focusManager}. + */ + this.focusables = {}; + + /** + * Event listeners associated with this balloon panel, activated on panel show. + * See {@link #addShowListener}, {@link #activateShowListeners}, {@link #deactivateShowListeners}. + */ + this.showListeners = {}; + + /** + * Event listeners associated with this balloon panel, active as long as the panel is shown. + * See {@link #addShowListener}, {@link #activateShowListeners}, {@link #deactivateShowListeners}. + */ + this.activeShowListeners = {}; + + /** + * @property rect Contains panel properties as {@link #move}, {@link #resize}, + * {@link #method-show} and {@link #method-hide} are called. It stores values and avoids unnecessary + * and expensive checks in the future. + * + * @property {Number} rect.width + * @property {Number} rect.height + * @property {Number} rect.top + * @property {Number} rect.left + * @property {Boolean} rect.visible + */ + this.rect = { + visible: false + }; + + // Build the UI of the balloon panel. + this.build(); + + // Handle panel destruction. + editor.on( 'destroy', function() { + this.destroy(); + }, this ); + + /** + * Event fired when the balloon panel is shown. + * + * @event show + */ + + /** + * Event fired when the balloon panel is hidden. + * + * @event hide + */ + + /** + * Event fired when the balloon panel is attached to an element. + * + * @event attach + */ + }; + + CKEDITOR.ui.balloonPanel.prototype = { + /** + * @property templateDefinitions Balloon panel templates. Automatically converted into a {@link CKEDITOR.template} in the panel constructor. + * @property {String} templateDefinitions.panel The template for the panel outermost container. + * @property {String} templateDefinitions.content The template for the panel content container. + * @property {String} templateDefinitions.title The template for the panel title bar. + * @property {String} templateDefinitions.close The template for the panel Close button. + * @property {String} templateDefinitions.triangleOuter The template for the panel outer triangle. + * @property {String} templateDefinitions.triangleInner The template for the panel inner triangle. + */ + templateDefinitions: { + panel: + '', + + content: '
{content}
', + + title: '', + + close: + '' + + 'X' + + '', + + triangleOuter: '', + + triangleInner: '' + }, + + /** + * Builds the UI of the balloon panel. + */ + build: function() { + var editor = this.editor; + + this.parts = { + title: CKEDITOR.dom.element.createFromHtml( this.templates.title.output( { + title: this.title + } ) ), + + close: CKEDITOR.dom.element.createFromHtml( this.templates.close.output() ), + + panel: CKEDITOR.dom.element.createFromHtml( this.templates.panel.output( { + id: editor.id, + langDir: editor.lang.dir, + langCode: editor.langCode, + name: editor.name, + style: 'display:none;', + voiceLabel: editor.lang.editorPanel + ', ' + editor.name + } ) ), + + content: CKEDITOR.dom.element.createFromHtml( this.templates.content.output( { + content: this.content || '' + } ) ), + + triangleOuter: CKEDITOR.dom.element.createFromHtml( this.templates.triangleOuter.output() ), + + triangleInner: CKEDITOR.dom.element.createFromHtml( this.templates.triangleInner.output() ) + }; + + // Append UI elements to create a panel. + this.parts.panel.append( this.parts.title, 1 ); + this.parts.panel.append( this.parts.close, 1 ); + this.parts.panel.append( this.parts.triangleOuter ); + this.parts.panel.append( this.parts.content ); + this.parts.triangleOuter.append( this.parts.triangleInner ); + + // Register panel children to focusManager (prevent from blurring the editor). + this.registerFocusable( this.parts.panel ); + this.registerFocusable( this.parts.close ); + + // Panel title and close button are not to be selected. + this.parts.title.unselectable(); + this.parts.close.unselectable(); + + // Append the panel to the global document. + CKEDITOR.document.getBody().append( this.parts.panel ); + + // Set default dimensions of the panel. + this.resize( this.width, this.height ); + + // Activates listeners on panel show. + // All listeners will be deactivated on panel hide. + this.on( 'show', this.activateShowListeners, this ); + + // Deactivate all listeners on panel hide. + this.on( 'hide', this.deactivateShowListeners, this ); + + this.parts.close.on( 'click', function( evt ) { + this.hide(); + evt.data.preventDefault(); + }, this ); + }, + + /** + * Shows the balloon panel. + */ + show: function() { + if ( this.rect.visible ) { + return; + } + + this.rect.visible = true; + this.parts.panel.show(); + + this.fire( 'show' ); + }, + + /** + * Hides the balloon panel and moves the focus back to the editable. + */ + hide: function() { + if ( !this.rect.visible ) { + return; + } + + this.rect.visible = false; + this.parts.panel.hide(); + this.blur(); + + this.fire( 'hide' ); + }, + + /** + * Moves the focus back to the editor's editable. + * + * @method blur + * @member CKEDITOR.ui.balloonPanel + */ + blur: function() { + this.editor.focus(); + }, + + /** + * Moves the **upper-left** balloon panel corner to the specified absolute position. + * + * @param {Number} top + * @param {Number} left + */ + move: function( top, left ) { + this.rect.left = left; + this.rect.top = top; + + this.parts.panel.setStyles( { + left: CKEDITOR.tools.cssLength( left ), + top: CKEDITOR.tools.cssLength( top ) + } ); + }, + + /** + * Places the balloon panel next to a specified element or a selection so the tip of the balloon's triangle + * touches that element or the center of the selection. Once the panel is attached it gains focus. + * + * @method attach + * @param {CKEDITOR.dom.element/CKEDITOR.dom.selection} elementOrSelection The element or selection to which the panel is attached. + * **Since 4.11.0** instead of an element it is possible to pass a selection {@link CKEDITOR.dom.selection}. + * @param {Object/CKEDITOR.dom.element/Boolean} [options] **Since 4.8.0** this parameter works as an `options` object. + * + * If a `{@link CKEDITOR.dom.element}/Boolean` instance is given, this parameter acts as an `options.focusElement`. + * @param {CKEDITOR.dom.element/Boolean} [options.focusElement] The element to be focused after the panel + * is attached. By default the `panel` property of {@link #parts} will be focused. You might specify the element + * to be focused by passing any {@link CKEDITOR.dom.element} instance. + * You can also prevent changing the focus at all by setting it to `false`. + * @param {Boolean} [options.show=true] Defines if the balloon panel should be shown after being attached. + */ + attach: ( function() { + var winGlobal, frame, editable, isInline; + + function rectIntersectArea( rect1, rect2 ) { + var hOverlap = Math.max( 0, Math.min( rect1.right, rect2.right ) - Math.max( rect1.left, rect2.left ) ), + vOverlap = Math.max( 0, Math.min( rect1.bottom, rect2.bottom ) - Math.max( rect1.top, rect2.top ) ); + + return hOverlap * vOverlap; + } + + function newPanelRect( top, left, panelWidth, panelHeight ) { + var newRect = { + top: top, + left: left + }; + + newRect.right = newRect.left + panelWidth; + newRect.bottom = newRect.top + panelHeight; + + return newRect; + } + + function createLineRect( first, last ) { + var newRect = first; + newRect.right = last.right; + newRect.width = newRect.right - newRect.left; + + if ( last.y ) { + newRect.y = last.y; + } + + return newRect; + } + + function getTopAndBottomRects( rectList ) { + var topAlignedRects = getAlignedRects( rectList, true ), + bottomAlignedRects = getAlignedRects( rectList ), + first = createLineRect( topAlignedRects[ 0 ], topAlignedRects.pop() ), + last = createLineRect( bottomAlignedRects[ 0 ], bottomAlignedRects.pop() ); + + // Make height of both rects equal to height of whole selection, so panel won't cover selection unless it needs to. + first.bottom = last.bottom; + first.height = first.bottom - first.top; + if ( last.y ) { + first.y = last.y; + } + + last.top = first.top; + last.height = first.height; + + return [ first, last ]; + } + + function getAlignedRects( rectList, top ) { + var edgeRect = top ? rectList [ 0 ] : rectList[ rectList.length - 1 ], + alignment = top ? 'top' : 'bottom'; + + return CKEDITOR.tools.array.filter( rectList, function( item ) { + if ( item[ alignment ] === edgeRect[ alignment ] ) { + return item; + } + } ); + } + + var triangleRelativePosition = { + right: 'left', + top: 'bottom', + topLeft: 'bottomLeft', + topRight: 'bottomRight', + bottom: 'top', + bottomLeft: 'topLeft', + bottomRight: 'topRight', + left: 'right' + }; + + return function( elementOrSelection, options ) { + if ( elementOrSelection instanceof CKEDITOR.dom.selection ) { + var ranges = elementOrSelection.getRanges(), + rectList; + + // Handle fake selection within table. + if ( elementOrSelection.isFake && elementOrSelection.isInTable() ) { + rectList = CKEDITOR.tools.array.map( ranges, function( item ) { + // With table selection the first rect represents `td` element rect. Lets use it in that case. + return item.getClientRects( true )[ 0 ]; + } ); + } else { + rectList = ranges[ ranges.length - 1 ].getClientRects( true ); + } + + // We need two rects, one representing the first selected line, and other representing the last selected line. + var first = rectList[ 0 ], + last = rectList[ rectList.length - 1 ], + selectionRects; + + if ( first === last ) { + selectionRects = [ first ]; + } else if ( first.top === last.top ) { + selectionRects = [ createLineRect( first, last ) ]; + } else { + selectionRects = getTopAndBottomRects( rectList ); + } + } + + if ( options instanceof CKEDITOR.dom.element || !options ) { + options = { focusElement: options }; + } + + options = CKEDITOR.tools.extend( options, { + show: true + } ); + + if ( options.show === true ) { + this.show(); + } + + this.fire( 'attach' ); + + winGlobal = CKEDITOR.document.getWindow(); + frame = this.editor.window.getFrame(); + editable = this.editor.editable(); + isInline = editable.isInline(); + + if ( !isInline && CKEDITOR.env.safari ) { + // Overwrite frame with editor iframe closest parent, because iframe has wrong rect values in mobile Safari (#1076). + frame = frame.getParent(); + } + + var panelWidth = this.getWidth(), + panelHeight = this.getHeight(), + + // The area of the panel. + panelArea = panelWidth * panelHeight, + alignments, minDifferenceAlignment, alignmentRect, areaDifference, + + elementRect = elementOrSelection.getClientRect && elementOrSelection.getClientRect( true ), + editorRect = isInline ? editable.getClientRect( true ) : frame.getClientRect( true ), + + viewPaneSize = winGlobal.getViewPaneSize(), + winGlobalScroll = winGlobal.getScrollPosition(), + + // allowedRect is the rect into which the panel should fit to remain + // both within the visible area of the editor and the viewport, i.e. + // the rect area covered by "#": + // + // [Viewport] + // +-------------------------------------+ + // | [Editor] | + // | +--------------------+ + // | |############| | + // | |############| | + // | |############| | + // | +--------------------+ + // | | + // +-------------------------------------+ + allowedRect = { + top: Math.max( editorRect.top, winGlobalScroll.y ), + left: Math.max( editorRect.left, winGlobalScroll.x ), + right: Math.min( editorRect.right, viewPaneSize.width + winGlobalScroll.x ), + bottom: Math.min( editorRect.bottom, viewPaneSize.height + winGlobalScroll.y ) + }, + alignmentKeys; + + // Position balloon on entire view port only when it's real inline mode (#1048). + if ( isInline && this.editor.elementMode === CKEDITOR.ELEMENT_MODE_INLINE ) { + // In inline we want to limit position within the window. + allowedRect = this._getViewPaneRect( winGlobal ); + + // We need also consider triangle. + allowedRect.right += this.triangleWidth; + allowedRect.bottom += this.triangleHeight; + } + + // This method will modify elementRect if the element is outside of allowedRect / editorRect. + // If it's outside then in + if ( selectionRects ) { + CKEDITOR.tools.array.forEach( selectionRects, function( item ) { + this._adjustElementRect( item, isInline ? allowedRect : editorRect ); + }, this ); + + alignments = this._getAlignments( selectionRects[ 0 ], panelWidth, panelHeight ); + + if ( selectionRects.length > 1 ) { + alignments[ 'bottom hcenter' ] = this._getAlignments( selectionRects[ 1 ], panelWidth, panelHeight )[ 'bottom hcenter' ]; + } + + alignmentKeys = { + 'top hcenter': true, + 'bottom hcenter': true + }; + } else { + this._adjustElementRect( elementRect, isInline ? allowedRect : editorRect ); + alignments = this._getAlignments( elementRect, panelWidth, panelHeight ); + } + + // Iterate over all possible alignments to find the optimal one. + for ( var a in alignmentKeys || alignments ) { + // Create a rect which would represent the panel in such alignment. + alignmentRect = newPanelRect( alignments[ a ].top, alignments[ a ].left, panelWidth, panelHeight ); + + // Calculate the difference between the area of the panel and intersection of allowed rect and alignment rect. + // It is the area of the panel, which would be OUT of allowed rect if such alignment was used. Less is better. + areaDifference = alignments[ a ].areaDifference = panelArea - rectIntersectArea( alignmentRect, allowedRect ); + + // If the difference is 0, it means that the panel is fully within allowed rect. That's great! + if ( areaDifference === 0 ) { + minDifferenceAlignment = a; + break; + } + + // If there's no alignment of a minimal area difference, use the first available. + if ( !minDifferenceAlignment ) { + minDifferenceAlignment = a; + } + + // Determine the alignment of a minimal area difference. It will be used as a fallback + // if no alignment provides a perfect fit into allowed rect. + if ( areaDifference < alignments[ minDifferenceAlignment ].areaDifference ) { + minDifferenceAlignment = a; + } + } + + // For non-static parent elements we need to remove its margin offset from balloon panel (#1048). + var parent = this.parts.panel.getAscendant( function( el ) { + return el instanceof CKEDITOR.dom.document ? false : el.getComputedStyle( 'position' ) !== 'static'; + } ), + parentMargin = { + left: parent ? parseInt( parent.getComputedStyle( 'margin-left' ), 10 ) : 0, + top: parent ? parseInt( parent.getComputedStyle( 'margin-top' ), 10 ) : 0 + }; + + this.move( alignments[ minDifferenceAlignment ].top - parentMargin.top , alignments[ minDifferenceAlignment ].left - parentMargin.left ); + + minDifferenceAlignment = minDifferenceAlignment.split( ' ' ); + this.setTriangle( triangleRelativePosition[ minDifferenceAlignment[ 0 ] ], minDifferenceAlignment[ 1 ] ); + + // Set focus to proper element. + if ( options.focusElement !== false ) { + ( options.focusElement || this.parts.panel ).focus(); + } + }; + } )(), + + /** + * Resizes the balloon panel container to given dimensions. Use `'auto'` to + * make the dimensions of the panel flexible. + * + * @param {Number} width + * @param {Number} height + */ + resize: function( width, height ) { + this.rect.width = width; + this.rect.height = height; + + this.parts.panel.setStyles( { + width: CKEDITOR.tools.cssLength( width ), + height: CKEDITOR.tools.cssLength( height ) + } ); + }, + + /** + * Returns the balloon panel width. + * + * @returns {Number} + */ + getWidth: function() { + return this.rect.width === 'auto' ? this.parts.panel.getClientRect().width : this.rect.width; + }, + + /** + * Returns the balloon panel height. + * + * @returns {Number} + */ + getHeight: function() { + return this.rect.height === 'auto' ? this.parts.panel.getClientRect().height : this.rect.height; + }, + + /** + * Changes the position of the balloon's triangle that points to the element in the editable. + * + * @param {String} side One of 'left', 'right', 'top' or 'bottom'. + */ + setTriangle: function( side, align ) { + var outer = this.parts.triangleOuter, + inner = this.parts.triangleInner; + + if ( this.triangleSide ) { + outer.removeClass( 'cke_balloon_triangle_' + this.triangleSide ); + outer.removeClass( 'cke_balloon_triangle_align_' + this.triangleAlign ); + inner.removeClass( 'cke_balloon_triangle_' + this.triangleSide ); + } + + this.triangleSide = side; + this.triangleAlign = align; + + outer.addClass( 'cke_balloon_triangle_' + side ); + outer.addClass( 'cke_balloon_triangle_align_' + align ); + inner.addClass( 'cke_balloon_triangle_' + side ); + }, + + /** + * Registers a new focusable element in the editor's focus manager so the instance + * does not blur once the child of the balloon panel gains focus. + * See {@link #focusables}. + * + * @param {CKEDITOR.dom.element} element An element to be registered. + */ + registerFocusable: function( element ) { + this.editor.focusManager.add( element ); + + this.focusables[ element.getUniqueId() ] = element; + }, + + /** + * Unregisters an element from editor's focus manager. + * See {@link #focusables}. + * + * @param {CKEDITOR.dom.element} element An element to be registered. + */ + deregisterFocusable: function( element ) { + this.editor.focusManager.remove( element ); + + delete this.focusables[ element.getUniqueId() ]; + }, + + /** + * Adds an event listener associated with this balloon panel. This listener + * will be activated on panel `show` and deactivated on panel `hide`. + * See {@link #showListeners}, {@link #activeShowListeners}, {@link #activateShowListeners}, + * {@link #deactivateShowListeners}. + * + * @param {Function} listener A function that, if called, attaches the listener + * and returns the listener object. + * @returns {Object} An object containing the `removeListener` method that removes + * the listener from the collection. + */ + addShowListener: function( listener ) { + var id = CKEDITOR.tools.getNextNumber(); + + // Adds the listener to the register of on-show-activated listeners. + this.showListeners[ id ] = listener; + + // Activate listener immediately if panel is already visible. + if ( this.rect.visible ) { + this.activateShowListener( id ); + } + + var that = this; + + return { + removeListener: function() { + that.removeShowListener( id ); + } + }; + }, + + /** + * Removes an event listener associated with this balloon panel visible state. + * See {@link #addShowListener}. + * + * @param {Number} id An ID of the listener. + */ + removeShowListener: function( id ) { + this.deactivateShowListener( id ); + delete this.showListeners[ id ]; + }, + + /** + * Activates an event listener associated with this balloon panel. + * See {@link #showListeners}, {@link #activeShowListeners}, {@link #deactivateShowListener}, + * {@link #addShowListener}, {@link #removeShowListener}. + */ + activateShowListener: function( id ) { + this.activeShowListeners[ id ] = this.showListeners[ id ].call( this ); + }, + + /** + * Deactivates an event listener associated with this balloon panel. + * See {@link #activateShowListener}. + */ + deactivateShowListener: function( id ) { + if ( this.activeShowListeners[ id ] ) { + this.activeShowListeners[ id ].removeListener(); + } + + delete this.activeShowListeners[ id ]; + }, + + /** + * Activates all event listeners associated with this balloon panel. + * See {@link #showListeners}, {@link #activeShowListeners}, {@link #deactivateShowListeners}, + * {@link #addShowListener}, {@link #removeShowListener}. + */ + activateShowListeners: function() { + for ( var id in this.showListeners ) { + this.activateShowListener( id ); + } + }, + + /** + * Removes all event listeners associated with this balloon panel. + * See {@link #activateShowListeners}. + */ + deactivateShowListeners: function() { + for ( var id in this.activeShowListeners ) { + this.deactivateShowListener( id ); + } + }, + + /** + * Destroys the balloon panel by removing it from DOM and purging + * all associated event listeners. + */ + destroy: function() { + this.deactivateShowListeners(); + this.parts.panel.remove(); + }, + + /** + * Sets the balloon panel title. + * + * @param {String} title A new panel title. + */ + setTitle: function( title ) { + this.parts.title.setHtml( title ); + }, + + /** + * Returns a dictionary containing different alignment positions. + * + * Keys tell where the balloon is positioned relative to the element, e.g. this would be the result for "top center": + * + * [Editor] + * +-------------------------------------+ + * | [Panel] | + * | +-----------------+ | + * | | | | + * | [El.] +--------v--------+ | + * | +-------------------------------+ | + * | | | | + * | | | | + * +--+-------------------------------+--+ + * + * Sample result: + * + * { + * "right vcenter": { top: 529.5, left: 175 }, + * "left vcenter": { top: 529.5, left: 10}, + * "top hcenter": { top: 402, left: 92.5}, + * "top left": { top: 402, left: 102.5}, + * "top right": { top: 402, left: 82.5}, + * "bottom hcenter": { top: 643, left: 92.5}, + * "bottom left": { top: 643, left: 102.5}, + * "bottom right": { top: 643, left: 82.5} + * } + * + * @private + * @param elementRect + * @param {Number} panelWidth + * @param {Number} panelHeight + * @returns {Object} + */ + _getAlignments: function( elementRect, panelWidth, panelHeight ) { + return { + 'right vcenter': { + top: elementRect.top + elementRect.height / 2 - panelHeight / 2, + left: elementRect.right + this.triangleWidth + }, + 'left vcenter': { + top: elementRect.top + elementRect.height / 2 - panelHeight / 2, + left: elementRect.left - panelWidth - this.triangleWidth + }, + 'top hcenter': { + top: elementRect.top - panelHeight - this.triangleHeight, + left: elementRect.left + elementRect.width / 2 - panelWidth / 2 + }, + 'top left': { + top: elementRect.top - panelHeight - this.triangleHeight, + left: elementRect.left + elementRect.width / 2 - this.triangleMinDistance + }, + 'top right': { + top: elementRect.top - panelHeight - this.triangleHeight, + left: elementRect.right - elementRect.width / 2 - panelWidth + this.triangleMinDistance + }, + 'bottom hcenter': { + top: elementRect.bottom + this.triangleHeight, + left: elementRect.left + elementRect.width / 2 - panelWidth / 2 + }, + 'bottom left': { + top: elementRect.bottom + this.triangleHeight, + left: elementRect.left + elementRect.width / 2 - this.triangleMinDistance + }, + 'bottom right': { + top: elementRect.bottom + this.triangleHeight, + left: elementRect.right - elementRect.width / 2 - panelWidth + this.triangleMinDistance + } + }; + }, + + /** + * This method will modify `elementRect` if the element is outside of `editorRect`. If it is outside, it is + * going to change it into a rectangle that is within `editorRect`. + * + * For example here `elementRect` is going to be changed into a very narrow rectangle (with unmodified height) + * representation within `editorRect`. + * + * +------------------------------------------+ + * | | + * | #| +----------+ + * | #| | | + * | #| | | + * | editorRect #| |elmentRect| + * | #| | | + * | #| | | + * | #| +----------+ + * | | + * +------------------------------------------+ + * + * @private + * @param elementRect Rectangle object that should be contained within `editorRect`. **This object might be modified.** + * @param editorRect Reference container that should contain `elementRect`. + */ + _adjustElementRect: function( elementRect, editorRect ) { + elementRect.left = numberInRange( editorRect.left, editorRect.right - 1, elementRect.left ); + elementRect.right = numberInRange( editorRect.left, editorRect.right, elementRect.right ); + elementRect.top = numberInRange( editorRect.top, editorRect.bottom - 1, elementRect.top ); + elementRect.bottom = numberInRange( editorRect.top, editorRect.bottom, elementRect.bottom ); + }, + + /** + * @param {CKEDITOR.dom.window} window + * @returns {Object} Returns viewport position, taking scroll offset into account. + * @returns {Number} return.top + * @returns {Number} return.bottom + * @returns {Number} return.left + * @returns {Number} return.right + */ + _getViewPaneRect: function( window ) { + var pos = window.getScrollPosition(), + viewSize = window.getViewPaneSize(); + + return { + top: pos.y, + bottom: pos.y + viewSize.height, + left: pos.x, + right: pos.x + viewSize.width + }; + } + }; + + function numberInRange( min, max, num ) { + return Math.max( min, Math.min( max, num ) ); + } + + CKEDITOR.event.implementOn( CKEDITOR.ui.balloonPanel.prototype ); + + /** + * The definition of a balloon panel. + * + * This virtual class illustrates the properties that developers can use to define and create + * balloon panels. + * + * CKEDITOR.ui.balloonPanel( editor, { + * title: 'My Panel', + * onShow: function() { + * ... + * } + * } ); + * + * @class CKEDITOR.ui.balloonPanel.definition + */ + + /** + * The title of the balloon panel. + * + * @member CKEDITOR.ui.balloonPanel.definition + * @property {String} title + */ + + /** + * The static content of the balloon panel. + * + * @member CKEDITOR.ui.balloonPanel.definition + * @property {String} content + */ +} )(); diff --git a/plugins/ckeditor/balloonpanel/skins/kama/balloonpanel.css b/plugins/ckeditor/balloonpanel/skins/kama/balloonpanel.css new file mode 100644 index 0000000..7f653af --- /dev/null +++ b/plugins/ckeditor/balloonpanel/skins/kama/balloonpanel.css @@ -0,0 +1,220 @@ +/* +Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ + +.cke_balloon +{ + position: absolute; + z-index: 10000; + + background: #fff; + border: 1px solid #b2b2b2; + border-bottom-color: #999; + + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, .15); + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .15); + box-shadow: 0 0 3px rgba(0, 0, 0, .15); + + outline: none; +} + +.cke_balloon_title +{ + font-weight: bold; + font-size: 14px; + cursor: default; + + color: #474747; + text-shadow: 0 1px 0 rgba(255,255,255,.75); + + padding: 3px 3px 8px; + border-bottom: 1px solid #eee; + + -moz-border-radius: 2px 2px 0 0; + -webkit-border-radius: 2px 2px 0 0; + border-radius: 2px 2px 0 0; + + -moz-box-shadow: 0 1px 0 #fff inset; + -webkit-box-shadow: 0 1px 0 #fff inset; + box-shadow: 0 1px 0 #fff inset; +} + +/* The close button at the top of the panel. */ +a.cke_balloon_close_button +{ + background-image: url(../../../../skins/kama/images/sprites.png); + background-repeat: no-repeat; + background-position: 0 -1022px; + position: absolute; + cursor: pointer; + text-align: center; + height: 20px; + width: 20px; + top: 5px; +} + +a.cke_balloon_close_button:hover { + background-position: 0 -1045px; +} + +.cke_balloon_content +{ + overflow: hidden; + min-height: 68px; +} + +.cke_balloon_close_button:hover +{ + opacity: 1; + filter: alpha(opacity = 100); +} + +.cke_balloon_close_button span +{ + display: none; +} + +.cke_ltr .cke_balloon_close_button +{ + right: 5px; +} + +.cke_rtl .cke_balloon_close_button +{ + left: 6px; +} + +.cke_balloon_triangle +{ + position: absolute; + border-style: solid; + display: block; + width: 0; + height: 0; +} + +.cke_balloon_triangle_inner +{ + z-index: 0; +} + +.cke_balloon_triangle_outer +{ + z-index: 0; +} + +/* side: [ bottom, top ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_bottom, + .cke_balloon_triangle_outer.cke_balloon_triangle_top + { + border-color: #6B6B6B transparent; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_bottom, + .cke_balloon_triangle_inner.cke_balloon_triangle_top + { + left: -20px; + } + +/* side: [ bottom ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_bottom + { + border-width: 20px 20px 0; + bottom: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_bottom + { + border-color: #fff transparent; + border-width: 20px 20px 0; + top: -21px; + } + +/* side: [ top ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_top + { + border-width: 0 20px 20px; + top: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_top + { + border-color: #fff transparent; + border-width: 0 20px 20px; + top: 1px; + } + +/* side: [ left, right ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_left, + .cke_balloon_triangle_outer.cke_balloon_triangle_right + { + border-color: transparent #6B6B6B; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_left, + .cke_balloon_triangle_inner.cke_balloon_triangle_right + { + border-color: transparent #fff; + top: -20px; + } + +/* side: [ left ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_left + { + border-width: 20px 20px 20px 0; + left: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_left + { + border-color: transparent #fff; + border-width: 20px 20px 20px 0; + left: 1px; + } + +/* side: [ right ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_right + { + border-width: 20px 0 20px 20px; + right: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_right + { + border-width: 20px 0 20px 20px; + right: 1px; + } + + +/* align: [ hcenter ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_hcenter + { + left: 50%; + margin-left: -20px; + } + +/* align: [ left ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_left + { + left: 20px; + margin-left: 0; + } + +/* align: [ right ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_right + { + right: 20px; + margin-left: 0; + } + +/* align: [ vcenter ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_vcenter + { + top: 50%; + margin-top: -20px; + } diff --git a/plugins/ckeditor/balloonpanel/skins/moono-lisa/balloonpanel.css b/plugins/ckeditor/balloonpanel/skins/moono-lisa/balloonpanel.css new file mode 100644 index 0000000..ccaae48 --- /dev/null +++ b/plugins/ckeditor/balloonpanel/skins/moono-lisa/balloonpanel.css @@ -0,0 +1,223 @@ +/* +Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ + +.cke_balloon +{ + position: absolute; + z-index: 10000; + + background: #fff; + border: 2px solid #aaa; + outline: none; +} + +.cke_balloon_title +{ + font-weight: bold; + font-size: 12px; + cursor: default; + + color: #484848; + + padding: 12px 30px 12px 12px; /* Let's the title and close don't overlap each other - padding-right. */ + background: #f8f8f8; +} + +/* The close button at the top of the panel. */ +a.cke_balloon_close_button +{ + background-image: url(images/close.png); + background-repeat: no-repeat; + background-position: 50% -1px; + position: absolute; + cursor: pointer; + text-align: center; + height: 16px; + width: 16px; + top: 8px; + z-index: 5; + opacity: 0.7; + filter: alpha(opacity = 70); + border: 2px solid transparent; + border-radius: 2px; +} + +a.cke_balloon_close_button:focus, +a.cke_balloon_close_button:active +{ + outline: none; + border: 2px solid #0079f7; +} + +.cke_balloon_content +{ + overflow: hidden; + min-height: 68px; + padding: 0 6px 6px 6px; +} + +.cke_balloon_close_button:hover +{ + opacity: 1; + filter: alpha(opacity = 100); +} + +.cke_balloon_close_button:focus:hover +{ + opacity: 1; + filter: alpha(opacity = 100); + border: 2px solid #139FF7; +} + +.cke_hidpi .cke_balloon_close_button +{ + background-image: url(images/hidpi/close.png); + background-size: 16px; +} + +.cke_balloon_close_button span +{ + display: none; +} + +.cke_ltr .cke_balloon_close_button +{ + right: 10px; +} + +.cke_rtl .cke_balloon_close_button +{ + left: 10px; +} + +.cke_balloon_triangle +{ + position: absolute; + border-style: solid; + display: block; + width: 0; + height: 0; +} + +.cke_balloon_triangle_inner +{ + z-index: 0; +} + +.cke_balloon_triangle_outer +{ + z-index: 0; +} + +/* side: [ bottom, top ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_bottom, +.cke_balloon_triangle_outer.cke_balloon_triangle_top +{ + border-color: #999 transparent; +} + +.cke_balloon_triangle_inner.cke_balloon_triangle_bottom, +.cke_balloon_triangle_inner.cke_balloon_triangle_top +{ + left: -20px; +} + +/* side: [ bottom ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_bottom +{ + border-width: 20px 20px 0; + bottom: -20px; +} + +.cke_balloon_triangle_inner.cke_balloon_triangle_bottom +{ + border-color: #fff transparent; + border-width: 20px 20px 0; + top: -22px; +} + +/* side: [ top ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_top +{ + border-width: 0 20px 20px; + top: -20px; +} + +.cke_balloon_triangle_inner.cke_balloon_triangle_top +{ + border-color: #f8f8f8 transparent; + border-width: 0 20px 20px; + top: 2px; +} + +/* side: [ left, right ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_left, +.cke_balloon_triangle_outer.cke_balloon_triangle_right +{ + border-color: transparent #999; +} + +.cke_balloon_triangle_inner.cke_balloon_triangle_left, +.cke_balloon_triangle_inner.cke_balloon_triangle_right +{ + border-color: transparent #fff; + top: -20px; +} + +/* side: [ left ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_left +{ + border-width: 20px 20px 20px 0; + left: -20px; +} + +.cke_balloon_triangle_inner.cke_balloon_triangle_left +{ + border-color: transparent #fff; + border-width: 20px 20px 20px 0; + left: 2px; +} + +/* side: [ right ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_right +{ + border-width: 20px 0 20px 20px; + right: -20px; +} + +.cke_balloon_triangle_inner.cke_balloon_triangle_right +{ + border-width: 20px 0 20px 20px; + right: 2px; +} + + +/* align: [ hcenter ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_align_hcenter +{ + left: 50%; + margin-left: -20px; +} + +/* align: [ left ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_align_left +{ + left: 20px; + margin-left: 0; +} + +/* align: [ right ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_align_right +{ + right: 20px; + margin-left: 0; +} + +/* align: [ vcenter ] */ +.cke_balloon_triangle_outer.cke_balloon_triangle_align_vcenter +{ + top: 50%; + margin-top: -20px; +} diff --git a/plugins/ckeditor/balloonpanel/skins/moono-lisa/images/close.png b/plugins/ckeditor/balloonpanel/skins/moono-lisa/images/close.png new file mode 100644 index 0000000000000000000000000000000000000000..40caa6ddfd01c2a26947761de218e4ce8f5e361e GIT binary patch literal 615 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CDO3=9p;3=BX21L>Cx45bDP46hOx7_4S6Fo@?*ia+WGRLhp+?e4A1op@M5>3aOmOBJL-|3|^OV7Yn$}BPM-7dk4;tau8cTbEB zVOVq2VrTe-pr)C+XL-*25p&w{RY|c$I<(f*c*aemrB}2+@TEpJ&78kybw|w+iRXsL zc6QXHdC8sL^yZ3dsB5%j+BccxKY6pB9X)fwF9YU; literal 0 HcmV?d00001 diff --git a/plugins/ckeditor/balloonpanel/skins/moono-lisa/images/hidpi/close.png b/plugins/ckeditor/balloonpanel/skins/moono-lisa/images/hidpi/close.png new file mode 100644 index 0000000000000000000000000000000000000000..0e4aa2efbeb3f7c1850fc0b51d8b151a0d5c9b08 GIT binary patch literal 1259 zcmZ{jdsGs36vuxkVp>!lE!4^-M>F&w_*f+6g9H^Dre>)mh_AvV711!W<lS5@O=yhS-6KUF7X+zZM+s6e6@D=S4q+8`E~{2l#ae`+Xx4`VjN- zIgFs~bBZeNDN1dVseMMoPa5ml6$G(9?Wm*J|K0kDyx48?fuB`G1wvUfR?T}bcR3XI ziJOyC>EMjS`V{X4KEcG>qEg;#fuODq0Wu?um?d1wB^152gdk zReuZGX8U#(dBG$S2`otN?jIO9c;#o2X9{4vIcZ1YxEVvFlQ?l(ZLE2Axq>a2%~w73 z_mKu$oy*EvMntrTU#qRR?55X@)*%Pj9aer*kxCp5FLJL$M=K%t0RjfXn&b6 zWoT|0ZsSZ*C#1xOA{XOPc_;Kjfq=H#$?2inhvaDtj+f_xj0}I}I`1(#YF=bC8Vk3z zp)6`vzN_WZz8<&wrOM34;B6eqIM3MxdKuU_KCOp7d97jk?iMTL5fODF-t*>h?g6_Q ztrmI8l4L&HP(L_z{E&g=_FQCG^`*1~r0VGK0 z)K;n6+uQ5Br$*fnb@%6S{DEcT%4!QH1u%Ub+d~?M78lCB@(T-B#bWV_=FG)rsAA@1 z>j?g;LK`Z?uzKWJJ$mO=l_Gh;zBFkN-^F`+hd($KcveJJjoFGpjh~9wz`S*5CLCVt zK(1WU9;|=LrOuLj=yT)aujmRQG|G-bo6@lL_%58oG1^a}w-y8}YO!JeEoS@0w6TtV za>Py$gzl_JH_?Gyj6!{h(#1uGQ;4q{G4m$V-!E1vY~H(REj!~2sLT@ik$yh7Q%YQ- zq29^hz0ZF4*02+{1Z8}N7O^NdT;d5$tRGv_KUTE1is<6bn?EotkaWM+2ZRsP=)}m{ zM>3g=Uq+RuEq~4{`LnsNukWx~mTLx&hjlDBb+~k!=7%t0yyPcJ$$9K&o#pR|Z z<)-0Na?(ry-dL{#Xeusp+RL{C^;QVDOPC O0LY{W;@u;R;(q~!2{T{- literal 0 HcmV?d00001 diff --git a/plugins/ckeditor/balloonpanel/skins/moono/balloonpanel.css b/plugins/ckeditor/balloonpanel/skins/moono/balloonpanel.css new file mode 100644 index 0000000..103dc82 --- /dev/null +++ b/plugins/ckeditor/balloonpanel/skins/moono/balloonpanel.css @@ -0,0 +1,237 @@ +/* +Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ + +.cke_balloon +{ + position: absolute; + z-index: 10000; + + background: #fff; + border: 1px solid #b2b2b2; + border-bottom-color: #999; + + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, .15); + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .15); + box-shadow: 0 0 3px rgba(0, 0, 0, .15); + + outline: none; +} + +.cke_balloon_title +{ + font-weight: bold; + font-size: 13px; + cursor: default; + + color: #474747; + text-shadow: 0 1px 0 rgba(255,255,255,.75); + + border-bottom: 1px solid #999; + padding: 6px 10px; + + -moz-border-radius: 2px 2px 0 0; + -webkit-border-radius: 2px 2px 0 0; + border-radius: 2px 2px 0 0; + + -moz-box-shadow: 0 1px 0 #fff inset; + -webkit-box-shadow: 0 1px 0 #fff inset; + box-shadow: 0 1px 0 #fff inset; + + /* Let's the title and close don't overlap each other */ + padding-right: 30px; + + background: #cfd1cf; + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#cfd1cf)); + background-image: -moz-linear-gradient(top, #f5f5f5, #cfd1cf); + background-image: -webkit-linear-gradient(top, #f5f5f5, #cfd1cf); + background-image: -o-linear-gradient(top, #f5f5f5, #cfd1cf); + background-image: -ms-linear-gradient(top, #f5f5f5, #cfd1cf); + background-image: linear-gradient(top, #f5f5f5, #cfd1cf); + filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#f5f5f5', endColorstr='#cfd1cf'); +} + +/* The close button at the top of the panel. */ +a.cke_balloon_close_button +{ + background-image: url(images/close.png); + background-repeat: no-repeat; + background-position: 50%; + position: absolute; + cursor: pointer; + text-align: center; + height: 20px; + width: 20px; + top: 3px; + z-index: 5; + opacity: 0.8; + filter: alpha(opacity = 80); +} + +.cke_balloon_content +{ + overflow: hidden; + min-height: 68px; +} + +.cke_balloon_close_button:hover +{ + opacity: 1; + filter: alpha(opacity = 100); +} + +.cke_hidpi .cke_balloon_close_button +{ + background-image: url(images/hidpi/close.png); + background-size: 16px; +} + +.cke_balloon_close_button span +{ + display: none; +} + +.cke_ltr .cke_balloon_close_button +{ + right: 5px; +} + +.cke_rtl .cke_balloon_close_button +{ + left: 6px; +} + +.cke_balloon_triangle +{ + position: absolute; + border-style: solid; + display: block; + width: 0; + height: 0; +} + +.cke_balloon_triangle_inner +{ + z-index: 0; +} + +.cke_balloon_triangle_outer +{ + z-index: 0; +} + +/* side: [ bottom, top ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_bottom, + .cke_balloon_triangle_outer.cke_balloon_triangle_top + { + border-color: #6B6B6B transparent; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_bottom, + .cke_balloon_triangle_inner.cke_balloon_triangle_top + { + left: -20px; + } + +/* side: [ bottom ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_bottom + { + border-width: 20px 20px 0; + bottom: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_bottom + { + border-color: #fff transparent; + border-width: 20px 20px 0; + top: -21px; + } + +/* side: [ top ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_top + { + border-width: 0 20px 20px; + top: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_top + { + border-color: #f5f5f5 transparent; + border-width: 0 20px 20px; + top: 1px; + } + +/* side: [ left, right ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_left, + .cke_balloon_triangle_outer.cke_balloon_triangle_right + { + border-color: transparent #6B6B6B; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_left, + .cke_balloon_triangle_inner.cke_balloon_triangle_right + { + border-color: transparent #fff; + top: -20px; + } + +/* side: [ left ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_left + { + border-width: 20px 20px 20px 0; + left: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_left + { + border-color: transparent #fff; + border-width: 20px 20px 20px 0; + left: 1px; + } + +/* side: [ right ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_right + { + border-width: 20px 0 20px 20px; + right: -20px; + } + + .cke_balloon_triangle_inner.cke_balloon_triangle_right + { + border-width: 20px 0 20px 20px; + right: 1px; + } + + +/* align: [ hcenter ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_hcenter + { + left: 50%; + margin-left: -20px; + } + +/* align: [ left ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_left + { + left: 20px; + margin-left: 0; + } + +/* align: [ right ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_right + { + right: 20px; + margin-left: 0; + } + +/* align: [ vcenter ] */ + .cke_balloon_triangle_outer.cke_balloon_triangle_align_vcenter + { + top: 50%; + margin-top: -20px; + } diff --git a/plugins/ckeditor/balloonpanel/skins/moono/images/close.png b/plugins/ckeditor/balloonpanel/skins/moono/images/close.png new file mode 100644 index 0000000000000000000000000000000000000000..04b9c97dde8273f8518060c82104b38b51832fa0 GIT binary patch literal 824 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zKpodXn9)gNb_D|iQ=q4dV~EA+rIQW2r5r_$|IbhBX1!iK_4XmB4ZGuZyxX>Mli(%E z3$HvlGJ~HaFWITo!p#_bl)s>kG4~ILn4ZiInf3+cyI*T<`W(k~_vmY;nOSN7-tT?5 z_kMlG#)t>?(@N_U7A5H`{SZHU%5|y8mTLiCjxG~YC1*%7ESS=i8mXXUVBBMvkk)Zx zV?z+v(jd;QQM_K73j+!*M=?Jx=(qL zyV9$j>$uN4a_JV!q7Nf$g=w9`d^C7^PGA=~P!uT~qnS$!bq$4+Rn zRm$m#aRaT?oBmm{=FQhCd#|NJzRQD|8O+Vit9IW#m0|Mh$eHi2ze*L#9F{1SQ8HHc z4Zg9BH*Meg5Uo|W-`4Hlx9=LmhWqbRZRG0Hp8YoQowzL1@kK9x5mTUu>hHgCQ-VsW z92RMKolEYT^XF2b3{bDqtLv}7DjKgo{7|9&u;Yx#t3aOM*WBA}k1bpkwU1Xgef{_8 zQ_+pAttB$Yjb{4TF*LmYZ6CdM+Pt%A&Pql~pVU2tB$E3i4jY^)wB33;_hapR#yj&8 z_k69LfBWq=h6*9Umc$MPj)YE+L~jQEM2UT~&p(fMdGbAP`{}*^yE`_mUcEY>LGD~J zSC6>3_=>RA3QDRDyr(~v8;?`jEllcfxgCxj?;QX|b^2DN4hVt@qz0ADq;^f4F ZRK5J7^x5xhq=1STJYD@<);T3K0RTH-Q&a!| literal 0 HcmV?d00001 diff --git a/plugins/ckeditor/balloonpanel/skins/moono/images/hidpi/close.png b/plugins/ckeditor/balloonpanel/skins/moono/images/hidpi/close.png new file mode 100644 index 0000000000000000000000000000000000000000..e406c2c35ba11a410291fe484d5bb406657b62eb GIT binary patch literal 1271 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0V0;tc6XJSh#fqyyv~uOORjaPA zT6JUf>YG5cX3Z@iTD$i4+O>Dqt-HH^{liV09&Ot6c=P5bK(uAc)2&;dZQc4Dh_-Ee zxqbV~9Xnp{-1%nb&bPaFzuUe0^S*tb_wWC5;K0{|2frOS@a^EiABPYBJAV8>5S=*j z|K!R4r%wGpefs~|v;WVX`+xrY{|guXU%veR%9a1uum8V!6X-&s03#y{4-coHAOjN< zF7?84azG>`D=R214OAv9D+@&WmX<*ETmk|DqM|?~A|b&mEX*e&!Vg415fKq-S=pfA zU_(nwIW;wHV`EcmYfF238%IZ5CnqO&cOO5$fY8vu(9p2(@W8OJkjTiem>7E}CkJO| zM^{%T7Z+D|F!1zr_ww=p0#8pbAn@_=1_B=+A73!=^Yilug8&c+3=9kcf?yB`0fJBv z2n!2~0D?#`h>nf|f@lzk0fM-=*x1;(xVU&ANC1Px#3Ud{PEJZmNls2q0fMx&bRfvc z$jZvf&dtrq$yq#0?-bDIOG<+Lf^mfyPH|QlH7z4^3rj0&8(Wb7JUqR8rcR%~VBw0D zt5&bwymi~F_wPS^_y_@CzJ7y)@87@wB@djAYS_oXz$E7B;uvCa`s(Dn{=$I*N1xw& zmuH{2J$}UvvF664p8t)Uot^w@YX3eHS4MMA5#twxCv@S8$@{k82K*Q#(4=#CkFcR=T*fdPzi-erDAcjNHHLY2?{u z8&A962-^K~k93eA&xEgw=dQTCQgSj^(SkcSY^&H+_8IlaZ_V2K&rrQ^&eMkQ)$jKo z-Tcq>QD?>Nw^8pe2fz6zd3@zMhG31h-+n9WXI3n`bLUlUNZF@U3*XP;JztqFmn8A) zU0x9Ny6(c2hoAc|9(u~aV3B*^;^~i<-tU_p^7Zw}pDGir7cZD)cvUg2KF)rcPiaAs zS>%pt^@k?OCJU~-)L1=_r!MxqtQmua!EBF+7&QyoKl9Ga%}ZnKFx)8aDEj5|?FysE zq4lX>wd7837f{l=vuW$Iz**P$qi^fSbuwKrJ02!+gJF&Q)q<8ZnjeXq6B~Ozf^oT&wN%`lJ4T<@emnIwOEc2%aZRRn;?5~LYTo7h wzq#6 0) { + CKEDITOR.config.extraPlugins += ","; + } + CKEDITOR.config.extraPlugins += pluginId; + }, + registerPlugin1: function (pluginId, moduleId) { + var pluginPath = "../../../" + moduleId + "/oup-ckeditor/plugins/ckeditor/a11ychecker/"; + + CKEDITOR.plugins.addExternal(pluginId, pluginPath, 'plugin.js'); + + if (CKEDITOR.config.extraPlugins.length > 0) { + CKEDITOR.config.extraPlugins += ","; + } + CKEDITOR.config.extraPlugins += pluginId; + }, + registerPlugin2: function (pluginId, moduleId) { + var pluginPath = "../../../" + moduleId + "/oup-ckeditor/plugins/ckeditor/balloonpanel/"; + + CKEDITOR.plugins.addExternal(pluginId, pluginPath, 'plugin.js'); + if (CKEDITOR.config.extraPlugins.length > 0) { CKEDITOR.config.extraPlugins += ","; }