mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
MDL-66388 h5p: add h5p atto button
This commit is contained in:
parent
1c3efe48f8
commit
297f7e411c
@ -9,6 +9,7 @@ cache/stores/mongodb/MongoDB/
|
||||
enrol/lti/ims-blti/
|
||||
filter/algebra/AlgParser.pm
|
||||
filter/tex/mimetex.*
|
||||
lib/editor/atto/plugins/h5p/js/h5p-resizer.js
|
||||
lib/editor/atto/plugins/html/yui/src/codemirror/
|
||||
lib/editor/atto/plugins/html/yui/src/beautify/
|
||||
lib/editor/atto/yui/src/rangy/js/*.*
|
||||
|
@ -10,6 +10,7 @@ cache/stores/mongodb/MongoDB/
|
||||
enrol/lti/ims-blti/
|
||||
filter/algebra/AlgParser.pm
|
||||
filter/tex/mimetex.*
|
||||
lib/editor/atto/plugins/h5p/js/h5p-resizer.js
|
||||
lib/editor/atto/plugins/html/yui/src/codemirror/
|
||||
lib/editor/atto/plugins/html/yui/src/beautify/
|
||||
lib/editor/atto/yui/src/rangy/js/*.*
|
||||
|
@ -1687,7 +1687,7 @@ class core_plugin_manager {
|
||||
'equation', 'fontcolor', 'html', 'image', 'indent', 'italic',
|
||||
'link', 'managefiles', 'media', 'noautolink', 'orderedlist',
|
||||
'recordrtc', 'rtl', 'strike', 'subscript', 'superscript', 'table',
|
||||
'title', 'underline', 'undo', 'unorderedlist'
|
||||
'title', 'underline', 'undo', 'unorderedlist', 'h5p'
|
||||
),
|
||||
|
||||
'assignment' => array(
|
||||
|
@ -103,5 +103,28 @@ function xmldb_editor_atto_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.7.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2019090900) {
|
||||
$toolbar = get_config('editor_atto', 'toolbar');
|
||||
|
||||
if (strpos($toolbar, 'h5p') === false) {
|
||||
$glue = "\r\n";
|
||||
if (strpos($toolbar, $glue) === false) {
|
||||
$glue = "\n";
|
||||
}
|
||||
$groups = explode($glue, $toolbar);
|
||||
// Try to put h5p in the files group.
|
||||
foreach ($groups as $i => $group) {
|
||||
$parts = explode('=', $group);
|
||||
if (trim($parts[0]) == 'files') {
|
||||
$groups[$i] = 'files = ' . trim($parts[1]) . ', h5p';
|
||||
// Update config variable.
|
||||
$toolbar = implode($glue, $groups);
|
||||
set_config('toolbar', $toolbar, 'editor_atto');
|
||||
}
|
||||
}
|
||||
}
|
||||
// Atto editor savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2019090900, 'editor', 'atto');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
46
lib/editor/atto/plugins/h5p/classes/privacy/provider.php
Normal file
46
lib/editor/atto/plugins/h5p/classes/privacy/provider.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for atto_h5p.
|
||||
*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace atto_h5p\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for atto_h5p implementing null_provider.
|
||||
*
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() : string {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
35
lib/editor/atto/plugins/h5p/db/access.php
Normal file
35
lib/editor/atto/plugins/h5p/db/access.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* H5P Atto button capabilities.
|
||||
*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$capabilities = [
|
||||
'atto/h5p:addembed' => [
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'archetypes' => [
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
],
|
||||
]
|
||||
];
|
131
lib/editor/atto/plugins/h5p/js/h5p-resizer.js
Normal file
131
lib/editor/atto/plugins/h5p/js/h5p-resizer.js
Normal file
@ -0,0 +1,131 @@
|
||||
// H5P iframe Resizer
|
||||
(function () {
|
||||
if (!window.postMessage || !window.addEventListener || window.h5pResizerInitialized) {
|
||||
return; // Not supported
|
||||
}
|
||||
window.h5pResizerInitialized = true;
|
||||
|
||||
// Map actions to handlers
|
||||
var actionHandlers = {};
|
||||
|
||||
/**
|
||||
* Prepare iframe resize.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} iframe Element
|
||||
* @param {Object} data Payload
|
||||
* @param {Function} respond Send a response to the iframe
|
||||
*/
|
||||
actionHandlers.hello = function (iframe, data, respond) {
|
||||
// Make iframe responsive
|
||||
iframe.style.width = '100%';
|
||||
|
||||
// Bugfix for Chrome: Force update of iframe width. If this is not done the
|
||||
// document size may not be updated before the content resizes.
|
||||
iframe.getBoundingClientRect();
|
||||
|
||||
// Tell iframe that it needs to resize when our window resizes
|
||||
var resize = function () {
|
||||
if (iframe.contentWindow) {
|
||||
// Limit resize calls to avoid flickering
|
||||
respond('resize');
|
||||
}
|
||||
else {
|
||||
// Frame is gone, unregister.
|
||||
window.removeEventListener('resize', resize);
|
||||
}
|
||||
};
|
||||
window.addEventListener('resize', resize, false);
|
||||
|
||||
// Respond to let the iframe know we can resize it
|
||||
respond('hello');
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare iframe resize.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} iframe Element
|
||||
* @param {Object} data Payload
|
||||
* @param {Function} respond Send a response to the iframe
|
||||
*/
|
||||
actionHandlers.prepareResize = function (iframe, data, respond) {
|
||||
// Do not resize unless page and scrolling differs
|
||||
if (iframe.clientHeight !== data.scrollHeight ||
|
||||
data.scrollHeight !== data.clientHeight) {
|
||||
|
||||
// Reset iframe height, in case content has shrinked.
|
||||
iframe.style.height = data.clientHeight + 'px';
|
||||
respond('resizePrepared');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resize parent and iframe to desired height.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} iframe Element
|
||||
* @param {Object} data Payload
|
||||
* @param {Function} respond Send a response to the iframe
|
||||
*/
|
||||
actionHandlers.resize = function (iframe, data) {
|
||||
// Resize iframe so all content is visible. Use scrollHeight to make sure we get everything
|
||||
iframe.style.height = data.scrollHeight + 'px';
|
||||
};
|
||||
|
||||
/**
|
||||
* Keyup event handler. Exits full screen on escape.
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
var escape = function (event) {
|
||||
if (event.keyCode === 27) {
|
||||
exitFullScreen();
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for messages from iframes
|
||||
window.addEventListener('message', function receiveMessage(event) {
|
||||
if (event.data.context !== 'h5p') {
|
||||
return; // Only handle h5p requests.
|
||||
}
|
||||
|
||||
// Find out who sent the message
|
||||
var iframe, iframes = document.getElementsByTagName('iframe');
|
||||
for (var i = 0; i < iframes.length; i++) {
|
||||
if (iframes[i].contentWindow === event.source) {
|
||||
iframe = iframes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!iframe) {
|
||||
return; // Cannot find sender
|
||||
}
|
||||
|
||||
// Find action handler handler
|
||||
if (actionHandlers[event.data.action]) {
|
||||
actionHandlers[event.data.action](iframe, event.data, function respond(action, data) {
|
||||
if (data === undefined) {
|
||||
data = {};
|
||||
}
|
||||
data.action = action;
|
||||
data.context = 'h5p';
|
||||
event.source.postMessage(data, event.origin);
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Let h5p iframes know we're ready!
|
||||
var iframes = document.getElementsByTagName('iframe');
|
||||
var ready = {
|
||||
context: 'h5p',
|
||||
action: 'ready'
|
||||
};
|
||||
for (var i = 0; i < iframes.length; i++) {
|
||||
if (iframes[i].src.indexOf('h5p') !== -1) {
|
||||
iframes[i].contentWindow.postMessage(ready, '*');
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
9
lib/editor/atto/plugins/h5p/js/readme_moodle.txt
Normal file
9
lib/editor/atto/plugins/h5p/js/readme_moodle.txt
Normal file
@ -0,0 +1,9 @@
|
||||
The H5P resizer JS.
|
||||
|
||||
to update:
|
||||
|
||||
Downloaded last release from: https://github.com/h5p/h5p-php-library/releases
|
||||
|
||||
Import
|
||||
|
||||
- In the downloaded h5p-php-library copy js/h5p-resizer.js into lib/editor/atto/plugins/h5p/js
|
31
lib/editor/atto/plugins/h5p/lang/en/atto_h5p.php
Normal file
31
lib/editor/atto/plugins/h5p/lang/en/atto_h5p.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Strings for component 'atto_h5p', language 'en'.
|
||||
*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['enterurl'] = 'Enter URL';
|
||||
$string['h5pproperties'] = 'H5P properties';
|
||||
$string['invalidh5purl'] = 'Invalid URL';
|
||||
$string['pluginname'] = 'Insert H5P';
|
||||
$string['privacy:metadata'] = 'The atto_h5p plugin does not store any personal data.';
|
||||
$string['h5p:addembed'] = 'Add embedded H5P';
|
||||
$string['saveh5p'] = 'Save H5P';
|
67
lib/editor/atto/plugins/h5p/lib.php
Normal file
67
lib/editor/atto/plugins/h5p/lib.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Atto text editor integration version file.
|
||||
*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Set params for this button.
|
||||
*
|
||||
* @param string $elementid
|
||||
* @param stdClass $options - the options for the editor, including the context.
|
||||
* @param stdClass $fpoptions - unused.
|
||||
*/
|
||||
function atto_h5p_params_for_js($elementid, $options, $fpoptions) {
|
||||
$context = $options['context'];
|
||||
if (!$context) {
|
||||
$context = context_system::instance();
|
||||
}
|
||||
$addembed = has_capability('atto/h5p:addembed', $context);
|
||||
|
||||
$allowedmethods = 'none';
|
||||
if ($addembed) {
|
||||
$allowedmethods = 'embed';
|
||||
}
|
||||
|
||||
$params = ['allowedmethods' => $allowedmethods];
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the strings required for js
|
||||
*/
|
||||
function atto_h5p_strings_for_js() {
|
||||
global $PAGE;
|
||||
|
||||
$strings = array(
|
||||
'saveh5p',
|
||||
'h5pproperties',
|
||||
'enterurl',
|
||||
'invalidh5purl'
|
||||
);
|
||||
|
||||
$PAGE->requires->strings_for_js($strings, 'atto_h5p');
|
||||
$PAGE->requires->js(new moodle_url('/lib/editor/atto/plugins/h5p/js/h5p-resizer.js'));
|
||||
}
|
||||
|
||||
|
BIN
lib/editor/atto/plugins/h5p/pix/icon.png
Normal file
BIN
lib/editor/atto/plugins/h5p/pix/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
14
lib/editor/atto/plugins/h5p/pix/icon.svg
Normal file
14
lib/editor/atto/plugins/h5p/pix/icon.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 345 150" style="enable-background:new 0 0 345 150;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M325.7,14.7C317.6,6.9,305.3,3,289,3h-43.5H234v31h-66l-5.4,22.2c4.5-2.1,10.9-4.2,15.3-5.3c4.4-1.1,8.8-0.9,13.1-0.9
|
||||
c14.6,0,26.5,4.5,35.6,13.3c9.1,8.8,13.6,20,13.6,33.4c0,9.4-2.3,18.5-7,27.2s-11.3,15.4-19.9,20c-3.1,1.6-6.5,3.1-10.2,4.1h42.4
|
||||
H259V95h25c18.2,0,31.7-4.2,40.6-12.5s13.3-19.9,13.3-34.6C337.9,33.6,333.8,22.5,325.7,14.7z M288.7,60.6c-3.5,3-9.6,4.4-18.3,4.4
|
||||
H259V33h13.2c8.4,0,14.2,1.5,17.2,4.7c3.1,3.2,4.6,6.9,4.6,11.5C294,53.9,292.2,57.6,288.7,60.6z"/>
|
||||
<path d="M176.5,76.3c-7.9,0-14.7,4.6-18,11.2L119,81.9L136.8,3h-23.6H101v62H51V3H7v145h44V95h50v53h12.2h42
|
||||
c-6.7-2-12.5-4.6-17.2-8.1c-4.8-3.6-8.7-7.7-11.7-12.3c-3-4.6-5.3-9.7-7.3-16.5l39.6-5.7c3.3,6.6,10.1,11.1,17.9,11.1
|
||||
c11.1,0,20.1-9,20.1-20.1S187.5,76.3,176.5,76.3z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
17
lib/editor/atto/plugins/h5p/styles.css
Normal file
17
lib/editor/atto/plugins/h5p/styles.css
Normal file
@ -0,0 +1,17 @@
|
||||
.attoh5poverlay {
|
||||
display: none;
|
||||
}
|
||||
.editor_atto_content_wrap .attoh5poverlay {
|
||||
display: block;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url([[pix:atto_h5p|icon]]) center center / 100px auto no-repeat #adb5bd;
|
||||
}
|
||||
.h5p-embed-placeholder .attoh5poverlay + br {
|
||||
display: none;
|
||||
}
|
56
lib/editor/atto/plugins/h5p/tests/behat/h5p.feature
Normal file
56
lib/editor/atto/plugins/h5p/tests/behat/h5p.feature
Normal file
@ -0,0 +1,56 @@
|
||||
@editor @editor_atto @atto @atto_h5p @_switch_iframe
|
||||
Feature: Add h5ps to Atto
|
||||
To write rich text - I need to add h5ps.
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| shortname | fullname |
|
||||
| C1 | Course 1 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | introformat | course | content | contentformat | idnumber |
|
||||
| page | PageName1 | PageDesc1 | 1 | C1 | H5Ptest | 1 | 1 |
|
||||
|
||||
@javascript
|
||||
Scenario: Insert an embedded h5p
|
||||
Given I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I set the field "Enter URL" to "https://h5p.org/h5p/embed/576651"
|
||||
And I click on "Save H5P" "button" in the "H5P properties" "dialogue"
|
||||
And I wait until the page is ready
|
||||
When I click on "Save and display" "button"
|
||||
And I switch to "h5pcontent" iframe
|
||||
Then ".h5p-iframe" "css_element" should exist
|
||||
|
||||
@javascript
|
||||
Scenario: Test an invalid url
|
||||
Given I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I click on "Insert H5P" "button" in the "#fitem_id_page" "css_element"
|
||||
And I set the field "Enter URL" to "ftp://h5p.org/h5p/embed/576651"
|
||||
And I click on "Save H5P" "button" in the "H5P properties" "dialogue"
|
||||
And I wait until the page is ready
|
||||
Then I should see "Invalid URL" in the "H5P properties" "dialogue"
|
||||
|
||||
@javascript
|
||||
Scenario: No embed h5p capabilities
|
||||
Given I log in as "admin"
|
||||
And I set the following system permissions of "Teacher" role:
|
||||
| capability | permission |
|
||||
| atto/h5p:addembed | Prohibit |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "PageName1"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
Then "Insert H5P" "button" should not exist
|
10
lib/editor/atto/plugins/h5p/thirdpartylibs.xml
Normal file
10
lib/editor/atto/plugins/h5p/thirdpartylibs.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<libraries>
|
||||
<library>
|
||||
<location>js/h5p-resizer.js</location>
|
||||
<name>H5P Resizer</name>
|
||||
<license>GPL-3.0</license>
|
||||
<version>1.23.1</version>
|
||||
<licenseversion></licenseversion>
|
||||
</library>
|
||||
</libraries>
|
29
lib/editor/atto/plugins/h5p/version.php
Normal file
29
lib/editor/atto/plugins/h5p/version.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Atto text editor integration version file.
|
||||
*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2019081900; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2019051100; // Requires this Moodle version.
|
||||
$plugin->component = 'atto_h5p'; // Full name of the plugin (used for diagnostics).
|
320
lib/editor/atto/plugins/h5p/yui/build/moodle-atto_h5p-button/moodle-atto_h5p-button-debug.js
vendored
Normal file
320
lib/editor/atto/plugins/h5p/yui/build/moodle-atto_h5p-button/moodle-atto_h5p-button-debug.js
vendored
Normal file
@ -0,0 +1,320 @@
|
||||
YUI.add('moodle-atto_h5p-button', function (Y, NAME) {
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module moodle-atto_h5p-button
|
||||
*/
|
||||
|
||||
/**
|
||||
* Atto h5p content tool.
|
||||
*
|
||||
* @namespace M.atto_h5p
|
||||
* @class Button
|
||||
* @extends M.editor_atto.EditorPlugin
|
||||
*/
|
||||
|
||||
var CSS = {
|
||||
INPUTALT: 'atto_h5p_altentry',
|
||||
INPUTSUBMIT: 'atto_h5p_urlentrysubmit',
|
||||
INPUTH5PURL: 'atto_h5p_url',
|
||||
URLWARNING: 'atto_h5p_warning'
|
||||
},
|
||||
SELECTORS = {
|
||||
INPUTH5PURL: '.' + CSS.INPUTH5PURL
|
||||
},
|
||||
|
||||
COMPONENTNAME = 'atto_h5p',
|
||||
|
||||
TEMPLATE = '' +
|
||||
'<form class="atto_form">' +
|
||||
'<div class="mb-4">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTH5PURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.URLWARNING}}">' +
|
||||
'{{get_string "invalidh5purl" component}}' +
|
||||
'</div>' +
|
||||
'<input class="form-control fullwidth {{CSS.INPUTH5PURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTH5PURL}}" size="32"/>' +
|
||||
'</div>' +
|
||||
'<div class="text-center">' +
|
||||
'<button class="btn btn-secondary {{CSS.INPUTSUBMIT}}" type="submit">' + '' +
|
||||
'{{get_string "saveh5p" component}}</button>' +
|
||||
'</div>' +
|
||||
'</form>',
|
||||
|
||||
H5PTEMPLATE = '' +
|
||||
'<div class="position-relative h5p-embed-placeholder">' +
|
||||
'<div class="attoh5poverlay"></div>' +
|
||||
'<iframe id="h5pcontent" class="h5pcontent" src="{{url}}/embed" ' +
|
||||
'width="100%" height="637" frameborder="0"' +
|
||||
'allowfullscreen="{{allowfullscreen}}" allowmedia="{{allowmedia}}">' +
|
||||
'</iframe>' +
|
||||
'<script src="' + M.cfg.wwwroot + '/lib/editor/atto/plugins/h5p/js/h5p-resizer.js"' +
|
||||
'charset="UTF-8"></script>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<p><br></p>';
|
||||
|
||||
Y.namespace('M.atto_h5p').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
|
||||
/**
|
||||
* A reference to the current selection at the time that the dialogue
|
||||
* was opened.
|
||||
*
|
||||
* @property _currentSelection
|
||||
* @type Range
|
||||
* @private
|
||||
*/
|
||||
_currentSelection: null,
|
||||
|
||||
/**
|
||||
* A reference to the currently open form.
|
||||
*
|
||||
* @param _form
|
||||
* @type Node
|
||||
* @private
|
||||
*/
|
||||
_form: null,
|
||||
|
||||
/**
|
||||
* A reference to the currently selected H5P placeholder.
|
||||
*
|
||||
* @param _form
|
||||
* @type Node
|
||||
* @private
|
||||
*/
|
||||
_placeholderH5P: null,
|
||||
|
||||
initializer: function() {
|
||||
var allowedmethods = this.get('allowedmethods');
|
||||
if (allowedmethods !== 'embed') {
|
||||
// Plugin not available here.
|
||||
return;
|
||||
}
|
||||
|
||||
this.addButton({
|
||||
icon: 'icon',
|
||||
iconComponent: 'atto_h5p',
|
||||
callback: this._displayDialogue,
|
||||
tags: '.attoh5poverlay',
|
||||
tagMatchRequiresAll: false
|
||||
});
|
||||
|
||||
this.editor.delegate('dblclick', this._handleDblClick, '.attoh5poverlay', this);
|
||||
this.editor.delegate('click', this._handleClick, '.attoh5poverlay', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a double click on a H5P Placeholder.
|
||||
*
|
||||
* @method _handleDblClick
|
||||
* @private
|
||||
*/
|
||||
_handleDblClick: function() {
|
||||
this._displayDialogue();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a click on a H5P Placeholder.
|
||||
*
|
||||
* @method _handleClick
|
||||
* @param {EventFacade} e
|
||||
* @private
|
||||
*/
|
||||
_handleClick: function(e) {
|
||||
var h5pplaceholder = e.target;
|
||||
|
||||
var selection = this.get('host').getSelectionFromNode(h5pplaceholder);
|
||||
if (this.get('host').getSelection() !== selection) {
|
||||
this.get('host').setSelection(selection);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the h5p editing tool.
|
||||
*
|
||||
* @method _displayDialogue
|
||||
* @private
|
||||
*/
|
||||
_displayDialogue: function() {
|
||||
// Store the current selection.
|
||||
this._currentSelection = this.get('host').getSelection();
|
||||
this._placeholderH5P = this._getH5PIframe();
|
||||
|
||||
if (this._currentSelection === false) {
|
||||
return;
|
||||
}
|
||||
var dialogue = this.getDialogue({
|
||||
headerContent: M.util.get_string('h5pproperties', COMPONENTNAME),
|
||||
width: 'auto',
|
||||
focusAfterHide: true,
|
||||
focusOnShowSelector: SELECTORS.INPUTH5PURL
|
||||
});
|
||||
|
||||
// Set the dialogue content, and then show the dialogue.
|
||||
dialogue.set('bodyContent', this._getDialogueContent())
|
||||
.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the H5P iframe
|
||||
*
|
||||
* @method _resolveH5P
|
||||
* @return {Node} The H5P iframe selected.
|
||||
* @private
|
||||
*/
|
||||
_getH5PIframe: function() {
|
||||
var selectednode = this.get('host').getSelectionParentNode();
|
||||
if (!selectednode) {
|
||||
return;
|
||||
}
|
||||
return Y.one(selectednode).one('iframe.h5pcontent');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the dialogue content for the tool, attaching any required
|
||||
* events.
|
||||
*
|
||||
* @method _getDialogueContent
|
||||
* @return {Node} The content to place in the dialogue.
|
||||
* @private
|
||||
*/
|
||||
_getDialogueContent: function() {
|
||||
var template = Y.Handlebars.compile(TEMPLATE),
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
CSS: CSS,
|
||||
component: COMPONENTNAME
|
||||
}));
|
||||
|
||||
this._form = content;
|
||||
|
||||
if (this._placeholderH5P) {
|
||||
var oldurl = this._placeholderH5P.getAttribute('src');
|
||||
this._form.one(SELECTORS.INPUTH5PURL).setAttribute('value', oldurl);
|
||||
}
|
||||
|
||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setH5P, this);
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the h5p in the contenteditable.
|
||||
*
|
||||
* @method _setH5P
|
||||
* @param {EventFacade} e
|
||||
* @private
|
||||
*/
|
||||
_setH5P: function(e) {
|
||||
var form = this._form,
|
||||
url = form.one(SELECTORS.INPUTH5PURL).get('value'),
|
||||
h5phtml,
|
||||
host = this.get('host');
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// Check if there are any issues.
|
||||
if (this._updateWarning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Focus on the editor in preparation for inserting the h5p.
|
||||
host.focus();
|
||||
|
||||
// If a H5P placeholder was selected we only update the placeholder.
|
||||
if (this._placeholderH5P) {
|
||||
this._placeholderH5P.setAttribute('src', url);
|
||||
|
||||
} else if (url !== '') {
|
||||
|
||||
host.setSelection(this._currentSelection);
|
||||
|
||||
var template = Y.Handlebars.compile(H5PTEMPLATE);
|
||||
h5phtml = template({
|
||||
url: url,
|
||||
allowfullscreen: 'allowfullscreen',
|
||||
allowmedia: 'geolocation *; microphone *; camera *; midi *; encrypted-media *'
|
||||
});
|
||||
|
||||
this.get('host').insertContentAtFocusPoint(h5phtml);
|
||||
|
||||
this.markUpdated();
|
||||
}
|
||||
|
||||
this.getDialogue({
|
||||
focusAfterHide: null
|
||||
}).hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if this could be a h5p URL.
|
||||
*
|
||||
* @method _updateWarning
|
||||
* @param {String} str
|
||||
* @return {boolean} whether a warning should be displayed.
|
||||
* @private
|
||||
*/
|
||||
_validURL: function(str) {
|
||||
var pattern = new RegExp('^(https?:\\/\\/)?' + // Protocol.
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name.
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address.
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path.
|
||||
return !!pattern.test(str);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the url warning.
|
||||
*
|
||||
* @method _updateWarning
|
||||
* @return {boolean} whether a warning should be displayed.
|
||||
* @private
|
||||
*/
|
||||
_updateWarning: function() {
|
||||
var form = this._form,
|
||||
state = true,
|
||||
url = form.one('.' + CSS.INPUTH5PURL).get('value');
|
||||
if (this._validURL(url)) {
|
||||
form.one('.' + CSS.URLWARNING).setStyle('display', 'none');
|
||||
state = false;
|
||||
} else {
|
||||
form.one('.' + CSS.URLWARNING).setStyle('display', 'block');
|
||||
state = true;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}, {
|
||||
ATTRS: {
|
||||
/**
|
||||
* The allowedmethods of adding h5p content.
|
||||
*
|
||||
* @attribute allowedmethods
|
||||
* @type String
|
||||
*/
|
||||
allowedmethods: {
|
||||
value: null
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
|
1
lib/editor/atto/plugins/h5p/yui/build/moodle-atto_h5p-button/moodle-atto_h5p-button-min.js
vendored
Normal file
1
lib/editor/atto/plugins/h5p/yui/build/moodle-atto_h5p-button/moodle-atto_h5p-button-min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
YUI.add("moodle-atto_h5p-button",function(e,t){var n={INPUTALT:"atto_h5p_altentry",INPUTSUBMIT:"atto_h5p_urlentrysubmit",INPUTH5PURL:"atto_h5p_url",URLWARNING:"atto_h5p_warning"},r={INPUTH5PURL:"."+n.INPUTH5PURL},i="atto_h5p",s='<form class="atto_form"><div class="mb-4"><label for="{{elementid}}_{{CSS.INPUTH5PURL}}">{{get_string "enterurl" component}}</label><div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.URLWARNING}}">{{get_string "invalidh5purl" component}}</div><input class="form-control fullwidth {{CSS.INPUTH5PURL}}" type="url" id="{{elementid}}_{{CSS.INPUTH5PURL}}" size="32"/></div><div class="text-center"><button class="btn btn-secondary {{CSS.INPUTSUBMIT}}" type="submit">{{get_string "saveh5p" component}}</button></div></form>',o='<div class="position-relative h5p-embed-placeholder"><div class="attoh5poverlay"></div><iframe id="h5pcontent" class="h5pcontent" src="{{url}}/embed" width="100%" height="637" frameborder="0"allowfullscreen="{{allowfullscreen}}" allowmedia="{{allowmedia}}"></iframe><script src="'+M.cfg.wwwroot+'/lib/editor/atto/plugins/h5p/js/h5p-resizer.js"'+'charset="UTF-8"></script>'+"</div>"+"</div>"+"<p><br></p>";e.namespace("M.atto_h5p").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_form:null,_placeholderH5P:null,initializer:function(){var e=this.get("allowedmethods");if(e!=="embed")return;this.addButton({icon:"icon",iconComponent:"atto_h5p",callback:this._displayDialogue,tags:".attoh5poverlay",tagMatchRequiresAll:!1}),this.editor.delegate("dblclick",this._handleDblClick,".attoh5poverlay",this),this.editor.delegate("click",this._handleClick,".attoh5poverlay",this)},_handleDblClick:function(){this._displayDialogue()},_handleClick:function(e){var t=e.target,n=this.get("host").getSelectionFromNode(t);this.get("host").getSelection()!==n&&this.get("host").setSelection(n)},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection(),this._placeholderH5P=this._getH5PIframe();if(this._currentSelection===!1)return;var e=this.getDialogue({headerContent:M.util.get_string("h5pproperties",i),width:"auto",focusAfterHide:!0,focusOnShowSelector:r.INPUTH5PURL});e.set("bodyContent",this._getDialogueContent()).show()},_getH5PIframe:function(){var t=this.get("host").getSelectionParentNode();if(!t)return;return e.one(t).one("iframe.h5pcontent")},_getDialogueContent:function(){var t=e.Handlebars.compile(s),o=e.Node.create(t({elementid:this.get("host").get("elementid"),CSS:n,component:i}));this._form=o;if(this._placeholderH5P){var u=this._placeholderH5P.getAttribute("src");this._form.one(r.INPUTH5PURL).setAttribute("value",u)}return this._form.one("."+n.INPUTSUBMIT).on("click",this._setH5P,this),o},_setH5P:function(t){var n=this._form,i=n.one(r.INPUTH5PURL).get("value"),s,u=this.get("host");t.preventDefault();if(this._updateWarning())return;u.focus();if(this._placeholderH5P)this._placeholderH5P.setAttribute("src",i);else if(i!==""){u.setSelection(this._currentSelection);var a=e.Handlebars.compile(o);s=a({url:i,allowfullscreen:"allowfullscreen",allowmedia:"geolocation *; microphone *; camera *; midi *; encrypted-media *"}),this.get("host").insertContentAtFocusPoint(s),this.markUpdated()}this.getDialogue({focusAfterHide:null}).hide()},_validURL:function(e){var t=new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*");return!!t.test(e)},_updateWarning:function(){var e=this._form,t=!0,r=e.one("."+n.INPUTH5PURL).get("value");return this._validURL(r)?(e.one("."+n.URLWARNING).setStyle("display","none"),t=!1):(e.one("."+n.URLWARNING).setStyle("display","block"),t=!0),t}},{ATTRS:{allowedmethods:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
|
320
lib/editor/atto/plugins/h5p/yui/build/moodle-atto_h5p-button/moodle-atto_h5p-button.js
vendored
Normal file
320
lib/editor/atto/plugins/h5p/yui/build/moodle-atto_h5p-button/moodle-atto_h5p-button.js
vendored
Normal file
@ -0,0 +1,320 @@
|
||||
YUI.add('moodle-atto_h5p-button', function (Y, NAME) {
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module moodle-atto_h5p-button
|
||||
*/
|
||||
|
||||
/**
|
||||
* Atto h5p content tool.
|
||||
*
|
||||
* @namespace M.atto_h5p
|
||||
* @class Button
|
||||
* @extends M.editor_atto.EditorPlugin
|
||||
*/
|
||||
|
||||
var CSS = {
|
||||
INPUTALT: 'atto_h5p_altentry',
|
||||
INPUTSUBMIT: 'atto_h5p_urlentrysubmit',
|
||||
INPUTH5PURL: 'atto_h5p_url',
|
||||
URLWARNING: 'atto_h5p_warning'
|
||||
},
|
||||
SELECTORS = {
|
||||
INPUTH5PURL: '.' + CSS.INPUTH5PURL
|
||||
},
|
||||
|
||||
COMPONENTNAME = 'atto_h5p',
|
||||
|
||||
TEMPLATE = '' +
|
||||
'<form class="atto_form">' +
|
||||
'<div class="mb-4">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTH5PURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.URLWARNING}}">' +
|
||||
'{{get_string "invalidh5purl" component}}' +
|
||||
'</div>' +
|
||||
'<input class="form-control fullwidth {{CSS.INPUTH5PURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTH5PURL}}" size="32"/>' +
|
||||
'</div>' +
|
||||
'<div class="text-center">' +
|
||||
'<button class="btn btn-secondary {{CSS.INPUTSUBMIT}}" type="submit">' + '' +
|
||||
'{{get_string "saveh5p" component}}</button>' +
|
||||
'</div>' +
|
||||
'</form>',
|
||||
|
||||
H5PTEMPLATE = '' +
|
||||
'<div class="position-relative h5p-embed-placeholder">' +
|
||||
'<div class="attoh5poverlay"></div>' +
|
||||
'<iframe id="h5pcontent" class="h5pcontent" src="{{url}}/embed" ' +
|
||||
'width="100%" height="637" frameborder="0"' +
|
||||
'allowfullscreen="{{allowfullscreen}}" allowmedia="{{allowmedia}}">' +
|
||||
'</iframe>' +
|
||||
'<script src="' + M.cfg.wwwroot + '/lib/editor/atto/plugins/h5p/js/h5p-resizer.js"' +
|
||||
'charset="UTF-8"></script>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<p><br></p>';
|
||||
|
||||
Y.namespace('M.atto_h5p').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
|
||||
/**
|
||||
* A reference to the current selection at the time that the dialogue
|
||||
* was opened.
|
||||
*
|
||||
* @property _currentSelection
|
||||
* @type Range
|
||||
* @private
|
||||
*/
|
||||
_currentSelection: null,
|
||||
|
||||
/**
|
||||
* A reference to the currently open form.
|
||||
*
|
||||
* @param _form
|
||||
* @type Node
|
||||
* @private
|
||||
*/
|
||||
_form: null,
|
||||
|
||||
/**
|
||||
* A reference to the currently selected H5P placeholder.
|
||||
*
|
||||
* @param _form
|
||||
* @type Node
|
||||
* @private
|
||||
*/
|
||||
_placeholderH5P: null,
|
||||
|
||||
initializer: function() {
|
||||
var allowedmethods = this.get('allowedmethods');
|
||||
if (allowedmethods !== 'embed') {
|
||||
// Plugin not available here.
|
||||
return;
|
||||
}
|
||||
|
||||
this.addButton({
|
||||
icon: 'icon',
|
||||
iconComponent: 'atto_h5p',
|
||||
callback: this._displayDialogue,
|
||||
tags: '.attoh5poverlay',
|
||||
tagMatchRequiresAll: false
|
||||
});
|
||||
|
||||
this.editor.delegate('dblclick', this._handleDblClick, '.attoh5poverlay', this);
|
||||
this.editor.delegate('click', this._handleClick, '.attoh5poverlay', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a double click on a H5P Placeholder.
|
||||
*
|
||||
* @method _handleDblClick
|
||||
* @private
|
||||
*/
|
||||
_handleDblClick: function() {
|
||||
this._displayDialogue();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a click on a H5P Placeholder.
|
||||
*
|
||||
* @method _handleClick
|
||||
* @param {EventFacade} e
|
||||
* @private
|
||||
*/
|
||||
_handleClick: function(e) {
|
||||
var h5pplaceholder = e.target;
|
||||
|
||||
var selection = this.get('host').getSelectionFromNode(h5pplaceholder);
|
||||
if (this.get('host').getSelection() !== selection) {
|
||||
this.get('host').setSelection(selection);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the h5p editing tool.
|
||||
*
|
||||
* @method _displayDialogue
|
||||
* @private
|
||||
*/
|
||||
_displayDialogue: function() {
|
||||
// Store the current selection.
|
||||
this._currentSelection = this.get('host').getSelection();
|
||||
this._placeholderH5P = this._getH5PIframe();
|
||||
|
||||
if (this._currentSelection === false) {
|
||||
return;
|
||||
}
|
||||
var dialogue = this.getDialogue({
|
||||
headerContent: M.util.get_string('h5pproperties', COMPONENTNAME),
|
||||
width: 'auto',
|
||||
focusAfterHide: true,
|
||||
focusOnShowSelector: SELECTORS.INPUTH5PURL
|
||||
});
|
||||
|
||||
// Set the dialogue content, and then show the dialogue.
|
||||
dialogue.set('bodyContent', this._getDialogueContent())
|
||||
.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the H5P iframe
|
||||
*
|
||||
* @method _resolveH5P
|
||||
* @return {Node} The H5P iframe selected.
|
||||
* @private
|
||||
*/
|
||||
_getH5PIframe: function() {
|
||||
var selectednode = this.get('host').getSelectionParentNode();
|
||||
if (!selectednode) {
|
||||
return;
|
||||
}
|
||||
return Y.one(selectednode).one('iframe.h5pcontent');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the dialogue content for the tool, attaching any required
|
||||
* events.
|
||||
*
|
||||
* @method _getDialogueContent
|
||||
* @return {Node} The content to place in the dialogue.
|
||||
* @private
|
||||
*/
|
||||
_getDialogueContent: function() {
|
||||
var template = Y.Handlebars.compile(TEMPLATE),
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
CSS: CSS,
|
||||
component: COMPONENTNAME
|
||||
}));
|
||||
|
||||
this._form = content;
|
||||
|
||||
if (this._placeholderH5P) {
|
||||
var oldurl = this._placeholderH5P.getAttribute('src');
|
||||
this._form.one(SELECTORS.INPUTH5PURL).setAttribute('value', oldurl);
|
||||
}
|
||||
|
||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setH5P, this);
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the h5p in the contenteditable.
|
||||
*
|
||||
* @method _setH5P
|
||||
* @param {EventFacade} e
|
||||
* @private
|
||||
*/
|
||||
_setH5P: function(e) {
|
||||
var form = this._form,
|
||||
url = form.one(SELECTORS.INPUTH5PURL).get('value'),
|
||||
h5phtml,
|
||||
host = this.get('host');
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// Check if there are any issues.
|
||||
if (this._updateWarning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Focus on the editor in preparation for inserting the h5p.
|
||||
host.focus();
|
||||
|
||||
// If a H5P placeholder was selected we only update the placeholder.
|
||||
if (this._placeholderH5P) {
|
||||
this._placeholderH5P.setAttribute('src', url);
|
||||
|
||||
} else if (url !== '') {
|
||||
|
||||
host.setSelection(this._currentSelection);
|
||||
|
||||
var template = Y.Handlebars.compile(H5PTEMPLATE);
|
||||
h5phtml = template({
|
||||
url: url,
|
||||
allowfullscreen: 'allowfullscreen',
|
||||
allowmedia: 'geolocation *; microphone *; camera *; midi *; encrypted-media *'
|
||||
});
|
||||
|
||||
this.get('host').insertContentAtFocusPoint(h5phtml);
|
||||
|
||||
this.markUpdated();
|
||||
}
|
||||
|
||||
this.getDialogue({
|
||||
focusAfterHide: null
|
||||
}).hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if this could be a h5p URL.
|
||||
*
|
||||
* @method _updateWarning
|
||||
* @param {String} str
|
||||
* @return {boolean} whether a warning should be displayed.
|
||||
* @private
|
||||
*/
|
||||
_validURL: function(str) {
|
||||
var pattern = new RegExp('^(https?:\\/\\/)?' + // Protocol.
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name.
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address.
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path.
|
||||
return !!pattern.test(str);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the url warning.
|
||||
*
|
||||
* @method _updateWarning
|
||||
* @return {boolean} whether a warning should be displayed.
|
||||
* @private
|
||||
*/
|
||||
_updateWarning: function() {
|
||||
var form = this._form,
|
||||
state = true,
|
||||
url = form.one('.' + CSS.INPUTH5PURL).get('value');
|
||||
if (this._validURL(url)) {
|
||||
form.one('.' + CSS.URLWARNING).setStyle('display', 'none');
|
||||
state = false;
|
||||
} else {
|
||||
form.one('.' + CSS.URLWARNING).setStyle('display', 'block');
|
||||
state = true;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}, {
|
||||
ATTRS: {
|
||||
/**
|
||||
* The allowedmethods of adding h5p content.
|
||||
*
|
||||
* @attribute allowedmethods
|
||||
* @type String
|
||||
*/
|
||||
allowedmethods: {
|
||||
value: null
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
|
10
lib/editor/atto/plugins/h5p/yui/src/button/build.json
Normal file
10
lib/editor/atto/plugins/h5p/yui/src/button/build.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "moodle-atto_h5p-button",
|
||||
"builds": {
|
||||
"moodle-atto_h5p-button": {
|
||||
"jsfiles": [
|
||||
"button.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
315
lib/editor/atto/plugins/h5p/yui/src/button/js/button.js
vendored
Normal file
315
lib/editor/atto/plugins/h5p/yui/src/button/js/button.js
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
* @package atto_h5p
|
||||
* @copyright 2019 Bas Brands <bas@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module moodle-atto_h5p-button
|
||||
*/
|
||||
|
||||
/**
|
||||
* Atto h5p content tool.
|
||||
*
|
||||
* @namespace M.atto_h5p
|
||||
* @class Button
|
||||
* @extends M.editor_atto.EditorPlugin
|
||||
*/
|
||||
|
||||
var CSS = {
|
||||
INPUTALT: 'atto_h5p_altentry',
|
||||
INPUTSUBMIT: 'atto_h5p_urlentrysubmit',
|
||||
INPUTH5PURL: 'atto_h5p_url',
|
||||
URLWARNING: 'atto_h5p_warning'
|
||||
},
|
||||
SELECTORS = {
|
||||
INPUTH5PURL: '.' + CSS.INPUTH5PURL
|
||||
},
|
||||
|
||||
COMPONENTNAME = 'atto_h5p',
|
||||
|
||||
TEMPLATE = '' +
|
||||
'<form class="atto_form">' +
|
||||
'<div class="mb-4">' +
|
||||
'<label for="{{elementid}}_{{CSS.INPUTH5PURL}}">{{get_string "enterurl" component}}</label>' +
|
||||
'<div style="display:none" role="alert" class="alert alert-warning mb-1 {{CSS.URLWARNING}}">' +
|
||||
'{{get_string "invalidh5purl" component}}' +
|
||||
'</div>' +
|
||||
'<input class="form-control fullwidth {{CSS.INPUTH5PURL}}" type="url" ' +
|
||||
'id="{{elementid}}_{{CSS.INPUTH5PURL}}" size="32"/>' +
|
||||
'</div>' +
|
||||
'<div class="text-center">' +
|
||||
'<button class="btn btn-secondary {{CSS.INPUTSUBMIT}}" type="submit">' + '' +
|
||||
'{{get_string "saveh5p" component}}</button>' +
|
||||
'</div>' +
|
||||
'</form>',
|
||||
|
||||
H5PTEMPLATE = '' +
|
||||
'<div class="position-relative h5p-embed-placeholder">' +
|
||||
'<div class="attoh5poverlay"></div>' +
|
||||
'<iframe id="h5pcontent" class="h5pcontent" src="{{url}}/embed" ' +
|
||||
'width="100%" height="637" frameborder="0"' +
|
||||
'allowfullscreen="{{allowfullscreen}}" allowmedia="{{allowmedia}}">' +
|
||||
'</iframe>' +
|
||||
'<script src="' + M.cfg.wwwroot + '/lib/editor/atto/plugins/h5p/js/h5p-resizer.js"' +
|
||||
'charset="UTF-8"></script>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<p><br></p>';
|
||||
|
||||
Y.namespace('M.atto_h5p').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
|
||||
/**
|
||||
* A reference to the current selection at the time that the dialogue
|
||||
* was opened.
|
||||
*
|
||||
* @property _currentSelection
|
||||
* @type Range
|
||||
* @private
|
||||
*/
|
||||
_currentSelection: null,
|
||||
|
||||
/**
|
||||
* A reference to the currently open form.
|
||||
*
|
||||
* @param _form
|
||||
* @type Node
|
||||
* @private
|
||||
*/
|
||||
_form: null,
|
||||
|
||||
/**
|
||||
* A reference to the currently selected H5P placeholder.
|
||||
*
|
||||
* @param _form
|
||||
* @type Node
|
||||
* @private
|
||||
*/
|
||||
_placeholderH5P: null,
|
||||
|
||||
initializer: function() {
|
||||
var allowedmethods = this.get('allowedmethods');
|
||||
if (allowedmethods !== 'embed') {
|
||||
// Plugin not available here.
|
||||
return;
|
||||
}
|
||||
|
||||
this.addButton({
|
||||
icon: 'icon',
|
||||
iconComponent: 'atto_h5p',
|
||||
callback: this._displayDialogue,
|
||||
tags: '.attoh5poverlay',
|
||||
tagMatchRequiresAll: false
|
||||
});
|
||||
|
||||
this.editor.delegate('dblclick', this._handleDblClick, '.attoh5poverlay', this);
|
||||
this.editor.delegate('click', this._handleClick, '.attoh5poverlay', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a double click on a H5P Placeholder.
|
||||
*
|
||||
* @method _handleDblClick
|
||||
* @private
|
||||
*/
|
||||
_handleDblClick: function() {
|
||||
this._displayDialogue();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a click on a H5P Placeholder.
|
||||
*
|
||||
* @method _handleClick
|
||||
* @param {EventFacade} e
|
||||
* @private
|
||||
*/
|
||||
_handleClick: function(e) {
|
||||
var h5pplaceholder = e.target;
|
||||
|
||||
var selection = this.get('host').getSelectionFromNode(h5pplaceholder);
|
||||
if (this.get('host').getSelection() !== selection) {
|
||||
this.get('host').setSelection(selection);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the h5p editing tool.
|
||||
*
|
||||
* @method _displayDialogue
|
||||
* @private
|
||||
*/
|
||||
_displayDialogue: function() {
|
||||
// Store the current selection.
|
||||
this._currentSelection = this.get('host').getSelection();
|
||||
this._placeholderH5P = this._getH5PIframe();
|
||||
|
||||
if (this._currentSelection === false) {
|
||||
return;
|
||||
}
|
||||
var dialogue = this.getDialogue({
|
||||
headerContent: M.util.get_string('h5pproperties', COMPONENTNAME),
|
||||
width: 'auto',
|
||||
focusAfterHide: true,
|
||||
focusOnShowSelector: SELECTORS.INPUTH5PURL
|
||||
});
|
||||
|
||||
// Set the dialogue content, and then show the dialogue.
|
||||
dialogue.set('bodyContent', this._getDialogueContent())
|
||||
.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the H5P iframe
|
||||
*
|
||||
* @method _resolveH5P
|
||||
* @return {Node} The H5P iframe selected.
|
||||
* @private
|
||||
*/
|
||||
_getH5PIframe: function() {
|
||||
var selectednode = this.get('host').getSelectionParentNode();
|
||||
if (!selectednode) {
|
||||
return;
|
||||
}
|
||||
return Y.one(selectednode).one('iframe.h5pcontent');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the dialogue content for the tool, attaching any required
|
||||
* events.
|
||||
*
|
||||
* @method _getDialogueContent
|
||||
* @return {Node} The content to place in the dialogue.
|
||||
* @private
|
||||
*/
|
||||
_getDialogueContent: function() {
|
||||
var template = Y.Handlebars.compile(TEMPLATE),
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
CSS: CSS,
|
||||
component: COMPONENTNAME
|
||||
}));
|
||||
|
||||
this._form = content;
|
||||
|
||||
if (this._placeholderH5P) {
|
||||
var oldurl = this._placeholderH5P.getAttribute('src');
|
||||
this._form.one(SELECTORS.INPUTH5PURL).setAttribute('value', oldurl);
|
||||
}
|
||||
|
||||
this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setH5P, this);
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the h5p in the contenteditable.
|
||||
*
|
||||
* @method _setH5P
|
||||
* @param {EventFacade} e
|
||||
* @private
|
||||
*/
|
||||
_setH5P: function(e) {
|
||||
var form = this._form,
|
||||
url = form.one(SELECTORS.INPUTH5PURL).get('value'),
|
||||
h5phtml,
|
||||
host = this.get('host');
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// Check if there are any issues.
|
||||
if (this._updateWarning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Focus on the editor in preparation for inserting the h5p.
|
||||
host.focus();
|
||||
|
||||
// If a H5P placeholder was selected we only update the placeholder.
|
||||
if (this._placeholderH5P) {
|
||||
this._placeholderH5P.setAttribute('src', url);
|
||||
|
||||
} else if (url !== '') {
|
||||
|
||||
host.setSelection(this._currentSelection);
|
||||
|
||||
var template = Y.Handlebars.compile(H5PTEMPLATE);
|
||||
h5phtml = template({
|
||||
url: url,
|
||||
allowfullscreen: 'allowfullscreen',
|
||||
allowmedia: 'geolocation *; microphone *; camera *; midi *; encrypted-media *'
|
||||
});
|
||||
|
||||
this.get('host').insertContentAtFocusPoint(h5phtml);
|
||||
|
||||
this.markUpdated();
|
||||
}
|
||||
|
||||
this.getDialogue({
|
||||
focusAfterHide: null
|
||||
}).hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if this could be a h5p URL.
|
||||
*
|
||||
* @method _updateWarning
|
||||
* @param {String} str
|
||||
* @return {boolean} whether a warning should be displayed.
|
||||
* @private
|
||||
*/
|
||||
_validURL: function(str) {
|
||||
var pattern = new RegExp('^(https?:\\/\\/)?' + // Protocol.
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name.
|
||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address.
|
||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path.
|
||||
return !!pattern.test(str);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the url warning.
|
||||
*
|
||||
* @method _updateWarning
|
||||
* @return {boolean} whether a warning should be displayed.
|
||||
* @private
|
||||
*/
|
||||
_updateWarning: function() {
|
||||
var form = this._form,
|
||||
state = true,
|
||||
url = form.one('.' + CSS.INPUTH5PURL).get('value');
|
||||
if (this._validURL(url)) {
|
||||
form.one('.' + CSS.URLWARNING).setStyle('display', 'none');
|
||||
state = false;
|
||||
} else {
|
||||
form.one('.' + CSS.URLWARNING).setStyle('display', 'block');
|
||||
state = true;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}, {
|
||||
ATTRS: {
|
||||
/**
|
||||
* The allowedmethods of adding h5p content.
|
||||
*
|
||||
* @attribute allowedmethods
|
||||
* @type String
|
||||
*/
|
||||
allowedmethods: {
|
||||
value: null
|
||||
}
|
||||
}
|
||||
});
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"moodle-atto_h5p-button": {
|
||||
"requires": [
|
||||
"moodle-editor_atto-plugin"
|
||||
]
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ if ($ADMIN->fulltree) {
|
||||
style1 = title, bold, italic
|
||||
list = unorderedlist, orderedlist
|
||||
links = link
|
||||
files = image, media, recordrtc, managefiles
|
||||
files = image, media, recordrtc, managefiles, h5p
|
||||
style2 = underline, strike, subscript, superscript
|
||||
align = align
|
||||
indent = indent
|
||||
|
@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2019052000; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2019090900; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2019051100; // Requires this Moodle version.
|
||||
$plugin->component = 'editor_atto'; // Full name of the plugin (used for diagnostics).
|
||||
|
Loading…
x
Reference in New Issue
Block a user