diff --git a/Gruntfile.js b/Gruntfile.js index 4635ddd..f694ede 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -27,7 +27,24 @@ module.exports = function (grunt) { "node_modules/infusion/src/framework/core/js/ModelTransformation.js", "node_modules/infusion/src/framework/core/js/ModelTransformationTransforms.js" ], - + webScriptsLib: [ + "node_modules/infusion/src/lib/jquery/core/js/jquery.js", + "node_modules/infusion/src/framework/core/js/Fluid.js", + "node_modules/infusion/src/framework/core/js/FluidPromises.js", + "node_modules/infusion/src/framework/core/js/FluidDocument.js", + "node_modules/infusion/src/framework/core/js/FluidDOMUtilities.js", + "node_modules/infusion/src/framework/core/js/FluidIoC.js", + "node_modules/infusion/src/framework/core/js/DataBinding.js", + "node_modules/infusion/src/framework/core/js/FluidView.js", + "node_modules/infusion/src/framework/core/js/FluidRequests.js", + "node_modules/infusion/src/lib/fastXmlPull/js/fastXmlPull.js", + "node_modules/infusion/src/framework/renderer/js/fluidParser.js", + "node_modules/infusion/src/framework/enhancement/js/ContextAwareness.js", + "node_modules/infusion/src/framework/enhancement/js/ProgressiveEnhancement.js", + "node_modules/infusion/src/framework/preferences/js/Enactors.js", + "node_modules/infusion/src/framework/preferences/js/CaptionsEnactor.js", + "extension/src/lib/captionsEnactor.js" + ], contentScriptsLib: [ "node_modules/infusion/src/lib/jquery/core/js/jquery.js", "node_modules/infusion/src/framework/core/js/Fluid.js", @@ -55,7 +72,8 @@ module.exports = function (grunt) { "node_modules/infusion/src/components/textToSpeech/js/TextToSpeech.js", "node_modules/infusion/src/components/orator/js/Orator.js", "node_modules/infusion/src/framework/preferences/js/SelfVoicingEnactor.js", - "node_modules/infusion/src/framework/preferences/js/LetterSpaceEnactor.js" + "node_modules/infusion/src/framework/preferences/js/LetterSpaceEnactor.js", + "node_modules/infusion/src/framework/preferences/js/CaptionsEnactor.js" ], adjustersLib: [ // jQuery @@ -100,8 +118,10 @@ module.exports = function (grunt) { "node_modules/infusion/src/framework/preferences/js/Panels.js", "node_modules/infusion/src/framework/preferences/js/SelfVoicingPanel.js", "node_modules/infusion/src/framework/preferences/js/LetterSpacePanel.js", + "node_modules/infusion/src/framework/preferences/js/CaptionsPanel.js", "node_modules/infusion/src/framework/preferences/js/Enactors.js", "node_modules/infusion/src/framework/preferences/js/LetterSpaceEnactor.js", + "node_modules/infusion/src/framework/preferences/js/CaptionsEnactor.js", "node_modules/infusion/src/framework/preferences/js/StarterGrades.js", "node_modules/infusion/src/framework/preferences/js/ArrowScrolling.js", "node_modules/infusion/src/framework/preferences/js/SeparatedPanelPrefsEditor.js", @@ -111,6 +131,7 @@ module.exports = function (grunt) { "node_modules/infusion/src/framework/preferences/js/StarterSchemas.js", "node_modules/infusion/src/framework/preferences/js/SelfVoicingSchemas.js", "node_modules/infusion/src/framework/preferences/js/LetterSpaceSchemas.js", + "node_modules/infusion/src/framework/preferences/js/CaptionsSchemas.js", "node_modules/infusion/src/framework/preferences/js/Builder.js", // from extension @@ -120,13 +141,7 @@ module.exports = function (grunt) { ], templates: [ "node_modules/infusion/src/components/tableOfContents/html/TableOfContents.html", - "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-textSize.html", - "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-lineSpace.html", - "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-letterSpace.html", - "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-contrast.html", - "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-layout.html", - "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-enhanceInputs.html", - "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-speak.html" + "node_modules/infusion/src/framework/preferences/html/PrefsEditorTemplate-*.html" ], messages: [ "node_modules/infusion/src/framework/preferences/messages/*.json" @@ -185,6 +200,9 @@ module.exports = function (grunt) { files.extensionLib, files.extension ), + "dist/<%= pkg.name %>-webScriptsLib.min.js" : [].concat( + files.webScriptsLib + ), "dist/<%= pkg.name %>-contentScriptsLib.min.js" : [].concat( files.contentScriptsLib ), @@ -222,6 +240,10 @@ module.exports = function (grunt) { src: ["extension/src/lib/adjusterScriptLoader.js"], dest: "build/src/adjusterScriptLoader.js" }, + { + src: ["extension/src/lib/captionsEnactor.js"], + dest: "build/src/captionsEnactor.js" + }, { expand: true, cwd: "extension/css/", diff --git a/README.md b/README.md index 2c3db52..108e286 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ The following adaptations are supported: * Reading Mode * Table of Contents * Enhance Inputs +* Captions (embedded YouTube videos) _**Note**: The ability to apply an adaptation will vary from page to page_ diff --git a/extension/manifest.json b/extension/manifest.json index 88c3f56..f2fe867 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "UI Options Plus (UIO+)", "short_name": "UIO+", - "version": "0.1.0.4", + "version": "0.1.0.5", "description": "User Interface Options Plus (UIO+) allows you to customize websites to match your own personal needs and preferences.", "author": "Fluid Project", "permissions": [ @@ -43,8 +43,9 @@ "all_frames": true }, { "matches": [""], + "exclude_globs": ["https://www.youtube.com/embed/*"], "js": [ - "content_scripts/fontInjection.js", + "content_scripts/webInjection.js", "content_scripts/utils.js", "content_scripts/simplification.js", "content_scripts/domEnactor.js", @@ -62,11 +63,12 @@ "src/background.js" ] }, - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "content_security_policy": "script-src 'self' https://youtube.com https://ytimg.com; object-src 'self'", "web_accessible_resources": [ "images/gpii.png", "templates/TableOfContents.html", "fonts/Orator-Icons.ttf", - "fonts/Orator-Icons.eot" + "fonts/Orator-Icons.eot", + "src/captionsEnactor.js" ] } diff --git a/extension/messages/ytCaptions.json b/extension/messages/ytCaptions.json new file mode 100644 index 0000000..9d5d653 --- /dev/null +++ b/extension/messages/ytCaptions.json @@ -0,0 +1,6 @@ +{ + "label": "youtube Captions", + "description": "Request embedded YouTube videos to display captions.", + "switchOn": "ON", + "switchOff": "OFF" +} diff --git a/extension/src/content_scripts/domEnactor.js b/extension/src/content_scripts/domEnactor.js index 619ff92..8dd9841 100644 --- a/extension/src/content_scripts/domEnactor.js +++ b/extension/src/content_scripts/domEnactor.js @@ -38,7 +38,8 @@ "onIncomingSettings.updateModel": { changePath: "", value: "{arguments}.0" - } + }, + "onIncomingSettings.postSettingsToWebPage": "{that}.postSettingsToWebPage" }, contextAwareness: { simplify: { @@ -50,6 +51,23 @@ } } }, + webSettings: { + type: "gpii.chrome.domEnactor", + filter: { + keys: ["captionsEnabled"], + exclude: false + } + }, + invokers: { + postSettingsToWebPage: { + funcName: "gpii.chrome.domEnactor.postSettingsToWebPage", + args: [ + "{that}.options.webSettings.type", + "{arguments}.0", + {filter: "{that}.options.webSettings.filter"} + ] + } + }, distributeOptions: { record: "{that}.container", target: "{that > fluid.prefs.enactor}.container" @@ -115,6 +133,40 @@ } }); + gpii.chrome.domEnactor.bindPortEvents = function (that) { + that.port = chrome.runtime.connect({name: "domEnactor-" + that.id}); + that.port.onMessage.addListener(function (data) { + that.events.onIncomingSettings.fire(data.settings); + }); + }; + + /** + * Posts a message to the webpage allowing for communication from the content script to the + * web page context. This is typically used to pass the model values from the extension to + * related enactors running in the web page context. + * + * @param {String} type - the value of the "type" field stored in the message's data + * @param {Object} settings - the settings to post to the web page context + * @param {Object} options - optional directives for filtering the settings to be posted. + * this allows the removal of settings that are not handled by + * the web page context, allowing for the remainder to be kept + * private. Options take the following form: + * {filter: {keys: [array of keys], exclude: true/false}} The + * exclude option determines if the filter keys are removed (true) + * or included (false). + */ + gpii.chrome.domEnactor.postSettingsToWebPage = function (type, settings, options) { + var data = { + type: type + }; + var keysToFilter = fluid.get(options, ["filter", "keys"]); + if (fluid.isArrayable(keysToFilter)) { + settings = fluid.filterKeys(settings, keysToFilter, options.filter.exclude); + } + data.payload = settings; + window.postMessage(data, "*"); + }; + fluid.defaults("gpii.chrome.domEnactor.simplify", { components: { simplify: { @@ -128,13 +180,6 @@ } }); - gpii.chrome.domEnactor.bindPortEvents = function (that) { - that.port = chrome.runtime.connect({name: "domEnactor-" + that.id}); - that.port.onMessage.addListener(function (data) { - that.events.onIncomingSettings.fire(data.settings); - }); - }; - // High contrast fluid.defaults("gpii.chrome.enactor.contrast", { gradeNames: ["fluid.prefs.enactor.contrast"], diff --git a/extension/src/content_scripts/fontInjection.js b/extension/src/content_scripts/fontInjection.js deleted file mode 100644 index ce8175a..0000000 --- a/extension/src/content_scripts/fontInjection.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * GPII Chrome Extension for Google Chrome - * - * Copyright 2018 OCAD University - * - * Licensed under the New BSD license. You may not use this file except in - * compliance with this license. - * - * You may obtain a copy of the license at - * https://github.com/GPII/gpii-chrome-extension/blob/master/LICENSE.txt - */ - -/* global fluid, gpii, chrome */ -"use strict"; - -fluid.registerNamespace("gpii.chrome.fontInjection"); - -gpii.chrome.fontInjection.fonts = [{ - fontFamily: "Orator-Icons", - urls: [ - chrome.runtime.getURL("fonts/Orator-Icons.ttf"), - chrome.runtime.getURL("fonts/Orator-Icons.eot") - ] -}]; - -gpii.chrome.fontInjection.styleTemplate = ""; - -fluid.each(gpii.chrome.fontInjection.fonts, function (fontInfo) { - var urls = fluid.transform(fluid.makeArray(fontInfo.urls), function (url) { - return "url(\"" + url + "\")"; - }); - - var info = { - fontFamily: fontInfo.fontFamily, - src: urls.join(",") - }; - - var styleElm = $(fluid.stringTemplate(gpii.chrome.fontInjection.styleTemplate, info)); - - $("head").append(styleElm); -}); diff --git a/extension/src/content_scripts/webInjection.js b/extension/src/content_scripts/webInjection.js new file mode 100644 index 0000000..aa555e5 --- /dev/null +++ b/extension/src/content_scripts/webInjection.js @@ -0,0 +1,67 @@ +/* + * GPII Chrome Extension for Google Chrome + * + * Copyright 2018 OCAD University + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this license. + * + * You may obtain a copy of the license at + * https://github.com/GPII/gpii-chrome-extension/blob/master/LICENSE.txt + */ + +/* global fluid, gpii, chrome */ +"use strict"; + +(function ($, fluid) { + + fluid.registerNamespace("gpii.chrome.webInjection"); + + gpii.chrome.webInjection.fonts = [{ + fontFamily: "Orator-Icons", + urls: [ + chrome.runtime.getURL("fonts/Orator-Icons.ttf"), + chrome.runtime.getURL("fonts/Orator-Icons.eot") + ] + }]; + + // Listed in the order the scripts will be injected into the page + gpii.chrome.webInjection.scripts = [ + "https://www.youtube.com/iframe_api", + chrome.runtime.getURL("src/captionsEnactor.js") + ]; + + gpii.chrome.webInjection.styleTemplate = ""; + + // inject fonts + fluid.each(gpii.chrome.webInjection.fonts, function (fontInfo) { + var urls = fluid.transform(fluid.makeArray(fontInfo.urls), function (url) { + return "url(\"" + url + "\")"; + }); + + var info = { + fontFamily: fontInfo.fontFamily, + src: urls.join(",") + }; + + var styleElm = $(fluid.stringTemplate(gpii.chrome.webInjection.styleTemplate, info)); + + $("head").append(styleElm); + }); + + // inject scripts + fluid.each(gpii.chrome.webInjection.scripts, function (src) { + var existingScript = $("script[src=\"" + src + "\"]"); + + // if the script doesn't already exist on the page, inject it. + if (existingScript.length === 0) { + var script = $(" diff --git a/tests/browser/html/captionsEnactorTests.html b/tests/browser/html/captionsEnactorTests.html new file mode 100644 index 0000000..13da6e0 --- /dev/null +++ b/tests/browser/html/captionsEnactorTests.html @@ -0,0 +1,53 @@ + + + + + GPII Chrome extension captionsEnactor tests + + + + + + + + + + + + + + + + + + +

GPII Chrome extension captionsEnactor tests

+

+
+

+
    + + + + + + + + + diff --git a/tests/browser/html/domEnactorTests.html b/tests/browser/html/domEnactorTests.html index c0a8f28..9540369 100644 --- a/tests/browser/html/domEnactorTests.html +++ b/tests/browser/html/domEnactorTests.html @@ -7,7 +7,7 @@ - + @@ -17,7 +17,7 @@ - + diff --git a/tests/browser/html/portBindingTests.html b/tests/browser/html/portBindingTests.html index 0637d16..17592de 100644 --- a/tests/browser/html/portBindingTests.html +++ b/tests/browser/html/portBindingTests.html @@ -12,7 +12,7 @@ - + diff --git a/tests/browser/html/prefsEditorTests.html b/tests/browser/html/prefsEditorTests.html index 7ed3e1b..9de33a8 100644 --- a/tests/browser/html/prefsEditorTests.html +++ b/tests/browser/html/prefsEditorTests.html @@ -12,7 +12,7 @@ - + diff --git a/tests/browser/html/simplificationTests.html b/tests/browser/html/simplificationTests.html index ce4e67c..7dc67e2 100644 --- a/tests/browser/html/simplificationTests.html +++ b/tests/browser/html/simplificationTests.html @@ -7,7 +7,7 @@ - + @@ -15,7 +15,7 @@ - + diff --git a/tests/browser/html/utilsTests.html b/tests/browser/html/utilsTests.html index 5e0b717..b7d3842 100644 --- a/tests/browser/html/utilsTests.html +++ b/tests/browser/html/utilsTests.html @@ -7,7 +7,7 @@ - + @@ -15,7 +15,7 @@ - + diff --git a/tests/browser/html/zoomTests.html b/tests/browser/html/zoomTests.html index 5aa316d..f091f7d 100644 --- a/tests/browser/html/zoomTests.html +++ b/tests/browser/html/zoomTests.html @@ -7,7 +7,7 @@ - + @@ -16,7 +16,7 @@ - + diff --git a/tests/browser/js/captionsEnactorTests.js b/tests/browser/js/captionsEnactorTests.js new file mode 100644 index 0000000..031cc14 --- /dev/null +++ b/tests/browser/js/captionsEnactorTests.js @@ -0,0 +1,437 @@ +/* + * GPII Chrome Extension for Google Chrome + * + * Copyright 2018 OCAD University + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this license. + * + * You may obtain a copy of the license at + * https://github.com/GPII/gpii-chrome-extension/blob/master/LICENSE.txt + */ + +/* global fluid, gpii, jqUnit, sinon */ +"use strict"; + +(function ($) { + + $(document).ready(function () { + + /************************************************************************************************************** + * Mock for the YT.Player + **************************************************************************************************************/ + + fluid.registerNamespace("gpii.tests.mock.YT"); + + // Thisist object to be created with the new constructor to match YT.Player api + gpii.tests.mock.YT.player = function (id, options) { + this.id = id; + this.options = options; + this.loadModule = sinon.stub(); + this.unloadModule = sinon.stub(); + this.setOption = sinon.stub(); + this.getOption = sinon.stub(); + }; + + gpii.tests.mock.YT.createGlobal = function () { + window.YT = { + Player: gpii.tests.mock.YT.player + }; + }; + + gpii.tests.mock.YT.removeGlobal = function () { + delete window.YT; + }; + + /************************************************************************************************************** + * gpii.uioPlus.allocateSimpleId tests + **************************************************************************************************************/ + + gpii.tests.allocateSimpleIdTests = [{ + name: "No ID", + selector: ".no-id", + expected: "gpii-uioPlus-id-" + gpii.uioPlus.gpii_prefix + "-0" + }, { + name: "Second without ID", + selector: ".no-id-2", + expected: "gpii-uioPlus-id-" + gpii.uioPlus.gpii_prefix + "-1" + }, { + name: "Existing ID", + selector: ".existing-id", + expected:"existing-id" + }]; + + jqUnit.test("Test gpii.uioPlus.allocateSimpleId", function () { + // setup + var originalGUID = gpii.uioPlus.guid; + gpii.uioPlus.guid = 0; + + // test + fluid.each(gpii.tests.allocateSimpleIdTests, function (testCase) { + var elm = $(testCase.selector); + var returnedID = gpii.uioPlus.allocateSimpleId(elm[0]); + jqUnit.assertEquals(testCase.name + ": the id should be returned", testCase.expected, returnedID); + jqUnit.assertEquals(testCase.name + ": the id should be added", testCase.expected, elm.attr("id")); + }); + + // cleanup + gpii.uioPlus.guid = originalGUID; + }); + + /************************************************************************************************************** + * gpii.uioPlus.player.enableJSAPI tests + **************************************************************************************************************/ + + fluid.registerNamespace("gpii.tests.player"); + + gpii.tests.player.enableJSAPITestCases = [{ + name: "no query parameters", + src: "https://localhost:8888/embed/SjnXy0Iplvs" + }, { + name: "jsapi disabled", + src: "https://localhost:8888/embed/SjnXy0Iplvs/?enablejsapi=0" + }, { + name: "jsapi invalid", + src: "https://localhost:8888/embed/SjnXy0Iplvs/?enablejsapi=false" + }, { + name: "jsapi enabled", + src: "https://localhost:8888/embed/SjnXy0Iplvs/?enablejsapi=1" + }, { + name: "other params", + src: "https://localhost:8888/embed/SjnXy0Iplvs/?controls=0&loop=1" + }]; + + jqUnit.test("Test gpii.uioPlus.player.enableJSAPI", function () { + var expected = "1"; + fluid.each(gpii.tests.player.enableJSAPITestCases, function (testCase) { + var elm = $("